blob: 248aeca4a3d345dab9f73611c2d97f24127c0335 [file] [log] [blame]
lmr6f669ce2009-05-31 19:02:42 +00001#!/usr/bin/python
lmr6f669ce2009-05-31 19:02:42 +00002"""
3Utility classes and functions to handle Virtual Machine creation using qemu.
4
5@copyright: 2008-2009 Red Hat Inc.
6"""
7
lmr9e964a02010-06-18 03:46:21 +00008import time, socket, os, logging, fcntl, re, commands, glob
lmrcbc86ef2010-07-07 20:50:08 +00009import kvm_utils, kvm_subprocess, kvm_monitor, rss_file_transfer
lmr47a853b2010-02-04 13:56:48 +000010from autotest_lib.client.common_lib import error
lmrd60882f2010-02-04 03:26:36 +000011from autotest_lib.client.bin import utils
lmrb635b862009-09-10 14:53:21 +000012
lmr6f669ce2009-05-31 19:02:42 +000013
lmr90b9fd52009-08-17 20:48:18 +000014def get_image_filename(params, root_dir):
lmr6f669ce2009-05-31 19:02:42 +000015 """
lmr90b9fd52009-08-17 20:48:18 +000016 Generate an image path from params and root_dir.
lmr6f669ce2009-05-31 19:02:42 +000017
18 @param params: Dictionary containing the test parameters.
lmr90b9fd52009-08-17 20:48:18 +000019 @param root_dir: Base directory for relative filenames.
lmr6f669ce2009-05-31 19:02:42 +000020
21 @note: params should contain:
22 image_name -- the name of the image file, without extension
23 image_format -- the format of the image (qcow2, raw etc)
24 """
25 image_name = params.get("image_name", "image")
26 image_format = params.get("image_format", "qcow2")
27 image_filename = "%s.%s" % (image_name, image_format)
lmr90b9fd52009-08-17 20:48:18 +000028 image_filename = kvm_utils.get_path(root_dir, image_filename)
lmr6f669ce2009-05-31 19:02:42 +000029 return image_filename
30
31
lmr52800ba2009-08-17 20:49:58 +000032def create_image(params, root_dir):
lmr6f669ce2009-05-31 19:02:42 +000033 """
34 Create an image using qemu_image.
35
36 @param params: Dictionary containing the test parameters.
lmr90b9fd52009-08-17 20:48:18 +000037 @param root_dir: Base directory for relative filenames.
lmr6f669ce2009-05-31 19:02:42 +000038
39 @note: params should contain:
40 image_name -- the name of the image file, without extension
41 image_format -- the format of the image (qcow2, raw etc)
42 image_size -- the requested size of the image (a string
43 qemu-img can understand, such as '10G')
44 """
lmr52800ba2009-08-17 20:49:58 +000045 qemu_img_cmd = kvm_utils.get_path(root_dir, params.get("qemu_img_binary",
46 "qemu-img"))
lmr6f669ce2009-05-31 19:02:42 +000047 qemu_img_cmd += " create"
48
49 format = params.get("image_format", "qcow2")
50 qemu_img_cmd += " -f %s" % format
51
lmr90b9fd52009-08-17 20:48:18 +000052 image_filename = get_image_filename(params, root_dir)
lmr6f669ce2009-05-31 19:02:42 +000053 qemu_img_cmd += " %s" % image_filename
54
55 size = params.get("image_size", "10G")
56 qemu_img_cmd += " %s" % size
57
lmr47a853b2010-02-04 13:56:48 +000058 try:
59 utils.system(qemu_img_cmd)
60 except error.CmdError, e:
61 logging.error("Could not create image; qemu-img command failed:\n%s",
62 str(e))
63 return None
lmr6f669ce2009-05-31 19:02:42 +000064
lmr6f669ce2009-05-31 19:02:42 +000065 if not os.path.exists(image_filename):
lmra4967622009-07-23 01:36:32 +000066 logging.error("Image could not be created for some reason; "
67 "qemu-img command:\n%s" % qemu_img_cmd)
lmr6f669ce2009-05-31 19:02:42 +000068 return None
69
70 logging.info("Image created in %s" % image_filename)
71 return image_filename
72
73
lmr90b9fd52009-08-17 20:48:18 +000074def remove_image(params, root_dir):
lmr6f669ce2009-05-31 19:02:42 +000075 """
76 Remove an image file.
77
78 @param params: A dict
lmr90b9fd52009-08-17 20:48:18 +000079 @param root_dir: Base directory for relative filenames.
lmr6f669ce2009-05-31 19:02:42 +000080
81 @note: params should contain:
82 image_name -- the name of the image file, without extension
83 image_format -- the format of the image (qcow2, raw etc)
84 """
lmr90b9fd52009-08-17 20:48:18 +000085 image_filename = get_image_filename(params, root_dir)
lmr6f669ce2009-05-31 19:02:42 +000086 logging.debug("Removing image file %s..." % image_filename)
87 if os.path.exists(image_filename):
88 os.unlink(image_filename)
89 else:
90 logging.debug("Image file %s not found")
91
92
93class VM:
94 """
95 This class handles all basic VM operations.
96 """
97
lmr52800ba2009-08-17 20:49:58 +000098 def __init__(self, name, params, root_dir, address_cache):
lmr6f669ce2009-05-31 19:02:42 +000099 """
100 Initialize the object and set a few attributes.
101
102 @param name: The name of the object
103 @param params: A dict containing VM params
104 (see method make_qemu_command for a full description)
lmr90b9fd52009-08-17 20:48:18 +0000105 @param root_dir: Base directory for relative filenames
lmree90dd92009-08-13 04:13:39 +0000106 @param address_cache: A dict that maps MAC addresses to IP addresses
lmr6f669ce2009-05-31 19:02:42 +0000107 """
lmra4967622009-07-23 01:36:32 +0000108 self.process = None
lmraa380a22010-06-22 02:05:29 +0000109 self.serial_console = None
lmr953ffba2009-07-27 13:20:10 +0000110 self.redirs = {}
111 self.vnc_port = 5900
lmra2533222009-07-20 12:43:46 +0000112 self.uuid = None
lmr9e964a02010-06-18 03:46:21 +0000113 self.monitors = []
114 self.pci_assignable = None
lmr6f669ce2009-05-31 19:02:42 +0000115
116 self.name = name
117 self.params = params
lmr90b9fd52009-08-17 20:48:18 +0000118 self.root_dir = root_dir
lmree90dd92009-08-13 04:13:39 +0000119 self.address_cache = address_cache
lmrb1cad1e2010-06-17 17:36:09 +0000120 self.netdev_id = []
lmr6f669ce2009-05-31 19:02:42 +0000121
lmr9e964a02010-06-18 03:46:21 +0000122 # Find a unique identifier for this VM
lmr8b134f92009-06-08 14:47:31 +0000123 while True:
lmrd16a67d2009-06-10 19:52:59 +0000124 self.instance = (time.strftime("%Y%m%d-%H%M%S-") +
125 kvm_utils.generate_random_string(4))
lmr9e964a02010-06-18 03:46:21 +0000126 if not glob.glob("/tmp/*%s" % self.instance):
lmr8b134f92009-06-08 14:47:31 +0000127 break
128
129
lmr52800ba2009-08-17 20:49:58 +0000130 def clone(self, name=None, params=None, root_dir=None, address_cache=None):
lmr2c241172009-06-08 15:11:29 +0000131 """
132 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().
134 Any parameters not passed to this function are copied from the source
135 VM.
136
137 @param name: Optional new VM name
138 @param params: Optional new VM creation parameters
lmr90b9fd52009-08-17 20:48:18 +0000139 @param root_dir: Optional new base directory for relative filenames
lmree90dd92009-08-13 04:13:39 +0000140 @param address_cache: A dict that maps MAC addresses to IP addresses
lmr2c241172009-06-08 15:11:29 +0000141 """
lmre45a1f22009-11-10 16:35:08 +0000142 if name is None:
lmr2c241172009-06-08 15:11:29 +0000143 name = self.name
lmre45a1f22009-11-10 16:35:08 +0000144 if params is None:
lmr2c241172009-06-08 15:11:29 +0000145 params = self.params.copy()
lmre45a1f22009-11-10 16:35:08 +0000146 if root_dir is None:
lmr90b9fd52009-08-17 20:48:18 +0000147 root_dir = self.root_dir
lmre45a1f22009-11-10 16:35:08 +0000148 if address_cache is None:
lmree90dd92009-08-13 04:13:39 +0000149 address_cache = self.address_cache
lmr52800ba2009-08-17 20:49:58 +0000150 return VM(name, params, root_dir, address_cache)
lmr2c241172009-06-08 15:11:29 +0000151
152
lmr52800ba2009-08-17 20:49:58 +0000153 def make_qemu_command(self, name=None, params=None, root_dir=None):
lmr6f669ce2009-05-31 19:02:42 +0000154 """
155 Generate a qemu command line. All parameters are optional. If a
156 parameter is not supplied, the corresponding value stored in the
157 class attributes is used.
158
lmr6f669ce2009-05-31 19:02:42 +0000159 @param name: The name of the object
160 @param params: A dict containing VM params
lmr90b9fd52009-08-17 20:48:18 +0000161 @param root_dir: Base directory for relative filenames
lmr6f669ce2009-05-31 19:02:42 +0000162
163 @note: The params dict should contain:
164 mem -- memory size in MBs
165 cdrom -- ISO filename to use with the qemu -cdrom parameter
lmr6f669ce2009-05-31 19:02:42 +0000166 extra_params -- a string to append to the qemu command
lmr912c74b2009-08-17 20:43:27 +0000167 shell_port -- port of the remote shell daemon on the guest
168 (SSH, Telnet or the home-made Remote Shell Server)
169 shell_client -- client program to use for connecting to the
170 remote shell daemon on the guest (ssh, telnet or nc)
lmreeff0eb2009-06-10 19:19:15 +0000171 x11_display -- if specified, the DISPLAY environment variable
172 will be be set to this value for the qemu process (useful for
173 SDL rendering)
lmr6f669ce2009-05-31 19:02:42 +0000174 images -- a list of image object names, separated by spaces
175 nics -- a list of NIC object names, separated by spaces
176
177 For each image in images:
178 drive_format -- string to pass as 'if' parameter for this
179 image (e.g. ide, scsi)
180 image_snapshot -- if yes, pass 'snapshot=on' to qemu for
181 this image
182 image_boot -- if yes, pass 'boot=on' to qemu for this image
183 In addition, all parameters required by get_image_filename.
184
185 For each NIC in nics:
186 nic_model -- string to pass as 'model' parameter for this
187 NIC (e.g. e1000)
188 """
lmr48abd7d2010-05-26 13:48:04 +0000189 # Helper function for command line option wrappers
190 def has_option(help, option):
191 return bool(re.search(r"^-%s(\s|$)" % option, help, re.MULTILINE))
192
193 # Wrappers for all supported qemu command line parameters.
194 # This is meant to allow support for multiple qemu versions.
195 # Each of these functions receives the output of 'qemu -help' as a
196 # parameter, and should add the requested command line option
197 # accordingly.
198
199 def add_name(help, name):
200 return " -name '%s'" % name
201
lmr9e964a02010-06-18 03:46:21 +0000202 def add_human_monitor(help, filename):
lmr09a78162010-06-14 16:29:23 +0000203 return " -monitor unix:'%s',server,nowait" % filename
lmr48abd7d2010-05-26 13:48:04 +0000204
lmr9e964a02010-06-18 03:46:21 +0000205 def add_qmp_monitor(help, filename):
206 return " -qmp unix:'%s',server,nowait" % filename
207
lmr2b06f332010-06-22 02:03:41 +0000208 def add_serial(help, filename):
209 return " -serial unix:'%s',server,nowait" % filename
210
lmr48abd7d2010-05-26 13:48:04 +0000211 def add_mem(help, mem):
212 return " -m %s" % mem
213
214 def add_smp(help, smp):
215 return " -smp %s" % smp
216
lmraf709702010-07-20 00:56:33 +0000217 def add_cdrom(help, filename, index=None):
lmr48abd7d2010-05-26 13:48:04 +0000218 if has_option(help, "drive"):
lmraf709702010-07-20 00:56:33 +0000219 cmd = " -drive file='%s',media=cdrom" % filename
220 if index is not None: cmd += ",index=%s" % index
221 return cmd
lmr48abd7d2010-05-26 13:48:04 +0000222 else:
lmr09a78162010-06-14 16:29:23 +0000223 return " -cdrom '%s'" % filename
lmr48abd7d2010-05-26 13:48:04 +0000224
lmraf709702010-07-20 00:56:33 +0000225 def add_drive(help, filename, index=None, format=None, cache=None,
226 werror=None, serial=None, snapshot=False, boot=False):
lmr09a78162010-06-14 16:29:23 +0000227 cmd = " -drive file='%s'" % filename
lmraf709702010-07-20 00:56:33 +0000228 if index is not None: cmd += ",index=%s" % index
lmr48abd7d2010-05-26 13:48:04 +0000229 if format: cmd += ",if=%s" % format
230 if cache: cmd += ",cache=%s" % cache
231 if werror: cmd += ",werror=%s" % werror
lmr09a78162010-06-14 16:29:23 +0000232 if serial: cmd += ",serial='%s'" % serial
lmr48abd7d2010-05-26 13:48:04 +0000233 if snapshot: cmd += ",snapshot=on"
234 if boot: cmd += ",boot=on"
235 return cmd
236
lmrb1cad1e2010-06-17 17:36:09 +0000237 def add_nic(help, vlan, model=None, mac=None, netdev_id=None):
lmr48abd7d2010-05-26 13:48:04 +0000238 cmd = " -net nic,vlan=%d" % vlan
lmrb1cad1e2010-06-17 17:36:09 +0000239 if has_option(help, "netdev"):
240 cmd +=",netdev=%s" % netdev_id
lmr48abd7d2010-05-26 13:48:04 +0000241 if model: cmd += ",model=%s" % model
lmr09a78162010-06-14 16:29:23 +0000242 if mac: cmd += ",macaddr='%s'" % mac
lmr48abd7d2010-05-26 13:48:04 +0000243 return cmd
244
245 def add_net(help, vlan, mode, ifname=None, script=None,
lmr6976faa2010-07-09 21:11:06 +0000246 downscript=None, tftp=None, bootfile=None, hostfwd=[],
247 netdev_id=None):
lmrb1cad1e2010-06-17 17:36:09 +0000248 if has_option(help, "netdev"):
249 cmd = " -netdev %s,id=%s" % (mode, netdev_id)
250 else:
251 cmd = " -net %s,vlan=%d" % (mode, vlan)
lmr48abd7d2010-05-26 13:48:04 +0000252 if mode == "tap":
lmr09a78162010-06-14 16:29:23 +0000253 if ifname: cmd += ",ifname='%s'" % ifname
254 if script: cmd += ",script='%s'" % script
255 cmd += ",downscript='%s'" % (downscript or "no")
lmr6976faa2010-07-09 21:11:06 +0000256 elif mode == "user":
257 if tftp and "[,tftp=" in help:
258 cmd += ",tftp='%s'" % tftp
259 if bootfile and "[,bootfile=" in help:
260 cmd += ",bootfile='%s'" % bootfile
261 if "[,hostfwd=" in help:
262 for host_port, guest_port in hostfwd:
263 cmd += ",hostfwd=tcp::%s-:%s" % (host_port, guest_port)
lmr48abd7d2010-05-26 13:48:04 +0000264 return cmd
265
266 def add_floppy(help, filename):
lmr09a78162010-06-14 16:29:23 +0000267 return " -fda '%s'" % filename
lmr48abd7d2010-05-26 13:48:04 +0000268
269 def add_tftp(help, filename):
lmr6976faa2010-07-09 21:11:06 +0000270 # If the new syntax is supported, don't add -tftp
271 if "[,tftp=" in help:
272 return ""
273 else:
274 return " -tftp '%s'" % filename
275
276 def add_bootp(help, filename):
277 # If the new syntax is supported, don't add -bootp
278 if "[,bootfile=" in help:
279 return ""
280 else:
281 return " -bootp '%s'" % filename
lmr48abd7d2010-05-26 13:48:04 +0000282
283 def add_tcp_redir(help, host_port, guest_port):
lmr6976faa2010-07-09 21:11:06 +0000284 # If the new syntax is supported, don't add -redir
285 if "[,hostfwd=" in help:
286 return ""
287 else:
288 return " -redir tcp:%s::%s" % (host_port, guest_port)
lmr48abd7d2010-05-26 13:48:04 +0000289
290 def add_vnc(help, vnc_port):
291 return " -vnc :%d" % (vnc_port - 5900)
292
293 def add_sdl(help):
294 if has_option(help, "sdl"):
295 return " -sdl"
296 else:
297 return ""
298
299 def add_nographic(help):
300 return " -nographic"
301
302 def add_uuid(help, uuid):
lmr09a78162010-06-14 16:29:23 +0000303 return " -uuid '%s'" % uuid
lmr48abd7d2010-05-26 13:48:04 +0000304
305 def add_pcidevice(help, host):
lmr09a78162010-06-14 16:29:23 +0000306 return " -pcidevice host='%s'" % host
lmr48abd7d2010-05-26 13:48:04 +0000307
lmr41cb8fc2010-06-10 15:30:45 +0000308 def add_kernel(help, filename):
lmr09a78162010-06-14 16:29:23 +0000309 return " -kernel '%s'" % filename
lmr41cb8fc2010-06-10 15:30:45 +0000310
311 def add_initrd(help, filename):
lmr09a78162010-06-14 16:29:23 +0000312 return " -initrd '%s'" % filename
lmr41cb8fc2010-06-10 15:30:45 +0000313
lmre0474e32010-06-29 14:10:09 +0000314 def add_kernel_cmdline(help, cmdline):
315 return " -append %s" % cmdline
316
lmr71024552010-06-29 14:12:35 +0000317 def add_testdev(help, filename):
318 return (" -chardev file,id=testlog,path=%s"
319 " -device testdev,chardev=testlog" % filename)
320
lmrce5c9252010-06-30 17:41:00 +0000321 def add_no_hpet(help):
322 if has_option(help, "no-hpet"):
323 return " -no-hpet"
324 else:
325 return ""
326
lmr48abd7d2010-05-26 13:48:04 +0000327 # End of command line option wrappers
328
329 if name is None: name = self.name
330 if params is None: params = self.params
331 if root_dir is None: root_dir = self.root_dir
332
333 qemu_binary = kvm_utils.get_path(root_dir, params.get("qemu_binary",
334 "qemu"))
335 # Get the output of 'qemu -help' (log a message in case this call never
336 # returns or causes some other kind of trouble)
337 logging.debug("Getting output of 'qemu -help'")
338 help = commands.getoutput("%s -help" % qemu_binary)
lmr6f669ce2009-05-31 19:02:42 +0000339
lmreeff0eb2009-06-10 19:19:15 +0000340 # Start constructing the qemu command
341 qemu_cmd = ""
342 # Set the X11 display parameter if requested
343 if params.get("x11_display"):
344 qemu_cmd += "DISPLAY=%s " % params.get("x11_display")
345 # Add the qemu binary
lmr48abd7d2010-05-26 13:48:04 +0000346 qemu_cmd += qemu_binary
lmreeff0eb2009-06-10 19:19:15 +0000347 # Add the VM's name
lmr48abd7d2010-05-26 13:48:04 +0000348 qemu_cmd += add_name(help, name)
lmr9e964a02010-06-18 03:46:21 +0000349 # Add monitors
350 for monitor_name in kvm_utils.get_sub_dict_names(params, "monitors"):
351 monitor_params = kvm_utils.get_sub_dict(params, monitor_name)
352 monitor_filename = self.get_monitor_filename(monitor_name)
353 if monitor_params.get("monitor_type") == "qmp":
354 qemu_cmd += add_qmp_monitor(help, monitor_filename)
355 else:
356 qemu_cmd += add_human_monitor(help, monitor_filename)
lmr6f669ce2009-05-31 19:02:42 +0000357
lmr2b06f332010-06-22 02:03:41 +0000358 # Add serial console redirection
359 qemu_cmd += add_serial(help, self.get_serial_console_filename())
360
lmr6f669ce2009-05-31 19:02:42 +0000361 for image_name in kvm_utils.get_sub_dict_names(params, "images"):
362 image_params = kvm_utils.get_sub_dict(params, image_name)
lmr9d75ee32009-09-29 13:14:13 +0000363 if image_params.get("boot_drive") == "no":
364 continue
lmr48abd7d2010-05-26 13:48:04 +0000365 qemu_cmd += add_drive(help,
366 get_image_filename(image_params, root_dir),
lmraf709702010-07-20 00:56:33 +0000367 image_params.get("drive_index"),
lmr48abd7d2010-05-26 13:48:04 +0000368 image_params.get("drive_format"),
369 image_params.get("drive_cache"),
370 image_params.get("drive_werror"),
371 image_params.get("drive_serial"),
372 image_params.get("image_snapshot") == "yes",
373 image_params.get("image_boot") == "yes")
lmr6f669ce2009-05-31 19:02:42 +0000374
lmr6976faa2010-07-09 21:11:06 +0000375 redirs = []
376 for redir_name in kvm_utils.get_sub_dict_names(params, "redirs"):
377 redir_params = kvm_utils.get_sub_dict(params, redir_name)
378 guest_port = int(redir_params.get("guest_port"))
379 host_port = self.redirs.get(guest_port)
380 redirs += [(host_port, guest_port)]
381
lmr6f669ce2009-05-31 19:02:42 +0000382 vlan = 0
383 for nic_name in kvm_utils.get_sub_dict_names(params, "nics"):
384 nic_params = kvm_utils.get_sub_dict(params, nic_name)
lmrf4696342009-08-13 04:06:33 +0000385 # Handle the '-net nic' part
lmr48abd7d2010-05-26 13:48:04 +0000386 mac = None
387 if "address_index" in nic_params:
388 mac = kvm_utils.get_mac_ip_pair_from_dict(nic_params)[0]
lmrb1cad1e2010-06-17 17:36:09 +0000389 qemu_cmd += add_nic(help, vlan, nic_params.get("nic_model"), mac,
390 self.netdev_id[vlan])
lmrf4696342009-08-13 04:06:33 +0000391 # Handle the '-net tap' or '-net user' part
lmre4eaa862010-06-01 19:15:14 +0000392 script = nic_params.get("nic_script")
393 downscript = nic_params.get("nic_downscript")
lmr6976faa2010-07-09 21:11:06 +0000394 tftp = nic_params.get("tftp")
lmr48abd7d2010-05-26 13:48:04 +0000395 if script:
396 script = kvm_utils.get_path(root_dir, script)
397 if downscript:
398 downscript = kvm_utils.get_path(root_dir, downscript)
lmr6976faa2010-07-09 21:11:06 +0000399 if tftp:
400 tftp = kvm_utils.get_path(root_dir, tftp)
lmr48abd7d2010-05-26 13:48:04 +0000401 qemu_cmd += add_net(help, vlan, nic_params.get("nic_mode", "user"),
402 nic_params.get("nic_ifname"),
lmr6976faa2010-07-09 21:11:06 +0000403 script, downscript, tftp,
404 nic_params.get("bootp"), redirs,
405 self.netdev_id[vlan])
lmrf4696342009-08-13 04:06:33 +0000406 # Proceed to next NIC
lmr6f669ce2009-05-31 19:02:42 +0000407 vlan += 1
408
409 mem = params.get("mem")
410 if mem:
lmr48abd7d2010-05-26 13:48:04 +0000411 qemu_cmd += add_mem(help, mem)
lmr6f669ce2009-05-31 19:02:42 +0000412
lmrc43bf372009-11-10 13:19:00 +0000413 smp = params.get("smp")
414 if smp:
lmrdb3fe612010-06-14 16:19:28 +0000415 qemu_cmd += add_smp(help, smp)
lmrc43bf372009-11-10 13:19:00 +0000416
lmr6f669ce2009-05-31 19:02:42 +0000417 iso = params.get("cdrom")
418 if iso:
lmr90b9fd52009-08-17 20:48:18 +0000419 iso = kvm_utils.get_path(root_dir, iso)
lmraf709702010-07-20 00:56:33 +0000420 qemu_cmd += add_cdrom(help, iso, 2)
lmr89fd6412010-01-18 02:42:57 +0000421
422 # Even though this is not a really scalable approach,
423 # it doesn't seem like we are going to need more than
424 # 2 CDs active on the same VM.
425 iso_extra = params.get("cdrom_extra")
426 if iso_extra:
427 iso_extra = kvm_utils.get_path(root_dir, iso_extra)
lmr48abd7d2010-05-26 13:48:04 +0000428 qemu_cmd += add_cdrom(help, iso_extra, 3)
lmr6f669ce2009-05-31 19:02:42 +0000429
lmrb0a9b762009-10-09 20:43:30 +0000430 # We may want to add {floppy_otps} parameter for -fda
lmr48abd7d2010-05-26 13:48:04 +0000431 # {fat:floppy:}/path/. However vvfat is not usually recommended.
lmrb0a9b762009-10-09 20:43:30 +0000432 floppy = params.get("floppy")
433 if floppy:
lmrf69f6b12009-11-10 16:33:44 +0000434 floppy = kvm_utils.get_path(root_dir, floppy)
lmr48abd7d2010-05-26 13:48:04 +0000435 qemu_cmd += add_floppy(help, floppy)
lmrb0a9b762009-10-09 20:43:30 +0000436
437 tftp = params.get("tftp")
lmr62fc2bb2010-07-08 23:45:11 +0000438 if tftp:
lmrf69f6b12009-11-10 16:33:44 +0000439 tftp = kvm_utils.get_path(root_dir, tftp)
lmr48abd7d2010-05-26 13:48:04 +0000440 qemu_cmd += add_tftp(help, tftp)
lmr6f669ce2009-05-31 19:02:42 +0000441
lmr6976faa2010-07-09 21:11:06 +0000442 bootp = params.get("bootp")
443 if bootp:
444 qemu_cmd += add_bootp(help, bootp)
445
lmr41cb8fc2010-06-10 15:30:45 +0000446 kernel = params.get("kernel")
447 if kernel:
448 kernel = kvm_utils.get_path(root_dir, kernel)
449 qemu_cmd += add_kernel(help, kernel)
450
lmre0474e32010-06-29 14:10:09 +0000451 kernel_cmdline = params.get("kernel_cmdline")
452 if kernel_cmdline:
453 qemu_cmd += add_kernel_cmdline(help, kernel_cmdline)
454
lmr41cb8fc2010-06-10 15:30:45 +0000455 initrd = params.get("initrd")
456 if initrd:
457 initrd = kvm_utils.get_path(root_dir, initrd)
458 qemu_cmd += add_initrd(help, initrd)
459
lmr6976faa2010-07-09 21:11:06 +0000460 for host_port, guest_port in redirs:
lmr48abd7d2010-05-26 13:48:04 +0000461 qemu_cmd += add_tcp_redir(help, host_port, guest_port)
lmr6f669ce2009-05-31 19:02:42 +0000462
463 if params.get("display") == "vnc":
lmr48abd7d2010-05-26 13:48:04 +0000464 qemu_cmd += add_vnc(help, self.vnc_port)
lmr6f669ce2009-05-31 19:02:42 +0000465 elif params.get("display") == "sdl":
lmr48abd7d2010-05-26 13:48:04 +0000466 qemu_cmd += add_sdl(help)
lmr6f669ce2009-05-31 19:02:42 +0000467 elif params.get("display") == "nographic":
lmr48abd7d2010-05-26 13:48:04 +0000468 qemu_cmd += add_nographic(help)
lmr6f669ce2009-05-31 19:02:42 +0000469
lmra2533222009-07-20 12:43:46 +0000470 if params.get("uuid") == "random":
lmr48abd7d2010-05-26 13:48:04 +0000471 qemu_cmd += add_uuid(help, self.uuid)
lmra2533222009-07-20 12:43:46 +0000472 elif params.get("uuid"):
lmr48abd7d2010-05-26 13:48:04 +0000473 qemu_cmd += add_uuid(help, params.get("uuid"))
lmra2533222009-07-20 12:43:46 +0000474
lmr71024552010-06-29 14:12:35 +0000475 if params.get("testdev") == "yes":
476 qemu_cmd += add_testdev(help, self.get_testlog_filename())
477
lmrce5c9252010-06-30 17:41:00 +0000478 if params.get("disable_hpet") == "yes":
479 qemu_cmd += add_no_hpet(help)
480
lmr31af3a12010-01-18 16:46:52 +0000481 # If the PCI assignment step went OK, add each one of the PCI assigned
482 # devices to the qemu command line.
483 if self.pci_assignable:
484 for pci_id in self.pa_pci_ids:
lmr48abd7d2010-05-26 13:48:04 +0000485 qemu_cmd += add_pcidevice(help, pci_id)
486
487 extra_params = params.get("extra_params")
488 if extra_params:
489 qemu_cmd += " %s" % extra_params
lmr31af3a12010-01-18 16:46:52 +0000490
lmr6f669ce2009-05-31 19:02:42 +0000491 return qemu_cmd
492
493
lmr52800ba2009-08-17 20:49:58 +0000494 def create(self, name=None, params=None, root_dir=None,
lmr1424f3e2010-06-17 13:57:09 +0000495 for_migration=False, timeout=5.0, extra_params=None):
lmr6f669ce2009-05-31 19:02:42 +0000496 """
497 Start the VM by running a qemu command.
498 All parameters are optional. The following applies to all parameters
499 but for_migration: If a parameter is not supplied, the corresponding
500 value stored in the class attributes is used, and if it is supplied,
501 it is stored for later use.
502
503 @param name: The name of the object
504 @param params: A dict containing VM params
lmr90b9fd52009-08-17 20:48:18 +0000505 @param root_dir: Base directory for relative filenames
lmr6f669ce2009-05-31 19:02:42 +0000506 @param for_migration: If True, start the VM with the -incoming
507 option
lmr1424f3e2010-06-17 13:57:09 +0000508 @param extra_params: extra params for qemu command.e.g -incoming option
509 Please use this parameter instead of for_migration.
lmr6f669ce2009-05-31 19:02:42 +0000510 """
lmr135b5e62009-06-10 19:22:31 +0000511 self.destroy()
512
lmre45a1f22009-11-10 16:35:08 +0000513 if name is not None:
lmr6f669ce2009-05-31 19:02:42 +0000514 self.name = name
lmre45a1f22009-11-10 16:35:08 +0000515 if params is not None:
lmr6f669ce2009-05-31 19:02:42 +0000516 self.params = params
lmre45a1f22009-11-10 16:35:08 +0000517 if root_dir is not None:
lmr90b9fd52009-08-17 20:48:18 +0000518 self.root_dir = root_dir
lmr6f669ce2009-05-31 19:02:42 +0000519 name = self.name
520 params = self.params
lmr90b9fd52009-08-17 20:48:18 +0000521 root_dir = self.root_dir
lmr6f669ce2009-05-31 19:02:42 +0000522
523 # Verify the md5sum of the ISO image
524 iso = params.get("cdrom")
525 if iso:
lmr90b9fd52009-08-17 20:48:18 +0000526 iso = kvm_utils.get_path(root_dir, iso)
lmr6f669ce2009-05-31 19:02:42 +0000527 if not os.path.exists(iso):
528 logging.error("ISO file not found: %s" % iso)
529 return False
530 compare = False
531 if params.get("md5sum_1m"):
lmrf4696342009-08-13 04:06:33 +0000532 logging.debug("Comparing expected MD5 sum with MD5 sum of "
533 "first MB of ISO file...")
lmrd60882f2010-02-04 03:26:36 +0000534 actual_hash = utils.hash_file(iso, 1048576, method="md5")
lmr03ba22e2009-10-23 12:07:44 +0000535 expected_hash = params.get("md5sum_1m")
lmr6f669ce2009-05-31 19:02:42 +0000536 compare = True
537 elif params.get("md5sum"):
lmrf4696342009-08-13 04:06:33 +0000538 logging.debug("Comparing expected MD5 sum with MD5 sum of ISO "
539 "file...")
lmrd60882f2010-02-04 03:26:36 +0000540 actual_hash = utils.hash_file(iso, method="md5")
lmr03ba22e2009-10-23 12:07:44 +0000541 expected_hash = params.get("md5sum")
542 compare = True
543 elif params.get("sha1sum"):
544 logging.debug("Comparing expected SHA1 sum with SHA1 sum of "
545 "ISO file...")
lmrd60882f2010-02-04 03:26:36 +0000546 actual_hash = utils.hash_file(iso, method="sha1")
lmrea1266e2009-11-10 16:41:08 +0000547 expected_hash = params.get("sha1sum")
lmr6f669ce2009-05-31 19:02:42 +0000548 compare = True
549 if compare:
lmr03ba22e2009-10-23 12:07:44 +0000550 if actual_hash == expected_hash:
551 logging.debug("Hashes match")
lmr6f669ce2009-05-31 19:02:42 +0000552 else:
lmr03ba22e2009-10-23 12:07:44 +0000553 logging.error("Actual hash differs from expected one")
lmr6f669ce2009-05-31 19:02:42 +0000554 return False
555
lmrdc2ac6a2009-06-10 19:15:49 +0000556 # Make sure the following code is not executed by more than one thread
557 # at the same time
558 lockfile = open("/tmp/kvm-autotest-vm-create.lock", "w+")
559 fcntl.lockf(lockfile, fcntl.LOCK_EX)
lmr6f669ce2009-05-31 19:02:42 +0000560
lmrdc2ac6a2009-06-10 19:15:49 +0000561 try:
562 # Handle port redirections
563 redir_names = kvm_utils.get_sub_dict_names(params, "redirs")
564 host_ports = kvm_utils.find_free_ports(5000, 6000, len(redir_names))
565 self.redirs = {}
566 for i in range(len(redir_names)):
567 redir_params = kvm_utils.get_sub_dict(params, redir_names[i])
568 guest_port = int(redir_params.get("guest_port"))
569 self.redirs[guest_port] = host_ports[i]
lmr6f669ce2009-05-31 19:02:42 +0000570
lmr48349072010-06-29 14:14:55 +0000571 for nic in kvm_utils.get_sub_dict_names(params, "nics"):
572 self.netdev_id.append(kvm_utils.generate_random_id())
573
lmrdc2ac6a2009-06-10 19:15:49 +0000574 # Find available VNC port, if needed
575 if params.get("display") == "vnc":
lmrc0da8902010-05-17 20:31:36 +0000576 self.vnc_port = kvm_utils.find_free_port(5900, 6100)
lmr6f669ce2009-05-31 19:02:42 +0000577
lmra2533222009-07-20 12:43:46 +0000578 # Find random UUID if specified 'uuid = random' in config file
579 if params.get("uuid") == "random":
580 f = open("/proc/sys/kernel/random/uuid")
581 self.uuid = f.read().strip()
582 f.close()
583
lmr31ed61d2010-06-07 13:21:38 +0000584 # Assign a PCI assignable device
585 self.pci_assignable = None
586 pa_type = params.get("pci_assignable")
587 if pa_type in ["vf", "pf", "mixed"]:
lmr31af3a12010-01-18 16:46:52 +0000588 pa_devices_requested = params.get("devices_requested")
589
590 # Virtual Functions (VF) assignable devices
591 if pa_type == "vf":
lmr31ed61d2010-06-07 13:21:38 +0000592 self.pci_assignable = kvm_utils.PciAssignable(
593 type=pa_type,
594 driver=params.get("driver"),
595 driver_option=params.get("driver_option"),
596 devices_requested=pa_devices_requested)
lmr31af3a12010-01-18 16:46:52 +0000597 # Physical NIC (PF) assignable devices
598 elif pa_type == "pf":
lmr31ed61d2010-06-07 13:21:38 +0000599 self.pci_assignable = kvm_utils.PciAssignable(
600 type=pa_type,
601 names=params.get("device_names"),
602 devices_requested=pa_devices_requested)
lmr31af3a12010-01-18 16:46:52 +0000603 # Working with both VF and PF
604 elif pa_type == "mixed":
lmr31ed61d2010-06-07 13:21:38 +0000605 self.pci_assignable = kvm_utils.PciAssignable(
606 type=pa_type,
607 driver=params.get("driver"),
608 driver_option=params.get("driver_option"),
609 names=params.get("device_names"),
610 devices_requested=pa_devices_requested)
lmr31af3a12010-01-18 16:46:52 +0000611
612 self.pa_pci_ids = self.pci_assignable.request_devs()
613
614 if self.pa_pci_ids:
lmr31ed61d2010-06-07 13:21:38 +0000615 logging.debug("Successfuly assigned devices: %s",
lmr31af3a12010-01-18 16:46:52 +0000616 self.pa_pci_ids)
617 else:
618 logging.error("No PCI assignable devices were assigned "
619 "and 'pci_assignable' is defined to %s "
lmr31ed61d2010-06-07 13:21:38 +0000620 "on your config file. Aborting VM creation.",
lmr31af3a12010-01-18 16:46:52 +0000621 pa_type)
622 return False
623
lmr856d58c2010-06-08 18:29:31 +0000624 elif pa_type and pa_type != "no":
lmr31ed61d2010-06-07 13:21:38 +0000625 logging.warn("Unsupported pci_assignable type: %s", pa_type)
lmr31af3a12010-01-18 16:46:52 +0000626
lmrdc2ac6a2009-06-10 19:15:49 +0000627 # Make qemu command
628 qemu_command = self.make_qemu_command()
lmr6f669ce2009-05-31 19:02:42 +0000629
lmr1424f3e2010-06-17 13:57:09 +0000630 # Enable migration support for VM by adding extra_params.
631 if extra_params is not None:
632 if " -incoming tcp:0:%d" == extra_params:
633 self.migration_port = kvm_utils.find_free_port(5200, 6000)
634 qemu_command += extra_params % self.migration_port
635 elif " -incoming unix:%s" == extra_params:
636 self.migration_file = os.path.join("/tmp/", "unix-" +
637 time.strftime("%Y%m%d-%H%M%S"))
638 qemu_command += extra_params % self.migration_file
639 else:
640 qemu_command += extra_params
lmr6f669ce2009-05-31 19:02:42 +0000641
lmrdc2ac6a2009-06-10 19:15:49 +0000642 logging.debug("Running qemu command:\n%s", qemu_command)
lmra4967622009-07-23 01:36:32 +0000643 self.process = kvm_subprocess.run_bg(qemu_command, None,
644 logging.debug, "(qemu) ")
lmr6f669ce2009-05-31 19:02:42 +0000645
lmr9e964a02010-06-18 03:46:21 +0000646 # Make sure the process was started successfully
lmra4967622009-07-23 01:36:32 +0000647 if not self.process.is_alive():
648 logging.error("VM could not be created; "
649 "qemu command failed:\n%s" % qemu_command)
650 logging.error("Status: %s" % self.process.get_status())
651 logging.error("Output:" + kvm_utils.format_str_for_message(
652 self.process.get_output()))
lmrdc2ac6a2009-06-10 19:15:49 +0000653 self.destroy()
654 return False
lmr6f669ce2009-05-31 19:02:42 +0000655
lmr9e964a02010-06-18 03:46:21 +0000656 # Establish monitor connections
657 self.monitors = []
658 for monitor_name in kvm_utils.get_sub_dict_names(params,
659 "monitors"):
660 monitor_params = kvm_utils.get_sub_dict(params, monitor_name)
661 # Wait for monitor connection to succeed
662 end_time = time.time() + timeout
663 while time.time() < end_time:
664 try:
665 if monitor_params.get("monitor_type") == "qmp":
lmr449d2252010-06-18 03:48:23 +0000666 # Add a QMP monitor
667 monitor = kvm_monitor.QMPMonitor(
668 monitor_name,
669 self.get_monitor_filename(monitor_name))
lmr9e964a02010-06-18 03:46:21 +0000670 else:
671 # Add a "human" monitor
672 monitor = kvm_monitor.HumanMonitor(
673 monitor_name,
674 self.get_monitor_filename(monitor_name))
675 except kvm_monitor.MonitorError, e:
676 logging.warn(e)
677 else:
lmr449d2252010-06-18 03:48:23 +0000678 if monitor.is_responsive():
lmr9e964a02010-06-18 03:46:21 +0000679 break
680 time.sleep(1)
681 else:
682 logging.error("Could not connect to monitor '%s'" %
683 monitor_name)
684 self.destroy()
685 return False
686 # Add this monitor to the list
687 self.monitors += [monitor]
lmra4967622009-07-23 01:36:32 +0000688
lmrfe6515e2009-07-29 13:01:17 +0000689 # Get the output so far, to see if we have any problems with
lmrb306bf82010-07-08 23:47:00 +0000690 # KVM modules or with hugepage setup.
lmrfe6515e2009-07-29 13:01:17 +0000691 output = self.process.get_output()
692
lmrb306bf82010-07-08 23:47:00 +0000693 if re.search("Could not initialize KVM", output, re.IGNORECASE):
694 logging.error("Could not initialize KVM; "
695 "qemu command:\n%s" % qemu_command)
696 logging.error("Output:" + kvm_utils.format_str_for_message(
697 self.process.get_output()))
698 self.destroy()
699 return False
700
lmrfe6515e2009-07-29 13:01:17 +0000701 if "alloc_mem_area" in output:
702 logging.error("Could not allocate hugepage memory; "
703 "qemu command:\n%s" % qemu_command)
704 logging.error("Output:" + kvm_utils.format_str_for_message(
705 self.process.get_output()))
lmr090cd7a2010-04-01 02:18:52 +0000706 self.destroy()
lmrfe6515e2009-07-29 13:01:17 +0000707 return False
708
lmr71fa4de2010-06-14 15:54:55 +0000709 logging.debug("VM appears to be alive with PID %s", self.get_pid())
lmraa380a22010-06-22 02:05:29 +0000710
711 # Establish a session with the serial console -- requires a version
712 # of netcat that supports -U
713 self.serial_console = kvm_subprocess.kvm_shell_session(
714 "nc -U %s" % self.get_serial_console_filename(),
715 auto_close=False,
716 output_func=kvm_utils.log_line,
717 output_params=("serial-%s.log" % name,))
718
lmrdc2ac6a2009-06-10 19:15:49 +0000719 return True
720
721 finally:
722 fcntl.lockf(lockfile, fcntl.LOCK_UN)
723 lockfile.close()
lmr6f669ce2009-05-31 19:02:42 +0000724
725
lmr6f669ce2009-05-31 19:02:42 +0000726 def destroy(self, gracefully=True):
727 """
728 Destroy the VM.
729
lmr912c74b2009-08-17 20:43:27 +0000730 If gracefully is True, first attempt to shutdown the VM with a shell
731 command. Then, attempt to destroy the VM via the monitor with a 'quit'
732 command. If that fails, send SIGKILL to the qemu process.
lmr6f669ce2009-05-31 19:02:42 +0000733
734 @param gracefully: Whether an attempt will be made to end the VM
lmr912c74b2009-08-17 20:43:27 +0000735 using a shell command before trying to end the qemu process
736 with a 'quit' or a kill signal.
lmr6f669ce2009-05-31 19:02:42 +0000737 """
lmrf320b042009-09-15 05:48:06 +0000738 try:
739 # Is it already dead?
740 if self.is_dead():
741 logging.debug("VM is already down")
742 return
lmr6f669ce2009-05-31 19:02:42 +0000743
lmr71fa4de2010-06-14 15:54:55 +0000744 logging.debug("Destroying VM with PID %s...", self.get_pid())
lmr6f669ce2009-05-31 19:02:42 +0000745
lmrf320b042009-09-15 05:48:06 +0000746 if gracefully and self.params.get("shutdown_command"):
747 # Try to destroy with shell command
748 logging.debug("Trying to shutdown VM with shell command...")
749 session = self.remote_login()
750 if session:
751 try:
752 # Send the shutdown command
753 session.sendline(self.params.get("shutdown_command"))
754 logging.debug("Shutdown command sent; waiting for VM "
755 "to go down...")
756 if kvm_utils.wait_for(self.is_dead, 60, 1, 1):
757 logging.debug("VM is down")
758 return
759 finally:
760 session.close()
lmr6f669ce2009-05-31 19:02:42 +0000761
lmr9e964a02010-06-18 03:46:21 +0000762 if self.monitor:
763 # Try to destroy with a monitor command
764 logging.debug("Trying to kill VM with monitor command...")
765 try:
766 self.monitor.quit()
767 except kvm_monitor.MonitorError, e:
768 logging.warn(e)
769 else:
770 # Wait for the VM to be really dead
771 if kvm_utils.wait_for(self.is_dead, 5, 0.5, 0.5):
772 logging.debug("VM is down")
773 return
lmrf320b042009-09-15 05:48:06 +0000774
775 # If the VM isn't dead yet...
776 logging.debug("Cannot quit normally; sending a kill to close the "
777 "deal...")
778 kvm_utils.kill_process_tree(self.process.get_pid(), 9)
lmr6f669ce2009-05-31 19:02:42 +0000779 # Wait for the VM to be really dead
780 if kvm_utils.wait_for(self.is_dead, 5, 0.5, 0.5):
781 logging.debug("VM is down")
lmr6f669ce2009-05-31 19:02:42 +0000782 return
783
lmrf320b042009-09-15 05:48:06 +0000784 logging.error("Process %s is a zombie!" % self.process.get_pid())
lmr6f669ce2009-05-31 19:02:42 +0000785
lmrf320b042009-09-15 05:48:06 +0000786 finally:
lmr9e964a02010-06-18 03:46:21 +0000787 self.monitors = []
lmr4513c432010-02-03 11:59:03 +0000788 if self.pci_assignable:
789 self.pci_assignable.release_devs()
lmrf320b042009-09-15 05:48:06 +0000790 if self.process:
791 self.process.close()
lmraa380a22010-06-22 02:05:29 +0000792 if self.serial_console:
793 self.serial_console.close()
lmr84546f22010-06-29 23:03:34 +0000794 for f in ([self.get_testlog_filename(),
795 self.get_serial_console_filename()] +
lmr9e964a02010-06-18 03:46:21 +0000796 self.get_monitor_filenames()):
797 try:
798 os.unlink(f)
799 except OSError:
800 pass
801
802
803 @property
804 def monitor(self):
805 """
806 Return the main monitor object, selected by the parameter main_monitor.
807 If main_monitor isn't defined, return the first monitor.
808 If no monitors exist, or if main_monitor refers to a nonexistent
809 monitor, return None.
810 """
811 for m in self.monitors:
812 if m.name == self.params.get("main_monitor"):
813 return m
814 if self.monitors and not self.params.get("main_monitor"):
815 return self.monitors[0]
lmr6f669ce2009-05-31 19:02:42 +0000816
817
818 def is_alive(self):
819 """
lmr9e964a02010-06-18 03:46:21 +0000820 Return True if the VM is alive and its monitor is responsive.
lmr6f669ce2009-05-31 19:02:42 +0000821 """
lmra4967622009-07-23 01:36:32 +0000822 # Check if the process is running
823 if self.is_dead():
lmr6f669ce2009-05-31 19:02:42 +0000824 return False
825 # Try sending a monitor command
lmr9e964a02010-06-18 03:46:21 +0000826 return bool(self.monitor) and self.monitor.is_responsive()
lmr6f669ce2009-05-31 19:02:42 +0000827
828
829 def is_dead(self):
830 """
lmra4967622009-07-23 01:36:32 +0000831 Return True if the qemu process is dead.
lmr6f669ce2009-05-31 19:02:42 +0000832 """
lmra4967622009-07-23 01:36:32 +0000833 return not self.process or not self.process.is_alive()
lmr6f669ce2009-05-31 19:02:42 +0000834
835
836 def get_params(self):
837 """
838 Return the VM's params dict. Most modified params take effect only
839 upon VM.create().
840 """
841 return self.params
842
843
lmr9e964a02010-06-18 03:46:21 +0000844 def get_monitor_filename(self, monitor_name):
845 """
846 Return the filename corresponding to a given monitor name.
847 """
848 return "/tmp/monitor-%s-%s" % (monitor_name, self.instance)
849
850
851 def get_monitor_filenames(self):
852 """
853 Return a list of all monitor filenames (as specified in the VM's
854 params).
855 """
856 return [self.get_monitor_filename(m) for m in
857 kvm_utils.get_sub_dict_names(self.params, "monitors")]
858
859
lmr2b06f332010-06-22 02:03:41 +0000860 def get_serial_console_filename(self):
861 """
862 Return the serial console filename.
863 """
864 return "/tmp/serial-%s" % self.instance
865
866
lmr9e964a02010-06-18 03:46:21 +0000867 def get_testlog_filename(self):
868 """
869 Return the testlog filename.
870 """
871 return "/tmp/testlog-%s" % self.instance
872
873
lmrf4696342009-08-13 04:06:33 +0000874 def get_address(self, index=0):
lmr6f669ce2009-05-31 19:02:42 +0000875 """
lmrf4696342009-08-13 04:06:33 +0000876 Return the address of a NIC of the guest, in host space.
lmr6f669ce2009-05-31 19:02:42 +0000877
lmrf4696342009-08-13 04:06:33 +0000878 If port redirection is used, return 'localhost' (the NIC has no IP
879 address of its own). Otherwise return the NIC's IP address.
880
881 @param index: Index of the NIC whose address is requested.
lmr6f669ce2009-05-31 19:02:42 +0000882 """
lmree90dd92009-08-13 04:13:39 +0000883 nics = kvm_utils.get_sub_dict_names(self.params, "nics")
884 nic_name = nics[index]
lmrf4696342009-08-13 04:06:33 +0000885 nic_params = kvm_utils.get_sub_dict(self.params, nic_name)
886 if nic_params.get("nic_mode") == "tap":
887 mac, ip = kvm_utils.get_mac_ip_pair_from_dict(nic_params)
lmree90dd92009-08-13 04:13:39 +0000888 if not mac:
889 logging.debug("MAC address unavailable")
890 return None
891 if not ip or nic_params.get("always_use_tcpdump") == "yes":
892 # Get the IP address from the cache
893 ip = self.address_cache.get(mac)
894 if not ip:
895 logging.debug("Could not find IP address for MAC address: "
896 "%s" % mac)
897 return None
898 # Make sure the IP address is assigned to this guest
899 nic_dicts = [kvm_utils.get_sub_dict(self.params, nic)
900 for nic in nics]
901 macs = [kvm_utils.get_mac_ip_pair_from_dict(dict)[0]
902 for dict in nic_dicts]
903 if not kvm_utils.verify_ip_address_ownership(ip, macs):
904 logging.debug("Could not verify MAC-IP address mapping: "
905 "%s ---> %s" % (mac, ip))
906 return None
lmrf4696342009-08-13 04:06:33 +0000907 return ip
908 else:
909 return "localhost"
lmr6f669ce2009-05-31 19:02:42 +0000910
911
lmree90dd92009-08-13 04:13:39 +0000912 def get_port(self, port, nic_index=0):
lmr6f669ce2009-05-31 19:02:42 +0000913 """
914 Return the port in host space corresponding to port in guest space.
915
916 @param port: Port number in host space.
lmree90dd92009-08-13 04:13:39 +0000917 @param nic_index: Index of the NIC.
lmr6f669ce2009-05-31 19:02:42 +0000918 @return: If port redirection is used, return the host port redirected
919 to guest port port. Otherwise return port.
920 """
lmree90dd92009-08-13 04:13:39 +0000921 nic_name = kvm_utils.get_sub_dict_names(self.params, "nics")[nic_index]
lmrf4696342009-08-13 04:06:33 +0000922 nic_params = kvm_utils.get_sub_dict(self.params, nic_name)
923 if nic_params.get("nic_mode") == "tap":
924 return port
lmr6f669ce2009-05-31 19:02:42 +0000925 else:
lmrf4696342009-08-13 04:06:33 +0000926 if not self.redirs.has_key(port):
927 logging.warn("Warning: guest port %s requested but not "
928 "redirected" % port)
929 return self.redirs.get(port)
lmr6f669ce2009-05-31 19:02:42 +0000930
931
lmra4967622009-07-23 01:36:32 +0000932 def get_pid(self):
933 """
lmr71fa4de2010-06-14 15:54:55 +0000934 Return the VM's PID. If the VM is dead return None.
935
936 @note: This works under the assumption that self.process.get_pid()
937 returns the PID of the parent shell process.
938 """
939 try:
940 children = commands.getoutput("ps --ppid=%d -o pid=" %
941 self.process.get_pid()).split()
942 return int(children[0])
943 except (TypeError, IndexError, ValueError):
944 return None
945
946
947 def get_shell_pid(self):
948 """
949 Return the PID of the parent shell process.
950
951 @note: This works under the assumption that self.process.get_pid()
952 returns the PID of the parent shell process.
lmra4967622009-07-23 01:36:32 +0000953 """
954 return self.process.get_pid()
955
956
lmr0bee2342010-02-24 00:01:37 +0000957 def get_shared_meminfo(self):
958 """
959 Returns the VM's shared memory information.
960
961 @return: Shared memory used by VM (MB)
962 """
963 if self.is_dead():
964 logging.error("Could not get shared memory info from dead VM.")
965 return None
966
lmr983ecdf2010-06-14 15:57:12 +0000967 filename = "/proc/%d/statm" % self.get_pid()
968 shm = int(open(filename).read().split()[2])
lmr0bee2342010-02-24 00:01:37 +0000969 # statm stores informations in pages, translate it to MB
lmr983ecdf2010-06-14 15:57:12 +0000970 return shm * 4.0 / 1024
lmr0bee2342010-02-24 00:01:37 +0000971
972
lmr912c74b2009-08-17 20:43:27 +0000973 def remote_login(self, nic_index=0, timeout=10):
lmr6f669ce2009-05-31 19:02:42 +0000974 """
lmr912c74b2009-08-17 20:43:27 +0000975 Log into the guest via SSH/Telnet/Netcat.
lmr6f669ce2009-05-31 19:02:42 +0000976 If timeout expires while waiting for output from the guest (e.g. a
977 password prompt or a shell prompt) -- fail.
978
lmree90dd92009-08-13 04:13:39 +0000979 @param nic_index: The index of the NIC to connect to.
lmr6f669ce2009-05-31 19:02:42 +0000980 @param timeout: Time (seconds) before giving up logging into the
981 guest.
982 @return: kvm_spawn object on success and None on failure.
983 """
984 username = self.params.get("username", "")
985 password = self.params.get("password", "")
lmr912c74b2009-08-17 20:43:27 +0000986 prompt = self.params.get("shell_prompt", "[\#\$]")
lmr59f9e2d2009-10-05 18:47:16 +0000987 linesep = eval("'%s'" % self.params.get("shell_linesep", r"\n"))
lmr912c74b2009-08-17 20:43:27 +0000988 client = self.params.get("shell_client")
lmree90dd92009-08-13 04:13:39 +0000989 address = self.get_address(nic_index)
lmr912c74b2009-08-17 20:43:27 +0000990 port = self.get_port(int(self.params.get("shell_port")))
lmre56903f2010-06-22 02:09:35 +0000991 log_filename = ("session-%s-%s.log" %
992 (self.name, kvm_utils.generate_random_string(4)))
lmr912c74b2009-08-17 20:43:27 +0000993
lmree90dd92009-08-13 04:13:39 +0000994 if not address or not port:
995 logging.debug("IP address or port unavailable")
lmr6f669ce2009-05-31 19:02:42 +0000996 return None
997
lmr158604f2010-06-22 01:57:34 +0000998 session = kvm_utils.remote_login(client, address, port, username,
lmre56903f2010-06-22 02:09:35 +0000999 password, prompt, linesep,
1000 log_filename, timeout)
lmr912c74b2009-08-17 20:43:27 +00001001
lmr6f669ce2009-05-31 19:02:42 +00001002 if session:
lmr912c74b2009-08-17 20:43:27 +00001003 session.set_status_test_command(self.params.get("status_test_"
lmr6f669ce2009-05-31 19:02:42 +00001004 "command", ""))
1005 return session
1006
1007
lmrc196d492010-05-07 14:14:26 +00001008 def copy_files_to(self, local_path, remote_path, nic_index=0, timeout=600):
lmr6f669ce2009-05-31 19:02:42 +00001009 """
lmr912c74b2009-08-17 20:43:27 +00001010 Transfer files to the guest.
lmr6f669ce2009-05-31 19:02:42 +00001011
1012 @param local_path: Host path
1013 @param remote_path: Guest path
lmr912c74b2009-08-17 20:43:27 +00001014 @param nic_index: The index of the NIC to connect to.
lmr6f669ce2009-05-31 19:02:42 +00001015 @param timeout: Time (seconds) before giving up on doing the remote
1016 copy.
1017 """
1018 username = self.params.get("username", "")
1019 password = self.params.get("password", "")
lmr912c74b2009-08-17 20:43:27 +00001020 client = self.params.get("file_transfer_client")
lmree90dd92009-08-13 04:13:39 +00001021 address = self.get_address(nic_index)
lmr912c74b2009-08-17 20:43:27 +00001022 port = self.get_port(int(self.params.get("file_transfer_port")))
1023
lmree90dd92009-08-13 04:13:39 +00001024 if not address or not port:
1025 logging.debug("IP address or port unavailable")
lmr6f669ce2009-05-31 19:02:42 +00001026 return None
lmr912c74b2009-08-17 20:43:27 +00001027
1028 if client == "scp":
lmrcbc86ef2010-07-07 20:50:08 +00001029 log_filename = ("scp-%s-%s.log" %
1030 (self.name, kvm_utils.generate_random_string(4)))
lmr912c74b2009-08-17 20:43:27 +00001031 return kvm_utils.scp_to_remote(address, port, username, password,
lmre56903f2010-06-22 02:09:35 +00001032 local_path, remote_path,
1033 log_filename, timeout)
lmrcbc86ef2010-07-07 20:50:08 +00001034 elif client == "rss":
1035 c = rss_file_transfer.FileUploadClient(address, port)
1036 c.upload(local_path, remote_path, timeout)
1037 c.close()
1038 return True
lmr6f669ce2009-05-31 19:02:42 +00001039
1040
lmrc196d492010-05-07 14:14:26 +00001041 def copy_files_from(self, remote_path, local_path, nic_index=0, timeout=600):
lmr6f669ce2009-05-31 19:02:42 +00001042 """
lmr912c74b2009-08-17 20:43:27 +00001043 Transfer files from the guest.
lmr6f669ce2009-05-31 19:02:42 +00001044
1045 @param local_path: Guest path
1046 @param remote_path: Host path
lmr912c74b2009-08-17 20:43:27 +00001047 @param nic_index: The index of the NIC to connect to.
lmr6f669ce2009-05-31 19:02:42 +00001048 @param timeout: Time (seconds) before giving up on doing the remote
1049 copy.
1050 """
1051 username = self.params.get("username", "")
1052 password = self.params.get("password", "")
lmr912c74b2009-08-17 20:43:27 +00001053 client = self.params.get("file_transfer_client")
lmree90dd92009-08-13 04:13:39 +00001054 address = self.get_address(nic_index)
lmr912c74b2009-08-17 20:43:27 +00001055 port = self.get_port(int(self.params.get("file_transfer_port")))
1056
lmree90dd92009-08-13 04:13:39 +00001057 if not address or not port:
1058 logging.debug("IP address or port unavailable")
lmr6f669ce2009-05-31 19:02:42 +00001059 return None
lmr6f669ce2009-05-31 19:02:42 +00001060
lmr912c74b2009-08-17 20:43:27 +00001061 if client == "scp":
lmrcbc86ef2010-07-07 20:50:08 +00001062 log_filename = ("scp-%s-%s.log" %
1063 (self.name, kvm_utils.generate_random_string(4)))
lmr912c74b2009-08-17 20:43:27 +00001064 return kvm_utils.scp_from_remote(address, port, username, password,
lmre56903f2010-06-22 02:09:35 +00001065 remote_path, local_path,
1066 log_filename, timeout)
lmrcbc86ef2010-07-07 20:50:08 +00001067 elif client == "rss":
1068 c = rss_file_transfer.FileDownloadClient(address, port)
1069 c.download(remote_path, local_path, timeout)
1070 c.close()
1071 return True
lmr6f669ce2009-05-31 19:02:42 +00001072
1073
lmraa380a22010-06-22 02:05:29 +00001074 def serial_login(self, timeout=10):
1075 """
1076 Log into the guest via the serial console.
1077 If timeout expires while waiting for output from the guest (e.g. a
1078 password prompt or a shell prompt) -- fail.
1079
1080 @param timeout: Time (seconds) before giving up logging into the guest.
1081 @return: kvm_spawn object on success and None on failure.
1082 """
1083 username = self.params.get("username", "")
1084 password = self.params.get("password", "")
1085 prompt = self.params.get("shell_prompt", "[\#\$]")
1086 linesep = eval("'%s'" % self.params.get("shell_linesep", r"\n"))
1087 status_test_command = self.params.get("status_test_command", "")
1088
1089 if self.serial_console:
1090 self.serial_console.set_linesep(linesep)
1091 self.serial_console.set_status_test_command(status_test_command)
1092 else:
1093 return None
1094
1095 # Make sure we get a login prompt
1096 self.serial_console.sendline()
1097
1098 if kvm_utils._remote_login(self.serial_console, username, password,
1099 prompt, timeout):
1100 return self.serial_console
1101
1102
lmr6f669ce2009-05-31 19:02:42 +00001103 def send_key(self, keystr):
1104 """
1105 Send a key event to the VM.
1106
1107 @param: keystr: A key event string (e.g. "ctrl-alt-delete")
1108 """
1109 # For compatibility with versions of QEMU that do not recognize all
1110 # key names: replace keyname with the hex value from the dict, which
1111 # QEMU will definitely accept
lmr9e964a02010-06-18 03:46:21 +00001112 dict = {"comma": "0x33",
1113 "dot": "0x34",
1114 "slash": "0x35"}
1115 for key, value in dict.items():
1116 keystr = keystr.replace(key, value)
1117 self.monitor.sendkey(keystr)
lmr6f669ce2009-05-31 19:02:42 +00001118 time.sleep(0.2)
1119
1120
1121 def send_string(self, str):
1122 """
1123 Send a string to the VM.
1124
1125 @param str: String, that must consist of alphanumeric characters only.
1126 Capital letters are allowed.
1127 """
1128 for char in str:
1129 if char.isupper():
1130 self.send_key("shift-%s" % char.lower())
1131 else:
1132 self.send_key(char)
lmra2533222009-07-20 12:43:46 +00001133
mbligh1ef218d2009-08-03 16:57:56 +00001134
lmra2533222009-07-20 12:43:46 +00001135 def get_uuid(self):
1136 """
1137 Catch UUID of the VM.
1138
1139 @return: None,if not specified in config file
1140 """
1141 if self.params.get("uuid") == "random":
1142 return self.uuid
1143 else:
1144 return self.params.get("uuid", None)
lmrdd2ff922009-12-01 23:39:12 +00001145
1146
1147 def get_cpu_count(self):
1148 """
1149 Get the cpu count of the VM.
1150 """
lmr13426552010-01-17 15:38:41 +00001151 session = self.remote_login()
1152 if not session:
1153 return None
lmrdd2ff922009-12-01 23:39:12 +00001154 try:
lmr13426552010-01-17 15:38:41 +00001155 cmd = self.params.get("cpu_chk_cmd")
1156 s, count = session.get_command_status_output(cmd)
1157 if s == 0:
1158 return int(count)
lmrdd2ff922009-12-01 23:39:12 +00001159 return None
1160 finally:
1161 session.close()
1162
1163
lmr28426c82010-04-16 06:02:58 +00001164 def get_memory_size(self, cmd=None):
lmrdd2ff922009-12-01 23:39:12 +00001165 """
lmr28426c82010-04-16 06:02:58 +00001166 Get bootup memory size of the VM.
1167
1168 @param check_cmd: Command used to check memory. If not provided,
1169 self.params.get("mem_chk_cmd") will be used.
lmrdd2ff922009-12-01 23:39:12 +00001170 """
lmr13426552010-01-17 15:38:41 +00001171 session = self.remote_login()
1172 if not session:
lmrdd2ff922009-12-01 23:39:12 +00001173 return None
lmr13426552010-01-17 15:38:41 +00001174 try:
lmr28426c82010-04-16 06:02:58 +00001175 if not cmd:
1176 cmd = self.params.get("mem_chk_cmd")
lmr13426552010-01-17 15:38:41 +00001177 s, mem_str = session.get_command_status_output(cmd)
1178 if s != 0:
1179 return None
lmr6d69f4d2010-02-12 11:35:55 +00001180 mem = re.findall("([0-9]+)", mem_str)
lmr13426552010-01-17 15:38:41 +00001181 mem_size = 0
1182 for m in mem:
1183 mem_size += int(m)
1184 if "GB" in mem_str:
1185 mem_size *= 1024
1186 elif "MB" in mem_str:
1187 pass
1188 else:
1189 mem_size /= 1024
1190 return int(mem_size)
lmrdd2ff922009-12-01 23:39:12 +00001191 finally:
1192 session.close()
lmr28426c82010-04-16 06:02:58 +00001193
1194
1195 def get_current_memory_size(self):
1196 """
1197 Get current memory size of the VM, rather than bootup memory.
1198 """
1199 cmd = self.params.get("mem_chk_cur_cmd")
1200 return self.get_memory_size(cmd)