blob: fb587c59e68a3e2b8bdd483bed8b08dcc6a5cec7 [file] [log] [blame]
lmr6f669ce2009-05-31 19:02:42 +00001import md5, thread, subprocess, time, string, random, socket, os, signal, pty
2import select, re, logging
lmr3f0b0cc2009-06-10 02:25:23 +00003from autotest_lib.client.bin import utils
4from autotest_lib.client.common_lib import error
lmrdc3a5b12009-07-23 01:40:40 +00005import kvm_subprocess
lmr6f669ce2009-05-31 19:02:42 +00006
7"""
8KVM test utility functions.
9
10@copyright: 2008-2009 Red Hat Inc.
11"""
12
13
14def get_sub_dict(dict, name):
15 """
16 Return a "sub-dict" corresponding to a specific object.
17
18 Operate on a copy of dict: for each key that ends with the suffix
19 "_" + name, strip the suffix from the key, and set the value of
20 the stripped key to that of the key. Return the resulting dict.
21
22 @param name: Suffix of the key we want to set the value.
23 """
24 suffix = "_" + name
25 new_dict = dict.copy()
26 for key in dict.keys():
27 if key.endswith(suffix):
28 new_key = key.split(suffix)[0]
29 new_dict[new_key] = dict[key]
30 return new_dict
31
32
33def get_sub_dict_names(dict, keyword):
34 """
35 Return a list of "sub-dict" names that may be extracted with get_sub_dict.
36
37 This function may be modified to change the behavior of all functions that
38 deal with multiple objects defined in dicts (e.g. VMs, images, NICs).
39
40 @param keyword: A key in dict (e.g. "vms", "images", "nics").
41 """
42 names = dict.get(keyword)
43 if names:
44 return names.split()
45 else:
46 return []
47
48
49# Functions for working with the environment (a dict-like object)
50
51def is_vm(obj):
52 """
53 Tests whether a given object is a VM object.
54
55 @param obj: Python object (pretty much everything on python).
56 """
57 return obj.__class__.__name__ == "VM"
58
59
60def env_get_all_vms(env):
61 """
62 Return a list of all VM objects on a given environment.
63
64 @param env: Dictionary with environment items.
65 """
66 vms = []
67 for obj in env.values():
68 if is_vm(obj):
69 vms.append(obj)
70 return vms
71
72
73def env_get_vm(env, name):
74 """
75 Return a VM object by its name.
76
77 @param name: VM name.
78 """
79 return env.get("vm__%s" % name)
80
81
82def env_register_vm(env, name, vm):
83 """
84 Register a given VM in a given env.
85
86 @param env: Environment where we will register the VM.
87 @param name: VM name.
88 @param vm: VM object.
89 """
90 env["vm__%s" % name] = vm
91
92
93def env_unregister_vm(env, name):
94 """
95 Remove a given VM from a given env.
96
97 @param env: Environment where we will un-register the VM.
98 @param name: VM name.
99 """
100 del env["vm__%s" % name]
101
102
103# Utility functions for dealing with external processes
104
105def pid_exists(pid):
106 """
107 Return True if a given PID exists.
108
109 @param pid: Process ID number.
110 """
111 try:
112 os.kill(pid, 0)
113 return True
114 except:
115 return False
116
117
118def safe_kill(pid, signal):
119 """
120 Attempt to send a signal to a given process that may or may not exist.
121
122 @param signal: Signal number.
123 """
124 try:
125 os.kill(pid, signal)
126 return True
127 except:
128 return False
129
130
lmr3f0b0cc2009-06-10 02:25:23 +0000131def get_latest_kvm_release_tag(release_dir):
132 """
133 Fetches the latest release tag for KVM.
134
135 @param release_dir: KVM source forge download location.
136 """
137 try:
138 page_url = os.path.join(release_dir, "showfiles.php")
139 local_web_page = utils.unmap_url("/", page_url, "/tmp")
140 f = open(local_web_page, "r")
141 data = f.read()
142 f.close()
lmr8ea274b2009-07-06 13:42:35 +0000143 rx = re.compile("kvm-(\d+).tar.gz", re.IGNORECASE)
lmr3f0b0cc2009-06-10 02:25:23 +0000144 matches = rx.findall(data)
145 package_id = matches[0]
lmr3f0b0cc2009-06-10 02:25:23 +0000146 return matches[0] # the first match contains the latest release tag
147 except Exception, e:
148 message = "Could not fetch latest KVM release tag: %s" % str(e)
149 logging.error(message)
150 raise error.TestError(message)
151
152
153def get_git_branch(repository, branch, srcdir, commit=None, lbranch=None):
154 """
155 Retrieves a given git code repository.
156
157 @param repository: Git repository URL
158 """
159 logging.info("Fetching git [REP '%s' BRANCH '%s' TAG '%s'] -> %s",
160 repository, branch, commit, srcdir)
161 if not os.path.exists(srcdir):
162 os.makedirs(srcdir)
163 os.chdir(srcdir)
164
165 if os.path.exists(".git"):
166 utils.system("git reset --hard")
167 else:
168 utils.system("git init")
169
170 if not lbranch:
171 lbranch = branch
172
173 utils.system("git fetch -q -f -u -t %s %s:%s" %
174 (repository, branch, lbranch))
175 utils.system("git checkout %s" % lbranch)
176 if commit:
177 utils.system("git checkout %s" % commit)
178
179 h = utils.system_output('git log --pretty=format:"%H" -1')
180 desc = utils.system_output("git describe")
181 logging.info("Commit hash for %s is %s (%s)" % (repository, h.strip(),
182 desc))
183 return srcdir
184
185
186def unload_module(module_name):
187 """
188 Removes a module. Handles dependencies. If even then it's not possible
189 to remove one of the modules, it will trhow an error.CmdError exception.
190
191 @param module_name: Name of the module we want to remove.
192 """
193 l_raw = utils.system_output("/sbin/lsmod").splitlines()
194 lsmod = [x for x in l_raw if x.split()[0] == module_name]
195 if len(lsmod) > 0:
196 line_parts = lsmod[0].split()
197 if len(line_parts) == 4:
198 submodules = line_parts[3].split(",")
199 for submodule in submodules:
200 unload_module(submodule)
201 utils.system("/sbin/modprobe -r %s" % module_name)
202 logging.info("Module %s unloaded" % module_name)
203 else:
204 logging.info("Module %s is already unloaded" % module_name)
205
206
207def check_kvm_source_dir(source_dir):
208 """
209 Inspects the kvm source directory and verifies its disposition. In some
210 occasions build may be dependant on the source directory disposition.
211 The reason why the return codes are numbers is that we might have more
212 changes on the source directory layout, so it's not scalable to just use
213 strings like 'old_repo', 'new_repo' and such.
214
215 @param source_dir: Source code path that will be inspected.
216 """
217 os.chdir(source_dir)
218 has_qemu_dir = os.path.isdir('qemu')
219 has_kvm_dir = os.path.isdir('kvm')
220 if has_qemu_dir and not has_kvm_dir:
221 logging.debug("qemu directory detected, source dir layout 1")
222 return 1
223 if has_kvm_dir and not has_qemu_dir:
224 logging.debug("kvm directory detected, source dir layout 2")
225 return 2
226 else:
227 raise error.TestError("Unknown source dir layout, cannot proceed.")
228
229
lmr6f669ce2009-05-31 19:02:42 +0000230# The following are a class and functions used for SSH, SCP and Telnet
231# communication with guests.
232
233class kvm_spawn:
234 """
235 This class is used for spawning and controlling a child process.
236 """
237
238 def __init__(self, command, linesep="\n"):
239 """
240 Initialize the class and run command as a child process.
241
242 @param command: Command that will be run.
243 @param linesep: Line separator for the given platform.
244 """
245 self.exitstatus = None
246 self.linesep = linesep
247 (pid, fd) = pty.fork()
248 if pid == 0:
249 os.execv("/bin/sh", ["/bin/sh", "-c", command])
250 else:
251 self.pid = pid
252 self.fd = fd
253
254
255 def set_linesep(self, linesep):
256 """
257 Sets the line separator string (usually "\\n").
258
259 @param linesep: Line separator character.
260 """
261 self.linesep = linesep
262
263
264 def is_responsive(self, timeout=5.0):
265 """
266 Return True if the session is responsive.
267
268 Send a newline to the child process (e.g. SSH or Telnet) and read some
269 output using read_nonblocking.
270 If all is OK, some output should be available (e.g. the shell prompt).
271 In that case return True. Otherwise return False.
272
273 @param timeout: Timeout that will happen before we consider the
274 process unresponsive
275 """
276 self.read_nonblocking(timeout=0.1)
277 self.sendline()
278 output = self.read_nonblocking(timeout=timeout)
279 if output.strip():
280 return True
281 return False
282
283
284 def poll(self):
285 """
286 If the process exited, return its exit status. Otherwise return None.
287 The exit status is stored for use in subsequent calls.
288 """
289 if self.exitstatus != None:
290 return self.exitstatus
291 pid, status = os.waitpid(self.pid, os.WNOHANG)
292 if pid:
293 self.exitstatus = os.WEXITSTATUS(status)
294 return self.exitstatus
295 else:
296 return None
297
298
299 def close(self):
300 """
301 Close the session (close the process filedescriptors and kills the
302 process ID), and return the exit status.
303 """
304 try:
305 os.close(self.fd)
306 os.kill(self.pid, signal.SIGTERM)
307 except OSError:
308 pass
309 return self.poll()
310
311
312 def sendline(self, str=""):
313 """
314 Sends a string followed by a line separator to the child process.
315
316 @param str: String that will be sent to the child process.
317 """
318 try:
319 os.write(self.fd, str + self.linesep)
320 except OSError:
321 pass
322
323
324 def read_nonblocking(self, timeout=1.0):
325 """
326 Read from child until there is nothing to read for timeout seconds.
327
328 @param timeout: Time (seconds) of wait before we give up reading from
329 the child process.
330 """
331 data = ""
332 while True:
333 r, w, x = select.select([self.fd], [], [], timeout)
334 if self.fd in r:
335 try:
336 data += os.read(self.fd, 1024)
337 except OSError:
338 return data
339 else:
340 return data
341
342
343 def match_patterns(self, str, patterns):
344 """
345 Match str against a list of patterns.
346
347 Return the index of the first pattern that matches a substring of str.
348 None and empty strings in patterns are ignored.
349 If no match is found, return None.
350
351 @param patterns: List of strings (regular expression patterns).
352 """
353 for i in range(len(patterns)):
354 if not patterns[i]:
355 continue
356 exp = re.compile(patterns[i])
357 if exp.search(str):
358 return i
359
360
361 def read_until_output_matches(self, patterns, filter=lambda(x):x,
362 timeout=30.0, internal_timeout=1.0,
363 print_func=None):
364 """
365 Read using read_nonblocking until a match is found using match_patterns,
366 or until timeout expires. Before attempting to search for a match, the
367 data is filtered using the filter function provided.
368
369 @brief: Read from child using read_nonblocking until a pattern
370 matches.
371 @param patterns: List of strings (regular expression patterns)
372 @param filter: Function to apply to the data read from the child before
373 attempting to match it against the patterns (should take and
374 return a string)
375 @param timeout: The duration (in seconds) to wait until a match is
376 found
377 @param internal_timeout: The timeout to pass to read_nonblocking
378 @param print_func: A function to be used to print the data being read
379 (should take a string parameter)
380 @return: Tuple containing the match index (or None if no match was
381 found) and the data read so far.
382 """
383 match = None
384 data = ""
385
386 end_time = time.time() + timeout
387 while time.time() < end_time:
388 # Read data from child
389 newdata = self.read_nonblocking(internal_timeout)
390 # Print it if necessary
391 if print_func and newdata:
392 map(print_func, newdata.splitlines())
393 data += newdata
394
395 done = False
396 # Look for patterns
397 match = self.match_patterns(filter(data), patterns)
398 if match != None:
399 done = True
400 # Check if child has died
401 if self.poll() != None:
lmr3f0b0cc2009-06-10 02:25:23 +0000402 logging.debug("Process terminated with status %d", self.poll())
lmr6f669ce2009-05-31 19:02:42 +0000403 done = True
404 # Are we done?
405 if done: break
406
407 # Print some debugging info
408 if match == None and self.poll() != 0:
lmr3f0b0cc2009-06-10 02:25:23 +0000409 logging.debug("Timeout elapsed or process terminated. Output: %s",
lmr6f669ce2009-05-31 19:02:42 +0000410 format_str_for_message(data.strip()))
411
412 return (match, data)
413
414
415 def get_last_word(self, str):
416 """
417 Return the last word in str.
418
419 @param str: String that will be analyzed.
420 """
421 if str:
422 return str.split()[-1]
423 else:
424 return ""
425
426
427 def get_last_line(self, str):
428 """
429 Return the last non-empty line in str.
430
431 @param str: String that will be analyzed.
432 """
433 last_line = ""
434 for line in str.splitlines():
435 if line != "":
436 last_line = line
437 return last_line
438
439
440 def read_until_last_word_matches(self, patterns, timeout=30.0,
441 internal_timeout=1.0, print_func=None):
442 """
443 Read using read_nonblocking until the last word of the output matches
444 one of the patterns (using match_patterns), or until timeout expires.
445
446 @param patterns: A list of strings (regular expression patterns)
447 @param timeout: The duration (in seconds) to wait until a match is
448 found
449 @param internal_timeout: The timeout to pass to read_nonblocking
450 @param print_func: A function to be used to print the data being read
451 (should take a string parameter)
452 @return: A tuple containing the match index (or None if no match was
453 found) and the data read so far.
454 """
455 return self.read_until_output_matches(patterns, self.get_last_word,
456 timeout, internal_timeout,
457 print_func)
458
459
460 def read_until_last_line_matches(self, patterns, timeout=30.0,
461 internal_timeout=1.0, print_func=None):
462 """
463 Read using read_nonblocking until the last non-empty line of the output
464 matches one of the patterns (using match_patterns), or until timeout
465 expires. Return a tuple containing the match index (or None if no match
466 was found) and the data read so far.
467
468 @brief: Read using read_nonblocking until the last non-empty line
469 matches a pattern.
470
471 @param patterns: A list of strings (regular expression patterns)
472 @param timeout: The duration (in seconds) to wait until a match is
473 found
474 @param internal_timeout: The timeout to pass to read_nonblocking
475 @param print_func: A function to be used to print the data being read
476 (should take a string parameter)
477 """
478 return self.read_until_output_matches(patterns, self.get_last_line,
479 timeout, internal_timeout,
480 print_func)
481
482
483 def set_prompt(self, prompt):
484 """
485 Set the prompt attribute for later use by read_up_to_prompt.
486
487 @param: String that describes the prompt contents.
488 """
489 self.prompt = prompt
490
491
492 def read_up_to_prompt(self, timeout=30.0, internal_timeout=1.0,
493 print_func=None):
494 """
495 Read using read_nonblocking until the last non-empty line of the output
496 matches the prompt regular expression set by set_prompt, or until
497 timeout expires.
498
499 @brief: Read using read_nonblocking until the last non-empty line
500 matches the prompt.
501
502 @param timeout: The duration (in seconds) to wait until a match is
503 found
504 @param internal_timeout: The timeout to pass to read_nonblocking
505 @param print_func: A function to be used to print the data being
506 read (should take a string parameter)
507
508 @return: A tuple containing True/False indicating whether the prompt
509 was found, and the data read so far.
510 """
511 (match, output) = self.read_until_last_line_matches([self.prompt],
512 timeout,
513 internal_timeout,
514 print_func)
515 if match == None:
516 return (False, output)
517 else:
518 return (True, output)
519
520
521 def set_status_test_command(self, status_test_command):
522 """
523 Set the command to be sent in order to get the last exit status.
524
525 @param status_test_command: Command that will be sent to get the last
526 exit status.
527 """
528 self.status_test_command = status_test_command
529
530
531 def get_command_status_output(self, command, timeout=30.0,
532 internal_timeout=1.0, print_func=None):
533 """
534 Send a command and return its exit status and output.
535
536 @param command: Command to send
537 @param timeout: The duration (in seconds) to wait until a match is
538 found
539 @param internal_timeout: The timeout to pass to read_nonblocking
540 @param print_func: A function to be used to print the data being read
541 (should take a string parameter)
542
543 @return: A tuple (status, output) where status is the exit status or
544 None if no exit status is available (e.g. timeout elapsed), and
545 output is the output of command.
546 """
547 # Print some debugging info
548 logging.debug("Sending command: %s" % command)
549
550 # Read everything that's waiting to be read
551 self.read_nonblocking(0.1)
552
553 # Send the command and get its output
554 self.sendline(command)
555 (match, output) = self.read_up_to_prompt(timeout, internal_timeout,
556 print_func)
557 if not match:
558 return (None, "\n".join(output.splitlines()[1:]))
559 output = "\n".join(output.splitlines()[1:-1])
560
561 # Send the 'echo ...' command to get the last exit status
562 self.sendline(self.status_test_command)
563 (match, status) = self.read_up_to_prompt(10.0, internal_timeout)
564 if not match:
565 return (None, output)
566 status = int("\n".join(status.splitlines()[1:-1]).strip())
567
568 # Print some debugging info
569 if status != 0:
lmra10d17c2009-06-10 19:58:09 +0000570 logging.debug("Command failed; status: %d, output:%s", status,
571 format_str_for_message(output.strip()))
lmr6f669ce2009-05-31 19:02:42 +0000572
573 return (status, output)
574
575
576 def get_command_status(self, command, timeout=30.0, internal_timeout=1.0,
577 print_func=None):
578 """
579 Send a command and return its exit status.
580
581 @param command: Command to send
582 @param timeout: The duration (in seconds) to wait until a match is
583 found
584 @param internal_timeout: The timeout to pass to read_nonblocking
585 @param print_func: A function to be used to print the data being read
586 (should take a string parameter)
587
588 @return: Exit status or None if no exit status is available (e.g.
589 timeout elapsed).
590 """
591 (status, output) = self.get_command_status_output(command, timeout,
592 internal_timeout,
593 print_func)
594 return status
595
596
597 def get_command_output(self, command, timeout=30.0, internal_timeout=1.0,
598 print_func=None):
599 """
600 Send a command and return its output.
601
602 @param command: Command to send
603 @param timeout: The duration (in seconds) to wait until a match is
604 found
605 @param internal_timeout: The timeout to pass to read_nonblocking
606 @param print_func: A function to be used to print the data being read
607 (should take a string parameter)
608 """
609 (status, output) = self.get_command_status_output(command, timeout,
610 internal_timeout,
611 print_func)
612 return output
613
614
615def remote_login(command, password, prompt, linesep="\n", timeout=10):
616 """
617 Log into a remote host (guest) using SSH or Telnet. Run the given command
618 using kvm_spawn and provide answers to the questions asked. If timeout
619 expires while waiting for output from the child (e.g. a password prompt
620 or a shell prompt) -- fail.
621
622 @brief: Log into a remote host (guest) using SSH or Telnet.
623
624 @param command: The command to execute (e.g. "ssh root@localhost")
625 @param password: The password to send in reply to a password prompt
626 @param prompt: The shell prompt that indicates a successful login
627 @param linesep: The line separator to send instead of "\\n"
628 (sometimes "\\r\\n" is required)
629 @param timeout: The maximal time duration (in seconds) to wait for each
630 step of the login procedure (i.e. the "Are you sure" prompt, the
631 password prompt, the shell prompt, etc)
632
633 @return Return the kvm_spawn object on success and None on failure.
634 """
lmrdc3a5b12009-07-23 01:40:40 +0000635 sub = kvm_subprocess.kvm_shell_session(command,
636 linesep=linesep,
637 prompt=prompt)
lmr6f669ce2009-05-31 19:02:42 +0000638
639 password_prompt_count = 0
640
641 logging.debug("Trying to login...")
642
643 while True:
644 (match, text) = sub.read_until_last_line_matches(
lmr3ca79fe2009-06-10 19:24:26 +0000645 [r"[Aa]re you sure", r"[Pp]assword:\s*$", r"^\s*[Ll]ogin:\s*$",
646 r"[Cc]onnection.*closed", r"[Cc]onnection.*refused", prompt],
lmr6f669ce2009-05-31 19:02:42 +0000647 timeout=timeout, internal_timeout=0.5)
648 if match == 0: # "Are you sure you want to continue connecting"
649 logging.debug("Got 'Are you sure...'; sending 'yes'")
650 sub.sendline("yes")
651 continue
652 elif match == 1: # "password:"
653 if password_prompt_count == 0:
654 logging.debug("Got password prompt; sending '%s'" % password)
655 sub.sendline(password)
656 password_prompt_count += 1
657 continue
658 else:
659 logging.debug("Got password prompt again")
660 sub.close()
661 return None
662 elif match == 2: # "login:"
663 logging.debug("Got unexpected login prompt")
664 sub.close()
665 return None
666 elif match == 3: # "Connection closed"
667 logging.debug("Got 'Connection closed'")
668 sub.close()
669 return None
lmr3ca79fe2009-06-10 19:24:26 +0000670 elif match == 4: # "Connection refused"
lmr0d2ed1f2009-07-01 03:23:18 +0000671 logging.debug("Got 'Connection refused'")
lmr3ca79fe2009-06-10 19:24:26 +0000672 sub.close()
673 return None
674 elif match == 5: # prompt
lmr6f669ce2009-05-31 19:02:42 +0000675 logging.debug("Got shell prompt -- logged in")
676 return sub
677 else: # match == None
lmr3ca79fe2009-06-10 19:24:26 +0000678 logging.debug("Timeout elapsed or process terminated")
lmr6f669ce2009-05-31 19:02:42 +0000679 sub.close()
680 return None
681
682
683def remote_scp(command, password, timeout=300, login_timeout=10):
684 """
685 Run the given command using kvm_spawn and provide answers to the questions
686 asked. If timeout expires while waiting for the transfer to complete ,
687 fail. If login_timeout expires while waiting for output from the child
688 (e.g. a password prompt), fail.
689
690 @brief: Transfer files using SCP, given a command line.
691
692 @param command: The command to execute
693 (e.g. "scp -r foobar root@localhost:/tmp/").
694 @param password: The password to send in reply to a password prompt.
695 @param timeout: The time duration (in seconds) to wait for the transfer
696 to complete.
697 @param login_timeout: The maximal time duration (in seconds) to wait for
698 each step of the login procedure (i.e. the "Are you sure" prompt or the
699 password prompt)
700
701 @return: True if the transfer succeeds and False on failure.
702 """
lmrdc3a5b12009-07-23 01:40:40 +0000703 sub = kvm_subprocess.kvm_expect(command)
lmr6f669ce2009-05-31 19:02:42 +0000704
705 password_prompt_count = 0
706 _timeout = login_timeout
707
708 logging.debug("Trying to login...")
709
710 while True:
711 (match, text) = sub.read_until_last_line_matches(
lmr3ca79fe2009-06-10 19:24:26 +0000712 [r"[Aa]re you sure", r"[Pp]assword:\s*$", r"lost connection"],
lmr6f669ce2009-05-31 19:02:42 +0000713 timeout=_timeout, internal_timeout=0.5)
714 if match == 0: # "Are you sure you want to continue connecting"
715 logging.debug("Got 'Are you sure...'; sending 'yes'")
716 sub.sendline("yes")
717 continue
718 elif match == 1: # "password:"
719 if password_prompt_count == 0:
720 logging.debug("Got password prompt; sending '%s'" % password)
721 sub.sendline(password)
722 password_prompt_count += 1
723 _timeout = timeout
724 continue
725 else:
726 logging.debug("Got password prompt again")
727 sub.close()
728 return False
729 elif match == 2: # "lost connection"
730 logging.debug("Got 'lost connection'")
731 sub.close()
732 return False
733 else: # match == None
lmrdc3a5b12009-07-23 01:40:40 +0000734 logging.debug("Timeout elapsed or process terminated")
735 status = sub.get_status()
lmr6f669ce2009-05-31 19:02:42 +0000736 sub.close()
lmrdc3a5b12009-07-23 01:40:40 +0000737 return status == 0
lmr6f669ce2009-05-31 19:02:42 +0000738
739
740def scp_to_remote(host, port, username, password, local_path, remote_path,
741 timeout=300):
742 """
743 Copy files to a remote host (guest).
744
745 @param host: Hostname of the guest
746 @param username: User that will be used to copy the files
747 @param password: Host's password
748 @param local_path: Path on the local machine where we are copying from
749 @param remote_path: Path on the remote machine where we are copying to
750 @param timeout: Time in seconds that we will wait before giving up to
751 copy the files.
752
753 @return: True on success and False on failure.
754 """
lmrd16a67d2009-06-10 19:52:59 +0000755 command = ("scp -o UserKnownHostsFile=/dev/null -r -P %s %s %s@%s:%s" %
756 (port, local_path, username, host, remote_path))
lmr6f669ce2009-05-31 19:02:42 +0000757 return remote_scp(command, password, timeout)
758
759
760def scp_from_remote(host, port, username, password, remote_path, local_path,
761 timeout=300):
762 """
763 Copy files from a remote host (guest).
764
765 @param host: Hostname of the guest
766 @param username: User that will be used to copy the files
767 @param password: Host's password
768 @param local_path: Path on the local machine where we are copying from
769 @param remote_path: Path on the remote machine where we are copying to
770 @param timeout: Time in seconds that we will wait before giving up to copy
771 the files.
772
773 @return: True on success and False on failure.
774 """
lmrd16a67d2009-06-10 19:52:59 +0000775 command = ("scp -o UserKnownHostsFile=/dev/null -r -P %s %s@%s:%s %s" %
776 (port, username, host, remote_path, local_path))
lmr6f669ce2009-05-31 19:02:42 +0000777 return remote_scp(command, password, timeout)
778
779
780def ssh(host, port, username, password, prompt, timeout=10):
781 """
782 Log into a remote host (guest) using SSH.
783
784 @param host: Hostname of the guest
785 @param username: User that will be used to log into the host.
786 @param password: Host's password
787 @timeout: Time in seconds that we will wait before giving up on logging
788 into the host.
789
790 @return: kvm_spawn object on success and None on failure.
791 """
lmrd16a67d2009-06-10 19:52:59 +0000792 command = ("ssh -o UserKnownHostsFile=/dev/null -p %s %s@%s" %
793 (port, username, host))
lmr6f669ce2009-05-31 19:02:42 +0000794 return remote_login(command, password, prompt, "\n", timeout)
795
796
797def telnet(host, port, username, password, prompt, timeout=10):
798 """
799 Log into a remote host (guest) using Telnet.
800
801 @param host: Hostname of the guest
802 @param username: User that will be used to log into the host.
803 @param password: Host's password
804 @timeout: Time in seconds that we will wait before giving up on logging
805 into the host.
806
807 @return: kvm_spawn object on success and None on failure.
808 """
809 command = "telnet -l %s %s %s" % (username, host, port)
810 return remote_login(command, password, prompt, "\r\n", timeout)
811
812
813# The following are functions used for running commands in the background.
814
815def track_process(sub, status_output=None, term_func=None, stdout_func=None,
816 prefix=""):
817 """
818 Read lines from the stdout pipe of the subprocess. Pass each line to
819 stdout_func prefixed by prefix. Place the lines in status_output[1].
820 When the subprocess exits, call term_func. Place the exit status in
821 status_output[0].
822
823 @brief Track a subprocess and report its output and termination.
824
825 @param sub: An object returned by subprocess.Popen
826 @param status_output: A list in which the exit status and output are to be
827 stored.
828 @param term_func: A function to call when the process terminates
829 (should take no parameters)
830 @param stdout_func: A function to call with each line of output from the
831 subprocess (should take a string parameter)
832
833 @param prefix -- a string to pre-pend to each line of the output, before
834 passing it to stdout_func
835 """
836 while True:
837 # Read a single line from stdout
838 text = sub.stdout.readline()
839 # If the subprocess exited...
840 if text == "":
841 # Get exit code
842 status = sub.wait()
843 # Report it
844 if status_output:
845 status_output[0] = status
846 # Call term_func
847 if term_func:
848 term_func()
849 return
850 # Report the text
851 if status_output:
852 status_output[1] += text
853 # Call stdout_func with the returned text
854 if stdout_func:
855 text = prefix + text.strip()
856 # We need to sanitize the text before passing it to the logging
857 # system
858 text = text.decode('utf-8', 'replace')
859 stdout_func(text)
860
861
862def run_bg(command, term_func=None, stdout_func=None, prefix="", timeout=1.0):
863 """
864 Run command as a subprocess. Call stdout_func with each line of output from
865 the subprocess (prefixed by prefix). Call term_func when the subprocess
866 terminates. If timeout expires and the subprocess is still running, return.
867
868 @brief: Run a subprocess in the background and collect its output and
869 exit status.
870
871 @param command: The shell command to execute
872 @param term_func: A function to call when the process terminates
873 (should take no parameters)
874 @param stdout_func: A function to call with each line of output from
875 the subprocess (should take a string parameter)
876 @param prefix: A string to pre-pend to each line of the output, before
877 passing it to stdout_func
878 @param timeout: Time duration (in seconds) to wait for the subprocess to
879 terminate before returning
880
881 @return: A 3-tuple containing the exit status (None if the subprocess is
882 still running), the PID of the subprocess (None if the subprocess
883 terminated), and the output collected so far.
884 """
885 # Start the process
886 sub = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE,
887 stderr=subprocess.STDOUT)
888 # Start the tracking thread
889 status_output = [None, ""]
890 thread.start_new_thread(track_process, (sub, status_output, term_func,
891 stdout_func, prefix))
892 # Wait up to timeout secs for the process to exit
893 end_time = time.time() + timeout
894 while time.time() < end_time:
895 # If the process exited, return
896 if status_output[0] != None:
897 return (status_output[0], None, status_output[1])
898 # Otherwise, sleep for a while
899 time.sleep(0.1)
900 # Report the PID and the output collected so far
901 return (None, sub.pid, status_output[1])
902
903
904# The following are utility functions related to ports.
905
906def is_sshd_running(host, port, timeout=10.0):
907 """
908 Connect to the given host and port and wait for output.
909 Return True if the given host and port are responsive.
910
911 @param host: Host's hostname
912 @param port: Host's port
913 @param timeout: Time (seconds) before we giving up on checking the SSH
914 daemon.
915
916 @return: If output is available, return True. If timeout expires and no
917 output was available, return False.
918 """
919 try:
920 # Try to connect
921 #s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
922 s = socket.socket()
923 s.connect((host, port))
924 except socket.error:
925 # Can't connect -- return False
926 s.close()
927 logging.debug("Could not connect")
928 return False
929 s.setblocking(False)
930 # Wait up to 'timeout' seconds
931 end_time = time.time() + timeout
932 while time.time() < end_time:
933 try:
934 time.sleep(0.1)
935 # Try to receive some text
936 str = s.recv(1024)
937 if len(str) > 0:
938 s.shutdown(socket.SHUT_RDWR)
939 s.close()
940 logging.debug("Success! got string %r" % str)
941 return True
942 except socket.error:
943 # No text was available; try again
944 pass
945 # Timeout elapsed and no text was received
946 s.shutdown(socket.SHUT_RDWR)
947 s.close()
948 logging.debug("Timeout")
949 return False
950
951
952def is_port_free(port):
953 """
954 Return True if the given port is available for use.
955
956 @param port: Port number
957 """
958 try:
959 s = socket.socket()
960 #s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
961 s.bind(("localhost", port))
962 free = True
963 except socket.error:
964 free = False
965 s.close()
966 return free
967
968
969def find_free_port(start_port, end_port):
970 """
971 Return a free port in the range [start_port, end_port).
972
973 @param start_port: First port that will be checked.
974 @param end_port: Port immediately after the last one that will be checked.
975 """
976 for i in range(start_port, end_port):
977 if is_port_free(i):
978 return i
979 return None
980
981
982def find_free_ports(start_port, end_port, count):
983 """
984 Return count free ports in the range [start_port, end_port).
985
986 @count: Initial number of ports known to be free in the range.
987 @param start_port: First port that will be checked.
988 @param end_port: Port immediately after the last one that will be checked.
989 """
990 ports = []
991 i = start_port
992 while i < end_port and count > 0:
993 if is_port_free(i):
994 ports.append(i)
995 count -= 1
996 i += 1
997 return ports
998
999
1000# The following are miscellaneous utility functions.
1001
1002def generate_random_string(length):
1003 """
1004 Return a random string using alphanumeric characters.
1005
1006 @length: length of the string that will be generated.
1007 """
1008 str = ""
1009 chars = string.letters + string.digits
1010 while length > 0:
1011 str += random.choice(chars)
1012 length -= 1
1013 return str
1014
1015
1016def format_str_for_message(str):
1017 """
1018 Format str so that it can be appended to a message.
1019 If str consists of one line, prefix it with a space.
1020 If str consists of multiple lines, prefix it with a newline.
1021
1022 @param str: string that will be formatted.
1023 """
1024 num_lines = len(str.splitlines())
1025 if num_lines == 0:
1026 return ""
1027 elif num_lines == 1:
1028 return " " + str
1029 else:
1030 return "\n" + str
1031
1032
1033def wait_for(func, timeout, first=0.0, step=1.0, text=None):
1034 """
1035 If func() evaluates to True before timeout expires, return the
1036 value of func(). Otherwise return None.
1037
1038 @brief: Wait until func() evaluates to True.
1039
1040 @param timeout: Timeout in seconds
1041 @param first: Time to sleep before first attempt
1042 @param steps: Time to sleep between attempts in seconds
1043 @param text: Text to print while waiting, for debug purposes
1044 """
1045 start_time = time.time()
1046 end_time = time.time() + timeout
1047
1048 time.sleep(first)
1049
1050 while time.time() < end_time:
1051 if text:
1052 logging.debug("%s (%f secs)" % (text, time.time() - start_time))
1053
1054 output = func()
1055 if output:
1056 return output
1057
1058 time.sleep(step)
1059
1060 logging.debug("Timeout elapsed")
1061 return None
1062
1063
1064def md5sum_file(filename, size=None):
1065 """
1066 Calculate the md5sum of filename.
1067 If size is not None, limit to first size bytes.
1068 Throw exception if something is wrong with filename.
1069 Can be also implemented with bash one-liner (assuming size%1024==0):
1070 dd if=filename bs=1024 count=size/1024 | md5sum -
1071
1072 @param filename: Path of the file that will have its md5sum calculated.
1073 @param returns: md5sum of the file.
1074 """
1075 chunksize = 4096
1076 fsize = os.path.getsize(filename)
1077 if not size or size>fsize:
1078 size = fsize
1079 f = open(filename, 'rb')
1080 o = md5.new()
1081 while size > 0:
1082 if chunksize > size:
1083 chunksize = size
1084 data = f.read(chunksize)
1085 if len(data) == 0:
1086 logging.debug("Nothing left to read but size=%d" % size)
1087 break
1088 o.update(data)
1089 size -= len(data)
1090 f.close()
1091 return o.hexdigest()