blob: d13597903d2afe6453d5c9bc9855876c7c79a244 [file] [log] [blame]
lmr6f669ce2009-05-31 19:02:42 +00001"""
2KVM test utility functions.
3
4@copyright: 2008-2009 Red Hat Inc.
5"""
6
lmr5ab537a2010-06-14 15:38:18 +00007import time, string, random, socket, os, signal, re, logging, commands, cPickle
Eric Li7edb3042011-01-06 17:57:17 -08008import fcntl, shelve, ConfigParser, rss_file_transfer, threading, sys, UserDict
Eric Lie0493a42010-11-15 13:05:43 -08009from autotest_lib.client.bin import utils, os_dep
lmr84154412010-02-03 18:34:43 +000010from autotest_lib.client.common_lib import error, logging_config
lmrb635b862009-09-10 14:53:21 +000011import kvm_subprocess
Eric Lie0493a42010-11-15 13:05:43 -080012try:
13 import koji
14 KOJI_INSTALLED = True
15except ImportError:
16 KOJI_INSTALLED = False
17
18
19def _lock_file(filename):
20 f = open(filename, "w")
21 fcntl.lockf(f, fcntl.LOCK_EX)
22 return f
23
24
25def _unlock_file(f):
26 fcntl.lockf(f, fcntl.LOCK_UN)
27 f.close()
lmrb635b862009-09-10 14:53:21 +000028
lmr6f669ce2009-05-31 19:02:42 +000029
Eric Li7edb3042011-01-06 17:57:17 -080030def is_vm(obj):
lmre8a66dd2009-09-15 19:51:07 +000031 """
Eric Li7edb3042011-01-06 17:57:17 -080032 Tests whether a given object is a VM object.
lmre8a66dd2009-09-15 19:51:07 +000033
Eric Li7edb3042011-01-06 17:57:17 -080034 @param obj: Python object.
lmre8a66dd2009-09-15 19:51:07 +000035 """
Eric Li7edb3042011-01-06 17:57:17 -080036 return obj.__class__.__name__ == "VM"
lmre8a66dd2009-09-15 19:51:07 +000037
38
Eric Li7edb3042011-01-06 17:57:17 -080039class Env(UserDict.IterableUserDict):
lmre8a66dd2009-09-15 19:51:07 +000040 """
Eric Li7edb3042011-01-06 17:57:17 -080041 A dict-like object containing global objects used by tests.
lmre8a66dd2009-09-15 19:51:07 +000042 """
Eric Li7edb3042011-01-06 17:57:17 -080043 def __init__(self, filename=None, version=0):
44 """
45 Create an empty Env object or load an existing one from a file.
46
47 If the version recorded in the file is lower than version, or if some
48 error occurs during unpickling, or if filename is not supplied,
49 create an empty Env object.
50
51 @param filename: Path to an env file.
52 @param version: Required env version (int).
53 """
54 UserDict.IterableUserDict.__init__(self)
55 empty = {"version": version}
56 if filename:
57 self._filename = filename
58 try:
59 f = open(filename, "r")
60 env = cPickle.load(f)
61 f.close()
62 if env.get("version", 0) >= version:
63 self.data = env
64 else:
65 logging.warn("Incompatible env file found. Not using it.")
66 self.data = empty
67 # Almost any exception can be raised during unpickling, so let's
68 # catch them all
69 except Exception, e:
70 logging.warn(e)
71 self.data = empty
72 else:
73 self.data = empty
lmre8a66dd2009-09-15 19:51:07 +000074
75
Eric Li7edb3042011-01-06 17:57:17 -080076 def save(self, filename=None):
77 """
78 Pickle the contents of the Env object into a file.
79
80 @param filename: Filename to pickle the dict into. If not supplied,
81 use the filename from which the dict was loaded.
82 """
83 filename = filename or self._filename
84 f = open(filename, "w")
85 cPickle.dump(self.data, f)
86 f.close()
87
88
89 def get_all_vms(self):
90 """
91 Return a list of all VM objects in this Env object.
92 """
93 return [o for o in self.values() if is_vm(o)]
94
95
96 def get_vm(self, name):
97 """
98 Return a VM object by its name.
99
100 @param name: VM name.
101 """
102 return self.get("vm__%s" % name)
103
104
105 def register_vm(self, name, vm):
106 """
107 Register a VM in this Env object.
108
109 @param name: VM name.
110 @param vm: VM object.
111 """
112 self["vm__%s" % name] = vm
113
114
115 def unregister_vm(self, name):
116 """
117 Remove a given VM.
118
119 @param name: VM name.
120 """
121 del self["vm__%s" % name]
122
123
124 def register_installer(self, installer):
125 """
126 Register a installer that was just run
127
128 The installer will be available for other tests, so that
129 information about the installed KVM modules and qemu-kvm can be used by
130 them.
131 """
132 self['last_installer'] = installer
133
134
135 def previous_installer(self):
136 """
137 Return the last installer that was registered
138 """
139 return self.get('last_installer')
140
141
142class Params(UserDict.IterableUserDict):
lmr6f669ce2009-05-31 19:02:42 +0000143 """
Eric Li7edb3042011-01-06 17:57:17 -0800144 A dict-like object passed to every test.
lmr6f669ce2009-05-31 19:02:42 +0000145 """
Eric Li7edb3042011-01-06 17:57:17 -0800146 def objects(self, key):
147 """
148 Return the names of objects defined using a given key.
149
150 @param key: The name of the key whose value lists the objects
151 (e.g. 'nics').
152 """
153 return self.get(key, "").split()
lmr6f669ce2009-05-31 19:02:42 +0000154
155
Eric Li7edb3042011-01-06 17:57:17 -0800156 def object_params(self, obj_name):
157 """
158 Return a dict-like object containing the parameters of an individual
159 object.
lmr6f669ce2009-05-31 19:02:42 +0000160
Eric Li7edb3042011-01-06 17:57:17 -0800161 This method behaves as follows: the suffix '_' + obj_name is removed
162 from all key names that have it. Other key names are left unchanged.
163 The values of keys with the suffix overwrite the values of their
164 suffixless versions.
lmr6f669ce2009-05-31 19:02:42 +0000165
Eric Li7edb3042011-01-06 17:57:17 -0800166 @param obj_name: The name of the object (objects are listed by the
167 objects() method).
168 """
169 suffix = "_" + obj_name
170 new_dict = self.copy()
171 for key in self:
172 if key.endswith(suffix):
173 new_key = key.split(suffix)[0]
174 new_dict[new_key] = self[key]
175 return new_dict
lmr6f669ce2009-05-31 19:02:42 +0000176
177
lmrac5089b2009-08-13 04:05:47 +0000178# Functions related to MAC/IP addresses
179
Eric Lie0493a42010-11-15 13:05:43 -0800180def _open_mac_pool(lock_mode):
181 lock_file = open("/tmp/mac_lock", "w+")
182 fcntl.lockf(lock_file, lock_mode)
183 pool = shelve.open("/tmp/address_pool")
184 return pool, lock_file
185
186
187def _close_mac_pool(pool, lock_file):
188 pool.close()
189 fcntl.lockf(lock_file, fcntl.LOCK_UN)
190 lock_file.close()
191
192
193def _generate_mac_address_prefix(mac_pool):
lmrac5089b2009-08-13 04:05:47 +0000194 """
Eric Lie0493a42010-11-15 13:05:43 -0800195 Generate a random MAC address prefix and add it to the MAC pool dictionary.
196 If there's a MAC prefix there already, do not update the MAC pool and just
197 return what's in there. By convention we will set KVM autotest MAC
198 addresses to start with 0x9a.
lmrac5089b2009-08-13 04:05:47 +0000199
Eric Lie0493a42010-11-15 13:05:43 -0800200 @param mac_pool: The MAC address pool object.
201 @return: The MAC address prefix.
lmrac5089b2009-08-13 04:05:47 +0000202 """
Eric Lie0493a42010-11-15 13:05:43 -0800203 if "prefix" in mac_pool:
204 prefix = mac_pool["prefix"]
205 logging.debug("Used previously generated MAC address prefix for this "
206 "host: %s", prefix)
207 else:
208 r = random.SystemRandom()
209 prefix = "9a:%02x:%02x:%02x:" % (r.randint(0x00, 0xff),
210 r.randint(0x00, 0xff),
211 r.randint(0x00, 0xff))
212 mac_pool["prefix"] = prefix
213 logging.debug("Generated MAC address prefix for this host: %s", prefix)
214 return prefix
lmrac5089b2009-08-13 04:05:47 +0000215
216
Eric Lie0493a42010-11-15 13:05:43 -0800217def generate_mac_address(vm_instance, nic_index):
lmrac5089b2009-08-13 04:05:47 +0000218 """
Eric Lie0493a42010-11-15 13:05:43 -0800219 Randomly generate a MAC address and add it to the MAC address pool.
lmrac5089b2009-08-13 04:05:47 +0000220
Eric Lie0493a42010-11-15 13:05:43 -0800221 Try to generate a MAC address based on a randomly generated MAC address
222 prefix and add it to a persistent dictionary.
223 key = VM instance + NIC index, value = MAC address
224 e.g. {'20100310-165222-Wt7l:0': '9a:5d:94:6a:9b:f9'}
225
226 @param vm_instance: The instance attribute of a VM.
227 @param nic_index: The index of the NIC.
228 @return: MAC address string.
lmrac5089b2009-08-13 04:05:47 +0000229 """
Eric Lie0493a42010-11-15 13:05:43 -0800230 mac_pool, lock_file = _open_mac_pool(fcntl.LOCK_EX)
231 key = "%s:%s" % (vm_instance, nic_index)
232 if key in mac_pool:
233 mac = mac_pool[key]
234 else:
235 prefix = _generate_mac_address_prefix(mac_pool)
236 r = random.SystemRandom()
237 while key not in mac_pool:
238 mac = prefix + "%02x:%02x" % (r.randint(0x00, 0xff),
239 r.randint(0x00, 0xff))
240 if mac in mac_pool.values():
241 continue
242 mac_pool[key] = mac
243 logging.debug("Generated MAC address for NIC %s: %s", key, mac)
244 _close_mac_pool(mac_pool, lock_file)
245 return mac
lmrac5089b2009-08-13 04:05:47 +0000246
247
Eric Lie0493a42010-11-15 13:05:43 -0800248def free_mac_address(vm_instance, nic_index):
lmrac5089b2009-08-13 04:05:47 +0000249 """
Eric Lie0493a42010-11-15 13:05:43 -0800250 Remove a MAC address from the address pool.
lmrac5089b2009-08-13 04:05:47 +0000251
Eric Lie0493a42010-11-15 13:05:43 -0800252 @param vm_instance: The instance attribute of a VM.
253 @param nic_index: The index of the NIC.
lmrac5089b2009-08-13 04:05:47 +0000254 """
Eric Lie0493a42010-11-15 13:05:43 -0800255 mac_pool, lock_file = _open_mac_pool(fcntl.LOCK_EX)
256 key = "%s:%s" % (vm_instance, nic_index)
257 if key in mac_pool:
258 logging.debug("Freeing MAC address for NIC %s: %s", key, mac_pool[key])
259 del mac_pool[key]
260 _close_mac_pool(mac_pool, lock_file)
lmrac5089b2009-08-13 04:05:47 +0000261
262
Eric Lie0493a42010-11-15 13:05:43 -0800263def set_mac_address(vm_instance, nic_index, mac):
lmrac5089b2009-08-13 04:05:47 +0000264 """
Eric Lie0493a42010-11-15 13:05:43 -0800265 Set a MAC address in the pool.
lmrac5089b2009-08-13 04:05:47 +0000266
Eric Lie0493a42010-11-15 13:05:43 -0800267 @param vm_instance: The instance attribute of a VM.
268 @param nic_index: The index of the NIC.
lmrac5089b2009-08-13 04:05:47 +0000269 """
Eric Lie0493a42010-11-15 13:05:43 -0800270 mac_pool, lock_file = _open_mac_pool(fcntl.LOCK_EX)
271 mac_pool["%s:%s" % (vm_instance, nic_index)] = mac
272 _close_mac_pool(mac_pool, lock_file)
lmrac5089b2009-08-13 04:05:47 +0000273
274
Eric Lie0493a42010-11-15 13:05:43 -0800275def get_mac_address(vm_instance, nic_index):
lmrac5089b2009-08-13 04:05:47 +0000276 """
Eric Lie0493a42010-11-15 13:05:43 -0800277 Return a MAC address from the pool.
lmrac5089b2009-08-13 04:05:47 +0000278
Eric Lie0493a42010-11-15 13:05:43 -0800279 @param vm_instance: The instance attribute of a VM.
280 @param nic_index: The index of the NIC.
281 @return: MAC address string.
lmrac5089b2009-08-13 04:05:47 +0000282 """
Eric Lie0493a42010-11-15 13:05:43 -0800283 mac_pool, lock_file = _open_mac_pool(fcntl.LOCK_SH)
284 mac = mac_pool.get("%s:%s" % (vm_instance, nic_index))
285 _close_mac_pool(mac_pool, lock_file)
286 return mac
lmrf3d3e522010-03-23 16:38:12 +0000287
288
lmr53d3e932009-09-09 21:56:18 +0000289def verify_ip_address_ownership(ip, macs, timeout=10.0):
lmree90dd92009-08-13 04:13:39 +0000290 """
lmr53d3e932009-09-09 21:56:18 +0000291 Use arping and the ARP cache to make sure a given IP address belongs to one
292 of the given MAC addresses.
lmree90dd92009-08-13 04:13:39 +0000293
294 @param ip: An IP address.
295 @param macs: A list or tuple of MAC addresses.
296 @return: True iff ip is assigned to a MAC address in macs.
297 """
lmr53d3e932009-09-09 21:56:18 +0000298 # Compile a regex that matches the given IP address and any of the given
299 # MAC addresses
lmree90dd92009-08-13 04:13:39 +0000300 mac_regex = "|".join("(%s)" % mac for mac in macs)
lmre35507b2009-10-28 16:53:08 +0000301 regex = re.compile(r"\b%s\b.*\b(%s)\b" % (ip, mac_regex), re.IGNORECASE)
lmree90dd92009-08-13 04:13:39 +0000302
lmr53d3e932009-09-09 21:56:18 +0000303 # Check the ARP cache
lmr3eb39c72010-07-08 23:34:05 +0000304 o = commands.getoutput("%s -n" % find_command("arp"))
lmre35507b2009-10-28 16:53:08 +0000305 if regex.search(o):
lmree90dd92009-08-13 04:13:39 +0000306 return True
307
lmr53d3e932009-09-09 21:56:18 +0000308 # Get the name of the bridge device for arping
lmr3eb39c72010-07-08 23:34:05 +0000309 o = commands.getoutput("%s route get %s" % (find_command("ip"), ip))
lmr53d3e932009-09-09 21:56:18 +0000310 dev = re.findall("dev\s+\S+", o, re.IGNORECASE)
311 if not dev:
312 return False
313 dev = dev[0].split()[-1]
314
315 # Send an ARP request
lmr3eb39c72010-07-08 23:34:05 +0000316 o = commands.getoutput("%s -f -c 3 -I %s %s" %
317 (find_command("arping"), dev, ip))
lmre35507b2009-10-28 16:53:08 +0000318 return bool(regex.search(o))
lmree90dd92009-08-13 04:13:39 +0000319
320
lmr6f669ce2009-05-31 19:02:42 +0000321# Utility functions for dealing with external processes
322
lmr049239c2010-07-08 23:27:18 +0000323def find_command(cmd):
324 for dir in ["/usr/local/sbin", "/usr/local/bin",
325 "/usr/sbin", "/usr/bin", "/sbin", "/bin"]:
326 file = os.path.join(dir, cmd)
327 if os.path.exists(file):
328 return file
329 raise ValueError('Missing command: %s' % cmd)
330
331
lmr6f669ce2009-05-31 19:02:42 +0000332def pid_exists(pid):
333 """
334 Return True if a given PID exists.
335
336 @param pid: Process ID number.
337 """
338 try:
339 os.kill(pid, 0)
340 return True
341 except:
342 return False
343
344
345def safe_kill(pid, signal):
346 """
347 Attempt to send a signal to a given process that may or may not exist.
348
349 @param signal: Signal number.
350 """
351 try:
352 os.kill(pid, signal)
353 return True
354 except:
355 return False
356
357
lmr1aeaefb2009-09-09 22:12:49 +0000358def kill_process_tree(pid, sig=signal.SIGKILL):
359 """Signal a process and all of its children.
360
361 If the process does not exist -- return.
362
363 @param pid: The pid of the process to signal.
364 @param sig: The signal to send to the processes.
365 """
366 if not safe_kill(pid, signal.SIGSTOP):
367 return
368 children = commands.getoutput("ps --ppid=%d -o pid=" % pid).split()
369 for child in children:
370 kill_process_tree(int(child), sig)
371 safe_kill(pid, sig)
372 safe_kill(pid, signal.SIGCONT)
373
374
lmr117bbcf2009-09-15 07:12:39 +0000375def get_latest_kvm_release_tag(release_listing):
lmr3f0b0cc2009-06-10 02:25:23 +0000376 """
377 Fetches the latest release tag for KVM.
378
lmr117bbcf2009-09-15 07:12:39 +0000379 @param release_listing: URL that contains a list of the Source Forge
380 KVM project files.
lmr3f0b0cc2009-06-10 02:25:23 +0000381 """
382 try:
lmr117bbcf2009-09-15 07:12:39 +0000383 release_page = utils.urlopen(release_listing)
384 data = release_page.read()
385 release_page.close()
lmr8ea274b2009-07-06 13:42:35 +0000386 rx = re.compile("kvm-(\d+).tar.gz", re.IGNORECASE)
lmr3f0b0cc2009-06-10 02:25:23 +0000387 matches = rx.findall(data)
lmr32525382009-08-10 13:53:37 +0000388 # In all regexp matches to something that looks like a release tag,
389 # get the largest integer. That will be our latest release tag.
390 latest_tag = max(int(x) for x in matches)
391 return str(latest_tag)
lmr3f0b0cc2009-06-10 02:25:23 +0000392 except Exception, e:
393 message = "Could not fetch latest KVM release tag: %s" % str(e)
394 logging.error(message)
395 raise error.TestError(message)
396
397
398def get_git_branch(repository, branch, srcdir, commit=None, lbranch=None):
399 """
400 Retrieves a given git code repository.
401
402 @param repository: Git repository URL
403 """
lmr160e9942010-06-09 14:09:19 +0000404 logging.info("Fetching git [REP '%s' BRANCH '%s' COMMIT '%s'] -> %s",
lmr3f0b0cc2009-06-10 02:25:23 +0000405 repository, branch, commit, srcdir)
406 if not os.path.exists(srcdir):
407 os.makedirs(srcdir)
408 os.chdir(srcdir)
409
410 if os.path.exists(".git"):
411 utils.system("git reset --hard")
412 else:
413 utils.system("git init")
414
415 if not lbranch:
416 lbranch = branch
417
418 utils.system("git fetch -q -f -u -t %s %s:%s" %
419 (repository, branch, lbranch))
420 utils.system("git checkout %s" % lbranch)
421 if commit:
422 utils.system("git checkout %s" % commit)
423
424 h = utils.system_output('git log --pretty=format:"%H" -1')
lmr05cec0f2010-01-26 17:50:29 +0000425 try:
426 desc = "tag %s" % utils.system_output("git describe")
427 except error.CmdError:
428 desc = "no tag found"
429
lmr3f0b0cc2009-06-10 02:25:23 +0000430 logging.info("Commit hash for %s is %s (%s)" % (repository, h.strip(),
431 desc))
432 return srcdir
433
434
lmr3f0b0cc2009-06-10 02:25:23 +0000435def check_kvm_source_dir(source_dir):
436 """
437 Inspects the kvm source directory and verifies its disposition. In some
438 occasions build may be dependant on the source directory disposition.
439 The reason why the return codes are numbers is that we might have more
440 changes on the source directory layout, so it's not scalable to just use
441 strings like 'old_repo', 'new_repo' and such.
442
443 @param source_dir: Source code path that will be inspected.
444 """
445 os.chdir(source_dir)
446 has_qemu_dir = os.path.isdir('qemu')
447 has_kvm_dir = os.path.isdir('kvm')
Eric Li7edb3042011-01-06 17:57:17 -0800448 if has_qemu_dir:
lmr3f0b0cc2009-06-10 02:25:23 +0000449 logging.debug("qemu directory detected, source dir layout 1")
450 return 1
451 if has_kvm_dir and not has_qemu_dir:
452 logging.debug("kvm directory detected, source dir layout 2")
453 return 2
454 else:
455 raise error.TestError("Unknown source dir layout, cannot proceed.")
456
457
lmrf9349c32009-07-23 01:44:24 +0000458# The following are functions used for SSH, SCP and Telnet communication with
459# guests.
lmr6f669ce2009-05-31 19:02:42 +0000460
lmra4cd1f12010-06-22 02:01:00 +0000461def _remote_login(session, username, password, prompt, timeout=10):
lmr6f669ce2009-05-31 19:02:42 +0000462 """
lmr158604f2010-06-22 01:57:34 +0000463 Log into a remote host (guest) using SSH or Telnet. Wait for questions
464 and provide answers. If timeout expires while waiting for output from the
465 child (e.g. a password prompt or a shell prompt) -- fail.
lmr6f669ce2009-05-31 19:02:42 +0000466
467 @brief: Log into a remote host (guest) using SSH or Telnet.
468
Eric Li7edb3042011-01-06 17:57:17 -0800469 @param session: An Expect or ShellSession instance to operate on
lmra4cd1f12010-06-22 02:01:00 +0000470 @param username: The username to send in reply to a login prompt
lmr6f669ce2009-05-31 19:02:42 +0000471 @param password: The password to send in reply to a password prompt
472 @param prompt: The shell prompt that indicates a successful login
lmr6f669ce2009-05-31 19:02:42 +0000473 @param timeout: The maximal time duration (in seconds) to wait for each
474 step of the login procedure (i.e. the "Are you sure" prompt, the
475 password prompt, the shell prompt, etc)
476
lmr158604f2010-06-22 01:57:34 +0000477 @return: True on success and False otherwise.
lmr6f669ce2009-05-31 19:02:42 +0000478 """
lmr6f669ce2009-05-31 19:02:42 +0000479 password_prompt_count = 0
lmra4cd1f12010-06-22 02:01:00 +0000480 login_prompt_count = 0
lmr6f669ce2009-05-31 19:02:42 +0000481
lmr6f669ce2009-05-31 19:02:42 +0000482 while True:
Eric Li7edb3042011-01-06 17:57:17 -0800483 try:
484 match, text = session.read_until_last_line_matches(
lmr2c502672010-06-22 02:02:19 +0000485 [r"[Aa]re you sure", r"[Pp]assword:\s*$", r"[Ll]ogin:\s*$",
lmrba69bc52010-03-25 01:50:09 +0000486 r"[Cc]onnection.*closed", r"[Cc]onnection.*refused",
487 r"[Pp]lease wait", prompt],
Eric Li7edb3042011-01-06 17:57:17 -0800488 timeout=timeout, internal_timeout=0.5)
489 if match == 0: # "Are you sure you want to continue connecting"
490 logging.debug("Got 'Are you sure...'; sending 'yes'")
491 session.sendline("yes")
lmr6f669ce2009-05-31 19:02:42 +0000492 continue
Eric Li7edb3042011-01-06 17:57:17 -0800493 elif match == 1: # "password:"
494 if password_prompt_count == 0:
495 logging.debug("Got password prompt; sending '%s'" % password)
496 session.sendline(password)
497 password_prompt_count += 1
498 continue
499 else:
500 logging.debug("Got password prompt again")
501 return False
502 elif match == 2: # "login:"
503 if login_prompt_count == 0:
504 logging.debug("Got username prompt; sending '%s'" % username)
505 session.sendline(username)
506 login_prompt_count += 1
507 continue
508 else:
509 logging.debug("Got username prompt again")
510 return False
511 elif match == 3: # "Connection closed"
512 logging.debug("Got 'Connection closed'")
lmr158604f2010-06-22 01:57:34 +0000513 return False
Eric Li7edb3042011-01-06 17:57:17 -0800514 elif match == 4: # "Connection refused"
515 logging.debug("Got 'Connection refused'")
516 return False
517 elif match == 5: # "Please wait"
518 logging.debug("Got 'Please wait'")
519 timeout = 30
lmra4cd1f12010-06-22 02:01:00 +0000520 continue
Eric Li7edb3042011-01-06 17:57:17 -0800521 elif match == 6: # prompt
522 logging.debug("Got shell prompt -- logged in")
523 return True
524 except kvm_subprocess.ExpectTimeoutError, e:
525 logging.debug("Timeout elapsed (output so far: %r)" % e.output)
lmr158604f2010-06-22 01:57:34 +0000526 return False
Eric Li7edb3042011-01-06 17:57:17 -0800527 except kvm_subprocess.ExpectProcessTerminatedError, e:
528 logging.debug("Process terminated (output so far: %r)" % e.output)
lmr158604f2010-06-22 01:57:34 +0000529 return False
530
531
532def _remote_scp(session, password, transfer_timeout=600, login_timeout=10):
533 """
534 Transfer file(s) to a remote host (guest) using SCP. Wait for questions
535 and provide answers. If login_timeout expires while waiting for output
536 from the child (e.g. a password prompt), fail. If transfer_timeout expires
537 while waiting for the transfer to complete, fail.
538
539 @brief: Transfer files using SCP, given a command line.
540
Eric Li7edb3042011-01-06 17:57:17 -0800541 @param session: An Expect or ShellSession instance to operate on
lmr158604f2010-06-22 01:57:34 +0000542 @param password: The password to send in reply to a password prompt.
543 @param transfer_timeout: The time duration (in seconds) to wait for the
544 transfer to complete.
545 @param login_timeout: The maximal time duration (in seconds) to wait for
546 each step of the login procedure (i.e. the "Are you sure" prompt or
547 the password prompt)
548
549 @return: True if the transfer succeeds and False on failure.
550 """
551 password_prompt_count = 0
552 timeout = login_timeout
553
554 while True:
Eric Li7edb3042011-01-06 17:57:17 -0800555 try:
556 match, text = session.read_until_last_line_matches(
lmr158604f2010-06-22 01:57:34 +0000557 [r"[Aa]re you sure", r"[Pp]assword:\s*$", r"lost connection"],
558 timeout=timeout, internal_timeout=0.5)
Eric Li7edb3042011-01-06 17:57:17 -0800559 if match == 0: # "Are you sure you want to continue connecting"
560 logging.debug("Got 'Are you sure...'; sending 'yes'")
561 session.sendline("yes")
lmr158604f2010-06-22 01:57:34 +0000562 continue
Eric Li7edb3042011-01-06 17:57:17 -0800563 elif match == 1: # "password:"
564 if password_prompt_count == 0:
565 logging.debug("Got password prompt; sending '%s'" % password)
566 session.sendline(password)
567 password_prompt_count += 1
568 timeout = transfer_timeout
569 continue
570 else:
571 logging.debug("Got password prompt again")
572 return False
573 elif match == 2: # "lost connection"
574 logging.debug("Got 'lost connection'")
lmr158604f2010-06-22 01:57:34 +0000575 return False
Eric Li7edb3042011-01-06 17:57:17 -0800576 except kvm_subprocess.ExpectTimeoutError, e:
577 logging.debug("Timeout expired")
lmr158604f2010-06-22 01:57:34 +0000578 return False
Eric Li7edb3042011-01-06 17:57:17 -0800579 except kvm_subprocess.ExpectProcessTerminatedError, e:
580 logging.debug("SCP process terminated with status %s", e.status)
581 return e.status == 0
lmr158604f2010-06-22 01:57:34 +0000582
583
584def remote_login(client, host, port, username, password, prompt, linesep="\n",
lmre56903f2010-06-22 02:09:35 +0000585 log_filename=None, timeout=10):
lmr158604f2010-06-22 01:57:34 +0000586 """
587 Log into a remote host (guest) using SSH/Telnet/Netcat.
588
589 @param client: The client to use ('ssh', 'telnet' or 'nc')
590 @param host: Hostname or IP address
591 @param port: Port to connect to
592 @param username: Username (if required)
593 @param password: Password (if required)
594 @param prompt: Shell prompt (regular expression)
595 @param linesep: The line separator to use when sending lines
596 (e.g. '\\n' or '\\r\\n')
lmre56903f2010-06-22 02:09:35 +0000597 @param log_filename: If specified, log all output to this file
lmr158604f2010-06-22 01:57:34 +0000598 @param timeout: The maximal time duration (in seconds) to wait for
599 each step of the login procedure (i.e. the "Are you sure" prompt
600 or the password prompt)
601
Eric Li7edb3042011-01-06 17:57:17 -0800602 @return: ShellSession object on success and None on failure.
lmr158604f2010-06-22 01:57:34 +0000603 """
604 if client == "ssh":
605 cmd = ("ssh -o UserKnownHostsFile=/dev/null "
606 "-o PreferredAuthentications=password -p %s %s@%s" %
607 (port, username, host))
608 elif client == "telnet":
609 cmd = "telnet -l %s %s %s" % (username, host, port)
610 elif client == "nc":
611 cmd = "nc %s %s" % (host, port)
612 else:
613 logging.error("Unknown remote shell client: %s" % client)
614 return
lmre56903f2010-06-22 02:09:35 +0000615
lmr158604f2010-06-22 01:57:34 +0000616 logging.debug("Trying to login with command '%s'" % cmd)
Eric Li7edb3042011-01-06 17:57:17 -0800617 session = kvm_subprocess.ShellSession(cmd, linesep=linesep, prompt=prompt)
lmra4cd1f12010-06-22 02:01:00 +0000618 if _remote_login(session, username, password, prompt, timeout):
lmre56903f2010-06-22 02:09:35 +0000619 if log_filename:
620 session.set_output_func(log_line)
621 session.set_output_params((log_filename,))
lmr158604f2010-06-22 01:57:34 +0000622 return session
623 else:
624 session.close()
lmr6f669ce2009-05-31 19:02:42 +0000625
626
lmre56903f2010-06-22 02:09:35 +0000627def remote_scp(command, password, log_filename=None, transfer_timeout=600,
628 login_timeout=10):
lmr6f669ce2009-05-31 19:02:42 +0000629 """
lmr158604f2010-06-22 01:57:34 +0000630 Transfer file(s) to a remote host (guest) using SCP.
lmr6f669ce2009-05-31 19:02:42 +0000631
632 @brief: Transfer files using SCP, given a command line.
633
634 @param command: The command to execute
635 (e.g. "scp -r foobar root@localhost:/tmp/").
636 @param password: The password to send in reply to a password prompt.
lmre56903f2010-06-22 02:09:35 +0000637 @param log_filename: If specified, log all output to this file
lmrc5f42a32010-06-18 14:15:19 +0000638 @param transfer_timeout: The time duration (in seconds) to wait for the
lmr158604f2010-06-22 01:57:34 +0000639 transfer to complete.
lmr6f669ce2009-05-31 19:02:42 +0000640 @param login_timeout: The maximal time duration (in seconds) to wait for
lmr158604f2010-06-22 01:57:34 +0000641 each step of the login procedure (i.e. the "Are you sure" prompt
642 or the password prompt)
lmr6f669ce2009-05-31 19:02:42 +0000643
644 @return: True if the transfer succeeds and False on failure.
645 """
lmr158604f2010-06-22 01:57:34 +0000646 logging.debug("Trying to SCP with command '%s', timeout %ss",
647 command, transfer_timeout)
lmre56903f2010-06-22 02:09:35 +0000648
649 if log_filename:
650 output_func = log_line
651 output_params = (log_filename,)
652 else:
653 output_func = None
654 output_params = ()
655
Eric Li7edb3042011-01-06 17:57:17 -0800656 session = kvm_subprocess.Expect(command,
657 output_func=output_func,
658 output_params=output_params)
lmr158604f2010-06-22 01:57:34 +0000659 try:
660 return _remote_scp(session, password, transfer_timeout, login_timeout)
661 finally:
662 session.close()
lmr6f669ce2009-05-31 19:02:42 +0000663
664
Eric Li7edb3042011-01-06 17:57:17 -0800665def copy_files_to(address, client, username, password, port, local_path,
666 remote_path, log_filename=None, timeout=600):
667 """
668 Decide the transfer cleint and copy file to a remote host (guest).
669
670 @param client: Type of transfer client
671 @param username: Username (if required)
672 @param password: Password (if requried)
673 @param local_path: Path on the local machine where we are copying from
674 @param remote_path: Path on the remote machine where we are copying to
675 @param address: Address of remote host(guest)
676 @param log_filename: If specified, log all output to this file
677 @param timeout: The time duration (in seconds) to wait for the transfer to
678 complete.
679
680 @return: True on success and False on failure.
681 """
682
683 if not address or not port:
684 logging.debug("IP address or port unavailable")
685 return None
686
687 if client == "scp":
688 return scp_to_remote(address, port, username, password, local_path,
689 remote_path, log_filename, timeout)
690 elif client == "rss":
691 c = rss_file_transfer.FileUploadClient(address, port)
692 c.upload(local_path, remote_path, timeout)
693 c.close()
694 return True
695
696
697def copy_files_from(address, client, username, password, port, local_path,
698 remote_path, log_filename=None, timeout=600):
699 """
700 Decide the transfer cleint and copy file from a remote host (guest).
701
702 @param client: Type of transfer client
703 @param username: Username (if required)
704 @param password: Password (if requried)
705 @param local_path: Path on the local machine where we are copying from
706 @param remote_path: Path on the remote machine where we are copying to
707 @param address: Address of remote host(guest)
708 @param log_filename: If specified, log all output to this file
709 @param timeout: The time duration (in seconds) to wait for the transfer to
710 complete.
711
712 @return: True on success and False on failure.
713 """
714
715 if not address or not port:
716 logging.debug("IP address or port unavailable")
717 return None
718
719 if client == "scp":
720 return scp_from_remote(address, port, username, password, remote_path,
721 local_path, log_filename, timeout)
722 elif client == "rss":
723 c = rss_file_transfer.FileDownloadClient(address, port)
724 c.download(remote_path, local_path, timeout)
725 c.close()
726 return True
727
728
lmr6f669ce2009-05-31 19:02:42 +0000729def scp_to_remote(host, port, username, password, local_path, remote_path,
lmre56903f2010-06-22 02:09:35 +0000730 log_filename=None, timeout=600):
lmr6f669ce2009-05-31 19:02:42 +0000731 """
Eric Li7edb3042011-01-06 17:57:17 -0800732 Copy files to a remote host (guest) through scp.
lmr6f669ce2009-05-31 19:02:42 +0000733
lmr912c74b2009-08-17 20:43:27 +0000734 @param host: Hostname or IP address
735 @param username: Username (if required)
736 @param password: Password (if required)
lmr6f669ce2009-05-31 19:02:42 +0000737 @param local_path: Path on the local machine where we are copying from
738 @param remote_path: Path on the remote machine where we are copying to
lmre56903f2010-06-22 02:09:35 +0000739 @param log_filename: If specified, log all output to this file
lmr158604f2010-06-22 01:57:34 +0000740 @param timeout: The time duration (in seconds) to wait for the transfer
741 to complete.
lmr6f669ce2009-05-31 19:02:42 +0000742
743 @return: True on success and False on failure.
744 """
lmrc196d492010-05-07 14:14:26 +0000745 command = ("scp -v -o UserKnownHostsFile=/dev/null "
lmr1b7e1702009-11-10 16:30:39 +0000746 "-o PreferredAuthentications=password -r -P %s %s %s@%s:%s" %
lmrd16a67d2009-06-10 19:52:59 +0000747 (port, local_path, username, host, remote_path))
lmre56903f2010-06-22 02:09:35 +0000748 return remote_scp(command, password, log_filename, timeout)
lmr6f669ce2009-05-31 19:02:42 +0000749
750
751def scp_from_remote(host, port, username, password, remote_path, local_path,
lmre56903f2010-06-22 02:09:35 +0000752 log_filename=None, timeout=600):
lmr6f669ce2009-05-31 19:02:42 +0000753 """
754 Copy files from a remote host (guest).
755
lmr912c74b2009-08-17 20:43:27 +0000756 @param host: Hostname or IP address
757 @param username: Username (if required)
758 @param password: Password (if required)
lmr6f669ce2009-05-31 19:02:42 +0000759 @param local_path: Path on the local machine where we are copying from
760 @param remote_path: Path on the remote machine where we are copying to
lmre56903f2010-06-22 02:09:35 +0000761 @param log_filename: If specified, log all output to this file
lmr158604f2010-06-22 01:57:34 +0000762 @param timeout: The time duration (in seconds) to wait for the transfer
763 to complete.
lmr6f669ce2009-05-31 19:02:42 +0000764
765 @return: True on success and False on failure.
766 """
lmrc196d492010-05-07 14:14:26 +0000767 command = ("scp -v -o UserKnownHostsFile=/dev/null "
lmr1b7e1702009-11-10 16:30:39 +0000768 "-o PreferredAuthentications=password -r -P %s %s@%s:%s %s" %
lmrd16a67d2009-06-10 19:52:59 +0000769 (port, username, host, remote_path, local_path))
lmre56903f2010-06-22 02:09:35 +0000770 return remote_scp(command, password, log_filename, timeout)
lmr6f669ce2009-05-31 19:02:42 +0000771
772
lmr6f669ce2009-05-31 19:02:42 +0000773# The following are utility functions related to ports.
774
Eric Lie0493a42010-11-15 13:05:43 -0800775def is_port_free(port, address):
lmr6f669ce2009-05-31 19:02:42 +0000776 """
777 Return True if the given port is available for use.
778
779 @param port: Port number
780 """
781 try:
782 s = socket.socket()
783 #s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
Eric Lie0493a42010-11-15 13:05:43 -0800784 if address == "localhost":
785 s.bind(("localhost", port))
786 free = True
787 else:
788 s.connect((address, port))
789 free = False
lmr6f669ce2009-05-31 19:02:42 +0000790 except socket.error:
Eric Lie0493a42010-11-15 13:05:43 -0800791 if address == "localhost":
792 free = False
793 else:
794 free = True
lmr6f669ce2009-05-31 19:02:42 +0000795 s.close()
796 return free
797
798
Eric Lie0493a42010-11-15 13:05:43 -0800799def find_free_port(start_port, end_port, address="localhost"):
lmr6f669ce2009-05-31 19:02:42 +0000800 """
lmra29a5cb2010-03-18 02:39:34 +0000801 Return a host free port in the range [start_port, end_port].
lmr6f669ce2009-05-31 19:02:42 +0000802
803 @param start_port: First port that will be checked.
804 @param end_port: Port immediately after the last one that will be checked.
805 """
806 for i in range(start_port, end_port):
Eric Lie0493a42010-11-15 13:05:43 -0800807 if is_port_free(i, address):
lmr6f669ce2009-05-31 19:02:42 +0000808 return i
809 return None
810
811
Eric Lie0493a42010-11-15 13:05:43 -0800812def find_free_ports(start_port, end_port, count, address="localhost"):
lmr6f669ce2009-05-31 19:02:42 +0000813 """
lmra29a5cb2010-03-18 02:39:34 +0000814 Return count of host free ports in the range [start_port, end_port].
lmr6f669ce2009-05-31 19:02:42 +0000815
816 @count: Initial number of ports known to be free in the range.
817 @param start_port: First port that will be checked.
818 @param end_port: Port immediately after the last one that will be checked.
819 """
820 ports = []
821 i = start_port
822 while i < end_port and count > 0:
Eric Lie0493a42010-11-15 13:05:43 -0800823 if is_port_free(i, address):
lmr6f669ce2009-05-31 19:02:42 +0000824 ports.append(i)
825 count -= 1
826 i += 1
827 return ports
828
829
lmr23894542010-06-22 01:54:37 +0000830# An easy way to log lines to files when the logging system can't be used
831
832_open_log_files = {}
833_log_file_dir = "/tmp"
834
835
836def log_line(filename, line):
837 """
838 Write a line to a file. '\n' is appended to the line.
839
840 @param filename: Path of file to write to, either absolute or relative to
841 the dir set by set_log_file_dir().
842 @param line: Line to write.
843 """
844 global _open_log_files, _log_file_dir
845 if filename not in _open_log_files:
846 path = get_path(_log_file_dir, filename)
847 try:
848 os.makedirs(os.path.dirname(path))
849 except OSError:
850 pass
851 _open_log_files[filename] = open(path, "w")
852 timestr = time.strftime("%Y-%m-%d %H:%M:%S")
853 _open_log_files[filename].write("%s: %s\n" % (timestr, line))
854 _open_log_files[filename].flush()
855
856
857def set_log_file_dir(dir):
858 """
859 Set the base directory for log files created by log_line().
860
861 @param dir: Directory for log files.
862 """
863 global _log_file_dir
864 _log_file_dir = dir
865
866
lmr6f669ce2009-05-31 19:02:42 +0000867# The following are miscellaneous utility functions.
868
lmrb4954e02009-08-17 20:44:42 +0000869def get_path(base_path, user_path):
870 """
871 Translate a user specified path to a real path.
872 If user_path is relative, append it to base_path.
873 If user_path is absolute, return it as is.
874
875 @param base_path: The base path of relative user specified paths.
876 @param user_path: The user specified path.
877 """
878 if os.path.isabs(user_path):
879 return user_path
880 else:
881 return os.path.join(base_path, user_path)
882
883
lmr6f669ce2009-05-31 19:02:42 +0000884def generate_random_string(length):
885 """
886 Return a random string using alphanumeric characters.
887
888 @length: length of the string that will be generated.
889 """
lmr45fc0c22009-09-15 05:16:45 +0000890 r = random.SystemRandom()
lmr6f669ce2009-05-31 19:02:42 +0000891 str = ""
892 chars = string.letters + string.digits
893 while length > 0:
lmr45fc0c22009-09-15 05:16:45 +0000894 str += r.choice(chars)
lmr6f669ce2009-05-31 19:02:42 +0000895 length -= 1
896 return str
897
lmr42b16662010-06-22 11:55:23 +0000898def generate_random_id():
899 """
900 Return a random string suitable for use as a qemu id.
901 """
902 return "id" + generate_random_string(6)
903
lmr6f669ce2009-05-31 19:02:42 +0000904
lmr0bee2342010-02-24 00:01:37 +0000905def generate_tmp_file_name(file, ext=None, dir='/tmp/'):
906 """
907 Returns a temporary file name. The file is not created.
908 """
909 while True:
910 file_name = (file + '-' + time.strftime("%Y%m%d-%H%M%S-") +
911 generate_random_string(4))
912 if ext:
913 file_name += '.' + ext
914 file_name = os.path.join(dir, file_name)
915 if not os.path.exists(file_name):
916 break
917
918 return file_name
919
920
lmr6f669ce2009-05-31 19:02:42 +0000921def format_str_for_message(str):
922 """
923 Format str so that it can be appended to a message.
924 If str consists of one line, prefix it with a space.
925 If str consists of multiple lines, prefix it with a newline.
926
927 @param str: string that will be formatted.
928 """
lmr57355592009-08-07 21:55:49 +0000929 lines = str.splitlines()
930 num_lines = len(lines)
931 str = "\n".join(lines)
lmr6f669ce2009-05-31 19:02:42 +0000932 if num_lines == 0:
933 return ""
934 elif num_lines == 1:
935 return " " + str
936 else:
937 return "\n" + str
938
939
940def wait_for(func, timeout, first=0.0, step=1.0, text=None):
941 """
942 If func() evaluates to True before timeout expires, return the
943 value of func(). Otherwise return None.
944
945 @brief: Wait until func() evaluates to True.
946
947 @param timeout: Timeout in seconds
948 @param first: Time to sleep before first attempt
949 @param steps: Time to sleep between attempts in seconds
950 @param text: Text to print while waiting, for debug purposes
951 """
952 start_time = time.time()
953 end_time = time.time() + timeout
954
955 time.sleep(first)
956
957 while time.time() < end_time:
958 if text:
959 logging.debug("%s (%f secs)" % (text, time.time() - start_time))
960
961 output = func()
962 if output:
963 return output
964
965 time.sleep(step)
966
967 logging.debug("Timeout elapsed")
968 return None
969
970
lmr03ba22e2009-10-23 12:07:44 +0000971def get_hash_from_file(hash_path, dvd_basename):
972 """
973 Get the a hash from a given DVD image from a hash file
974 (Hash files are usually named MD5SUM or SHA1SUM and are located inside the
975 download directories of the DVDs)
976
977 @param hash_path: Local path to a hash file.
978 @param cd_image: Basename of a CD image
979 """
980 hash_file = open(hash_path, 'r')
981 for line in hash_file.readlines():
982 if dvd_basename in line:
983 return line.split()[0]
984
985
lmr43beef12009-12-11 21:03:36 +0000986def run_tests(test_list, job):
987 """
988 Runs the sequence of KVM tests based on the list of dictionaries
989 generated by the configuration system, handling dependencies.
990
991 @param test_list: List with all dictionary test parameters.
992 @param job: Autotest job object.
993
994 @return: True, if all tests ran passed, False if any of them failed.
995 """
996 status_dict = {}
lmr43beef12009-12-11 21:03:36 +0000997 failed = False
lmr5df6eb42010-03-23 15:34:16 +0000998
lmr43beef12009-12-11 21:03:36 +0000999 for dict in test_list:
1000 if dict.get("skip") == "yes":
1001 continue
1002 dependencies_satisfied = True
1003 for dep in dict.get("depend"):
1004 for test_name in status_dict.keys():
1005 if not dep in test_name:
1006 continue
1007 if not status_dict[test_name]:
1008 dependencies_satisfied = False
1009 break
1010 if dependencies_satisfied:
1011 test_iterations = int(dict.get("iterations", 1))
lmr7da99b22010-01-12 22:30:28 +00001012 test_tag = dict.get("shortname")
lmrd960a722010-04-01 02:32:59 +00001013 # Setting up profilers during test execution.
1014 profilers = dict.get("profilers", "").split()
1015 for profiler in profilers:
1016 job.profilers.add(profiler)
lmr7da99b22010-01-12 22:30:28 +00001017
lmr7da99b22010-01-12 22:30:28 +00001018 # We need only one execution, profiled, hence we're passing
1019 # the profile_only parameter to job.run_test().
1020 current_status = job.run_test("kvm", params=dict, tag=test_tag,
1021 iterations=test_iterations,
lmrd960a722010-04-01 02:32:59 +00001022 profile_only= bool(profilers) or None)
1023
1024 for profiler in profilers:
1025 job.profilers.delete(profiler)
lmr7da99b22010-01-12 22:30:28 +00001026
lmr43beef12009-12-11 21:03:36 +00001027 if not current_status:
1028 failed = True
1029 else:
1030 current_status = False
1031 status_dict[dict.get("name")] = current_status
1032
1033 return not failed
1034
1035
1036def create_report(report_dir, results_dir):
1037 """
1038 Creates a neatly arranged HTML results report in the results dir.
1039
1040 @param report_dir: Directory where the report script is located.
1041 @param results_dir: Directory where the results will be output.
1042 """
1043 reporter = os.path.join(report_dir, 'html_report.py')
1044 html_file = os.path.join(results_dir, 'results.html')
1045 os.system('%s -r %s -f %s -R' % (reporter, results_dir, html_file))
lmr31af3a12010-01-18 16:46:52 +00001046
1047
1048def get_full_pci_id(pci_id):
1049 """
1050 Get full PCI ID of pci_id.
1051
1052 @param pci_id: PCI ID of a device.
1053 """
1054 cmd = "lspci -D | awk '/%s/ {print $1}'" % pci_id
1055 status, full_id = commands.getstatusoutput(cmd)
1056 if status != 0:
1057 return None
1058 return full_id
1059
1060
1061def get_vendor_from_pci_id(pci_id):
1062 """
1063 Check out the device vendor ID according to pci_id.
1064
1065 @param pci_id: PCI ID of a device.
1066 """
1067 cmd = "lspci -n | awk '/%s/ {print $3}'" % pci_id
1068 return re.sub(":", " ", commands.getoutput(cmd))
1069
1070
Eric Li7edb3042011-01-06 17:57:17 -08001071class Thread(threading.Thread):
1072 """
1073 Run a function in a background thread.
1074 """
1075 def __init__(self, target, args=(), kwargs={}):
1076 """
1077 Initialize the instance.
1078
1079 @param target: Function to run in the thread.
1080 @param args: Arguments to pass to target.
1081 @param kwargs: Keyword arguments to pass to target.
1082 """
1083 threading.Thread.__init__(self)
1084 self._target = target
1085 self._args = args
1086 self._kwargs = kwargs
1087
1088
1089 def run(self):
1090 """
1091 Run target (passed to the constructor). No point in calling this
1092 function directly. Call start() to make this function run in a new
1093 thread.
1094 """
1095 self._e = None
1096 self._retval = None
1097 try:
1098 try:
1099 self._retval = self._target(*self._args, **self._kwargs)
1100 except:
1101 self._e = sys.exc_info()
1102 raise
1103 finally:
1104 # Avoid circular references (start() may be called only once so
1105 # it's OK to delete these)
1106 del self._target, self._args, self._kwargs
1107
1108
1109 def join(self, timeout=None):
1110 """
1111 Join the thread. If target raised an exception, re-raise it.
1112 Otherwise, return the value returned by target.
1113
1114 @param timeout: Timeout value to pass to threading.Thread.join().
1115 """
1116 threading.Thread.join(self, timeout)
1117 try:
1118 if self._e:
1119 raise self._e[0], self._e[1], self._e[2]
1120 else:
1121 return self._retval
1122 finally:
1123 # Avoid circular references (join() may be called multiple times
1124 # so we can't delete these)
1125 self._e = None
1126 self._retval = None
1127
1128
1129def parallel(targets):
1130 """
1131 Run multiple functions in parallel.
1132
1133 @param targets: A sequence of tuples or functions. If it's a sequence of
1134 tuples, each tuple will be interpreted as (target, args, kwargs) or
1135 (target, args) or (target,) depending on its length. If it's a
1136 sequence of functions, the functions will be called without
1137 arguments.
1138 @return: A list of the values returned by the functions called.
1139 """
1140 threads = []
1141 for target in targets:
1142 if isinstance(target, tuple) or isinstance(target, list):
1143 t = Thread(*target)
1144 else:
1145 t = Thread(target)
1146 threads.append(t)
1147 t.start()
1148 return [t.join() for t in threads]
1149
1150
lmr84154412010-02-03 18:34:43 +00001151class KvmLoggingConfig(logging_config.LoggingConfig):
1152 """
1153 Used with the sole purpose of providing convenient logging setup
1154 for the KVM test auxiliary programs.
1155 """
1156 def configure_logging(self, results_dir=None, verbose=False):
1157 super(KvmLoggingConfig, self).configure_logging(use_console=True,
1158 verbose=verbose)
1159
1160
lmr31af3a12010-01-18 16:46:52 +00001161class PciAssignable(object):
1162 """
1163 Request PCI assignable devices on host. It will check whether to request
1164 PF (physical Functions) or VF (Virtual Functions).
1165 """
lmr83d3b1a2010-01-19 08:14:36 +00001166 def __init__(self, type="vf", driver=None, driver_option=None,
lmr31af3a12010-01-18 16:46:52 +00001167 names=None, devices_requested=None):
1168 """
1169 Initialize parameter 'type' which could be:
lmr83d3b1a2010-01-19 08:14:36 +00001170 vf: Virtual Functions
1171 pf: Physical Function (actual hardware)
lmr31af3a12010-01-18 16:46:52 +00001172 mixed: Both includes VFs and PFs
1173
1174 If pass through Physical NIC cards, we need to specify which devices
1175 to be assigned, e.g. 'eth1 eth2'.
1176
1177 If pass through Virtual Functions, we need to specify how many vfs
1178 are going to be assigned, e.g. passthrough_count = 8 and max_vfs in
1179 config file.
1180
1181 @param type: PCI device type.
1182 @param driver: Kernel module for the PCI assignable device.
1183 @param driver_option: Module option to specify the maximum number of
1184 VFs (eg 'max_vfs=7')
1185 @param names: Physical NIC cards correspondent network interfaces,
1186 e.g.'eth1 eth2 ...'
1187 @param devices_requested: Number of devices being requested.
1188 """
1189 self.type = type
1190 self.driver = driver
1191 self.driver_option = driver_option
1192 if names:
1193 self.name_list = names.split()
1194 if devices_requested:
1195 self.devices_requested = int(devices_requested)
1196 else:
1197 self.devices_requested = None
1198
1199
1200 def _get_pf_pci_id(self, name, search_str):
1201 """
1202 Get the PF PCI ID according to name.
1203
1204 @param name: Name of the PCI device.
1205 @param search_str: Search string to be used on lspci.
1206 """
1207 cmd = "ethtool -i %s | awk '/bus-info/ {print $2}'" % name
1208 s, pci_id = commands.getstatusoutput(cmd)
1209 if not (s or "Cannot get driver information" in pci_id):
1210 return pci_id[5:]
1211 cmd = "lspci | awk '/%s/ {print $1}'" % search_str
1212 pci_ids = [id for id in commands.getoutput(cmd).splitlines()]
1213 nic_id = int(re.search('[0-9]+', name).group(0))
1214 if (len(pci_ids) - 1) < nic_id:
1215 return None
1216 return pci_ids[nic_id]
1217
1218
1219 def _release_dev(self, pci_id):
1220 """
1221 Release a single PCI device.
1222
1223 @param pci_id: PCI ID of a given PCI device.
1224 """
1225 base_dir = "/sys/bus/pci"
1226 full_id = get_full_pci_id(pci_id)
1227 vendor_id = get_vendor_from_pci_id(pci_id)
1228 drv_path = os.path.join(base_dir, "devices/%s/driver" % full_id)
1229 if 'pci-stub' in os.readlink(drv_path):
1230 cmd = "echo '%s' > %s/new_id" % (vendor_id, drv_path)
1231 if os.system(cmd):
1232 return False
1233
1234 stub_path = os.path.join(base_dir, "drivers/pci-stub")
1235 cmd = "echo '%s' > %s/unbind" % (full_id, stub_path)
1236 if os.system(cmd):
1237 return False
1238
1239 driver = self.dev_drivers[pci_id]
1240 cmd = "echo '%s' > %s/bind" % (full_id, driver)
1241 if os.system(cmd):
1242 return False
1243
1244 return True
1245
1246
1247 def get_vf_devs(self):
1248 """
1249 Catch all VFs PCI IDs.
1250
1251 @return: List with all PCI IDs for the Virtual Functions avaliable
1252 """
1253 if not self.sr_iov_setup():
1254 return []
1255
1256 cmd = "lspci | awk '/Virtual Function/ {print $1}'"
1257 return commands.getoutput(cmd).split()
1258
1259
1260 def get_pf_devs(self):
1261 """
1262 Catch all PFs PCI IDs.
1263
1264 @return: List with all PCI IDs for the physical hardware requested
1265 """
1266 pf_ids = []
1267 for name in self.name_list:
1268 pf_id = self._get_pf_pci_id(name, "Ethernet")
1269 if not pf_id:
1270 continue
1271 pf_ids.append(pf_id)
1272 return pf_ids
1273
1274
1275 def get_devs(self, count):
1276 """
1277 Check out all devices' PCI IDs according to their name.
1278
1279 @param count: count number of PCI devices needed for pass through
1280 @return: a list of all devices' PCI IDs
1281 """
lmr83d3b1a2010-01-19 08:14:36 +00001282 if self.type == "vf":
lmr31af3a12010-01-18 16:46:52 +00001283 vf_ids = self.get_vf_devs()
lmr83d3b1a2010-01-19 08:14:36 +00001284 elif self.type == "pf":
lmr31af3a12010-01-18 16:46:52 +00001285 vf_ids = self.get_pf_devs()
1286 elif self.type == "mixed":
1287 vf_ids = self.get_vf_devs()
1288 vf_ids.extend(self.get_pf_devs())
1289 return vf_ids[0:count]
1290
1291
1292 def get_vfs_count(self):
1293 """
1294 Get VFs count number according to lspci.
1295 """
lmr224a5412010-03-17 12:00:36 +00001296 # FIXME: Need to think out a method of identify which
1297 # 'virtual function' belongs to which physical card considering
1298 # that if the host has more than one 82576 card. PCI_ID?
lmr31af3a12010-01-18 16:46:52 +00001299 cmd = "lspci | grep 'Virtual Function' | wc -l"
lmr224a5412010-03-17 12:00:36 +00001300 return int(commands.getoutput(cmd))
lmr31af3a12010-01-18 16:46:52 +00001301
1302
1303 def check_vfs_count(self):
1304 """
1305 Check VFs count number according to the parameter driver_options.
1306 """
lmr224a5412010-03-17 12:00:36 +00001307 # Network card 82576 has two network interfaces and each can be
1308 # virtualized up to 7 virtual functions, therefore we multiply
1309 # two for the value of driver_option 'max_vfs'.
1310 expected_count = int((re.findall("(\d)", self.driver_option)[0])) * 2
1311 return (self.get_vfs_count == expected_count)
lmr31af3a12010-01-18 16:46:52 +00001312
1313
1314 def is_binded_to_stub(self, full_id):
1315 """
1316 Verify whether the device with full_id is already binded to pci-stub.
1317
1318 @param full_id: Full ID for the given PCI device
1319 """
1320 base_dir = "/sys/bus/pci"
1321 stub_path = os.path.join(base_dir, "drivers/pci-stub")
1322 if os.path.exists(os.path.join(stub_path, full_id)):
1323 return True
1324 return False
1325
1326
1327 def sr_iov_setup(self):
1328 """
1329 Ensure the PCI device is working in sr_iov mode.
1330
1331 Check if the PCI hardware device drive is loaded with the appropriate,
1332 parameters (number of VFs), and if it's not, perform setup.
1333
1334 @return: True, if the setup was completed successfuly, False otherwise.
1335 """
1336 re_probe = False
1337 s, o = commands.getstatusoutput('lsmod | grep %s' % self.driver)
1338 if s:
1339 re_probe = True
1340 elif not self.check_vfs_count():
1341 os.system("modprobe -r %s" % self.driver)
1342 re_probe = True
lmr224a5412010-03-17 12:00:36 +00001343 else:
1344 return True
lmr31af3a12010-01-18 16:46:52 +00001345
1346 # Re-probe driver with proper number of VFs
1347 if re_probe:
1348 cmd = "modprobe %s %s" % (self.driver, self.driver_option)
lmr224a5412010-03-17 12:00:36 +00001349 logging.info("Loading the driver '%s' with option '%s'" %
1350 (self.driver, self.driver_option))
lmr31af3a12010-01-18 16:46:52 +00001351 s, o = commands.getstatusoutput(cmd)
1352 if s:
1353 return False
lmr31af3a12010-01-18 16:46:52 +00001354 return True
1355
1356
1357 def request_devs(self):
1358 """
1359 Implement setup process: unbind the PCI device and then bind it
1360 to the pci-stub driver.
1361
1362 @return: a list of successfully requested devices' PCI IDs.
1363 """
1364 base_dir = "/sys/bus/pci"
1365 stub_path = os.path.join(base_dir, "drivers/pci-stub")
1366
1367 self.pci_ids = self.get_devs(self.devices_requested)
lmrbc72f082010-02-08 10:02:40 +00001368 logging.debug("The following pci_ids were found: %s", self.pci_ids)
lmr31af3a12010-01-18 16:46:52 +00001369 requested_pci_ids = []
1370 self.dev_drivers = {}
1371
1372 # Setup all devices specified for assignment to guest
1373 for pci_id in self.pci_ids:
1374 full_id = get_full_pci_id(pci_id)
1375 if not full_id:
1376 continue
1377 drv_path = os.path.join(base_dir, "devices/%s/driver" % full_id)
1378 dev_prev_driver= os.path.realpath(os.path.join(drv_path,
1379 os.readlink(drv_path)))
1380 self.dev_drivers[pci_id] = dev_prev_driver
1381
1382 # Judge whether the device driver has been binded to stub
1383 if not self.is_binded_to_stub(full_id):
lmrbc72f082010-02-08 10:02:40 +00001384 logging.debug("Binding device %s to stub", full_id)
lmr31af3a12010-01-18 16:46:52 +00001385 vendor_id = get_vendor_from_pci_id(pci_id)
1386 stub_new_id = os.path.join(stub_path, 'new_id')
1387 unbind_dev = os.path.join(drv_path, 'unbind')
1388 stub_bind = os.path.join(stub_path, 'bind')
1389
1390 info_write_to_files = [(vendor_id, stub_new_id),
1391 (full_id, unbind_dev),
1392 (full_id, stub_bind)]
1393
1394 for content, file in info_write_to_files:
1395 try:
lmrbc72f082010-02-08 10:02:40 +00001396 utils.open_write_close(file, content)
lmr31af3a12010-01-18 16:46:52 +00001397 except IOError:
lmrbc72f082010-02-08 10:02:40 +00001398 logging.debug("Failed to write %s to file %s", content,
1399 file)
lmr31af3a12010-01-18 16:46:52 +00001400 continue
1401
1402 if not self.is_binded_to_stub(full_id):
lmrbc72f082010-02-08 10:02:40 +00001403 logging.error("Binding device %s to stub failed", pci_id)
lmr31af3a12010-01-18 16:46:52 +00001404 continue
1405 else:
lmrbc72f082010-02-08 10:02:40 +00001406 logging.debug("Device %s already binded to stub", pci_id)
lmr31af3a12010-01-18 16:46:52 +00001407 requested_pci_ids.append(pci_id)
1408 self.pci_ids = requested_pci_ids
1409 return self.pci_ids
1410
1411
1412 def release_devs(self):
1413 """
1414 Release all PCI devices currently assigned to VMs back to the
1415 virtualization host.
1416 """
1417 try:
1418 for pci_id in self.dev_drivers:
1419 if not self._release_dev(pci_id):
lmrbc72f082010-02-08 10:02:40 +00001420 logging.error("Failed to release device %s to host", pci_id)
lmr31af3a12010-01-18 16:46:52 +00001421 else:
lmrbc72f082010-02-08 10:02:40 +00001422 logging.info("Released device %s successfully", pci_id)
lmr31af3a12010-01-18 16:46:52 +00001423 except:
1424 return
Eric Lie0493a42010-11-15 13:05:43 -08001425
1426
1427class KojiDownloader(object):
1428 """
1429 Stablish a connection with the build system, either koji or brew.
1430
1431 This class provides a convenience methods to retrieve packages hosted on
1432 the build system.
1433 """
1434 def __init__(self, cmd):
1435 """
1436 Verifies whether the system has koji or brew installed, then loads
1437 the configuration file that will be used to download the files.
1438
1439 @param cmd: Command name, either 'brew' or 'koji'. It is important
1440 to figure out the appropriate configuration used by the
1441 downloader.
1442 @param dst_dir: Destination dir for the packages.
1443 """
1444 if not KOJI_INSTALLED:
1445 raise ValueError('No koji/brew installed on the machine')
1446
1447 if os.path.isfile(cmd):
1448 koji_cmd = cmd
1449 else:
1450 koji_cmd = os_dep.command(cmd)
1451
1452 logging.debug("Found %s as the buildsystem interface", koji_cmd)
1453
1454 config_map = {'/usr/bin/koji': '/etc/koji.conf',
1455 '/usr/bin/brew': '/etc/brewkoji.conf'}
1456
1457 try:
1458 config_file = config_map[koji_cmd]
1459 except IndexError:
1460 raise ValueError('Could not find config file for %s' % koji_cmd)
1461
1462 base_name = os.path.basename(koji_cmd)
1463 if os.access(config_file, os.F_OK):
1464 f = open(config_file)
1465 config = ConfigParser.ConfigParser()
1466 config.readfp(f)
1467 f.close()
1468 else:
1469 raise IOError('Configuration file %s missing or with wrong '
1470 'permissions' % config_file)
1471
1472 if config.has_section(base_name):
1473 self.koji_options = {}
1474 session_options = {}
1475 server = None
1476 for name, value in config.items(base_name):
1477 if name in ('user', 'password', 'debug_xmlrpc', 'debug'):
1478 session_options[name] = value
1479 self.koji_options[name] = value
1480 self.session = koji.ClientSession(self.koji_options['server'],
1481 session_options)
1482 else:
1483 raise ValueError('Koji config file %s does not have a %s '
1484 'session' % (config_file, base_name))
1485
1486
1487 def get(self, src_package, dst_dir, rfilter=None, tag=None, build=None,
1488 arch=None):
1489 """
1490 Download a list of packages from the build system.
1491
1492 This will download all packages originated from source package [package]
1493 with given [tag] or [build] for the architecture reported by the
1494 machine.
1495
1496 @param src_package: Source package name.
1497 @param dst_dir: Destination directory for the downloaded packages.
1498 @param rfilter: Regexp filter, only download the packages that match
1499 that particular filter.
1500 @param tag: Build system tag.
1501 @param build: Build system ID.
1502 @param arch: Package arch. Useful when you want to download noarch
1503 packages.
1504
1505 @return: List of paths with the downloaded rpm packages.
1506 """
1507 if build and build.isdigit():
1508 build = int(build)
1509
1510 if tag and build:
1511 logging.info("Both tag and build parameters provided, ignoring tag "
1512 "parameter...")
1513
1514 if not tag and not build:
1515 raise ValueError("Koji install selected but neither koji_tag "
1516 "nor koji_build parameters provided. Please "
1517 "provide an appropriate tag or build name.")
1518
1519 if not build:
Eric Li7edb3042011-01-06 17:57:17 -08001520 builds = self.session.listTagged(tag, latest=True, inherit=True,
Eric Lie0493a42010-11-15 13:05:43 -08001521 package=src_package)
1522 if not builds:
1523 raise ValueError("Tag %s has no builds of %s" % (tag,
1524 src_package))
1525 info = builds[0]
1526 else:
1527 info = self.session.getBuild(build)
1528
1529 if info is None:
1530 raise ValueError('No such brew/koji build: %s' % build)
1531
1532 if arch is None:
1533 arch = utils.get_arch()
1534
1535 rpms = self.session.listRPMs(buildID=info['id'],
1536 arches=arch)
1537 if not rpms:
1538 raise ValueError("No %s packages available for %s" %
1539 arch, koji.buildLabel(info))
1540
1541 rpm_paths = []
1542 for rpm in rpms:
1543 rpm_name = koji.pathinfo.rpm(rpm)
1544 url = ("%s/%s/%s/%s/%s" % (self.koji_options['pkgurl'],
1545 info['package_name'],
1546 info['version'], info['release'],
1547 rpm_name))
1548 if rfilter:
1549 filter_regexp = re.compile(rfilter, re.IGNORECASE)
1550 if filter_regexp.match(os.path.basename(rpm_name)):
1551 download = True
1552 else:
1553 download = False
1554 else:
1555 download = True
1556
1557 if download:
1558 r = utils.get_file(url,
1559 os.path.join(dst_dir, os.path.basename(url)))
1560 rpm_paths.append(r)
1561
1562 return rpm_paths
Eric Li7edb3042011-01-06 17:57:17 -08001563
1564
1565def umount(src, mount_point, type):
1566 """
1567 Umount the src mounted in mount_point.
1568
1569 @src: mount source
1570 @mount_point: mount point
1571 @type: file system type
1572 """
1573
1574 mount_string = "%s %s %s" % (src, mount_point, type)
1575 if mount_string in file("/etc/mtab").read():
1576 umount_cmd = "umount %s" % mount_point
1577 try:
1578 utils.system(umount_cmd)
1579 return True
1580 except error.CmdError:
1581 return False
1582 else:
1583 logging.debug("%s is not mounted under %s" % (src, mount_point))
1584 return True
1585
1586
1587def mount(src, mount_point, type, perm="rw"):
1588 """
1589 Mount the src into mount_point of the host.
1590
1591 @src: mount source
1592 @mount_point: mount point
1593 @type: file system type
1594 @perm: mount premission
1595 """
1596 umount(src, mount_point, type)
1597 mount_string = "%s %s %s %s" % (src, mount_point, type, perm)
1598
1599 if mount_string in file("/etc/mtab").read():
1600 logging.debug("%s is already mounted in %s with %s" %
1601 (src, mount_point, perm))
1602 return True
1603
1604 mount_cmd = "mount -t %s %s %s -o %s" % (type, src, mount_point, perm)
1605 try:
1606 utils.system(mount_cmd)
1607 except error.CmdError:
1608 return False
1609
1610 logging.debug("Verify the mount through /etc/mtab")
1611 if mount_string in file("/etc/mtab").read():
1612 logging.debug("%s is successfully mounted" % src)
1613 return True
1614 else:
1615 logging.error("Can't find mounted NFS share - /etc/mtab contents \n%s" %
1616 file("/etc/mtab").read())
1617 return False