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 | |
10 from autotest_lib.client.common_lib import error | 9 from autotest_lib.client.common_lib import error |
11 from autotest_lib.client.bin import utils | 10 from autotest_lib.client.bin import utils |
| 11 import virt_utils, virt_vm, kvm_monitor, aexpect |
12 | 12 |
13 | 13 |
14 class VMError(Exception): | 14 class VM(virt_vm.BaseVM): |
15 pass | |
16 | |
17 | |
18 class VMCreateError(VMError): | |
19 def __init__(self, cmd, status, output): | |
20 VMError.__init__(self, cmd, status, output) | |
21 self.cmd = cmd | |
22 self.status = status | |
23 self.output = output | |
24 | |
25 def __str__(self): | |
26 return ("VM creation command failed: %r (status: %s, " | |
27 "output: %r)" % (self.cmd, self.status, self.output)) | |
28 | |
29 | |
30 class VMHashMismatchError(VMError): | |
31 def __init__(self, actual, expected): | |
32 VMError.__init__(self, actual, expected) | |
33 self.actual_hash = actual | |
34 self.expected_hash = expected | |
35 | |
36 def __str__(self): | |
37 return ("CD image hash (%s) differs from expected one (%s)" % | |
38 (self.actual_hash, self.expected_hash)) | |
39 | |
40 | |
41 class VMImageMissingError(VMError): | |
42 def __init__(self, filename): | |
43 VMError.__init__(self, filename) | |
44 self.filename = filename | |
45 | |
46 def __str__(self): | |
47 return "CD image file not found: %r" % self.filename | |
48 | |
49 | |
50 class VMImageCheckError(VMError): | |
51 def __init__(self, filename): | |
52 VMError.__init__(self, filename) | |
53 self.filename = filename | |
54 | |
55 def __str__(self): | |
56 return "Errors found on image: %r" % self.filename | |
57 | |
58 | |
59 class VMBadPATypeError(VMError): | |
60 def __init__(self, pa_type): | |
61 VMError.__init__(self, pa_type) | |
62 self.pa_type = pa_type | |
63 | |
64 def __str__(self): | |
65 return "Unsupported PCI assignable type: %r" % self.pa_type | |
66 | |
67 | |
68 class VMPAError(VMError): | |
69 def __init__(self, pa_type): | |
70 VMError.__init__(self, pa_type) | |
71 self.pa_type = pa_type | |
72 | |
73 def __str__(self): | |
74 return ("No PCI assignable devices could be assigned " | |
75 "(pci_assignable=%r)" % self.pa_type) | |
76 | |
77 | |
78 class VMPostCreateError(VMError): | |
79 def __init__(self, cmd, output): | |
80 VMError.__init__(self, cmd, output) | |
81 self.cmd = cmd | |
82 self.output = output | |
83 | |
84 | |
85 class VMHugePageError(VMPostCreateError): | |
86 def __str__(self): | |
87 return ("Cannot allocate hugepage memory (command: %r, " | |
88 "output: %r)" % (self.cmd, self.output)) | |
89 | |
90 | |
91 class VMKVMInitError(VMPostCreateError): | |
92 def __str__(self): | |
93 return ("Cannot initialize KVM (command: %r, output: %r)" % | |
94 (self.cmd, self.output)) | |
95 | |
96 | |
97 class VMDeadError(VMError): | |
98 def __init__(self, status, output): | |
99 VMError.__init__(self, status, output) | |
100 self.status = status | |
101 self.output = output | |
102 | |
103 def __str__(self): | |
104 return ("VM process is dead (status: %s, output: %r)" % | |
105 (self.status, self.output)) | |
106 | |
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 | |
117 class VMAddressError(VMError): | |
118 pass | |
119 | |
120 | |
121 class VMPortNotRedirectedError(VMAddressError): | |
122 def __init__(self, port): | |
123 VMAddressError.__init__(self, port) | |
124 self.port = port | |
125 | |
126 def __str__(self): | |
127 return "Port not redirected: %s" % self.port | |
128 | |
129 | |
130 class VMAddressVerificationError(VMAddressError): | |
131 def __init__(self, mac, ip): | |
132 VMAddressError.__init__(self, mac, ip) | |
133 self.mac = mac | |
134 self.ip = ip | |
135 | |
136 def __str__(self): | |
137 return ("Cannot verify MAC-IP address mapping using arping: " | |
138 "%s ---> %s" % (self.mac, self.ip)) | |
139 | |
140 | |
141 class VMMACAddressMissingError(VMAddressError): | |
142 def __init__(self, nic_index): | |
143 VMAddressError.__init__(self, nic_index) | |
144 self.nic_index = nic_index | |
145 | |
146 def __str__(self): | |
147 return "No MAC address defined for NIC #%s" % self.nic_index | |
148 | |
149 | |
150 class VMIPAddressMissingError(VMAddressError): | |
151 def __init__(self, mac): | |
152 VMAddressError.__init__(self, mac) | |
153 self.mac = mac | |
154 | |
155 def __str__(self): | |
156 return "Cannot find IP address for MAC address %s" % self.mac | |
157 | |
158 | |
159 class VMMigrateError(VMError): | |
160 pass | |
161 | |
162 | |
163 class VMMigrateTimeoutError(VMMigrateError): | |
164 pass | |
165 | |
166 | |
167 class VMMigrateCancelError(VMMigrateError): | |
168 pass | |
169 | |
170 | |
171 class VMMigrateFailedError(VMMigrateError): | |
172 pass | |
173 | |
174 | |
175 class VMMigrateStateMismatchError(VMMigrateError): | |
176 def __init__(self, src_hash, dst_hash): | |
177 VMMigrateError.__init__(self, src_hash, dst_hash) | |
178 self.src_hash = src_hash | |
179 self.dst_hash = dst_hash | |
180 | |
181 def __str__(self): | |
182 return ("Mismatch of VM state before and after migration (%s != %s)" % | |
183 (self.src_hash, self.dst_hash)) | |
184 | |
185 | |
186 class VMRebootError(VMError): | |
187 pass | |
188 | |
189 | |
190 def get_image_filename(params, root_dir): | |
191 """ | |
192 Generate an image path from params and root_dir. | |
193 | |
194 @param params: Dictionary containing the test parameters. | |
195 @param root_dir: Base directory for relative filenames. | |
196 | |
197 @note: params should contain: | |
198 image_name -- the name of the image file, without extension | |
199 image_format -- the format of the image (qcow2, raw etc) | |
200 """ | |
201 image_name = params.get("image_name", "image") | |
202 image_format = params.get("image_format", "qcow2") | |
203 if params.get("image_raw_device") == "yes": | |
204 return image_name | |
205 image_filename = "%s.%s" % (image_name, image_format) | |
206 image_filename = kvm_utils.get_path(root_dir, image_filename) | |
207 return image_filename | |
208 | |
209 | |
210 def create_image(params, root_dir): | |
211 """ | |
212 Create an image using qemu_image. | |
213 | |
214 @param params: Dictionary containing the test parameters. | |
215 @param root_dir: Base directory for relative filenames. | |
216 | |
217 @note: params should contain: | |
218 image_name -- the name of the image file, without extension | |
219 image_format -- the format of the image (qcow2, raw etc) | |
220 image_size -- the requested size of the image (a string | |
221 qemu-img can understand, such as '10G') | |
222 """ | |
223 qemu_img_cmd = kvm_utils.get_path(root_dir, params.get("qemu_img_binary", | |
224 "qemu-img")) | |
225 qemu_img_cmd += " create" | |
226 | |
227 format = params.get("image_format", "qcow2") | |
228 qemu_img_cmd += " -f %s" % format | |
229 | |
230 image_filename = get_image_filename(params, root_dir) | |
231 qemu_img_cmd += " %s" % image_filename | |
232 | |
233 size = params.get("image_size", "10G") | |
234 qemu_img_cmd += " %s" % size | |
235 | |
236 utils.system(qemu_img_cmd) | |
237 logging.info("Image created in %r", image_filename) | |
238 return image_filename | |
239 | |
240 | |
241 def remove_image(params, root_dir): | |
242 """ | |
243 Remove an image file. | |
244 | |
245 @param params: A dict | |
246 @param root_dir: Base directory for relative filenames. | |
247 | |
248 @note: params should contain: | |
249 image_name -- the name of the image file, without extension | |
250 image_format -- the format of the image (qcow2, raw etc) | |
251 """ | |
252 image_filename = get_image_filename(params, root_dir) | |
253 logging.debug("Removing image file %s...", image_filename) | |
254 if os.path.exists(image_filename): | |
255 os.unlink(image_filename) | |
256 else: | |
257 logging.debug("Image file %s not found") | |
258 | |
259 | |
260 def check_image(params, root_dir): | |
261 """ | |
262 Check an image using qemu-img. | |
263 | |
264 @param params: Dictionary containing the test parameters. | |
265 @param root_dir: Base directory for relative filenames. | |
266 | |
267 @note: params should contain: | |
268 image_name -- the name of the image file, without extension | |
269 image_format -- the format of the image (qcow2, raw etc) | |
270 | |
271 @raise VMImageCheckError: In case qemu-img check fails on the image. | |
272 """ | |
273 image_filename = get_image_filename(params, root_dir) | |
274 logging.debug("Checking image file %s...", image_filename) | |
275 qemu_img_cmd = kvm_utils.get_path(root_dir, | |
276 params.get("qemu_img_binary", "qemu-img")) | |
277 image_is_qcow2 = params.get("image_format") == 'qcow2' | |
278 if os.path.exists(image_filename) and image_is_qcow2: | |
279 # Verifying if qemu-img supports 'check' | |
280 q_result = utils.run(qemu_img_cmd, ignore_status=True) | |
281 q_output = q_result.stdout | |
282 check_img = True | |
283 if not "check" in q_output: | |
284 logging.error("qemu-img does not support 'check', " | |
285 "skipping check...") | |
286 check_img = False | |
287 if not "info" in q_output: | |
288 logging.error("qemu-img does not support 'info', " | |
289 "skipping check...") | |
290 check_img = False | |
291 if check_img: | |
292 try: | |
293 utils.system("%s info %s" % (qemu_img_cmd, image_filename)) | |
294 except error.CmdError: | |
295 logging.error("Error getting info from image %s", | |
296 image_filename) | |
297 | |
298 cmd_result = utils.run("%s check %s" % | |
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) | |
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.") | |
322 | |
323 else: | |
324 if not os.path.exists(image_filename): | |
325 logging.debug("Image file %s not found, skipping check...", | |
326 image_filename) | |
327 elif not image_is_qcow2: | |
328 logging.debug("Image file %s not qcow2, skipping check...", | |
329 image_filename) | |
330 | |
331 | |
332 class VM: | |
333 """ | 15 """ |
334 This class handles all basic VM operations. | 16 This class handles all basic VM operations. |
335 """ | 17 """ |
336 | 18 |
| 19 MIGRATION_PROTOS = ['tcp', 'unix', 'exec'] |
| 20 |
337 def __init__(self, name, params, root_dir, address_cache, state=None): | 21 def __init__(self, name, params, root_dir, address_cache, state=None): |
338 """ | 22 """ |
339 Initialize the object and set a few attributes. | 23 Initialize the object and set a few attributes. |
340 | 24 |
341 @param name: The name of the object | 25 @param name: The name of the object |
342 @param params: A dict containing VM params | 26 @param params: A dict containing VM params |
343 (see method make_qemu_command for a full description) | 27 (see method make_qemu_command for a full description) |
344 @param root_dir: Base directory for relative filenames | 28 @param root_dir: Base directory for relative filenames |
345 @param address_cache: A dict that maps MAC addresses to IP addresses | 29 @param address_cache: A dict that maps MAC addresses to IP addresses |
346 @param state: If provided, use this as self.__dict__ | 30 @param state: If provided, use this as self.__dict__ |
347 """ | 31 """ |
| 32 virt_vm.BaseVM.__init__(self, name, params) |
| 33 |
348 if state: | 34 if state: |
349 self.__dict__ = state | 35 self.__dict__ = state |
350 else: | 36 else: |
351 self.process = None | 37 self.process = None |
352 self.serial_console = None | 38 self.serial_console = None |
353 self.redirs = {} | 39 self.redirs = {} |
354 self.vnc_port = 5900 | 40 self.vnc_port = 5900 |
355 self.monitors = [] | 41 self.monitors = [] |
356 self.pci_assignable = None | 42 self.pci_assignable = None |
357 self.netdev_id = [] | 43 self.netdev_id = [] |
358 self.device_id = [] | 44 self.device_id = [] |
359 self.uuid = None | 45 self.uuid = None |
360 | 46 |
361 # Find a unique identifier for this VM | |
362 while True: | |
363 self.instance = (time.strftime("%Y%m%d-%H%M%S-") + | |
364 kvm_utils.generate_random_string(4)) | |
365 if not glob.glob("/tmp/*%s" % self.instance): | |
366 break | |
367 | 47 |
368 self.spice_port = 8000 | 48 self.spice_port = 8000 |
369 self.name = name | 49 self.name = name |
370 self.params = params | 50 self.params = params |
371 self.root_dir = root_dir | 51 self.root_dir = root_dir |
372 self.address_cache = address_cache | 52 self.address_cache = address_cache |
373 | 53 |
374 | 54 |
| 55 def verify_alive(self): |
| 56 """ |
| 57 Make sure the VM is alive and that the main monitor is responsive. |
| 58 |
| 59 @raise VMDeadError: If the VM is dead |
| 60 @raise: Various monitor exceptions if the monitor is unresponsive |
| 61 """ |
| 62 try: |
| 63 virt_vm.BaseVM.verify_alive(self) |
| 64 if self.monitors: |
| 65 self.monitor.verify_responsive() |
| 66 except virt_vm.VMDeadError: |
| 67 raise virt_vm.VMDeadError(self.process.get_status(), |
| 68 self.process.get_output()) |
| 69 |
| 70 |
| 71 def is_alive(self): |
| 72 """ |
| 73 Return True if the VM is alive and its monitor is responsive. |
| 74 """ |
| 75 return not self.is_dead() and (not self.monitors or |
| 76 self.monitor.is_responsive()) |
| 77 |
| 78 |
| 79 def is_dead(self): |
| 80 """ |
| 81 Return True if the qemu process is dead. |
| 82 """ |
| 83 return not self.process or not self.process.is_alive() |
| 84 |
| 85 |
| 86 |
| 87 |
375 def clone(self, name=None, params=None, root_dir=None, address_cache=None, | 88 def clone(self, name=None, params=None, root_dir=None, address_cache=None, |
376 copy_state=False): | 89 copy_state=False): |
377 """ | 90 """ |
378 Return a clone of the VM object with optionally modified parameters. | 91 Return a clone of the VM object with optionally modified parameters. |
379 The clone is initially not alive and needs to be started using create(). | 92 The clone is initially not alive and needs to be started using create(). |
380 Any parameters not passed to this function are copied from the source | 93 Any parameters not passed to this function are copied from the source |
381 VM. | 94 VM. |
382 | 95 |
383 @param name: Optional new VM name | 96 @param name: Optional new VM name |
384 @param params: Optional new VM creation parameters | 97 @param params: Optional new VM creation parameters |
(...skipping 10 matching lines...) Expand all Loading... |
395 root_dir = self.root_dir | 108 root_dir = self.root_dir |
396 if address_cache is None: | 109 if address_cache is None: |
397 address_cache = self.address_cache | 110 address_cache = self.address_cache |
398 if copy_state: | 111 if copy_state: |
399 state = self.__dict__.copy() | 112 state = self.__dict__.copy() |
400 else: | 113 else: |
401 state = None | 114 state = None |
402 return VM(name, params, root_dir, address_cache, state) | 115 return VM(name, params, root_dir, address_cache, state) |
403 | 116 |
404 | 117 |
405 def make_qemu_command(self, name=None, params=None, root_dir=None): | 118 def __make_qemu_command(self, name=None, params=None, root_dir=None): |
406 """ | 119 """ |
407 Generate a qemu command line. All parameters are optional. If a | 120 Generate a qemu command line. All parameters are optional. If a |
408 parameter is not supplied, the corresponding value stored in the | 121 parameter is not supplied, the corresponding value stored in the |
409 class attributes is used. | 122 class attributes is used. |
410 | 123 |
411 @param name: The name of the object | 124 @param name: The name of the object |
412 @param params: A dict containing VM params | 125 @param params: A dict containing VM params |
413 @param root_dir: Base directory for relative filenames | 126 @param root_dir: Base directory for relative filenames |
414 | 127 |
415 @note: The params dict should contain: | 128 @note: The params dict should contain: |
(...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
628 if name is None: | 341 if name is None: |
629 name = self.name | 342 name = self.name |
630 if params is None: | 343 if params is None: |
631 params = self.params | 344 params = self.params |
632 if root_dir is None: | 345 if root_dir is None: |
633 root_dir = self.root_dir | 346 root_dir = self.root_dir |
634 | 347 |
635 # Clone this VM using the new params | 348 # Clone this VM using the new params |
636 vm = self.clone(name, params, root_dir, copy_state=True) | 349 vm = self.clone(name, params, root_dir, copy_state=True) |
637 | 350 |
638 qemu_binary = kvm_utils.get_path(root_dir, params.get("qemu_binary", | 351 qemu_binary = virt_utils.get_path(root_dir, params.get("qemu_binary", |
639 "qemu")) | 352 "qemu")) |
640 # Get the output of 'qemu -help' (log a message in case this call never | 353 # Get the output of 'qemu -help' (log a message in case this call never |
641 # returns or causes some other kind of trouble) | 354 # returns or causes some other kind of trouble) |
642 logging.debug("Getting output of 'qemu -help'") | 355 logging.debug("Getting output of 'qemu -help'") |
643 help = commands.getoutput("%s -help" % qemu_binary) | 356 help = commands.getoutput("%s -help" % qemu_binary) |
644 | 357 |
645 # Start constructing the qemu command | 358 # Start constructing the qemu command |
646 qemu_cmd = "" | 359 qemu_cmd = "" |
647 # Set the X11 display parameter if requested | 360 # Set the X11 display parameter if requested |
648 if params.get("x11_display"): | 361 if params.get("x11_display"): |
(...skipping 12 matching lines...) Expand all Loading... |
661 qemu_cmd += add_human_monitor(help, monitor_filename) | 374 qemu_cmd += add_human_monitor(help, monitor_filename) |
662 | 375 |
663 # Add serial console redirection | 376 # Add serial console redirection |
664 qemu_cmd += add_serial(help, vm.get_serial_console_filename()) | 377 qemu_cmd += add_serial(help, vm.get_serial_console_filename()) |
665 | 378 |
666 for image_name in params.objects("images"): | 379 for image_name in params.objects("images"): |
667 image_params = params.object_params(image_name) | 380 image_params = params.object_params(image_name) |
668 if image_params.get("boot_drive") == "no": | 381 if image_params.get("boot_drive") == "no": |
669 continue | 382 continue |
670 qemu_cmd += add_drive(help, | 383 qemu_cmd += add_drive(help, |
671 get_image_filename(image_params, root_dir), | 384 virt_vm.get_image_filename(image_params, root_dir), |
672 image_params.get("drive_index"), | 385 image_params.get("drive_index"), |
673 image_params.get("drive_format"), | 386 image_params.get("drive_format"), |
674 image_params.get("drive_cache"), | 387 image_params.get("drive_cache"), |
675 image_params.get("drive_werror"), | 388 image_params.get("drive_werror"), |
676 image_params.get("drive_serial"), | 389 image_params.get("drive_serial"), |
677 image_params.get("image_snapshot") == "yes", | 390 image_params.get("image_snapshot") == "yes", |
678 image_params.get("image_boot") == "yes") | 391 image_params.get("image_boot") == "yes") |
679 | 392 |
680 redirs = [] | 393 redirs = [] |
681 for redir_name in params.objects("redirs"): | 394 for redir_name in params.objects("redirs"): |
682 redir_params = params.object_params(redir_name) | 395 redir_params = params.object_params(redir_name) |
683 guest_port = int(redir_params.get("guest_port")) | 396 guest_port = int(redir_params.get("guest_port")) |
684 host_port = vm.redirs.get(guest_port) | 397 host_port = vm.redirs.get(guest_port) |
685 redirs += [(host_port, guest_port)] | 398 redirs += [(host_port, guest_port)] |
686 | 399 |
687 vlan = 0 | 400 vlan = 0 |
688 for nic_name in params.objects("nics"): | 401 for nic_name in params.objects("nics"): |
689 nic_params = params.object_params(nic_name) | 402 nic_params = params.object_params(nic_name) |
690 try: | 403 try: |
691 netdev_id = vm.netdev_id[vlan] | 404 netdev_id = vm.netdev_id[vlan] |
692 device_id = vm.device_id[vlan] | 405 device_id = vm.device_id[vlan] |
693 except IndexError: | 406 except IndexError: |
694 netdev_id = None | 407 netdev_id = None |
695 # Handle the '-net nic' part | 408 # Handle the '-net nic' part |
696 try: | 409 try: |
697 mac = vm.get_mac_address(vlan) | 410 mac = vm.get_mac_address(vlan) |
698 except VMAddressError: | 411 except virt_vm.VMAddressError: |
699 mac = None | 412 mac = None |
700 qemu_cmd += add_nic(help, vlan, nic_params.get("nic_model"), mac, | 413 qemu_cmd += add_nic(help, vlan, nic_params.get("nic_model"), mac, |
701 device_id, netdev_id, nic_params.get("nic_extra_
params")) | 414 device_id, netdev_id, nic_params.get("nic_extra_
params")) |
702 # Handle the '-net tap' or '-net user' or '-netdev' part | 415 # Handle the '-net tap' or '-net user' or '-netdev' part |
703 script = nic_params.get("nic_script") | 416 script = nic_params.get("nic_script") |
704 downscript = nic_params.get("nic_downscript") | 417 downscript = nic_params.get("nic_downscript") |
705 tftp = nic_params.get("tftp") | 418 tftp = nic_params.get("tftp") |
706 if script: | 419 if script: |
707 script = kvm_utils.get_path(root_dir, script) | 420 script = virt_utils.get_path(root_dir, script) |
708 if downscript: | 421 if downscript: |
709 downscript = kvm_utils.get_path(root_dir, downscript) | 422 downscript = virt_utils.get_path(root_dir, downscript) |
710 if tftp: | 423 if tftp: |
711 tftp = kvm_utils.get_path(root_dir, tftp) | 424 tftp = virt_utils.get_path(root_dir, tftp) |
712 qemu_cmd += add_net(help, vlan, nic_params.get("nic_mode", "user"), | 425 qemu_cmd += add_net(help, vlan, nic_params.get("nic_mode", "user"), |
713 vm.get_ifname(vlan), | 426 vm.get_ifname(vlan), |
714 script, downscript, tftp, | 427 script, downscript, tftp, |
715 nic_params.get("bootp"), redirs, netdev_id, | 428 nic_params.get("bootp"), redirs, netdev_id, |
716 nic_params.get("netdev_extra_params")) | 429 nic_params.get("netdev_extra_params")) |
717 # Proceed to next NIC | 430 # Proceed to next NIC |
718 vlan += 1 | 431 vlan += 1 |
719 | 432 |
720 mem = params.get("mem") | 433 mem = params.get("mem") |
721 if mem: | 434 if mem: |
722 qemu_cmd += add_mem(help, mem) | 435 qemu_cmd += add_mem(help, mem) |
723 | 436 |
724 smp = params.get("smp") | 437 smp = params.get("smp") |
725 if smp: | 438 if smp: |
726 qemu_cmd += add_smp(help, smp) | 439 qemu_cmd += add_smp(help, smp) |
727 | 440 |
728 for cdrom in params.objects("cdroms"): | 441 for cdrom in params.objects("cdroms"): |
729 cdrom_params = params.object_params(cdrom) | 442 cdrom_params = params.object_params(cdrom) |
730 iso = cdrom_params.get("cdrom") | 443 iso = cdrom_params.get("cdrom") |
731 if iso: | 444 if iso: |
732 qemu_cmd += add_cdrom(help, kvm_utils.get_path(root_dir, iso), | 445 qemu_cmd += add_cdrom(help, virt_utils.get_path(root_dir, iso), |
733 cdrom_params.get("drive_index")) | 446 cdrom_params.get("drive_index")) |
734 | 447 |
735 # We may want to add {floppy_otps} parameter for -fda | 448 # We may want to add {floppy_otps} parameter for -fda |
736 # {fat:floppy:}/path/. However vvfat is not usually recommended. | 449 # {fat:floppy:}/path/. However vvfat is not usually recommended. |
737 floppy = params.get("floppy") | 450 floppy = params.get("floppy") |
738 if floppy: | 451 if floppy: |
739 floppy = kvm_utils.get_path(root_dir, floppy) | 452 floppy = virt_utils.get_path(root_dir, floppy) |
740 qemu_cmd += add_floppy(help, floppy) | 453 qemu_cmd += add_floppy(help, floppy) |
741 | 454 |
742 tftp = params.get("tftp") | 455 tftp = params.get("tftp") |
743 if tftp: | 456 if tftp: |
744 tftp = kvm_utils.get_path(root_dir, tftp) | 457 tftp = virt_utils.get_path(root_dir, tftp) |
745 qemu_cmd += add_tftp(help, tftp) | 458 qemu_cmd += add_tftp(help, tftp) |
746 | 459 |
747 bootp = params.get("bootp") | 460 bootp = params.get("bootp") |
748 if bootp: | 461 if bootp: |
749 qemu_cmd += add_bootp(help, bootp) | 462 qemu_cmd += add_bootp(help, bootp) |
750 | 463 |
751 kernel = params.get("kernel") | 464 kernel = params.get("kernel") |
752 if kernel: | 465 if kernel: |
753 kernel = kvm_utils.get_path(root_dir, kernel) | 466 kernel = virt_utils.get_path(root_dir, kernel) |
754 qemu_cmd += add_kernel(help, kernel) | 467 qemu_cmd += add_kernel(help, kernel) |
755 | 468 |
756 kernel_cmdline = params.get("kernel_cmdline") | 469 kernel_cmdline = params.get("kernel_cmdline") |
757 if kernel_cmdline: | 470 if kernel_cmdline: |
758 qemu_cmd += add_kernel_cmdline(help, kernel_cmdline) | 471 qemu_cmd += add_kernel_cmdline(help, kernel_cmdline) |
759 | 472 |
760 initrd = params.get("initrd") | 473 initrd = params.get("initrd") |
761 if initrd: | 474 if initrd: |
762 initrd = kvm_utils.get_path(root_dir, initrd) | 475 initrd = virt_utils.get_path(root_dir, initrd) |
763 qemu_cmd += add_initrd(help, initrd) | 476 qemu_cmd += add_initrd(help, initrd) |
764 | 477 |
765 for host_port, guest_port in redirs: | 478 for host_port, guest_port in redirs: |
766 qemu_cmd += add_tcp_redir(help, host_port, guest_port) | 479 qemu_cmd += add_tcp_redir(help, host_port, guest_port) |
767 | 480 |
768 if params.get("display") == "vnc": | 481 if params.get("display") == "vnc": |
769 qemu_cmd += add_vnc(help, vm.vnc_port) | 482 qemu_cmd += add_vnc(help, vm.vnc_port) |
770 elif params.get("display") == "sdl": | 483 elif params.get("display") == "sdl": |
771 qemu_cmd += add_sdl(help) | 484 qemu_cmd += add_sdl(help) |
772 elif params.get("display") == "nographic": | 485 elif params.get("display") == "nographic": |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
848 self.root_dir = root_dir | 561 self.root_dir = root_dir |
849 name = self.name | 562 name = self.name |
850 params = self.params | 563 params = self.params |
851 root_dir = self.root_dir | 564 root_dir = self.root_dir |
852 | 565 |
853 # Verify the md5sum of the ISO images | 566 # Verify the md5sum of the ISO images |
854 for cdrom in params.objects("cdroms"): | 567 for cdrom in params.objects("cdroms"): |
855 cdrom_params = params.object_params(cdrom) | 568 cdrom_params = params.object_params(cdrom) |
856 iso = cdrom_params.get("cdrom") | 569 iso = cdrom_params.get("cdrom") |
857 if iso: | 570 if iso: |
858 iso = kvm_utils.get_path(root_dir, iso) | 571 iso = virt_utils.get_path(root_dir, iso) |
859 if not os.path.exists(iso): | 572 if not os.path.exists(iso): |
860 raise VMImageMissingError(iso) | 573 raise virt_vm.VMImageMissingError(iso) |
861 compare = False | 574 compare = False |
862 if cdrom_params.get("md5sum_1m"): | 575 if cdrom_params.get("md5sum_1m"): |
863 logging.debug("Comparing expected MD5 sum with MD5 sum of " | 576 logging.debug("Comparing expected MD5 sum with MD5 sum of " |
864 "first MB of ISO file...") | 577 "first MB of ISO file...") |
865 actual_hash = utils.hash_file(iso, 1048576, method="md5") | 578 actual_hash = utils.hash_file(iso, 1048576, method="md5") |
866 expected_hash = cdrom_params.get("md5sum_1m") | 579 expected_hash = cdrom_params.get("md5sum_1m") |
867 compare = True | 580 compare = True |
868 elif cdrom_params.get("md5sum"): | 581 elif cdrom_params.get("md5sum"): |
869 logging.debug("Comparing expected MD5 sum with MD5 sum of " | 582 logging.debug("Comparing expected MD5 sum with MD5 sum of " |
870 "ISO file...") | 583 "ISO file...") |
871 actual_hash = utils.hash_file(iso, method="md5") | 584 actual_hash = utils.hash_file(iso, method="md5") |
872 expected_hash = cdrom_params.get("md5sum") | 585 expected_hash = cdrom_params.get("md5sum") |
873 compare = True | 586 compare = True |
874 elif cdrom_params.get("sha1sum"): | 587 elif cdrom_params.get("sha1sum"): |
875 logging.debug("Comparing expected SHA1 sum with SHA1 sum " | 588 logging.debug("Comparing expected SHA1 sum with SHA1 sum " |
876 "of ISO file...") | 589 "of ISO file...") |
877 actual_hash = utils.hash_file(iso, method="sha1") | 590 actual_hash = utils.hash_file(iso, method="sha1") |
878 expected_hash = cdrom_params.get("sha1sum") | 591 expected_hash = cdrom_params.get("sha1sum") |
879 compare = True | 592 compare = True |
880 if compare: | 593 if compare: |
881 if actual_hash == expected_hash: | 594 if actual_hash == expected_hash: |
882 logging.debug("Hashes match") | 595 logging.debug("Hashes match") |
883 else: | 596 else: |
884 raise VMHashMismatchError(actual_hash, expected_hash) | 597 raise virt_vm.VMHashMismatchError(actual_hash, |
| 598 expected_hash) |
885 | 599 |
886 # Make sure the following code is not executed by more than one thread | 600 # Make sure the following code is not executed by more than one thread |
887 # at the same time | 601 # at the same time |
888 lockfile = open("/tmp/kvm-autotest-vm-create.lock", "w+") | 602 lockfile = open("/tmp/kvm-autotest-vm-create.lock", "w+") |
889 fcntl.lockf(lockfile, fcntl.LOCK_EX) | 603 fcntl.lockf(lockfile, fcntl.LOCK_EX) |
890 | 604 |
891 try: | 605 try: |
892 # Handle port redirections | 606 # Handle port redirections |
893 redir_names = params.objects("redirs") | 607 redir_names = params.objects("redirs") |
894 host_ports = kvm_utils.find_free_ports(5000, 6000, len(redir_names)) | 608 host_ports = virt_utils.find_free_ports(5000, 6000, len(redir_names)
) |
895 self.redirs = {} | 609 self.redirs = {} |
896 for i in range(len(redir_names)): | 610 for i in range(len(redir_names)): |
897 redir_params = params.object_params(redir_names[i]) | 611 redir_params = params.object_params(redir_names[i]) |
898 guest_port = int(redir_params.get("guest_port")) | 612 guest_port = int(redir_params.get("guest_port")) |
899 self.redirs[guest_port] = host_ports[i] | 613 self.redirs[guest_port] = host_ports[i] |
900 | 614 |
901 # Generate netdev/device IDs for all NICs | 615 # Generate netdev/device IDs for all NICs |
902 self.netdev_id = [] | 616 self.netdev_id = [] |
903 self.device_id = [] | 617 self.device_id = [] |
904 for nic in params.objects("nics"): | 618 for nic in params.objects("nics"): |
905 self.netdev_id.append(kvm_utils.generate_random_id()) | 619 self.netdev_id.append(virt_utils.generate_random_id()) |
906 self.device_id.append(kvm_utils.generate_random_id()) | 620 self.device_id.append(virt_utils.generate_random_id()) |
907 | 621 |
908 # Find available VNC port, if needed | 622 # Find available VNC port, if needed |
909 if params.get("display") == "vnc": | 623 if params.get("display") == "vnc": |
910 self.vnc_port = kvm_utils.find_free_port(5900, 6100) | 624 self.vnc_port = virt_utils.find_free_port(5900, 6100) |
911 | 625 |
912 # Find available spice port | 626 # Find available spice port, if needed |
913 if params.get("spice"): | 627 if params.get("spice"): |
914 self.spice_port = kvm_utils.find_free_port(8000, 8100) | 628 self.spice_port = virt_utils.find_free_port(8000, 8100) |
915 | 629 |
916 # Find random UUID if specified 'uuid = random' in config file | 630 # Find random UUID if specified 'uuid = random' in config file |
917 if params.get("uuid") == "random": | 631 if params.get("uuid") == "random": |
918 f = open("/proc/sys/kernel/random/uuid") | 632 f = open("/proc/sys/kernel/random/uuid") |
919 self.uuid = f.read().strip() | 633 self.uuid = f.read().strip() |
920 f.close() | 634 f.close() |
921 | 635 |
922 # Generate or copy MAC addresses for all NICs | 636 # Generate or copy MAC addresses for all NICs |
923 num_nics = len(params.objects("nics")) | 637 num_nics = len(params.objects("nics")) |
924 for vlan in range(num_nics): | 638 for vlan in range(num_nics): |
925 nic_name = params.objects("nics")[vlan] | 639 nic_name = params.objects("nics")[vlan] |
926 nic_params = params.object_params(nic_name) | 640 nic_params = params.object_params(nic_name) |
927 mac = (nic_params.get("nic_mac") or | 641 mac = (nic_params.get("nic_mac") or |
928 mac_source and mac_source.get_mac_address(vlan)) | 642 mac_source and mac_source.get_mac_address(vlan)) |
929 if mac: | 643 if mac: |
930 kvm_utils.set_mac_address(self.instance, vlan, mac) | 644 virt_utils.set_mac_address(self.instance, vlan, mac) |
931 else: | 645 else: |
932 kvm_utils.generate_mac_address(self.instance, vlan) | 646 virt_utils.generate_mac_address(self.instance, vlan) |
933 | 647 |
934 # Assign a PCI assignable device | 648 # Assign a PCI assignable device |
935 self.pci_assignable = None | 649 self.pci_assignable = None |
936 pa_type = params.get("pci_assignable") | 650 pa_type = params.get("pci_assignable") |
937 if pa_type and pa_type != "no": | 651 if pa_type and pa_type != "no": |
938 pa_devices_requested = params.get("devices_requested") | 652 pa_devices_requested = params.get("devices_requested") |
939 | 653 |
940 # Virtual Functions (VF) assignable devices | 654 # Virtual Functions (VF) assignable devices |
941 if pa_type == "vf": | 655 if pa_type == "vf": |
942 self.pci_assignable = kvm_utils.PciAssignable( | 656 self.pci_assignable = virt_utils.PciAssignable( |
943 type=pa_type, | 657 type=pa_type, |
944 driver=params.get("driver"), | 658 driver=params.get("driver"), |
945 driver_option=params.get("driver_option"), | 659 driver_option=params.get("driver_option"), |
946 devices_requested=pa_devices_requested) | 660 devices_requested=pa_devices_requested) |
947 # Physical NIC (PF) assignable devices | 661 # Physical NIC (PF) assignable devices |
948 elif pa_type == "pf": | 662 elif pa_type == "pf": |
949 self.pci_assignable = kvm_utils.PciAssignable( | 663 self.pci_assignable = virt_utils.PciAssignable( |
950 type=pa_type, | 664 type=pa_type, |
951 names=params.get("device_names"), | 665 names=params.get("device_names"), |
952 devices_requested=pa_devices_requested) | 666 devices_requested=pa_devices_requested) |
953 # Working with both VF and PF | 667 # Working with both VF and PF |
954 elif pa_type == "mixed": | 668 elif pa_type == "mixed": |
955 self.pci_assignable = kvm_utils.PciAssignable( | 669 self.pci_assignable = virt_utils.PciAssignable( |
956 type=pa_type, | 670 type=pa_type, |
957 driver=params.get("driver"), | 671 driver=params.get("driver"), |
958 driver_option=params.get("driver_option"), | 672 driver_option=params.get("driver_option"), |
959 names=params.get("device_names"), | 673 names=params.get("device_names"), |
960 devices_requested=pa_devices_requested) | 674 devices_requested=pa_devices_requested) |
961 else: | 675 else: |
962 raise VMBadPATypeError(pa_type) | 676 raise virt_vm.VMBadPATypeError(pa_type) |
963 | 677 |
964 self.pa_pci_ids = self.pci_assignable.request_devs() | 678 self.pa_pci_ids = self.pci_assignable.request_devs() |
965 | 679 |
966 if self.pa_pci_ids: | 680 if self.pa_pci_ids: |
967 logging.debug("Successfuly assigned devices: %s", | 681 logging.debug("Successfuly assigned devices: %s", |
968 self.pa_pci_ids) | 682 self.pa_pci_ids) |
969 else: | 683 else: |
970 raise VMPAError(pa_type) | 684 raise virt_vm.VMPAError(pa_type) |
971 | 685 |
972 # Make qemu command | 686 # Make qemu command |
973 qemu_command = self.make_qemu_command() | 687 qemu_command = self.__make_qemu_command() |
974 | 688 |
975 # Add migration parameters if required | 689 # Add migration parameters if required |
976 if migration_mode == "tcp": | 690 if migration_mode == "tcp": |
977 self.migration_port = kvm_utils.find_free_port(5200, 6000) | 691 self.migration_port = virt_utils.find_free_port(5200, 6000) |
978 qemu_command += " -incoming tcp:0:%d" % self.migration_port | 692 qemu_command += " -incoming tcp:0:%d" % self.migration_port |
979 elif migration_mode == "unix": | 693 elif migration_mode == "unix": |
980 self.migration_file = "/tmp/migration-unix-%s" % self.instance | 694 self.migration_file = "/tmp/migration-unix-%s" % self.instance |
981 qemu_command += " -incoming unix:%s" % self.migration_file | 695 qemu_command += " -incoming unix:%s" % self.migration_file |
982 elif migration_mode == "exec": | 696 elif migration_mode == "exec": |
983 self.migration_port = kvm_utils.find_free_port(5200, 6000) | 697 self.migration_port = virt_utils.find_free_port(5200, 6000) |
984 qemu_command += (' -incoming "exec:nc -l %s"' % | 698 qemu_command += (' -incoming "exec:nc -l %s"' % |
985 self.migration_port) | 699 self.migration_port) |
986 | 700 |
987 logging.info("Running qemu command:\n%s", qemu_command) | 701 logging.info("Running qemu command:\n%s", qemu_command) |
988 self.process = kvm_subprocess.run_bg(qemu_command, None, | 702 self.process = aexpect.run_bg(qemu_command, None, |
989 logging.info, "(qemu) ") | 703 logging.info, "(qemu) ") |
990 | 704 |
991 # Make sure the process was started successfully | 705 # Make sure the process was started successfully |
992 if not self.process.is_alive(): | 706 if not self.process.is_alive(): |
993 e = VMCreateError(qemu_command, | 707 e = virt_vm.VMCreateError(qemu_command, |
994 self.process.get_status(), | 708 self.process.get_status(), |
995 self.process.get_output()) | 709 self.process.get_output()) |
996 self.destroy() | 710 self.destroy() |
997 raise e | 711 raise e |
998 | 712 |
999 # Establish monitor connections | 713 # Establish monitor connections |
1000 self.monitors = [] | 714 self.monitors = [] |
1001 for monitor_name in params.objects("monitors"): | 715 for monitor_name in params.objects("monitors"): |
1002 monitor_params = params.object_params(monitor_name) | 716 monitor_params = params.object_params(monitor_name) |
1003 # Wait for monitor connection to succeed | 717 # Wait for monitor connection to succeed |
1004 end_time = time.time() + timeout | 718 end_time = time.time() + timeout |
1005 while time.time() < end_time: | 719 while time.time() < end_time: |
(...skipping 17 matching lines...) Expand all Loading... |
1023 self.destroy() | 737 self.destroy() |
1024 raise e | 738 raise e |
1025 # Add this monitor to the list | 739 # Add this monitor to the list |
1026 self.monitors += [monitor] | 740 self.monitors += [monitor] |
1027 | 741 |
1028 # Get the output so far, to see if we have any problems with | 742 # Get the output so far, to see if we have any problems with |
1029 # KVM modules or with hugepage setup. | 743 # KVM modules or with hugepage setup. |
1030 output = self.process.get_output() | 744 output = self.process.get_output() |
1031 | 745 |
1032 if re.search("Could not initialize KVM", output, re.IGNORECASE): | 746 if re.search("Could not initialize KVM", output, re.IGNORECASE): |
1033 e = VMKVMInitError(qemu_command, self.process.get_output()) | 747 e = virt_vm.VMKVMInitError(qemu_command, self.process.get_output
()) |
1034 self.destroy() | 748 self.destroy() |
1035 raise e | 749 raise e |
1036 | 750 |
1037 if "alloc_mem_area" in output: | 751 if "alloc_mem_area" in output: |
1038 e = VMHugePageError(qemu_command, self.process.get_output()) | 752 e = virt_vm.VMHugePageError(qemu_command, self.process.get_outpu
t()) |
1039 self.destroy() | 753 self.destroy() |
1040 raise e | 754 raise e |
1041 | 755 |
1042 logging.debug("VM appears to be alive with PID %s", self.get_pid()) | 756 logging.debug("VM appears to be alive with PID %s", self.get_pid()) |
1043 | 757 |
1044 # Establish a session with the serial console -- requires a version | 758 # Establish a session with the serial console -- requires a version |
1045 # of netcat that supports -U | 759 # of netcat that supports -U |
1046 self.serial_console = kvm_subprocess.ShellSession( | 760 self.serial_console = aexpect.ShellSession( |
1047 "nc -U %s" % self.get_serial_console_filename(), | 761 "nc -U %s" % self.get_serial_console_filename(), |
1048 auto_close=False, | 762 auto_close=False, |
1049 output_func=kvm_utils.log_line, | 763 output_func=virt_utils.log_line, |
1050 output_params=("serial-%s.log" % name,)) | 764 output_params=("serial-%s.log" % name,)) |
1051 | 765 |
1052 finally: | 766 finally: |
1053 fcntl.lockf(lockfile, fcntl.LOCK_UN) | 767 fcntl.lockf(lockfile, fcntl.LOCK_UN) |
1054 lockfile.close() | 768 lockfile.close() |
1055 | 769 |
1056 | 770 |
1057 def destroy(self, gracefully=True, free_mac_addresses=True): | 771 def destroy(self, gracefully=True, free_mac_addresses=True): |
1058 """ | 772 """ |
1059 Destroy the VM. | 773 Destroy the VM. |
(...skipping 13 matching lines...) Expand all Loading... |
1073 if self.is_dead(): | 787 if self.is_dead(): |
1074 return | 788 return |
1075 | 789 |
1076 logging.debug("Destroying VM with PID %s...", self.get_pid()) | 790 logging.debug("Destroying VM with PID %s...", self.get_pid()) |
1077 | 791 |
1078 if gracefully and self.params.get("shutdown_command"): | 792 if gracefully and self.params.get("shutdown_command"): |
1079 # Try to destroy with shell command | 793 # Try to destroy with shell command |
1080 logging.debug("Trying to shutdown VM with shell command...") | 794 logging.debug("Trying to shutdown VM with shell command...") |
1081 try: | 795 try: |
1082 session = self.login() | 796 session = self.login() |
1083 except (kvm_utils.LoginError, VMError), e: | 797 except (virt_utils.LoginError, virt_vm.VMError), e: |
1084 logging.debug(e) | 798 logging.debug(e) |
1085 else: | 799 else: |
1086 try: | 800 try: |
1087 # Send the shutdown command | 801 # Send the shutdown command |
1088 session.sendline(self.params.get("shutdown_command")) | 802 session.sendline(self.params.get("shutdown_command")) |
1089 logging.debug("Shutdown command sent; waiting for VM " | 803 logging.debug("Shutdown command sent; waiting for VM " |
1090 "to go down...") | 804 "to go down...") |
1091 if kvm_utils.wait_for(self.is_dead, 60, 1, 1): | 805 if virt_utils.wait_for(self.is_dead, 60, 1, 1): |
1092 logging.debug("VM is down") | 806 logging.debug("VM is down") |
1093 return | 807 return |
1094 finally: | 808 finally: |
1095 session.close() | 809 session.close() |
1096 | 810 |
1097 if self.monitor: | 811 if self.monitor: |
1098 # Try to destroy with a monitor command | 812 # Try to destroy with a monitor command |
1099 logging.debug("Trying to kill VM with monitor command...") | 813 logging.debug("Trying to kill VM with monitor command...") |
1100 try: | 814 try: |
1101 self.monitor.quit() | 815 self.monitor.quit() |
1102 except kvm_monitor.MonitorError, e: | 816 except kvm_monitor.MonitorError, e: |
1103 logging.warn(e) | 817 logging.warn(e) |
1104 else: | 818 else: |
1105 # Wait for the VM to be really dead | 819 # Wait for the VM to be really dead |
1106 if kvm_utils.wait_for(self.is_dead, 5, 0.5, 0.5): | 820 if virt_utils.wait_for(self.is_dead, 5, 0.5, 0.5): |
1107 logging.debug("VM is down") | 821 logging.debug("VM is down") |
1108 return | 822 return |
1109 | 823 |
1110 # If the VM isn't dead yet... | 824 # If the VM isn't dead yet... |
1111 logging.debug("Cannot quit normally; sending a kill to close the " | 825 logging.debug("Cannot quit normally; sending a kill to close the " |
1112 "deal...") | 826 "deal...") |
1113 kvm_utils.kill_process_tree(self.process.get_pid(), 9) | 827 virt_utils.kill_process_tree(self.process.get_pid(), 9) |
1114 # Wait for the VM to be really dead | 828 # Wait for the VM to be really dead |
1115 if kvm_utils.wait_for(self.is_dead, 5, 0.5, 0.5): | 829 if virt_utils.wait_for(self.is_dead, 5, 0.5, 0.5): |
1116 logging.debug("VM is down") | 830 logging.debug("VM is down") |
1117 return | 831 return |
1118 | 832 |
1119 logging.error("Process %s is a zombie!", self.process.get_pid()) | 833 logging.error("Process %s is a zombie!", self.process.get_pid()) |
1120 | 834 |
1121 finally: | 835 finally: |
1122 self.monitors = [] | 836 self.monitors = [] |
1123 if self.pci_assignable: | 837 if self.pci_assignable: |
1124 self.pci_assignable.release_devs() | 838 self.pci_assignable.release_devs() |
1125 if self.process: | 839 if self.process: |
(...skipping 26 matching lines...) Expand all Loading... |
1152 If no monitors exist, or if main_monitor refers to a nonexistent | 866 If no monitors exist, or if main_monitor refers to a nonexistent |
1153 monitor, return None. | 867 monitor, return None. |
1154 """ | 868 """ |
1155 for m in self.monitors: | 869 for m in self.monitors: |
1156 if m.name == self.params.get("main_monitor"): | 870 if m.name == self.params.get("main_monitor"): |
1157 return m | 871 return m |
1158 if self.monitors and not self.params.get("main_monitor"): | 872 if self.monitors and not self.params.get("main_monitor"): |
1159 return self.monitors[0] | 873 return self.monitors[0] |
1160 | 874 |
1161 | 875 |
1162 def verify_alive(self): | |
1163 """ | |
1164 Make sure the VM is alive and that the main monitor is responsive. | |
1165 | |
1166 @raise VMDeadError: If the VM is dead | |
1167 @raise: Various monitor exceptions if the monitor is unresponsive | |
1168 """ | |
1169 if self.is_dead(): | |
1170 raise VMDeadError(self.process.get_status(), | |
1171 self.process.get_output()) | |
1172 if self.monitors: | |
1173 self.monitor.verify_responsive() | |
1174 | |
1175 | |
1176 def is_alive(self): | |
1177 """ | |
1178 Return True if the VM is alive and its monitor is responsive. | |
1179 """ | |
1180 return not self.is_dead() and (not self.monitors or | |
1181 self.monitor.is_responsive()) | |
1182 | |
1183 | |
1184 def is_dead(self): | |
1185 """ | |
1186 Return True if the qemu process is dead. | |
1187 """ | |
1188 return not self.process or not self.process.is_alive() | |
1189 | |
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 | |
1205 def get_params(self): | |
1206 """ | |
1207 Return the VM's params dict. Most modified params take effect only | |
1208 upon VM.create(). | |
1209 """ | |
1210 return self.params | |
1211 | |
1212 | |
1213 def get_monitor_filename(self, monitor_name): | 876 def get_monitor_filename(self, monitor_name): |
1214 """ | 877 """ |
1215 Return the filename corresponding to a given monitor name. | 878 Return the filename corresponding to a given monitor name. |
1216 """ | 879 """ |
1217 return "/tmp/monitor-%s-%s" % (monitor_name, self.instance) | 880 return "/tmp/monitor-%s-%s" % (monitor_name, self.instance) |
1218 | 881 |
1219 | 882 |
1220 def get_monitor_filenames(self): | 883 def get_monitor_filenames(self): |
1221 """ | 884 """ |
1222 Return a list of all monitor filenames (as specified in the VM's | 885 Return a list of all monitor filenames (as specified in the VM's |
1223 params). | 886 params). |
1224 """ | 887 """ |
1225 return [self.get_monitor_filename(m) for m in | 888 return [self.get_monitor_filename(m) for m in |
1226 self.params.objects("monitors")] | 889 self.params.objects("monitors")] |
1227 | 890 |
1228 | 891 |
1229 def get_serial_console_filename(self): | |
1230 """ | |
1231 Return the serial console filename. | |
1232 """ | |
1233 return "/tmp/serial-%s" % self.instance | |
1234 | |
1235 | |
1236 def get_testlog_filename(self): | |
1237 """ | |
1238 Return the testlog filename. | |
1239 """ | |
1240 return "/tmp/testlog-%s" % self.instance | |
1241 | |
1242 | |
1243 def get_address(self, index=0): | 892 def get_address(self, index=0): |
1244 """ | 893 """ |
1245 Return the address of a NIC of the guest, in host space. | 894 Return the address of a NIC of the guest, in host space. |
1246 | 895 |
1247 If port redirection is used, return 'localhost' (the NIC has no IP | 896 If port redirection is used, return 'localhost' (the NIC has no IP |
1248 address of its own). Otherwise return the NIC's IP address. | 897 address of its own). Otherwise return the NIC's IP address. |
1249 | 898 |
1250 @param index: Index of the NIC whose address is requested. | 899 @param index: Index of the NIC whose address is requested. |
1251 @raise VMMACAddressMissingError: If no MAC address is defined for the | 900 @raise VMMACAddressMissingError: If no MAC address is defined for the |
1252 requested NIC | 901 requested NIC |
1253 @raise VMIPAddressMissingError: If no IP address is found for the the | 902 @raise VMIPAddressMissingError: If no IP address is found for the the |
1254 NIC's MAC address | 903 NIC's MAC address |
1255 @raise VMAddressVerificationError: If the MAC-IP address mapping cannot | 904 @raise VMAddressVerificationError: If the MAC-IP address mapping cannot |
1256 be verified (using arping) | 905 be verified (using arping) |
1257 """ | 906 """ |
1258 nics = self.params.objects("nics") | 907 nics = self.params.objects("nics") |
1259 nic_name = nics[index] | 908 nic_name = nics[index] |
1260 nic_params = self.params.object_params(nic_name) | 909 nic_params = self.params.object_params(nic_name) |
1261 if nic_params.get("nic_mode") == "tap": | 910 if nic_params.get("nic_mode") == "tap": |
1262 mac = self.get_mac_address(index).lower() | 911 mac = self.get_mac_address(index).lower() |
1263 # Get the IP address from the cache | 912 # Get the IP address from the cache |
1264 ip = self.address_cache.get(mac) | 913 ip = self.address_cache.get(mac) |
1265 if not ip: | 914 if not ip: |
1266 raise VMIPAddressMissingError(mac) | 915 raise virt_vm.VMIPAddressMissingError(mac) |
1267 # Make sure the IP address is assigned to this guest | 916 # Make sure the IP address is assigned to this guest |
1268 macs = [self.get_mac_address(i) for i in range(len(nics))] | 917 macs = [self.get_mac_address(i) for i in range(len(nics))] |
1269 if not kvm_utils.verify_ip_address_ownership(ip, macs): | 918 if not virt_utils.verify_ip_address_ownership(ip, macs): |
1270 raise VMAddressVerificationError(mac, ip) | 919 raise virt_vm.VMAddressVerificationError(mac, ip) |
1271 return ip | 920 return ip |
1272 else: | 921 else: |
1273 return "localhost" | 922 return "localhost" |
1274 | 923 |
1275 | 924 |
1276 def get_port(self, port, nic_index=0): | 925 def get_port(self, port, nic_index=0): |
1277 """ | 926 """ |
1278 Return the port in host space corresponding to port in guest space. | 927 Return the port in host space corresponding to port in guest space. |
1279 | 928 |
1280 @param port: Port number in host space. | 929 @param port: Port number in host space. |
1281 @param nic_index: Index of the NIC. | 930 @param nic_index: Index of the NIC. |
1282 @return: If port redirection is used, return the host port redirected | 931 @return: If port redirection is used, return the host port redirected |
1283 to guest port port. Otherwise return port. | 932 to guest port port. Otherwise return port. |
1284 @raise VMPortNotRedirectedError: If an unredirected port is requested | 933 @raise VMPortNotRedirectedError: If an unredirected port is requested |
1285 in user mode | 934 in user mode |
1286 """ | 935 """ |
1287 nic_name = self.params.objects("nics")[nic_index] | 936 nic_name = self.params.objects("nics")[nic_index] |
1288 nic_params = self.params.object_params(nic_name) | 937 nic_params = self.params.object_params(nic_name) |
1289 if nic_params.get("nic_mode") == "tap": | 938 if nic_params.get("nic_mode") == "tap": |
1290 return port | 939 return port |
1291 else: | 940 else: |
1292 try: | 941 try: |
1293 return self.redirs[port] | 942 return self.redirs[port] |
1294 except KeyError: | 943 except KeyError: |
1295 raise VMPortNotRedirectedError(port) | 944 raise virt_vm.VMPortNotRedirectedError(port) |
1296 | 945 |
1297 | 946 |
1298 def get_peer(self, netid): | 947 def get_peer(self, netid): |
1299 """ | 948 """ |
1300 Return the peer of netdev or network deivce. | 949 Return the peer of netdev or network deivce. |
1301 | 950 |
1302 @param netid: id of netdev or device | 951 @param netid: id of netdev or device |
1303 @return: id of the peer device otherwise None | 952 @return: id of the peer device otherwise None |
1304 """ | 953 """ |
1305 network_info = self.monitor.info("network") | 954 network_info = self.monitor.info("network") |
(...skipping 22 matching lines...) Expand all Loading... |
1328 """ | 977 """ |
1329 Return the MAC address of a NIC. | 978 Return the MAC address of a NIC. |
1330 | 979 |
1331 @param nic_index: Index of the NIC | 980 @param nic_index: Index of the NIC |
1332 @raise VMMACAddressMissingError: If no MAC address is defined for the | 981 @raise VMMACAddressMissingError: If no MAC address is defined for the |
1333 requested NIC | 982 requested NIC |
1334 """ | 983 """ |
1335 nic_name = self.params.objects("nics")[nic_index] | 984 nic_name = self.params.objects("nics")[nic_index] |
1336 nic_params = self.params.object_params(nic_name) | 985 nic_params = self.params.object_params(nic_name) |
1337 mac = (nic_params.get("nic_mac") or | 986 mac = (nic_params.get("nic_mac") or |
1338 kvm_utils.get_mac_address(self.instance, nic_index)) | 987 virt_utils.get_mac_address(self.instance, nic_index)) |
1339 if not mac: | 988 if not mac: |
1340 raise VMMACAddressMissingError(nic_index) | 989 raise virt_vm.VMMACAddressMissingError(nic_index) |
1341 return mac | 990 return mac |
1342 | 991 |
1343 | 992 |
1344 def free_mac_address(self, nic_index=0): | 993 def free_mac_address(self, nic_index=0): |
1345 """ | 994 """ |
1346 Free a NIC's MAC address. | 995 Free a NIC's MAC address. |
1347 | 996 |
1348 @param nic_index: Index of the NIC | 997 @param nic_index: Index of the NIC |
1349 """ | 998 """ |
1350 kvm_utils.free_mac_address(self.instance, nic_index) | 999 virt_utils.free_mac_address(self.instance, nic_index) |
1351 | 1000 |
1352 | 1001 |
1353 def get_pid(self): | 1002 def get_pid(self): |
1354 """ | 1003 """ |
1355 Return the VM's PID. If the VM is dead return None. | 1004 Return the VM's PID. If the VM is dead return None. |
1356 | 1005 |
1357 @note: This works under the assumption that self.process.get_pid() | 1006 @note: This works under the assumption that self.process.get_pid() |
1358 returns the PID of the parent shell process. | 1007 returns the PID of the parent shell process. |
1359 """ | 1008 """ |
1360 try: | 1009 try: |
(...skipping 24 matching lines...) Expand all Loading... |
1385 logging.error("Could not get shared memory info from dead VM.") | 1034 logging.error("Could not get shared memory info from dead VM.") |
1386 return None | 1035 return None |
1387 | 1036 |
1388 filename = "/proc/%d/statm" % self.get_pid() | 1037 filename = "/proc/%d/statm" % self.get_pid() |
1389 shm = int(open(filename).read().split()[2]) | 1038 shm = int(open(filename).read().split()[2]) |
1390 # statm stores informations in pages, translate it to MB | 1039 # statm stores informations in pages, translate it to MB |
1391 return shm * 4.0 / 1024 | 1040 return shm * 4.0 / 1024 |
1392 | 1041 |
1393 | 1042 |
1394 @error.context_aware | 1043 @error.context_aware |
1395 def login(self, nic_index=0, timeout=10): | |
1396 """ | |
1397 Log into the guest via SSH/Telnet/Netcat. | |
1398 If timeout expires while waiting for output from the guest (e.g. a | |
1399 password prompt or a shell prompt) -- fail. | |
1400 | |
1401 @param nic_index: The index of the NIC to connect to. | |
1402 @param timeout: Time (seconds) before giving up logging into the | |
1403 guest. | |
1404 @return: A ShellSession object. | |
1405 """ | |
1406 error.context("logging into '%s'" % self.name) | |
1407 username = self.params.get("username", "") | |
1408 password = self.params.get("password", "") | |
1409 prompt = self.params.get("shell_prompt", "[\#\$]") | |
1410 linesep = eval("'%s'" % self.params.get("shell_linesep", r"\n")) | |
1411 client = self.params.get("shell_client") | |
1412 address = self.get_address(nic_index) | |
1413 port = self.get_port(int(self.params.get("shell_port"))) | |
1414 log_filename = ("session-%s-%s.log" % | |
1415 (self.name, kvm_utils.generate_random_string(4))) | |
1416 session = kvm_utils.remote_login(client, address, port, username, | |
1417 password, prompt, linesep, | |
1418 log_filename, timeout) | |
1419 session.set_status_test_command(self.params.get("status_test_command", | |
1420 "")) | |
1421 return session | |
1422 | |
1423 | |
1424 def remote_login(self, nic_index=0, timeout=10): | |
1425 """ | |
1426 Alias for login() for backward compatibility. | |
1427 """ | |
1428 return self.login(nic_index, timeout) | |
1429 | |
1430 | |
1431 def wait_for_login(self, nic_index=0, timeout=240, internal_timeout=10): | |
1432 """ | |
1433 Make multiple attempts to log into the guest via SSH/Telnet/Netcat. | |
1434 | |
1435 @param nic_index: The index of the NIC to connect to. | |
1436 @param timeout: Time (seconds) to keep trying to log in. | |
1437 @param internal_timeout: Timeout to pass to login(). | |
1438 @return: A ShellSession object. | |
1439 """ | |
1440 logging.debug("Attempting to log into '%s' (timeout %ds)", self.name, | |
1441 timeout) | |
1442 end_time = time.time() + timeout | |
1443 while time.time() < end_time: | |
1444 try: | |
1445 return self.login(nic_index, internal_timeout) | |
1446 except (kvm_utils.LoginError, VMError), e: | |
1447 logging.debug(e) | |
1448 time.sleep(2) | |
1449 # Timeout expired; try one more time but don't catch exceptions | |
1450 return self.login(nic_index, internal_timeout) | |
1451 | |
1452 | |
1453 @error.context_aware | |
1454 def copy_files_to(self, host_path, guest_path, nic_index=0, verbose=False, | |
1455 timeout=600): | |
1456 """ | |
1457 Transfer files to the remote host(guest). | |
1458 | |
1459 @param host_path: Host path | |
1460 @param guest_path: Guest path | |
1461 @param nic_index: The index of the NIC to connect to. | |
1462 @param verbose: If True, log some stats using logging.debug (RSS only) | |
1463 @param timeout: Time (seconds) before giving up on doing the remote | |
1464 copy. | |
1465 """ | |
1466 error.context("sending file(s) to '%s'" % self.name) | |
1467 username = self.params.get("username", "") | |
1468 password = self.params.get("password", "") | |
1469 client = self.params.get("file_transfer_client") | |
1470 address = self.get_address(nic_index) | |
1471 port = self.get_port(int(self.params.get("file_transfer_port"))) | |
1472 log_filename = ("transfer-%s-to-%s-%s.log" % | |
1473 (self.name, address, | |
1474 kvm_utils.generate_random_string(4))) | |
1475 kvm_utils.copy_files_to(address, client, username, password, port, | |
1476 host_path, guest_path, log_filename, verbose, | |
1477 timeout) | |
1478 | |
1479 | |
1480 @error.context_aware | |
1481 def copy_files_from(self, guest_path, host_path, nic_index=0, | |
1482 verbose=False, timeout=600): | |
1483 """ | |
1484 Transfer files from the guest. | |
1485 | |
1486 @param host_path: Guest path | |
1487 @param guest_path: Host path | |
1488 @param nic_index: The index of the NIC to connect to. | |
1489 @param verbose: If True, log some stats using logging.debug (RSS only) | |
1490 @param timeout: Time (seconds) before giving up on doing the remote | |
1491 copy. | |
1492 """ | |
1493 error.context("receiving file(s) from '%s'" % self.name) | |
1494 username = self.params.get("username", "") | |
1495 password = self.params.get("password", "") | |
1496 client = self.params.get("file_transfer_client") | |
1497 address = self.get_address(nic_index) | |
1498 port = self.get_port(int(self.params.get("file_transfer_port"))) | |
1499 log_filename = ("transfer-%s-from-%s-%s.log" % | |
1500 (self.name, address, | |
1501 kvm_utils.generate_random_string(4))) | |
1502 kvm_utils.copy_files_from(address, client, username, password, port, | |
1503 guest_path, host_path, log_filename, | |
1504 verbose, timeout) | |
1505 | |
1506 | |
1507 @error.context_aware | |
1508 def serial_login(self, timeout=10): | |
1509 """ | |
1510 Log into the guest via the serial console. | |
1511 If timeout expires while waiting for output from the guest (e.g. a | |
1512 password prompt or a shell prompt) -- fail. | |
1513 | |
1514 @param timeout: Time (seconds) before giving up logging into the guest. | |
1515 @return: ShellSession object on success and None on failure. | |
1516 """ | |
1517 error.context("logging into '%s' via serial console" % self.name) | |
1518 username = self.params.get("username", "") | |
1519 password = self.params.get("password", "") | |
1520 prompt = self.params.get("shell_prompt", "[\#\$]") | |
1521 linesep = eval("'%s'" % self.params.get("shell_linesep", r"\n")) | |
1522 status_test_command = self.params.get("status_test_command", "") | |
1523 | |
1524 self.serial_console.set_linesep(linesep) | |
1525 self.serial_console.set_status_test_command(status_test_command) | |
1526 | |
1527 # Try to get a login prompt | |
1528 self.serial_console.sendline() | |
1529 | |
1530 kvm_utils._remote_login(self.serial_console, username, password, | |
1531 prompt, timeout) | |
1532 return self.serial_console | |
1533 | |
1534 | |
1535 def wait_for_serial_login(self, timeout=240, internal_timeout=10): | |
1536 """ | |
1537 Make multiple attempts to log into the guest via serial console. | |
1538 | |
1539 @param timeout: Time (seconds) to keep trying to log in. | |
1540 @param internal_timeout: Timeout to pass to serial_login(). | |
1541 @return: A ShellSession object. | |
1542 """ | |
1543 logging.debug("Attempting to log into '%s' via serial console " | |
1544 "(timeout %ds)", self.name, timeout) | |
1545 end_time = time.time() + timeout | |
1546 while time.time() < end_time: | |
1547 try: | |
1548 return self.serial_login(internal_timeout) | |
1549 except kvm_utils.LoginError, e: | |
1550 logging.debug(e) | |
1551 time.sleep(2) | |
1552 # Timeout expired; try one more time but don't catch exceptions | |
1553 return self.serial_login(internal_timeout) | |
1554 | |
1555 | |
1556 @error.context_aware | |
1557 def migrate(self, timeout=3600, protocol="tcp", cancel_delay=None, | 1044 def migrate(self, timeout=3600, protocol="tcp", cancel_delay=None, |
1558 offline=False, stable_check=False, clean=True, | 1045 offline=False, stable_check=False, clean=True, |
1559 save_path="/tmp", dest_host="localhost", remote_port=None): | 1046 save_path="/tmp", dest_host="localhost", remote_port=None): |
1560 """ | 1047 """ |
1561 Migrate the VM. | 1048 Migrate the VM. |
1562 | 1049 |
1563 If the migration is local, the VM object's state is switched with that | 1050 If the migration is local, the VM object's state is switched with that |
1564 of the destination VM. Otherwise, the state is switched with that of | 1051 of the destination VM. Otherwise, the state is switched with that of |
1565 a dead VM (returned by self.clone()). | 1052 a dead VM (returned by self.clone()). |
1566 | 1053 |
1567 @param timeout: Time to wait for migration to complete. | 1054 @param timeout: Time to wait for migration to complete. |
1568 @param protocol: Migration protocol ('tcp', 'unix' or 'exec'). | 1055 @param protocol: Migration protocol (as defined in MIGRATION_PROTOS) |
1569 @param cancel_delay: If provided, specifies a time duration after which | 1056 @param cancel_delay: If provided, specifies a time duration after which |
1570 migration will be canceled. Used for testing migrate_cancel. | 1057 migration will be canceled. Used for testing migrate_cancel. |
1571 @param offline: If True, pause the source VM before migration. | 1058 @param offline: If True, pause the source VM before migration. |
1572 @param stable_check: If True, compare the VM's state after migration to | 1059 @param stable_check: If True, compare the VM's state after migration to |
1573 its state before migration and raise an exception if they | 1060 its state before migration and raise an exception if they |
1574 differ. | 1061 differ. |
1575 @param clean: If True, delete the saved state files (relevant only if | 1062 @param clean: If True, delete the saved state files (relevant only if |
1576 stable_check is also True). | 1063 stable_check is also True). |
1577 @save_path: The path for state files. | 1064 @save_path: The path for state files. |
1578 @param dest_host: Destination host (defaults to 'localhost'). | 1065 @param dest_host: Destination host (defaults to 'localhost'). |
1579 @param remote_port: Port to use for remote migration. | 1066 @param remote_port: Port to use for remote migration. |
1580 """ | 1067 """ |
| 1068 if protocol not in self.MIGRATION_PROTOS: |
| 1069 raise virt_vm.VMMigrateProtoUnsupportedError |
| 1070 |
1581 error.base_context("migrating '%s'" % self.name) | 1071 error.base_context("migrating '%s'" % self.name) |
1582 | 1072 |
1583 def mig_finished(): | 1073 def mig_finished(): |
1584 o = self.monitor.info("migrate") | 1074 o = self.monitor.info("migrate") |
1585 if isinstance(o, str): | 1075 if isinstance(o, str): |
1586 return "status: active" not in o | 1076 return "status: active" not in o |
1587 else: | 1077 else: |
1588 return o.get("status") != "active" | 1078 return o.get("status") != "active" |
1589 | 1079 |
1590 def mig_succeeded(): | 1080 def mig_succeeded(): |
(...skipping 13 matching lines...) Expand all Loading... |
1604 def mig_cancelled(): | 1094 def mig_cancelled(): |
1605 o = self.monitor.info("migrate") | 1095 o = self.monitor.info("migrate") |
1606 if isinstance(o, str): | 1096 if isinstance(o, str): |
1607 return ("Migration status: cancelled" in o or | 1097 return ("Migration status: cancelled" in o or |
1608 "Migration status: canceled" in o) | 1098 "Migration status: canceled" in o) |
1609 else: | 1099 else: |
1610 return (o.get("status") == "cancelled" or | 1100 return (o.get("status") == "cancelled" or |
1611 o.get("status") == "canceled") | 1101 o.get("status") == "canceled") |
1612 | 1102 |
1613 def wait_for_migration(): | 1103 def wait_for_migration(): |
1614 if not kvm_utils.wait_for(mig_finished, timeout, 2, 2, | 1104 if not virt_utils.wait_for(mig_finished, timeout, 2, 2, |
1615 "Waiting for migration to complete"): | 1105 "Waiting for migration to complete"): |
1616 raise VMMigrateTimeoutError("Timeout expired while waiting " | 1106 raise virt_vm.VMMigrateTimeoutError("Timeout expired while waiti
ng " |
1617 "for migration to finish") | 1107 "for migration to finish") |
1618 | 1108 |
1619 local = dest_host == "localhost" | 1109 local = dest_host == "localhost" |
1620 | 1110 |
1621 clone = self.clone() | 1111 clone = self.clone() |
1622 if local: | 1112 if local: |
1623 error.context("creating destination VM") | 1113 error.context("creating destination VM") |
1624 if stable_check: | 1114 if stable_check: |
1625 # Pause the dest vm after creation | 1115 # Pause the dest vm after creation |
1626 extra_params = clone.params.get("extra_params", "") + " -S" | 1116 extra_params = clone.params.get("extra_params", "") + " -S" |
(...skipping 14 matching lines...) Expand all Loading... |
1641 | 1131 |
1642 if offline: | 1132 if offline: |
1643 self.monitor.cmd("stop") | 1133 self.monitor.cmd("stop") |
1644 | 1134 |
1645 logging.info("Migrating to %s", uri) | 1135 logging.info("Migrating to %s", uri) |
1646 self.monitor.migrate(uri) | 1136 self.monitor.migrate(uri) |
1647 | 1137 |
1648 if cancel_delay: | 1138 if cancel_delay: |
1649 time.sleep(cancel_delay) | 1139 time.sleep(cancel_delay) |
1650 self.monitor.cmd("migrate_cancel") | 1140 self.monitor.cmd("migrate_cancel") |
1651 if not kvm_utils.wait_for(mig_cancelled, 60, 2, 2, | 1141 if not virt_utils.wait_for(mig_cancelled, 60, 2, 2, |
1652 "Waiting for migration " | 1142 "Waiting for migration " |
1653 "cancellation"): | 1143 "cancellation"): |
1654 raise VMMigrateCancelError("Cannot cancel migration") | 1144 raise virt_vm.VMMigrateCancelError("Cannot cancel migration"
) |
1655 return | 1145 return |
1656 | 1146 |
1657 wait_for_migration() | 1147 wait_for_migration() |
1658 | 1148 |
1659 # Report migration status | 1149 # Report migration status |
1660 if mig_succeeded(): | 1150 if mig_succeeded(): |
1661 logging.info("Migration completed successfully") | 1151 logging.info("Migration completed successfully") |
1662 elif mig_failed(): | 1152 elif mig_failed(): |
1663 raise VMMigrateFailedError("Migration failed") | 1153 raise virt_vm.VMMigrateFailedError("Migration failed") |
1664 else: | 1154 else: |
1665 raise VMMigrateFailedError("Migration ended with unknown " | 1155 raise virt_vm.VMMigrateFailedError("Migration ended with " |
1666 "status") | 1156 "unknown status") |
1667 | 1157 |
1668 # Switch self <-> clone | 1158 # Switch self <-> clone |
1669 temp = self.clone(copy_state=True) | 1159 temp = self.clone(copy_state=True) |
1670 self.__dict__ = clone.__dict__ | 1160 self.__dict__ = clone.__dict__ |
1671 clone = temp | 1161 clone = temp |
1672 | 1162 |
1673 # From now on, clone is the source VM that will soon be destroyed | 1163 # From now on, clone is the source VM that will soon be destroyed |
1674 # and self is the destination VM that will remain alive. If this | 1164 # and self is the destination VM that will remain alive. If this |
1675 # is remote migration, self is a dead VM object. | 1165 # is remote migration, self is a dead VM object. |
1676 | 1166 |
1677 error.context("after migration") | 1167 error.context("after migration") |
1678 if local: | 1168 if local: |
1679 time.sleep(1) | 1169 time.sleep(1) |
1680 self.verify_alive() | 1170 self.verify_alive() |
1681 self.verify_kernel_crash() | |
1682 | 1171 |
1683 if local and stable_check: | 1172 if local and stable_check: |
1684 try: | 1173 try: |
1685 save1 = os.path.join(save_path, "src-" + clone.instance) | 1174 save1 = os.path.join(save_path, "src-" + clone.instance) |
1686 save2 = os.path.join(save_path, "dst-" + self.instance) | 1175 save2 = os.path.join(save_path, "dst-" + self.instance) |
1687 clone.save_to_file(save1) | 1176 clone.save_to_file(save1) |
1688 self.save_to_file(save2) | 1177 self.save_to_file(save2) |
1689 # Fail if we see deltas | 1178 # Fail if we see deltas |
1690 md5_save1 = utils.hash_file(save1) | 1179 md5_save1 = utils.hash_file(save1) |
1691 md5_save2 = utils.hash_file(save2) | 1180 md5_save2 = utils.hash_file(save2) |
1692 if md5_save1 != md5_save2: | 1181 if md5_save1 != md5_save2: |
1693 raise VMMigrateStateMismatchError(md5_save1, md5_save2) | 1182 raise virt_vm.VMMigrateStateMismatchError(md5_save1, |
| 1183 md5_save2) |
1694 finally: | 1184 finally: |
1695 if clean: | 1185 if clean: |
1696 if os.path.isfile(save1): | 1186 if os.path.isfile(save1): |
1697 os.remove(save1) | 1187 os.remove(save1) |
1698 if os.path.isfile(save2): | 1188 if os.path.isfile(save2): |
1699 os.remove(save2) | 1189 os.remove(save2) |
1700 | 1190 |
1701 finally: | 1191 finally: |
1702 # If we're doing remote migration and it's completed successfully, | 1192 # If we're doing remote migration and it's completed successfully, |
1703 # self points to a dead VM object | 1193 # self points to a dead VM object |
(...skipping 29 matching lines...) Expand all Loading... |
1733 for m in qmp_monitors: | 1223 for m in qmp_monitors: |
1734 m.clear_events() | 1224 m.clear_events() |
1735 # Send a system_reset monitor command | 1225 # Send a system_reset monitor command |
1736 self.monitor.cmd("system_reset") | 1226 self.monitor.cmd("system_reset") |
1737 # Look for RESET QMP events | 1227 # Look for RESET QMP events |
1738 time.sleep(1) | 1228 time.sleep(1) |
1739 for m in qmp_monitors: | 1229 for m in qmp_monitors: |
1740 if m.get_event("RESET"): | 1230 if m.get_event("RESET"): |
1741 logging.info("RESET QMP event received") | 1231 logging.info("RESET QMP event received") |
1742 else: | 1232 else: |
1743 raise VMRebootError("RESET QMP event not received after " | 1233 raise virt_vm.VMRebootError("RESET QMP event not received " |
1744 "system_reset (monitor '%s')" % m.name) | 1234 "after system_reset " |
| 1235 "(monitor '%s')" % m.name) |
1745 else: | 1236 else: |
1746 raise VMRebootError("Unknown reboot method: %s" % method) | 1237 raise virt_vm.VMRebootError("Unknown reboot method: %s" % method) |
1747 | 1238 |
1748 error.context("waiting for guest to go down", logging.info) | 1239 error.context("waiting for guest to go down", logging.info) |
1749 if not kvm_utils.wait_for(lambda: | 1240 if not virt_utils.wait_for(lambda: |
1750 not session.is_responsive(timeout=30), | 1241 not session.is_responsive(timeout=30), |
1751 120, 0, 1): | 1242 120, 0, 1): |
1752 raise VMRebootError("Guest refuses to go down") | 1243 raise virt_vm.VMRebootError("Guest refuses to go down") |
1753 session.close() | 1244 session.close() |
1754 | 1245 |
1755 error.context("logging in after reboot", logging.info) | 1246 error.context("logging in after reboot", logging.info) |
1756 return self.wait_for_login(nic_index, timeout=timeout) | 1247 return self.wait_for_login(nic_index, timeout=timeout) |
1757 | 1248 |
1758 | 1249 |
1759 def send_key(self, keystr): | 1250 def send_key(self, keystr): |
1760 """ | 1251 """ |
1761 Send a key event to the VM. | 1252 Send a key event to the VM. |
1762 | 1253 |
1763 @param: keystr: A key event string (e.g. "ctrl-alt-delete") | 1254 @param: keystr: A key event string (e.g. "ctrl-alt-delete") |
1764 """ | 1255 """ |
1765 # For compatibility with versions of QEMU that do not recognize all | 1256 # For compatibility with versions of QEMU that do not recognize all |
1766 # key names: replace keyname with the hex value from the dict, which | 1257 # key names: replace keyname with the hex value from the dict, which |
1767 # QEMU will definitely accept | 1258 # QEMU will definitely accept |
1768 dict = {"comma": "0x33", | 1259 dict = {"comma": "0x33", |
1769 "dot": "0x34", | 1260 "dot": "0x34", |
1770 "slash": "0x35"} | 1261 "slash": "0x35"} |
1771 for key, value in dict.items(): | 1262 for key, value in dict.items(): |
1772 keystr = keystr.replace(key, value) | 1263 keystr = keystr.replace(key, value) |
1773 self.monitor.sendkey(keystr) | 1264 self.monitor.sendkey(keystr) |
1774 time.sleep(0.2) | 1265 time.sleep(0.2) |
1775 | 1266 |
1776 | 1267 |
1777 def send_string(self, str): | 1268 # should this really be expected from VMs of all hypervisor types? |
1778 """ | 1269 def screendump(self, filename): |
1779 Send a string to the VM. | |
1780 | |
1781 @param str: String, that must consist of alphanumeric characters only. | |
1782 Capital letters are allowed. | |
1783 """ | |
1784 for char in str: | |
1785 if char.isupper(): | |
1786 self.send_key("shift-%s" % char.lower()) | |
1787 else: | |
1788 self.send_key(char) | |
1789 | |
1790 | |
1791 def get_uuid(self): | |
1792 """ | |
1793 Catch UUID of the VM. | |
1794 | |
1795 @return: None,if not specified in config file | |
1796 """ | |
1797 if self.params.get("uuid") == "random": | |
1798 return self.uuid | |
1799 else: | |
1800 return self.params.get("uuid", None) | |
1801 | |
1802 | |
1803 def get_cpu_count(self): | |
1804 """ | |
1805 Get the cpu count of the VM. | |
1806 """ | |
1807 session = self.login() | |
1808 try: | 1270 try: |
1809 return int(session.cmd(self.params.get("cpu_chk_cmd"))) | 1271 if self.monitor: |
1810 finally: | 1272 self.monitor.screendump(filename=filename) |
1811 session.close() | 1273 except kvm_monitor.MonitorError, e: |
1812 | 1274 logging.warn(e) |
1813 | |
1814 def get_memory_size(self, cmd=None): | |
1815 """ | |
1816 Get bootup memory size of the VM. | |
1817 | |
1818 @param check_cmd: Command used to check memory. If not provided, | |
1819 self.params.get("mem_chk_cmd") will be used. | |
1820 """ | |
1821 session = self.login() | |
1822 try: | |
1823 if not cmd: | |
1824 cmd = self.params.get("mem_chk_cmd") | |
1825 mem_str = session.cmd(cmd) | |
1826 mem = re.findall("([0-9]+)", mem_str) | |
1827 mem_size = 0 | |
1828 for m in mem: | |
1829 mem_size += int(m) | |
1830 if "GB" in mem_str: | |
1831 mem_size *= 1024 | |
1832 elif "MB" in mem_str: | |
1833 pass | |
1834 else: | |
1835 mem_size /= 1024 | |
1836 return int(mem_size) | |
1837 finally: | |
1838 session.close() | |
1839 | |
1840 | |
1841 def get_current_memory_size(self): | |
1842 """ | |
1843 Get current memory size of the VM, rather than bootup memory. | |
1844 """ | |
1845 cmd = self.params.get("mem_chk_cur_cmd") | |
1846 return self.get_memory_size(cmd) | |
1847 | 1275 |
1848 | 1276 |
1849 def save_to_file(self, path): | 1277 def save_to_file(self, path): |
1850 """ | 1278 """ |
1851 Save the state of virtual machine to a file through migrate to | 1279 Save the state of virtual machine to a file through migrate to |
1852 exec | 1280 exec |
1853 """ | 1281 """ |
1854 # Make sure we only get one iteration | 1282 # Make sure we only get one iteration |
1855 self.monitor.cmd("migrate_set_speed 1000g") | 1283 self.monitor.cmd("migrate_set_speed 1000g") |
1856 self.monitor.cmd("migrate_set_downtime 100000000") | 1284 self.monitor.cmd("migrate_set_downtime 100000000") |
1857 self.monitor.migrate('"exec:cat>%s"' % path) | 1285 self.monitor.migrate('"exec:cat>%s"' % path) |
1858 # Restore the speed and downtime of migration | 1286 # Restore the speed and downtime of migration |
1859 self.monitor.cmd("migrate_set_speed %d" % (32<<20)) | 1287 self.monitor.cmd("migrate_set_speed %d" % (32<<20)) |
1860 self.monitor.cmd("migrate_set_downtime 0.03") | 1288 self.monitor.cmd("migrate_set_downtime 0.03") |
| 1289 |
| 1290 |
| 1291 def needs_restart(self, name, params, basedir): |
| 1292 """ |
| 1293 Verifies whether the current qemu commandline matches the requested |
| 1294 one, based on the test parameters. |
| 1295 """ |
| 1296 return (self.__make_qemu_command() != |
| 1297 self.__make_qemu_command(name, params, basedir)) |
OLD | NEW |