blob: 3c643fdde7cf7dbb0554075107fed05c09c6050e [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
lmrb635b862009-09-10 14:53:21 +00008import time, socket, os, logging, fcntl, re, commands
9import kvm_utils, kvm_subprocess
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
lmr953ffba2009-07-27 13:20:10 +0000109 self.redirs = {}
110 self.vnc_port = 5900
lmra2533222009-07-20 12:43:46 +0000111 self.uuid = None
lmr6f669ce2009-05-31 19:02:42 +0000112
113 self.name = name
114 self.params = params
lmr90b9fd52009-08-17 20:48:18 +0000115 self.root_dir = root_dir
lmree90dd92009-08-13 04:13:39 +0000116 self.address_cache = address_cache
lmr31af3a12010-01-18 16:46:52 +0000117 self.pci_assignable = None
lmr6f669ce2009-05-31 19:02:42 +0000118
lmr8b134f92009-06-08 14:47:31 +0000119 # Find available monitor filename
120 while True:
121 # The monitor filename should be unique
lmrd16a67d2009-06-10 19:52:59 +0000122 self.instance = (time.strftime("%Y%m%d-%H%M%S-") +
123 kvm_utils.generate_random_string(4))
lmr8b134f92009-06-08 14:47:31 +0000124 self.monitor_file_name = os.path.join("/tmp",
125 "monitor-" + self.instance)
126 if not os.path.exists(self.monitor_file_name):
127 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 """
lmre45a1f22009-11-10 16:35:08 +0000189 if name is None:
lmr6f669ce2009-05-31 19:02:42 +0000190 name = self.name
lmre45a1f22009-11-10 16:35:08 +0000191 if params is None:
lmr6f669ce2009-05-31 19:02:42 +0000192 params = self.params
lmre45a1f22009-11-10 16:35:08 +0000193 if root_dir is None:
lmr90b9fd52009-08-17 20:48:18 +0000194 root_dir = self.root_dir
lmr6f669ce2009-05-31 19:02:42 +0000195
lmreeff0eb2009-06-10 19:19:15 +0000196 # Start constructing the qemu command
197 qemu_cmd = ""
198 # Set the X11 display parameter if requested
199 if params.get("x11_display"):
200 qemu_cmd += "DISPLAY=%s " % params.get("x11_display")
201 # Add the qemu binary
lmr52800ba2009-08-17 20:49:58 +0000202 qemu_cmd += kvm_utils.get_path(root_dir, params.get("qemu_binary",
203 "qemu"))
lmreeff0eb2009-06-10 19:19:15 +0000204 # Add the VM's name
lmr6f669ce2009-05-31 19:02:42 +0000205 qemu_cmd += " -name '%s'" % name
lmreeff0eb2009-06-10 19:19:15 +0000206 # Add the monitor socket parameter
lmr6f669ce2009-05-31 19:02:42 +0000207 qemu_cmd += " -monitor unix:%s,server,nowait" % self.monitor_file_name
208
209 for image_name in kvm_utils.get_sub_dict_names(params, "images"):
210 image_params = kvm_utils.get_sub_dict(params, image_name)
lmr9d75ee32009-09-29 13:14:13 +0000211 if image_params.get("boot_drive") == "no":
212 continue
lmr6f669ce2009-05-31 19:02:42 +0000213 qemu_cmd += " -drive file=%s" % get_image_filename(image_params,
lmr90b9fd52009-08-17 20:48:18 +0000214 root_dir)
lmr6f669ce2009-05-31 19:02:42 +0000215 if image_params.get("drive_format"):
216 qemu_cmd += ",if=%s" % image_params.get("drive_format")
lmr0d4d2742009-06-18 06:15:17 +0000217 if image_params.get("drive_cache"):
218 qemu_cmd += ",cache=%s" % image_params.get("drive_cache")
lmrd00c0992010-01-19 15:13:27 +0000219 if image_params.get("drive_werror"):
220 qemu_cmd += ",werror=%s" % image_params.get("drive_werror")
lmr0d4d2742009-06-18 06:15:17 +0000221 if image_params.get("drive_serial"):
222 qemu_cmd += ",serial=%s" % image_params.get("drive_serial")
lmr6f669ce2009-05-31 19:02:42 +0000223 if image_params.get("image_snapshot") == "yes":
224 qemu_cmd += ",snapshot=on"
225 if image_params.get("image_boot") == "yes":
226 qemu_cmd += ",boot=on"
227
228 vlan = 0
229 for nic_name in kvm_utils.get_sub_dict_names(params, "nics"):
230 nic_params = kvm_utils.get_sub_dict(params, nic_name)
lmrf4696342009-08-13 04:06:33 +0000231 # Handle the '-net nic' part
lmr6f669ce2009-05-31 19:02:42 +0000232 qemu_cmd += " -net nic,vlan=%d" % vlan
233 if nic_params.get("nic_model"):
234 qemu_cmd += ",model=%s" % nic_params.get("nic_model")
lmrf4696342009-08-13 04:06:33 +0000235 if nic_params.has_key("address_index"):
236 mac, ip = kvm_utils.get_mac_ip_pair_from_dict(nic_params)
lmr71247002009-09-10 03:13:34 +0000237 if mac:
238 qemu_cmd += ",macaddr=%s" % mac
lmrf4696342009-08-13 04:06:33 +0000239 # Handle the '-net tap' or '-net user' part
240 mode = nic_params.get("nic_mode", "user")
241 qemu_cmd += " -net %s,vlan=%d" % (mode, vlan)
242 if mode == "tap":
243 if nic_params.get("nic_ifname"):
244 qemu_cmd += ",ifname=%s" % nic_params.get("nic_ifname")
lmr90b9fd52009-08-17 20:48:18 +0000245 script_path = nic_params.get("nic_script")
246 if script_path:
247 script_path = kvm_utils.get_path(root_dir, script_path)
lmrf4696342009-08-13 04:06:33 +0000248 qemu_cmd += ",script=%s" % script_path
lmr90b9fd52009-08-17 20:48:18 +0000249 script_path = nic_params.get("nic_downscript")
250 if script_path:
251 script_path = kvm_utils.get_path(root_dir, script_path)
lmrf4696342009-08-13 04:06:33 +0000252 qemu_cmd += ",downscript=%s" % script_path
lmr620b8bc2009-10-23 12:12:53 +0000253 else:
254 qemu_cmd += ",downscript=no"
lmrf4696342009-08-13 04:06:33 +0000255 # Proceed to next NIC
lmr6f669ce2009-05-31 19:02:42 +0000256 vlan += 1
257
258 mem = params.get("mem")
259 if mem:
260 qemu_cmd += " -m %s" % mem
261
lmrc43bf372009-11-10 13:19:00 +0000262 smp = params.get("smp")
263 if smp:
264 qemu_cmd += " -smp %s" % smp
265
lmr6f669ce2009-05-31 19:02:42 +0000266 iso = params.get("cdrom")
267 if iso:
lmr90b9fd52009-08-17 20:48:18 +0000268 iso = kvm_utils.get_path(root_dir, iso)
lmr89fd6412010-01-18 02:42:57 +0000269 qemu_cmd += " -drive file=%s,index=2,media=cdrom" % iso
270
271 # Even though this is not a really scalable approach,
272 # it doesn't seem like we are going to need more than
273 # 2 CDs active on the same VM.
274 iso_extra = params.get("cdrom_extra")
275 if iso_extra:
276 iso_extra = kvm_utils.get_path(root_dir, iso_extra)
277 qemu_cmd += " -drive file=%s,index=3,media=cdrom" % iso_extra
lmr6f669ce2009-05-31 19:02:42 +0000278
lmrb0a9b762009-10-09 20:43:30 +0000279 # We may want to add {floppy_otps} parameter for -fda
280 # {fat:floppy:}/path/. However vvfat is not usually recommended
281 floppy = params.get("floppy")
282 if floppy:
lmrf69f6b12009-11-10 16:33:44 +0000283 floppy = kvm_utils.get_path(root_dir, floppy)
lmrb0a9b762009-10-09 20:43:30 +0000284 qemu_cmd += " -fda %s" % floppy
285
286 tftp = params.get("tftp")
287 if tftp:
lmrf69f6b12009-11-10 16:33:44 +0000288 tftp = kvm_utils.get_path(root_dir, tftp)
lmrb0a9b762009-10-09 20:43:30 +0000289 qemu_cmd += " -tftp %s" % tftp
290
lmr6f669ce2009-05-31 19:02:42 +0000291 extra_params = params.get("extra_params")
292 if extra_params:
293 qemu_cmd += " %s" % extra_params
294
295 for redir_name in kvm_utils.get_sub_dict_names(params, "redirs"):
296 redir_params = kvm_utils.get_sub_dict(params, redir_name)
297 guest_port = int(redir_params.get("guest_port"))
lmrf4696342009-08-13 04:06:33 +0000298 host_port = self.redirs.get(guest_port)
lmr6f669ce2009-05-31 19:02:42 +0000299 qemu_cmd += " -redir tcp:%s::%s" % (host_port, guest_port)
300
301 if params.get("display") == "vnc":
302 qemu_cmd += " -vnc :%d" % (self.vnc_port - 5900)
303 elif params.get("display") == "sdl":
304 qemu_cmd += " -sdl"
305 elif params.get("display") == "nographic":
306 qemu_cmd += " -nographic"
307
lmra2533222009-07-20 12:43:46 +0000308 if params.get("uuid") == "random":
309 qemu_cmd += " -uuid %s" % self.uuid
310 elif params.get("uuid"):
311 qemu_cmd += " -uuid %s" % params.get("uuid")
312
lmr31af3a12010-01-18 16:46:52 +0000313 # If the PCI assignment step went OK, add each one of the PCI assigned
314 # devices to the qemu command line.
315 if self.pci_assignable:
316 for pci_id in self.pa_pci_ids:
317 qemu_cmd += " -pcidevice host=%s" % pci_id
318
lmr6f669ce2009-05-31 19:02:42 +0000319 return qemu_cmd
320
321
lmr52800ba2009-08-17 20:49:58 +0000322 def create(self, name=None, params=None, root_dir=None,
lmr90b9fd52009-08-17 20:48:18 +0000323 for_migration=False, timeout=5.0):
lmr6f669ce2009-05-31 19:02:42 +0000324 """
325 Start the VM by running a qemu command.
326 All parameters are optional. The following applies to all parameters
327 but for_migration: If a parameter is not supplied, the corresponding
328 value stored in the class attributes is used, and if it is supplied,
329 it is stored for later use.
330
331 @param name: The name of the object
332 @param params: A dict containing VM params
lmr90b9fd52009-08-17 20:48:18 +0000333 @param root_dir: Base directory for relative filenames
lmr6f669ce2009-05-31 19:02:42 +0000334 @param for_migration: If True, start the VM with the -incoming
335 option
336 """
lmr135b5e62009-06-10 19:22:31 +0000337 self.destroy()
338
lmre45a1f22009-11-10 16:35:08 +0000339 if name is not None:
lmr6f669ce2009-05-31 19:02:42 +0000340 self.name = name
lmre45a1f22009-11-10 16:35:08 +0000341 if params is not None:
lmr6f669ce2009-05-31 19:02:42 +0000342 self.params = params
lmre45a1f22009-11-10 16:35:08 +0000343 if root_dir is not None:
lmr90b9fd52009-08-17 20:48:18 +0000344 self.root_dir = root_dir
lmr6f669ce2009-05-31 19:02:42 +0000345 name = self.name
346 params = self.params
lmr90b9fd52009-08-17 20:48:18 +0000347 root_dir = self.root_dir
lmr6f669ce2009-05-31 19:02:42 +0000348
349 # Verify the md5sum of the ISO image
350 iso = params.get("cdrom")
351 if iso:
lmr90b9fd52009-08-17 20:48:18 +0000352 iso = kvm_utils.get_path(root_dir, iso)
lmr6f669ce2009-05-31 19:02:42 +0000353 if not os.path.exists(iso):
354 logging.error("ISO file not found: %s" % iso)
355 return False
356 compare = False
357 if params.get("md5sum_1m"):
lmrf4696342009-08-13 04:06:33 +0000358 logging.debug("Comparing expected MD5 sum with MD5 sum of "
359 "first MB of ISO file...")
lmrd60882f2010-02-04 03:26:36 +0000360 actual_hash = utils.hash_file(iso, 1048576, method="md5")
lmr03ba22e2009-10-23 12:07:44 +0000361 expected_hash = params.get("md5sum_1m")
lmr6f669ce2009-05-31 19:02:42 +0000362 compare = True
363 elif params.get("md5sum"):
lmrf4696342009-08-13 04:06:33 +0000364 logging.debug("Comparing expected MD5 sum with MD5 sum of ISO "
365 "file...")
lmrd60882f2010-02-04 03:26:36 +0000366 actual_hash = utils.hash_file(iso, method="md5")
lmr03ba22e2009-10-23 12:07:44 +0000367 expected_hash = params.get("md5sum")
368 compare = True
369 elif params.get("sha1sum"):
370 logging.debug("Comparing expected SHA1 sum with SHA1 sum of "
371 "ISO file...")
lmrd60882f2010-02-04 03:26:36 +0000372 actual_hash = utils.hash_file(iso, method="sha1")
lmrea1266e2009-11-10 16:41:08 +0000373 expected_hash = params.get("sha1sum")
lmr6f669ce2009-05-31 19:02:42 +0000374 compare = True
375 if compare:
lmr03ba22e2009-10-23 12:07:44 +0000376 if actual_hash == expected_hash:
377 logging.debug("Hashes match")
lmr6f669ce2009-05-31 19:02:42 +0000378 else:
lmr03ba22e2009-10-23 12:07:44 +0000379 logging.error("Actual hash differs from expected one")
lmr6f669ce2009-05-31 19:02:42 +0000380 return False
381
lmrdc2ac6a2009-06-10 19:15:49 +0000382 # Make sure the following code is not executed by more than one thread
383 # at the same time
384 lockfile = open("/tmp/kvm-autotest-vm-create.lock", "w+")
385 fcntl.lockf(lockfile, fcntl.LOCK_EX)
lmr6f669ce2009-05-31 19:02:42 +0000386
lmrdc2ac6a2009-06-10 19:15:49 +0000387 try:
388 # Handle port redirections
389 redir_names = kvm_utils.get_sub_dict_names(params, "redirs")
390 host_ports = kvm_utils.find_free_ports(5000, 6000, len(redir_names))
391 self.redirs = {}
392 for i in range(len(redir_names)):
393 redir_params = kvm_utils.get_sub_dict(params, redir_names[i])
394 guest_port = int(redir_params.get("guest_port"))
395 self.redirs[guest_port] = host_ports[i]
lmr6f669ce2009-05-31 19:02:42 +0000396
lmrdc2ac6a2009-06-10 19:15:49 +0000397 # Find available VNC port, if needed
398 if params.get("display") == "vnc":
399 self.vnc_port = kvm_utils.find_free_port(5900, 6000)
lmr6f669ce2009-05-31 19:02:42 +0000400
lmra2533222009-07-20 12:43:46 +0000401 # Find random UUID if specified 'uuid = random' in config file
402 if params.get("uuid") == "random":
403 f = open("/proc/sys/kernel/random/uuid")
404 self.uuid = f.read().strip()
405 f.close()
406
lmr31af3a12010-01-18 16:46:52 +0000407 if not params.get("pci_assignable") == "no":
408 pa_type = params.get("pci_assignable")
409 pa_devices_requested = params.get("devices_requested")
410
411 # Virtual Functions (VF) assignable devices
412 if pa_type == "vf":
413 pa_driver = params.get("driver")
414 pa_driver_option = params.get("driver_option")
415 self.pci_assignable = kvm_utils.PciAssignable(type=pa_type,
416 driver=pa_driver,
417 driver_option=pa_driver_option,
418 devices_requested=pa_devices_requested)
419 # Physical NIC (PF) assignable devices
420 elif pa_type == "pf":
421 pa_device_names = params.get("device_names")
422 self.pci_assignable = kvm_utils.PciAssignable(type=pa_type,
423 names=pa_device_names,
424 devices_requested=pa_devices_requested)
425 # Working with both VF and PF
426 elif pa_type == "mixed":
427 pa_device_names = params.get("device_names")
428 pa_driver = params.get("driver")
429 pa_driver_option = params.get("driver_option")
430 self.pci_assignable = kvm_utils.PciAssignable(type=pa_type,
431 driver=pa_driver,
432 driver_option=pa_driver_option,
433 names=pa_device_names,
434 devices_requested=pa_devices_requested)
435
436 self.pa_pci_ids = self.pci_assignable.request_devs()
437
438 if self.pa_pci_ids:
439 logging.debug("Successfuly assigned devices: %s" %
440 self.pa_pci_ids)
441 else:
442 logging.error("No PCI assignable devices were assigned "
443 "and 'pci_assignable' is defined to %s "
444 "on your config file. Aborting VM creation." %
445 pa_type)
446 return False
447
448 else:
449 self.pci_assignable = None
450
lmrdc2ac6a2009-06-10 19:15:49 +0000451 # Make qemu command
452 qemu_command = self.make_qemu_command()
lmr6f669ce2009-05-31 19:02:42 +0000453
lmrdc2ac6a2009-06-10 19:15:49 +0000454 # Is this VM supposed to accept incoming migrations?
455 if for_migration:
456 # Find available migration port
457 self.migration_port = kvm_utils.find_free_port(5200, 6000)
458 # Add -incoming option to the qemu command
459 qemu_command += " -incoming tcp:0:%d" % self.migration_port
lmr6f669ce2009-05-31 19:02:42 +0000460
lmrdc2ac6a2009-06-10 19:15:49 +0000461 logging.debug("Running qemu command:\n%s", qemu_command)
lmra4967622009-07-23 01:36:32 +0000462 self.process = kvm_subprocess.run_bg(qemu_command, None,
463 logging.debug, "(qemu) ")
lmr6f669ce2009-05-31 19:02:42 +0000464
lmra4967622009-07-23 01:36:32 +0000465 if not self.process.is_alive():
466 logging.error("VM could not be created; "
467 "qemu command failed:\n%s" % qemu_command)
468 logging.error("Status: %s" % self.process.get_status())
469 logging.error("Output:" + kvm_utils.format_str_for_message(
470 self.process.get_output()))
lmrdc2ac6a2009-06-10 19:15:49 +0000471 self.destroy()
472 return False
lmr6f669ce2009-05-31 19:02:42 +0000473
lmra4967622009-07-23 01:36:32 +0000474 if not kvm_utils.wait_for(self.is_alive, timeout, 0, 1):
475 logging.error("VM is not alive for some reason; "
476 "qemu command:\n%s" % qemu_command)
477 self.destroy()
478 return False
479
lmrfe6515e2009-07-29 13:01:17 +0000480 # Get the output so far, to see if we have any problems with
481 # hugepage setup.
482 output = self.process.get_output()
483
484 if "alloc_mem_area" in output:
485 logging.error("Could not allocate hugepage memory; "
486 "qemu command:\n%s" % qemu_command)
487 logging.error("Output:" + kvm_utils.format_str_for_message(
488 self.process.get_output()))
lmr090cd7a2010-04-01 02:18:52 +0000489 self.destroy()
lmrfe6515e2009-07-29 13:01:17 +0000490 return False
491
lmra4967622009-07-23 01:36:32 +0000492 logging.debug("VM appears to be alive with PID %d",
493 self.process.get_pid())
lmrdc2ac6a2009-06-10 19:15:49 +0000494 return True
495
496 finally:
497 fcntl.lockf(lockfile, fcntl.LOCK_UN)
498 lockfile.close()
lmr6f669ce2009-05-31 19:02:42 +0000499
500
501 def send_monitor_cmd(self, command, block=True, timeout=20.0):
502 """
503 Send command to the QEMU monitor.
504
505 Connect to the VM's monitor socket and wait for the (qemu) prompt.
506 If block is True, read output from the socket until the (qemu) prompt
507 is found again, or until timeout expires.
508
509 Return a tuple containing an integer indicating success or failure,
510 and the data read so far. The integer is 0 on success and 1 on failure.
511 A failure is any of the following cases: connection to the socket
512 failed, or the first (qemu) prompt could not be found, or block is
513 True and the second prompt could not be found.
514
515 @param command: Command that will be sent to the monitor
516 @param block: Whether the output from the socket will be read until
517 the timeout expires
518 @param timeout: Timeout (seconds) before giving up on reading from
519 socket
520 """
521 def read_up_to_qemu_prompt(s, timeout):
522 """
523 Read data from socket s until the (qemu) prompt is found.
524
525 If the prompt is found before timeout expires, return a tuple
526 containing True and the data read. Otherwise return a tuple
527 containing False and the data read so far.
528
529 @param s: Socket object
530 @param timeout: Time (seconds) before giving up trying to get the
531 qemu prompt.
532 """
533 o = ""
534 end_time = time.time() + timeout
535 while time.time() < end_time:
536 try:
lmrbcd20832009-10-15 11:56:56 +0000537 o += s.recv(1024)
lmr6f669ce2009-05-31 19:02:42 +0000538 if o.splitlines()[-1].split()[-1] == "(qemu)":
539 return (True, o)
540 except:
541 time.sleep(0.01)
542 return (False, o)
543
544 # Connect to monitor
545 logging.debug("Sending monitor command: %s" % command)
546 try:
547 s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
548 s.setblocking(False)
549 s.connect(self.monitor_file_name)
550 except:
551 logging.debug("Could not connect to monitor socket")
552 return (1, "")
lmrbcd20832009-10-15 11:56:56 +0000553
554 # Send the command and get the resulting output
555 try:
lmr6f669ce2009-05-31 19:02:42 +0000556 status, data = read_up_to_qemu_prompt(s, timeout)
lmr6f669ce2009-05-31 19:02:42 +0000557 if not status:
lmrbcd20832009-10-15 11:56:56 +0000558 logging.debug("Could not find (qemu) prompt; output so far:" +
559 kvm_utils.format_str_for_message(data))
560 return (1, "")
561 # Send command
562 s.sendall(command + "\n")
563 # Receive command output
564 data = ""
565 if block:
566 status, data = read_up_to_qemu_prompt(s, timeout)
567 data = "\n".join(data.splitlines()[1:])
568 if not status:
569 logging.debug("Could not find (qemu) prompt after command; "
570 "output so far:" +
571 kvm_utils.format_str_for_message(data))
572 return (1, data)
573 return (0, data)
574
575 # Clean up before exiting
576 finally:
577 s.shutdown(socket.SHUT_RDWR)
578 s.close()
lmr6f669ce2009-05-31 19:02:42 +0000579
580
581 def destroy(self, gracefully=True):
582 """
583 Destroy the VM.
584
lmr912c74b2009-08-17 20:43:27 +0000585 If gracefully is True, first attempt to shutdown the VM with a shell
586 command. Then, attempt to destroy the VM via the monitor with a 'quit'
587 command. If that fails, send SIGKILL to the qemu process.
lmr6f669ce2009-05-31 19:02:42 +0000588
589 @param gracefully: Whether an attempt will be made to end the VM
lmr912c74b2009-08-17 20:43:27 +0000590 using a shell command before trying to end the qemu process
591 with a 'quit' or a kill signal.
lmr6f669ce2009-05-31 19:02:42 +0000592 """
lmrf320b042009-09-15 05:48:06 +0000593 try:
594 # Is it already dead?
595 if self.is_dead():
596 logging.debug("VM is already down")
597 return
lmr6f669ce2009-05-31 19:02:42 +0000598
lmrf320b042009-09-15 05:48:06 +0000599 logging.debug("Destroying VM with PID %d..." %
600 self.process.get_pid())
lmr6f669ce2009-05-31 19:02:42 +0000601
lmrf320b042009-09-15 05:48:06 +0000602 if gracefully and self.params.get("shutdown_command"):
603 # Try to destroy with shell command
604 logging.debug("Trying to shutdown VM with shell command...")
605 session = self.remote_login()
606 if session:
607 try:
608 # Send the shutdown command
609 session.sendline(self.params.get("shutdown_command"))
610 logging.debug("Shutdown command sent; waiting for VM "
611 "to go down...")
612 if kvm_utils.wait_for(self.is_dead, 60, 1, 1):
613 logging.debug("VM is down")
614 return
615 finally:
616 session.close()
lmr6f669ce2009-05-31 19:02:42 +0000617
lmrf320b042009-09-15 05:48:06 +0000618 # Try to destroy with a monitor command
619 logging.debug("Trying to kill VM with monitor command...")
620 status, output = self.send_monitor_cmd("quit", block=False)
621 # Was the command sent successfully?
622 if status == 0:
623 # Wait for the VM to be really dead
624 if kvm_utils.wait_for(self.is_dead, 5, 0.5, 0.5):
625 logging.debug("VM is down")
626 return
627
628 # If the VM isn't dead yet...
629 logging.debug("Cannot quit normally; sending a kill to close the "
630 "deal...")
631 kvm_utils.kill_process_tree(self.process.get_pid(), 9)
lmr6f669ce2009-05-31 19:02:42 +0000632 # Wait for the VM to be really dead
633 if kvm_utils.wait_for(self.is_dead, 5, 0.5, 0.5):
634 logging.debug("VM is down")
lmr6f669ce2009-05-31 19:02:42 +0000635 return
636
lmrf320b042009-09-15 05:48:06 +0000637 logging.error("Process %s is a zombie!" % self.process.get_pid())
lmr6f669ce2009-05-31 19:02:42 +0000638
lmrf320b042009-09-15 05:48:06 +0000639 finally:
lmr4513c432010-02-03 11:59:03 +0000640 if self.pci_assignable:
641 self.pci_assignable.release_devs()
lmrf320b042009-09-15 05:48:06 +0000642 if self.process:
643 self.process.close()
lmr4dcc0072009-11-10 16:52:27 +0000644 try:
645 os.unlink(self.monitor_file_name)
646 except OSError:
647 pass
lmr6f669ce2009-05-31 19:02:42 +0000648
649
650 def is_alive(self):
651 """
652 Return True if the VM's monitor is responsive.
653 """
lmra4967622009-07-23 01:36:32 +0000654 # Check if the process is running
655 if self.is_dead():
lmr6f669ce2009-05-31 19:02:42 +0000656 return False
657 # Try sending a monitor command
658 (status, output) = self.send_monitor_cmd("help")
659 if status:
660 return False
661 return True
662
663
664 def is_dead(self):
665 """
lmra4967622009-07-23 01:36:32 +0000666 Return True if the qemu process is dead.
lmr6f669ce2009-05-31 19:02:42 +0000667 """
lmra4967622009-07-23 01:36:32 +0000668 return not self.process or not self.process.is_alive()
lmr6f669ce2009-05-31 19:02:42 +0000669
670
lmra4197002009-08-13 05:00:51 +0000671 def kill_tail_thread(self):
672 """
673 Stop the tailing thread which reports the output of qemu.
674 """
675 if self.process:
676 self.process.kill_tail_thread()
677
678
lmr6f669ce2009-05-31 19:02:42 +0000679 def get_params(self):
680 """
681 Return the VM's params dict. Most modified params take effect only
682 upon VM.create().
683 """
684 return self.params
685
686
lmrf4696342009-08-13 04:06:33 +0000687 def get_address(self, index=0):
lmr6f669ce2009-05-31 19:02:42 +0000688 """
lmrf4696342009-08-13 04:06:33 +0000689 Return the address of a NIC of the guest, in host space.
lmr6f669ce2009-05-31 19:02:42 +0000690
lmrf4696342009-08-13 04:06:33 +0000691 If port redirection is used, return 'localhost' (the NIC has no IP
692 address of its own). Otherwise return the NIC's IP address.
693
694 @param index: Index of the NIC whose address is requested.
lmr6f669ce2009-05-31 19:02:42 +0000695 """
lmree90dd92009-08-13 04:13:39 +0000696 nics = kvm_utils.get_sub_dict_names(self.params, "nics")
697 nic_name = nics[index]
lmrf4696342009-08-13 04:06:33 +0000698 nic_params = kvm_utils.get_sub_dict(self.params, nic_name)
699 if nic_params.get("nic_mode") == "tap":
700 mac, ip = kvm_utils.get_mac_ip_pair_from_dict(nic_params)
lmree90dd92009-08-13 04:13:39 +0000701 if not mac:
702 logging.debug("MAC address unavailable")
703 return None
704 if not ip or nic_params.get("always_use_tcpdump") == "yes":
705 # Get the IP address from the cache
706 ip = self.address_cache.get(mac)
707 if not ip:
708 logging.debug("Could not find IP address for MAC address: "
709 "%s" % mac)
710 return None
711 # Make sure the IP address is assigned to this guest
712 nic_dicts = [kvm_utils.get_sub_dict(self.params, nic)
713 for nic in nics]
714 macs = [kvm_utils.get_mac_ip_pair_from_dict(dict)[0]
715 for dict in nic_dicts]
716 if not kvm_utils.verify_ip_address_ownership(ip, macs):
717 logging.debug("Could not verify MAC-IP address mapping: "
718 "%s ---> %s" % (mac, ip))
719 return None
lmrf4696342009-08-13 04:06:33 +0000720 return ip
721 else:
722 return "localhost"
lmr6f669ce2009-05-31 19:02:42 +0000723
724
lmree90dd92009-08-13 04:13:39 +0000725 def get_port(self, port, nic_index=0):
lmr6f669ce2009-05-31 19:02:42 +0000726 """
727 Return the port in host space corresponding to port in guest space.
728
729 @param port: Port number in host space.
lmree90dd92009-08-13 04:13:39 +0000730 @param nic_index: Index of the NIC.
lmr6f669ce2009-05-31 19:02:42 +0000731 @return: If port redirection is used, return the host port redirected
732 to guest port port. Otherwise return port.
733 """
lmree90dd92009-08-13 04:13:39 +0000734 nic_name = kvm_utils.get_sub_dict_names(self.params, "nics")[nic_index]
lmrf4696342009-08-13 04:06:33 +0000735 nic_params = kvm_utils.get_sub_dict(self.params, nic_name)
736 if nic_params.get("nic_mode") == "tap":
737 return port
lmr6f669ce2009-05-31 19:02:42 +0000738 else:
lmrf4696342009-08-13 04:06:33 +0000739 if not self.redirs.has_key(port):
740 logging.warn("Warning: guest port %s requested but not "
741 "redirected" % port)
742 return self.redirs.get(port)
lmr6f669ce2009-05-31 19:02:42 +0000743
744
lmra4967622009-07-23 01:36:32 +0000745 def get_pid(self):
746 """
747 Return the VM's PID.
748 """
749 return self.process.get_pid()
750
751
lmr0bee2342010-02-24 00:01:37 +0000752 def get_shared_meminfo(self):
753 """
754 Returns the VM's shared memory information.
755
756 @return: Shared memory used by VM (MB)
757 """
758 if self.is_dead():
759 logging.error("Could not get shared memory info from dead VM.")
760 return None
761
762 cmd = "cat /proc/%d/statm" % self.params.get('pid_' + self.name)
763 shm = int(os.popen(cmd).readline().split()[2])
764 # statm stores informations in pages, translate it to MB
765 shm = shm * 4 / 1024
766 return shm
767
768
lmr912c74b2009-08-17 20:43:27 +0000769 def remote_login(self, nic_index=0, timeout=10):
lmr6f669ce2009-05-31 19:02:42 +0000770 """
lmr912c74b2009-08-17 20:43:27 +0000771 Log into the guest via SSH/Telnet/Netcat.
lmr6f669ce2009-05-31 19:02:42 +0000772 If timeout expires while waiting for output from the guest (e.g. a
773 password prompt or a shell prompt) -- fail.
774
lmree90dd92009-08-13 04:13:39 +0000775 @param nic_index: The index of the NIC to connect to.
lmr6f669ce2009-05-31 19:02:42 +0000776 @param timeout: Time (seconds) before giving up logging into the
777 guest.
778 @return: kvm_spawn object on success and None on failure.
779 """
780 username = self.params.get("username", "")
781 password = self.params.get("password", "")
lmr912c74b2009-08-17 20:43:27 +0000782 prompt = self.params.get("shell_prompt", "[\#\$]")
lmr59f9e2d2009-10-05 18:47:16 +0000783 linesep = eval("'%s'" % self.params.get("shell_linesep", r"\n"))
lmr912c74b2009-08-17 20:43:27 +0000784 client = self.params.get("shell_client")
lmree90dd92009-08-13 04:13:39 +0000785 address = self.get_address(nic_index)
lmr912c74b2009-08-17 20:43:27 +0000786 port = self.get_port(int(self.params.get("shell_port")))
787
lmree90dd92009-08-13 04:13:39 +0000788 if not address or not port:
789 logging.debug("IP address or port unavailable")
lmr6f669ce2009-05-31 19:02:42 +0000790 return None
791
lmr912c74b2009-08-17 20:43:27 +0000792 if client == "ssh":
lmr6f669ce2009-05-31 19:02:42 +0000793 session = kvm_utils.ssh(address, port, username, password,
lmr59f9e2d2009-10-05 18:47:16 +0000794 prompt, linesep, timeout)
lmr912c74b2009-08-17 20:43:27 +0000795 elif client == "telnet":
796 session = kvm_utils.telnet(address, port, username, password,
lmr59f9e2d2009-10-05 18:47:16 +0000797 prompt, linesep, timeout)
lmr9f6ebf12009-08-17 20:44:10 +0000798 elif client == "nc":
799 session = kvm_utils.netcat(address, port, username, password,
lmr59f9e2d2009-10-05 18:47:16 +0000800 prompt, linesep, timeout)
lmr912c74b2009-08-17 20:43:27 +0000801
lmr6f669ce2009-05-31 19:02:42 +0000802 if session:
lmr912c74b2009-08-17 20:43:27 +0000803 session.set_status_test_command(self.params.get("status_test_"
lmr6f669ce2009-05-31 19:02:42 +0000804 "command", ""))
805 return session
806
807
lmr912c74b2009-08-17 20:43:27 +0000808 def copy_files_to(self, local_path, remote_path, nic_index=0, timeout=300):
lmr6f669ce2009-05-31 19:02:42 +0000809 """
lmr912c74b2009-08-17 20:43:27 +0000810 Transfer files to the guest.
lmr6f669ce2009-05-31 19:02:42 +0000811
812 @param local_path: Host path
813 @param remote_path: Guest path
lmr912c74b2009-08-17 20:43:27 +0000814 @param nic_index: The index of the NIC to connect to.
lmr6f669ce2009-05-31 19:02:42 +0000815 @param timeout: Time (seconds) before giving up on doing the remote
816 copy.
817 """
818 username = self.params.get("username", "")
819 password = self.params.get("password", "")
lmr912c74b2009-08-17 20:43:27 +0000820 client = self.params.get("file_transfer_client")
lmree90dd92009-08-13 04:13:39 +0000821 address = self.get_address(nic_index)
lmr912c74b2009-08-17 20:43:27 +0000822 port = self.get_port(int(self.params.get("file_transfer_port")))
823
lmree90dd92009-08-13 04:13:39 +0000824 if not address or not port:
825 logging.debug("IP address or port unavailable")
lmr6f669ce2009-05-31 19:02:42 +0000826 return None
lmr912c74b2009-08-17 20:43:27 +0000827
828 if client == "scp":
829 return kvm_utils.scp_to_remote(address, port, username, password,
830 local_path, remote_path, timeout)
lmr6f669ce2009-05-31 19:02:42 +0000831
832
lmr912c74b2009-08-17 20:43:27 +0000833 def copy_files_from(self, remote_path, local_path, nic_index=0, timeout=300):
lmr6f669ce2009-05-31 19:02:42 +0000834 """
lmr912c74b2009-08-17 20:43:27 +0000835 Transfer files from the guest.
lmr6f669ce2009-05-31 19:02:42 +0000836
837 @param local_path: Guest path
838 @param remote_path: Host path
lmr912c74b2009-08-17 20:43:27 +0000839 @param nic_index: The index of the NIC to connect to.
lmr6f669ce2009-05-31 19:02:42 +0000840 @param timeout: Time (seconds) before giving up on doing the remote
841 copy.
842 """
843 username = self.params.get("username", "")
844 password = self.params.get("password", "")
lmr912c74b2009-08-17 20:43:27 +0000845 client = self.params.get("file_transfer_client")
lmree90dd92009-08-13 04:13:39 +0000846 address = self.get_address(nic_index)
lmr912c74b2009-08-17 20:43:27 +0000847 port = self.get_port(int(self.params.get("file_transfer_port")))
848
lmree90dd92009-08-13 04:13:39 +0000849 if not address or not port:
850 logging.debug("IP address or port unavailable")
lmr6f669ce2009-05-31 19:02:42 +0000851 return None
lmr6f669ce2009-05-31 19:02:42 +0000852
lmr912c74b2009-08-17 20:43:27 +0000853 if client == "scp":
854 return kvm_utils.scp_from_remote(address, port, username, password,
855 remote_path, local_path, timeout)
lmr6f669ce2009-05-31 19:02:42 +0000856
857
858 def send_key(self, keystr):
859 """
860 Send a key event to the VM.
861
862 @param: keystr: A key event string (e.g. "ctrl-alt-delete")
863 """
864 # For compatibility with versions of QEMU that do not recognize all
865 # key names: replace keyname with the hex value from the dict, which
866 # QEMU will definitely accept
867 dict = { "comma": "0x33",
868 "dot": "0x34",
869 "slash": "0x35" }
870 for key in dict.keys():
871 keystr = keystr.replace(key, dict[key])
872 self.send_monitor_cmd("sendkey %s 1" % keystr)
873 time.sleep(0.2)
874
875
876 def send_string(self, str):
877 """
878 Send a string to the VM.
879
880 @param str: String, that must consist of alphanumeric characters only.
881 Capital letters are allowed.
882 """
883 for char in str:
884 if char.isupper():
885 self.send_key("shift-%s" % char.lower())
886 else:
887 self.send_key(char)
lmra2533222009-07-20 12:43:46 +0000888
mbligh1ef218d2009-08-03 16:57:56 +0000889
lmra2533222009-07-20 12:43:46 +0000890 def get_uuid(self):
891 """
892 Catch UUID of the VM.
893
894 @return: None,if not specified in config file
895 """
896 if self.params.get("uuid") == "random":
897 return self.uuid
898 else:
899 return self.params.get("uuid", None)
lmrdd2ff922009-12-01 23:39:12 +0000900
901
902 def get_cpu_count(self):
903 """
904 Get the cpu count of the VM.
905 """
lmr13426552010-01-17 15:38:41 +0000906 session = self.remote_login()
907 if not session:
908 return None
lmrdd2ff922009-12-01 23:39:12 +0000909 try:
lmr13426552010-01-17 15:38:41 +0000910 cmd = self.params.get("cpu_chk_cmd")
911 s, count = session.get_command_status_output(cmd)
912 if s == 0:
913 return int(count)
lmrdd2ff922009-12-01 23:39:12 +0000914 return None
915 finally:
916 session.close()
917
918
lmr28426c82010-04-16 06:02:58 +0000919 def get_memory_size(self, cmd=None):
lmrdd2ff922009-12-01 23:39:12 +0000920 """
lmr28426c82010-04-16 06:02:58 +0000921 Get bootup memory size of the VM.
922
923 @param check_cmd: Command used to check memory. If not provided,
924 self.params.get("mem_chk_cmd") will be used.
lmrdd2ff922009-12-01 23:39:12 +0000925 """
lmr13426552010-01-17 15:38:41 +0000926 session = self.remote_login()
927 if not session:
lmrdd2ff922009-12-01 23:39:12 +0000928 return None
lmr13426552010-01-17 15:38:41 +0000929 try:
lmr28426c82010-04-16 06:02:58 +0000930 if not cmd:
931 cmd = self.params.get("mem_chk_cmd")
lmr13426552010-01-17 15:38:41 +0000932 s, mem_str = session.get_command_status_output(cmd)
933 if s != 0:
934 return None
lmr6d69f4d2010-02-12 11:35:55 +0000935 mem = re.findall("([0-9]+)", mem_str)
lmr13426552010-01-17 15:38:41 +0000936 mem_size = 0
937 for m in mem:
938 mem_size += int(m)
939 if "GB" in mem_str:
940 mem_size *= 1024
941 elif "MB" in mem_str:
942 pass
943 else:
944 mem_size /= 1024
945 return int(mem_size)
lmrdd2ff922009-12-01 23:39:12 +0000946 finally:
947 session.close()
lmr28426c82010-04-16 06:02:58 +0000948
949
950 def get_current_memory_size(self):
951 """
952 Get current memory size of the VM, rather than bootup memory.
953 """
954 cmd = self.params.get("mem_chk_cur_cmd")
955 return self.get_memory_size(cmd)