virt.virt_test_utils: run_autotest - 'tar' needs relative paths to strip the leading '/'
[autotest-zwu.git] / client / virt / virt_utils.py
blobd443a8487856bfc14bef293161a7e84debc4d54c
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, tarfile
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 else:
300 r = random.SystemRandom()
301 prefix = "9a:%02x:%02x:%02x:" % (r.randint(0x00, 0xff),
302 r.randint(0x00, 0xff),
303 r.randint(0x00, 0xff))
304 mac_pool["prefix"] = prefix
305 return prefix
308 def generate_mac_address(vm_instance, nic_index):
310 Randomly generate a MAC address and add it to the MAC address pool.
312 Try to generate a MAC address based on a randomly generated MAC address
313 prefix and add it to a persistent dictionary.
314 key = VM instance + NIC index, value = MAC address
315 e.g. {'20100310-165222-Wt7l:0': '9a:5d:94:6a:9b:f9'}
317 @param vm_instance: The instance attribute of a VM.
318 @param nic_index: The index of the NIC.
319 @return: MAC address string.
321 mac_pool, lock_file = _open_mac_pool(fcntl.LOCK_EX)
322 key = "%s:%s" % (vm_instance, nic_index)
323 if key in mac_pool:
324 mac = mac_pool[key]
325 else:
326 prefix = _generate_mac_address_prefix(mac_pool)
327 r = random.SystemRandom()
328 while key not in mac_pool:
329 mac = prefix + "%02x:%02x" % (r.randint(0x00, 0xff),
330 r.randint(0x00, 0xff))
331 if mac in mac_pool.values():
332 continue
333 mac_pool[key] = mac
334 _close_mac_pool(mac_pool, lock_file)
335 return mac
338 def free_mac_address(vm_instance, nic_index):
340 Remove a MAC address from the address pool.
342 @param vm_instance: The instance attribute of a VM.
343 @param nic_index: The index of the NIC.
345 mac_pool, lock_file = _open_mac_pool(fcntl.LOCK_EX)
346 key = "%s:%s" % (vm_instance, nic_index)
347 if key in mac_pool:
348 del mac_pool[key]
349 _close_mac_pool(mac_pool, lock_file)
352 def set_mac_address(vm_instance, nic_index, mac):
354 Set a MAC address in the pool.
356 @param vm_instance: The instance attribute of a VM.
357 @param nic_index: The index of the NIC.
359 mac_pool, lock_file = _open_mac_pool(fcntl.LOCK_EX)
360 mac_pool["%s:%s" % (vm_instance, nic_index)] = mac
361 _close_mac_pool(mac_pool, lock_file)
364 def get_mac_address(vm_instance, nic_index):
366 Return a MAC address from the pool.
368 @param vm_instance: The instance attribute of a VM.
369 @param nic_index: The index of the NIC.
370 @return: MAC address string.
372 mac_pool, lock_file = _open_mac_pool(fcntl.LOCK_SH)
373 mac = mac_pool.get("%s:%s" % (vm_instance, nic_index))
374 _close_mac_pool(mac_pool, lock_file)
375 return mac
378 def verify_ip_address_ownership(ip, macs, timeout=10.0):
380 Use arping and the ARP cache to make sure a given IP address belongs to one
381 of the given MAC addresses.
383 @param ip: An IP address.
384 @param macs: A list or tuple of MAC addresses.
385 @return: True iff ip is assigned to a MAC address in macs.
387 # Compile a regex that matches the given IP address and any of the given
388 # MAC addresses
389 mac_regex = "|".join("(%s)" % mac for mac in macs)
390 regex = re.compile(r"\b%s\b.*\b(%s)\b" % (ip, mac_regex), re.IGNORECASE)
392 # Check the ARP cache
393 o = commands.getoutput("%s -n" % find_command("arp"))
394 if regex.search(o):
395 return True
397 # Get the name of the bridge device for arping
398 o = commands.getoutput("%s route get %s" % (find_command("ip"), ip))
399 dev = re.findall("dev\s+\S+", o, re.IGNORECASE)
400 if not dev:
401 return False
402 dev = dev[0].split()[-1]
404 # Send an ARP request
405 o = commands.getoutput("%s -f -c 3 -I %s %s" %
406 (find_command("arping"), dev, ip))
407 return bool(regex.search(o))
410 # Utility functions for dealing with external processes
412 def find_command(cmd):
413 for dir in ["/usr/local/sbin", "/usr/local/bin",
414 "/usr/sbin", "/usr/bin", "/sbin", "/bin"]:
415 file = os.path.join(dir, cmd)
416 if os.path.exists(file):
417 return file
418 raise ValueError('Missing command: %s' % cmd)
421 def pid_exists(pid):
423 Return True if a given PID exists.
425 @param pid: Process ID number.
427 try:
428 os.kill(pid, 0)
429 return True
430 except:
431 return False
434 def safe_kill(pid, signal):
436 Attempt to send a signal to a given process that may or may not exist.
438 @param signal: Signal number.
440 try:
441 os.kill(pid, signal)
442 return True
443 except:
444 return False
447 def kill_process_tree(pid, sig=signal.SIGKILL):
448 """Signal a process and all of its children.
450 If the process does not exist -- return.
452 @param pid: The pid of the process to signal.
453 @param sig: The signal to send to the processes.
455 if not safe_kill(pid, signal.SIGSTOP):
456 return
457 children = commands.getoutput("ps --ppid=%d -o pid=" % pid).split()
458 for child in children:
459 kill_process_tree(int(child), sig)
460 safe_kill(pid, sig)
461 safe_kill(pid, signal.SIGCONT)
464 def get_git_branch(repository, branch, srcdir, commit=None, lbranch=None):
466 Retrieves a given git code repository.
468 @param repository: Git repository URL
470 logging.info("Fetching git [REP '%s' BRANCH '%s' COMMIT '%s'] -> %s",
471 repository, branch, commit, srcdir)
472 if not os.path.exists(srcdir):
473 os.makedirs(srcdir)
474 os.chdir(srcdir)
476 if os.path.exists(".git"):
477 utils.system("git reset --hard")
478 else:
479 utils.system("git init")
481 if not lbranch:
482 lbranch = branch
484 utils.system("git fetch -q -f -u -t %s %s:%s" %
485 (repository, branch, lbranch))
486 utils.system("git checkout %s" % lbranch)
487 if commit:
488 utils.system("git checkout %s" % commit)
490 h = utils.system_output('git log --pretty=format:"%H" -1')
491 try:
492 desc = "tag %s" % utils.system_output("git describe")
493 except error.CmdError:
494 desc = "no tag found"
496 logging.info("Commit hash for %s is %s (%s)", repository, h.strip(), desc)
497 return srcdir
500 def check_kvm_source_dir(source_dir):
502 Inspects the kvm source directory and verifies its disposition. In some
503 occasions build may be dependant on the source directory disposition.
504 The reason why the return codes are numbers is that we might have more
505 changes on the source directory layout, so it's not scalable to just use
506 strings like 'old_repo', 'new_repo' and such.
508 @param source_dir: Source code path that will be inspected.
510 os.chdir(source_dir)
511 has_qemu_dir = os.path.isdir('qemu')
512 has_kvm_dir = os.path.isdir('kvm')
513 if has_qemu_dir:
514 logging.debug("qemu directory detected, source dir layout 1")
515 return 1
516 if has_kvm_dir and not has_qemu_dir:
517 logging.debug("kvm directory detected, source dir layout 2")
518 return 2
519 else:
520 raise error.TestError("Unknown source dir layout, cannot proceed.")
523 # Functions and classes used for logging into guests and transferring files
525 class LoginError(Exception):
526 def __init__(self, msg, output):
527 Exception.__init__(self, msg, output)
528 self.msg = msg
529 self.output = output
531 def __str__(self):
532 return "%s (output: %r)" % (self.msg, self.output)
535 class LoginAuthenticationError(LoginError):
536 pass
539 class LoginTimeoutError(LoginError):
540 def __init__(self, output):
541 LoginError.__init__(self, "Login timeout expired", output)
544 class LoginProcessTerminatedError(LoginError):
545 def __init__(self, status, output):
546 LoginError.__init__(self, None, output)
547 self.status = status
549 def __str__(self):
550 return ("Client process terminated (status: %s, output: %r)" %
551 (self.status, self.output))
554 class LoginBadClientError(LoginError):
555 def __init__(self, client):
556 LoginError.__init__(self, None, None)
557 self.client = client
559 def __str__(self):
560 return "Unknown remote shell client: %r" % self.client
563 class SCPError(Exception):
564 def __init__(self, msg, output):
565 Exception.__init__(self, msg, output)
566 self.msg = msg
567 self.output = output
569 def __str__(self):
570 return "%s (output: %r)" % (self.msg, self.output)
573 class SCPAuthenticationError(SCPError):
574 pass
577 class SCPAuthenticationTimeoutError(SCPAuthenticationError):
578 def __init__(self, output):
579 SCPAuthenticationError.__init__(self, "Authentication timeout expired",
580 output)
583 class SCPTransferTimeoutError(SCPError):
584 def __init__(self, output):
585 SCPError.__init__(self, "Transfer timeout expired", output)
588 class SCPTransferFailedError(SCPError):
589 def __init__(self, status, output):
590 SCPError.__init__(self, None, output)
591 self.status = status
593 def __str__(self):
594 return ("SCP transfer failed (status: %s, output: %r)" %
595 (self.status, self.output))
598 def _remote_login(session, username, password, prompt, timeout=10):
600 Log into a remote host (guest) using SSH or Telnet. Wait for questions
601 and provide answers. If timeout expires while waiting for output from the
602 child (e.g. a password prompt or a shell prompt) -- fail.
604 @brief: Log into a remote host (guest) using SSH or Telnet.
606 @param session: An Expect or ShellSession instance to operate on
607 @param username: The username to send in reply to a login prompt
608 @param password: The password to send in reply to a password prompt
609 @param prompt: The shell prompt that indicates a successful login
610 @param timeout: The maximal time duration (in seconds) to wait for each
611 step of the login procedure (i.e. the "Are you sure" prompt, the
612 password prompt, the shell prompt, etc)
613 @raise LoginTimeoutError: If timeout expires
614 @raise LoginAuthenticationError: If authentication fails
615 @raise LoginProcessTerminatedError: If the client terminates during login
616 @raise LoginError: If some other error occurs
618 password_prompt_count = 0
619 login_prompt_count = 0
621 while True:
622 try:
623 match, text = session.read_until_last_line_matches(
624 [r"[Aa]re you sure", r"[Pp]assword:\s*$", r"[Ll]ogin:\s*$",
625 r"[Cc]onnection.*closed", r"[Cc]onnection.*refused",
626 r"[Pp]lease wait", r"[Ww]arning", prompt],
627 timeout=timeout, internal_timeout=0.5)
628 if match == 0: # "Are you sure you want to continue connecting"
629 logging.debug("Got 'Are you sure...', sending 'yes'")
630 session.sendline("yes")
631 continue
632 elif match == 1: # "password:"
633 if password_prompt_count == 0:
634 logging.debug("Got password prompt, sending '%s'", password)
635 session.sendline(password)
636 password_prompt_count += 1
637 continue
638 else:
639 raise LoginAuthenticationError("Got password prompt twice",
640 text)
641 elif match == 2: # "login:"
642 if login_prompt_count == 0 and password_prompt_count == 0:
643 logging.debug("Got username prompt; sending '%s'", username)
644 session.sendline(username)
645 login_prompt_count += 1
646 continue
647 else:
648 if login_prompt_count > 0:
649 msg = "Got username prompt twice"
650 else:
651 msg = "Got username prompt after password prompt"
652 raise LoginAuthenticationError(msg, text)
653 elif match == 3: # "Connection closed"
654 raise LoginError("Client said 'connection closed'", text)
655 elif match == 4: # "Connection refused"
656 raise LoginError("Client said 'connection refused'", text)
657 elif match == 5: # "Please wait"
658 logging.debug("Got 'Please wait'")
659 timeout = 30
660 continue
661 elif match == 6: # "Warning added RSA"
662 logging.debug("Got 'Warning added RSA to known host list")
663 continue
664 elif match == 7: # prompt
665 logging.debug("Got shell prompt -- logged in")
666 break
667 except aexpect.ExpectTimeoutError, e:
668 raise LoginTimeoutError(e.output)
669 except aexpect.ExpectProcessTerminatedError, e:
670 raise LoginProcessTerminatedError(e.status, e.output)
673 def remote_login(client, host, port, username, password, prompt, linesep="\n",
674 log_filename=None, timeout=10):
676 Log into a remote host (guest) using SSH/Telnet/Netcat.
678 @param client: The client to use ('ssh', 'telnet' or 'nc')
679 @param host: Hostname or IP address
680 @param port: Port to connect to
681 @param username: Username (if required)
682 @param password: Password (if required)
683 @param prompt: Shell prompt (regular expression)
684 @param linesep: The line separator to use when sending lines
685 (e.g. '\\n' or '\\r\\n')
686 @param log_filename: If specified, log all output to this file
687 @param timeout: The maximal time duration (in seconds) to wait for
688 each step of the login procedure (i.e. the "Are you sure" prompt
689 or the password prompt)
690 @raise LoginBadClientError: If an unknown client is requested
691 @raise: Whatever _remote_login() raises
692 @return: A ShellSession object.
694 if client == "ssh":
695 cmd = ("ssh -o UserKnownHostsFile=/dev/null "
696 "-o PreferredAuthentications=password -p %s %s@%s" %
697 (port, username, host))
698 elif client == "telnet":
699 cmd = "telnet -l %s %s %s" % (username, host, port)
700 elif client == "nc":
701 cmd = "nc %s %s" % (host, port)
702 else:
703 raise LoginBadClientError(client)
705 logging.debug("Trying to login with command '%s'", cmd)
706 session = aexpect.ShellSession(cmd, linesep=linesep, prompt=prompt)
707 try:
708 _remote_login(session, username, password, prompt, timeout)
709 except:
710 session.close()
711 raise
712 if log_filename:
713 session.set_output_func(log_line)
714 session.set_output_params((log_filename,))
715 return session
718 def wait_for_login(client, host, port, username, password, prompt, linesep="\n",
719 log_filename=None, timeout=240, internal_timeout=10):
721 Make multiple attempts to log into a remote host (guest) until one succeeds
722 or timeout expires.
724 @param timeout: Total time duration to wait for a successful login
725 @param internal_timeout: The maximal time duration (in seconds) to wait for
726 each step of the login procedure (e.g. the "Are you sure" prompt
727 or the password prompt)
728 @see: remote_login()
729 @raise: Whatever remote_login() raises
730 @return: A ShellSession object.
732 logging.debug("Attempting to log into %s:%s using %s (timeout %ds)",
733 host, port, client, timeout)
734 end_time = time.time() + timeout
735 while time.time() < end_time:
736 try:
737 return remote_login(client, host, port, username, password, prompt,
738 linesep, log_filename, internal_timeout)
739 except LoginError, e:
740 logging.debug(e)
741 time.sleep(2)
742 # Timeout expired; try one more time but don't catch exceptions
743 return remote_login(client, host, port, username, password, prompt,
744 linesep, log_filename, internal_timeout)
747 def _remote_scp(session, password_list, transfer_timeout=600, login_timeout=10):
749 Transfer file(s) to a remote host (guest) using SCP. Wait for questions
750 and provide answers. If login_timeout expires while waiting for output
751 from the child (e.g. a password prompt), fail. If transfer_timeout expires
752 while waiting for the transfer to complete, fail.
754 @brief: Transfer files using SCP, given a command line.
756 @param session: An Expect or ShellSession instance to operate on
757 @param password_list: Password list to send in reply to the password prompt
758 @param transfer_timeout: The time duration (in seconds) to wait for the
759 transfer to complete.
760 @param login_timeout: The maximal time duration (in seconds) to wait for
761 each step of the login procedure (i.e. the "Are you sure" prompt or
762 the password prompt)
763 @raise SCPAuthenticationError: If authentication fails
764 @raise SCPTransferTimeoutError: If the transfer fails to complete in time
765 @raise SCPTransferFailedError: If the process terminates with a nonzero
766 exit code
767 @raise SCPError: If some other error occurs
769 password_prompt_count = 0
770 timeout = login_timeout
771 authentication_done = False
773 scp_type = len(password_list)
775 while True:
776 try:
777 match, text = session.read_until_last_line_matches(
778 [r"[Aa]re you sure", r"[Pp]assword:\s*$", r"lost connection"],
779 timeout=timeout, internal_timeout=0.5)
780 if match == 0: # "Are you sure you want to continue connecting"
781 logging.debug("Got 'Are you sure...', sending 'yes'")
782 session.sendline("yes")
783 continue
784 elif match == 1: # "password:"
785 if password_prompt_count == 0:
786 logging.debug("Got password prompt, sending '%s'" %
787 password_list[password_prompt_count])
788 session.sendline(password_list[password_prompt_count])
789 password_prompt_count += 1
790 timeout = transfer_timeout
791 if scp_type == 1:
792 authentication_done = True
793 continue
794 elif password_prompt_count == 1 and scp_type == 2:
795 logging.debug("Got password prompt, sending '%s'" %
796 password_list[password_prompt_count])
797 session.sendline(password_list[password_prompt_count])
798 password_prompt_count += 1
799 timeout = transfer_timeout
800 authentication_done = True
801 continue
802 else:
803 raise SCPAuthenticationError("Got password prompt twice",
804 text)
805 elif match == 2: # "lost connection"
806 raise SCPError("SCP client said 'lost connection'", text)
807 except aexpect.ExpectTimeoutError, e:
808 if authentication_done:
809 raise SCPTransferTimeoutError(e.output)
810 else:
811 raise SCPAuthenticationTimeoutError(e.output)
812 except aexpect.ExpectProcessTerminatedError, e:
813 if e.status == 0:
814 logging.debug("SCP process terminated with status 0")
815 break
816 else:
817 raise SCPTransferFailedError(e.status, e.output)
820 def remote_scp(command, password_list, log_filename=None, transfer_timeout=600,
821 login_timeout=10):
823 Transfer file(s) to a remote host (guest) using SCP.
825 @brief: Transfer files using SCP, given a command line.
827 @param command: The command to execute
828 (e.g. "scp -r foobar root@localhost:/tmp/").
829 @param password_list: Password list to send in reply to a password prompt.
830 @param log_filename: If specified, log all output to this file
831 @param transfer_timeout: The time duration (in seconds) to wait for the
832 transfer to complete.
833 @param login_timeout: The maximal time duration (in seconds) to wait for
834 each step of the login procedure (i.e. the "Are you sure" prompt
835 or the password prompt)
836 @raise: Whatever _remote_scp() raises
838 logging.debug("Trying to SCP with command '%s', timeout %ss",
839 command, transfer_timeout)
840 if log_filename:
841 output_func = log_line
842 output_params = (log_filename,)
843 else:
844 output_func = None
845 output_params = ()
846 session = aexpect.Expect(command,
847 output_func=output_func,
848 output_params=output_params)
849 try:
850 _remote_scp(session, password_list, transfer_timeout, login_timeout)
851 finally:
852 session.close()
855 def scp_to_remote(host, port, username, password, local_path, remote_path,
856 log_filename=None, timeout=600):
858 Copy files to a remote host (guest) through scp.
860 @param host: Hostname or IP address
861 @param username: Username (if required)
862 @param password: Password (if required)
863 @param local_path: Path on the local machine where we are copying from
864 @param remote_path: Path on the remote machine where we are copying to
865 @param log_filename: If specified, log all output to this file
866 @param timeout: The time duration (in seconds) to wait for the transfer
867 to complete.
868 @raise: Whatever remote_scp() raises
870 command = ("scp -v -o UserKnownHostsFile=/dev/null "
871 "-o PreferredAuthentications=password -r -P %s %s %s@%s:%s" %
872 (port, local_path, username, host, remote_path))
873 password_list = []
874 password_list.append(password)
875 return remote_scp(command, password_list, log_filename, timeout)
879 def scp_from_remote(host, port, username, password, remote_path, local_path,
880 log_filename=None, timeout=600):
882 Copy files from a remote host (guest).
884 @param host: Hostname or IP address
885 @param username: Username (if required)
886 @param password: Password (if required)
887 @param local_path: Path on the local machine where we are copying from
888 @param remote_path: Path on the remote machine where we are copying to
889 @param log_filename: If specified, log all output to this file
890 @param timeout: The time duration (in seconds) to wait for the transfer
891 to complete.
892 @raise: Whatever remote_scp() raises
894 command = ("scp -v -o UserKnownHostsFile=/dev/null "
895 "-o PreferredAuthentications=password -r -P %s %s@%s:%s %s" %
896 (port, username, host, remote_path, local_path))
897 password_list = []
898 password_list.append(password)
899 remote_scp(command, password_list, log_filename, timeout)
902 def scp_between_remotes(src, dst, port, s_passwd, d_passwd, s_name, d_name,
903 s_path, d_path, log_filename=None, timeout=600):
905 Copy files from a remote host (guest) to another remote host (guest).
907 @param src/dst: Hostname or IP address of src and dst
908 @param s_name/d_name: Username (if required)
909 @param s_passwd/d_passwd: Password (if required)
910 @param s_path/d_path: Path on the remote machine where we are copying
911 from/to
912 @param log_filename: If specified, log all output to this file
913 @param timeout: The time duration (in seconds) to wait for the transfer
914 to complete.
916 @return: True on success and False on failure.
918 command = ("scp -v -o UserKnownHostsFile=/dev/null -o "
919 "PreferredAuthentications=password -r -P %s %s@%s:%s %s@%s:%s" %
920 (port, s_name, src, s_path, d_name, dst, d_path))
921 password_list = []
922 password_list.append(s_passwd)
923 password_list.append(d_passwd)
924 return remote_scp(command, password_list, log_filename, timeout)
927 def copy_files_to(address, client, username, password, port, local_path,
928 remote_path, log_filename=None, verbose=False, timeout=600):
930 Copy files to a remote host (guest) using the selected client.
932 @param client: Type of transfer client
933 @param username: Username (if required)
934 @param password: Password (if requried)
935 @param local_path: Path on the local machine where we are copying from
936 @param remote_path: Path on the remote machine where we are copying to
937 @param address: Address of remote host(guest)
938 @param log_filename: If specified, log all output to this file (SCP only)
939 @param verbose: If True, log some stats using logging.debug (RSS only)
940 @param timeout: The time duration (in seconds) to wait for the transfer to
941 complete.
942 @raise: Whatever remote_scp() raises
944 if client == "scp":
945 scp_to_remote(address, port, username, password, local_path,
946 remote_path, log_filename, timeout)
947 elif client == "rss":
948 log_func = None
949 if verbose:
950 log_func = logging.debug
951 c = rss_client.FileUploadClient(address, port, log_func)
952 c.upload(local_path, remote_path, timeout)
953 c.close()
956 def copy_files_from(address, client, username, password, port, remote_path,
957 local_path, log_filename=None, verbose=False, timeout=600):
959 Copy files from a remote host (guest) using the selected client.
961 @param client: Type of transfer client
962 @param username: Username (if required)
963 @param password: Password (if requried)
964 @param remote_path: Path on the remote machine where we are copying from
965 @param local_path: Path on the local machine where we are copying to
966 @param address: Address of remote host(guest)
967 @param log_filename: If specified, log all output to this file (SCP only)
968 @param verbose: If True, log some stats using logging.debug (RSS only)
969 @param timeout: The time duration (in seconds) to wait for the transfer to
970 complete.
971 @raise: Whatever remote_scp() raises
973 if client == "scp":
974 scp_from_remote(address, port, username, password, remote_path,
975 local_path, log_filename, timeout)
976 elif client == "rss":
977 log_func = None
978 if verbose:
979 log_func = logging.debug
980 c = rss_client.FileDownloadClient(address, port, log_func)
981 c.download(remote_path, local_path, timeout)
982 c.close()
985 # The following are utility functions related to ports.
987 def is_port_free(port, address):
989 Return True if the given port is available for use.
991 @param port: Port number
993 try:
994 s = socket.socket()
995 #s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
996 if address == "localhost":
997 s.bind(("localhost", port))
998 free = True
999 else:
1000 s.connect((address, port))
1001 free = False
1002 except socket.error:
1003 if address == "localhost":
1004 free = False
1005 else:
1006 free = True
1007 s.close()
1008 return free
1011 def find_free_port(start_port, end_port, address="localhost"):
1013 Return a host free port in the range [start_port, end_port].
1015 @param start_port: First port that will be checked.
1016 @param end_port: Port immediately after the last one that will be checked.
1018 for i in range(start_port, end_port):
1019 if is_port_free(i, address):
1020 return i
1021 return None
1024 def find_free_ports(start_port, end_port, count, address="localhost"):
1026 Return count of host free ports in the range [start_port, end_port].
1028 @count: Initial number of ports known to be free in the range.
1029 @param start_port: First port that will be checked.
1030 @param end_port: Port immediately after the last one that will be checked.
1032 ports = []
1033 i = start_port
1034 while i < end_port and count > 0:
1035 if is_port_free(i, address):
1036 ports.append(i)
1037 count -= 1
1038 i += 1
1039 return ports
1042 # An easy way to log lines to files when the logging system can't be used
1044 _open_log_files = {}
1045 _log_file_dir = "/tmp"
1048 def log_line(filename, line):
1050 Write a line to a file. '\n' is appended to the line.
1052 @param filename: Path of file to write to, either absolute or relative to
1053 the dir set by set_log_file_dir().
1054 @param line: Line to write.
1056 global _open_log_files, _log_file_dir
1057 if filename not in _open_log_files:
1058 path = get_path(_log_file_dir, filename)
1059 try:
1060 os.makedirs(os.path.dirname(path))
1061 except OSError:
1062 pass
1063 _open_log_files[filename] = open(path, "w")
1064 timestr = time.strftime("%Y-%m-%d %H:%M:%S")
1065 _open_log_files[filename].write("%s: %s\n" % (timestr, line))
1066 _open_log_files[filename].flush()
1069 def set_log_file_dir(dir):
1071 Set the base directory for log files created by log_line().
1073 @param dir: Directory for log files.
1075 global _log_file_dir
1076 _log_file_dir = dir
1079 # The following are miscellaneous utility functions.
1081 def get_path(base_path, user_path):
1083 Translate a user specified path to a real path.
1084 If user_path is relative, append it to base_path.
1085 If user_path is absolute, return it as is.
1087 @param base_path: The base path of relative user specified paths.
1088 @param user_path: The user specified path.
1090 if os.path.isabs(user_path):
1091 return user_path
1092 else:
1093 return os.path.join(base_path, user_path)
1096 def generate_random_string(length):
1098 Return a random string using alphanumeric characters.
1100 @length: length of the string that will be generated.
1102 r = random.SystemRandom()
1103 str = ""
1104 chars = string.letters + string.digits
1105 while length > 0:
1106 str += r.choice(chars)
1107 length -= 1
1108 return str
1110 def generate_random_id():
1112 Return a random string suitable for use as a qemu id.
1114 return "id" + generate_random_string(6)
1117 def generate_tmp_file_name(file, ext=None, dir='/tmp/'):
1119 Returns a temporary file name. The file is not created.
1121 while True:
1122 file_name = (file + '-' + time.strftime("%Y%m%d-%H%M%S-") +
1123 generate_random_string(4))
1124 if ext:
1125 file_name += '.' + ext
1126 file_name = os.path.join(dir, file_name)
1127 if not os.path.exists(file_name):
1128 break
1130 return file_name
1133 def format_str_for_message(str):
1135 Format str so that it can be appended to a message.
1136 If str consists of one line, prefix it with a space.
1137 If str consists of multiple lines, prefix it with a newline.
1139 @param str: string that will be formatted.
1141 lines = str.splitlines()
1142 num_lines = len(lines)
1143 str = "\n".join(lines)
1144 if num_lines == 0:
1145 return ""
1146 elif num_lines == 1:
1147 return " " + str
1148 else:
1149 return "\n" + str
1152 def wait_for(func, timeout, first=0.0, step=1.0, text=None):
1154 If func() evaluates to True before timeout expires, return the
1155 value of func(). Otherwise return None.
1157 @brief: Wait until func() evaluates to True.
1159 @param timeout: Timeout in seconds
1160 @param first: Time to sleep before first attempt
1161 @param steps: Time to sleep between attempts in seconds
1162 @param text: Text to print while waiting, for debug purposes
1164 start_time = time.time()
1165 end_time = time.time() + timeout
1167 time.sleep(first)
1169 while time.time() < end_time:
1170 if text:
1171 logging.debug("%s (%f secs)", text, (time.time() - start_time))
1173 output = func()
1174 if output:
1175 return output
1177 time.sleep(step)
1179 return None
1182 def get_hash_from_file(hash_path, dvd_basename):
1184 Get the a hash from a given DVD image from a hash file
1185 (Hash files are usually named MD5SUM or SHA1SUM and are located inside the
1186 download directories of the DVDs)
1188 @param hash_path: Local path to a hash file.
1189 @param cd_image: Basename of a CD image
1191 hash_file = open(hash_path, 'r')
1192 for line in hash_file.readlines():
1193 if dvd_basename in line:
1194 return line.split()[0]
1197 def run_tests(parser, job):
1199 Runs the sequence of KVM tests based on the list of dictionaries
1200 generated by the configuration system, handling dependencies.
1202 @param parser: Config parser object.
1203 @param job: Autotest job object.
1205 @return: True, if all tests ran passed, False if any of them failed.
1207 for i, d in enumerate(parser.get_dicts()):
1208 logging.info("Test %4d: %s" % (i + 1, d["shortname"]))
1210 status_dict = {}
1211 failed = False
1213 for dict in parser.get_dicts():
1214 if dict.get("skip") == "yes":
1215 continue
1216 dependencies_satisfied = True
1217 for dep in dict.get("dep"):
1218 for test_name in status_dict.keys():
1219 if not dep in test_name:
1220 continue
1221 # So the only really non-fatal state is WARN,
1222 # All the others make it not safe to proceed with dependency
1223 # execution
1224 if status_dict[test_name] not in ['GOOD', 'WARN']:
1225 dependencies_satisfied = False
1226 break
1227 test_iterations = int(dict.get("iterations", 1))
1228 test_tag = dict.get("shortname")
1230 if dependencies_satisfied:
1231 # Setting up profilers during test execution.
1232 profilers = dict.get("profilers", "").split()
1233 for profiler in profilers:
1234 job.profilers.add(profiler)
1235 # We need only one execution, profiled, hence we're passing
1236 # the profile_only parameter to job.run_test().
1237 profile_only = bool(profilers) or None
1238 current_status = job.run_test_detail(dict.get("vm_type"),
1239 params=dict,
1240 tag=test_tag,
1241 iterations=test_iterations,
1242 profile_only=profile_only)
1243 for profiler in profilers:
1244 job.profilers.delete(profiler)
1245 else:
1246 # We will force the test to fail as TestNA during preprocessing
1247 dict['dependency_failed'] = 'yes'
1248 current_status = job.run_test_detail(dict.get("vm_type"),
1249 params=dict,
1250 tag=test_tag,
1251 iterations=test_iterations)
1253 if not current_status:
1254 failed = True
1255 status_dict[dict.get("name")] = current_status
1257 return not failed
1260 def display_attributes(instance):
1262 Inspects a given class instance attributes and displays them, convenient
1263 for debugging.
1265 logging.debug("Attributes set:")
1266 for member in inspect.getmembers(instance):
1267 name, value = member
1268 attribute = getattr(instance, name)
1269 if not (name.startswith("__") or callable(attribute) or not value):
1270 logging.debug(" %s: %s", name, value)
1273 def get_full_pci_id(pci_id):
1275 Get full PCI ID of pci_id.
1277 @param pci_id: PCI ID of a device.
1279 cmd = "lspci -D | awk '/%s/ {print $1}'" % pci_id
1280 status, full_id = commands.getstatusoutput(cmd)
1281 if status != 0:
1282 return None
1283 return full_id
1286 def get_vendor_from_pci_id(pci_id):
1288 Check out the device vendor ID according to pci_id.
1290 @param pci_id: PCI ID of a device.
1292 cmd = "lspci -n | awk '/%s/ {print $3}'" % pci_id
1293 return re.sub(":", " ", commands.getoutput(cmd))
1296 def get_cpu_flags():
1298 Returns a list of the CPU flags
1300 flags_re = re.compile(r'^flags\s*:(.*)')
1301 for line in open('/proc/cpuinfo').readlines():
1302 match = flags_re.match(line)
1303 if match:
1304 return match.groups()[0].split()
1305 return []
1308 def get_cpu_vendor(cpu_flags=[], verbose=True):
1310 Returns the name of the CPU vendor, either intel, amd or unknown
1312 if not cpu_flags:
1313 cpu_flags = get_cpu_flags()
1315 if 'vmx' in cpu_flags:
1316 vendor = 'intel'
1317 elif 'svm' in cpu_flags:
1318 vendor = 'amd'
1319 else:
1320 vendor = 'unknown'
1322 if verbose:
1323 logging.debug("Detected CPU vendor as '%s'", vendor)
1324 return vendor
1327 def get_archive_tarball_name(source_dir, tarball_name, compression):
1329 Get the name for a tarball file, based on source, name and compression
1331 if tarball_name is None:
1332 tarball_name = os.path.basename(source_dir)
1334 if not tarball_name.endswith('.tar'):
1335 tarball_name = '%s.tar' % tarball_name
1337 if compression and not tarball_name.endswith('.%s' % compression):
1338 tarball_name = '%s.%s' % (tarball_name, compression)
1340 return tarball_name
1343 def archive_as_tarball(source_dir, dest_dir, tarball_name=None,
1344 compression='bz2', verbose=True):
1346 Saves the given source directory to the given destination as a tarball
1348 If the name of the archive is omitted, it will be taken from the
1349 source_dir. If it is an absolute path, dest_dir will be ignored. But,
1350 if both the destination directory and tarball anem is given, and the
1351 latter is not an absolute path, they will be combined.
1353 For archiving directory '/tmp' in '/net/server/backup' as file
1354 'tmp.tar.bz2', simply use:
1356 >>> virt_utils.archive_as_tarball('/tmp', '/net/server/backup')
1358 To save the file it with a different name, say 'host1-tmp.tar.bz2'
1359 and save it under '/net/server/backup', use:
1361 >>> virt_utils.archive_as_tarball('/tmp', '/net/server/backup',
1362 'host1-tmp')
1364 To save with gzip compression instead (resulting in the file
1365 '/net/server/backup/host1-tmp.tar.gz'), use:
1367 >>> virt_utils.archive_as_tarball('/tmp', '/net/server/backup',
1368 'host1-tmp', 'gz')
1370 tarball_name = get_archive_tarball_name(source_dir,
1371 tarball_name,
1372 compression)
1373 if not os.path.isabs(tarball_name):
1374 tarball_path = os.path.join(dest_dir, tarball_name)
1375 else:
1376 tarball_path = tarball_name
1378 if verbose:
1379 logging.debug('Archiving %s as %s' % (source_dir,
1380 tarball_path))
1382 os.chdir(os.path.dirname(source_dir))
1383 tarball = tarfile.TarFile(name=tarball_path, mode='w')
1384 tarball = tarball.open(name=tarball_path, mode='w:%s' % compression)
1385 tarball.add(os.path.basename(source_dir))
1386 tarball.close()
1389 class Thread(threading.Thread):
1391 Run a function in a background thread.
1393 def __init__(self, target, args=(), kwargs={}):
1395 Initialize the instance.
1397 @param target: Function to run in the thread.
1398 @param args: Arguments to pass to target.
1399 @param kwargs: Keyword arguments to pass to target.
1401 threading.Thread.__init__(self)
1402 self._target = target
1403 self._args = args
1404 self._kwargs = kwargs
1407 def run(self):
1409 Run target (passed to the constructor). No point in calling this
1410 function directly. Call start() to make this function run in a new
1411 thread.
1413 self._e = None
1414 self._retval = None
1415 try:
1416 try:
1417 self._retval = self._target(*self._args, **self._kwargs)
1418 except:
1419 self._e = sys.exc_info()
1420 raise
1421 finally:
1422 # Avoid circular references (start() may be called only once so
1423 # it's OK to delete these)
1424 del self._target, self._args, self._kwargs
1427 def join(self, timeout=None, suppress_exception=False):
1429 Join the thread. If target raised an exception, re-raise it.
1430 Otherwise, return the value returned by target.
1432 @param timeout: Timeout value to pass to threading.Thread.join().
1433 @param suppress_exception: If True, don't re-raise the exception.
1435 threading.Thread.join(self, timeout)
1436 try:
1437 if self._e:
1438 if not suppress_exception:
1439 # Because the exception was raised in another thread, we
1440 # need to explicitly insert the current context into it
1441 s = error.exception_context(self._e[1])
1442 s = error.join_contexts(error.get_context(), s)
1443 error.set_exception_context(self._e[1], s)
1444 raise self._e[0], self._e[1], self._e[2]
1445 else:
1446 return self._retval
1447 finally:
1448 # Avoid circular references (join() may be called multiple times
1449 # so we can't delete these)
1450 self._e = None
1451 self._retval = None
1454 def parallel(targets):
1456 Run multiple functions in parallel.
1458 @param targets: A sequence of tuples or functions. If it's a sequence of
1459 tuples, each tuple will be interpreted as (target, args, kwargs) or
1460 (target, args) or (target,) depending on its length. If it's a
1461 sequence of functions, the functions will be called without
1462 arguments.
1463 @return: A list of the values returned by the functions called.
1465 threads = []
1466 for target in targets:
1467 if isinstance(target, tuple) or isinstance(target, list):
1468 t = Thread(*target)
1469 else:
1470 t = Thread(target)
1471 threads.append(t)
1472 t.start()
1473 return [t.join() for t in threads]
1476 class VirtLoggingConfig(logging_config.LoggingConfig):
1478 Used with the sole purpose of providing convenient logging setup
1479 for the KVM test auxiliary programs.
1481 def configure_logging(self, results_dir=None, verbose=False):
1482 super(VirtLoggingConfig, self).configure_logging(use_console=True,
1483 verbose=verbose)
1486 class PciAssignable(object):
1488 Request PCI assignable devices on host. It will check whether to request
1489 PF (physical Functions) or VF (Virtual Functions).
1491 def __init__(self, type="vf", driver=None, driver_option=None,
1492 names=None, devices_requested=None):
1494 Initialize parameter 'type' which could be:
1495 vf: Virtual Functions
1496 pf: Physical Function (actual hardware)
1497 mixed: Both includes VFs and PFs
1499 If pass through Physical NIC cards, we need to specify which devices
1500 to be assigned, e.g. 'eth1 eth2'.
1502 If pass through Virtual Functions, we need to specify how many vfs
1503 are going to be assigned, e.g. passthrough_count = 8 and max_vfs in
1504 config file.
1506 @param type: PCI device type.
1507 @param driver: Kernel module for the PCI assignable device.
1508 @param driver_option: Module option to specify the maximum number of
1509 VFs (eg 'max_vfs=7')
1510 @param names: Physical NIC cards correspondent network interfaces,
1511 e.g.'eth1 eth2 ...'
1512 @param devices_requested: Number of devices being requested.
1514 self.type = type
1515 self.driver = driver
1516 self.driver_option = driver_option
1517 if names:
1518 self.name_list = names.split()
1519 if devices_requested:
1520 self.devices_requested = int(devices_requested)
1521 else:
1522 self.devices_requested = None
1525 def _get_pf_pci_id(self, name, search_str):
1527 Get the PF PCI ID according to name.
1529 @param name: Name of the PCI device.
1530 @param search_str: Search string to be used on lspci.
1532 cmd = "ethtool -i %s | awk '/bus-info/ {print $2}'" % name
1533 s, pci_id = commands.getstatusoutput(cmd)
1534 if not (s or "Cannot get driver information" in pci_id):
1535 return pci_id[5:]
1536 cmd = "lspci | awk '/%s/ {print $1}'" % search_str
1537 pci_ids = [id for id in commands.getoutput(cmd).splitlines()]
1538 nic_id = int(re.search('[0-9]+', name).group(0))
1539 if (len(pci_ids) - 1) < nic_id:
1540 return None
1541 return pci_ids[nic_id]
1544 def _release_dev(self, pci_id):
1546 Release a single PCI device.
1548 @param pci_id: PCI ID of a given PCI device.
1550 base_dir = "/sys/bus/pci"
1551 full_id = get_full_pci_id(pci_id)
1552 vendor_id = get_vendor_from_pci_id(pci_id)
1553 drv_path = os.path.join(base_dir, "devices/%s/driver" % full_id)
1554 if 'pci-stub' in os.readlink(drv_path):
1555 cmd = "echo '%s' > %s/new_id" % (vendor_id, drv_path)
1556 if os.system(cmd):
1557 return False
1559 stub_path = os.path.join(base_dir, "drivers/pci-stub")
1560 cmd = "echo '%s' > %s/unbind" % (full_id, stub_path)
1561 if os.system(cmd):
1562 return False
1564 driver = self.dev_drivers[pci_id]
1565 cmd = "echo '%s' > %s/bind" % (full_id, driver)
1566 if os.system(cmd):
1567 return False
1569 return True
1572 def get_vf_devs(self):
1574 Catch all VFs PCI IDs.
1576 @return: List with all PCI IDs for the Virtual Functions avaliable
1578 if not self.sr_iov_setup():
1579 return []
1581 cmd = "lspci | awk '/Virtual Function/ {print $1}'"
1582 return commands.getoutput(cmd).split()
1585 def get_pf_devs(self):
1587 Catch all PFs PCI IDs.
1589 @return: List with all PCI IDs for the physical hardware requested
1591 pf_ids = []
1592 for name in self.name_list:
1593 pf_id = self._get_pf_pci_id(name, "Ethernet")
1594 if not pf_id:
1595 continue
1596 pf_ids.append(pf_id)
1597 return pf_ids
1600 def get_devs(self, count):
1602 Check out all devices' PCI IDs according to their name.
1604 @param count: count number of PCI devices needed for pass through
1605 @return: a list of all devices' PCI IDs
1607 if self.type == "vf":
1608 vf_ids = self.get_vf_devs()
1609 elif self.type == "pf":
1610 vf_ids = self.get_pf_devs()
1611 elif self.type == "mixed":
1612 vf_ids = self.get_vf_devs()
1613 vf_ids.extend(self.get_pf_devs())
1614 return vf_ids[0:count]
1617 def get_vfs_count(self):
1619 Get VFs count number according to lspci.
1621 # FIXME: Need to think out a method of identify which
1622 # 'virtual function' belongs to which physical card considering
1623 # that if the host has more than one 82576 card. PCI_ID?
1624 cmd = "lspci | grep 'Virtual Function' | wc -l"
1625 return int(commands.getoutput(cmd))
1628 def check_vfs_count(self):
1630 Check VFs count number according to the parameter driver_options.
1632 # Network card 82576 has two network interfaces and each can be
1633 # virtualized up to 7 virtual functions, therefore we multiply
1634 # two for the value of driver_option 'max_vfs'.
1635 expected_count = int((re.findall("(\d)", self.driver_option)[0])) * 2
1636 return (self.get_vfs_count == expected_count)
1639 def is_binded_to_stub(self, full_id):
1641 Verify whether the device with full_id is already binded to pci-stub.
1643 @param full_id: Full ID for the given PCI device
1645 base_dir = "/sys/bus/pci"
1646 stub_path = os.path.join(base_dir, "drivers/pci-stub")
1647 if os.path.exists(os.path.join(stub_path, full_id)):
1648 return True
1649 return False
1652 def sr_iov_setup(self):
1654 Ensure the PCI device is working in sr_iov mode.
1656 Check if the PCI hardware device drive is loaded with the appropriate,
1657 parameters (number of VFs), and if it's not, perform setup.
1659 @return: True, if the setup was completed successfuly, False otherwise.
1661 re_probe = False
1662 s, o = commands.getstatusoutput('lsmod | grep %s' % self.driver)
1663 if s:
1664 re_probe = True
1665 elif not self.check_vfs_count():
1666 os.system("modprobe -r %s" % self.driver)
1667 re_probe = True
1668 else:
1669 return True
1671 # Re-probe driver with proper number of VFs
1672 if re_probe:
1673 cmd = "modprobe %s %s" % (self.driver, self.driver_option)
1674 logging.info("Loading the driver '%s' with option '%s'",
1675 self.driver, self.driver_option)
1676 s, o = commands.getstatusoutput(cmd)
1677 if s:
1678 return False
1679 return True
1682 def request_devs(self):
1684 Implement setup process: unbind the PCI device and then bind it
1685 to the pci-stub driver.
1687 @return: a list of successfully requested devices' PCI IDs.
1689 base_dir = "/sys/bus/pci"
1690 stub_path = os.path.join(base_dir, "drivers/pci-stub")
1692 self.pci_ids = self.get_devs(self.devices_requested)
1693 logging.debug("The following pci_ids were found: %s", self.pci_ids)
1694 requested_pci_ids = []
1695 self.dev_drivers = {}
1697 # Setup all devices specified for assignment to guest
1698 for pci_id in self.pci_ids:
1699 full_id = get_full_pci_id(pci_id)
1700 if not full_id:
1701 continue
1702 drv_path = os.path.join(base_dir, "devices/%s/driver" % full_id)
1703 dev_prev_driver = os.path.realpath(os.path.join(drv_path,
1704 os.readlink(drv_path)))
1705 self.dev_drivers[pci_id] = dev_prev_driver
1707 # Judge whether the device driver has been binded to stub
1708 if not self.is_binded_to_stub(full_id):
1709 logging.debug("Binding device %s to stub", full_id)
1710 vendor_id = get_vendor_from_pci_id(pci_id)
1711 stub_new_id = os.path.join(stub_path, 'new_id')
1712 unbind_dev = os.path.join(drv_path, 'unbind')
1713 stub_bind = os.path.join(stub_path, 'bind')
1715 info_write_to_files = [(vendor_id, stub_new_id),
1716 (full_id, unbind_dev),
1717 (full_id, stub_bind)]
1719 for content, file in info_write_to_files:
1720 try:
1721 utils.open_write_close(file, content)
1722 except IOError:
1723 logging.debug("Failed to write %s to file %s", content,
1724 file)
1725 continue
1727 if not self.is_binded_to_stub(full_id):
1728 logging.error("Binding device %s to stub failed", pci_id)
1729 continue
1730 else:
1731 logging.debug("Device %s already binded to stub", pci_id)
1732 requested_pci_ids.append(pci_id)
1733 self.pci_ids = requested_pci_ids
1734 return self.pci_ids
1737 def release_devs(self):
1739 Release all PCI devices currently assigned to VMs back to the
1740 virtualization host.
1742 try:
1743 for pci_id in self.dev_drivers:
1744 if not self._release_dev(pci_id):
1745 logging.error("Failed to release device %s to host", pci_id)
1746 else:
1747 logging.info("Released device %s successfully", pci_id)
1748 except:
1749 return
1752 class KojiClient(object):
1754 Stablishes a connection with the build system, either koji or brew.
1756 This class provides convenience methods to retrieve information on packages
1757 and the packages themselves hosted on the build system. Packages should be
1758 specified in the KojiPgkSpec syntax.
1761 CMD_LOOKUP_ORDER = ['/usr/bin/brew', '/usr/bin/koji' ]
1763 CONFIG_MAP = {'/usr/bin/brew': '/etc/brewkoji.conf',
1764 '/usr/bin/koji': '/etc/koji.conf'}
1767 def __init__(self, cmd=None):
1769 Verifies whether the system has koji or brew installed, then loads
1770 the configuration file that will be used to download the files.
1772 @type cmd: string
1773 @param cmd: Optional command name, either 'brew' or 'koji'. If not
1774 set, get_default_command() is used and to look for
1775 one of them.
1776 @raise: ValueError
1778 if not KOJI_INSTALLED:
1779 raise ValueError('No koji/brew installed on the machine')
1781 # Instance variables used by many methods
1782 self.command = None
1783 self.config = None
1784 self.config_options = {}
1785 self.session = None
1787 # Set koji command or get default
1788 if cmd is None:
1789 self.command = self.get_default_command()
1790 else:
1791 self.command = cmd
1793 # Check koji command
1794 if not self.is_command_valid():
1795 raise ValueError('Koji command "%s" is not valid' % self.command)
1797 # Assuming command is valid, set configuration file and read it
1798 self.config = self.CONFIG_MAP[self.command]
1799 self.read_config()
1801 # Setup koji session
1802 server_url = self.config_options['server']
1803 session_options = self.get_session_options()
1804 self.session = koji.ClientSession(server_url,
1805 session_options)
1808 def read_config(self, check_is_valid=True):
1810 Reads options from the Koji configuration file
1812 By default it checks if the koji configuration is valid
1814 @type check_valid: boolean
1815 @param check_valid: whether to include a check on the configuration
1816 @raises: ValueError
1817 @returns: None
1819 if check_is_valid:
1820 if not self.is_config_valid():
1821 raise ValueError('Koji config "%s" is not valid' % self.config)
1823 config = ConfigParser.ConfigParser()
1824 config.read(self.config)
1826 basename = os.path.basename(self.command)
1827 for name, value in config.items(basename):
1828 self.config_options[name] = value
1831 def get_session_options(self):
1833 Filter only options necessary for setting up a cobbler client session
1835 @returns: only the options used for session setup
1837 session_options = {}
1838 for name, value in self.config_options.items():
1839 if name in ('user', 'password', 'debug_xmlrpc', 'debug'):
1840 session_options[name] = value
1841 return session_options
1844 def is_command_valid(self):
1846 Checks if the currently set koji command is valid
1848 @returns: True or False
1850 koji_command_ok = True
1852 if not os.path.isfile(self.command):
1853 logging.error('Koji command "%s" is not a regular file',
1854 self.command)
1855 koji_command_ok = False
1857 if not os.access(self.command, os.X_OK):
1858 logging.warn('Koji command "%s" is not executable: this is '
1859 'not fatal but indicates an unexpected situation',
1860 self.command)
1862 if not self.command in self.CONFIG_MAP.keys():
1863 logging.error('Koji command "%s" does not have a configuration '
1864 'file associated to it', self.command)
1865 koji_command_ok = False
1867 return koji_command_ok
1870 def is_config_valid(self):
1872 Checks if the currently set koji configuration is valid
1874 @returns: True or False
1876 koji_config_ok = True
1878 if not os.path.isfile(self.config):
1879 logging.error('Koji config "%s" is not a regular file', self.config)
1880 koji_config_ok = False
1882 if not os.access(self.config, os.R_OK):
1883 logging.error('Koji config "%s" is not readable', self.config)
1884 koji_config_ok = False
1886 config = ConfigParser.ConfigParser()
1887 config.read(self.config)
1888 basename = os.path.basename(self.command)
1889 if not config.has_section(basename):
1890 logging.error('Koji configuration file "%s" does not have a '
1891 'section "%s", named after the base name of the '
1892 'currently set koji command "%s"', self.config,
1893 basename, self.command)
1894 koji_config_ok = False
1896 return koji_config_ok
1899 def get_default_command(self):
1901 Looks up for koji or brew "binaries" on the system
1903 Systems with plain koji usually don't have a brew cmd, while systems
1904 with koji, have *both* koji and brew utilities. So we look for brew
1905 first, and if found, we consider that the system is configured for
1906 brew. If not, we consider this is a system with plain koji.
1908 @returns: either koji or brew command line executable path, or None
1910 koji_command = None
1911 for command in self.CMD_LOOKUP_ORDER:
1912 if os.path.isfile(command):
1913 koji_command = command
1914 break
1915 else:
1916 koji_command_basename = os.path.basename(koji_command)
1917 try:
1918 koji_command = os_dep.command(koji_command_basename)
1919 break
1920 except ValueError:
1921 pass
1922 return koji_command
1925 def get_pkg_info(self, pkg):
1927 Returns information from Koji on the package
1929 @type pkg: KojiPkgSpec
1930 @param pkg: information about the package, as a KojiPkgSpec instance
1932 @returns: information from Koji about the specified package
1934 info = {}
1935 if pkg.build is not None:
1936 info = self.session.getBuild(int(pkg.build))
1937 elif pkg.tag is not None and pkg.package is not None:
1938 builds = self.session.listTagged(pkg.tag,
1939 latest=True,
1940 inherit=True,
1941 package=pkg.package)
1942 if builds:
1943 info = builds[0]
1944 return info
1947 def is_pkg_valid(self, pkg):
1949 Checks if this package is altogether valid on Koji
1951 This verifies if the build or tag specified in the package
1952 specification actually exist on the Koji server
1954 @returns: True or False
1956 valid = True
1957 if pkg.build:
1958 if not self.is_pkg_spec_build_valid(pkg):
1959 valid = False
1960 elif pkg.tag:
1961 if not self.is_pkg_spec_tag_valid(pkg):
1962 valid = False
1963 else:
1964 valid = False
1965 return valid
1968 def is_pkg_spec_build_valid(self, pkg):
1970 Checks if build is valid on Koji
1972 @param pkg: a Pkg instance
1974 if pkg.build is not None:
1975 info = self.session.getBuild(int(pkg.build))
1976 if info:
1977 return True
1978 return False
1981 def is_pkg_spec_tag_valid(self, pkg):
1983 Checks if tag is valid on Koji
1985 @type pkg: KojiPkgSpec
1986 @param pkg: a package specification
1988 if pkg.tag is not None:
1989 tag = self.session.getTag(pkg.tag)
1990 if tag:
1991 return True
1992 return False
1995 def get_pkg_rpm_info(self, pkg, arch=None):
1997 Returns a list of infomation on the RPM packages found on koji
1999 @type pkg: KojiPkgSpec
2000 @param pkg: a package specification
2001 @type arch: string
2002 @param arch: packages built for this architecture, but also including
2003 architecture independent (noarch) packages
2005 if arch is None:
2006 arch = utils.get_arch()
2007 rpms = []
2008 info = self.get_pkg_info(pkg)
2009 if info:
2010 rpms = self.session.listRPMs(buildID=info['id'],
2011 arches=[arch, 'noarch'])
2012 if pkg.subpackages:
2013 rpms = [d for d in rpms if d['name'] in pkg.subpackages]
2014 return rpms
2017 def get_pkg_rpm_names(self, pkg, arch=None):
2019 Gets the names for the RPM packages specified in pkg
2021 @type pkg: KojiPkgSpec
2022 @param pkg: a package specification
2023 @type arch: string
2024 @param arch: packages built for this architecture, but also including
2025 architecture independent (noarch) packages
2027 if arch is None:
2028 arch = utils.get_arch()
2029 rpms = self.get_pkg_rpm_info(pkg, arch)
2030 return [rpm['name'] for rpm in rpms]
2033 def get_pkg_rpm_file_names(self, pkg, arch=None):
2035 Gets the file names for the RPM packages specified in pkg
2037 @type pkg: KojiPkgSpec
2038 @param pkg: a package specification
2039 @type arch: string
2040 @param arch: packages built for this architecture, but also including
2041 architecture independent (noarch) packages
2043 if arch is None:
2044 arch = utils.get_arch()
2045 rpm_names = []
2046 rpms = self.get_pkg_rpm_info(pkg, arch)
2047 for rpm in rpms:
2048 arch_rpm_name = koji.pathinfo.rpm(rpm)
2049 rpm_name = os.path.basename(arch_rpm_name)
2050 rpm_names.append(rpm_name)
2051 return rpm_names
2054 def get_pkg_urls(self, pkg, arch=None):
2056 Gets the urls for the packages specified in pkg
2058 @type pkg: KojiPkgSpec
2059 @param pkg: a package specification
2060 @type arch: string
2061 @param arch: packages built for this architecture, but also including
2062 architecture independent (noarch) packages
2064 info = self.get_pkg_info(pkg)
2065 rpms = self.get_pkg_rpm_info(pkg, arch)
2066 rpm_urls = []
2067 for rpm in rpms:
2068 rpm_name = koji.pathinfo.rpm(rpm)
2069 url = ("%s/%s/%s/%s/%s" % (self.config_options['pkgurl'],
2070 info['package_name'],
2071 info['version'], info['release'],
2072 rpm_name))
2073 rpm_urls.append(url)
2074 return rpm_urls
2077 def get_pkgs(self, pkg, dst_dir, arch=None):
2079 Download the packages
2081 @type pkg: KojiPkgSpec
2082 @param pkg: a package specification
2083 @type dst_dir: string
2084 @param dst_dir: the destination directory, where the downloaded
2085 packages will be saved on
2086 @type arch: string
2087 @param arch: packages built for this architecture, but also including
2088 architecture independent (noarch) packages
2090 rpm_urls = self.get_pkg_urls(pkg, arch)
2091 for url in rpm_urls:
2092 utils.get_file(url,
2093 os.path.join(dst_dir, os.path.basename(url)))
2096 DEFAULT_KOJI_TAG = None
2097 def set_default_koji_tag(tag):
2099 Sets the default tag that will be used
2101 global DEFAULT_KOJI_TAG
2102 DEFAULT_KOJI_TAG = tag
2105 def get_default_koji_tag():
2106 return DEFAULT_KOJI_TAG
2109 class KojiPkgSpec:
2111 A package specification syntax parser for Koji
2113 This holds information on either tag or build, and packages to be fetched
2114 from koji and possibly installed (features external do this class).
2116 New objects can be created either by providing information in the textual
2117 format or by using the actual parameters for tag, build, package and sub-
2118 packages. The textual format is useful for command line interfaces and
2119 configuration files, while using parameters is better for using this in
2120 a programatic fashion.
2122 The following sets of examples are interchangeable. Specifying all packages
2123 part of build number 1000:
2125 >>> from kvm_utils import KojiPkgSpec
2126 >>> pkg = KojiPkgSpec('1000')
2128 >>> pkg = KojiPkgSpec(build=1000)
2130 Specifying only a subset of packages of build number 1000:
2132 >>> pkg = KojiPkgSpec('1000:kernel,kernel-devel')
2134 >>> pkg = KojiPkgSpec(build=1000,
2135 subpackages=['kernel', 'kernel-devel'])
2137 Specifying the latest build for the 'kernel' package tagged with 'dist-f14':
2139 >>> pkg = KojiPkgSpec('dist-f14:kernel')
2141 >>> pkg = KojiPkgSpec(tag='dist-f14', package='kernel')
2143 Specifying the 'kernel' package using the default tag:
2145 >>> kvm_utils.set_default_koji_tag('dist-f14')
2146 >>> pkg = KojiPkgSpec('kernel')
2148 >>> pkg = KojiPkgSpec(package='kernel')
2150 Specifying the 'kernel' package using the default tag:
2152 >>> kvm_utils.set_default_koji_tag('dist-f14')
2153 >>> pkg = KojiPkgSpec('kernel')
2155 >>> pkg = KojiPkgSpec(package='kernel')
2157 If you do not specify a default tag, and give a package name without an
2158 explicit tag, your package specification is considered invalid:
2160 >>> print kvm_utils.get_default_koji_tag()
2161 None
2162 >>> print kvm_utils.KojiPkgSpec('kernel').is_valid()
2163 False
2165 >>> print kvm_utils.KojiPkgSpec(package='kernel').is_valid()
2166 False
2169 SEP = ':'
2171 def __init__(self, text='', tag=None, build=None,
2172 package=None, subpackages=[]):
2174 Instantiates a new KojiPkgSpec object
2176 @type text: string
2177 @param text: a textual representation of a package on Koji that
2178 will be parsed
2179 @type tag: string
2180 @param tag: a koji tag, example: Fedora-14-RELEASE
2181 (see U{http://fedoraproject.org/wiki/Koji#Tags_and_Targets})
2182 @type build: number
2183 @param build: a koji build, example: 1001
2184 (see U{http://fedoraproject.org/wiki/Koji#Koji_Architecture})
2185 @type package: string
2186 @param package: a koji package, example: python
2187 (see U{http://fedoraproject.org/wiki/Koji#Koji_Architecture})
2188 @type subpackages: list of strings
2189 @param subpackages: a list of package names, usually a subset of
2190 the RPM packages generated by a given build
2193 # Set to None to indicate 'not set' (and be able to use 'is')
2194 self.tag = None
2195 self.build = None
2196 self.package = None
2197 self.subpackages = []
2199 self.default_tag = None
2201 # Textual representation takes precedence (most common use case)
2202 if text:
2203 self.parse(text)
2204 else:
2205 self.tag = tag
2206 self.build = build
2207 self.package = package
2208 self.subpackages = subpackages
2210 # Set the default tag, if set, as a fallback
2211 if not self.build and not self.tag:
2212 default_tag = get_default_koji_tag()
2213 if default_tag is not None:
2214 self.tag = default_tag
2217 def parse(self, text):
2219 Parses a textual representation of a package specification
2221 @type text: string
2222 @param text: textual representation of a package in koji
2224 parts = text.count(self.SEP) + 1
2225 if parts == 1:
2226 if text.isdigit():
2227 self.build = text
2228 else:
2229 self.package = text
2230 elif parts == 2:
2231 part1, part2 = text.split(self.SEP)
2232 if part1.isdigit():
2233 self.build = part1
2234 self.subpackages = part2.split(',')
2235 else:
2236 self.tag = part1
2237 self.package = part2
2238 elif parts >= 3:
2239 # Instead of erroring on more arguments, we simply ignore them
2240 # This makes the parser suitable for future syntax additions, such
2241 # as specifying the package architecture
2242 part1, part2, part3 = text.split(self.SEP)[0:3]
2243 self.tag = part1
2244 self.package = part2
2245 self.subpackages = part3.split(',')
2248 def _is_invalid_neither_tag_or_build(self):
2250 Checks if this package is invalid due to not having either a valid
2251 tag or build set, that is, both are empty.
2253 @returns: True if this is invalid and False if it's valid
2255 return (self.tag is None and self.build is None)
2258 def _is_invalid_package_but_no_tag(self):
2260 Checks if this package is invalid due to having a package name set
2261 but tag or build set, that is, both are empty.
2263 @returns: True if this is invalid and False if it's valid
2265 return (self.package and not self.tag)
2268 def _is_invalid_subpackages_but_no_main_package(self):
2270 Checks if this package is invalid due to having a tag set (this is Ok)
2271 but specifying subpackage names without specifying the main package
2272 name.
2274 Specifying subpackages without a main package name is only valid when
2275 a build is used instead of a tag.
2277 @returns: True if this is invalid and False if it's valid
2279 return (self.tag and self.subpackages and not self.package)
2282 def is_valid(self):
2284 Checks if this package specification is valid.
2286 Being valid means that it has enough and not conflicting information.
2287 It does not validate that the packages specified actually existe on
2288 the Koji server.
2290 @returns: True or False
2292 if self._is_invalid_neither_tag_or_build():
2293 return False
2294 elif self._is_invalid_package_but_no_tag():
2295 return False
2296 elif self._is_invalid_subpackages_but_no_main_package():
2297 return False
2299 return True
2302 def describe_invalid(self):
2304 Describes why this is not valid, in a human friendly way
2306 if self._is_invalid_neither_tag_or_build():
2307 return 'neither a tag or build are set, and of them should be set'
2308 elif self._is_invalid_package_but_no_tag():
2309 return 'package name specified but no tag is set'
2310 elif self._is_invalid_subpackages_but_no_main_package():
2311 return 'subpackages specified but no main package is set'
2313 return 'unkwown reason, seems to be valid'
2316 def describe(self):
2318 Describe this package specification, in a human friendly way
2320 @returns: package specification description
2322 if self.is_valid():
2323 description = ''
2324 if not self.subpackages:
2325 description += 'all subpackages from %s ' % self.package
2326 else:
2327 description += ('only subpackage(s) %s from package %s ' %
2328 (', '.join(self.subpackages), self.package))
2330 if self.build:
2331 description += 'from build %s' % self.build
2332 elif self.tag:
2333 description += 'tagged with %s' % self.tag
2334 else:
2335 raise ValueError, 'neither build or tag is set'
2337 return description
2338 else:
2339 return ('Invalid package specification: %s' %
2340 self.describe_invalid())
2343 def __repr__(self):
2344 return ("<KojiPkgSpec tag=%s build=%s pkg=%s subpkgs=%s>" %
2345 (self.tag, self.build, self.package,
2346 ", ".join(self.subpackages)))
2349 def umount(src, mount_point, type):
2351 Umount the src mounted in mount_point.
2353 @src: mount source
2354 @mount_point: mount point
2355 @type: file system type
2358 mount_string = "%s %s %s" % (src, mount_point, type)
2359 if mount_string in file("/etc/mtab").read():
2360 umount_cmd = "umount %s" % mount_point
2361 try:
2362 utils.system(umount_cmd)
2363 return True
2364 except error.CmdError:
2365 return False
2366 else:
2367 logging.debug("%s is not mounted under %s", src, mount_point)
2368 return True
2371 def mount(src, mount_point, type, perm="rw"):
2373 Mount the src into mount_point of the host.
2375 @src: mount source
2376 @mount_point: mount point
2377 @type: file system type
2378 @perm: mount premission
2380 umount(src, mount_point, type)
2381 mount_string = "%s %s %s %s" % (src, mount_point, type, perm)
2383 if mount_string in file("/etc/mtab").read():
2384 logging.debug("%s is already mounted in %s with %s",
2385 src, mount_point, perm)
2386 return True
2388 mount_cmd = "mount -t %s %s %s -o %s" % (type, src, mount_point, perm)
2389 try:
2390 utils.system(mount_cmd)
2391 except error.CmdError:
2392 return False
2394 logging.debug("Verify the mount through /etc/mtab")
2395 if mount_string in file("/etc/mtab").read():
2396 logging.debug("%s is successfully mounted", src)
2397 return True
2398 else:
2399 logging.error("Can't find mounted NFS share - /etc/mtab contents \n%s",
2400 file("/etc/mtab").read())
2401 return False
2404 def install_host_kernel(job, params):
2406 Install a host kernel, given the appropriate params.
2408 @param job: Job object.
2409 @param params: Dict with host kernel install params.
2411 install_type = params.get('host_kernel_install_type')
2413 rpm_url = params.get('host_kernel_rpm_url')
2415 koji_cmd = params.get('host_kernel_koji_cmd')
2416 koji_build = params.get('host_kernel_koji_build')
2417 koji_tag = params.get('host_kernel_koji_tag')
2419 git_repo = params.get('host_kernel_git_repo')
2420 git_branch = params.get('host_kernel_git_branch')
2421 git_commit = params.get('host_kernel_git_commit')
2422 patch_list = params.get('host_kernel_patch_list')
2423 if patch_list:
2424 patch_list = patch_list.split()
2425 kernel_config = params.get('host_kernel_config')
2427 if install_type == 'rpm':
2428 logging.info('Installing host kernel through rpm')
2429 dst = os.path.join("/tmp", os.path.basename(rpm_url))
2430 k = utils.get_file(rpm_url, dst)
2431 host_kernel = job.kernel(k)
2432 host_kernel.install(install_vmlinux=False)
2433 host_kernel.boot()
2435 elif install_type in ['koji', 'brew']:
2436 k_deps = KojiPkgSpec(tag=koji_tag, package='kernel',
2437 subpackages=['kernel-devel', 'kernel-firmware'])
2438 k = KojiPkgSpec(tag=koji_tag, package='kernel',
2439 subpackages=['kernel'])
2441 c = KojiClient(koji_cmd)
2442 logging.info('Fetching kernel dependencies (-devel, -firmware)')
2443 c.get_pkgs(k_deps, job.tmpdir)
2444 logging.info('Installing kernel dependencies (-devel, -firmware) '
2445 'through %s', install_type)
2446 k_deps_rpm_file_names = [os.path.join(job.tmpdir, rpm_file_name) for
2447 rpm_file_name in c.get_pkg_rpm_file_names(k_deps)]
2448 utils.run('rpm -U --force %s' % " ".join(k_deps_rpm_file_names))
2450 c.get_pkgs(k, job.tmpdir)
2451 k_rpm = os.path.join(job.tmpdir,
2452 c.get_pkg_rpm_file_names(k)[0])
2453 host_kernel = job.kernel(k_rpm)
2454 host_kernel.install(install_vmlinux=False)
2455 host_kernel.boot()
2457 elif install_type == 'git':
2458 logging.info('Chose to install host kernel through git, proceeding')
2459 repodir = os.path.join("/tmp", 'kernel_src')
2460 r = get_git_branch(git_repo, git_branch, repodir, git_commit)
2461 host_kernel = job.kernel(r)
2462 if patch_list:
2463 host_kernel.patch(patch_list)
2464 host_kernel.config(kernel_config)
2465 host_kernel.build()
2466 host_kernel.install()
2467 host_kernel.boot()
2469 else:
2470 logging.info('Chose %s, using the current kernel for the host',
2471 install_type)
2474 def if_nametoindex(ifname):
2476 Map an interface name into its corresponding index.
2477 Returns 0 on error, as 0 is not a valid index
2479 @param ifname: interface name
2481 index = 0
2482 ctrl_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
2483 ifr = struct.pack("16si", ifname, 0)
2484 r = fcntl.ioctl(ctrl_sock, SIOCGIFINDEX, ifr)
2485 index = struct.unpack("16si", r)[1]
2486 ctrl_sock.close()
2487 return index
2490 def vnet_hdr_probe(tapfd):
2492 Check if the IFF_VNET_HDR is support by tun.
2494 @param tapfd: the file descriptor of /dev/net/tun
2496 u = struct.pack("I", 0)
2497 try:
2498 r = fcntl.ioctl(tapfd, TUNGETFEATURES, u)
2499 except OverflowError:
2500 return False
2501 flags = struct.unpack("I", r)[0]
2502 if flags & IFF_VNET_HDR:
2503 return True
2504 else:
2505 return False
2508 def open_tap(devname, ifname, vnet_hdr=True):
2510 Open a tap device and returns its file descriptor which is used by
2511 fd=<fd> parameter of qemu-kvm.
2513 @param ifname: TAP interface name
2514 @param vnet_hdr: Whether enable the vnet header
2516 try:
2517 tapfd = os.open(devname, os.O_RDWR)
2518 except OSError, e:
2519 raise TAPModuleError(devname, "open", e)
2520 flags = IFF_TAP | IFF_NO_PI
2521 if vnet_hdr and vnet_hdr_probe(tapfd):
2522 flags |= IFF_VNET_HDR
2524 ifr = struct.pack("16sh", ifname, flags)
2525 try:
2526 r = fcntl.ioctl(tapfd, TUNSETIFF, ifr)
2527 except IOError, details:
2528 raise TAPCreationError(ifname, details)
2529 ifname = struct.unpack("16sh", r)[0].strip("\x00")
2530 return tapfd
2533 def add_to_bridge(ifname, brname):
2535 Add a TAP device to bridge
2537 @param ifname: Name of TAP device
2538 @param brname: Name of the bridge
2540 ctrl_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
2541 index = if_nametoindex(ifname)
2542 if index == 0:
2543 raise TAPNotExistError(ifname)
2544 ifr = struct.pack("16si", brname, index)
2545 try:
2546 r = fcntl.ioctl(ctrl_sock, SIOCBRADDIF, ifr)
2547 except IOError, details:
2548 raise BRAddIfError(ifname, brname, details)
2549 ctrl_sock.close()
2552 def bring_up_ifname(ifname):
2554 Bring up an interface
2556 @param ifname: Name of the interface
2558 ctrl_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
2559 ifr = struct.pack("16si", ifname, IFF_UP)
2560 try:
2561 fcntl.ioctl(ctrl_sock, SIOCSIFFLAGS, ifr)
2562 except IOError:
2563 raise TAPBringUpError(ifname)
2564 ctrl_sock.close()
2567 def if_set_macaddress(ifname, mac):
2569 Set the mac address for an interface
2571 @param ifname: Name of the interface
2572 @mac: Mac address
2574 ctrl_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
2576 ifr = struct.pack("256s", ifname)
2577 try:
2578 mac_dev = fcntl.ioctl(ctrl_sock, SIOCGIFHWADDR, ifr)[18:24]
2579 mac_dev = ":".join(["%02x" % ord(m) for m in mac_dev])
2580 except IOError, e:
2581 raise HwAddrGetError(ifname)
2583 if mac_dev.lower() == mac.lower():
2584 return
2586 ifr = struct.pack("16sH14s", ifname, 1,
2587 "".join([chr(int(m, 16)) for m in mac.split(":")]))
2588 try:
2589 fcntl.ioctl(ctrl_sock, SIOCSIFHWADDR, ifr)
2590 except IOError, e:
2591 logging.info(e)
2592 raise HwAddrSetError(ifname, mac)
2593 ctrl_sock.close()