summaryrefslogtreecommitdiff
path: root/bin/virtualization
diff options
Diffstat (limited to 'bin/virtualization')
-rwxr-xr-xbin/virtualization209
1 files changed, 165 insertions, 44 deletions
diff --git a/bin/virtualization b/bin/virtualization
index 486a554..f1d043c 100755
--- a/bin/virtualization
+++ b/bin/virtualization
@@ -50,14 +50,137 @@ DEFAULT_TIMEOUT = 500
class XENTest(object):
pass
-
+# The "TAR" type is a tarball that contains both
+# a disk image and a kernel binary. This is useful
+# on architectures that don't (yet) have a bootloader
+# in the disk image that we can chain to, and instead
+# we need to have qemu load boot files externally
+CLOUD_IMAGE_TYPE_TAR = 1
+CLOUD_IMAGE_TYPE_DISK = 2
+
+QEMU_DISK_TYPE_SD = 1
+QEMU_DISK_TYPE_VIRTIO = 2
+QEMU_DISK_TYPE_VIRTIO_BLK = 3
+
+QEMU_ARCH_CONFIG = {
+ 'arm64': {
+ 'cloudimg_type': CLOUD_IMAGE_TYPE_TAR,
+ 'cloudimg_arch': 'arm64',
+ 'qemu_bin': 'qemu-system-aarch64',
+ 'qemu_disk_type': QEMU_DISK_TYPE_VIRTIO_BLK,
+ 'qemu_extra_args': [
+ '-machine', 'virt',
+ '-cpu', 'host',
+ '-enable-kvm',
+ '-serial', 'stdio',
+ ],
+ },
+ 'armhf': {
+ 'cloudimg_type': CLOUD_IMAGE_TYPE_TAR,
+ 'cloudimg_arch': 'armhf',
+ 'qemu_bin': 'qemu-system-arm',
+ 'qemu_disk_type': QEMU_DISK_TYPE_VIRTIO_BLK,
+ 'qemu_extra_args': [
+ '-machine', 'virt',
+ '-cpu', 'host',
+ '-enable-kvm',
+ '-serial', 'stdio',
+ ],
+ },
+ 'amd64': {
+ 'cloudimg_type': CLOUD_IMAGE_TYPE_DISK,
+ 'cloudimg_arch': 'i386',
+ 'qemu_bin': 'qemu-system-x86_64',
+ 'qemu_disk_type': QEMU_DISK_TYPE_VIRTIO,
+ 'qemu_extra_args': [
+ '-machine', 'accel=kvm:tcg',
+ ],
+ },
+ 'i386': {
+ 'cloudimg_type': CLOUD_IMAGE_TYPE_DISK,
+ 'cloudimg_arch': 'i386',
+ 'qemu_bin': 'qemu-system-x86_64',
+ 'qemu_disk_type': QEMU_DISK_TYPE_VIRTIO,
+ 'qemu_extra_args': [
+ '-machine', 'accel=kvm:tcg',
+ ],
+ },
+ 'ppc64el': {
+ 'cloudimg_type': CLOUD_IMAGE_TYPE_DISK,
+ 'cloudimg_arch': 'ppc64el',
+ 'qemu_bin': 'qemu-system-ppc64',
+ 'qemu_disk_type': QEMU_DISK_TYPE_VIRTIO,
+ 'qemu_extra_args': [
+ '-machine', 'prep',
+ ],
+ },
+}
+
+class QemuRunner(object):
+ def __init__(self, arch):
+ self.arch = arch
+ self.config = QEMU_ARCH_CONFIG[arch]
+ self.drive_id = 0
+ # Parameters common to all architectures
+ self.params = [
+ self.config['qemu_bin'],
+ "-m", "256",
+ "-display", "none",
+ "-nographic",
+ "-net", "nic",
+ "-net", "user,net=10.0.0.0/8,host=10.0.0.1,hostfwd=tcp::2222-:22",
+ ]
+ # Add any architecture-specific parameters
+ if 'qemu_extra_args' in self.config:
+ self.params = self.params + self.config['qemu_extra_args']
+
+ self.append = []
+ if self.config['cloudimg_type'] == CLOUD_IMAGE_TYPE_TAR:
+ self.append = self.append + [
+ 'console=ttyAMA0',
+ 'earlyprintk=serial',
+ 'ro',
+ 'rootfstype=ext4',
+ 'root=LABEL=cloudimg-rootfs',
+ 'rootdelay=10',
+ ]
+
+ def add_boot_files(self, kernel=None, initrd=None, dtb=None):
+ if kernel:
+ self.params = self.params + ['-kernel', kernel]
+ if initrd:
+ self.params = self.params + ['-initrd', initrd]
+ if dtb:
+ self.params = self.params + ['-dtb', dtb]
+
+ def add_drive(self, cloudimg):
+ drive = ["-drive"]
+ if self.config['qemu_disk_type'] == QEMU_DISK_TYPE_SD:
+ drive = drive + ["file=%s,if=sd,cache=writeback" % (cloudimg)]
+ elif self.config['qemu_disk_type'] == QEMU_DISK_TYPE_VIRTIO:
+ drive = drive + ["file=%s,if=virtio" % (cloudimg)]
+ elif self.config['qemu_disk_type'] == QEMU_DISK_TYPE_VIRTIO_BLK:
+ drive = drive + [ "file=%s,if=none,id=disk.%d"
+ % (cloudimg, self.drive_id) ]
+ drive = drive + [ "-device", "virtio-blk-device,drive=disk.%d"
+ % (self.drive_id) ]
+ self.params = self.params + drive
+ self.drive_id = self.drive_id + 1
+
+ def get_params(self):
+ params = self.params
+ if self.append:
+ params = params + ['-append', '"%s"' % (" ".join(self.append))]
+ return params
+
class KVMTest(object):
- def __init__(self, image=None, timeout=500, debug_file="virt_debug"):
+ def __init__(self, image=None, timeout=500, debug_file=None):
self.image = image
self.timeout = timeout
- self.debug_file = os.path.join(os.getcwd(), debug_file)
- self.arch = check_output(['arch'], universal_newlines=True)
+ self.debug_file = debug_file
+ self.arch = check_output(['dpkg', '--print-architecture'], universal_newlines=True).strip()
+ self.qemu_config = QEMU_ARCH_CONFIG[self.arch]
def download_image(self):
"""
@@ -70,10 +193,13 @@ class KVMTest(object):
# Construct URL
cloud_url = "http://cloud-images.ubuntu.com"
- if re.match("arm.*", self.arch):
- cloud_iso = release + "-server-cloudimg-armhf.tar.gz"
+ if self.qemu_config['cloudimg_type'] == CLOUD_IMAGE_TYPE_TAR:
+ cloud_iso = "%s-server-cloudimg-%s.tar.gz" % (release, self.qemu_config['cloudimg_arch'])
+ elif self.qemu_config['cloudimg_type'] == CLOUD_IMAGE_TYPE_DISK:
+ cloud_iso = "%s-server-cloudimg-%s-disk1.img" % (release, self.qemu_config['cloudimg_arch'])
else:
- cloud_iso = release + "-server-cloudimg-i386-disk1.img"
+ logging.error("Unknown cloud image type")
+ return False
image_url = "/".join((
cloud_url, release, "current", cloud_iso))
@@ -82,12 +208,15 @@ class KVMTest(object):
# Attempt download
try:
resp = urllib.request.urlretrieve(image_url, cloud_iso)
- except (IOError, OSError, urllib.error.HTTPError) as exception:
+ except (IOError,
+ OSError,
+ urllib.error.HTTPError,
+ urllib.error.URLError) as exception:
logging.error("Failed download of image from %s: %s", image_url, exception)
return False
# Unpack img file from tar
- if re.match("arm.*", self.arch):
+ if self.qemu_config['cloudimg_type'] == CLOUD_IMAGE_TYPE_TAR:
cloud_iso_tgz = tarfile.open(cloud_iso)
cloud_iso = cloud_iso.replace('tar.gz', 'img')
cloud_iso_tgz.extract(cloud_iso)
@@ -105,42 +234,29 @@ class KVMTest(object):
logging.debug("Attempting boot for:{}".format(data_disk))
- # Set Arbitrary IP values
- netrange = "10.0.0.0/8"
- image_ip = "10.0.0.1"
- hostfwd = "tcp::2222-:22"
- cloud_disk = ""
+ qemu = QemuRunner(self.arch)
+
+ # Assume that a tar type image is not self-bootable, so
+ # therefore requires explicit bootfiles (otherwise, why
+ # not just use the disk format directly?
+ if self.qemu_config['cloudimg_type'] == CLOUD_IMAGE_TYPE_TAR:
+ for dir in ['/boot', '/']:
+ kernel = os.path.join(dir, 'vmlinuz')
+ initrd = os.path.join(dir, 'initrd.img')
+ if os.path.isfile(kernel):
+ qemu.add_boot_files(kernel=kernel, initrd=initrd)
+ break
+
+ qemu.add_drive(data_disk)
# Should we attach the cloud config disk
if os.path.isfile("seed.iso"):
logging.debug("Attaching Cloud config disk")
- cloud_disk = "-drive file=seed.iso,if=virtio"
+ qemu.add_drive("seed.iso")
+
+ params = qemu.get_params()
+ logging.debug("Using params:{}".format(" ".join(params)))
- if re.match("(arm.*|aarch64)", self.arch):
- uname = check_output(['uname', '-r'], universal_newlines=True)
- cloud_disk = cloud_disk.replace("virtio", "sd")
- params = 'qemu-system-arm -machine vexpress-a15 -cpu cortex-a15 -enable-kvm -m {} -kernel /boot/vmlinuz -append "console=ttyAMA0 earlyprintk=serial root=/dev/mmcblk0 ro rootfstype=ext4" -serial stdio -dtb /lib/firmware/{}/device-tree/vexpress-v2p-ca15-tc1.dtb -initrd /boot/initrd.img -net nic -net user,net={},host={},hostfwd={} -drive file={},if=sd,cache=writeback {} -display none -nographic'.format(uname, "256", netrange, image_ip, hostfwd, data_disk, cloud_disk)
- else:
- params = \
- '''
- qemu-system-x86_64 -machine accel=kvm:tcg -m {0} -net nic
- -net user,net={1},host={2},hostfwd={3}
- -drive file={4},if=virtio {5} -display none -nographic
- '''.format(
- "256",
- netrange,
- image_ip,
- hostfwd,
- data_disk,
- cloud_disk).replace("\n", "").replace(" ", "")
-
- logging.debug("Using params:{}".format(params))
-
- # Default file location for log file is in checkbox output directory
- checkbox_dir = os.getenv("CHECKBOX_DATA")
-
- if checkbox_dir is not None:
- self.debug_file = os.path.join(checkbox_dir, self.debug_file)
logging.info("Storing VM console output in {}".format(
os.path.realpath(self.debug_file)))
# Open VM STDERR/STDOUT log file for writing
@@ -152,7 +268,7 @@ class KVMTest(object):
# Start Virtual machine
self.process = Popen(
- shlex.split(params), stdin=PIPE, stderr=file, stdout=file,
+ params, stdin=PIPE, stderr=file, stdout=file,
universal_newlines=True, shell=False)
def create_cloud_disk(self):
@@ -220,8 +336,10 @@ final_message: CERTIFICATION BOOT COMPLETE
instance = self.boot_image(self.image)
time.sleep(self.timeout)
- # Reset Console window to regain control from VM Serial I/0
- call('reset')
+ # If running in console, reset console window to regain
+ # control from VM Serial I/0
+ if sys.stdout.isatty():
+ call('reset')
# Check to be sure VM boot was successful
with open(self.debug_file, 'r') as debug_file:
file_contents = debug_file.read()
@@ -296,7 +414,7 @@ def test_kvm(args):
if args.image:
image = args.image
- kvm_test = KVMTest(image, timeout)
+ kvm_test = KVMTest(image, timeout, args.log_file)
result = kvm_test.start()
sys.exit(result)
@@ -319,6 +437,9 @@ def main():
'-i', '--image', type=str, default=None)
kvm_test_parser.add_argument(
'-t', '--timeout', type=int)
+ kvm_test_parser.add_argument(
+ '-l', '--log-file', default='virt_debug',
+ help="Location for debugging output log. Defaults to %(default)s.")
kvm_test_parser.add_argument('--debug', dest='log_level',
action="store_const", const=logging.DEBUG,
default=logging.INFO)