Skip to content

Commit 75ab46b

Browse files
committed
OpTestKernelTest: Add support for automated boot bisection v2.0
This code is modifed and develped on top of the previous code (2f103 : OpTestKernelTest: Git bisect automation..) With version 2.0 these are the new changes added: 1. Added support to handle nested bisection loop, which was not working with previous code 2. Modified code to work for end to end CI server 3. Better handle all cases of good flow and bad flow 4. Add json store of good boot commit for good run 5. Fix the previous code blocker to properly manage the console thread TODO: Add boot json generation after bisection with bad commit Email format for boot bug report Signed-off-by: Abdul Haleem <[email protected]>
1 parent 9fc07f3 commit 75ab46b

File tree

1 file changed

+86
-56
lines changed

1 file changed

+86
-56
lines changed

testcases/OpTestKernelTest.py

+86-56
Original file line numberDiff line numberDiff line change
@@ -52,17 +52,14 @@ def setUp(self):
5252
self.con = self.cv_SYSTEM.cv_HOST.get_ssh_connection()
5353
self.host_cmd_timeout = self.conf.args.host_cmd_timeout
5454
self.repo = self.conf.args.git_repo
55-
self.repo_reference = self.conf.args.git_repo_reference
5655
self.branch = self.conf.args.git_branch
5756
self.home = self.conf.args.git_home
5857
self.config_path = self.conf.args.git_repoconfigpath
59-
self.config = self.conf.args.git_repoconfig
6058
self.good_commit = self.conf.args.good_commit
6159
self.bad_commit = self.conf.args.bad_commit
62-
self.bisect_script = self.conf.args.bisect_script
63-
self.bisect_category = self.conf.args.bisect_category
6460
self.append_kernel_cmdline = self.conf.args.append_kernel_cmdline
6561
self.linux_path = os.path.join(self.home, "linux")
62+
self.local_path = os.path.join(os.getcwd(), "linux")
6663
self.bisect_flag = self.conf.args.bisect_flag
6764
self.util = OpTestUtil(OpTestConfiguration.conf)
6865
self.host_distro_name = self.util.distro_name()
@@ -98,7 +95,7 @@ def is_url(path):
9895
self.con.run_command("wget %s -O linux/.config" % self.config_path)
9996
else:
10097
self.cv_HOST.copy_test_file_to_host(self.config_path, sourcedir="", dstdir=os.path.join(linux_path, ".config"))
101-
self.con.run_command("cd linux && make olddefconfig")
98+
self.con.run_command("cd linux && make olddefconfig", timeout=60)
10299
# the below part of the code is needed for only first run and will be decided bisect flag false
103100
ker_ver = self.con.run_command("make kernelrelease")[-1]
104101
sha = self.con.run_command("git rev-parse HEAD")
@@ -109,8 +106,8 @@ def is_url(path):
109106
log.info("Upstream kernel commit-time: %s", tcommit)
110107
log.debug("Compile the upstream kernel")
111108
try:
112-
cpu= self.con.run_command("lscpu | grep '^CPU(s):' | awk '{print $2}'")
113-
err=self.con.run_command("make -j {} -s vmlinux".format(cpu[-1]), timeout=self.host_cmd_timeout)
109+
cpu = int(self.con.run_command("lscpu --online -e|wc -l")[-1])
110+
err=self.con.run_command("make -j {} -s".format(cpu), timeout=self.host_cmd_timeout)
114111
log.info("Kernel build successful")
115112
return 0,err
116113
except CommandFailed as e:
@@ -130,13 +127,13 @@ def boot_kernel(self):
130127
Does kexec boot for Upstream Linux
131128
"""
132129
self.con.run_command("export TERM=dumb; export NO_COLOR=1; alias ls='ls --color=never'; alias grep='grep --color=never'; git config --global color.ui false; bind 'set enable-bracketed-paste off'")
133-
self.con.run_command("make olddefconfig")
130+
self.con.run_command("make olddefconfig", timeout=60)
134131
base_version = self.con.run_command("uname -r")
135132
ker_ver = self.con.run_command("make kernelrelease")[-1]
136-
cpu= self.con.run_command("lscpu | grep '^CPU(s):' | awk '{print $2}'")
137-
self.con.run_command("make -j {} all -s vmlinux".format(cpu[-1]), timeout = 60000)
138-
self.con.run_command("make modules_install")
139-
self.con.run_command("make install")
133+
cpu = int(self.con.run_command("lscpu --online -e|wc -l")[-1])
134+
self.con.run_command("make -j {} -s".format(cpu), timeout=60000)
135+
self.con.run_command("make modules_install", timeout=300)
136+
self.con.run_command("make install", timeout=120)
140137
if self.host_distro_name in ['rhel', 'Red Hat', 'ubuntu', 'Ubuntu']:
141138
self.con.run_command('grubby --set-default /boot/vmlinu*-{}'.format(base_version[-1]))
142139
elif self.host_distro_name in ['sles', 'SLES']:
@@ -151,18 +148,18 @@ def boot_kernel(self):
151148
except Exception:
152149
initrd_file = self.con.run_command("ls -l /boot/initr*-%s" % ker_ver)[-1].split(" ")[-1]
153150
kexec_cmdline = "kexec --initrd %s --command-line=\"%s\" /boot/vmlinu*-%s -l" % (initrd_file, cmdline, ker_ver)
154-
self.con.run_command("grub2-mkconfig --output=/boot/grub2/grub.cfg")
151+
self.con.run_command("grub2-mkconfig --output=/boot/grub2/grub.cfg", timeout=30)
155152
self.con.run_command(kexec_cmdline)
156153
self.con.run_command("bind 'set enable-bracketed-paste off'")
157154
self.con.close()
158155
self.console_thread.console_terminate()
159156
self.cv_SYSTEM.util.build_prompt()
160157
self.console_thread.console_terminate()
161-
time.sleep(15)
158+
time.sleep(30)
162159
for i in range(5):
163-
raw_pty = self.util.wait_for(self.cv_SYSTEM.console.get_console, timeout=20)
160+
raw_pty = self.util.wait_for(self.cv_SYSTEM.console.get_console, timeout=80)
164161
time.sleep(10)
165-
if raw_pty is not None:
162+
if raw_pty:
166163
raw_pty.sendline("uname -r")
167164
break
168165
try:
@@ -171,25 +168,33 @@ def boot_kernel(self):
171168
log.info(e)
172169
rc = raw_pty.expect(["login:", "WARNING: CPU"], timeout=600)
173170
if rc == 1:
171+
dmessage = []
174172
raw_pty.close()
175-
self.con = self.cv_SYSTEM.cv_HOST.get_ssh_connection()
176-
dmessage = self.con.run_command("dmesg --color=never")
177-
log.info(dmessage)
178-
log.info("WARNING: CPU catched")
179-
return False
180-
kernel_version_output = self.con.run_command("uname -r")[-1]
181-
log.info("Installed upstream kernel version: %s", kernel_version_output[-1])
182-
if self.conf.args.host_cmd:
183-
self.con.run_command(self.conf.args.host_cmd,
184-
timeout=60)
185-
self.cv_HOST.host_gather_opal_msg_log()
186-
self.cv_HOST.host_gather_kernel_log()
187-
if kernel_version_output != base_version :
188-
print("Kernel booted fine. Kernel version:", kernel_version_output.strip())
189-
return True
190-
else:
173+
self.cv_SYSTEM.console.close()
174+
log.info("Kernel Boot WARNING: Found!")
175+
#in case of kernel crash or oops or hung we might need system OFF ON or wait for base login
176+
#this delay is must to make sure the lpar has booted to login and ssh service is up
177+
while True:
178+
status = subprocess.run(["ping", "-c", "3", self.conf.args.host_ip], capture_output=True)
179+
if status.returncode != 0:
180+
log.info("booting...")
181+
time.sleep(10)
182+
self.con = self.cv_SYSTEM.cv_HOST.get_ssh_connection()
183+
if self.con:
184+
self.con.run_command('uname -r')
185+
break
191186
return False
192187

188+
if rc == 0:
189+
raw_pty.close()
190+
self.cv_SYSTEM.console.close()
191+
log.info("Kernel Booted ..")
192+
self.con = self.cv_SYSTEM.cv_HOST.get_ssh_connection()
193+
kernel_version_output = self.con.run_command("uname -r")[-1]
194+
log.info("Installed upstream kernel version: %s", kernel_version_output)
195+
if kernel_version_output[-1] == base_version[-1]:
196+
log.error("Kexec failed, booted back to base kernel !")
197+
return True
193198

194199
class KernelBuild(KernelTest):
195200
"""
@@ -212,7 +217,7 @@ def runTest(self):
212217
self.con.run_command("cd {}".format(self.home))
213218
if not self.branch:
214219
self.branch='master'
215-
self.con.run_command("git clone --depth 1 -b {} {} linux".format( self.branch, self.repo),timeout=3000)
220+
self.con.run_command("git clone -b {} {} linux".format( self.branch, self.repo),timeout=3000)
216221
self.con.run_command("cd linux")
217222
commit = self.con.run_command(" git log -1 --format=%H | sed -r 's/\x1B\[[0-9:]*[JKsu]//g'")
218223
self.con.run_command("cd ..")
@@ -266,7 +271,7 @@ def runTest(self):
266271
self.con.run_command("cd {}".format(self.home))
267272
if not self.branch:
268273
self.branch='master'
269-
self.con.run_command("git clone --depth 1 -b {} {} linux".format( self.branch, self.repo),timeout=3000)
274+
self.con.run_command("git clone -b {} {} linux".format( self.branch, self.repo),timeout=3000)
270275
self.con.run_command("cd linux")
271276
commit = self.con.run_command(" git log -1 --format=%H | sed -r 's/\x1B\[[0-9:]*[JKsu]//g'")
272277
self.con.run_command("cd ..")
@@ -281,47 +286,72 @@ def runTest(self):
281286
except Exception as e:
282287
log.info("EXCEPTION")
283288
if not boot and self.bisect_flag == '1':
284-
log.info("BISECTION STARTING")
285-
subprocess.run(f"if [ -d {self.linux_path} ]; then rm -rf {self.linux_path}; fi", shell=True, check=True)
286-
subprocess.run(f"if [ ! -d {self.linux_path} ]; then mkdir -p {self.linux_path}; fi", shell=True, check=True)
287-
subprocess.run(f"cd {self.home}", shell=True, check=True)
289+
count = 0
290+
exit_code = 4
291+
local_path = os.getcwd()
292+
log.info("BOOT BISECTION STARTING")
293+
self.con.run_command("cd {}; make clean".format(self.linux_path), timeout=60)
294+
dmessage = self.con.run_command("dmesg --color=never --level=warn")
295+
subprocess.run(f"if [ -d {self.local_path} ]; then rm -rf {self.local_path}; fi", shell=True, check=True)
288296
subprocess.run("git config --global http.postBuffer 1048576000", shell=True, check=True)
289-
subprocess.run(f"git clone -b {self.branch} {self.repo} {self.linux_path}" , shell=True, check=True,timeout=1800)
290-
subprocess.run(f"cd {self.linux_path}",shell=True,check=True)
297+
subprocess.run(f"git clone -b {self.branch} {self.repo} linux", shell=True, check=True, timeout=1800)
298+
subprocess.run(f"cd {self.local_path}", shell=True, check=True)
291299
try:
292-
subprocess.run("git bisect start", shell=True, check=True,cwd=self.linux_path)
293-
subprocess.run("git bisect bad", shell=True, check=True,cwd=self.linux_path)
300+
subprocess.run("git bisect start", shell=True, check=True, cwd=self.local_path)
301+
subprocess.run("git bisect bad", shell=True, check=True, cwd=self.local_path)
294302
folder_type=re.split(r'[\/\\.]',str(self.repo))[-2]
295303
if folder_type == 'linux-next':
296304
subprocess.run("git fetch --tags" , shell=True, check=True)
297305
good_tag=subprocess.run("git tag -l 'v[0-9]*' | sort -V | tail -n 1", shell=True, check=True)
298-
subprocess.run(f"git bisect good {good_tag}", shell=True, check=True,cwd=self.linux_path)
306+
subprocess.run(f"git bisect good {good_tag}", shell=True, check=True, cwd=self.local_path)
299307
else:
300308
subprocess.run("pwd")
301-
subprocess.run(f"git bisect good {self.good_commit}", shell=True, check=True,cwd=self.linux_path)
309+
subprocess.run(f"git bisect good {self.good_commit}", shell=True, check=True, cwd=self.local_path)
302310
while True:
303-
print("ENTERED LOOP BISECT")
304-
subprocess.run("git bisect next", shell=True, check=True, cwd=self.linux_path)
305-
commit_to_test = subprocess.check_output("git rev-parse HEAD", shell=True, cwd=self.linux_path).decode().strip()
306-
log.info("commit to test is")
311+
log.info("ENTERED BISECTION LOOP {}".format(count))
312+
subprocess.run("git bisect next", shell=True, check=True, cwd=self.local_path)
313+
commit_to_test = subprocess.check_output("git rev-parse HEAD", shell=True, cwd=self.local_path).decode().strip()
307314
log.info(commit_to_test)
308315
self.con.run_command(" if [ '$(pwd)' != {} ]; then cd {} || exit 1 ; fi ; bind 'set enable-bracketed-paste off'".format(self.linux_path,self.linux_path))
309316
self.con.run_command("git checkout {}; git checkout {};".format(self.branch,commit_to_test))
310317
result = self.boot_kernel()
318+
count += 1
311319
if result == True:
312-
subprocess.run("git bisect good", shell=True, check=True,cwd=self.linux_path)
320+
log.info("\n ------ git bisect good {} ------ \n".format(commit_to_test))
321+
subprocess.run("git bisect good", shell=True, check=True, cwd=self.local_path)
313322
else:
314-
subprocess.run("git bisect bad", shell=True, check=True,cwd=self.linux_path)
323+
log.info("\n ------ git bisect bad {} ------ \n".format(commit_to_test))
324+
subprocess.run("git bisect bad", shell=True, check=True, cwd=self.local_path)
325+
bilogs = []
326+
bilogs = subprocess.check_output("git bisect log", shell=True, cwd=self.local_path).decode().split('\n')
327+
biflag = False
328+
for logs in bilogs:
329+
if 'first bad commit' in logs:
330+
badCommit = commit_to_test
331+
biflag = True
332+
if biflag:
333+
break
315334
except subprocess.CalledProcessError as e:
316335
log.info("Error:", e)
317336
finally:
318-
bisect_log = subprocess.run("git bisect log", shell=True, check=True,cwd=self.linux_path)
319-
log.info(bisect_log)
320-
subprocess.run("git bisect reset", shell=True, check=True,cwd=self.linux_path)
321-
elif self.bisect_flag != '1':
322-
log.info("BOOT FAILURE, NO BISECTION")
337+
bilogs = subprocess.run("git bisect log", shell=True, check=True, cwd=self.local_path)
338+
log.info(bilogs)
339+
entry = self.con.run_command("dmesg --color=never --level=warn | grep 'WARNING:'")
340+
emaili = subprocess.run("git config --global color.ui true;git show --format=%ce {} | sed -r 's/\x1B\[[0-9:]*[JKsu]//g'".format(badCommit), shell=True, check=True, cwd=self.local_path)
341+
subprocess.run("git bisect reset", shell=True, check=True,cwd=self.local_path)
342+
log.info("Boot Bisection Completed ! Bad Commit: {} Author: {}".format(badCommit,emaili))
343+
# WRITING BOOT BISECTION DATA TO JSON AND FORMAT EMAIL FOR BOOT REPORT IS TODO
344+
#with open('output.json','w') as f:
345+
# json.dump({"exit_code":exit_code,"email":emaili,"commit": badCommit,"error":entry,"err_msg":dmessage,"flag":self.bisect_flag},f)
346+
#self.util.format_email(self.linux_path, self.repo)
347+
elif boot and self.bisect_flag == '1':
348+
exit_code = 0
349+
goodCommit = commit
350+
log.info("Boot Successfull.. Updating the last good commit to json")
351+
with open('output.json','w') as f:
352+
json.dump({"exit_code":exit_code,"commit": goodCommit,"flag":self.bisect_flag},f)
323353
else:
324-
log.info("NO BISECTION NEEDED, NOT BOOT FAILURE")
354+
log.info("BOOT FAILED, NO BISECTION SELECTED")
325355

326356
def tearDown(self):
327357
self.console_thread.console_terminate()

0 commit comments

Comments
 (0)