Virt: re-write and refactor of cpu_vendor() utility function
[autotest-zwu.git] / client / virt / virt_utils.py
blobf959825afb42ab3c14a6bf10cf71a5db17e19867
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, threading, sys, UserDict, inspect
9 import struct
10 from autotest_lib.client.bin import utils, os_dep
11 from autotest_lib.client.common_lib import error, logging_config
12 import rss_client, aexpect
13 try:
14 import koji
15 KOJI_INSTALLED = True
16 except ImportError:
17 KOJI_INSTALLED = False
19 # From include/linux/sockios.h
20 SIOCSIFHWADDR = 0x8924
21 SIOCGIFHWADDR = 0x8927
22 SIOCSIFFLAGS = 0x8914
23 SIOCGIFINDEX = 0x8933
24 SIOCBRADDIF = 0x89a2
25 # From linux/include/linux/if_tun.h
26 TUNSETIFF = 0x400454ca
27 TUNGETIFF = 0x800454d2
28 TUNGETFEATURES = 0x800454cf
29 IFF_UP = 0x1
30 IFF_TAP = 0x0002
31 IFF_NO_PI = 0x1000
32 IFF_VNET_HDR = 0x4000
34 def _lock_file(filename):
35 f = open(filename, "w")
36 fcntl.lockf(f, fcntl.LOCK_EX)
37 return f
40 def _unlock_file(f):
41 fcntl.lockf(f, fcntl.LOCK_UN)
42 f.close()
45 def is_vm(obj):
46 """
47 Tests whether a given object is a VM object.
49 @param obj: Python object.
50 """
51 return obj.__class__.__name__ == "VM"
54 class NetError(Exception):
55 pass
58 class TAPModuleError(NetError):
59 def __init__(self, devname, action="open", details=None):
60 NetError.__init__(self, devname)
61 self.devname = devname
62 self.details = details
64 def __str__(self):
65 e_msg = "Can't %s %s" % (self.action, self.devname)
66 if self.details is not None:
67 e_msg += " : %s" % self.details
68 return e_msg
71 class TAPNotExistError(NetError):
72 def __init__(self, ifname):
73 NetError.__init__(self, ifname)
74 self.ifname = ifname
76 def __str__(self):
77 return "Interface %s does not exist" % self.ifname
80 class TAPCreationError(NetError):
81 def __init__(self, ifname, details=None):
82 NetError.__init__(self, ifname, details)
83 self.ifname = ifname
84 self.details = details
86 def __str__(self):
87 e_msg = "Cannot create TAP device %s" % self.ifname
88 if self.details is not None:
89 e_msg += ": %s" % self.details
90 return e_msg
93 class TAPBringUpError(NetError):
94 def __init__(self, ifname):
95 NetError.__init__(self, ifname)
96 self.ifname = ifname
98 def __str__(self):
99 return "Cannot bring up TAP %s" % self.ifname
102 class BRAddIfError(NetError):
103 def __init__(self, ifname, brname, details):
104 NetError.__init__(self, ifname, brname, details)
105 self.ifname = ifname
106 self.brname = brname
107 self.details = details
109 def __str__(self):
110 return ("Can not add if %s to bridge %s: %s" %
111 (self.ifname, self.brname, self.details))
114 class HwAddrSetError(NetError):
115 def __init__(self, ifname, mac):
116 NetError.__init__(self, ifname, mac)
117 self.ifname = ifname
118 self.mac = mac
120 def __str__(self):
121 return "Can not set mac %s to interface %s" % (self.mac, self.ifname)
124 class HwAddrGetError(NetError):
125 def __init__(self, ifname):
126 NetError.__init__(self, ifname)
127 self.ifname = ifname
129 def __str__(self):
130 return "Can not get mac of interface %s" % self.ifname
133 class Env(UserDict.IterableUserDict):
135 A dict-like object containing global objects used by tests.
137 def __init__(self, filename=None, version=0):
139 Create an empty Env object or load an existing one from a file.
141 If the version recorded in the file is lower than version, or if some
142 error occurs during unpickling, or if filename is not supplied,
143 create an empty Env object.
145 @param filename: Path to an env file.
146 @param version: Required env version (int).
148 UserDict.IterableUserDict.__init__(self)
149 empty = {"version": version}
150 if filename:
151 self._filename = filename
152 try:
153 f = open(filename, "r")
154 env = cPickle.load(f)
155 f.close()
156 if env.get("version", 0) >= version:
157 self.data = env
158 else:
159 logging.warn("Incompatible env file found. Not using it.")
160 self.data = empty
161 # Almost any exception can be raised during unpickling, so let's
162 # catch them all
163 except Exception, e:
164 logging.warn(e)
165 self.data = empty
166 else:
167 self.data = empty
170 def save(self, filename=None):
172 Pickle the contents of the Env object into a file.
174 @param filename: Filename to pickle the dict into. If not supplied,
175 use the filename from which the dict was loaded.
177 filename = filename or self._filename
178 f = open(filename, "w")
179 cPickle.dump(self.data, f)
180 f.close()
183 def get_all_vms(self):
185 Return a list of all VM objects in this Env object.
187 return [o for o in self.values() if is_vm(o)]
190 def get_vm(self, name):
192 Return a VM object by its name.
194 @param name: VM name.
196 return self.get("vm__%s" % name)
199 def register_vm(self, name, vm):
201 Register a VM in this Env object.
203 @param name: VM name.
204 @param vm: VM object.
206 self["vm__%s" % name] = vm
209 def unregister_vm(self, name):
211 Remove a given VM.
213 @param name: VM name.
215 del self["vm__%s" % name]
218 def register_installer(self, installer):
220 Register a installer that was just run
222 The installer will be available for other tests, so that
223 information about the installed KVM modules and qemu-kvm can be used by
224 them.
226 self['last_installer'] = installer
229 def previous_installer(self):
231 Return the last installer that was registered
233 return self.get('last_installer')
236 class Params(UserDict.IterableUserDict):
238 A dict-like object passed to every test.
240 def objects(self, key):
242 Return the names of objects defined using a given key.
244 @param key: The name of the key whose value lists the objects
245 (e.g. 'nics').
247 return self.get(key, "").split()
250 def object_params(self, obj_name):
252 Return a dict-like object containing the parameters of an individual
253 object.
255 This method behaves as follows: the suffix '_' + obj_name is removed
256 from all key names that have it. Other key names are left unchanged.
257 The values of keys with the suffix overwrite the values of their
258 suffixless versions.
260 @param obj_name: The name of the object (objects are listed by the
261 objects() method).
263 suffix = "_" + obj_name
264 new_dict = self.copy()
265 for key in self:
266 if key.endswith(suffix):
267 new_key = key.split(suffix)[0]
268 new_dict[new_key] = self[key]
269 return new_dict
272 # Functions related to MAC/IP addresses
274 def _open_mac_pool(lock_mode):
275 lock_file = open("/tmp/mac_lock", "w+")
276 fcntl.lockf(lock_file, lock_mode)
277 pool = shelve.open("/tmp/address_pool")
278 return pool, lock_file
281 def _close_mac_pool(pool, lock_file):
282 pool.close()
283 fcntl.lockf(lock_file, fcntl.LOCK_UN)
284 lock_file.close()
287 def _generate_mac_address_prefix(mac_pool):
289 Generate a random MAC address prefix and add it to the MAC pool dictionary.
290 If there's a MAC prefix there already, do not update the MAC pool and just
291 return what's in there. By convention we will set KVM autotest MAC
292 addresses to start with 0x9a.
294 @param mac_pool: The MAC address pool object.
295 @return: The MAC address prefix.
297 if "prefix" in mac_pool:
298 prefix = mac_pool["prefix"]
299 logging.debug("Used previously generated MAC address prefix for this "
300 "host: %s", prefix)
301 else:
302 r = random.SystemRandom()
303 prefix = "9a:%02x:%02x:%02x:" % (r.randint(0x00, 0xff),
304 r.randint(0x00, 0xff),
305 r.randint(0x00, 0xff))
306 mac_pool["prefix"] = prefix
307 logging.debug("Generated MAC address prefix for this host: %s", prefix)
308 return prefix
311 def generate_mac_address(vm_instance, nic_index):
313 Randomly generate a MAC address and add it to the MAC address pool.
315 Try to generate a MAC address based on a randomly generated MAC address
316 prefix and add it to a persistent dictionary.
317 key = VM instance + NIC index, value = MAC address
318 e.g. {'20100310-165222-Wt7l:0': '9a:5d:94:6a:9b:f9'}
320 @param vm_instance: The instance attribute of a VM.
321 @param nic_index: The index of the NIC.
322 @return: MAC address string.
324 mac_pool, lock_file = _open_mac_pool(fcntl.LOCK_EX)
325 key = "%s:%s" % (vm_instance, nic_index)
326 if key in mac_pool:
327 mac = mac_pool[key]
328 else:
329 prefix = _generate_mac_address_prefix(mac_pool)
330 r = random.SystemRandom()
331 while key not in mac_pool:
332 mac = prefix + "%02x:%02x" % (r.randint(0x00, 0xff),
333 r.randint(0x00, 0xff))
334 if mac in mac_pool.values():
335 continue
336 mac_pool[key] = mac
337 logging.debug("Generated MAC address for NIC %s: %s", key, mac)
338 _close_mac_pool(mac_pool, lock_file)
339 return mac
342 def free_mac_address(vm_instance, nic_index):
344 Remove a MAC address from the address pool.
346 @param vm_instance: The instance attribute of a VM.
347 @param nic_index: The index of the NIC.
349 mac_pool, lock_file = _open_mac_pool(fcntl.LOCK_EX)
350 key = "%s:%s" % (vm_instance, nic_index)
351 if key in mac_pool:
352 logging.debug("Freeing MAC address for NIC %s: %s", key, mac_pool[key])
353 del mac_pool[key]
354 _close_mac_pool(mac_pool, lock_file)
357 def set_mac_address(vm_instance, nic_index, mac):
359 Set a MAC address in the pool.
361 @param vm_instance: The instance attribute of a VM.
362 @param nic_index: The index of the NIC.
364 mac_pool, lock_file = _open_mac_pool(fcntl.LOCK_EX)
365 mac_pool["%s:%s" % (vm_instance, nic_index)] = mac
366 _close_mac_pool(mac_pool, lock_file)
369 def get_mac_address(vm_instance, nic_index):
371 Return a MAC address from the pool.
373 @param vm_instance: The instance attribute of a VM.
374 @param nic_index: The index of the NIC.
375 @return: MAC address string.
377 mac_pool, lock_file = _open_mac_pool(fcntl.LOCK_SH)
378 mac = mac_pool.get("%s:%s" % (vm_instance, nic_index))
379 _close_mac_pool(mac_pool, lock_file)
380 return mac
383 def verify_ip_address_ownership(ip, macs, timeout=10.0):
385 Use arping and the ARP cache to make sure a given IP address belongs to one
386 of the given MAC addresses.
388 @param ip: An IP address.
389 @param macs: A list or tuple of MAC addresses.
390 @return: True iff ip is assigned to a MAC address in macs.
392 # Compile a regex that matches the given IP address and any of the given
393 # MAC addresses
394 mac_regex = "|".join("(%s)" % mac for mac in macs)
395 regex = re.compile(r"\b%s\b.*\b(%s)\b" % (ip, mac_regex), re.IGNORECASE)
397 # Check the ARP cache
398 o = commands.getoutput("%s -n" % find_command("arp"))
399 if regex.search(o):
400 return True
402 # Get the name of the bridge device for arping
403 o = commands.getoutput("%s route get %s" % (find_command("ip"), ip))
404 dev = re.findall("dev\s+\S+", o, re.IGNORECASE)
405 if not dev:
406 return False
407 dev = dev[0].split()[-1]
409 # Send an ARP request
410 o = commands.getoutput("%s -f -c 3 -I %s %s" %
411 (find_command("arping"), dev, ip))
412 return bool(regex.search(o))
415 # Utility functions for dealing with external processes
417 def find_command(cmd):
418 for dir in ["/usr/local/sbin", "/usr/local/bin",
419 "/usr/sbin", "/usr/bin", "/sbin", "/bin"]:
420 file = os.path.join(dir, cmd)
421 if os.path.exists(file):
422 return file
423 raise ValueError('Missing command: %s' % cmd)
426 def pid_exists(pid):
428 Return True if a given PID exists.
430 @param pid: Process ID number.
432 try:
433 os.kill(pid, 0)
434 return True
435 except:
436 return False
439 def safe_kill(pid, signal):
441 Attempt to send a signal to a given process that may or may not exist.
443 @param signal: Signal number.
445 try:
446 os.kill(pid, signal)
447 return True
448 except:
449 return False
452 def kill_process_tree(pid, sig=signal.SIGKILL):
453 """Signal a process and all of its children.
455 If the process does not exist -- return.
457 @param pid: The pid of the process to signal.
458 @param sig: The signal to send to the processes.
460 if not safe_kill(pid, signal.SIGSTOP):
461 return
462 children = commands.getoutput("ps --ppid=%d -o pid=" % pid).split()
463 for child in children:
464 kill_process_tree(int(child), sig)
465 safe_kill(pid, sig)
466 safe_kill(pid, signal.SIGCONT)
469 def get_latest_kvm_release_tag(release_listing):
471 Fetches the latest release tag for KVM.
473 @param release_listing: URL that contains a list of the Source Forge
474 KVM project files.
476 try:
477 release_page = utils.urlopen(release_listing)
478 data = release_page.read()
479 release_page.close()
480 rx = re.compile("kvm-(\d+).tar.gz", re.IGNORECASE)
481 matches = rx.findall(data)
482 # In all regexp matches to something that looks like a release tag,
483 # get the largest integer. That will be our latest release tag.
484 latest_tag = max(int(x) for x in matches)
485 return str(latest_tag)
486 except Exception, e:
487 message = "Could not fetch latest KVM release tag: %s" % str(e)
488 logging.error(message)
489 raise error.TestError(message)
492 def get_git_branch(repository, branch, srcdir, commit=None, lbranch=None):
494 Retrieves a given git code repository.
496 @param repository: Git repository URL
498 logging.info("Fetching git [REP '%s' BRANCH '%s' COMMIT '%s'] -> %s",
499 repository, branch, commit, srcdir)
500 if not os.path.exists(srcdir):
501 os.makedirs(srcdir)
502 os.chdir(srcdir)
504 if os.path.exists(".git"):
505 utils.system("git reset --hard")
506 else:
507 utils.system("git init")
509 if not lbranch:
510 lbranch = branch
512 utils.system("git fetch -q -f -u -t %s %s:%s" %
513 (repository, branch, lbranch))
514 utils.system("git checkout %s" % lbranch)
515 if commit:
516 utils.system("git checkout %s" % commit)
518 h = utils.system_output('git log --pretty=format:"%H" -1')
519 try:
520 desc = "tag %s" % utils.system_output("git describe")
521 except error.CmdError:
522 desc = "no tag found"
524 logging.info("Commit hash for %s is %s (%s)", repository, h.strip(), desc)
525 return srcdir
528 def check_kvm_source_dir(source_dir):
530 Inspects the kvm source directory and verifies its disposition. In some
531 occasions build may be dependant on the source directory disposition.
532 The reason why the return codes are numbers is that we might have more
533 changes on the source directory layout, so it's not scalable to just use
534 strings like 'old_repo', 'new_repo' and such.
536 @param source_dir: Source code path that will be inspected.
538 os.chdir(source_dir)
539 has_qemu_dir = os.path.isdir('qemu')
540 has_kvm_dir = os.path.isdir('kvm')
541 if has_qemu_dir:
542 logging.debug("qemu directory detected, source dir layout 1")
543 return 1
544 if has_kvm_dir and not has_qemu_dir:
545 logging.debug("kvm directory detected, source dir layout 2")
546 return 2
547 else:
548 raise error.TestError("Unknown source dir layout, cannot proceed.")
551 # Functions and classes used for logging into guests and transferring files
553 class LoginError(Exception):
554 def __init__(self, msg, output):
555 Exception.__init__(self, msg, output)
556 self.msg = msg
557 self.output = output
559 def __str__(self):
560 return "%s (output: %r)" % (self.msg, self.output)
563 class LoginAuthenticationError(LoginError):
564 pass
567 class LoginTimeoutError(LoginError):
568 def __init__(self, output):
569 LoginError.__init__(self, "Login timeout expired", output)
572 class LoginProcessTerminatedError(LoginError):
573 def __init__(self, status, output):
574 LoginError.__init__(self, None, output)
575 self.status = status
577 def __str__(self):
578 return ("Client process terminated (status: %s, output: %r)" %
579 (self.status, self.output))
582 class LoginBadClientError(LoginError):
583 def __init__(self, client):
584 LoginError.__init__(self, None, None)
585 self.client = client
587 def __str__(self):
588 return "Unknown remote shell client: %r" % self.client
591 class SCPError(Exception):
592 def __init__(self, msg, output):
593 Exception.__init__(self, msg, output)
594 self.msg = msg
595 self.output = output
597 def __str__(self):
598 return "%s (output: %r)" % (self.msg, self.output)
601 class SCPAuthenticationError(SCPError):
602 pass
605 class SCPAuthenticationTimeoutError(SCPAuthenticationError):
606 def __init__(self, output):
607 SCPAuthenticationError.__init__(self, "Authentication timeout expired",
608 output)
611 class SCPTransferTimeoutError(SCPError):
612 def __init__(self, output):
613 SCPError.__init__(self, "Transfer timeout expired", output)
616 class SCPTransferFailedError(SCPError):
617 def __init__(self, status, output):
618 SCPError.__init__(self, None, output)
619 self.status = status
621 def __str__(self):
622 return ("SCP transfer failed (status: %s, output: %r)" %
623 (self.status, self.output))
626 def _remote_login(session, username, password, prompt, timeout=10):
628 Log into a remote host (guest) using SSH or Telnet. Wait for questions
629 and provide answers. If timeout expires while waiting for output from the
630 child (e.g. a password prompt or a shell prompt) -- fail.
632 @brief: Log into a remote host (guest) using SSH or Telnet.
634 @param session: An Expect or ShellSession instance to operate on
635 @param username: The username to send in reply to a login prompt
636 @param password: The password to send in reply to a password prompt
637 @param prompt: The shell prompt that indicates a successful login
638 @param timeout: The maximal time duration (in seconds) to wait for each
639 step of the login procedure (i.e. the "Are you sure" prompt, the
640 password prompt, the shell prompt, etc)
641 @raise LoginTimeoutError: If timeout expires
642 @raise LoginAuthenticationError: If authentication fails
643 @raise LoginProcessTerminatedError: If the client terminates during login
644 @raise LoginError: If some other error occurs
646 password_prompt_count = 0
647 login_prompt_count = 0
649 while True:
650 try:
651 match, text = session.read_until_last_line_matches(
652 [r"[Aa]re you sure", r"[Pp]assword:\s*$", r"[Ll]ogin:\s*$",
653 r"[Cc]onnection.*closed", r"[Cc]onnection.*refused",
654 r"[Pp]lease wait", r"[Ww]arning", prompt],
655 timeout=timeout, internal_timeout=0.5)
656 if match == 0: # "Are you sure you want to continue connecting"
657 logging.debug("Got 'Are you sure...'; sending 'yes'")
658 session.sendline("yes")
659 continue
660 elif match == 1: # "password:"
661 if password_prompt_count == 0:
662 logging.debug("Got password prompt; sending '%s'", password)
663 session.sendline(password)
664 password_prompt_count += 1
665 continue
666 else:
667 raise LoginAuthenticationError("Got password prompt twice",
668 text)
669 elif match == 2: # "login:"
670 if login_prompt_count == 0 and password_prompt_count == 0:
671 logging.debug("Got username prompt; sending '%s'", username)
672 session.sendline(username)
673 login_prompt_count += 1
674 continue
675 else:
676 if login_prompt_count > 0:
677 msg = "Got username prompt twice"
678 else:
679 msg = "Got username prompt after password prompt"
680 raise LoginAuthenticationError(msg, text)
681 elif match == 3: # "Connection closed"
682 raise LoginError("Client said 'connection closed'", text)
683 elif match == 4: # "Connection refused"
684 raise LoginError("Client said 'connection refused'", text)
685 elif match == 5: # "Please wait"
686 logging.debug("Got 'Please wait'")
687 timeout = 30
688 continue
689 elif match == 6: # "Warning added RSA"
690 logging.debug("Got 'Warning added RSA to known host list")
691 continue
692 elif match == 7: # prompt
693 logging.debug("Got shell prompt -- logged in")
694 break
695 except aexpect.ExpectTimeoutError, e:
696 raise LoginTimeoutError(e.output)
697 except aexpect.ExpectProcessTerminatedError, e:
698 raise LoginProcessTerminatedError(e.status, e.output)
701 def remote_login(client, host, port, username, password, prompt, linesep="\n",
702 log_filename=None, timeout=10):
704 Log into a remote host (guest) using SSH/Telnet/Netcat.
706 @param client: The client to use ('ssh', 'telnet' or 'nc')
707 @param host: Hostname or IP address
708 @param port: Port to connect to
709 @param username: Username (if required)
710 @param password: Password (if required)
711 @param prompt: Shell prompt (regular expression)
712 @param linesep: The line separator to use when sending lines
713 (e.g. '\\n' or '\\r\\n')
714 @param log_filename: If specified, log all output to this file
715 @param timeout: The maximal time duration (in seconds) to wait for
716 each step of the login procedure (i.e. the "Are you sure" prompt
717 or the password prompt)
718 @raise LoginBadClientError: If an unknown client is requested
719 @raise: Whatever _remote_login() raises
720 @return: A ShellSession object.
722 if client == "ssh":
723 cmd = ("ssh -o UserKnownHostsFile=/dev/null "
724 "-o PreferredAuthentications=password -p %s %s@%s" %
725 (port, username, host))
726 elif client == "telnet":
727 cmd = "telnet -l %s %s %s" % (username, host, port)
728 elif client == "nc":
729 cmd = "nc %s %s" % (host, port)
730 else:
731 raise LoginBadClientError(client)
733 logging.debug("Trying to login with command '%s'", cmd)
734 session = aexpect.ShellSession(cmd, linesep=linesep, prompt=prompt)
735 try:
736 _remote_login(session, username, password, prompt, timeout)
737 except:
738 session.close()
739 raise
740 if log_filename:
741 session.set_output_func(log_line)
742 session.set_output_params((log_filename,))
743 return session
746 def wait_for_login(client, host, port, username, password, prompt, linesep="\n",
747 log_filename=None, timeout=240, internal_timeout=10):
749 Make multiple attempts to log into a remote host (guest) until one succeeds
750 or timeout expires.
752 @param timeout: Total time duration to wait for a successful login
753 @param internal_timeout: The maximal time duration (in seconds) to wait for
754 each step of the login procedure (e.g. the "Are you sure" prompt
755 or the password prompt)
756 @see: remote_login()
757 @raise: Whatever remote_login() raises
758 @return: A ShellSession object.
760 logging.debug("Attempting to log into %s:%s using %s (timeout %ds)",
761 host, port, client, timeout)
762 end_time = time.time() + timeout
763 while time.time() < end_time:
764 try:
765 return remote_login(client, host, port, username, password, prompt,
766 linesep, log_filename, internal_timeout)
767 except LoginError, e:
768 logging.debug(e)
769 time.sleep(2)
770 # Timeout expired; try one more time but don't catch exceptions
771 return remote_login(client, host, port, username, password, prompt,
772 linesep, log_filename, internal_timeout)
775 def _remote_scp(session, password_list, transfer_timeout=600, login_timeout=10):
777 Transfer file(s) to a remote host (guest) using SCP. Wait for questions
778 and provide answers. If login_timeout expires while waiting for output
779 from the child (e.g. a password prompt), fail. If transfer_timeout expires
780 while waiting for the transfer to complete, fail.
782 @brief: Transfer files using SCP, given a command line.
784 @param session: An Expect or ShellSession instance to operate on
785 @param password_list: Password list to send in reply to the password prompt
786 @param transfer_timeout: The time duration (in seconds) to wait for the
787 transfer to complete.
788 @param login_timeout: The maximal time duration (in seconds) to wait for
789 each step of the login procedure (i.e. the "Are you sure" prompt or
790 the password prompt)
791 @raise SCPAuthenticationError: If authentication fails
792 @raise SCPTransferTimeoutError: If the transfer fails to complete in time
793 @raise SCPTransferFailedError: If the process terminates with a nonzero
794 exit code
795 @raise SCPError: If some other error occurs
797 password_prompt_count = 0
798 timeout = login_timeout
799 authentication_done = False
801 scp_type = len(password_list)
803 while True:
804 try:
805 match, text = session.read_until_last_line_matches(
806 [r"[Aa]re you sure", r"[Pp]assword:\s*$", r"lost connection"],
807 timeout=timeout, internal_timeout=0.5)
808 if match == 0: # "Are you sure you want to continue connecting"
809 logging.debug("Got 'Are you sure...'; sending 'yes'")
810 session.sendline("yes")
811 continue
812 elif match == 1: # "password:"
813 if password_prompt_count == 0:
814 logging.debug("Got password prompt; sending '%s'" %
815 password_list[password_prompt_count])
816 session.sendline(password_list[password_prompt_count])
817 password_prompt_count += 1
818 timeout = transfer_timeout
819 if scp_type == 1:
820 authentication_done = True
821 continue
822 elif password_prompt_count == 1 and scp_type == 2:
823 logging.debug("Got password prompt; sending '%s'" %
824 password_list[password_prompt_count])
825 session.sendline(password_list[password_prompt_count])
826 password_prompt_count += 1
827 timeout = transfer_timeout
828 authentication_done = True
829 continue
830 else:
831 raise SCPAuthenticationError("Got password prompt twice",
832 text)
833 elif match == 2: # "lost connection"
834 raise SCPError("SCP client said 'lost connection'", text)
835 except aexpect.ExpectTimeoutError, e:
836 if authentication_done:
837 raise SCPTransferTimeoutError(e.output)
838 else:
839 raise SCPAuthenticationTimeoutError(e.output)
840 except aexpect.ExpectProcessTerminatedError, e:
841 if e.status == 0:
842 logging.debug("SCP process terminated with status 0")
843 break
844 else:
845 raise SCPTransferFailedError(e.status, e.output)
848 def remote_scp(command, password_list, log_filename=None, transfer_timeout=600,
849 login_timeout=10):
851 Transfer file(s) to a remote host (guest) using SCP.
853 @brief: Transfer files using SCP, given a command line.
855 @param command: The command to execute
856 (e.g. "scp -r foobar root@localhost:/tmp/").
857 @param password_list: Password list to send in reply to a password prompt.
858 @param log_filename: If specified, log all output to this file
859 @param transfer_timeout: The time duration (in seconds) to wait for the
860 transfer to complete.
861 @param login_timeout: The maximal time duration (in seconds) to wait for
862 each step of the login procedure (i.e. the "Are you sure" prompt
863 or the password prompt)
864 @raise: Whatever _remote_scp() raises
866 logging.debug("Trying to SCP with command '%s', timeout %ss",
867 command, transfer_timeout)
868 if log_filename:
869 output_func = log_line
870 output_params = (log_filename,)
871 else:
872 output_func = None
873 output_params = ()
874 session = aexpect.Expect(command,
875 output_func=output_func,
876 output_params=output_params)
877 try:
878 _remote_scp(session, password_list, transfer_timeout, login_timeout)
879 finally:
880 session.close()
883 def scp_to_remote(host, port, username, password, local_path, remote_path,
884 log_filename=None, timeout=600):
886 Copy files to a remote host (guest) through scp.
888 @param host: Hostname or IP address
889 @param username: Username (if required)
890 @param password: Password (if required)
891 @param local_path: Path on the local machine where we are copying from
892 @param remote_path: Path on the remote machine where we are copying to
893 @param log_filename: If specified, log all output to this file
894 @param timeout: The time duration (in seconds) to wait for the transfer
895 to complete.
896 @raise: Whatever remote_scp() raises
898 command = ("scp -v -o UserKnownHostsFile=/dev/null "
899 "-o PreferredAuthentications=password -r -P %s %s %s@%s:%s" %
900 (port, local_path, username, host, remote_path))
901 password_list = []
902 password_list.append(password)
903 return remote_scp(command, password_list, log_filename, timeout)
907 def scp_from_remote(host, port, username, password, remote_path, local_path,
908 log_filename=None, timeout=600):
910 Copy files from a remote host (guest).
912 @param host: Hostname or IP address
913 @param username: Username (if required)
914 @param password: Password (if required)
915 @param local_path: Path on the local machine where we are copying from
916 @param remote_path: Path on the remote machine where we are copying to
917 @param log_filename: If specified, log all output to this file
918 @param timeout: The time duration (in seconds) to wait for the transfer
919 to complete.
920 @raise: Whatever remote_scp() raises
922 command = ("scp -v -o UserKnownHostsFile=/dev/null "
923 "-o PreferredAuthentications=password -r -P %s %s@%s:%s %s" %
924 (port, username, host, remote_path, local_path))
925 password_list = []
926 password_list.append(password)
927 remote_scp(command, password_list, log_filename, timeout)
930 def scp_between_remotes(src, dst, port, s_passwd, d_passwd, s_name, d_name,
931 s_path, d_path, log_filename=None, timeout=600):
933 Copy files from a remote host (guest) to another remote host (guest).
935 @param src/dst: Hostname or IP address of src and dst
936 @param s_name/d_name: Username (if required)
937 @param s_passwd/d_passwd: Password (if required)
938 @param s_path/d_path: Path on the remote machine where we are copying
939 from/to
940 @param log_filename: If specified, log all output to this file
941 @param timeout: The time duration (in seconds) to wait for the transfer
942 to complete.
944 @return: True on success and False on failure.
946 command = ("scp -v -o UserKnownHostsFile=/dev/null -o "
947 "PreferredAuthentications=password -r -P %s %s@%s:%s %s@%s:%s" %
948 (port, s_name, src, s_path, d_name, dst, d_path))
949 password_list = []
950 password_list.append(s_passwd)
951 password_list.append(d_passwd)
952 return remote_scp(command, password_list, log_filename, timeout)
955 def copy_files_to(address, client, username, password, port, local_path,
956 remote_path, log_filename=None, verbose=False, timeout=600):
958 Copy files to a remote host (guest) using the selected client.
960 @param client: Type of transfer client
961 @param username: Username (if required)
962 @param password: Password (if requried)
963 @param local_path: Path on the local machine where we are copying from
964 @param remote_path: Path on the remote machine where we are copying to
965 @param address: Address of remote host(guest)
966 @param log_filename: If specified, log all output to this file (SCP only)
967 @param verbose: If True, log some stats using logging.debug (RSS only)
968 @param timeout: The time duration (in seconds) to wait for the transfer to
969 complete.
970 @raise: Whatever remote_scp() raises
972 if client == "scp":
973 scp_to_remote(address, port, username, password, local_path,
974 remote_path, log_filename, timeout)
975 elif client == "rss":
976 log_func = None
977 if verbose:
978 log_func = logging.debug
979 c = rss_client.FileUploadClient(address, port, log_func)
980 c.upload(local_path, remote_path, timeout)
981 c.close()
984 def copy_files_from(address, client, username, password, port, remote_path,
985 local_path, log_filename=None, verbose=False, timeout=600):
987 Copy files from a remote host (guest) using the selected client.
989 @param client: Type of transfer client
990 @param username: Username (if required)
991 @param password: Password (if requried)
992 @param remote_path: Path on the remote machine where we are copying from
993 @param local_path: Path on the local machine where we are copying to
994 @param address: Address of remote host(guest)
995 @param log_filename: If specified, log all output to this file (SCP only)
996 @param verbose: If True, log some stats using logging.debug (RSS only)
997 @param timeout: The time duration (in seconds) to wait for the transfer to
998 complete.
999 @raise: Whatever remote_scp() raises
1001 if client == "scp":
1002 scp_from_remote(address, port, username, password, remote_path,
1003 local_path, log_filename, timeout)
1004 elif client == "rss":
1005 log_func = None
1006 if verbose:
1007 log_func = logging.debug
1008 c = rss_client.FileDownloadClient(address, port, log_func)
1009 c.download(remote_path, local_path, timeout)
1010 c.close()
1013 # The following are utility functions related to ports.
1015 def is_port_free(port, address):
1017 Return True if the given port is available for use.
1019 @param port: Port number
1021 try:
1022 s = socket.socket()
1023 #s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
1024 if address == "localhost":
1025 s.bind(("localhost", port))
1026 free = True
1027 else:
1028 s.connect((address, port))
1029 free = False
1030 except socket.error:
1031 if address == "localhost":
1032 free = False
1033 else:
1034 free = True
1035 s.close()
1036 return free
1039 def find_free_port(start_port, end_port, address="localhost"):
1041 Return a host free port in the range [start_port, end_port].
1043 @param start_port: First port that will be checked.
1044 @param end_port: Port immediately after the last one that will be checked.
1046 for i in range(start_port, end_port):
1047 if is_port_free(i, address):
1048 return i
1049 return None
1052 def find_free_ports(start_port, end_port, count, address="localhost"):
1054 Return count of host free ports in the range [start_port, end_port].
1056 @count: Initial number of ports known to be free in the range.
1057 @param start_port: First port that will be checked.
1058 @param end_port: Port immediately after the last one that will be checked.
1060 ports = []
1061 i = start_port
1062 while i < end_port and count > 0:
1063 if is_port_free(i, address):
1064 ports.append(i)
1065 count -= 1
1066 i += 1
1067 return ports
1070 # An easy way to log lines to files when the logging system can't be used
1072 _open_log_files = {}
1073 _log_file_dir = "/tmp"
1076 def log_line(filename, line):
1078 Write a line to a file. '\n' is appended to the line.
1080 @param filename: Path of file to write to, either absolute or relative to
1081 the dir set by set_log_file_dir().
1082 @param line: Line to write.
1084 global _open_log_files, _log_file_dir
1085 if filename not in _open_log_files:
1086 path = get_path(_log_file_dir, filename)
1087 try:
1088 os.makedirs(os.path.dirname(path))
1089 except OSError:
1090 pass
1091 _open_log_files[filename] = open(path, "w")
1092 timestr = time.strftime("%Y-%m-%d %H:%M:%S")
1093 _open_log_files[filename].write("%s: %s\n" % (timestr, line))
1094 _open_log_files[filename].flush()
1097 def set_log_file_dir(dir):
1099 Set the base directory for log files created by log_line().
1101 @param dir: Directory for log files.
1103 global _log_file_dir
1104 _log_file_dir = dir
1107 # The following are miscellaneous utility functions.
1109 def get_path(base_path, user_path):
1111 Translate a user specified path to a real path.
1112 If user_path is relative, append it to base_path.
1113 If user_path is absolute, return it as is.
1115 @param base_path: The base path of relative user specified paths.
1116 @param user_path: The user specified path.
1118 if os.path.isabs(user_path):
1119 return user_path
1120 else:
1121 return os.path.join(base_path, user_path)
1124 def generate_random_string(length):
1126 Return a random string using alphanumeric characters.
1128 @length: length of the string that will be generated.
1130 r = random.SystemRandom()
1131 str = ""
1132 chars = string.letters + string.digits
1133 while length > 0:
1134 str += r.choice(chars)
1135 length -= 1
1136 return str
1138 def generate_random_id():
1140 Return a random string suitable for use as a qemu id.
1142 return "id" + generate_random_string(6)
1145 def generate_tmp_file_name(file, ext=None, dir='/tmp/'):
1147 Returns a temporary file name. The file is not created.
1149 while True:
1150 file_name = (file + '-' + time.strftime("%Y%m%d-%H%M%S-") +
1151 generate_random_string(4))
1152 if ext:
1153 file_name += '.' + ext
1154 file_name = os.path.join(dir, file_name)
1155 if not os.path.exists(file_name):
1156 break
1158 return file_name
1161 def format_str_for_message(str):
1163 Format str so that it can be appended to a message.
1164 If str consists of one line, prefix it with a space.
1165 If str consists of multiple lines, prefix it with a newline.
1167 @param str: string that will be formatted.
1169 lines = str.splitlines()
1170 num_lines = len(lines)
1171 str = "\n".join(lines)
1172 if num_lines == 0:
1173 return ""
1174 elif num_lines == 1:
1175 return " " + str
1176 else:
1177 return "\n" + str
1180 def wait_for(func, timeout, first=0.0, step=1.0, text=None):
1182 If func() evaluates to True before timeout expires, return the
1183 value of func(). Otherwise return None.
1185 @brief: Wait until func() evaluates to True.
1187 @param timeout: Timeout in seconds
1188 @param first: Time to sleep before first attempt
1189 @param steps: Time to sleep between attempts in seconds
1190 @param text: Text to print while waiting, for debug purposes
1192 start_time = time.time()
1193 end_time = time.time() + timeout
1195 time.sleep(first)
1197 while time.time() < end_time:
1198 if text:
1199 logging.debug("%s (%f secs)", text, (time.time() - start_time))
1201 output = func()
1202 if output:
1203 return output
1205 time.sleep(step)
1207 logging.debug("Timeout elapsed")
1208 return None
1211 def get_hash_from_file(hash_path, dvd_basename):
1213 Get the a hash from a given DVD image from a hash file
1214 (Hash files are usually named MD5SUM or SHA1SUM and are located inside the
1215 download directories of the DVDs)
1217 @param hash_path: Local path to a hash file.
1218 @param cd_image: Basename of a CD image
1220 hash_file = open(hash_path, 'r')
1221 for line in hash_file.readlines():
1222 if dvd_basename in line:
1223 return line.split()[0]
1226 def run_tests(parser, job):
1228 Runs the sequence of KVM tests based on the list of dictionaries
1229 generated by the configuration system, handling dependencies.
1231 @param parser: Config parser object.
1232 @param job: Autotest job object.
1234 @return: True, if all tests ran passed, False if any of them failed.
1236 for i, d in enumerate(parser.get_dicts()):
1237 logging.info("Test %4d: %s" % (i + 1, d["shortname"]))
1239 status_dict = {}
1240 failed = False
1242 for dict in parser.get_dicts():
1243 if dict.get("skip") == "yes":
1244 continue
1245 dependencies_satisfied = True
1246 for dep in dict.get("dep"):
1247 for test_name in status_dict.keys():
1248 if not dep in test_name:
1249 continue
1250 # So the only really non-fatal state is WARN,
1251 # All the others make it not safe to proceed with dependency
1252 # execution
1253 if status_dict[test_name] not in ['GOOD', 'WARN']:
1254 dependencies_satisfied = False
1255 break
1256 test_iterations = int(dict.get("iterations", 1))
1257 test_tag = dict.get("shortname")
1259 if dependencies_satisfied:
1260 # Setting up profilers during test execution.
1261 profilers = dict.get("profilers", "").split()
1262 for profiler in profilers:
1263 job.profilers.add(profiler)
1264 # We need only one execution, profiled, hence we're passing
1265 # the profile_only parameter to job.run_test().
1266 profile_only = bool(profilers) or None
1267 current_status = job.run_test_detail(dict.get("vm_type"),
1268 params=dict,
1269 tag=test_tag,
1270 iterations=test_iterations,
1271 profile_only=profile_only)
1272 for profiler in profilers:
1273 job.profilers.delete(profiler)
1274 else:
1275 # We will force the test to fail as TestNA during preprocessing
1276 dict['dependency_failed'] = 'yes'
1277 current_status = job.run_test_detail(dict.get("vm_type"),
1278 params=dict,
1279 tag=test_tag,
1280 iterations=test_iterations)
1282 if not current_status:
1283 failed = True
1284 status_dict[dict.get("name")] = current_status
1286 return not failed
1289 def display_attributes(instance):
1291 Inspects a given class instance attributes and displays them, convenient
1292 for debugging.
1294 logging.debug("Attributes set:")
1295 for member in inspect.getmembers(instance):
1296 name, value = member
1297 attribute = getattr(instance, name)
1298 if not (name.startswith("__") or callable(attribute) or not value):
1299 logging.debug(" %s: %s", name, value)
1302 def get_full_pci_id(pci_id):
1304 Get full PCI ID of pci_id.
1306 @param pci_id: PCI ID of a device.
1308 cmd = "lspci -D | awk '/%s/ {print $1}'" % pci_id
1309 status, full_id = commands.getstatusoutput(cmd)
1310 if status != 0:
1311 return None
1312 return full_id
1315 def get_vendor_from_pci_id(pci_id):
1317 Check out the device vendor ID according to pci_id.
1319 @param pci_id: PCI ID of a device.
1321 cmd = "lspci -n | awk '/%s/ {print $3}'" % pci_id
1322 return re.sub(":", " ", commands.getoutput(cmd))
1325 def get_cpu_flags():
1327 Returns a list of the CPU flags
1329 flags_re = re.compile(r'^flags\s*:(.*)')
1330 for line in open('/proc/cpuinfo').readlines():
1331 match = flags_re.match(line)
1332 if match:
1333 return match.groups()[0].split()
1334 return []
1337 def get_cpu_vendor(cpu_flags=[], verbose=True):
1339 Returns the name of the CPU vendor, either intel, amd or unknown
1341 if not cpu_flags:
1342 cpu_flags = get_cpu_flags()
1344 if 'vmx' in cpu_flags:
1345 vendor = 'intel'
1346 elif 'svm' in cpu_flags:
1347 vendor = 'amd'
1348 else:
1349 vendor = 'unknown'
1351 if verbose:
1352 logging.debug("Detected CPU vendor as '%s'", vendor)
1353 return vendor
1356 class Thread(threading.Thread):
1358 Run a function in a background thread.
1360 def __init__(self, target, args=(), kwargs={}):
1362 Initialize the instance.
1364 @param target: Function to run in the thread.
1365 @param args: Arguments to pass to target.
1366 @param kwargs: Keyword arguments to pass to target.
1368 threading.Thread.__init__(self)
1369 self._target = target
1370 self._args = args
1371 self._kwargs = kwargs
1374 def run(self):
1376 Run target (passed to the constructor). No point in calling this
1377 function directly. Call start() to make this function run in a new
1378 thread.
1380 self._e = None
1381 self._retval = None
1382 try:
1383 try:
1384 self._retval = self._target(*self._args, **self._kwargs)
1385 except:
1386 self._e = sys.exc_info()
1387 raise
1388 finally:
1389 # Avoid circular references (start() may be called only once so
1390 # it's OK to delete these)
1391 del self._target, self._args, self._kwargs
1394 def join(self, timeout=None, suppress_exception=False):
1396 Join the thread. If target raised an exception, re-raise it.
1397 Otherwise, return the value returned by target.
1399 @param timeout: Timeout value to pass to threading.Thread.join().
1400 @param suppress_exception: If True, don't re-raise the exception.
1402 threading.Thread.join(self, timeout)
1403 try:
1404 if self._e:
1405 if not suppress_exception:
1406 # Because the exception was raised in another thread, we
1407 # need to explicitly insert the current context into it
1408 s = error.exception_context(self._e[1])
1409 s = error.join_contexts(error.get_context(), s)
1410 error.set_exception_context(self._e[1], s)
1411 raise self._e[0], self._e[1], self._e[2]
1412 else:
1413 return self._retval
1414 finally:
1415 # Avoid circular references (join() may be called multiple times
1416 # so we can't delete these)
1417 self._e = None
1418 self._retval = None
1421 def parallel(targets):
1423 Run multiple functions in parallel.
1425 @param targets: A sequence of tuples or functions. If it's a sequence of
1426 tuples, each tuple will be interpreted as (target, args, kwargs) or
1427 (target, args) or (target,) depending on its length. If it's a
1428 sequence of functions, the functions will be called without
1429 arguments.
1430 @return: A list of the values returned by the functions called.
1432 threads = []
1433 for target in targets:
1434 if isinstance(target, tuple) or isinstance(target, list):
1435 t = Thread(*target)
1436 else:
1437 t = Thread(target)
1438 threads.append(t)
1439 t.start()
1440 return [t.join() for t in threads]
1443 class VirtLoggingConfig(logging_config.LoggingConfig):
1445 Used with the sole purpose of providing convenient logging setup
1446 for the KVM test auxiliary programs.
1448 def configure_logging(self, results_dir=None, verbose=False):
1449 super(VirtLoggingConfig, self).configure_logging(use_console=True,
1450 verbose=verbose)
1453 class PciAssignable(object):
1455 Request PCI assignable devices on host. It will check whether to request
1456 PF (physical Functions) or VF (Virtual Functions).
1458 def __init__(self, type="vf", driver=None, driver_option=None,
1459 names=None, devices_requested=None):
1461 Initialize parameter 'type' which could be:
1462 vf: Virtual Functions
1463 pf: Physical Function (actual hardware)
1464 mixed: Both includes VFs and PFs
1466 If pass through Physical NIC cards, we need to specify which devices
1467 to be assigned, e.g. 'eth1 eth2'.
1469 If pass through Virtual Functions, we need to specify how many vfs
1470 are going to be assigned, e.g. passthrough_count = 8 and max_vfs in
1471 config file.
1473 @param type: PCI device type.
1474 @param driver: Kernel module for the PCI assignable device.
1475 @param driver_option: Module option to specify the maximum number of
1476 VFs (eg 'max_vfs=7')
1477 @param names: Physical NIC cards correspondent network interfaces,
1478 e.g.'eth1 eth2 ...'
1479 @param devices_requested: Number of devices being requested.
1481 self.type = type
1482 self.driver = driver
1483 self.driver_option = driver_option
1484 if names:
1485 self.name_list = names.split()
1486 if devices_requested:
1487 self.devices_requested = int(devices_requested)
1488 else:
1489 self.devices_requested = None
1492 def _get_pf_pci_id(self, name, search_str):
1494 Get the PF PCI ID according to name.
1496 @param name: Name of the PCI device.
1497 @param search_str: Search string to be used on lspci.
1499 cmd = "ethtool -i %s | awk '/bus-info/ {print $2}'" % name
1500 s, pci_id = commands.getstatusoutput(cmd)
1501 if not (s or "Cannot get driver information" in pci_id):
1502 return pci_id[5:]
1503 cmd = "lspci | awk '/%s/ {print $1}'" % search_str
1504 pci_ids = [id for id in commands.getoutput(cmd).splitlines()]
1505 nic_id = int(re.search('[0-9]+', name).group(0))
1506 if (len(pci_ids) - 1) < nic_id:
1507 return None
1508 return pci_ids[nic_id]
1511 def _release_dev(self, pci_id):
1513 Release a single PCI device.
1515 @param pci_id: PCI ID of a given PCI device.
1517 base_dir = "/sys/bus/pci"
1518 full_id = get_full_pci_id(pci_id)
1519 vendor_id = get_vendor_from_pci_id(pci_id)
1520 drv_path = os.path.join(base_dir, "devices/%s/driver" % full_id)
1521 if 'pci-stub' in os.readlink(drv_path):
1522 cmd = "echo '%s' > %s/new_id" % (vendor_id, drv_path)
1523 if os.system(cmd):
1524 return False
1526 stub_path = os.path.join(base_dir, "drivers/pci-stub")
1527 cmd = "echo '%s' > %s/unbind" % (full_id, stub_path)
1528 if os.system(cmd):
1529 return False
1531 driver = self.dev_drivers[pci_id]
1532 cmd = "echo '%s' > %s/bind" % (full_id, driver)
1533 if os.system(cmd):
1534 return False
1536 return True
1539 def get_vf_devs(self):
1541 Catch all VFs PCI IDs.
1543 @return: List with all PCI IDs for the Virtual Functions avaliable
1545 if not self.sr_iov_setup():
1546 return []
1548 cmd = "lspci | awk '/Virtual Function/ {print $1}'"
1549 return commands.getoutput(cmd).split()
1552 def get_pf_devs(self):
1554 Catch all PFs PCI IDs.
1556 @return: List with all PCI IDs for the physical hardware requested
1558 pf_ids = []
1559 for name in self.name_list:
1560 pf_id = self._get_pf_pci_id(name, "Ethernet")
1561 if not pf_id:
1562 continue
1563 pf_ids.append(pf_id)
1564 return pf_ids
1567 def get_devs(self, count):
1569 Check out all devices' PCI IDs according to their name.
1571 @param count: count number of PCI devices needed for pass through
1572 @return: a list of all devices' PCI IDs
1574 if self.type == "vf":
1575 vf_ids = self.get_vf_devs()
1576 elif self.type == "pf":
1577 vf_ids = self.get_pf_devs()
1578 elif self.type == "mixed":
1579 vf_ids = self.get_vf_devs()
1580 vf_ids.extend(self.get_pf_devs())
1581 return vf_ids[0:count]
1584 def get_vfs_count(self):
1586 Get VFs count number according to lspci.
1588 # FIXME: Need to think out a method of identify which
1589 # 'virtual function' belongs to which physical card considering
1590 # that if the host has more than one 82576 card. PCI_ID?
1591 cmd = "lspci | grep 'Virtual Function' | wc -l"
1592 return int(commands.getoutput(cmd))
1595 def check_vfs_count(self):
1597 Check VFs count number according to the parameter driver_options.
1599 # Network card 82576 has two network interfaces and each can be
1600 # virtualized up to 7 virtual functions, therefore we multiply
1601 # two for the value of driver_option 'max_vfs'.
1602 expected_count = int((re.findall("(\d)", self.driver_option)[0])) * 2
1603 return (self.get_vfs_count == expected_count)
1606 def is_binded_to_stub(self, full_id):
1608 Verify whether the device with full_id is already binded to pci-stub.
1610 @param full_id: Full ID for the given PCI device
1612 base_dir = "/sys/bus/pci"
1613 stub_path = os.path.join(base_dir, "drivers/pci-stub")
1614 if os.path.exists(os.path.join(stub_path, full_id)):
1615 return True
1616 return False
1619 def sr_iov_setup(self):
1621 Ensure the PCI device is working in sr_iov mode.
1623 Check if the PCI hardware device drive is loaded with the appropriate,
1624 parameters (number of VFs), and if it's not, perform setup.
1626 @return: True, if the setup was completed successfuly, False otherwise.
1628 re_probe = False
1629 s, o = commands.getstatusoutput('lsmod | grep %s' % self.driver)
1630 if s:
1631 re_probe = True
1632 elif not self.check_vfs_count():
1633 os.system("modprobe -r %s" % self.driver)
1634 re_probe = True
1635 else:
1636 return True
1638 # Re-probe driver with proper number of VFs
1639 if re_probe:
1640 cmd = "modprobe %s %s" % (self.driver, self.driver_option)
1641 logging.info("Loading the driver '%s' with option '%s'",
1642 self.driver, self.driver_option)
1643 s, o = commands.getstatusoutput(cmd)
1644 if s:
1645 return False
1646 return True
1649 def request_devs(self):
1651 Implement setup process: unbind the PCI device and then bind it
1652 to the pci-stub driver.
1654 @return: a list of successfully requested devices' PCI IDs.
1656 base_dir = "/sys/bus/pci"
1657 stub_path = os.path.join(base_dir, "drivers/pci-stub")
1659 self.pci_ids = self.get_devs(self.devices_requested)
1660 logging.debug("The following pci_ids were found: %s", self.pci_ids)
1661 requested_pci_ids = []
1662 self.dev_drivers = {}
1664 # Setup all devices specified for assignment to guest
1665 for pci_id in self.pci_ids:
1666 full_id = get_full_pci_id(pci_id)
1667 if not full_id:
1668 continue
1669 drv_path = os.path.join(base_dir, "devices/%s/driver" % full_id)
1670 dev_prev_driver = os.path.realpath(os.path.join(drv_path,
1671 os.readlink(drv_path)))
1672 self.dev_drivers[pci_id] = dev_prev_driver
1674 # Judge whether the device driver has been binded to stub
1675 if not self.is_binded_to_stub(full_id):
1676 logging.debug("Binding device %s to stub", full_id)
1677 vendor_id = get_vendor_from_pci_id(pci_id)
1678 stub_new_id = os.path.join(stub_path, 'new_id')
1679 unbind_dev = os.path.join(drv_path, 'unbind')
1680 stub_bind = os.path.join(stub_path, 'bind')
1682 info_write_to_files = [(vendor_id, stub_new_id),
1683 (full_id, unbind_dev),
1684 (full_id, stub_bind)]
1686 for content, file in info_write_to_files:
1687 try:
1688 utils.open_write_close(file, content)
1689 except IOError:
1690 logging.debug("Failed to write %s to file %s", content,
1691 file)
1692 continue
1694 if not self.is_binded_to_stub(full_id):
1695 logging.error("Binding device %s to stub failed", pci_id)
1696 continue
1697 else:
1698 logging.debug("Device %s already binded to stub", pci_id)
1699 requested_pci_ids.append(pci_id)
1700 self.pci_ids = requested_pci_ids
1701 return self.pci_ids
1704 def release_devs(self):
1706 Release all PCI devices currently assigned to VMs back to the
1707 virtualization host.
1709 try:
1710 for pci_id in self.dev_drivers:
1711 if not self._release_dev(pci_id):
1712 logging.error("Failed to release device %s to host", pci_id)
1713 else:
1714 logging.info("Released device %s successfully", pci_id)
1715 except:
1716 return
1719 class KojiClient(object):
1721 Stablishes a connection with the build system, either koji or brew.
1723 This class provides convenience methods to retrieve information on packages
1724 and the packages themselves hosted on the build system. Packages should be
1725 specified in the KojiPgkSpec syntax.
1728 CMD_LOOKUP_ORDER = ['/usr/bin/brew', '/usr/bin/koji' ]
1730 CONFIG_MAP = {'/usr/bin/brew': '/etc/brewkoji.conf',
1731 '/usr/bin/koji': '/etc/koji.conf'}
1734 def __init__(self, cmd=None):
1736 Verifies whether the system has koji or brew installed, then loads
1737 the configuration file that will be used to download the files.
1739 @type cmd: string
1740 @param cmd: Optional command name, either 'brew' or 'koji'. If not
1741 set, get_default_command() is used and to look for
1742 one of them.
1743 @raise: ValueError
1745 if not KOJI_INSTALLED:
1746 raise ValueError('No koji/brew installed on the machine')
1748 # Instance variables used by many methods
1749 self.command = None
1750 self.config = None
1751 self.config_options = {}
1752 self.session = None
1754 # Set koji command or get default
1755 if cmd is None:
1756 self.command = self.get_default_command()
1757 else:
1758 self.command = cmd
1760 # Check koji command
1761 if not self.is_command_valid():
1762 raise ValueError('Koji command "%s" is not valid' % self.command)
1764 # Assuming command is valid, set configuration file and read it
1765 self.config = self.CONFIG_MAP[self.command]
1766 self.read_config()
1768 # Setup koji session
1769 server_url = self.config_options['server']
1770 session_options = self.get_session_options()
1771 self.session = koji.ClientSession(server_url,
1772 session_options)
1775 def read_config(self, check_is_valid=True):
1777 Reads options from the Koji configuration file
1779 By default it checks if the koji configuration is valid
1781 @type check_valid: boolean
1782 @param check_valid: whether to include a check on the configuration
1783 @raises: ValueError
1784 @returns: None
1786 if check_is_valid:
1787 if not self.is_config_valid():
1788 raise ValueError('Koji config "%s" is not valid' % self.config)
1790 config = ConfigParser.ConfigParser()
1791 config.read(self.config)
1793 basename = os.path.basename(self.command)
1794 for name, value in config.items(basename):
1795 self.config_options[name] = value
1798 def get_session_options(self):
1800 Filter only options necessary for setting up a cobbler client session
1802 @returns: only the options used for session setup
1804 session_options = {}
1805 for name, value in self.config_options.items():
1806 if name in ('user', 'password', 'debug_xmlrpc', 'debug'):
1807 session_options[name] = value
1808 return session_options
1811 def is_command_valid(self):
1813 Checks if the currently set koji command is valid
1815 @returns: True or False
1817 koji_command_ok = True
1819 if not os.path.isfile(self.command):
1820 logging.error('Koji command "%s" is not a regular file',
1821 self.command)
1822 koji_command_ok = False
1824 if not os.access(self.command, os.X_OK):
1825 logging.warn('Koji command "%s" is not executable: this is '
1826 'not fatal but indicates an unexpected situation',
1827 self.command)
1829 if not self.command in self.CONFIG_MAP.keys():
1830 logging.error('Koji command "%s" does not have a configuration '
1831 'file associated to it', self.command)
1832 koji_command_ok = False
1834 return koji_command_ok
1837 def is_config_valid(self):
1839 Checks if the currently set koji configuration is valid
1841 @returns: True or False
1843 koji_config_ok = True
1845 if not os.path.isfile(self.config):
1846 logging.error('Koji config "%s" is not a regular file', self.config)
1847 koji_config_ok = False
1849 if not os.access(self.config, os.R_OK):
1850 logging.error('Koji config "%s" is not readable', self.config)
1851 koji_config_ok = False
1853 config = ConfigParser.ConfigParser()
1854 config.read(self.config)
1855 basename = os.path.basename(self.command)
1856 if not config.has_section(basename):
1857 logging.error('Koji configuration file "%s" does not have a '
1858 'section "%s", named after the base name of the '
1859 'currently set koji command "%s"', self.config,
1860 basename, self.command)
1861 koji_config_ok = False
1863 return koji_config_ok
1866 def get_default_command(self):
1868 Looks up for koji or brew "binaries" on the system
1870 Systems with plain koji usually don't have a brew cmd, while systems
1871 with koji, have *both* koji and brew utilities. So we look for brew
1872 first, and if found, we consider that the system is configured for
1873 brew. If not, we consider this is a system with plain koji.
1875 @returns: either koji or brew command line executable path, or None
1877 koji_command = None
1878 for command in self.CMD_LOOKUP_ORDER:
1879 if os.path.isfile(command):
1880 koji_command = command
1881 break
1882 else:
1883 koji_command_basename = os.path.basename(koji_command)
1884 try:
1885 koji_command = os_dep.command(koji_command_basename)
1886 break
1887 except ValueError:
1888 pass
1889 return koji_command
1892 def get_pkg_info(self, pkg):
1894 Returns information from Koji on the package
1896 @type pkg: KojiPkgSpec
1897 @param pkg: information about the package, as a KojiPkgSpec instance
1899 @returns: information from Koji about the specified package
1901 info = {}
1902 if pkg.build is not None:
1903 info = self.session.getBuild(int(pkg.build))
1904 elif pkg.tag is not None and pkg.package is not None:
1905 builds = self.session.listTagged(pkg.tag,
1906 latest=True,
1907 inherit=True,
1908 package=pkg.package)
1909 if builds:
1910 info = builds[0]
1911 return info
1914 def is_pkg_valid(self, pkg):
1916 Checks if this package is altogether valid on Koji
1918 This verifies if the build or tag specified in the package
1919 specification actually exist on the Koji server
1921 @returns: True or False
1923 valid = True
1924 if pkg.build:
1925 if not self.is_pkg_spec_build_valid(pkg):
1926 valid = False
1927 elif pkg.tag:
1928 if not self.is_pkg_spec_tag_valid(pkg):
1929 valid = False
1930 else:
1931 valid = False
1932 return valid
1935 def is_pkg_spec_build_valid(self, pkg):
1937 Checks if build is valid on Koji
1939 @param pkg: a Pkg instance
1941 if pkg.build is not None:
1942 info = self.session.getBuild(int(pkg.build))
1943 if info:
1944 return True
1945 return False
1948 def is_pkg_spec_tag_valid(self, pkg):
1950 Checks if tag is valid on Koji
1952 @type pkg: KojiPkgSpec
1953 @param pkg: a package specification
1955 if pkg.tag is not None:
1956 tag = self.session.getTag(pkg.tag)
1957 if tag:
1958 return True
1959 return False
1962 def get_pkg_rpm_info(self, pkg, arch=None):
1964 Returns a list of infomation on the RPM packages found on koji
1966 @type pkg: KojiPkgSpec
1967 @param pkg: a package specification
1968 @type arch: string
1969 @param arch: packages built for this architecture, but also including
1970 architecture independent (noarch) packages
1972 if arch is None:
1973 arch = utils.get_arch()
1974 rpms = []
1975 info = self.get_pkg_info(pkg)
1976 if info:
1977 rpms = self.session.listRPMs(buildID=info['id'],
1978 arches=[arch, 'noarch'])
1979 if pkg.subpackages:
1980 rpms = [d for d in rpms if d['name'] in pkg.subpackages]
1981 return rpms
1984 def get_pkg_rpm_names(self, pkg, arch=None):
1986 Gets the names for the RPM packages specified in pkg
1988 @type pkg: KojiPkgSpec
1989 @param pkg: a package specification
1990 @type arch: string
1991 @param arch: packages built for this architecture, but also including
1992 architecture independent (noarch) packages
1994 if arch is None:
1995 arch = utils.get_arch()
1996 rpms = self.get_pkg_rpm_info(pkg, arch)
1997 return [rpm['name'] for rpm in rpms]
2000 def get_pkg_rpm_file_names(self, pkg, arch=None):
2002 Gets the file names for the RPM packages specified in pkg
2004 @type pkg: KojiPkgSpec
2005 @param pkg: a package specification
2006 @type arch: string
2007 @param arch: packages built for this architecture, but also including
2008 architecture independent (noarch) packages
2010 if arch is None:
2011 arch = utils.get_arch()
2012 rpm_names = []
2013 rpms = self.get_pkg_rpm_info(pkg, arch)
2014 for rpm in rpms:
2015 arch_rpm_name = koji.pathinfo.rpm(rpm)
2016 rpm_name = os.path.basename(arch_rpm_name)
2017 rpm_names.append(rpm_name)
2018 return rpm_names
2021 def get_pkg_urls(self, pkg, arch=None):
2023 Gets the urls for the packages specified in pkg
2025 @type pkg: KojiPkgSpec
2026 @param pkg: a package specification
2027 @type arch: string
2028 @param arch: packages built for this architecture, but also including
2029 architecture independent (noarch) packages
2031 info = self.get_pkg_info(pkg)
2032 rpms = self.get_pkg_rpm_info(pkg, arch)
2033 rpm_urls = []
2034 for rpm in rpms:
2035 rpm_name = koji.pathinfo.rpm(rpm)
2036 url = ("%s/%s/%s/%s/%s" % (self.config_options['pkgurl'],
2037 info['package_name'],
2038 info['version'], info['release'],
2039 rpm_name))
2040 rpm_urls.append(url)
2041 return rpm_urls
2044 def get_pkgs(self, pkg, dst_dir, arch=None):
2046 Download the packages
2048 @type pkg: KojiPkgSpec
2049 @param pkg: a package specification
2050 @type dst_dir: string
2051 @param dst_dir: the destination directory, where the downloaded
2052 packages will be saved on
2053 @type arch: string
2054 @param arch: packages built for this architecture, but also including
2055 architecture independent (noarch) packages
2057 rpm_urls = self.get_pkg_urls(pkg, arch)
2058 for url in rpm_urls:
2059 utils.get_file(url,
2060 os.path.join(dst_dir, os.path.basename(url)))
2063 DEFAULT_KOJI_TAG = None
2064 def set_default_koji_tag(tag):
2066 Sets the default tag that will be used
2068 global DEFAULT_KOJI_TAG
2069 DEFAULT_KOJI_TAG = tag
2072 def get_default_koji_tag():
2073 return DEFAULT_KOJI_TAG
2076 class KojiPkgSpec:
2078 A package specification syntax parser for Koji
2080 This holds information on either tag or build, and packages to be fetched
2081 from koji and possibly installed (features external do this class).
2083 New objects can be created either by providing information in the textual
2084 format or by using the actual parameters for tag, build, package and sub-
2085 packages. The textual format is useful for command line interfaces and
2086 configuration files, while using parameters is better for using this in
2087 a programatic fashion.
2089 The following sets of examples are interchangeable. Specifying all packages
2090 part of build number 1000:
2092 >>> from kvm_utils import KojiPkgSpec
2093 >>> pkg = KojiPkgSpec('1000')
2095 >>> pkg = KojiPkgSpec(build=1000)
2097 Specifying only a subset of packages of build number 1000:
2099 >>> pkg = KojiPkgSpec('1000:kernel,kernel-devel')
2101 >>> pkg = KojiPkgSpec(build=1000,
2102 subpackages=['kernel', 'kernel-devel'])
2104 Specifying the latest build for the 'kernel' package tagged with 'dist-f14':
2106 >>> pkg = KojiPkgSpec('dist-f14:kernel')
2108 >>> pkg = KojiPkgSpec(tag='dist-f14', package='kernel')
2110 Specifying the 'kernel' package using the default tag:
2112 >>> kvm_utils.set_default_koji_tag('dist-f14')
2113 >>> pkg = KojiPkgSpec('kernel')
2115 >>> pkg = KojiPkgSpec(package='kernel')
2117 Specifying the 'kernel' package using the default tag:
2119 >>> kvm_utils.set_default_koji_tag('dist-f14')
2120 >>> pkg = KojiPkgSpec('kernel')
2122 >>> pkg = KojiPkgSpec(package='kernel')
2124 If you do not specify a default tag, and give a package name without an
2125 explicit tag, your package specification is considered invalid:
2127 >>> print kvm_utils.get_default_koji_tag()
2128 None
2129 >>> print kvm_utils.KojiPkgSpec('kernel').is_valid()
2130 False
2132 >>> print kvm_utils.KojiPkgSpec(package='kernel').is_valid()
2133 False
2136 SEP = ':'
2138 def __init__(self, text='', tag=None, build=None,
2139 package=None, subpackages=[]):
2141 Instantiates a new KojiPkgSpec object
2143 @type text: string
2144 @param text: a textual representation of a package on Koji that
2145 will be parsed
2146 @type tag: string
2147 @param tag: a koji tag, example: Fedora-14-RELEASE
2148 (see U{http://fedoraproject.org/wiki/Koji#Tags_and_Targets})
2149 @type build: number
2150 @param build: a koji build, example: 1001
2151 (see U{http://fedoraproject.org/wiki/Koji#Koji_Architecture})
2152 @type package: string
2153 @param package: a koji package, example: python
2154 (see U{http://fedoraproject.org/wiki/Koji#Koji_Architecture})
2155 @type subpackages: list of strings
2156 @param subpackages: a list of package names, usually a subset of
2157 the RPM packages generated by a given build
2160 # Set to None to indicate 'not set' (and be able to use 'is')
2161 self.tag = None
2162 self.build = None
2163 self.package = None
2164 self.subpackages = []
2166 self.default_tag = None
2168 # Textual representation takes precedence (most common use case)
2169 if text:
2170 self.parse(text)
2171 else:
2172 self.tag = tag
2173 self.build = build
2174 self.package = package
2175 self.subpackages = subpackages
2177 # Set the default tag, if set, as a fallback
2178 if not self.build and not self.tag:
2179 default_tag = get_default_koji_tag()
2180 if default_tag is not None:
2181 self.tag = default_tag
2184 def parse(self, text):
2186 Parses a textual representation of a package specification
2188 @type text: string
2189 @param text: textual representation of a package in koji
2191 parts = text.count(self.SEP) + 1
2192 if parts == 1:
2193 if text.isdigit():
2194 self.build = text
2195 else:
2196 self.package = text
2197 elif parts == 2:
2198 part1, part2 = text.split(self.SEP)
2199 if part1.isdigit():
2200 self.build = part1
2201 self.subpackages = part2.split(',')
2202 else:
2203 self.tag = part1
2204 self.package = part2
2205 elif parts >= 3:
2206 # Instead of erroring on more arguments, we simply ignore them
2207 # This makes the parser suitable for future syntax additions, such
2208 # as specifying the package architecture
2209 part1, part2, part3 = text.split(self.SEP)[0:3]
2210 self.tag = part1
2211 self.package = part2
2212 self.subpackages = part3.split(',')
2215 def _is_invalid_neither_tag_or_build(self):
2217 Checks if this package is invalid due to not having either a valid
2218 tag or build set, that is, both are empty.
2220 @returns: True if this is invalid and False if it's valid
2222 return (self.tag is None and self.build is None)
2225 def _is_invalid_package_but_no_tag(self):
2227 Checks if this package is invalid due to having a package name set
2228 but tag or build set, that is, both are empty.
2230 @returns: True if this is invalid and False if it's valid
2232 return (self.package and not self.tag)
2235 def _is_invalid_subpackages_but_no_main_package(self):
2237 Checks if this package is invalid due to having a tag set (this is Ok)
2238 but specifying subpackage names without specifying the main package
2239 name.
2241 Specifying subpackages without a main package name is only valid when
2242 a build is used instead of a tag.
2244 @returns: True if this is invalid and False if it's valid
2246 return (self.tag and self.subpackages and not self.package)
2249 def is_valid(self):
2251 Checks if this package specification is valid.
2253 Being valid means that it has enough and not conflicting information.
2254 It does not validate that the packages specified actually existe on
2255 the Koji server.
2257 @returns: True or False
2259 if self._is_invalid_neither_tag_or_build():
2260 return False
2261 elif self._is_invalid_package_but_no_tag():
2262 return False
2263 elif self._is_invalid_subpackages_but_no_main_package():
2264 return False
2266 return True
2269 def describe_invalid(self):
2271 Describes why this is not valid, in a human friendly way
2273 if self._is_invalid_neither_tag_or_build():
2274 return 'neither a tag or build are set, and of them should be set'
2275 elif self._is_invalid_package_but_no_tag():
2276 return 'package name specified but no tag is set'
2277 elif self._is_invalid_subpackages_but_no_main_package():
2278 return 'subpackages specified but no main package is set'
2280 return 'unkwown reason, seems to be valid'
2283 def describe(self):
2285 Describe this package specification, in a human friendly way
2287 @returns: package specification description
2289 if self.is_valid():
2290 description = ''
2291 if not self.subpackages:
2292 description += 'all subpackages from %s ' % self.package
2293 else:
2294 description += ('only subpackage(s) %s from package %s ' %
2295 (', '.join(self.subpackages), self.package))
2297 if self.build:
2298 description += 'from build %s' % self.build
2299 elif self.tag:
2300 description += 'tagged with %s' % self.tag
2301 else:
2302 raise ValueError, 'neither build or tag is set'
2304 return description
2305 else:
2306 return ('Invalid package specification: %s' %
2307 self.describe_invalid())
2310 def __repr__(self):
2311 return ("<KojiPkgSpec tag=%s build=%s pkg=%s subpkgs=%s>" %
2312 (self.tag, self.build, self.package,
2313 ", ".join(self.subpackages)))
2316 def umount(src, mount_point, type):
2318 Umount the src mounted in mount_point.
2320 @src: mount source
2321 @mount_point: mount point
2322 @type: file system type
2325 mount_string = "%s %s %s" % (src, mount_point, type)
2326 if mount_string in file("/etc/mtab").read():
2327 umount_cmd = "umount %s" % mount_point
2328 try:
2329 utils.system(umount_cmd)
2330 return True
2331 except error.CmdError:
2332 return False
2333 else:
2334 logging.debug("%s is not mounted under %s", src, mount_point)
2335 return True
2338 def mount(src, mount_point, type, perm="rw"):
2340 Mount the src into mount_point of the host.
2342 @src: mount source
2343 @mount_point: mount point
2344 @type: file system type
2345 @perm: mount premission
2347 umount(src, mount_point, type)
2348 mount_string = "%s %s %s %s" % (src, mount_point, type, perm)
2350 if mount_string in file("/etc/mtab").read():
2351 logging.debug("%s is already mounted in %s with %s",
2352 src, mount_point, perm)
2353 return True
2355 mount_cmd = "mount -t %s %s %s -o %s" % (type, src, mount_point, perm)
2356 try:
2357 utils.system(mount_cmd)
2358 except error.CmdError:
2359 return False
2361 logging.debug("Verify the mount through /etc/mtab")
2362 if mount_string in file("/etc/mtab").read():
2363 logging.debug("%s is successfully mounted", src)
2364 return True
2365 else:
2366 logging.error("Can't find mounted NFS share - /etc/mtab contents \n%s",
2367 file("/etc/mtab").read())
2368 return False
2371 def install_host_kernel(job, params):
2373 Install a host kernel, given the appropriate params.
2375 @param job: Job object.
2376 @param params: Dict with host kernel install params.
2378 install_type = params.get('host_kernel_install_type')
2380 rpm_url = params.get('host_kernel_rpm_url')
2382 koji_cmd = params.get('host_kernel_koji_cmd')
2383 koji_build = params.get('host_kernel_koji_build')
2384 koji_tag = params.get('host_kernel_koji_tag')
2386 git_repo = params.get('host_kernel_git_repo')
2387 git_branch = params.get('host_kernel_git_branch')
2388 git_commit = params.get('host_kernel_git_commit')
2389 patch_list = params.get('host_kernel_patch_list')
2390 if patch_list:
2391 patch_list = patch_list.split()
2392 kernel_config = params.get('host_kernel_config')
2394 if install_type == 'rpm':
2395 logging.info('Installing host kernel through rpm')
2396 dst = os.path.join("/tmp", os.path.basename(rpm_url))
2397 k = utils.get_file(rpm_url, dst)
2398 host_kernel = job.kernel(k)
2399 host_kernel.install(install_vmlinux=False)
2400 host_kernel.boot()
2402 elif install_type in ['koji', 'brew']:
2403 k_deps = KojiPkgSpec(tag=koji_tag, package='kernel',
2404 subpackages=['kernel-devel', 'kernel-firmware'])
2405 k = KojiPkgSpec(tag=koji_tag, package='kernel',
2406 subpackages=['kernel'])
2408 c = KojiClient(koji_cmd)
2409 logging.info('Fetching kernel dependencies (-devel, -firmware)')
2410 c.get_pkgs(k_deps, job.tmpdir)
2411 logging.info('Installing kernel dependencies (-devel, -firmware) '
2412 'through %s', install_type)
2413 k_deps_rpm_file_names = [os.path.join(job.tmpdir, rpm_file_name) for
2414 rpm_file_name in c.get_pkg_rpm_file_names(k_deps)]
2415 utils.run('rpm -U --force %s' % " ".join(k_deps_rpm_file_names))
2417 c.get_pkgs(k, job.tmpdir)
2418 k_rpm = os.path.join(job.tmpdir,
2419 c.get_pkg_rpm_file_names(k)[0])
2420 host_kernel = job.kernel(k_rpm)
2421 host_kernel.install(install_vmlinux=False)
2422 host_kernel.boot()
2424 elif install_type == 'git':
2425 logging.info('Chose to install host kernel through git, proceeding')
2426 repodir = os.path.join("/tmp", 'kernel_src')
2427 r = get_git_branch(git_repo, git_branch, repodir, git_commit)
2428 host_kernel = job.kernel(r)
2429 if patch_list:
2430 host_kernel.patch(patch_list)
2431 host_kernel.config(kernel_config)
2432 host_kernel.build()
2433 host_kernel.install()
2434 host_kernel.boot()
2436 else:
2437 logging.info('Chose %s, using the current kernel for the host',
2438 install_type)
2441 def if_nametoindex(ifname):
2443 Map an interface name into its corresponding index.
2444 Returns 0 on error, as 0 is not a valid index
2446 @param ifname: interface name
2448 index = 0
2449 ctrl_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
2450 ifr = struct.pack("16si", ifname, 0)
2451 r = fcntl.ioctl(ctrl_sock, SIOCGIFINDEX, ifr)
2452 index = struct.unpack("16si", r)[1]
2453 ctrl_sock.close()
2454 return index
2457 def vnet_hdr_probe(tapfd):
2459 Check if the IFF_VNET_HDR is support by tun.
2461 @param tapfd: the file descriptor of /dev/net/tun
2463 u = struct.pack("I", 0)
2464 try:
2465 r = fcntl.ioctl(tapfd, TUNGETFEATURES, u)
2466 except OverflowError:
2467 return False
2468 flags = struct.unpack("I", r)[0]
2469 if flags & IFF_VNET_HDR:
2470 return True
2471 else:
2472 return False
2475 def open_tap(devname, ifname, vnet_hdr=True):
2477 Open a tap device and returns its file descriptor which is used by
2478 fd=<fd> parameter of qemu-kvm.
2480 @param ifname: TAP interface name
2481 @param vnet_hdr: Whether enable the vnet header
2483 try:
2484 tapfd = os.open(devname, os.O_RDWR)
2485 except OSError, e:
2486 raise TAPModuleError(devname, "open", e)
2487 flags = IFF_TAP | IFF_NO_PI
2488 if vnet_hdr and vnet_hdr_probe(tapfd):
2489 flags |= IFF_VNET_HDR
2491 ifr = struct.pack("16sh", ifname, flags)
2492 try:
2493 r = fcntl.ioctl(tapfd, TUNSETIFF, ifr)
2494 except IOError, details:
2495 raise TAPCreationError(ifname, details)
2496 ifname = struct.unpack("16sh", r)[0].strip("\x00")
2497 return tapfd
2500 def add_to_bridge(ifname, brname):
2502 Add a TAP device to bridge
2504 @param ifname: Name of TAP device
2505 @param brname: Name of the bridge
2507 ctrl_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
2508 index = if_nametoindex(ifname)
2509 if index == 0:
2510 raise TAPNotExistError(ifname)
2511 ifr = struct.pack("16si", brname, index)
2512 try:
2513 r = fcntl.ioctl(ctrl_sock, SIOCBRADDIF, ifr)
2514 except IOError, details:
2515 raise BRAddIfError(ifname, brname, details)
2516 ctrl_sock.close()
2519 def bring_up_ifname(ifname):
2521 Bring up an interface
2523 @param ifname: Name of the interface
2525 ctrl_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
2526 ifr = struct.pack("16si", ifname, IFF_UP)
2527 try:
2528 fcntl.ioctl(ctrl_sock, SIOCSIFFLAGS, ifr)
2529 except IOError:
2530 raise TAPBringUpError(ifname)
2531 ctrl_sock.close()
2534 def if_set_macaddress(ifname, mac):
2536 Set the mac address for an interface
2538 @param ifname: Name of the interface
2539 @mac: Mac address
2541 ctrl_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
2543 ifr = struct.pack("256s", ifname)
2544 try:
2545 mac_dev = fcntl.ioctl(ctrl_sock, SIOCGIFHWADDR, ifr)[18:24]
2546 mac_dev = ":".join(["%02x" % ord(m) for m in mac_dev])
2547 except IOError, e:
2548 raise HwAddrGetError(ifname)
2550 if mac_dev.lower() == mac.lower():
2551 return
2553 ifr = struct.pack("16sH14s", ifname, 1,
2554 "".join([chr(int(m, 16)) for m in mac.split(":")]))
2555 try:
2556 fcntl.ioctl(ctrl_sock, SIOCSIFHWADDR, ifr)
2557 except IOError, e:
2558 logging.info(e)
2559 raise HwAddrSetError(ifname, mac)
2560 ctrl_sock.close()