blob: 6cd06882823cfef900fd3960b607bb9bc6434824 [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
217 def add_cdrom(help, filename, index=2):
218 if has_option(help, "drive"):
lmr09a78162010-06-14 16:29:23 +0000219 return " -drive file='%s',index=%d,media=cdrom" % (filename,
220 index)
lmr48abd7d2010-05-26 13:48:04 +0000221 else:
lmr09a78162010-06-14 16:29:23 +0000222 return " -cdrom '%s'" % filename
lmr48abd7d2010-05-26 13:48:04 +0000223
224 def add_drive(help, filename, format=None, cache=None, werror=None,
225 serial=None, snapshot=False, boot=False):
lmr09a78162010-06-14 16:29:23 +0000226 cmd = " -drive file='%s'" % filename
lmr48abd7d2010-05-26 13:48:04 +0000227 if format: cmd += ",if=%s" % format
228 if cache: cmd += ",cache=%s" % cache
229 if werror: cmd += ",werror=%s" % werror
lmr09a78162010-06-14 16:29:23 +0000230 if serial: cmd += ",serial='%s'" % serial
lmr48abd7d2010-05-26 13:48:04 +0000231 if snapshot: cmd += ",snapshot=on"
232 if boot: cmd += ",boot=on"
233 return cmd
234
lmrb1cad1e2010-06-17 17:36:09 +0000235 def add_nic(help, vlan, model=None, mac=None, netdev_id=None):
lmr48abd7d2010-05-26 13:48:04 +0000236 cmd = " -net nic,vlan=%d" % vlan
lmrb1cad1e2010-06-17 17:36:09 +0000237 if has_option(help, "netdev"):
238 cmd +=",netdev=%s" % netdev_id
lmr48abd7d2010-05-26 13:48:04 +0000239 if model: cmd += ",model=%s" % model
lmr09a78162010-06-14 16:29:23 +0000240 if mac: cmd += ",macaddr='%s'" % mac
lmr48abd7d2010-05-26 13:48:04 +0000241 return cmd
242
243 def add_net(help, vlan, mode, ifname=None, script=None,
lmr6976faa2010-07-09 21:11:06 +0000244 downscript=None, tftp=None, bootfile=None, hostfwd=[],
245 netdev_id=None):
lmrb1cad1e2010-06-17 17:36:09 +0000246 if has_option(help, "netdev"):
247 cmd = " -netdev %s,id=%s" % (mode, netdev_id)
248 else:
249 cmd = " -net %s,vlan=%d" % (mode, vlan)
lmr48abd7d2010-05-26 13:48:04 +0000250 if mode == "tap":
lmr09a78162010-06-14 16:29:23 +0000251 if ifname: cmd += ",ifname='%s'" % ifname
252 if script: cmd += ",script='%s'" % script
253 cmd += ",downscript='%s'" % (downscript or "no")
lmr6976faa2010-07-09 21:11:06 +0000254 elif mode == "user":
255 if tftp and "[,tftp=" in help:
256 cmd += ",tftp='%s'" % tftp
257 if bootfile and "[,bootfile=" in help:
258 cmd += ",bootfile='%s'" % bootfile
259 if "[,hostfwd=" in help:
260 for host_port, guest_port in hostfwd:
261 cmd += ",hostfwd=tcp::%s-:%s" % (host_port, guest_port)
lmr48abd7d2010-05-26 13:48:04 +0000262 return cmd
263
264 def add_floppy(help, filename):
lmr09a78162010-06-14 16:29:23 +0000265 return " -fda '%s'" % filename
lmr48abd7d2010-05-26 13:48:04 +0000266
267 def add_tftp(help, filename):
lmr6976faa2010-07-09 21:11:06 +0000268 # If the new syntax is supported, don't add -tftp
269 if "[,tftp=" in help:
270 return ""
271 else:
272 return " -tftp '%s'" % filename
273
274 def add_bootp(help, filename):
275 # If the new syntax is supported, don't add -bootp
276 if "[,bootfile=" in help:
277 return ""
278 else:
279 return " -bootp '%s'" % filename
lmr48abd7d2010-05-26 13:48:04 +0000280
281 def add_tcp_redir(help, host_port, guest_port):
lmr6976faa2010-07-09 21:11:06 +0000282 # If the new syntax is supported, don't add -redir
283 if "[,hostfwd=" in help:
284 return ""
285 else:
286 return " -redir tcp:%s::%s" % (host_port, guest_port)
lmr48abd7d2010-05-26 13:48:04 +0000287
288 def add_vnc(help, vnc_port):
289 return " -vnc :%d" % (vnc_port - 5900)
290
291 def add_sdl(help):
292 if has_option(help, "sdl"):
293 return " -sdl"
294 else:
295 return ""
296
297 def add_nographic(help):
298 return " -nographic"
299
300 def add_uuid(help, uuid):
lmr09a78162010-06-14 16:29:23 +0000301 return " -uuid '%s'" % uuid
lmr48abd7d2010-05-26 13:48:04 +0000302
303 def add_pcidevice(help, host):
lmr09a78162010-06-14 16:29:23 +0000304 return " -pcidevice host='%s'" % host
lmr48abd7d2010-05-26 13:48:04 +0000305
lmr41cb8fc2010-06-10 15:30:45 +0000306 def add_kernel(help, filename):
lmr09a78162010-06-14 16:29:23 +0000307 return " -kernel '%s'" % filename
lmr41cb8fc2010-06-10 15:30:45 +0000308
309 def add_initrd(help, filename):
lmr09a78162010-06-14 16:29:23 +0000310 return " -initrd '%s'" % filename
lmr41cb8fc2010-06-10 15:30:45 +0000311
lmre0474e32010-06-29 14:10:09 +0000312 def add_kernel_cmdline(help, cmdline):
313 return " -append %s" % cmdline
314
lmr71024552010-06-29 14:12:35 +0000315 def add_testdev(help, filename):
316 return (" -chardev file,id=testlog,path=%s"
317 " -device testdev,chardev=testlog" % filename)
318
lmrce5c9252010-06-30 17:41:00 +0000319 def add_no_hpet(help):
320 if has_option(help, "no-hpet"):
321 return " -no-hpet"
322 else:
323 return ""
324
lmr48abd7d2010-05-26 13:48:04 +0000325 # End of command line option wrappers
326
327 if name is None: name = self.name
328 if params is None: params = self.params
329 if root_dir is None: root_dir = self.root_dir
330
331 qemu_binary = kvm_utils.get_path(root_dir, params.get("qemu_binary",
332 "qemu"))
333 # Get the output of 'qemu -help' (log a message in case this call never
334 # returns or causes some other kind of trouble)
335 logging.debug("Getting output of 'qemu -help'")
336 help = commands.getoutput("%s -help" % qemu_binary)
lmr6f669ce2009-05-31 19:02:42 +0000337
lmreeff0eb2009-06-10 19:19:15 +0000338 # Start constructing the qemu command
339 qemu_cmd = ""
340 # Set the X11 display parameter if requested
341 if params.get("x11_display"):
342 qemu_cmd += "DISPLAY=%s " % params.get("x11_display")
343 # Add the qemu binary
lmr48abd7d2010-05-26 13:48:04 +0000344 qemu_cmd += qemu_binary
lmreeff0eb2009-06-10 19:19:15 +0000345 # Add the VM's name
lmr48abd7d2010-05-26 13:48:04 +0000346 qemu_cmd += add_name(help, name)
lmr9e964a02010-06-18 03:46:21 +0000347 # Add monitors
348 for monitor_name in kvm_utils.get_sub_dict_names(params, "monitors"):
349 monitor_params = kvm_utils.get_sub_dict(params, monitor_name)
350 monitor_filename = self.get_monitor_filename(monitor_name)
351 if monitor_params.get("monitor_type") == "qmp":
352 qemu_cmd += add_qmp_monitor(help, monitor_filename)
353 else:
354 qemu_cmd += add_human_monitor(help, monitor_filename)
lmr6f669ce2009-05-31 19:02:42 +0000355
lmr2b06f332010-06-22 02:03:41 +0000356 # Add serial console redirection
357 qemu_cmd += add_serial(help, self.get_serial_console_filename())
358
lmr6f669ce2009-05-31 19:02:42 +0000359 for image_name in kvm_utils.get_sub_dict_names(params, "images"):
360 image_params = kvm_utils.get_sub_dict(params, image_name)
lmr9d75ee32009-09-29 13:14:13 +0000361 if image_params.get("boot_drive") == "no":
362 continue
lmr48abd7d2010-05-26 13:48:04 +0000363 qemu_cmd += add_drive(help,
364 get_image_filename(image_params, root_dir),
365 image_params.get("drive_format"),
366 image_params.get("drive_cache"),
367 image_params.get("drive_werror"),
368 image_params.get("drive_serial"),
369 image_params.get("image_snapshot") == "yes",
370 image_params.get("image_boot") == "yes")
lmr6f669ce2009-05-31 19:02:42 +0000371
lmr6976faa2010-07-09 21:11:06 +0000372 redirs = []
373 for redir_name in kvm_utils.get_sub_dict_names(params, "redirs"):
374 redir_params = kvm_utils.get_sub_dict(params, redir_name)
375 guest_port = int(redir_params.get("guest_port"))
376 host_port = self.redirs.get(guest_port)
377 redirs += [(host_port, guest_port)]
378
lmr6f669ce2009-05-31 19:02:42 +0000379 vlan = 0
380 for nic_name in kvm_utils.get_sub_dict_names(params, "nics"):
381 nic_params = kvm_utils.get_sub_dict(params, nic_name)
lmrf4696342009-08-13 04:06:33 +0000382 # Handle the '-net nic' part
lmr48abd7d2010-05-26 13:48:04 +0000383 mac = None
384 if "address_index" in nic_params:
385 mac = kvm_utils.get_mac_ip_pair_from_dict(nic_params)[0]
lmrb1cad1e2010-06-17 17:36:09 +0000386 qemu_cmd += add_nic(help, vlan, nic_params.get("nic_model"), mac,
387 self.netdev_id[vlan])
lmrf4696342009-08-13 04:06:33 +0000388 # Handle the '-net tap' or '-net user' part
lmre4eaa862010-06-01 19:15:14 +0000389 script = nic_params.get("nic_script")
390 downscript = nic_params.get("nic_downscript")
lmr6976faa2010-07-09 21:11:06 +0000391 tftp = nic_params.get("tftp")
lmr48abd7d2010-05-26 13:48:04 +0000392 if script:
393 script = kvm_utils.get_path(root_dir, script)
394 if downscript:
395 downscript = kvm_utils.get_path(root_dir, downscript)
lmr6976faa2010-07-09 21:11:06 +0000396 if tftp:
397 tftp = kvm_utils.get_path(root_dir, tftp)
lmr48abd7d2010-05-26 13:48:04 +0000398 qemu_cmd += add_net(help, vlan, nic_params.get("nic_mode", "user"),
399 nic_params.get("nic_ifname"),
lmr6976faa2010-07-09 21:11:06 +0000400 script, downscript, tftp,
401 nic_params.get("bootp"), redirs,
402 self.netdev_id[vlan])
lmrf4696342009-08-13 04:06:33 +0000403 # Proceed to next NIC
lmr6f669ce2009-05-31 19:02:42 +0000404 vlan += 1
405
406 mem = params.get("mem")
407 if mem:
lmr48abd7d2010-05-26 13:48:04 +0000408 qemu_cmd += add_mem(help, mem)
lmr6f669ce2009-05-31 19:02:42 +0000409
lmrc43bf372009-11-10 13:19:00 +0000410 smp = params.get("smp")
411 if smp:
lmrdb3fe612010-06-14 16:19:28 +0000412 qemu_cmd += add_smp(help, smp)
lmrc43bf372009-11-10 13:19:00 +0000413
lmr6f669ce2009-05-31 19:02:42 +0000414 iso = params.get("cdrom")
415 if iso:
lmr90b9fd52009-08-17 20:48:18 +0000416 iso = kvm_utils.get_path(root_dir, iso)
lmr48abd7d2010-05-26 13:48:04 +0000417 qemu_cmd += add_cdrom(help, iso)
lmr89fd6412010-01-18 02:42:57 +0000418
419 # Even though this is not a really scalable approach,
420 # it doesn't seem like we are going to need more than
421 # 2 CDs active on the same VM.
422 iso_extra = params.get("cdrom_extra")
423 if iso_extra:
424 iso_extra = kvm_utils.get_path(root_dir, iso_extra)
lmr48abd7d2010-05-26 13:48:04 +0000425 qemu_cmd += add_cdrom(help, iso_extra, 3)
lmr6f669ce2009-05-31 19:02:42 +0000426
lmrb0a9b762009-10-09 20:43:30 +0000427 # We may want to add {floppy_otps} parameter for -fda
lmr48abd7d2010-05-26 13:48:04 +0000428 # {fat:floppy:}/path/. However vvfat is not usually recommended.
lmrb0a9b762009-10-09 20:43:30 +0000429 floppy = params.get("floppy")
430 if floppy:
lmrf69f6b12009-11-10 16:33:44 +0000431 floppy = kvm_utils.get_path(root_dir, floppy)
lmr48abd7d2010-05-26 13:48:04 +0000432 qemu_cmd += add_floppy(help, floppy)
lmrb0a9b762009-10-09 20:43:30 +0000433
434 tftp = params.get("tftp")
lmr62fc2bb2010-07-08 23:45:11 +0000435 if tftp:
lmrf69f6b12009-11-10 16:33:44 +0000436 tftp = kvm_utils.get_path(root_dir, tftp)
lmr48abd7d2010-05-26 13:48:04 +0000437 qemu_cmd += add_tftp(help, tftp)
lmr6f669ce2009-05-31 19:02:42 +0000438
lmr6976faa2010-07-09 21:11:06 +0000439 bootp = params.get("bootp")
440 if bootp:
441 qemu_cmd += add_bootp(help, bootp)
442
lmr41cb8fc2010-06-10 15:30:45 +0000443 kernel = params.get("kernel")
444 if kernel:
445 kernel = kvm_utils.get_path(root_dir, kernel)
446 qemu_cmd += add_kernel(help, kernel)
447
lmre0474e32010-06-29 14:10:09 +0000448 kernel_cmdline = params.get("kernel_cmdline")
449 if kernel_cmdline:
450 qemu_cmd += add_kernel_cmdline(help, kernel_cmdline)
451
lmr41cb8fc2010-06-10 15:30:45 +0000452 initrd = params.get("initrd")
453 if initrd:
454 initrd = kvm_utils.get_path(root_dir, initrd)
455 qemu_cmd += add_initrd(help, initrd)
456
lmr6976faa2010-07-09 21:11:06 +0000457 for host_port, guest_port in redirs:
lmr48abd7d2010-05-26 13:48:04 +0000458 qemu_cmd += add_tcp_redir(help, host_port, guest_port)
lmr6f669ce2009-05-31 19:02:42 +0000459
460 if params.get("display") == "vnc":
lmr48abd7d2010-05-26 13:48:04 +0000461 qemu_cmd += add_vnc(help, self.vnc_port)
lmr6f669ce2009-05-31 19:02:42 +0000462 elif params.get("display") == "sdl":
lmr48abd7d2010-05-26 13:48:04 +0000463 qemu_cmd += add_sdl(help)
lmr6f669ce2009-05-31 19:02:42 +0000464 elif params.get("display") == "nographic":
lmr48abd7d2010-05-26 13:48:04 +0000465 qemu_cmd += add_nographic(help)
lmr6f669ce2009-05-31 19:02:42 +0000466
lmra2533222009-07-20 12:43:46 +0000467 if params.get("uuid") == "random":
lmr48abd7d2010-05-26 13:48:04 +0000468 qemu_cmd += add_uuid(help, self.uuid)
lmra2533222009-07-20 12:43:46 +0000469 elif params.get("uuid"):
lmr48abd7d2010-05-26 13:48:04 +0000470 qemu_cmd += add_uuid(help, params.get("uuid"))
lmra2533222009-07-20 12:43:46 +0000471
lmr71024552010-06-29 14:12:35 +0000472 if params.get("testdev") == "yes":
473 qemu_cmd += add_testdev(help, self.get_testlog_filename())
474
lmrce5c9252010-06-30 17:41:00 +0000475 if params.get("disable_hpet") == "yes":
476 qemu_cmd += add_no_hpet(help)
477
lmr31af3a12010-01-18 16:46:52 +0000478 # If the PCI assignment step went OK, add each one of the PCI assigned
479 # devices to the qemu command line.
480 if self.pci_assignable:
481 for pci_id in self.pa_pci_ids:
lmr48abd7d2010-05-26 13:48:04 +0000482 qemu_cmd += add_pcidevice(help, pci_id)
483
484 extra_params = params.get("extra_params")
485 if extra_params:
486 qemu_cmd += " %s" % extra_params
lmr31af3a12010-01-18 16:46:52 +0000487
lmr6f669ce2009-05-31 19:02:42 +0000488 return qemu_cmd
489
490
lmr52800ba2009-08-17 20:49:58 +0000491 def create(self, name=None, params=None, root_dir=None,
lmr1424f3e2010-06-17 13:57:09 +0000492 for_migration=False, timeout=5.0, extra_params=None):
lmr6f669ce2009-05-31 19:02:42 +0000493 """
494 Start the VM by running a qemu command.
495 All parameters are optional. The following applies to all parameters
496 but for_migration: If a parameter is not supplied, the corresponding
497 value stored in the class attributes is used, and if it is supplied,
498 it is stored for later use.
499
500 @param name: The name of the object
501 @param params: A dict containing VM params
lmr90b9fd52009-08-17 20:48:18 +0000502 @param root_dir: Base directory for relative filenames
lmr6f669ce2009-05-31 19:02:42 +0000503 @param for_migration: If True, start the VM with the -incoming
504 option
lmr1424f3e2010-06-17 13:57:09 +0000505 @param extra_params: extra params for qemu command.e.g -incoming option
506 Please use this parameter instead of for_migration.
lmr6f669ce2009-05-31 19:02:42 +0000507 """
lmr135b5e62009-06-10 19:22:31 +0000508 self.destroy()
509
lmre45a1f22009-11-10 16:35:08 +0000510 if name is not None:
lmr6f669ce2009-05-31 19:02:42 +0000511 self.name = name
lmre45a1f22009-11-10 16:35:08 +0000512 if params is not None:
lmr6f669ce2009-05-31 19:02:42 +0000513 self.params = params
lmre45a1f22009-11-10 16:35:08 +0000514 if root_dir is not None:
lmr90b9fd52009-08-17 20:48:18 +0000515 self.root_dir = root_dir
lmr6f669ce2009-05-31 19:02:42 +0000516 name = self.name
517 params = self.params
lmr90b9fd52009-08-17 20:48:18 +0000518 root_dir = self.root_dir
lmr6f669ce2009-05-31 19:02:42 +0000519
520 # Verify the md5sum of the ISO image
521 iso = params.get("cdrom")
522 if iso:
lmr90b9fd52009-08-17 20:48:18 +0000523 iso = kvm_utils.get_path(root_dir, iso)
lmr6f669ce2009-05-31 19:02:42 +0000524 if not os.path.exists(iso):
525 logging.error("ISO file not found: %s" % iso)
526 return False
527 compare = False
528 if params.get("md5sum_1m"):
lmrf4696342009-08-13 04:06:33 +0000529 logging.debug("Comparing expected MD5 sum with MD5 sum of "
530 "first MB of ISO file...")
lmrd60882f2010-02-04 03:26:36 +0000531 actual_hash = utils.hash_file(iso, 1048576, method="md5")
lmr03ba22e2009-10-23 12:07:44 +0000532 expected_hash = params.get("md5sum_1m")
lmr6f669ce2009-05-31 19:02:42 +0000533 compare = True
534 elif params.get("md5sum"):
lmrf4696342009-08-13 04:06:33 +0000535 logging.debug("Comparing expected MD5 sum with MD5 sum of ISO "
536 "file...")
lmrd60882f2010-02-04 03:26:36 +0000537 actual_hash = utils.hash_file(iso, method="md5")
lmr03ba22e2009-10-23 12:07:44 +0000538 expected_hash = params.get("md5sum")
539 compare = True
540 elif params.get("sha1sum"):
541 logging.debug("Comparing expected SHA1 sum with SHA1 sum of "
542 "ISO file...")
lmrd60882f2010-02-04 03:26:36 +0000543 actual_hash = utils.hash_file(iso, method="sha1")
lmrea1266e2009-11-10 16:41:08 +0000544 expected_hash = params.get("sha1sum")
lmr6f669ce2009-05-31 19:02:42 +0000545 compare = True
546 if compare:
lmr03ba22e2009-10-23 12:07:44 +0000547 if actual_hash == expected_hash:
548 logging.debug("Hashes match")
lmr6f669ce2009-05-31 19:02:42 +0000549 else:
lmr03ba22e2009-10-23 12:07:44 +0000550 logging.error("Actual hash differs from expected one")
lmr6f669ce2009-05-31 19:02:42 +0000551 return False
552
lmrdc2ac6a2009-06-10 19:15:49 +0000553 # Make sure the following code is not executed by more than one thread
554 # at the same time
555 lockfile = open("/tmp/kvm-autotest-vm-create.lock", "w+")
556 fcntl.lockf(lockfile, fcntl.LOCK_EX)
lmr6f669ce2009-05-31 19:02:42 +0000557
lmrdc2ac6a2009-06-10 19:15:49 +0000558 try:
559 # Handle port redirections
560 redir_names = kvm_utils.get_sub_dict_names(params, "redirs")
561 host_ports = kvm_utils.find_free_ports(5000, 6000, len(redir_names))
562 self.redirs = {}
563 for i in range(len(redir_names)):
564 redir_params = kvm_utils.get_sub_dict(params, redir_names[i])
565 guest_port = int(redir_params.get("guest_port"))
566 self.redirs[guest_port] = host_ports[i]
lmr6f669ce2009-05-31 19:02:42 +0000567
lmr48349072010-06-29 14:14:55 +0000568 for nic in kvm_utils.get_sub_dict_names(params, "nics"):
569 self.netdev_id.append(kvm_utils.generate_random_id())
570
lmrdc2ac6a2009-06-10 19:15:49 +0000571 # Find available VNC port, if needed
572 if params.get("display") == "vnc":
lmrc0da8902010-05-17 20:31:36 +0000573 self.vnc_port = kvm_utils.find_free_port(5900, 6100)
lmr6f669ce2009-05-31 19:02:42 +0000574
lmra2533222009-07-20 12:43:46 +0000575 # Find random UUID if specified 'uuid = random' in config file
576 if params.get("uuid") == "random":
577 f = open("/proc/sys/kernel/random/uuid")
578 self.uuid = f.read().strip()
579 f.close()
580
lmr31ed61d2010-06-07 13:21:38 +0000581 # Assign a PCI assignable device
582 self.pci_assignable = None
583 pa_type = params.get("pci_assignable")
584 if pa_type in ["vf", "pf", "mixed"]:
lmr31af3a12010-01-18 16:46:52 +0000585 pa_devices_requested = params.get("devices_requested")
586
587 # Virtual Functions (VF) assignable devices
588 if pa_type == "vf":
lmr31ed61d2010-06-07 13:21:38 +0000589 self.pci_assignable = kvm_utils.PciAssignable(
590 type=pa_type,
591 driver=params.get("driver"),
592 driver_option=params.get("driver_option"),
593 devices_requested=pa_devices_requested)
lmr31af3a12010-01-18 16:46:52 +0000594 # Physical NIC (PF) assignable devices
595 elif pa_type == "pf":
lmr31ed61d2010-06-07 13:21:38 +0000596 self.pci_assignable = kvm_utils.PciAssignable(
597 type=pa_type,
598 names=params.get("device_names"),
599 devices_requested=pa_devices_requested)
lmr31af3a12010-01-18 16:46:52 +0000600 # Working with both VF and PF
601 elif pa_type == "mixed":
lmr31ed61d2010-06-07 13:21:38 +0000602 self.pci_assignable = kvm_utils.PciAssignable(
603 type=pa_type,
604 driver=params.get("driver"),
605 driver_option=params.get("driver_option"),
606 names=params.get("device_names"),
607 devices_requested=pa_devices_requested)
lmr31af3a12010-01-18 16:46:52 +0000608
609 self.pa_pci_ids = self.pci_assignable.request_devs()
610
611 if self.pa_pci_ids:
lmr31ed61d2010-06-07 13:21:38 +0000612 logging.debug("Successfuly assigned devices: %s",
lmr31af3a12010-01-18 16:46:52 +0000613 self.pa_pci_ids)
614 else:
615 logging.error("No PCI assignable devices were assigned "
616 "and 'pci_assignable' is defined to %s "
lmr31ed61d2010-06-07 13:21:38 +0000617 "on your config file. Aborting VM creation.",
lmr31af3a12010-01-18 16:46:52 +0000618 pa_type)
619 return False
620
lmr856d58c2010-06-08 18:29:31 +0000621 elif pa_type and pa_type != "no":
lmr31ed61d2010-06-07 13:21:38 +0000622 logging.warn("Unsupported pci_assignable type: %s", pa_type)
lmr31af3a12010-01-18 16:46:52 +0000623
lmrdc2ac6a2009-06-10 19:15:49 +0000624 # Make qemu command
625 qemu_command = self.make_qemu_command()
lmr6f669ce2009-05-31 19:02:42 +0000626
lmr1424f3e2010-06-17 13:57:09 +0000627 # Enable migration support for VM by adding extra_params.
628 if extra_params is not None:
629 if " -incoming tcp:0:%d" == extra_params:
630 self.migration_port = kvm_utils.find_free_port(5200, 6000)
631 qemu_command += extra_params % self.migration_port
632 elif " -incoming unix:%s" == extra_params:
633 self.migration_file = os.path.join("/tmp/", "unix-" +
634 time.strftime("%Y%m%d-%H%M%S"))
635 qemu_command += extra_params % self.migration_file
636 else:
637 qemu_command += extra_params
lmr6f669ce2009-05-31 19:02:42 +0000638
lmrdc2ac6a2009-06-10 19:15:49 +0000639 logging.debug("Running qemu command:\n%s", qemu_command)
lmra4967622009-07-23 01:36:32 +0000640 self.process = kvm_subprocess.run_bg(qemu_command, None,
641 logging.debug, "(qemu) ")
lmr6f669ce2009-05-31 19:02:42 +0000642
lmr9e964a02010-06-18 03:46:21 +0000643 # Make sure the process was started successfully
lmra4967622009-07-23 01:36:32 +0000644 if not self.process.is_alive():
645 logging.error("VM could not be created; "
646 "qemu command failed:\n%s" % qemu_command)
647 logging.error("Status: %s" % self.process.get_status())
648 logging.error("Output:" + kvm_utils.format_str_for_message(
649 self.process.get_output()))
lmrdc2ac6a2009-06-10 19:15:49 +0000650 self.destroy()
651 return False
lmr6f669ce2009-05-31 19:02:42 +0000652
lmr9e964a02010-06-18 03:46:21 +0000653 # Establish monitor connections
654 self.monitors = []
655 for monitor_name in kvm_utils.get_sub_dict_names(params,
656 "monitors"):
657 monitor_params = kvm_utils.get_sub_dict(params, monitor_name)
658 # Wait for monitor connection to succeed
659 end_time = time.time() + timeout
660 while time.time() < end_time:
661 try:
662 if monitor_params.get("monitor_type") == "qmp":
lmr449d2252010-06-18 03:48:23 +0000663 # Add a QMP monitor
664 monitor = kvm_monitor.QMPMonitor(
665 monitor_name,
666 self.get_monitor_filename(monitor_name))
lmr9e964a02010-06-18 03:46:21 +0000667 else:
668 # Add a "human" monitor
669 monitor = kvm_monitor.HumanMonitor(
670 monitor_name,
671 self.get_monitor_filename(monitor_name))
672 except kvm_monitor.MonitorError, e:
673 logging.warn(e)
674 else:
lmr449d2252010-06-18 03:48:23 +0000675 if monitor.is_responsive():
lmr9e964a02010-06-18 03:46:21 +0000676 break
677 time.sleep(1)
678 else:
679 logging.error("Could not connect to monitor '%s'" %
680 monitor_name)
681 self.destroy()
682 return False
683 # Add this monitor to the list
684 self.monitors += [monitor]
lmra4967622009-07-23 01:36:32 +0000685
lmrfe6515e2009-07-29 13:01:17 +0000686 # Get the output so far, to see if we have any problems with
lmrb306bf82010-07-08 23:47:00 +0000687 # KVM modules or with hugepage setup.
lmrfe6515e2009-07-29 13:01:17 +0000688 output = self.process.get_output()
689
lmrb306bf82010-07-08 23:47:00 +0000690 if re.search("Could not initialize KVM", output, re.IGNORECASE):
691 logging.error("Could not initialize KVM; "
692 "qemu command:\n%s" % qemu_command)
693 logging.error("Output:" + kvm_utils.format_str_for_message(
694 self.process.get_output()))
695 self.destroy()
696 return False
697
lmrfe6515e2009-07-29 13:01:17 +0000698 if "alloc_mem_area" in output:
699 logging.error("Could not allocate hugepage memory; "
700 "qemu command:\n%s" % qemu_command)
701 logging.error("Output:" + kvm_utils.format_str_for_message(
702 self.process.get_output()))
lmr090cd7a2010-04-01 02:18:52 +0000703 self.destroy()
lmrfe6515e2009-07-29 13:01:17 +0000704 return False
705
lmr71fa4de2010-06-14 15:54:55 +0000706 logging.debug("VM appears to be alive with PID %s", self.get_pid())
lmraa380a22010-06-22 02:05:29 +0000707
708 # Establish a session with the serial console -- requires a version
709 # of netcat that supports -U
710 self.serial_console = kvm_subprocess.kvm_shell_session(
711 "nc -U %s" % self.get_serial_console_filename(),
712 auto_close=False,
713 output_func=kvm_utils.log_line,
714 output_params=("serial-%s.log" % name,))
715
lmrdc2ac6a2009-06-10 19:15:49 +0000716 return True
717
718 finally:
719 fcntl.lockf(lockfile, fcntl.LOCK_UN)
720 lockfile.close()
lmr6f669ce2009-05-31 19:02:42 +0000721
722
lmr6f669ce2009-05-31 19:02:42 +0000723 def destroy(self, gracefully=True):
724 """
725 Destroy the VM.
726
lmr912c74b2009-08-17 20:43:27 +0000727 If gracefully is True, first attempt to shutdown the VM with a shell
728 command. Then, attempt to destroy the VM via the monitor with a 'quit'
729 command. If that fails, send SIGKILL to the qemu process.
lmr6f669ce2009-05-31 19:02:42 +0000730
731 @param gracefully: Whether an attempt will be made to end the VM
lmr912c74b2009-08-17 20:43:27 +0000732 using a shell command before trying to end the qemu process
733 with a 'quit' or a kill signal.
lmr6f669ce2009-05-31 19:02:42 +0000734 """
lmrf320b042009-09-15 05:48:06 +0000735 try:
736 # Is it already dead?
737 if self.is_dead():
738 logging.debug("VM is already down")
739 return
lmr6f669ce2009-05-31 19:02:42 +0000740
lmr71fa4de2010-06-14 15:54:55 +0000741 logging.debug("Destroying VM with PID %s...", self.get_pid())
lmr6f669ce2009-05-31 19:02:42 +0000742
lmrf320b042009-09-15 05:48:06 +0000743 if gracefully and self.params.get("shutdown_command"):
744 # Try to destroy with shell command
745 logging.debug("Trying to shutdown VM with shell command...")
746 session = self.remote_login()
747 if session:
748 try:
749 # Send the shutdown command
750 session.sendline(self.params.get("shutdown_command"))
751 logging.debug("Shutdown command sent; waiting for VM "
752 "to go down...")
753 if kvm_utils.wait_for(self.is_dead, 60, 1, 1):
754 logging.debug("VM is down")
755 return
756 finally:
757 session.close()
lmr6f669ce2009-05-31 19:02:42 +0000758
lmr9e964a02010-06-18 03:46:21 +0000759 if self.monitor:
760 # Try to destroy with a monitor command
761 logging.debug("Trying to kill VM with monitor command...")
762 try:
763 self.monitor.quit()
764 except kvm_monitor.MonitorError, e:
765 logging.warn(e)
766 else:
767 # Wait for the VM to be really dead
768 if kvm_utils.wait_for(self.is_dead, 5, 0.5, 0.5):
769 logging.debug("VM is down")
770 return
lmrf320b042009-09-15 05:48:06 +0000771
772 # If the VM isn't dead yet...
773 logging.debug("Cannot quit normally; sending a kill to close the "
774 "deal...")
775 kvm_utils.kill_process_tree(self.process.get_pid(), 9)
lmr6f669ce2009-05-31 19:02:42 +0000776 # Wait for the VM to be really dead
777 if kvm_utils.wait_for(self.is_dead, 5, 0.5, 0.5):
778 logging.debug("VM is down")
lmr6f669ce2009-05-31 19:02:42 +0000779 return
780
lmrf320b042009-09-15 05:48:06 +0000781 logging.error("Process %s is a zombie!" % self.process.get_pid())
lmr6f669ce2009-05-31 19:02:42 +0000782
lmrf320b042009-09-15 05:48:06 +0000783 finally:
lmr9e964a02010-06-18 03:46:21 +0000784 self.monitors = []
lmr4513c432010-02-03 11:59:03 +0000785 if self.pci_assignable:
786 self.pci_assignable.release_devs()
lmrf320b042009-09-15 05:48:06 +0000787 if self.process:
788 self.process.close()
lmraa380a22010-06-22 02:05:29 +0000789 if self.serial_console:
790 self.serial_console.close()
lmr84546f22010-06-29 23:03:34 +0000791 for f in ([self.get_testlog_filename(),
792 self.get_serial_console_filename()] +
lmr9e964a02010-06-18 03:46:21 +0000793 self.get_monitor_filenames()):
794 try:
795 os.unlink(f)
796 except OSError:
797 pass
798
799
800 @property
801 def monitor(self):
802 """
803 Return the main monitor object, selected by the parameter main_monitor.
804 If main_monitor isn't defined, return the first monitor.
805 If no monitors exist, or if main_monitor refers to a nonexistent
806 monitor, return None.
807 """
808 for m in self.monitors:
809 if m.name == self.params.get("main_monitor"):
810 return m
811 if self.monitors and not self.params.get("main_monitor"):
812 return self.monitors[0]
lmr6f669ce2009-05-31 19:02:42 +0000813
814
815 def is_alive(self):
816 """
lmr9e964a02010-06-18 03:46:21 +0000817 Return True if the VM is alive and its monitor is responsive.
lmr6f669ce2009-05-31 19:02:42 +0000818 """
lmra4967622009-07-23 01:36:32 +0000819 # Check if the process is running
820 if self.is_dead():
lmr6f669ce2009-05-31 19:02:42 +0000821 return False
822 # Try sending a monitor command
lmr9e964a02010-06-18 03:46:21 +0000823 return bool(self.monitor) and self.monitor.is_responsive()
lmr6f669ce2009-05-31 19:02:42 +0000824
825
826 def is_dead(self):
827 """
lmra4967622009-07-23 01:36:32 +0000828 Return True if the qemu process is dead.
lmr6f669ce2009-05-31 19:02:42 +0000829 """
lmra4967622009-07-23 01:36:32 +0000830 return not self.process or not self.process.is_alive()
lmr6f669ce2009-05-31 19:02:42 +0000831
832
833 def get_params(self):
834 """
835 Return the VM's params dict. Most modified params take effect only
836 upon VM.create().
837 """
838 return self.params
839
840
lmr9e964a02010-06-18 03:46:21 +0000841 def get_monitor_filename(self, monitor_name):
842 """
843 Return the filename corresponding to a given monitor name.
844 """
845 return "/tmp/monitor-%s-%s" % (monitor_name, self.instance)
846
847
848 def get_monitor_filenames(self):
849 """
850 Return a list of all monitor filenames (as specified in the VM's
851 params).
852 """
853 return [self.get_monitor_filename(m) for m in
854 kvm_utils.get_sub_dict_names(self.params, "monitors")]
855
856
lmr2b06f332010-06-22 02:03:41 +0000857 def get_serial_console_filename(self):
858 """
859 Return the serial console filename.
860 """
861 return "/tmp/serial-%s" % self.instance
862
863
lmr9e964a02010-06-18 03:46:21 +0000864 def get_testlog_filename(self):
865 """
866 Return the testlog filename.
867 """
868 return "/tmp/testlog-%s" % self.instance
869
870
lmrf4696342009-08-13 04:06:33 +0000871 def get_address(self, index=0):
lmr6f669ce2009-05-31 19:02:42 +0000872 """
lmrf4696342009-08-13 04:06:33 +0000873 Return the address of a NIC of the guest, in host space.
lmr6f669ce2009-05-31 19:02:42 +0000874
lmrf4696342009-08-13 04:06:33 +0000875 If port redirection is used, return 'localhost' (the NIC has no IP
876 address of its own). Otherwise return the NIC's IP address.
877
878 @param index: Index of the NIC whose address is requested.
lmr6f669ce2009-05-31 19:02:42 +0000879 """
lmree90dd92009-08-13 04:13:39 +0000880 nics = kvm_utils.get_sub_dict_names(self.params, "nics")
881 nic_name = nics[index]
lmrf4696342009-08-13 04:06:33 +0000882 nic_params = kvm_utils.get_sub_dict(self.params, nic_name)
883 if nic_params.get("nic_mode") == "tap":
884 mac, ip = kvm_utils.get_mac_ip_pair_from_dict(nic_params)
lmree90dd92009-08-13 04:13:39 +0000885 if not mac:
886 logging.debug("MAC address unavailable")
887 return None
888 if not ip or nic_params.get("always_use_tcpdump") == "yes":
889 # Get the IP address from the cache
890 ip = self.address_cache.get(mac)
891 if not ip:
892 logging.debug("Could not find IP address for MAC address: "
893 "%s" % mac)
894 return None
895 # Make sure the IP address is assigned to this guest
896 nic_dicts = [kvm_utils.get_sub_dict(self.params, nic)
897 for nic in nics]
898 macs = [kvm_utils.get_mac_ip_pair_from_dict(dict)[0]
899 for dict in nic_dicts]
900 if not kvm_utils.verify_ip_address_ownership(ip, macs):
901 logging.debug("Could not verify MAC-IP address mapping: "
902 "%s ---> %s" % (mac, ip))
903 return None
lmrf4696342009-08-13 04:06:33 +0000904 return ip
905 else:
906 return "localhost"
lmr6f669ce2009-05-31 19:02:42 +0000907
908
lmree90dd92009-08-13 04:13:39 +0000909 def get_port(self, port, nic_index=0):
lmr6f669ce2009-05-31 19:02:42 +0000910 """
911 Return the port in host space corresponding to port in guest space.
912
913 @param port: Port number in host space.
lmree90dd92009-08-13 04:13:39 +0000914 @param nic_index: Index of the NIC.
lmr6f669ce2009-05-31 19:02:42 +0000915 @return: If port redirection is used, return the host port redirected
916 to guest port port. Otherwise return port.
917 """
lmree90dd92009-08-13 04:13:39 +0000918 nic_name = kvm_utils.get_sub_dict_names(self.params, "nics")[nic_index]
lmrf4696342009-08-13 04:06:33 +0000919 nic_params = kvm_utils.get_sub_dict(self.params, nic_name)
920 if nic_params.get("nic_mode") == "tap":
921 return port
lmr6f669ce2009-05-31 19:02:42 +0000922 else:
lmrf4696342009-08-13 04:06:33 +0000923 if not self.redirs.has_key(port):
924 logging.warn("Warning: guest port %s requested but not "
925 "redirected" % port)
926 return self.redirs.get(port)
lmr6f669ce2009-05-31 19:02:42 +0000927
928
lmra4967622009-07-23 01:36:32 +0000929 def get_pid(self):
930 """
lmr71fa4de2010-06-14 15:54:55 +0000931 Return the VM's PID. If the VM is dead return None.
932
933 @note: This works under the assumption that self.process.get_pid()
934 returns the PID of the parent shell process.
935 """
936 try:
937 children = commands.getoutput("ps --ppid=%d -o pid=" %
938 self.process.get_pid()).split()
939 return int(children[0])
940 except (TypeError, IndexError, ValueError):
941 return None
942
943
944 def get_shell_pid(self):
945 """
946 Return the PID of the parent shell process.
947
948 @note: This works under the assumption that self.process.get_pid()
949 returns the PID of the parent shell process.
lmra4967622009-07-23 01:36:32 +0000950 """
951 return self.process.get_pid()
952
953
lmr0bee2342010-02-24 00:01:37 +0000954 def get_shared_meminfo(self):
955 """
956 Returns the VM's shared memory information.
957
958 @return: Shared memory used by VM (MB)
959 """
960 if self.is_dead():
961 logging.error("Could not get shared memory info from dead VM.")
962 return None
963
lmr983ecdf2010-06-14 15:57:12 +0000964 filename = "/proc/%d/statm" % self.get_pid()
965 shm = int(open(filename).read().split()[2])
lmr0bee2342010-02-24 00:01:37 +0000966 # statm stores informations in pages, translate it to MB
lmr983ecdf2010-06-14 15:57:12 +0000967 return shm * 4.0 / 1024
lmr0bee2342010-02-24 00:01:37 +0000968
969
lmr912c74b2009-08-17 20:43:27 +0000970 def remote_login(self, nic_index=0, timeout=10):
lmr6f669ce2009-05-31 19:02:42 +0000971 """
lmr912c74b2009-08-17 20:43:27 +0000972 Log into the guest via SSH/Telnet/Netcat.
lmr6f669ce2009-05-31 19:02:42 +0000973 If timeout expires while waiting for output from the guest (e.g. a
974 password prompt or a shell prompt) -- fail.
975
lmree90dd92009-08-13 04:13:39 +0000976 @param nic_index: The index of the NIC to connect to.
lmr6f669ce2009-05-31 19:02:42 +0000977 @param timeout: Time (seconds) before giving up logging into the
978 guest.
979 @return: kvm_spawn object on success and None on failure.
980 """
981 username = self.params.get("username", "")
982 password = self.params.get("password", "")
lmr912c74b2009-08-17 20:43:27 +0000983 prompt = self.params.get("shell_prompt", "[\#\$]")
lmr59f9e2d2009-10-05 18:47:16 +0000984 linesep = eval("'%s'" % self.params.get("shell_linesep", r"\n"))
lmr912c74b2009-08-17 20:43:27 +0000985 client = self.params.get("shell_client")
lmree90dd92009-08-13 04:13:39 +0000986 address = self.get_address(nic_index)
lmr912c74b2009-08-17 20:43:27 +0000987 port = self.get_port(int(self.params.get("shell_port")))
lmre56903f2010-06-22 02:09:35 +0000988 log_filename = ("session-%s-%s.log" %
989 (self.name, kvm_utils.generate_random_string(4)))
lmr912c74b2009-08-17 20:43:27 +0000990
lmree90dd92009-08-13 04:13:39 +0000991 if not address or not port:
992 logging.debug("IP address or port unavailable")
lmr6f669ce2009-05-31 19:02:42 +0000993 return None
994
lmr158604f2010-06-22 01:57:34 +0000995 session = kvm_utils.remote_login(client, address, port, username,
lmre56903f2010-06-22 02:09:35 +0000996 password, prompt, linesep,
997 log_filename, timeout)
lmr912c74b2009-08-17 20:43:27 +0000998
lmr6f669ce2009-05-31 19:02:42 +0000999 if session:
lmr912c74b2009-08-17 20:43:27 +00001000 session.set_status_test_command(self.params.get("status_test_"
lmr6f669ce2009-05-31 19:02:42 +00001001 "command", ""))
1002 return session
1003
1004
lmrc196d492010-05-07 14:14:26 +00001005 def copy_files_to(self, local_path, remote_path, nic_index=0, timeout=600):
lmr6f669ce2009-05-31 19:02:42 +00001006 """
lmr912c74b2009-08-17 20:43:27 +00001007 Transfer files to the guest.
lmr6f669ce2009-05-31 19:02:42 +00001008
1009 @param local_path: Host path
1010 @param remote_path: Guest path
lmr912c74b2009-08-17 20:43:27 +00001011 @param nic_index: The index of the NIC to connect to.
lmr6f669ce2009-05-31 19:02:42 +00001012 @param timeout: Time (seconds) before giving up on doing the remote
1013 copy.
1014 """
1015 username = self.params.get("username", "")
1016 password = self.params.get("password", "")
lmr912c74b2009-08-17 20:43:27 +00001017 client = self.params.get("file_transfer_client")
lmree90dd92009-08-13 04:13:39 +00001018 address = self.get_address(nic_index)
lmr912c74b2009-08-17 20:43:27 +00001019 port = self.get_port(int(self.params.get("file_transfer_port")))
1020
lmree90dd92009-08-13 04:13:39 +00001021 if not address or not port:
1022 logging.debug("IP address or port unavailable")
lmr6f669ce2009-05-31 19:02:42 +00001023 return None
lmr912c74b2009-08-17 20:43:27 +00001024
1025 if client == "scp":
lmrcbc86ef2010-07-07 20:50:08 +00001026 log_filename = ("scp-%s-%s.log" %
1027 (self.name, kvm_utils.generate_random_string(4)))
lmr912c74b2009-08-17 20:43:27 +00001028 return kvm_utils.scp_to_remote(address, port, username, password,
lmre56903f2010-06-22 02:09:35 +00001029 local_path, remote_path,
1030 log_filename, timeout)
lmrcbc86ef2010-07-07 20:50:08 +00001031 elif client == "rss":
1032 c = rss_file_transfer.FileUploadClient(address, port)
1033 c.upload(local_path, remote_path, timeout)
1034 c.close()
1035 return True
lmr6f669ce2009-05-31 19:02:42 +00001036
1037
lmrc196d492010-05-07 14:14:26 +00001038 def copy_files_from(self, remote_path, local_path, nic_index=0, timeout=600):
lmr6f669ce2009-05-31 19:02:42 +00001039 """
lmr912c74b2009-08-17 20:43:27 +00001040 Transfer files from the guest.
lmr6f669ce2009-05-31 19:02:42 +00001041
1042 @param local_path: Guest path
1043 @param remote_path: Host path
lmr912c74b2009-08-17 20:43:27 +00001044 @param nic_index: The index of the NIC to connect to.
lmr6f669ce2009-05-31 19:02:42 +00001045 @param timeout: Time (seconds) before giving up on doing the remote
1046 copy.
1047 """
1048 username = self.params.get("username", "")
1049 password = self.params.get("password", "")
lmr912c74b2009-08-17 20:43:27 +00001050 client = self.params.get("file_transfer_client")
lmree90dd92009-08-13 04:13:39 +00001051 address = self.get_address(nic_index)
lmr912c74b2009-08-17 20:43:27 +00001052 port = self.get_port(int(self.params.get("file_transfer_port")))
1053
lmree90dd92009-08-13 04:13:39 +00001054 if not address or not port:
1055 logging.debug("IP address or port unavailable")
lmr6f669ce2009-05-31 19:02:42 +00001056 return None
lmr6f669ce2009-05-31 19:02:42 +00001057
lmr912c74b2009-08-17 20:43:27 +00001058 if client == "scp":
lmrcbc86ef2010-07-07 20:50:08 +00001059 log_filename = ("scp-%s-%s.log" %
1060 (self.name, kvm_utils.generate_random_string(4)))
lmr912c74b2009-08-17 20:43:27 +00001061 return kvm_utils.scp_from_remote(address, port, username, password,
lmre56903f2010-06-22 02:09:35 +00001062 remote_path, local_path,
1063 log_filename, timeout)
lmrcbc86ef2010-07-07 20:50:08 +00001064 elif client == "rss":
1065 c = rss_file_transfer.FileDownloadClient(address, port)
1066 c.download(remote_path, local_path, timeout)
1067 c.close()
1068 return True
lmr6f669ce2009-05-31 19:02:42 +00001069
1070
lmraa380a22010-06-22 02:05:29 +00001071 def serial_login(self, timeout=10):
1072 """
1073 Log into the guest via the serial console.
1074 If timeout expires while waiting for output from the guest (e.g. a
1075 password prompt or a shell prompt) -- fail.
1076
1077 @param timeout: Time (seconds) before giving up logging into the guest.
1078 @return: kvm_spawn object on success and None on failure.
1079 """
1080 username = self.params.get("username", "")
1081 password = self.params.get("password", "")
1082 prompt = self.params.get("shell_prompt", "[\#\$]")
1083 linesep = eval("'%s'" % self.params.get("shell_linesep", r"\n"))
1084 status_test_command = self.params.get("status_test_command", "")
1085
1086 if self.serial_console:
1087 self.serial_console.set_linesep(linesep)
1088 self.serial_console.set_status_test_command(status_test_command)
1089 else:
1090 return None
1091
1092 # Make sure we get a login prompt
1093 self.serial_console.sendline()
1094
1095 if kvm_utils._remote_login(self.serial_console, username, password,
1096 prompt, timeout):
1097 return self.serial_console
1098
1099
lmr6f669ce2009-05-31 19:02:42 +00001100 def send_key(self, keystr):
1101 """
1102 Send a key event to the VM.
1103
1104 @param: keystr: A key event string (e.g. "ctrl-alt-delete")
1105 """
1106 # For compatibility with versions of QEMU that do not recognize all
1107 # key names: replace keyname with the hex value from the dict, which
1108 # QEMU will definitely accept
lmr9e964a02010-06-18 03:46:21 +00001109 dict = {"comma": "0x33",
1110 "dot": "0x34",
1111 "slash": "0x35"}
1112 for key, value in dict.items():
1113 keystr = keystr.replace(key, value)
1114 self.monitor.sendkey(keystr)
lmr6f669ce2009-05-31 19:02:42 +00001115 time.sleep(0.2)
1116
1117
1118 def send_string(self, str):
1119 """
1120 Send a string to the VM.
1121
1122 @param str: String, that must consist of alphanumeric characters only.
1123 Capital letters are allowed.
1124 """
1125 for char in str:
1126 if char.isupper():
1127 self.send_key("shift-%s" % char.lower())
1128 else:
1129 self.send_key(char)
lmra2533222009-07-20 12:43:46 +00001130
mbligh1ef218d2009-08-03 16:57:56 +00001131
lmra2533222009-07-20 12:43:46 +00001132 def get_uuid(self):
1133 """
1134 Catch UUID of the VM.
1135
1136 @return: None,if not specified in config file
1137 """
1138 if self.params.get("uuid") == "random":
1139 return self.uuid
1140 else:
1141 return self.params.get("uuid", None)
lmrdd2ff922009-12-01 23:39:12 +00001142
1143
1144 def get_cpu_count(self):
1145 """
1146 Get the cpu count of the VM.
1147 """
lmr13426552010-01-17 15:38:41 +00001148 session = self.remote_login()
1149 if not session:
1150 return None
lmrdd2ff922009-12-01 23:39:12 +00001151 try:
lmr13426552010-01-17 15:38:41 +00001152 cmd = self.params.get("cpu_chk_cmd")
1153 s, count = session.get_command_status_output(cmd)
1154 if s == 0:
1155 return int(count)
lmrdd2ff922009-12-01 23:39:12 +00001156 return None
1157 finally:
1158 session.close()
1159
1160
lmr28426c82010-04-16 06:02:58 +00001161 def get_memory_size(self, cmd=None):
lmrdd2ff922009-12-01 23:39:12 +00001162 """
lmr28426c82010-04-16 06:02:58 +00001163 Get bootup memory size of the VM.
1164
1165 @param check_cmd: Command used to check memory. If not provided,
1166 self.params.get("mem_chk_cmd") will be used.
lmrdd2ff922009-12-01 23:39:12 +00001167 """
lmr13426552010-01-17 15:38:41 +00001168 session = self.remote_login()
1169 if not session:
lmrdd2ff922009-12-01 23:39:12 +00001170 return None
lmr13426552010-01-17 15:38:41 +00001171 try:
lmr28426c82010-04-16 06:02:58 +00001172 if not cmd:
1173 cmd = self.params.get("mem_chk_cmd")
lmr13426552010-01-17 15:38:41 +00001174 s, mem_str = session.get_command_status_output(cmd)
1175 if s != 0:
1176 return None
lmr6d69f4d2010-02-12 11:35:55 +00001177 mem = re.findall("([0-9]+)", mem_str)
lmr13426552010-01-17 15:38:41 +00001178 mem_size = 0
1179 for m in mem:
1180 mem_size += int(m)
1181 if "GB" in mem_str:
1182 mem_size *= 1024
1183 elif "MB" in mem_str:
1184 pass
1185 else:
1186 mem_size /= 1024
1187 return int(mem_size)
lmrdd2ff922009-12-01 23:39:12 +00001188 finally:
1189 session.close()
lmr28426c82010-04-16 06:02:58 +00001190
1191
1192 def get_current_memory_size(self):
1193 """
1194 Get current memory size of the VM, rather than bootup memory.
1195 """
1196 cmd = self.params.get("mem_chk_cur_cmd")
1197 return self.get_memory_size(cmd)