blob: fb2d1c2a1019052bcae9f5bb04b6a769741e2a69 [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
lmrb635b862009-09-10 14:53:21 +00008from autotest_lib.client.bin import utils
lmr84154412010-02-03 18:34:43 +00009from autotest_lib.client.common_lib import error, logging_config
lmrb635b862009-09-10 14:53:21 +000010import kvm_subprocess
11
lmr6f669ce2009-05-31 19:02:42 +000012
lmre8a66dd2009-09-15 19:51:07 +000013def dump_env(obj, filename):
14 """
15 Dump KVM test environment to a file.
16
17 @param filename: Path to a file where the environment will be dumped to.
18 """
19 file = open(filename, "w")
20 cPickle.dump(obj, file)
21 file.close()
22
23
lmr287363c2010-07-08 23:52:03 +000024def load_env(filename, version):
lmre8a66dd2009-09-15 19:51:07 +000025 """
lmr287363c2010-07-08 23:52:03 +000026 Load KVM test environment from an env file.
27 If the version recorded in the file is lower than version, return an empty
28 env. If some other error occurs during unpickling, return an empty env.
lmre8a66dd2009-09-15 19:51:07 +000029
lmr287363c2010-07-08 23:52:03 +000030 @param filename: Path to an env file.
lmre8a66dd2009-09-15 19:51:07 +000031 """
lmr287363c2010-07-08 23:52:03 +000032 default = {"version": version}
lmre8a66dd2009-09-15 19:51:07 +000033 try:
34 file = open(filename, "r")
lmr287363c2010-07-08 23:52:03 +000035 env = cPickle.load(file)
lmr8b5400c2010-03-23 15:32:29 +000036 file.close()
lmr287363c2010-07-08 23:52:03 +000037 if env.get("version", 0) < version:
38 logging.warn("Incompatible env file found. Not using it.")
39 return default
40 return env
lmr8b5400c2010-03-23 15:32:29 +000041 # Almost any exception can be raised during unpickling, so let's catch
42 # them all
lmr258f7932010-06-14 15:37:02 +000043 except Exception, e:
44 logging.warn(e)
lmre8a66dd2009-09-15 19:51:07 +000045 return default
lmre8a66dd2009-09-15 19:51:07 +000046
47
lmr6f669ce2009-05-31 19:02:42 +000048def get_sub_dict(dict, name):
49 """
50 Return a "sub-dict" corresponding to a specific object.
51
52 Operate on a copy of dict: for each key that ends with the suffix
53 "_" + name, strip the suffix from the key, and set the value of
54 the stripped key to that of the key. Return the resulting dict.
55
56 @param name: Suffix of the key we want to set the value.
57 """
58 suffix = "_" + name
59 new_dict = dict.copy()
60 for key in dict.keys():
61 if key.endswith(suffix):
62 new_key = key.split(suffix)[0]
63 new_dict[new_key] = dict[key]
64 return new_dict
65
66
67def get_sub_dict_names(dict, keyword):
68 """
69 Return a list of "sub-dict" names that may be extracted with get_sub_dict.
70
71 This function may be modified to change the behavior of all functions that
72 deal with multiple objects defined in dicts (e.g. VMs, images, NICs).
73
74 @param keyword: A key in dict (e.g. "vms", "images", "nics").
75 """
76 names = dict.get(keyword)
77 if names:
78 return names.split()
79 else:
80 return []
81
82
lmrac5089b2009-08-13 04:05:47 +000083# Functions related to MAC/IP addresses
84
85def mac_str_to_int(addr):
86 """
87 Convert MAC address string to integer.
88
89 @param addr: String representing the MAC address.
90 """
91 return sum(int(s, 16) * 256 ** i
92 for i, s in enumerate(reversed(addr.split(":"))))
93
94
95def mac_int_to_str(addr):
96 """
97 Convert MAC address integer to string.
98
99 @param addr: Integer representing the MAC address.
100 """
101 return ":".join("%02x" % (addr >> 8 * i & 0xFF)
102 for i in reversed(range(6)))
103
104
105def ip_str_to_int(addr):
106 """
107 Convert IP address string to integer.
108
109 @param addr: String representing the IP address.
110 """
111 return sum(int(s) * 256 ** i
112 for i, s in enumerate(reversed(addr.split("."))))
113
114
115def ip_int_to_str(addr):
116 """
117 Convert IP address integer to string.
118
119 @param addr: Integer representing the IP address.
120 """
121 return ".".join(str(addr >> 8 * i & 0xFF)
122 for i in reversed(range(4)))
123
124
125def offset_mac(base, offset):
126 """
127 Add offset to a given MAC address.
128
129 @param base: String representing a MAC address.
130 @param offset: Offset to add to base (integer)
131 @return: A string representing the offset MAC address.
132 """
133 return mac_int_to_str(mac_str_to_int(base) + offset)
134
135
136def offset_ip(base, offset):
137 """
138 Add offset to a given IP address.
139
140 @param base: String representing an IP address.
141 @param offset: Offset to add to base (integer)
142 @return: A string representing the offset IP address.
143 """
144 return ip_int_to_str(ip_str_to_int(base) + offset)
145
146
147def get_mac_ip_pair_from_dict(dict):
148 """
149 Fetch a MAC-IP address pair from dict and return it.
150
151 The parameters in dict are expected to conform to a certain syntax.
152 Typical usage may be:
153
154 address_ranges = r1 r2 r3
155
156 address_range_base_mac_r1 = 55:44:33:22:11:00
157 address_range_base_ip_r1 = 10.0.0.0
158 address_range_size_r1 = 16
159
160 address_range_base_mac_r2 = 55:44:33:22:11:40
161 address_range_base_ip_r2 = 10.0.0.60
162 address_range_size_r2 = 25
163
164 address_range_base_mac_r3 = 55:44:33:22:12:10
165 address_range_base_ip_r3 = 10.0.1.20
166 address_range_size_r3 = 230
167
168 address_index = 0
169
170 All parameters except address_index specify a MAC-IP address pool. The
171 pool consists of several MAC-IP address ranges.
172 address_index specified the index of the desired MAC-IP pair from the pool.
173
174 @param dict: The dictionary from which to fetch the addresses.
175 """
176 index = int(dict.get("address_index", 0))
177 for mac_range_name in get_sub_dict_names(dict, "address_ranges"):
178 mac_range_params = get_sub_dict(dict, mac_range_name)
179 mac_base = mac_range_params.get("address_range_base_mac")
180 ip_base = mac_range_params.get("address_range_base_ip")
181 size = int(mac_range_params.get("address_range_size", 1))
182 if index < size:
183 return (mac_base and offset_mac(mac_base, index),
184 ip_base and offset_ip(ip_base, index))
185 index -= size
186 return (None, None)
187
188
lmrf3d3e522010-03-23 16:38:12 +0000189def get_sub_pool(dict, piece, num_pieces):
190 """
191 Split a MAC-IP pool and return a single requested piece.
192
193 For example, get_sub_pool(dict, 0, 3) will split the pool in 3 pieces and
194 return a dict representing the first piece.
195
196 @param dict: A dict that contains pool parameters.
197 @param piece: The index of the requested piece. Should range from 0 to
198 num_pieces - 1.
199 @param num_pieces: The total number of pieces.
200 @return: A copy of dict, modified to describe the requested sub-pool.
201 """
202 range_dicts = [get_sub_dict(dict, name) for name in
203 get_sub_dict_names(dict, "address_ranges")]
204 if not range_dicts:
205 return dict
206 ranges = [[d.get("address_range_base_mac"),
207 d.get("address_range_base_ip"),
208 int(d.get("address_range_size", 1))] for d in range_dicts]
209 total_size = sum(r[2] for r in ranges)
210 base = total_size * piece / num_pieces
211 size = total_size * (piece + 1) / num_pieces - base
212
213 # Find base of current sub-pool
214 for i in range(len(ranges)):
215 r = ranges[i]
216 if base < r[2]:
217 r[0] = r[0] and offset_mac(r[0], base)
218 r[1] = r[1] and offset_ip(r[1], base)
219 r[2] -= base
220 break
221 base -= r[2]
222
223 # Collect ranges up to end of current sub-pool
224 new_ranges = []
225 for i in range(i, len(ranges)):
226 r = ranges[i]
227 new_ranges.append(r)
228 if size <= r[2]:
229 r[2] = size
230 break
231 size -= r[2]
232
233 # Write new dict
234 new_dict = dict.copy()
235 new_dict["address_ranges"] = " ".join("r%d" % i for i in
236 range(len(new_ranges)))
237 for i in range(len(new_ranges)):
238 new_dict["address_range_base_mac_r%d" % i] = new_ranges[i][0]
239 new_dict["address_range_base_ip_r%d" % i] = new_ranges[i][1]
240 new_dict["address_range_size_r%d" % i] = new_ranges[i][2]
241 return new_dict
242
243
lmr53d3e932009-09-09 21:56:18 +0000244def verify_ip_address_ownership(ip, macs, timeout=10.0):
lmree90dd92009-08-13 04:13:39 +0000245 """
lmr53d3e932009-09-09 21:56:18 +0000246 Use arping and the ARP cache to make sure a given IP address belongs to one
247 of the given MAC addresses.
lmree90dd92009-08-13 04:13:39 +0000248
249 @param ip: An IP address.
250 @param macs: A list or tuple of MAC addresses.
251 @return: True iff ip is assigned to a MAC address in macs.
252 """
lmr53d3e932009-09-09 21:56:18 +0000253 # Compile a regex that matches the given IP address and any of the given
254 # MAC addresses
lmree90dd92009-08-13 04:13:39 +0000255 mac_regex = "|".join("(%s)" % mac for mac in macs)
lmre35507b2009-10-28 16:53:08 +0000256 regex = re.compile(r"\b%s\b.*\b(%s)\b" % (ip, mac_regex), re.IGNORECASE)
lmree90dd92009-08-13 04:13:39 +0000257
lmr53d3e932009-09-09 21:56:18 +0000258 # Check the ARP cache
lmr3eb39c72010-07-08 23:34:05 +0000259 o = commands.getoutput("%s -n" % find_command("arp"))
lmre35507b2009-10-28 16:53:08 +0000260 if regex.search(o):
lmree90dd92009-08-13 04:13:39 +0000261 return True
262
lmr53d3e932009-09-09 21:56:18 +0000263 # Get the name of the bridge device for arping
lmr3eb39c72010-07-08 23:34:05 +0000264 o = commands.getoutput("%s route get %s" % (find_command("ip"), ip))
lmr53d3e932009-09-09 21:56:18 +0000265 dev = re.findall("dev\s+\S+", o, re.IGNORECASE)
266 if not dev:
267 return False
268 dev = dev[0].split()[-1]
269
270 # Send an ARP request
lmr3eb39c72010-07-08 23:34:05 +0000271 o = commands.getoutput("%s -f -c 3 -I %s %s" %
272 (find_command("arping"), dev, ip))
lmre35507b2009-10-28 16:53:08 +0000273 return bool(regex.search(o))
lmree90dd92009-08-13 04:13:39 +0000274
275
lmr6f669ce2009-05-31 19:02:42 +0000276# Functions for working with the environment (a dict-like object)
277
278def is_vm(obj):
279 """
280 Tests whether a given object is a VM object.
281
282 @param obj: Python object (pretty much everything on python).
283 """
284 return obj.__class__.__name__ == "VM"
285
286
287def env_get_all_vms(env):
288 """
289 Return a list of all VM objects on a given environment.
290
291 @param env: Dictionary with environment items.
292 """
293 vms = []
294 for obj in env.values():
295 if is_vm(obj):
296 vms.append(obj)
297 return vms
298
299
300def env_get_vm(env, name):
301 """
302 Return a VM object by its name.
303
304 @param name: VM name.
305 """
306 return env.get("vm__%s" % name)
307
308
309def env_register_vm(env, name, vm):
310 """
311 Register a given VM in a given env.
312
313 @param env: Environment where we will register the VM.
314 @param name: VM name.
315 @param vm: VM object.
316 """
317 env["vm__%s" % name] = vm
318
319
320def env_unregister_vm(env, name):
321 """
322 Remove a given VM from a given env.
323
324 @param env: Environment where we will un-register the VM.
325 @param name: VM name.
326 """
327 del env["vm__%s" % name]
328
329
330# Utility functions for dealing with external processes
331
lmr049239c2010-07-08 23:27:18 +0000332def find_command(cmd):
333 for dir in ["/usr/local/sbin", "/usr/local/bin",
334 "/usr/sbin", "/usr/bin", "/sbin", "/bin"]:
335 file = os.path.join(dir, cmd)
336 if os.path.exists(file):
337 return file
338 raise ValueError('Missing command: %s' % cmd)
339
340
lmr6f669ce2009-05-31 19:02:42 +0000341def pid_exists(pid):
342 """
343 Return True if a given PID exists.
344
345 @param pid: Process ID number.
346 """
347 try:
348 os.kill(pid, 0)
349 return True
350 except:
351 return False
352
353
354def safe_kill(pid, signal):
355 """
356 Attempt to send a signal to a given process that may or may not exist.
357
358 @param signal: Signal number.
359 """
360 try:
361 os.kill(pid, signal)
362 return True
363 except:
364 return False
365
366
lmr1aeaefb2009-09-09 22:12:49 +0000367def kill_process_tree(pid, sig=signal.SIGKILL):
368 """Signal a process and all of its children.
369
370 If the process does not exist -- return.
371
372 @param pid: The pid of the process to signal.
373 @param sig: The signal to send to the processes.
374 """
375 if not safe_kill(pid, signal.SIGSTOP):
376 return
377 children = commands.getoutput("ps --ppid=%d -o pid=" % pid).split()
378 for child in children:
379 kill_process_tree(int(child), sig)
380 safe_kill(pid, sig)
381 safe_kill(pid, signal.SIGCONT)
382
383
lmr117bbcf2009-09-15 07:12:39 +0000384def get_latest_kvm_release_tag(release_listing):
lmr3f0b0cc2009-06-10 02:25:23 +0000385 """
386 Fetches the latest release tag for KVM.
387
lmr117bbcf2009-09-15 07:12:39 +0000388 @param release_listing: URL that contains a list of the Source Forge
389 KVM project files.
lmr3f0b0cc2009-06-10 02:25:23 +0000390 """
391 try:
lmr117bbcf2009-09-15 07:12:39 +0000392 release_page = utils.urlopen(release_listing)
393 data = release_page.read()
394 release_page.close()
lmr8ea274b2009-07-06 13:42:35 +0000395 rx = re.compile("kvm-(\d+).tar.gz", re.IGNORECASE)
lmr3f0b0cc2009-06-10 02:25:23 +0000396 matches = rx.findall(data)
lmr32525382009-08-10 13:53:37 +0000397 # In all regexp matches to something that looks like a release tag,
398 # get the largest integer. That will be our latest release tag.
399 latest_tag = max(int(x) for x in matches)
400 return str(latest_tag)
lmr3f0b0cc2009-06-10 02:25:23 +0000401 except Exception, e:
402 message = "Could not fetch latest KVM release tag: %s" % str(e)
403 logging.error(message)
404 raise error.TestError(message)
405
406
407def get_git_branch(repository, branch, srcdir, commit=None, lbranch=None):
408 """
409 Retrieves a given git code repository.
410
411 @param repository: Git repository URL
412 """
lmr160e9942010-06-09 14:09:19 +0000413 logging.info("Fetching git [REP '%s' BRANCH '%s' COMMIT '%s'] -> %s",
lmr3f0b0cc2009-06-10 02:25:23 +0000414 repository, branch, commit, srcdir)
415 if not os.path.exists(srcdir):
416 os.makedirs(srcdir)
417 os.chdir(srcdir)
418
419 if os.path.exists(".git"):
420 utils.system("git reset --hard")
421 else:
422 utils.system("git init")
423
424 if not lbranch:
425 lbranch = branch
426
427 utils.system("git fetch -q -f -u -t %s %s:%s" %
428 (repository, branch, lbranch))
429 utils.system("git checkout %s" % lbranch)
430 if commit:
431 utils.system("git checkout %s" % commit)
432
433 h = utils.system_output('git log --pretty=format:"%H" -1')
lmr05cec0f2010-01-26 17:50:29 +0000434 try:
435 desc = "tag %s" % utils.system_output("git describe")
436 except error.CmdError:
437 desc = "no tag found"
438
lmr3f0b0cc2009-06-10 02:25:23 +0000439 logging.info("Commit hash for %s is %s (%s)" % (repository, h.strip(),
440 desc))
441 return srcdir
442
443
lmr3f0b0cc2009-06-10 02:25:23 +0000444def check_kvm_source_dir(source_dir):
445 """
446 Inspects the kvm source directory and verifies its disposition. In some
447 occasions build may be dependant on the source directory disposition.
448 The reason why the return codes are numbers is that we might have more
449 changes on the source directory layout, so it's not scalable to just use
450 strings like 'old_repo', 'new_repo' and such.
451
452 @param source_dir: Source code path that will be inspected.
453 """
454 os.chdir(source_dir)
455 has_qemu_dir = os.path.isdir('qemu')
456 has_kvm_dir = os.path.isdir('kvm')
457 if has_qemu_dir and not has_kvm_dir:
458 logging.debug("qemu directory detected, source dir layout 1")
459 return 1
460 if has_kvm_dir and not has_qemu_dir:
461 logging.debug("kvm directory detected, source dir layout 2")
462 return 2
463 else:
464 raise error.TestError("Unknown source dir layout, cannot proceed.")
465
466
lmrf9349c32009-07-23 01:44:24 +0000467# The following are functions used for SSH, SCP and Telnet communication with
468# guests.
lmr6f669ce2009-05-31 19:02:42 +0000469
lmra4cd1f12010-06-22 02:01:00 +0000470def _remote_login(session, username, password, prompt, timeout=10):
lmr6f669ce2009-05-31 19:02:42 +0000471 """
lmr158604f2010-06-22 01:57:34 +0000472 Log into a remote host (guest) using SSH or Telnet. Wait for questions
473 and provide answers. If timeout expires while waiting for output from the
474 child (e.g. a password prompt or a shell prompt) -- fail.
lmr6f669ce2009-05-31 19:02:42 +0000475
476 @brief: Log into a remote host (guest) using SSH or Telnet.
477
lmr158604f2010-06-22 01:57:34 +0000478 @param session: A kvm_expect or kvm_shell_session instance to operate on
lmra4cd1f12010-06-22 02:01:00 +0000479 @param username: The username to send in reply to a login prompt
lmr6f669ce2009-05-31 19:02:42 +0000480 @param password: The password to send in reply to a password prompt
481 @param prompt: The shell prompt that indicates a successful login
lmr6f669ce2009-05-31 19:02:42 +0000482 @param timeout: The maximal time duration (in seconds) to wait for each
483 step of the login procedure (i.e. the "Are you sure" prompt, the
484 password prompt, the shell prompt, etc)
485
lmr158604f2010-06-22 01:57:34 +0000486 @return: True on success and False otherwise.
lmr6f669ce2009-05-31 19:02:42 +0000487 """
lmr6f669ce2009-05-31 19:02:42 +0000488 password_prompt_count = 0
lmra4cd1f12010-06-22 02:01:00 +0000489 login_prompt_count = 0
lmr6f669ce2009-05-31 19:02:42 +0000490
lmr6f669ce2009-05-31 19:02:42 +0000491 while True:
lmr158604f2010-06-22 01:57:34 +0000492 (match, text) = session.read_until_last_line_matches(
lmr2c502672010-06-22 02:02:19 +0000493 [r"[Aa]re you sure", r"[Pp]assword:\s*$", r"[Ll]ogin:\s*$",
lmrba69bc52010-03-25 01:50:09 +0000494 r"[Cc]onnection.*closed", r"[Cc]onnection.*refused",
495 r"[Pp]lease wait", prompt],
lmr6f669ce2009-05-31 19:02:42 +0000496 timeout=timeout, internal_timeout=0.5)
497 if match == 0: # "Are you sure you want to continue connecting"
498 logging.debug("Got 'Are you sure...'; sending 'yes'")
lmr158604f2010-06-22 01:57:34 +0000499 session.sendline("yes")
lmr6f669ce2009-05-31 19:02:42 +0000500 continue
501 elif match == 1: # "password:"
502 if password_prompt_count == 0:
503 logging.debug("Got password prompt; sending '%s'" % password)
lmr158604f2010-06-22 01:57:34 +0000504 session.sendline(password)
lmr6f669ce2009-05-31 19:02:42 +0000505 password_prompt_count += 1
506 continue
507 else:
508 logging.debug("Got password prompt again")
lmr158604f2010-06-22 01:57:34 +0000509 return False
lmr6f669ce2009-05-31 19:02:42 +0000510 elif match == 2: # "login:"
lmra4cd1f12010-06-22 02:01:00 +0000511 if login_prompt_count == 0:
512 logging.debug("Got username prompt; sending '%s'" % username)
513 session.sendline(username)
514 login_prompt_count += 1
515 continue
516 else:
517 logging.debug("Got username prompt again")
518 return False
lmr6f669ce2009-05-31 19:02:42 +0000519 elif match == 3: # "Connection closed"
520 logging.debug("Got 'Connection closed'")
lmr158604f2010-06-22 01:57:34 +0000521 return False
lmr3ca79fe2009-06-10 19:24:26 +0000522 elif match == 4: # "Connection refused"
lmr0d2ed1f2009-07-01 03:23:18 +0000523 logging.debug("Got 'Connection refused'")
lmr158604f2010-06-22 01:57:34 +0000524 return False
lmrba69bc52010-03-25 01:50:09 +0000525 elif match == 5: # "Please wait"
526 logging.debug("Got 'Please wait'")
527 timeout = 30
528 continue
529 elif match == 6: # prompt
lmr6f669ce2009-05-31 19:02:42 +0000530 logging.debug("Got shell prompt -- logged in")
lmr158604f2010-06-22 01:57:34 +0000531 return session
lmr6f669ce2009-05-31 19:02:42 +0000532 else: # match == None
lmr3ca79fe2009-06-10 19:24:26 +0000533 logging.debug("Timeout elapsed or process terminated")
lmr158604f2010-06-22 01:57:34 +0000534 return False
535
536
537def _remote_scp(session, password, transfer_timeout=600, login_timeout=10):
538 """
539 Transfer file(s) to a remote host (guest) using SCP. Wait for questions
540 and provide answers. If login_timeout expires while waiting for output
541 from the child (e.g. a password prompt), fail. If transfer_timeout expires
542 while waiting for the transfer to complete, fail.
543
544 @brief: Transfer files using SCP, given a command line.
545
546 @param session: A kvm_expect or kvm_shell_session instance to operate on
547 @param password: The password to send in reply to a password prompt.
548 @param transfer_timeout: The time duration (in seconds) to wait for the
549 transfer to complete.
550 @param login_timeout: The maximal time duration (in seconds) to wait for
551 each step of the login procedure (i.e. the "Are you sure" prompt or
552 the password prompt)
553
554 @return: True if the transfer succeeds and False on failure.
555 """
556 password_prompt_count = 0
557 timeout = login_timeout
558
559 while True:
560 (match, text) = session.read_until_last_line_matches(
561 [r"[Aa]re you sure", r"[Pp]assword:\s*$", r"lost connection"],
562 timeout=timeout, internal_timeout=0.5)
563 if match == 0: # "Are you sure you want to continue connecting"
564 logging.debug("Got 'Are you sure...'; sending 'yes'")
565 session.sendline("yes")
566 continue
567 elif match == 1: # "password:"
568 if password_prompt_count == 0:
569 logging.debug("Got password prompt; sending '%s'" % password)
570 session.sendline(password)
571 password_prompt_count += 1
572 timeout = transfer_timeout
573 continue
574 else:
575 logging.debug("Got password prompt again")
576 return False
577 elif match == 2: # "lost connection"
578 logging.debug("Got 'lost connection'")
579 return False
580 else: # match == None
581 if session.is_alive():
582 logging.debug("Timeout expired")
583 return False
584 else:
585 status = session.get_status()
586 logging.debug("SCP process terminated with status %s", status)
587 return status == 0
588
589
590def remote_login(client, host, port, username, password, prompt, linesep="\n",
lmre56903f2010-06-22 02:09:35 +0000591 log_filename=None, timeout=10):
lmr158604f2010-06-22 01:57:34 +0000592 """
593 Log into a remote host (guest) using SSH/Telnet/Netcat.
594
595 @param client: The client to use ('ssh', 'telnet' or 'nc')
596 @param host: Hostname or IP address
597 @param port: Port to connect to
598 @param username: Username (if required)
599 @param password: Password (if required)
600 @param prompt: Shell prompt (regular expression)
601 @param linesep: The line separator to use when sending lines
602 (e.g. '\\n' or '\\r\\n')
lmre56903f2010-06-22 02:09:35 +0000603 @param log_filename: If specified, log all output to this file
lmr158604f2010-06-22 01:57:34 +0000604 @param timeout: The maximal time duration (in seconds) to wait for
605 each step of the login procedure (i.e. the "Are you sure" prompt
606 or the password prompt)
607
608 @return: kvm_shell_session object on success and None on failure.
609 """
610 if client == "ssh":
611 cmd = ("ssh -o UserKnownHostsFile=/dev/null "
612 "-o PreferredAuthentications=password -p %s %s@%s" %
613 (port, username, host))
614 elif client == "telnet":
615 cmd = "telnet -l %s %s %s" % (username, host, port)
616 elif client == "nc":
617 cmd = "nc %s %s" % (host, port)
618 else:
619 logging.error("Unknown remote shell client: %s" % client)
620 return
lmre56903f2010-06-22 02:09:35 +0000621
lmr158604f2010-06-22 01:57:34 +0000622 logging.debug("Trying to login with command '%s'" % cmd)
623 session = kvm_subprocess.kvm_shell_session(cmd, linesep=linesep,
624 prompt=prompt)
lmra4cd1f12010-06-22 02:01:00 +0000625 if _remote_login(session, username, password, prompt, timeout):
lmre56903f2010-06-22 02:09:35 +0000626 if log_filename:
627 session.set_output_func(log_line)
628 session.set_output_params((log_filename,))
lmr158604f2010-06-22 01:57:34 +0000629 return session
630 else:
631 session.close()
lmr6f669ce2009-05-31 19:02:42 +0000632
633
lmre56903f2010-06-22 02:09:35 +0000634def remote_scp(command, password, log_filename=None, transfer_timeout=600,
635 login_timeout=10):
lmr6f669ce2009-05-31 19:02:42 +0000636 """
lmr158604f2010-06-22 01:57:34 +0000637 Transfer file(s) to a remote host (guest) using SCP.
lmr6f669ce2009-05-31 19:02:42 +0000638
639 @brief: Transfer files using SCP, given a command line.
640
641 @param command: The command to execute
642 (e.g. "scp -r foobar root@localhost:/tmp/").
643 @param password: The password to send in reply to a password prompt.
lmre56903f2010-06-22 02:09:35 +0000644 @param log_filename: If specified, log all output to this file
lmrc5f42a32010-06-18 14:15:19 +0000645 @param transfer_timeout: The time duration (in seconds) to wait for the
lmr158604f2010-06-22 01:57:34 +0000646 transfer to complete.
lmr6f669ce2009-05-31 19:02:42 +0000647 @param login_timeout: The maximal time duration (in seconds) to wait for
lmr158604f2010-06-22 01:57:34 +0000648 each step of the login procedure (i.e. the "Are you sure" prompt
649 or the password prompt)
lmr6f669ce2009-05-31 19:02:42 +0000650
651 @return: True if the transfer succeeds and False on failure.
652 """
lmr158604f2010-06-22 01:57:34 +0000653 logging.debug("Trying to SCP with command '%s', timeout %ss",
654 command, transfer_timeout)
lmre56903f2010-06-22 02:09:35 +0000655
656 if log_filename:
657 output_func = log_line
658 output_params = (log_filename,)
659 else:
660 output_func = None
661 output_params = ()
662
663 session = kvm_subprocess.kvm_expect(command,
664 output_func=output_func,
665 output_params=output_params)
lmr158604f2010-06-22 01:57:34 +0000666 try:
667 return _remote_scp(session, password, transfer_timeout, login_timeout)
668 finally:
669 session.close()
lmr6f669ce2009-05-31 19:02:42 +0000670
671
672def scp_to_remote(host, port, username, password, local_path, remote_path,
lmre56903f2010-06-22 02:09:35 +0000673 log_filename=None, timeout=600):
lmr6f669ce2009-05-31 19:02:42 +0000674 """
675 Copy files to a remote host (guest).
676
lmr912c74b2009-08-17 20:43:27 +0000677 @param host: Hostname or IP address
678 @param username: Username (if required)
679 @param password: Password (if required)
lmr6f669ce2009-05-31 19:02:42 +0000680 @param local_path: Path on the local machine where we are copying from
681 @param remote_path: Path on the remote machine where we are copying to
lmre56903f2010-06-22 02:09:35 +0000682 @param log_filename: If specified, log all output to this file
lmr158604f2010-06-22 01:57:34 +0000683 @param timeout: The time duration (in seconds) to wait for the transfer
684 to complete.
lmr6f669ce2009-05-31 19:02:42 +0000685
686 @return: True on success and False on failure.
687 """
lmrc196d492010-05-07 14:14:26 +0000688 command = ("scp -v -o UserKnownHostsFile=/dev/null "
lmr1b7e1702009-11-10 16:30:39 +0000689 "-o PreferredAuthentications=password -r -P %s %s %s@%s:%s" %
lmrd16a67d2009-06-10 19:52:59 +0000690 (port, local_path, username, host, remote_path))
lmre56903f2010-06-22 02:09:35 +0000691 return remote_scp(command, password, log_filename, timeout)
lmr6f669ce2009-05-31 19:02:42 +0000692
693
694def scp_from_remote(host, port, username, password, remote_path, local_path,
lmre56903f2010-06-22 02:09:35 +0000695 log_filename=None, timeout=600):
lmr6f669ce2009-05-31 19:02:42 +0000696 """
697 Copy files from a remote host (guest).
698
lmr912c74b2009-08-17 20:43:27 +0000699 @param host: Hostname or IP address
700 @param username: Username (if required)
701 @param password: Password (if required)
lmr6f669ce2009-05-31 19:02:42 +0000702 @param local_path: Path on the local machine where we are copying from
703 @param remote_path: Path on the remote machine where we are copying to
lmre56903f2010-06-22 02:09:35 +0000704 @param log_filename: If specified, log all output to this file
lmr158604f2010-06-22 01:57:34 +0000705 @param timeout: The time duration (in seconds) to wait for the transfer
706 to complete.
lmr6f669ce2009-05-31 19:02:42 +0000707
708 @return: True on success and False on failure.
709 """
lmrc196d492010-05-07 14:14:26 +0000710 command = ("scp -v -o UserKnownHostsFile=/dev/null "
lmr1b7e1702009-11-10 16:30:39 +0000711 "-o PreferredAuthentications=password -r -P %s %s@%s:%s %s" %
lmrd16a67d2009-06-10 19:52:59 +0000712 (port, username, host, remote_path, local_path))
lmre56903f2010-06-22 02:09:35 +0000713 return remote_scp(command, password, log_filename, timeout)
lmr6f669ce2009-05-31 19:02:42 +0000714
715
lmr6f669ce2009-05-31 19:02:42 +0000716# The following are utility functions related to ports.
717
lmr6f669ce2009-05-31 19:02:42 +0000718def is_port_free(port):
719 """
720 Return True if the given port is available for use.
721
722 @param port: Port number
723 """
724 try:
725 s = socket.socket()
726 #s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
727 s.bind(("localhost", port))
728 free = True
729 except socket.error:
730 free = False
731 s.close()
732 return free
733
734
735def find_free_port(start_port, end_port):
736 """
lmra29a5cb2010-03-18 02:39:34 +0000737 Return a host free port in the range [start_port, end_port].
lmr6f669ce2009-05-31 19:02:42 +0000738
739 @param start_port: First port that will be checked.
740 @param end_port: Port immediately after the last one that will be checked.
741 """
742 for i in range(start_port, end_port):
743 if is_port_free(i):
744 return i
745 return None
746
747
748def find_free_ports(start_port, end_port, count):
749 """
lmra29a5cb2010-03-18 02:39:34 +0000750 Return count of host free ports in the range [start_port, end_port].
lmr6f669ce2009-05-31 19:02:42 +0000751
752 @count: Initial number of ports known to be free in the range.
753 @param start_port: First port that will be checked.
754 @param end_port: Port immediately after the last one that will be checked.
755 """
756 ports = []
757 i = start_port
758 while i < end_port and count > 0:
759 if is_port_free(i):
760 ports.append(i)
761 count -= 1
762 i += 1
763 return ports
764
765
lmr23894542010-06-22 01:54:37 +0000766# An easy way to log lines to files when the logging system can't be used
767
768_open_log_files = {}
769_log_file_dir = "/tmp"
770
771
772def log_line(filename, line):
773 """
774 Write a line to a file. '\n' is appended to the line.
775
776 @param filename: Path of file to write to, either absolute or relative to
777 the dir set by set_log_file_dir().
778 @param line: Line to write.
779 """
780 global _open_log_files, _log_file_dir
781 if filename not in _open_log_files:
782 path = get_path(_log_file_dir, filename)
783 try:
784 os.makedirs(os.path.dirname(path))
785 except OSError:
786 pass
787 _open_log_files[filename] = open(path, "w")
788 timestr = time.strftime("%Y-%m-%d %H:%M:%S")
789 _open_log_files[filename].write("%s: %s\n" % (timestr, line))
790 _open_log_files[filename].flush()
791
792
793def set_log_file_dir(dir):
794 """
795 Set the base directory for log files created by log_line().
796
797 @param dir: Directory for log files.
798 """
799 global _log_file_dir
800 _log_file_dir = dir
801
802
lmr6f669ce2009-05-31 19:02:42 +0000803# The following are miscellaneous utility functions.
804
lmrb4954e02009-08-17 20:44:42 +0000805def get_path(base_path, user_path):
806 """
807 Translate a user specified path to a real path.
808 If user_path is relative, append it to base_path.
809 If user_path is absolute, return it as is.
810
811 @param base_path: The base path of relative user specified paths.
812 @param user_path: The user specified path.
813 """
814 if os.path.isabs(user_path):
815 return user_path
816 else:
817 return os.path.join(base_path, user_path)
818
819
lmr6f669ce2009-05-31 19:02:42 +0000820def generate_random_string(length):
821 """
822 Return a random string using alphanumeric characters.
823
824 @length: length of the string that will be generated.
825 """
lmr45fc0c22009-09-15 05:16:45 +0000826 r = random.SystemRandom()
lmr6f669ce2009-05-31 19:02:42 +0000827 str = ""
828 chars = string.letters + string.digits
829 while length > 0:
lmr45fc0c22009-09-15 05:16:45 +0000830 str += r.choice(chars)
lmr6f669ce2009-05-31 19:02:42 +0000831 length -= 1
832 return str
833
lmr42b16662010-06-22 11:55:23 +0000834def generate_random_id():
835 """
836 Return a random string suitable for use as a qemu id.
837 """
838 return "id" + generate_random_string(6)
839
lmr6f669ce2009-05-31 19:02:42 +0000840
lmr0bee2342010-02-24 00:01:37 +0000841def generate_tmp_file_name(file, ext=None, dir='/tmp/'):
842 """
843 Returns a temporary file name. The file is not created.
844 """
845 while True:
846 file_name = (file + '-' + time.strftime("%Y%m%d-%H%M%S-") +
847 generate_random_string(4))
848 if ext:
849 file_name += '.' + ext
850 file_name = os.path.join(dir, file_name)
851 if not os.path.exists(file_name):
852 break
853
854 return file_name
855
856
lmr6f669ce2009-05-31 19:02:42 +0000857def format_str_for_message(str):
858 """
859 Format str so that it can be appended to a message.
860 If str consists of one line, prefix it with a space.
861 If str consists of multiple lines, prefix it with a newline.
862
863 @param str: string that will be formatted.
864 """
lmr57355592009-08-07 21:55:49 +0000865 lines = str.splitlines()
866 num_lines = len(lines)
867 str = "\n".join(lines)
lmr6f669ce2009-05-31 19:02:42 +0000868 if num_lines == 0:
869 return ""
870 elif num_lines == 1:
871 return " " + str
872 else:
873 return "\n" + str
874
875
876def wait_for(func, timeout, first=0.0, step=1.0, text=None):
877 """
878 If func() evaluates to True before timeout expires, return the
879 value of func(). Otherwise return None.
880
881 @brief: Wait until func() evaluates to True.
882
883 @param timeout: Timeout in seconds
884 @param first: Time to sleep before first attempt
885 @param steps: Time to sleep between attempts in seconds
886 @param text: Text to print while waiting, for debug purposes
887 """
888 start_time = time.time()
889 end_time = time.time() + timeout
890
891 time.sleep(first)
892
893 while time.time() < end_time:
894 if text:
895 logging.debug("%s (%f secs)" % (text, time.time() - start_time))
896
897 output = func()
898 if output:
899 return output
900
901 time.sleep(step)
902
903 logging.debug("Timeout elapsed")
904 return None
905
906
lmr03ba22e2009-10-23 12:07:44 +0000907def get_hash_from_file(hash_path, dvd_basename):
908 """
909 Get the a hash from a given DVD image from a hash file
910 (Hash files are usually named MD5SUM or SHA1SUM and are located inside the
911 download directories of the DVDs)
912
913 @param hash_path: Local path to a hash file.
914 @param cd_image: Basename of a CD image
915 """
916 hash_file = open(hash_path, 'r')
917 for line in hash_file.readlines():
918 if dvd_basename in line:
919 return line.split()[0]
920
921
lmr43beef12009-12-11 21:03:36 +0000922def run_tests(test_list, job):
923 """
924 Runs the sequence of KVM tests based on the list of dictionaries
925 generated by the configuration system, handling dependencies.
926
927 @param test_list: List with all dictionary test parameters.
928 @param job: Autotest job object.
929
930 @return: True, if all tests ran passed, False if any of them failed.
931 """
932 status_dict = {}
lmr43beef12009-12-11 21:03:36 +0000933 failed = False
lmr5df6eb42010-03-23 15:34:16 +0000934
lmr43beef12009-12-11 21:03:36 +0000935 for dict in test_list:
936 if dict.get("skip") == "yes":
937 continue
938 dependencies_satisfied = True
939 for dep in dict.get("depend"):
940 for test_name in status_dict.keys():
941 if not dep in test_name:
942 continue
943 if not status_dict[test_name]:
944 dependencies_satisfied = False
945 break
946 if dependencies_satisfied:
947 test_iterations = int(dict.get("iterations", 1))
lmr7da99b22010-01-12 22:30:28 +0000948 test_tag = dict.get("shortname")
lmrd960a722010-04-01 02:32:59 +0000949 # Setting up profilers during test execution.
950 profilers = dict.get("profilers", "").split()
951 for profiler in profilers:
952 job.profilers.add(profiler)
lmr7da99b22010-01-12 22:30:28 +0000953
lmr7da99b22010-01-12 22:30:28 +0000954 # We need only one execution, profiled, hence we're passing
955 # the profile_only parameter to job.run_test().
956 current_status = job.run_test("kvm", params=dict, tag=test_tag,
957 iterations=test_iterations,
lmrd960a722010-04-01 02:32:59 +0000958 profile_only= bool(profilers) or None)
959
960 for profiler in profilers:
961 job.profilers.delete(profiler)
lmr7da99b22010-01-12 22:30:28 +0000962
lmr43beef12009-12-11 21:03:36 +0000963 if not current_status:
964 failed = True
965 else:
966 current_status = False
967 status_dict[dict.get("name")] = current_status
968
969 return not failed
970
971
972def create_report(report_dir, results_dir):
973 """
974 Creates a neatly arranged HTML results report in the results dir.
975
976 @param report_dir: Directory where the report script is located.
977 @param results_dir: Directory where the results will be output.
978 """
979 reporter = os.path.join(report_dir, 'html_report.py')
980 html_file = os.path.join(results_dir, 'results.html')
981 os.system('%s -r %s -f %s -R' % (reporter, results_dir, html_file))
lmr31af3a12010-01-18 16:46:52 +0000982
983
984def get_full_pci_id(pci_id):
985 """
986 Get full PCI ID of pci_id.
987
988 @param pci_id: PCI ID of a device.
989 """
990 cmd = "lspci -D | awk '/%s/ {print $1}'" % pci_id
991 status, full_id = commands.getstatusoutput(cmd)
992 if status != 0:
993 return None
994 return full_id
995
996
997def get_vendor_from_pci_id(pci_id):
998 """
999 Check out the device vendor ID according to pci_id.
1000
1001 @param pci_id: PCI ID of a device.
1002 """
1003 cmd = "lspci -n | awk '/%s/ {print $3}'" % pci_id
1004 return re.sub(":", " ", commands.getoutput(cmd))
1005
1006
lmr84154412010-02-03 18:34:43 +00001007class KvmLoggingConfig(logging_config.LoggingConfig):
1008 """
1009 Used with the sole purpose of providing convenient logging setup
1010 for the KVM test auxiliary programs.
1011 """
1012 def configure_logging(self, results_dir=None, verbose=False):
1013 super(KvmLoggingConfig, self).configure_logging(use_console=True,
1014 verbose=verbose)
1015
1016
lmr31af3a12010-01-18 16:46:52 +00001017class PciAssignable(object):
1018 """
1019 Request PCI assignable devices on host. It will check whether to request
1020 PF (physical Functions) or VF (Virtual Functions).
1021 """
lmr83d3b1a2010-01-19 08:14:36 +00001022 def __init__(self, type="vf", driver=None, driver_option=None,
lmr31af3a12010-01-18 16:46:52 +00001023 names=None, devices_requested=None):
1024 """
1025 Initialize parameter 'type' which could be:
lmr83d3b1a2010-01-19 08:14:36 +00001026 vf: Virtual Functions
1027 pf: Physical Function (actual hardware)
lmr31af3a12010-01-18 16:46:52 +00001028 mixed: Both includes VFs and PFs
1029
1030 If pass through Physical NIC cards, we need to specify which devices
1031 to be assigned, e.g. 'eth1 eth2'.
1032
1033 If pass through Virtual Functions, we need to specify how many vfs
1034 are going to be assigned, e.g. passthrough_count = 8 and max_vfs in
1035 config file.
1036
1037 @param type: PCI device type.
1038 @param driver: Kernel module for the PCI assignable device.
1039 @param driver_option: Module option to specify the maximum number of
1040 VFs (eg 'max_vfs=7')
1041 @param names: Physical NIC cards correspondent network interfaces,
1042 e.g.'eth1 eth2 ...'
1043 @param devices_requested: Number of devices being requested.
1044 """
1045 self.type = type
1046 self.driver = driver
1047 self.driver_option = driver_option
1048 if names:
1049 self.name_list = names.split()
1050 if devices_requested:
1051 self.devices_requested = int(devices_requested)
1052 else:
1053 self.devices_requested = None
1054
1055
1056 def _get_pf_pci_id(self, name, search_str):
1057 """
1058 Get the PF PCI ID according to name.
1059
1060 @param name: Name of the PCI device.
1061 @param search_str: Search string to be used on lspci.
1062 """
1063 cmd = "ethtool -i %s | awk '/bus-info/ {print $2}'" % name
1064 s, pci_id = commands.getstatusoutput(cmd)
1065 if not (s or "Cannot get driver information" in pci_id):
1066 return pci_id[5:]
1067 cmd = "lspci | awk '/%s/ {print $1}'" % search_str
1068 pci_ids = [id for id in commands.getoutput(cmd).splitlines()]
1069 nic_id = int(re.search('[0-9]+', name).group(0))
1070 if (len(pci_ids) - 1) < nic_id:
1071 return None
1072 return pci_ids[nic_id]
1073
1074
1075 def _release_dev(self, pci_id):
1076 """
1077 Release a single PCI device.
1078
1079 @param pci_id: PCI ID of a given PCI device.
1080 """
1081 base_dir = "/sys/bus/pci"
1082 full_id = get_full_pci_id(pci_id)
1083 vendor_id = get_vendor_from_pci_id(pci_id)
1084 drv_path = os.path.join(base_dir, "devices/%s/driver" % full_id)
1085 if 'pci-stub' in os.readlink(drv_path):
1086 cmd = "echo '%s' > %s/new_id" % (vendor_id, drv_path)
1087 if os.system(cmd):
1088 return False
1089
1090 stub_path = os.path.join(base_dir, "drivers/pci-stub")
1091 cmd = "echo '%s' > %s/unbind" % (full_id, stub_path)
1092 if os.system(cmd):
1093 return False
1094
1095 driver = self.dev_drivers[pci_id]
1096 cmd = "echo '%s' > %s/bind" % (full_id, driver)
1097 if os.system(cmd):
1098 return False
1099
1100 return True
1101
1102
1103 def get_vf_devs(self):
1104 """
1105 Catch all VFs PCI IDs.
1106
1107 @return: List with all PCI IDs for the Virtual Functions avaliable
1108 """
1109 if not self.sr_iov_setup():
1110 return []
1111
1112 cmd = "lspci | awk '/Virtual Function/ {print $1}'"
1113 return commands.getoutput(cmd).split()
1114
1115
1116 def get_pf_devs(self):
1117 """
1118 Catch all PFs PCI IDs.
1119
1120 @return: List with all PCI IDs for the physical hardware requested
1121 """
1122 pf_ids = []
1123 for name in self.name_list:
1124 pf_id = self._get_pf_pci_id(name, "Ethernet")
1125 if not pf_id:
1126 continue
1127 pf_ids.append(pf_id)
1128 return pf_ids
1129
1130
1131 def get_devs(self, count):
1132 """
1133 Check out all devices' PCI IDs according to their name.
1134
1135 @param count: count number of PCI devices needed for pass through
1136 @return: a list of all devices' PCI IDs
1137 """
lmr83d3b1a2010-01-19 08:14:36 +00001138 if self.type == "vf":
lmr31af3a12010-01-18 16:46:52 +00001139 vf_ids = self.get_vf_devs()
lmr83d3b1a2010-01-19 08:14:36 +00001140 elif self.type == "pf":
lmr31af3a12010-01-18 16:46:52 +00001141 vf_ids = self.get_pf_devs()
1142 elif self.type == "mixed":
1143 vf_ids = self.get_vf_devs()
1144 vf_ids.extend(self.get_pf_devs())
1145 return vf_ids[0:count]
1146
1147
1148 def get_vfs_count(self):
1149 """
1150 Get VFs count number according to lspci.
1151 """
lmr224a5412010-03-17 12:00:36 +00001152 # FIXME: Need to think out a method of identify which
1153 # 'virtual function' belongs to which physical card considering
1154 # that if the host has more than one 82576 card. PCI_ID?
lmr31af3a12010-01-18 16:46:52 +00001155 cmd = "lspci | grep 'Virtual Function' | wc -l"
lmr224a5412010-03-17 12:00:36 +00001156 return int(commands.getoutput(cmd))
lmr31af3a12010-01-18 16:46:52 +00001157
1158
1159 def check_vfs_count(self):
1160 """
1161 Check VFs count number according to the parameter driver_options.
1162 """
lmr224a5412010-03-17 12:00:36 +00001163 # Network card 82576 has two network interfaces and each can be
1164 # virtualized up to 7 virtual functions, therefore we multiply
1165 # two for the value of driver_option 'max_vfs'.
1166 expected_count = int((re.findall("(\d)", self.driver_option)[0])) * 2
1167 return (self.get_vfs_count == expected_count)
lmr31af3a12010-01-18 16:46:52 +00001168
1169
1170 def is_binded_to_stub(self, full_id):
1171 """
1172 Verify whether the device with full_id is already binded to pci-stub.
1173
1174 @param full_id: Full ID for the given PCI device
1175 """
1176 base_dir = "/sys/bus/pci"
1177 stub_path = os.path.join(base_dir, "drivers/pci-stub")
1178 if os.path.exists(os.path.join(stub_path, full_id)):
1179 return True
1180 return False
1181
1182
1183 def sr_iov_setup(self):
1184 """
1185 Ensure the PCI device is working in sr_iov mode.
1186
1187 Check if the PCI hardware device drive is loaded with the appropriate,
1188 parameters (number of VFs), and if it's not, perform setup.
1189
1190 @return: True, if the setup was completed successfuly, False otherwise.
1191 """
1192 re_probe = False
1193 s, o = commands.getstatusoutput('lsmod | grep %s' % self.driver)
1194 if s:
1195 re_probe = True
1196 elif not self.check_vfs_count():
1197 os.system("modprobe -r %s" % self.driver)
1198 re_probe = True
lmr224a5412010-03-17 12:00:36 +00001199 else:
1200 return True
lmr31af3a12010-01-18 16:46:52 +00001201
1202 # Re-probe driver with proper number of VFs
1203 if re_probe:
1204 cmd = "modprobe %s %s" % (self.driver, self.driver_option)
lmr224a5412010-03-17 12:00:36 +00001205 logging.info("Loading the driver '%s' with option '%s'" %
1206 (self.driver, self.driver_option))
lmr31af3a12010-01-18 16:46:52 +00001207 s, o = commands.getstatusoutput(cmd)
1208 if s:
1209 return False
lmr31af3a12010-01-18 16:46:52 +00001210 return True
1211
1212
1213 def request_devs(self):
1214 """
1215 Implement setup process: unbind the PCI device and then bind it
1216 to the pci-stub driver.
1217
1218 @return: a list of successfully requested devices' PCI IDs.
1219 """
1220 base_dir = "/sys/bus/pci"
1221 stub_path = os.path.join(base_dir, "drivers/pci-stub")
1222
1223 self.pci_ids = self.get_devs(self.devices_requested)
lmrbc72f082010-02-08 10:02:40 +00001224 logging.debug("The following pci_ids were found: %s", self.pci_ids)
lmr31af3a12010-01-18 16:46:52 +00001225 requested_pci_ids = []
1226 self.dev_drivers = {}
1227
1228 # Setup all devices specified for assignment to guest
1229 for pci_id in self.pci_ids:
1230 full_id = get_full_pci_id(pci_id)
1231 if not full_id:
1232 continue
1233 drv_path = os.path.join(base_dir, "devices/%s/driver" % full_id)
1234 dev_prev_driver= os.path.realpath(os.path.join(drv_path,
1235 os.readlink(drv_path)))
1236 self.dev_drivers[pci_id] = dev_prev_driver
1237
1238 # Judge whether the device driver has been binded to stub
1239 if not self.is_binded_to_stub(full_id):
lmrbc72f082010-02-08 10:02:40 +00001240 logging.debug("Binding device %s to stub", full_id)
lmr31af3a12010-01-18 16:46:52 +00001241 vendor_id = get_vendor_from_pci_id(pci_id)
1242 stub_new_id = os.path.join(stub_path, 'new_id')
1243 unbind_dev = os.path.join(drv_path, 'unbind')
1244 stub_bind = os.path.join(stub_path, 'bind')
1245
1246 info_write_to_files = [(vendor_id, stub_new_id),
1247 (full_id, unbind_dev),
1248 (full_id, stub_bind)]
1249
1250 for content, file in info_write_to_files:
1251 try:
lmrbc72f082010-02-08 10:02:40 +00001252 utils.open_write_close(file, content)
lmr31af3a12010-01-18 16:46:52 +00001253 except IOError:
lmrbc72f082010-02-08 10:02:40 +00001254 logging.debug("Failed to write %s to file %s", content,
1255 file)
lmr31af3a12010-01-18 16:46:52 +00001256 continue
1257
1258 if not self.is_binded_to_stub(full_id):
lmrbc72f082010-02-08 10:02:40 +00001259 logging.error("Binding device %s to stub failed", pci_id)
lmr31af3a12010-01-18 16:46:52 +00001260 continue
1261 else:
lmrbc72f082010-02-08 10:02:40 +00001262 logging.debug("Device %s already binded to stub", pci_id)
lmr31af3a12010-01-18 16:46:52 +00001263 requested_pci_ids.append(pci_id)
1264 self.pci_ids = requested_pci_ids
1265 return self.pci_ids
1266
1267
1268 def release_devs(self):
1269 """
1270 Release all PCI devices currently assigned to VMs back to the
1271 virtualization host.
1272 """
1273 try:
1274 for pci_id in self.dev_drivers:
1275 if not self._release_dev(pci_id):
lmrbc72f082010-02-08 10:02:40 +00001276 logging.error("Failed to release device %s to host", pci_id)
lmr31af3a12010-01-18 16:46:52 +00001277 else:
lmrbc72f082010-02-08 10:02:40 +00001278 logging.info("Released device %s successfully", pci_id)
lmr31af3a12010-01-18 16:46:52 +00001279 except:
1280 return