summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Poessinger <christian@poessinger.com>2021-01-12 16:56:24 +0100
committerChristian Poessinger <christian@poessinger.com>2021-01-12 16:56:24 +0100
commit10408791914c53a058e9f935512a5a967fd72512 (patch)
treecae42c0d9756f63ec9111023c047b324020a7dd5
parentcd15b3e3e5187e80180bdd67c9fc94532d526c97 (diff)
downloadvyos-build-10408791914c53a058e9f935512a5a967fd72512.tar.gz
vyos-build-10408791914c53a058e9f935512a5a967fd72512.zip
Jenkins: boot ISO into QEMU after assembly
-rw-r--r--.gitignore1
-rw-r--r--Jenkinsfile8
-rw-r--r--Makefile9
-rw-r--r--docker/Dockerfile4
-rwxr-xr-xscripts/check-qemu-install371
5 files changed, 393 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index 003b3015..5f60ff6c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@ key/*
packages/*.buildlog
packages/*
!packages/*/
+testinstall*.img
diff --git a/Jenkinsfile b/Jenkinsfile
index 93de8cd1..f57e93b6 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -128,6 +128,14 @@ pipeline {
}
}
}
+ stage('QEMU') {
+ when {
+ expression { fileExists 'build/live-image-amd64.hybrid.iso' }
+ }
+ steps {
+ sh "sudo make test"
+ }
+ }
}
post {
cleanup {
diff --git a/Makefile b/Makefile
index ebd71ddb..21e5662e 100644
--- a/Makefile
+++ b/Makefile
@@ -254,6 +254,15 @@ dell: check_build_config clean prepare
cd ..
@scripts/copy-image
+.PHONY: test
+.ONESHELL:
+test:
+ if [ ! -f build/live-image-amd64.hybrid.iso ]; then
+ echo "Could not find build/live-image-amd64.hybrid.iso"
+ exit 1
+ fi
+ scripts/check-qemu-install --debug build/live-image-amd64.hybrid.iso
+
.PHONY: clean
.ONESHELL:
clean:
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 1ca25310..a69c8987 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -370,6 +370,10 @@ RUN echo 'deb http://ftp.debian.org/debian stretch main' | tee -a /etc/apt/sourc
apt-get update && \
rm -rf /var/lib/apt/lists/*
+
+# For smoketests in QEMU
+RUN pip3 install pexpect
+
# Install packer
RUN export LATEST="$(curl -s https://checkpoint-api.hashicorp.com/v1/check/packer | \
jq -r -M '.current_version')"; \
diff --git a/scripts/check-qemu-install b/scripts/check-qemu-install
new file mode 100755
index 00000000..4bc16160
--- /dev/null
+++ b/scripts/check-qemu-install
@@ -0,0 +1,371 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2019, VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# File: check-qemu-install
+# Purpose:
+# This script installs a system on a emulated qemu host to verify
+# that the iso produced is installable and boots.
+# after the iso is booted from disk it also tries to execute the
+# vyos-smoketest script to verify checks there.
+#
+# For now it will not fail on failed smoketest but will fail on
+# install and boot errors.
+# Arguments:
+# iso iso image to install
+# [disk] disk filename to use, if none is provided it
+# is autogenerated
+# [--keep] Keep the disk image after completion
+# [--logfile] name of logfile to save, defaulting to stdout
+# [--silent] only print on errors
+# [--debug] print all communication with the device
+
+import pexpect
+import sys
+import os
+import time
+import argparse
+import subprocess
+import random
+import traceback
+import logging
+import re
+
+from io import BytesIO, StringIO
+from datetime import datetime
+
+EXCEPTION = 0
+now = datetime.now()
+
+parser = argparse.ArgumentParser(description='Install and start a test VyOS vm.')
+parser.add_argument('iso', help='ISO file to install')
+parser.add_argument('disk', help='name of disk image file',
+ nargs='?',
+ default='testinstall-{}-{}.img'.format(now.strftime('%Y%m%d-%H%M%S'),
+ "%04x" % random.randint(0,65535)))
+parser.add_argument('--keep', help='Do not remove disk-image after installation',
+ action='store_true',
+ default=False)
+parser.add_argument('--silent', help='Do not show output on stdout unless an error has occured',
+ action='store_true',
+ default=False)
+parser.add_argument('--debug', help='Send all debug output to stdout',
+ action='store_true',
+ default=False)
+parser.add_argument('--logfile', help='Log to file')
+parser.add_argument('--no-kvm', help='Disable use of kvm',
+ action='store_true',
+ default=False)
+parser.add_argument('--configd', help='Execute testsuite with config daemon',
+ action='store_true',
+ default=False)
+parser.add_argument('--configtest', help='Execute load/commit config tests',
+ action='store_true',
+ default=False)
+
+args = parser.parse_args()
+
+class StreamToLogger(object):
+ """
+ Fake file-like stream object that redirects writes to a logger instance.
+ """
+ def __init__(self, logger, log_level=logging.INFO):
+ self.logger = logger
+ self.log_level = log_level
+ self.linebuf = b''
+ self.ansi_escape = re.compile(r'\x1B[@-_][0-?]*[ -/]*[@-~]')
+
+ def write(self, buf):
+ self.linebuf += buf
+ #print('.')
+ while b'\n' in self.linebuf:
+ f = self.linebuf.split(b'\n', 1)
+ if len(f) == 2:
+ self.logger.debug(self.ansi_escape.sub('', f[0].decode(errors="replace").rstrip()))
+ self.linebuf = f[1]
+ #print(f)
+
+
+ def flush(self):
+ pass
+
+def get_half_cpus():
+ """ return 1/2 of the numbers of available CPUs """
+ cpu = os.cpu_count()
+ if cpu > 1:
+ cpu /= 2
+ return int(cpu)
+
+def get_qemu_cmd(name, enable_kvm, disk_img, iso_img=None):
+ kvm = ""
+ cpu = "-cpu host"
+ if not enable_kvm:
+ kvm = "--no-kvm"
+ cpu = ""
+
+ cdrom = ""
+ if iso_img:
+ cdrom = "-boot d -cdrom {}".format(iso_img)
+
+ # test using half of the available CPUs on the system
+ cpucount = get_half_cpus()
+
+ cmd = 'qemu-system-x86_64 \
+ -name "{0}" \
+ -smp {1} \
+ -m 2G \
+ -net nic,macaddr=00:50:00:00:00:00,model=virtio \
+ -net nic,macaddr=00:50:00:00:00:01,model=virtio \
+ -net nic,macaddr=00:50:00:00:00:02,model=virtio \
+ -net nic,macaddr=00:50:00:00:00:03,model=virtio \
+ -machine accel=kvm \
+ -nographic {2} {3} {4} \
+ -drive format=raw,file={5}'.format(name, cpucount, cpu, cdrom, kvm, disk_img)
+
+ return cmd
+
+
+# Setting up logger
+log = logging.getLogger()
+log.setLevel(logging.DEBUG)
+
+stl = StreamToLogger(log)
+formatter = logging.Formatter('%(levelname)5s - %(message)s')
+
+handler = logging.StreamHandler(sys.stdout)
+if args.silent:
+ handler.setLevel(logging.ERROR)
+elif args.debug:
+ handler.setLevel(logging.DEBUG)
+else:
+ handler.setLevel(logging.INFO)
+
+handler.setFormatter(formatter)
+log.addHandler(handler)
+
+if args.logfile:
+ filehandler = logging.FileHandler(args.logfile)
+ filehandler.setLevel(logging.DEBUG)
+ filehandler.setFormatter(formatter)
+ log.addHandler(filehandler)
+
+if args.silent:
+ output = BytesIO()
+else:
+ output = sys.stdout.buffer
+
+if not os.path.isfile(args.iso):
+ log.error("Unable to find iso image to install")
+ sys.exit(1)
+
+if args.no_kvm:
+ log.error("KVM forced off by command line")
+ kvm=False
+elif not os.path.exists("/dev/kvm"):
+ log.error("KVM is not enabled on host, proceeding with software emulation")
+ kvm=False
+else:
+ kvm=True
+
+# Creating diskimage!!
+if not os.path.isfile(args.disk):
+ log.info("Creating Disk image {}".format(args.disk))
+ c = subprocess.check_output(["qemu-img", "create", args.disk, "2G"])
+ log.debug(c.decode())
+else:
+ log.info("Diskimage already exists, using the existing one")
+
+try:
+ #################################################
+ # Installing image to disk
+ #################################################
+ log.info("Installing system")
+ cmd = get_qemu_cmd("TESTVM", kvm, args.disk, args.iso)
+ log.debug("Executing command: {}".format(cmd))
+ c = pexpect.spawn(cmd, logfile=stl)
+
+ #################################################
+ # Logging into VyOS system
+ #################################################
+ try:
+ c.expect('Automatic boot in', timeout=10)
+ c.sendline('')
+ except pexpect.TIMEOUT:
+ log.warning("Did not find grub countdown window, ignoring")
+
+ log.info('Waiting for login prompt')
+ c.expect('[Ll]ogin:', timeout=300)
+ c.sendline('vyos')
+ c.expect('[Pp]assword:', timeout=10)
+ c.sendline('vyos')
+ c.expect(r'vyos@vyos:~\$')
+ log.info('Logged in!')
+
+ #################################################
+ # Installing into VyOS system
+ #################################################
+ log.info("Starting installer")
+ c.sendline('install image')
+ c.expect('\nWould you like to continue?.*:')
+ c.sendline('yes')
+ log.info("Partitioning disk")
+ c.expect('\nPartition.*:')
+ c.sendline('')
+ c.expect('\nInstall the image on.*:')
+ c.sendline('')
+ c.expect(r'\nContinue\?.*:')
+ c.sendline('Yes')
+ c.expect('\nHow big of a root partition should I create?.*:')
+ c.sendline('')
+ log.info('Disk partitioned, installing')
+ c.expect('\nWhat would you like to name this image?.*:')
+ c.sendline('')
+ log.info('Copying files')
+ c.expect('\nWhich one should I copy to.*:', timeout=300)
+ c.sendline('')
+ log.info('Files Copied!')
+ c.expect('\nEnter password for user.*:')
+ c.sendline('vyos')
+ c.expect('\nRetype password for user.*:')
+ c.sendline('vyos')
+ c.expect('\nWhich drive should GRUB modify the boot partition on.*:')
+ c.sendline('')
+ c.expect(r'\nvyos@vyos:~\$')
+ log.info('system installed, shutting down')
+
+ #################################################
+ # Powering down installer
+ #################################################
+ log.info("Shutting down installation system")
+ c.sendline('poweroff')
+ c.expect(r'\nAre you sure you want to poweroff this system.*\]')
+ c.sendline('Y')
+ for i in range(30):
+ log.info("Waiting for shutdown...")
+ if not c.isalive():
+ log.info("VM is shut down!")
+ break
+ time.sleep(10)
+ else:
+ log.error("VM Did not shut down after 300sec, killing")
+ c.close()
+
+ #################################################
+ # Booting installed system
+ #################################################
+ log.info("Booting installed system")
+ cmd = get_qemu_cmd("TESTVM", kvm, args.disk)
+ log.debug('Executing command: {}'.format(cmd))
+ c = pexpect.spawn(cmd, logfile=stl)
+
+ #################################################
+ # Logging into VyOS system
+ #################################################
+ try:
+ c.expect('The highlighted entry will be executed automatically in', timeout=10)
+ c.sendline('')
+ except pexpect.TIMEOUT:
+ log.warning("Did not find grub countdown window, ignoring")
+
+ log.info('Waiting for login prompt')
+ c.expect('[Ll]ogin:', timeout=300)
+ c.sendline('vyos')
+ c.expect('[Pp]assword:', timeout=10)
+ c.sendline('vyos')
+ c.expect(r'vyos@vyos:~\$')
+ log.info('Logged in!')
+
+ # additional settling time
+ time.sleep(20)
+
+ ################################################
+ # Always load the WiFi simulation module
+ ################################################
+ c.sendline('sudo modprobe mac80211_hwsim')
+ c.expect(r'vyos@vyos:~\$')
+
+ #################################################
+ # Start/stop config daemon
+ #################################################
+ if args.configd:
+ c.sendline('sudo systemctl start vyos-configd.service &> /dev/null')
+ c.expect(r'vyos@vyos:~\$')
+ else:
+ c.sendline('sudo systemctl stop vyos-configd.service &> /dev/null')
+ c.expect(r'vyos@vyos:~\$')
+
+ #################################################
+ # Basic Configmode/Opmode switch
+ #################################################
+ log.info("Basic CLI configuration mode test")
+ c.sendline('configure')
+ c.expect(r'vyos@vyos#')
+ c.sendline('run show version')
+ c.sendline('exit')
+ c.expect(r'vyos@vyos:~\$')
+
+ #################################################
+ # Powering off system
+ #################################################
+ log.info("Powering off system ")
+ c.sendline('poweroff')
+ c.expect(r'\nAre you sure you want to poweroff this system.*\]')
+ c.sendline('Y')
+ log.info("Shutting down virtual machine")
+ for i in range(30):
+ log.info("Waiting for shutdown...")
+ if not c.isalive():
+ log.info("VM is shut down!")
+ break
+ time.sleep(10)
+ else:
+ log.error("VM Did not shut down after 300sec")
+ raise Exception("VM Did not shut down after 300sec")
+ c.close()
+
+except pexpect.exceptions.TIMEOUT:
+ log.error("Timeout waiting for VyOS system")
+ log.error(traceback.format_exc())
+ EXCEPTION = 1
+
+except pexpect.exceptions.ExceptionPexpect:
+ log.error("Exeption while executing QEMU")
+ log.error("Is qemu working on this system?")
+ log.error(traceback.format_exc())
+ EXCEPTION = 1
+
+except Exception:
+ log.error("An unknown error occured when installing the VyOS system")
+ traceback.print_exc()
+ EXCEPTION = 1
+
+#################################################
+# Cleaning up
+#################################################
+log.info("Cleaning up")
+
+if not args.keep:
+ log.info("Removing disk file: {}".format(args.disk))
+ try:
+ os.remove(args.disk)
+ except Exception:
+ log.error("Exception while removing diskimage")
+ log.error(traceback.format_exc())
+ EXCEPTION = 1
+
+if EXCEPTION:
+ log.error("Hmm... System got an exception while processing")
+ log.error("The ISO is not considered usable")
+ sys.exit(1)