virt.virt_test_utils: run_autotest - 'tar' needs relative paths to strip the leading '/'
[autotest-zwu.git] / client / virt / virt_vm.py
blob8815bf40cd2f835393339f894d3e625edad111e6
1 import os, logging, time, glob, re
2 from autotest_lib.client.common_lib import error
3 from autotest_lib.client.bin import utils
4 import virt_utils
6 class VMError(Exception):
7 pass
10 class VMCreateError(VMError):
11 def __init__(self, cmd, status, output):
12 VMError.__init__(self, cmd, status, output)
13 self.cmd = cmd
14 self.status = status
15 self.output = output
17 def __str__(self):
18 return ("VM creation command failed: %r (status: %s, "
19 "output: %r)" % (self.cmd, self.status, self.output))
22 class VMHashMismatchError(VMError):
23 def __init__(self, actual, expected):
24 VMError.__init__(self, actual, expected)
25 self.actual_hash = actual
26 self.expected_hash = expected
28 def __str__(self):
29 return ("CD image hash (%s) differs from expected one (%s)" %
30 (self.actual_hash, self.expected_hash))
33 class VMImageMissingError(VMError):
34 def __init__(self, filename):
35 VMError.__init__(self, filename)
36 self.filename = filename
38 def __str__(self):
39 return "CD image file not found: %r" % self.filename
42 class VMImageCheckError(VMError):
43 def __init__(self, filename):
44 VMError.__init__(self, filename)
45 self.filename = filename
47 def __str__(self):
48 return "Errors found on image: %r" % self.filename
51 class VMBadPATypeError(VMError):
52 def __init__(self, pa_type):
53 VMError.__init__(self, pa_type)
54 self.pa_type = pa_type
56 def __str__(self):
57 return "Unsupported PCI assignable type: %r" % self.pa_type
60 class VMPAError(VMError):
61 def __init__(self, pa_type):
62 VMError.__init__(self, pa_type)
63 self.pa_type = pa_type
65 def __str__(self):
66 return ("No PCI assignable devices could be assigned "
67 "(pci_assignable=%r)" % self.pa_type)
70 class VMPostCreateError(VMError):
71 def __init__(self, cmd, output):
72 VMError.__init__(self, cmd, output)
73 self.cmd = cmd
74 self.output = output
77 class VMHugePageError(VMPostCreateError):
78 def __str__(self):
79 return ("Cannot allocate hugepage memory (command: %r, "
80 "output: %r)" % (self.cmd, self.output))
83 class VMKVMInitError(VMPostCreateError):
84 def __str__(self):
85 return ("Cannot initialize KVM (command: %r, output: %r)" %
86 (self.cmd, self.output))
89 class VMDeadError(VMError):
90 def __init__(self, reason='', detail=''):
91 VMError.__init__(self)
92 self.reason = reason
93 self.detail = detail
95 def __str__(self):
96 msg = "VM is dead"
97 if self.reason:
98 msg += " reason: %s" % self.reason
99 if self.detail:
100 msg += " detail: %r" % self.detail
101 return (msg)
104 class VMDeadKernelCrashError(VMError):
105 def __init__(self, kernel_crash):
106 VMError.__init__(self, kernel_crash)
107 self.kernel_crash = kernel_crash
109 def __str__(self):
110 return ("VM is dead due to a kernel crash:\n%s" % self.kernel_crash)
113 class VMAddressError(VMError):
114 pass
117 class VMPortNotRedirectedError(VMAddressError):
118 def __init__(self, port):
119 VMAddressError.__init__(self, port)
120 self.port = port
122 def __str__(self):
123 return "Port not redirected: %s" % self.port
126 class VMAddressVerificationError(VMAddressError):
127 def __init__(self, mac, ip):
128 VMAddressError.__init__(self, mac, ip)
129 self.mac = mac
130 self.ip = ip
132 def __str__(self):
133 return ("Cannot verify MAC-IP address mapping using arping: "
134 "%s ---> %s" % (self.mac, self.ip))
137 class VMMACAddressMissingError(VMAddressError):
138 def __init__(self, nic_index):
139 VMAddressError.__init__(self, nic_index)
140 self.nic_index = nic_index
142 def __str__(self):
143 return "No MAC address defined for NIC #%s" % self.nic_index
146 class VMIPAddressMissingError(VMAddressError):
147 def __init__(self, mac):
148 VMAddressError.__init__(self, mac)
149 self.mac = mac
151 def __str__(self):
152 return "Cannot find IP address for MAC address %s" % self.mac
155 class VMMigrateError(VMError):
156 pass
159 class VMMigrateTimeoutError(VMMigrateError):
160 pass
163 class VMMigrateCancelError(VMMigrateError):
164 pass
167 class VMMigrateFailedError(VMMigrateError):
168 pass
170 class VMMigrateProtoUnsupportedError(VMMigrateError):
171 pass
174 class VMMigrateStateMismatchError(VMMigrateError):
175 def __init__(self, src_hash, dst_hash):
176 VMMigrateError.__init__(self, src_hash, dst_hash)
177 self.src_hash = src_hash
178 self.dst_hash = dst_hash
180 def __str__(self):
181 return ("Mismatch of VM state before and after migration (%s != %s)" %
182 (self.src_hash, self.dst_hash))
185 class VMRebootError(VMError):
186 pass
188 class VMStatusError(VMError):
189 pass
191 def get_image_filename(params, root_dir):
193 Generate an image path from params and root_dir.
195 @param params: Dictionary containing the test parameters.
196 @param root_dir: Base directory for relative filenames.
198 @note: params should contain:
199 image_name -- the name of the image file, without extension
200 image_format -- the format of the image (qcow2, raw etc)
202 image_name = params.get("image_name", "image")
203 image_format = params.get("image_format", "qcow2")
204 if params.get("image_raw_device") == "yes":
205 return image_name
206 image_filename = "%s.%s" % (image_name, image_format)
207 image_filename = virt_utils.get_path(root_dir, image_filename)
208 return image_filename
211 def create_image(params, root_dir):
213 Create an image using qemu_image.
215 @param params: Dictionary containing the test parameters.
216 @param root_dir: Base directory for relative filenames.
218 @note: params should contain:
219 image_name -- the name of the image file, without extension
220 image_format -- the format of the image (qcow2, raw etc)
221 image_cluster_size (optional) -- the cluster size for the image
222 image_size -- the requested size of the image (a string
223 qemu-img can understand, such as '10G')
225 qemu_img_cmd = virt_utils.get_path(root_dir, params.get("qemu_img_binary",
226 "qemu-img"))
227 qemu_img_cmd += " create"
229 format = params.get("image_format", "qcow2")
230 qemu_img_cmd += " -f %s" % format
232 image_cluster_size = params.get("image_cluster_size", None)
233 if image_cluster_size is not None:
234 qemu_img_cmd += " -o cluster_size=%s" % image_cluster_size
236 image_filename = get_image_filename(params, root_dir)
237 qemu_img_cmd += " %s" % image_filename
239 size = params.get("image_size", "10G")
240 qemu_img_cmd += " %s" % size
242 utils.system(qemu_img_cmd)
243 return image_filename
246 def remove_image(params, root_dir):
248 Remove an image file.
250 @param params: A dict
251 @param root_dir: Base directory for relative filenames.
253 @note: params should contain:
254 image_name -- the name of the image file, without extension
255 image_format -- the format of the image (qcow2, raw etc)
257 image_filename = get_image_filename(params, root_dir)
258 logging.debug("Removing image file %s", image_filename)
259 if os.path.exists(image_filename):
260 os.unlink(image_filename)
261 else:
262 logging.debug("Image file %s not found")
265 def check_image(params, root_dir):
267 Check an image using the appropriate tools for each virt backend.
269 @param params: Dictionary containing the test parameters.
270 @param root_dir: Base directory for relative filenames.
272 @note: params should contain:
273 image_name -- the name of the image file, without extension
274 image_format -- the format of the image (qcow2, raw etc)
276 @raise VMImageCheckError: In case qemu-img check fails on the image.
278 vm_type = params.get("vm_type")
279 if vm_type == 'kvm':
280 image_filename = get_image_filename(params, root_dir)
281 logging.debug("Checking image file %s", image_filename)
282 qemu_img_cmd = virt_utils.get_path(root_dir,
283 params.get("qemu_img_binary", "qemu-img"))
284 image_is_qcow2 = params.get("image_format") == 'qcow2'
285 if os.path.exists(image_filename) and image_is_qcow2:
286 # Verifying if qemu-img supports 'check'
287 q_result = utils.run(qemu_img_cmd, ignore_status=True)
288 q_output = q_result.stdout
289 check_img = True
290 if not "check" in q_output:
291 logging.error("qemu-img does not support 'check', "
292 "skipping check")
293 check_img = False
294 if not "info" in q_output:
295 logging.error("qemu-img does not support 'info', "
296 "skipping check")
297 check_img = False
298 if check_img:
299 try:
300 utils.system("%s info %s" % (qemu_img_cmd, image_filename))
301 except error.CmdError:
302 logging.error("Error getting info from image %s",
303 image_filename)
305 cmd_result = utils.run("%s check %s" %
306 (qemu_img_cmd, image_filename),
307 ignore_status=True)
308 # Error check, large chances of a non-fatal problem.
309 # There are chances that bad data was skipped though
310 if cmd_result.exit_status == 1:
311 for e_line in cmd_result.stdout.splitlines():
312 logging.error("[stdout] %s", e_line)
313 for e_line in cmd_result.stderr.splitlines():
314 logging.error("[stderr] %s", e_line)
315 raise error.TestWarn("qemu-img check error. Some bad data "
316 "in the image may have gone unnoticed")
317 # Exit status 2 is data corruption for sure, so fail the test
318 elif cmd_result.exit_status == 2:
319 for e_line in cmd_result.stdout.splitlines():
320 logging.error("[stdout] %s", e_line)
321 for e_line in cmd_result.stderr.splitlines():
322 logging.error("[stderr] %s", e_line)
323 raise VMImageCheckError(image_filename)
324 # Leaked clusters, they are known to be harmless to data
325 # integrity
326 elif cmd_result.exit_status == 3:
327 raise error.TestWarn("Leaked clusters were noticed during "
328 "image check. No data integrity "
329 "problem was found though.")
331 else:
332 if not os.path.exists(image_filename):
333 logging.debug("Image file %s not found, skipping check",
334 image_filename)
335 elif not image_is_qcow2:
336 logging.debug("Image file %s not qcow2, skipping check",
337 image_filename)
340 class BaseVM(object):
342 Base class for all hypervisor specific VM subclasses.
344 This class should not be used directly, that is, do not attempt to
345 instantiate and use this class. Instead, one should implement a subclass
346 that implements, at the very least, all methods defined right after the
347 the comment blocks that are marked with:
349 "Public API - *must* be reimplemented with virt specific code"
353 "Protected API - *must* be reimplemented with virt specific classes"
355 The current proposal regarding methods naming convention is:
357 - Public API methods: named in the usual way, consumed by tests
358 - Protected API methods: name begins with a single underline, to be
359 consumed only by BaseVM and subclasses
360 - Private API methods: name begins with double underline, to be consumed
361 only by the VM subclass itself (usually implements virt specific
362 functionality: example: __make_qemu_command())
364 So called "protected" methods are intended to be used only by VM classes,
365 and not be consumed by tests. Theses should respect a naming convention
366 and always be preceeded by a single underline.
368 Currently most (if not all) methods are public and appears to be consumed
369 by tests. It is a ongoing task to determine whether methods should be
370 "public" or "protected".
374 # Assuming that all low-level hypervisor have at least migration via tcp
375 # (true for xen & kvm). Also true for libvirt (using xen and kvm drivers)
377 MIGRATION_PROTOS = ['tcp', ]
379 def __init__(self, name, params):
380 self.name = name
381 self.params = params
384 # Assuming all low-level hypervisors will have a serial (like) console
385 # connection to the guest. libvirt also supports serial (like) consoles
386 # (virDomainOpenConsole). subclasses should set this to an object that
387 # is or behaves like aexpect.ShellSession.
389 self.serial_console = None
391 self._generate_unique_id()
394 def _generate_unique_id(self):
396 Generate a unique identifier for this VM
398 while True:
399 self.instance = (time.strftime("%Y%m%d-%H%M%S-") +
400 virt_utils.generate_random_string(4))
401 if not glob.glob("/tmp/*%s" % self.instance):
402 break
406 # Public API - could be reimplemented with virt specific code
408 def verify_alive(self):
410 Make sure the VM is alive and that the main monitor is responsive.
412 Can be subclassed to provide better information on why the VM is
413 not alive (reason, detail)
415 @raise VMDeadError: If the VM is dead
416 @raise: Various monitor exceptions if the monitor is unresponsive
418 if self.is_dead():
419 raise VMDeadError
422 def get_mac_address(self, nic_index=0):
424 Return the MAC address of a NIC.
426 @param nic_index: Index of the NIC
427 @raise VMMACAddressMissingError: If no MAC address is defined for the
428 requested NIC
430 nic_name = self.params.objects("nics")[nic_index]
431 nic_params = self.params.object_params(nic_name)
432 mac = (nic_params.get("nic_mac") or
433 virt_utils.get_mac_address(self.instance, nic_index))
434 if not mac:
435 raise VMMACAddressMissingError(nic_index)
436 return mac
439 def verify_kernel_crash(self):
441 Find kernel crash message on the VM serial console.
443 @raise: VMDeadKernelCrashError, in case a kernel crash message was
444 found.
446 if self.serial_console is not None:
447 data = self.serial_console.get_output()
448 match = re.search(r"BUG:.*---\[ end trace .* \]---", data,
449 re.DOTALL|re.MULTILINE)
450 if match is not None:
451 raise VMDeadKernelCrashError(match.group(0))
454 def get_params(self):
456 Return the VM's params dict. Most modified params take effect only
457 upon VM.create().
459 return self.params
462 def get_serial_console_filename(self):
464 Return the serial console filename.
466 return "/tmp/serial-%s" % self.instance
469 def get_testlog_filename(self):
471 Return the testlog filename.
473 return "/tmp/testlog-%s" % self.instance
476 @error.context_aware
477 def login(self, nic_index=0, timeout=10):
479 Log into the guest via SSH/Telnet/Netcat.
480 If timeout expires while waiting for output from the guest (e.g. a
481 password prompt or a shell prompt) -- fail.
483 @param nic_index: The index of the NIC to connect to.
484 @param timeout: Time (seconds) before giving up logging into the
485 guest.
486 @return: A ShellSession object.
488 error.context("logging into '%s'" % self.name)
489 username = self.params.get("username", "")
490 password = self.params.get("password", "")
491 prompt = self.params.get("shell_prompt", "[\#\$]")
492 linesep = eval("'%s'" % self.params.get("shell_linesep", r"\n"))
493 client = self.params.get("shell_client")
494 address = self.get_address(nic_index)
495 port = self.get_port(int(self.params.get("shell_port")))
496 log_filename = ("session-%s-%s.log" %
497 (self.name, virt_utils.generate_random_string(4)))
498 session = virt_utils.remote_login(client, address, port, username,
499 password, prompt, linesep,
500 log_filename, timeout)
501 session.set_status_test_command(self.params.get("status_test_command",
502 ""))
503 return session
506 def remote_login(self, nic_index=0, timeout=10):
508 Alias for login() for backward compatibility.
510 return self.login(nic_index, timeout)
513 def wait_for_login(self, nic_index=0, timeout=240, internal_timeout=10):
515 Make multiple attempts to log into the guest via SSH/Telnet/Netcat.
517 @param nic_index: The index of the NIC to connect to.
518 @param timeout: Time (seconds) to keep trying to log in.
519 @param internal_timeout: Timeout to pass to login().
520 @return: A ShellSession object.
522 logging.debug("Attempting to log into '%s' (timeout %ds)", self.name,
523 timeout)
524 end_time = time.time() + timeout
525 while time.time() < end_time:
526 try:
527 return self.login(nic_index, internal_timeout)
528 except (virt_utils.LoginError, VMError), e:
529 logging.debug(e)
530 time.sleep(2)
531 # Timeout expired; try one more time but don't catch exceptions
532 return self.login(nic_index, internal_timeout)
535 @error.context_aware
536 def copy_files_to(self, host_path, guest_path, nic_index=0, verbose=False,
537 timeout=600):
539 Transfer files to the remote host(guest).
541 @param host_path: Host path
542 @param guest_path: Guest path
543 @param nic_index: The index of the NIC to connect to.
544 @param verbose: If True, log some stats using logging.debug (RSS only)
545 @param timeout: Time (seconds) before giving up on doing the remote
546 copy.
548 error.context("sending file(s) to '%s'" % self.name)
549 username = self.params.get("username", "")
550 password = self.params.get("password", "")
551 client = self.params.get("file_transfer_client")
552 address = self.get_address(nic_index)
553 port = self.get_port(int(self.params.get("file_transfer_port")))
554 log_filename = ("transfer-%s-to-%s-%s.log" %
555 (self.name, address,
556 virt_utils.generate_random_string(4)))
557 virt_utils.copy_files_to(address, client, username, password, port,
558 host_path, guest_path, log_filename, verbose,
559 timeout)
562 @error.context_aware
563 def copy_files_from(self, guest_path, host_path, nic_index=0,
564 verbose=False, timeout=600):
566 Transfer files from the guest.
568 @param host_path: Guest path
569 @param guest_path: Host path
570 @param nic_index: The index of the NIC to connect to.
571 @param verbose: If True, log some stats using logging.debug (RSS only)
572 @param timeout: Time (seconds) before giving up on doing the remote
573 copy.
575 error.context("receiving file(s) from '%s'" % self.name)
576 username = self.params.get("username", "")
577 password = self.params.get("password", "")
578 client = self.params.get("file_transfer_client")
579 address = self.get_address(nic_index)
580 port = self.get_port(int(self.params.get("file_transfer_port")))
581 log_filename = ("transfer-%s-from-%s-%s.log" %
582 (self.name, address,
583 virt_utils.generate_random_string(4)))
584 virt_utils.copy_files_from(address, client, username, password, port,
585 guest_path, host_path, log_filename,
586 verbose, timeout)
589 @error.context_aware
590 def serial_login(self, timeout=10):
592 Log into the guest via the serial console.
593 If timeout expires while waiting for output from the guest (e.g. a
594 password prompt or a shell prompt) -- fail.
596 @param timeout: Time (seconds) before giving up logging into the guest.
597 @return: ShellSession object on success and None on failure.
599 error.context("logging into '%s' via serial console" % self.name)
600 username = self.params.get("username", "")
601 password = self.params.get("password", "")
602 prompt = self.params.get("shell_prompt", "[\#\$]")
603 linesep = eval("'%s'" % self.params.get("shell_linesep", r"\n"))
604 status_test_command = self.params.get("status_test_command", "")
606 self.serial_console.set_linesep(linesep)
607 self.serial_console.set_status_test_command(status_test_command)
609 # Try to get a login prompt
610 self.serial_console.sendline()
612 virt_utils._remote_login(self.serial_console, username, password,
613 prompt, timeout)
614 return self.serial_console
617 def wait_for_serial_login(self, timeout=240, internal_timeout=10):
619 Make multiple attempts to log into the guest via serial console.
621 @param timeout: Time (seconds) to keep trying to log in.
622 @param internal_timeout: Timeout to pass to serial_login().
623 @return: A ShellSession object.
625 logging.debug("Attempting to log into '%s' via serial console "
626 "(timeout %ds)", self.name, timeout)
627 end_time = time.time() + timeout
628 while time.time() < end_time:
629 try:
630 return self.serial_login(internal_timeout)
631 except virt_utils.LoginError, e:
632 logging.debug(e)
633 time.sleep(2)
634 # Timeout expired; try one more time but don't catch exceptions
635 return self.serial_login(internal_timeout)
638 def get_uuid(self):
640 Catch UUID of the VM.
642 @return: None,if not specified in config file
644 if self.params.get("uuid") == "random":
645 return self.uuid
646 else:
647 return self.params.get("uuid", None)
650 def send_string(self, str):
652 Send a string to the VM.
654 @param str: String, that must consist of alphanumeric characters only.
655 Capital letters are allowed.
657 for char in str:
658 if char.isupper():
659 self.send_key("shift-%s" % char.lower())
660 else:
661 self.send_key(char)
664 def get_cpu_count(self):
666 Get the cpu count of the VM.
668 session = self.login()
669 try:
670 return int(session.cmd(self.params.get("cpu_chk_cmd")))
671 finally:
672 session.close()
675 def get_memory_size(self, cmd=None):
677 Get bootup memory size of the VM.
679 @param check_cmd: Command used to check memory. If not provided,
680 self.params.get("mem_chk_cmd") will be used.
682 session = self.login()
683 try:
684 if not cmd:
685 cmd = self.params.get("mem_chk_cmd")
686 mem_str = session.cmd(cmd)
687 mem = re.findall("([0-9]+)", mem_str)
688 mem_size = 0
689 for m in mem:
690 mem_size += int(m)
691 if "GB" in mem_str:
692 mem_size *= 1024
693 elif "MB" in mem_str:
694 pass
695 else:
696 mem_size /= 1024
697 return int(mem_size)
698 finally:
699 session.close()
702 def get_current_memory_size(self):
704 Get current memory size of the VM, rather than bootup memory.
706 cmd = self.params.get("mem_chk_cur_cmd")
707 return self.get_memory_size(cmd)
711 # Public API - *must* be reimplemented with virt specific code
713 def is_alive(self):
715 Return True if the VM is alive and the management interface is responsive.
717 raise NotImplementedError
720 def is_dead(self):
722 Return True if the the VM is dead.
724 raise NotImplementedError
727 def get_address(self, index=0):
729 Return the IP address of a NIC of the guest
731 @param index: Index of the NIC whose address is requested.
732 @raise VMMACAddressMissingError: If no MAC address is defined for the
733 requested NIC
734 @raise VMIPAddressMissingError: If no IP address is found for the the
735 NIC's MAC address
736 @raise VMAddressVerificationError: If the MAC-IP address mapping cannot
737 be verified (using arping)
739 raise NotImplementedError
742 def clone(self, name, **params):
744 Return a clone of the VM object with optionally modified parameters.
746 This method should be implemented by
748 raise NotImplementedError
751 def destroy(self, gracefully=True, free_mac_addresses=True):
753 Destroy the VM.
755 If gracefully is True, first attempt to shutdown the VM with a shell
756 command. Then, attempt to destroy the VM via the monitor with a 'quit'
757 command. If that fails, send SIGKILL to the qemu process.
759 @param gracefully: If True, an attempt will be made to end the VM
760 using a shell command before trying to end the qemu process
761 with a 'quit' or a kill signal.
762 @param free_mac_addresses: If True, the MAC addresses used by the VM
763 will be freed.
765 raise NotImplementedError
768 def migrate(self, timeout=3600, protocol="tcp", cancel_delay=None,
769 offline=False, stable_check=False, clean=True,
770 save_path="/tmp", dest_host="localhost", remote_port=None):
772 Migrate the VM.
774 If the migration is local, the VM object's state is switched with that
775 of the destination VM. Otherwise, the state is switched with that of
776 a dead VM (returned by self.clone()).
778 @param timeout: Time to wait for migration to complete.
779 @param protocol: Migration protocol ('tcp', 'unix' or 'exec').
780 @param cancel_delay: If provided, specifies a time duration after which
781 migration will be canceled. Used for testing migrate_cancel.
782 @param offline: If True, pause the source VM before migration.
783 @param stable_check: If True, compare the VM's state after migration to
784 its state before migration and raise an exception if they
785 differ.
786 @param clean: If True, delete the saved state files (relevant only if
787 stable_check is also True).
788 @save_path: The path for state files.
789 @param dest_host: Destination host (defaults to 'localhost').
790 @param remote_port: Port to use for remote migration.
792 raise NotImplementedError
795 def reboot(self, session=None, method="shell", nic_index=0, timeout=240):
797 Reboot the VM and wait for it to come back up by trying to log in until
798 timeout expires.
800 @param session: A shell session object or None.
801 @param method: Reboot method. Can be "shell" (send a shell reboot
802 command) or "system_reset" (send a system_reset monitor command).
803 @param nic_index: Index of NIC to access in the VM, when logging in
804 after rebooting.
805 @param timeout: Time to wait for login to succeed (after rebooting).
806 @return: A new shell session object.
808 raise NotImplementedError
811 # should this really be expected from VMs of all hypervisor types?
812 def send_key(self, keystr):
814 Send a key event to the VM.
816 @param: keystr: A key event string (e.g. "ctrl-alt-delete")
818 raise NotImplementedError
821 def save_to_file(self, path):
823 Save the state of virtual machine to a file through migrate to
824 exec
826 raise NotImplementedError
829 def needs_restart(self, name, params, basedir):
831 Based on virt preprocessing information, decide whether the VM needs
832 a restart.
834 raise NotImplementedError