Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(378)

Unified Diff: client/virt/kvm_vm.py

Issue 6883246: Merge autotest upstream from @5318 ~ @5336 (Closed) Base URL: ssh://gitrw.chromium.org:9222/autotest.git@master
Patch Set: patch Created 9 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « client/virt/kvm_monitor.py ('k') | client/virt/ppm_utils.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: client/virt/kvm_vm.py
diff --git a/client/tests/kvm/kvm_vm.py b/client/virt/kvm_vm.py
old mode 100755
new mode 100644
similarity index 64%
rename from client/tests/kvm/kvm_vm.py
rename to client/virt/kvm_vm.py
index f0b81528d2e69cc0384e2f6d69815d9e9e4381a3..5df87191fe12aa73e804d4cc8f2cfe1f2a18c684
--- a/client/tests/kvm/kvm_vm.py
+++ b/client/virt/kvm_vm.py
@@ -6,334 +6,18 @@ Utility classes and functions to handle Virtual Machine creation using qemu.
"""
import time, os, logging, fcntl, re, commands, glob
-import kvm_utils, kvm_subprocess, kvm_monitor
from autotest_lib.client.common_lib import error
from autotest_lib.client.bin import utils
+import virt_utils, virt_vm, kvm_monitor, aexpect
-class VMError(Exception):
- pass
-
-
-class VMCreateError(VMError):
- def __init__(self, cmd, status, output):
- VMError.__init__(self, cmd, status, output)
- self.cmd = cmd
- self.status = status
- self.output = output
-
- def __str__(self):
- return ("VM creation command failed: %r (status: %s, "
- "output: %r)" % (self.cmd, self.status, self.output))
-
-
-class VMHashMismatchError(VMError):
- def __init__(self, actual, expected):
- VMError.__init__(self, actual, expected)
- self.actual_hash = actual
- self.expected_hash = expected
-
- def __str__(self):
- return ("CD image hash (%s) differs from expected one (%s)" %
- (self.actual_hash, self.expected_hash))
-
-
-class VMImageMissingError(VMError):
- def __init__(self, filename):
- VMError.__init__(self, filename)
- self.filename = filename
-
- def __str__(self):
- return "CD image file not found: %r" % self.filename
-
-
-class VMImageCheckError(VMError):
- def __init__(self, filename):
- VMError.__init__(self, filename)
- self.filename = filename
-
- def __str__(self):
- return "Errors found on image: %r" % self.filename
-
-
-class VMBadPATypeError(VMError):
- def __init__(self, pa_type):
- VMError.__init__(self, pa_type)
- self.pa_type = pa_type
-
- def __str__(self):
- return "Unsupported PCI assignable type: %r" % self.pa_type
-
-
-class VMPAError(VMError):
- def __init__(self, pa_type):
- VMError.__init__(self, pa_type)
- self.pa_type = pa_type
-
- def __str__(self):
- return ("No PCI assignable devices could be assigned "
- "(pci_assignable=%r)" % self.pa_type)
-
-
-class VMPostCreateError(VMError):
- def __init__(self, cmd, output):
- VMError.__init__(self, cmd, output)
- self.cmd = cmd
- self.output = output
-
-
-class VMHugePageError(VMPostCreateError):
- def __str__(self):
- return ("Cannot allocate hugepage memory (command: %r, "
- "output: %r)" % (self.cmd, self.output))
-
-
-class VMKVMInitError(VMPostCreateError):
- def __str__(self):
- return ("Cannot initialize KVM (command: %r, output: %r)" %
- (self.cmd, self.output))
-
-
-class VMDeadError(VMError):
- def __init__(self, status, output):
- VMError.__init__(self, status, output)
- self.status = status
- self.output = output
-
- def __str__(self):
- return ("VM process is dead (status: %s, output: %r)" %
- (self.status, self.output))
-
-
-class VMDeadKernelCrashError(VMError):
- def __init__(self, kernel_crash):
- VMError.__init__(self, kernel_crash)
- self.kernel_crash = kernel_crash
-
- def __str__(self):
- return ("VM is dead due to a kernel crash:\n%s" % self.kernel_crash)
-
-
-class VMAddressError(VMError):
- pass
-
-
-class VMPortNotRedirectedError(VMAddressError):
- def __init__(self, port):
- VMAddressError.__init__(self, port)
- self.port = port
-
- def __str__(self):
- return "Port not redirected: %s" % self.port
-
-
-class VMAddressVerificationError(VMAddressError):
- def __init__(self, mac, ip):
- VMAddressError.__init__(self, mac, ip)
- self.mac = mac
- self.ip = ip
-
- def __str__(self):
- return ("Cannot verify MAC-IP address mapping using arping: "
- "%s ---> %s" % (self.mac, self.ip))
-
-
-class VMMACAddressMissingError(VMAddressError):
- def __init__(self, nic_index):
- VMAddressError.__init__(self, nic_index)
- self.nic_index = nic_index
-
- def __str__(self):
- return "No MAC address defined for NIC #%s" % self.nic_index
-
-
-class VMIPAddressMissingError(VMAddressError):
- def __init__(self, mac):
- VMAddressError.__init__(self, mac)
- self.mac = mac
-
- def __str__(self):
- return "Cannot find IP address for MAC address %s" % self.mac
-
-
-class VMMigrateError(VMError):
- pass
-
-
-class VMMigrateTimeoutError(VMMigrateError):
- pass
-
-
-class VMMigrateCancelError(VMMigrateError):
- pass
-
-
-class VMMigrateFailedError(VMMigrateError):
- pass
-
-
-class VMMigrateStateMismatchError(VMMigrateError):
- def __init__(self, src_hash, dst_hash):
- VMMigrateError.__init__(self, src_hash, dst_hash)
- self.src_hash = src_hash
- self.dst_hash = dst_hash
-
- def __str__(self):
- return ("Mismatch of VM state before and after migration (%s != %s)" %
- (self.src_hash, self.dst_hash))
-
-
-class VMRebootError(VMError):
- pass
-
-
-def get_image_filename(params, root_dir):
- """
- Generate an image path from params and root_dir.
-
- @param params: Dictionary containing the test parameters.
- @param root_dir: Base directory for relative filenames.
-
- @note: params should contain:
- image_name -- the name of the image file, without extension
- image_format -- the format of the image (qcow2, raw etc)
- """
- image_name = params.get("image_name", "image")
- image_format = params.get("image_format", "qcow2")
- if params.get("image_raw_device") == "yes":
- return image_name
- image_filename = "%s.%s" % (image_name, image_format)
- image_filename = kvm_utils.get_path(root_dir, image_filename)
- return image_filename
-
-
-def create_image(params, root_dir):
- """
- Create an image using qemu_image.
-
- @param params: Dictionary containing the test parameters.
- @param root_dir: Base directory for relative filenames.
-
- @note: params should contain:
- image_name -- the name of the image file, without extension
- image_format -- the format of the image (qcow2, raw etc)
- image_size -- the requested size of the image (a string
- qemu-img can understand, such as '10G')
- """
- qemu_img_cmd = kvm_utils.get_path(root_dir, params.get("qemu_img_binary",
- "qemu-img"))
- qemu_img_cmd += " create"
-
- format = params.get("image_format", "qcow2")
- qemu_img_cmd += " -f %s" % format
-
- image_filename = get_image_filename(params, root_dir)
- qemu_img_cmd += " %s" % image_filename
-
- size = params.get("image_size", "10G")
- qemu_img_cmd += " %s" % size
-
- utils.system(qemu_img_cmd)
- logging.info("Image created in %r", image_filename)
- return image_filename
-
-
-def remove_image(params, root_dir):
- """
- Remove an image file.
-
- @param params: A dict
- @param root_dir: Base directory for relative filenames.
-
- @note: params should contain:
- image_name -- the name of the image file, without extension
- image_format -- the format of the image (qcow2, raw etc)
- """
- image_filename = get_image_filename(params, root_dir)
- logging.debug("Removing image file %s...", image_filename)
- if os.path.exists(image_filename):
- os.unlink(image_filename)
- else:
- logging.debug("Image file %s not found")
-
-
-def check_image(params, root_dir):
- """
- Check an image using qemu-img.
-
- @param params: Dictionary containing the test parameters.
- @param root_dir: Base directory for relative filenames.
-
- @note: params should contain:
- image_name -- the name of the image file, without extension
- image_format -- the format of the image (qcow2, raw etc)
-
- @raise VMImageCheckError: In case qemu-img check fails on the image.
- """
- image_filename = get_image_filename(params, root_dir)
- logging.debug("Checking image file %s...", image_filename)
- qemu_img_cmd = kvm_utils.get_path(root_dir,
- params.get("qemu_img_binary", "qemu-img"))
- image_is_qcow2 = params.get("image_format") == 'qcow2'
- if os.path.exists(image_filename) and image_is_qcow2:
- # Verifying if qemu-img supports 'check'
- q_result = utils.run(qemu_img_cmd, ignore_status=True)
- q_output = q_result.stdout
- check_img = True
- if not "check" in q_output:
- logging.error("qemu-img does not support 'check', "
- "skipping check...")
- check_img = False
- if not "info" in q_output:
- logging.error("qemu-img does not support 'info', "
- "skipping check...")
- check_img = False
- if check_img:
- try:
- utils.system("%s info %s" % (qemu_img_cmd, image_filename))
- except error.CmdError:
- logging.error("Error getting info from image %s",
- image_filename)
-
- cmd_result = utils.run("%s check %s" %
- (qemu_img_cmd, image_filename),
- ignore_status=True)
- # Error check, large chances of a non-fatal problem.
- # There are chances that bad data was skipped though
- if cmd_result.exit_status == 1:
- for e_line in cmd_result.stdout.splitlines():
- logging.error("[stdout] %s", e_line)
- for e_line in cmd_result.stderr.splitlines():
- logging.error("[stderr] %s", e_line)
- raise error.TestWarn("qemu-img check error. Some bad data in "
- "the image may have gone unnoticed")
- # Exit status 2 is data corruption for sure, so fail the test
- elif cmd_result.exit_status == 2:
- for e_line in cmd_result.stdout.splitlines():
- logging.error("[stdout] %s", e_line)
- for e_line in cmd_result.stderr.splitlines():
- logging.error("[stderr] %s", e_line)
- raise VMImageCheckError(image_filename)
- # Leaked clusters, they are known to be harmless to data integrity
- elif cmd_result.exit_status == 3:
- raise error.TestWarn("Leaked clusters were noticed during "
- "image check. No data integrity problem "
- "was found though.")
-
- else:
- if not os.path.exists(image_filename):
- logging.debug("Image file %s not found, skipping check...",
- image_filename)
- elif not image_is_qcow2:
- logging.debug("Image file %s not qcow2, skipping check...",
- image_filename)
-
-
-class VM:
+class VM(virt_vm.BaseVM):
"""
This class handles all basic VM operations.
"""
+ MIGRATION_PROTOS = ['tcp', 'unix', 'exec']
+
def __init__(self, name, params, root_dir, address_cache, state=None):
"""
Initialize the object and set a few attributes.
@@ -345,6 +29,8 @@ class VM:
@param address_cache: A dict that maps MAC addresses to IP addresses
@param state: If provided, use this as self.__dict__
"""
+ virt_vm.BaseVM.__init__(self, name, params)
+
if state:
self.__dict__ = state
else:
@@ -358,12 +44,6 @@ class VM:
self.device_id = []
self.uuid = None
- # Find a unique identifier for this VM
- while True:
- self.instance = (time.strftime("%Y%m%d-%H%M%S-") +
- kvm_utils.generate_random_string(4))
- if not glob.glob("/tmp/*%s" % self.instance):
- break
self.spice_port = 8000
self.name = name
@@ -372,6 +52,39 @@ class VM:
self.address_cache = address_cache
+ def verify_alive(self):
+ """
+ Make sure the VM is alive and that the main monitor is responsive.
+
+ @raise VMDeadError: If the VM is dead
+ @raise: Various monitor exceptions if the monitor is unresponsive
+ """
+ try:
+ virt_vm.BaseVM.verify_alive(self)
+ if self.monitors:
+ self.monitor.verify_responsive()
+ except virt_vm.VMDeadError:
+ raise virt_vm.VMDeadError(self.process.get_status(),
+ self.process.get_output())
+
+
+ def is_alive(self):
+ """
+ Return True if the VM is alive and its monitor is responsive.
+ """
+ return not self.is_dead() and (not self.monitors or
+ self.monitor.is_responsive())
+
+
+ def is_dead(self):
+ """
+ Return True if the qemu process is dead.
+ """
+ return not self.process or not self.process.is_alive()
+
+
+
+
def clone(self, name=None, params=None, root_dir=None, address_cache=None,
copy_state=False):
"""
@@ -402,7 +115,7 @@ class VM:
return VM(name, params, root_dir, address_cache, state)
- def make_qemu_command(self, name=None, params=None, root_dir=None):
+ def __make_qemu_command(self, name=None, params=None, root_dir=None):
"""
Generate a qemu command line. All parameters are optional. If a
parameter is not supplied, the corresponding value stored in the
@@ -635,7 +348,7 @@ class VM:
# Clone this VM using the new params
vm = self.clone(name, params, root_dir, copy_state=True)
- qemu_binary = kvm_utils.get_path(root_dir, params.get("qemu_binary",
+ qemu_binary = virt_utils.get_path(root_dir, params.get("qemu_binary",
"qemu"))
# Get the output of 'qemu -help' (log a message in case this call never
# returns or causes some other kind of trouble)
@@ -668,7 +381,7 @@ class VM:
if image_params.get("boot_drive") == "no":
continue
qemu_cmd += add_drive(help,
- get_image_filename(image_params, root_dir),
+ virt_vm.get_image_filename(image_params, root_dir),
image_params.get("drive_index"),
image_params.get("drive_format"),
image_params.get("drive_cache"),
@@ -695,7 +408,7 @@ class VM:
# Handle the '-net nic' part
try:
mac = vm.get_mac_address(vlan)
- except VMAddressError:
+ except virt_vm.VMAddressError:
mac = None
qemu_cmd += add_nic(help, vlan, nic_params.get("nic_model"), mac,
device_id, netdev_id, nic_params.get("nic_extra_params"))
@@ -704,11 +417,11 @@ class VM:
downscript = nic_params.get("nic_downscript")
tftp = nic_params.get("tftp")
if script:
- script = kvm_utils.get_path(root_dir, script)
+ script = virt_utils.get_path(root_dir, script)
if downscript:
- downscript = kvm_utils.get_path(root_dir, downscript)
+ downscript = virt_utils.get_path(root_dir, downscript)
if tftp:
- tftp = kvm_utils.get_path(root_dir, tftp)
+ tftp = virt_utils.get_path(root_dir, tftp)
qemu_cmd += add_net(help, vlan, nic_params.get("nic_mode", "user"),
vm.get_ifname(vlan),
script, downscript, tftp,
@@ -729,19 +442,19 @@ class VM:
cdrom_params = params.object_params(cdrom)
iso = cdrom_params.get("cdrom")
if iso:
- qemu_cmd += add_cdrom(help, kvm_utils.get_path(root_dir, iso),
+ qemu_cmd += add_cdrom(help, virt_utils.get_path(root_dir, iso),
cdrom_params.get("drive_index"))
# We may want to add {floppy_otps} parameter for -fda
# {fat:floppy:}/path/. However vvfat is not usually recommended.
floppy = params.get("floppy")
if floppy:
- floppy = kvm_utils.get_path(root_dir, floppy)
+ floppy = virt_utils.get_path(root_dir, floppy)
qemu_cmd += add_floppy(help, floppy)
tftp = params.get("tftp")
if tftp:
- tftp = kvm_utils.get_path(root_dir, tftp)
+ tftp = virt_utils.get_path(root_dir, tftp)
qemu_cmd += add_tftp(help, tftp)
bootp = params.get("bootp")
@@ -750,7 +463,7 @@ class VM:
kernel = params.get("kernel")
if kernel:
- kernel = kvm_utils.get_path(root_dir, kernel)
+ kernel = virt_utils.get_path(root_dir, kernel)
qemu_cmd += add_kernel(help, kernel)
kernel_cmdline = params.get("kernel_cmdline")
@@ -759,7 +472,7 @@ class VM:
initrd = params.get("initrd")
if initrd:
- initrd = kvm_utils.get_path(root_dir, initrd)
+ initrd = virt_utils.get_path(root_dir, initrd)
qemu_cmd += add_initrd(help, initrd)
for host_port, guest_port in redirs:
@@ -855,9 +568,9 @@ class VM:
cdrom_params = params.object_params(cdrom)
iso = cdrom_params.get("cdrom")
if iso:
- iso = kvm_utils.get_path(root_dir, iso)
+ iso = virt_utils.get_path(root_dir, iso)
if not os.path.exists(iso):
- raise VMImageMissingError(iso)
+ raise virt_vm.VMImageMissingError(iso)
compare = False
if cdrom_params.get("md5sum_1m"):
logging.debug("Comparing expected MD5 sum with MD5 sum of "
@@ -881,7 +594,8 @@ class VM:
if actual_hash == expected_hash:
logging.debug("Hashes match")
else:
- raise VMHashMismatchError(actual_hash, expected_hash)
+ raise virt_vm.VMHashMismatchError(actual_hash,
+ expected_hash)
# Make sure the following code is not executed by more than one thread
# at the same time
@@ -891,7 +605,7 @@ class VM:
try:
# Handle port redirections
redir_names = params.objects("redirs")
- host_ports = kvm_utils.find_free_ports(5000, 6000, len(redir_names))
+ host_ports = virt_utils.find_free_ports(5000, 6000, len(redir_names))
self.redirs = {}
for i in range(len(redir_names)):
redir_params = params.object_params(redir_names[i])
@@ -902,16 +616,16 @@ class VM:
self.netdev_id = []
self.device_id = []
for nic in params.objects("nics"):
- self.netdev_id.append(kvm_utils.generate_random_id())
- self.device_id.append(kvm_utils.generate_random_id())
+ self.netdev_id.append(virt_utils.generate_random_id())
+ self.device_id.append(virt_utils.generate_random_id())
# Find available VNC port, if needed
if params.get("display") == "vnc":
- self.vnc_port = kvm_utils.find_free_port(5900, 6100)
+ self.vnc_port = virt_utils.find_free_port(5900, 6100)
- # Find available spice port
+ # Find available spice port, if needed
if params.get("spice"):
- self.spice_port = kvm_utils.find_free_port(8000, 8100)
+ self.spice_port = virt_utils.find_free_port(8000, 8100)
# Find random UUID if specified 'uuid = random' in config file
if params.get("uuid") == "random":
@@ -927,9 +641,9 @@ class VM:
mac = (nic_params.get("nic_mac") or
mac_source and mac_source.get_mac_address(vlan))
if mac:
- kvm_utils.set_mac_address(self.instance, vlan, mac)
+ virt_utils.set_mac_address(self.instance, vlan, mac)
else:
- kvm_utils.generate_mac_address(self.instance, vlan)
+ virt_utils.generate_mac_address(self.instance, vlan)
# Assign a PCI assignable device
self.pci_assignable = None
@@ -939,27 +653,27 @@ class VM:
# Virtual Functions (VF) assignable devices
if pa_type == "vf":
- self.pci_assignable = kvm_utils.PciAssignable(
+ self.pci_assignable = virt_utils.PciAssignable(
type=pa_type,
driver=params.get("driver"),
driver_option=params.get("driver_option"),
devices_requested=pa_devices_requested)
# Physical NIC (PF) assignable devices
elif pa_type == "pf":
- self.pci_assignable = kvm_utils.PciAssignable(
+ self.pci_assignable = virt_utils.PciAssignable(
type=pa_type,
names=params.get("device_names"),
devices_requested=pa_devices_requested)
# Working with both VF and PF
elif pa_type == "mixed":
- self.pci_assignable = kvm_utils.PciAssignable(
+ self.pci_assignable = virt_utils.PciAssignable(
type=pa_type,
driver=params.get("driver"),
driver_option=params.get("driver_option"),
names=params.get("device_names"),
devices_requested=pa_devices_requested)
else:
- raise VMBadPATypeError(pa_type)
+ raise virt_vm.VMBadPATypeError(pa_type)
self.pa_pci_ids = self.pci_assignable.request_devs()
@@ -967,32 +681,32 @@ class VM:
logging.debug("Successfuly assigned devices: %s",
self.pa_pci_ids)
else:
- raise VMPAError(pa_type)
+ raise virt_vm.VMPAError(pa_type)
# Make qemu command
- qemu_command = self.make_qemu_command()
+ qemu_command = self.__make_qemu_command()
# Add migration parameters if required
if migration_mode == "tcp":
- self.migration_port = kvm_utils.find_free_port(5200, 6000)
+ self.migration_port = virt_utils.find_free_port(5200, 6000)
qemu_command += " -incoming tcp:0:%d" % self.migration_port
elif migration_mode == "unix":
self.migration_file = "/tmp/migration-unix-%s" % self.instance
qemu_command += " -incoming unix:%s" % self.migration_file
elif migration_mode == "exec":
- self.migration_port = kvm_utils.find_free_port(5200, 6000)
+ self.migration_port = virt_utils.find_free_port(5200, 6000)
qemu_command += (' -incoming "exec:nc -l %s"' %
self.migration_port)
logging.info("Running qemu command:\n%s", qemu_command)
- self.process = kvm_subprocess.run_bg(qemu_command, None,
+ self.process = aexpect.run_bg(qemu_command, None,
logging.info, "(qemu) ")
# Make sure the process was started successfully
if not self.process.is_alive():
- e = VMCreateError(qemu_command,
- self.process.get_status(),
- self.process.get_output())
+ e = virt_vm.VMCreateError(qemu_command,
+ self.process.get_status(),
+ self.process.get_output())
self.destroy()
raise e
@@ -1030,12 +744,12 @@ class VM:
output = self.process.get_output()
if re.search("Could not initialize KVM", output, re.IGNORECASE):
- e = VMKVMInitError(qemu_command, self.process.get_output())
+ e = virt_vm.VMKVMInitError(qemu_command, self.process.get_output())
self.destroy()
raise e
if "alloc_mem_area" in output:
- e = VMHugePageError(qemu_command, self.process.get_output())
+ e = virt_vm.VMHugePageError(qemu_command, self.process.get_output())
self.destroy()
raise e
@@ -1043,10 +757,10 @@ class VM:
# Establish a session with the serial console -- requires a version
# of netcat that supports -U
- self.serial_console = kvm_subprocess.ShellSession(
+ self.serial_console = aexpect.ShellSession(
"nc -U %s" % self.get_serial_console_filename(),
auto_close=False,
- output_func=kvm_utils.log_line,
+ output_func=virt_utils.log_line,
output_params=("serial-%s.log" % name,))
finally:
@@ -1080,7 +794,7 @@ class VM:
logging.debug("Trying to shutdown VM with shell command...")
try:
session = self.login()
- except (kvm_utils.LoginError, VMError), e:
+ except (virt_utils.LoginError, virt_vm.VMError), e:
logging.debug(e)
else:
try:
@@ -1088,7 +802,7 @@ class VM:
session.sendline(self.params.get("shutdown_command"))
logging.debug("Shutdown command sent; waiting for VM "
"to go down...")
- if kvm_utils.wait_for(self.is_dead, 60, 1, 1):
+ if virt_utils.wait_for(self.is_dead, 60, 1, 1):
logging.debug("VM is down")
return
finally:
@@ -1103,16 +817,16 @@ class VM:
logging.warn(e)
else:
# Wait for the VM to be really dead
- if kvm_utils.wait_for(self.is_dead, 5, 0.5, 0.5):
+ if virt_utils.wait_for(self.is_dead, 5, 0.5, 0.5):
logging.debug("VM is down")
return
# If the VM isn't dead yet...
logging.debug("Cannot quit normally; sending a kill to close the "
"deal...")
- kvm_utils.kill_process_tree(self.process.get_pid(), 9)
+ virt_utils.kill_process_tree(self.process.get_pid(), 9)
# Wait for the VM to be really dead
- if kvm_utils.wait_for(self.is_dead, 5, 0.5, 0.5):
+ if virt_utils.wait_for(self.is_dead, 5, 0.5, 0.5):
logging.debug("VM is down")
return
@@ -1159,57 +873,6 @@ class VM:
return self.monitors[0]
- def verify_alive(self):
- """
- Make sure the VM is alive and that the main monitor is responsive.
-
- @raise VMDeadError: If the VM is dead
- @raise: Various monitor exceptions if the monitor is unresponsive
- """
- if self.is_dead():
- raise VMDeadError(self.process.get_status(),
- self.process.get_output())
- if self.monitors:
- self.monitor.verify_responsive()
-
-
- def is_alive(self):
- """
- Return True if the VM is alive and its monitor is responsive.
- """
- return not self.is_dead() and (not self.monitors or
- self.monitor.is_responsive())
-
-
- def is_dead(self):
- """
- Return True if the qemu process is dead.
- """
- return not self.process or not self.process.is_alive()
-
-
- def verify_kernel_crash(self):
- """
- Find kernel crash message on the VM serial console.
-
- @raise: VMDeadKernelCrashError, in case a kernel crash message was
- found.
- """
- data = self.serial_console.get_output()
- match = re.search(r"BUG:.*---\[ end trace .* \]---", data,
- re.DOTALL|re.MULTILINE)
- if match is not None:
- raise VMDeadKernelCrashError(match.group(0))
-
-
- def get_params(self):
- """
- Return the VM's params dict. Most modified params take effect only
- upon VM.create().
- """
- return self.params
-
-
def get_monitor_filename(self, monitor_name):
"""
Return the filename corresponding to a given monitor name.
@@ -1226,20 +889,6 @@ class VM:
self.params.objects("monitors")]
- def get_serial_console_filename(self):
- """
- Return the serial console filename.
- """
- return "/tmp/serial-%s" % self.instance
-
-
- def get_testlog_filename(self):
- """
- Return the testlog filename.
- """
- return "/tmp/testlog-%s" % self.instance
-
-
def get_address(self, index=0):
"""
Return the address of a NIC of the guest, in host space.
@@ -1263,11 +912,11 @@ class VM:
# Get the IP address from the cache
ip = self.address_cache.get(mac)
if not ip:
- raise VMIPAddressMissingError(mac)
+ raise virt_vm.VMIPAddressMissingError(mac)
# Make sure the IP address is assigned to this guest
macs = [self.get_mac_address(i) for i in range(len(nics))]
- if not kvm_utils.verify_ip_address_ownership(ip, macs):
- raise VMAddressVerificationError(mac, ip)
+ if not virt_utils.verify_ip_address_ownership(ip, macs):
+ raise virt_vm.VMAddressVerificationError(mac, ip)
return ip
else:
return "localhost"
@@ -1292,7 +941,7 @@ class VM:
try:
return self.redirs[port]
except KeyError:
- raise VMPortNotRedirectedError(port)
+ raise virt_vm.VMPortNotRedirectedError(port)
def get_peer(self, netid):
@@ -1335,9 +984,9 @@ class VM:
nic_name = self.params.objects("nics")[nic_index]
nic_params = self.params.object_params(nic_name)
mac = (nic_params.get("nic_mac") or
- kvm_utils.get_mac_address(self.instance, nic_index))
+ virt_utils.get_mac_address(self.instance, nic_index))
if not mac:
- raise VMMACAddressMissingError(nic_index)
+ raise virt_vm.VMMACAddressMissingError(nic_index)
return mac
@@ -1347,7 +996,7 @@ class VM:
@param nic_index: Index of the NIC
"""
- kvm_utils.free_mac_address(self.instance, nic_index)
+ virt_utils.free_mac_address(self.instance, nic_index)
def get_pid(self):
@@ -1392,168 +1041,6 @@ class VM:
@error.context_aware
- def login(self, nic_index=0, timeout=10):
- """
- Log into the guest via SSH/Telnet/Netcat.
- If timeout expires while waiting for output from the guest (e.g. a
- password prompt or a shell prompt) -- fail.
-
- @param nic_index: The index of the NIC to connect to.
- @param timeout: Time (seconds) before giving up logging into the
- guest.
- @return: A ShellSession object.
- """
- error.context("logging into '%s'" % self.name)
- username = self.params.get("username", "")
- password = self.params.get("password", "")
- prompt = self.params.get("shell_prompt", "[\#\$]")
- linesep = eval("'%s'" % self.params.get("shell_linesep", r"\n"))
- client = self.params.get("shell_client")
- address = self.get_address(nic_index)
- port = self.get_port(int(self.params.get("shell_port")))
- log_filename = ("session-%s-%s.log" %
- (self.name, kvm_utils.generate_random_string(4)))
- session = kvm_utils.remote_login(client, address, port, username,
- password, prompt, linesep,
- log_filename, timeout)
- session.set_status_test_command(self.params.get("status_test_command",
- ""))
- return session
-
-
- def remote_login(self, nic_index=0, timeout=10):
- """
- Alias for login() for backward compatibility.
- """
- return self.login(nic_index, timeout)
-
-
- def wait_for_login(self, nic_index=0, timeout=240, internal_timeout=10):
- """
- Make multiple attempts to log into the guest via SSH/Telnet/Netcat.
-
- @param nic_index: The index of the NIC to connect to.
- @param timeout: Time (seconds) to keep trying to log in.
- @param internal_timeout: Timeout to pass to login().
- @return: A ShellSession object.
- """
- logging.debug("Attempting to log into '%s' (timeout %ds)", self.name,
- timeout)
- end_time = time.time() + timeout
- while time.time() < end_time:
- try:
- return self.login(nic_index, internal_timeout)
- except (kvm_utils.LoginError, VMError), e:
- logging.debug(e)
- time.sleep(2)
- # Timeout expired; try one more time but don't catch exceptions
- return self.login(nic_index, internal_timeout)
-
-
- @error.context_aware
- def copy_files_to(self, host_path, guest_path, nic_index=0, verbose=False,
- timeout=600):
- """
- Transfer files to the remote host(guest).
-
- @param host_path: Host path
- @param guest_path: Guest path
- @param nic_index: The index of the NIC to connect to.
- @param verbose: If True, log some stats using logging.debug (RSS only)
- @param timeout: Time (seconds) before giving up on doing the remote
- copy.
- """
- error.context("sending file(s) to '%s'" % self.name)
- username = self.params.get("username", "")
- password = self.params.get("password", "")
- client = self.params.get("file_transfer_client")
- address = self.get_address(nic_index)
- port = self.get_port(int(self.params.get("file_transfer_port")))
- log_filename = ("transfer-%s-to-%s-%s.log" %
- (self.name, address,
- kvm_utils.generate_random_string(4)))
- kvm_utils.copy_files_to(address, client, username, password, port,
- host_path, guest_path, log_filename, verbose,
- timeout)
-
-
- @error.context_aware
- def copy_files_from(self, guest_path, host_path, nic_index=0,
- verbose=False, timeout=600):
- """
- Transfer files from the guest.
-
- @param host_path: Guest path
- @param guest_path: Host path
- @param nic_index: The index of the NIC to connect to.
- @param verbose: If True, log some stats using logging.debug (RSS only)
- @param timeout: Time (seconds) before giving up on doing the remote
- copy.
- """
- error.context("receiving file(s) from '%s'" % self.name)
- username = self.params.get("username", "")
- password = self.params.get("password", "")
- client = self.params.get("file_transfer_client")
- address = self.get_address(nic_index)
- port = self.get_port(int(self.params.get("file_transfer_port")))
- log_filename = ("transfer-%s-from-%s-%s.log" %
- (self.name, address,
- kvm_utils.generate_random_string(4)))
- kvm_utils.copy_files_from(address, client, username, password, port,
- guest_path, host_path, log_filename,
- verbose, timeout)
-
-
- @error.context_aware
- def serial_login(self, timeout=10):
- """
- Log into the guest via the serial console.
- If timeout expires while waiting for output from the guest (e.g. a
- password prompt or a shell prompt) -- fail.
-
- @param timeout: Time (seconds) before giving up logging into the guest.
- @return: ShellSession object on success and None on failure.
- """
- error.context("logging into '%s' via serial console" % self.name)
- username = self.params.get("username", "")
- password = self.params.get("password", "")
- prompt = self.params.get("shell_prompt", "[\#\$]")
- linesep = eval("'%s'" % self.params.get("shell_linesep", r"\n"))
- status_test_command = self.params.get("status_test_command", "")
-
- self.serial_console.set_linesep(linesep)
- self.serial_console.set_status_test_command(status_test_command)
-
- # Try to get a login prompt
- self.serial_console.sendline()
-
- kvm_utils._remote_login(self.serial_console, username, password,
- prompt, timeout)
- return self.serial_console
-
-
- def wait_for_serial_login(self, timeout=240, internal_timeout=10):
- """
- Make multiple attempts to log into the guest via serial console.
-
- @param timeout: Time (seconds) to keep trying to log in.
- @param internal_timeout: Timeout to pass to serial_login().
- @return: A ShellSession object.
- """
- logging.debug("Attempting to log into '%s' via serial console "
- "(timeout %ds)", self.name, timeout)
- end_time = time.time() + timeout
- while time.time() < end_time:
- try:
- return self.serial_login(internal_timeout)
- except kvm_utils.LoginError, e:
- logging.debug(e)
- time.sleep(2)
- # Timeout expired; try one more time but don't catch exceptions
- return self.serial_login(internal_timeout)
-
-
- @error.context_aware
def migrate(self, timeout=3600, protocol="tcp", cancel_delay=None,
offline=False, stable_check=False, clean=True,
save_path="/tmp", dest_host="localhost", remote_port=None):
@@ -1565,7 +1052,7 @@ class VM:
a dead VM (returned by self.clone()).
@param timeout: Time to wait for migration to complete.
- @param protocol: Migration protocol ('tcp', 'unix' or 'exec').
+ @param protocol: Migration protocol (as defined in MIGRATION_PROTOS)
@param cancel_delay: If provided, specifies a time duration after which
migration will be canceled. Used for testing migrate_cancel.
@param offline: If True, pause the source VM before migration.
@@ -1578,6 +1065,9 @@ class VM:
@param dest_host: Destination host (defaults to 'localhost').
@param remote_port: Port to use for remote migration.
"""
+ if protocol not in self.MIGRATION_PROTOS:
+ raise virt_vm.VMMigrateProtoUnsupportedError
+
error.base_context("migrating '%s'" % self.name)
def mig_finished():
@@ -1611,9 +1101,9 @@ class VM:
o.get("status") == "canceled")
def wait_for_migration():
- if not kvm_utils.wait_for(mig_finished, timeout, 2, 2,
+ if not virt_utils.wait_for(mig_finished, timeout, 2, 2,
"Waiting for migration to complete"):
- raise VMMigrateTimeoutError("Timeout expired while waiting "
+ raise virt_vm.VMMigrateTimeoutError("Timeout expired while waiting "
"for migration to finish")
local = dest_host == "localhost"
@@ -1648,10 +1138,10 @@ class VM:
if cancel_delay:
time.sleep(cancel_delay)
self.monitor.cmd("migrate_cancel")
- if not kvm_utils.wait_for(mig_cancelled, 60, 2, 2,
+ if not virt_utils.wait_for(mig_cancelled, 60, 2, 2,
"Waiting for migration "
"cancellation"):
- raise VMMigrateCancelError("Cannot cancel migration")
+ raise virt_vm.VMMigrateCancelError("Cannot cancel migration")
return
wait_for_migration()
@@ -1660,10 +1150,10 @@ class VM:
if mig_succeeded():
logging.info("Migration completed successfully")
elif mig_failed():
- raise VMMigrateFailedError("Migration failed")
+ raise virt_vm.VMMigrateFailedError("Migration failed")
else:
- raise VMMigrateFailedError("Migration ended with unknown "
- "status")
+ raise virt_vm.VMMigrateFailedError("Migration ended with "
+ "unknown status")
# Switch self <-> clone
temp = self.clone(copy_state=True)
@@ -1678,7 +1168,6 @@ class VM:
if local:
time.sleep(1)
self.verify_alive()
- self.verify_kernel_crash()
if local and stable_check:
try:
@@ -1690,7 +1179,8 @@ class VM:
md5_save1 = utils.hash_file(save1)
md5_save2 = utils.hash_file(save2)
if md5_save1 != md5_save2:
- raise VMMigrateStateMismatchError(md5_save1, md5_save2)
+ raise virt_vm.VMMigrateStateMismatchError(md5_save1,
+ md5_save2)
finally:
if clean:
if os.path.isfile(save1):
@@ -1740,16 +1230,17 @@ class VM:
if m.get_event("RESET"):
logging.info("RESET QMP event received")
else:
- raise VMRebootError("RESET QMP event not received after "
- "system_reset (monitor '%s')" % m.name)
+ raise virt_vm.VMRebootError("RESET QMP event not received "
+ "after system_reset "
+ "(monitor '%s')" % m.name)
else:
- raise VMRebootError("Unknown reboot method: %s" % method)
+ raise virt_vm.VMRebootError("Unknown reboot method: %s" % method)
error.context("waiting for guest to go down", logging.info)
- if not kvm_utils.wait_for(lambda:
+ if not virt_utils.wait_for(lambda:
not session.is_responsive(timeout=30),
120, 0, 1):
- raise VMRebootError("Guest refuses to go down")
+ raise virt_vm.VMRebootError("Guest refuses to go down")
session.close()
error.context("logging in after reboot", logging.info)
@@ -1774,76 +1265,13 @@ class VM:
time.sleep(0.2)
- def send_string(self, str):
- """
- Send a string to the VM.
-
- @param str: String, that must consist of alphanumeric characters only.
- Capital letters are allowed.
- """
- for char in str:
- if char.isupper():
- self.send_key("shift-%s" % char.lower())
- else:
- self.send_key(char)
-
-
- def get_uuid(self):
- """
- Catch UUID of the VM.
-
- @return: None,if not specified in config file
- """
- if self.params.get("uuid") == "random":
- return self.uuid
- else:
- return self.params.get("uuid", None)
-
-
- def get_cpu_count(self):
- """
- Get the cpu count of the VM.
- """
- session = self.login()
- try:
- return int(session.cmd(self.params.get("cpu_chk_cmd")))
- finally:
- session.close()
-
-
- def get_memory_size(self, cmd=None):
- """
- Get bootup memory size of the VM.
-
- @param check_cmd: Command used to check memory. If not provided,
- self.params.get("mem_chk_cmd") will be used.
- """
- session = self.login()
+ # should this really be expected from VMs of all hypervisor types?
+ def screendump(self, filename):
try:
- if not cmd:
- cmd = self.params.get("mem_chk_cmd")
- mem_str = session.cmd(cmd)
- mem = re.findall("([0-9]+)", mem_str)
- mem_size = 0
- for m in mem:
- mem_size += int(m)
- if "GB" in mem_str:
- mem_size *= 1024
- elif "MB" in mem_str:
- pass
- else:
- mem_size /= 1024
- return int(mem_size)
- finally:
- session.close()
-
-
- def get_current_memory_size(self):
- """
- Get current memory size of the VM, rather than bootup memory.
- """
- cmd = self.params.get("mem_chk_cur_cmd")
- return self.get_memory_size(cmd)
+ if self.monitor:
+ self.monitor.screendump(filename=filename)
+ except kvm_monitor.MonitorError, e:
+ logging.warn(e)
def save_to_file(self, path):
@@ -1858,3 +1286,12 @@ class VM:
# Restore the speed and downtime of migration
self.monitor.cmd("migrate_set_speed %d" % (32<<20))
self.monitor.cmd("migrate_set_downtime 0.03")
+
+
+ def needs_restart(self, name, params, basedir):
+ """
+ Verifies whether the current qemu commandline matches the requested
+ one, based on the test parameters.
+ """
+ return (self.__make_qemu_command() !=
+ self.__make_qemu_command(name, params, basedir))
« no previous file with comments | « client/virt/kvm_monitor.py ('k') | client/virt/ppm_utils.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698