blob: 7a54e5779a95f6215355647d69d90f903918ca6f [file] [log] [blame]
lmr6f669ce2009-05-31 19:02:42 +00001"""
2KVM test utility functions.
3
4@copyright: 2008-2009 Red Hat Inc.
5"""
6
lmr03ba22e2009-10-23 12:07:44 +00007import md5, sha, thread, subprocess, time, string, random, socket, os, signal
8import 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
371def unload_module(module_name):
372 """
373 Removes a module. Handles dependencies. If even then it's not possible
374 to remove one of the modules, it will trhow an error.CmdError exception.
375
376 @param module_name: Name of the module we want to remove.
377 """
378 l_raw = utils.system_output("/sbin/lsmod").splitlines()
379 lsmod = [x for x in l_raw if x.split()[0] == module_name]
380 if len(lsmod) > 0:
381 line_parts = lsmod[0].split()
382 if len(line_parts) == 4:
383 submodules = line_parts[3].split(",")
384 for submodule in submodules:
385 unload_module(submodule)
386 utils.system("/sbin/modprobe -r %s" % module_name)
387 logging.info("Module %s unloaded" % module_name)
388 else:
389 logging.info("Module %s is already unloaded" % module_name)
390
391
392def check_kvm_source_dir(source_dir):
393 """
394 Inspects the kvm source directory and verifies its disposition. In some
395 occasions build may be dependant on the source directory disposition.
396 The reason why the return codes are numbers is that we might have more
397 changes on the source directory layout, so it's not scalable to just use
398 strings like 'old_repo', 'new_repo' and such.
399
400 @param source_dir: Source code path that will be inspected.
401 """
402 os.chdir(source_dir)
403 has_qemu_dir = os.path.isdir('qemu')
404 has_kvm_dir = os.path.isdir('kvm')
405 if has_qemu_dir and not has_kvm_dir:
406 logging.debug("qemu directory detected, source dir layout 1")
407 return 1
408 if has_kvm_dir and not has_qemu_dir:
409 logging.debug("kvm directory detected, source dir layout 2")
410 return 2
411 else:
412 raise error.TestError("Unknown source dir layout, cannot proceed.")
413
414
lmrf9349c32009-07-23 01:44:24 +0000415# The following are functions used for SSH, SCP and Telnet communication with
416# guests.
lmr6f669ce2009-05-31 19:02:42 +0000417
418def remote_login(command, password, prompt, linesep="\n", timeout=10):
419 """
420 Log into a remote host (guest) using SSH or Telnet. Run the given command
421 using kvm_spawn and provide answers to the questions asked. If timeout
422 expires while waiting for output from the child (e.g. a password prompt
423 or a shell prompt) -- fail.
424
425 @brief: Log into a remote host (guest) using SSH or Telnet.
426
427 @param command: The command to execute (e.g. "ssh root@localhost")
428 @param password: The password to send in reply to a password prompt
429 @param prompt: The shell prompt that indicates a successful login
430 @param linesep: The line separator to send instead of "\\n"
431 (sometimes "\\r\\n" is required)
432 @param timeout: The maximal time duration (in seconds) to wait for each
433 step of the login procedure (i.e. the "Are you sure" prompt, the
434 password prompt, the shell prompt, etc)
435
436 @return Return the kvm_spawn object on success and None on failure.
437 """
lmrdc3a5b12009-07-23 01:40:40 +0000438 sub = kvm_subprocess.kvm_shell_session(command,
439 linesep=linesep,
440 prompt=prompt)
lmr6f669ce2009-05-31 19:02:42 +0000441
442 password_prompt_count = 0
443
lmr8691f422009-07-28 02:52:30 +0000444 logging.debug("Trying to login with command '%s'" % command)
lmr6f669ce2009-05-31 19:02:42 +0000445
446 while True:
447 (match, text) = sub.read_until_last_line_matches(
lmr3ca79fe2009-06-10 19:24:26 +0000448 [r"[Aa]re you sure", r"[Pp]assword:\s*$", r"^\s*[Ll]ogin:\s*$",
449 r"[Cc]onnection.*closed", r"[Cc]onnection.*refused", prompt],
lmr6f669ce2009-05-31 19:02:42 +0000450 timeout=timeout, internal_timeout=0.5)
451 if match == 0: # "Are you sure you want to continue connecting"
452 logging.debug("Got 'Are you sure...'; sending 'yes'")
453 sub.sendline("yes")
454 continue
455 elif match == 1: # "password:"
456 if password_prompt_count == 0:
457 logging.debug("Got password prompt; sending '%s'" % password)
458 sub.sendline(password)
459 password_prompt_count += 1
460 continue
461 else:
462 logging.debug("Got password prompt again")
463 sub.close()
464 return None
465 elif match == 2: # "login:"
466 logging.debug("Got unexpected login prompt")
467 sub.close()
468 return None
469 elif match == 3: # "Connection closed"
470 logging.debug("Got 'Connection closed'")
471 sub.close()
472 return None
lmr3ca79fe2009-06-10 19:24:26 +0000473 elif match == 4: # "Connection refused"
lmr0d2ed1f2009-07-01 03:23:18 +0000474 logging.debug("Got 'Connection refused'")
lmr3ca79fe2009-06-10 19:24:26 +0000475 sub.close()
476 return None
477 elif match == 5: # prompt
lmr6f669ce2009-05-31 19:02:42 +0000478 logging.debug("Got shell prompt -- logged in")
479 return sub
480 else: # match == None
lmr3ca79fe2009-06-10 19:24:26 +0000481 logging.debug("Timeout elapsed or process terminated")
lmr6f669ce2009-05-31 19:02:42 +0000482 sub.close()
483 return None
484
485
486def remote_scp(command, password, timeout=300, login_timeout=10):
487 """
488 Run the given command using kvm_spawn and provide answers to the questions
489 asked. If timeout expires while waiting for the transfer to complete ,
490 fail. If login_timeout expires while waiting for output from the child
491 (e.g. a password prompt), fail.
492
493 @brief: Transfer files using SCP, given a command line.
494
495 @param command: The command to execute
496 (e.g. "scp -r foobar root@localhost:/tmp/").
497 @param password: The password to send in reply to a password prompt.
498 @param timeout: The time duration (in seconds) to wait for the transfer
499 to complete.
500 @param login_timeout: The maximal time duration (in seconds) to wait for
501 each step of the login procedure (i.e. the "Are you sure" prompt or the
502 password prompt)
503
504 @return: True if the transfer succeeds and False on failure.
505 """
lmrdc3a5b12009-07-23 01:40:40 +0000506 sub = kvm_subprocess.kvm_expect(command)
lmr6f669ce2009-05-31 19:02:42 +0000507
508 password_prompt_count = 0
509 _timeout = login_timeout
510
511 logging.debug("Trying to login...")
512
513 while True:
514 (match, text) = sub.read_until_last_line_matches(
lmr3ca79fe2009-06-10 19:24:26 +0000515 [r"[Aa]re you sure", r"[Pp]assword:\s*$", r"lost connection"],
lmr6f669ce2009-05-31 19:02:42 +0000516 timeout=_timeout, internal_timeout=0.5)
517 if match == 0: # "Are you sure you want to continue connecting"
518 logging.debug("Got 'Are you sure...'; sending 'yes'")
519 sub.sendline("yes")
520 continue
521 elif match == 1: # "password:"
522 if password_prompt_count == 0:
523 logging.debug("Got password prompt; sending '%s'" % password)
524 sub.sendline(password)
525 password_prompt_count += 1
526 _timeout = timeout
527 continue
528 else:
529 logging.debug("Got password prompt again")
530 sub.close()
531 return False
532 elif match == 2: # "lost connection"
533 logging.debug("Got 'lost connection'")
534 sub.close()
535 return False
536 else: # match == None
lmrdc3a5b12009-07-23 01:40:40 +0000537 logging.debug("Timeout elapsed or process terminated")
538 status = sub.get_status()
lmr6f669ce2009-05-31 19:02:42 +0000539 sub.close()
lmrdc3a5b12009-07-23 01:40:40 +0000540 return status == 0
lmr6f669ce2009-05-31 19:02:42 +0000541
542
543def scp_to_remote(host, port, username, password, local_path, remote_path,
544 timeout=300):
545 """
546 Copy files to 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
554 copy 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, local_path, username, host, remote_path))
lmr6f669ce2009-05-31 19:02:42 +0000561 return remote_scp(command, password, timeout)
562
563
564def scp_from_remote(host, port, username, password, remote_path, local_path,
565 timeout=300):
566 """
567 Copy files from a remote host (guest).
568
lmr912c74b2009-08-17 20:43:27 +0000569 @param host: Hostname or IP address
570 @param username: Username (if required)
571 @param password: Password (if required)
lmr6f669ce2009-05-31 19:02:42 +0000572 @param local_path: Path on the local machine where we are copying from
573 @param remote_path: Path on the remote machine where we are copying to
574 @param timeout: Time in seconds that we will wait before giving up to copy
575 the files.
576
577 @return: True on success and False on failure.
578 """
lmr1b7e1702009-11-10 16:30:39 +0000579 command = ("scp -o UserKnownHostsFile=/dev/null "
580 "-o PreferredAuthentications=password -r -P %s %s@%s:%s %s" %
lmrd16a67d2009-06-10 19:52:59 +0000581 (port, username, host, remote_path, local_path))
lmr6f669ce2009-05-31 19:02:42 +0000582 return remote_scp(command, password, timeout)
583
584
lmr59f9e2d2009-10-05 18:47:16 +0000585def ssh(host, port, username, password, prompt, linesep="\n", timeout=10):
lmr6f669ce2009-05-31 19:02:42 +0000586 """
587 Log into a remote host (guest) using SSH.
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 """
lmr1b7e1702009-11-10 16:30:39 +0000598 command = ("ssh -o UserKnownHostsFile=/dev/null "
599 "-o PreferredAuthentications=password -p %s %s@%s" %
lmrd16a67d2009-06-10 19:52:59 +0000600 (port, username, host))
lmr59f9e2d2009-10-05 18:47:16 +0000601 return remote_login(command, password, prompt, linesep, timeout)
lmr6f669ce2009-05-31 19:02:42 +0000602
603
lmr59f9e2d2009-10-05 18:47:16 +0000604def telnet(host, port, username, password, prompt, linesep="\n", timeout=10):
lmr6f669ce2009-05-31 19:02:42 +0000605 """
606 Log into a remote host (guest) using Telnet.
607
lmr912c74b2009-08-17 20:43:27 +0000608 @param host: Hostname or IP address
609 @param username: Username (if required)
610 @param password: Password (if required)
611 @param prompt: Shell prompt (regular expression)
lmr6f669ce2009-05-31 19:02:42 +0000612 @timeout: Time in seconds that we will wait before giving up on logging
613 into the host.
614
615 @return: kvm_spawn object on success and None on failure.
616 """
617 command = "telnet -l %s %s %s" % (username, host, port)
lmr59f9e2d2009-10-05 18:47:16 +0000618 return remote_login(command, password, prompt, linesep, timeout)
lmr6f669ce2009-05-31 19:02:42 +0000619
620
lmr59f9e2d2009-10-05 18:47:16 +0000621def netcat(host, port, username, password, prompt, linesep="\n", timeout=10):
lmr9f6ebf12009-08-17 20:44:10 +0000622 """
623 Log into a remote host (guest) using Netcat.
624
625 @param host: Hostname or IP address
626 @param username: Username (if required)
627 @param password: Password (if required)
628 @param prompt: Shell prompt (regular expression)
629 @timeout: Time in seconds that we will wait before giving up on logging
630 into the host.
631
632 @return: kvm_spawn object on success and None on failure.
633 """
634 command = "nc %s %s" % (host, port)
lmr59f9e2d2009-10-05 18:47:16 +0000635 return remote_login(command, password, prompt, linesep, timeout)
lmr9f6ebf12009-08-17 20:44:10 +0000636
637
lmr6f669ce2009-05-31 19:02:42 +0000638# The following are utility functions related to ports.
639
lmr6f669ce2009-05-31 19:02:42 +0000640def is_port_free(port):
641 """
642 Return True if the given port is available for use.
643
644 @param port: Port number
645 """
646 try:
647 s = socket.socket()
648 #s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
649 s.bind(("localhost", port))
650 free = True
651 except socket.error:
652 free = False
653 s.close()
654 return free
655
656
657def find_free_port(start_port, end_port):
658 """
659 Return a free port in the range [start_port, end_port).
660
661 @param start_port: First port that will be checked.
662 @param end_port: Port immediately after the last one that will be checked.
663 """
664 for i in range(start_port, end_port):
665 if is_port_free(i):
666 return i
667 return None
668
669
670def find_free_ports(start_port, end_port, count):
671 """
672 Return count free ports in the range [start_port, end_port).
673
674 @count: Initial number of ports known to be free in the range.
675 @param start_port: First port that will be checked.
676 @param end_port: Port immediately after the last one that will be checked.
677 """
678 ports = []
679 i = start_port
680 while i < end_port and count > 0:
681 if is_port_free(i):
682 ports.append(i)
683 count -= 1
684 i += 1
685 return ports
686
687
688# The following are miscellaneous utility functions.
689
lmrb4954e02009-08-17 20:44:42 +0000690def get_path(base_path, user_path):
691 """
692 Translate a user specified path to a real path.
693 If user_path is relative, append it to base_path.
694 If user_path is absolute, return it as is.
695
696 @param base_path: The base path of relative user specified paths.
697 @param user_path: The user specified path.
698 """
699 if os.path.isabs(user_path):
700 return user_path
701 else:
702 return os.path.join(base_path, user_path)
703
704
lmr6f669ce2009-05-31 19:02:42 +0000705def generate_random_string(length):
706 """
707 Return a random string using alphanumeric characters.
708
709 @length: length of the string that will be generated.
710 """
lmr45fc0c22009-09-15 05:16:45 +0000711 r = random.SystemRandom()
lmr6f669ce2009-05-31 19:02:42 +0000712 str = ""
713 chars = string.letters + string.digits
714 while length > 0:
lmr45fc0c22009-09-15 05:16:45 +0000715 str += r.choice(chars)
lmr6f669ce2009-05-31 19:02:42 +0000716 length -= 1
717 return str
718
719
720def format_str_for_message(str):
721 """
722 Format str so that it can be appended to a message.
723 If str consists of one line, prefix it with a space.
724 If str consists of multiple lines, prefix it with a newline.
725
726 @param str: string that will be formatted.
727 """
lmr57355592009-08-07 21:55:49 +0000728 lines = str.splitlines()
729 num_lines = len(lines)
730 str = "\n".join(lines)
lmr6f669ce2009-05-31 19:02:42 +0000731 if num_lines == 0:
732 return ""
733 elif num_lines == 1:
734 return " " + str
735 else:
736 return "\n" + str
737
738
739def wait_for(func, timeout, first=0.0, step=1.0, text=None):
740 """
741 If func() evaluates to True before timeout expires, return the
742 value of func(). Otherwise return None.
743
744 @brief: Wait until func() evaluates to True.
745
746 @param timeout: Timeout in seconds
747 @param first: Time to sleep before first attempt
748 @param steps: Time to sleep between attempts in seconds
749 @param text: Text to print while waiting, for debug purposes
750 """
751 start_time = time.time()
752 end_time = time.time() + timeout
753
754 time.sleep(first)
755
756 while time.time() < end_time:
757 if text:
758 logging.debug("%s (%f secs)" % (text, time.time() - start_time))
759
760 output = func()
761 if output:
762 return output
763
764 time.sleep(step)
765
766 logging.debug("Timeout elapsed")
767 return None
768
769
lmr03ba22e2009-10-23 12:07:44 +0000770def hash_file(filename, size=None, method="md5"):
lmr6f669ce2009-05-31 19:02:42 +0000771 """
lmr03ba22e2009-10-23 12:07:44 +0000772 Calculate the hash of filename.
lmr6f669ce2009-05-31 19:02:42 +0000773 If size is not None, limit to first size bytes.
774 Throw exception if something is wrong with filename.
775 Can be also implemented with bash one-liner (assuming size%1024==0):
lmr03ba22e2009-10-23 12:07:44 +0000776 dd if=filename bs=1024 count=size/1024 | sha1sum -
lmr6f669ce2009-05-31 19:02:42 +0000777
lmr03ba22e2009-10-23 12:07:44 +0000778 @param filename: Path of the file that will have its hash calculated.
779 @param method: Method used to calculate the hash. Supported methods:
780 * md5
781 * sha1
782 @returns: Hash of the file, if something goes wrong, return None.
lmr6f669ce2009-05-31 19:02:42 +0000783 """
784 chunksize = 4096
785 fsize = os.path.getsize(filename)
lmr03ba22e2009-10-23 12:07:44 +0000786
787 if not size or size > fsize:
lmr6f669ce2009-05-31 19:02:42 +0000788 size = fsize
789 f = open(filename, 'rb')
lmr03ba22e2009-10-23 12:07:44 +0000790
791 if method == "md5":
792 hash = md5.new()
793 elif method == "sha1":
794 hash = sha.new()
795 else:
796 logging.error("Unknown hash type %s, returning None" % method)
797 return None
798
lmr6f669ce2009-05-31 19:02:42 +0000799 while size > 0:
800 if chunksize > size:
801 chunksize = size
802 data = f.read(chunksize)
803 if len(data) == 0:
804 logging.debug("Nothing left to read but size=%d" % size)
805 break
lmr03ba22e2009-10-23 12:07:44 +0000806 hash.update(data)
lmr6f669ce2009-05-31 19:02:42 +0000807 size -= len(data)
808 f.close()
lmr03ba22e2009-10-23 12:07:44 +0000809 return hash.hexdigest()
810
811
812def get_hash_from_file(hash_path, dvd_basename):
813 """
814 Get the a hash from a given DVD image from a hash file
815 (Hash files are usually named MD5SUM or SHA1SUM and are located inside the
816 download directories of the DVDs)
817
818 @param hash_path: Local path to a hash file.
819 @param cd_image: Basename of a CD image
820 """
821 hash_file = open(hash_path, 'r')
822 for line in hash_file.readlines():
823 if dvd_basename in line:
824 return line.split()[0]
825
826
827def unmap_url_cache(cachedir, url, expected_hash, method="md5"):
828 """
829 Downloads a file from a URL to a cache directory. If the file is already
830 at the expected position and has the expected hash, let's not download it
831 again.
832
833 @param cachedir: Directory that might hold a copy of the file we want to
834 download.
835 @param url: URL for the file we want to download.
836 @param expected_hash: Hash string that we expect the file downloaded to
837 have.
838 @param method: Method used to calculate the hash string (md5, sha1).
839 """
840 # Let's convert cachedir to a canonical path, if it's not already
841 cachedir = os.path.realpath(cachedir)
842 if not os.path.isdir(cachedir):
843 try:
844 os.makedirs(cachedir)
845 except:
846 raise ValueError('Could not create cache directory %s' % cachedir)
847 file_from_url = os.path.basename(url)
848 file_local_path = os.path.join(cachedir, file_from_url)
849
850 file_hash = None
851 failure_counter = 0
852 while not file_hash == expected_hash:
853 if os.path.isfile(file_local_path):
854 if method == "md5":
855 file_hash = hash_file(file_local_path, method="md5")
856 elif method == "sha1":
857 file_hash = hash_file(file_local_path, method="sha1")
858
859 if file_hash == expected_hash:
860 # File is already at the expected position and ready to go
861 src = file_from_url
862 else:
863 # Let's download the package again, it's corrupted...
864 logging.error("Seems that file %s is corrupted, trying to "
865 "download it again" % file_from_url)
866 src = url
867 failure_counter += 1
868 else:
869 # File is not there, let's download it
870 src = url
871 if failure_counter > 1:
872 raise EnvironmentError("Consistently failed to download the "
873 "package %s. Aborting further download "
874 "attempts. This might mean either the "
875 "network connection has problems or the "
876 "expected hash string that was determined "
877 "for this file is wrong" % file_from_url)
878 file_path = utils.unmap_url(cachedir, src, cachedir)
879
880 return file_path
lmr43beef12009-12-11 21:03:36 +0000881
882
883def run_tests(test_list, job):
884 """
885 Runs the sequence of KVM tests based on the list of dictionaries
886 generated by the configuration system, handling dependencies.
887
888 @param test_list: List with all dictionary test parameters.
889 @param job: Autotest job object.
890
891 @return: True, if all tests ran passed, False if any of them failed.
892 """
893 status_dict = {}
894
895 failed = False
896 for dict in test_list:
897 if dict.get("skip") == "yes":
898 continue
899 dependencies_satisfied = True
900 for dep in dict.get("depend"):
901 for test_name in status_dict.keys():
902 if not dep in test_name:
903 continue
904 if not status_dict[test_name]:
905 dependencies_satisfied = False
906 break
907 if dependencies_satisfied:
908 test_iterations = int(dict.get("iterations", 1))
lmr7da99b22010-01-12 22:30:28 +0000909 test_tag = dict.get("shortname")
910 # Setting up kvm_stat profiling during test execution.
911 # We don't need kvm_stat profiling on the build tests.
912 if "build" in test_tag:
913 # None because it's the default value on the base_test class
914 # and the value None is specifically checked there.
915 profile = None
916 else:
917 profile = True
918
919 if profile:
920 job.profilers.add('kvm_stat')
921 # We need only one execution, profiled, hence we're passing
922 # the profile_only parameter to job.run_test().
923 current_status = job.run_test("kvm", params=dict, tag=test_tag,
924 iterations=test_iterations,
925 profile_only=profile)
926 if profile:
927 job.profilers.delete('kvm_stat')
928
lmr43beef12009-12-11 21:03:36 +0000929 if not current_status:
930 failed = True
931 else:
932 current_status = False
933 status_dict[dict.get("name")] = current_status
934
935 return not failed
936
937
938def create_report(report_dir, results_dir):
939 """
940 Creates a neatly arranged HTML results report in the results dir.
941
942 @param report_dir: Directory where the report script is located.
943 @param results_dir: Directory where the results will be output.
944 """
945 reporter = os.path.join(report_dir, 'html_report.py')
946 html_file = os.path.join(results_dir, 'results.html')
947 os.system('%s -r %s -f %s -R' % (reporter, results_dir, html_file))
lmr31af3a12010-01-18 16:46:52 +0000948
949
950def get_full_pci_id(pci_id):
951 """
952 Get full PCI ID of pci_id.
953
954 @param pci_id: PCI ID of a device.
955 """
956 cmd = "lspci -D | awk '/%s/ {print $1}'" % pci_id
957 status, full_id = commands.getstatusoutput(cmd)
958 if status != 0:
959 return None
960 return full_id
961
962
963def get_vendor_from_pci_id(pci_id):
964 """
965 Check out the device vendor ID according to pci_id.
966
967 @param pci_id: PCI ID of a device.
968 """
969 cmd = "lspci -n | awk '/%s/ {print $3}'" % pci_id
970 return re.sub(":", " ", commands.getoutput(cmd))
971
972
lmr84154412010-02-03 18:34:43 +0000973class KvmLoggingConfig(logging_config.LoggingConfig):
974 """
975 Used with the sole purpose of providing convenient logging setup
976 for the KVM test auxiliary programs.
977 """
978 def configure_logging(self, results_dir=None, verbose=False):
979 super(KvmLoggingConfig, self).configure_logging(use_console=True,
980 verbose=verbose)
981
982
lmr31af3a12010-01-18 16:46:52 +0000983class PciAssignable(object):
984 """
985 Request PCI assignable devices on host. It will check whether to request
986 PF (physical Functions) or VF (Virtual Functions).
987 """
lmr83d3b1a2010-01-19 08:14:36 +0000988 def __init__(self, type="vf", driver=None, driver_option=None,
lmr31af3a12010-01-18 16:46:52 +0000989 names=None, devices_requested=None):
990 """
991 Initialize parameter 'type' which could be:
lmr83d3b1a2010-01-19 08:14:36 +0000992 vf: Virtual Functions
993 pf: Physical Function (actual hardware)
lmr31af3a12010-01-18 16:46:52 +0000994 mixed: Both includes VFs and PFs
995
996 If pass through Physical NIC cards, we need to specify which devices
997 to be assigned, e.g. 'eth1 eth2'.
998
999 If pass through Virtual Functions, we need to specify how many vfs
1000 are going to be assigned, e.g. passthrough_count = 8 and max_vfs in
1001 config file.
1002
1003 @param type: PCI device type.
1004 @param driver: Kernel module for the PCI assignable device.
1005 @param driver_option: Module option to specify the maximum number of
1006 VFs (eg 'max_vfs=7')
1007 @param names: Physical NIC cards correspondent network interfaces,
1008 e.g.'eth1 eth2 ...'
1009 @param devices_requested: Number of devices being requested.
1010 """
1011 self.type = type
1012 self.driver = driver
1013 self.driver_option = driver_option
1014 if names:
1015 self.name_list = names.split()
1016 if devices_requested:
1017 self.devices_requested = int(devices_requested)
1018 else:
1019 self.devices_requested = None
1020
1021
1022 def _get_pf_pci_id(self, name, search_str):
1023 """
1024 Get the PF PCI ID according to name.
1025
1026 @param name: Name of the PCI device.
1027 @param search_str: Search string to be used on lspci.
1028 """
1029 cmd = "ethtool -i %s | awk '/bus-info/ {print $2}'" % name
1030 s, pci_id = commands.getstatusoutput(cmd)
1031 if not (s or "Cannot get driver information" in pci_id):
1032 return pci_id[5:]
1033 cmd = "lspci | awk '/%s/ {print $1}'" % search_str
1034 pci_ids = [id for id in commands.getoutput(cmd).splitlines()]
1035 nic_id = int(re.search('[0-9]+', name).group(0))
1036 if (len(pci_ids) - 1) < nic_id:
1037 return None
1038 return pci_ids[nic_id]
1039
1040
1041 def _release_dev(self, pci_id):
1042 """
1043 Release a single PCI device.
1044
1045 @param pci_id: PCI ID of a given PCI device.
1046 """
1047 base_dir = "/sys/bus/pci"
1048 full_id = get_full_pci_id(pci_id)
1049 vendor_id = get_vendor_from_pci_id(pci_id)
1050 drv_path = os.path.join(base_dir, "devices/%s/driver" % full_id)
1051 if 'pci-stub' in os.readlink(drv_path):
1052 cmd = "echo '%s' > %s/new_id" % (vendor_id, drv_path)
1053 if os.system(cmd):
1054 return False
1055
1056 stub_path = os.path.join(base_dir, "drivers/pci-stub")
1057 cmd = "echo '%s' > %s/unbind" % (full_id, stub_path)
1058 if os.system(cmd):
1059 return False
1060
1061 driver = self.dev_drivers[pci_id]
1062 cmd = "echo '%s' > %s/bind" % (full_id, driver)
1063 if os.system(cmd):
1064 return False
1065
1066 return True
1067
1068
1069 def get_vf_devs(self):
1070 """
1071 Catch all VFs PCI IDs.
1072
1073 @return: List with all PCI IDs for the Virtual Functions avaliable
1074 """
1075 if not self.sr_iov_setup():
1076 return []
1077
1078 cmd = "lspci | awk '/Virtual Function/ {print $1}'"
1079 return commands.getoutput(cmd).split()
1080
1081
1082 def get_pf_devs(self):
1083 """
1084 Catch all PFs PCI IDs.
1085
1086 @return: List with all PCI IDs for the physical hardware requested
1087 """
1088 pf_ids = []
1089 for name in self.name_list:
1090 pf_id = self._get_pf_pci_id(name, "Ethernet")
1091 if not pf_id:
1092 continue
1093 pf_ids.append(pf_id)
1094 return pf_ids
1095
1096
1097 def get_devs(self, count):
1098 """
1099 Check out all devices' PCI IDs according to their name.
1100
1101 @param count: count number of PCI devices needed for pass through
1102 @return: a list of all devices' PCI IDs
1103 """
lmr83d3b1a2010-01-19 08:14:36 +00001104 if self.type == "vf":
lmr31af3a12010-01-18 16:46:52 +00001105 vf_ids = self.get_vf_devs()
lmr83d3b1a2010-01-19 08:14:36 +00001106 elif self.type == "pf":
lmr31af3a12010-01-18 16:46:52 +00001107 vf_ids = self.get_pf_devs()
1108 elif self.type == "mixed":
1109 vf_ids = self.get_vf_devs()
1110 vf_ids.extend(self.get_pf_devs())
1111 return vf_ids[0:count]
1112
1113
1114 def get_vfs_count(self):
1115 """
1116 Get VFs count number according to lspci.
1117 """
1118 cmd = "lspci | grep 'Virtual Function' | wc -l"
1119 # For each VF we'll see 2 prints of 'Virtual Function', so let's
1120 # divide the result per 2
1121 return int(commands.getoutput(cmd)) / 2
1122
1123
1124 def check_vfs_count(self):
1125 """
1126 Check VFs count number according to the parameter driver_options.
1127 """
1128 return (self.get_vfs_count == self.devices_requested)
1129
1130
1131 def is_binded_to_stub(self, full_id):
1132 """
1133 Verify whether the device with full_id is already binded to pci-stub.
1134
1135 @param full_id: Full ID for the given PCI device
1136 """
1137 base_dir = "/sys/bus/pci"
1138 stub_path = os.path.join(base_dir, "drivers/pci-stub")
1139 if os.path.exists(os.path.join(stub_path, full_id)):
1140 return True
1141 return False
1142
1143
1144 def sr_iov_setup(self):
1145 """
1146 Ensure the PCI device is working in sr_iov mode.
1147
1148 Check if the PCI hardware device drive is loaded with the appropriate,
1149 parameters (number of VFs), and if it's not, perform setup.
1150
1151 @return: True, if the setup was completed successfuly, False otherwise.
1152 """
1153 re_probe = False
1154 s, o = commands.getstatusoutput('lsmod | grep %s' % self.driver)
1155 if s:
1156 re_probe = True
1157 elif not self.check_vfs_count():
1158 os.system("modprobe -r %s" % self.driver)
1159 re_probe = True
1160
1161 # Re-probe driver with proper number of VFs
1162 if re_probe:
1163 cmd = "modprobe %s %s" % (self.driver, self.driver_option)
1164 s, o = commands.getstatusoutput(cmd)
1165 if s:
1166 return False
1167 if not self.check_vfs_count():
1168 return False
1169 return True
1170
1171
1172 def request_devs(self):
1173 """
1174 Implement setup process: unbind the PCI device and then bind it
1175 to the pci-stub driver.
1176
1177 @return: a list of successfully requested devices' PCI IDs.
1178 """
1179 base_dir = "/sys/bus/pci"
1180 stub_path = os.path.join(base_dir, "drivers/pci-stub")
1181
1182 self.pci_ids = self.get_devs(self.devices_requested)
1183 logging.debug("The following pci_ids were found: %s" % self.pci_ids)
1184 requested_pci_ids = []
1185 self.dev_drivers = {}
1186
1187 # Setup all devices specified for assignment to guest
1188 for pci_id in self.pci_ids:
1189 full_id = get_full_pci_id(pci_id)
1190 if not full_id:
1191 continue
1192 drv_path = os.path.join(base_dir, "devices/%s/driver" % full_id)
1193 dev_prev_driver= os.path.realpath(os.path.join(drv_path,
1194 os.readlink(drv_path)))
1195 self.dev_drivers[pci_id] = dev_prev_driver
1196
1197 # Judge whether the device driver has been binded to stub
1198 if not self.is_binded_to_stub(full_id):
1199 logging.debug("Binding device %s to stub" % full_id)
1200 vendor_id = get_vendor_from_pci_id(pci_id)
1201 stub_new_id = os.path.join(stub_path, 'new_id')
1202 unbind_dev = os.path.join(drv_path, 'unbind')
1203 stub_bind = os.path.join(stub_path, 'bind')
1204
1205 info_write_to_files = [(vendor_id, stub_new_id),
1206 (full_id, unbind_dev),
1207 (full_id, stub_bind)]
1208
1209 for content, file in info_write_to_files:
1210 try:
1211 utils.open_write_close(content, file)
1212 except IOError:
1213 logging.debug("Failed to write %s to file %s" %
1214 (content, file))
1215 continue
1216
1217 if not self.is_binded_to_stub(full_id):
1218 logging.error("Binding device %s to stub failed" %
1219 pci_id)
1220 continue
1221 else:
1222 logging.debug("Device %s already binded to stub" % pci_id)
1223 requested_pci_ids.append(pci_id)
1224 self.pci_ids = requested_pci_ids
1225 return self.pci_ids
1226
1227
1228 def release_devs(self):
1229 """
1230 Release all PCI devices currently assigned to VMs back to the
1231 virtualization host.
1232 """
1233 try:
1234 for pci_id in self.dev_drivers:
1235 if not self._release_dev(pci_id):
1236 logging.error("Failed to release device %s to host" %
1237 pci_id)
1238 else:
1239 logging.info("Released device %s successfully" % pci_id)
1240 except:
1241 return