blob: ff69aedddc12cf5c9ecc7846a9da006c2bda58b2 [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 = []
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,
lmrb1cad1e2010-06-17 17:36:09 +0000244 downscript=None, netdev_id=None):
245 if has_option(help, "netdev"):
246 cmd = " -netdev %s,id=%s" % (mode, netdev_id)
247 else:
248 cmd = " -net %s,vlan=%d" % (mode, vlan)
lmr48abd7d2010-05-26 13:48:04 +0000249 if mode == "tap":
lmr09a78162010-06-14 16:29:23 +0000250 if ifname: cmd += ",ifname='%s'" % ifname
251 if script: cmd += ",script='%s'" % script
252 cmd += ",downscript='%s'" % (downscript or "no")
lmr48abd7d2010-05-26 13:48:04 +0000253 return cmd
254
255 def add_floppy(help, filename):
lmr09a78162010-06-14 16:29:23 +0000256 return " -fda '%s'" % filename
lmr48abd7d2010-05-26 13:48:04 +0000257
258 def add_tftp(help, filename):
lmr09a78162010-06-14 16:29:23 +0000259 return " -tftp '%s'" % filename
lmr48abd7d2010-05-26 13:48:04 +0000260
261 def add_tcp_redir(help, host_port, guest_port):
262 return " -redir tcp:%s::%s" % (host_port, guest_port)
263
264 def add_vnc(help, vnc_port):
265 return " -vnc :%d" % (vnc_port - 5900)
266
267 def add_sdl(help):
268 if has_option(help, "sdl"):
269 return " -sdl"
270 else:
271 return ""
272
273 def add_nographic(help):
274 return " -nographic"
275
276 def add_uuid(help, uuid):
lmr09a78162010-06-14 16:29:23 +0000277 return " -uuid '%s'" % uuid
lmr48abd7d2010-05-26 13:48:04 +0000278
279 def add_pcidevice(help, host):
lmr09a78162010-06-14 16:29:23 +0000280 return " -pcidevice host='%s'" % host
lmr48abd7d2010-05-26 13:48:04 +0000281
lmr41cb8fc2010-06-10 15:30:45 +0000282 def add_kernel(help, filename):
lmr09a78162010-06-14 16:29:23 +0000283 return " -kernel '%s'" % filename
lmr41cb8fc2010-06-10 15:30:45 +0000284
285 def add_initrd(help, filename):
lmr09a78162010-06-14 16:29:23 +0000286 return " -initrd '%s'" % filename
lmr41cb8fc2010-06-10 15:30:45 +0000287
lmre0474e32010-06-29 14:10:09 +0000288 def add_kernel_cmdline(help, cmdline):
289 return " -append %s" % cmdline
290
lmr71024552010-06-29 14:12:35 +0000291 def add_testdev(help, filename):
292 return (" -chardev file,id=testlog,path=%s"
293 " -device testdev,chardev=testlog" % filename)
294
lmr48abd7d2010-05-26 13:48:04 +0000295 # End of command line option wrappers
296
297 if name is None: name = self.name
298 if params is None: params = self.params
299 if root_dir is None: root_dir = self.root_dir
300
301 qemu_binary = kvm_utils.get_path(root_dir, params.get("qemu_binary",
302 "qemu"))
303 # Get the output of 'qemu -help' (log a message in case this call never
304 # returns or causes some other kind of trouble)
305 logging.debug("Getting output of 'qemu -help'")
306 help = commands.getoutput("%s -help" % qemu_binary)
lmr6f669ce2009-05-31 19:02:42 +0000307
lmreeff0eb2009-06-10 19:19:15 +0000308 # Start constructing the qemu command
309 qemu_cmd = ""
310 # Set the X11 display parameter if requested
311 if params.get("x11_display"):
312 qemu_cmd += "DISPLAY=%s " % params.get("x11_display")
313 # Add the qemu binary
lmr48abd7d2010-05-26 13:48:04 +0000314 qemu_cmd += qemu_binary
lmreeff0eb2009-06-10 19:19:15 +0000315 # Add the VM's name
lmr48abd7d2010-05-26 13:48:04 +0000316 qemu_cmd += add_name(help, name)
lmr9e964a02010-06-18 03:46:21 +0000317 # Add monitors
318 for monitor_name in kvm_utils.get_sub_dict_names(params, "monitors"):
319 monitor_params = kvm_utils.get_sub_dict(params, monitor_name)
320 monitor_filename = self.get_monitor_filename(monitor_name)
321 if monitor_params.get("monitor_type") == "qmp":
322 qemu_cmd += add_qmp_monitor(help, monitor_filename)
323 else:
324 qemu_cmd += add_human_monitor(help, monitor_filename)
lmr6f669ce2009-05-31 19:02:42 +0000325
lmr2b06f332010-06-22 02:03:41 +0000326 # Add serial console redirection
327 qemu_cmd += add_serial(help, self.get_serial_console_filename())
328
lmr6f669ce2009-05-31 19:02:42 +0000329 for image_name in kvm_utils.get_sub_dict_names(params, "images"):
330 image_params = kvm_utils.get_sub_dict(params, image_name)
lmr9d75ee32009-09-29 13:14:13 +0000331 if image_params.get("boot_drive") == "no":
332 continue
lmr48abd7d2010-05-26 13:48:04 +0000333 qemu_cmd += add_drive(help,
334 get_image_filename(image_params, root_dir),
335 image_params.get("drive_format"),
336 image_params.get("drive_cache"),
337 image_params.get("drive_werror"),
338 image_params.get("drive_serial"),
339 image_params.get("image_snapshot") == "yes",
340 image_params.get("image_boot") == "yes")
lmr6f669ce2009-05-31 19:02:42 +0000341
342 vlan = 0
343 for nic_name in kvm_utils.get_sub_dict_names(params, "nics"):
344 nic_params = kvm_utils.get_sub_dict(params, nic_name)
lmrf4696342009-08-13 04:06:33 +0000345 # Handle the '-net nic' part
lmr48abd7d2010-05-26 13:48:04 +0000346 mac = None
347 if "address_index" in nic_params:
348 mac = kvm_utils.get_mac_ip_pair_from_dict(nic_params)[0]
lmrb1cad1e2010-06-17 17:36:09 +0000349 qemu_cmd += add_nic(help, vlan, nic_params.get("nic_model"), mac,
350 self.netdev_id[vlan])
lmrf4696342009-08-13 04:06:33 +0000351 # Handle the '-net tap' or '-net user' part
lmre4eaa862010-06-01 19:15:14 +0000352 script = nic_params.get("nic_script")
353 downscript = nic_params.get("nic_downscript")
lmr48abd7d2010-05-26 13:48:04 +0000354 if script:
355 script = kvm_utils.get_path(root_dir, script)
356 if downscript:
357 downscript = kvm_utils.get_path(root_dir, downscript)
358 qemu_cmd += add_net(help, vlan, nic_params.get("nic_mode", "user"),
359 nic_params.get("nic_ifname"),
lmrb1cad1e2010-06-17 17:36:09 +0000360 script, downscript, self.netdev_id[vlan])
lmrf4696342009-08-13 04:06:33 +0000361 # Proceed to next NIC
lmr6f669ce2009-05-31 19:02:42 +0000362 vlan += 1
363
364 mem = params.get("mem")
365 if mem:
lmr48abd7d2010-05-26 13:48:04 +0000366 qemu_cmd += add_mem(help, mem)
lmr6f669ce2009-05-31 19:02:42 +0000367
lmrc43bf372009-11-10 13:19:00 +0000368 smp = params.get("smp")
369 if smp:
lmrdb3fe612010-06-14 16:19:28 +0000370 qemu_cmd += add_smp(help, smp)
lmrc43bf372009-11-10 13:19:00 +0000371
lmr6f669ce2009-05-31 19:02:42 +0000372 iso = params.get("cdrom")
373 if iso:
lmr90b9fd52009-08-17 20:48:18 +0000374 iso = kvm_utils.get_path(root_dir, iso)
lmr48abd7d2010-05-26 13:48:04 +0000375 qemu_cmd += add_cdrom(help, iso)
lmr89fd6412010-01-18 02:42:57 +0000376
377 # Even though this is not a really scalable approach,
378 # it doesn't seem like we are going to need more than
379 # 2 CDs active on the same VM.
380 iso_extra = params.get("cdrom_extra")
381 if iso_extra:
382 iso_extra = kvm_utils.get_path(root_dir, iso_extra)
lmr48abd7d2010-05-26 13:48:04 +0000383 qemu_cmd += add_cdrom(help, iso_extra, 3)
lmr6f669ce2009-05-31 19:02:42 +0000384
lmrb0a9b762009-10-09 20:43:30 +0000385 # We may want to add {floppy_otps} parameter for -fda
lmr48abd7d2010-05-26 13:48:04 +0000386 # {fat:floppy:}/path/. However vvfat is not usually recommended.
lmrb0a9b762009-10-09 20:43:30 +0000387 floppy = params.get("floppy")
388 if floppy:
lmrf69f6b12009-11-10 16:33:44 +0000389 floppy = kvm_utils.get_path(root_dir, floppy)
lmr48abd7d2010-05-26 13:48:04 +0000390 qemu_cmd += add_floppy(help, floppy)
lmrb0a9b762009-10-09 20:43:30 +0000391
392 tftp = params.get("tftp")
393 if tftp:
lmrf69f6b12009-11-10 16:33:44 +0000394 tftp = kvm_utils.get_path(root_dir, tftp)
lmr48abd7d2010-05-26 13:48:04 +0000395 qemu_cmd += add_tftp(help, tftp)
lmr6f669ce2009-05-31 19:02:42 +0000396
lmr41cb8fc2010-06-10 15:30:45 +0000397 kernel = params.get("kernel")
398 if kernel:
399 kernel = kvm_utils.get_path(root_dir, kernel)
400 qemu_cmd += add_kernel(help, kernel)
401
lmre0474e32010-06-29 14:10:09 +0000402 kernel_cmdline = params.get("kernel_cmdline")
403 if kernel_cmdline:
404 qemu_cmd += add_kernel_cmdline(help, kernel_cmdline)
405
lmr41cb8fc2010-06-10 15:30:45 +0000406 initrd = params.get("initrd")
407 if initrd:
408 initrd = kvm_utils.get_path(root_dir, initrd)
409 qemu_cmd += add_initrd(help, initrd)
410
lmr6f669ce2009-05-31 19:02:42 +0000411 for redir_name in kvm_utils.get_sub_dict_names(params, "redirs"):
412 redir_params = kvm_utils.get_sub_dict(params, redir_name)
413 guest_port = int(redir_params.get("guest_port"))
lmrf4696342009-08-13 04:06:33 +0000414 host_port = self.redirs.get(guest_port)
lmr48abd7d2010-05-26 13:48:04 +0000415 qemu_cmd += add_tcp_redir(help, host_port, guest_port)
lmr6f669ce2009-05-31 19:02:42 +0000416
417 if params.get("display") == "vnc":
lmr48abd7d2010-05-26 13:48:04 +0000418 qemu_cmd += add_vnc(help, self.vnc_port)
lmr6f669ce2009-05-31 19:02:42 +0000419 elif params.get("display") == "sdl":
lmr48abd7d2010-05-26 13:48:04 +0000420 qemu_cmd += add_sdl(help)
lmr6f669ce2009-05-31 19:02:42 +0000421 elif params.get("display") == "nographic":
lmr48abd7d2010-05-26 13:48:04 +0000422 qemu_cmd += add_nographic(help)
lmr6f669ce2009-05-31 19:02:42 +0000423
lmra2533222009-07-20 12:43:46 +0000424 if params.get("uuid") == "random":
lmr48abd7d2010-05-26 13:48:04 +0000425 qemu_cmd += add_uuid(help, self.uuid)
lmra2533222009-07-20 12:43:46 +0000426 elif params.get("uuid"):
lmr48abd7d2010-05-26 13:48:04 +0000427 qemu_cmd += add_uuid(help, params.get("uuid"))
lmra2533222009-07-20 12:43:46 +0000428
lmr71024552010-06-29 14:12:35 +0000429 if params.get("testdev") == "yes":
430 qemu_cmd += add_testdev(help, self.get_testlog_filename())
431
lmr31af3a12010-01-18 16:46:52 +0000432 # If the PCI assignment step went OK, add each one of the PCI assigned
433 # devices to the qemu command line.
434 if self.pci_assignable:
435 for pci_id in self.pa_pci_ids:
lmr48abd7d2010-05-26 13:48:04 +0000436 qemu_cmd += add_pcidevice(help, pci_id)
437
438 extra_params = params.get("extra_params")
439 if extra_params:
440 qemu_cmd += " %s" % extra_params
lmr31af3a12010-01-18 16:46:52 +0000441
lmr6f669ce2009-05-31 19:02:42 +0000442 return qemu_cmd
443
444
lmr52800ba2009-08-17 20:49:58 +0000445 def create(self, name=None, params=None, root_dir=None,
lmr1424f3e2010-06-17 13:57:09 +0000446 for_migration=False, timeout=5.0, extra_params=None):
lmr6f669ce2009-05-31 19:02:42 +0000447 """
448 Start the VM by running a qemu command.
449 All parameters are optional. The following applies to all parameters
450 but for_migration: If a parameter is not supplied, the corresponding
451 value stored in the class attributes is used, and if it is supplied,
452 it is stored for later use.
453
454 @param name: The name of the object
455 @param params: A dict containing VM params
lmr90b9fd52009-08-17 20:48:18 +0000456 @param root_dir: Base directory for relative filenames
lmr6f669ce2009-05-31 19:02:42 +0000457 @param for_migration: If True, start the VM with the -incoming
458 option
lmr1424f3e2010-06-17 13:57:09 +0000459 @param extra_params: extra params for qemu command.e.g -incoming option
460 Please use this parameter instead of for_migration.
lmr6f669ce2009-05-31 19:02:42 +0000461 """
lmr135b5e62009-06-10 19:22:31 +0000462 self.destroy()
463
lmre45a1f22009-11-10 16:35:08 +0000464 if name is not None:
lmr6f669ce2009-05-31 19:02:42 +0000465 self.name = name
lmre45a1f22009-11-10 16:35:08 +0000466 if params is not None:
lmr6f669ce2009-05-31 19:02:42 +0000467 self.params = params
lmre45a1f22009-11-10 16:35:08 +0000468 if root_dir is not None:
lmr90b9fd52009-08-17 20:48:18 +0000469 self.root_dir = root_dir
lmr6f669ce2009-05-31 19:02:42 +0000470 name = self.name
471 params = self.params
lmr90b9fd52009-08-17 20:48:18 +0000472 root_dir = self.root_dir
lmr6f669ce2009-05-31 19:02:42 +0000473
474 # Verify the md5sum of the ISO image
475 iso = params.get("cdrom")
476 if iso:
lmr90b9fd52009-08-17 20:48:18 +0000477 iso = kvm_utils.get_path(root_dir, iso)
lmr6f669ce2009-05-31 19:02:42 +0000478 if not os.path.exists(iso):
479 logging.error("ISO file not found: %s" % iso)
480 return False
481 compare = False
482 if params.get("md5sum_1m"):
lmrf4696342009-08-13 04:06:33 +0000483 logging.debug("Comparing expected MD5 sum with MD5 sum of "
484 "first MB of ISO file...")
lmrd60882f2010-02-04 03:26:36 +0000485 actual_hash = utils.hash_file(iso, 1048576, method="md5")
lmr03ba22e2009-10-23 12:07:44 +0000486 expected_hash = params.get("md5sum_1m")
lmr6f669ce2009-05-31 19:02:42 +0000487 compare = True
488 elif params.get("md5sum"):
lmrf4696342009-08-13 04:06:33 +0000489 logging.debug("Comparing expected MD5 sum with MD5 sum of ISO "
490 "file...")
lmrd60882f2010-02-04 03:26:36 +0000491 actual_hash = utils.hash_file(iso, method="md5")
lmr03ba22e2009-10-23 12:07:44 +0000492 expected_hash = params.get("md5sum")
493 compare = True
494 elif params.get("sha1sum"):
495 logging.debug("Comparing expected SHA1 sum with SHA1 sum of "
496 "ISO file...")
lmrd60882f2010-02-04 03:26:36 +0000497 actual_hash = utils.hash_file(iso, method="sha1")
lmrea1266e2009-11-10 16:41:08 +0000498 expected_hash = params.get("sha1sum")
lmr6f669ce2009-05-31 19:02:42 +0000499 compare = True
500 if compare:
lmr03ba22e2009-10-23 12:07:44 +0000501 if actual_hash == expected_hash:
502 logging.debug("Hashes match")
lmr6f669ce2009-05-31 19:02:42 +0000503 else:
lmr03ba22e2009-10-23 12:07:44 +0000504 logging.error("Actual hash differs from expected one")
lmr6f669ce2009-05-31 19:02:42 +0000505 return False
506
lmrdc2ac6a2009-06-10 19:15:49 +0000507 # Make sure the following code is not executed by more than one thread
508 # at the same time
509 lockfile = open("/tmp/kvm-autotest-vm-create.lock", "w+")
510 fcntl.lockf(lockfile, fcntl.LOCK_EX)
lmr6f669ce2009-05-31 19:02:42 +0000511
lmrdc2ac6a2009-06-10 19:15:49 +0000512 try:
513 # Handle port redirections
514 redir_names = kvm_utils.get_sub_dict_names(params, "redirs")
515 host_ports = kvm_utils.find_free_ports(5000, 6000, len(redir_names))
516 self.redirs = {}
517 for i in range(len(redir_names)):
518 redir_params = kvm_utils.get_sub_dict(params, redir_names[i])
519 guest_port = int(redir_params.get("guest_port"))
520 self.redirs[guest_port] = host_ports[i]
lmr6f669ce2009-05-31 19:02:42 +0000521
lmr48349072010-06-29 14:14:55 +0000522 for nic in kvm_utils.get_sub_dict_names(params, "nics"):
523 self.netdev_id.append(kvm_utils.generate_random_id())
524
lmrdc2ac6a2009-06-10 19:15:49 +0000525 # Find available VNC port, if needed
526 if params.get("display") == "vnc":
lmrc0da8902010-05-17 20:31:36 +0000527 self.vnc_port = kvm_utils.find_free_port(5900, 6100)
lmr6f669ce2009-05-31 19:02:42 +0000528
lmra2533222009-07-20 12:43:46 +0000529 # Find random UUID if specified 'uuid = random' in config file
530 if params.get("uuid") == "random":
531 f = open("/proc/sys/kernel/random/uuid")
532 self.uuid = f.read().strip()
533 f.close()
534
lmr31ed61d2010-06-07 13:21:38 +0000535 # Assign a PCI assignable device
536 self.pci_assignable = None
537 pa_type = params.get("pci_assignable")
538 if pa_type in ["vf", "pf", "mixed"]:
lmr31af3a12010-01-18 16:46:52 +0000539 pa_devices_requested = params.get("devices_requested")
540
541 # Virtual Functions (VF) assignable devices
542 if pa_type == "vf":
lmr31ed61d2010-06-07 13:21:38 +0000543 self.pci_assignable = kvm_utils.PciAssignable(
544 type=pa_type,
545 driver=params.get("driver"),
546 driver_option=params.get("driver_option"),
547 devices_requested=pa_devices_requested)
lmr31af3a12010-01-18 16:46:52 +0000548 # Physical NIC (PF) assignable devices
549 elif pa_type == "pf":
lmr31ed61d2010-06-07 13:21:38 +0000550 self.pci_assignable = kvm_utils.PciAssignable(
551 type=pa_type,
552 names=params.get("device_names"),
553 devices_requested=pa_devices_requested)
lmr31af3a12010-01-18 16:46:52 +0000554 # Working with both VF and PF
555 elif pa_type == "mixed":
lmr31ed61d2010-06-07 13:21:38 +0000556 self.pci_assignable = kvm_utils.PciAssignable(
557 type=pa_type,
558 driver=params.get("driver"),
559 driver_option=params.get("driver_option"),
560 names=params.get("device_names"),
561 devices_requested=pa_devices_requested)
lmr31af3a12010-01-18 16:46:52 +0000562
563 self.pa_pci_ids = self.pci_assignable.request_devs()
564
565 if self.pa_pci_ids:
lmr31ed61d2010-06-07 13:21:38 +0000566 logging.debug("Successfuly assigned devices: %s",
lmr31af3a12010-01-18 16:46:52 +0000567 self.pa_pci_ids)
568 else:
569 logging.error("No PCI assignable devices were assigned "
570 "and 'pci_assignable' is defined to %s "
lmr31ed61d2010-06-07 13:21:38 +0000571 "on your config file. Aborting VM creation.",
lmr31af3a12010-01-18 16:46:52 +0000572 pa_type)
573 return False
574
lmr856d58c2010-06-08 18:29:31 +0000575 elif pa_type and pa_type != "no":
lmr31ed61d2010-06-07 13:21:38 +0000576 logging.warn("Unsupported pci_assignable type: %s", pa_type)
lmr31af3a12010-01-18 16:46:52 +0000577
lmrdc2ac6a2009-06-10 19:15:49 +0000578 # Make qemu command
579 qemu_command = self.make_qemu_command()
lmr6f669ce2009-05-31 19:02:42 +0000580
lmr1424f3e2010-06-17 13:57:09 +0000581 # Enable migration support for VM by adding extra_params.
582 if extra_params is not None:
583 if " -incoming tcp:0:%d" == extra_params:
584 self.migration_port = kvm_utils.find_free_port(5200, 6000)
585 qemu_command += extra_params % self.migration_port
586 elif " -incoming unix:%s" == extra_params:
587 self.migration_file = os.path.join("/tmp/", "unix-" +
588 time.strftime("%Y%m%d-%H%M%S"))
589 qemu_command += extra_params % self.migration_file
590 else:
591 qemu_command += extra_params
lmr6f669ce2009-05-31 19:02:42 +0000592
lmrdc2ac6a2009-06-10 19:15:49 +0000593 logging.debug("Running qemu command:\n%s", qemu_command)
lmra4967622009-07-23 01:36:32 +0000594 self.process = kvm_subprocess.run_bg(qemu_command, None,
595 logging.debug, "(qemu) ")
lmr6f669ce2009-05-31 19:02:42 +0000596
lmr9e964a02010-06-18 03:46:21 +0000597 # Make sure the process was started successfully
lmra4967622009-07-23 01:36:32 +0000598 if not self.process.is_alive():
599 logging.error("VM could not be created; "
600 "qemu command failed:\n%s" % qemu_command)
601 logging.error("Status: %s" % self.process.get_status())
602 logging.error("Output:" + kvm_utils.format_str_for_message(
603 self.process.get_output()))
lmrdc2ac6a2009-06-10 19:15:49 +0000604 self.destroy()
605 return False
lmr6f669ce2009-05-31 19:02:42 +0000606
lmr9e964a02010-06-18 03:46:21 +0000607 # Establish monitor connections
608 self.monitors = []
609 for monitor_name in kvm_utils.get_sub_dict_names(params,
610 "monitors"):
611 monitor_params = kvm_utils.get_sub_dict(params, monitor_name)
612 # Wait for monitor connection to succeed
613 end_time = time.time() + timeout
614 while time.time() < end_time:
615 try:
616 if monitor_params.get("monitor_type") == "qmp":
lmr449d2252010-06-18 03:48:23 +0000617 # Add a QMP monitor
618 monitor = kvm_monitor.QMPMonitor(
619 monitor_name,
620 self.get_monitor_filename(monitor_name))
lmr9e964a02010-06-18 03:46:21 +0000621 else:
622 # Add a "human" monitor
623 monitor = kvm_monitor.HumanMonitor(
624 monitor_name,
625 self.get_monitor_filename(monitor_name))
626 except kvm_monitor.MonitorError, e:
627 logging.warn(e)
628 else:
lmr449d2252010-06-18 03:48:23 +0000629 if monitor.is_responsive():
lmr9e964a02010-06-18 03:46:21 +0000630 break
631 time.sleep(1)
632 else:
633 logging.error("Could not connect to monitor '%s'" %
634 monitor_name)
635 self.destroy()
636 return False
637 # Add this monitor to the list
638 self.monitors += [monitor]
lmra4967622009-07-23 01:36:32 +0000639
lmrfe6515e2009-07-29 13:01:17 +0000640 # Get the output so far, to see if we have any problems with
641 # hugepage setup.
642 output = self.process.get_output()
643
644 if "alloc_mem_area" in output:
645 logging.error("Could not allocate hugepage memory; "
646 "qemu command:\n%s" % qemu_command)
647 logging.error("Output:" + kvm_utils.format_str_for_message(
648 self.process.get_output()))
lmr090cd7a2010-04-01 02:18:52 +0000649 self.destroy()
lmrfe6515e2009-07-29 13:01:17 +0000650 return False
651
lmr71fa4de2010-06-14 15:54:55 +0000652 logging.debug("VM appears to be alive with PID %s", self.get_pid())
lmraa380a22010-06-22 02:05:29 +0000653
654 # Establish a session with the serial console -- requires a version
655 # of netcat that supports -U
656 self.serial_console = kvm_subprocess.kvm_shell_session(
657 "nc -U %s" % self.get_serial_console_filename(),
658 auto_close=False,
659 output_func=kvm_utils.log_line,
660 output_params=("serial-%s.log" % name,))
661
lmrdc2ac6a2009-06-10 19:15:49 +0000662 return True
663
664 finally:
665 fcntl.lockf(lockfile, fcntl.LOCK_UN)
666 lockfile.close()
lmr6f669ce2009-05-31 19:02:42 +0000667
668
lmr6f669ce2009-05-31 19:02:42 +0000669 def destroy(self, gracefully=True):
670 """
671 Destroy the VM.
672
lmr912c74b2009-08-17 20:43:27 +0000673 If gracefully is True, first attempt to shutdown the VM with a shell
674 command. Then, attempt to destroy the VM via the monitor with a 'quit'
675 command. If that fails, send SIGKILL to the qemu process.
lmr6f669ce2009-05-31 19:02:42 +0000676
677 @param gracefully: Whether an attempt will be made to end the VM
lmr912c74b2009-08-17 20:43:27 +0000678 using a shell command before trying to end the qemu process
679 with a 'quit' or a kill signal.
lmr6f669ce2009-05-31 19:02:42 +0000680 """
lmrf320b042009-09-15 05:48:06 +0000681 try:
682 # Is it already dead?
683 if self.is_dead():
684 logging.debug("VM is already down")
685 return
lmr6f669ce2009-05-31 19:02:42 +0000686
lmr71fa4de2010-06-14 15:54:55 +0000687 logging.debug("Destroying VM with PID %s...", self.get_pid())
lmr6f669ce2009-05-31 19:02:42 +0000688
lmrf320b042009-09-15 05:48:06 +0000689 if gracefully and self.params.get("shutdown_command"):
690 # Try to destroy with shell command
691 logging.debug("Trying to shutdown VM with shell command...")
692 session = self.remote_login()
693 if session:
694 try:
695 # Send the shutdown command
696 session.sendline(self.params.get("shutdown_command"))
697 logging.debug("Shutdown command sent; waiting for VM "
698 "to go down...")
699 if kvm_utils.wait_for(self.is_dead, 60, 1, 1):
700 logging.debug("VM is down")
701 return
702 finally:
703 session.close()
lmr6f669ce2009-05-31 19:02:42 +0000704
lmr9e964a02010-06-18 03:46:21 +0000705 if self.monitor:
706 # Try to destroy with a monitor command
707 logging.debug("Trying to kill VM with monitor command...")
708 try:
709 self.monitor.quit()
710 except kvm_monitor.MonitorError, e:
711 logging.warn(e)
712 else:
713 # Wait for the VM to be really dead
714 if kvm_utils.wait_for(self.is_dead, 5, 0.5, 0.5):
715 logging.debug("VM is down")
716 return
lmrf320b042009-09-15 05:48:06 +0000717
718 # If the VM isn't dead yet...
719 logging.debug("Cannot quit normally; sending a kill to close the "
720 "deal...")
721 kvm_utils.kill_process_tree(self.process.get_pid(), 9)
lmr6f669ce2009-05-31 19:02:42 +0000722 # Wait for the VM to be really dead
723 if kvm_utils.wait_for(self.is_dead, 5, 0.5, 0.5):
724 logging.debug("VM is down")
lmr6f669ce2009-05-31 19:02:42 +0000725 return
726
lmrf320b042009-09-15 05:48:06 +0000727 logging.error("Process %s is a zombie!" % self.process.get_pid())
lmr6f669ce2009-05-31 19:02:42 +0000728
lmrf320b042009-09-15 05:48:06 +0000729 finally:
lmr9e964a02010-06-18 03:46:21 +0000730 self.monitors = []
lmr4513c432010-02-03 11:59:03 +0000731 if self.pci_assignable:
732 self.pci_assignable.release_devs()
lmrf320b042009-09-15 05:48:06 +0000733 if self.process:
734 self.process.close()
lmraa380a22010-06-22 02:05:29 +0000735 if self.serial_console:
736 self.serial_console.close()
lmr9e964a02010-06-18 03:46:21 +0000737 for f in ([self.get_testlog_filename()] +
738 self.get_monitor_filenames()):
739 try:
740 os.unlink(f)
741 except OSError:
742 pass
743
744
745 @property
746 def monitor(self):
747 """
748 Return the main monitor object, selected by the parameter main_monitor.
749 If main_monitor isn't defined, return the first monitor.
750 If no monitors exist, or if main_monitor refers to a nonexistent
751 monitor, return None.
752 """
753 for m in self.monitors:
754 if m.name == self.params.get("main_monitor"):
755 return m
756 if self.monitors and not self.params.get("main_monitor"):
757 return self.monitors[0]
lmr6f669ce2009-05-31 19:02:42 +0000758
759
760 def is_alive(self):
761 """
lmr9e964a02010-06-18 03:46:21 +0000762 Return True if the VM is alive and its monitor is responsive.
lmr6f669ce2009-05-31 19:02:42 +0000763 """
lmra4967622009-07-23 01:36:32 +0000764 # Check if the process is running
765 if self.is_dead():
lmr6f669ce2009-05-31 19:02:42 +0000766 return False
767 # Try sending a monitor command
lmr9e964a02010-06-18 03:46:21 +0000768 return bool(self.monitor) and self.monitor.is_responsive()
lmr6f669ce2009-05-31 19:02:42 +0000769
770
771 def is_dead(self):
772 """
lmra4967622009-07-23 01:36:32 +0000773 Return True if the qemu process is dead.
lmr6f669ce2009-05-31 19:02:42 +0000774 """
lmra4967622009-07-23 01:36:32 +0000775 return not self.process or not self.process.is_alive()
lmr6f669ce2009-05-31 19:02:42 +0000776
777
lmra4197002009-08-13 05:00:51 +0000778 def kill_tail_thread(self):
779 """
780 Stop the tailing thread which reports the output of qemu.
781 """
782 if self.process:
783 self.process.kill_tail_thread()
784
785
lmr6f669ce2009-05-31 19:02:42 +0000786 def get_params(self):
787 """
788 Return the VM's params dict. Most modified params take effect only
789 upon VM.create().
790 """
791 return self.params
792
793
lmr9e964a02010-06-18 03:46:21 +0000794 def get_monitor_filename(self, monitor_name):
795 """
796 Return the filename corresponding to a given monitor name.
797 """
798 return "/tmp/monitor-%s-%s" % (monitor_name, self.instance)
799
800
801 def get_monitor_filenames(self):
802 """
803 Return a list of all monitor filenames (as specified in the VM's
804 params).
805 """
806 return [self.get_monitor_filename(m) for m in
807 kvm_utils.get_sub_dict_names(self.params, "monitors")]
808
809
lmr2b06f332010-06-22 02:03:41 +0000810 def get_serial_console_filename(self):
811 """
812 Return the serial console filename.
813 """
814 return "/tmp/serial-%s" % self.instance
815
816
lmr9e964a02010-06-18 03:46:21 +0000817 def get_testlog_filename(self):
818 """
819 Return the testlog filename.
820 """
821 return "/tmp/testlog-%s" % self.instance
822
823
lmrf4696342009-08-13 04:06:33 +0000824 def get_address(self, index=0):
lmr6f669ce2009-05-31 19:02:42 +0000825 """
lmrf4696342009-08-13 04:06:33 +0000826 Return the address of a NIC of the guest, in host space.
lmr6f669ce2009-05-31 19:02:42 +0000827
lmrf4696342009-08-13 04:06:33 +0000828 If port redirection is used, return 'localhost' (the NIC has no IP
829 address of its own). Otherwise return the NIC's IP address.
830
831 @param index: Index of the NIC whose address is requested.
lmr6f669ce2009-05-31 19:02:42 +0000832 """
lmree90dd92009-08-13 04:13:39 +0000833 nics = kvm_utils.get_sub_dict_names(self.params, "nics")
834 nic_name = nics[index]
lmrf4696342009-08-13 04:06:33 +0000835 nic_params = kvm_utils.get_sub_dict(self.params, nic_name)
836 if nic_params.get("nic_mode") == "tap":
837 mac, ip = kvm_utils.get_mac_ip_pair_from_dict(nic_params)
lmree90dd92009-08-13 04:13:39 +0000838 if not mac:
839 logging.debug("MAC address unavailable")
840 return None
841 if not ip or nic_params.get("always_use_tcpdump") == "yes":
842 # Get the IP address from the cache
843 ip = self.address_cache.get(mac)
844 if not ip:
845 logging.debug("Could not find IP address for MAC address: "
846 "%s" % mac)
847 return None
848 # Make sure the IP address is assigned to this guest
849 nic_dicts = [kvm_utils.get_sub_dict(self.params, nic)
850 for nic in nics]
851 macs = [kvm_utils.get_mac_ip_pair_from_dict(dict)[0]
852 for dict in nic_dicts]
853 if not kvm_utils.verify_ip_address_ownership(ip, macs):
854 logging.debug("Could not verify MAC-IP address mapping: "
855 "%s ---> %s" % (mac, ip))
856 return None
lmrf4696342009-08-13 04:06:33 +0000857 return ip
858 else:
859 return "localhost"
lmr6f669ce2009-05-31 19:02:42 +0000860
861
lmree90dd92009-08-13 04:13:39 +0000862 def get_port(self, port, nic_index=0):
lmr6f669ce2009-05-31 19:02:42 +0000863 """
864 Return the port in host space corresponding to port in guest space.
865
866 @param port: Port number in host space.
lmree90dd92009-08-13 04:13:39 +0000867 @param nic_index: Index of the NIC.
lmr6f669ce2009-05-31 19:02:42 +0000868 @return: If port redirection is used, return the host port redirected
869 to guest port port. Otherwise return port.
870 """
lmree90dd92009-08-13 04:13:39 +0000871 nic_name = kvm_utils.get_sub_dict_names(self.params, "nics")[nic_index]
lmrf4696342009-08-13 04:06:33 +0000872 nic_params = kvm_utils.get_sub_dict(self.params, nic_name)
873 if nic_params.get("nic_mode") == "tap":
874 return port
lmr6f669ce2009-05-31 19:02:42 +0000875 else:
lmrf4696342009-08-13 04:06:33 +0000876 if not self.redirs.has_key(port):
877 logging.warn("Warning: guest port %s requested but not "
878 "redirected" % port)
879 return self.redirs.get(port)
lmr6f669ce2009-05-31 19:02:42 +0000880
881
lmra4967622009-07-23 01:36:32 +0000882 def get_pid(self):
883 """
lmr71fa4de2010-06-14 15:54:55 +0000884 Return the VM's PID. If the VM is dead return None.
885
886 @note: This works under the assumption that self.process.get_pid()
887 returns the PID of the parent shell process.
888 """
889 try:
890 children = commands.getoutput("ps --ppid=%d -o pid=" %
891 self.process.get_pid()).split()
892 return int(children[0])
893 except (TypeError, IndexError, ValueError):
894 return None
895
896
897 def get_shell_pid(self):
898 """
899 Return the PID of the parent shell process.
900
901 @note: This works under the assumption that self.process.get_pid()
902 returns the PID of the parent shell process.
lmra4967622009-07-23 01:36:32 +0000903 """
904 return self.process.get_pid()
905
906
lmr0bee2342010-02-24 00:01:37 +0000907 def get_shared_meminfo(self):
908 """
909 Returns the VM's shared memory information.
910
911 @return: Shared memory used by VM (MB)
912 """
913 if self.is_dead():
914 logging.error("Could not get shared memory info from dead VM.")
915 return None
916
lmr983ecdf2010-06-14 15:57:12 +0000917 filename = "/proc/%d/statm" % self.get_pid()
918 shm = int(open(filename).read().split()[2])
lmr0bee2342010-02-24 00:01:37 +0000919 # statm stores informations in pages, translate it to MB
lmr983ecdf2010-06-14 15:57:12 +0000920 return shm * 4.0 / 1024
lmr0bee2342010-02-24 00:01:37 +0000921
922
lmr912c74b2009-08-17 20:43:27 +0000923 def remote_login(self, nic_index=0, timeout=10):
lmr6f669ce2009-05-31 19:02:42 +0000924 """
lmr912c74b2009-08-17 20:43:27 +0000925 Log into the guest via SSH/Telnet/Netcat.
lmr6f669ce2009-05-31 19:02:42 +0000926 If timeout expires while waiting for output from the guest (e.g. a
927 password prompt or a shell prompt) -- fail.
928
lmree90dd92009-08-13 04:13:39 +0000929 @param nic_index: The index of the NIC to connect to.
lmr6f669ce2009-05-31 19:02:42 +0000930 @param timeout: Time (seconds) before giving up logging into the
931 guest.
932 @return: kvm_spawn object on success and None on failure.
933 """
934 username = self.params.get("username", "")
935 password = self.params.get("password", "")
lmr912c74b2009-08-17 20:43:27 +0000936 prompt = self.params.get("shell_prompt", "[\#\$]")
lmr59f9e2d2009-10-05 18:47:16 +0000937 linesep = eval("'%s'" % self.params.get("shell_linesep", r"\n"))
lmr912c74b2009-08-17 20:43:27 +0000938 client = self.params.get("shell_client")
lmree90dd92009-08-13 04:13:39 +0000939 address = self.get_address(nic_index)
lmr912c74b2009-08-17 20:43:27 +0000940 port = self.get_port(int(self.params.get("shell_port")))
lmre56903f2010-06-22 02:09:35 +0000941 log_filename = ("session-%s-%s.log" %
942 (self.name, kvm_utils.generate_random_string(4)))
lmr912c74b2009-08-17 20:43:27 +0000943
lmree90dd92009-08-13 04:13:39 +0000944 if not address or not port:
945 logging.debug("IP address or port unavailable")
lmr6f669ce2009-05-31 19:02:42 +0000946 return None
947
lmr158604f2010-06-22 01:57:34 +0000948 session = kvm_utils.remote_login(client, address, port, username,
lmre56903f2010-06-22 02:09:35 +0000949 password, prompt, linesep,
950 log_filename, timeout)
lmr912c74b2009-08-17 20:43:27 +0000951
lmr6f669ce2009-05-31 19:02:42 +0000952 if session:
lmr912c74b2009-08-17 20:43:27 +0000953 session.set_status_test_command(self.params.get("status_test_"
lmr6f669ce2009-05-31 19:02:42 +0000954 "command", ""))
955 return session
956
957
lmrc196d492010-05-07 14:14:26 +0000958 def copy_files_to(self, local_path, remote_path, nic_index=0, timeout=600):
lmr6f669ce2009-05-31 19:02:42 +0000959 """
lmr912c74b2009-08-17 20:43:27 +0000960 Transfer files to the guest.
lmr6f669ce2009-05-31 19:02:42 +0000961
962 @param local_path: Host path
963 @param remote_path: Guest path
lmr912c74b2009-08-17 20:43:27 +0000964 @param nic_index: The index of the NIC to connect to.
lmr6f669ce2009-05-31 19:02:42 +0000965 @param timeout: Time (seconds) before giving up on doing the remote
966 copy.
967 """
968 username = self.params.get("username", "")
969 password = self.params.get("password", "")
lmr912c74b2009-08-17 20:43:27 +0000970 client = self.params.get("file_transfer_client")
lmree90dd92009-08-13 04:13:39 +0000971 address = self.get_address(nic_index)
lmr912c74b2009-08-17 20:43:27 +0000972 port = self.get_port(int(self.params.get("file_transfer_port")))
lmre56903f2010-06-22 02:09:35 +0000973 log_filename = ("scp-%s-%s.log" %
974 (self.name, kvm_utils.generate_random_string(4)))
lmr912c74b2009-08-17 20:43:27 +0000975
lmree90dd92009-08-13 04:13:39 +0000976 if not address or not port:
977 logging.debug("IP address or port unavailable")
lmr6f669ce2009-05-31 19:02:42 +0000978 return None
lmr912c74b2009-08-17 20:43:27 +0000979
980 if client == "scp":
981 return kvm_utils.scp_to_remote(address, port, username, password,
lmre56903f2010-06-22 02:09:35 +0000982 local_path, remote_path,
983 log_filename, timeout)
lmr6f669ce2009-05-31 19:02:42 +0000984
985
lmrc196d492010-05-07 14:14:26 +0000986 def copy_files_from(self, remote_path, local_path, nic_index=0, timeout=600):
lmr6f669ce2009-05-31 19:02:42 +0000987 """
lmr912c74b2009-08-17 20:43:27 +0000988 Transfer files from the guest.
lmr6f669ce2009-05-31 19:02:42 +0000989
990 @param local_path: Guest path
991 @param remote_path: Host path
lmr912c74b2009-08-17 20:43:27 +0000992 @param nic_index: The index of the NIC to connect to.
lmr6f669ce2009-05-31 19:02:42 +0000993 @param timeout: Time (seconds) before giving up on doing the remote
994 copy.
995 """
996 username = self.params.get("username", "")
997 password = self.params.get("password", "")
lmr912c74b2009-08-17 20:43:27 +0000998 client = self.params.get("file_transfer_client")
lmree90dd92009-08-13 04:13:39 +0000999 address = self.get_address(nic_index)
lmr912c74b2009-08-17 20:43:27 +00001000 port = self.get_port(int(self.params.get("file_transfer_port")))
lmre56903f2010-06-22 02:09:35 +00001001 log_filename = ("scp-%s-%s.log" %
1002 (self.name, kvm_utils.generate_random_string(4)))
lmr912c74b2009-08-17 20:43:27 +00001003
lmree90dd92009-08-13 04:13:39 +00001004 if not address or not port:
1005 logging.debug("IP address or port unavailable")
lmr6f669ce2009-05-31 19:02:42 +00001006 return None
lmr6f669ce2009-05-31 19:02:42 +00001007
lmr912c74b2009-08-17 20:43:27 +00001008 if client == "scp":
1009 return kvm_utils.scp_from_remote(address, port, username, password,
lmre56903f2010-06-22 02:09:35 +00001010 remote_path, local_path,
1011 log_filename, timeout)
lmr6f669ce2009-05-31 19:02:42 +00001012
1013
lmraa380a22010-06-22 02:05:29 +00001014 def serial_login(self, timeout=10):
1015 """
1016 Log into the guest via the serial console.
1017 If timeout expires while waiting for output from the guest (e.g. a
1018 password prompt or a shell prompt) -- fail.
1019
1020 @param timeout: Time (seconds) before giving up logging into the guest.
1021 @return: kvm_spawn object on success and None on failure.
1022 """
1023 username = self.params.get("username", "")
1024 password = self.params.get("password", "")
1025 prompt = self.params.get("shell_prompt", "[\#\$]")
1026 linesep = eval("'%s'" % self.params.get("shell_linesep", r"\n"))
1027 status_test_command = self.params.get("status_test_command", "")
1028
1029 if self.serial_console:
1030 self.serial_console.set_linesep(linesep)
1031 self.serial_console.set_status_test_command(status_test_command)
1032 else:
1033 return None
1034
1035 # Make sure we get a login prompt
1036 self.serial_console.sendline()
1037
1038 if kvm_utils._remote_login(self.serial_console, username, password,
1039 prompt, timeout):
1040 return self.serial_console
1041
1042
lmr6f669ce2009-05-31 19:02:42 +00001043 def send_key(self, keystr):
1044 """
1045 Send a key event to the VM.
1046
1047 @param: keystr: A key event string (e.g. "ctrl-alt-delete")
1048 """
1049 # For compatibility with versions of QEMU that do not recognize all
1050 # key names: replace keyname with the hex value from the dict, which
1051 # QEMU will definitely accept
lmr9e964a02010-06-18 03:46:21 +00001052 dict = {"comma": "0x33",
1053 "dot": "0x34",
1054 "slash": "0x35"}
1055 for key, value in dict.items():
1056 keystr = keystr.replace(key, value)
1057 self.monitor.sendkey(keystr)
lmr6f669ce2009-05-31 19:02:42 +00001058 time.sleep(0.2)
1059
1060
1061 def send_string(self, str):
1062 """
1063 Send a string to the VM.
1064
1065 @param str: String, that must consist of alphanumeric characters only.
1066 Capital letters are allowed.
1067 """
1068 for char in str:
1069 if char.isupper():
1070 self.send_key("shift-%s" % char.lower())
1071 else:
1072 self.send_key(char)
lmra2533222009-07-20 12:43:46 +00001073
mbligh1ef218d2009-08-03 16:57:56 +00001074
lmra2533222009-07-20 12:43:46 +00001075 def get_uuid(self):
1076 """
1077 Catch UUID of the VM.
1078
1079 @return: None,if not specified in config file
1080 """
1081 if self.params.get("uuid") == "random":
1082 return self.uuid
1083 else:
1084 return self.params.get("uuid", None)
lmrdd2ff922009-12-01 23:39:12 +00001085
1086
1087 def get_cpu_count(self):
1088 """
1089 Get the cpu count of the VM.
1090 """
lmr13426552010-01-17 15:38:41 +00001091 session = self.remote_login()
1092 if not session:
1093 return None
lmrdd2ff922009-12-01 23:39:12 +00001094 try:
lmr13426552010-01-17 15:38:41 +00001095 cmd = self.params.get("cpu_chk_cmd")
1096 s, count = session.get_command_status_output(cmd)
1097 if s == 0:
1098 return int(count)
lmrdd2ff922009-12-01 23:39:12 +00001099 return None
1100 finally:
1101 session.close()
1102
1103
lmr28426c82010-04-16 06:02:58 +00001104 def get_memory_size(self, cmd=None):
lmrdd2ff922009-12-01 23:39:12 +00001105 """
lmr28426c82010-04-16 06:02:58 +00001106 Get bootup memory size of the VM.
1107
1108 @param check_cmd: Command used to check memory. If not provided,
1109 self.params.get("mem_chk_cmd") will be used.
lmrdd2ff922009-12-01 23:39:12 +00001110 """
lmr13426552010-01-17 15:38:41 +00001111 session = self.remote_login()
1112 if not session:
lmrdd2ff922009-12-01 23:39:12 +00001113 return None
lmr13426552010-01-17 15:38:41 +00001114 try:
lmr28426c82010-04-16 06:02:58 +00001115 if not cmd:
1116 cmd = self.params.get("mem_chk_cmd")
lmr13426552010-01-17 15:38:41 +00001117 s, mem_str = session.get_command_status_output(cmd)
1118 if s != 0:
1119 return None
lmr6d69f4d2010-02-12 11:35:55 +00001120 mem = re.findall("([0-9]+)", mem_str)
lmr13426552010-01-17 15:38:41 +00001121 mem_size = 0
1122 for m in mem:
1123 mem_size += int(m)
1124 if "GB" in mem_str:
1125 mem_size *= 1024
1126 elif "MB" in mem_str:
1127 pass
1128 else:
1129 mem_size /= 1024
1130 return int(mem_size)
lmrdd2ff922009-12-01 23:39:12 +00001131 finally:
1132 session.close()
lmr28426c82010-04-16 06:02:58 +00001133
1134
1135 def get_current_memory_size(self):
1136 """
1137 Get current memory size of the VM, rather than bootup memory.
1138 """
1139 cmd = self.params.get("mem_chk_cur_cmd")
1140 return self.get_memory_size(cmd)