blob: 37bab53ff543d1377a06c3b326daffcc276c437a [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
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
lmr6f80e7a2010-02-04 03:18:28 +0000791 hash = utils.hash(method)
lmr03ba22e2009-10-23 12:07:44 +0000792
lmr6f669ce2009-05-31 19:02:42 +0000793 while size > 0:
794 if chunksize > size:
795 chunksize = size
796 data = f.read(chunksize)
797 if len(data) == 0:
798 logging.debug("Nothing left to read but size=%d" % size)
799 break
lmr03ba22e2009-10-23 12:07:44 +0000800 hash.update(data)
lmr6f669ce2009-05-31 19:02:42 +0000801 size -= len(data)
802 f.close()
lmr03ba22e2009-10-23 12:07:44 +0000803 return hash.hexdigest()
804
805
806def get_hash_from_file(hash_path, dvd_basename):
807 """
808 Get the a hash from a given DVD image from a hash file
809 (Hash files are usually named MD5SUM or SHA1SUM and are located inside the
810 download directories of the DVDs)
811
812 @param hash_path: Local path to a hash file.
813 @param cd_image: Basename of a CD image
814 """
815 hash_file = open(hash_path, 'r')
816 for line in hash_file.readlines():
817 if dvd_basename in line:
818 return line.split()[0]
819
820
821def unmap_url_cache(cachedir, url, expected_hash, method="md5"):
822 """
823 Downloads a file from a URL to a cache directory. If the file is already
824 at the expected position and has the expected hash, let's not download it
825 again.
826
827 @param cachedir: Directory that might hold a copy of the file we want to
828 download.
829 @param url: URL for the file we want to download.
830 @param expected_hash: Hash string that we expect the file downloaded to
831 have.
832 @param method: Method used to calculate the hash string (md5, sha1).
833 """
834 # Let's convert cachedir to a canonical path, if it's not already
835 cachedir = os.path.realpath(cachedir)
836 if not os.path.isdir(cachedir):
837 try:
838 os.makedirs(cachedir)
839 except:
840 raise ValueError('Could not create cache directory %s' % cachedir)
841 file_from_url = os.path.basename(url)
842 file_local_path = os.path.join(cachedir, file_from_url)
843
844 file_hash = None
845 failure_counter = 0
846 while not file_hash == expected_hash:
847 if os.path.isfile(file_local_path):
lmr6f80e7a2010-02-04 03:18:28 +0000848 file_hash = hash_file(file_local_path, method)
lmr03ba22e2009-10-23 12:07:44 +0000849 if file_hash == expected_hash:
850 # File is already at the expected position and ready to go
851 src = file_from_url
852 else:
853 # Let's download the package again, it's corrupted...
854 logging.error("Seems that file %s is corrupted, trying to "
855 "download it again" % file_from_url)
856 src = url
857 failure_counter += 1
858 else:
859 # File is not there, let's download it
860 src = url
861 if failure_counter > 1:
862 raise EnvironmentError("Consistently failed to download the "
863 "package %s. Aborting further download "
864 "attempts. This might mean either the "
865 "network connection has problems or the "
866 "expected hash string that was determined "
867 "for this file is wrong" % file_from_url)
868 file_path = utils.unmap_url(cachedir, src, cachedir)
869
870 return file_path
lmr43beef12009-12-11 21:03:36 +0000871
872
873def run_tests(test_list, job):
874 """
875 Runs the sequence of KVM tests based on the list of dictionaries
876 generated by the configuration system, handling dependencies.
877
878 @param test_list: List with all dictionary test parameters.
879 @param job: Autotest job object.
880
881 @return: True, if all tests ran passed, False if any of them failed.
882 """
883 status_dict = {}
884
885 failed = False
886 for dict in test_list:
887 if dict.get("skip") == "yes":
888 continue
889 dependencies_satisfied = True
890 for dep in dict.get("depend"):
891 for test_name in status_dict.keys():
892 if not dep in test_name:
893 continue
894 if not status_dict[test_name]:
895 dependencies_satisfied = False
896 break
897 if dependencies_satisfied:
898 test_iterations = int(dict.get("iterations", 1))
lmr7da99b22010-01-12 22:30:28 +0000899 test_tag = dict.get("shortname")
900 # Setting up kvm_stat profiling during test execution.
901 # We don't need kvm_stat profiling on the build tests.
902 if "build" in test_tag:
903 # None because it's the default value on the base_test class
904 # and the value None is specifically checked there.
905 profile = None
906 else:
907 profile = True
908
909 if profile:
910 job.profilers.add('kvm_stat')
911 # We need only one execution, profiled, hence we're passing
912 # the profile_only parameter to job.run_test().
913 current_status = job.run_test("kvm", params=dict, tag=test_tag,
914 iterations=test_iterations,
915 profile_only=profile)
916 if profile:
917 job.profilers.delete('kvm_stat')
918
lmr43beef12009-12-11 21:03:36 +0000919 if not current_status:
920 failed = True
921 else:
922 current_status = False
923 status_dict[dict.get("name")] = current_status
924
925 return not failed
926
927
928def create_report(report_dir, results_dir):
929 """
930 Creates a neatly arranged HTML results report in the results dir.
931
932 @param report_dir: Directory where the report script is located.
933 @param results_dir: Directory where the results will be output.
934 """
935 reporter = os.path.join(report_dir, 'html_report.py')
936 html_file = os.path.join(results_dir, 'results.html')
937 os.system('%s -r %s -f %s -R' % (reporter, results_dir, html_file))
lmr31af3a12010-01-18 16:46:52 +0000938
939
940def get_full_pci_id(pci_id):
941 """
942 Get full PCI ID of pci_id.
943
944 @param pci_id: PCI ID of a device.
945 """
946 cmd = "lspci -D | awk '/%s/ {print $1}'" % pci_id
947 status, full_id = commands.getstatusoutput(cmd)
948 if status != 0:
949 return None
950 return full_id
951
952
953def get_vendor_from_pci_id(pci_id):
954 """
955 Check out the device vendor ID according to pci_id.
956
957 @param pci_id: PCI ID of a device.
958 """
959 cmd = "lspci -n | awk '/%s/ {print $3}'" % pci_id
960 return re.sub(":", " ", commands.getoutput(cmd))
961
962
lmr84154412010-02-03 18:34:43 +0000963class KvmLoggingConfig(logging_config.LoggingConfig):
964 """
965 Used with the sole purpose of providing convenient logging setup
966 for the KVM test auxiliary programs.
967 """
968 def configure_logging(self, results_dir=None, verbose=False):
969 super(KvmLoggingConfig, self).configure_logging(use_console=True,
970 verbose=verbose)
971
972
lmr31af3a12010-01-18 16:46:52 +0000973class PciAssignable(object):
974 """
975 Request PCI assignable devices on host. It will check whether to request
976 PF (physical Functions) or VF (Virtual Functions).
977 """
lmr83d3b1a2010-01-19 08:14:36 +0000978 def __init__(self, type="vf", driver=None, driver_option=None,
lmr31af3a12010-01-18 16:46:52 +0000979 names=None, devices_requested=None):
980 """
981 Initialize parameter 'type' which could be:
lmr83d3b1a2010-01-19 08:14:36 +0000982 vf: Virtual Functions
983 pf: Physical Function (actual hardware)
lmr31af3a12010-01-18 16:46:52 +0000984 mixed: Both includes VFs and PFs
985
986 If pass through Physical NIC cards, we need to specify which devices
987 to be assigned, e.g. 'eth1 eth2'.
988
989 If pass through Virtual Functions, we need to specify how many vfs
990 are going to be assigned, e.g. passthrough_count = 8 and max_vfs in
991 config file.
992
993 @param type: PCI device type.
994 @param driver: Kernel module for the PCI assignable device.
995 @param driver_option: Module option to specify the maximum number of
996 VFs (eg 'max_vfs=7')
997 @param names: Physical NIC cards correspondent network interfaces,
998 e.g.'eth1 eth2 ...'
999 @param devices_requested: Number of devices being requested.
1000 """
1001 self.type = type
1002 self.driver = driver
1003 self.driver_option = driver_option
1004 if names:
1005 self.name_list = names.split()
1006 if devices_requested:
1007 self.devices_requested = int(devices_requested)
1008 else:
1009 self.devices_requested = None
1010
1011
1012 def _get_pf_pci_id(self, name, search_str):
1013 """
1014 Get the PF PCI ID according to name.
1015
1016 @param name: Name of the PCI device.
1017 @param search_str: Search string to be used on lspci.
1018 """
1019 cmd = "ethtool -i %s | awk '/bus-info/ {print $2}'" % name
1020 s, pci_id = commands.getstatusoutput(cmd)
1021 if not (s or "Cannot get driver information" in pci_id):
1022 return pci_id[5:]
1023 cmd = "lspci | awk '/%s/ {print $1}'" % search_str
1024 pci_ids = [id for id in commands.getoutput(cmd).splitlines()]
1025 nic_id = int(re.search('[0-9]+', name).group(0))
1026 if (len(pci_ids) - 1) < nic_id:
1027 return None
1028 return pci_ids[nic_id]
1029
1030
1031 def _release_dev(self, pci_id):
1032 """
1033 Release a single PCI device.
1034
1035 @param pci_id: PCI ID of a given PCI device.
1036 """
1037 base_dir = "/sys/bus/pci"
1038 full_id = get_full_pci_id(pci_id)
1039 vendor_id = get_vendor_from_pci_id(pci_id)
1040 drv_path = os.path.join(base_dir, "devices/%s/driver" % full_id)
1041 if 'pci-stub' in os.readlink(drv_path):
1042 cmd = "echo '%s' > %s/new_id" % (vendor_id, drv_path)
1043 if os.system(cmd):
1044 return False
1045
1046 stub_path = os.path.join(base_dir, "drivers/pci-stub")
1047 cmd = "echo '%s' > %s/unbind" % (full_id, stub_path)
1048 if os.system(cmd):
1049 return False
1050
1051 driver = self.dev_drivers[pci_id]
1052 cmd = "echo '%s' > %s/bind" % (full_id, driver)
1053 if os.system(cmd):
1054 return False
1055
1056 return True
1057
1058
1059 def get_vf_devs(self):
1060 """
1061 Catch all VFs PCI IDs.
1062
1063 @return: List with all PCI IDs for the Virtual Functions avaliable
1064 """
1065 if not self.sr_iov_setup():
1066 return []
1067
1068 cmd = "lspci | awk '/Virtual Function/ {print $1}'"
1069 return commands.getoutput(cmd).split()
1070
1071
1072 def get_pf_devs(self):
1073 """
1074 Catch all PFs PCI IDs.
1075
1076 @return: List with all PCI IDs for the physical hardware requested
1077 """
1078 pf_ids = []
1079 for name in self.name_list:
1080 pf_id = self._get_pf_pci_id(name, "Ethernet")
1081 if not pf_id:
1082 continue
1083 pf_ids.append(pf_id)
1084 return pf_ids
1085
1086
1087 def get_devs(self, count):
1088 """
1089 Check out all devices' PCI IDs according to their name.
1090
1091 @param count: count number of PCI devices needed for pass through
1092 @return: a list of all devices' PCI IDs
1093 """
lmr83d3b1a2010-01-19 08:14:36 +00001094 if self.type == "vf":
lmr31af3a12010-01-18 16:46:52 +00001095 vf_ids = self.get_vf_devs()
lmr83d3b1a2010-01-19 08:14:36 +00001096 elif self.type == "pf":
lmr31af3a12010-01-18 16:46:52 +00001097 vf_ids = self.get_pf_devs()
1098 elif self.type == "mixed":
1099 vf_ids = self.get_vf_devs()
1100 vf_ids.extend(self.get_pf_devs())
1101 return vf_ids[0:count]
1102
1103
1104 def get_vfs_count(self):
1105 """
1106 Get VFs count number according to lspci.
1107 """
1108 cmd = "lspci | grep 'Virtual Function' | wc -l"
1109 # For each VF we'll see 2 prints of 'Virtual Function', so let's
1110 # divide the result per 2
1111 return int(commands.getoutput(cmd)) / 2
1112
1113
1114 def check_vfs_count(self):
1115 """
1116 Check VFs count number according to the parameter driver_options.
1117 """
1118 return (self.get_vfs_count == self.devices_requested)
1119
1120
1121 def is_binded_to_stub(self, full_id):
1122 """
1123 Verify whether the device with full_id is already binded to pci-stub.
1124
1125 @param full_id: Full ID for the given PCI device
1126 """
1127 base_dir = "/sys/bus/pci"
1128 stub_path = os.path.join(base_dir, "drivers/pci-stub")
1129 if os.path.exists(os.path.join(stub_path, full_id)):
1130 return True
1131 return False
1132
1133
1134 def sr_iov_setup(self):
1135 """
1136 Ensure the PCI device is working in sr_iov mode.
1137
1138 Check if the PCI hardware device drive is loaded with the appropriate,
1139 parameters (number of VFs), and if it's not, perform setup.
1140
1141 @return: True, if the setup was completed successfuly, False otherwise.
1142 """
1143 re_probe = False
1144 s, o = commands.getstatusoutput('lsmod | grep %s' % self.driver)
1145 if s:
1146 re_probe = True
1147 elif not self.check_vfs_count():
1148 os.system("modprobe -r %s" % self.driver)
1149 re_probe = True
1150
1151 # Re-probe driver with proper number of VFs
1152 if re_probe:
1153 cmd = "modprobe %s %s" % (self.driver, self.driver_option)
1154 s, o = commands.getstatusoutput(cmd)
1155 if s:
1156 return False
1157 if not self.check_vfs_count():
1158 return False
1159 return True
1160
1161
1162 def request_devs(self):
1163 """
1164 Implement setup process: unbind the PCI device and then bind it
1165 to the pci-stub driver.
1166
1167 @return: a list of successfully requested devices' PCI IDs.
1168 """
1169 base_dir = "/sys/bus/pci"
1170 stub_path = os.path.join(base_dir, "drivers/pci-stub")
1171
1172 self.pci_ids = self.get_devs(self.devices_requested)
1173 logging.debug("The following pci_ids were found: %s" % self.pci_ids)
1174 requested_pci_ids = []
1175 self.dev_drivers = {}
1176
1177 # Setup all devices specified for assignment to guest
1178 for pci_id in self.pci_ids:
1179 full_id = get_full_pci_id(pci_id)
1180 if not full_id:
1181 continue
1182 drv_path = os.path.join(base_dir, "devices/%s/driver" % full_id)
1183 dev_prev_driver= os.path.realpath(os.path.join(drv_path,
1184 os.readlink(drv_path)))
1185 self.dev_drivers[pci_id] = dev_prev_driver
1186
1187 # Judge whether the device driver has been binded to stub
1188 if not self.is_binded_to_stub(full_id):
1189 logging.debug("Binding device %s to stub" % full_id)
1190 vendor_id = get_vendor_from_pci_id(pci_id)
1191 stub_new_id = os.path.join(stub_path, 'new_id')
1192 unbind_dev = os.path.join(drv_path, 'unbind')
1193 stub_bind = os.path.join(stub_path, 'bind')
1194
1195 info_write_to_files = [(vendor_id, stub_new_id),
1196 (full_id, unbind_dev),
1197 (full_id, stub_bind)]
1198
1199 for content, file in info_write_to_files:
1200 try:
1201 utils.open_write_close(content, file)
1202 except IOError:
1203 logging.debug("Failed to write %s to file %s" %
1204 (content, file))
1205 continue
1206
1207 if not self.is_binded_to_stub(full_id):
1208 logging.error("Binding device %s to stub failed" %
1209 pci_id)
1210 continue
1211 else:
1212 logging.debug("Device %s already binded to stub" % pci_id)
1213 requested_pci_ids.append(pci_id)
1214 self.pci_ids = requested_pci_ids
1215 return self.pci_ids
1216
1217
1218 def release_devs(self):
1219 """
1220 Release all PCI devices currently assigned to VMs back to the
1221 virtualization host.
1222 """
1223 try:
1224 for pci_id in self.dev_drivers:
1225 if not self._release_dev(pci_id):
1226 logging.error("Failed to release device %s to host" %
1227 pci_id)
1228 else:
1229 logging.info("Released device %s successfully" % pci_id)
1230 except:
1231 return