| 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 |