KVM test: introduce exception classes for _remote_login() and _remote_scp()
[autotest-zwu.git] / client / tests / kvm / kvm_utils.py
bloba802d49651be037e1d8fbbe6f2db0090044a62e9
1 """
2 KVM test utility functions.
4 @copyright: 2008-2009 Red Hat Inc.
5 """
7 import time, string, random, socket, os, signal, re, logging, commands, cPickle
8 import fcntl, shelve, ConfigParser, rss_file_transfer, threading, sys, UserDict
9 from autotest_lib.client.bin import utils, os_dep
10 from autotest_lib.client.common_lib import error, logging_config
11 import kvm_subprocess
12 try:
13 import koji
14 KOJI_INSTALLED = True
15 except ImportError:
16 KOJI_INSTALLED = False
19 def _lock_file(filename):
20 f = open(filename, "w")
21 fcntl.lockf(f, fcntl.LOCK_EX)
22 return f
25 def _unlock_file(f):
26 fcntl.lockf(f, fcntl.LOCK_UN)
27 f.close()
30 def is_vm(obj):
31 """
32 Tests whether a given object is a VM object.
34 @param obj: Python object.
35 """
36 return obj.__class__.__name__ == "VM"
39 class Env(UserDict.IterableUserDict):
40 """
41 A dict-like object containing global objects used by tests.
42 """
43 def __init__(self, filename=None, version=0):
44 """
45 Create an empty Env object or load an existing one from a file.
47 If the version recorded in the file is lower than version, or if some
48 error occurs during unpickling, or if filename is not supplied,
49 create an empty Env object.
51 @param filename: Path to an env file.
52 @param version: Required env version (int).
53 """
54 UserDict.IterableUserDict.__init__(self)
55 empty = {"version": version}
56 if filename:
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
76 def save(self, filename=None):
77 """
78 Pickle the contents of the Env object into a file.
80 @param filename: Filename to pickle the dict into. If not supplied,
81 use the filename from which the dict was loaded.
82 """
83 filename = filename or self._filename
84 f = open(filename, "w")
85 cPickle.dump(self.data, f)
86 f.close()
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)]
96 def get_vm(self, name):
97 """
98 Return a VM object by its name.
100 @param name: VM name.
102 return self.get("vm__%s" % name)
105 def register_vm(self, name, vm):
107 Register a VM in this Env object.
109 @param name: VM name.
110 @param vm: VM object.
112 self["vm__%s" % name] = vm
115 def unregister_vm(self, name):
117 Remove a given VM.
119 @param name: VM name.
121 del self["vm__%s" % name]
124 def register_installer(self, installer):
126 Register a installer that was just run
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.
132 self['last_installer'] = installer
135 def previous_installer(self):
137 Return the last installer that was registered
139 return self.get('last_installer')
142 class Params(UserDict.IterableUserDict):
144 A dict-like object passed to every test.
146 def objects(self, key):
148 Return the names of objects defined using a given key.
150 @param key: The name of the key whose value lists the objects
151 (e.g. 'nics').
153 return self.get(key, "").split()
156 def object_params(self, obj_name):
158 Return a dict-like object containing the parameters of an individual
159 object.
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.
166 @param obj_name: The name of the object (objects are listed by the
167 objects() method).
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
178 # Functions related to MAC/IP addresses
180 def _open_mac_pool(lock_mode):
181 lock_file = open("/tmp/mac_lock", "w+")
182 fcntl.lockf(lock_file, lock_mode)
183 pool = shelve.open("/tmp/address_pool")
184 return pool, lock_file
187 def _close_mac_pool(pool, lock_file):
188 pool.close()
189 fcntl.lockf(lock_file, fcntl.LOCK_UN)
190 lock_file.close()
193 def _generate_mac_address_prefix(mac_pool):
195 Generate a random MAC address prefix and add it to the MAC pool dictionary.
196 If there's a MAC prefix there already, do not update the MAC pool and just
197 return what's in there. By convention we will set KVM autotest MAC
198 addresses to start with 0x9a.
200 @param mac_pool: The MAC address pool object.
201 @return: The MAC address prefix.
203 if "prefix" in mac_pool:
204 prefix = mac_pool["prefix"]
205 logging.debug("Used previously generated MAC address prefix for this "
206 "host: %s", prefix)
207 else:
208 r = random.SystemRandom()
209 prefix = "9a:%02x:%02x:%02x:" % (r.randint(0x00, 0xff),
210 r.randint(0x00, 0xff),
211 r.randint(0x00, 0xff))
212 mac_pool["prefix"] = prefix
213 logging.debug("Generated MAC address prefix for this host: %s", prefix)
214 return prefix
217 def generate_mac_address(vm_instance, nic_index):
219 Randomly generate a MAC address and add it to the MAC address pool.
221 Try to generate a MAC address based on a randomly generated MAC address
222 prefix and add it to a persistent dictionary.
223 key = VM instance + NIC index, value = MAC address
224 e.g. {'20100310-165222-Wt7l:0': '9a:5d:94:6a:9b:f9'}
226 @param vm_instance: The instance attribute of a VM.
227 @param nic_index: The index of the NIC.
228 @return: MAC address string.
230 mac_pool, lock_file = _open_mac_pool(fcntl.LOCK_EX)
231 key = "%s:%s" % (vm_instance, nic_index)
232 if key in mac_pool:
233 mac = mac_pool[key]
234 else:
235 prefix = _generate_mac_address_prefix(mac_pool)
236 r = random.SystemRandom()
237 while key not in mac_pool:
238 mac = prefix + "%02x:%02x" % (r.randint(0x00, 0xff),
239 r.randint(0x00, 0xff))
240 if mac in mac_pool.values():
241 continue
242 mac_pool[key] = mac
243 logging.debug("Generated MAC address for NIC %s: %s", key, mac)
244 _close_mac_pool(mac_pool, lock_file)
245 return mac
248 def free_mac_address(vm_instance, nic_index):
250 Remove a MAC address from the address pool.
252 @param vm_instance: The instance attribute of a VM.
253 @param nic_index: The index of the NIC.
255 mac_pool, lock_file = _open_mac_pool(fcntl.LOCK_EX)
256 key = "%s:%s" % (vm_instance, nic_index)
257 if key in mac_pool:
258 logging.debug("Freeing MAC address for NIC %s: %s", key, mac_pool[key])
259 del mac_pool[key]
260 _close_mac_pool(mac_pool, lock_file)
263 def set_mac_address(vm_instance, nic_index, mac):
265 Set a MAC address in the pool.
267 @param vm_instance: The instance attribute of a VM.
268 @param nic_index: The index of the NIC.
270 mac_pool, lock_file = _open_mac_pool(fcntl.LOCK_EX)
271 mac_pool["%s:%s" % (vm_instance, nic_index)] = mac
272 _close_mac_pool(mac_pool, lock_file)
275 def get_mac_address(vm_instance, nic_index):
277 Return a MAC address from the pool.
279 @param vm_instance: The instance attribute of a VM.
280 @param nic_index: The index of the NIC.
281 @return: MAC address string.
283 mac_pool, lock_file = _open_mac_pool(fcntl.LOCK_SH)
284 mac = mac_pool.get("%s:%s" % (vm_instance, nic_index))
285 _close_mac_pool(mac_pool, lock_file)
286 return mac
289 def verify_ip_address_ownership(ip, macs, timeout=10.0):
291 Use arping and the ARP cache to make sure a given IP address belongs to one
292 of the given MAC addresses.
294 @param ip: An IP address.
295 @param macs: A list or tuple of MAC addresses.
296 @return: True iff ip is assigned to a MAC address in macs.
298 # Compile a regex that matches the given IP address and any of the given
299 # MAC addresses
300 mac_regex = "|".join("(%s)" % mac for mac in macs)
301 regex = re.compile(r"\b%s\b.*\b(%s)\b" % (ip, mac_regex), re.IGNORECASE)
303 # Check the ARP cache
304 o = commands.getoutput("%s -n" % find_command("arp"))
305 if regex.search(o):
306 return True
308 # Get the name of the bridge device for arping
309 o = commands.getoutput("%s route get %s" % (find_command("ip"), ip))
310 dev = re.findall("dev\s+\S+", o, re.IGNORECASE)
311 if not dev:
312 return False
313 dev = dev[0].split()[-1]
315 # Send an ARP request
316 o = commands.getoutput("%s -f -c 3 -I %s %s" %
317 (find_command("arping"), dev, ip))
318 return bool(regex.search(o))
321 # Utility functions for dealing with external processes
323 def find_command(cmd):
324 for dir in ["/usr/local/sbin", "/usr/local/bin",
325 "/usr/sbin", "/usr/bin", "/sbin", "/bin"]:
326 file = os.path.join(dir, cmd)
327 if os.path.exists(file):
328 return file
329 raise ValueError('Missing command: %s' % cmd)
332 def pid_exists(pid):
334 Return True if a given PID exists.
336 @param pid: Process ID number.
338 try:
339 os.kill(pid, 0)
340 return True
341 except:
342 return False
345 def safe_kill(pid, signal):
347 Attempt to send a signal to a given process that may or may not exist.
349 @param signal: Signal number.
351 try:
352 os.kill(pid, signal)
353 return True
354 except:
355 return False
358 def kill_process_tree(pid, sig=signal.SIGKILL):
359 """Signal a process and all of its children.
361 If the process does not exist -- return.
363 @param pid: The pid of the process to signal.
364 @param sig: The signal to send to the processes.
366 if not safe_kill(pid, signal.SIGSTOP):
367 return
368 children = commands.getoutput("ps --ppid=%d -o pid=" % pid).split()
369 for child in children:
370 kill_process_tree(int(child), sig)
371 safe_kill(pid, sig)
372 safe_kill(pid, signal.SIGCONT)
375 def get_latest_kvm_release_tag(release_listing):
377 Fetches the latest release tag for KVM.
379 @param release_listing: URL that contains a list of the Source Forge
380 KVM project files.
382 try:
383 release_page = utils.urlopen(release_listing)
384 data = release_page.read()
385 release_page.close()
386 rx = re.compile("kvm-(\d+).tar.gz", re.IGNORECASE)
387 matches = rx.findall(data)
388 # In all regexp matches to something that looks like a release tag,
389 # get the largest integer. That will be our latest release tag.
390 latest_tag = max(int(x) for x in matches)
391 return str(latest_tag)
392 except Exception, e:
393 message = "Could not fetch latest KVM release tag: %s" % str(e)
394 logging.error(message)
395 raise error.TestError(message)
398 def get_git_branch(repository, branch, srcdir, commit=None, lbranch=None):
400 Retrieves a given git code repository.
402 @param repository: Git repository URL
404 logging.info("Fetching git [REP '%s' BRANCH '%s' COMMIT '%s'] -> %s",
405 repository, branch, commit, srcdir)
406 if not os.path.exists(srcdir):
407 os.makedirs(srcdir)
408 os.chdir(srcdir)
410 if os.path.exists(".git"):
411 utils.system("git reset --hard")
412 else:
413 utils.system("git init")
415 if not lbranch:
416 lbranch = branch
418 utils.system("git fetch -q -f -u -t %s %s:%s" %
419 (repository, branch, lbranch))
420 utils.system("git checkout %s" % lbranch)
421 if commit:
422 utils.system("git checkout %s" % commit)
424 h = utils.system_output('git log --pretty=format:"%H" -1')
425 try:
426 desc = "tag %s" % utils.system_output("git describe")
427 except error.CmdError:
428 desc = "no tag found"
430 logging.info("Commit hash for %s is %s (%s)" % (repository, h.strip(),
431 desc))
432 return srcdir
435 def check_kvm_source_dir(source_dir):
437 Inspects the kvm source directory and verifies its disposition. In some
438 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
440 changes on the source directory layout, so it's not scalable to just use
441 strings like 'old_repo', 'new_repo' and such.
443 @param source_dir: Source code path that will be inspected.
445 os.chdir(source_dir)
446 has_qemu_dir = os.path.isdir('qemu')
447 has_kvm_dir = os.path.isdir('kvm')
448 if has_qemu_dir:
449 logging.debug("qemu directory detected, source dir layout 1")
450 return 1
451 if has_kvm_dir and not has_qemu_dir:
452 logging.debug("kvm directory detected, source dir layout 2")
453 return 2
454 else:
455 raise error.TestError("Unknown source dir layout, cannot proceed.")
458 # Functions and classes used for logging into guests and transferring files
460 class LoginError(Exception):
461 pass
464 class LoginAuthenticationError(LoginError):
465 pass
468 class LoginTimeoutError(LoginError):
469 def __init__(self, output):
470 LoginError.__init__(self, output)
471 self.output = output
473 def __str__(self):
474 return "Timeout expired (output so far: %r)" % self.output
477 class LoginProcessTerminatedError(LoginError):
478 def __init__(self, status, output):
479 LoginError.__init__(self, status, output)
480 self.status = status
481 self.output = output
483 def __str__(self):
484 return ("Client process terminated (status: %s, output: %r)" %
485 (self.status, self.output))
488 class LoginBadClientError(LoginError):
489 def __init__(self, client):
490 LoginError.__init__(self, client)
491 self.client = client
493 def __str__(self):
494 return "Unknown remote shell client: %r" % self.client
497 class SCPError(Exception):
498 pass
501 class SCPAuthenticationError(SCPError):
502 pass
505 class SCPTransferTimeoutError(SCPError):
506 def __init__(self, output):
507 SCPError.__init__(self, output)
508 self.output = output
510 def __str__(self):
511 return "Transfer timeout expired (output so far: %r)" % self.output
514 class SCPTransferFailedError(SCPError):
515 def __init__(self, status, output):
516 SCPError.__init__(self, status, output)
517 self.status = status
518 self.output = output
520 def __str__(self):
521 return "SCP transfer failed (status: %s, output: %r)" % (self.status,
522 self.output)
525 def _remote_login(session, username, password, prompt, timeout=10):
527 Log into a remote host (guest) using SSH or Telnet. Wait for questions
528 and provide answers. If timeout expires while waiting for output from the
529 child (e.g. a password prompt or a shell prompt) -- fail.
531 @brief: Log into a remote host (guest) using SSH or Telnet.
533 @param session: An Expect or ShellSession instance to operate on
534 @param username: The username to send in reply to a login prompt
535 @param password: The password to send in reply to a password prompt
536 @param prompt: The shell prompt that indicates a successful login
537 @param timeout: The maximal time duration (in seconds) to wait for each
538 step of the login procedure (i.e. the "Are you sure" prompt, the
539 password prompt, the shell prompt, etc)
541 @return: True on success and False otherwise.
543 password_prompt_count = 0
544 login_prompt_count = 0
546 while True:
547 try:
548 match, text = session.read_until_last_line_matches(
549 [r"[Aa]re you sure", r"[Pp]assword:\s*$", r"[Ll]ogin:\s*$",
550 r"[Cc]onnection.*closed", r"[Cc]onnection.*refused",
551 r"[Pp]lease wait", prompt],
552 timeout=timeout, internal_timeout=0.5)
553 if match == 0: # "Are you sure you want to continue connecting"
554 logging.debug("Got 'Are you sure...'; sending 'yes'")
555 session.sendline("yes")
556 continue
557 elif match == 1: # "password:"
558 if password_prompt_count == 0:
559 logging.debug("Got password prompt; sending '%s'" % password)
560 session.sendline(password)
561 password_prompt_count += 1
562 continue
563 else:
564 logging.debug("Got password prompt again")
565 return False
566 elif match == 2: # "login:"
567 if login_prompt_count == 0:
568 logging.debug("Got username prompt; sending '%s'" % username)
569 session.sendline(username)
570 login_prompt_count += 1
571 continue
572 else:
573 logging.debug("Got username prompt again")
574 return False
575 elif match == 3: # "Connection closed"
576 logging.debug("Got 'Connection closed'")
577 return False
578 elif match == 4: # "Connection refused"
579 logging.debug("Got 'Connection refused'")
580 return False
581 elif match == 5: # "Please wait"
582 logging.debug("Got 'Please wait'")
583 timeout = 30
584 continue
585 elif match == 6: # prompt
586 logging.debug("Got shell prompt -- logged in")
587 return True
588 except kvm_subprocess.ExpectTimeoutError, e:
589 logging.debug("Timeout elapsed (output so far: %r)" % e.output)
590 return False
591 except kvm_subprocess.ExpectProcessTerminatedError, e:
592 logging.debug("Process terminated (output so far: %r)" % e.output)
593 return False
596 def _remote_scp(session, password, transfer_timeout=600, login_timeout=10):
598 Transfer file(s) to a remote host (guest) using SCP. Wait for questions
599 and provide answers. If login_timeout expires while waiting for output
600 from the child (e.g. a password prompt), fail. If transfer_timeout expires
601 while waiting for the transfer to complete, fail.
603 @brief: Transfer files using SCP, given a command line.
605 @param session: An Expect or ShellSession instance to operate on
606 @param password: The password to send in reply to a password prompt.
607 @param transfer_timeout: The time duration (in seconds) to wait for the
608 transfer to complete.
609 @param login_timeout: The maximal time duration (in seconds) to wait for
610 each step of the login procedure (i.e. the "Are you sure" prompt or
611 the password prompt)
613 @return: True if the transfer succeeds and False on failure.
615 password_prompt_count = 0
616 timeout = login_timeout
618 while True:
619 try:
620 match, text = session.read_until_last_line_matches(
621 [r"[Aa]re you sure", r"[Pp]assword:\s*$", r"lost connection"],
622 timeout=timeout, internal_timeout=0.5)
623 if match == 0: # "Are you sure you want to continue connecting"
624 logging.debug("Got 'Are you sure...'; sending 'yes'")
625 session.sendline("yes")
626 continue
627 elif match == 1: # "password:"
628 if password_prompt_count == 0:
629 logging.debug("Got password prompt; sending '%s'" % password)
630 session.sendline(password)
631 password_prompt_count += 1
632 timeout = transfer_timeout
633 continue
634 else:
635 logging.debug("Got password prompt again")
636 return False
637 elif match == 2: # "lost connection"
638 logging.debug("Got 'lost connection'")
639 return False
640 except kvm_subprocess.ExpectTimeoutError, e:
641 logging.debug("Timeout expired")
642 return False
643 except kvm_subprocess.ExpectProcessTerminatedError, e:
644 logging.debug("SCP process terminated with status %s", e.status)
645 return e.status == 0
648 def remote_login(client, host, port, username, password, prompt, linesep="\n",
649 log_filename=None, timeout=10):
651 Log into a remote host (guest) using SSH/Telnet/Netcat.
653 @param client: The client to use ('ssh', 'telnet' or 'nc')
654 @param host: Hostname or IP address
655 @param port: Port to connect to
656 @param username: Username (if required)
657 @param password: Password (if required)
658 @param prompt: Shell prompt (regular expression)
659 @param linesep: The line separator to use when sending lines
660 (e.g. '\\n' or '\\r\\n')
661 @param log_filename: If specified, log all output to this file
662 @param timeout: The maximal time duration (in seconds) to wait for
663 each step of the login procedure (i.e. the "Are you sure" prompt
664 or the password prompt)
666 @return: ShellSession object on success and None on failure.
668 if client == "ssh":
669 cmd = ("ssh -o UserKnownHostsFile=/dev/null "
670 "-o PreferredAuthentications=password -p %s %s@%s" %
671 (port, username, host))
672 elif client == "telnet":
673 cmd = "telnet -l %s %s %s" % (username, host, port)
674 elif client == "nc":
675 cmd = "nc %s %s" % (host, port)
676 else:
677 logging.error("Unknown remote shell client: %s" % client)
678 return
680 logging.debug("Trying to login with command '%s'" % cmd)
681 session = kvm_subprocess.ShellSession(cmd, linesep=linesep, prompt=prompt)
682 if _remote_login(session, username, password, prompt, timeout):
683 if log_filename:
684 session.set_output_func(log_line)
685 session.set_output_params((log_filename,))
686 return session
687 else:
688 session.close()
691 def remote_scp(command, password, log_filename=None, transfer_timeout=600,
692 login_timeout=10):
694 Transfer file(s) to a remote host (guest) using SCP.
696 @brief: Transfer files using SCP, given a command line.
698 @param command: The command to execute
699 (e.g. "scp -r foobar root@localhost:/tmp/").
700 @param password: The password to send in reply to a password prompt.
701 @param log_filename: If specified, log all output to this file
702 @param transfer_timeout: The time duration (in seconds) to wait for the
703 transfer to complete.
704 @param login_timeout: The maximal time duration (in seconds) to wait for
705 each step of the login procedure (i.e. the "Are you sure" prompt
706 or the password prompt)
708 @return: True if the transfer succeeds and False on failure.
710 logging.debug("Trying to SCP with command '%s', timeout %ss",
711 command, transfer_timeout)
713 if log_filename:
714 output_func = log_line
715 output_params = (log_filename,)
716 else:
717 output_func = None
718 output_params = ()
720 session = kvm_subprocess.Expect(command,
721 output_func=output_func,
722 output_params=output_params)
723 try:
724 return _remote_scp(session, password, transfer_timeout, login_timeout)
725 finally:
726 session.close()
729 def copy_files_to(address, client, username, password, port, local_path,
730 remote_path, log_filename=None, timeout=600):
732 Decide the transfer cleint and copy file to a remote host (guest).
734 @param client: Type of transfer client
735 @param username: Username (if required)
736 @param password: Password (if requried)
737 @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
739 @param address: Address of remote host(guest)
740 @param log_filename: If specified, log all output to this file
741 @param timeout: The time duration (in seconds) to wait for the transfer to
742 complete.
744 @return: True on success and False on failure.
747 if not address or not port:
748 logging.debug("IP address or port unavailable")
749 return None
751 if client == "scp":
752 return scp_to_remote(address, port, username, password, local_path,
753 remote_path, log_filename, timeout)
754 elif client == "rss":
755 c = rss_file_transfer.FileUploadClient(address, port)
756 c.upload(local_path, remote_path, timeout)
757 c.close()
758 return True
761 def copy_files_from(address, client, username, password, port, local_path,
762 remote_path, log_filename=None, timeout=600):
764 Decide the transfer cleint and copy file from a remote host (guest).
766 @param client: Type of transfer client
767 @param username: Username (if required)
768 @param password: Password (if requried)
769 @param local_path: Path on the local machine where we are copying from
770 @param remote_path: Path on the remote machine where we are copying to
771 @param address: Address of remote host(guest)
772 @param log_filename: If specified, log all output to this file
773 @param timeout: The time duration (in seconds) to wait for the transfer to
774 complete.
776 @return: True on success and False on failure.
779 if not address or not port:
780 logging.debug("IP address or port unavailable")
781 return None
783 if client == "scp":
784 return scp_from_remote(address, port, username, password, remote_path,
785 local_path, log_filename, timeout)
786 elif client == "rss":
787 c = rss_file_transfer.FileDownloadClient(address, port)
788 c.download(remote_path, local_path, timeout)
789 c.close()
790 return True
793 def scp_to_remote(host, port, username, password, local_path, remote_path,
794 log_filename=None, timeout=600):
796 Copy files to a remote host (guest) through scp.
798 @param host: Hostname or IP address
799 @param username: Username (if required)
800 @param password: Password (if required)
801 @param local_path: Path on the local machine where we are copying from
802 @param remote_path: Path on the remote machine where we are copying to
803 @param log_filename: If specified, log all output to this file
804 @param timeout: The time duration (in seconds) to wait for the transfer
805 to complete.
807 @return: True on success and False on failure.
809 command = ("scp -v -o UserKnownHostsFile=/dev/null "
810 "-o PreferredAuthentications=password -r -P %s %s %s@%s:%s" %
811 (port, local_path, username, host, remote_path))
812 return remote_scp(command, password, log_filename, timeout)
815 def scp_from_remote(host, port, username, password, remote_path, local_path,
816 log_filename=None, timeout=600):
818 Copy files from a remote host (guest).
820 @param host: Hostname or IP address
821 @param username: Username (if required)
822 @param password: Password (if required)
823 @param local_path: Path on the local machine where we are copying from
824 @param remote_path: Path on the remote machine where we are copying to
825 @param log_filename: If specified, log all output to this file
826 @param timeout: The time duration (in seconds) to wait for the transfer
827 to complete.
829 @return: True on success and False on failure.
831 command = ("scp -v -o UserKnownHostsFile=/dev/null "
832 "-o PreferredAuthentications=password -r -P %s %s@%s:%s %s" %
833 (port, username, host, remote_path, local_path))
834 return remote_scp(command, password, log_filename, timeout)
837 # The following are utility functions related to ports.
839 def is_port_free(port, address):
841 Return True if the given port is available for use.
843 @param port: Port number
845 try:
846 s = socket.socket()
847 #s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
848 if address == "localhost":
849 s.bind(("localhost", port))
850 free = True
851 else:
852 s.connect((address, port))
853 free = False
854 except socket.error:
855 if address == "localhost":
856 free = False
857 else:
858 free = True
859 s.close()
860 return free
863 def find_free_port(start_port, end_port, address="localhost"):
865 Return a host free port in the range [start_port, end_port].
867 @param start_port: First port that will be checked.
868 @param end_port: Port immediately after the last one that will be checked.
870 for i in range(start_port, end_port):
871 if is_port_free(i, address):
872 return i
873 return None
876 def find_free_ports(start_port, end_port, count, address="localhost"):
878 Return count of host free ports in the range [start_port, end_port].
880 @count: Initial number of ports known to be free in the range.
881 @param start_port: First port that will be checked.
882 @param end_port: Port immediately after the last one that will be checked.
884 ports = []
885 i = start_port
886 while i < end_port and count > 0:
887 if is_port_free(i, address):
888 ports.append(i)
889 count -= 1
890 i += 1
891 return ports
894 # An easy way to log lines to files when the logging system can't be used
896 _open_log_files = {}
897 _log_file_dir = "/tmp"
900 def log_line(filename, line):
902 Write a line to a file. '\n' is appended to the line.
904 @param filename: Path of file to write to, either absolute or relative to
905 the dir set by set_log_file_dir().
906 @param line: Line to write.
908 global _open_log_files, _log_file_dir
909 if filename not in _open_log_files:
910 path = get_path(_log_file_dir, filename)
911 try:
912 os.makedirs(os.path.dirname(path))
913 except OSError:
914 pass
915 _open_log_files[filename] = open(path, "w")
916 timestr = time.strftime("%Y-%m-%d %H:%M:%S")
917 _open_log_files[filename].write("%s: %s\n" % (timestr, line))
918 _open_log_files[filename].flush()
921 def set_log_file_dir(dir):
923 Set the base directory for log files created by log_line().
925 @param dir: Directory for log files.
927 global _log_file_dir
928 _log_file_dir = dir
931 # The following are miscellaneous utility functions.
933 def get_path(base_path, user_path):
935 Translate a user specified path to a real path.
936 If user_path is relative, append it to base_path.
937 If user_path is absolute, return it as is.
939 @param base_path: The base path of relative user specified paths.
940 @param user_path: The user specified path.
942 if os.path.isabs(user_path):
943 return user_path
944 else:
945 return os.path.join(base_path, user_path)
948 def generate_random_string(length):
950 Return a random string using alphanumeric characters.
952 @length: length of the string that will be generated.
954 r = random.SystemRandom()
955 str = ""
956 chars = string.letters + string.digits
957 while length > 0:
958 str += r.choice(chars)
959 length -= 1
960 return str
962 def generate_random_id():
964 Return a random string suitable for use as a qemu id.
966 return "id" + generate_random_string(6)
969 def generate_tmp_file_name(file, ext=None, dir='/tmp/'):
971 Returns a temporary file name. The file is not created.
973 while True:
974 file_name = (file + '-' + time.strftime("%Y%m%d-%H%M%S-") +
975 generate_random_string(4))
976 if ext:
977 file_name += '.' + ext
978 file_name = os.path.join(dir, file_name)
979 if not os.path.exists(file_name):
980 break
982 return file_name
985 def format_str_for_message(str):
987 Format str so that it can be appended to a message.
988 If str consists of one line, prefix it with a space.
989 If str consists of multiple lines, prefix it with a newline.
991 @param str: string that will be formatted.
993 lines = str.splitlines()
994 num_lines = len(lines)
995 str = "\n".join(lines)
996 if num_lines == 0:
997 return ""
998 elif num_lines == 1:
999 return " " + str
1000 else:
1001 return "\n" + str
1004 def wait_for(func, timeout, first=0.0, step=1.0, text=None):
1006 If func() evaluates to True before timeout expires, return the
1007 value of func(). Otherwise return None.
1009 @brief: Wait until func() evaluates to True.
1011 @param timeout: Timeout in seconds
1012 @param first: Time to sleep before first attempt
1013 @param steps: Time to sleep between attempts in seconds
1014 @param text: Text to print while waiting, for debug purposes
1016 start_time = time.time()
1017 end_time = time.time() + timeout
1019 time.sleep(first)
1021 while time.time() < end_time:
1022 if text:
1023 logging.debug("%s (%f secs)" % (text, time.time() - start_time))
1025 output = func()
1026 if output:
1027 return output
1029 time.sleep(step)
1031 logging.debug("Timeout elapsed")
1032 return None
1035 def get_hash_from_file(hash_path, dvd_basename):
1037 Get the a hash from a given DVD image from a hash file
1038 (Hash files are usually named MD5SUM or SHA1SUM and are located inside the
1039 download directories of the DVDs)
1041 @param hash_path: Local path to a hash file.
1042 @param cd_image: Basename of a CD image
1044 hash_file = open(hash_path, 'r')
1045 for line in hash_file.readlines():
1046 if dvd_basename in line:
1047 return line.split()[0]
1050 def run_tests(test_list, job):
1052 Runs the sequence of KVM tests based on the list of dictionaries
1053 generated by the configuration system, handling dependencies.
1055 @param test_list: List with all dictionary test parameters.
1056 @param job: Autotest job object.
1058 @return: True, if all tests ran passed, False if any of them failed.
1060 status_dict = {}
1061 failed = False
1063 for dict in test_list:
1064 if dict.get("skip") == "yes":
1065 continue
1066 dependencies_satisfied = True
1067 for dep in dict.get("depend"):
1068 for test_name in status_dict.keys():
1069 if not dep in test_name:
1070 continue
1071 if not status_dict[test_name]:
1072 dependencies_satisfied = False
1073 break
1074 if dependencies_satisfied:
1075 test_iterations = int(dict.get("iterations", 1))
1076 test_tag = dict.get("shortname")
1077 # Setting up profilers during test execution.
1078 profilers = dict.get("profilers", "").split()
1079 for profiler in profilers:
1080 job.profilers.add(profiler)
1082 # We need only one execution, profiled, hence we're passing
1083 # the profile_only parameter to job.run_test().
1084 current_status = job.run_test("kvm", params=dict, tag=test_tag,
1085 iterations=test_iterations,
1086 profile_only= bool(profilers) or None)
1088 for profiler in profilers:
1089 job.profilers.delete(profiler)
1091 if not current_status:
1092 failed = True
1093 else:
1094 current_status = False
1095 status_dict[dict.get("name")] = current_status
1097 return not failed
1100 def create_report(report_dir, results_dir):
1102 Creates a neatly arranged HTML results report in the results dir.
1104 @param report_dir: Directory where the report script is located.
1105 @param results_dir: Directory where the results will be output.
1107 reporter = os.path.join(report_dir, 'html_report.py')
1108 html_file = os.path.join(results_dir, 'results.html')
1109 os.system('%s -r %s -f %s -R' % (reporter, results_dir, html_file))
1112 def get_full_pci_id(pci_id):
1114 Get full PCI ID of pci_id.
1116 @param pci_id: PCI ID of a device.
1118 cmd = "lspci -D | awk '/%s/ {print $1}'" % pci_id
1119 status, full_id = commands.getstatusoutput(cmd)
1120 if status != 0:
1121 return None
1122 return full_id
1125 def get_vendor_from_pci_id(pci_id):
1127 Check out the device vendor ID according to pci_id.
1129 @param pci_id: PCI ID of a device.
1131 cmd = "lspci -n | awk '/%s/ {print $3}'" % pci_id
1132 return re.sub(":", " ", commands.getoutput(cmd))
1135 class Thread(threading.Thread):
1137 Run a function in a background thread.
1139 def __init__(self, target, args=(), kwargs={}):
1141 Initialize the instance.
1143 @param target: Function to run in the thread.
1144 @param args: Arguments to pass to target.
1145 @param kwargs: Keyword arguments to pass to target.
1147 threading.Thread.__init__(self)
1148 self._target = target
1149 self._args = args
1150 self._kwargs = kwargs
1153 def run(self):
1155 Run target (passed to the constructor). No point in calling this
1156 function directly. Call start() to make this function run in a new
1157 thread.
1159 self._e = None
1160 self._retval = None
1161 try:
1162 try:
1163 self._retval = self._target(*self._args, **self._kwargs)
1164 except:
1165 self._e = sys.exc_info()
1166 raise
1167 finally:
1168 # Avoid circular references (start() may be called only once so
1169 # it's OK to delete these)
1170 del self._target, self._args, self._kwargs
1173 def join(self, timeout=None):
1175 Join the thread. If target raised an exception, re-raise it.
1176 Otherwise, return the value returned by target.
1178 @param timeout: Timeout value to pass to threading.Thread.join().
1180 threading.Thread.join(self, timeout)
1181 try:
1182 if self._e:
1183 raise self._e[0], self._e[1], self._e[2]
1184 else:
1185 return self._retval
1186 finally:
1187 # Avoid circular references (join() may be called multiple times
1188 # so we can't delete these)
1189 self._e = None
1190 self._retval = None
1193 def parallel(targets):
1195 Run multiple functions in parallel.
1197 @param targets: A sequence of tuples or functions. If it's a sequence of
1198 tuples, each tuple will be interpreted as (target, args, kwargs) or
1199 (target, args) or (target,) depending on its length. If it's a
1200 sequence of functions, the functions will be called without
1201 arguments.
1202 @return: A list of the values returned by the functions called.
1204 threads = []
1205 for target in targets:
1206 if isinstance(target, tuple) or isinstance(target, list):
1207 t = Thread(*target)
1208 else:
1209 t = Thread(target)
1210 threads.append(t)
1211 t.start()
1212 return [t.join() for t in threads]
1215 class KvmLoggingConfig(logging_config.LoggingConfig):
1217 Used with the sole purpose of providing convenient logging setup
1218 for the KVM test auxiliary programs.
1220 def configure_logging(self, results_dir=None, verbose=False):
1221 super(KvmLoggingConfig, self).configure_logging(use_console=True,
1222 verbose=verbose)
1225 class PciAssignable(object):
1227 Request PCI assignable devices on host. It will check whether to request
1228 PF (physical Functions) or VF (Virtual Functions).
1230 def __init__(self, type="vf", driver=None, driver_option=None,
1231 names=None, devices_requested=None):
1233 Initialize parameter 'type' which could be:
1234 vf: Virtual Functions
1235 pf: Physical Function (actual hardware)
1236 mixed: Both includes VFs and PFs
1238 If pass through Physical NIC cards, we need to specify which devices
1239 to be assigned, e.g. 'eth1 eth2'.
1241 If pass through Virtual Functions, we need to specify how many vfs
1242 are going to be assigned, e.g. passthrough_count = 8 and max_vfs in
1243 config file.
1245 @param type: PCI device type.
1246 @param driver: Kernel module for the PCI assignable device.
1247 @param driver_option: Module option to specify the maximum number of
1248 VFs (eg 'max_vfs=7')
1249 @param names: Physical NIC cards correspondent network interfaces,
1250 e.g.'eth1 eth2 ...'
1251 @param devices_requested: Number of devices being requested.
1253 self.type = type
1254 self.driver = driver
1255 self.driver_option = driver_option
1256 if names:
1257 self.name_list = names.split()
1258 if devices_requested:
1259 self.devices_requested = int(devices_requested)
1260 else:
1261 self.devices_requested = None
1264 def _get_pf_pci_id(self, name, search_str):
1266 Get the PF PCI ID according to name.
1268 @param name: Name of the PCI device.
1269 @param search_str: Search string to be used on lspci.
1271 cmd = "ethtool -i %s | awk '/bus-info/ {print $2}'" % name
1272 s, pci_id = commands.getstatusoutput(cmd)
1273 if not (s or "Cannot get driver information" in pci_id):
1274 return pci_id[5:]
1275 cmd = "lspci | awk '/%s/ {print $1}'" % search_str
1276 pci_ids = [id for id in commands.getoutput(cmd).splitlines()]
1277 nic_id = int(re.search('[0-9]+', name).group(0))
1278 if (len(pci_ids) - 1) < nic_id:
1279 return None
1280 return pci_ids[nic_id]
1283 def _release_dev(self, pci_id):
1285 Release a single PCI device.
1287 @param pci_id: PCI ID of a given PCI device.
1289 base_dir = "/sys/bus/pci"
1290 full_id = get_full_pci_id(pci_id)
1291 vendor_id = get_vendor_from_pci_id(pci_id)
1292 drv_path = os.path.join(base_dir, "devices/%s/driver" % full_id)
1293 if 'pci-stub' in os.readlink(drv_path):
1294 cmd = "echo '%s' > %s/new_id" % (vendor_id, drv_path)
1295 if os.system(cmd):
1296 return False
1298 stub_path = os.path.join(base_dir, "drivers/pci-stub")
1299 cmd = "echo '%s' > %s/unbind" % (full_id, stub_path)
1300 if os.system(cmd):
1301 return False
1303 driver = self.dev_drivers[pci_id]
1304 cmd = "echo '%s' > %s/bind" % (full_id, driver)
1305 if os.system(cmd):
1306 return False
1308 return True
1311 def get_vf_devs(self):
1313 Catch all VFs PCI IDs.
1315 @return: List with all PCI IDs for the Virtual Functions avaliable
1317 if not self.sr_iov_setup():
1318 return []
1320 cmd = "lspci | awk '/Virtual Function/ {print $1}'"
1321 return commands.getoutput(cmd).split()
1324 def get_pf_devs(self):
1326 Catch all PFs PCI IDs.
1328 @return: List with all PCI IDs for the physical hardware requested
1330 pf_ids = []
1331 for name in self.name_list:
1332 pf_id = self._get_pf_pci_id(name, "Ethernet")
1333 if not pf_id:
1334 continue
1335 pf_ids.append(pf_id)
1336 return pf_ids
1339 def get_devs(self, count):
1341 Check out all devices' PCI IDs according to their name.
1343 @param count: count number of PCI devices needed for pass through
1344 @return: a list of all devices' PCI IDs
1346 if self.type == "vf":
1347 vf_ids = self.get_vf_devs()
1348 elif self.type == "pf":
1349 vf_ids = self.get_pf_devs()
1350 elif self.type == "mixed":
1351 vf_ids = self.get_vf_devs()
1352 vf_ids.extend(self.get_pf_devs())
1353 return vf_ids[0:count]
1356 def get_vfs_count(self):
1358 Get VFs count number according to lspci.
1360 # FIXME: Need to think out a method of identify which
1361 # 'virtual function' belongs to which physical card considering
1362 # that if the host has more than one 82576 card. PCI_ID?
1363 cmd = "lspci | grep 'Virtual Function' | wc -l"
1364 return int(commands.getoutput(cmd))
1367 def check_vfs_count(self):
1369 Check VFs count number according to the parameter driver_options.
1371 # Network card 82576 has two network interfaces and each can be
1372 # virtualized up to 7 virtual functions, therefore we multiply
1373 # two for the value of driver_option 'max_vfs'.
1374 expected_count = int((re.findall("(\d)", self.driver_option)[0])) * 2
1375 return (self.get_vfs_count == expected_count)
1378 def is_binded_to_stub(self, full_id):
1380 Verify whether the device with full_id is already binded to pci-stub.
1382 @param full_id: Full ID for the given PCI device
1384 base_dir = "/sys/bus/pci"
1385 stub_path = os.path.join(base_dir, "drivers/pci-stub")
1386 if os.path.exists(os.path.join(stub_path, full_id)):
1387 return True
1388 return False
1391 def sr_iov_setup(self):
1393 Ensure the PCI device is working in sr_iov mode.
1395 Check if the PCI hardware device drive is loaded with the appropriate,
1396 parameters (number of VFs), and if it's not, perform setup.
1398 @return: True, if the setup was completed successfuly, False otherwise.
1400 re_probe = False
1401 s, o = commands.getstatusoutput('lsmod | grep %s' % self.driver)
1402 if s:
1403 re_probe = True
1404 elif not self.check_vfs_count():
1405 os.system("modprobe -r %s" % self.driver)
1406 re_probe = True
1407 else:
1408 return True
1410 # Re-probe driver with proper number of VFs
1411 if re_probe:
1412 cmd = "modprobe %s %s" % (self.driver, self.driver_option)
1413 logging.info("Loading the driver '%s' with option '%s'" %
1414 (self.driver, self.driver_option))
1415 s, o = commands.getstatusoutput(cmd)
1416 if s:
1417 return False
1418 return True
1421 def request_devs(self):
1423 Implement setup process: unbind the PCI device and then bind it
1424 to the pci-stub driver.
1426 @return: a list of successfully requested devices' PCI IDs.
1428 base_dir = "/sys/bus/pci"
1429 stub_path = os.path.join(base_dir, "drivers/pci-stub")
1431 self.pci_ids = self.get_devs(self.devices_requested)
1432 logging.debug("The following pci_ids were found: %s", self.pci_ids)
1433 requested_pci_ids = []
1434 self.dev_drivers = {}
1436 # Setup all devices specified for assignment to guest
1437 for pci_id in self.pci_ids:
1438 full_id = get_full_pci_id(pci_id)
1439 if not full_id:
1440 continue
1441 drv_path = os.path.join(base_dir, "devices/%s/driver" % full_id)
1442 dev_prev_driver= os.path.realpath(os.path.join(drv_path,
1443 os.readlink(drv_path)))
1444 self.dev_drivers[pci_id] = dev_prev_driver
1446 # Judge whether the device driver has been binded to stub
1447 if not self.is_binded_to_stub(full_id):
1448 logging.debug("Binding device %s to stub", full_id)
1449 vendor_id = get_vendor_from_pci_id(pci_id)
1450 stub_new_id = os.path.join(stub_path, 'new_id')
1451 unbind_dev = os.path.join(drv_path, 'unbind')
1452 stub_bind = os.path.join(stub_path, 'bind')
1454 info_write_to_files = [(vendor_id, stub_new_id),
1455 (full_id, unbind_dev),
1456 (full_id, stub_bind)]
1458 for content, file in info_write_to_files:
1459 try:
1460 utils.open_write_close(file, content)
1461 except IOError:
1462 logging.debug("Failed to write %s to file %s", content,
1463 file)
1464 continue
1466 if not self.is_binded_to_stub(full_id):
1467 logging.error("Binding device %s to stub failed", pci_id)
1468 continue
1469 else:
1470 logging.debug("Device %s already binded to stub", pci_id)
1471 requested_pci_ids.append(pci_id)
1472 self.pci_ids = requested_pci_ids
1473 return self.pci_ids
1476 def release_devs(self):
1478 Release all PCI devices currently assigned to VMs back to the
1479 virtualization host.
1481 try:
1482 for pci_id in self.dev_drivers:
1483 if not self._release_dev(pci_id):
1484 logging.error("Failed to release device %s to host", pci_id)
1485 else:
1486 logging.info("Released device %s successfully", pci_id)
1487 except:
1488 return
1491 class KojiDownloader(object):
1493 Stablish a connection with the build system, either koji or brew.
1495 This class provides a convenience methods to retrieve packages hosted on
1496 the build system.
1498 def __init__(self, cmd):
1500 Verifies whether the system has koji or brew installed, then loads
1501 the configuration file that will be used to download the files.
1503 @param cmd: Command name, either 'brew' or 'koji'. It is important
1504 to figure out the appropriate configuration used by the
1505 downloader.
1506 @param dst_dir: Destination dir for the packages.
1508 if not KOJI_INSTALLED:
1509 raise ValueError('No koji/brew installed on the machine')
1511 if os.path.isfile(cmd):
1512 koji_cmd = cmd
1513 else:
1514 koji_cmd = os_dep.command(cmd)
1516 logging.debug("Found %s as the buildsystem interface", koji_cmd)
1518 config_map = {'/usr/bin/koji': '/etc/koji.conf',
1519 '/usr/bin/brew': '/etc/brewkoji.conf'}
1521 try:
1522 config_file = config_map[koji_cmd]
1523 except IndexError:
1524 raise ValueError('Could not find config file for %s' % koji_cmd)
1526 base_name = os.path.basename(koji_cmd)
1527 if os.access(config_file, os.F_OK):
1528 f = open(config_file)
1529 config = ConfigParser.ConfigParser()
1530 config.readfp(f)
1531 f.close()
1532 else:
1533 raise IOError('Configuration file %s missing or with wrong '
1534 'permissions' % config_file)
1536 if config.has_section(base_name):
1537 self.koji_options = {}
1538 session_options = {}
1539 server = None
1540 for name, value in config.items(base_name):
1541 if name in ('user', 'password', 'debug_xmlrpc', 'debug'):
1542 session_options[name] = value
1543 self.koji_options[name] = value
1544 self.session = koji.ClientSession(self.koji_options['server'],
1545 session_options)
1546 else:
1547 raise ValueError('Koji config file %s does not have a %s '
1548 'session' % (config_file, base_name))
1551 def get(self, src_package, dst_dir, rfilter=None, tag=None, build=None,
1552 arch=None):
1554 Download a list of packages from the build system.
1556 This will download all packages originated from source package [package]
1557 with given [tag] or [build] for the architecture reported by the
1558 machine.
1560 @param src_package: Source package name.
1561 @param dst_dir: Destination directory for the downloaded packages.
1562 @param rfilter: Regexp filter, only download the packages that match
1563 that particular filter.
1564 @param tag: Build system tag.
1565 @param build: Build system ID.
1566 @param arch: Package arch. Useful when you want to download noarch
1567 packages.
1569 @return: List of paths with the downloaded rpm packages.
1571 if build and build.isdigit():
1572 build = int(build)
1574 if tag and build:
1575 logging.info("Both tag and build parameters provided, ignoring tag "
1576 "parameter...")
1578 if not tag and not build:
1579 raise ValueError("Koji install selected but neither koji_tag "
1580 "nor koji_build parameters provided. Please "
1581 "provide an appropriate tag or build name.")
1583 if not build:
1584 builds = self.session.listTagged(tag, latest=True, inherit=True,
1585 package=src_package)
1586 if not builds:
1587 raise ValueError("Tag %s has no builds of %s" % (tag,
1588 src_package))
1589 info = builds[0]
1590 else:
1591 info = self.session.getBuild(build)
1593 if info is None:
1594 raise ValueError('No such brew/koji build: %s' % build)
1596 if arch is None:
1597 arch = utils.get_arch()
1599 rpms = self.session.listRPMs(buildID=info['id'],
1600 arches=arch)
1601 if not rpms:
1602 raise ValueError("No %s packages available for %s" %
1603 arch, koji.buildLabel(info))
1605 rpm_paths = []
1606 for rpm in rpms:
1607 rpm_name = koji.pathinfo.rpm(rpm)
1608 url = ("%s/%s/%s/%s/%s" % (self.koji_options['pkgurl'],
1609 info['package_name'],
1610 info['version'], info['release'],
1611 rpm_name))
1612 if rfilter:
1613 filter_regexp = re.compile(rfilter, re.IGNORECASE)
1614 if filter_regexp.match(os.path.basename(rpm_name)):
1615 download = True
1616 else:
1617 download = False
1618 else:
1619 download = True
1621 if download:
1622 r = utils.get_file(url,
1623 os.path.join(dst_dir, os.path.basename(url)))
1624 rpm_paths.append(r)
1626 return rpm_paths
1629 def umount(src, mount_point, type):
1631 Umount the src mounted in mount_point.
1633 @src: mount source
1634 @mount_point: mount point
1635 @type: file system type
1638 mount_string = "%s %s %s" % (src, mount_point, type)
1639 if mount_string in file("/etc/mtab").read():
1640 umount_cmd = "umount %s" % mount_point
1641 try:
1642 utils.system(umount_cmd)
1643 return True
1644 except error.CmdError:
1645 return False
1646 else:
1647 logging.debug("%s is not mounted under %s" % (src, mount_point))
1648 return True
1651 def mount(src, mount_point, type, perm="rw"):
1653 Mount the src into mount_point of the host.
1655 @src: mount source
1656 @mount_point: mount point
1657 @type: file system type
1658 @perm: mount premission
1660 umount(src, mount_point, type)
1661 mount_string = "%s %s %s %s" % (src, mount_point, type, perm)
1663 if mount_string in file("/etc/mtab").read():
1664 logging.debug("%s is already mounted in %s with %s" %
1665 (src, mount_point, perm))
1666 return True
1668 mount_cmd = "mount -t %s %s %s -o %s" % (type, src, mount_point, perm)
1669 try:
1670 utils.system(mount_cmd)
1671 except error.CmdError:
1672 return False
1674 logging.debug("Verify the mount through /etc/mtab")
1675 if mount_string in file("/etc/mtab").read():
1676 logging.debug("%s is successfully mounted" % src)
1677 return True
1678 else:
1679 logging.error("Can't find mounted NFS share - /etc/mtab contents \n%s" %
1680 file("/etc/mtab").read())
1681 return False