blob: f3240c7095f62c1ac9bb0473dacbc387cd17c7a4 [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
10
lmr6f669ce2009-05-31 19:02:42 +000011
lmr90b9fd52009-08-17 20:48:18 +000012def get_image_filename(params, root_dir):
lmr6f669ce2009-05-31 19:02:42 +000013 """
lmr90b9fd52009-08-17 20:48:18 +000014 Generate an image path from params and root_dir.
lmr6f669ce2009-05-31 19:02:42 +000015
16 @param params: Dictionary containing the test parameters.
lmr90b9fd52009-08-17 20:48:18 +000017 @param root_dir: Base directory for relative filenames.
lmr6f669ce2009-05-31 19:02:42 +000018
19 @note: params should contain:
20 image_name -- the name of the image file, without extension
21 image_format -- the format of the image (qcow2, raw etc)
22 """
23 image_name = params.get("image_name", "image")
24 image_format = params.get("image_format", "qcow2")
25 image_filename = "%s.%s" % (image_name, image_format)
lmr90b9fd52009-08-17 20:48:18 +000026 image_filename = kvm_utils.get_path(root_dir, image_filename)
lmr6f669ce2009-05-31 19:02:42 +000027 return image_filename
28
29
lmr52800ba2009-08-17 20:49:58 +000030def create_image(params, root_dir):
lmr6f669ce2009-05-31 19:02:42 +000031 """
32 Create an image using qemu_image.
33
34 @param params: Dictionary containing the test parameters.
lmr90b9fd52009-08-17 20:48:18 +000035 @param root_dir: Base directory for relative filenames.
lmr6f669ce2009-05-31 19:02:42 +000036
37 @note: params should contain:
38 image_name -- the name of the image file, without extension
39 image_format -- the format of the image (qcow2, raw etc)
40 image_size -- the requested size of the image (a string
41 qemu-img can understand, such as '10G')
42 """
lmr52800ba2009-08-17 20:49:58 +000043 qemu_img_cmd = kvm_utils.get_path(root_dir, params.get("qemu_img_binary",
44 "qemu-img"))
lmr6f669ce2009-05-31 19:02:42 +000045 qemu_img_cmd += " create"
46
47 format = params.get("image_format", "qcow2")
48 qemu_img_cmd += " -f %s" % format
49
lmr90b9fd52009-08-17 20:48:18 +000050 image_filename = get_image_filename(params, root_dir)
lmr6f669ce2009-05-31 19:02:42 +000051 qemu_img_cmd += " %s" % image_filename
52
53 size = params.get("image_size", "10G")
54 qemu_img_cmd += " %s" % size
55
56 logging.debug("Running qemu-img command:\n%s" % qemu_img_cmd)
lmra4967622009-07-23 01:36:32 +000057 (status, output) = kvm_subprocess.run_fg(qemu_img_cmd, logging.debug,
lmr1e36c522009-10-05 19:15:03 +000058 "(qemu-img) ", timeout=120)
lmr6f669ce2009-05-31 19:02:42 +000059
lmra4967622009-07-23 01:36:32 +000060 if status is None:
61 logging.error("Timeout elapsed while waiting for qemu-img command "
62 "to complete:\n%s" % qemu_img_cmd)
63 return None
64 elif status != 0:
65 logging.error("Could not create image; "
66 "qemu-img command failed:\n%s" % qemu_img_cmd)
67 logging.error("Status: %s" % status)
68 logging.error("Output:" + kvm_utils.format_str_for_message(output))
lmr6f669ce2009-05-31 19:02:42 +000069 return None
70 if not os.path.exists(image_filename):
lmra4967622009-07-23 01:36:32 +000071 logging.error("Image could not be created for some reason; "
72 "qemu-img command:\n%s" % qemu_img_cmd)
lmr6f669ce2009-05-31 19:02:42 +000073 return None
74
75 logging.info("Image created in %s" % image_filename)
76 return image_filename
77
78
lmr90b9fd52009-08-17 20:48:18 +000079def remove_image(params, root_dir):
lmr6f669ce2009-05-31 19:02:42 +000080 """
81 Remove an image file.
82
83 @param params: A dict
lmr90b9fd52009-08-17 20:48:18 +000084 @param root_dir: Base directory for relative filenames.
lmr6f669ce2009-05-31 19:02:42 +000085
86 @note: params should contain:
87 image_name -- the name of the image file, without extension
88 image_format -- the format of the image (qcow2, raw etc)
89 """
lmr90b9fd52009-08-17 20:48:18 +000090 image_filename = get_image_filename(params, root_dir)
lmr6f669ce2009-05-31 19:02:42 +000091 logging.debug("Removing image file %s..." % image_filename)
92 if os.path.exists(image_filename):
93 os.unlink(image_filename)
94 else:
95 logging.debug("Image file %s not found")
96
97
98class VM:
99 """
100 This class handles all basic VM operations.
101 """
102
lmr52800ba2009-08-17 20:49:58 +0000103 def __init__(self, name, params, root_dir, address_cache):
lmr6f669ce2009-05-31 19:02:42 +0000104 """
105 Initialize the object and set a few attributes.
106
107 @param name: The name of the object
108 @param params: A dict containing VM params
109 (see method make_qemu_command for a full description)
lmr90b9fd52009-08-17 20:48:18 +0000110 @param root_dir: Base directory for relative filenames
lmree90dd92009-08-13 04:13:39 +0000111 @param address_cache: A dict that maps MAC addresses to IP addresses
lmr6f669ce2009-05-31 19:02:42 +0000112 """
lmra4967622009-07-23 01:36:32 +0000113 self.process = None
lmr953ffba2009-07-27 13:20:10 +0000114 self.redirs = {}
115 self.vnc_port = 5900
lmra2533222009-07-20 12:43:46 +0000116 self.uuid = None
lmr6f669ce2009-05-31 19:02:42 +0000117
118 self.name = name
119 self.params = params
lmr90b9fd52009-08-17 20:48:18 +0000120 self.root_dir = root_dir
lmree90dd92009-08-13 04:13:39 +0000121 self.address_cache = address_cache
lmr6f669ce2009-05-31 19:02:42 +0000122
lmr8b134f92009-06-08 14:47:31 +0000123 # Find available monitor filename
124 while True:
125 # The monitor filename should be unique
lmrd16a67d2009-06-10 19:52:59 +0000126 self.instance = (time.strftime("%Y%m%d-%H%M%S-") +
127 kvm_utils.generate_random_string(4))
lmr8b134f92009-06-08 14:47:31 +0000128 self.monitor_file_name = os.path.join("/tmp",
129 "monitor-" + self.instance)
130 if not os.path.exists(self.monitor_file_name):
131 break
132
133
lmr52800ba2009-08-17 20:49:58 +0000134 def clone(self, name=None, params=None, root_dir=None, address_cache=None):
lmr2c241172009-06-08 15:11:29 +0000135 """
136 Return a clone of the VM object with optionally modified parameters.
137 The clone is initially not alive and needs to be started using create().
138 Any parameters not passed to this function are copied from the source
139 VM.
140
141 @param name: Optional new VM name
142 @param params: Optional new VM creation parameters
lmr90b9fd52009-08-17 20:48:18 +0000143 @param root_dir: Optional new base directory for relative filenames
lmree90dd92009-08-13 04:13:39 +0000144 @param address_cache: A dict that maps MAC addresses to IP addresses
lmr2c241172009-06-08 15:11:29 +0000145 """
146 if name == None:
147 name = self.name
148 if params == None:
149 params = self.params.copy()
lmr90b9fd52009-08-17 20:48:18 +0000150 if root_dir == None:
151 root_dir = self.root_dir
lmree90dd92009-08-13 04:13:39 +0000152 if address_cache == None:
153 address_cache = self.address_cache
lmr52800ba2009-08-17 20:49:58 +0000154 return VM(name, params, root_dir, address_cache)
lmr2c241172009-06-08 15:11:29 +0000155
156
lmr52800ba2009-08-17 20:49:58 +0000157 def make_qemu_command(self, name=None, params=None, root_dir=None):
lmr6f669ce2009-05-31 19:02:42 +0000158 """
159 Generate a qemu command line. All parameters are optional. If a
160 parameter is not supplied, the corresponding value stored in the
161 class attributes is used.
162
lmr6f669ce2009-05-31 19:02:42 +0000163 @param name: The name of the object
164 @param params: A dict containing VM params
lmr90b9fd52009-08-17 20:48:18 +0000165 @param root_dir: Base directory for relative filenames
lmr6f669ce2009-05-31 19:02:42 +0000166
167 @note: The params dict should contain:
168 mem -- memory size in MBs
169 cdrom -- ISO filename to use with the qemu -cdrom parameter
lmr6f669ce2009-05-31 19:02:42 +0000170 extra_params -- a string to append to the qemu command
lmr912c74b2009-08-17 20:43:27 +0000171 shell_port -- port of the remote shell daemon on the guest
172 (SSH, Telnet or the home-made Remote Shell Server)
173 shell_client -- client program to use for connecting to the
174 remote shell daemon on the guest (ssh, telnet or nc)
lmreeff0eb2009-06-10 19:19:15 +0000175 x11_display -- if specified, the DISPLAY environment variable
176 will be be set to this value for the qemu process (useful for
177 SDL rendering)
lmr6f669ce2009-05-31 19:02:42 +0000178 images -- a list of image object names, separated by spaces
179 nics -- a list of NIC object names, separated by spaces
180
181 For each image in images:
182 drive_format -- string to pass as 'if' parameter for this
183 image (e.g. ide, scsi)
184 image_snapshot -- if yes, pass 'snapshot=on' to qemu for
185 this image
186 image_boot -- if yes, pass 'boot=on' to qemu for this image
187 In addition, all parameters required by get_image_filename.
188
189 For each NIC in nics:
190 nic_model -- string to pass as 'model' parameter for this
191 NIC (e.g. e1000)
192 """
193 if name == None:
194 name = self.name
195 if params == None:
196 params = self.params
lmr90b9fd52009-08-17 20:48:18 +0000197 if root_dir == None:
198 root_dir = self.root_dir
lmr6f669ce2009-05-31 19:02:42 +0000199
lmreeff0eb2009-06-10 19:19:15 +0000200 # Start constructing the qemu command
201 qemu_cmd = ""
202 # Set the X11 display parameter if requested
203 if params.get("x11_display"):
204 qemu_cmd += "DISPLAY=%s " % params.get("x11_display")
205 # Add the qemu binary
lmr52800ba2009-08-17 20:49:58 +0000206 qemu_cmd += kvm_utils.get_path(root_dir, params.get("qemu_binary",
207 "qemu"))
lmreeff0eb2009-06-10 19:19:15 +0000208 # Add the VM's name
lmr6f669ce2009-05-31 19:02:42 +0000209 qemu_cmd += " -name '%s'" % name
lmreeff0eb2009-06-10 19:19:15 +0000210 # Add the monitor socket parameter
lmr6f669ce2009-05-31 19:02:42 +0000211 qemu_cmd += " -monitor unix:%s,server,nowait" % self.monitor_file_name
212
213 for image_name in kvm_utils.get_sub_dict_names(params, "images"):
214 image_params = kvm_utils.get_sub_dict(params, image_name)
lmr9d75ee32009-09-29 13:14:13 +0000215 if image_params.get("boot_drive") == "no":
216 continue
lmr6f669ce2009-05-31 19:02:42 +0000217 qemu_cmd += " -drive file=%s" % get_image_filename(image_params,
lmr90b9fd52009-08-17 20:48:18 +0000218 root_dir)
lmr6f669ce2009-05-31 19:02:42 +0000219 if image_params.get("drive_format"):
220 qemu_cmd += ",if=%s" % image_params.get("drive_format")
lmr0d4d2742009-06-18 06:15:17 +0000221 if image_params.get("drive_cache"):
222 qemu_cmd += ",cache=%s" % image_params.get("drive_cache")
223 if image_params.get("drive_serial"):
224 qemu_cmd += ",serial=%s" % image_params.get("drive_serial")
lmr6f669ce2009-05-31 19:02:42 +0000225 if image_params.get("image_snapshot") == "yes":
226 qemu_cmd += ",snapshot=on"
227 if image_params.get("image_boot") == "yes":
228 qemu_cmd += ",boot=on"
229
230 vlan = 0
231 for nic_name in kvm_utils.get_sub_dict_names(params, "nics"):
232 nic_params = kvm_utils.get_sub_dict(params, nic_name)
lmrf4696342009-08-13 04:06:33 +0000233 # Handle the '-net nic' part
lmr6f669ce2009-05-31 19:02:42 +0000234 qemu_cmd += " -net nic,vlan=%d" % vlan
235 if nic_params.get("nic_model"):
236 qemu_cmd += ",model=%s" % nic_params.get("nic_model")
lmrf4696342009-08-13 04:06:33 +0000237 if nic_params.has_key("address_index"):
238 mac, ip = kvm_utils.get_mac_ip_pair_from_dict(nic_params)
lmr71247002009-09-10 03:13:34 +0000239 if mac:
240 qemu_cmd += ",macaddr=%s" % mac
lmrf4696342009-08-13 04:06:33 +0000241 # Handle the '-net tap' or '-net user' part
242 mode = nic_params.get("nic_mode", "user")
243 qemu_cmd += " -net %s,vlan=%d" % (mode, vlan)
244 if mode == "tap":
245 if nic_params.get("nic_ifname"):
246 qemu_cmd += ",ifname=%s" % nic_params.get("nic_ifname")
lmr90b9fd52009-08-17 20:48:18 +0000247 script_path = nic_params.get("nic_script")
248 if script_path:
249 script_path = kvm_utils.get_path(root_dir, script_path)
lmrf4696342009-08-13 04:06:33 +0000250 qemu_cmd += ",script=%s" % script_path
lmr90b9fd52009-08-17 20:48:18 +0000251 script_path = nic_params.get("nic_downscript")
252 if script_path:
253 script_path = kvm_utils.get_path(root_dir, script_path)
lmrf4696342009-08-13 04:06:33 +0000254 qemu_cmd += ",downscript=%s" % script_path
lmr620b8bc2009-10-23 12:12:53 +0000255 else:
256 qemu_cmd += ",downscript=no"
lmrf4696342009-08-13 04:06:33 +0000257 # Proceed to next NIC
lmr6f669ce2009-05-31 19:02:42 +0000258 vlan += 1
259
260 mem = params.get("mem")
261 if mem:
262 qemu_cmd += " -m %s" % mem
263
lmrc43bf372009-11-10 13:19:00 +0000264 smp = params.get("smp")
265 if smp:
266 qemu_cmd += " -smp %s" % smp
267
lmr6f669ce2009-05-31 19:02:42 +0000268 iso = params.get("cdrom")
269 if iso:
lmr90b9fd52009-08-17 20:48:18 +0000270 iso = kvm_utils.get_path(root_dir, iso)
lmr6f669ce2009-05-31 19:02:42 +0000271 qemu_cmd += " -cdrom %s" % iso
272
lmrb0a9b762009-10-09 20:43:30 +0000273 # We may want to add {floppy_otps} parameter for -fda
274 # {fat:floppy:}/path/. However vvfat is not usually recommended
275 floppy = params.get("floppy")
276 if floppy:
lmrf69f6b12009-11-10 16:33:44 +0000277 floppy = kvm_utils.get_path(root_dir, floppy)
lmrb0a9b762009-10-09 20:43:30 +0000278 qemu_cmd += " -fda %s" % floppy
279
280 tftp = params.get("tftp")
281 if tftp:
lmrf69f6b12009-11-10 16:33:44 +0000282 tftp = kvm_utils.get_path(root_dir, tftp)
lmrb0a9b762009-10-09 20:43:30 +0000283 qemu_cmd += " -tftp %s" % tftp
284
lmr6f669ce2009-05-31 19:02:42 +0000285 extra_params = params.get("extra_params")
286 if extra_params:
287 qemu_cmd += " %s" % extra_params
288
289 for redir_name in kvm_utils.get_sub_dict_names(params, "redirs"):
290 redir_params = kvm_utils.get_sub_dict(params, redir_name)
291 guest_port = int(redir_params.get("guest_port"))
lmrf4696342009-08-13 04:06:33 +0000292 host_port = self.redirs.get(guest_port)
lmr6f669ce2009-05-31 19:02:42 +0000293 qemu_cmd += " -redir tcp:%s::%s" % (host_port, guest_port)
294
295 if params.get("display") == "vnc":
296 qemu_cmd += " -vnc :%d" % (self.vnc_port - 5900)
297 elif params.get("display") == "sdl":
298 qemu_cmd += " -sdl"
299 elif params.get("display") == "nographic":
300 qemu_cmd += " -nographic"
301
lmra2533222009-07-20 12:43:46 +0000302 if params.get("uuid") == "random":
303 qemu_cmd += " -uuid %s" % self.uuid
304 elif params.get("uuid"):
305 qemu_cmd += " -uuid %s" % params.get("uuid")
306
lmr6f669ce2009-05-31 19:02:42 +0000307 return qemu_cmd
308
309
lmr52800ba2009-08-17 20:49:58 +0000310 def create(self, name=None, params=None, root_dir=None,
lmr90b9fd52009-08-17 20:48:18 +0000311 for_migration=False, timeout=5.0):
lmr6f669ce2009-05-31 19:02:42 +0000312 """
313 Start the VM by running a qemu command.
314 All parameters are optional. The following applies to all parameters
315 but for_migration: If a parameter is not supplied, the corresponding
316 value stored in the class attributes is used, and if it is supplied,
317 it is stored for later use.
318
319 @param name: The name of the object
320 @param params: A dict containing VM params
lmr90b9fd52009-08-17 20:48:18 +0000321 @param root_dir: Base directory for relative filenames
lmr6f669ce2009-05-31 19:02:42 +0000322 @param for_migration: If True, start the VM with the -incoming
323 option
324 """
lmr135b5e62009-06-10 19:22:31 +0000325 self.destroy()
326
lmr6f669ce2009-05-31 19:02:42 +0000327 if name != None:
328 self.name = name
329 if params != None:
330 self.params = params
lmr90b9fd52009-08-17 20:48:18 +0000331 if root_dir != None:
332 self.root_dir = root_dir
lmr6f669ce2009-05-31 19:02:42 +0000333 name = self.name
334 params = self.params
lmr90b9fd52009-08-17 20:48:18 +0000335 root_dir = self.root_dir
lmr6f669ce2009-05-31 19:02:42 +0000336
337 # Verify the md5sum of the ISO image
338 iso = params.get("cdrom")
339 if iso:
lmr90b9fd52009-08-17 20:48:18 +0000340 iso = kvm_utils.get_path(root_dir, iso)
lmr6f669ce2009-05-31 19:02:42 +0000341 if not os.path.exists(iso):
342 logging.error("ISO file not found: %s" % iso)
343 return False
344 compare = False
345 if params.get("md5sum_1m"):
lmrf4696342009-08-13 04:06:33 +0000346 logging.debug("Comparing expected MD5 sum with MD5 sum of "
347 "first MB of ISO file...")
lmr03ba22e2009-10-23 12:07:44 +0000348 actual_hash = kvm_utils.hash_file(iso, 1048576, method="md5")
349 expected_hash = params.get("md5sum_1m")
lmr6f669ce2009-05-31 19:02:42 +0000350 compare = True
351 elif params.get("md5sum"):
lmrf4696342009-08-13 04:06:33 +0000352 logging.debug("Comparing expected MD5 sum with MD5 sum of ISO "
353 "file...")
lmr03ba22e2009-10-23 12:07:44 +0000354 actual_hash = kvm_utils.md5sum_file(iso, method="md5")
355 expected_hash = params.get("md5sum")
356 compare = True
357 elif params.get("sha1sum"):
358 logging.debug("Comparing expected SHA1 sum with SHA1 sum of "
359 "ISO file...")
360 actual_hash = kvm_utils.md5sum_file(iso, method="sha1")
361 expected_hash = params.get("md5sum")
lmr6f669ce2009-05-31 19:02:42 +0000362 compare = True
363 if compare:
lmr03ba22e2009-10-23 12:07:44 +0000364 if actual_hash == expected_hash:
365 logging.debug("Hashes match")
lmr6f669ce2009-05-31 19:02:42 +0000366 else:
lmr03ba22e2009-10-23 12:07:44 +0000367 logging.error("Actual hash differs from expected one")
lmr6f669ce2009-05-31 19:02:42 +0000368 return False
369
lmrdc2ac6a2009-06-10 19:15:49 +0000370 # Make sure the following code is not executed by more than one thread
371 # at the same time
372 lockfile = open("/tmp/kvm-autotest-vm-create.lock", "w+")
373 fcntl.lockf(lockfile, fcntl.LOCK_EX)
lmr6f669ce2009-05-31 19:02:42 +0000374
lmrdc2ac6a2009-06-10 19:15:49 +0000375 try:
376 # Handle port redirections
377 redir_names = kvm_utils.get_sub_dict_names(params, "redirs")
378 host_ports = kvm_utils.find_free_ports(5000, 6000, len(redir_names))
379 self.redirs = {}
380 for i in range(len(redir_names)):
381 redir_params = kvm_utils.get_sub_dict(params, redir_names[i])
382 guest_port = int(redir_params.get("guest_port"))
383 self.redirs[guest_port] = host_ports[i]
lmr6f669ce2009-05-31 19:02:42 +0000384
lmrdc2ac6a2009-06-10 19:15:49 +0000385 # Find available VNC port, if needed
386 if params.get("display") == "vnc":
387 self.vnc_port = kvm_utils.find_free_port(5900, 6000)
lmr6f669ce2009-05-31 19:02:42 +0000388
lmra2533222009-07-20 12:43:46 +0000389 # Find random UUID if specified 'uuid = random' in config file
390 if params.get("uuid") == "random":
391 f = open("/proc/sys/kernel/random/uuid")
392 self.uuid = f.read().strip()
393 f.close()
394
lmrdc2ac6a2009-06-10 19:15:49 +0000395 # Make qemu command
396 qemu_command = self.make_qemu_command()
lmr6f669ce2009-05-31 19:02:42 +0000397
lmrdc2ac6a2009-06-10 19:15:49 +0000398 # Is this VM supposed to accept incoming migrations?
399 if for_migration:
400 # Find available migration port
401 self.migration_port = kvm_utils.find_free_port(5200, 6000)
402 # Add -incoming option to the qemu command
403 qemu_command += " -incoming tcp:0:%d" % self.migration_port
lmr6f669ce2009-05-31 19:02:42 +0000404
lmrdc2ac6a2009-06-10 19:15:49 +0000405 logging.debug("Running qemu command:\n%s", qemu_command)
lmra4967622009-07-23 01:36:32 +0000406 self.process = kvm_subprocess.run_bg(qemu_command, None,
407 logging.debug, "(qemu) ")
lmr6f669ce2009-05-31 19:02:42 +0000408
lmra4967622009-07-23 01:36:32 +0000409 if not self.process.is_alive():
410 logging.error("VM could not be created; "
411 "qemu command failed:\n%s" % qemu_command)
412 logging.error("Status: %s" % self.process.get_status())
413 logging.error("Output:" + kvm_utils.format_str_for_message(
414 self.process.get_output()))
lmrdc2ac6a2009-06-10 19:15:49 +0000415 self.destroy()
416 return False
lmr6f669ce2009-05-31 19:02:42 +0000417
lmra4967622009-07-23 01:36:32 +0000418 if not kvm_utils.wait_for(self.is_alive, timeout, 0, 1):
419 logging.error("VM is not alive for some reason; "
420 "qemu command:\n%s" % qemu_command)
421 self.destroy()
422 return False
423
lmrfe6515e2009-07-29 13:01:17 +0000424 # Get the output so far, to see if we have any problems with
425 # hugepage setup.
426 output = self.process.get_output()
427
428 if "alloc_mem_area" in output:
429 logging.error("Could not allocate hugepage memory; "
430 "qemu command:\n%s" % qemu_command)
431 logging.error("Output:" + kvm_utils.format_str_for_message(
432 self.process.get_output()))
433 return False
434
lmra4967622009-07-23 01:36:32 +0000435 logging.debug("VM appears to be alive with PID %d",
436 self.process.get_pid())
lmrdc2ac6a2009-06-10 19:15:49 +0000437 return True
438
439 finally:
440 fcntl.lockf(lockfile, fcntl.LOCK_UN)
441 lockfile.close()
lmr6f669ce2009-05-31 19:02:42 +0000442
443
444 def send_monitor_cmd(self, command, block=True, timeout=20.0):
445 """
446 Send command to the QEMU monitor.
447
448 Connect to the VM's monitor socket and wait for the (qemu) prompt.
449 If block is True, read output from the socket until the (qemu) prompt
450 is found again, or until timeout expires.
451
452 Return a tuple containing an integer indicating success or failure,
453 and the data read so far. The integer is 0 on success and 1 on failure.
454 A failure is any of the following cases: connection to the socket
455 failed, or the first (qemu) prompt could not be found, or block is
456 True and the second prompt could not be found.
457
458 @param command: Command that will be sent to the monitor
459 @param block: Whether the output from the socket will be read until
460 the timeout expires
461 @param timeout: Timeout (seconds) before giving up on reading from
462 socket
463 """
464 def read_up_to_qemu_prompt(s, timeout):
465 """
466 Read data from socket s until the (qemu) prompt is found.
467
468 If the prompt is found before timeout expires, return a tuple
469 containing True and the data read. Otherwise return a tuple
470 containing False and the data read so far.
471
472 @param s: Socket object
473 @param timeout: Time (seconds) before giving up trying to get the
474 qemu prompt.
475 """
476 o = ""
477 end_time = time.time() + timeout
478 while time.time() < end_time:
479 try:
lmrbcd20832009-10-15 11:56:56 +0000480 o += s.recv(1024)
lmr6f669ce2009-05-31 19:02:42 +0000481 if o.splitlines()[-1].split()[-1] == "(qemu)":
482 return (True, o)
483 except:
484 time.sleep(0.01)
485 return (False, o)
486
487 # Connect to monitor
488 logging.debug("Sending monitor command: %s" % command)
489 try:
490 s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
491 s.setblocking(False)
492 s.connect(self.monitor_file_name)
493 except:
494 logging.debug("Could not connect to monitor socket")
495 return (1, "")
lmrbcd20832009-10-15 11:56:56 +0000496
497 # Send the command and get the resulting output
498 try:
lmr6f669ce2009-05-31 19:02:42 +0000499 status, data = read_up_to_qemu_prompt(s, timeout)
lmr6f669ce2009-05-31 19:02:42 +0000500 if not status:
lmrbcd20832009-10-15 11:56:56 +0000501 logging.debug("Could not find (qemu) prompt; output so far:" +
502 kvm_utils.format_str_for_message(data))
503 return (1, "")
504 # Send command
505 s.sendall(command + "\n")
506 # Receive command output
507 data = ""
508 if block:
509 status, data = read_up_to_qemu_prompt(s, timeout)
510 data = "\n".join(data.splitlines()[1:])
511 if not status:
512 logging.debug("Could not find (qemu) prompt after command; "
513 "output so far:" +
514 kvm_utils.format_str_for_message(data))
515 return (1, data)
516 return (0, data)
517
518 # Clean up before exiting
519 finally:
520 s.shutdown(socket.SHUT_RDWR)
521 s.close()
lmr6f669ce2009-05-31 19:02:42 +0000522
523
524 def destroy(self, gracefully=True):
525 """
526 Destroy the VM.
527
lmr912c74b2009-08-17 20:43:27 +0000528 If gracefully is True, first attempt to shutdown the VM with a shell
529 command. Then, attempt to destroy the VM via the monitor with a 'quit'
530 command. If that fails, send SIGKILL to the qemu process.
lmr6f669ce2009-05-31 19:02:42 +0000531
532 @param gracefully: Whether an attempt will be made to end the VM
lmr912c74b2009-08-17 20:43:27 +0000533 using a shell command before trying to end the qemu process
534 with a 'quit' or a kill signal.
lmr6f669ce2009-05-31 19:02:42 +0000535 """
lmrf320b042009-09-15 05:48:06 +0000536 try:
537 # Is it already dead?
538 if self.is_dead():
539 logging.debug("VM is already down")
540 return
lmr6f669ce2009-05-31 19:02:42 +0000541
lmrf320b042009-09-15 05:48:06 +0000542 logging.debug("Destroying VM with PID %d..." %
543 self.process.get_pid())
lmr6f669ce2009-05-31 19:02:42 +0000544
lmrf320b042009-09-15 05:48:06 +0000545 if gracefully and self.params.get("shutdown_command"):
546 # Try to destroy with shell command
547 logging.debug("Trying to shutdown VM with shell command...")
548 session = self.remote_login()
549 if session:
550 try:
551 # Send the shutdown command
552 session.sendline(self.params.get("shutdown_command"))
553 logging.debug("Shutdown command sent; waiting for VM "
554 "to go down...")
555 if kvm_utils.wait_for(self.is_dead, 60, 1, 1):
556 logging.debug("VM is down")
557 return
558 finally:
559 session.close()
lmr6f669ce2009-05-31 19:02:42 +0000560
lmrf320b042009-09-15 05:48:06 +0000561 # Try to destroy with a monitor command
562 logging.debug("Trying to kill VM with monitor command...")
563 status, output = self.send_monitor_cmd("quit", block=False)
564 # Was the command sent successfully?
565 if status == 0:
566 # Wait for the VM to be really dead
567 if kvm_utils.wait_for(self.is_dead, 5, 0.5, 0.5):
568 logging.debug("VM is down")
569 return
570
571 # If the VM isn't dead yet...
572 logging.debug("Cannot quit normally; sending a kill to close the "
573 "deal...")
574 kvm_utils.kill_process_tree(self.process.get_pid(), 9)
lmr6f669ce2009-05-31 19:02:42 +0000575 # Wait for the VM to be really dead
576 if kvm_utils.wait_for(self.is_dead, 5, 0.5, 0.5):
577 logging.debug("VM is down")
lmr6f669ce2009-05-31 19:02:42 +0000578 return
579
lmrf320b042009-09-15 05:48:06 +0000580 logging.error("Process %s is a zombie!" % self.process.get_pid())
lmr6f669ce2009-05-31 19:02:42 +0000581
lmrf320b042009-09-15 05:48:06 +0000582 finally:
583 if self.process:
584 self.process.close()
lmr6f669ce2009-05-31 19:02:42 +0000585
586
587 def is_alive(self):
588 """
589 Return True if the VM's monitor is responsive.
590 """
lmra4967622009-07-23 01:36:32 +0000591 # Check if the process is running
592 if self.is_dead():
lmr6f669ce2009-05-31 19:02:42 +0000593 return False
594 # Try sending a monitor command
595 (status, output) = self.send_monitor_cmd("help")
596 if status:
597 return False
598 return True
599
600
601 def is_dead(self):
602 """
lmra4967622009-07-23 01:36:32 +0000603 Return True if the qemu process is dead.
lmr6f669ce2009-05-31 19:02:42 +0000604 """
lmra4967622009-07-23 01:36:32 +0000605 return not self.process or not self.process.is_alive()
lmr6f669ce2009-05-31 19:02:42 +0000606
607
lmra4197002009-08-13 05:00:51 +0000608 def kill_tail_thread(self):
609 """
610 Stop the tailing thread which reports the output of qemu.
611 """
612 if self.process:
613 self.process.kill_tail_thread()
614
615
lmr6f669ce2009-05-31 19:02:42 +0000616 def get_params(self):
617 """
618 Return the VM's params dict. Most modified params take effect only
619 upon VM.create().
620 """
621 return self.params
622
623
lmrf4696342009-08-13 04:06:33 +0000624 def get_address(self, index=0):
lmr6f669ce2009-05-31 19:02:42 +0000625 """
lmrf4696342009-08-13 04:06:33 +0000626 Return the address of a NIC of the guest, in host space.
lmr6f669ce2009-05-31 19:02:42 +0000627
lmrf4696342009-08-13 04:06:33 +0000628 If port redirection is used, return 'localhost' (the NIC has no IP
629 address of its own). Otherwise return the NIC's IP address.
630
631 @param index: Index of the NIC whose address is requested.
lmr6f669ce2009-05-31 19:02:42 +0000632 """
lmree90dd92009-08-13 04:13:39 +0000633 nics = kvm_utils.get_sub_dict_names(self.params, "nics")
634 nic_name = nics[index]
lmrf4696342009-08-13 04:06:33 +0000635 nic_params = kvm_utils.get_sub_dict(self.params, nic_name)
636 if nic_params.get("nic_mode") == "tap":
637 mac, ip = kvm_utils.get_mac_ip_pair_from_dict(nic_params)
lmree90dd92009-08-13 04:13:39 +0000638 if not mac:
639 logging.debug("MAC address unavailable")
640 return None
641 if not ip or nic_params.get("always_use_tcpdump") == "yes":
642 # Get the IP address from the cache
643 ip = self.address_cache.get(mac)
644 if not ip:
645 logging.debug("Could not find IP address for MAC address: "
646 "%s" % mac)
647 return None
648 # Make sure the IP address is assigned to this guest
649 nic_dicts = [kvm_utils.get_sub_dict(self.params, nic)
650 for nic in nics]
651 macs = [kvm_utils.get_mac_ip_pair_from_dict(dict)[0]
652 for dict in nic_dicts]
653 if not kvm_utils.verify_ip_address_ownership(ip, macs):
654 logging.debug("Could not verify MAC-IP address mapping: "
655 "%s ---> %s" % (mac, ip))
656 return None
lmrf4696342009-08-13 04:06:33 +0000657 return ip
658 else:
659 return "localhost"
lmr6f669ce2009-05-31 19:02:42 +0000660
661
lmree90dd92009-08-13 04:13:39 +0000662 def get_port(self, port, nic_index=0):
lmr6f669ce2009-05-31 19:02:42 +0000663 """
664 Return the port in host space corresponding to port in guest space.
665
666 @param port: Port number in host space.
lmree90dd92009-08-13 04:13:39 +0000667 @param nic_index: Index of the NIC.
lmr6f669ce2009-05-31 19:02:42 +0000668 @return: If port redirection is used, return the host port redirected
669 to guest port port. Otherwise return port.
670 """
lmree90dd92009-08-13 04:13:39 +0000671 nic_name = kvm_utils.get_sub_dict_names(self.params, "nics")[nic_index]
lmrf4696342009-08-13 04:06:33 +0000672 nic_params = kvm_utils.get_sub_dict(self.params, nic_name)
673 if nic_params.get("nic_mode") == "tap":
674 return port
lmr6f669ce2009-05-31 19:02:42 +0000675 else:
lmrf4696342009-08-13 04:06:33 +0000676 if not self.redirs.has_key(port):
677 logging.warn("Warning: guest port %s requested but not "
678 "redirected" % port)
679 return self.redirs.get(port)
lmr6f669ce2009-05-31 19:02:42 +0000680
681
lmra4967622009-07-23 01:36:32 +0000682 def get_pid(self):
683 """
684 Return the VM's PID.
685 """
686 return self.process.get_pid()
687
688
lmr912c74b2009-08-17 20:43:27 +0000689 def remote_login(self, nic_index=0, timeout=10):
lmr6f669ce2009-05-31 19:02:42 +0000690 """
lmr912c74b2009-08-17 20:43:27 +0000691 Log into the guest via SSH/Telnet/Netcat.
lmr6f669ce2009-05-31 19:02:42 +0000692 If timeout expires while waiting for output from the guest (e.g. a
693 password prompt or a shell prompt) -- fail.
694
lmree90dd92009-08-13 04:13:39 +0000695 @param nic_index: The index of the NIC to connect to.
lmr6f669ce2009-05-31 19:02:42 +0000696 @param timeout: Time (seconds) before giving up logging into the
697 guest.
698 @return: kvm_spawn object on success and None on failure.
699 """
700 username = self.params.get("username", "")
701 password = self.params.get("password", "")
lmr912c74b2009-08-17 20:43:27 +0000702 prompt = self.params.get("shell_prompt", "[\#\$]")
lmr59f9e2d2009-10-05 18:47:16 +0000703 linesep = eval("'%s'" % self.params.get("shell_linesep", r"\n"))
lmr912c74b2009-08-17 20:43:27 +0000704 client = self.params.get("shell_client")
lmree90dd92009-08-13 04:13:39 +0000705 address = self.get_address(nic_index)
lmr912c74b2009-08-17 20:43:27 +0000706 port = self.get_port(int(self.params.get("shell_port")))
707
lmree90dd92009-08-13 04:13:39 +0000708 if not address or not port:
709 logging.debug("IP address or port unavailable")
lmr6f669ce2009-05-31 19:02:42 +0000710 return None
711
lmr912c74b2009-08-17 20:43:27 +0000712 if client == "ssh":
lmr6f669ce2009-05-31 19:02:42 +0000713 session = kvm_utils.ssh(address, port, username, password,
lmr59f9e2d2009-10-05 18:47:16 +0000714 prompt, linesep, timeout)
lmr912c74b2009-08-17 20:43:27 +0000715 elif client == "telnet":
716 session = kvm_utils.telnet(address, port, username, password,
lmr59f9e2d2009-10-05 18:47:16 +0000717 prompt, linesep, timeout)
lmr9f6ebf12009-08-17 20:44:10 +0000718 elif client == "nc":
719 session = kvm_utils.netcat(address, port, username, password,
lmr59f9e2d2009-10-05 18:47:16 +0000720 prompt, linesep, timeout)
lmr912c74b2009-08-17 20:43:27 +0000721
lmr6f669ce2009-05-31 19:02:42 +0000722 if session:
lmr912c74b2009-08-17 20:43:27 +0000723 session.set_status_test_command(self.params.get("status_test_"
lmr6f669ce2009-05-31 19:02:42 +0000724 "command", ""))
725 return session
726
727
lmr912c74b2009-08-17 20:43:27 +0000728 def copy_files_to(self, local_path, remote_path, nic_index=0, timeout=300):
lmr6f669ce2009-05-31 19:02:42 +0000729 """
lmr912c74b2009-08-17 20:43:27 +0000730 Transfer files to the guest.
lmr6f669ce2009-05-31 19:02:42 +0000731
732 @param local_path: Host path
733 @param remote_path: Guest path
lmr912c74b2009-08-17 20:43:27 +0000734 @param nic_index: The index of the NIC to connect to.
lmr6f669ce2009-05-31 19:02:42 +0000735 @param timeout: Time (seconds) before giving up on doing the remote
736 copy.
737 """
738 username = self.params.get("username", "")
739 password = self.params.get("password", "")
lmr912c74b2009-08-17 20:43:27 +0000740 client = self.params.get("file_transfer_client")
lmree90dd92009-08-13 04:13:39 +0000741 address = self.get_address(nic_index)
lmr912c74b2009-08-17 20:43:27 +0000742 port = self.get_port(int(self.params.get("file_transfer_port")))
743
lmree90dd92009-08-13 04:13:39 +0000744 if not address or not port:
745 logging.debug("IP address or port unavailable")
lmr6f669ce2009-05-31 19:02:42 +0000746 return None
lmr912c74b2009-08-17 20:43:27 +0000747
748 if client == "scp":
749 return kvm_utils.scp_to_remote(address, port, username, password,
750 local_path, remote_path, timeout)
lmr6f669ce2009-05-31 19:02:42 +0000751
752
lmr912c74b2009-08-17 20:43:27 +0000753 def copy_files_from(self, remote_path, local_path, nic_index=0, timeout=300):
lmr6f669ce2009-05-31 19:02:42 +0000754 """
lmr912c74b2009-08-17 20:43:27 +0000755 Transfer files from the guest.
lmr6f669ce2009-05-31 19:02:42 +0000756
757 @param local_path: Guest path
758 @param remote_path: Host path
lmr912c74b2009-08-17 20:43:27 +0000759 @param nic_index: The index of the NIC to connect to.
lmr6f669ce2009-05-31 19:02:42 +0000760 @param timeout: Time (seconds) before giving up on doing the remote
761 copy.
762 """
763 username = self.params.get("username", "")
764 password = self.params.get("password", "")
lmr912c74b2009-08-17 20:43:27 +0000765 client = self.params.get("file_transfer_client")
lmree90dd92009-08-13 04:13:39 +0000766 address = self.get_address(nic_index)
lmr912c74b2009-08-17 20:43:27 +0000767 port = self.get_port(int(self.params.get("file_transfer_port")))
768
lmree90dd92009-08-13 04:13:39 +0000769 if not address or not port:
770 logging.debug("IP address or port unavailable")
lmr6f669ce2009-05-31 19:02:42 +0000771 return None
lmr6f669ce2009-05-31 19:02:42 +0000772
lmr912c74b2009-08-17 20:43:27 +0000773 if client == "scp":
774 return kvm_utils.scp_from_remote(address, port, username, password,
775 remote_path, local_path, timeout)
lmr6f669ce2009-05-31 19:02:42 +0000776
777
778 def send_key(self, keystr):
779 """
780 Send a key event to the VM.
781
782 @param: keystr: A key event string (e.g. "ctrl-alt-delete")
783 """
784 # For compatibility with versions of QEMU that do not recognize all
785 # key names: replace keyname with the hex value from the dict, which
786 # QEMU will definitely accept
787 dict = { "comma": "0x33",
788 "dot": "0x34",
789 "slash": "0x35" }
790 for key in dict.keys():
791 keystr = keystr.replace(key, dict[key])
792 self.send_monitor_cmd("sendkey %s 1" % keystr)
793 time.sleep(0.2)
794
795
796 def send_string(self, str):
797 """
798 Send a string to the VM.
799
800 @param str: String, that must consist of alphanumeric characters only.
801 Capital letters are allowed.
802 """
803 for char in str:
804 if char.isupper():
805 self.send_key("shift-%s" % char.lower())
806 else:
807 self.send_key(char)
lmra2533222009-07-20 12:43:46 +0000808
mbligh1ef218d2009-08-03 16:57:56 +0000809
lmra2533222009-07-20 12:43:46 +0000810 def get_uuid(self):
811 """
812 Catch UUID of the VM.
813
814 @return: None,if not specified in config file
815 """
816 if self.params.get("uuid") == "random":
817 return self.uuid
818 else:
819 return self.params.get("uuid", None)