From: Uladzimir Bely <ubely@ilbers.de>
To: isar-users@googlegroups.com
Subject: [PATCH 3/8] cibuilder.py: Split vm_start function to smaller subfunctions
Date: Fri, 18 Aug 2023 09:07:01 +0200 [thread overview]
Message-ID: <20230818070706.27913-4-ubely@ilbers.de> (raw)
In-Reply-To: <20230818070706.27913-1-ubely@ilbers.de>
vm_start is a pretty large function that does the following:
- starts machine in qemu;
- parses machine output (e.g., serial console);
- runs remote command over ssh, if requested;
- kills qemu process.
Splitting it to smaller sub-functions improves code readability and
potentially allows to reuse these sub-functions results in different
tests tests.
Signed-off-by: Uladzimir Bely <ubely@ilbers.de>
---
testsuite/cibuilder.py | 186 +++++++++++++++++++++++------------------
1 file changed, 103 insertions(+), 83 deletions(-)
diff --git a/testsuite/cibuilder.py b/testsuite/cibuilder.py
index 80b3aa88..8f28d05a 100755
--- a/testsuite/cibuilder.py
+++ b/testsuite/cibuilder.py
@@ -346,47 +346,70 @@ class CIBuilder(Test):
self.fail('No command to run specified')
- def vm_start(self, arch='amd64', distro='buster',
- enforce_pcbios=False, skip_modulecheck=False,
- image='isar-image-base', cmd=None, script=None):
- time_to_wait = self.params.get('time_to_wait', default=DEF_VM_TO_SEC)
-
- self.log.info('===================================================')
- self.log.info('Running Isar VM boot test for (' + distro + '-' + arch + ')')
- self.log.info('Remote command is ' + str(cmd))
- self.log.info('Remote script is ' + str(script))
- self.log.info('Isar build folder is: ' + self.build_dir)
- self.log.info('===================================================')
-
- self.check_init()
-
+ def vm_turn_on(self, arch='amd64', distro='buster', image='isar-image-base',
+ enforce_pcbios=False):
logdir = '%s/vm_start' % self.build_dir
if not os.path.exists(logdir):
os.mkdir(logdir)
prefix = '%s-vm_start_%s_%s_' % (time.strftime('%Y%m%d-%H%M%S'),
distro, arch)
- fd, output_file = tempfile.mkstemp(suffix='_log.txt', prefix=prefix,
+ fd, boot_log = tempfile.mkstemp(suffix='_log.txt', prefix=prefix,
dir=logdir, text=True)
- os.chmod(output_file, 0o644)
+ os.chmod(boot_log, 0o644)
latest_link = '%s/vm_start_%s_%s_latest.txt' % (logdir, distro, arch)
if os.path.exists(latest_link):
os.unlink(latest_link)
- os.symlink(os.path.basename(output_file), latest_link)
+ os.symlink(os.path.basename(boot_log), latest_link)
cmdline = start_vm.format_qemu_cmdline(arch, self.build_dir, distro, image,
- output_file, None, enforce_pcbios)
+ boot_log, None, enforce_pcbios)
cmdline.insert(1, '-nographic')
self.log.info('QEMU boot line:\n' + ' '.join(cmdline))
+ self.log.info('QEMU boot log:\n' + boot_log)
+
+ p1 = subprocess.Popen('exec ' + ' '.join(cmdline), shell=True,
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+ universal_newlines=True)
+ self.log.debug("Started VM with pid %s" % (p1.pid))
- login_prompt = b'isar login:'
+ return p1, cmdline, boot_log
+
+
+ def vm_wait_boot(self, p1, timeout):
+ login_prompt = b' login:'
+
+ poller = select.poll()
+ poller.register(p1.stdout, select.POLLIN)
+ poller.register(p1.stderr, select.POLLIN)
+
+ while time.time() < timeout and p1.poll() is None:
+ events = poller.poll(1000 * (timeout - time.time()))
+ for fd, event in events:
+ if event != select.POLLIN:
+ continue
+ if fd == p1.stdout.fileno():
+ # Wait for the complete string if it is read in chunks
+ # like "i", "sar", " login:"
+ time.sleep(0.01)
+ data = os.read(fd, 1024)
+ if login_prompt in data:
+ self.log.debug('Got login prompt')
+ return 0
+ if fd == p1.stderr.fileno():
+ app_log.error(p1.stderr.readline().rstrip())
+
+ self.log.error("Didn't get login prompt")
+ return 1
+
+
+ def vm_parse_output(self, boot_log, bb_output, skip_modulecheck):
# the printk of recipes-kernel/example-module
module_output = b'Just an example'
resize_output = None
-
- bb_output = start_vm.get_bitbake_env(arch, distro, image).decode()
image_fstypes = start_vm.get_bitbake_var(bb_output, 'IMAGE_FSTYPES')
wks_file = start_vm.get_bitbake_var(bb_output, 'WKS_FILE')
+
# only the first type will be tested in start_vm.py
if image_fstypes.split()[0] == 'wic':
if wks_file:
@@ -399,72 +422,69 @@ class CIBuilder(Test):
resize_output = b'resized filesystem to'
if "sdimage-efi-btrfs" in wks_file:
resize_output = b': resize device '
+ rc = 0
+ if os.path.exists(boot_log) and os.path.getsize(boot_log) > 0:
+ with open(boot_log, "rb") as f1:
+ data = f1.read()
+ if (module_output in data or skip_modulecheck):
+ if resize_output and not resize_output in data:
+ rc = 1
+ self.log.error("No resize output while expected")
+ else:
+ rc = 2
+ self.log.error("No example module output while expected")
+ return rc
- timeout = time.time() + int(time_to_wait)
- p1 = subprocess.Popen('exec ' + ' '.join(cmdline), shell=True,
- stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
- universal_newlines=True)
+ def vm_turn_off(self, p1):
+ if p1.poll() is None:
+ p1.kill()
+ p1.wait()
- if cmd is not None or script is not None:
- try:
- user='ci'
- host='localhost'
- port = 22
- for arg in cmdline:
- match = re.match(r".*hostfwd=tcp::(\d*).*", arg)
- if match:
- port = match.group(1)
- break
-
- rc = self.remote_run(user, host, port, cmd, script, p1, timeout)
-
- finally:
- if p1.poll() is None:
- self.log.debug('Killing qemu...')
- p1.kill()
- p1.wait()
+ self.log.debug("Stopped VM with pid %s" % (p1.pid))
- if rc != 0:
- self.fail('Log ' + output_file)
- return
+ def vm_start(self, arch='amd64', distro='buster',
+ enforce_pcbios=False, skip_modulecheck=False,
+ image='isar-image-base', cmd=None, script=None):
+ time_to_wait = self.params.get('time_to_wait', default=DEF_VM_TO_SEC)
- try:
- poller = select.poll()
- poller.register(p1.stdout, select.POLLIN)
- poller.register(p1.stderr, select.POLLIN)
- while time.time() < timeout and p1.poll() is None:
- events = poller.poll(1000 * (timeout - time.time()))
- for fd, event in events:
- if event != select.POLLIN:
- continue
- if fd == p1.stdout.fileno():
- # Wait for the complete string if it is read in chunks
- # like "i", "sar", " login:"
- time.sleep(0.01)
- data = os.read(fd, 1024)
- if login_prompt in data:
- raise CanBeFinished
- if fd == p1.stderr.fileno():
- app_log.error(p1.stderr.readline().rstrip())
- except CanBeFinished:
- self.log.debug('Got login prompt')
- finally:
- if p1.poll() is None:
- p1.kill()
- p1.wait()
+ self.log.info('===================================================')
+ self.log.info('Running Isar VM boot test for (' + distro + '-' + arch + ')')
+ self.log.info('Remote command is ' + str(cmd))
+ self.log.info('Remote script is ' + str(script))
+ self.log.info('Isar build folder is: ' + self.build_dir)
+ self.log.info('===================================================')
- if os.path.exists(output_file) and os.path.getsize(output_file) > 0:
- with open(output_file, "rb") as f1:
- data = f1.read()
- if (module_output in data or skip_modulecheck) \
- and login_prompt in data:
- if resize_output:
- if resize_output in data:
- return
- else:
- return
- app_log.error(data.decode(errors='replace'))
-
- self.fail('Log ' + output_file)
+ self.check_init()
+
+ timeout = time.time() + int(time_to_wait)
+
+ p1, cmdline, boot_log = self.vm_turn_on(arch, distro, image, enforce_pcbios)
+
+ rc = self.vm_wait_boot(p1, timeout)
+ if rc != 0:
+ self.vm_turn_off(p1)
+ self.fail('Failed to boot qemu machine')
+
+ if cmd is not None or script is not None:
+ user='ci'
+ host='localhost'
+ port = 22
+ for arg in cmdline:
+ match = re.match(r".*hostfwd=tcp::(\d*).*", arg)
+ if match:
+ port = match.group(1)
+ break
+ rc = self.remote_run(user, host, port, cmd, script, p1, timeout)
+ if rc != 0:
+ self.vm_turn_off(p1)
+ self.fail('Failed to run test over ssh')
+ else:
+ bb_output = start_vm.get_bitbake_env(arch, distro, image).decode()
+ rc = self.vm_parse_output(boot_log, bb_output, skip_modulecheck)
+ if rc != 0:
+ self.vm_turn_off(p1)
+ self.fail('Failed to parse output')
+
+ self.vm_turn_off(p1)
--
2.20.1
next prev parent reply other threads:[~2023-08-18 7:07 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-08-18 7:06 [PATCH 0/8] Testsuite improvements for SSH-based tests Uladzimir Bely
2023-08-18 7:06 ` [PATCH 1/8] cibuilder.py: Support custom arguments passing to CI scripts Uladzimir Bely
2023-08-18 7:07 ` [PATCH 2/8] meta-isar: Add more extra space to qemu ext4 images Uladzimir Bely
2023-08-18 7:07 ` Uladzimir Bely [this message]
2023-08-18 7:07 ` [PATCH 4/8] cibuilder.py: Simplify remote_run command Uladzimir Bely
2023-08-18 7:07 ` [PATCH 5/8] cibuilder.py: Reuse the same qemu machine in ssh-based tests Uladzimir Bely
2023-08-18 7:07 ` [PATCH 6/8] citest.py: Adapt tests to qemu reuse Uladzimir Bely
2023-08-18 7:07 ` [PATCH 7/8] cibuilder.py: enable output from remote scripts Uladzimir Bely
2023-08-18 7:07 ` [PATCH 8/8] testsuite: Switch to remote scripts with arguments Uladzimir Bely
2023-08-24 15:34 ` [PATCH 0/8] Testsuite improvements for SSH-based tests Uladzimir Bely
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20230818070706.27913-4-ubely@ilbers.de \
--to=ubely@ilbers.de \
--cc=isar-users@googlegroups.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox