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

Side by Side Diff: client/tests/kvm/kvm_vm.py

Issue 6246035: Merge remote branch 'cros/upstream' into master (Closed) Base URL: ssh://git@gitrw.chromium.org:9222/autotest.git@master
Patch Set: patch Created 9 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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, socket, os, logging, fcntl, re, commands, shelve, glob 8 import time, os, logging, fcntl, re, commands, glob
9 import kvm_utils, kvm_subprocess, kvm_monitor, rss_file_transfer 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
11 from autotest_lib.client.bin import utils 11 from autotest_lib.client.bin import utils
12 12
13 13
14 class VMError(Exception):
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 VMAddressError(VMError):
109 pass
110
111
112 class VMPortNotRedirectedError(VMAddressError):
113 def __init__(self, port):
114 VMAddressError.__init__(self, port)
115 self.port = port
116
117 def __str__(self):
118 return "Port not redirected: %s" % self.port
119
120
121 class VMAddressVerificationError(VMAddressError):
122 def __init__(self, mac, ip):
123 VMAddressError.__init__(self, mac, ip)
124 self.mac = mac
125 self.ip = ip
126
127 def __str__(self):
128 return ("Cannot verify MAC-IP address mapping using arping: "
129 "%s ---> %s" % (self.mac, self.ip))
130
131
132 class VMMACAddressMissingError(VMAddressError):
133 def __init__(self, nic_index):
134 VMAddressError.__init__(self, nic_index)
135 self.nic_index = nic_index
136
137 def __str__(self):
138 return "No MAC address defined for NIC #%s" % self.nic_index
139
140
141 class VMIPAddressMissingError(VMAddressError):
142 def __init__(self, mac):
143 VMAddressError.__init__(self, mac)
144 self.mac = mac
145
146 def __str__(self):
147 return "Cannot find IP address for MAC address %s" % self.mac
148
149
150 class VMMigrateError(VMError):
151 pass
152
153
154 class VMMigrateTimeoutError(VMMigrateError):
155 pass
156
157
158 class VMMigrateCancelError(VMMigrateError):
159 pass
160
161
162 class VMMigrateFailedError(VMMigrateError):
163 pass
164
165
166 class VMMigrateStateMismatchError(VMMigrateError):
167 def __init__(self, src_hash, dst_hash):
168 VMMigrateError.__init__(self, src_hash, dst_hash)
169 self.src_hash = src_hash
170 self.dst_hash = dst_hash
171
172 def __str__(self):
173 return ("Mismatch of VM state before and after migration (%s != %s)" %
174 (self.src_hash, self.dst_hash))
175
176
177 class VMRebootError(VMError):
178 pass
179
180
14 def get_image_filename(params, root_dir): 181 def get_image_filename(params, root_dir):
15 """ 182 """
16 Generate an image path from params and root_dir. 183 Generate an image path from params and root_dir.
17 184
18 @param params: Dictionary containing the test parameters. 185 @param params: Dictionary containing the test parameters.
19 @param root_dir: Base directory for relative filenames. 186 @param root_dir: Base directory for relative filenames.
20 187
21 @note: params should contain: 188 @note: params should contain:
22 image_name -- the name of the image file, without extension 189 image_name -- the name of the image file, without extension
23 image_format -- the format of the image (qcow2, raw etc) 190 image_format -- the format of the image (qcow2, raw etc)
24 """ 191 """
25 image_name = params.get("image_name", "image") 192 image_name = params.get("image_name", "image")
26 image_format = params.get("image_format", "qcow2") 193 image_format = params.get("image_format", "qcow2")
194 if params.get("image_raw_device") == "yes":
195 return image_name
27 image_filename = "%s.%s" % (image_name, image_format) 196 image_filename = "%s.%s" % (image_name, image_format)
28 image_filename = kvm_utils.get_path(root_dir, image_filename) 197 image_filename = kvm_utils.get_path(root_dir, image_filename)
29 return image_filename 198 return image_filename
30 199
31 200
32 def create_image(params, root_dir): 201 def create_image(params, root_dir):
33 """ 202 """
34 Create an image using qemu_image. 203 Create an image using qemu_image.
35 204
36 @param params: Dictionary containing the test parameters. 205 @param params: Dictionary containing the test parameters.
(...skipping 11 matching lines...) Expand all
48 217
49 format = params.get("image_format", "qcow2") 218 format = params.get("image_format", "qcow2")
50 qemu_img_cmd += " -f %s" % format 219 qemu_img_cmd += " -f %s" % format
51 220
52 image_filename = get_image_filename(params, root_dir) 221 image_filename = get_image_filename(params, root_dir)
53 qemu_img_cmd += " %s" % image_filename 222 qemu_img_cmd += " %s" % image_filename
54 223
55 size = params.get("image_size", "10G") 224 size = params.get("image_size", "10G")
56 qemu_img_cmd += " %s" % size 225 qemu_img_cmd += " %s" % size
57 226
58 try: 227 utils.system(qemu_img_cmd)
59 utils.system(qemu_img_cmd) 228 logging.info("Image created in %r", image_filename)
60 except error.CmdError, e:
61 logging.error("Could not create image; qemu-img command failed:\n%s",
62 str(e))
63 return None
64
65 if not os.path.exists(image_filename):
66 logging.error("Image could not be created for some reason; "
67 "qemu-img command:\n%s" % qemu_img_cmd)
68 return None
69
70 logging.info("Image created in %s" % image_filename)
71 return image_filename 229 return image_filename
72 230
73 231
74 def remove_image(params, root_dir): 232 def remove_image(params, root_dir):
75 """ 233 """
76 Remove an image file. 234 Remove an image file.
77 235
78 @param params: A dict 236 @param params: A dict
79 @param root_dir: Base directory for relative filenames. 237 @param root_dir: Base directory for relative filenames.
80 238
81 @note: params should contain: 239 @note: params should contain:
82 image_name -- the name of the image file, without extension 240 image_name -- the name of the image file, without extension
83 image_format -- the format of the image (qcow2, raw etc) 241 image_format -- the format of the image (qcow2, raw etc)
84 """ 242 """
85 image_filename = get_image_filename(params, root_dir) 243 image_filename = get_image_filename(params, root_dir)
86 logging.debug("Removing image file %s..." % image_filename) 244 logging.debug("Removing image file %s...", image_filename)
87 if os.path.exists(image_filename): 245 if os.path.exists(image_filename):
88 os.unlink(image_filename) 246 os.unlink(image_filename)
89 else: 247 else:
90 logging.debug("Image file %s not found") 248 logging.debug("Image file %s not found")
91 249
92 250
251 def check_image(params, root_dir):
252 """
253 Check an image using qemu-img.
254
255 @param params: Dictionary containing the test parameters.
256 @param root_dir: Base directory for relative filenames.
257
258 @note: params should contain:
259 image_name -- the name of the image file, without extension
260 image_format -- the format of the image (qcow2, raw etc)
261
262 @raise VMImageCheckError: In case qemu-img check fails on the image.
263 """
264 image_filename = get_image_filename(params, root_dir)
265 logging.debug("Checking image file %s...", image_filename)
266 qemu_img_cmd = kvm_utils.get_path(root_dir,
267 params.get("qemu_img_binary", "qemu-img"))
268 image_is_qcow2 = params.get("image_format") == 'qcow2'
269 if os.path.exists(image_filename) and image_is_qcow2:
270 # Verifying if qemu-img supports 'check'
271 q_result = utils.run(qemu_img_cmd, ignore_status=True)
272 q_output = q_result.stdout
273 check_img = True
274 if not "check" in q_output:
275 logging.error("qemu-img does not support 'check', "
276 "skipping check...")
277 check_img = False
278 if not "info" in q_output:
279 logging.error("qemu-img does not support 'info', "
280 "skipping check...")
281 check_img = False
282 if check_img:
283 try:
284 utils.system("%s info %s" % (qemu_img_cmd, image_filename))
285 except error.CmdError:
286 logging.error("Error getting info from image %s",
287 image_filename)
288 try:
289 utils.system("%s check %s" % (qemu_img_cmd, image_filename))
290 except error.CmdError:
291 raise VMImageCheckError(image_filename)
292
293 else:
294 if not os.path.exists(image_filename):
295 logging.debug("Image file %s not found, skipping check...",
296 image_filename)
297 elif not image_is_qcow2:
298 logging.debug("Image file %s not qcow2, skipping check...",
299 image_filename)
300
301
93 class VM: 302 class VM:
94 """ 303 """
95 This class handles all basic VM operations. 304 This class handles all basic VM operations.
96 """ 305 """
97 306
98 def __init__(self, name, params, root_dir, address_cache): 307 def __init__(self, name, params, root_dir, address_cache, state=None):
99 """ 308 """
100 Initialize the object and set a few attributes. 309 Initialize the object and set a few attributes.
101 310
102 @param name: The name of the object 311 @param name: The name of the object
103 @param params: A dict containing VM params 312 @param params: A dict containing VM params
104 (see method make_qemu_command for a full description) 313 (see method make_qemu_command for a full description)
105 @param root_dir: Base directory for relative filenames 314 @param root_dir: Base directory for relative filenames
106 @param address_cache: A dict that maps MAC addresses to IP addresses 315 @param address_cache: A dict that maps MAC addresses to IP addresses
316 @param state: If provided, use this as self.__dict__
107 """ 317 """
108 self.process = None 318 if state:
109 self.serial_console = None 319 self.__dict__ = state
110 self.redirs = {} 320 else:
111 self.vnc_port = 5900 321 self.process = None
112 self.monitors = [] 322 self.serial_console = None
113 self.pci_assignable = None 323 self.redirs = {}
114 self.netdev_id = [] 324 self.vnc_port = 5900
115 self.uuid = None 325 self.monitors = []
326 self.pci_assignable = None
327 self.netdev_id = []
328 self.uuid = None
329
330 # Find a unique identifier for this VM
331 while True:
332 self.instance = (time.strftime("%Y%m%d-%H%M%S-") +
333 kvm_utils.generate_random_string(4))
334 if not glob.glob("/tmp/*%s" % self.instance):
335 break
116 336
117 self.name = name 337 self.name = name
118 self.params = params 338 self.params = params
119 self.root_dir = root_dir 339 self.root_dir = root_dir
120 self.address_cache = address_cache 340 self.address_cache = address_cache
121 341
122 # Find a unique identifier for this VM
123 while True:
124 self.instance = (time.strftime("%Y%m%d-%H%M%S-") +
125 kvm_utils.generate_random_string(4))
126 if not glob.glob("/tmp/*%s" % self.instance):
127 break
128 342
129 343 def clone(self, name=None, params=None, root_dir=None, address_cache=None,
130 def clone(self, name=None, params=None, root_dir=None, address_cache=None): 344 copy_state=False):
131 """ 345 """
132 Return a clone of the VM object with optionally modified parameters. 346 Return a clone of the VM object with optionally modified parameters.
133 The clone is initially not alive and needs to be started using create(). 347 The clone is initially not alive and needs to be started using create().
134 Any parameters not passed to this function are copied from the source 348 Any parameters not passed to this function are copied from the source
135 VM. 349 VM.
136 350
137 @param name: Optional new VM name 351 @param name: Optional new VM name
138 @param params: Optional new VM creation parameters 352 @param params: Optional new VM creation parameters
139 @param root_dir: Optional new base directory for relative filenames 353 @param root_dir: Optional new base directory for relative filenames
140 @param address_cache: A dict that maps MAC addresses to IP addresses 354 @param address_cache: A dict that maps MAC addresses to IP addresses
355 @param copy_state: If True, copy the original VM's state to the clone.
356 Mainly useful for make_qemu_command().
141 """ 357 """
142 if name is None: 358 if name is None:
143 name = self.name 359 name = self.name
144 if params is None: 360 if params is None:
145 params = self.params.copy() 361 params = self.params.copy()
146 if root_dir is None: 362 if root_dir is None:
147 root_dir = self.root_dir 363 root_dir = self.root_dir
148 if address_cache is None: 364 if address_cache is None:
149 address_cache = self.address_cache 365 address_cache = self.address_cache
150 return VM(name, params, root_dir, address_cache) 366 if copy_state:
367 state = self.__dict__.copy()
368 else:
369 state = None
370 return VM(name, params, root_dir, address_cache, state)
151 371
152 372
153 def make_qemu_command(self, name=None, params=None, root_dir=None): 373 def make_qemu_command(self, name=None, params=None, root_dir=None):
154 """ 374 """
155 Generate a qemu command line. All parameters are optional. If a 375 Generate a qemu command line. All parameters are optional. If a
156 parameter is not supplied, the corresponding value stored in the 376 parameter is not supplied, the corresponding value stored in the
157 class attributes is used. 377 class attributes is used.
158 378
159 @param name: The name of the object 379 @param name: The name of the object
160 @param params: A dict containing VM params 380 @param params: A dict containing VM params
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
218 if has_option(help, "drive"): 438 if has_option(help, "drive"):
219 cmd = " -drive file='%s',media=cdrom" % filename 439 cmd = " -drive file='%s',media=cdrom" % filename
220 if index is not None: cmd += ",index=%s" % index 440 if index is not None: cmd += ",index=%s" % index
221 return cmd 441 return cmd
222 else: 442 else:
223 return " -cdrom '%s'" % filename 443 return " -cdrom '%s'" % filename
224 444
225 def add_drive(help, filename, index=None, format=None, cache=None, 445 def add_drive(help, filename, index=None, format=None, cache=None,
226 werror=None, serial=None, snapshot=False, boot=False): 446 werror=None, serial=None, snapshot=False, boot=False):
227 cmd = " -drive file='%s'" % filename 447 cmd = " -drive file='%s'" % filename
228 if index is not None: cmd += ",index=%s" % index 448 if index is not None:
229 if format: cmd += ",if=%s" % format 449 cmd += ",index=%s" % index
230 if cache: cmd += ",cache=%s" % cache 450 if format:
231 if werror: cmd += ",werror=%s" % werror 451 cmd += ",if=%s" % format
232 if serial: cmd += ",serial='%s'" % serial 452 if cache:
233 if snapshot: cmd += ",snapshot=on" 453 cmd += ",cache=%s" % cache
234 if boot: cmd += ",boot=on" 454 if werror:
455 cmd += ",werror=%s" % werror
456 if serial:
457 cmd += ",serial='%s'" % serial
458 if snapshot:
459 cmd += ",snapshot=on"
460 if boot:
461 cmd += ",boot=on"
235 return cmd 462 return cmd
236 463
237 def add_nic(help, vlan, model=None, mac=None, netdev_id=None, 464 def add_nic(help, vlan, model=None, mac=None, netdev_id=None,
238 nic_extra_params=None): 465 nic_extra_params=None):
466 if has_option(help, "netdev"):
467 netdev_vlan_str = ",netdev=%s" % netdev_id
468 else:
469 netdev_vlan_str = ",vlan=%d" % vlan
239 if has_option(help, "device"): 470 if has_option(help, "device"):
240 if model == "virtio":
241 model="virtio-net-pci"
242 if not model: 471 if not model:
243 model= "rtl8139" 472 model = "rtl8139"
244 cmd = " -device %s" % model 473 elif model == "virtio":
474 model = "virtio-net-pci"
475 cmd = " -device %s" % model + netdev_vlan_str
245 if mac: 476 if mac:
246 cmd += ",mac=%s" % mac 477 cmd += ",mac='%s'" % mac
247 if has_option(help, "netdev"):
248 cmd += ",netdev=%s" % netdev_id
249 else:
250 cmd += "vlan=%d," % vlan
251 if nic_extra_params: 478 if nic_extra_params:
252 cmd += ",%s" % nic_extra_params 479 cmd += ",%s" % nic_extra_params
253 else: 480 else:
254 if has_option(help, "netdev"): 481 cmd = " -net nic" + netdev_vlan_str
255 cmd = " -net nic,netdev=%s" % netdev_id
256 else:
257 cmd = " -net nic,vlan=%d" % vlan
258 if model: 482 if model:
259 cmd += ",model=%s" % model 483 cmd += ",model=%s" % model
260 if mac: 484 if mac:
261 cmd += ",macaddr='%s'" % mac 485 cmd += ",macaddr='%s'" % mac
262 return cmd 486 return cmd
263 487
264 def add_net(help, vlan, mode, ifname=None, script=None, 488 def add_net(help, vlan, mode, ifname=None, script=None,
265 downscript=None, tftp=None, bootfile=None, hostfwd=[], 489 downscript=None, tftp=None, bootfile=None, hostfwd=[],
266 netdev_id=None, vhost=False): 490 netdev_id=None, netdev_extra_params=None):
267 if has_option(help, "netdev"): 491 if has_option(help, "netdev"):
268 cmd = " -netdev %s,id=%s" % (mode, netdev_id) 492 cmd = " -netdev %s,id=%s" % (mode, netdev_id)
269 if vhost: 493 if netdev_extra_params:
270 cmd +=",vhost=on" 494 cmd += ",%s" % netdev_extra_params
271 else: 495 else:
272 cmd = " -net %s,vlan=%d" % (mode, vlan) 496 cmd = " -net %s,vlan=%d" % (mode, vlan)
273 if mode == "tap": 497 if mode == "tap":
274 if ifname: cmd += ",ifname='%s'" % ifname 498 if ifname: cmd += ",ifname='%s'" % ifname
275 if script: cmd += ",script='%s'" % script 499 if script: cmd += ",script='%s'" % script
276 cmd += ",downscript='%s'" % (downscript or "no") 500 cmd += ",downscript='%s'" % (downscript or "no")
277 elif mode == "user": 501 elif mode == "user":
278 if tftp and "[,tftp=" in help: 502 if tftp and "[,tftp=" in help:
279 cmd += ",tftp='%s'" % tftp 503 cmd += ",tftp='%s'" % tftp
280 if bootfile and "[,bootfile=" in help: 504 if bootfile and "[,bootfile=" in help:
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
340 " -device testdev,chardev=testlog" % filename) 564 " -device testdev,chardev=testlog" % filename)
341 565
342 def add_no_hpet(help): 566 def add_no_hpet(help):
343 if has_option(help, "no-hpet"): 567 if has_option(help, "no-hpet"):
344 return " -no-hpet" 568 return " -no-hpet"
345 else: 569 else:
346 return "" 570 return ""
347 571
348 # End of command line option wrappers 572 # End of command line option wrappers
349 573
350 if name is None: name = self.name 574 if name is None:
351 if params is None: params = self.params 575 name = self.name
352 if root_dir is None: root_dir = self.root_dir 576 if params is None:
577 params = self.params
578 if root_dir is None:
579 root_dir = self.root_dir
580
581 # Clone this VM using the new params
582 vm = self.clone(name, params, root_dir, copy_state=True)
353 583
354 qemu_binary = kvm_utils.get_path(root_dir, params.get("qemu_binary", 584 qemu_binary = kvm_utils.get_path(root_dir, params.get("qemu_binary",
355 "qemu")) 585 "qemu"))
356 # Get the output of 'qemu -help' (log a message in case this call never 586 # Get the output of 'qemu -help' (log a message in case this call never
357 # returns or causes some other kind of trouble) 587 # returns or causes some other kind of trouble)
358 logging.debug("Getting output of 'qemu -help'") 588 logging.debug("Getting output of 'qemu -help'")
359 help = commands.getoutput("%s -help" % qemu_binary) 589 help = commands.getoutput("%s -help" % qemu_binary)
360 590
361 # Start constructing the qemu command 591 # Start constructing the qemu command
362 qemu_cmd = "" 592 qemu_cmd = ""
363 # Set the X11 display parameter if requested 593 # Set the X11 display parameter if requested
364 if params.get("x11_display"): 594 if params.get("x11_display"):
365 qemu_cmd += "DISPLAY=%s " % params.get("x11_display") 595 qemu_cmd += "DISPLAY=%s " % params.get("x11_display")
366 # Add the qemu binary 596 # Add the qemu binary
367 qemu_cmd += qemu_binary 597 qemu_cmd += qemu_binary
368 # Add the VM's name 598 # Add the VM's name
369 qemu_cmd += add_name(help, name) 599 qemu_cmd += add_name(help, name)
370 # Add monitors 600 # Add monitors
371 for monitor_name in kvm_utils.get_sub_dict_names(params, "monitors"): 601 for monitor_name in params.objects("monitors"):
372 monitor_params = kvm_utils.get_sub_dict(params, monitor_name) 602 monitor_params = params.object_params(monitor_name)
373 monitor_filename = self.get_monitor_filename(monitor_name) 603 monitor_filename = vm.get_monitor_filename(monitor_name)
374 if monitor_params.get("monitor_type") == "qmp": 604 if monitor_params.get("monitor_type") == "qmp":
375 qemu_cmd += add_qmp_monitor(help, monitor_filename) 605 qemu_cmd += add_qmp_monitor(help, monitor_filename)
376 else: 606 else:
377 qemu_cmd += add_human_monitor(help, monitor_filename) 607 qemu_cmd += add_human_monitor(help, monitor_filename)
378 608
379 # Add serial console redirection 609 # Add serial console redirection
380 qemu_cmd += add_serial(help, self.get_serial_console_filename()) 610 qemu_cmd += add_serial(help, vm.get_serial_console_filename())
381 611
382 for image_name in kvm_utils.get_sub_dict_names(params, "images"): 612 for image_name in params.objects("images"):
383 image_params = kvm_utils.get_sub_dict(params, image_name) 613 image_params = params.object_params(image_name)
384 if image_params.get("boot_drive") == "no": 614 if image_params.get("boot_drive") == "no":
385 continue 615 continue
386 qemu_cmd += add_drive(help, 616 qemu_cmd += add_drive(help,
387 get_image_filename(image_params, root_dir), 617 get_image_filename(image_params, root_dir),
388 image_params.get("drive_index"), 618 image_params.get("drive_index"),
389 image_params.get("drive_format"), 619 image_params.get("drive_format"),
390 image_params.get("drive_cache"), 620 image_params.get("drive_cache"),
391 image_params.get("drive_werror"), 621 image_params.get("drive_werror"),
392 image_params.get("drive_serial"), 622 image_params.get("drive_serial"),
393 image_params.get("image_snapshot") == "yes", 623 image_params.get("image_snapshot") == "yes",
394 image_params.get("image_boot") == "yes") 624 image_params.get("image_boot") == "yes")
395 625
396 redirs = [] 626 redirs = []
397 for redir_name in kvm_utils.get_sub_dict_names(params, "redirs"): 627 for redir_name in params.objects("redirs"):
398 redir_params = kvm_utils.get_sub_dict(params, redir_name) 628 redir_params = params.object_params(redir_name)
399 guest_port = int(redir_params.get("guest_port")) 629 guest_port = int(redir_params.get("guest_port"))
400 host_port = self.redirs.get(guest_port) 630 host_port = vm.redirs.get(guest_port)
401 redirs += [(host_port, guest_port)] 631 redirs += [(host_port, guest_port)]
402 632
403 vlan = 0 633 vlan = 0
404 for nic_name in kvm_utils.get_sub_dict_names(params, "nics"): 634 for nic_name in params.objects("nics"):
405 nic_params = kvm_utils.get_sub_dict(params, nic_name) 635 nic_params = params.object_params(nic_name)
636 try:
637 netdev_id = vm.netdev_id[vlan]
638 except IndexError:
639 netdev_id = None
406 # Handle the '-net nic' part 640 # Handle the '-net nic' part
407 mac = self.get_mac_address(vlan) 641 mac = vm.get_mac_address(vlan)
408 qemu_cmd += add_nic(help, vlan, nic_params.get("nic_model"), mac, 642 qemu_cmd += add_nic(help, vlan, nic_params.get("nic_model"), mac,
409 self.netdev_id[vlan], 643 netdev_id, nic_params.get("nic_extra_params"))
410 nic_params.get("nic_extra_params"))
411 # Handle the '-net tap' or '-net user' part 644 # Handle the '-net tap' or '-net user' part
412 script = nic_params.get("nic_script") 645 script = nic_params.get("nic_script")
413 downscript = nic_params.get("nic_downscript") 646 downscript = nic_params.get("nic_downscript")
414 tftp = nic_params.get("tftp") 647 tftp = nic_params.get("tftp")
415 if script: 648 if script:
416 script = kvm_utils.get_path(root_dir, script) 649 script = kvm_utils.get_path(root_dir, script)
417 if downscript: 650 if downscript:
418 downscript = kvm_utils.get_path(root_dir, downscript) 651 downscript = kvm_utils.get_path(root_dir, downscript)
419 if tftp: 652 if tftp:
420 tftp = kvm_utils.get_path(root_dir, tftp) 653 tftp = kvm_utils.get_path(root_dir, tftp)
421 qemu_cmd += add_net(help, vlan, nic_params.get("nic_mode", "user"), 654 qemu_cmd += add_net(help, vlan, nic_params.get("nic_mode", "user"),
422 self.get_ifname(vlan), 655 vm.get_ifname(vlan),
423 script, downscript, tftp, 656 script, downscript, tftp,
424 nic_params.get("bootp"), redirs, 657 nic_params.get("bootp"), redirs, netdev_id,
425 self.netdev_id[vlan], 658 nic_params.get("netdev_extra_params"))
426 nic_params.get("vhost")=="yes")
427 # Proceed to next NIC 659 # Proceed to next NIC
428 vlan += 1 660 vlan += 1
429 661
430 mem = params.get("mem") 662 mem = params.get("mem")
431 if mem: 663 if mem:
432 qemu_cmd += add_mem(help, mem) 664 qemu_cmd += add_mem(help, mem)
433 665
434 smp = params.get("smp") 666 smp = params.get("smp")
435 if smp: 667 if smp:
436 qemu_cmd += add_smp(help, smp) 668 qemu_cmd += add_smp(help, smp)
437 669
438 cdroms = kvm_utils.get_sub_dict_names(params, "cdroms") 670 for cdrom in params.objects("cdroms"):
439 for cdrom in cdroms: 671 cdrom_params = params.object_params(cdrom)
440 cdrom_params = kvm_utils.get_sub_dict(params, cdrom)
441 iso = cdrom_params.get("cdrom") 672 iso = cdrom_params.get("cdrom")
442 if iso: 673 if iso:
443 qemu_cmd += add_cdrom(help, kvm_utils.get_path(root_dir, iso), 674 qemu_cmd += add_cdrom(help, kvm_utils.get_path(root_dir, iso),
444 cdrom_params.get("drive_index")) 675 cdrom_params.get("drive_index"))
445 676
446 # We may want to add {floppy_otps} parameter for -fda 677 # We may want to add {floppy_otps} parameter for -fda
447 # {fat:floppy:}/path/. However vvfat is not usually recommended. 678 # {fat:floppy:}/path/. However vvfat is not usually recommended.
448 floppy = params.get("floppy") 679 floppy = params.get("floppy")
449 if floppy: 680 if floppy:
450 floppy = kvm_utils.get_path(root_dir, floppy) 681 floppy = kvm_utils.get_path(root_dir, floppy)
(...skipping 19 matching lines...) Expand all
470 701
471 initrd = params.get("initrd") 702 initrd = params.get("initrd")
472 if initrd: 703 if initrd:
473 initrd = kvm_utils.get_path(root_dir, initrd) 704 initrd = kvm_utils.get_path(root_dir, initrd)
474 qemu_cmd += add_initrd(help, initrd) 705 qemu_cmd += add_initrd(help, initrd)
475 706
476 for host_port, guest_port in redirs: 707 for host_port, guest_port in redirs:
477 qemu_cmd += add_tcp_redir(help, host_port, guest_port) 708 qemu_cmd += add_tcp_redir(help, host_port, guest_port)
478 709
479 if params.get("display") == "vnc": 710 if params.get("display") == "vnc":
480 qemu_cmd += add_vnc(help, self.vnc_port) 711 qemu_cmd += add_vnc(help, vm.vnc_port)
481 elif params.get("display") == "sdl": 712 elif params.get("display") == "sdl":
482 qemu_cmd += add_sdl(help) 713 qemu_cmd += add_sdl(help)
483 elif params.get("display") == "nographic": 714 elif params.get("display") == "nographic":
484 qemu_cmd += add_nographic(help) 715 qemu_cmd += add_nographic(help)
485 716
486 if params.get("uuid") == "random": 717 if params.get("uuid") == "random":
487 qemu_cmd += add_uuid(help, self.uuid) 718 qemu_cmd += add_uuid(help, vm.uuid)
488 elif params.get("uuid"): 719 elif params.get("uuid"):
489 qemu_cmd += add_uuid(help, params.get("uuid")) 720 qemu_cmd += add_uuid(help, params.get("uuid"))
490 721
491 if params.get("testdev") == "yes": 722 if params.get("testdev") == "yes":
492 qemu_cmd += add_testdev(help, self.get_testlog_filename()) 723 qemu_cmd += add_testdev(help, vm.get_testlog_filename())
493 724
494 if params.get("disable_hpet") == "yes": 725 if params.get("disable_hpet") == "yes":
495 qemu_cmd += add_no_hpet(help) 726 qemu_cmd += add_no_hpet(help)
496 727
497 # If the PCI assignment step went OK, add each one of the PCI assigned 728 # If the PCI assignment step went OK, add each one of the PCI assigned
498 # devices to the qemu command line. 729 # devices to the qemu command line.
499 if self.pci_assignable: 730 if vm.pci_assignable:
500 for pci_id in self.pa_pci_ids: 731 for pci_id in vm.pa_pci_ids:
501 qemu_cmd += add_pcidevice(help, pci_id) 732 qemu_cmd += add_pcidevice(help, pci_id)
502 733
503 extra_params = params.get("extra_params") 734 extra_params = params.get("extra_params")
504 if extra_params: 735 if extra_params:
505 qemu_cmd += " %s" % extra_params 736 qemu_cmd += " %s" % extra_params
506 737
507 return qemu_cmd 738 return qemu_cmd
508 739
509 740
741 @error.context_aware
510 def create(self, name=None, params=None, root_dir=None, timeout=5.0, 742 def create(self, name=None, params=None, root_dir=None, timeout=5.0,
511 migration_mode=None, migration_exec_cmd=None, mac_source=None): 743 migration_mode=None, mac_source=None):
512 """ 744 """
513 Start the VM by running a qemu command. 745 Start the VM by running a qemu command.
514 All parameters are optional. If name, params or root_dir are not 746 All parameters are optional. If name, params or root_dir are not
515 supplied, the respective values stored as class attributes are used. 747 supplied, the respective values stored as class attributes are used.
516 748
517 @param name: The name of the object 749 @param name: The name of the object
518 @param params: A dict containing VM params 750 @param params: A dict containing VM params
519 @param root_dir: Base directory for relative filenames 751 @param root_dir: Base directory for relative filenames
520 @param migration_mode: If supplied, start VM for incoming migration 752 @param migration_mode: If supplied, start VM for incoming migration
521 using this protocol (either 'tcp', 'unix' or 'exec') 753 using this protocol (either 'tcp', 'unix' or 'exec')
522 @param migration_exec_cmd: Command to embed in '-incoming "exec: ..."' 754 @param migration_exec_cmd: Command to embed in '-incoming "exec: ..."'
523 (e.g. 'gzip -c -d filename') if migration_mode is 'exec' 755 (e.g. 'gzip -c -d filename') if migration_mode is 'exec'
524 @param mac_source: A VM object from which to copy MAC addresses. If not 756 @param mac_source: A VM object from which to copy MAC addresses. If not
525 specified, new addresses will be generated. 757 specified, new addresses will be generated.
758
759 @raise VMCreateError: If qemu terminates unexpectedly
760 @raise VMKVMInitError: If KVM initialization fails
761 @raise VMHugePageError: If hugepage initialization fails
762 @raise VMImageMissingError: If a CD image is missing
763 @raise VMHashMismatchError: If a CD image hash has doesn't match the
764 expected hash
765 @raise VMBadPATypeError: If an unsupported PCI assignment type is
766 requested
767 @raise VMPAError: If no PCI assignable devices could be assigned
526 """ 768 """
527 self.destroy() 769 error.context("creating '%s'" % self.name)
770 self.destroy(free_mac_addresses=False)
528 771
529 if name is not None: 772 if name is not None:
530 self.name = name 773 self.name = name
531 if params is not None: 774 if params is not None:
532 self.params = params 775 self.params = params
533 if root_dir is not None: 776 if root_dir is not None:
534 self.root_dir = root_dir 777 self.root_dir = root_dir
535 name = self.name 778 name = self.name
536 params = self.params 779 params = self.params
537 root_dir = self.root_dir 780 root_dir = self.root_dir
538 781
539 # Verify the md5sum of the ISO image 782 # Verify the md5sum of the ISO images
540 iso = params.get("cdrom") 783 for cdrom in params.objects("cdroms"):
541 if iso: 784 cdrom_params = params.object_params(cdrom)
542 iso = kvm_utils.get_path(root_dir, iso) 785 iso = cdrom_params.get("cdrom")
543 if not os.path.exists(iso): 786 if iso:
544 logging.error("ISO file not found: %s" % iso) 787 iso = kvm_utils.get_path(root_dir, iso)
545 return False 788 if not os.path.exists(iso):
546 compare = False 789 raise VMImageMissingError(iso)
547 if params.get("md5sum_1m"): 790 compare = False
548 logging.debug("Comparing expected MD5 sum with MD5 sum of " 791 if cdrom_params.get("md5sum_1m"):
549 "first MB of ISO file...") 792 logging.debug("Comparing expected MD5 sum with MD5 sum of "
550 actual_hash = utils.hash_file(iso, 1048576, method="md5") 793 "first MB of ISO file...")
551 expected_hash = params.get("md5sum_1m") 794 actual_hash = utils.hash_file(iso, 1048576, method="md5")
552 compare = True 795 expected_hash = cdrom_params.get("md5sum_1m")
553 elif params.get("md5sum"): 796 compare = True
554 logging.debug("Comparing expected MD5 sum with MD5 sum of ISO " 797 elif cdrom_params.get("md5sum"):
555 "file...") 798 logging.debug("Comparing expected MD5 sum with MD5 sum of "
556 actual_hash = utils.hash_file(iso, method="md5") 799 "ISO file...")
557 expected_hash = params.get("md5sum") 800 actual_hash = utils.hash_file(iso, method="md5")
558 compare = True 801 expected_hash = cdrom_params.get("md5sum")
559 elif params.get("sha1sum"): 802 compare = True
560 logging.debug("Comparing expected SHA1 sum with SHA1 sum of " 803 elif cdrom_params.get("sha1sum"):
561 "ISO file...") 804 logging.debug("Comparing expected SHA1 sum with SHA1 sum "
562 actual_hash = utils.hash_file(iso, method="sha1") 805 "of ISO file...")
563 expected_hash = params.get("sha1sum") 806 actual_hash = utils.hash_file(iso, method="sha1")
564 compare = True 807 expected_hash = cdrom_params.get("sha1sum")
565 if compare: 808 compare = True
566 if actual_hash == expected_hash: 809 if compare:
567 logging.debug("Hashes match") 810 if actual_hash == expected_hash:
568 else: 811 logging.debug("Hashes match")
569 logging.error("Actual hash differs from expected one") 812 else:
570 return False 813 raise VMHashMismatchError(actual_hash, expected_hash)
571 814
572 # Make sure the following code is not executed by more than one thread 815 # Make sure the following code is not executed by more than one thread
573 # at the same time 816 # at the same time
574 lockfile = open("/tmp/kvm-autotest-vm-create.lock", "w+") 817 lockfile = open("/tmp/kvm-autotest-vm-create.lock", "w+")
575 fcntl.lockf(lockfile, fcntl.LOCK_EX) 818 fcntl.lockf(lockfile, fcntl.LOCK_EX)
576 819
577 try: 820 try:
578 # Handle port redirections 821 # Handle port redirections
579 redir_names = kvm_utils.get_sub_dict_names(params, "redirs") 822 redir_names = params.objects("redirs")
580 host_ports = kvm_utils.find_free_ports(5000, 6000, len(redir_names)) 823 host_ports = kvm_utils.find_free_ports(5000, 6000, len(redir_names))
581 self.redirs = {} 824 self.redirs = {}
582 for i in range(len(redir_names)): 825 for i in range(len(redir_names)):
583 redir_params = kvm_utils.get_sub_dict(params, redir_names[i]) 826 redir_params = params.object_params(redir_names[i])
584 guest_port = int(redir_params.get("guest_port")) 827 guest_port = int(redir_params.get("guest_port"))
585 self.redirs[guest_port] = host_ports[i] 828 self.redirs[guest_port] = host_ports[i]
586 829
587 for nic in kvm_utils.get_sub_dict_names(params, "nics"): 830 # Generate netdev IDs for all NICs
831 self.netdev_id = []
832 for nic in params.objects("nics"):
588 self.netdev_id.append(kvm_utils.generate_random_id()) 833 self.netdev_id.append(kvm_utils.generate_random_id())
589 834
590 # Find available VNC port, if needed 835 # Find available VNC port, if needed
591 if params.get("display") == "vnc": 836 if params.get("display") == "vnc":
592 self.vnc_port = kvm_utils.find_free_port(5900, 6100) 837 self.vnc_port = kvm_utils.find_free_port(5900, 6100)
593 838
594 # Find random UUID if specified 'uuid = random' in config file 839 # Find random UUID if specified 'uuid = random' in config file
595 if params.get("uuid") == "random": 840 if params.get("uuid") == "random":
596 f = open("/proc/sys/kernel/random/uuid") 841 f = open("/proc/sys/kernel/random/uuid")
597 self.uuid = f.read().strip() 842 self.uuid = f.read().strip()
598 f.close() 843 f.close()
599 844
600 # Generate or copy MAC addresses for all NICs 845 # Generate or copy MAC addresses for all NICs
601 num_nics = len(kvm_utils.get_sub_dict_names(params, "nics")) 846 num_nics = len(params.objects("nics"))
602 for vlan in range(num_nics): 847 for vlan in range(num_nics):
603 mac = mac_source and mac_source.get_mac_address(vlan) 848 nic_name = params.objects("nics")[vlan]
604 if mac: 849 nic_params = params.object_params(nic_name)
850 if nic_params.get("nic_mac", None):
851 mac = nic_params.get("nic_mac")
605 kvm_utils.set_mac_address(self.instance, vlan, mac) 852 kvm_utils.set_mac_address(self.instance, vlan, mac)
606 else: 853 else:
607 kvm_utils.generate_mac_address(self.instance, vlan) 854 mac = mac_source and mac_source.get_mac_address(vlan)
855 if mac:
856 kvm_utils.set_mac_address(self.instance, vlan, mac)
857 else:
858 kvm_utils.generate_mac_address(self.instance, vlan)
608 859
609 # Assign a PCI assignable device 860 # Assign a PCI assignable device
610 self.pci_assignable = None 861 self.pci_assignable = None
611 pa_type = params.get("pci_assignable") 862 pa_type = params.get("pci_assignable")
612 if pa_type in ["vf", "pf", "mixed"]: 863 if pa_type and pa_type != "no":
613 pa_devices_requested = params.get("devices_requested") 864 pa_devices_requested = params.get("devices_requested")
614 865
615 # Virtual Functions (VF) assignable devices 866 # Virtual Functions (VF) assignable devices
616 if pa_type == "vf": 867 if pa_type == "vf":
617 self.pci_assignable = kvm_utils.PciAssignable( 868 self.pci_assignable = kvm_utils.PciAssignable(
618 type=pa_type, 869 type=pa_type,
619 driver=params.get("driver"), 870 driver=params.get("driver"),
620 driver_option=params.get("driver_option"), 871 driver_option=params.get("driver_option"),
621 devices_requested=pa_devices_requested) 872 devices_requested=pa_devices_requested)
622 # Physical NIC (PF) assignable devices 873 # Physical NIC (PF) assignable devices
623 elif pa_type == "pf": 874 elif pa_type == "pf":
624 self.pci_assignable = kvm_utils.PciAssignable( 875 self.pci_assignable = kvm_utils.PciAssignable(
625 type=pa_type, 876 type=pa_type,
626 names=params.get("device_names"), 877 names=params.get("device_names"),
627 devices_requested=pa_devices_requested) 878 devices_requested=pa_devices_requested)
628 # Working with both VF and PF 879 # Working with both VF and PF
629 elif pa_type == "mixed": 880 elif pa_type == "mixed":
630 self.pci_assignable = kvm_utils.PciAssignable( 881 self.pci_assignable = kvm_utils.PciAssignable(
631 type=pa_type, 882 type=pa_type,
632 driver=params.get("driver"), 883 driver=params.get("driver"),
633 driver_option=params.get("driver_option"), 884 driver_option=params.get("driver_option"),
634 names=params.get("device_names"), 885 names=params.get("device_names"),
635 devices_requested=pa_devices_requested) 886 devices_requested=pa_devices_requested)
887 else:
888 raise VMBadPATypeError(pa_type)
636 889
637 self.pa_pci_ids = self.pci_assignable.request_devs() 890 self.pa_pci_ids = self.pci_assignable.request_devs()
638 891
639 if self.pa_pci_ids: 892 if self.pa_pci_ids:
640 logging.debug("Successfuly assigned devices: %s", 893 logging.debug("Successfuly assigned devices: %s",
641 self.pa_pci_ids) 894 self.pa_pci_ids)
642 else: 895 else:
643 logging.error("No PCI assignable devices were assigned " 896 raise VMPAError(pa_type)
644 "and 'pci_assignable' is defined to %s "
645 "on your config file. Aborting VM creation.",
646 pa_type)
647 return False
648
649 elif pa_type and pa_type != "no":
650 logging.warn("Unsupported pci_assignable type: %s", pa_type)
651 897
652 # Make qemu command 898 # Make qemu command
653 qemu_command = self.make_qemu_command() 899 qemu_command = self.make_qemu_command()
654 900
655 # Add migration parameters if required 901 # Add migration parameters if required
656 if migration_mode == "tcp": 902 if migration_mode == "tcp":
657 self.migration_port = kvm_utils.find_free_port(5200, 6000) 903 self.migration_port = kvm_utils.find_free_port(5200, 6000)
658 qemu_command += " -incoming tcp:0:%d" % self.migration_port 904 qemu_command += " -incoming tcp:0:%d" % self.migration_port
659 elif migration_mode == "unix": 905 elif migration_mode == "unix":
660 self.migration_file = "/tmp/migration-unix-%s" % self.instance 906 self.migration_file = "/tmp/migration-unix-%s" % self.instance
661 qemu_command += " -incoming unix:%s" % self.migration_file 907 qemu_command += " -incoming unix:%s" % self.migration_file
662 elif migration_mode == "exec": 908 elif migration_mode == "exec":
663 qemu_command += ' -incoming "exec:%s"' % migration_exec_cmd 909 self.migration_port = kvm_utils.find_free_port(5200, 6000)
910 qemu_command += (' -incoming "exec:nc -l %s"' %
911 self.migration_port)
664 912
665 logging.debug("Running qemu command:\n%s", qemu_command) 913 logging.info("Running qemu command:\n%s", qemu_command)
666 self.process = kvm_subprocess.run_bg(qemu_command, None, 914 self.process = kvm_subprocess.run_bg(qemu_command, None,
667 logging.debug, "(qemu) ") 915 logging.info, "(qemu) ")
668 916
669 # Make sure the process was started successfully 917 # Make sure the process was started successfully
670 if not self.process.is_alive(): 918 if not self.process.is_alive():
671 logging.error("VM could not be created; " 919 e = VMCreateError(qemu_command,
672 "qemu command failed:\n%s" % qemu_command) 920 self.process.get_status(),
673 logging.error("Status: %s" % self.process.get_status()) 921 self.process.get_output())
674 logging.error("Output:" + kvm_utils.format_str_for_message(
675 self.process.get_output()))
676 self.destroy() 922 self.destroy()
677 return False 923 raise e
678 924
679 # Establish monitor connections 925 # Establish monitor connections
680 self.monitors = [] 926 self.monitors = []
681 for monitor_name in kvm_utils.get_sub_dict_names(params, 927 for monitor_name in params.objects("monitors"):
682 "monitors"): 928 monitor_params = params.object_params(monitor_name)
683 monitor_params = kvm_utils.get_sub_dict(params, monitor_name)
684 # Wait for monitor connection to succeed 929 # Wait for monitor connection to succeed
685 end_time = time.time() + timeout 930 end_time = time.time() + timeout
686 while time.time() < end_time: 931 while time.time() < end_time:
687 try: 932 try:
688 if monitor_params.get("monitor_type") == "qmp": 933 if monitor_params.get("monitor_type") == "qmp":
689 # Add a QMP monitor 934 # Add a QMP monitor
690 monitor = kvm_monitor.QMPMonitor( 935 monitor = kvm_monitor.QMPMonitor(
691 monitor_name, 936 monitor_name,
692 self.get_monitor_filename(monitor_name)) 937 self.get_monitor_filename(monitor_name))
693 else: 938 else:
694 # Add a "human" monitor 939 # Add a "human" monitor
695 monitor = kvm_monitor.HumanMonitor( 940 monitor = kvm_monitor.HumanMonitor(
696 monitor_name, 941 monitor_name,
697 self.get_monitor_filename(monitor_name)) 942 self.get_monitor_filename(monitor_name))
943 monitor.verify_responsive()
944 break
698 except kvm_monitor.MonitorError, e: 945 except kvm_monitor.MonitorError, e:
699 logging.warn(e) 946 logging.warn(e)
700 else: 947 time.sleep(1)
701 if monitor.is_responsive():
702 break
703 time.sleep(1)
704 else: 948 else:
705 logging.error("Could not connect to monitor '%s'" %
706 monitor_name)
707 self.destroy() 949 self.destroy()
708 return False 950 raise e
709 # Add this monitor to the list 951 # Add this monitor to the list
710 self.monitors += [monitor] 952 self.monitors += [monitor]
711 953
712 # Get the output so far, to see if we have any problems with 954 # Get the output so far, to see if we have any problems with
713 # KVM modules or with hugepage setup. 955 # KVM modules or with hugepage setup.
714 output = self.process.get_output() 956 output = self.process.get_output()
715 957
716 if re.search("Could not initialize KVM", output, re.IGNORECASE): 958 if re.search("Could not initialize KVM", output, re.IGNORECASE):
717 logging.error("Could not initialize KVM; " 959 e = VMKVMInitError(qemu_command, self.process.get_output())
718 "qemu command:\n%s" % qemu_command)
719 logging.error("Output:" + kvm_utils.format_str_for_message(
720 self.process.get_output()))
721 self.destroy() 960 self.destroy()
722 return False 961 raise e
723 962
724 if "alloc_mem_area" in output: 963 if "alloc_mem_area" in output:
725 logging.error("Could not allocate hugepage memory; " 964 e = VMHugePageError(qemu_command, self.process.get_output())
726 "qemu command:\n%s" % qemu_command)
727 logging.error("Output:" + kvm_utils.format_str_for_message(
728 self.process.get_output()))
729 self.destroy() 965 self.destroy()
730 return False 966 raise e
731 967
732 logging.debug("VM appears to be alive with PID %s", self.get_pid()) 968 logging.debug("VM appears to be alive with PID %s", self.get_pid())
733 969
734 # Establish a session with the serial console -- requires a version 970 # Establish a session with the serial console -- requires a version
735 # of netcat that supports -U 971 # of netcat that supports -U
736 self.serial_console = kvm_subprocess.kvm_shell_session( 972 self.serial_console = kvm_subprocess.ShellSession(
737 "nc -U %s" % self.get_serial_console_filename(), 973 "nc -U %s" % self.get_serial_console_filename(),
738 auto_close=False, 974 auto_close=False,
739 output_func=kvm_utils.log_line, 975 output_func=kvm_utils.log_line,
740 output_params=("serial-%s.log" % name,)) 976 output_params=("serial-%s.log" % name,))
741 977
742 return True
743
744 finally: 978 finally:
745 fcntl.lockf(lockfile, fcntl.LOCK_UN) 979 fcntl.lockf(lockfile, fcntl.LOCK_UN)
746 lockfile.close() 980 lockfile.close()
747 981
748 982
749 def destroy(self, gracefully=True): 983 def destroy(self, gracefully=True, free_mac_addresses=True):
750 """ 984 """
751 Destroy the VM. 985 Destroy the VM.
752 986
753 If gracefully is True, first attempt to shutdown the VM with a shell 987 If gracefully is True, first attempt to shutdown the VM with a shell
754 command. Then, attempt to destroy the VM via the monitor with a 'quit' 988 command. Then, attempt to destroy the VM via the monitor with a 'quit'
755 command. If that fails, send SIGKILL to the qemu process. 989 command. If that fails, send SIGKILL to the qemu process.
756 990
757 @param gracefully: Whether an attempt will be made to end the VM 991 @param gracefully: If True, an attempt will be made to end the VM
758 using a shell command before trying to end the qemu process 992 using a shell command before trying to end the qemu process
759 with a 'quit' or a kill signal. 993 with a 'quit' or a kill signal.
994 @param free_mac_addresses: If True, the MAC addresses used by the VM
995 will be freed.
760 """ 996 """
761 try: 997 try:
762 # Is it already dead? 998 # Is it already dead?
763 if self.is_dead(): 999 if self.is_dead():
764 logging.debug("VM is already down")
765 return 1000 return
766 1001
767 logging.debug("Destroying VM with PID %s...", self.get_pid()) 1002 logging.debug("Destroying VM with PID %s...", self.get_pid())
768 1003
769 if gracefully and self.params.get("shutdown_command"): 1004 if gracefully and self.params.get("shutdown_command"):
770 # Try to destroy with shell command 1005 # Try to destroy with shell command
771 logging.debug("Trying to shutdown VM with shell command...") 1006 logging.debug("Trying to shutdown VM with shell command...")
772 session = self.remote_login() 1007 try:
773 if session: 1008 session = self.login()
1009 except (kvm_utils.LoginError, VMError), e:
1010 logging.debug(e)
1011 else:
774 try: 1012 try:
775 # Send the shutdown command 1013 # Send the shutdown command
776 session.sendline(self.params.get("shutdown_command")) 1014 session.sendline(self.params.get("shutdown_command"))
777 logging.debug("Shutdown command sent; waiting for VM " 1015 logging.debug("Shutdown command sent; waiting for VM "
778 "to go down...") 1016 "to go down...")
779 if kvm_utils.wait_for(self.is_dead, 60, 1, 1): 1017 if kvm_utils.wait_for(self.is_dead, 60, 1, 1):
780 logging.debug("VM is down, freeing mac address.") 1018 logging.debug("VM is down")
781 return 1019 return
782 finally: 1020 finally:
783 session.close() 1021 session.close()
784 1022
785 if self.monitor: 1023 if self.monitor:
786 # Try to destroy with a monitor command 1024 # Try to destroy with a monitor command
787 logging.debug("Trying to kill VM with monitor command...") 1025 logging.debug("Trying to kill VM with monitor command...")
788 try: 1026 try:
789 self.monitor.quit() 1027 self.monitor.quit()
790 except kvm_monitor.MonitorError, e: 1028 except kvm_monitor.MonitorError, e:
791 logging.warn(e) 1029 logging.warn(e)
792 else: 1030 else:
793 # Wait for the VM to be really dead 1031 # Wait for the VM to be really dead
794 if kvm_utils.wait_for(self.is_dead, 5, 0.5, 0.5): 1032 if kvm_utils.wait_for(self.is_dead, 5, 0.5, 0.5):
795 logging.debug("VM is down") 1033 logging.debug("VM is down")
796 return 1034 return
797 1035
798 # If the VM isn't dead yet... 1036 # If the VM isn't dead yet...
799 logging.debug("Cannot quit normally; sending a kill to close the " 1037 logging.debug("Cannot quit normally; sending a kill to close the "
800 "deal...") 1038 "deal...")
801 kvm_utils.kill_process_tree(self.process.get_pid(), 9) 1039 kvm_utils.kill_process_tree(self.process.get_pid(), 9)
802 # Wait for the VM to be really dead 1040 # Wait for the VM to be really dead
803 if kvm_utils.wait_for(self.is_dead, 5, 0.5, 0.5): 1041 if kvm_utils.wait_for(self.is_dead, 5, 0.5, 0.5):
804 logging.debug("VM is down") 1042 logging.debug("VM is down")
805 return 1043 return
806 1044
807 logging.error("Process %s is a zombie!" % self.process.get_pid()) 1045 logging.error("Process %s is a zombie!", self.process.get_pid())
808 1046
809 finally: 1047 finally:
810 self.monitors = [] 1048 self.monitors = []
811 if self.pci_assignable: 1049 if self.pci_assignable:
812 self.pci_assignable.release_devs() 1050 self.pci_assignable.release_devs()
813 if self.process: 1051 if self.process:
814 self.process.close() 1052 self.process.close()
815 if self.serial_console: 1053 if self.serial_console:
816 self.serial_console.close() 1054 self.serial_console.close()
817 for f in ([self.get_testlog_filename(), 1055 for f in ([self.get_testlog_filename(),
818 self.get_serial_console_filename()] + 1056 self.get_serial_console_filename()] +
819 self.get_monitor_filenames()): 1057 self.get_monitor_filenames()):
820 try: 1058 try:
821 os.unlink(f) 1059 os.unlink(f)
822 except OSError: 1060 except OSError:
823 pass 1061 pass
824 if hasattr(self, "migration_file"): 1062 if hasattr(self, "migration_file"):
825 try: 1063 try:
826 os.unlink(self.migration_file) 1064 os.unlink(self.migration_file)
827 except OSError: 1065 except OSError:
828 pass 1066 pass
829 num_nics = len(kvm_utils.get_sub_dict_names(self.params, "nics")) 1067 if free_mac_addresses:
830 for vlan in range(num_nics): 1068 num_nics = len(self.params.objects("nics"))
831 self.free_mac_address(vlan) 1069 for vlan in range(num_nics):
1070 self.free_mac_address(vlan)
832 1071
833 1072
834 @property 1073 @property
835 def monitor(self): 1074 def monitor(self):
836 """ 1075 """
837 Return the main monitor object, selected by the parameter main_monitor. 1076 Return the main monitor object, selected by the parameter main_monitor.
838 If main_monitor isn't defined, return the first monitor. 1077 If main_monitor isn't defined, return the first monitor.
839 If no monitors exist, or if main_monitor refers to a nonexistent 1078 If no monitors exist, or if main_monitor refers to a nonexistent
840 monitor, return None. 1079 monitor, return None.
841 """ 1080 """
842 for m in self.monitors: 1081 for m in self.monitors:
843 if m.name == self.params.get("main_monitor"): 1082 if m.name == self.params.get("main_monitor"):
844 return m 1083 return m
845 if self.monitors and not self.params.get("main_monitor"): 1084 if self.monitors and not self.params.get("main_monitor"):
846 return self.monitors[0] 1085 return self.monitors[0]
847 1086
848 1087
1088 def verify_alive(self):
1089 """
1090 Make sure the VM is alive and that the main monitor is responsive.
1091
1092 @raise VMDeadError: If the VM is dead
1093 @raise: Various monitor exceptions if the monitor is unresponsive
1094 """
1095 if self.is_dead():
1096 raise VMDeadError(self.process.get_status(),
1097 self.process.get_output())
1098 if self.monitors:
1099 self.monitor.verify_responsive()
1100
1101
849 def is_alive(self): 1102 def is_alive(self):
850 """ 1103 """
851 Return True if the VM is alive and its monitor is responsive. 1104 Return True if the VM is alive and its monitor is responsive.
852 """ 1105 """
853 # Check if the process is running 1106 return not self.is_dead() and (not self.monitors or
854 if self.is_dead(): 1107 self.monitor.is_responsive())
855 return False
856 # Try sending a monitor command
857 return bool(self.monitor) and self.monitor.is_responsive()
858 1108
859 1109
860 def is_dead(self): 1110 def is_dead(self):
861 """ 1111 """
862 Return True if the qemu process is dead. 1112 Return True if the qemu process is dead.
863 """ 1113 """
864 return not self.process or not self.process.is_alive() 1114 return not self.process or not self.process.is_alive()
865 1115
866 1116
867 def get_params(self): 1117 def get_params(self):
(...skipping 10 matching lines...) Expand all
878 """ 1128 """
879 return "/tmp/monitor-%s-%s" % (monitor_name, self.instance) 1129 return "/tmp/monitor-%s-%s" % (monitor_name, self.instance)
880 1130
881 1131
882 def get_monitor_filenames(self): 1132 def get_monitor_filenames(self):
883 """ 1133 """
884 Return a list of all monitor filenames (as specified in the VM's 1134 Return a list of all monitor filenames (as specified in the VM's
885 params). 1135 params).
886 """ 1136 """
887 return [self.get_monitor_filename(m) for m in 1137 return [self.get_monitor_filename(m) for m in
888 kvm_utils.get_sub_dict_names(self.params, "monitors")] 1138 self.params.objects("monitors")]
889 1139
890 1140
891 def get_serial_console_filename(self): 1141 def get_serial_console_filename(self):
892 """ 1142 """
893 Return the serial console filename. 1143 Return the serial console filename.
894 """ 1144 """
895 return "/tmp/serial-%s" % self.instance 1145 return "/tmp/serial-%s" % self.instance
896 1146
897 1147
898 def get_testlog_filename(self): 1148 def get_testlog_filename(self):
899 """ 1149 """
900 Return the testlog filename. 1150 Return the testlog filename.
901 """ 1151 """
902 return "/tmp/testlog-%s" % self.instance 1152 return "/tmp/testlog-%s" % self.instance
903 1153
904 1154
905 def get_address(self, index=0): 1155 def get_address(self, index=0):
906 """ 1156 """
907 Return the address of a NIC of the guest, in host space. 1157 Return the address of a NIC of the guest, in host space.
908 1158
909 If port redirection is used, return 'localhost' (the NIC has no IP 1159 If port redirection is used, return 'localhost' (the NIC has no IP
910 address of its own). Otherwise return the NIC's IP address. 1160 address of its own). Otherwise return the NIC's IP address.
911 1161
912 @param index: Index of the NIC whose address is requested. 1162 @param index: Index of the NIC whose address is requested.
1163 @raise VMMACAddressMissingError: If no MAC address is defined for the
1164 requested NIC
1165 @raise VMIPAddressMissingError: If no IP address is found for the the
1166 NIC's MAC address
1167 @raise VMAddressVerificationError: If the MAC-IP address mapping cannot
1168 be verified (using arping)
913 """ 1169 """
914 nics = kvm_utils.get_sub_dict_names(self.params, "nics") 1170 nics = self.params.objects("nics")
915 nic_name = nics[index] 1171 nic_name = nics[index]
916 nic_params = kvm_utils.get_sub_dict(self.params, nic_name) 1172 nic_params = self.params.object_params(nic_name)
917 if nic_params.get("nic_mode") == "tap": 1173 if nic_params.get("nic_mode") == "tap":
918 mac = self.get_mac_address(index) 1174 mac = self.get_mac_address(index).lower()
919 if not mac:
920 logging.debug("MAC address unavailable")
921 return None
922 mac = mac.lower()
923 # Get the IP address from the cache 1175 # Get the IP address from the cache
924 ip = self.address_cache.get(mac) 1176 ip = self.address_cache.get(mac)
925 if not ip: 1177 if not ip:
926 logging.debug("Could not find IP address for MAC address: %s" % 1178 raise VMIPAddressMissingError(mac)
927 mac)
928 return None
929 # Make sure the IP address is assigned to this guest 1179 # Make sure the IP address is assigned to this guest
930 macs = [self.get_mac_address(i) for i in range(len(nics))] 1180 macs = [self.get_mac_address(i) for i in range(len(nics))]
931 if not kvm_utils.verify_ip_address_ownership(ip, macs): 1181 if not kvm_utils.verify_ip_address_ownership(ip, macs):
932 logging.debug("Could not verify MAC-IP address mapping: " 1182 raise VMAddressVerificationError(mac, ip)
933 "%s ---> %s" % (mac, ip))
934 return None
935 return ip 1183 return ip
936 else: 1184 else:
937 return "localhost" 1185 return "localhost"
938 1186
939 1187
940 def get_port(self, port, nic_index=0): 1188 def get_port(self, port, nic_index=0):
941 """ 1189 """
942 Return the port in host space corresponding to port in guest space. 1190 Return the port in host space corresponding to port in guest space.
943 1191
944 @param port: Port number in host space. 1192 @param port: Port number in host space.
945 @param nic_index: Index of the NIC. 1193 @param nic_index: Index of the NIC.
946 @return: If port redirection is used, return the host port redirected 1194 @return: If port redirection is used, return the host port redirected
947 to guest port port. Otherwise return port. 1195 to guest port port. Otherwise return port.
1196 @raise VMPortNotRedirectedError: If an unredirected port is requested
1197 in user mode
948 """ 1198 """
949 nic_name = kvm_utils.get_sub_dict_names(self.params, "nics")[nic_index] 1199 nic_name = self.params.objects("nics")[nic_index]
950 nic_params = kvm_utils.get_sub_dict(self.params, nic_name) 1200 nic_params = self.params.object_params(nic_name)
951 if nic_params.get("nic_mode") == "tap": 1201 if nic_params.get("nic_mode") == "tap":
952 return port 1202 return port
953 else: 1203 else:
954 if not self.redirs.has_key(port): 1204 try:
955 logging.warn("Warning: guest port %s requested but not " 1205 return self.redirs[port]
956 "redirected" % port) 1206 except KeyError:
957 return self.redirs.get(port) 1207 raise VMPortNotRedirectedError(port)
958 1208
959 1209
960 def get_ifname(self, nic_index=0): 1210 def get_ifname(self, nic_index=0):
961 """ 1211 """
962 Return the ifname of a tap device associated with a NIC. 1212 Return the ifname of a tap device associated with a NIC.
963 1213
964 @param nic_index: Index of the NIC 1214 @param nic_index: Index of the NIC
965 """ 1215 """
966 nics = kvm_utils.get_sub_dict_names(self.params, "nics") 1216 nics = self.params.objects("nics")
967 nic_name = nics[nic_index] 1217 nic_name = nics[nic_index]
968 nic_params = kvm_utils.get_sub_dict(self.params, nic_name) 1218 nic_params = self.params.object_params(nic_name)
969 if nic_params.get("nic_ifname"): 1219 if nic_params.get("nic_ifname"):
970 return nic_params.get("nic_ifname") 1220 return nic_params.get("nic_ifname")
971 else: 1221 else:
972 return "t%d-%s" % (nic_index, self.instance[-11:]) 1222 return "t%d-%s" % (nic_index, self.instance[-11:])
973 1223
974 1224
975 def get_mac_address(self, nic_index=0): 1225 def get_mac_address(self, nic_index=0):
976 """ 1226 """
977 Return the MAC address of a NIC. 1227 Return the MAC address of a NIC.
978 1228
979 @param nic_index: Index of the NIC 1229 @param nic_index: Index of the NIC
1230 @raise VMMACAddressMissingError: If no MAC address is defined for the
1231 requested NIC
980 """ 1232 """
981 return kvm_utils.get_mac_address(self.instance, nic_index) 1233 mac = kvm_utils.get_mac_address(self.instance, nic_index)
1234 if not mac:
1235 raise VMMACAddressMissingError(nic_index)
1236 return mac
982 1237
983 1238
984 def free_mac_address(self, nic_index=0): 1239 def free_mac_address(self, nic_index=0):
985 """ 1240 """
986 Free a NIC's MAC address. 1241 Free a NIC's MAC address.
987 1242
988 @param nic_index: Index of the NIC 1243 @param nic_index: Index of the NIC
989 """ 1244 """
990 kvm_utils.free_mac_address(self.instance, nic_index) 1245 kvm_utils.free_mac_address(self.instance, nic_index)
991 1246
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
1024 if self.is_dead(): 1279 if self.is_dead():
1025 logging.error("Could not get shared memory info from dead VM.") 1280 logging.error("Could not get shared memory info from dead VM.")
1026 return None 1281 return None
1027 1282
1028 filename = "/proc/%d/statm" % self.get_pid() 1283 filename = "/proc/%d/statm" % self.get_pid()
1029 shm = int(open(filename).read().split()[2]) 1284 shm = int(open(filename).read().split()[2])
1030 # statm stores informations in pages, translate it to MB 1285 # statm stores informations in pages, translate it to MB
1031 return shm * 4.0 / 1024 1286 return shm * 4.0 / 1024
1032 1287
1033 1288
1034 def remote_login(self, nic_index=0, timeout=10): 1289 @error.context_aware
1290 def login(self, nic_index=0, timeout=10):
1035 """ 1291 """
1036 Log into the guest via SSH/Telnet/Netcat. 1292 Log into the guest via SSH/Telnet/Netcat.
1037 If timeout expires while waiting for output from the guest (e.g. a 1293 If timeout expires while waiting for output from the guest (e.g. a
1038 password prompt or a shell prompt) -- fail. 1294 password prompt or a shell prompt) -- fail.
1039 1295
1040 @param nic_index: The index of the NIC to connect to. 1296 @param nic_index: The index of the NIC to connect to.
1041 @param timeout: Time (seconds) before giving up logging into the 1297 @param timeout: Time (seconds) before giving up logging into the
1042 guest. 1298 guest.
1043 @return: kvm_spawn object on success and None on failure. 1299 @return: A ShellSession object.
1044 """ 1300 """
1301 error.context("logging into '%s'" % self.name)
1045 username = self.params.get("username", "") 1302 username = self.params.get("username", "")
1046 password = self.params.get("password", "") 1303 password = self.params.get("password", "")
1047 prompt = self.params.get("shell_prompt", "[\#\$]") 1304 prompt = self.params.get("shell_prompt", "[\#\$]")
1048 linesep = eval("'%s'" % self.params.get("shell_linesep", r"\n")) 1305 linesep = eval("'%s'" % self.params.get("shell_linesep", r"\n"))
1049 client = self.params.get("shell_client") 1306 client = self.params.get("shell_client")
1050 address = self.get_address(nic_index) 1307 address = self.get_address(nic_index)
1051 port = self.get_port(int(self.params.get("shell_port"))) 1308 port = self.get_port(int(self.params.get("shell_port")))
1052 log_filename = ("session-%s-%s.log" % 1309 log_filename = ("session-%s-%s.log" %
1053 (self.name, kvm_utils.generate_random_string(4))) 1310 (self.name, kvm_utils.generate_random_string(4)))
1054
1055 if not address or not port:
1056 logging.debug("IP address or port unavailable")
1057 return None
1058
1059 session = kvm_utils.remote_login(client, address, port, username, 1311 session = kvm_utils.remote_login(client, address, port, username,
1060 password, prompt, linesep, 1312 password, prompt, linesep,
1061 log_filename, timeout) 1313 log_filename, timeout)
1062 1314 session.set_status_test_command(self.params.get("status_test_command",
1063 if session: 1315 ""))
1064 session.set_status_test_command(self.params.get("status_test_"
1065 "command", ""))
1066 return session 1316 return session
1067 1317
1068 1318
1069 def copy_files_to(self, local_path, remote_path, nic_index=0, timeout=600): 1319 def remote_login(self, nic_index=0, timeout=10):
1070 """ 1320 """
1071 Transfer files to the guest. 1321 Alias for login() for backward compatibility.
1322 """
1323 return self.login(nic_index, timeout)
1072 1324
1073 @param local_path: Host path 1325
1074 @param remote_path: Guest path 1326 def wait_for_login(self, nic_index=0, timeout=240, internal_timeout=10):
1327 """
1328 Make multiple attempts to log into the guest via SSH/Telnet/Netcat.
1329
1075 @param nic_index: The index of the NIC to connect to. 1330 @param nic_index: The index of the NIC to connect to.
1331 @param timeout: Time (seconds) to keep trying to log in.
1332 @param internal_timeout: Timeout to pass to login().
1333 @return: A ShellSession object.
1334 """
1335 logging.debug("Attempting to log into '%s' (timeout %ds)", self.name,
1336 timeout)
1337 end_time = time.time() + timeout
1338 while time.time() < end_time:
1339 try:
1340 return self.login(nic_index, internal_timeout)
1341 except (kvm_utils.LoginError, VMError), e:
1342 logging.debug(e)
1343 time.sleep(2)
1344 # Timeout expired; try one more time but don't catch exceptions
1345 return self.login(nic_index, internal_timeout)
1346
1347
1348 @error.context_aware
1349 def copy_files_to(self, host_path, guest_path, nic_index=0, verbose=False,
1350 timeout=600):
1351 """
1352 Transfer files to the remote host(guest).
1353
1354 @param host_path: Host path
1355 @param guest_path: Guest path
1356 @param nic_index: The index of the NIC to connect to.
1357 @param verbose: If True, log some stats using logging.debug (RSS only)
1076 @param timeout: Time (seconds) before giving up on doing the remote 1358 @param timeout: Time (seconds) before giving up on doing the remote
1077 copy. 1359 copy.
1078 """ 1360 """
1361 error.context("sending file(s) to '%s'" % self.name)
1079 username = self.params.get("username", "") 1362 username = self.params.get("username", "")
1080 password = self.params.get("password", "") 1363 password = self.params.get("password", "")
1081 client = self.params.get("file_transfer_client") 1364 client = self.params.get("file_transfer_client")
1082 address = self.get_address(nic_index) 1365 address = self.get_address(nic_index)
1083 port = self.get_port(int(self.params.get("file_transfer_port"))) 1366 port = self.get_port(int(self.params.get("file_transfer_port")))
1084 1367 log_filename = ("transfer-%s-to-%s-%s.log" %
1085 if not address or not port: 1368 (self.name, address,
1086 logging.debug("IP address or port unavailable") 1369 kvm_utils.generate_random_string(4)))
1087 return None 1370 kvm_utils.copy_files_to(address, client, username, password, port,
1088 1371 host_path, guest_path, log_filename, verbose,
1089 if client == "scp": 1372 timeout)
1090 log_filename = ("scp-%s-%s.log" %
1091 (self.name, kvm_utils.generate_random_string(4)))
1092 return kvm_utils.scp_to_remote(address, port, username, password,
1093 local_path, remote_path,
1094 log_filename, timeout)
1095 elif client == "rss":
1096 c = rss_file_transfer.FileUploadClient(address, port)
1097 c.upload(local_path, remote_path, timeout)
1098 c.close()
1099 return True
1100 1373
1101 1374
1102 def copy_files_from(self, remote_path, local_path, nic_index=0, timeout=600) : 1375 @error.context_aware
1376 def copy_files_from(self, guest_path, host_path, nic_index=0,
1377 verbose=False, timeout=600):
1103 """ 1378 """
1104 Transfer files from the guest. 1379 Transfer files from the guest.
1105 1380
1106 @param local_path: Guest path 1381 @param host_path: Guest path
1107 @param remote_path: Host path 1382 @param guest_path: Host path
1108 @param nic_index: The index of the NIC to connect to. 1383 @param nic_index: The index of the NIC to connect to.
1384 @param verbose: If True, log some stats using logging.debug (RSS only)
1109 @param timeout: Time (seconds) before giving up on doing the remote 1385 @param timeout: Time (seconds) before giving up on doing the remote
1110 copy. 1386 copy.
1111 """ 1387 """
1388 error.context("receiving file(s) from '%s'" % self.name)
1112 username = self.params.get("username", "") 1389 username = self.params.get("username", "")
1113 password = self.params.get("password", "") 1390 password = self.params.get("password", "")
1114 client = self.params.get("file_transfer_client") 1391 client = self.params.get("file_transfer_client")
1115 address = self.get_address(nic_index) 1392 address = self.get_address(nic_index)
1116 port = self.get_port(int(self.params.get("file_transfer_port"))) 1393 port = self.get_port(int(self.params.get("file_transfer_port")))
1117 1394 log_filename = ("transfer-%s-from-%s-%s.log" %
1118 if not address or not port: 1395 (self.name, address,
1119 logging.debug("IP address or port unavailable") 1396 kvm_utils.generate_random_string(4)))
1120 return None 1397 kvm_utils.copy_files_from(address, client, username, password, port,
1121 1398 guest_path, host_path, log_filename,
1122 if client == "scp": 1399 verbose, timeout)
1123 log_filename = ("scp-%s-%s.log" %
1124 (self.name, kvm_utils.generate_random_string(4)))
1125 return kvm_utils.scp_from_remote(address, port, username, password,
1126 remote_path, local_path,
1127 log_filename, timeout)
1128 elif client == "rss":
1129 c = rss_file_transfer.FileDownloadClient(address, port)
1130 c.download(remote_path, local_path, timeout)
1131 c.close()
1132 return True
1133 1400
1134 1401
1402 @error.context_aware
1135 def serial_login(self, timeout=10): 1403 def serial_login(self, timeout=10):
1136 """ 1404 """
1137 Log into the guest via the serial console. 1405 Log into the guest via the serial console.
1138 If timeout expires while waiting for output from the guest (e.g. a 1406 If timeout expires while waiting for output from the guest (e.g. a
1139 password prompt or a shell prompt) -- fail. 1407 password prompt or a shell prompt) -- fail.
1140 1408
1141 @param timeout: Time (seconds) before giving up logging into the guest. 1409 @param timeout: Time (seconds) before giving up logging into the guest.
1142 @return: kvm_spawn object on success and None on failure. 1410 @return: ShellSession object on success and None on failure.
1143 """ 1411 """
1412 error.context("logging into '%s' via serial console" % self.name)
1144 username = self.params.get("username", "") 1413 username = self.params.get("username", "")
1145 password = self.params.get("password", "") 1414 password = self.params.get("password", "")
1146 prompt = self.params.get("shell_prompt", "[\#\$]") 1415 prompt = self.params.get("shell_prompt", "[\#\$]")
1147 linesep = eval("'%s'" % self.params.get("shell_linesep", r"\n")) 1416 linesep = eval("'%s'" % self.params.get("shell_linesep", r"\n"))
1148 status_test_command = self.params.get("status_test_command", "") 1417 status_test_command = self.params.get("status_test_command", "")
1149 1418
1150 if self.serial_console: 1419 self.serial_console.set_linesep(linesep)
1151 self.serial_console.set_linesep(linesep) 1420 self.serial_console.set_status_test_command(status_test_command)
1152 self.serial_console.set_status_test_command(status_test_command) 1421
1422 # Try to get a login prompt
1423 self.serial_console.sendline()
1424
1425 kvm_utils._remote_login(self.serial_console, username, password,
1426 prompt, timeout)
1427 return self.serial_console
1428
1429
1430 def wait_for_serial_login(self, timeout=240, internal_timeout=10):
1431 """
1432 Make multiple attempts to log into the guest via serial console.
1433
1434 @param timeout: Time (seconds) to keep trying to log in.
1435 @param internal_timeout: Timeout to pass to serial_login().
1436 @return: A ShellSession object.
1437 """
1438 logging.debug("Attempting to log into '%s' via serial console "
1439 "(timeout %ds)", self.name, timeout)
1440 end_time = time.time() + timeout
1441 while time.time() < end_time:
1442 try:
1443 return self.serial_login(internal_timeout)
1444 except kvm_utils.LoginError, e:
1445 logging.debug(e)
1446 time.sleep(2)
1447 # Timeout expired; try one more time but don't catch exceptions
1448 return self.serial_login(internal_timeout)
1449
1450
1451 @error.context_aware
1452 def migrate(self, timeout=3600, protocol="tcp", cancel_delay=None,
1453 offline=False, stable_check=False, clean=True,
1454 save_path="/tmp", dest_host="localhost", remote_port=None):
1455 """
1456 Migrate the VM.
1457
1458 If the migration is local, the VM object's state is switched with that
1459 of the destination VM. Otherwise, the state is switched with that of
1460 a dead VM (returned by self.clone()).
1461
1462 @param timeout: Time to wait for migration to complete.
1463 @param protocol: Migration protocol ('tcp', 'unix' or 'exec').
1464 @param cancel_delay: If provided, specifies a time duration after which
1465 migration will be canceled. Used for testing migrate_cancel.
1466 @param offline: If True, pause the source VM before migration.
1467 @param stable_check: If True, compare the VM's state after migration to
1468 its state before migration and raise an exception if they
1469 differ.
1470 @param clean: If True, delete the saved state files (relevant only if
1471 stable_check is also True).
1472 @save_path: The path for state files.
1473 @param dest_host: Destination host (defaults to 'localhost').
1474 @param remote_port: Port to use for remote migration.
1475 """
1476 error.base_context("migrating '%s'" % self.name)
1477
1478 def mig_finished():
1479 o = self.monitor.info("migrate")
1480 if isinstance(o, str):
1481 return "status: active" not in o
1482 else:
1483 return o.get("status") != "active"
1484
1485 def mig_succeeded():
1486 o = self.monitor.info("migrate")
1487 if isinstance(o, str):
1488 return "status: completed" in o
1489 else:
1490 return o.get("status") == "completed"
1491
1492 def mig_failed():
1493 o = self.monitor.info("migrate")
1494 if isinstance(o, str):
1495 return "status: failed" in o
1496 else:
1497 return o.get("status") == "failed"
1498
1499 def mig_cancelled():
1500 o = self.monitor.info("migrate")
1501 if isinstance(o, str):
1502 return ("Migration status: cancelled" in o or
1503 "Migration status: canceled" in o)
1504 else:
1505 return (o.get("status") == "cancelled" or
1506 o.get("status") == "canceled")
1507
1508 def wait_for_migration():
1509 if not kvm_utils.wait_for(mig_finished, timeout, 2, 2,
1510 "Waiting for migration to complete"):
1511 raise VMMigrateTimeoutError("Timeout expired while waiting "
1512 "for migration to finish")
1513
1514 local = dest_host == "localhost"
1515
1516 clone = self.clone()
1517 if local:
1518 error.context("creating destination VM")
1519 if stable_check:
1520 # Pause the dest vm after creation
1521 extra_params = clone.params.get("extra_params", "") + " -S"
1522 clone.params["extra_params"] = extra_params
1523 clone.create(migration_mode=protocol, mac_source=self)
1524 error.context()
1525
1526 try:
1527 if protocol == "tcp":
1528 if local:
1529 uri = "tcp:localhost:%d" % clone.migration_port
1530 else:
1531 uri = "tcp:%s:%d" % (dest_host, remote_port)
1532 elif protocol == "unix":
1533 uri = "unix:%s" % clone.migration_file
1534 elif protocol == "exec":
1535 uri = '"exec:nc localhost %s"' % clone.migration_port
1536
1537 if offline:
1538 self.monitor.cmd("stop")
1539
1540 logging.info("Migrating to %s", uri)
1541 self.monitor.migrate(uri)
1542
1543 if cancel_delay:
1544 time.sleep(cancel_delay)
1545 self.monitor.cmd("migrate_cancel")
1546 if not kvm_utils.wait_for(mig_cancelled, 60, 2, 2,
1547 "Waiting for migration "
1548 "cancellation"):
1549 raise VMMigrateCancelError("Cannot cancel migration")
1550 return
1551
1552 wait_for_migration()
1553
1554 # Report migration status
1555 if mig_succeeded():
1556 logging.info("Migration completed successfully")
1557 elif mig_failed():
1558 raise VMMigrateFailedError("Migration failed")
1559 else:
1560 raise VMMigrateFailedError("Migration ended with unknown "
1561 "status")
1562
1563 # Switch self <-> clone
1564 temp = self.clone(copy_state=True)
1565 self.__dict__ = clone.__dict__
1566 clone = temp
1567
1568 # From now on, clone is the source VM that will soon be destroyed
1569 # and self is the destination VM that will remain alive. If this
1570 # is remote migration, self is a dead VM object.
1571
1572 error.context("after migration")
1573 if local:
1574 time.sleep(1)
1575 self.verify_alive()
1576
1577 if local and stable_check:
1578 try:
1579 save1 = os.path.join(save_path, "src-" + clone.instance)
1580 save2 = os.path.join(save_path, "dst-" + self.instance)
1581 clone.save_to_file(save1)
1582 self.save_to_file(save2)
1583 # Fail if we see deltas
1584 md5_save1 = utils.hash_file(save1)
1585 md5_save2 = utils.hash_file(save2)
1586 if md5_save1 != md5_save2:
1587 raise VMMigrateStateMismatchError(md5_save1, md5_save2)
1588 finally:
1589 if clean:
1590 if os.path.isfile(save1):
1591 os.remove(save1)
1592 if os.path.isfile(save2):
1593 os.remove(save2)
1594
1595 finally:
1596 # If we're doing remote migration and it's completed successfully,
1597 # self points to a dead VM object
1598 if self.is_alive():
1599 self.monitor.cmd("cont")
1600 clone.destroy(gracefully=False)
1601
1602
1603 @error.context_aware
1604 def reboot(self, session=None, method="shell", nic_index=0, timeout=240):
1605 """
1606 Reboot the VM and wait for it to come back up by trying to log in until
1607 timeout expires.
1608
1609 @param session: A shell session object or None.
1610 @param method: Reboot method. Can be "shell" (send a shell reboot
1611 command) or "system_reset" (send a system_reset monitor command) .
1612 @param nic_index: Index of NIC to access in the VM, when logging in
1613 after rebooting.
1614 @param timeout: Time to wait for login to succeed (after rebooting).
1615 @return: A new shell session object.
1616 """
1617 error.base_context("rebooting '%s'" % self.name, logging.info)
1618 error.context("before reboot")
1619 session = session or self.login()
1620 error.context()
1621
1622 if method == "shell":
1623 session.sendline(self.params.get("reboot_command"))
1624 elif method == "system_reset":
1625 # Clear the event list of all QMP monitors
1626 qmp_monitors = [m for m in self.monitors if m.protocol == "qmp"]
1627 for m in qmp_monitors:
1628 m.clear_events()
1629 # Send a system_reset monitor command
1630 self.monitor.cmd("system_reset")
1631 # Look for RESET QMP events
1632 time.sleep(1)
1633 for m in qmp_monitors:
1634 if m.get_event("RESET"):
1635 logging.info("RESET QMP event received")
1636 else:
1637 raise VMRebootError("RESET QMP event not received after "
1638 "system_reset (monitor '%s')" % m.name)
1153 else: 1639 else:
1154 return None 1640 raise VMRebootError("Unknown reboot method: %s" % method)
1155 1641
1156 # Make sure we get a login prompt 1642 error.context("waiting for guest to go down", logging.info)
1157 self.serial_console.sendline() 1643 if not kvm_utils.wait_for(lambda:
1158 1644 not session.is_responsive(timeout=30),
1159 if kvm_utils._remote_login(self.serial_console, username, password, 1645 120, 0, 1):
1160 prompt, timeout): 1646 raise VMRebootError("Guest refuses to go down")
1161 return self.serial_console 1647 session.close()
1648
1649 error.context("logging in after reboot", logging.info)
1650 return self.wait_for_login(nic_index, timeout=timeout)
1162 1651
1163 1652
1164 def send_key(self, keystr): 1653 def send_key(self, keystr):
1165 """ 1654 """
1166 Send a key event to the VM. 1655 Send a key event to the VM.
1167 1656
1168 @param: keystr: A key event string (e.g. "ctrl-alt-delete") 1657 @param: keystr: A key event string (e.g. "ctrl-alt-delete")
1169 """ 1658 """
1170 # For compatibility with versions of QEMU that do not recognize all 1659 # For compatibility with versions of QEMU that do not recognize all
1171 # key names: replace keyname with the hex value from the dict, which 1660 # key names: replace keyname with the hex value from the dict, which
(...skipping 30 matching lines...) Expand all
1202 if self.params.get("uuid") == "random": 1691 if self.params.get("uuid") == "random":
1203 return self.uuid 1692 return self.uuid
1204 else: 1693 else:
1205 return self.params.get("uuid", None) 1694 return self.params.get("uuid", None)
1206 1695
1207 1696
1208 def get_cpu_count(self): 1697 def get_cpu_count(self):
1209 """ 1698 """
1210 Get the cpu count of the VM. 1699 Get the cpu count of the VM.
1211 """ 1700 """
1212 session = self.remote_login() 1701 session = self.login()
1213 if not session:
1214 return None
1215 try: 1702 try:
1216 cmd = self.params.get("cpu_chk_cmd") 1703 return int(session.cmd(self.params.get("cpu_chk_cmd")))
1217 s, count = session.get_command_status_output(cmd)
1218 if s == 0:
1219 return int(count)
1220 return None
1221 finally: 1704 finally:
1222 session.close() 1705 session.close()
1223 1706
1224 1707
1225 def get_memory_size(self, cmd=None): 1708 def get_memory_size(self, cmd=None):
1226 """ 1709 """
1227 Get bootup memory size of the VM. 1710 Get bootup memory size of the VM.
1228 1711
1229 @param check_cmd: Command used to check memory. If not provided, 1712 @param check_cmd: Command used to check memory. If not provided,
1230 self.params.get("mem_chk_cmd") will be used. 1713 self.params.get("mem_chk_cmd") will be used.
1231 """ 1714 """
1232 session = self.remote_login() 1715 session = self.login()
1233 if not session:
1234 return None
1235 try: 1716 try:
1236 if not cmd: 1717 if not cmd:
1237 cmd = self.params.get("mem_chk_cmd") 1718 cmd = self.params.get("mem_chk_cmd")
1238 s, mem_str = session.get_command_status_output(cmd) 1719 mem_str = session.cmd(cmd)
1239 if s != 0:
1240 return None
1241 mem = re.findall("([0-9]+)", mem_str) 1720 mem = re.findall("([0-9]+)", mem_str)
1242 mem_size = 0 1721 mem_size = 0
1243 for m in mem: 1722 for m in mem:
1244 mem_size += int(m) 1723 mem_size += int(m)
1245 if "GB" in mem_str: 1724 if "GB" in mem_str:
1246 mem_size *= 1024 1725 mem_size *= 1024
1247 elif "MB" in mem_str: 1726 elif "MB" in mem_str:
1248 pass 1727 pass
1249 else: 1728 else:
1250 mem_size /= 1024 1729 mem_size /= 1024
1251 return int(mem_size) 1730 return int(mem_size)
1252 finally: 1731 finally:
1253 session.close() 1732 session.close()
1254 1733
1255 1734
1256 def get_current_memory_size(self): 1735 def get_current_memory_size(self):
1257 """ 1736 """
1258 Get current memory size of the VM, rather than bootup memory. 1737 Get current memory size of the VM, rather than bootup memory.
1259 """ 1738 """
1260 cmd = self.params.get("mem_chk_cur_cmd") 1739 cmd = self.params.get("mem_chk_cur_cmd")
1261 return self.get_memory_size(cmd) 1740 return self.get_memory_size(cmd)
1741
1742
1743 def save_to_file(self, path):
1744 """
1745 Save the state of virtual machine to a file through migrate to
1746 exec
1747 """
1748 # Make sure we only get one iteration
1749 self.monitor.cmd("migrate_set_speed 1000g")
1750 self.monitor.cmd("migrate_set_downtime 100000000")
1751 self.monitor.migrate('"exec:cat>%s"' % path)
1752 # Restore the speed and downtime of migration
1753 self.monitor.cmd("migrate_set_speed %d" % (32<<20))
1754 self.monitor.cmd("migrate_set_downtime 0.03")
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698