blob: 503f636e2d14e3f3821d67bcf0a33758d144a4a5 [file] [log] [blame]
lmr6f669ce2009-05-31 19:02:42 +00001#!/usr/bin/python
lmr25342d12009-06-10 20:19:07 +00002import time, socket, os, logging, fcntl
lmr6f669ce2009-05-31 19:02:42 +00003import kvm_utils
4
5"""
6Utility classes and functions to handle Virtual Machine creation using qemu.
7
8@copyright: 2008-2009 Red Hat Inc.
9"""
10
11
12def get_image_filename(params, image_dir):
13 """
14 Generate an image path from params and image_dir.
15
16 @param params: Dictionary containing the test parameters.
17 @param image_dir: The directory where the image is to be located
18
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)
26 image_filename = os.path.join(image_dir, image_filename)
27 return image_filename
28
29
30def create_image(params, qemu_img_path, image_dir):
31 """
32 Create an image using qemu_image.
33
34 @param params: Dictionary containing the test parameters.
35 @param qemu_img_path: The path of the qemu-img binary
36 @param image_dir: The directory where the image is to be located
37
38 @note: params should contain:
39 image_name -- the name of the image file, without extension
40 image_format -- the format of the image (qcow2, raw etc)
41 image_size -- the requested size of the image (a string
42 qemu-img can understand, such as '10G')
43 """
44 qemu_img_cmd = qemu_img_path
45 qemu_img_cmd += " create"
46
47 format = params.get("image_format", "qcow2")
48 qemu_img_cmd += " -f %s" % format
49
50 image_filename = get_image_filename(params, image_dir)
51 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)
57 (status, pid, output) = kvm_utils.run_bg(qemu_img_cmd, None,
58 logging.debug, "(qemu-img) ",
59 timeout=30)
60
61 if status:
62 logging.debug("qemu-img exited with status %d" % status)
63 logging.error("Could not create image %s" % image_filename)
64 return None
65 if not os.path.exists(image_filename):
66 logging.debug("Image file does not exist for some reason")
67 logging.error("Could not create image %s" % image_filename)
68 return None
69
70 logging.info("Image created in %s" % image_filename)
71 return image_filename
72
73
74def remove_image(params, image_dir):
75 """
76 Remove an image file.
77
78 @param params: A dict
79 @param image_dir: The directory where the image is to be located
80
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 """
85 image_filename = get_image_filename(params, image_dir)
86 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
98 def __init__(self, name, params, qemu_path, image_dir, iso_dir):
99 """
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)
105 @param qemu_path: The path of the qemu binary
106 @param image_dir: The directory where images reside
107 @param iso_dir: The directory where ISOs reside
108 """
109 self.pid = None
110
111 self.name = name
112 self.params = params
113 self.qemu_path = qemu_path
114 self.image_dir = image_dir
115 self.iso_dir = iso_dir
116
117
lmr8b134f92009-06-08 14:47:31 +0000118 # Find available monitor filename
119 while True:
120 # The monitor filename should be unique
lmrd16a67d2009-06-10 19:52:59 +0000121 self.instance = (time.strftime("%Y%m%d-%H%M%S-") +
122 kvm_utils.generate_random_string(4))
lmr8b134f92009-06-08 14:47:31 +0000123 self.monitor_file_name = os.path.join("/tmp",
124 "monitor-" + self.instance)
125 if not os.path.exists(self.monitor_file_name):
126 break
127
128
lmr2c241172009-06-08 15:11:29 +0000129 def clone(self, name=None, params=None, qemu_path=None, image_dir=None,
130 iso_dir=None):
131 """
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
139 @param qemu_path: Optional new path to qemu
140 @param image_dir: Optional new image dir
141 @param iso_dir: Optional new iso directory
142 """
143 if name == None:
144 name = self.name
145 if params == None:
146 params = self.params.copy()
147 if qemu_path == None:
148 qemu_path = self.qemu_path
149 if image_dir == None:
150 image_dir = self.image_dir
151 if iso_dir == None:
152 iso_dir = self.iso_dir
153 return VM(name, params, qemu_path, image_dir, iso_dir)
154
155
lmr6f669ce2009-05-31 19:02:42 +0000156 def verify_process_identity(self):
157 """
158 Make sure .pid really points to the original qemu process. If .pid
159 points to the same process that was created with the create method,
160 or to a dead process, return True. Otherwise return False.
161 """
162 if self.is_dead():
163 return True
164 filename = "/proc/%d/cmdline" % self.pid
165 if not os.path.exists(filename):
166 logging.debug("Filename %s does not exist" % filename)
167 return False
168 file = open(filename)
169 cmdline = file.read()
170 file.close()
171 if not self.qemu_path in cmdline:
172 return False
173 if not self.monitor_file_name in cmdline:
174 return False
175 return True
176
177
178 def make_qemu_command(self, name=None, params=None, qemu_path=None,
179 image_dir=None, iso_dir=None):
180 """
181 Generate a qemu command line. All parameters are optional. If a
182 parameter is not supplied, the corresponding value stored in the
183 class attributes is used.
184
185
186 @param name: The name of the object
187 @param params: A dict containing VM params
188 @param qemu_path: The path of the qemu binary
189 @param image_dir: The directory where images reside
190 @param iso_dir: The directory where ISOs reside
191
192
193 @note: The params dict should contain:
194 mem -- memory size in MBs
195 cdrom -- ISO filename to use with the qemu -cdrom parameter
196 (iso_dir is pre-pended to the ISO filename)
197 extra_params -- a string to append to the qemu command
198 ssh_port -- should be 22 for SSH, 23 for Telnet
lmreeff0eb2009-06-10 19:19:15 +0000199 x11_display -- if specified, the DISPLAY environment variable
200 will be be set to this value for the qemu process (useful for
201 SDL rendering)
lmr6f669ce2009-05-31 19:02:42 +0000202 images -- a list of image object names, separated by spaces
203 nics -- a list of NIC object names, separated by spaces
204
205 For each image in images:
206 drive_format -- string to pass as 'if' parameter for this
207 image (e.g. ide, scsi)
208 image_snapshot -- if yes, pass 'snapshot=on' to qemu for
209 this image
210 image_boot -- if yes, pass 'boot=on' to qemu for this image
211 In addition, all parameters required by get_image_filename.
212
213 For each NIC in nics:
214 nic_model -- string to pass as 'model' parameter for this
215 NIC (e.g. e1000)
216 """
217 if name == None:
218 name = self.name
219 if params == None:
220 params = self.params
221 if qemu_path == None:
222 qemu_path = self.qemu_path
223 if image_dir == None:
224 image_dir = self.image_dir
225 if iso_dir == None:
226 iso_dir = self.iso_dir
227
lmreeff0eb2009-06-10 19:19:15 +0000228 # Start constructing the qemu command
229 qemu_cmd = ""
230 # Set the X11 display parameter if requested
231 if params.get("x11_display"):
232 qemu_cmd += "DISPLAY=%s " % params.get("x11_display")
233 # Add the qemu binary
234 qemu_cmd += qemu_path
235 # Add the VM's name
lmr6f669ce2009-05-31 19:02:42 +0000236 qemu_cmd += " -name '%s'" % name
lmreeff0eb2009-06-10 19:19:15 +0000237 # Add the monitor socket parameter
lmr6f669ce2009-05-31 19:02:42 +0000238 qemu_cmd += " -monitor unix:%s,server,nowait" % self.monitor_file_name
239
240 for image_name in kvm_utils.get_sub_dict_names(params, "images"):
241 image_params = kvm_utils.get_sub_dict(params, image_name)
242 qemu_cmd += " -drive file=%s" % get_image_filename(image_params,
243 image_dir)
244 if image_params.get("drive_format"):
245 qemu_cmd += ",if=%s" % image_params.get("drive_format")
lmr0d4d2742009-06-18 06:15:17 +0000246 if image_params.get("drive_cache"):
247 qemu_cmd += ",cache=%s" % image_params.get("drive_cache")
248 if image_params.get("drive_serial"):
249 qemu_cmd += ",serial=%s" % image_params.get("drive_serial")
lmr6f669ce2009-05-31 19:02:42 +0000250 if image_params.get("image_snapshot") == "yes":
251 qemu_cmd += ",snapshot=on"
252 if image_params.get("image_boot") == "yes":
253 qemu_cmd += ",boot=on"
254
255 vlan = 0
256 for nic_name in kvm_utils.get_sub_dict_names(params, "nics"):
257 nic_params = kvm_utils.get_sub_dict(params, nic_name)
258 qemu_cmd += " -net nic,vlan=%d" % vlan
259 if nic_params.get("nic_model"):
260 qemu_cmd += ",model=%s" % nic_params.get("nic_model")
261 qemu_cmd += " -net user,vlan=%d" % vlan
262 vlan += 1
263
264 mem = params.get("mem")
265 if mem:
266 qemu_cmd += " -m %s" % mem
267
268 iso = params.get("cdrom")
269 if iso:
270 iso = os.path.join(iso_dir, iso)
271 qemu_cmd += " -cdrom %s" % iso
272
273 extra_params = params.get("extra_params")
274 if extra_params:
275 qemu_cmd += " %s" % extra_params
276
277 for redir_name in kvm_utils.get_sub_dict_names(params, "redirs"):
278 redir_params = kvm_utils.get_sub_dict(params, redir_name)
279 guest_port = int(redir_params.get("guest_port"))
280 host_port = self.get_port(guest_port)
281 qemu_cmd += " -redir tcp:%s::%s" % (host_port, guest_port)
282
283 if params.get("display") == "vnc":
284 qemu_cmd += " -vnc :%d" % (self.vnc_port - 5900)
285 elif params.get("display") == "sdl":
286 qemu_cmd += " -sdl"
287 elif params.get("display") == "nographic":
288 qemu_cmd += " -nographic"
289
290 return qemu_cmd
291
292
293 def create(self, name=None, params=None, qemu_path=None, image_dir=None,
294 iso_dir=None, for_migration=False, timeout=5.0):
295 """
296 Start the VM by running a qemu command.
297 All parameters are optional. The following applies to all parameters
298 but for_migration: If a parameter is not supplied, the corresponding
299 value stored in the class attributes is used, and if it is supplied,
300 it is stored for later use.
301
302 @param name: The name of the object
303 @param params: A dict containing VM params
304 @param qemu_path: The path of the qemu binary
305 @param image_dir: The directory where images reside
306 @param iso_dir: The directory where ISOs reside
307 @param for_migration: If True, start the VM with the -incoming
308 option
309 """
lmr135b5e62009-06-10 19:22:31 +0000310 self.destroy()
311
lmr6f669ce2009-05-31 19:02:42 +0000312 if name != None:
313 self.name = name
314 if params != None:
315 self.params = params
316 if qemu_path != None:
317 self.qemu_path = qemu_path
318 if image_dir != None:
319 self.image_dir = image_dir
320 if iso_dir != None:
321 self.iso_dir = iso_dir
322 name = self.name
323 params = self.params
324 qemu_path = self.qemu_path
325 image_dir = self.image_dir
326 iso_dir = self.iso_dir
327
328 # Verify the md5sum of the ISO image
329 iso = params.get("cdrom")
330 if iso:
331 iso = os.path.join(iso_dir, iso)
332 if not os.path.exists(iso):
333 logging.error("ISO file not found: %s" % iso)
334 return False
335 compare = False
336 if params.get("md5sum_1m"):
337 logging.debug("Comparing expected MD5 sum with MD5 sum of first"
338 "MB of ISO file...")
339 actual_md5sum = kvm_utils.md5sum_file(iso, 1048576)
340 expected_md5sum = params.get("md5sum_1m")
341 compare = True
342 elif params.get("md5sum"):
343 logging.debug("Comparing expected MD5 sum with MD5 sum of ISO"
344 " file...")
345 actual_md5sum = kvm_utils.md5sum_file(iso)
346 expected_md5sum = params.get("md5sum")
347 compare = True
348 if compare:
349 if actual_md5sum == expected_md5sum:
350 logging.debug("MD5 sums match")
351 else:
352 logging.error("Actual MD5 sum differs from expected one")
353 return False
354
lmrdc2ac6a2009-06-10 19:15:49 +0000355 # Make sure the following code is not executed by more than one thread
356 # at the same time
357 lockfile = open("/tmp/kvm-autotest-vm-create.lock", "w+")
358 fcntl.lockf(lockfile, fcntl.LOCK_EX)
lmr6f669ce2009-05-31 19:02:42 +0000359
lmrdc2ac6a2009-06-10 19:15:49 +0000360 try:
361 # Handle port redirections
362 redir_names = kvm_utils.get_sub_dict_names(params, "redirs")
363 host_ports = kvm_utils.find_free_ports(5000, 6000, len(redir_names))
364 self.redirs = {}
365 for i in range(len(redir_names)):
366 redir_params = kvm_utils.get_sub_dict(params, redir_names[i])
367 guest_port = int(redir_params.get("guest_port"))
368 self.redirs[guest_port] = host_ports[i]
lmr6f669ce2009-05-31 19:02:42 +0000369
lmrdc2ac6a2009-06-10 19:15:49 +0000370 # Find available VNC port, if needed
371 if params.get("display") == "vnc":
372 self.vnc_port = kvm_utils.find_free_port(5900, 6000)
lmr6f669ce2009-05-31 19:02:42 +0000373
lmrdc2ac6a2009-06-10 19:15:49 +0000374 # Make qemu command
375 qemu_command = self.make_qemu_command()
lmr6f669ce2009-05-31 19:02:42 +0000376
lmrdc2ac6a2009-06-10 19:15:49 +0000377 # Is this VM supposed to accept incoming migrations?
378 if for_migration:
379 # Find available migration port
380 self.migration_port = kvm_utils.find_free_port(5200, 6000)
381 # Add -incoming option to the qemu command
382 qemu_command += " -incoming tcp:0:%d" % self.migration_port
lmr6f669ce2009-05-31 19:02:42 +0000383
lmrdc2ac6a2009-06-10 19:15:49 +0000384 logging.debug("Running qemu command:\n%s", qemu_command)
385 (status, pid, output) = kvm_utils.run_bg(qemu_command, None,
386 logging.debug, "(qemu) ")
lmr6f669ce2009-05-31 19:02:42 +0000387
lmrdc2ac6a2009-06-10 19:15:49 +0000388 if status:
389 logging.debug("qemu exited with status %d", status)
390 logging.error("VM could not be created -- qemu command"
391 " failed:\n%s", qemu_command)
392 return False
lmr6f669ce2009-05-31 19:02:42 +0000393
lmrdc2ac6a2009-06-10 19:15:49 +0000394 self.pid = pid
lmr6f669ce2009-05-31 19:02:42 +0000395
lmrdc2ac6a2009-06-10 19:15:49 +0000396 if not kvm_utils.wait_for(self.is_alive, timeout, 0, 1):
397 logging.debug("VM is not alive for some reason")
398 logging.error("VM could not be created with"
399 " command:\n%s", qemu_command)
400 self.destroy()
401 return False
lmr6f669ce2009-05-31 19:02:42 +0000402
lmrdc2ac6a2009-06-10 19:15:49 +0000403 logging.debug("VM appears to be alive with PID %d", self.pid)
404 return True
405
406 finally:
407 fcntl.lockf(lockfile, fcntl.LOCK_UN)
408 lockfile.close()
lmr6f669ce2009-05-31 19:02:42 +0000409
410
411 def send_monitor_cmd(self, command, block=True, timeout=20.0):
412 """
413 Send command to the QEMU monitor.
414
415 Connect to the VM's monitor socket and wait for the (qemu) prompt.
416 If block is True, read output from the socket until the (qemu) prompt
417 is found again, or until timeout expires.
418
419 Return a tuple containing an integer indicating success or failure,
420 and the data read so far. The integer is 0 on success and 1 on failure.
421 A failure is any of the following cases: connection to the socket
422 failed, or the first (qemu) prompt could not be found, or block is
423 True and the second prompt could not be found.
424
425 @param command: Command that will be sent to the monitor
426 @param block: Whether the output from the socket will be read until
427 the timeout expires
428 @param timeout: Timeout (seconds) before giving up on reading from
429 socket
430 """
431 def read_up_to_qemu_prompt(s, timeout):
432 """
433 Read data from socket s until the (qemu) prompt is found.
434
435 If the prompt is found before timeout expires, return a tuple
436 containing True and the data read. Otherwise return a tuple
437 containing False and the data read so far.
438
439 @param s: Socket object
440 @param timeout: Time (seconds) before giving up trying to get the
441 qemu prompt.
442 """
443 o = ""
444 end_time = time.time() + timeout
445 while time.time() < end_time:
446 try:
447 o += s.recv(16384)
448 if o.splitlines()[-1].split()[-1] == "(qemu)":
449 return (True, o)
450 except:
451 time.sleep(0.01)
452 return (False, o)
453
454 # Connect to monitor
455 logging.debug("Sending monitor command: %s" % command)
456 try:
457 s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
458 s.setblocking(False)
459 s.connect(self.monitor_file_name)
460 except:
461 logging.debug("Could not connect to monitor socket")
462 return (1, "")
463 status, data = read_up_to_qemu_prompt(s, timeout)
464 if not status:
465 s.close()
466 logging.debug("Could not find (qemu) prompt; output so far:" \
467 + kvm_utils.format_str_for_message(data))
468 return (1, "")
469 # Send command
470 s.sendall(command + "\n")
471 # Receive command output
472 data = ""
473 if block:
474 status, data = read_up_to_qemu_prompt(s, timeout)
475 data = "\n".join(data.splitlines()[1:])
476 if not status:
477 s.close()
478 logging.debug("Could not find (qemu) prompt after command;"
479 " output so far: %s",
480 kvm_utils.format_str_for_message(data))
481 return (1, data)
482 s.close()
483 return (0, data)
484
485
486 def destroy(self, gracefully=True):
487 """
488 Destroy the VM.
489
490 If gracefully is True, first attempt to kill the VM via SSH/Telnet
491 with a shutdown command. Then, attempt to destroy the VM via the
492 monitor with a 'quit' command. If that fails, send SIGKILL to the
493 qemu process.
494
495 @param gracefully: Whether an attempt will be made to end the VM
496 using monitor command before trying to kill the qemu process
497 or not.
498 """
499 # Is it already dead?
500 if self.is_dead():
501 logging.debug("VM is already down")
502 return
503
504 logging.debug("Destroying VM with PID %d..." % self.pid)
505
506 if gracefully and self.params.get("cmd_shutdown"):
507 # Try to destroy with SSH command
508 logging.debug("Trying to shutdown VM with SSH command...")
509 (status, output) = self.ssh(self.params.get("cmd_shutdown"))
510 # Was the command sent successfully?
511 if status == 0:
512 #if self.ssh(self.params.get("cmd_shutdown")):
513 logging.debug("Shutdown command sent; Waiting for VM to go"
514 "down...")
515 if kvm_utils.wait_for(self.is_dead, 60, 1, 1):
516 logging.debug("VM is down")
517 self.pid = None
518 return
519
520 # Try to destroy with a monitor command
521 logging.debug("Trying to kill VM with monitor command...")
522 (status, output) = self.send_monitor_cmd("quit", block=False)
523 # Was the command sent successfully?
524 if status == 0:
525 # Wait for the VM to be really dead
526 if kvm_utils.wait_for(self.is_dead, 5, 0.5, 0.5):
527 logging.debug("VM is down")
528 self.pid = None
529 return
530
531 # If the VM isn't dead yet...
532 logging.debug("Cannot quit normally; Sending a kill to close the"
533 " deal...")
534 kvm_utils.safe_kill(self.pid, 9)
535 # Wait for the VM to be really dead
536 if kvm_utils.wait_for(self.is_dead, 5, 0.5, 0.5):
537 logging.debug("VM is down")
538 self.pid = None
539 return
540
541 logging.error("We have a zombie! PID %d is a zombie!" % self.pid)
542
543
544 def is_alive(self):
545 """
546 Return True if the VM's monitor is responsive.
547 """
548 # Check if the process exists
549 if not kvm_utils.pid_exists(self.pid):
550 return False
551 # Try sending a monitor command
552 (status, output) = self.send_monitor_cmd("help")
553 if status:
554 return False
555 return True
556
557
558 def is_dead(self):
559 """
560 Return True iff the VM's PID does not exist.
561 """
562 return not kvm_utils.pid_exists(self.pid)
563
564
565 def get_params(self):
566 """
567 Return the VM's params dict. Most modified params take effect only
568 upon VM.create().
569 """
570 return self.params
571
572
573 def get_address(self):
574 """
575 Return the guest's address in host space.
576
577 If port redirection is used, return 'localhost' (the guest has no IP
578 address of its own). Otherwise return the guest's IP address.
579 """
580 # Currently redirection is always used, so return 'localhost'
581 return "localhost"
582
583
584 def get_port(self, port):
585 """
586 Return the port in host space corresponding to port in guest space.
587
588 @param port: Port number in host space.
589 @return: If port redirection is used, return the host port redirected
590 to guest port port. Otherwise return port.
591 """
592 # Currently redirection is always used, so use the redirs dict
593 if self.redirs.has_key(port):
594 return self.redirs[port]
595 else:
596 logging.debug("Warning: guest port %s requested but not"
597 " redirected" % port)
598 return None
599
600
601 def is_sshd_running(self, timeout=10):
602 """
603 Return True iff the guest's SSH port is responsive.
604
605 @param timeout: Time (seconds) before giving up checking the SSH daemon
606 responsiveness.
607 """
608 address = self.get_address()
609 port = self.get_port(int(self.params.get("ssh_port")))
610 if not port:
611 return False
612 return kvm_utils.is_sshd_running(address, port, timeout=timeout)
613
614
615 def ssh_login(self, timeout=10):
616 """
617 Log into the guest via SSH/Telnet.
618 If timeout expires while waiting for output from the guest (e.g. a
619 password prompt or a shell prompt) -- fail.
620
621 @param timeout: Time (seconds) before giving up logging into the
622 guest.
623 @return: kvm_spawn object on success and None on failure.
624 """
625 username = self.params.get("username", "")
626 password = self.params.get("password", "")
627 prompt = self.params.get("ssh_prompt", "[\#\$]")
628 use_telnet = self.params.get("use_telnet") == "yes"
629 address = self.get_address()
630 port = self.get_port(int(self.params.get("ssh_port")))
631 if not port:
632 return None
633
634 if use_telnet:
635 session = kvm_utils.telnet(address, port, username, password,
636 prompt, timeout)
637 else:
638 session = kvm_utils.ssh(address, port, username, password,
639 prompt, timeout)
640 if session:
641 session.set_status_test_command(self.params.get("ssh_status_test_"
642 "command", ""))
643 return session
644
645
646 def scp_to_remote(self, local_path, remote_path, timeout=300):
647 """
648 Transfer files to the guest via SCP.
649
650 @param local_path: Host path
651 @param remote_path: Guest path
652 @param timeout: Time (seconds) before giving up on doing the remote
653 copy.
654 """
655 username = self.params.get("username", "")
656 password = self.params.get("password", "")
657 address = self.get_address()
658 port = self.get_port(int(self.params.get("ssh_port")))
659 if not port:
660 return None
661 return kvm_utils.scp_to_remote(address, port, username, password,
662 local_path, remote_path, timeout)
663
664
665 def scp_from_remote(self, remote_path, local_path, timeout=300):
666 """
667 Transfer files from the guest via SCP.
668
669 @param local_path: Guest path
670 @param remote_path: Host path
671 @param timeout: Time (seconds) before giving up on doing the remote
672 copy.
673 """
674 username = self.params.get("username", "")
675 password = self.params.get("password", "")
676 address = self.get_address()
677 port = self.get_port(int(self.params.get("ssh_port")))
678 if not port:
679 return None
680 return kvm_utils.scp_from_remote(address, port, username, password,
681 remote_path, local_path, timeout)
682
683
684 def ssh(self, command, timeout=10):
685 """
686 Login via SSH/Telnet and send a command.
687
688 @command: Command that will be sent.
689 @timeout: Time before giving up waiting on a status return.
690 @return: A tuple (status, output). status is 0 on success and 1 on
691 failure.
692 """
693 session = self.ssh_login(timeout)
694 if not session:
695 return (1, "")
696
697 logging.debug("Sending command: %s" % command)
698 session.sendline(command)
699 output = session.read_nonblocking(1.0)
700 session.close()
701
702 return (0, output)
703
704
705 def send_key(self, keystr):
706 """
707 Send a key event to the VM.
708
709 @param: keystr: A key event string (e.g. "ctrl-alt-delete")
710 """
711 # For compatibility with versions of QEMU that do not recognize all
712 # key names: replace keyname with the hex value from the dict, which
713 # QEMU will definitely accept
714 dict = { "comma": "0x33",
715 "dot": "0x34",
716 "slash": "0x35" }
717 for key in dict.keys():
718 keystr = keystr.replace(key, dict[key])
719 self.send_monitor_cmd("sendkey %s 1" % keystr)
720 time.sleep(0.2)
721
722
723 def send_string(self, str):
724 """
725 Send a string to the VM.
726
727 @param str: String, that must consist of alphanumeric characters only.
728 Capital letters are allowed.
729 """
730 for char in str:
731 if char.isupper():
732 self.send_key("shift-%s" % char.lower())
733 else:
734 self.send_key(char)