blob: 0d92d79d28cee9cc8b659d40e1cdf58d2143b9fd [file] [log] [blame]
lmr6f669ce2009-05-31 19:02:42 +00001"""
2KVM test utility functions.
3
4@copyright: 2008-2009 Red Hat Inc.
5"""
6
lmr6f80e7a2010-02-04 03:18:28 +00007import thread, subprocess, time, string, random, socket, os, signal
lmr03ba22e2009-10-23 12:07:44 +00008import select, re, logging, commands, cPickle, pty
lmrb635b862009-09-10 14:53:21 +00009from autotest_lib.client.bin import utils
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
12
lmr6f669ce2009-05-31 19:02:42 +000013
lmre8a66dd2009-09-15 19:51:07 +000014def dump_env(obj, filename):
15 """
16 Dump KVM test environment to a file.
17
18 @param filename: Path to a file where the environment will be dumped to.
19 """
20 file = open(filename, "w")
21 cPickle.dump(obj, file)
22 file.close()
23
24
lmr8b5400c2010-03-23 15:32:29 +000025def load_env(filename, default={}):
lmre8a66dd2009-09-15 19:51:07 +000026 """
27 Load KVM test environment from an environment file.
28
29 @param filename: Path to a file where the environment was dumped to.
30 """
31 try:
32 file = open(filename, "r")
lmr8b5400c2010-03-23 15:32:29 +000033 obj = cPickle.load(file)
34 file.close()
35 return obj
36 # Almost any exception can be raised during unpickling, so let's catch
37 # them all
lmre8a66dd2009-09-15 19:51:07 +000038 except:
39 return default
lmre8a66dd2009-09-15 19:51:07 +000040
41
lmr6f669ce2009-05-31 19:02:42 +000042def get_sub_dict(dict, name):
43 """
44 Return a "sub-dict" corresponding to a specific object.
45
46 Operate on a copy of dict: for each key that ends with the suffix
47 "_" + name, strip the suffix from the key, and set the value of
48 the stripped key to that of the key. Return the resulting dict.
49
50 @param name: Suffix of the key we want to set the value.
51 """
52 suffix = "_" + name
53 new_dict = dict.copy()
54 for key in dict.keys():
55 if key.endswith(suffix):
56 new_key = key.split(suffix)[0]
57 new_dict[new_key] = dict[key]
58 return new_dict
59
60
61def get_sub_dict_names(dict, keyword):
62 """
63 Return a list of "sub-dict" names that may be extracted with get_sub_dict.
64
65 This function may be modified to change the behavior of all functions that
66 deal with multiple objects defined in dicts (e.g. VMs, images, NICs).
67
68 @param keyword: A key in dict (e.g. "vms", "images", "nics").
69 """
70 names = dict.get(keyword)
71 if names:
72 return names.split()
73 else:
74 return []
75
76
lmrac5089b2009-08-13 04:05:47 +000077# Functions related to MAC/IP addresses
78
79def mac_str_to_int(addr):
80 """
81 Convert MAC address string to integer.
82
83 @param addr: String representing the MAC address.
84 """
85 return sum(int(s, 16) * 256 ** i
86 for i, s in enumerate(reversed(addr.split(":"))))
87
88
89def mac_int_to_str(addr):
90 """
91 Convert MAC address integer to string.
92
93 @param addr: Integer representing the MAC address.
94 """
95 return ":".join("%02x" % (addr >> 8 * i & 0xFF)
96 for i in reversed(range(6)))
97
98
99def ip_str_to_int(addr):
100 """
101 Convert IP address string to integer.
102
103 @param addr: String representing the IP address.
104 """
105 return sum(int(s) * 256 ** i
106 for i, s in enumerate(reversed(addr.split("."))))
107
108
109def ip_int_to_str(addr):
110 """
111 Convert IP address integer to string.
112
113 @param addr: Integer representing the IP address.
114 """
115 return ".".join(str(addr >> 8 * i & 0xFF)
116 for i in reversed(range(4)))
117
118
119def offset_mac(base, offset):
120 """
121 Add offset to a given MAC address.
122
123 @param base: String representing a MAC address.
124 @param offset: Offset to add to base (integer)
125 @return: A string representing the offset MAC address.
126 """
127 return mac_int_to_str(mac_str_to_int(base) + offset)
128
129
130def offset_ip(base, offset):
131 """
132 Add offset to a given IP address.
133
134 @param base: String representing an IP address.
135 @param offset: Offset to add to base (integer)
136 @return: A string representing the offset IP address.
137 """
138 return ip_int_to_str(ip_str_to_int(base) + offset)
139
140
141def get_mac_ip_pair_from_dict(dict):
142 """
143 Fetch a MAC-IP address pair from dict and return it.
144
145 The parameters in dict are expected to conform to a certain syntax.
146 Typical usage may be:
147
148 address_ranges = r1 r2 r3
149
150 address_range_base_mac_r1 = 55:44:33:22:11:00
151 address_range_base_ip_r1 = 10.0.0.0
152 address_range_size_r1 = 16
153
154 address_range_base_mac_r2 = 55:44:33:22:11:40
155 address_range_base_ip_r2 = 10.0.0.60
156 address_range_size_r2 = 25
157
158 address_range_base_mac_r3 = 55:44:33:22:12:10
159 address_range_base_ip_r3 = 10.0.1.20
160 address_range_size_r3 = 230
161
162 address_index = 0
163
164 All parameters except address_index specify a MAC-IP address pool. The
165 pool consists of several MAC-IP address ranges.
166 address_index specified the index of the desired MAC-IP pair from the pool.
167
168 @param dict: The dictionary from which to fetch the addresses.
169 """
170 index = int(dict.get("address_index", 0))
171 for mac_range_name in get_sub_dict_names(dict, "address_ranges"):
172 mac_range_params = get_sub_dict(dict, mac_range_name)
173 mac_base = mac_range_params.get("address_range_base_mac")
174 ip_base = mac_range_params.get("address_range_base_ip")
175 size = int(mac_range_params.get("address_range_size", 1))
176 if index < size:
177 return (mac_base and offset_mac(mac_base, index),
178 ip_base and offset_ip(ip_base, index))
179 index -= size
180 return (None, None)
181
182
lmr53d3e932009-09-09 21:56:18 +0000183def verify_ip_address_ownership(ip, macs, timeout=10.0):
lmree90dd92009-08-13 04:13:39 +0000184 """
lmr53d3e932009-09-09 21:56:18 +0000185 Use arping and the ARP cache to make sure a given IP address belongs to one
186 of the given MAC addresses.
lmree90dd92009-08-13 04:13:39 +0000187
188 @param ip: An IP address.
189 @param macs: A list or tuple of MAC addresses.
190 @return: True iff ip is assigned to a MAC address in macs.
191 """
lmr53d3e932009-09-09 21:56:18 +0000192 # Compile a regex that matches the given IP address and any of the given
193 # MAC addresses
lmree90dd92009-08-13 04:13:39 +0000194 mac_regex = "|".join("(%s)" % mac for mac in macs)
lmre35507b2009-10-28 16:53:08 +0000195 regex = re.compile(r"\b%s\b.*\b(%s)\b" % (ip, mac_regex), re.IGNORECASE)
lmree90dd92009-08-13 04:13:39 +0000196
lmr53d3e932009-09-09 21:56:18 +0000197 # Check the ARP cache
198 o = commands.getoutput("/sbin/arp -n")
lmre35507b2009-10-28 16:53:08 +0000199 if regex.search(o):
lmree90dd92009-08-13 04:13:39 +0000200 return True
201
lmr53d3e932009-09-09 21:56:18 +0000202 # Get the name of the bridge device for arping
203 o = commands.getoutput("/sbin/ip route get %s" % ip)
204 dev = re.findall("dev\s+\S+", o, re.IGNORECASE)
205 if not dev:
206 return False
207 dev = dev[0].split()[-1]
208
209 # Send an ARP request
210 o = commands.getoutput("/sbin/arping -f -c 3 -I %s %s" % (dev, ip))
lmre35507b2009-10-28 16:53:08 +0000211 return bool(regex.search(o))
lmree90dd92009-08-13 04:13:39 +0000212
213
lmr6f669ce2009-05-31 19:02:42 +0000214# Functions for working with the environment (a dict-like object)
215
216def is_vm(obj):
217 """
218 Tests whether a given object is a VM object.
219
220 @param obj: Python object (pretty much everything on python).
221 """
222 return obj.__class__.__name__ == "VM"
223
224
225def env_get_all_vms(env):
226 """
227 Return a list of all VM objects on a given environment.
228
229 @param env: Dictionary with environment items.
230 """
231 vms = []
232 for obj in env.values():
233 if is_vm(obj):
234 vms.append(obj)
235 return vms
236
237
238def env_get_vm(env, name):
239 """
240 Return a VM object by its name.
241
242 @param name: VM name.
243 """
244 return env.get("vm__%s" % name)
245
246
247def env_register_vm(env, name, vm):
248 """
249 Register a given VM in a given env.
250
251 @param env: Environment where we will register the VM.
252 @param name: VM name.
253 @param vm: VM object.
254 """
255 env["vm__%s" % name] = vm
256
257
258def env_unregister_vm(env, name):
259 """
260 Remove a given VM from a given env.
261
262 @param env: Environment where we will un-register the VM.
263 @param name: VM name.
264 """
265 del env["vm__%s" % name]
266
267
268# Utility functions for dealing with external processes
269
270def pid_exists(pid):
271 """
272 Return True if a given PID exists.
273
274 @param pid: Process ID number.
275 """
276 try:
277 os.kill(pid, 0)
278 return True
279 except:
280 return False
281
282
283def safe_kill(pid, signal):
284 """
285 Attempt to send a signal to a given process that may or may not exist.
286
287 @param signal: Signal number.
288 """
289 try:
290 os.kill(pid, signal)
291 return True
292 except:
293 return False
294
295
lmr1aeaefb2009-09-09 22:12:49 +0000296def kill_process_tree(pid, sig=signal.SIGKILL):
297 """Signal a process and all of its children.
298
299 If the process does not exist -- return.
300
301 @param pid: The pid of the process to signal.
302 @param sig: The signal to send to the processes.
303 """
304 if not safe_kill(pid, signal.SIGSTOP):
305 return
306 children = commands.getoutput("ps --ppid=%d -o pid=" % pid).split()
307 for child in children:
308 kill_process_tree(int(child), sig)
309 safe_kill(pid, sig)
310 safe_kill(pid, signal.SIGCONT)
311
312
lmr117bbcf2009-09-15 07:12:39 +0000313def get_latest_kvm_release_tag(release_listing):
lmr3f0b0cc2009-06-10 02:25:23 +0000314 """
315 Fetches the latest release tag for KVM.
316
lmr117bbcf2009-09-15 07:12:39 +0000317 @param release_listing: URL that contains a list of the Source Forge
318 KVM project files.
lmr3f0b0cc2009-06-10 02:25:23 +0000319 """
320 try:
lmr117bbcf2009-09-15 07:12:39 +0000321 release_page = utils.urlopen(release_listing)
322 data = release_page.read()
323 release_page.close()
lmr8ea274b2009-07-06 13:42:35 +0000324 rx = re.compile("kvm-(\d+).tar.gz", re.IGNORECASE)
lmr3f0b0cc2009-06-10 02:25:23 +0000325 matches = rx.findall(data)
lmr32525382009-08-10 13:53:37 +0000326 # In all regexp matches to something that looks like a release tag,
327 # get the largest integer. That will be our latest release tag.
328 latest_tag = max(int(x) for x in matches)
329 return str(latest_tag)
lmr3f0b0cc2009-06-10 02:25:23 +0000330 except Exception, e:
331 message = "Could not fetch latest KVM release tag: %s" % str(e)
332 logging.error(message)
333 raise error.TestError(message)
334
335
336def get_git_branch(repository, branch, srcdir, commit=None, lbranch=None):
337 """
338 Retrieves a given git code repository.
339
340 @param repository: Git repository URL
341 """
342 logging.info("Fetching git [REP '%s' BRANCH '%s' TAG '%s'] -> %s",
343 repository, branch, commit, srcdir)
344 if not os.path.exists(srcdir):
345 os.makedirs(srcdir)
346 os.chdir(srcdir)
347
348 if os.path.exists(".git"):
349 utils.system("git reset --hard")
350 else:
351 utils.system("git init")
352
353 if not lbranch:
354 lbranch = branch
355
356 utils.system("git fetch -q -f -u -t %s %s:%s" %
357 (repository, branch, lbranch))
358 utils.system("git checkout %s" % lbranch)
359 if commit:
360 utils.system("git checkout %s" % commit)
361
362 h = utils.system_output('git log --pretty=format:"%H" -1')
lmr05cec0f2010-01-26 17:50:29 +0000363 try:
364 desc = "tag %s" % utils.system_output("git describe")
365 except error.CmdError:
366 desc = "no tag found"
367
lmr3f0b0cc2009-06-10 02:25:23 +0000368 logging.info("Commit hash for %s is %s (%s)" % (repository, h.strip(),
369 desc))
370 return srcdir
371
372
lmr3f0b0cc2009-06-10 02:25:23 +0000373def check_kvm_source_dir(source_dir):
374 """
375 Inspects the kvm source directory and verifies its disposition. In some
376 occasions build may be dependant on the source directory disposition.
377 The reason why the return codes are numbers is that we might have more
378 changes on the source directory layout, so it's not scalable to just use
379 strings like 'old_repo', 'new_repo' and such.
380
381 @param source_dir: Source code path that will be inspected.
382 """
383 os.chdir(source_dir)
384 has_qemu_dir = os.path.isdir('qemu')
385 has_kvm_dir = os.path.isdir('kvm')
386 if has_qemu_dir and not has_kvm_dir:
387 logging.debug("qemu directory detected, source dir layout 1")
388 return 1
389 if has_kvm_dir and not has_qemu_dir:
390 logging.debug("kvm directory detected, source dir layout 2")
391 return 2
392 else:
393 raise error.TestError("Unknown source dir layout, cannot proceed.")
394
395
lmrf9349c32009-07-23 01:44:24 +0000396# The following are functions used for SSH, SCP and Telnet communication with
397# guests.
lmr6f669ce2009-05-31 19:02:42 +0000398
399def remote_login(command, password, prompt, linesep="\n", timeout=10):
400 """
401 Log into a remote host (guest) using SSH or Telnet. Run the given command
402 using kvm_spawn and provide answers to the questions asked. If timeout
403 expires while waiting for output from the child (e.g. a password prompt
404 or a shell prompt) -- fail.
405
406 @brief: Log into a remote host (guest) using SSH or Telnet.
407
408 @param command: The command to execute (e.g. "ssh root@localhost")
409 @param password: The password to send in reply to a password prompt
410 @param prompt: The shell prompt that indicates a successful login
411 @param linesep: The line separator to send instead of "\\n"
412 (sometimes "\\r\\n" is required)
413 @param timeout: The maximal time duration (in seconds) to wait for each
414 step of the login procedure (i.e. the "Are you sure" prompt, the
415 password prompt, the shell prompt, etc)
416
417 @return Return the kvm_spawn object on success and None on failure.
418 """
lmrdc3a5b12009-07-23 01:40:40 +0000419 sub = kvm_subprocess.kvm_shell_session(command,
420 linesep=linesep,
421 prompt=prompt)
lmr6f669ce2009-05-31 19:02:42 +0000422
423 password_prompt_count = 0
424
lmr8691f422009-07-28 02:52:30 +0000425 logging.debug("Trying to login with command '%s'" % command)
lmr6f669ce2009-05-31 19:02:42 +0000426
427 while True:
428 (match, text) = sub.read_until_last_line_matches(
lmr3ca79fe2009-06-10 19:24:26 +0000429 [r"[Aa]re you sure", r"[Pp]assword:\s*$", r"^\s*[Ll]ogin:\s*$",
430 r"[Cc]onnection.*closed", r"[Cc]onnection.*refused", prompt],
lmr6f669ce2009-05-31 19:02:42 +0000431 timeout=timeout, internal_timeout=0.5)
432 if match == 0: # "Are you sure you want to continue connecting"
433 logging.debug("Got 'Are you sure...'; sending 'yes'")
434 sub.sendline("yes")
435 continue
436 elif match == 1: # "password:"
437 if password_prompt_count == 0:
438 logging.debug("Got password prompt; sending '%s'" % password)
439 sub.sendline(password)
440 password_prompt_count += 1
441 continue
442 else:
443 logging.debug("Got password prompt again")
444 sub.close()
445 return None
446 elif match == 2: # "login:"
447 logging.debug("Got unexpected login prompt")
448 sub.close()
449 return None
450 elif match == 3: # "Connection closed"
451 logging.debug("Got 'Connection closed'")
452 sub.close()
453 return None
lmr3ca79fe2009-06-10 19:24:26 +0000454 elif match == 4: # "Connection refused"
lmr0d2ed1f2009-07-01 03:23:18 +0000455 logging.debug("Got 'Connection refused'")
lmr3ca79fe2009-06-10 19:24:26 +0000456 sub.close()
457 return None
458 elif match == 5: # prompt
lmr6f669ce2009-05-31 19:02:42 +0000459 logging.debug("Got shell prompt -- logged in")
460 return sub
461 else: # match == None
lmr3ca79fe2009-06-10 19:24:26 +0000462 logging.debug("Timeout elapsed or process terminated")
lmr6f669ce2009-05-31 19:02:42 +0000463 sub.close()
464 return None
465
466
467def remote_scp(command, password, timeout=300, login_timeout=10):
468 """
469 Run the given command using kvm_spawn and provide answers to the questions
470 asked. If timeout expires while waiting for the transfer to complete ,
471 fail. If login_timeout expires while waiting for output from the child
472 (e.g. a password prompt), fail.
473
474 @brief: Transfer files using SCP, given a command line.
475
476 @param command: The command to execute
477 (e.g. "scp -r foobar root@localhost:/tmp/").
478 @param password: The password to send in reply to a password prompt.
479 @param timeout: The time duration (in seconds) to wait for the transfer
480 to complete.
481 @param login_timeout: The maximal time duration (in seconds) to wait for
482 each step of the login procedure (i.e. the "Are you sure" prompt or the
483 password prompt)
484
485 @return: True if the transfer succeeds and False on failure.
486 """
lmrdc3a5b12009-07-23 01:40:40 +0000487 sub = kvm_subprocess.kvm_expect(command)
lmr6f669ce2009-05-31 19:02:42 +0000488
489 password_prompt_count = 0
490 _timeout = login_timeout
491
492 logging.debug("Trying to login...")
493
494 while True:
495 (match, text) = sub.read_until_last_line_matches(
lmr3ca79fe2009-06-10 19:24:26 +0000496 [r"[Aa]re you sure", r"[Pp]assword:\s*$", r"lost connection"],
lmr6f669ce2009-05-31 19:02:42 +0000497 timeout=_timeout, internal_timeout=0.5)
498 if match == 0: # "Are you sure you want to continue connecting"
499 logging.debug("Got 'Are you sure...'; sending 'yes'")
500 sub.sendline("yes")
501 continue
502 elif match == 1: # "password:"
503 if password_prompt_count == 0:
504 logging.debug("Got password prompt; sending '%s'" % password)
505 sub.sendline(password)
506 password_prompt_count += 1
507 _timeout = timeout
508 continue
509 else:
510 logging.debug("Got password prompt again")
511 sub.close()
512 return False
513 elif match == 2: # "lost connection"
514 logging.debug("Got 'lost connection'")
515 sub.close()
516 return False
517 else: # match == None
lmrdc3a5b12009-07-23 01:40:40 +0000518 logging.debug("Timeout elapsed or process terminated")
519 status = sub.get_status()
lmr6f669ce2009-05-31 19:02:42 +0000520 sub.close()
lmrdc3a5b12009-07-23 01:40:40 +0000521 return status == 0
lmr6f669ce2009-05-31 19:02:42 +0000522
523
524def scp_to_remote(host, port, username, password, local_path, remote_path,
525 timeout=300):
526 """
527 Copy files to a remote host (guest).
528
lmr912c74b2009-08-17 20:43:27 +0000529 @param host: Hostname or IP address
530 @param username: Username (if required)
531 @param password: Password (if required)
lmr6f669ce2009-05-31 19:02:42 +0000532 @param local_path: Path on the local machine where we are copying from
533 @param remote_path: Path on the remote machine where we are copying to
534 @param timeout: Time in seconds that we will wait before giving up to
535 copy the files.
536
537 @return: True on success and False on failure.
538 """
lmr1b7e1702009-11-10 16:30:39 +0000539 command = ("scp -o UserKnownHostsFile=/dev/null "
540 "-o PreferredAuthentications=password -r -P %s %s %s@%s:%s" %
lmrd16a67d2009-06-10 19:52:59 +0000541 (port, local_path, username, host, remote_path))
lmr6f669ce2009-05-31 19:02:42 +0000542 return remote_scp(command, password, timeout)
543
544
545def scp_from_remote(host, port, username, password, remote_path, local_path,
546 timeout=300):
547 """
548 Copy files from a remote host (guest).
549
lmr912c74b2009-08-17 20:43:27 +0000550 @param host: Hostname or IP address
551 @param username: Username (if required)
552 @param password: Password (if required)
lmr6f669ce2009-05-31 19:02:42 +0000553 @param local_path: Path on the local machine where we are copying from
554 @param remote_path: Path on the remote machine where we are copying to
555 @param timeout: Time in seconds that we will wait before giving up to copy
556 the files.
557
558 @return: True on success and False on failure.
559 """
lmr1b7e1702009-11-10 16:30:39 +0000560 command = ("scp -o UserKnownHostsFile=/dev/null "
561 "-o PreferredAuthentications=password -r -P %s %s@%s:%s %s" %
lmrd16a67d2009-06-10 19:52:59 +0000562 (port, username, host, remote_path, local_path))
lmr6f669ce2009-05-31 19:02:42 +0000563 return remote_scp(command, password, timeout)
564
565
lmr59f9e2d2009-10-05 18:47:16 +0000566def ssh(host, port, username, password, prompt, linesep="\n", timeout=10):
lmr6f669ce2009-05-31 19:02:42 +0000567 """
568 Log into a remote host (guest) using SSH.
569
lmr912c74b2009-08-17 20:43:27 +0000570 @param host: Hostname or IP address
571 @param username: Username (if required)
572 @param password: Password (if required)
573 @param prompt: Shell prompt (regular expression)
lmr6f669ce2009-05-31 19:02:42 +0000574 @timeout: Time in seconds that we will wait before giving up on logging
575 into the host.
576
577 @return: kvm_spawn object on success and None on failure.
578 """
lmr1b7e1702009-11-10 16:30:39 +0000579 command = ("ssh -o UserKnownHostsFile=/dev/null "
580 "-o PreferredAuthentications=password -p %s %s@%s" %
lmrd16a67d2009-06-10 19:52:59 +0000581 (port, username, host))
lmr59f9e2d2009-10-05 18:47:16 +0000582 return remote_login(command, password, prompt, linesep, timeout)
lmr6f669ce2009-05-31 19:02:42 +0000583
584
lmr59f9e2d2009-10-05 18:47:16 +0000585def telnet(host, port, username, password, prompt, linesep="\n", timeout=10):
lmr6f669ce2009-05-31 19:02:42 +0000586 """
587 Log into a remote host (guest) using Telnet.
588
lmr912c74b2009-08-17 20:43:27 +0000589 @param host: Hostname or IP address
590 @param username: Username (if required)
591 @param password: Password (if required)
592 @param prompt: Shell prompt (regular expression)
lmr6f669ce2009-05-31 19:02:42 +0000593 @timeout: Time in seconds that we will wait before giving up on logging
594 into the host.
595
596 @return: kvm_spawn object on success and None on failure.
597 """
598 command = "telnet -l %s %s %s" % (username, host, port)
lmr59f9e2d2009-10-05 18:47:16 +0000599 return remote_login(command, password, prompt, linesep, timeout)
lmr6f669ce2009-05-31 19:02:42 +0000600
601
lmr59f9e2d2009-10-05 18:47:16 +0000602def netcat(host, port, username, password, prompt, linesep="\n", timeout=10):
lmr9f6ebf12009-08-17 20:44:10 +0000603 """
604 Log into a remote host (guest) using Netcat.
605
606 @param host: Hostname or IP address
607 @param username: Username (if required)
608 @param password: Password (if required)
609 @param prompt: Shell prompt (regular expression)
610 @timeout: Time in seconds that we will wait before giving up on logging
611 into the host.
612
613 @return: kvm_spawn object on success and None on failure.
614 """
615 command = "nc %s %s" % (host, port)
lmr59f9e2d2009-10-05 18:47:16 +0000616 return remote_login(command, password, prompt, linesep, timeout)
lmr9f6ebf12009-08-17 20:44:10 +0000617
618
lmr6f669ce2009-05-31 19:02:42 +0000619# The following are utility functions related to ports.
620
lmr6f669ce2009-05-31 19:02:42 +0000621def is_port_free(port):
622 """
623 Return True if the given port is available for use.
624
625 @param port: Port number
626 """
627 try:
628 s = socket.socket()
629 #s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
630 s.bind(("localhost", port))
631 free = True
632 except socket.error:
633 free = False
634 s.close()
635 return free
636
637
638def find_free_port(start_port, end_port):
639 """
lmra29a5cb2010-03-18 02:39:34 +0000640 Return a host free port in the range [start_port, end_port].
lmr6f669ce2009-05-31 19:02:42 +0000641
642 @param start_port: First port that will be checked.
643 @param end_port: Port immediately after the last one that will be checked.
644 """
645 for i in range(start_port, end_port):
646 if is_port_free(i):
647 return i
648 return None
649
650
651def find_free_ports(start_port, end_port, count):
652 """
lmra29a5cb2010-03-18 02:39:34 +0000653 Return count of host free ports in the range [start_port, end_port].
lmr6f669ce2009-05-31 19:02:42 +0000654
655 @count: Initial number of ports known to be free in the range.
656 @param start_port: First port that will be checked.
657 @param end_port: Port immediately after the last one that will be checked.
658 """
659 ports = []
660 i = start_port
661 while i < end_port and count > 0:
662 if is_port_free(i):
663 ports.append(i)
664 count -= 1
665 i += 1
666 return ports
667
668
669# The following are miscellaneous utility functions.
670
lmrb4954e02009-08-17 20:44:42 +0000671def get_path(base_path, user_path):
672 """
673 Translate a user specified path to a real path.
674 If user_path is relative, append it to base_path.
675 If user_path is absolute, return it as is.
676
677 @param base_path: The base path of relative user specified paths.
678 @param user_path: The user specified path.
679 """
680 if os.path.isabs(user_path):
681 return user_path
682 else:
683 return os.path.join(base_path, user_path)
684
685
lmr6f669ce2009-05-31 19:02:42 +0000686def generate_random_string(length):
687 """
688 Return a random string using alphanumeric characters.
689
690 @length: length of the string that will be generated.
691 """
lmr45fc0c22009-09-15 05:16:45 +0000692 r = random.SystemRandom()
lmr6f669ce2009-05-31 19:02:42 +0000693 str = ""
694 chars = string.letters + string.digits
695 while length > 0:
lmr45fc0c22009-09-15 05:16:45 +0000696 str += r.choice(chars)
lmr6f669ce2009-05-31 19:02:42 +0000697 length -= 1
698 return str
699
700
lmr0bee2342010-02-24 00:01:37 +0000701def generate_tmp_file_name(file, ext=None, dir='/tmp/'):
702 """
703 Returns a temporary file name. The file is not created.
704 """
705 while True:
706 file_name = (file + '-' + time.strftime("%Y%m%d-%H%M%S-") +
707 generate_random_string(4))
708 if ext:
709 file_name += '.' + ext
710 file_name = os.path.join(dir, file_name)
711 if not os.path.exists(file_name):
712 break
713
714 return file_name
715
716
lmr6f669ce2009-05-31 19:02:42 +0000717def format_str_for_message(str):
718 """
719 Format str so that it can be appended to a message.
720 If str consists of one line, prefix it with a space.
721 If str consists of multiple lines, prefix it with a newline.
722
723 @param str: string that will be formatted.
724 """
lmr57355592009-08-07 21:55:49 +0000725 lines = str.splitlines()
726 num_lines = len(lines)
727 str = "\n".join(lines)
lmr6f669ce2009-05-31 19:02:42 +0000728 if num_lines == 0:
729 return ""
730 elif num_lines == 1:
731 return " " + str
732 else:
733 return "\n" + str
734
735
736def wait_for(func, timeout, first=0.0, step=1.0, text=None):
737 """
738 If func() evaluates to True before timeout expires, return the
739 value of func(). Otherwise return None.
740
741 @brief: Wait until func() evaluates to True.
742
743 @param timeout: Timeout in seconds
744 @param first: Time to sleep before first attempt
745 @param steps: Time to sleep between attempts in seconds
746 @param text: Text to print while waiting, for debug purposes
747 """
748 start_time = time.time()
749 end_time = time.time() + timeout
750
751 time.sleep(first)
752
753 while time.time() < end_time:
754 if text:
755 logging.debug("%s (%f secs)" % (text, time.time() - start_time))
756
757 output = func()
758 if output:
759 return output
760
761 time.sleep(step)
762
763 logging.debug("Timeout elapsed")
764 return None
765
766
lmr03ba22e2009-10-23 12:07:44 +0000767def get_hash_from_file(hash_path, dvd_basename):
768 """
769 Get the a hash from a given DVD image from a hash file
770 (Hash files are usually named MD5SUM or SHA1SUM and are located inside the
771 download directories of the DVDs)
772
773 @param hash_path: Local path to a hash file.
774 @param cd_image: Basename of a CD image
775 """
776 hash_file = open(hash_path, 'r')
777 for line in hash_file.readlines():
778 if dvd_basename in line:
779 return line.split()[0]
780
781
lmr43beef12009-12-11 21:03:36 +0000782def run_tests(test_list, job):
783 """
784 Runs the sequence of KVM tests based on the list of dictionaries
785 generated by the configuration system, handling dependencies.
786
787 @param test_list: List with all dictionary test parameters.
788 @param job: Autotest job object.
789
790 @return: True, if all tests ran passed, False if any of them failed.
791 """
792 status_dict = {}
793
794 failed = False
795 for dict in test_list:
796 if dict.get("skip") == "yes":
797 continue
798 dependencies_satisfied = True
799 for dep in dict.get("depend"):
800 for test_name in status_dict.keys():
801 if not dep in test_name:
802 continue
803 if not status_dict[test_name]:
804 dependencies_satisfied = False
805 break
806 if dependencies_satisfied:
807 test_iterations = int(dict.get("iterations", 1))
lmr7da99b22010-01-12 22:30:28 +0000808 test_tag = dict.get("shortname")
809 # Setting up kvm_stat profiling during test execution.
810 # We don't need kvm_stat profiling on the build tests.
811 if "build" in test_tag:
812 # None because it's the default value on the base_test class
813 # and the value None is specifically checked there.
814 profile = None
815 else:
816 profile = True
817
818 if profile:
819 job.profilers.add('kvm_stat')
820 # We need only one execution, profiled, hence we're passing
821 # the profile_only parameter to job.run_test().
822 current_status = job.run_test("kvm", params=dict, tag=test_tag,
823 iterations=test_iterations,
824 profile_only=profile)
825 if profile:
826 job.profilers.delete('kvm_stat')
827
lmr43beef12009-12-11 21:03:36 +0000828 if not current_status:
829 failed = True
830 else:
831 current_status = False
832 status_dict[dict.get("name")] = current_status
833
834 return not failed
835
836
837def create_report(report_dir, results_dir):
838 """
839 Creates a neatly arranged HTML results report in the results dir.
840
841 @param report_dir: Directory where the report script is located.
842 @param results_dir: Directory where the results will be output.
843 """
844 reporter = os.path.join(report_dir, 'html_report.py')
845 html_file = os.path.join(results_dir, 'results.html')
846 os.system('%s -r %s -f %s -R' % (reporter, results_dir, html_file))
lmr31af3a12010-01-18 16:46:52 +0000847
848
849def get_full_pci_id(pci_id):
850 """
851 Get full PCI ID of pci_id.
852
853 @param pci_id: PCI ID of a device.
854 """
855 cmd = "lspci -D | awk '/%s/ {print $1}'" % pci_id
856 status, full_id = commands.getstatusoutput(cmd)
857 if status != 0:
858 return None
859 return full_id
860
861
862def get_vendor_from_pci_id(pci_id):
863 """
864 Check out the device vendor ID according to pci_id.
865
866 @param pci_id: PCI ID of a device.
867 """
868 cmd = "lspci -n | awk '/%s/ {print $3}'" % pci_id
869 return re.sub(":", " ", commands.getoutput(cmd))
870
871
lmr84154412010-02-03 18:34:43 +0000872class KvmLoggingConfig(logging_config.LoggingConfig):
873 """
874 Used with the sole purpose of providing convenient logging setup
875 for the KVM test auxiliary programs.
876 """
877 def configure_logging(self, results_dir=None, verbose=False):
878 super(KvmLoggingConfig, self).configure_logging(use_console=True,
879 verbose=verbose)
880
881
lmr31af3a12010-01-18 16:46:52 +0000882class PciAssignable(object):
883 """
884 Request PCI assignable devices on host. It will check whether to request
885 PF (physical Functions) or VF (Virtual Functions).
886 """
lmr83d3b1a2010-01-19 08:14:36 +0000887 def __init__(self, type="vf", driver=None, driver_option=None,
lmr31af3a12010-01-18 16:46:52 +0000888 names=None, devices_requested=None):
889 """
890 Initialize parameter 'type' which could be:
lmr83d3b1a2010-01-19 08:14:36 +0000891 vf: Virtual Functions
892 pf: Physical Function (actual hardware)
lmr31af3a12010-01-18 16:46:52 +0000893 mixed: Both includes VFs and PFs
894
895 If pass through Physical NIC cards, we need to specify which devices
896 to be assigned, e.g. 'eth1 eth2'.
897
898 If pass through Virtual Functions, we need to specify how many vfs
899 are going to be assigned, e.g. passthrough_count = 8 and max_vfs in
900 config file.
901
902 @param type: PCI device type.
903 @param driver: Kernel module for the PCI assignable device.
904 @param driver_option: Module option to specify the maximum number of
905 VFs (eg 'max_vfs=7')
906 @param names: Physical NIC cards correspondent network interfaces,
907 e.g.'eth1 eth2 ...'
908 @param devices_requested: Number of devices being requested.
909 """
910 self.type = type
911 self.driver = driver
912 self.driver_option = driver_option
913 if names:
914 self.name_list = names.split()
915 if devices_requested:
916 self.devices_requested = int(devices_requested)
917 else:
918 self.devices_requested = None
919
920
921 def _get_pf_pci_id(self, name, search_str):
922 """
923 Get the PF PCI ID according to name.
924
925 @param name: Name of the PCI device.
926 @param search_str: Search string to be used on lspci.
927 """
928 cmd = "ethtool -i %s | awk '/bus-info/ {print $2}'" % name
929 s, pci_id = commands.getstatusoutput(cmd)
930 if not (s or "Cannot get driver information" in pci_id):
931 return pci_id[5:]
932 cmd = "lspci | awk '/%s/ {print $1}'" % search_str
933 pci_ids = [id for id in commands.getoutput(cmd).splitlines()]
934 nic_id = int(re.search('[0-9]+', name).group(0))
935 if (len(pci_ids) - 1) < nic_id:
936 return None
937 return pci_ids[nic_id]
938
939
940 def _release_dev(self, pci_id):
941 """
942 Release a single PCI device.
943
944 @param pci_id: PCI ID of a given PCI device.
945 """
946 base_dir = "/sys/bus/pci"
947 full_id = get_full_pci_id(pci_id)
948 vendor_id = get_vendor_from_pci_id(pci_id)
949 drv_path = os.path.join(base_dir, "devices/%s/driver" % full_id)
950 if 'pci-stub' in os.readlink(drv_path):
951 cmd = "echo '%s' > %s/new_id" % (vendor_id, drv_path)
952 if os.system(cmd):
953 return False
954
955 stub_path = os.path.join(base_dir, "drivers/pci-stub")
956 cmd = "echo '%s' > %s/unbind" % (full_id, stub_path)
957 if os.system(cmd):
958 return False
959
960 driver = self.dev_drivers[pci_id]
961 cmd = "echo '%s' > %s/bind" % (full_id, driver)
962 if os.system(cmd):
963 return False
964
965 return True
966
967
968 def get_vf_devs(self):
969 """
970 Catch all VFs PCI IDs.
971
972 @return: List with all PCI IDs for the Virtual Functions avaliable
973 """
974 if not self.sr_iov_setup():
975 return []
976
977 cmd = "lspci | awk '/Virtual Function/ {print $1}'"
978 return commands.getoutput(cmd).split()
979
980
981 def get_pf_devs(self):
982 """
983 Catch all PFs PCI IDs.
984
985 @return: List with all PCI IDs for the physical hardware requested
986 """
987 pf_ids = []
988 for name in self.name_list:
989 pf_id = self._get_pf_pci_id(name, "Ethernet")
990 if not pf_id:
991 continue
992 pf_ids.append(pf_id)
993 return pf_ids
994
995
996 def get_devs(self, count):
997 """
998 Check out all devices' PCI IDs according to their name.
999
1000 @param count: count number of PCI devices needed for pass through
1001 @return: a list of all devices' PCI IDs
1002 """
lmr83d3b1a2010-01-19 08:14:36 +00001003 if self.type == "vf":
lmr31af3a12010-01-18 16:46:52 +00001004 vf_ids = self.get_vf_devs()
lmr83d3b1a2010-01-19 08:14:36 +00001005 elif self.type == "pf":
lmr31af3a12010-01-18 16:46:52 +00001006 vf_ids = self.get_pf_devs()
1007 elif self.type == "mixed":
1008 vf_ids = self.get_vf_devs()
1009 vf_ids.extend(self.get_pf_devs())
1010 return vf_ids[0:count]
1011
1012
1013 def get_vfs_count(self):
1014 """
1015 Get VFs count number according to lspci.
1016 """
lmr224a5412010-03-17 12:00:36 +00001017 # FIXME: Need to think out a method of identify which
1018 # 'virtual function' belongs to which physical card considering
1019 # that if the host has more than one 82576 card. PCI_ID?
lmr31af3a12010-01-18 16:46:52 +00001020 cmd = "lspci | grep 'Virtual Function' | wc -l"
lmr224a5412010-03-17 12:00:36 +00001021 return int(commands.getoutput(cmd))
lmr31af3a12010-01-18 16:46:52 +00001022
1023
1024 def check_vfs_count(self):
1025 """
1026 Check VFs count number according to the parameter driver_options.
1027 """
lmr224a5412010-03-17 12:00:36 +00001028 # Network card 82576 has two network interfaces and each can be
1029 # virtualized up to 7 virtual functions, therefore we multiply
1030 # two for the value of driver_option 'max_vfs'.
1031 expected_count = int((re.findall("(\d)", self.driver_option)[0])) * 2
1032 return (self.get_vfs_count == expected_count)
lmr31af3a12010-01-18 16:46:52 +00001033
1034
1035 def is_binded_to_stub(self, full_id):
1036 """
1037 Verify whether the device with full_id is already binded to pci-stub.
1038
1039 @param full_id: Full ID for the given PCI device
1040 """
1041 base_dir = "/sys/bus/pci"
1042 stub_path = os.path.join(base_dir, "drivers/pci-stub")
1043 if os.path.exists(os.path.join(stub_path, full_id)):
1044 return True
1045 return False
1046
1047
1048 def sr_iov_setup(self):
1049 """
1050 Ensure the PCI device is working in sr_iov mode.
1051
1052 Check if the PCI hardware device drive is loaded with the appropriate,
1053 parameters (number of VFs), and if it's not, perform setup.
1054
1055 @return: True, if the setup was completed successfuly, False otherwise.
1056 """
1057 re_probe = False
1058 s, o = commands.getstatusoutput('lsmod | grep %s' % self.driver)
1059 if s:
1060 re_probe = True
1061 elif not self.check_vfs_count():
1062 os.system("modprobe -r %s" % self.driver)
1063 re_probe = True
lmr224a5412010-03-17 12:00:36 +00001064 else:
1065 return True
lmr31af3a12010-01-18 16:46:52 +00001066
1067 # Re-probe driver with proper number of VFs
1068 if re_probe:
1069 cmd = "modprobe %s %s" % (self.driver, self.driver_option)
lmr224a5412010-03-17 12:00:36 +00001070 logging.info("Loading the driver '%s' with option '%s'" %
1071 (self.driver, self.driver_option))
lmr31af3a12010-01-18 16:46:52 +00001072 s, o = commands.getstatusoutput(cmd)
1073 if s:
1074 return False
lmr31af3a12010-01-18 16:46:52 +00001075 return True
1076
1077
1078 def request_devs(self):
1079 """
1080 Implement setup process: unbind the PCI device and then bind it
1081 to the pci-stub driver.
1082
1083 @return: a list of successfully requested devices' PCI IDs.
1084 """
1085 base_dir = "/sys/bus/pci"
1086 stub_path = os.path.join(base_dir, "drivers/pci-stub")
1087
1088 self.pci_ids = self.get_devs(self.devices_requested)
lmrbc72f082010-02-08 10:02:40 +00001089 logging.debug("The following pci_ids were found: %s", self.pci_ids)
lmr31af3a12010-01-18 16:46:52 +00001090 requested_pci_ids = []
1091 self.dev_drivers = {}
1092
1093 # Setup all devices specified for assignment to guest
1094 for pci_id in self.pci_ids:
1095 full_id = get_full_pci_id(pci_id)
1096 if not full_id:
1097 continue
1098 drv_path = os.path.join(base_dir, "devices/%s/driver" % full_id)
1099 dev_prev_driver= os.path.realpath(os.path.join(drv_path,
1100 os.readlink(drv_path)))
1101 self.dev_drivers[pci_id] = dev_prev_driver
1102
1103 # Judge whether the device driver has been binded to stub
1104 if not self.is_binded_to_stub(full_id):
lmrbc72f082010-02-08 10:02:40 +00001105 logging.debug("Binding device %s to stub", full_id)
lmr31af3a12010-01-18 16:46:52 +00001106 vendor_id = get_vendor_from_pci_id(pci_id)
1107 stub_new_id = os.path.join(stub_path, 'new_id')
1108 unbind_dev = os.path.join(drv_path, 'unbind')
1109 stub_bind = os.path.join(stub_path, 'bind')
1110
1111 info_write_to_files = [(vendor_id, stub_new_id),
1112 (full_id, unbind_dev),
1113 (full_id, stub_bind)]
1114
1115 for content, file in info_write_to_files:
1116 try:
lmrbc72f082010-02-08 10:02:40 +00001117 utils.open_write_close(file, content)
lmr31af3a12010-01-18 16:46:52 +00001118 except IOError:
lmrbc72f082010-02-08 10:02:40 +00001119 logging.debug("Failed to write %s to file %s", content,
1120 file)
lmr31af3a12010-01-18 16:46:52 +00001121 continue
1122
1123 if not self.is_binded_to_stub(full_id):
lmrbc72f082010-02-08 10:02:40 +00001124 logging.error("Binding device %s to stub failed", pci_id)
lmr31af3a12010-01-18 16:46:52 +00001125 continue
1126 else:
lmrbc72f082010-02-08 10:02:40 +00001127 logging.debug("Device %s already binded to stub", pci_id)
lmr31af3a12010-01-18 16:46:52 +00001128 requested_pci_ids.append(pci_id)
1129 self.pci_ids = requested_pci_ids
1130 return self.pci_ids
1131
1132
1133 def release_devs(self):
1134 """
1135 Release all PCI devices currently assigned to VMs back to the
1136 virtualization host.
1137 """
1138 try:
1139 for pci_id in self.dev_drivers:
1140 if not self._release_dev(pci_id):
lmrbc72f082010-02-08 10:02:40 +00001141 logging.error("Failed to release device %s to host", pci_id)
lmr31af3a12010-01-18 16:46:52 +00001142 else:
lmrbc72f082010-02-08 10:02:40 +00001143 logging.info("Released device %s successfully", pci_id)
lmr31af3a12010-01-18 16:46:52 +00001144 except:
1145 return