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