blob: b849b37b2dd83a1a8670a4a5d71af82459d6a40e [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 Lie0493a42010-11-15 13:05:43 -08008import fcntl, shelve, ConfigParser
9from 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
lmre8a66dd2009-09-15 19:51:07 +000030def dump_env(obj, filename):
31 """
32 Dump KVM test environment to a file.
33
34 @param filename: Path to a file where the environment will be dumped to.
35 """
36 file = open(filename, "w")
37 cPickle.dump(obj, file)
38 file.close()
39
40
lmr287363c2010-07-08 23:52:03 +000041def load_env(filename, version):
lmre8a66dd2009-09-15 19:51:07 +000042 """
lmr287363c2010-07-08 23:52:03 +000043 Load KVM test environment from an env file.
44 If the version recorded in the file is lower than version, return an empty
45 env. If some other error occurs during unpickling, return an empty env.
lmre8a66dd2009-09-15 19:51:07 +000046
lmr287363c2010-07-08 23:52:03 +000047 @param filename: Path to an env file.
lmre8a66dd2009-09-15 19:51:07 +000048 """
lmr287363c2010-07-08 23:52:03 +000049 default = {"version": version}
lmre8a66dd2009-09-15 19:51:07 +000050 try:
51 file = open(filename, "r")
lmr287363c2010-07-08 23:52:03 +000052 env = cPickle.load(file)
lmr8b5400c2010-03-23 15:32:29 +000053 file.close()
lmr287363c2010-07-08 23:52:03 +000054 if env.get("version", 0) < version:
55 logging.warn("Incompatible env file found. Not using it.")
56 return default
57 return env
lmr8b5400c2010-03-23 15:32:29 +000058 # Almost any exception can be raised during unpickling, so let's catch
59 # them all
lmr258f7932010-06-14 15:37:02 +000060 except Exception, e:
61 logging.warn(e)
lmre8a66dd2009-09-15 19:51:07 +000062 return default
lmre8a66dd2009-09-15 19:51:07 +000063
64
lmr6f669ce2009-05-31 19:02:42 +000065def get_sub_dict(dict, name):
66 """
67 Return a "sub-dict" corresponding to a specific object.
68
69 Operate on a copy of dict: for each key that ends with the suffix
70 "_" + name, strip the suffix from the key, and set the value of
71 the stripped key to that of the key. Return the resulting dict.
72
73 @param name: Suffix of the key we want to set the value.
74 """
75 suffix = "_" + name
76 new_dict = dict.copy()
77 for key in dict.keys():
78 if key.endswith(suffix):
79 new_key = key.split(suffix)[0]
80 new_dict[new_key] = dict[key]
81 return new_dict
82
83
84def get_sub_dict_names(dict, keyword):
85 """
86 Return a list of "sub-dict" names that may be extracted with get_sub_dict.
87
88 This function may be modified to change the behavior of all functions that
89 deal with multiple objects defined in dicts (e.g. VMs, images, NICs).
90
91 @param keyword: A key in dict (e.g. "vms", "images", "nics").
92 """
93 names = dict.get(keyword)
94 if names:
95 return names.split()
96 else:
97 return []
98
99
lmrac5089b2009-08-13 04:05:47 +0000100# Functions related to MAC/IP addresses
101
Eric Lie0493a42010-11-15 13:05:43 -0800102def _open_mac_pool(lock_mode):
103 lock_file = open("/tmp/mac_lock", "w+")
104 fcntl.lockf(lock_file, lock_mode)
105 pool = shelve.open("/tmp/address_pool")
106 return pool, lock_file
107
108
109def _close_mac_pool(pool, lock_file):
110 pool.close()
111 fcntl.lockf(lock_file, fcntl.LOCK_UN)
112 lock_file.close()
113
114
115def _generate_mac_address_prefix(mac_pool):
lmrac5089b2009-08-13 04:05:47 +0000116 """
Eric Lie0493a42010-11-15 13:05:43 -0800117 Generate a random MAC address prefix and add it to the MAC pool dictionary.
118 If there's a MAC prefix there already, do not update the MAC pool and just
119 return what's in there. By convention we will set KVM autotest MAC
120 addresses to start with 0x9a.
lmrac5089b2009-08-13 04:05:47 +0000121
Eric Lie0493a42010-11-15 13:05:43 -0800122 @param mac_pool: The MAC address pool object.
123 @return: The MAC address prefix.
lmrac5089b2009-08-13 04:05:47 +0000124 """
Eric Lie0493a42010-11-15 13:05:43 -0800125 if "prefix" in mac_pool:
126 prefix = mac_pool["prefix"]
127 logging.debug("Used previously generated MAC address prefix for this "
128 "host: %s", prefix)
129 else:
130 r = random.SystemRandom()
131 prefix = "9a:%02x:%02x:%02x:" % (r.randint(0x00, 0xff),
132 r.randint(0x00, 0xff),
133 r.randint(0x00, 0xff))
134 mac_pool["prefix"] = prefix
135 logging.debug("Generated MAC address prefix for this host: %s", prefix)
136 return prefix
lmrac5089b2009-08-13 04:05:47 +0000137
138
Eric Lie0493a42010-11-15 13:05:43 -0800139def generate_mac_address(vm_instance, nic_index):
lmrac5089b2009-08-13 04:05:47 +0000140 """
Eric Lie0493a42010-11-15 13:05:43 -0800141 Randomly generate a MAC address and add it to the MAC address pool.
lmrac5089b2009-08-13 04:05:47 +0000142
Eric Lie0493a42010-11-15 13:05:43 -0800143 Try to generate a MAC address based on a randomly generated MAC address
144 prefix and add it to a persistent dictionary.
145 key = VM instance + NIC index, value = MAC address
146 e.g. {'20100310-165222-Wt7l:0': '9a:5d:94:6a:9b:f9'}
147
148 @param vm_instance: The instance attribute of a VM.
149 @param nic_index: The index of the NIC.
150 @return: MAC address string.
lmrac5089b2009-08-13 04:05:47 +0000151 """
Eric Lie0493a42010-11-15 13:05:43 -0800152 mac_pool, lock_file = _open_mac_pool(fcntl.LOCK_EX)
153 key = "%s:%s" % (vm_instance, nic_index)
154 if key in mac_pool:
155 mac = mac_pool[key]
156 else:
157 prefix = _generate_mac_address_prefix(mac_pool)
158 r = random.SystemRandom()
159 while key not in mac_pool:
160 mac = prefix + "%02x:%02x" % (r.randint(0x00, 0xff),
161 r.randint(0x00, 0xff))
162 if mac in mac_pool.values():
163 continue
164 mac_pool[key] = mac
165 logging.debug("Generated MAC address for NIC %s: %s", key, mac)
166 _close_mac_pool(mac_pool, lock_file)
167 return mac
lmrac5089b2009-08-13 04:05:47 +0000168
169
Eric Lie0493a42010-11-15 13:05:43 -0800170def free_mac_address(vm_instance, nic_index):
lmrac5089b2009-08-13 04:05:47 +0000171 """
Eric Lie0493a42010-11-15 13:05:43 -0800172 Remove a MAC address from the address pool.
lmrac5089b2009-08-13 04:05:47 +0000173
Eric Lie0493a42010-11-15 13:05:43 -0800174 @param vm_instance: The instance attribute of a VM.
175 @param nic_index: The index of the NIC.
lmrac5089b2009-08-13 04:05:47 +0000176 """
Eric Lie0493a42010-11-15 13:05:43 -0800177 mac_pool, lock_file = _open_mac_pool(fcntl.LOCK_EX)
178 key = "%s:%s" % (vm_instance, nic_index)
179 if key in mac_pool:
180 logging.debug("Freeing MAC address for NIC %s: %s", key, mac_pool[key])
181 del mac_pool[key]
182 _close_mac_pool(mac_pool, lock_file)
lmrac5089b2009-08-13 04:05:47 +0000183
184
Eric Lie0493a42010-11-15 13:05:43 -0800185def set_mac_address(vm_instance, nic_index, mac):
lmrac5089b2009-08-13 04:05:47 +0000186 """
Eric Lie0493a42010-11-15 13:05:43 -0800187 Set a MAC address in the pool.
lmrac5089b2009-08-13 04:05:47 +0000188
Eric Lie0493a42010-11-15 13:05:43 -0800189 @param vm_instance: The instance attribute of a VM.
190 @param nic_index: The index of the NIC.
lmrac5089b2009-08-13 04:05:47 +0000191 """
Eric Lie0493a42010-11-15 13:05:43 -0800192 mac_pool, lock_file = _open_mac_pool(fcntl.LOCK_EX)
193 mac_pool["%s:%s" % (vm_instance, nic_index)] = mac
194 _close_mac_pool(mac_pool, lock_file)
lmrac5089b2009-08-13 04:05:47 +0000195
196
Eric Lie0493a42010-11-15 13:05:43 -0800197def get_mac_address(vm_instance, nic_index):
lmrac5089b2009-08-13 04:05:47 +0000198 """
Eric Lie0493a42010-11-15 13:05:43 -0800199 Return a MAC address from the pool.
lmrac5089b2009-08-13 04:05:47 +0000200
Eric Lie0493a42010-11-15 13:05:43 -0800201 @param vm_instance: The instance attribute of a VM.
202 @param nic_index: The index of the NIC.
203 @return: MAC address string.
lmrac5089b2009-08-13 04:05:47 +0000204 """
Eric Lie0493a42010-11-15 13:05:43 -0800205 mac_pool, lock_file = _open_mac_pool(fcntl.LOCK_SH)
206 mac = mac_pool.get("%s:%s" % (vm_instance, nic_index))
207 _close_mac_pool(mac_pool, lock_file)
208 return mac
lmrf3d3e522010-03-23 16:38:12 +0000209
210
lmr53d3e932009-09-09 21:56:18 +0000211def verify_ip_address_ownership(ip, macs, timeout=10.0):
lmree90dd92009-08-13 04:13:39 +0000212 """
lmr53d3e932009-09-09 21:56:18 +0000213 Use arping and the ARP cache to make sure a given IP address belongs to one
214 of the given MAC addresses.
lmree90dd92009-08-13 04:13:39 +0000215
216 @param ip: An IP address.
217 @param macs: A list or tuple of MAC addresses.
218 @return: True iff ip is assigned to a MAC address in macs.
219 """
lmr53d3e932009-09-09 21:56:18 +0000220 # Compile a regex that matches the given IP address and any of the given
221 # MAC addresses
lmree90dd92009-08-13 04:13:39 +0000222 mac_regex = "|".join("(%s)" % mac for mac in macs)
lmre35507b2009-10-28 16:53:08 +0000223 regex = re.compile(r"\b%s\b.*\b(%s)\b" % (ip, mac_regex), re.IGNORECASE)
lmree90dd92009-08-13 04:13:39 +0000224
lmr53d3e932009-09-09 21:56:18 +0000225 # Check the ARP cache
lmr3eb39c72010-07-08 23:34:05 +0000226 o = commands.getoutput("%s -n" % find_command("arp"))
lmre35507b2009-10-28 16:53:08 +0000227 if regex.search(o):
lmree90dd92009-08-13 04:13:39 +0000228 return True
229
lmr53d3e932009-09-09 21:56:18 +0000230 # Get the name of the bridge device for arping
lmr3eb39c72010-07-08 23:34:05 +0000231 o = commands.getoutput("%s route get %s" % (find_command("ip"), ip))
lmr53d3e932009-09-09 21:56:18 +0000232 dev = re.findall("dev\s+\S+", o, re.IGNORECASE)
233 if not dev:
234 return False
235 dev = dev[0].split()[-1]
236
237 # Send an ARP request
lmr3eb39c72010-07-08 23:34:05 +0000238 o = commands.getoutput("%s -f -c 3 -I %s %s" %
239 (find_command("arping"), dev, ip))
lmre35507b2009-10-28 16:53:08 +0000240 return bool(regex.search(o))
lmree90dd92009-08-13 04:13:39 +0000241
242
lmr6f669ce2009-05-31 19:02:42 +0000243# Functions for working with the environment (a dict-like object)
244
245def is_vm(obj):
246 """
247 Tests whether a given object is a VM object.
248
249 @param obj: Python object (pretty much everything on python).
250 """
251 return obj.__class__.__name__ == "VM"
252
253
254def env_get_all_vms(env):
255 """
256 Return a list of all VM objects on a given environment.
257
258 @param env: Dictionary with environment items.
259 """
260 vms = []
261 for obj in env.values():
262 if is_vm(obj):
263 vms.append(obj)
264 return vms
265
266
267def env_get_vm(env, name):
268 """
269 Return a VM object by its name.
270
271 @param name: VM name.
272 """
273 return env.get("vm__%s" % name)
274
275
276def env_register_vm(env, name, vm):
277 """
278 Register a given VM in a given env.
279
280 @param env: Environment where we will register the VM.
281 @param name: VM name.
282 @param vm: VM object.
283 """
284 env["vm__%s" % name] = vm
285
286
287def env_unregister_vm(env, name):
288 """
289 Remove a given VM from a given env.
290
291 @param env: Environment where we will un-register the VM.
292 @param name: VM name.
293 """
294 del env["vm__%s" % name]
295
296
297# Utility functions for dealing with external processes
298
lmr049239c2010-07-08 23:27:18 +0000299def find_command(cmd):
300 for dir in ["/usr/local/sbin", "/usr/local/bin",
301 "/usr/sbin", "/usr/bin", "/sbin", "/bin"]:
302 file = os.path.join(dir, cmd)
303 if os.path.exists(file):
304 return file
305 raise ValueError('Missing command: %s' % cmd)
306
307
lmr6f669ce2009-05-31 19:02:42 +0000308def pid_exists(pid):
309 """
310 Return True if a given PID exists.
311
312 @param pid: Process ID number.
313 """
314 try:
315 os.kill(pid, 0)
316 return True
317 except:
318 return False
319
320
321def safe_kill(pid, signal):
322 """
323 Attempt to send a signal to a given process that may or may not exist.
324
325 @param signal: Signal number.
326 """
327 try:
328 os.kill(pid, signal)
329 return True
330 except:
331 return False
332
333
lmr1aeaefb2009-09-09 22:12:49 +0000334def kill_process_tree(pid, sig=signal.SIGKILL):
335 """Signal a process and all of its children.
336
337 If the process does not exist -- return.
338
339 @param pid: The pid of the process to signal.
340 @param sig: The signal to send to the processes.
341 """
342 if not safe_kill(pid, signal.SIGSTOP):
343 return
344 children = commands.getoutput("ps --ppid=%d -o pid=" % pid).split()
345 for child in children:
346 kill_process_tree(int(child), sig)
347 safe_kill(pid, sig)
348 safe_kill(pid, signal.SIGCONT)
349
350
lmr117bbcf2009-09-15 07:12:39 +0000351def get_latest_kvm_release_tag(release_listing):
lmr3f0b0cc2009-06-10 02:25:23 +0000352 """
353 Fetches the latest release tag for KVM.
354
lmr117bbcf2009-09-15 07:12:39 +0000355 @param release_listing: URL that contains a list of the Source Forge
356 KVM project files.
lmr3f0b0cc2009-06-10 02:25:23 +0000357 """
358 try:
lmr117bbcf2009-09-15 07:12:39 +0000359 release_page = utils.urlopen(release_listing)
360 data = release_page.read()
361 release_page.close()
lmr8ea274b2009-07-06 13:42:35 +0000362 rx = re.compile("kvm-(\d+).tar.gz", re.IGNORECASE)
lmr3f0b0cc2009-06-10 02:25:23 +0000363 matches = rx.findall(data)
lmr32525382009-08-10 13:53:37 +0000364 # In all regexp matches to something that looks like a release tag,
365 # get the largest integer. That will be our latest release tag.
366 latest_tag = max(int(x) for x in matches)
367 return str(latest_tag)
lmr3f0b0cc2009-06-10 02:25:23 +0000368 except Exception, e:
369 message = "Could not fetch latest KVM release tag: %s" % str(e)
370 logging.error(message)
371 raise error.TestError(message)
372
373
374def get_git_branch(repository, branch, srcdir, commit=None, lbranch=None):
375 """
376 Retrieves a given git code repository.
377
378 @param repository: Git repository URL
379 """
lmr160e9942010-06-09 14:09:19 +0000380 logging.info("Fetching git [REP '%s' BRANCH '%s' COMMIT '%s'] -> %s",
lmr3f0b0cc2009-06-10 02:25:23 +0000381 repository, branch, commit, srcdir)
382 if not os.path.exists(srcdir):
383 os.makedirs(srcdir)
384 os.chdir(srcdir)
385
386 if os.path.exists(".git"):
387 utils.system("git reset --hard")
388 else:
389 utils.system("git init")
390
391 if not lbranch:
392 lbranch = branch
393
394 utils.system("git fetch -q -f -u -t %s %s:%s" %
395 (repository, branch, lbranch))
396 utils.system("git checkout %s" % lbranch)
397 if commit:
398 utils.system("git checkout %s" % commit)
399
400 h = utils.system_output('git log --pretty=format:"%H" -1')
lmr05cec0f2010-01-26 17:50:29 +0000401 try:
402 desc = "tag %s" % utils.system_output("git describe")
403 except error.CmdError:
404 desc = "no tag found"
405
lmr3f0b0cc2009-06-10 02:25:23 +0000406 logging.info("Commit hash for %s is %s (%s)" % (repository, h.strip(),
407 desc))
408 return srcdir
409
410
lmr3f0b0cc2009-06-10 02:25:23 +0000411def check_kvm_source_dir(source_dir):
412 """
413 Inspects the kvm source directory and verifies its disposition. In some
414 occasions build may be dependant on the source directory disposition.
415 The reason why the return codes are numbers is that we might have more
416 changes on the source directory layout, so it's not scalable to just use
417 strings like 'old_repo', 'new_repo' and such.
418
419 @param source_dir: Source code path that will be inspected.
420 """
421 os.chdir(source_dir)
422 has_qemu_dir = os.path.isdir('qemu')
423 has_kvm_dir = os.path.isdir('kvm')
424 if has_qemu_dir and not has_kvm_dir:
425 logging.debug("qemu directory detected, source dir layout 1")
426 return 1
427 if has_kvm_dir and not has_qemu_dir:
428 logging.debug("kvm directory detected, source dir layout 2")
429 return 2
430 else:
431 raise error.TestError("Unknown source dir layout, cannot proceed.")
432
433
lmrf9349c32009-07-23 01:44:24 +0000434# The following are functions used for SSH, SCP and Telnet communication with
435# guests.
lmr6f669ce2009-05-31 19:02:42 +0000436
lmra4cd1f12010-06-22 02:01:00 +0000437def _remote_login(session, username, password, prompt, timeout=10):
lmr6f669ce2009-05-31 19:02:42 +0000438 """
lmr158604f2010-06-22 01:57:34 +0000439 Log into a remote host (guest) using SSH or Telnet. Wait for questions
440 and provide answers. If timeout expires while waiting for output from the
441 child (e.g. a password prompt or a shell prompt) -- fail.
lmr6f669ce2009-05-31 19:02:42 +0000442
443 @brief: Log into a remote host (guest) using SSH or Telnet.
444
lmr158604f2010-06-22 01:57:34 +0000445 @param session: A kvm_expect or kvm_shell_session instance to operate on
lmra4cd1f12010-06-22 02:01:00 +0000446 @param username: The username to send in reply to a login prompt
lmr6f669ce2009-05-31 19:02:42 +0000447 @param password: The password to send in reply to a password prompt
448 @param prompt: The shell prompt that indicates a successful login
lmr6f669ce2009-05-31 19:02:42 +0000449 @param timeout: The maximal time duration (in seconds) to wait for each
450 step of the login procedure (i.e. the "Are you sure" prompt, the
451 password prompt, the shell prompt, etc)
452
lmr158604f2010-06-22 01:57:34 +0000453 @return: True on success and False otherwise.
lmr6f669ce2009-05-31 19:02:42 +0000454 """
lmr6f669ce2009-05-31 19:02:42 +0000455 password_prompt_count = 0
lmra4cd1f12010-06-22 02:01:00 +0000456 login_prompt_count = 0
lmr6f669ce2009-05-31 19:02:42 +0000457
lmr6f669ce2009-05-31 19:02:42 +0000458 while True:
lmr158604f2010-06-22 01:57:34 +0000459 (match, text) = session.read_until_last_line_matches(
lmr2c502672010-06-22 02:02:19 +0000460 [r"[Aa]re you sure", r"[Pp]assword:\s*$", r"[Ll]ogin:\s*$",
lmrba69bc52010-03-25 01:50:09 +0000461 r"[Cc]onnection.*closed", r"[Cc]onnection.*refused",
462 r"[Pp]lease wait", prompt],
lmr6f669ce2009-05-31 19:02:42 +0000463 timeout=timeout, internal_timeout=0.5)
464 if match == 0: # "Are you sure you want to continue connecting"
465 logging.debug("Got 'Are you sure...'; sending 'yes'")
lmr158604f2010-06-22 01:57:34 +0000466 session.sendline("yes")
lmr6f669ce2009-05-31 19:02:42 +0000467 continue
468 elif match == 1: # "password:"
469 if password_prompt_count == 0:
470 logging.debug("Got password prompt; sending '%s'" % password)
lmr158604f2010-06-22 01:57:34 +0000471 session.sendline(password)
lmr6f669ce2009-05-31 19:02:42 +0000472 password_prompt_count += 1
473 continue
474 else:
475 logging.debug("Got password prompt again")
lmr158604f2010-06-22 01:57:34 +0000476 return False
lmr6f669ce2009-05-31 19:02:42 +0000477 elif match == 2: # "login:"
lmra4cd1f12010-06-22 02:01:00 +0000478 if login_prompt_count == 0:
479 logging.debug("Got username prompt; sending '%s'" % username)
480 session.sendline(username)
481 login_prompt_count += 1
482 continue
483 else:
484 logging.debug("Got username prompt again")
485 return False
lmr6f669ce2009-05-31 19:02:42 +0000486 elif match == 3: # "Connection closed"
487 logging.debug("Got 'Connection closed'")
lmr158604f2010-06-22 01:57:34 +0000488 return False
lmr3ca79fe2009-06-10 19:24:26 +0000489 elif match == 4: # "Connection refused"
lmr0d2ed1f2009-07-01 03:23:18 +0000490 logging.debug("Got 'Connection refused'")
lmr158604f2010-06-22 01:57:34 +0000491 return False
lmrba69bc52010-03-25 01:50:09 +0000492 elif match == 5: # "Please wait"
493 logging.debug("Got 'Please wait'")
494 timeout = 30
495 continue
496 elif match == 6: # prompt
lmr6f669ce2009-05-31 19:02:42 +0000497 logging.debug("Got shell prompt -- logged in")
lmr158604f2010-06-22 01:57:34 +0000498 return session
lmr6f669ce2009-05-31 19:02:42 +0000499 else: # match == None
lmr3ca79fe2009-06-10 19:24:26 +0000500 logging.debug("Timeout elapsed or process terminated")
lmr158604f2010-06-22 01:57:34 +0000501 return False
502
503
504def _remote_scp(session, password, transfer_timeout=600, login_timeout=10):
505 """
506 Transfer file(s) to a remote host (guest) using SCP. Wait for questions
507 and provide answers. If login_timeout expires while waiting for output
508 from the child (e.g. a password prompt), fail. If transfer_timeout expires
509 while waiting for the transfer to complete, fail.
510
511 @brief: Transfer files using SCP, given a command line.
512
513 @param session: A kvm_expect or kvm_shell_session instance to operate on
514 @param password: The password to send in reply to a password prompt.
515 @param transfer_timeout: The time duration (in seconds) to wait for the
516 transfer to complete.
517 @param login_timeout: The maximal time duration (in seconds) to wait for
518 each step of the login procedure (i.e. the "Are you sure" prompt or
519 the password prompt)
520
521 @return: True if the transfer succeeds and False on failure.
522 """
523 password_prompt_count = 0
524 timeout = login_timeout
525
526 while True:
527 (match, text) = session.read_until_last_line_matches(
528 [r"[Aa]re you sure", r"[Pp]assword:\s*$", r"lost connection"],
529 timeout=timeout, internal_timeout=0.5)
530 if match == 0: # "Are you sure you want to continue connecting"
531 logging.debug("Got 'Are you sure...'; sending 'yes'")
532 session.sendline("yes")
533 continue
534 elif match == 1: # "password:"
535 if password_prompt_count == 0:
536 logging.debug("Got password prompt; sending '%s'" % password)
537 session.sendline(password)
538 password_prompt_count += 1
539 timeout = transfer_timeout
540 continue
541 else:
542 logging.debug("Got password prompt again")
543 return False
544 elif match == 2: # "lost connection"
545 logging.debug("Got 'lost connection'")
546 return False
547 else: # match == None
548 if session.is_alive():
549 logging.debug("Timeout expired")
550 return False
551 else:
552 status = session.get_status()
553 logging.debug("SCP process terminated with status %s", status)
554 return status == 0
555
556
557def remote_login(client, host, port, username, password, prompt, linesep="\n",
lmre56903f2010-06-22 02:09:35 +0000558 log_filename=None, timeout=10):
lmr158604f2010-06-22 01:57:34 +0000559 """
560 Log into a remote host (guest) using SSH/Telnet/Netcat.
561
562 @param client: The client to use ('ssh', 'telnet' or 'nc')
563 @param host: Hostname or IP address
564 @param port: Port to connect to
565 @param username: Username (if required)
566 @param password: Password (if required)
567 @param prompt: Shell prompt (regular expression)
568 @param linesep: The line separator to use when sending lines
569 (e.g. '\\n' or '\\r\\n')
lmre56903f2010-06-22 02:09:35 +0000570 @param log_filename: If specified, log all output to this file
lmr158604f2010-06-22 01:57:34 +0000571 @param timeout: The maximal time duration (in seconds) to wait for
572 each step of the login procedure (i.e. the "Are you sure" prompt
573 or the password prompt)
574
575 @return: kvm_shell_session object on success and None on failure.
576 """
577 if client == "ssh":
578 cmd = ("ssh -o UserKnownHostsFile=/dev/null "
579 "-o PreferredAuthentications=password -p %s %s@%s" %
580 (port, username, host))
581 elif client == "telnet":
582 cmd = "telnet -l %s %s %s" % (username, host, port)
583 elif client == "nc":
584 cmd = "nc %s %s" % (host, port)
585 else:
586 logging.error("Unknown remote shell client: %s" % client)
587 return
lmre56903f2010-06-22 02:09:35 +0000588
lmr158604f2010-06-22 01:57:34 +0000589 logging.debug("Trying to login with command '%s'" % cmd)
590 session = kvm_subprocess.kvm_shell_session(cmd, linesep=linesep,
591 prompt=prompt)
lmra4cd1f12010-06-22 02:01:00 +0000592 if _remote_login(session, username, password, prompt, timeout):
lmre56903f2010-06-22 02:09:35 +0000593 if log_filename:
594 session.set_output_func(log_line)
595 session.set_output_params((log_filename,))
lmr158604f2010-06-22 01:57:34 +0000596 return session
597 else:
598 session.close()
lmr6f669ce2009-05-31 19:02:42 +0000599
600
lmre56903f2010-06-22 02:09:35 +0000601def remote_scp(command, password, log_filename=None, transfer_timeout=600,
602 login_timeout=10):
lmr6f669ce2009-05-31 19:02:42 +0000603 """
lmr158604f2010-06-22 01:57:34 +0000604 Transfer file(s) to a remote host (guest) using SCP.
lmr6f669ce2009-05-31 19:02:42 +0000605
606 @brief: Transfer files using SCP, given a command line.
607
608 @param command: The command to execute
609 (e.g. "scp -r foobar root@localhost:/tmp/").
610 @param password: The password to send in reply to a password prompt.
lmre56903f2010-06-22 02:09:35 +0000611 @param log_filename: If specified, log all output to this file
lmrc5f42a32010-06-18 14:15:19 +0000612 @param transfer_timeout: The time duration (in seconds) to wait for the
lmr158604f2010-06-22 01:57:34 +0000613 transfer to complete.
lmr6f669ce2009-05-31 19:02:42 +0000614 @param login_timeout: The maximal time duration (in seconds) to wait for
lmr158604f2010-06-22 01:57:34 +0000615 each step of the login procedure (i.e. the "Are you sure" prompt
616 or the password prompt)
lmr6f669ce2009-05-31 19:02:42 +0000617
618 @return: True if the transfer succeeds and False on failure.
619 """
lmr158604f2010-06-22 01:57:34 +0000620 logging.debug("Trying to SCP with command '%s', timeout %ss",
621 command, transfer_timeout)
lmre56903f2010-06-22 02:09:35 +0000622
623 if log_filename:
624 output_func = log_line
625 output_params = (log_filename,)
626 else:
627 output_func = None
628 output_params = ()
629
630 session = kvm_subprocess.kvm_expect(command,
631 output_func=output_func,
632 output_params=output_params)
lmr158604f2010-06-22 01:57:34 +0000633 try:
634 return _remote_scp(session, password, transfer_timeout, login_timeout)
635 finally:
636 session.close()
lmr6f669ce2009-05-31 19:02:42 +0000637
638
639def scp_to_remote(host, port, username, password, local_path, remote_path,
lmre56903f2010-06-22 02:09:35 +0000640 log_filename=None, timeout=600):
lmr6f669ce2009-05-31 19:02:42 +0000641 """
642 Copy files to a remote host (guest).
643
lmr912c74b2009-08-17 20:43:27 +0000644 @param host: Hostname or IP address
645 @param username: Username (if required)
646 @param password: Password (if required)
lmr6f669ce2009-05-31 19:02:42 +0000647 @param local_path: Path on the local machine where we are copying from
648 @param remote_path: Path on the remote machine where we are copying to
lmre56903f2010-06-22 02:09:35 +0000649 @param log_filename: If specified, log all output to this file
lmr158604f2010-06-22 01:57:34 +0000650 @param timeout: The time duration (in seconds) to wait for the transfer
651 to complete.
lmr6f669ce2009-05-31 19:02:42 +0000652
653 @return: True on success and False on failure.
654 """
lmrc196d492010-05-07 14:14:26 +0000655 command = ("scp -v -o UserKnownHostsFile=/dev/null "
lmr1b7e1702009-11-10 16:30:39 +0000656 "-o PreferredAuthentications=password -r -P %s %s %s@%s:%s" %
lmrd16a67d2009-06-10 19:52:59 +0000657 (port, local_path, username, host, remote_path))
lmre56903f2010-06-22 02:09:35 +0000658 return remote_scp(command, password, log_filename, timeout)
lmr6f669ce2009-05-31 19:02:42 +0000659
660
661def scp_from_remote(host, port, username, password, remote_path, local_path,
lmre56903f2010-06-22 02:09:35 +0000662 log_filename=None, timeout=600):
lmr6f669ce2009-05-31 19:02:42 +0000663 """
664 Copy files from a remote host (guest).
665
lmr912c74b2009-08-17 20:43:27 +0000666 @param host: Hostname or IP address
667 @param username: Username (if required)
668 @param password: Password (if required)
lmr6f669ce2009-05-31 19:02:42 +0000669 @param local_path: Path on the local machine where we are copying from
670 @param remote_path: Path on the remote machine where we are copying to
lmre56903f2010-06-22 02:09:35 +0000671 @param log_filename: If specified, log all output to this file
lmr158604f2010-06-22 01:57:34 +0000672 @param timeout: The time duration (in seconds) to wait for the transfer
673 to complete.
lmr6f669ce2009-05-31 19:02:42 +0000674
675 @return: True on success and False on failure.
676 """
lmrc196d492010-05-07 14:14:26 +0000677 command = ("scp -v -o UserKnownHostsFile=/dev/null "
lmr1b7e1702009-11-10 16:30:39 +0000678 "-o PreferredAuthentications=password -r -P %s %s@%s:%s %s" %
lmrd16a67d2009-06-10 19:52:59 +0000679 (port, username, host, remote_path, local_path))
lmre56903f2010-06-22 02:09:35 +0000680 return remote_scp(command, password, log_filename, timeout)
lmr6f669ce2009-05-31 19:02:42 +0000681
682
lmr6f669ce2009-05-31 19:02:42 +0000683# The following are utility functions related to ports.
684
Eric Lie0493a42010-11-15 13:05:43 -0800685def is_port_free(port, address):
lmr6f669ce2009-05-31 19:02:42 +0000686 """
687 Return True if the given port is available for use.
688
689 @param port: Port number
690 """
691 try:
692 s = socket.socket()
693 #s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
Eric Lie0493a42010-11-15 13:05:43 -0800694 if address == "localhost":
695 s.bind(("localhost", port))
696 free = True
697 else:
698 s.connect((address, port))
699 free = False
lmr6f669ce2009-05-31 19:02:42 +0000700 except socket.error:
Eric Lie0493a42010-11-15 13:05:43 -0800701 if address == "localhost":
702 free = False
703 else:
704 free = True
lmr6f669ce2009-05-31 19:02:42 +0000705 s.close()
706 return free
707
708
Eric Lie0493a42010-11-15 13:05:43 -0800709def find_free_port(start_port, end_port, address="localhost"):
lmr6f669ce2009-05-31 19:02:42 +0000710 """
lmra29a5cb2010-03-18 02:39:34 +0000711 Return a host free port in the range [start_port, end_port].
lmr6f669ce2009-05-31 19:02:42 +0000712
713 @param start_port: First port that will be checked.
714 @param end_port: Port immediately after the last one that will be checked.
715 """
716 for i in range(start_port, end_port):
Eric Lie0493a42010-11-15 13:05:43 -0800717 if is_port_free(i, address):
lmr6f669ce2009-05-31 19:02:42 +0000718 return i
719 return None
720
721
Eric Lie0493a42010-11-15 13:05:43 -0800722def find_free_ports(start_port, end_port, count, address="localhost"):
lmr6f669ce2009-05-31 19:02:42 +0000723 """
lmra29a5cb2010-03-18 02:39:34 +0000724 Return count of host free ports in the range [start_port, end_port].
lmr6f669ce2009-05-31 19:02:42 +0000725
726 @count: Initial number of ports known to be free in the range.
727 @param start_port: First port that will be checked.
728 @param end_port: Port immediately after the last one that will be checked.
729 """
730 ports = []
731 i = start_port
732 while i < end_port and count > 0:
Eric Lie0493a42010-11-15 13:05:43 -0800733 if is_port_free(i, address):
lmr6f669ce2009-05-31 19:02:42 +0000734 ports.append(i)
735 count -= 1
736 i += 1
737 return ports
738
739
lmr23894542010-06-22 01:54:37 +0000740# An easy way to log lines to files when the logging system can't be used
741
742_open_log_files = {}
743_log_file_dir = "/tmp"
744
745
746def log_line(filename, line):
747 """
748 Write a line to a file. '\n' is appended to the line.
749
750 @param filename: Path of file to write to, either absolute or relative to
751 the dir set by set_log_file_dir().
752 @param line: Line to write.
753 """
754 global _open_log_files, _log_file_dir
755 if filename not in _open_log_files:
756 path = get_path(_log_file_dir, filename)
757 try:
758 os.makedirs(os.path.dirname(path))
759 except OSError:
760 pass
761 _open_log_files[filename] = open(path, "w")
762 timestr = time.strftime("%Y-%m-%d %H:%M:%S")
763 _open_log_files[filename].write("%s: %s\n" % (timestr, line))
764 _open_log_files[filename].flush()
765
766
767def set_log_file_dir(dir):
768 """
769 Set the base directory for log files created by log_line().
770
771 @param dir: Directory for log files.
772 """
773 global _log_file_dir
774 _log_file_dir = dir
775
776
lmr6f669ce2009-05-31 19:02:42 +0000777# The following are miscellaneous utility functions.
778
lmrb4954e02009-08-17 20:44:42 +0000779def get_path(base_path, user_path):
780 """
781 Translate a user specified path to a real path.
782 If user_path is relative, append it to base_path.
783 If user_path is absolute, return it as is.
784
785 @param base_path: The base path of relative user specified paths.
786 @param user_path: The user specified path.
787 """
788 if os.path.isabs(user_path):
789 return user_path
790 else:
791 return os.path.join(base_path, user_path)
792
793
lmr6f669ce2009-05-31 19:02:42 +0000794def generate_random_string(length):
795 """
796 Return a random string using alphanumeric characters.
797
798 @length: length of the string that will be generated.
799 """
lmr45fc0c22009-09-15 05:16:45 +0000800 r = random.SystemRandom()
lmr6f669ce2009-05-31 19:02:42 +0000801 str = ""
802 chars = string.letters + string.digits
803 while length > 0:
lmr45fc0c22009-09-15 05:16:45 +0000804 str += r.choice(chars)
lmr6f669ce2009-05-31 19:02:42 +0000805 length -= 1
806 return str
807
lmr42b16662010-06-22 11:55:23 +0000808def generate_random_id():
809 """
810 Return a random string suitable for use as a qemu id.
811 """
812 return "id" + generate_random_string(6)
813
lmr6f669ce2009-05-31 19:02:42 +0000814
lmr0bee2342010-02-24 00:01:37 +0000815def generate_tmp_file_name(file, ext=None, dir='/tmp/'):
816 """
817 Returns a temporary file name. The file is not created.
818 """
819 while True:
820 file_name = (file + '-' + time.strftime("%Y%m%d-%H%M%S-") +
821 generate_random_string(4))
822 if ext:
823 file_name += '.' + ext
824 file_name = os.path.join(dir, file_name)
825 if not os.path.exists(file_name):
826 break
827
828 return file_name
829
830
lmr6f669ce2009-05-31 19:02:42 +0000831def format_str_for_message(str):
832 """
833 Format str so that it can be appended to a message.
834 If str consists of one line, prefix it with a space.
835 If str consists of multiple lines, prefix it with a newline.
836
837 @param str: string that will be formatted.
838 """
lmr57355592009-08-07 21:55:49 +0000839 lines = str.splitlines()
840 num_lines = len(lines)
841 str = "\n".join(lines)
lmr6f669ce2009-05-31 19:02:42 +0000842 if num_lines == 0:
843 return ""
844 elif num_lines == 1:
845 return " " + str
846 else:
847 return "\n" + str
848
849
850def wait_for(func, timeout, first=0.0, step=1.0, text=None):
851 """
852 If func() evaluates to True before timeout expires, return the
853 value of func(). Otherwise return None.
854
855 @brief: Wait until func() evaluates to True.
856
857 @param timeout: Timeout in seconds
858 @param first: Time to sleep before first attempt
859 @param steps: Time to sleep between attempts in seconds
860 @param text: Text to print while waiting, for debug purposes
861 """
862 start_time = time.time()
863 end_time = time.time() + timeout
864
865 time.sleep(first)
866
867 while time.time() < end_time:
868 if text:
869 logging.debug("%s (%f secs)" % (text, time.time() - start_time))
870
871 output = func()
872 if output:
873 return output
874
875 time.sleep(step)
876
877 logging.debug("Timeout elapsed")
878 return None
879
880
lmr03ba22e2009-10-23 12:07:44 +0000881def get_hash_from_file(hash_path, dvd_basename):
882 """
883 Get the a hash from a given DVD image from a hash file
884 (Hash files are usually named MD5SUM or SHA1SUM and are located inside the
885 download directories of the DVDs)
886
887 @param hash_path: Local path to a hash file.
888 @param cd_image: Basename of a CD image
889 """
890 hash_file = open(hash_path, 'r')
891 for line in hash_file.readlines():
892 if dvd_basename in line:
893 return line.split()[0]
894
895
lmr43beef12009-12-11 21:03:36 +0000896def run_tests(test_list, job):
897 """
898 Runs the sequence of KVM tests based on the list of dictionaries
899 generated by the configuration system, handling dependencies.
900
901 @param test_list: List with all dictionary test parameters.
902 @param job: Autotest job object.
903
904 @return: True, if all tests ran passed, False if any of them failed.
905 """
906 status_dict = {}
lmr43beef12009-12-11 21:03:36 +0000907 failed = False
lmr5df6eb42010-03-23 15:34:16 +0000908
lmr43beef12009-12-11 21:03:36 +0000909 for dict in test_list:
910 if dict.get("skip") == "yes":
911 continue
912 dependencies_satisfied = True
913 for dep in dict.get("depend"):
914 for test_name in status_dict.keys():
915 if not dep in test_name:
916 continue
917 if not status_dict[test_name]:
918 dependencies_satisfied = False
919 break
920 if dependencies_satisfied:
921 test_iterations = int(dict.get("iterations", 1))
lmr7da99b22010-01-12 22:30:28 +0000922 test_tag = dict.get("shortname")
lmrd960a722010-04-01 02:32:59 +0000923 # Setting up profilers during test execution.
924 profilers = dict.get("profilers", "").split()
925 for profiler in profilers:
926 job.profilers.add(profiler)
lmr7da99b22010-01-12 22:30:28 +0000927
lmr7da99b22010-01-12 22:30:28 +0000928 # We need only one execution, profiled, hence we're passing
929 # the profile_only parameter to job.run_test().
930 current_status = job.run_test("kvm", params=dict, tag=test_tag,
931 iterations=test_iterations,
lmrd960a722010-04-01 02:32:59 +0000932 profile_only= bool(profilers) or None)
933
934 for profiler in profilers:
935 job.profilers.delete(profiler)
lmr7da99b22010-01-12 22:30:28 +0000936
lmr43beef12009-12-11 21:03:36 +0000937 if not current_status:
938 failed = True
939 else:
940 current_status = False
941 status_dict[dict.get("name")] = current_status
942
943 return not failed
944
945
946def create_report(report_dir, results_dir):
947 """
948 Creates a neatly arranged HTML results report in the results dir.
949
950 @param report_dir: Directory where the report script is located.
951 @param results_dir: Directory where the results will be output.
952 """
953 reporter = os.path.join(report_dir, 'html_report.py')
954 html_file = os.path.join(results_dir, 'results.html')
955 os.system('%s -r %s -f %s -R' % (reporter, results_dir, html_file))
lmr31af3a12010-01-18 16:46:52 +0000956
957
958def get_full_pci_id(pci_id):
959 """
960 Get full PCI ID of pci_id.
961
962 @param pci_id: PCI ID of a device.
963 """
964 cmd = "lspci -D | awk '/%s/ {print $1}'" % pci_id
965 status, full_id = commands.getstatusoutput(cmd)
966 if status != 0:
967 return None
968 return full_id
969
970
971def get_vendor_from_pci_id(pci_id):
972 """
973 Check out the device vendor ID according to pci_id.
974
975 @param pci_id: PCI ID of a device.
976 """
977 cmd = "lspci -n | awk '/%s/ {print $3}'" % pci_id
978 return re.sub(":", " ", commands.getoutput(cmd))
979
980
lmr84154412010-02-03 18:34:43 +0000981class KvmLoggingConfig(logging_config.LoggingConfig):
982 """
983 Used with the sole purpose of providing convenient logging setup
984 for the KVM test auxiliary programs.
985 """
986 def configure_logging(self, results_dir=None, verbose=False):
987 super(KvmLoggingConfig, self).configure_logging(use_console=True,
988 verbose=verbose)
989
990
lmr31af3a12010-01-18 16:46:52 +0000991class PciAssignable(object):
992 """
993 Request PCI assignable devices on host. It will check whether to request
994 PF (physical Functions) or VF (Virtual Functions).
995 """
lmr83d3b1a2010-01-19 08:14:36 +0000996 def __init__(self, type="vf", driver=None, driver_option=None,
lmr31af3a12010-01-18 16:46:52 +0000997 names=None, devices_requested=None):
998 """
999 Initialize parameter 'type' which could be:
lmr83d3b1a2010-01-19 08:14:36 +00001000 vf: Virtual Functions
1001 pf: Physical Function (actual hardware)
lmr31af3a12010-01-18 16:46:52 +00001002 mixed: Both includes VFs and PFs
1003
1004 If pass through Physical NIC cards, we need to specify which devices
1005 to be assigned, e.g. 'eth1 eth2'.
1006
1007 If pass through Virtual Functions, we need to specify how many vfs
1008 are going to be assigned, e.g. passthrough_count = 8 and max_vfs in
1009 config file.
1010
1011 @param type: PCI device type.
1012 @param driver: Kernel module for the PCI assignable device.
1013 @param driver_option: Module option to specify the maximum number of
1014 VFs (eg 'max_vfs=7')
1015 @param names: Physical NIC cards correspondent network interfaces,
1016 e.g.'eth1 eth2 ...'
1017 @param devices_requested: Number of devices being requested.
1018 """
1019 self.type = type
1020 self.driver = driver
1021 self.driver_option = driver_option
1022 if names:
1023 self.name_list = names.split()
1024 if devices_requested:
1025 self.devices_requested = int(devices_requested)
1026 else:
1027 self.devices_requested = None
1028
1029
1030 def _get_pf_pci_id(self, name, search_str):
1031 """
1032 Get the PF PCI ID according to name.
1033
1034 @param name: Name of the PCI device.
1035 @param search_str: Search string to be used on lspci.
1036 """
1037 cmd = "ethtool -i %s | awk '/bus-info/ {print $2}'" % name
1038 s, pci_id = commands.getstatusoutput(cmd)
1039 if not (s or "Cannot get driver information" in pci_id):
1040 return pci_id[5:]
1041 cmd = "lspci | awk '/%s/ {print $1}'" % search_str
1042 pci_ids = [id for id in commands.getoutput(cmd).splitlines()]
1043 nic_id = int(re.search('[0-9]+', name).group(0))
1044 if (len(pci_ids) - 1) < nic_id:
1045 return None
1046 return pci_ids[nic_id]
1047
1048
1049 def _release_dev(self, pci_id):
1050 """
1051 Release a single PCI device.
1052
1053 @param pci_id: PCI ID of a given PCI device.
1054 """
1055 base_dir = "/sys/bus/pci"
1056 full_id = get_full_pci_id(pci_id)
1057 vendor_id = get_vendor_from_pci_id(pci_id)
1058 drv_path = os.path.join(base_dir, "devices/%s/driver" % full_id)
1059 if 'pci-stub' in os.readlink(drv_path):
1060 cmd = "echo '%s' > %s/new_id" % (vendor_id, drv_path)
1061 if os.system(cmd):
1062 return False
1063
1064 stub_path = os.path.join(base_dir, "drivers/pci-stub")
1065 cmd = "echo '%s' > %s/unbind" % (full_id, stub_path)
1066 if os.system(cmd):
1067 return False
1068
1069 driver = self.dev_drivers[pci_id]
1070 cmd = "echo '%s' > %s/bind" % (full_id, driver)
1071 if os.system(cmd):
1072 return False
1073
1074 return True
1075
1076
1077 def get_vf_devs(self):
1078 """
1079 Catch all VFs PCI IDs.
1080
1081 @return: List with all PCI IDs for the Virtual Functions avaliable
1082 """
1083 if not self.sr_iov_setup():
1084 return []
1085
1086 cmd = "lspci | awk '/Virtual Function/ {print $1}'"
1087 return commands.getoutput(cmd).split()
1088
1089
1090 def get_pf_devs(self):
1091 """
1092 Catch all PFs PCI IDs.
1093
1094 @return: List with all PCI IDs for the physical hardware requested
1095 """
1096 pf_ids = []
1097 for name in self.name_list:
1098 pf_id = self._get_pf_pci_id(name, "Ethernet")
1099 if not pf_id:
1100 continue
1101 pf_ids.append(pf_id)
1102 return pf_ids
1103
1104
1105 def get_devs(self, count):
1106 """
1107 Check out all devices' PCI IDs according to their name.
1108
1109 @param count: count number of PCI devices needed for pass through
1110 @return: a list of all devices' PCI IDs
1111 """
lmr83d3b1a2010-01-19 08:14:36 +00001112 if self.type == "vf":
lmr31af3a12010-01-18 16:46:52 +00001113 vf_ids = self.get_vf_devs()
lmr83d3b1a2010-01-19 08:14:36 +00001114 elif self.type == "pf":
lmr31af3a12010-01-18 16:46:52 +00001115 vf_ids = self.get_pf_devs()
1116 elif self.type == "mixed":
1117 vf_ids = self.get_vf_devs()
1118 vf_ids.extend(self.get_pf_devs())
1119 return vf_ids[0:count]
1120
1121
1122 def get_vfs_count(self):
1123 """
1124 Get VFs count number according to lspci.
1125 """
lmr224a5412010-03-17 12:00:36 +00001126 # FIXME: Need to think out a method of identify which
1127 # 'virtual function' belongs to which physical card considering
1128 # that if the host has more than one 82576 card. PCI_ID?
lmr31af3a12010-01-18 16:46:52 +00001129 cmd = "lspci | grep 'Virtual Function' | wc -l"
lmr224a5412010-03-17 12:00:36 +00001130 return int(commands.getoutput(cmd))
lmr31af3a12010-01-18 16:46:52 +00001131
1132
1133 def check_vfs_count(self):
1134 """
1135 Check VFs count number according to the parameter driver_options.
1136 """
lmr224a5412010-03-17 12:00:36 +00001137 # Network card 82576 has two network interfaces and each can be
1138 # virtualized up to 7 virtual functions, therefore we multiply
1139 # two for the value of driver_option 'max_vfs'.
1140 expected_count = int((re.findall("(\d)", self.driver_option)[0])) * 2
1141 return (self.get_vfs_count == expected_count)
lmr31af3a12010-01-18 16:46:52 +00001142
1143
1144 def is_binded_to_stub(self, full_id):
1145 """
1146 Verify whether the device with full_id is already binded to pci-stub.
1147
1148 @param full_id: Full ID for the given PCI device
1149 """
1150 base_dir = "/sys/bus/pci"
1151 stub_path = os.path.join(base_dir, "drivers/pci-stub")
1152 if os.path.exists(os.path.join(stub_path, full_id)):
1153 return True
1154 return False
1155
1156
1157 def sr_iov_setup(self):
1158 """
1159 Ensure the PCI device is working in sr_iov mode.
1160
1161 Check if the PCI hardware device drive is loaded with the appropriate,
1162 parameters (number of VFs), and if it's not, perform setup.
1163
1164 @return: True, if the setup was completed successfuly, False otherwise.
1165 """
1166 re_probe = False
1167 s, o = commands.getstatusoutput('lsmod | grep %s' % self.driver)
1168 if s:
1169 re_probe = True
1170 elif not self.check_vfs_count():
1171 os.system("modprobe -r %s" % self.driver)
1172 re_probe = True
lmr224a5412010-03-17 12:00:36 +00001173 else:
1174 return True
lmr31af3a12010-01-18 16:46:52 +00001175
1176 # Re-probe driver with proper number of VFs
1177 if re_probe:
1178 cmd = "modprobe %s %s" % (self.driver, self.driver_option)
lmr224a5412010-03-17 12:00:36 +00001179 logging.info("Loading the driver '%s' with option '%s'" %
1180 (self.driver, self.driver_option))
lmr31af3a12010-01-18 16:46:52 +00001181 s, o = commands.getstatusoutput(cmd)
1182 if s:
1183 return False
lmr31af3a12010-01-18 16:46:52 +00001184 return True
1185
1186
1187 def request_devs(self):
1188 """
1189 Implement setup process: unbind the PCI device and then bind it
1190 to the pci-stub driver.
1191
1192 @return: a list of successfully requested devices' PCI IDs.
1193 """
1194 base_dir = "/sys/bus/pci"
1195 stub_path = os.path.join(base_dir, "drivers/pci-stub")
1196
1197 self.pci_ids = self.get_devs(self.devices_requested)
lmrbc72f082010-02-08 10:02:40 +00001198 logging.debug("The following pci_ids were found: %s", self.pci_ids)
lmr31af3a12010-01-18 16:46:52 +00001199 requested_pci_ids = []
1200 self.dev_drivers = {}
1201
1202 # Setup all devices specified for assignment to guest
1203 for pci_id in self.pci_ids:
1204 full_id = get_full_pci_id(pci_id)
1205 if not full_id:
1206 continue
1207 drv_path = os.path.join(base_dir, "devices/%s/driver" % full_id)
1208 dev_prev_driver= os.path.realpath(os.path.join(drv_path,
1209 os.readlink(drv_path)))
1210 self.dev_drivers[pci_id] = dev_prev_driver
1211
1212 # Judge whether the device driver has been binded to stub
1213 if not self.is_binded_to_stub(full_id):
lmrbc72f082010-02-08 10:02:40 +00001214 logging.debug("Binding device %s to stub", full_id)
lmr31af3a12010-01-18 16:46:52 +00001215 vendor_id = get_vendor_from_pci_id(pci_id)
1216 stub_new_id = os.path.join(stub_path, 'new_id')
1217 unbind_dev = os.path.join(drv_path, 'unbind')
1218 stub_bind = os.path.join(stub_path, 'bind')
1219
1220 info_write_to_files = [(vendor_id, stub_new_id),
1221 (full_id, unbind_dev),
1222 (full_id, stub_bind)]
1223
1224 for content, file in info_write_to_files:
1225 try:
lmrbc72f082010-02-08 10:02:40 +00001226 utils.open_write_close(file, content)
lmr31af3a12010-01-18 16:46:52 +00001227 except IOError:
lmrbc72f082010-02-08 10:02:40 +00001228 logging.debug("Failed to write %s to file %s", content,
1229 file)
lmr31af3a12010-01-18 16:46:52 +00001230 continue
1231
1232 if not self.is_binded_to_stub(full_id):
lmrbc72f082010-02-08 10:02:40 +00001233 logging.error("Binding device %s to stub failed", pci_id)
lmr31af3a12010-01-18 16:46:52 +00001234 continue
1235 else:
lmrbc72f082010-02-08 10:02:40 +00001236 logging.debug("Device %s already binded to stub", pci_id)
lmr31af3a12010-01-18 16:46:52 +00001237 requested_pci_ids.append(pci_id)
1238 self.pci_ids = requested_pci_ids
1239 return self.pci_ids
1240
1241
1242 def release_devs(self):
1243 """
1244 Release all PCI devices currently assigned to VMs back to the
1245 virtualization host.
1246 """
1247 try:
1248 for pci_id in self.dev_drivers:
1249 if not self._release_dev(pci_id):
lmrbc72f082010-02-08 10:02:40 +00001250 logging.error("Failed to release device %s to host", pci_id)
lmr31af3a12010-01-18 16:46:52 +00001251 else:
lmrbc72f082010-02-08 10:02:40 +00001252 logging.info("Released device %s successfully", pci_id)
lmr31af3a12010-01-18 16:46:52 +00001253 except:
1254 return
Eric Lie0493a42010-11-15 13:05:43 -08001255
1256
1257class KojiDownloader(object):
1258 """
1259 Stablish a connection with the build system, either koji or brew.
1260
1261 This class provides a convenience methods to retrieve packages hosted on
1262 the build system.
1263 """
1264 def __init__(self, cmd):
1265 """
1266 Verifies whether the system has koji or brew installed, then loads
1267 the configuration file that will be used to download the files.
1268
1269 @param cmd: Command name, either 'brew' or 'koji'. It is important
1270 to figure out the appropriate configuration used by the
1271 downloader.
1272 @param dst_dir: Destination dir for the packages.
1273 """
1274 if not KOJI_INSTALLED:
1275 raise ValueError('No koji/brew installed on the machine')
1276
1277 if os.path.isfile(cmd):
1278 koji_cmd = cmd
1279 else:
1280 koji_cmd = os_dep.command(cmd)
1281
1282 logging.debug("Found %s as the buildsystem interface", koji_cmd)
1283
1284 config_map = {'/usr/bin/koji': '/etc/koji.conf',
1285 '/usr/bin/brew': '/etc/brewkoji.conf'}
1286
1287 try:
1288 config_file = config_map[koji_cmd]
1289 except IndexError:
1290 raise ValueError('Could not find config file for %s' % koji_cmd)
1291
1292 base_name = os.path.basename(koji_cmd)
1293 if os.access(config_file, os.F_OK):
1294 f = open(config_file)
1295 config = ConfigParser.ConfigParser()
1296 config.readfp(f)
1297 f.close()
1298 else:
1299 raise IOError('Configuration file %s missing or with wrong '
1300 'permissions' % config_file)
1301
1302 if config.has_section(base_name):
1303 self.koji_options = {}
1304 session_options = {}
1305 server = None
1306 for name, value in config.items(base_name):
1307 if name in ('user', 'password', 'debug_xmlrpc', 'debug'):
1308 session_options[name] = value
1309 self.koji_options[name] = value
1310 self.session = koji.ClientSession(self.koji_options['server'],
1311 session_options)
1312 else:
1313 raise ValueError('Koji config file %s does not have a %s '
1314 'session' % (config_file, base_name))
1315
1316
1317 def get(self, src_package, dst_dir, rfilter=None, tag=None, build=None,
1318 arch=None):
1319 """
1320 Download a list of packages from the build system.
1321
1322 This will download all packages originated from source package [package]
1323 with given [tag] or [build] for the architecture reported by the
1324 machine.
1325
1326 @param src_package: Source package name.
1327 @param dst_dir: Destination directory for the downloaded packages.
1328 @param rfilter: Regexp filter, only download the packages that match
1329 that particular filter.
1330 @param tag: Build system tag.
1331 @param build: Build system ID.
1332 @param arch: Package arch. Useful when you want to download noarch
1333 packages.
1334
1335 @return: List of paths with the downloaded rpm packages.
1336 """
1337 if build and build.isdigit():
1338 build = int(build)
1339
1340 if tag and build:
1341 logging.info("Both tag and build parameters provided, ignoring tag "
1342 "parameter...")
1343
1344 if not tag and not build:
1345 raise ValueError("Koji install selected but neither koji_tag "
1346 "nor koji_build parameters provided. Please "
1347 "provide an appropriate tag or build name.")
1348
1349 if not build:
1350 builds = self.session.listTagged(tag, latest=True,
1351 package=src_package)
1352 if not builds:
1353 raise ValueError("Tag %s has no builds of %s" % (tag,
1354 src_package))
1355 info = builds[0]
1356 else:
1357 info = self.session.getBuild(build)
1358
1359 if info is None:
1360 raise ValueError('No such brew/koji build: %s' % build)
1361
1362 if arch is None:
1363 arch = utils.get_arch()
1364
1365 rpms = self.session.listRPMs(buildID=info['id'],
1366 arches=arch)
1367 if not rpms:
1368 raise ValueError("No %s packages available for %s" %
1369 arch, koji.buildLabel(info))
1370
1371 rpm_paths = []
1372 for rpm in rpms:
1373 rpm_name = koji.pathinfo.rpm(rpm)
1374 url = ("%s/%s/%s/%s/%s" % (self.koji_options['pkgurl'],
1375 info['package_name'],
1376 info['version'], info['release'],
1377 rpm_name))
1378 if rfilter:
1379 filter_regexp = re.compile(rfilter, re.IGNORECASE)
1380 if filter_regexp.match(os.path.basename(rpm_name)):
1381 download = True
1382 else:
1383 download = False
1384 else:
1385 download = True
1386
1387 if download:
1388 r = utils.get_file(url,
1389 os.path.join(dst_dir, os.path.basename(url)))
1390 rpm_paths.append(r)
1391
1392 return rpm_paths