OLD | NEW |
1 """ | 1 """ |
2 KVM test utility functions. | 2 KVM test utility functions. |
3 | 3 |
4 @copyright: 2008-2009 Red Hat Inc. | 4 @copyright: 2008-2009 Red Hat Inc. |
5 """ | 5 """ |
6 | 6 |
7 import time, string, random, socket, os, signal, re, logging, commands, cPickle | 7 import time, string, random, socket, os, signal, re, logging, commands, cPickle |
8 import fcntl, shelve, ConfigParser, rss_file_transfer, threading, sys, UserDict | 8 import fcntl, shelve, ConfigParser |
9 from autotest_lib.client.bin import utils, os_dep | 9 from autotest_lib.client.bin import utils, os_dep |
10 from autotest_lib.client.common_lib import error, logging_config | 10 from autotest_lib.client.common_lib import error, logging_config |
11 import kvm_subprocess | 11 import kvm_subprocess |
12 try: | 12 try: |
13 import koji | 13 import koji |
14 KOJI_INSTALLED = True | 14 KOJI_INSTALLED = True |
15 except ImportError: | 15 except ImportError: |
16 KOJI_INSTALLED = False | 16 KOJI_INSTALLED = False |
17 | 17 |
18 | 18 |
19 def _lock_file(filename): | 19 def _lock_file(filename): |
20 f = open(filename, "w") | 20 f = open(filename, "w") |
21 fcntl.lockf(f, fcntl.LOCK_EX) | 21 fcntl.lockf(f, fcntl.LOCK_EX) |
22 return f | 22 return f |
23 | 23 |
24 | 24 |
25 def _unlock_file(f): | 25 def _unlock_file(f): |
26 fcntl.lockf(f, fcntl.LOCK_UN) | 26 fcntl.lockf(f, fcntl.LOCK_UN) |
27 f.close() | 27 f.close() |
28 | 28 |
29 | 29 |
30 def is_vm(obj): | 30 def dump_env(obj, filename): |
31 """ | 31 """ |
32 Tests whether a given object is a VM object. | 32 Dump KVM test environment to a file. |
33 | 33 |
34 @param obj: Python object. | 34 @param filename: Path to a file where the environment will be dumped to. |
35 """ | 35 """ |
36 return obj.__class__.__name__ == "VM" | 36 file = open(filename, "w") |
| 37 cPickle.dump(obj, file) |
| 38 file.close() |
37 | 39 |
38 | 40 |
39 class Env(UserDict.IterableUserDict): | 41 def load_env(filename, version): |
40 """ | 42 """ |
41 A dict-like object containing global objects used by tests. | 43 Load KVM test environment from an env file. |
| 44 If the version recorded in the file is lower than version, return an empty |
| 45 env. If some other error occurs during unpickling, return an empty env. |
| 46 |
| 47 @param filename: Path to an env file. |
42 """ | 48 """ |
43 def __init__(self, filename=None, version=0): | 49 default = {"version": version} |
44 """ | 50 try: |
45 Create an empty Env object or load an existing one from a file. | 51 file = open(filename, "r") |
46 | 52 env = cPickle.load(file) |
47 If the version recorded in the file is lower than version, or if some | 53 file.close() |
48 error occurs during unpickling, or if filename is not supplied, | 54 if env.get("version", 0) < version: |
49 create an empty Env object. | 55 logging.warn("Incompatible env file found. Not using it.") |
50 | 56 return default |
51 @param filename: Path to an env file. | 57 return env |
52 @param version: Required env version (int). | 58 # Almost any exception can be raised during unpickling, so let's catch |
53 """ | 59 # them all |
54 UserDict.IterableUserDict.__init__(self) | 60 except Exception, e: |
55 empty = {"version": version} | 61 logging.warn(e) |
56 if filename: | 62 return default |
57 self._filename = filename | |
58 try: | |
59 f = open(filename, "r") | |
60 env = cPickle.load(f) | |
61 f.close() | |
62 if env.get("version", 0) >= version: | |
63 self.data = env | |
64 else: | |
65 logging.warn("Incompatible env file found. Not using it.") | |
66 self.data = empty | |
67 # Almost any exception can be raised during unpickling, so let's | |
68 # catch them all | |
69 except Exception, e: | |
70 logging.warn(e) | |
71 self.data = empty | |
72 else: | |
73 self.data = empty | |
74 | 63 |
75 | 64 |
76 def save(self, filename=None): | 65 def get_sub_dict(dict, name): |
77 """ | 66 """ |
78 Pickle the contents of the Env object into a file. | 67 Return a "sub-dict" corresponding to a specific object. |
79 | 68 |
80 @param filename: Filename to pickle the dict into. If not supplied, | 69 Operate on a copy of dict: for each key that ends with the suffix |
81 use the filename from which the dict was loaded. | 70 "_" + name, strip the suffix from the key, and set the value of |
82 """ | 71 the stripped key to that of the key. Return the resulting dict. |
83 filename = filename or self._filename | 72 |
84 f = open(filename, "w") | 73 @param name: Suffix of the key we want to set the value. |
85 cPickle.dump(self.data, f) | 74 """ |
86 f.close() | 75 suffix = "_" + name |
| 76 new_dict = dict.copy() |
| 77 for key in dict.keys(): |
| 78 if key.endswith(suffix): |
| 79 new_key = key.split(suffix)[0] |
| 80 new_dict[new_key] = dict[key] |
| 81 return new_dict |
87 | 82 |
88 | 83 |
89 def get_all_vms(self): | 84 def get_sub_dict_names(dict, keyword): |
90 """ | 85 """ |
91 Return a list of all VM objects in this Env object. | 86 Return a list of "sub-dict" names that may be extracted with get_sub_dict. |
92 """ | |
93 return [o for o in self.values() if is_vm(o)] | |
94 | 87 |
| 88 This function may be modified to change the behavior of all functions that |
| 89 deal with multiple objects defined in dicts (e.g. VMs, images, NICs). |
95 | 90 |
96 def get_vm(self, name): | 91 @param keyword: A key in dict (e.g. "vms", "images", "nics"). |
97 """ | |
98 Return a VM object by its name. | |
99 | |
100 @param name: VM name. | |
101 """ | |
102 return self.get("vm__%s" % name) | |
103 | |
104 | |
105 def register_vm(self, name, vm): | |
106 """ | |
107 Register a VM in this Env object. | |
108 | |
109 @param name: VM name. | |
110 @param vm: VM object. | |
111 """ | |
112 self["vm__%s" % name] = vm | |
113 | |
114 | |
115 def unregister_vm(self, name): | |
116 """ | |
117 Remove a given VM. | |
118 | |
119 @param name: VM name. | |
120 """ | |
121 del self["vm__%s" % name] | |
122 | |
123 | |
124 def register_installer(self, installer): | |
125 """ | |
126 Register a installer that was just run | |
127 | |
128 The installer will be available for other tests, so that | |
129 information about the installed KVM modules and qemu-kvm can be used by | |
130 them. | |
131 """ | |
132 self['last_installer'] = installer | |
133 | |
134 | |
135 def previous_installer(self): | |
136 """ | |
137 Return the last installer that was registered | |
138 """ | |
139 return self.get('last_installer') | |
140 | |
141 | |
142 class Params(UserDict.IterableUserDict): | |
143 """ | 92 """ |
144 A dict-like object passed to every test. | 93 names = dict.get(keyword) |
145 """ | 94 if names: |
146 def objects(self, key): | 95 return names.split() |
147 """ | 96 else: |
148 Return the names of objects defined using a given key. | 97 return [] |
149 | |
150 @param key: The name of the key whose value lists the objects | |
151 (e.g. 'nics'). | |
152 """ | |
153 return self.get(key, "").split() | |
154 | |
155 | |
156 def object_params(self, obj_name): | |
157 """ | |
158 Return a dict-like object containing the parameters of an individual | |
159 object. | |
160 | |
161 This method behaves as follows: the suffix '_' + obj_name is removed | |
162 from all key names that have it. Other key names are left unchanged. | |
163 The values of keys with the suffix overwrite the values of their | |
164 suffixless versions. | |
165 | |
166 @param obj_name: The name of the object (objects are listed by the | |
167 objects() method). | |
168 """ | |
169 suffix = "_" + obj_name | |
170 new_dict = self.copy() | |
171 for key in self: | |
172 if key.endswith(suffix): | |
173 new_key = key.split(suffix)[0] | |
174 new_dict[new_key] = self[key] | |
175 return new_dict | |
176 | 98 |
177 | 99 |
178 # Functions related to MAC/IP addresses | 100 # Functions related to MAC/IP addresses |
179 | 101 |
180 def _open_mac_pool(lock_mode): | 102 def _open_mac_pool(lock_mode): |
181 lock_file = open("/tmp/mac_lock", "w+") | 103 lock_file = open("/tmp/mac_lock", "w+") |
182 fcntl.lockf(lock_file, lock_mode) | 104 fcntl.lockf(lock_file, lock_mode) |
183 pool = shelve.open("/tmp/address_pool") | 105 pool = shelve.open("/tmp/address_pool") |
184 return pool, lock_file | 106 return pool, lock_file |
185 | 107 |
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
311 if not dev: | 233 if not dev: |
312 return False | 234 return False |
313 dev = dev[0].split()[-1] | 235 dev = dev[0].split()[-1] |
314 | 236 |
315 # Send an ARP request | 237 # Send an ARP request |
316 o = commands.getoutput("%s -f -c 3 -I %s %s" % | 238 o = commands.getoutput("%s -f -c 3 -I %s %s" % |
317 (find_command("arping"), dev, ip)) | 239 (find_command("arping"), dev, ip)) |
318 return bool(regex.search(o)) | 240 return bool(regex.search(o)) |
319 | 241 |
320 | 242 |
| 243 # Functions for working with the environment (a dict-like object) |
| 244 |
| 245 def is_vm(obj): |
| 246 """ |
| 247 Tests whether a given object is a VM object. |
| 248 |
| 249 @param obj: Python object (pretty much everything on python). |
| 250 """ |
| 251 return obj.__class__.__name__ == "VM" |
| 252 |
| 253 |
| 254 def env_get_all_vms(env): |
| 255 """ |
| 256 Return a list of all VM objects on a given environment. |
| 257 |
| 258 @param env: Dictionary with environment items. |
| 259 """ |
| 260 vms = [] |
| 261 for obj in env.values(): |
| 262 if is_vm(obj): |
| 263 vms.append(obj) |
| 264 return vms |
| 265 |
| 266 |
| 267 def env_get_vm(env, name): |
| 268 """ |
| 269 Return a VM object by its name. |
| 270 |
| 271 @param name: VM name. |
| 272 """ |
| 273 return env.get("vm__%s" % name) |
| 274 |
| 275 |
| 276 def env_register_vm(env, name, vm): |
| 277 """ |
| 278 Register a given VM in a given env. |
| 279 |
| 280 @param env: Environment where we will register the VM. |
| 281 @param name: VM name. |
| 282 @param vm: VM object. |
| 283 """ |
| 284 env["vm__%s" % name] = vm |
| 285 |
| 286 |
| 287 def env_unregister_vm(env, name): |
| 288 """ |
| 289 Remove a given VM from a given env. |
| 290 |
| 291 @param env: Environment where we will un-register the VM. |
| 292 @param name: VM name. |
| 293 """ |
| 294 del env["vm__%s" % name] |
| 295 |
| 296 |
321 # Utility functions for dealing with external processes | 297 # Utility functions for dealing with external processes |
322 | 298 |
323 def find_command(cmd): | 299 def find_command(cmd): |
324 for dir in ["/usr/local/sbin", "/usr/local/bin", | 300 for dir in ["/usr/local/sbin", "/usr/local/bin", |
325 "/usr/sbin", "/usr/bin", "/sbin", "/bin"]: | 301 "/usr/sbin", "/usr/bin", "/sbin", "/bin"]: |
326 file = os.path.join(dir, cmd) | 302 file = os.path.join(dir, cmd) |
327 if os.path.exists(file): | 303 if os.path.exists(file): |
328 return file | 304 return file |
329 raise ValueError('Missing command: %s' % cmd) | 305 raise ValueError('Missing command: %s' % cmd) |
330 | 306 |
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
438 occasions build may be dependant on the source directory disposition. | 414 occasions build may be dependant on the source directory disposition. |
439 The reason why the return codes are numbers is that we might have more | 415 The reason why the return codes are numbers is that we might have more |
440 changes on the source directory layout, so it's not scalable to just use | 416 changes on the source directory layout, so it's not scalable to just use |
441 strings like 'old_repo', 'new_repo' and such. | 417 strings like 'old_repo', 'new_repo' and such. |
442 | 418 |
443 @param source_dir: Source code path that will be inspected. | 419 @param source_dir: Source code path that will be inspected. |
444 """ | 420 """ |
445 os.chdir(source_dir) | 421 os.chdir(source_dir) |
446 has_qemu_dir = os.path.isdir('qemu') | 422 has_qemu_dir = os.path.isdir('qemu') |
447 has_kvm_dir = os.path.isdir('kvm') | 423 has_kvm_dir = os.path.isdir('kvm') |
448 if has_qemu_dir: | 424 if has_qemu_dir and not has_kvm_dir: |
449 logging.debug("qemu directory detected, source dir layout 1") | 425 logging.debug("qemu directory detected, source dir layout 1") |
450 return 1 | 426 return 1 |
451 if has_kvm_dir and not has_qemu_dir: | 427 if has_kvm_dir and not has_qemu_dir: |
452 logging.debug("kvm directory detected, source dir layout 2") | 428 logging.debug("kvm directory detected, source dir layout 2") |
453 return 2 | 429 return 2 |
454 else: | 430 else: |
455 raise error.TestError("Unknown source dir layout, cannot proceed.") | 431 raise error.TestError("Unknown source dir layout, cannot proceed.") |
456 | 432 |
457 | 433 |
458 # The following are functions used for SSH, SCP and Telnet communication with | 434 # The following are functions used for SSH, SCP and Telnet communication with |
459 # guests. | 435 # guests. |
460 | 436 |
461 def _remote_login(session, username, password, prompt, timeout=10): | 437 def _remote_login(session, username, password, prompt, timeout=10): |
462 """ | 438 """ |
463 Log into a remote host (guest) using SSH or Telnet. Wait for questions | 439 Log into a remote host (guest) using SSH or Telnet. Wait for questions |
464 and provide answers. If timeout expires while waiting for output from the | 440 and provide answers. If timeout expires while waiting for output from the |
465 child (e.g. a password prompt or a shell prompt) -- fail. | 441 child (e.g. a password prompt or a shell prompt) -- fail. |
466 | 442 |
467 @brief: Log into a remote host (guest) using SSH or Telnet. | 443 @brief: Log into a remote host (guest) using SSH or Telnet. |
468 | 444 |
469 @param session: An Expect or ShellSession instance to operate on | 445 @param session: A kvm_expect or kvm_shell_session instance to operate on |
470 @param username: The username to send in reply to a login prompt | 446 @param username: The username to send in reply to a login prompt |
471 @param password: The password to send in reply to a password prompt | 447 @param password: The password to send in reply to a password prompt |
472 @param prompt: The shell prompt that indicates a successful login | 448 @param prompt: The shell prompt that indicates a successful login |
473 @param timeout: The maximal time duration (in seconds) to wait for each | 449 @param timeout: The maximal time duration (in seconds) to wait for each |
474 step of the login procedure (i.e. the "Are you sure" prompt, the | 450 step of the login procedure (i.e. the "Are you sure" prompt, the |
475 password prompt, the shell prompt, etc) | 451 password prompt, the shell prompt, etc) |
476 | 452 |
477 @return: True on success and False otherwise. | 453 @return: True on success and False otherwise. |
478 """ | 454 """ |
479 password_prompt_count = 0 | 455 password_prompt_count = 0 |
480 login_prompt_count = 0 | 456 login_prompt_count = 0 |
481 | 457 |
482 while True: | 458 while True: |
483 try: | 459 (match, text) = session.read_until_last_line_matches( |
484 match, text = session.read_until_last_line_matches( | |
485 [r"[Aa]re you sure", r"[Pp]assword:\s*$", r"[Ll]ogin:\s*$", | 460 [r"[Aa]re you sure", r"[Pp]assword:\s*$", r"[Ll]ogin:\s*$", |
486 r"[Cc]onnection.*closed", r"[Cc]onnection.*refused", | 461 r"[Cc]onnection.*closed", r"[Cc]onnection.*refused", |
487 r"[Pp]lease wait", prompt], | 462 r"[Pp]lease wait", prompt], |
488 timeout=timeout, internal_timeout=0.5) | 463 timeout=timeout, internal_timeout=0.5) |
489 if match == 0: # "Are you sure you want to continue connecting" | 464 if match == 0: # "Are you sure you want to continue connecting" |
490 logging.debug("Got 'Are you sure...'; sending 'yes'") | 465 logging.debug("Got 'Are you sure...'; sending 'yes'") |
491 session.sendline("yes") | 466 session.sendline("yes") |
| 467 continue |
| 468 elif match == 1: # "password:" |
| 469 if password_prompt_count == 0: |
| 470 logging.debug("Got password prompt; sending '%s'" % password) |
| 471 session.sendline(password) |
| 472 password_prompt_count += 1 |
492 continue | 473 continue |
493 elif match == 1: # "password:" | 474 else: |
494 if password_prompt_count == 0: | 475 logging.debug("Got password prompt again") |
495 logging.debug("Got password prompt; sending '%s'" % password
) | |
496 session.sendline(password) | |
497 password_prompt_count += 1 | |
498 continue | |
499 else: | |
500 logging.debug("Got password prompt again") | |
501 return False | |
502 elif match == 2: # "login:" | |
503 if login_prompt_count == 0: | |
504 logging.debug("Got username prompt; sending '%s'" % username
) | |
505 session.sendline(username) | |
506 login_prompt_count += 1 | |
507 continue | |
508 else: | |
509 logging.debug("Got username prompt again") | |
510 return False | |
511 elif match == 3: # "Connection closed" | |
512 logging.debug("Got 'Connection closed'") | |
513 return False | 476 return False |
514 elif match == 4: # "Connection refused" | 477 elif match == 2: # "login:" |
515 logging.debug("Got 'Connection refused'") | 478 if login_prompt_count == 0: |
| 479 logging.debug("Got username prompt; sending '%s'" % username) |
| 480 session.sendline(username) |
| 481 login_prompt_count += 1 |
| 482 continue |
| 483 else: |
| 484 logging.debug("Got username prompt again") |
516 return False | 485 return False |
517 elif match == 5: # "Please wait" | 486 elif match == 3: # "Connection closed" |
518 logging.debug("Got 'Please wait'") | 487 logging.debug("Got 'Connection closed'") |
519 timeout = 30 | |
520 continue | |
521 elif match == 6: # prompt | |
522 logging.debug("Got shell prompt -- logged in") | |
523 return True | |
524 except kvm_subprocess.ExpectTimeoutError, e: | |
525 logging.debug("Timeout elapsed (output so far: %r)" % e.output) | |
526 return False | 488 return False |
527 except kvm_subprocess.ExpectProcessTerminatedError, e: | 489 elif match == 4: # "Connection refused" |
528 logging.debug("Process terminated (output so far: %r)" % e.output) | 490 logging.debug("Got 'Connection refused'") |
| 491 return False |
| 492 elif match == 5: # "Please wait" |
| 493 logging.debug("Got 'Please wait'") |
| 494 timeout = 30 |
| 495 continue |
| 496 elif match == 6: # prompt |
| 497 logging.debug("Got shell prompt -- logged in") |
| 498 return session |
| 499 else: # match == None |
| 500 logging.debug("Timeout elapsed or process terminated") |
529 return False | 501 return False |
530 | 502 |
531 | 503 |
532 def _remote_scp(session, password, transfer_timeout=600, login_timeout=10): | 504 def _remote_scp(session, password, transfer_timeout=600, login_timeout=10): |
533 """ | 505 """ |
534 Transfer file(s) to a remote host (guest) using SCP. Wait for questions | 506 Transfer file(s) to a remote host (guest) using SCP. Wait for questions |
535 and provide answers. If login_timeout expires while waiting for output | 507 and provide answers. If login_timeout expires while waiting for output |
536 from the child (e.g. a password prompt), fail. If transfer_timeout expires | 508 from the child (e.g. a password prompt), fail. If transfer_timeout expires |
537 while waiting for the transfer to complete, fail. | 509 while waiting for the transfer to complete, fail. |
538 | 510 |
539 @brief: Transfer files using SCP, given a command line. | 511 @brief: Transfer files using SCP, given a command line. |
540 | 512 |
541 @param session: An Expect or ShellSession instance to operate on | 513 @param session: A kvm_expect or kvm_shell_session instance to operate on |
542 @param password: The password to send in reply to a password prompt. | 514 @param password: The password to send in reply to a password prompt. |
543 @param transfer_timeout: The time duration (in seconds) to wait for the | 515 @param transfer_timeout: The time duration (in seconds) to wait for the |
544 transfer to complete. | 516 transfer to complete. |
545 @param login_timeout: The maximal time duration (in seconds) to wait for | 517 @param login_timeout: The maximal time duration (in seconds) to wait for |
546 each step of the login procedure (i.e. the "Are you sure" prompt or | 518 each step of the login procedure (i.e. the "Are you sure" prompt or |
547 the password prompt) | 519 the password prompt) |
548 | 520 |
549 @return: True if the transfer succeeds and False on failure. | 521 @return: True if the transfer succeeds and False on failure. |
550 """ | 522 """ |
551 password_prompt_count = 0 | 523 password_prompt_count = 0 |
552 timeout = login_timeout | 524 timeout = login_timeout |
553 | 525 |
554 while True: | 526 while True: |
555 try: | 527 (match, text) = session.read_until_last_line_matches( |
556 match, text = session.read_until_last_line_matches( | |
557 [r"[Aa]re you sure", r"[Pp]assword:\s*$", r"lost connection"], | 528 [r"[Aa]re you sure", r"[Pp]assword:\s*$", r"lost connection"], |
558 timeout=timeout, internal_timeout=0.5) | 529 timeout=timeout, internal_timeout=0.5) |
559 if match == 0: # "Are you sure you want to continue connecting" | 530 if match == 0: # "Are you sure you want to continue connecting" |
560 logging.debug("Got 'Are you sure...'; sending 'yes'") | 531 logging.debug("Got 'Are you sure...'; sending 'yes'") |
561 session.sendline("yes") | 532 session.sendline("yes") |
| 533 continue |
| 534 elif match == 1: # "password:" |
| 535 if password_prompt_count == 0: |
| 536 logging.debug("Got password prompt; sending '%s'" % password) |
| 537 session.sendline(password) |
| 538 password_prompt_count += 1 |
| 539 timeout = transfer_timeout |
562 continue | 540 continue |
563 elif match == 1: # "password:" | 541 else: |
564 if password_prompt_count == 0: | 542 logging.debug("Got password prompt again") |
565 logging.debug("Got password prompt; sending '%s'" % password
) | |
566 session.sendline(password) | |
567 password_prompt_count += 1 | |
568 timeout = transfer_timeout | |
569 continue | |
570 else: | |
571 logging.debug("Got password prompt again") | |
572 return False | |
573 elif match == 2: # "lost connection" | |
574 logging.debug("Got 'lost connection'") | |
575 return False | 543 return False |
576 except kvm_subprocess.ExpectTimeoutError, e: | 544 elif match == 2: # "lost connection" |
577 logging.debug("Timeout expired") | 545 logging.debug("Got 'lost connection'") |
578 return False | 546 return False |
579 except kvm_subprocess.ExpectProcessTerminatedError, e: | 547 else: # match == None |
580 logging.debug("SCP process terminated with status %s", e.status) | 548 if session.is_alive(): |
581 return e.status == 0 | 549 logging.debug("Timeout expired") |
| 550 return False |
| 551 else: |
| 552 status = session.get_status() |
| 553 logging.debug("SCP process terminated with status %s", status) |
| 554 return status == 0 |
582 | 555 |
583 | 556 |
584 def remote_login(client, host, port, username, password, prompt, linesep="\n", | 557 def remote_login(client, host, port, username, password, prompt, linesep="\n", |
585 log_filename=None, timeout=10): | 558 log_filename=None, timeout=10): |
586 """ | 559 """ |
587 Log into a remote host (guest) using SSH/Telnet/Netcat. | 560 Log into a remote host (guest) using SSH/Telnet/Netcat. |
588 | 561 |
589 @param client: The client to use ('ssh', 'telnet' or 'nc') | 562 @param client: The client to use ('ssh', 'telnet' or 'nc') |
590 @param host: Hostname or IP address | 563 @param host: Hostname or IP address |
591 @param port: Port to connect to | 564 @param port: Port to connect to |
592 @param username: Username (if required) | 565 @param username: Username (if required) |
593 @param password: Password (if required) | 566 @param password: Password (if required) |
594 @param prompt: Shell prompt (regular expression) | 567 @param prompt: Shell prompt (regular expression) |
595 @param linesep: The line separator to use when sending lines | 568 @param linesep: The line separator to use when sending lines |
596 (e.g. '\\n' or '\\r\\n') | 569 (e.g. '\\n' or '\\r\\n') |
597 @param log_filename: If specified, log all output to this file | 570 @param log_filename: If specified, log all output to this file |
598 @param timeout: The maximal time duration (in seconds) to wait for | 571 @param timeout: The maximal time duration (in seconds) to wait for |
599 each step of the login procedure (i.e. the "Are you sure" prompt | 572 each step of the login procedure (i.e. the "Are you sure" prompt |
600 or the password prompt) | 573 or the password prompt) |
601 | 574 |
602 @return: ShellSession object on success and None on failure. | 575 @return: kvm_shell_session object on success and None on failure. |
603 """ | 576 """ |
604 if client == "ssh": | 577 if client == "ssh": |
605 cmd = ("ssh -o UserKnownHostsFile=/dev/null " | 578 cmd = ("ssh -o UserKnownHostsFile=/dev/null " |
606 "-o PreferredAuthentications=password -p %s %s@%s" % | 579 "-o PreferredAuthentications=password -p %s %s@%s" % |
607 (port, username, host)) | 580 (port, username, host)) |
608 elif client == "telnet": | 581 elif client == "telnet": |
609 cmd = "telnet -l %s %s %s" % (username, host, port) | 582 cmd = "telnet -l %s %s %s" % (username, host, port) |
610 elif client == "nc": | 583 elif client == "nc": |
611 cmd = "nc %s %s" % (host, port) | 584 cmd = "nc %s %s" % (host, port) |
612 else: | 585 else: |
613 logging.error("Unknown remote shell client: %s" % client) | 586 logging.error("Unknown remote shell client: %s" % client) |
614 return | 587 return |
615 | 588 |
616 logging.debug("Trying to login with command '%s'" % cmd) | 589 logging.debug("Trying to login with command '%s'" % cmd) |
617 session = kvm_subprocess.ShellSession(cmd, linesep=linesep, prompt=prompt) | 590 session = kvm_subprocess.kvm_shell_session(cmd, linesep=linesep, |
| 591 prompt=prompt) |
618 if _remote_login(session, username, password, prompt, timeout): | 592 if _remote_login(session, username, password, prompt, timeout): |
619 if log_filename: | 593 if log_filename: |
620 session.set_output_func(log_line) | 594 session.set_output_func(log_line) |
621 session.set_output_params((log_filename,)) | 595 session.set_output_params((log_filename,)) |
622 return session | 596 return session |
623 else: | 597 else: |
624 session.close() | 598 session.close() |
625 | 599 |
626 | 600 |
627 def remote_scp(command, password, log_filename=None, transfer_timeout=600, | 601 def remote_scp(command, password, log_filename=None, transfer_timeout=600, |
(...skipping 18 matching lines...) Expand all Loading... |
646 logging.debug("Trying to SCP with command '%s', timeout %ss", | 620 logging.debug("Trying to SCP with command '%s', timeout %ss", |
647 command, transfer_timeout) | 621 command, transfer_timeout) |
648 | 622 |
649 if log_filename: | 623 if log_filename: |
650 output_func = log_line | 624 output_func = log_line |
651 output_params = (log_filename,) | 625 output_params = (log_filename,) |
652 else: | 626 else: |
653 output_func = None | 627 output_func = None |
654 output_params = () | 628 output_params = () |
655 | 629 |
656 session = kvm_subprocess.Expect(command, | 630 session = kvm_subprocess.kvm_expect(command, |
657 output_func=output_func, | 631 output_func=output_func, |
658 output_params=output_params) | 632 output_params=output_params) |
659 try: | 633 try: |
660 return _remote_scp(session, password, transfer_timeout, login_timeout) | 634 return _remote_scp(session, password, transfer_timeout, login_timeout) |
661 finally: | 635 finally: |
662 session.close() | 636 session.close() |
663 | 637 |
664 | 638 |
665 def copy_files_to(address, client, username, password, port, local_path, | |
666 remote_path, log_filename=None, timeout=600): | |
667 """ | |
668 Decide the transfer cleint and copy file to a remote host (guest). | |
669 | |
670 @param client: Type of transfer client | |
671 @param username: Username (if required) | |
672 @param password: Password (if requried) | |
673 @param local_path: Path on the local machine where we are copying from | |
674 @param remote_path: Path on the remote machine where we are copying to | |
675 @param address: Address of remote host(guest) | |
676 @param log_filename: If specified, log all output to this file | |
677 @param timeout: The time duration (in seconds) to wait for the transfer to | |
678 complete. | |
679 | |
680 @return: True on success and False on failure. | |
681 """ | |
682 | |
683 if not address or not port: | |
684 logging.debug("IP address or port unavailable") | |
685 return None | |
686 | |
687 if client == "scp": | |
688 return scp_to_remote(address, port, username, password, local_path, | |
689 remote_path, log_filename, timeout) | |
690 elif client == "rss": | |
691 c = rss_file_transfer.FileUploadClient(address, port) | |
692 c.upload(local_path, remote_path, timeout) | |
693 c.close() | |
694 return True | |
695 | |
696 | |
697 def copy_files_from(address, client, username, password, port, local_path, | |
698 remote_path, log_filename=None, timeout=600): | |
699 """ | |
700 Decide the transfer cleint and copy file from a remote host (guest). | |
701 | |
702 @param client: Type of transfer client | |
703 @param username: Username (if required) | |
704 @param password: Password (if requried) | |
705 @param local_path: Path on the local machine where we are copying from | |
706 @param remote_path: Path on the remote machine where we are copying to | |
707 @param address: Address of remote host(guest) | |
708 @param log_filename: If specified, log all output to this file | |
709 @param timeout: The time duration (in seconds) to wait for the transfer to | |
710 complete. | |
711 | |
712 @return: True on success and False on failure. | |
713 """ | |
714 | |
715 if not address or not port: | |
716 logging.debug("IP address or port unavailable") | |
717 return None | |
718 | |
719 if client == "scp": | |
720 return scp_from_remote(address, port, username, password, remote_path, | |
721 local_path, log_filename, timeout) | |
722 elif client == "rss": | |
723 c = rss_file_transfer.FileDownloadClient(address, port) | |
724 c.download(remote_path, local_path, timeout) | |
725 c.close() | |
726 return True | |
727 | |
728 | |
729 def scp_to_remote(host, port, username, password, local_path, remote_path, | 639 def scp_to_remote(host, port, username, password, local_path, remote_path, |
730 log_filename=None, timeout=600): | 640 log_filename=None, timeout=600): |
731 """ | 641 """ |
732 Copy files to a remote host (guest) through scp. | 642 Copy files to a remote host (guest). |
733 | 643 |
734 @param host: Hostname or IP address | 644 @param host: Hostname or IP address |
735 @param username: Username (if required) | 645 @param username: Username (if required) |
736 @param password: Password (if required) | 646 @param password: Password (if required) |
737 @param local_path: Path on the local machine where we are copying from | 647 @param local_path: Path on the local machine where we are copying from |
738 @param remote_path: Path on the remote machine where we are copying to | 648 @param remote_path: Path on the remote machine where we are copying to |
739 @param log_filename: If specified, log all output to this file | 649 @param log_filename: If specified, log all output to this file |
740 @param timeout: The time duration (in seconds) to wait for the transfer | 650 @param timeout: The time duration (in seconds) to wait for the transfer |
741 to complete. | 651 to complete. |
742 | 652 |
(...skipping 318 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1061 def get_vendor_from_pci_id(pci_id): | 971 def get_vendor_from_pci_id(pci_id): |
1062 """ | 972 """ |
1063 Check out the device vendor ID according to pci_id. | 973 Check out the device vendor ID according to pci_id. |
1064 | 974 |
1065 @param pci_id: PCI ID of a device. | 975 @param pci_id: PCI ID of a device. |
1066 """ | 976 """ |
1067 cmd = "lspci -n | awk '/%s/ {print $3}'" % pci_id | 977 cmd = "lspci -n | awk '/%s/ {print $3}'" % pci_id |
1068 return re.sub(":", " ", commands.getoutput(cmd)) | 978 return re.sub(":", " ", commands.getoutput(cmd)) |
1069 | 979 |
1070 | 980 |
1071 class Thread(threading.Thread): | |
1072 """ | |
1073 Run a function in a background thread. | |
1074 """ | |
1075 def __init__(self, target, args=(), kwargs={}): | |
1076 """ | |
1077 Initialize the instance. | |
1078 | |
1079 @param target: Function to run in the thread. | |
1080 @param args: Arguments to pass to target. | |
1081 @param kwargs: Keyword arguments to pass to target. | |
1082 """ | |
1083 threading.Thread.__init__(self) | |
1084 self._target = target | |
1085 self._args = args | |
1086 self._kwargs = kwargs | |
1087 | |
1088 | |
1089 def run(self): | |
1090 """ | |
1091 Run target (passed to the constructor). No point in calling this | |
1092 function directly. Call start() to make this function run in a new | |
1093 thread. | |
1094 """ | |
1095 self._e = None | |
1096 self._retval = None | |
1097 try: | |
1098 try: | |
1099 self._retval = self._target(*self._args, **self._kwargs) | |
1100 except: | |
1101 self._e = sys.exc_info() | |
1102 raise | |
1103 finally: | |
1104 # Avoid circular references (start() may be called only once so | |
1105 # it's OK to delete these) | |
1106 del self._target, self._args, self._kwargs | |
1107 | |
1108 | |
1109 def join(self, timeout=None): | |
1110 """ | |
1111 Join the thread. If target raised an exception, re-raise it. | |
1112 Otherwise, return the value returned by target. | |
1113 | |
1114 @param timeout: Timeout value to pass to threading.Thread.join(). | |
1115 """ | |
1116 threading.Thread.join(self, timeout) | |
1117 try: | |
1118 if self._e: | |
1119 raise self._e[0], self._e[1], self._e[2] | |
1120 else: | |
1121 return self._retval | |
1122 finally: | |
1123 # Avoid circular references (join() may be called multiple times | |
1124 # so we can't delete these) | |
1125 self._e = None | |
1126 self._retval = None | |
1127 | |
1128 | |
1129 def parallel(targets): | |
1130 """ | |
1131 Run multiple functions in parallel. | |
1132 | |
1133 @param targets: A sequence of tuples or functions. If it's a sequence of | |
1134 tuples, each tuple will be interpreted as (target, args, kwargs) or | |
1135 (target, args) or (target,) depending on its length. If it's a | |
1136 sequence of functions, the functions will be called without | |
1137 arguments. | |
1138 @return: A list of the values returned by the functions called. | |
1139 """ | |
1140 threads = [] | |
1141 for target in targets: | |
1142 if isinstance(target, tuple) or isinstance(target, list): | |
1143 t = Thread(*target) | |
1144 else: | |
1145 t = Thread(target) | |
1146 threads.append(t) | |
1147 t.start() | |
1148 return [t.join() for t in threads] | |
1149 | |
1150 | |
1151 class KvmLoggingConfig(logging_config.LoggingConfig): | 981 class KvmLoggingConfig(logging_config.LoggingConfig): |
1152 """ | 982 """ |
1153 Used with the sole purpose of providing convenient logging setup | 983 Used with the sole purpose of providing convenient logging setup |
1154 for the KVM test auxiliary programs. | 984 for the KVM test auxiliary programs. |
1155 """ | 985 """ |
1156 def configure_logging(self, results_dir=None, verbose=False): | 986 def configure_logging(self, results_dir=None, verbose=False): |
1157 super(KvmLoggingConfig, self).configure_logging(use_console=True, | 987 super(KvmLoggingConfig, self).configure_logging(use_console=True, |
1158 verbose=verbose) | 988 verbose=verbose) |
1159 | 989 |
1160 | 990 |
(...skipping 349 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1510 if tag and build: | 1340 if tag and build: |
1511 logging.info("Both tag and build parameters provided, ignoring tag " | 1341 logging.info("Both tag and build parameters provided, ignoring tag " |
1512 "parameter...") | 1342 "parameter...") |
1513 | 1343 |
1514 if not tag and not build: | 1344 if not tag and not build: |
1515 raise ValueError("Koji install selected but neither koji_tag " | 1345 raise ValueError("Koji install selected but neither koji_tag " |
1516 "nor koji_build parameters provided. Please " | 1346 "nor koji_build parameters provided. Please " |
1517 "provide an appropriate tag or build name.") | 1347 "provide an appropriate tag or build name.") |
1518 | 1348 |
1519 if not build: | 1349 if not build: |
1520 builds = self.session.listTagged(tag, latest=True, inherit=True, | 1350 builds = self.session.listTagged(tag, latest=True, |
1521 package=src_package) | 1351 package=src_package) |
1522 if not builds: | 1352 if not builds: |
1523 raise ValueError("Tag %s has no builds of %s" % (tag, | 1353 raise ValueError("Tag %s has no builds of %s" % (tag, |
1524 src_package)) | 1354 src_package)) |
1525 info = builds[0] | 1355 info = builds[0] |
1526 else: | 1356 else: |
1527 info = self.session.getBuild(build) | 1357 info = self.session.getBuild(build) |
1528 | 1358 |
1529 if info is None: | 1359 if info is None: |
1530 raise ValueError('No such brew/koji build: %s' % build) | 1360 raise ValueError('No such brew/koji build: %s' % build) |
(...skipping 22 matching lines...) Expand all Loading... |
1553 download = False | 1383 download = False |
1554 else: | 1384 else: |
1555 download = True | 1385 download = True |
1556 | 1386 |
1557 if download: | 1387 if download: |
1558 r = utils.get_file(url, | 1388 r = utils.get_file(url, |
1559 os.path.join(dst_dir, os.path.basename(url))) | 1389 os.path.join(dst_dir, os.path.basename(url))) |
1560 rpm_paths.append(r) | 1390 rpm_paths.append(r) |
1561 | 1391 |
1562 return rpm_paths | 1392 return rpm_paths |
1563 | |
1564 | |
1565 def umount(src, mount_point, type): | |
1566 """ | |
1567 Umount the src mounted in mount_point. | |
1568 | |
1569 @src: mount source | |
1570 @mount_point: mount point | |
1571 @type: file system type | |
1572 """ | |
1573 | |
1574 mount_string = "%s %s %s" % (src, mount_point, type) | |
1575 if mount_string in file("/etc/mtab").read(): | |
1576 umount_cmd = "umount %s" % mount_point | |
1577 try: | |
1578 utils.system(umount_cmd) | |
1579 return True | |
1580 except error.CmdError: | |
1581 return False | |
1582 else: | |
1583 logging.debug("%s is not mounted under %s" % (src, mount_point)) | |
1584 return True | |
1585 | |
1586 | |
1587 def mount(src, mount_point, type, perm="rw"): | |
1588 """ | |
1589 Mount the src into mount_point of the host. | |
1590 | |
1591 @src: mount source | |
1592 @mount_point: mount point | |
1593 @type: file system type | |
1594 @perm: mount premission | |
1595 """ | |
1596 umount(src, mount_point, type) | |
1597 mount_string = "%s %s %s %s" % (src, mount_point, type, perm) | |
1598 | |
1599 if mount_string in file("/etc/mtab").read(): | |
1600 logging.debug("%s is already mounted in %s with %s" % | |
1601 (src, mount_point, perm)) | |
1602 return True | |
1603 | |
1604 mount_cmd = "mount -t %s %s %s -o %s" % (type, src, mount_point, perm) | |
1605 try: | |
1606 utils.system(mount_cmd) | |
1607 except error.CmdError: | |
1608 return False | |
1609 | |
1610 logging.debug("Verify the mount through /etc/mtab") | |
1611 if mount_string in file("/etc/mtab").read(): | |
1612 logging.debug("%s is successfully mounted" % src) | |
1613 return True | |
1614 else: | |
1615 logging.error("Can't find mounted NFS share - /etc/mtab contents \n%s" % | |
1616 file("/etc/mtab").read()) | |
1617 return False | |
OLD | NEW |