[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [RFC 21/24] avocado_qemu: Introduce the add_image() VM API
From: |
Eduardo Habkost |
Subject: |
[Qemu-devel] [RFC 21/24] avocado_qemu: Introduce the add_image() VM API |
Date: |
Fri, 20 Apr 2018 15:19:48 -0300 |
From: Amador Pahim <address@hidden>
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 <address@hidden>
Signed-off-by: Eduardo Habkost <address@hidden>
---
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_qemu/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
@@ -81,6 +82,12 @@ class QEMUMigrationError(Exception):
"""
+class QEMUCloudinitError(Exception):
+ """
+ If some error with the cloudinit happens
+ """
+
+
def _get_qemu_bin(arch):
git_root = process.system_output('git rev-parse --show-toplevel',
ignore_status=True,
@@ -219,8 +226,8 @@ def _handle_prompts(session, username, password, prompt,
timeout=60,
class _VM(qemu.QEMUMachine):
'''A QEMU VM'''
- def __init__(self, qemu_bin=None, arch=None, username=None, password=None,
- qemu_dst_bin=None):
+ def __init__(self, qemu_bin=None, arch=None, qemu_dst_bin=None,
+ username=None, password=None):
if arch is None:
arch = os.uname()[4]
self.arch = arch
@@ -245,8 +252,11 @@ class _VM(qemu.QEMUMachine):
:param prompt: The regex to identify we reached the prompt.
"""
+ 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.')
if console_address is None:
if self._console_address is None:
@@ -285,9 +295,12 @@ class _VM(qemu.QEMUMachine):
return False
port = self.ports.find_free_port()
- newvm = _VM(self.qemu_dst_bin, self._arch, self.username,
self.password)
+ newvm = _VM(self.qemu_dst_bin, self._arch, username=self.username,
+ password=self.password)
newvm.args = self.args
newvm.args.extend(['-incoming', 'tcp:0:%s' % port])
+ newvm.username = self.username
+ newvm.password = self.password
newvm.launch(console_address)
cmd = 'migrate -d tcp:0:%s' % port
@@ -301,6 +314,80 @@ class _VM(qemu.QEMUMachine):
return newvm
+ def add_image(self, path, username=None, password=None, cloudinit=False,
+ snapshot=True, extra=None):
+ """
+ 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=on will be used
+ :param extra: Extra parameters to the -drive option
+ """
+ file_option = 'file=%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 += ',%s' % extra
+
+ if snapshot:
+ file_option += ',snapshot=on'
+
+ self.args.extend(['-drive', file_option])
+
+ if username is not None:
+ self.username = username
+
+ if password is not None:
+ self.password = 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 = utils_path.find_command('genisoimage')
+ except:
+ raise QEMUCloudinitError('Command not found (genisoimage)')
+
+ data_dir = tempfile.mkdtemp()
+
+ metadata_path = os.path.join(data_dir, 'meta-data')
+ metadata_content = ("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 = os.path.join(data_dir, 'user-data')
+ userdata_content = ("#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 = 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])
class QemuTest(Test):
@@ -311,9 +398,11 @@ class QemuTest(Test):
job=job, runner_queue=runner_queue)
self.vm = _VM(qemu_bin=self.params.get('qemu_bin'),
arch=self.params.get('arch'),
- username=self.params.get('image_user', default="root"),
- password=self.params.get('image_pass', default="123456"),
- qemu_dst_bin=self.params.get('qemu_dst_bin'))
+ qemu_dst_bin=self.params.get('qemu_dst_bin'),
+ username=self.params.get('image_user',
+ default='avocado'),
+ password=self.params.get('image_pass',
+ default='avocado'))
machine_type = self.params.get('machine_type')
machine_accel = self.params.get('machine_accel')
@@ -327,36 +416,3 @@ class QemuTest(Test):
machine += "kvm-type=%s," % machine_kvm_type
if machine:
self.vm.args.extend(['-machine', machine])
-
- def request_image(self, path=None, snapshot=None, extra=None):
- """
- 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=on" (snapshot=off is not
- supplied)
- * image_extra - free-form string to extend the "-drive" params
-
- :param path: Override the path ("image_path" param is used otherwise)
- :param snapshot: Override the usage of snapshot
- :param extra: Extra arguments to be added to drive definition
- """
- if snapshot is None:
- snapshot = self.params.get("image_snapshot", default=True)
- if extra is None:
- extra = self.params.get("image_extra", default="")
- if path is None:
- path = self.params.get("image_path")
- if path is None:
- arch = self.vm.arch
- path = os.path.join(QEMU_ROOT, "boot_image_%s.qcow2" % arch)
- 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 += ",snapshot=on"
- self.vm.args.extend(['-drive', 'file=%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
-# 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-usb-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
from avocado_qemu import test
from avocado.utils import process
+from avocado.utils import vmimage
class TestNecUsbXhci(test.QemuTest):
"""
@@ -17,8 +18,10 @@ class TestNecUsbXhci(test.QemuTest):
"""
def setUp(self):
+ self.image = vmimage.get()
+ self.vm.add_image(self.image.path, cloudinit=True, snapshot=False)
+
usbdevice = os.path.join(self.workdir, 'usb.img')
- self.request_image()
process.run('dd if=/dev/zero of=%s bs=1M count=10' % usbdevice)
self.vm.args.extend(['-device', 'pci-bridge,id=bridge1,chassis_nr=1'])
self.vm.args.extend(['-device',
'nec-usb-xhci,id=xhci1,bus=bridge1,addr=0x3'])
@@ -35,17 +38,18 @@ class TestNecUsbXhci(test.QemuTest):
:avocado: tags=migration,RHBZ1436616
"""
+
console = self.vm.get_console()
- console.sendline('fdisk -l')
- result = console.read_nonblocking()
+ console.sendline('sudo fdisk -l')
+ result = console.read_up_to_prompt()
console.close()
self.assertIn('Disk /dev/sdb: 10 MiB, 10485760 bytes, 20480 sectors',
result)
self.vm_dst = self.vm.migrate()
console = self.vm_dst.get_console()
- console.sendline('fdisk -l')
- result = console.read_nonblocking()
+ console.sendline('sudo fdisk -l')
+ result = console.read_up_to_prompt()
console.close()
self.assertIn('Disk /dev/sdb: 10 MiB, 10485760 bytes, 20480 sectors',
result)
diff --git a/tests/avocado/test_numa_hotplug.py
b/tests/avocado/test_numa_hotplug.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
from avocado_qemu import test
+from avocado.utils import vmimage
class TestNumaHotplug(test.QemuTest):
@@ -20,7 +21,9 @@ class TestNumaHotplug(test.QemuTest):
"""
def setUp(self):
- self.request_image()
+ self.image = vmimage.get()
+ self.vm.add_image(self.image.path, cloudinit=True, snapshot=False)
+
self.vm.args.extend(["-m", "4G,slots=208,maxmem=80G"])
self.vm.args.extend(["-numa", "node"] * 16)
self.vm.launch()
--
2.14.3
- [Qemu-devel] [RFC 13/24] avocado_qemu: Functional test for RHBZ#1431939, (continued)
- [Qemu-devel] [RFC 13/24] avocado_qemu: Functional test for RHBZ#1431939, Eduardo Habkost, 2018/04/20
- [Qemu-devel] [RFC 14/24] avocado_qemu: Functional test for RHBZ#1447027, Eduardo Habkost, 2018/04/20
- [Qemu-devel] [RFC 15/24] avocado_qemu: Functional test for RHBZ#1436616, Eduardo Habkost, 2018/04/20
- [Qemu-devel] [RFC 16/24] avocado_qemu: Functional test for RHBZ1473203, Eduardo Habkost, 2018/04/20
- [Qemu-devel] [RFC 17/24] avocado_qemu: Remove duplicate PortTracker implementation, Eduardo Habkost, 2018/04/20
- [Qemu-devel] [RFC 19/24] avocado_qemu: Clean unneeded 'pass', Eduardo Habkost, 2018/04/20
- [Qemu-devel] [RFC 18/24] avocado_qemu: Simplify the installation instructions, Eduardo Habkost, 2018/04/20
- [Qemu-devel] [RFC 20/24] avocado_qemu: Set QMP log level to INFO, Eduardo Habkost, 2018/04/20
- [Qemu-devel] [RFC 22/24] avocado_qemu: Tests fixes, Eduardo Habkost, 2018/04/20
- [Qemu-devel] [RFC 21/24] avocado_qemu: Introduce the add_image() VM API,
Eduardo Habkost <=
- [Qemu-devel] [RFC 23/24] avocado_qemu: Force vmimage distro, Eduardo Habkost, 2018/04/20
- [Qemu-devel] [RFC 24/24] avocado_qemu: Add a few VNC related tests, Eduardo Habkost, 2018/04/20
- Re: [Qemu-devel] [RFC 00/24] Avocado-based functional tests, no-reply, 2018/04/21