OLD | NEW |
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 """ | 2 """ |
3 Utility classes and functions to handle Virtual Machine creation using qemu. | 3 Utility classes and functions to handle Virtual Machine creation using qemu. |
4 | 4 |
5 @copyright: 2008-2009 Red Hat Inc. | 5 @copyright: 2008-2009 Red Hat Inc. |
6 """ | 6 """ |
7 | 7 |
8 import time, os, logging, fcntl, re, commands, glob | 8 import time, os, logging, fcntl, re, commands, glob |
9 import kvm_utils, kvm_subprocess, kvm_monitor | 9 import kvm_utils, kvm_subprocess, kvm_monitor |
10 from autotest_lib.client.common_lib import error | 10 from autotest_lib.client.common_lib import error |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
98 def __init__(self, status, output): | 98 def __init__(self, status, output): |
99 VMError.__init__(self, status, output) | 99 VMError.__init__(self, status, output) |
100 self.status = status | 100 self.status = status |
101 self.output = output | 101 self.output = output |
102 | 102 |
103 def __str__(self): | 103 def __str__(self): |
104 return ("VM process is dead (status: %s, output: %r)" % | 104 return ("VM process is dead (status: %s, output: %r)" % |
105 (self.status, self.output)) | 105 (self.status, self.output)) |
106 | 106 |
107 | 107 |
| 108 class VMDeadKernelCrashError(VMError): |
| 109 def __init__(self, kernel_crash): |
| 110 VMError.__init__(self, kernel_crash) |
| 111 self.kernel_crash = kernel_crash |
| 112 |
| 113 def __str__(self): |
| 114 return ("VM is dead due to a kernel crash:\n%s" % self.kernel_crash) |
| 115 |
| 116 |
108 class VMAddressError(VMError): | 117 class VMAddressError(VMError): |
109 pass | 118 pass |
110 | 119 |
111 | 120 |
112 class VMPortNotRedirectedError(VMAddressError): | 121 class VMPortNotRedirectedError(VMAddressError): |
113 def __init__(self, port): | 122 def __init__(self, port): |
114 VMAddressError.__init__(self, port) | 123 VMAddressError.__init__(self, port) |
115 self.port = port | 124 self.port = port |
116 | 125 |
117 def __str__(self): | 126 def __str__(self): |
(...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
278 if not "info" in q_output: | 287 if not "info" in q_output: |
279 logging.error("qemu-img does not support 'info', " | 288 logging.error("qemu-img does not support 'info', " |
280 "skipping check...") | 289 "skipping check...") |
281 check_img = False | 290 check_img = False |
282 if check_img: | 291 if check_img: |
283 try: | 292 try: |
284 utils.system("%s info %s" % (qemu_img_cmd, image_filename)) | 293 utils.system("%s info %s" % (qemu_img_cmd, image_filename)) |
285 except error.CmdError: | 294 except error.CmdError: |
286 logging.error("Error getting info from image %s", | 295 logging.error("Error getting info from image %s", |
287 image_filename) | 296 image_filename) |
288 try: | 297 |
289 utils.system("%s check %s" % (qemu_img_cmd, image_filename)) | 298 cmd_result = utils.run("%s check %s" % |
290 except error.CmdError: | 299 (qemu_img_cmd, image_filename), |
| 300 ignore_status=True) |
| 301 # Error check, large chances of a non-fatal problem. |
| 302 # There are chances that bad data was skipped though |
| 303 if cmd_result.exit_status == 1: |
| 304 for e_line in cmd_result.stdout.splitlines(): |
| 305 logging.error("[stdout] %s", e_line) |
| 306 for e_line in cmd_result.stderr.splitlines(): |
| 307 logging.error("[stderr] %s", e_line) |
| 308 raise error.TestWarn("qemu-img check error. Some bad data in " |
| 309 "the image may have gone unnoticed") |
| 310 # Exit status 2 is data corruption for sure, so fail the test |
| 311 elif cmd_result.exit_status == 2: |
| 312 for e_line in cmd_result.stdout.splitlines(): |
| 313 logging.error("[stdout] %s", e_line) |
| 314 for e_line in cmd_result.stderr.splitlines(): |
| 315 logging.error("[stderr] %s", e_line) |
291 raise VMImageCheckError(image_filename) | 316 raise VMImageCheckError(image_filename) |
| 317 # Leaked clusters, they are known to be harmless to data integrity |
| 318 elif cmd_result.exit_status == 3: |
| 319 raise error.TestWarn("Leaked clusters were noticed during " |
| 320 "image check. No data integrity problem " |
| 321 "was found though.") |
292 | 322 |
293 else: | 323 else: |
294 if not os.path.exists(image_filename): | 324 if not os.path.exists(image_filename): |
295 logging.debug("Image file %s not found, skipping check...", | 325 logging.debug("Image file %s not found, skipping check...", |
296 image_filename) | 326 image_filename) |
297 elif not image_is_qcow2: | 327 elif not image_is_qcow2: |
298 logging.debug("Image file %s not qcow2, skipping check...", | 328 logging.debug("Image file %s not qcow2, skipping check...", |
299 image_filename) | 329 image_filename) |
300 | 330 |
301 | 331 |
(...skipping 26 matching lines...) Expand all Loading... |
328 self.device_id = [] | 358 self.device_id = [] |
329 self.uuid = None | 359 self.uuid = None |
330 | 360 |
331 # Find a unique identifier for this VM | 361 # Find a unique identifier for this VM |
332 while True: | 362 while True: |
333 self.instance = (time.strftime("%Y%m%d-%H%M%S-") + | 363 self.instance = (time.strftime("%Y%m%d-%H%M%S-") + |
334 kvm_utils.generate_random_string(4)) | 364 kvm_utils.generate_random_string(4)) |
335 if not glob.glob("/tmp/*%s" % self.instance): | 365 if not glob.glob("/tmp/*%s" % self.instance): |
336 break | 366 break |
337 | 367 |
| 368 self.spice_port = 8000 |
338 self.name = name | 369 self.name = name |
339 self.params = params | 370 self.params = params |
340 self.root_dir = root_dir | 371 self.root_dir = root_dir |
341 self.address_cache = address_cache | 372 self.address_cache = address_cache |
342 | 373 |
343 | 374 |
344 def clone(self, name=None, params=None, root_dir=None, address_cache=None, | 375 def clone(self, name=None, params=None, root_dir=None, address_cache=None, |
345 copy_state=False): | 376 copy_state=False): |
346 """ | 377 """ |
347 Return a clone of the VM object with optionally modified parameters. | 378 Return a clone of the VM object with optionally modified parameters. |
(...skipping 198 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
546 | 577 |
547 def add_nographic(help): | 578 def add_nographic(help): |
548 return " -nographic" | 579 return " -nographic" |
549 | 580 |
550 def add_uuid(help, uuid): | 581 def add_uuid(help, uuid): |
551 return " -uuid '%s'" % uuid | 582 return " -uuid '%s'" % uuid |
552 | 583 |
553 def add_pcidevice(help, host): | 584 def add_pcidevice(help, host): |
554 return " -pcidevice host='%s'" % host | 585 return " -pcidevice host='%s'" % host |
555 | 586 |
| 587 def add_spice(help, port, param): |
| 588 if has_option(help,"spice"): |
| 589 return " -spice port=%s,%s" % (port, param) |
| 590 else: |
| 591 return "" |
| 592 |
| 593 def add_qxl_vga(help, qxl, vga, qxl_dev_nr=None): |
| 594 str = "" |
| 595 if has_option(help, "qxl"): |
| 596 if qxl and qxl_dev_nr is not None: |
| 597 str += " -qxl %s" % qxl_dev_nr |
| 598 if has_option(help, "vga") and vga and vga != "qxl": |
| 599 str += " -vga %s" % vga |
| 600 elif has_option(help, "vga"): |
| 601 if qxl: |
| 602 str += " -vga qxl" |
| 603 elif vga: |
| 604 str += " -vga %s" % vga |
| 605 return str |
| 606 |
556 def add_kernel(help, filename): | 607 def add_kernel(help, filename): |
557 return " -kernel '%s'" % filename | 608 return " -kernel '%s'" % filename |
558 | 609 |
559 def add_initrd(help, filename): | 610 def add_initrd(help, filename): |
560 return " -initrd '%s'" % filename | 611 return " -initrd '%s'" % filename |
561 | 612 |
562 def add_kernel_cmdline(help, cmdline): | 613 def add_kernel_cmdline(help, cmdline): |
563 return " -append %s" % cmdline | 614 return " -append %s" % cmdline |
564 | 615 |
565 def add_testdev(help, filename): | 616 def add_testdev(help, filename): |
(...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
713 | 764 |
714 for host_port, guest_port in redirs: | 765 for host_port, guest_port in redirs: |
715 qemu_cmd += add_tcp_redir(help, host_port, guest_port) | 766 qemu_cmd += add_tcp_redir(help, host_port, guest_port) |
716 | 767 |
717 if params.get("display") == "vnc": | 768 if params.get("display") == "vnc": |
718 qemu_cmd += add_vnc(help, vm.vnc_port) | 769 qemu_cmd += add_vnc(help, vm.vnc_port) |
719 elif params.get("display") == "sdl": | 770 elif params.get("display") == "sdl": |
720 qemu_cmd += add_sdl(help) | 771 qemu_cmd += add_sdl(help) |
721 elif params.get("display") == "nographic": | 772 elif params.get("display") == "nographic": |
722 qemu_cmd += add_nographic(help) | 773 qemu_cmd += add_nographic(help) |
| 774 elif params.get("display") == "spice": |
| 775 qemu_cmd += add_spice(help, self.spice_port, params.get("spice")) |
| 776 |
| 777 qxl = "" |
| 778 vga = "" |
| 779 if params.get("qxl"): |
| 780 qxl = params.get("qxl") |
| 781 if params.get("vga"): |
| 782 vga = params.get("vga") |
| 783 if qxl or vga: |
| 784 if params.get("display") == "spice": |
| 785 qxl_dev_nr = params.get("qxl_dev_nr", None) |
| 786 qemu_cmd += add_qxl_vga(help, qxl, vga, qxl_dev_nr) |
723 | 787 |
724 if params.get("uuid") == "random": | 788 if params.get("uuid") == "random": |
725 qemu_cmd += add_uuid(help, vm.uuid) | 789 qemu_cmd += add_uuid(help, vm.uuid) |
726 elif params.get("uuid"): | 790 elif params.get("uuid"): |
727 qemu_cmd += add_uuid(help, params.get("uuid")) | 791 qemu_cmd += add_uuid(help, params.get("uuid")) |
728 | 792 |
729 if params.get("testdev") == "yes": | 793 if params.get("testdev") == "yes": |
730 qemu_cmd += add_testdev(help, vm.get_testlog_filename()) | 794 qemu_cmd += add_testdev(help, vm.get_testlog_filename()) |
731 | 795 |
732 if params.get("disable_hpet") == "yes": | 796 if params.get("disable_hpet") == "yes": |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
838 self.netdev_id = [] | 902 self.netdev_id = [] |
839 self.device_id = [] | 903 self.device_id = [] |
840 for nic in params.objects("nics"): | 904 for nic in params.objects("nics"): |
841 self.netdev_id.append(kvm_utils.generate_random_id()) | 905 self.netdev_id.append(kvm_utils.generate_random_id()) |
842 self.device_id.append(kvm_utils.generate_random_id()) | 906 self.device_id.append(kvm_utils.generate_random_id()) |
843 | 907 |
844 # Find available VNC port, if needed | 908 # Find available VNC port, if needed |
845 if params.get("display") == "vnc": | 909 if params.get("display") == "vnc": |
846 self.vnc_port = kvm_utils.find_free_port(5900, 6100) | 910 self.vnc_port = kvm_utils.find_free_port(5900, 6100) |
847 | 911 |
| 912 # Find available spice port |
| 913 if params.get("spice"): |
| 914 self.spice_port = kvm_utils.find_free_port(8000, 8100) |
| 915 |
848 # Find random UUID if specified 'uuid = random' in config file | 916 # Find random UUID if specified 'uuid = random' in config file |
849 if params.get("uuid") == "random": | 917 if params.get("uuid") == "random": |
850 f = open("/proc/sys/kernel/random/uuid") | 918 f = open("/proc/sys/kernel/random/uuid") |
851 self.uuid = f.read().strip() | 919 self.uuid = f.read().strip() |
852 f.close() | 920 f.close() |
853 | 921 |
854 # Generate or copy MAC addresses for all NICs | 922 # Generate or copy MAC addresses for all NICs |
855 num_nics = len(params.objects("nics")) | 923 num_nics = len(params.objects("nics")) |
856 for vlan in range(num_nics): | 924 for vlan in range(num_nics): |
857 nic_name = params.objects("nics")[vlan] | 925 nic_name = params.objects("nics")[vlan] |
(...skipping 255 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1113 self.monitor.is_responsive()) | 1181 self.monitor.is_responsive()) |
1114 | 1182 |
1115 | 1183 |
1116 def is_dead(self): | 1184 def is_dead(self): |
1117 """ | 1185 """ |
1118 Return True if the qemu process is dead. | 1186 Return True if the qemu process is dead. |
1119 """ | 1187 """ |
1120 return not self.process or not self.process.is_alive() | 1188 return not self.process or not self.process.is_alive() |
1121 | 1189 |
1122 | 1190 |
| 1191 def verify_kernel_crash(self): |
| 1192 """ |
| 1193 Find kernel crash message on the VM serial console. |
| 1194 |
| 1195 @raise: VMDeadKernelCrashError, in case a kernel crash message was |
| 1196 found. |
| 1197 """ |
| 1198 data = self.serial_console.get_output() |
| 1199 match = re.search(r"BUG:.*---\[ end trace .* \]---", data, |
| 1200 re.DOTALL|re.MULTILINE) |
| 1201 if match is not None: |
| 1202 raise VMDeadKernelCrashError(match.group(0)) |
| 1203 |
| 1204 |
1123 def get_params(self): | 1205 def get_params(self): |
1124 """ | 1206 """ |
1125 Return the VM's params dict. Most modified params take effect only | 1207 Return the VM's params dict. Most modified params take effect only |
1126 upon VM.create(). | 1208 upon VM.create(). |
1127 """ | 1209 """ |
1128 return self.params | 1210 return self.params |
1129 | 1211 |
1130 | 1212 |
1131 def get_monitor_filename(self, monitor_name): | 1213 def get_monitor_filename(self, monitor_name): |
1132 """ | 1214 """ |
(...skipping 456 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1589 clone = temp | 1671 clone = temp |
1590 | 1672 |
1591 # From now on, clone is the source VM that will soon be destroyed | 1673 # From now on, clone is the source VM that will soon be destroyed |
1592 # and self is the destination VM that will remain alive. If this | 1674 # and self is the destination VM that will remain alive. If this |
1593 # is remote migration, self is a dead VM object. | 1675 # is remote migration, self is a dead VM object. |
1594 | 1676 |
1595 error.context("after migration") | 1677 error.context("after migration") |
1596 if local: | 1678 if local: |
1597 time.sleep(1) | 1679 time.sleep(1) |
1598 self.verify_alive() | 1680 self.verify_alive() |
| 1681 self.verify_kernel_crash() |
1599 | 1682 |
1600 if local and stable_check: | 1683 if local and stable_check: |
1601 try: | 1684 try: |
1602 save1 = os.path.join(save_path, "src-" + clone.instance) | 1685 save1 = os.path.join(save_path, "src-" + clone.instance) |
1603 save2 = os.path.join(save_path, "dst-" + self.instance) | 1686 save2 = os.path.join(save_path, "dst-" + self.instance) |
1604 clone.save_to_file(save1) | 1687 clone.save_to_file(save1) |
1605 self.save_to_file(save2) | 1688 self.save_to_file(save2) |
1606 # Fail if we see deltas | 1689 # Fail if we see deltas |
1607 md5_save1 = utils.hash_file(save1) | 1690 md5_save1 = utils.hash_file(save1) |
1608 md5_save2 = utils.hash_file(save2) | 1691 md5_save2 = utils.hash_file(save2) |
(...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1768 Save the state of virtual machine to a file through migrate to | 1851 Save the state of virtual machine to a file through migrate to |
1769 exec | 1852 exec |
1770 """ | 1853 """ |
1771 # Make sure we only get one iteration | 1854 # Make sure we only get one iteration |
1772 self.monitor.cmd("migrate_set_speed 1000g") | 1855 self.monitor.cmd("migrate_set_speed 1000g") |
1773 self.monitor.cmd("migrate_set_downtime 100000000") | 1856 self.monitor.cmd("migrate_set_downtime 100000000") |
1774 self.monitor.migrate('"exec:cat>%s"' % path) | 1857 self.monitor.migrate('"exec:cat>%s"' % path) |
1775 # Restore the speed and downtime of migration | 1858 # Restore the speed and downtime of migration |
1776 self.monitor.cmd("migrate_set_speed %d" % (32<<20)) | 1859 self.monitor.cmd("migrate_set_speed %d" % (32<<20)) |
1777 self.monitor.cmd("migrate_set_downtime 0.03") | 1860 self.monitor.cmd("migrate_set_downtime 0.03") |
OLD | NEW |