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 | 8 import fcntl, shelve, ConfigParser, rss_file_transfer, threading, sys, UserDict |
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 dump_env(obj, filename): | 30 def is_vm(obj): |
31 """ | 31 """ |
32 Dump KVM test environment to a file. | 32 Tests whether a given object is a VM object. |
33 | 33 |
34 @param filename: Path to a file where the environment will be dumped to. | 34 @param obj: Python object. |
35 """ | 35 """ |
36 file = open(filename, "w") | 36 return obj.__class__.__name__ == "VM" |
37 cPickle.dump(obj, file) | |
38 file.close() | |
39 | 37 |
40 | 38 |
41 def load_env(filename, version): | 39 class Env(UserDict.IterableUserDict): |
42 """ | 40 """ |
43 Load KVM test environment from an env file. | 41 A dict-like object containing global objects used by tests. |
44 If the version recorded in the file is lower than version, return an empty | 42 """ |
45 env. If some other error occurs during unpickling, return an empty env. | 43 def __init__(self, filename=None, version=0): |
| 44 """ |
| 45 Create an empty Env object or load an existing one from a file. |
46 | 46 |
47 @param filename: Path to an env file. | 47 If the version recorded in the file is lower than version, or if some |
48 """ | 48 error occurs during unpickling, or if filename is not supplied, |
49 default = {"version": version} | 49 create an empty Env object. |
50 try: | 50 |
51 file = open(filename, "r") | 51 @param filename: Path to an env file. |
52 env = cPickle.load(file) | 52 @param version: Required env version (int). |
53 file.close() | 53 """ |
54 if env.get("version", 0) < version: | 54 UserDict.IterableUserDict.__init__(self) |
55 logging.warn("Incompatible env file found. Not using it.") | 55 empty = {"version": version} |
56 return default | 56 if filename: |
57 return env | 57 self._filename = filename |
58 # Almost any exception can be raised during unpickling, so let's catch | 58 try: |
59 # them all | 59 f = open(filename, "r") |
60 except Exception, e: | 60 env = cPickle.load(f) |
61 logging.warn(e) | 61 f.close() |
62 return default | 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 |
63 | 74 |
64 | 75 |
65 def get_sub_dict(dict, name): | 76 def save(self, filename=None): |
66 """ | 77 """ |
67 Return a "sub-dict" corresponding to a specific object. | 78 Pickle the contents of the Env object into a file. |
68 | 79 |
69 Operate on a copy of dict: for each key that ends with the suffix | 80 @param filename: Filename to pickle the dict into. If not supplied, |
70 "_" + name, strip the suffix from the key, and set the value of | 81 use the filename from which the dict was loaded. |
71 the stripped key to that of the key. Return the resulting dict. | 82 """ |
72 | 83 filename = filename or self._filename |
73 @param name: Suffix of the key we want to set the value. | 84 f = open(filename, "w") |
74 """ | 85 cPickle.dump(self.data, f) |
75 suffix = "_" + name | 86 f.close() |
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 | |
82 | 87 |
83 | 88 |
84 def get_sub_dict_names(dict, keyword): | 89 def get_all_vms(self): |
| 90 """ |
| 91 Return a list of all VM objects in this Env object. |
| 92 """ |
| 93 return [o for o in self.values() if is_vm(o)] |
| 94 |
| 95 |
| 96 def get_vm(self, name): |
| 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): |
85 """ | 143 """ |
86 Return a list of "sub-dict" names that may be extracted with get_sub_dict. | 144 A dict-like object passed to every test. |
| 145 """ |
| 146 def objects(self, key): |
| 147 """ |
| 148 Return the names of objects defined using a given key. |
87 | 149 |
88 This function may be modified to change the behavior of all functions that | 150 @param key: The name of the key whose value lists the objects |
89 deal with multiple objects defined in dicts (e.g. VMs, images, NICs). | 151 (e.g. 'nics'). |
| 152 """ |
| 153 return self.get(key, "").split() |
90 | 154 |
91 @param keyword: A key in dict (e.g. "vms", "images", "nics"). | 155 |
92 """ | 156 def object_params(self, obj_name): |
93 names = dict.get(keyword) | 157 """ |
94 if names: | 158 Return a dict-like object containing the parameters of an individual |
95 return names.split() | 159 object. |
96 else: | 160 |
97 return [] | 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 |
98 | 176 |
99 | 177 |
100 # Functions related to MAC/IP addresses | 178 # Functions related to MAC/IP addresses |
101 | 179 |
102 def _open_mac_pool(lock_mode): | 180 def _open_mac_pool(lock_mode): |
103 lock_file = open("/tmp/mac_lock", "w+") | 181 lock_file = open("/tmp/mac_lock", "w+") |
104 fcntl.lockf(lock_file, lock_mode) | 182 fcntl.lockf(lock_file, lock_mode) |
105 pool = shelve.open("/tmp/address_pool") | 183 pool = shelve.open("/tmp/address_pool") |
106 return pool, lock_file | 184 return pool, lock_file |
107 | 185 |
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
233 if not dev: | 311 if not dev: |
234 return False | 312 return False |
235 dev = dev[0].split()[-1] | 313 dev = dev[0].split()[-1] |
236 | 314 |
237 # Send an ARP request | 315 # Send an ARP request |
238 o = commands.getoutput("%s -f -c 3 -I %s %s" % | 316 o = commands.getoutput("%s -f -c 3 -I %s %s" % |
239 (find_command("arping"), dev, ip)) | 317 (find_command("arping"), dev, ip)) |
240 return bool(regex.search(o)) | 318 return bool(regex.search(o)) |
241 | 319 |
242 | 320 |
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 | |
297 # Utility functions for dealing with external processes | 321 # Utility functions for dealing with external processes |
298 | 322 |
299 def find_command(cmd): | 323 def find_command(cmd): |
300 for dir in ["/usr/local/sbin", "/usr/local/bin", | 324 for dir in ["/usr/local/sbin", "/usr/local/bin", |
301 "/usr/sbin", "/usr/bin", "/sbin", "/bin"]: | 325 "/usr/sbin", "/usr/bin", "/sbin", "/bin"]: |
302 file = os.path.join(dir, cmd) | 326 file = os.path.join(dir, cmd) |
303 if os.path.exists(file): | 327 if os.path.exists(file): |
304 return file | 328 return file |
305 raise ValueError('Missing command: %s' % cmd) | 329 raise ValueError('Missing command: %s' % cmd) |
306 | 330 |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
396 utils.system("git checkout %s" % lbranch) | 420 utils.system("git checkout %s" % lbranch) |
397 if commit: | 421 if commit: |
398 utils.system("git checkout %s" % commit) | 422 utils.system("git checkout %s" % commit) |
399 | 423 |
400 h = utils.system_output('git log --pretty=format:"%H" -1') | 424 h = utils.system_output('git log --pretty=format:"%H" -1') |
401 try: | 425 try: |
402 desc = "tag %s" % utils.system_output("git describe") | 426 desc = "tag %s" % utils.system_output("git describe") |
403 except error.CmdError: | 427 except error.CmdError: |
404 desc = "no tag found" | 428 desc = "no tag found" |
405 | 429 |
406 logging.info("Commit hash for %s is %s (%s)" % (repository, h.strip(), | 430 logging.info("Commit hash for %s is %s (%s)", repository, h.strip(), desc) |
407 desc)) | |
408 return srcdir | 431 return srcdir |
409 | 432 |
410 | 433 |
411 def check_kvm_source_dir(source_dir): | 434 def check_kvm_source_dir(source_dir): |
412 """ | 435 """ |
413 Inspects the kvm source directory and verifies its disposition. In some | 436 Inspects the kvm source directory and verifies its disposition. In some |
414 occasions build may be dependant on the source directory disposition. | 437 occasions build may be dependant on the source directory disposition. |
415 The reason why the return codes are numbers is that we might have more | 438 The reason why the return codes are numbers is that we might have more |
416 changes on the source directory layout, so it's not scalable to just use | 439 changes on the source directory layout, so it's not scalable to just use |
417 strings like 'old_repo', 'new_repo' and such. | 440 strings like 'old_repo', 'new_repo' and such. |
418 | 441 |
419 @param source_dir: Source code path that will be inspected. | 442 @param source_dir: Source code path that will be inspected. |
420 """ | 443 """ |
421 os.chdir(source_dir) | 444 os.chdir(source_dir) |
422 has_qemu_dir = os.path.isdir('qemu') | 445 has_qemu_dir = os.path.isdir('qemu') |
423 has_kvm_dir = os.path.isdir('kvm') | 446 has_kvm_dir = os.path.isdir('kvm') |
424 if has_qemu_dir and not has_kvm_dir: | 447 if has_qemu_dir: |
425 logging.debug("qemu directory detected, source dir layout 1") | 448 logging.debug("qemu directory detected, source dir layout 1") |
426 return 1 | 449 return 1 |
427 if has_kvm_dir and not has_qemu_dir: | 450 if has_kvm_dir and not has_qemu_dir: |
428 logging.debug("kvm directory detected, source dir layout 2") | 451 logging.debug("kvm directory detected, source dir layout 2") |
429 return 2 | 452 return 2 |
430 else: | 453 else: |
431 raise error.TestError("Unknown source dir layout, cannot proceed.") | 454 raise error.TestError("Unknown source dir layout, cannot proceed.") |
432 | 455 |
433 | 456 |
434 # The following are functions used for SSH, SCP and Telnet communication with | 457 # Functions and classes used for logging into guests and transferring files |
435 # guests. | 458 |
| 459 class LoginError(Exception): |
| 460 def __init__(self, msg, output): |
| 461 Exception.__init__(self, msg, output) |
| 462 self.msg = msg |
| 463 self.output = output |
| 464 |
| 465 def __str__(self): |
| 466 return "%s (output: %r)" % (self.msg, self.output) |
| 467 |
| 468 |
| 469 class LoginAuthenticationError(LoginError): |
| 470 pass |
| 471 |
| 472 |
| 473 class LoginTimeoutError(LoginError): |
| 474 def __init__(self, output): |
| 475 LoginError.__init__(self, "Login timeout expired", output) |
| 476 |
| 477 |
| 478 class LoginProcessTerminatedError(LoginError): |
| 479 def __init__(self, status, output): |
| 480 LoginError.__init__(self, None, output) |
| 481 self.status = status |
| 482 |
| 483 def __str__(self): |
| 484 return ("Client process terminated (status: %s, output: %r)" % |
| 485 (self.status, self.output)) |
| 486 |
| 487 |
| 488 class LoginBadClientError(LoginError): |
| 489 def __init__(self, client): |
| 490 LoginError.__init__(self, None, None) |
| 491 self.client = client |
| 492 |
| 493 def __str__(self): |
| 494 return "Unknown remote shell client: %r" % self.client |
| 495 |
| 496 |
| 497 class SCPError(Exception): |
| 498 def __init__(self, msg, output): |
| 499 Exception.__init__(self, msg, output) |
| 500 self.msg = msg |
| 501 self.output = output |
| 502 |
| 503 def __str__(self): |
| 504 return "%s (output: %r)" % (self.msg, self.output) |
| 505 |
| 506 |
| 507 class SCPAuthenticationError(SCPError): |
| 508 pass |
| 509 |
| 510 |
| 511 class SCPAuthenticationTimeoutError(SCPAuthenticationError): |
| 512 def __init__(self, output): |
| 513 SCPAuthenticationError.__init__(self, "Authentication timeout expired", |
| 514 output) |
| 515 |
| 516 |
| 517 class SCPTransferTimeoutError(SCPError): |
| 518 def __init__(self, output): |
| 519 SCPError.__init__(self, "Transfer timeout expired", output) |
| 520 |
| 521 |
| 522 class SCPTransferFailedError(SCPError): |
| 523 def __init__(self, status, output): |
| 524 SCPError.__init__(self, None, output) |
| 525 self.status = status |
| 526 |
| 527 def __str__(self): |
| 528 return ("SCP transfer failed (status: %s, output: %r)" % |
| 529 (self.status, self.output)) |
| 530 |
436 | 531 |
437 def _remote_login(session, username, password, prompt, timeout=10): | 532 def _remote_login(session, username, password, prompt, timeout=10): |
438 """ | 533 """ |
439 Log into a remote host (guest) using SSH or Telnet. Wait for questions | 534 Log into a remote host (guest) using SSH or Telnet. Wait for questions |
440 and provide answers. If timeout expires while waiting for output from the | 535 and provide answers. If timeout expires while waiting for output from the |
441 child (e.g. a password prompt or a shell prompt) -- fail. | 536 child (e.g. a password prompt or a shell prompt) -- fail. |
442 | 537 |
443 @brief: Log into a remote host (guest) using SSH or Telnet. | 538 @brief: Log into a remote host (guest) using SSH or Telnet. |
444 | 539 |
445 @param session: A kvm_expect or kvm_shell_session instance to operate on | 540 @param session: An Expect or ShellSession instance to operate on |
446 @param username: The username to send in reply to a login prompt | 541 @param username: The username to send in reply to a login prompt |
447 @param password: The password to send in reply to a password prompt | 542 @param password: The password to send in reply to a password prompt |
448 @param prompt: The shell prompt that indicates a successful login | 543 @param prompt: The shell prompt that indicates a successful login |
449 @param timeout: The maximal time duration (in seconds) to wait for each | 544 @param timeout: The maximal time duration (in seconds) to wait for each |
450 step of the login procedure (i.e. the "Are you sure" prompt, the | 545 step of the login procedure (i.e. the "Are you sure" prompt, the |
451 password prompt, the shell prompt, etc) | 546 password prompt, the shell prompt, etc) |
452 | 547 @raise LoginTimeoutError: If timeout expires |
453 @return: True on success and False otherwise. | 548 @raise LoginAuthenticationError: If authentication fails |
| 549 @raise LoginProcessTerminatedError: If the client terminates during login |
| 550 @raise LoginError: If some other error occurs |
454 """ | 551 """ |
455 password_prompt_count = 0 | 552 password_prompt_count = 0 |
456 login_prompt_count = 0 | 553 login_prompt_count = 0 |
457 | 554 |
458 while True: | 555 while True: |
459 (match, text) = session.read_until_last_line_matches( | 556 try: |
| 557 match, text = session.read_until_last_line_matches( |
460 [r"[Aa]re you sure", r"[Pp]assword:\s*$", r"[Ll]ogin:\s*$", | 558 [r"[Aa]re you sure", r"[Pp]assword:\s*$", r"[Ll]ogin:\s*$", |
461 r"[Cc]onnection.*closed", r"[Cc]onnection.*refused", | 559 r"[Cc]onnection.*closed", r"[Cc]onnection.*refused", |
462 r"[Pp]lease wait", prompt], | 560 r"[Pp]lease wait", prompt], |
463 timeout=timeout, internal_timeout=0.5) | 561 timeout=timeout, internal_timeout=0.5) |
464 if match == 0: # "Are you sure you want to continue connecting" | 562 if match == 0: # "Are you sure you want to continue connecting" |
465 logging.debug("Got 'Are you sure...'; sending 'yes'") | 563 logging.debug("Got 'Are you sure...'; sending 'yes'") |
466 session.sendline("yes") | 564 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 | |
473 continue | 565 continue |
474 else: | 566 elif match == 1: # "password:" |
475 logging.debug("Got password prompt again") | 567 if password_prompt_count == 0: |
476 return False | 568 logging.debug("Got password prompt; sending '%s'", password) |
477 elif match == 2: # "login:" | 569 session.sendline(password) |
478 if login_prompt_count == 0: | 570 password_prompt_count += 1 |
479 logging.debug("Got username prompt; sending '%s'" % username) | 571 continue |
480 session.sendline(username) | 572 else: |
481 login_prompt_count += 1 | 573 raise LoginAuthenticationError("Got password prompt twice", |
| 574 text) |
| 575 elif match == 2: # "login:" |
| 576 if login_prompt_count == 0 and password_prompt_count == 0: |
| 577 logging.debug("Got username prompt; sending '%s'", username) |
| 578 session.sendline(username) |
| 579 login_prompt_count += 1 |
| 580 continue |
| 581 else: |
| 582 if login_prompt_count > 0: |
| 583 msg = "Got username prompt twice" |
| 584 else: |
| 585 msg = "Got username prompt after password prompt" |
| 586 raise LoginAuthenticationError(msg, text) |
| 587 elif match == 3: # "Connection closed" |
| 588 raise LoginError("Client said 'connection closed'", text) |
| 589 elif match == 4: # "Connection refused" |
| 590 raise LoginError("Client said 'connection refused'", text) |
| 591 elif match == 5: # "Please wait" |
| 592 logging.debug("Got 'Please wait'") |
| 593 timeout = 30 |
482 continue | 594 continue |
483 else: | 595 elif match == 6: # prompt |
484 logging.debug("Got username prompt again") | 596 logging.debug("Got shell prompt -- logged in") |
485 return False | 597 break |
486 elif match == 3: # "Connection closed" | 598 except kvm_subprocess.ExpectTimeoutError, e: |
487 logging.debug("Got 'Connection closed'") | 599 raise LoginTimeoutError(e.output) |
488 return False | 600 except kvm_subprocess.ExpectProcessTerminatedError, e: |
489 elif match == 4: # "Connection refused" | 601 raise LoginProcessTerminatedError(e.status, 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") | |
501 return False | |
502 | |
503 | |
504 def _remote_scp(session, password, transfer_timeout=600, login_timeout=10): | |
505 """ | |
506 Transfer file(s) to a remote host (guest) using SCP. Wait for questions | |
507 and provide answers. If login_timeout expires while waiting for output | |
508 from the child (e.g. a password prompt), fail. If transfer_timeout expires | |
509 while waiting for the transfer to complete, fail. | |
510 | |
511 @brief: Transfer files using SCP, given a command line. | |
512 | |
513 @param session: A kvm_expect or kvm_shell_session instance to operate on | |
514 @param password: The password to send in reply to a password prompt. | |
515 @param transfer_timeout: The time duration (in seconds) to wait for the | |
516 transfer to complete. | |
517 @param login_timeout: The maximal time duration (in seconds) to wait for | |
518 each step of the login procedure (i.e. the "Are you sure" prompt or | |
519 the password prompt) | |
520 | |
521 @return: True if the transfer succeeds and False on failure. | |
522 """ | |
523 password_prompt_count = 0 | |
524 timeout = login_timeout | |
525 | |
526 while True: | |
527 (match, text) = session.read_until_last_line_matches( | |
528 [r"[Aa]re you sure", r"[Pp]assword:\s*$", r"lost connection"], | |
529 timeout=timeout, internal_timeout=0.5) | |
530 if match == 0: # "Are you sure you want to continue connecting" | |
531 logging.debug("Got 'Are you sure...'; sending '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 | |
540 continue | |
541 else: | |
542 logging.debug("Got password prompt again") | |
543 return False | |
544 elif match == 2: # "lost connection" | |
545 logging.debug("Got 'lost connection'") | |
546 return False | |
547 else: # match == None | |
548 if session.is_alive(): | |
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 | |
555 | 602 |
556 | 603 |
557 def remote_login(client, host, port, username, password, prompt, linesep="\n", | 604 def remote_login(client, host, port, username, password, prompt, linesep="\n", |
558 log_filename=None, timeout=10): | 605 log_filename=None, timeout=10): |
559 """ | 606 """ |
560 Log into a remote host (guest) using SSH/Telnet/Netcat. | 607 Log into a remote host (guest) using SSH/Telnet/Netcat. |
561 | 608 |
562 @param client: The client to use ('ssh', 'telnet' or 'nc') | 609 @param client: The client to use ('ssh', 'telnet' or 'nc') |
563 @param host: Hostname or IP address | 610 @param host: Hostname or IP address |
564 @param port: Port to connect to | 611 @param port: Port to connect to |
565 @param username: Username (if required) | 612 @param username: Username (if required) |
566 @param password: Password (if required) | 613 @param password: Password (if required) |
567 @param prompt: Shell prompt (regular expression) | 614 @param prompt: Shell prompt (regular expression) |
568 @param linesep: The line separator to use when sending lines | 615 @param linesep: The line separator to use when sending lines |
569 (e.g. '\\n' or '\\r\\n') | 616 (e.g. '\\n' or '\\r\\n') |
570 @param log_filename: If specified, log all output to this file | 617 @param log_filename: If specified, log all output to this file |
571 @param timeout: The maximal time duration (in seconds) to wait for | 618 @param timeout: The maximal time duration (in seconds) to wait for |
572 each step of the login procedure (i.e. the "Are you sure" prompt | 619 each step of the login procedure (i.e. the "Are you sure" prompt |
573 or the password prompt) | 620 or the password prompt) |
574 | 621 @raise LoginBadClientError: If an unknown client is requested |
575 @return: kvm_shell_session object on success and None on failure. | 622 @raise: Whatever _remote_login() raises |
| 623 @return: A ShellSession object. |
576 """ | 624 """ |
577 if client == "ssh": | 625 if client == "ssh": |
578 cmd = ("ssh -o UserKnownHostsFile=/dev/null " | 626 cmd = ("ssh -o UserKnownHostsFile=/dev/null " |
579 "-o PreferredAuthentications=password -p %s %s@%s" % | 627 "-o PreferredAuthentications=password -p %s %s@%s" % |
580 (port, username, host)) | 628 (port, username, host)) |
581 elif client == "telnet": | 629 elif client == "telnet": |
582 cmd = "telnet -l %s %s %s" % (username, host, port) | 630 cmd = "telnet -l %s %s %s" % (username, host, port) |
583 elif client == "nc": | 631 elif client == "nc": |
584 cmd = "nc %s %s" % (host, port) | 632 cmd = "nc %s %s" % (host, port) |
585 else: | 633 else: |
586 logging.error("Unknown remote shell client: %s" % client) | 634 raise LoginBadClientError(client) |
587 return | |
588 | 635 |
589 logging.debug("Trying to login with command '%s'" % cmd) | 636 logging.debug("Trying to login with command '%s'", cmd) |
590 session = kvm_subprocess.kvm_shell_session(cmd, linesep=linesep, | 637 session = kvm_subprocess.ShellSession(cmd, linesep=linesep, prompt=prompt) |
591 prompt=prompt) | 638 try: |
592 if _remote_login(session, username, password, prompt, timeout): | 639 _remote_login(session, username, password, prompt, timeout) |
593 if log_filename: | 640 except: |
594 session.set_output_func(log_line) | |
595 session.set_output_params((log_filename,)) | |
596 return session | |
597 else: | |
598 session.close() | 641 session.close() |
| 642 raise |
| 643 if log_filename: |
| 644 session.set_output_func(log_line) |
| 645 session.set_output_params((log_filename,)) |
| 646 return session |
| 647 |
| 648 |
| 649 def wait_for_login(client, host, port, username, password, prompt, linesep="\n", |
| 650 log_filename=None, timeout=240, internal_timeout=10): |
| 651 """ |
| 652 Make multiple attempts to log into a remote host (guest) until one succeeds |
| 653 or timeout expires. |
| 654 |
| 655 @param timeout: Total time duration to wait for a successful login |
| 656 @param internal_timeout: The maximal time duration (in seconds) to wait for |
| 657 each step of the login procedure (e.g. the "Are you sure" prompt |
| 658 or the password prompt) |
| 659 @see: remote_login() |
| 660 @raise: Whatever remote_login() raises |
| 661 @return: A ShellSession object. |
| 662 """ |
| 663 logging.debug("Attempting to log into %s:%s using %s (timeout %ds)", |
| 664 host, port, client, timeout) |
| 665 end_time = time.time() + timeout |
| 666 while time.time() < end_time: |
| 667 try: |
| 668 return remote_login(client, host, port, username, password, prompt, |
| 669 linesep, log_filename, internal_timeout) |
| 670 except LoginError, e: |
| 671 logging.debug(e) |
| 672 time.sleep(2) |
| 673 # Timeout expired; try one more time but don't catch exceptions |
| 674 return remote_login(client, host, port, username, password, prompt, |
| 675 linesep, log_filename, internal_timeout) |
| 676 |
| 677 |
| 678 def _remote_scp(session, password, transfer_timeout=600, login_timeout=10): |
| 679 """ |
| 680 Transfer file(s) to a remote host (guest) using SCP. Wait for questions |
| 681 and provide answers. If login_timeout expires while waiting for output |
| 682 from the child (e.g. a password prompt), fail. If transfer_timeout expires |
| 683 while waiting for the transfer to complete, fail. |
| 684 |
| 685 @brief: Transfer files using SCP, given a command line. |
| 686 |
| 687 @param session: An Expect or ShellSession instance to operate on |
| 688 @param password: The password to send in reply to a password prompt. |
| 689 @param transfer_timeout: The time duration (in seconds) to wait for the |
| 690 transfer to complete. |
| 691 @param login_timeout: The maximal time duration (in seconds) to wait for |
| 692 each step of the login procedure (i.e. the "Are you sure" prompt or |
| 693 the password prompt) |
| 694 @raise SCPAuthenticationError: If authentication fails |
| 695 @raise SCPTransferTimeoutError: If the transfer fails to complete in time |
| 696 @raise SCPTransferFailedError: If the process terminates with a nonzero |
| 697 exit code |
| 698 @raise SCPError: If some other error occurs |
| 699 """ |
| 700 password_prompt_count = 0 |
| 701 timeout = login_timeout |
| 702 authentication_done = False |
| 703 |
| 704 while True: |
| 705 try: |
| 706 match, text = session.read_until_last_line_matches( |
| 707 [r"[Aa]re you sure", r"[Pp]assword:\s*$", r"lost connection"], |
| 708 timeout=timeout, internal_timeout=0.5) |
| 709 if match == 0: # "Are you sure you want to continue connecting" |
| 710 logging.debug("Got 'Are you sure...'; sending 'yes'") |
| 711 session.sendline("yes") |
| 712 continue |
| 713 elif match == 1: # "password:" |
| 714 if password_prompt_count == 0: |
| 715 logging.debug("Got password prompt; sending '%s'", password) |
| 716 session.sendline(password) |
| 717 password_prompt_count += 1 |
| 718 timeout = transfer_timeout |
| 719 authentication_done = True |
| 720 continue |
| 721 else: |
| 722 raise SCPAuthenticationError("Got password prompt twice", |
| 723 text) |
| 724 elif match == 2: # "lost connection" |
| 725 raise SCPError("SCP client said 'lost connection'", text) |
| 726 except kvm_subprocess.ExpectTimeoutError, e: |
| 727 if authentication_done: |
| 728 raise SCPTransferTimeoutError(e.output) |
| 729 else: |
| 730 raise SCPAuthenticationTimeoutError(e.output) |
| 731 except kvm_subprocess.ExpectProcessTerminatedError, e: |
| 732 if e.status == 0: |
| 733 logging.debug("SCP process terminated with status 0") |
| 734 break |
| 735 else: |
| 736 raise SCPTransferFailedError(e.status, e.output) |
599 | 737 |
600 | 738 |
601 def remote_scp(command, password, log_filename=None, transfer_timeout=600, | 739 def remote_scp(command, password, log_filename=None, transfer_timeout=600, |
602 login_timeout=10): | 740 login_timeout=10): |
603 """ | 741 """ |
604 Transfer file(s) to a remote host (guest) using SCP. | 742 Transfer file(s) to a remote host (guest) using SCP. |
605 | 743 |
606 @brief: Transfer files using SCP, given a command line. | 744 @brief: Transfer files using SCP, given a command line. |
607 | 745 |
608 @param command: The command to execute | 746 @param command: The command to execute |
609 (e.g. "scp -r foobar root@localhost:/tmp/"). | 747 (e.g. "scp -r foobar root@localhost:/tmp/"). |
610 @param password: The password to send in reply to a password prompt. | 748 @param password: The password to send in reply to a password prompt. |
611 @param log_filename: If specified, log all output to this file | 749 @param log_filename: If specified, log all output to this file |
612 @param transfer_timeout: The time duration (in seconds) to wait for the | 750 @param transfer_timeout: The time duration (in seconds) to wait for the |
613 transfer to complete. | 751 transfer to complete. |
614 @param login_timeout: The maximal time duration (in seconds) to wait for | 752 @param login_timeout: The maximal time duration (in seconds) to wait for |
615 each step of the login procedure (i.e. the "Are you sure" prompt | 753 each step of the login procedure (i.e. the "Are you sure" prompt |
616 or the password prompt) | 754 or the password prompt) |
617 | 755 @raise: Whatever _remote_scp() raises |
618 @return: True if the transfer succeeds and False on failure. | |
619 """ | 756 """ |
620 logging.debug("Trying to SCP with command '%s', timeout %ss", | 757 logging.debug("Trying to SCP with command '%s', timeout %ss", |
621 command, transfer_timeout) | 758 command, transfer_timeout) |
622 | |
623 if log_filename: | 759 if log_filename: |
624 output_func = log_line | 760 output_func = log_line |
625 output_params = (log_filename,) | 761 output_params = (log_filename,) |
626 else: | 762 else: |
627 output_func = None | 763 output_func = None |
628 output_params = () | 764 output_params = () |
629 | 765 session = kvm_subprocess.Expect(command, |
630 session = kvm_subprocess.kvm_expect(command, | 766 output_func=output_func, |
631 output_func=output_func, | 767 output_params=output_params) |
632 output_params=output_params) | |
633 try: | 768 try: |
634 return _remote_scp(session, password, transfer_timeout, login_timeout) | 769 _remote_scp(session, password, transfer_timeout, login_timeout) |
635 finally: | 770 finally: |
636 session.close() | 771 session.close() |
637 | 772 |
638 | 773 |
639 def scp_to_remote(host, port, username, password, local_path, remote_path, | 774 def scp_to_remote(host, port, username, password, local_path, remote_path, |
640 log_filename=None, timeout=600): | 775 log_filename=None, timeout=600): |
641 """ | 776 """ |
642 Copy files to a remote host (guest). | 777 Copy files to a remote host (guest) through scp. |
643 | 778 |
644 @param host: Hostname or IP address | 779 @param host: Hostname or IP address |
645 @param username: Username (if required) | 780 @param username: Username (if required) |
646 @param password: Password (if required) | 781 @param password: Password (if required) |
647 @param local_path: Path on the local machine where we are copying from | 782 @param local_path: Path on the local machine where we are copying from |
648 @param remote_path: Path on the remote machine where we are copying to | 783 @param remote_path: Path on the remote machine where we are copying to |
649 @param log_filename: If specified, log all output to this file | 784 @param log_filename: If specified, log all output to this file |
650 @param timeout: The time duration (in seconds) to wait for the transfer | 785 @param timeout: The time duration (in seconds) to wait for the transfer |
651 to complete. | 786 to complete. |
652 | 787 @raise: Whatever remote_scp() raises |
653 @return: True on success and False on failure. | |
654 """ | 788 """ |
655 command = ("scp -v -o UserKnownHostsFile=/dev/null " | 789 command = ("scp -v -o UserKnownHostsFile=/dev/null " |
656 "-o PreferredAuthentications=password -r -P %s %s %s@%s:%s" % | 790 "-o PreferredAuthentications=password -r -P %s %s %s@%s:%s" % |
657 (port, local_path, username, host, remote_path)) | 791 (port, local_path, username, host, remote_path)) |
658 return remote_scp(command, password, log_filename, timeout) | 792 remote_scp(command, password, log_filename, timeout) |
659 | 793 |
660 | 794 |
661 def scp_from_remote(host, port, username, password, remote_path, local_path, | 795 def scp_from_remote(host, port, username, password, remote_path, local_path, |
662 log_filename=None, timeout=600): | 796 log_filename=None, timeout=600): |
663 """ | 797 """ |
664 Copy files from a remote host (guest). | 798 Copy files from a remote host (guest). |
665 | 799 |
666 @param host: Hostname or IP address | 800 @param host: Hostname or IP address |
667 @param username: Username (if required) | 801 @param username: Username (if required) |
668 @param password: Password (if required) | 802 @param password: Password (if required) |
669 @param local_path: Path on the local machine where we are copying from | 803 @param local_path: Path on the local machine where we are copying from |
670 @param remote_path: Path on the remote machine where we are copying to | 804 @param remote_path: Path on the remote machine where we are copying to |
671 @param log_filename: If specified, log all output to this file | 805 @param log_filename: If specified, log all output to this file |
672 @param timeout: The time duration (in seconds) to wait for the transfer | 806 @param timeout: The time duration (in seconds) to wait for the transfer |
673 to complete. | 807 to complete. |
674 | 808 @raise: Whatever remote_scp() raises |
675 @return: True on success and False on failure. | |
676 """ | 809 """ |
677 command = ("scp -v -o UserKnownHostsFile=/dev/null " | 810 command = ("scp -v -o UserKnownHostsFile=/dev/null " |
678 "-o PreferredAuthentications=password -r -P %s %s@%s:%s %s" % | 811 "-o PreferredAuthentications=password -r -P %s %s@%s:%s %s" % |
679 (port, username, host, remote_path, local_path)) | 812 (port, username, host, remote_path, local_path)) |
680 return remote_scp(command, password, log_filename, timeout) | 813 remote_scp(command, password, log_filename, timeout) |
| 814 |
| 815 |
| 816 def copy_files_to(address, client, username, password, port, local_path, |
| 817 remote_path, log_filename=None, verbose=False, timeout=600): |
| 818 """ |
| 819 Copy files to a remote host (guest) using the selected client. |
| 820 |
| 821 @param client: Type of transfer client |
| 822 @param username: Username (if required) |
| 823 @param password: Password (if requried) |
| 824 @param local_path: Path on the local machine where we are copying from |
| 825 @param remote_path: Path on the remote machine where we are copying to |
| 826 @param address: Address of remote host(guest) |
| 827 @param log_filename: If specified, log all output to this file (SCP only) |
| 828 @param verbose: If True, log some stats using logging.debug (RSS only) |
| 829 @param timeout: The time duration (in seconds) to wait for the transfer to |
| 830 complete. |
| 831 @raise: Whatever remote_scp() raises |
| 832 """ |
| 833 if client == "scp": |
| 834 scp_to_remote(address, port, username, password, local_path, |
| 835 remote_path, log_filename, timeout) |
| 836 elif client == "rss": |
| 837 log_func = None |
| 838 if verbose: |
| 839 log_func = logging.debug |
| 840 c = rss_file_transfer.FileUploadClient(address, port, log_func) |
| 841 c.upload(local_path, remote_path, timeout) |
| 842 c.close() |
| 843 |
| 844 |
| 845 def copy_files_from(address, client, username, password, port, remote_path, |
| 846 local_path, log_filename=None, verbose=False, timeout=600): |
| 847 """ |
| 848 Copy files from a remote host (guest) using the selected client. |
| 849 |
| 850 @param client: Type of transfer client |
| 851 @param username: Username (if required) |
| 852 @param password: Password (if requried) |
| 853 @param remote_path: Path on the remote machine where we are copying from |
| 854 @param local_path: Path on the local machine where we are copying to |
| 855 @param address: Address of remote host(guest) |
| 856 @param log_filename: If specified, log all output to this file (SCP only) |
| 857 @param verbose: If True, log some stats using logging.debug (RSS only) |
| 858 @param timeout: The time duration (in seconds) to wait for the transfer to |
| 859 complete. |
| 860 @raise: Whatever remote_scp() raises |
| 861 """ |
| 862 if client == "scp": |
| 863 scp_from_remote(address, port, username, password, remote_path, |
| 864 local_path, log_filename, timeout) |
| 865 elif client == "rss": |
| 866 log_func = None |
| 867 if verbose: |
| 868 log_func = logging.debug |
| 869 c = rss_file_transfer.FileDownloadClient(address, port, log_func) |
| 870 c.download(remote_path, local_path, timeout) |
| 871 c.close() |
681 | 872 |
682 | 873 |
683 # The following are utility functions related to ports. | 874 # The following are utility functions related to ports. |
684 | 875 |
685 def is_port_free(port, address): | 876 def is_port_free(port, address): |
686 """ | 877 """ |
687 Return True if the given port is available for use. | 878 Return True if the given port is available for use. |
688 | 879 |
689 @param port: Port number | 880 @param port: Port number |
690 """ | 881 """ |
(...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
859 @param steps: Time to sleep between attempts in seconds | 1050 @param steps: Time to sleep between attempts in seconds |
860 @param text: Text to print while waiting, for debug purposes | 1051 @param text: Text to print while waiting, for debug purposes |
861 """ | 1052 """ |
862 start_time = time.time() | 1053 start_time = time.time() |
863 end_time = time.time() + timeout | 1054 end_time = time.time() + timeout |
864 | 1055 |
865 time.sleep(first) | 1056 time.sleep(first) |
866 | 1057 |
867 while time.time() < end_time: | 1058 while time.time() < end_time: |
868 if text: | 1059 if text: |
869 logging.debug("%s (%f secs)" % (text, time.time() - start_time)) | 1060 logging.debug("%s (%f secs)", text, (time.time() - start_time)) |
870 | 1061 |
871 output = func() | 1062 output = func() |
872 if output: | 1063 if output: |
873 return output | 1064 return output |
874 | 1065 |
875 time.sleep(step) | 1066 time.sleep(step) |
876 | 1067 |
877 logging.debug("Timeout elapsed") | 1068 logging.debug("Timeout elapsed") |
878 return None | 1069 return None |
879 | 1070 |
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
971 def get_vendor_from_pci_id(pci_id): | 1162 def get_vendor_from_pci_id(pci_id): |
972 """ | 1163 """ |
973 Check out the device vendor ID according to pci_id. | 1164 Check out the device vendor ID according to pci_id. |
974 | 1165 |
975 @param pci_id: PCI ID of a device. | 1166 @param pci_id: PCI ID of a device. |
976 """ | 1167 """ |
977 cmd = "lspci -n | awk '/%s/ {print $3}'" % pci_id | 1168 cmd = "lspci -n | awk '/%s/ {print $3}'" % pci_id |
978 return re.sub(":", " ", commands.getoutput(cmd)) | 1169 return re.sub(":", " ", commands.getoutput(cmd)) |
979 | 1170 |
980 | 1171 |
| 1172 class Thread(threading.Thread): |
| 1173 """ |
| 1174 Run a function in a background thread. |
| 1175 """ |
| 1176 def __init__(self, target, args=(), kwargs={}): |
| 1177 """ |
| 1178 Initialize the instance. |
| 1179 |
| 1180 @param target: Function to run in the thread. |
| 1181 @param args: Arguments to pass to target. |
| 1182 @param kwargs: Keyword arguments to pass to target. |
| 1183 """ |
| 1184 threading.Thread.__init__(self) |
| 1185 self._target = target |
| 1186 self._args = args |
| 1187 self._kwargs = kwargs |
| 1188 |
| 1189 |
| 1190 def run(self): |
| 1191 """ |
| 1192 Run target (passed to the constructor). No point in calling this |
| 1193 function directly. Call start() to make this function run in a new |
| 1194 thread. |
| 1195 """ |
| 1196 self._e = None |
| 1197 self._retval = None |
| 1198 try: |
| 1199 try: |
| 1200 self._retval = self._target(*self._args, **self._kwargs) |
| 1201 except: |
| 1202 self._e = sys.exc_info() |
| 1203 raise |
| 1204 finally: |
| 1205 # Avoid circular references (start() may be called only once so |
| 1206 # it's OK to delete these) |
| 1207 del self._target, self._args, self._kwargs |
| 1208 |
| 1209 |
| 1210 def join(self, timeout=None, suppress_exception=False): |
| 1211 """ |
| 1212 Join the thread. If target raised an exception, re-raise it. |
| 1213 Otherwise, return the value returned by target. |
| 1214 |
| 1215 @param timeout: Timeout value to pass to threading.Thread.join(). |
| 1216 @param suppress_exception: If True, don't re-raise the exception. |
| 1217 """ |
| 1218 threading.Thread.join(self, timeout) |
| 1219 try: |
| 1220 if self._e: |
| 1221 if not suppress_exception: |
| 1222 # Because the exception was raised in another thread, we |
| 1223 # need to explicitly insert the current context into it |
| 1224 s = error.exception_context(self._e[1]) |
| 1225 s = error.join_contexts(error.get_context(), s) |
| 1226 error.set_exception_context(self._e[1], s) |
| 1227 raise self._e[0], self._e[1], self._e[2] |
| 1228 else: |
| 1229 return self._retval |
| 1230 finally: |
| 1231 # Avoid circular references (join() may be called multiple times |
| 1232 # so we can't delete these) |
| 1233 self._e = None |
| 1234 self._retval = None |
| 1235 |
| 1236 |
| 1237 def parallel(targets): |
| 1238 """ |
| 1239 Run multiple functions in parallel. |
| 1240 |
| 1241 @param targets: A sequence of tuples or functions. If it's a sequence of |
| 1242 tuples, each tuple will be interpreted as (target, args, kwargs) or |
| 1243 (target, args) or (target,) depending on its length. If it's a |
| 1244 sequence of functions, the functions will be called without |
| 1245 arguments. |
| 1246 @return: A list of the values returned by the functions called. |
| 1247 """ |
| 1248 threads = [] |
| 1249 for target in targets: |
| 1250 if isinstance(target, tuple) or isinstance(target, list): |
| 1251 t = Thread(*target) |
| 1252 else: |
| 1253 t = Thread(target) |
| 1254 threads.append(t) |
| 1255 t.start() |
| 1256 return [t.join() for t in threads] |
| 1257 |
| 1258 |
981 class KvmLoggingConfig(logging_config.LoggingConfig): | 1259 class KvmLoggingConfig(logging_config.LoggingConfig): |
982 """ | 1260 """ |
983 Used with the sole purpose of providing convenient logging setup | 1261 Used with the sole purpose of providing convenient logging setup |
984 for the KVM test auxiliary programs. | 1262 for the KVM test auxiliary programs. |
985 """ | 1263 """ |
986 def configure_logging(self, results_dir=None, verbose=False): | 1264 def configure_logging(self, results_dir=None, verbose=False): |
987 super(KvmLoggingConfig, self).configure_logging(use_console=True, | 1265 super(KvmLoggingConfig, self).configure_logging(use_console=True, |
988 verbose=verbose) | 1266 verbose=verbose) |
989 | 1267 |
990 | 1268 |
(...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1169 re_probe = True | 1447 re_probe = True |
1170 elif not self.check_vfs_count(): | 1448 elif not self.check_vfs_count(): |
1171 os.system("modprobe -r %s" % self.driver) | 1449 os.system("modprobe -r %s" % self.driver) |
1172 re_probe = True | 1450 re_probe = True |
1173 else: | 1451 else: |
1174 return True | 1452 return True |
1175 | 1453 |
1176 # Re-probe driver with proper number of VFs | 1454 # Re-probe driver with proper number of VFs |
1177 if re_probe: | 1455 if re_probe: |
1178 cmd = "modprobe %s %s" % (self.driver, self.driver_option) | 1456 cmd = "modprobe %s %s" % (self.driver, self.driver_option) |
1179 logging.info("Loading the driver '%s' with option '%s'" % | 1457 logging.info("Loading the driver '%s' with option '%s'", |
1180 (self.driver, self.driver_option)) | 1458 self.driver, self.driver_option) |
1181 s, o = commands.getstatusoutput(cmd) | 1459 s, o = commands.getstatusoutput(cmd) |
1182 if s: | 1460 if s: |
1183 return False | 1461 return False |
1184 return True | 1462 return True |
1185 | 1463 |
1186 | 1464 |
1187 def request_devs(self): | 1465 def request_devs(self): |
1188 """ | 1466 """ |
1189 Implement setup process: unbind the PCI device and then bind it | 1467 Implement setup process: unbind the PCI device and then bind it |
1190 to the pci-stub driver. | 1468 to the pci-stub driver. |
1191 | 1469 |
1192 @return: a list of successfully requested devices' PCI IDs. | 1470 @return: a list of successfully requested devices' PCI IDs. |
1193 """ | 1471 """ |
1194 base_dir = "/sys/bus/pci" | 1472 base_dir = "/sys/bus/pci" |
1195 stub_path = os.path.join(base_dir, "drivers/pci-stub") | 1473 stub_path = os.path.join(base_dir, "drivers/pci-stub") |
1196 | 1474 |
1197 self.pci_ids = self.get_devs(self.devices_requested) | 1475 self.pci_ids = self.get_devs(self.devices_requested) |
1198 logging.debug("The following pci_ids were found: %s", self.pci_ids) | 1476 logging.debug("The following pci_ids were found: %s", self.pci_ids) |
1199 requested_pci_ids = [] | 1477 requested_pci_ids = [] |
1200 self.dev_drivers = {} | 1478 self.dev_drivers = {} |
1201 | 1479 |
1202 # Setup all devices specified for assignment to guest | 1480 # Setup all devices specified for assignment to guest |
1203 for pci_id in self.pci_ids: | 1481 for pci_id in self.pci_ids: |
1204 full_id = get_full_pci_id(pci_id) | 1482 full_id = get_full_pci_id(pci_id) |
1205 if not full_id: | 1483 if not full_id: |
1206 continue | 1484 continue |
1207 drv_path = os.path.join(base_dir, "devices/%s/driver" % full_id) | 1485 drv_path = os.path.join(base_dir, "devices/%s/driver" % full_id) |
1208 dev_prev_driver= os.path.realpath(os.path.join(drv_path, | 1486 dev_prev_driver = os.path.realpath(os.path.join(drv_path, |
1209 os.readlink(drv_path))) | 1487 os.readlink(drv_path))) |
1210 self.dev_drivers[pci_id] = dev_prev_driver | 1488 self.dev_drivers[pci_id] = dev_prev_driver |
1211 | 1489 |
1212 # Judge whether the device driver has been binded to stub | 1490 # Judge whether the device driver has been binded to stub |
1213 if not self.is_binded_to_stub(full_id): | 1491 if not self.is_binded_to_stub(full_id): |
1214 logging.debug("Binding device %s to stub", full_id) | 1492 logging.debug("Binding device %s to stub", full_id) |
1215 vendor_id = get_vendor_from_pci_id(pci_id) | 1493 vendor_id = get_vendor_from_pci_id(pci_id) |
1216 stub_new_id = os.path.join(stub_path, 'new_id') | 1494 stub_new_id = os.path.join(stub_path, 'new_id') |
1217 unbind_dev = os.path.join(drv_path, 'unbind') | 1495 unbind_dev = os.path.join(drv_path, 'unbind') |
1218 stub_bind = os.path.join(stub_path, 'bind') | 1496 stub_bind = os.path.join(stub_path, 'bind') |
1219 | 1497 |
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1340 if tag and build: | 1618 if tag and build: |
1341 logging.info("Both tag and build parameters provided, ignoring tag " | 1619 logging.info("Both tag and build parameters provided, ignoring tag " |
1342 "parameter...") | 1620 "parameter...") |
1343 | 1621 |
1344 if not tag and not build: | 1622 if not tag and not build: |
1345 raise ValueError("Koji install selected but neither koji_tag " | 1623 raise ValueError("Koji install selected but neither koji_tag " |
1346 "nor koji_build parameters provided. Please " | 1624 "nor koji_build parameters provided. Please " |
1347 "provide an appropriate tag or build name.") | 1625 "provide an appropriate tag or build name.") |
1348 | 1626 |
1349 if not build: | 1627 if not build: |
1350 builds = self.session.listTagged(tag, latest=True, | 1628 builds = self.session.listTagged(tag, latest=True, inherit=True, |
1351 package=src_package) | 1629 package=src_package) |
1352 if not builds: | 1630 if not builds: |
1353 raise ValueError("Tag %s has no builds of %s" % (tag, | 1631 raise ValueError("Tag %s has no builds of %s" % (tag, |
1354 src_package)) | 1632 src_package)) |
1355 info = builds[0] | 1633 info = builds[0] |
1356 else: | 1634 else: |
1357 info = self.session.getBuild(build) | 1635 info = self.session.getBuild(build) |
1358 | 1636 |
1359 if info is None: | 1637 if info is None: |
1360 raise ValueError('No such brew/koji build: %s' % build) | 1638 raise ValueError('No such brew/koji build: %s' % build) |
(...skipping 22 matching lines...) Expand all Loading... |
1383 download = False | 1661 download = False |
1384 else: | 1662 else: |
1385 download = True | 1663 download = True |
1386 | 1664 |
1387 if download: | 1665 if download: |
1388 r = utils.get_file(url, | 1666 r = utils.get_file(url, |
1389 os.path.join(dst_dir, os.path.basename(url))) | 1667 os.path.join(dst_dir, os.path.basename(url))) |
1390 rpm_paths.append(r) | 1668 rpm_paths.append(r) |
1391 | 1669 |
1392 return rpm_paths | 1670 return rpm_paths |
| 1671 |
| 1672 |
| 1673 def umount(src, mount_point, type): |
| 1674 """ |
| 1675 Umount the src mounted in mount_point. |
| 1676 |
| 1677 @src: mount source |
| 1678 @mount_point: mount point |
| 1679 @type: file system type |
| 1680 """ |
| 1681 |
| 1682 mount_string = "%s %s %s" % (src, mount_point, type) |
| 1683 if mount_string in file("/etc/mtab").read(): |
| 1684 umount_cmd = "umount %s" % mount_point |
| 1685 try: |
| 1686 utils.system(umount_cmd) |
| 1687 return True |
| 1688 except error.CmdError: |
| 1689 return False |
| 1690 else: |
| 1691 logging.debug("%s is not mounted under %s", src, mount_point) |
| 1692 return True |
| 1693 |
| 1694 |
| 1695 def mount(src, mount_point, type, perm="rw"): |
| 1696 """ |
| 1697 Mount the src into mount_point of the host. |
| 1698 |
| 1699 @src: mount source |
| 1700 @mount_point: mount point |
| 1701 @type: file system type |
| 1702 @perm: mount premission |
| 1703 """ |
| 1704 umount(src, mount_point, type) |
| 1705 mount_string = "%s %s %s %s" % (src, mount_point, type, perm) |
| 1706 |
| 1707 if mount_string in file("/etc/mtab").read(): |
| 1708 logging.debug("%s is already mounted in %s with %s", |
| 1709 src, mount_point, perm) |
| 1710 return True |
| 1711 |
| 1712 mount_cmd = "mount -t %s %s %s -o %s" % (type, src, mount_point, perm) |
| 1713 try: |
| 1714 utils.system(mount_cmd) |
| 1715 except error.CmdError: |
| 1716 return False |
| 1717 |
| 1718 logging.debug("Verify the mount through /etc/mtab") |
| 1719 if mount_string in file("/etc/mtab").read(): |
| 1720 logging.debug("%s is successfully mounted", src) |
| 1721 return True |
| 1722 else: |
| 1723 logging.error("Can't find mounted NFS share - /etc/mtab contents \n%s", |
| 1724 file("/etc/mtab").read()) |
| 1725 return False |
OLD | NEW |