From nobody Tue Feb 10 23:01:15 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 152424954986569.19318083294786; Fri, 20 Apr 2018 11:39:09 -0700 (PDT) Received: from localhost ([::1]:50738 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1f9aw9-0003Qu-0J for importer@patchew.org; Fri, 20 Apr 2018 14:39:09 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:39845) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1f9ahF-0007UQ-2K for qemu-devel@nongnu.org; Fri, 20 Apr 2018 14:23:47 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1f9ahC-00070V-Rq for qemu-devel@nongnu.org; Fri, 20 Apr 2018 14:23:45 -0400 Received: from mx1.redhat.com ([209.132.183.28]:48938) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1f9ahC-0006z0-IW for qemu-devel@nongnu.org; Fri, 20 Apr 2018 14:23:42 -0400 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id CFD74316422D; Fri, 20 Apr 2018 18:23:41 +0000 (UTC) Received: from localhost (ovpn-116-52.gru2.redhat.com [10.97.116.52]) by smtp.corp.redhat.com (Postfix) with ESMTP id C83CF60BE0; Fri, 20 Apr 2018 18:23:40 +0000 (UTC) From: Eduardo Habkost To: qemu-devel@nongnu.org Date: Fri, 20 Apr 2018 15:19:48 -0300 Message-Id: <20180420181951.7252-22-ehabkost@redhat.com> In-Reply-To: <20180420181951.7252-1-ehabkost@redhat.com> References: <20180420181951.7252-1-ehabkost@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.44]); Fri, 20 Apr 2018 18:23:41 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [RFC 21/24] avocado_qemu: Introduce the add_image() VM API X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: =?UTF-8?q?Luk=C3=A1=C5=A1=20Doktor?= , Fam Zheng , Stefan Hajnoczi , Amador Pahim , Cleber Rosa , Alistair Francis Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" From: Amador Pahim Uses can not add an image to the virtual machine with the option to configure the user/password using cloudinit. Signed-off-by: Amador Pahim Signed-off-by: Eduardo Habkost --- tests/avocado/README.rst | 4 +- tests/avocado/avocado_qemu/test.py | 136 ++++++++++++++++++++++++++-------= ---- tests/avocado/parameters.yaml | 9 --- tests/avocado/test_nec-usb-xhci.py | 14 ++-- tests/avocado/test_numa_hotplug.py | 5 +- 5 files changed, 111 insertions(+), 57 deletions(-) diff --git a/tests/avocado/README.rst b/tests/avocado/README.rst index e2aa993501..a33c4a2577 100644 --- a/tests/avocado/README.rst +++ b/tests/avocado/README.rst @@ -78,8 +78,8 @@ file using the Avocado parameters system: ``image_snapshot`` parameter. - ``image_user`` and ``image_pass``: When using a ``image_path``, if you want to get the console from the Guest OS you have to define the Guest - OS credentials. Example: ``image_user: root`` and - ``image_pass: p4ssw0rd``. By default it uses ``root`` and ``123456``. + OS credentials. Example: ``image_user: avocado`` and + ``image_pass: p4ssw0rd``. Both parameters have defaults to ``avocado``. - ``machine_type``: Use this option to define a machine type for the VM. Example: ``machine_type: pc`` - ``machine_accel``: Use this option to define a machine acceleration diff --git a/tests/avocado/avocado_qemu/test.py b/tests/avocado/avocado_qem= u/test.py index 308fdfa514..5a08dace45 100644 --- a/tests/avocado/avocado_qemu/test.py +++ b/tests/avocado/avocado_qemu/test.py @@ -28,6 +28,7 @@ import logging import os import re import sys +import tempfile import time import uuid =20 @@ -81,6 +82,12 @@ class QEMUMigrationError(Exception): """ =20 =20 +class QEMUCloudinitError(Exception): + """ + If some error with the cloudinit happens + """ + + def _get_qemu_bin(arch): git_root =3D process.system_output('git rev-parse --show-toplevel', ignore_status=3DTrue, @@ -219,8 +226,8 @@ def _handle_prompts(session, username, password, prompt= , timeout=3D60, class _VM(qemu.QEMUMachine): '''A QEMU VM''' =20 - def __init__(self, qemu_bin=3DNone, arch=3DNone, username=3DNone, pass= word=3DNone, - qemu_dst_bin=3DNone): + def __init__(self, qemu_bin=3DNone, arch=3DNone, qemu_dst_bin=3DNone, + username=3DNone, password=3DNone): if arch is None: arch =3D os.uname()[4] self.arch =3D arch @@ -245,8 +252,11 @@ class _VM(qemu.QEMUMachine): :param prompt: The regex to identify we reached the prompt. """ =20 + if not all((self.username, self.password)): + raise QEMULoginError('Username or password not set.') + if not self.is_running(): - raise QEMUConsoleError('VM is not running.') + raise QEMULoginError('VM is not running.') =20 if console_address is None: if self._console_address is None: @@ -285,9 +295,12 @@ class _VM(qemu.QEMUMachine): return False =20 port =3D self.ports.find_free_port() - newvm =3D _VM(self.qemu_dst_bin, self._arch, self.username, self.p= assword) + newvm =3D _VM(self.qemu_dst_bin, self._arch, username=3Dself.usern= ame, + password=3Dself.password) newvm.args =3D self.args newvm.args.extend(['-incoming', 'tcp:0:%s' % port]) + newvm.username =3D self.username + newvm.password =3D self.password =20 newvm.launch(console_address) cmd =3D 'migrate -d tcp:0:%s' % port @@ -301,6 +314,80 @@ class _VM(qemu.QEMUMachine): =20 return newvm =20 + def add_image(self, path, username=3DNone, password=3DNone, cloudinit= =3DFalse, + snapshot=3DTrue, extra=3DNone): + """ + Adds the '-drive' command line option and its parameters to + the Qemu VM + + :param path: Image path (i.e. /var/lib/images/guestos.qcow2) + :param username: The username to log into the Guest OS with + :param password: The password to log into the Guest OS with + :param cloudinit: Whether the cloudinit cdrom will be attached to + the image + :param snapshot: Whether the parameter snapshot=3Don will be used + :param extra: Extra parameters to the -drive option + """ + file_option =3D 'file=3D%s' % path + for item in self.args: + if file_option in item: + logging.error('Image %s already present', path) + return + + if extra is not None: + file_option +=3D ',%s' % extra + + if snapshot: + file_option +=3D ',snapshot=3Don' + + self.args.extend(['-drive', file_option]) + + if username is not None: + self.username =3D username + + if password is not None: + self.password =3D password + + if cloudinit: + self._cloudinit() + + def _cloudinit(self): + """ + Creates a CDROM Iso Image with the required cloudinit files + (meta-data and user-data) to make the initial Cloud Image + configuration, attaching the CDROM to the VM. + """ + try: + geniso_bin =3D utils_path.find_command('genisoimage') + except: + raise QEMUCloudinitError('Command not found (genisoimage)') + + data_dir =3D tempfile.mkdtemp() + + metadata_path =3D os.path.join(data_dir, 'meta-data') + metadata_content =3D ("instance-id: %s\n" + "local-hostname: %s\n" % (self.name, self.name= )) + with open(metadata_path, 'w') as metadata_file: + metadata_file.write(metadata_content) + + userdata_path =3D os.path.join(data_dir, 'user-data') + userdata_content =3D ("#cloud-config\n" + "password: %s\n" + "ssh_pwauth: True\n" + "chpasswd: { expire: False }\n" + "system_info:\n" + " default_user:\n" + " name: %s\n" % + (self.password, self.username)) + + with open(userdata_path, 'w') as userdata_file: + userdata_file.write(userdata_content) + + iso_path =3D os.path.join(data_dir, 'cdrom.iso') + process.run("%s -output %s -volid cidata -joliet -rock %s %s" % + (geniso_bin, iso_path, metadata_path, userdata_path)) + + self.args.extend(['-cdrom', iso_path]) =20 class QemuTest(Test): =20 @@ -311,9 +398,11 @@ class QemuTest(Test): job=3Djob, runner_queue=3Drunner_qu= eue) self.vm =3D _VM(qemu_bin=3Dself.params.get('qemu_bin'), arch=3Dself.params.get('arch'), - username=3Dself.params.get('image_user', default=3D"= root"), - password=3Dself.params.get('image_pass', default=3D"= 123456"), - qemu_dst_bin=3Dself.params.get('qemu_dst_bin')) + qemu_dst_bin=3Dself.params.get('qemu_dst_bin'), + username=3Dself.params.get('image_user', + default=3D'avocado'), + password=3Dself.params.get('image_pass', + default=3D'avocado')) =20 machine_type =3D self.params.get('machine_type') machine_accel =3D self.params.get('machine_accel') @@ -327,36 +416,3 @@ class QemuTest(Test): machine +=3D "kvm-type=3D%s," % machine_kvm_type if machine: self.vm.args.extend(['-machine', machine]) - - def request_image(self, path=3DNone, snapshot=3DNone, extra=3DNone): - """ - Add image to the `self.vm` using params or arguments. - - Unless it's overridden by arguments it uses following test params - to specify the image: - - * image_path - defines the path to the user-image. If not specified - it uses "QEMU_ROOT/boot_image_$arch.qcow2" - * image_snapshot - whether to use "snapshot=3Don" (snapshot=3Doff = is not - supplied) - * image_extra - free-form string to extend the "-drive" params - - :param path: Override the path ("image_path" param is used otherwi= se) - :param snapshot: Override the usage of snapshot - :param extra: Extra arguments to be added to drive definition - """ - if snapshot is None: - snapshot =3D self.params.get("image_snapshot", default=3DTrue) - if extra is None: - extra =3D self.params.get("image_extra", default=3D"") - if path is None: - path =3D self.params.get("image_path") - if path is None: - arch =3D self.vm.arch - path =3D os.path.join(QEMU_ROOT, "boot_image_%s.qcow2" % a= rch) - if not os.path.exists(path): - self.error("Require a bootable image, which was not found. " - "Please provide one in '%s'." % path) - if snapshot: - extra +=3D ",snapshot=3Don" - self.vm.args.extend(['-drive', 'file=3D%s%s' % (path, extra)]) diff --git a/tests/avocado/parameters.yaml b/tests/avocado/parameters.yaml index 3c5a0f92e0..03c4ed1416 100644 --- a/tests/avocado/parameters.yaml +++ b/tests/avocado/parameters.yaml @@ -10,15 +10,6 @@ qemu_bin: null # used in the source VM will be used for the destination VM. qemu_dst_bin: null =20 -# VMs are defined without image. If the 'image_path' is specified, it -# will be used as the VM image. The '-snapshot' option will then be used -# to avoid writing data to the image. -image_path: null -# Username used to get the console from the Guest OS. -image_user: null -# Password used to get the console from the Guest OS. -image_pass: null - # Use this option to define a machine type for the VM. machine_type: null # Use this option to define a machine acceleration for the VM. diff --git a/tests/avocado/test_nec-usb-xhci.py b/tests/avocado/test_nec-us= b-xhci.py index 3f0d645032..1c4822544e 100644 --- a/tests/avocado/test_nec-usb-xhci.py +++ b/tests/avocado/test_nec-usb-xhci.py @@ -4,6 +4,7 @@ import tempfile =20 from avocado_qemu import test from avocado.utils import process +from avocado.utils import vmimage =20 class TestNecUsbXhci(test.QemuTest): """ @@ -17,8 +18,10 @@ class TestNecUsbXhci(test.QemuTest): """ =20 def setUp(self): + self.image =3D vmimage.get() + self.vm.add_image(self.image.path, cloudinit=3DTrue, snapshot=3DFa= lse) + usbdevice =3D os.path.join(self.workdir, 'usb.img') - self.request_image() process.run('dd if=3D/dev/zero of=3D%s bs=3D1M count=3D10' % usbde= vice) self.vm.args.extend(['-device', 'pci-bridge,id=3Dbridge1,chassis_n= r=3D1']) self.vm.args.extend(['-device', 'nec-usb-xhci,id=3Dxhci1,bus=3Dbri= dge1,addr=3D0x3']) @@ -35,17 +38,18 @@ class TestNecUsbXhci(test.QemuTest): =20 :avocado: tags=3Dmigration,RHBZ1436616 """ + console =3D self.vm.get_console() - console.sendline('fdisk -l') - result =3D console.read_nonblocking() + console.sendline('sudo fdisk -l') + result =3D console.read_up_to_prompt() console.close() self.assertIn('Disk /dev/sdb: 10 MiB, 10485760 bytes, 20480 sector= s', result) =20 self.vm_dst =3D self.vm.migrate() console =3D self.vm_dst.get_console() - console.sendline('fdisk -l') - result =3D console.read_nonblocking() + console.sendline('sudo fdisk -l') + result =3D console.read_up_to_prompt() console.close() self.assertIn('Disk /dev/sdb: 10 MiB, 10485760 bytes, 20480 sector= s', result) diff --git a/tests/avocado/test_numa_hotplug.py b/tests/avocado/test_numa_h= otplug.py index a99b8dcebf..ee43e60089 100644 --- a/tests/avocado/test_numa_hotplug.py +++ b/tests/avocado/test_numa_hotplug.py @@ -2,6 +2,7 @@ import re import time =20 from avocado_qemu import test +from avocado.utils import vmimage =20 =20 class TestNumaHotplug(test.QemuTest): @@ -20,7 +21,9 @@ class TestNumaHotplug(test.QemuTest): """ =20 def setUp(self): - self.request_image() + self.image =3D vmimage.get() + self.vm.add_image(self.image.path, cloudinit=3DTrue, snapshot=3DFa= lse) + self.vm.args.extend(["-m", "4G,slots=3D208,maxmem=3D80G"]) self.vm.args.extend(["-numa", "node"] * 16) self.vm.launch() --=20 2.14.3