diff options
Diffstat (limited to 'bin/virtualization')
-rwxr-xr-x | bin/virtualization | 209 |
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) |