blob: 7b1fc0556c3e0cc31255b6d2986324ad8964c8b7 [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
9import kvm_utils, kvm_subprocess, kvm_monitor
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 = []
121 for nic in params.get("nics").split():
lmr42b16662010-06-22 11:55:23 +0000122 self.netdev_id.append(kvm_utils.generate_random_id())
lmr6f669ce2009-05-31 19:02:42 +0000123
lmr9e964a02010-06-18 03:46:21 +0000124 # Find a unique identifier for this VM
lmr8b134f92009-06-08 14:47:31 +0000125 while True:
lmrd16a67d2009-06-10 19:52:59 +0000126 self.instance = (time.strftime("%Y%m%d-%H%M%S-") +
127 kvm_utils.generate_random_string(4))
lmr9e964a02010-06-18 03:46:21 +0000128 if not glob.glob("/tmp/*%s" % self.instance):
lmr8b134f92009-06-08 14:47:31 +0000129 break
130
131
lmr52800ba2009-08-17 20:49:58 +0000132 def clone(self, name=None, params=None, root_dir=None, address_cache=None):
lmr2c241172009-06-08 15:11:29 +0000133 """
134 Return a clone of the VM object with optionally modified parameters.
135 The clone is initially not alive and needs to be started using create().
136 Any parameters not passed to this function are copied from the source
137 VM.
138
139 @param name: Optional new VM name
140 @param params: Optional new VM creation parameters
lmr90b9fd52009-08-17 20:48:18 +0000141 @param root_dir: Optional new base directory for relative filenames
lmree90dd92009-08-13 04:13:39 +0000142 @param address_cache: A dict that maps MAC addresses to IP addresses
lmr2c241172009-06-08 15:11:29 +0000143 """
lmre45a1f22009-11-10 16:35:08 +0000144 if name is None:
lmr2c241172009-06-08 15:11:29 +0000145 name = self.name
lmre45a1f22009-11-10 16:35:08 +0000146 if params is None:
lmr2c241172009-06-08 15:11:29 +0000147 params = self.params.copy()
lmre45a1f22009-11-10 16:35:08 +0000148 if root_dir is None:
lmr90b9fd52009-08-17 20:48:18 +0000149 root_dir = self.root_dir
lmre45a1f22009-11-10 16:35:08 +0000150 if address_cache is None:
lmree90dd92009-08-13 04:13:39 +0000151 address_cache = self.address_cache
lmr52800ba2009-08-17 20:49:58 +0000152 return VM(name, params, root_dir, address_cache)
lmr2c241172009-06-08 15:11:29 +0000153
154
lmr52800ba2009-08-17 20:49:58 +0000155 def make_qemu_command(self, name=None, params=None, root_dir=None):
lmr6f669ce2009-05-31 19:02:42 +0000156 """
157 Generate a qemu command line. All parameters are optional. If a
158 parameter is not supplied, the corresponding value stored in the
159 class attributes is used.
160
lmr6f669ce2009-05-31 19:02:42 +0000161 @param name: The name of the object
162 @param params: A dict containing VM params
lmr90b9fd52009-08-17 20:48:18 +0000163 @param root_dir: Base directory for relative filenames
lmr6f669ce2009-05-31 19:02:42 +0000164
165 @note: The params dict should contain:
166 mem -- memory size in MBs
167 cdrom -- ISO filename to use with the qemu -cdrom parameter
lmr6f669ce2009-05-31 19:02:42 +0000168 extra_params -- a string to append to the qemu command
lmr912c74b2009-08-17 20:43:27 +0000169 shell_port -- port of the remote shell daemon on the guest
170 (SSH, Telnet or the home-made Remote Shell Server)
171 shell_client -- client program to use for connecting to the
172 remote shell daemon on the guest (ssh, telnet or nc)
lmreeff0eb2009-06-10 19:19:15 +0000173 x11_display -- if specified, the DISPLAY environment variable
174 will be be set to this value for the qemu process (useful for
175 SDL rendering)
lmr6f669ce2009-05-31 19:02:42 +0000176 images -- a list of image object names, separated by spaces
177 nics -- a list of NIC object names, separated by spaces
178
179 For each image in images:
180 drive_format -- string to pass as 'if' parameter for this
181 image (e.g. ide, scsi)
182 image_snapshot -- if yes, pass 'snapshot=on' to qemu for
183 this image
184 image_boot -- if yes, pass 'boot=on' to qemu for this image
185 In addition, all parameters required by get_image_filename.
186
187 For each NIC in nics:
188 nic_model -- string to pass as 'model' parameter for this
189 NIC (e.g. e1000)
190 """
lmr48abd7d2010-05-26 13:48:04 +0000191 # Helper function for command line option wrappers
192 def has_option(help, option):
193 return bool(re.search(r"^-%s(\s|$)" % option, help, re.MULTILINE))
194
195 # Wrappers for all supported qemu command line parameters.
196 # This is meant to allow support for multiple qemu versions.
197 # Each of these functions receives the output of 'qemu -help' as a
198 # parameter, and should add the requested command line option
199 # accordingly.
200
201 def add_name(help, name):
202 return " -name '%s'" % name
203
lmr9e964a02010-06-18 03:46:21 +0000204 def add_human_monitor(help, filename):
lmr09a78162010-06-14 16:29:23 +0000205 return " -monitor unix:'%s',server,nowait" % filename
lmr48abd7d2010-05-26 13:48:04 +0000206
lmr9e964a02010-06-18 03:46:21 +0000207 def add_qmp_monitor(help, filename):
208 return " -qmp unix:'%s',server,nowait" % filename
209
lmr2b06f332010-06-22 02:03:41 +0000210 def add_serial(help, filename):
211 return " -serial unix:'%s',server,nowait" % filename
212
lmr48abd7d2010-05-26 13:48:04 +0000213 def add_mem(help, mem):
214 return " -m %s" % mem
215
216 def add_smp(help, smp):
217 return " -smp %s" % smp
218
219 def add_cdrom(help, filename, index=2):
220 if has_option(help, "drive"):
lmr09a78162010-06-14 16:29:23 +0000221 return " -drive file='%s',index=%d,media=cdrom" % (filename,
222 index)
lmr48abd7d2010-05-26 13:48:04 +0000223 else:
lmr09a78162010-06-14 16:29:23 +0000224 return " -cdrom '%s'" % filename
lmr48abd7d2010-05-26 13:48:04 +0000225
226 def add_drive(help, filename, format=None, cache=None, werror=None,
227 serial=None, snapshot=False, boot=False):
lmr09a78162010-06-14 16:29:23 +0000228 cmd = " -drive file='%s'" % filename
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,
lmrb1cad1e2010-06-17 17:36:09 +0000246 downscript=None, netdev_id=None):
247 if has_option(help, "netdev"):
248 cmd = " -netdev %s,id=%s" % (mode, netdev_id)
249 else:
250 cmd = " -net %s,vlan=%d" % (mode, vlan)
lmr48abd7d2010-05-26 13:48:04 +0000251 if mode == "tap":
lmr09a78162010-06-14 16:29:23 +0000252 if ifname: cmd += ",ifname='%s'" % ifname
253 if script: cmd += ",script='%s'" % script
254 cmd += ",downscript='%s'" % (downscript or "no")
lmr48abd7d2010-05-26 13:48:04 +0000255 return cmd
256
257 def add_floppy(help, filename):
lmr09a78162010-06-14 16:29:23 +0000258 return " -fda '%s'" % filename
lmr48abd7d2010-05-26 13:48:04 +0000259
260 def add_tftp(help, filename):
lmr09a78162010-06-14 16:29:23 +0000261 return " -tftp '%s'" % filename
lmr48abd7d2010-05-26 13:48:04 +0000262
263 def add_tcp_redir(help, host_port, guest_port):
264 return " -redir tcp:%s::%s" % (host_port, guest_port)
265
266 def add_vnc(help, vnc_port):
267 return " -vnc :%d" % (vnc_port - 5900)
268
269 def add_sdl(help):
270 if has_option(help, "sdl"):
271 return " -sdl"
272 else:
273 return ""
274
275 def add_nographic(help):
276 return " -nographic"
277
278 def add_uuid(help, uuid):
lmr09a78162010-06-14 16:29:23 +0000279 return " -uuid '%s'" % uuid
lmr48abd7d2010-05-26 13:48:04 +0000280
281 def add_pcidevice(help, host):
lmr09a78162010-06-14 16:29:23 +0000282 return " -pcidevice host='%s'" % host
lmr48abd7d2010-05-26 13:48:04 +0000283
lmr41cb8fc2010-06-10 15:30:45 +0000284 def add_kernel(help, filename):
lmr09a78162010-06-14 16:29:23 +0000285 return " -kernel '%s'" % filename
lmr41cb8fc2010-06-10 15:30:45 +0000286
287 def add_initrd(help, filename):
lmr09a78162010-06-14 16:29:23 +0000288 return " -initrd '%s'" % filename
lmr41cb8fc2010-06-10 15:30:45 +0000289
lmre0474e32010-06-29 14:10:09 +0000290 def add_kernel_cmdline(help, cmdline):
291 return " -append %s" % cmdline
292
lmr71024552010-06-29 14:12:35 +0000293 def add_testdev(help, filename):
294 return (" -chardev file,id=testlog,path=%s"
295 " -device testdev,chardev=testlog" % filename)
296
lmr48abd7d2010-05-26 13:48:04 +0000297 # End of command line option wrappers
298
299 if name is None: name = self.name
300 if params is None: params = self.params
301 if root_dir is None: root_dir = self.root_dir
302
303 qemu_binary = kvm_utils.get_path(root_dir, params.get("qemu_binary",
304 "qemu"))
305 # Get the output of 'qemu -help' (log a message in case this call never
306 # returns or causes some other kind of trouble)
307 logging.debug("Getting output of 'qemu -help'")
308 help = commands.getoutput("%s -help" % qemu_binary)
lmr6f669ce2009-05-31 19:02:42 +0000309
lmreeff0eb2009-06-10 19:19:15 +0000310 # Start constructing the qemu command
311 qemu_cmd = ""
312 # Set the X11 display parameter if requested
313 if params.get("x11_display"):
314 qemu_cmd += "DISPLAY=%s " % params.get("x11_display")
315 # Add the qemu binary
lmr48abd7d2010-05-26 13:48:04 +0000316 qemu_cmd += qemu_binary
lmreeff0eb2009-06-10 19:19:15 +0000317 # Add the VM's name
lmr48abd7d2010-05-26 13:48:04 +0000318 qemu_cmd += add_name(help, name)
lmr9e964a02010-06-18 03:46:21 +0000319 # Add monitors
320 for monitor_name in kvm_utils.get_sub_dict_names(params, "monitors"):
321 monitor_params = kvm_utils.get_sub_dict(params, monitor_name)
322 monitor_filename = self.get_monitor_filename(monitor_name)
323 if monitor_params.get("monitor_type") == "qmp":
324 qemu_cmd += add_qmp_monitor(help, monitor_filename)
325 else:
326 qemu_cmd += add_human_monitor(help, monitor_filename)
lmr6f669ce2009-05-31 19:02:42 +0000327
lmr2b06f332010-06-22 02:03:41 +0000328 # Add serial console redirection
329 qemu_cmd += add_serial(help, self.get_serial_console_filename())
330
lmr6f669ce2009-05-31 19:02:42 +0000331 for image_name in kvm_utils.get_sub_dict_names(params, "images"):
332 image_params = kvm_utils.get_sub_dict(params, image_name)
lmr9d75ee32009-09-29 13:14:13 +0000333 if image_params.get("boot_drive") == "no":
334 continue
lmr48abd7d2010-05-26 13:48:04 +0000335 qemu_cmd += add_drive(help,
336 get_image_filename(image_params, root_dir),
337 image_params.get("drive_format"),
338 image_params.get("drive_cache"),
339 image_params.get("drive_werror"),
340 image_params.get("drive_serial"),
341 image_params.get("image_snapshot") == "yes",
342 image_params.get("image_boot") == "yes")
lmr6f669ce2009-05-31 19:02:42 +0000343
344 vlan = 0
345 for nic_name in kvm_utils.get_sub_dict_names(params, "nics"):
346 nic_params = kvm_utils.get_sub_dict(params, nic_name)
lmrf4696342009-08-13 04:06:33 +0000347 # Handle the '-net nic' part
lmr48abd7d2010-05-26 13:48:04 +0000348 mac = None
349 if "address_index" in nic_params:
350 mac = kvm_utils.get_mac_ip_pair_from_dict(nic_params)[0]
lmrb1cad1e2010-06-17 17:36:09 +0000351 qemu_cmd += add_nic(help, vlan, nic_params.get("nic_model"), mac,
352 self.netdev_id[vlan])
lmrf4696342009-08-13 04:06:33 +0000353 # Handle the '-net tap' or '-net user' part
lmre4eaa862010-06-01 19:15:14 +0000354 script = nic_params.get("nic_script")
355 downscript = nic_params.get("nic_downscript")
lmr48abd7d2010-05-26 13:48:04 +0000356 if script:
357 script = kvm_utils.get_path(root_dir, script)
358 if downscript:
359 downscript = kvm_utils.get_path(root_dir, downscript)
360 qemu_cmd += add_net(help, vlan, nic_params.get("nic_mode", "user"),
361 nic_params.get("nic_ifname"),
lmrb1cad1e2010-06-17 17:36:09 +0000362 script, downscript, self.netdev_id[vlan])
lmrf4696342009-08-13 04:06:33 +0000363 # Proceed to next NIC
lmr6f669ce2009-05-31 19:02:42 +0000364 vlan += 1
365
366 mem = params.get("mem")
367 if mem:
lmr48abd7d2010-05-26 13:48:04 +0000368 qemu_cmd += add_mem(help, mem)
lmr6f669ce2009-05-31 19:02:42 +0000369
lmrc43bf372009-11-10 13:19:00 +0000370 smp = params.get("smp")
371 if smp:
lmrdb3fe612010-06-14 16:19:28 +0000372 qemu_cmd += add_smp(help, smp)
lmrc43bf372009-11-10 13:19:00 +0000373
lmr6f669ce2009-05-31 19:02:42 +0000374 iso = params.get("cdrom")
375 if iso:
lmr90b9fd52009-08-17 20:48:18 +0000376 iso = kvm_utils.get_path(root_dir, iso)
lmr48abd7d2010-05-26 13:48:04 +0000377 qemu_cmd += add_cdrom(help, iso)
lmr89fd6412010-01-18 02:42:57 +0000378
379 # Even though this is not a really scalable approach,
380 # it doesn't seem like we are going to need more than
381 # 2 CDs active on the same VM.
382 iso_extra = params.get("cdrom_extra")
383 if iso_extra:
384 iso_extra = kvm_utils.get_path(root_dir, iso_extra)
lmr48abd7d2010-05-26 13:48:04 +0000385 qemu_cmd += add_cdrom(help, iso_extra, 3)
lmr6f669ce2009-05-31 19:02:42 +0000386
lmrb0a9b762009-10-09 20:43:30 +0000387 # We may want to add {floppy_otps} parameter for -fda
lmr48abd7d2010-05-26 13:48:04 +0000388 # {fat:floppy:}/path/. However vvfat is not usually recommended.
lmrb0a9b762009-10-09 20:43:30 +0000389 floppy = params.get("floppy")
390 if floppy:
lmrf69f6b12009-11-10 16:33:44 +0000391 floppy = kvm_utils.get_path(root_dir, floppy)
lmr48abd7d2010-05-26 13:48:04 +0000392 qemu_cmd += add_floppy(help, floppy)
lmrb0a9b762009-10-09 20:43:30 +0000393
394 tftp = params.get("tftp")
395 if tftp:
lmrf69f6b12009-11-10 16:33:44 +0000396 tftp = kvm_utils.get_path(root_dir, tftp)
lmr48abd7d2010-05-26 13:48:04 +0000397 qemu_cmd += add_tftp(help, tftp)
lmr6f669ce2009-05-31 19:02:42 +0000398
lmr41cb8fc2010-06-10 15:30:45 +0000399 kernel = params.get("kernel")
400 if kernel:
401 kernel = kvm_utils.get_path(root_dir, kernel)
402 qemu_cmd += add_kernel(help, kernel)
403
lmre0474e32010-06-29 14:10:09 +0000404 kernel_cmdline = params.get("kernel_cmdline")
405 if kernel_cmdline:
406 qemu_cmd += add_kernel_cmdline(help, kernel_cmdline)
407
lmr41cb8fc2010-06-10 15:30:45 +0000408 initrd = params.get("initrd")
409 if initrd:
410 initrd = kvm_utils.get_path(root_dir, initrd)
411 qemu_cmd += add_initrd(help, initrd)
412
lmr6f669ce2009-05-31 19:02:42 +0000413 for redir_name in kvm_utils.get_sub_dict_names(params, "redirs"):
414 redir_params = kvm_utils.get_sub_dict(params, redir_name)
415 guest_port = int(redir_params.get("guest_port"))
lmrf4696342009-08-13 04:06:33 +0000416 host_port = self.redirs.get(guest_port)
lmr48abd7d2010-05-26 13:48:04 +0000417 qemu_cmd += add_tcp_redir(help, host_port, guest_port)
lmr6f669ce2009-05-31 19:02:42 +0000418
419 if params.get("display") == "vnc":
lmr48abd7d2010-05-26 13:48:04 +0000420 qemu_cmd += add_vnc(help, self.vnc_port)
lmr6f669ce2009-05-31 19:02:42 +0000421 elif params.get("display") == "sdl":
lmr48abd7d2010-05-26 13:48:04 +0000422 qemu_cmd += add_sdl(help)
lmr6f669ce2009-05-31 19:02:42 +0000423 elif params.get("display") == "nographic":
lmr48abd7d2010-05-26 13:48:04 +0000424 qemu_cmd += add_nographic(help)
lmr6f669ce2009-05-31 19:02:42 +0000425
lmra2533222009-07-20 12:43:46 +0000426 if params.get("uuid") == "random":
lmr48abd7d2010-05-26 13:48:04 +0000427 qemu_cmd += add_uuid(help, self.uuid)
lmra2533222009-07-20 12:43:46 +0000428 elif params.get("uuid"):
lmr48abd7d2010-05-26 13:48:04 +0000429 qemu_cmd += add_uuid(help, params.get("uuid"))
lmra2533222009-07-20 12:43:46 +0000430
lmr71024552010-06-29 14:12:35 +0000431 if params.get("testdev") == "yes":
432 qemu_cmd += add_testdev(help, self.get_testlog_filename())
433
lmr31af3a12010-01-18 16:46:52 +0000434 # If the PCI assignment step went OK, add each one of the PCI assigned
435 # devices to the qemu command line.
436 if self.pci_assignable:
437 for pci_id in self.pa_pci_ids:
lmr48abd7d2010-05-26 13:48:04 +0000438 qemu_cmd += add_pcidevice(help, pci_id)
439
440 extra_params = params.get("extra_params")
441 if extra_params:
442 qemu_cmd += " %s" % extra_params
lmr31af3a12010-01-18 16:46:52 +0000443
lmr6f669ce2009-05-31 19:02:42 +0000444 return qemu_cmd
445
446
lmr52800ba2009-08-17 20:49:58 +0000447 def create(self, name=None, params=None, root_dir=None,
lmr1424f3e2010-06-17 13:57:09 +0000448 for_migration=False, timeout=5.0, extra_params=None):
lmr6f669ce2009-05-31 19:02:42 +0000449 """
450 Start the VM by running a qemu command.
451 All parameters are optional. The following applies to all parameters
452 but for_migration: If a parameter is not supplied, the corresponding
453 value stored in the class attributes is used, and if it is supplied,
454 it is stored for later use.
455
456 @param name: The name of the object
457 @param params: A dict containing VM params
lmr90b9fd52009-08-17 20:48:18 +0000458 @param root_dir: Base directory for relative filenames
lmr6f669ce2009-05-31 19:02:42 +0000459 @param for_migration: If True, start the VM with the -incoming
460 option
lmr1424f3e2010-06-17 13:57:09 +0000461 @param extra_params: extra params for qemu command.e.g -incoming option
462 Please use this parameter instead of for_migration.
lmr6f669ce2009-05-31 19:02:42 +0000463 """
lmr135b5e62009-06-10 19:22:31 +0000464 self.destroy()
465
lmre45a1f22009-11-10 16:35:08 +0000466 if name is not None:
lmr6f669ce2009-05-31 19:02:42 +0000467 self.name = name
lmre45a1f22009-11-10 16:35:08 +0000468 if params is not None:
lmr6f669ce2009-05-31 19:02:42 +0000469 self.params = params
lmre45a1f22009-11-10 16:35:08 +0000470 if root_dir is not None:
lmr90b9fd52009-08-17 20:48:18 +0000471 self.root_dir = root_dir
lmr6f669ce2009-05-31 19:02:42 +0000472 name = self.name
473 params = self.params
lmr90b9fd52009-08-17 20:48:18 +0000474 root_dir = self.root_dir
lmr6f669ce2009-05-31 19:02:42 +0000475
476 # Verify the md5sum of the ISO image
477 iso = params.get("cdrom")
478 if iso:
lmr90b9fd52009-08-17 20:48:18 +0000479 iso = kvm_utils.get_path(root_dir, iso)
lmr6f669ce2009-05-31 19:02:42 +0000480 if not os.path.exists(iso):
481 logging.error("ISO file not found: %s" % iso)
482 return False
483 compare = False
484 if params.get("md5sum_1m"):
lmrf4696342009-08-13 04:06:33 +0000485 logging.debug("Comparing expected MD5 sum with MD5 sum of "
486 "first MB of ISO file...")
lmrd60882f2010-02-04 03:26:36 +0000487 actual_hash = utils.hash_file(iso, 1048576, method="md5")
lmr03ba22e2009-10-23 12:07:44 +0000488 expected_hash = params.get("md5sum_1m")
lmr6f669ce2009-05-31 19:02:42 +0000489 compare = True
490 elif params.get("md5sum"):
lmrf4696342009-08-13 04:06:33 +0000491 logging.debug("Comparing expected MD5 sum with MD5 sum of ISO "
492 "file...")
lmrd60882f2010-02-04 03:26:36 +0000493 actual_hash = utils.hash_file(iso, method="md5")
lmr03ba22e2009-10-23 12:07:44 +0000494 expected_hash = params.get("md5sum")
495 compare = True
496 elif params.get("sha1sum"):
497 logging.debug("Comparing expected SHA1 sum with SHA1 sum of "
498 "ISO file...")
lmrd60882f2010-02-04 03:26:36 +0000499 actual_hash = utils.hash_file(iso, method="sha1")
lmrea1266e2009-11-10 16:41:08 +0000500 expected_hash = params.get("sha1sum")
lmr6f669ce2009-05-31 19:02:42 +0000501 compare = True
502 if compare:
lmr03ba22e2009-10-23 12:07:44 +0000503 if actual_hash == expected_hash:
504 logging.debug("Hashes match")
lmr6f669ce2009-05-31 19:02:42 +0000505 else:
lmr03ba22e2009-10-23 12:07:44 +0000506 logging.error("Actual hash differs from expected one")
lmr6f669ce2009-05-31 19:02:42 +0000507 return False
508
lmrdc2ac6a2009-06-10 19:15:49 +0000509 # Make sure the following code is not executed by more than one thread
510 # at the same time
511 lockfile = open("/tmp/kvm-autotest-vm-create.lock", "w+")
512 fcntl.lockf(lockfile, fcntl.LOCK_EX)
lmr6f669ce2009-05-31 19:02:42 +0000513
lmrdc2ac6a2009-06-10 19:15:49 +0000514 try:
515 # Handle port redirections
516 redir_names = kvm_utils.get_sub_dict_names(params, "redirs")
517 host_ports = kvm_utils.find_free_ports(5000, 6000, len(redir_names))
518 self.redirs = {}
519 for i in range(len(redir_names)):
520 redir_params = kvm_utils.get_sub_dict(params, redir_names[i])
521 guest_port = int(redir_params.get("guest_port"))
522 self.redirs[guest_port] = host_ports[i]
lmr6f669ce2009-05-31 19:02:42 +0000523
lmrdc2ac6a2009-06-10 19:15:49 +0000524 # Find available VNC port, if needed
525 if params.get("display") == "vnc":
lmrc0da8902010-05-17 20:31:36 +0000526 self.vnc_port = kvm_utils.find_free_port(5900, 6100)
lmr6f669ce2009-05-31 19:02:42 +0000527
lmra2533222009-07-20 12:43:46 +0000528 # Find random UUID if specified 'uuid = random' in config file
529 if params.get("uuid") == "random":
530 f = open("/proc/sys/kernel/random/uuid")
531 self.uuid = f.read().strip()
532 f.close()
533
lmr31ed61d2010-06-07 13:21:38 +0000534 # Assign a PCI assignable device
535 self.pci_assignable = None
536 pa_type = params.get("pci_assignable")
537 if pa_type in ["vf", "pf", "mixed"]:
lmr31af3a12010-01-18 16:46:52 +0000538 pa_devices_requested = params.get("devices_requested")
539
540 # Virtual Functions (VF) assignable devices
541 if pa_type == "vf":
lmr31ed61d2010-06-07 13:21:38 +0000542 self.pci_assignable = kvm_utils.PciAssignable(
543 type=pa_type,
544 driver=params.get("driver"),
545 driver_option=params.get("driver_option"),
546 devices_requested=pa_devices_requested)
lmr31af3a12010-01-18 16:46:52 +0000547 # Physical NIC (PF) assignable devices
548 elif pa_type == "pf":
lmr31ed61d2010-06-07 13:21:38 +0000549 self.pci_assignable = kvm_utils.PciAssignable(
550 type=pa_type,
551 names=params.get("device_names"),
552 devices_requested=pa_devices_requested)
lmr31af3a12010-01-18 16:46:52 +0000553 # Working with both VF and PF
554 elif pa_type == "mixed":
lmr31ed61d2010-06-07 13:21:38 +0000555 self.pci_assignable = kvm_utils.PciAssignable(
556 type=pa_type,
557 driver=params.get("driver"),
558 driver_option=params.get("driver_option"),
559 names=params.get("device_names"),
560 devices_requested=pa_devices_requested)
lmr31af3a12010-01-18 16:46:52 +0000561
562 self.pa_pci_ids = self.pci_assignable.request_devs()
563
564 if self.pa_pci_ids:
lmr31ed61d2010-06-07 13:21:38 +0000565 logging.debug("Successfuly assigned devices: %s",
lmr31af3a12010-01-18 16:46:52 +0000566 self.pa_pci_ids)
567 else:
568 logging.error("No PCI assignable devices were assigned "
569 "and 'pci_assignable' is defined to %s "
lmr31ed61d2010-06-07 13:21:38 +0000570 "on your config file. Aborting VM creation.",
lmr31af3a12010-01-18 16:46:52 +0000571 pa_type)
572 return False
573
lmr856d58c2010-06-08 18:29:31 +0000574 elif pa_type and pa_type != "no":
lmr31ed61d2010-06-07 13:21:38 +0000575 logging.warn("Unsupported pci_assignable type: %s", pa_type)
lmr31af3a12010-01-18 16:46:52 +0000576
lmrdc2ac6a2009-06-10 19:15:49 +0000577 # Make qemu command
578 qemu_command = self.make_qemu_command()
lmr6f669ce2009-05-31 19:02:42 +0000579
lmr1424f3e2010-06-17 13:57:09 +0000580 # Enable migration support for VM by adding extra_params.
581 if extra_params is not None:
582 if " -incoming tcp:0:%d" == extra_params:
583 self.migration_port = kvm_utils.find_free_port(5200, 6000)
584 qemu_command += extra_params % self.migration_port
585 elif " -incoming unix:%s" == extra_params:
586 self.migration_file = os.path.join("/tmp/", "unix-" +
587 time.strftime("%Y%m%d-%H%M%S"))
588 qemu_command += extra_params % self.migration_file
589 else:
590 qemu_command += extra_params
lmr6f669ce2009-05-31 19:02:42 +0000591
lmrdc2ac6a2009-06-10 19:15:49 +0000592 logging.debug("Running qemu command:\n%s", qemu_command)
lmra4967622009-07-23 01:36:32 +0000593 self.process = kvm_subprocess.run_bg(qemu_command, None,
594 logging.debug, "(qemu) ")
lmr6f669ce2009-05-31 19:02:42 +0000595
lmr9e964a02010-06-18 03:46:21 +0000596 # Make sure the process was started successfully
lmra4967622009-07-23 01:36:32 +0000597 if not self.process.is_alive():
598 logging.error("VM could not be created; "
599 "qemu command failed:\n%s" % qemu_command)
600 logging.error("Status: %s" % self.process.get_status())
601 logging.error("Output:" + kvm_utils.format_str_for_message(
602 self.process.get_output()))
lmrdc2ac6a2009-06-10 19:15:49 +0000603 self.destroy()
604 return False
lmr6f669ce2009-05-31 19:02:42 +0000605
lmr9e964a02010-06-18 03:46:21 +0000606 # Establish monitor connections
607 self.monitors = []
608 for monitor_name in kvm_utils.get_sub_dict_names(params,
609 "monitors"):
610 monitor_params = kvm_utils.get_sub_dict(params, monitor_name)
611 # Wait for monitor connection to succeed
612 end_time = time.time() + timeout
613 while time.time() < end_time:
614 try:
615 if monitor_params.get("monitor_type") == "qmp":
lmr449d2252010-06-18 03:48:23 +0000616 # Add a QMP monitor
617 monitor = kvm_monitor.QMPMonitor(
618 monitor_name,
619 self.get_monitor_filename(monitor_name))
lmr9e964a02010-06-18 03:46:21 +0000620 else:
621 # Add a "human" monitor
622 monitor = kvm_monitor.HumanMonitor(
623 monitor_name,
624 self.get_monitor_filename(monitor_name))
625 except kvm_monitor.MonitorError, e:
626 logging.warn(e)
627 else:
lmr449d2252010-06-18 03:48:23 +0000628 if monitor.is_responsive():
lmr9e964a02010-06-18 03:46:21 +0000629 break
630 time.sleep(1)
631 else:
632 logging.error("Could not connect to monitor '%s'" %
633 monitor_name)
634 self.destroy()
635 return False
636 # Add this monitor to the list
637 self.monitors += [monitor]
lmra4967622009-07-23 01:36:32 +0000638
lmrfe6515e2009-07-29 13:01:17 +0000639 # Get the output so far, to see if we have any problems with
640 # hugepage setup.
641 output = self.process.get_output()
642
643 if "alloc_mem_area" in output:
644 logging.error("Could not allocate hugepage memory; "
645 "qemu command:\n%s" % qemu_command)
646 logging.error("Output:" + kvm_utils.format_str_for_message(
647 self.process.get_output()))
lmr090cd7a2010-04-01 02:18:52 +0000648 self.destroy()
lmrfe6515e2009-07-29 13:01:17 +0000649 return False
650
lmr71fa4de2010-06-14 15:54:55 +0000651 logging.debug("VM appears to be alive with PID %s", self.get_pid())
lmraa380a22010-06-22 02:05:29 +0000652
653 # Establish a session with the serial console -- requires a version
654 # of netcat that supports -U
655 self.serial_console = kvm_subprocess.kvm_shell_session(
656 "nc -U %s" % self.get_serial_console_filename(),
657 auto_close=False,
658 output_func=kvm_utils.log_line,
659 output_params=("serial-%s.log" % name,))
660
lmrdc2ac6a2009-06-10 19:15:49 +0000661 return True
662
663 finally:
664 fcntl.lockf(lockfile, fcntl.LOCK_UN)
665 lockfile.close()
lmr6f669ce2009-05-31 19:02:42 +0000666
667
lmr6f669ce2009-05-31 19:02:42 +0000668 def destroy(self, gracefully=True):
669 """
670 Destroy the VM.
671
lmr912c74b2009-08-17 20:43:27 +0000672 If gracefully is True, first attempt to shutdown the VM with a shell
673 command. Then, attempt to destroy the VM via the monitor with a 'quit'
674 command. If that fails, send SIGKILL to the qemu process.
lmr6f669ce2009-05-31 19:02:42 +0000675
676 @param gracefully: Whether an attempt will be made to end the VM
lmr912c74b2009-08-17 20:43:27 +0000677 using a shell command before trying to end the qemu process
678 with a 'quit' or a kill signal.
lmr6f669ce2009-05-31 19:02:42 +0000679 """
lmrf320b042009-09-15 05:48:06 +0000680 try:
681 # Is it already dead?
682 if self.is_dead():
683 logging.debug("VM is already down")
684 return
lmr6f669ce2009-05-31 19:02:42 +0000685
lmr71fa4de2010-06-14 15:54:55 +0000686 logging.debug("Destroying VM with PID %s...", self.get_pid())
lmr6f669ce2009-05-31 19:02:42 +0000687
lmrf320b042009-09-15 05:48:06 +0000688 if gracefully and self.params.get("shutdown_command"):
689 # Try to destroy with shell command
690 logging.debug("Trying to shutdown VM with shell command...")
691 session = self.remote_login()
692 if session:
693 try:
694 # Send the shutdown command
695 session.sendline(self.params.get("shutdown_command"))
696 logging.debug("Shutdown command sent; waiting for VM "
697 "to go down...")
698 if kvm_utils.wait_for(self.is_dead, 60, 1, 1):
699 logging.debug("VM is down")
700 return
701 finally:
702 session.close()
lmr6f669ce2009-05-31 19:02:42 +0000703
lmr9e964a02010-06-18 03:46:21 +0000704 if self.monitor:
705 # Try to destroy with a monitor command
706 logging.debug("Trying to kill VM with monitor command...")
707 try:
708 self.monitor.quit()
709 except kvm_monitor.MonitorError, e:
710 logging.warn(e)
711 else:
712 # Wait for the VM to be really dead
713 if kvm_utils.wait_for(self.is_dead, 5, 0.5, 0.5):
714 logging.debug("VM is down")
715 return
lmrf320b042009-09-15 05:48:06 +0000716
717 # If the VM isn't dead yet...
718 logging.debug("Cannot quit normally; sending a kill to close the "
719 "deal...")
720 kvm_utils.kill_process_tree(self.process.get_pid(), 9)
lmr6f669ce2009-05-31 19:02:42 +0000721 # Wait for the VM to be really dead
722 if kvm_utils.wait_for(self.is_dead, 5, 0.5, 0.5):
723 logging.debug("VM is down")
lmr6f669ce2009-05-31 19:02:42 +0000724 return
725
lmrf320b042009-09-15 05:48:06 +0000726 logging.error("Process %s is a zombie!" % self.process.get_pid())
lmr6f669ce2009-05-31 19:02:42 +0000727
lmrf320b042009-09-15 05:48:06 +0000728 finally:
lmr9e964a02010-06-18 03:46:21 +0000729 self.monitors = []
lmr4513c432010-02-03 11:59:03 +0000730 if self.pci_assignable:
731 self.pci_assignable.release_devs()
lmrf320b042009-09-15 05:48:06 +0000732 if self.process:
733 self.process.close()
lmraa380a22010-06-22 02:05:29 +0000734 if self.serial_console:
735 self.serial_console.close()
lmr9e964a02010-06-18 03:46:21 +0000736 for f in ([self.get_testlog_filename()] +
737 self.get_monitor_filenames()):
738 try:
739 os.unlink(f)
740 except OSError:
741 pass
742
743
744 @property
745 def monitor(self):
746 """
747 Return the main monitor object, selected by the parameter main_monitor.
748 If main_monitor isn't defined, return the first monitor.
749 If no monitors exist, or if main_monitor refers to a nonexistent
750 monitor, return None.
751 """
752 for m in self.monitors:
753 if m.name == self.params.get("main_monitor"):
754 return m
755 if self.monitors and not self.params.get("main_monitor"):
756 return self.monitors[0]
lmr6f669ce2009-05-31 19:02:42 +0000757
758
759 def is_alive(self):
760 """
lmr9e964a02010-06-18 03:46:21 +0000761 Return True if the VM is alive and its monitor is responsive.
lmr6f669ce2009-05-31 19:02:42 +0000762 """
lmra4967622009-07-23 01:36:32 +0000763 # Check if the process is running
764 if self.is_dead():
lmr6f669ce2009-05-31 19:02:42 +0000765 return False
766 # Try sending a monitor command
lmr9e964a02010-06-18 03:46:21 +0000767 return bool(self.monitor) and self.monitor.is_responsive()
lmr6f669ce2009-05-31 19:02:42 +0000768
769
770 def is_dead(self):
771 """
lmra4967622009-07-23 01:36:32 +0000772 Return True if the qemu process is dead.
lmr6f669ce2009-05-31 19:02:42 +0000773 """
lmra4967622009-07-23 01:36:32 +0000774 return not self.process or not self.process.is_alive()
lmr6f669ce2009-05-31 19:02:42 +0000775
776
lmra4197002009-08-13 05:00:51 +0000777 def kill_tail_thread(self):
778 """
779 Stop the tailing thread which reports the output of qemu.
780 """
781 if self.process:
782 self.process.kill_tail_thread()
783
784
lmr6f669ce2009-05-31 19:02:42 +0000785 def get_params(self):
786 """
787 Return the VM's params dict. Most modified params take effect only
788 upon VM.create().
789 """
790 return self.params
791
792
lmr9e964a02010-06-18 03:46:21 +0000793 def get_monitor_filename(self, monitor_name):
794 """
795 Return the filename corresponding to a given monitor name.
796 """
797 return "/tmp/monitor-%s-%s" % (monitor_name, self.instance)
798
799
800 def get_monitor_filenames(self):
801 """
802 Return a list of all monitor filenames (as specified in the VM's
803 params).
804 """
805 return [self.get_monitor_filename(m) for m in
806 kvm_utils.get_sub_dict_names(self.params, "monitors")]
807
808
lmr2b06f332010-06-22 02:03:41 +0000809 def get_serial_console_filename(self):
810 """
811 Return the serial console filename.
812 """
813 return "/tmp/serial-%s" % self.instance
814
815
lmr9e964a02010-06-18 03:46:21 +0000816 def get_testlog_filename(self):
817 """
818 Return the testlog filename.
819 """
820 return "/tmp/testlog-%s" % self.instance
821
822
lmrf4696342009-08-13 04:06:33 +0000823 def get_address(self, index=0):
lmr6f669ce2009-05-31 19:02:42 +0000824 """
lmrf4696342009-08-13 04:06:33 +0000825 Return the address of a NIC of the guest, in host space.
lmr6f669ce2009-05-31 19:02:42 +0000826
lmrf4696342009-08-13 04:06:33 +0000827 If port redirection is used, return 'localhost' (the NIC has no IP
828 address of its own). Otherwise return the NIC's IP address.
829
830 @param index: Index of the NIC whose address is requested.
lmr6f669ce2009-05-31 19:02:42 +0000831 """
lmree90dd92009-08-13 04:13:39 +0000832 nics = kvm_utils.get_sub_dict_names(self.params, "nics")
833 nic_name = nics[index]
lmrf4696342009-08-13 04:06:33 +0000834 nic_params = kvm_utils.get_sub_dict(self.params, nic_name)
835 if nic_params.get("nic_mode") == "tap":
836 mac, ip = kvm_utils.get_mac_ip_pair_from_dict(nic_params)
lmree90dd92009-08-13 04:13:39 +0000837 if not mac:
838 logging.debug("MAC address unavailable")
839 return None
840 if not ip or nic_params.get("always_use_tcpdump") == "yes":
841 # Get the IP address from the cache
842 ip = self.address_cache.get(mac)
843 if not ip:
844 logging.debug("Could not find IP address for MAC address: "
845 "%s" % mac)
846 return None
847 # Make sure the IP address is assigned to this guest
848 nic_dicts = [kvm_utils.get_sub_dict(self.params, nic)
849 for nic in nics]
850 macs = [kvm_utils.get_mac_ip_pair_from_dict(dict)[0]
851 for dict in nic_dicts]
852 if not kvm_utils.verify_ip_address_ownership(ip, macs):
853 logging.debug("Could not verify MAC-IP address mapping: "
854 "%s ---> %s" % (mac, ip))
855 return None
lmrf4696342009-08-13 04:06:33 +0000856 return ip
857 else:
858 return "localhost"
lmr6f669ce2009-05-31 19:02:42 +0000859
860
lmree90dd92009-08-13 04:13:39 +0000861 def get_port(self, port, nic_index=0):
lmr6f669ce2009-05-31 19:02:42 +0000862 """
863 Return the port in host space corresponding to port in guest space.
864
865 @param port: Port number in host space.
lmree90dd92009-08-13 04:13:39 +0000866 @param nic_index: Index of the NIC.
lmr6f669ce2009-05-31 19:02:42 +0000867 @return: If port redirection is used, return the host port redirected
868 to guest port port. Otherwise return port.
869 """
lmree90dd92009-08-13 04:13:39 +0000870 nic_name = kvm_utils.get_sub_dict_names(self.params, "nics")[nic_index]
lmrf4696342009-08-13 04:06:33 +0000871 nic_params = kvm_utils.get_sub_dict(self.params, nic_name)
872 if nic_params.get("nic_mode") == "tap":
873 return port
lmr6f669ce2009-05-31 19:02:42 +0000874 else:
lmrf4696342009-08-13 04:06:33 +0000875 if not self.redirs.has_key(port):
876 logging.warn("Warning: guest port %s requested but not "
877 "redirected" % port)
878 return self.redirs.get(port)
lmr6f669ce2009-05-31 19:02:42 +0000879
880
lmra4967622009-07-23 01:36:32 +0000881 def get_pid(self):
882 """
lmr71fa4de2010-06-14 15:54:55 +0000883 Return the VM's PID. If the VM is dead return None.
884
885 @note: This works under the assumption that self.process.get_pid()
886 returns the PID of the parent shell process.
887 """
888 try:
889 children = commands.getoutput("ps --ppid=%d -o pid=" %
890 self.process.get_pid()).split()
891 return int(children[0])
892 except (TypeError, IndexError, ValueError):
893 return None
894
895
896 def get_shell_pid(self):
897 """
898 Return the PID of the parent shell process.
899
900 @note: This works under the assumption that self.process.get_pid()
901 returns the PID of the parent shell process.
lmra4967622009-07-23 01:36:32 +0000902 """
903 return self.process.get_pid()
904
905
lmr0bee2342010-02-24 00:01:37 +0000906 def get_shared_meminfo(self):
907 """
908 Returns the VM's shared memory information.
909
910 @return: Shared memory used by VM (MB)
911 """
912 if self.is_dead():
913 logging.error("Could not get shared memory info from dead VM.")
914 return None
915
lmr983ecdf2010-06-14 15:57:12 +0000916 filename = "/proc/%d/statm" % self.get_pid()
917 shm = int(open(filename).read().split()[2])
lmr0bee2342010-02-24 00:01:37 +0000918 # statm stores informations in pages, translate it to MB
lmr983ecdf2010-06-14 15:57:12 +0000919 return shm * 4.0 / 1024
lmr0bee2342010-02-24 00:01:37 +0000920
921
lmr912c74b2009-08-17 20:43:27 +0000922 def remote_login(self, nic_index=0, timeout=10):
lmr6f669ce2009-05-31 19:02:42 +0000923 """
lmr912c74b2009-08-17 20:43:27 +0000924 Log into the guest via SSH/Telnet/Netcat.
lmr6f669ce2009-05-31 19:02:42 +0000925 If timeout expires while waiting for output from the guest (e.g. a
926 password prompt or a shell prompt) -- fail.
927
lmree90dd92009-08-13 04:13:39 +0000928 @param nic_index: The index of the NIC to connect to.
lmr6f669ce2009-05-31 19:02:42 +0000929 @param timeout: Time (seconds) before giving up logging into the
930 guest.
931 @return: kvm_spawn object on success and None on failure.
932 """
933 username = self.params.get("username", "")
934 password = self.params.get("password", "")
lmr912c74b2009-08-17 20:43:27 +0000935 prompt = self.params.get("shell_prompt", "[\#\$]")
lmr59f9e2d2009-10-05 18:47:16 +0000936 linesep = eval("'%s'" % self.params.get("shell_linesep", r"\n"))
lmr912c74b2009-08-17 20:43:27 +0000937 client = self.params.get("shell_client")
lmree90dd92009-08-13 04:13:39 +0000938 address = self.get_address(nic_index)
lmr912c74b2009-08-17 20:43:27 +0000939 port = self.get_port(int(self.params.get("shell_port")))
lmre56903f2010-06-22 02:09:35 +0000940 log_filename = ("session-%s-%s.log" %
941 (self.name, kvm_utils.generate_random_string(4)))
lmr912c74b2009-08-17 20:43:27 +0000942
lmree90dd92009-08-13 04:13:39 +0000943 if not address or not port:
944 logging.debug("IP address or port unavailable")
lmr6f669ce2009-05-31 19:02:42 +0000945 return None
946
lmr158604f2010-06-22 01:57:34 +0000947 session = kvm_utils.remote_login(client, address, port, username,
lmre56903f2010-06-22 02:09:35 +0000948 password, prompt, linesep,
949 log_filename, timeout)
lmr912c74b2009-08-17 20:43:27 +0000950
lmr6f669ce2009-05-31 19:02:42 +0000951 if session:
lmr912c74b2009-08-17 20:43:27 +0000952 session.set_status_test_command(self.params.get("status_test_"
lmr6f669ce2009-05-31 19:02:42 +0000953 "command", ""))
954 return session
955
956
lmrc196d492010-05-07 14:14:26 +0000957 def copy_files_to(self, local_path, remote_path, nic_index=0, timeout=600):
lmr6f669ce2009-05-31 19:02:42 +0000958 """
lmr912c74b2009-08-17 20:43:27 +0000959 Transfer files to the guest.
lmr6f669ce2009-05-31 19:02:42 +0000960
961 @param local_path: Host path
962 @param remote_path: Guest path
lmr912c74b2009-08-17 20:43:27 +0000963 @param nic_index: The index of the NIC to connect to.
lmr6f669ce2009-05-31 19:02:42 +0000964 @param timeout: Time (seconds) before giving up on doing the remote
965 copy.
966 """
967 username = self.params.get("username", "")
968 password = self.params.get("password", "")
lmr912c74b2009-08-17 20:43:27 +0000969 client = self.params.get("file_transfer_client")
lmree90dd92009-08-13 04:13:39 +0000970 address = self.get_address(nic_index)
lmr912c74b2009-08-17 20:43:27 +0000971 port = self.get_port(int(self.params.get("file_transfer_port")))
lmre56903f2010-06-22 02:09:35 +0000972 log_filename = ("scp-%s-%s.log" %
973 (self.name, kvm_utils.generate_random_string(4)))
lmr912c74b2009-08-17 20:43:27 +0000974
lmree90dd92009-08-13 04:13:39 +0000975 if not address or not port:
976 logging.debug("IP address or port unavailable")
lmr6f669ce2009-05-31 19:02:42 +0000977 return None
lmr912c74b2009-08-17 20:43:27 +0000978
979 if client == "scp":
980 return kvm_utils.scp_to_remote(address, port, username, password,
lmre56903f2010-06-22 02:09:35 +0000981 local_path, remote_path,
982 log_filename, timeout)
lmr6f669ce2009-05-31 19:02:42 +0000983
984
lmrc196d492010-05-07 14:14:26 +0000985 def copy_files_from(self, remote_path, local_path, nic_index=0, timeout=600):
lmr6f669ce2009-05-31 19:02:42 +0000986 """
lmr912c74b2009-08-17 20:43:27 +0000987 Transfer files from the guest.
lmr6f669ce2009-05-31 19:02:42 +0000988
989 @param local_path: Guest path
990 @param remote_path: Host path
lmr912c74b2009-08-17 20:43:27 +0000991 @param nic_index: The index of the NIC to connect to.
lmr6f669ce2009-05-31 19:02:42 +0000992 @param timeout: Time (seconds) before giving up on doing the remote
993 copy.
994 """
995 username = self.params.get("username", "")
996 password = self.params.get("password", "")
lmr912c74b2009-08-17 20:43:27 +0000997 client = self.params.get("file_transfer_client")
lmree90dd92009-08-13 04:13:39 +0000998 address = self.get_address(nic_index)
lmr912c74b2009-08-17 20:43:27 +0000999 port = self.get_port(int(self.params.get("file_transfer_port")))
lmre56903f2010-06-22 02:09:35 +00001000 log_filename = ("scp-%s-%s.log" %
1001 (self.name, kvm_utils.generate_random_string(4)))
lmr912c74b2009-08-17 20:43:27 +00001002
lmree90dd92009-08-13 04:13:39 +00001003 if not address or not port:
1004 logging.debug("IP address or port unavailable")
lmr6f669ce2009-05-31 19:02:42 +00001005 return None
lmr6f669ce2009-05-31 19:02:42 +00001006
lmr912c74b2009-08-17 20:43:27 +00001007 if client == "scp":
1008 return kvm_utils.scp_from_remote(address, port, username, password,
lmre56903f2010-06-22 02:09:35 +00001009 remote_path, local_path,
1010 log_filename, timeout)
lmr6f669ce2009-05-31 19:02:42 +00001011
1012
lmraa380a22010-06-22 02:05:29 +00001013 def serial_login(self, timeout=10):
1014 """
1015 Log into the guest via the serial console.
1016 If timeout expires while waiting for output from the guest (e.g. a
1017 password prompt or a shell prompt) -- fail.
1018
1019 @param timeout: Time (seconds) before giving up logging into the guest.
1020 @return: kvm_spawn object on success and None on failure.
1021 """
1022 username = self.params.get("username", "")
1023 password = self.params.get("password", "")
1024 prompt = self.params.get("shell_prompt", "[\#\$]")
1025 linesep = eval("'%s'" % self.params.get("shell_linesep", r"\n"))
1026 status_test_command = self.params.get("status_test_command", "")
1027
1028 if self.serial_console:
1029 self.serial_console.set_linesep(linesep)
1030 self.serial_console.set_status_test_command(status_test_command)
1031 else:
1032 return None
1033
1034 # Make sure we get a login prompt
1035 self.serial_console.sendline()
1036
1037 if kvm_utils._remote_login(self.serial_console, username, password,
1038 prompt, timeout):
1039 return self.serial_console
1040
1041
lmr6f669ce2009-05-31 19:02:42 +00001042 def send_key(self, keystr):
1043 """
1044 Send a key event to the VM.
1045
1046 @param: keystr: A key event string (e.g. "ctrl-alt-delete")
1047 """
1048 # For compatibility with versions of QEMU that do not recognize all
1049 # key names: replace keyname with the hex value from the dict, which
1050 # QEMU will definitely accept
lmr9e964a02010-06-18 03:46:21 +00001051 dict = {"comma": "0x33",
1052 "dot": "0x34",
1053 "slash": "0x35"}
1054 for key, value in dict.items():
1055 keystr = keystr.replace(key, value)
1056 self.monitor.sendkey(keystr)
lmr6f669ce2009-05-31 19:02:42 +00001057 time.sleep(0.2)
1058
1059
1060 def send_string(self, str):
1061 """
1062 Send a string to the VM.
1063
1064 @param str: String, that must consist of alphanumeric characters only.
1065 Capital letters are allowed.
1066 """
1067 for char in str:
1068 if char.isupper():
1069 self.send_key("shift-%s" % char.lower())
1070 else:
1071 self.send_key(char)
lmra2533222009-07-20 12:43:46 +00001072
mbligh1ef218d2009-08-03 16:57:56 +00001073
lmra2533222009-07-20 12:43:46 +00001074 def get_uuid(self):
1075 """
1076 Catch UUID of the VM.
1077
1078 @return: None,if not specified in config file
1079 """
1080 if self.params.get("uuid") == "random":
1081 return self.uuid
1082 else:
1083 return self.params.get("uuid", None)
lmrdd2ff922009-12-01 23:39:12 +00001084
1085
1086 def get_cpu_count(self):
1087 """
1088 Get the cpu count of the VM.
1089 """
lmr13426552010-01-17 15:38:41 +00001090 session = self.remote_login()
1091 if not session:
1092 return None
lmrdd2ff922009-12-01 23:39:12 +00001093 try:
lmr13426552010-01-17 15:38:41 +00001094 cmd = self.params.get("cpu_chk_cmd")
1095 s, count = session.get_command_status_output(cmd)
1096 if s == 0:
1097 return int(count)
lmrdd2ff922009-12-01 23:39:12 +00001098 return None
1099 finally:
1100 session.close()
1101
1102
lmr28426c82010-04-16 06:02:58 +00001103 def get_memory_size(self, cmd=None):
lmrdd2ff922009-12-01 23:39:12 +00001104 """
lmr28426c82010-04-16 06:02:58 +00001105 Get bootup memory size of the VM.
1106
1107 @param check_cmd: Command used to check memory. If not provided,
1108 self.params.get("mem_chk_cmd") will be used.
lmrdd2ff922009-12-01 23:39:12 +00001109 """
lmr13426552010-01-17 15:38:41 +00001110 session = self.remote_login()
1111 if not session:
lmrdd2ff922009-12-01 23:39:12 +00001112 return None
lmr13426552010-01-17 15:38:41 +00001113 try:
lmr28426c82010-04-16 06:02:58 +00001114 if not cmd:
1115 cmd = self.params.get("mem_chk_cmd")
lmr13426552010-01-17 15:38:41 +00001116 s, mem_str = session.get_command_status_output(cmd)
1117 if s != 0:
1118 return None
lmr6d69f4d2010-02-12 11:35:55 +00001119 mem = re.findall("([0-9]+)", mem_str)
lmr13426552010-01-17 15:38:41 +00001120 mem_size = 0
1121 for m in mem:
1122 mem_size += int(m)
1123 if "GB" in mem_str:
1124 mem_size *= 1024
1125 elif "MB" in mem_str:
1126 pass
1127 else:
1128 mem_size /= 1024
1129 return int(mem_size)
lmrdd2ff922009-12-01 23:39:12 +00001130 finally:
1131 session.close()
lmr28426c82010-04-16 06:02:58 +00001132
1133
1134 def get_current_memory_size(self):
1135 """
1136 Get current memory size of the VM, rather than bootup memory.
1137 """
1138 cmd = self.params.get("mem_chk_cur_cmd")
1139 return self.get_memory_size(cmd)