Skip to content

Commit 32b4cbb

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 32b4cbb

File tree

1 file changed

+88
-56
lines changed

1 file changed

+88
-56
lines changed

testcases/OpTestKernelTest.py

+88-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,8 @@ 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 --depth 1 -b {} {} linux".format( self.branch, self.repo),timeout=3000)
221+
self.con.run_command("git clone -b {} {} linux".format( self.branch, self.repo),timeout=3000)
216222
self.con.run_command("cd linux")
217223
commit = self.con.run_command(" git log -1 --format=%H | sed -r 's/\x1B\[[0-9:]*[JKsu]//g'")
218224
self.con.run_command("cd ..")
@@ -266,7 +272,7 @@ def runTest(self):
266272
self.con.run_command("cd {}".format(self.home))
267273
if not self.branch:
268274
self.branch='master'
269-
self.con.run_command("git clone --depth 1 -b {} {} linux".format( self.branch, self.repo),timeout=3000)
275+
self.con.run_command("git clone -b {} {} linux".format( self.branch, self.repo),timeout=3000)
270276
self.con.run_command("cd linux")
271277
commit = self.con.run_command(" git log -1 --format=%H | sed -r 's/\x1B\[[0-9:]*[JKsu]//g'")
272278
self.con.run_command("cd ..")
@@ -281,47 +287,73 @@ def runTest(self):
281287
except Exception as e:
282288
log.info("EXCEPTION")
283289
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)
290+
count = 0
291+
exit_code = 4
292+
local_path = os.getcwd()
293+
log.info("BOOT BISECTION STARTING")
294+
self.con.run_command("cd {}; make clean".format(self.linux_path), timeout=60)
295+
dmessage = self.con.run_command("dmesg --color=never --level=warn")
296+
#self.con.run_command("git fetch --unshallow", timeout=3000)
297+
subprocess.run(f"if [ -d {self.local_path} ]; then rm -rf {self.local_path}; fi", shell=True, check=True)
288298
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)
299+
subprocess.run(f"git clone -b {self.branch} {self.repo} linux", shell=True, check=True, timeout=1800)
300+
subprocess.run(f"cd {self.local_path}", shell=True, check=True)
291301
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)
302+
subprocess.run("git bisect start", shell=True, check=True, cwd=self.local_path)
303+
subprocess.run("git bisect bad", shell=True, check=True, cwd=self.local_path)
294304
folder_type=re.split(r'[\/\\.]',str(self.repo))[-2]
295305
if folder_type == 'linux-next':
296306
subprocess.run("git fetch --tags" , shell=True, check=True)
297307
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)
308+
subprocess.run(f"git bisect good {good_tag}", shell=True, check=True, cwd=self.local_path)
299309
else:
300310
subprocess.run("pwd")
301-
subprocess.run(f"git bisect good {self.good_commit}", shell=True, check=True,cwd=self.linux_path)
311+
subprocess.run(f"git bisect good {self.good_commit}", shell=True, check=True, cwd=self.local_path)
302312
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")
313+
log.info("ENTERED BISECTION LOOP {}".format(count))
314+
subprocess.run("git bisect next", shell=True, check=True, cwd=self.local_path)
315+
commit_to_test = subprocess.check_output("git rev-parse HEAD", shell=True, cwd=self.local_path).decode().strip()
307316
log.info(commit_to_test)
308317
self.con.run_command(" if [ '$(pwd)' != {} ]; then cd {} || exit 1 ; fi ; bind 'set enable-bracketed-paste off'".format(self.linux_path,self.linux_path))
309318
self.con.run_command("git checkout {}; git checkout {};".format(self.branch,commit_to_test))
310319
result = self.boot_kernel()
320+
count += 1
311321
if result == True:
312-
subprocess.run("git bisect good", shell=True, check=True,cwd=self.linux_path)
322+
log.info("\n ------ git bisect good {} ------ \n".format(commit_to_test))
323+
subprocess.run("git bisect good", shell=True, check=True, cwd=self.localx_path)
313324
else:
314-
subprocess.run("git bisect bad", shell=True, check=True,cwd=self.linux_path)
325+
log.info("\n ------ git bisect bad {} ------ \n".format(commit_to_test))
326+
subprocess.run("git bisect bad", shell=True, check=True, cwd=self.local_path)
327+
bilogs = []
328+
bilogs = subprocess.check_output("git bisect log", shell=True, cwd=self.local_path).decode().split('\n')
329+
biflag = False
330+
for logs in bilogs:
331+
if 'first bad commit' in logs:
332+
badCommit = commit_to_test
333+
biflag = True
334+
if biflag:
335+
break
315336
except subprocess.CalledProcessError as e:
316337
log.info("Error:", e)
317338
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")
339+
bilogs = subprocess.run("git bisect log", shell=True, check=True, cwd=self.local_path)
340+
log.info(bilogs)
341+
entry = self.con.run_command("dmesg --color=never --level=warn | grep 'WARNING:'")
342+
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)
343+
subprocess.run("git bisect reset", shell=True, check=True,cwd=self.local_path)
344+
log.info("Boot Bisection Completed ! Bad Commit: {} Author: {}".format(badCommit,emaili))
345+
# WRITING BOOT BISECTION DATA TO JSON AND FORMAT EMAIL FOR BOOT REPORT IS TODO
346+
#with open('output.json','w') as f:
347+
# json.dump({"exit_code":exit_code,"email":emaili,"commit": badCommit,"error":entry,"err_msg":dmessage,"flag":self.bisect_flag},f)
348+
#self.util.format_email(self.linux_path, self.repo)
349+
elif boot and self.bisect_flag == '1':
350+
exit_code = 0
351+
goodCommit = commit
352+
log.info("Boot Successfull.. Updating the last good commit to json")
353+
with open('output.json','w') as f:
354+
json.dump({"exit_code":exit_code,"commit": goodCommit,"flag":self.bisect_flag},f)
323355
else:
324-
log.info("NO BISECTION NEEDED, NOT BOOT FAILURE")
356+
log.info("BOOT FAILED, NO BISECTION SELECTED")
325357

326358
def tearDown(self):
327359
self.console_thread.console_terminate()

0 commit comments

Comments
 (0)