blob: 0da7015122ed1ab56d0842c202f5a99fa62a1f3b [file] [log] [blame]
lmr6f669ce2009-05-31 19:02:42 +00001"""
2KVM test utility functions.
3
4@copyright: 2008-2009 Red Hat Inc.
5"""
6
lmr6f80e7a2010-02-04 03:18:28 +00007import thread, subprocess, time, string, random, socket, os, signal
lmr03ba22e2009-10-23 12:07:44 +00008import select, re, logging, commands, cPickle, pty
lmrb635b862009-09-10 14:53:21 +00009from autotest_lib.client.bin import utils
lmr84154412010-02-03 18:34:43 +000010from autotest_lib.client.common_lib import error, logging_config
lmrb635b862009-09-10 14:53:21 +000011import kvm_subprocess
12
lmr6f669ce2009-05-31 19:02:42 +000013
lmre8a66dd2009-09-15 19:51:07 +000014def dump_env(obj, filename):
15 """
16 Dump KVM test environment to a file.
17
18 @param filename: Path to a file where the environment will be dumped to.
19 """
20 file = open(filename, "w")
21 cPickle.dump(obj, file)
22 file.close()
23
24
lmr8b5400c2010-03-23 15:32:29 +000025def load_env(filename, default={}):
lmre8a66dd2009-09-15 19:51:07 +000026 """
27 Load KVM test environment from an environment file.
28
29 @param filename: Path to a file where the environment was dumped to.
30 """
31 try:
32 file = open(filename, "r")
lmr8b5400c2010-03-23 15:32:29 +000033 obj = cPickle.load(file)
34 file.close()
35 return obj
36 # Almost any exception can be raised during unpickling, so let's catch
37 # them all
lmre8a66dd2009-09-15 19:51:07 +000038 except:
39 return default
lmre8a66dd2009-09-15 19:51:07 +000040
41
lmr6f669ce2009-05-31 19:02:42 +000042def get_sub_dict(dict, name):
43 """
44 Return a "sub-dict" corresponding to a specific object.
45
46 Operate on a copy of dict: for each key that ends with the suffix
47 "_" + name, strip the suffix from the key, and set the value of
48 the stripped key to that of the key. Return the resulting dict.
49
50 @param name: Suffix of the key we want to set the value.
51 """
52 suffix = "_" + name
53 new_dict = dict.copy()
54 for key in dict.keys():
55 if key.endswith(suffix):
56 new_key = key.split(suffix)[0]
57 new_dict[new_key] = dict[key]
58 return new_dict
59
60
61def get_sub_dict_names(dict, keyword):
62 """
63 Return a list of "sub-dict" names that may be extracted with get_sub_dict.
64
65 This function may be modified to change the behavior of all functions that
66 deal with multiple objects defined in dicts (e.g. VMs, images, NICs).
67
68 @param keyword: A key in dict (e.g. "vms", "images", "nics").
69 """
70 names = dict.get(keyword)
71 if names:
72 return names.split()
73 else:
74 return []
75
76
lmrac5089b2009-08-13 04:05:47 +000077# Functions related to MAC/IP addresses
78
79def mac_str_to_int(addr):
80 """
81 Convert MAC address string to integer.
82
83 @param addr: String representing the MAC address.
84 """
85 return sum(int(s, 16) * 256 ** i
86 for i, s in enumerate(reversed(addr.split(":"))))
87
88
89def mac_int_to_str(addr):
90 """
91 Convert MAC address integer to string.
92
93 @param addr: Integer representing the MAC address.
94 """
95 return ":".join("%02x" % (addr >> 8 * i & 0xFF)
96 for i in reversed(range(6)))
97
98
99def ip_str_to_int(addr):
100 """
101 Convert IP address string to integer.
102
103 @param addr: String representing the IP address.
104 """
105 return sum(int(s) * 256 ** i
106 for i, s in enumerate(reversed(addr.split("."))))
107
108
109def ip_int_to_str(addr):
110 """
111 Convert IP address integer to string.
112
113 @param addr: Integer representing the IP address.
114 """
115 return ".".join(str(addr >> 8 * i & 0xFF)
116 for i in reversed(range(4)))
117
118
119def offset_mac(base, offset):
120 """
121 Add offset to a given MAC address.
122
123 @param base: String representing a MAC address.
124 @param offset: Offset to add to base (integer)
125 @return: A string representing the offset MAC address.
126 """
127 return mac_int_to_str(mac_str_to_int(base) + offset)
128
129
130def offset_ip(base, offset):
131 """
132 Add offset to a given IP address.
133
134 @param base: String representing an IP address.
135 @param offset: Offset to add to base (integer)
136 @return: A string representing the offset IP address.
137 """
138 return ip_int_to_str(ip_str_to_int(base) + offset)
139
140
141def get_mac_ip_pair_from_dict(dict):
142 """
143 Fetch a MAC-IP address pair from dict and return it.
144
145 The parameters in dict are expected to conform to a certain syntax.
146 Typical usage may be:
147
148 address_ranges = r1 r2 r3
149
150 address_range_base_mac_r1 = 55:44:33:22:11:00
151 address_range_base_ip_r1 = 10.0.0.0
152 address_range_size_r1 = 16
153
154 address_range_base_mac_r2 = 55:44:33:22:11:40
155 address_range_base_ip_r2 = 10.0.0.60
156 address_range_size_r2 = 25
157
158 address_range_base_mac_r3 = 55:44:33:22:12:10
159 address_range_base_ip_r3 = 10.0.1.20
160 address_range_size_r3 = 230
161
162 address_index = 0
163
164 All parameters except address_index specify a MAC-IP address pool. The
165 pool consists of several MAC-IP address ranges.
166 address_index specified the index of the desired MAC-IP pair from the pool.
167
168 @param dict: The dictionary from which to fetch the addresses.
169 """
170 index = int(dict.get("address_index", 0))
171 for mac_range_name in get_sub_dict_names(dict, "address_ranges"):
172 mac_range_params = get_sub_dict(dict, mac_range_name)
173 mac_base = mac_range_params.get("address_range_base_mac")
174 ip_base = mac_range_params.get("address_range_base_ip")
175 size = int(mac_range_params.get("address_range_size", 1))
176 if index < size:
177 return (mac_base and offset_mac(mac_base, index),
178 ip_base and offset_ip(ip_base, index))
179 index -= size
180 return (None, None)
181
182
lmrf3d3e522010-03-23 16:38:12 +0000183def get_sub_pool(dict, piece, num_pieces):
184 """
185 Split a MAC-IP pool and return a single requested piece.
186
187 For example, get_sub_pool(dict, 0, 3) will split the pool in 3 pieces and
188 return a dict representing the first piece.
189
190 @param dict: A dict that contains pool parameters.
191 @param piece: The index of the requested piece. Should range from 0 to
192 num_pieces - 1.
193 @param num_pieces: The total number of pieces.
194 @return: A copy of dict, modified to describe the requested sub-pool.
195 """
196 range_dicts = [get_sub_dict(dict, name) for name in
197 get_sub_dict_names(dict, "address_ranges")]
198 if not range_dicts:
199 return dict
200 ranges = [[d.get("address_range_base_mac"),
201 d.get("address_range_base_ip"),
202 int(d.get("address_range_size", 1))] for d in range_dicts]
203 total_size = sum(r[2] for r in ranges)
204 base = total_size * piece / num_pieces
205 size = total_size * (piece + 1) / num_pieces - base
206
207 # Find base of current sub-pool
208 for i in range(len(ranges)):
209 r = ranges[i]
210 if base < r[2]:
211 r[0] = r[0] and offset_mac(r[0], base)
212 r[1] = r[1] and offset_ip(r[1], base)
213 r[2] -= base
214 break
215 base -= r[2]
216
217 # Collect ranges up to end of current sub-pool
218 new_ranges = []
219 for i in range(i, len(ranges)):
220 r = ranges[i]
221 new_ranges.append(r)
222 if size <= r[2]:
223 r[2] = size
224 break
225 size -= r[2]
226
227 # Write new dict
228 new_dict = dict.copy()
229 new_dict["address_ranges"] = " ".join("r%d" % i for i in
230 range(len(new_ranges)))
231 for i in range(len(new_ranges)):
232 new_dict["address_range_base_mac_r%d" % i] = new_ranges[i][0]
233 new_dict["address_range_base_ip_r%d" % i] = new_ranges[i][1]
234 new_dict["address_range_size_r%d" % i] = new_ranges[i][2]
235 return new_dict
236
237
lmr53d3e932009-09-09 21:56:18 +0000238def verify_ip_address_ownership(ip, macs, timeout=10.0):
lmree90dd92009-08-13 04:13:39 +0000239 """
lmr53d3e932009-09-09 21:56:18 +0000240 Use arping and the ARP cache to make sure a given IP address belongs to one
241 of the given MAC addresses.
lmree90dd92009-08-13 04:13:39 +0000242
243 @param ip: An IP address.
244 @param macs: A list or tuple of MAC addresses.
245 @return: True iff ip is assigned to a MAC address in macs.
246 """
lmr53d3e932009-09-09 21:56:18 +0000247 # Compile a regex that matches the given IP address and any of the given
248 # MAC addresses
lmree90dd92009-08-13 04:13:39 +0000249 mac_regex = "|".join("(%s)" % mac for mac in macs)
lmre35507b2009-10-28 16:53:08 +0000250 regex = re.compile(r"\b%s\b.*\b(%s)\b" % (ip, mac_regex), re.IGNORECASE)
lmree90dd92009-08-13 04:13:39 +0000251
lmr53d3e932009-09-09 21:56:18 +0000252 # Check the ARP cache
253 o = commands.getoutput("/sbin/arp -n")
lmre35507b2009-10-28 16:53:08 +0000254 if regex.search(o):
lmree90dd92009-08-13 04:13:39 +0000255 return True
256
lmr53d3e932009-09-09 21:56:18 +0000257 # Get the name of the bridge device for arping
258 o = commands.getoutput("/sbin/ip route get %s" % ip)
259 dev = re.findall("dev\s+\S+", o, re.IGNORECASE)
260 if not dev:
261 return False
262 dev = dev[0].split()[-1]
263
264 # Send an ARP request
265 o = commands.getoutput("/sbin/arping -f -c 3 -I %s %s" % (dev, ip))
lmre35507b2009-10-28 16:53:08 +0000266 return bool(regex.search(o))
lmree90dd92009-08-13 04:13:39 +0000267
268
lmr6f669ce2009-05-31 19:02:42 +0000269# Functions for working with the environment (a dict-like object)
270
271def is_vm(obj):
272 """
273 Tests whether a given object is a VM object.
274
275 @param obj: Python object (pretty much everything on python).
276 """
277 return obj.__class__.__name__ == "VM"
278
279
280def env_get_all_vms(env):
281 """
282 Return a list of all VM objects on a given environment.
283
284 @param env: Dictionary with environment items.
285 """
286 vms = []
287 for obj in env.values():
288 if is_vm(obj):
289 vms.append(obj)
290 return vms
291
292
293def env_get_vm(env, name):
294 """
295 Return a VM object by its name.
296
297 @param name: VM name.
298 """
299 return env.get("vm__%s" % name)
300
301
302def env_register_vm(env, name, vm):
303 """
304 Register a given VM in a given env.
305
306 @param env: Environment where we will register the VM.
307 @param name: VM name.
308 @param vm: VM object.
309 """
310 env["vm__%s" % name] = vm
311
312
313def env_unregister_vm(env, name):
314 """
315 Remove a given VM from a given env.
316
317 @param env: Environment where we will un-register the VM.
318 @param name: VM name.
319 """
320 del env["vm__%s" % name]
321
322
323# Utility functions for dealing with external processes
324
325def pid_exists(pid):
326 """
327 Return True if a given PID exists.
328
329 @param pid: Process ID number.
330 """
331 try:
332 os.kill(pid, 0)
333 return True
334 except:
335 return False
336
337
338def safe_kill(pid, signal):
339 """
340 Attempt to send a signal to a given process that may or may not exist.
341
342 @param signal: Signal number.
343 """
344 try:
345 os.kill(pid, signal)
346 return True
347 except:
348 return False
349
350
lmr1aeaefb2009-09-09 22:12:49 +0000351def kill_process_tree(pid, sig=signal.SIGKILL):
352 """Signal a process and all of its children.
353
354 If the process does not exist -- return.
355
356 @param pid: The pid of the process to signal.
357 @param sig: The signal to send to the processes.
358 """
359 if not safe_kill(pid, signal.SIGSTOP):
360 return
361 children = commands.getoutput("ps --ppid=%d -o pid=" % pid).split()
362 for child in children:
363 kill_process_tree(int(child), sig)
364 safe_kill(pid, sig)
365 safe_kill(pid, signal.SIGCONT)
366
367
lmr117bbcf2009-09-15 07:12:39 +0000368def get_latest_kvm_release_tag(release_listing):
lmr3f0b0cc2009-06-10 02:25:23 +0000369 """
370 Fetches the latest release tag for KVM.
371
lmr117bbcf2009-09-15 07:12:39 +0000372 @param release_listing: URL that contains a list of the Source Forge
373 KVM project files.
lmr3f0b0cc2009-06-10 02:25:23 +0000374 """
375 try:
lmr117bbcf2009-09-15 07:12:39 +0000376 release_page = utils.urlopen(release_listing)
377 data = release_page.read()
378 release_page.close()
lmr8ea274b2009-07-06 13:42:35 +0000379 rx = re.compile("kvm-(\d+).tar.gz", re.IGNORECASE)
lmr3f0b0cc2009-06-10 02:25:23 +0000380 matches = rx.findall(data)
lmr32525382009-08-10 13:53:37 +0000381 # In all regexp matches to something that looks like a release tag,
382 # get the largest integer. That will be our latest release tag.
383 latest_tag = max(int(x) for x in matches)
384 return str(latest_tag)
lmr3f0b0cc2009-06-10 02:25:23 +0000385 except Exception, e:
386 message = "Could not fetch latest KVM release tag: %s" % str(e)
387 logging.error(message)
388 raise error.TestError(message)
389
390
391def get_git_branch(repository, branch, srcdir, commit=None, lbranch=None):
392 """
393 Retrieves a given git code repository.
394
395 @param repository: Git repository URL
396 """
lmr160e9942010-06-09 14:09:19 +0000397 logging.info("Fetching git [REP '%s' BRANCH '%s' COMMIT '%s'] -> %s",
lmr3f0b0cc2009-06-10 02:25:23 +0000398 repository, branch, commit, srcdir)
399 if not os.path.exists(srcdir):
400 os.makedirs(srcdir)
401 os.chdir(srcdir)
402
403 if os.path.exists(".git"):
404 utils.system("git reset --hard")
405 else:
406 utils.system("git init")
407
408 if not lbranch:
409 lbranch = branch
410
411 utils.system("git fetch -q -f -u -t %s %s:%s" %
412 (repository, branch, lbranch))
413 utils.system("git checkout %s" % lbranch)
414 if commit:
415 utils.system("git checkout %s" % commit)
416
417 h = utils.system_output('git log --pretty=format:"%H" -1')
lmr05cec0f2010-01-26 17:50:29 +0000418 try:
419 desc = "tag %s" % utils.system_output("git describe")
420 except error.CmdError:
421 desc = "no tag found"
422
lmr3f0b0cc2009-06-10 02:25:23 +0000423 logging.info("Commit hash for %s is %s (%s)" % (repository, h.strip(),
424 desc))
425 return srcdir
426
427
lmr3f0b0cc2009-06-10 02:25:23 +0000428def check_kvm_source_dir(source_dir):
429 """
430 Inspects the kvm source directory and verifies its disposition. In some
431 occasions build may be dependant on the source directory disposition.
432 The reason why the return codes are numbers is that we might have more
433 changes on the source directory layout, so it's not scalable to just use
434 strings like 'old_repo', 'new_repo' and such.
435
436 @param source_dir: Source code path that will be inspected.
437 """
438 os.chdir(source_dir)
439 has_qemu_dir = os.path.isdir('qemu')
440 has_kvm_dir = os.path.isdir('kvm')
441 if has_qemu_dir and not has_kvm_dir:
442 logging.debug("qemu directory detected, source dir layout 1")
443 return 1
444 if has_kvm_dir and not has_qemu_dir:
445 logging.debug("kvm directory detected, source dir layout 2")
446 return 2
447 else:
448 raise error.TestError("Unknown source dir layout, cannot proceed.")
449
450
lmrf9349c32009-07-23 01:44:24 +0000451# The following are functions used for SSH, SCP and Telnet communication with
452# guests.
lmr6f669ce2009-05-31 19:02:42 +0000453
454def remote_login(command, password, prompt, linesep="\n", timeout=10):
455 """
456 Log into a remote host (guest) using SSH or Telnet. Run the given command
457 using kvm_spawn and provide answers to the questions asked. If timeout
458 expires while waiting for output from the child (e.g. a password prompt
459 or a shell prompt) -- fail.
460
461 @brief: Log into a remote host (guest) using SSH or Telnet.
462
463 @param command: The command to execute (e.g. "ssh root@localhost")
464 @param password: The password to send in reply to a password prompt
465 @param prompt: The shell prompt that indicates a successful login
466 @param linesep: The line separator to send instead of "\\n"
467 (sometimes "\\r\\n" is required)
468 @param timeout: The maximal time duration (in seconds) to wait for each
469 step of the login procedure (i.e. the "Are you sure" prompt, the
470 password prompt, the shell prompt, etc)
471
472 @return Return the kvm_spawn object on success and None on failure.
473 """
lmrdc3a5b12009-07-23 01:40:40 +0000474 sub = kvm_subprocess.kvm_shell_session(command,
475 linesep=linesep,
476 prompt=prompt)
lmr6f669ce2009-05-31 19:02:42 +0000477
478 password_prompt_count = 0
479
lmr8691f422009-07-28 02:52:30 +0000480 logging.debug("Trying to login with command '%s'" % command)
lmr6f669ce2009-05-31 19:02:42 +0000481
482 while True:
483 (match, text) = sub.read_until_last_line_matches(
lmr3ca79fe2009-06-10 19:24:26 +0000484 [r"[Aa]re you sure", r"[Pp]assword:\s*$", r"^\s*[Ll]ogin:\s*$",
lmrba69bc52010-03-25 01:50:09 +0000485 r"[Cc]onnection.*closed", r"[Cc]onnection.*refused",
486 r"[Pp]lease wait", prompt],
lmr6f669ce2009-05-31 19:02:42 +0000487 timeout=timeout, internal_timeout=0.5)
488 if match == 0: # "Are you sure you want to continue connecting"
489 logging.debug("Got 'Are you sure...'; sending 'yes'")
490 sub.sendline("yes")
491 continue
492 elif match == 1: # "password:"
493 if password_prompt_count == 0:
494 logging.debug("Got password prompt; sending '%s'" % password)
495 sub.sendline(password)
496 password_prompt_count += 1
497 continue
498 else:
499 logging.debug("Got password prompt again")
500 sub.close()
501 return None
502 elif match == 2: # "login:"
503 logging.debug("Got unexpected login prompt")
504 sub.close()
505 return None
506 elif match == 3: # "Connection closed"
507 logging.debug("Got 'Connection closed'")
508 sub.close()
509 return None
lmr3ca79fe2009-06-10 19:24:26 +0000510 elif match == 4: # "Connection refused"
lmr0d2ed1f2009-07-01 03:23:18 +0000511 logging.debug("Got 'Connection refused'")
lmr3ca79fe2009-06-10 19:24:26 +0000512 sub.close()
513 return None
lmrba69bc52010-03-25 01:50:09 +0000514 elif match == 5: # "Please wait"
515 logging.debug("Got 'Please wait'")
516 timeout = 30
517 continue
518 elif match == 6: # prompt
lmr6f669ce2009-05-31 19:02:42 +0000519 logging.debug("Got shell prompt -- logged in")
520 return sub
521 else: # match == None
lmr3ca79fe2009-06-10 19:24:26 +0000522 logging.debug("Timeout elapsed or process terminated")
lmr6f669ce2009-05-31 19:02:42 +0000523 sub.close()
524 return None
525
526
lmrc196d492010-05-07 14:14:26 +0000527def remote_scp(command, password, timeout=600, login_timeout=10):
lmr6f669ce2009-05-31 19:02:42 +0000528 """
529 Run the given command using kvm_spawn and provide answers to the questions
530 asked. If timeout expires while waiting for the transfer to complete ,
531 fail. If login_timeout expires while waiting for output from the child
532 (e.g. a password prompt), fail.
533
534 @brief: Transfer files using SCP, given a command line.
535
536 @param command: The command to execute
537 (e.g. "scp -r foobar root@localhost:/tmp/").
538 @param password: The password to send in reply to a password prompt.
539 @param timeout: The time duration (in seconds) to wait for the transfer
540 to complete.
541 @param login_timeout: The maximal time duration (in seconds) to wait for
542 each step of the login procedure (i.e. the "Are you sure" prompt or the
543 password prompt)
544
545 @return: True if the transfer succeeds and False on failure.
546 """
lmrdc3a5b12009-07-23 01:40:40 +0000547 sub = kvm_subprocess.kvm_expect(command)
lmr6f669ce2009-05-31 19:02:42 +0000548
549 password_prompt_count = 0
550 _timeout = login_timeout
lmrc196d492010-05-07 14:14:26 +0000551 end_time = time.time() + timeout
552 logging.debug("Trying to SCP with command '%s', timeout %ss", command,
553 timeout)
lmr6f669ce2009-05-31 19:02:42 +0000554
555 while True:
lmrc196d492010-05-07 14:14:26 +0000556 if end_time <= time.time():
557 logging.debug("SCP transfer timed out")
558 sub.close()
559 return False
lmr6f669ce2009-05-31 19:02:42 +0000560 (match, text) = sub.read_until_last_line_matches(
lmrc196d492010-05-07 14:14:26 +0000561 [r"[Aa]re you sure", r"[Pp]assword:\s*$", r"lost connection",
562 r"Exit status", r"stalled"],
lmr6f669ce2009-05-31 19:02:42 +0000563 timeout=_timeout, internal_timeout=0.5)
564 if match == 0: # "Are you sure you want to continue connecting"
565 logging.debug("Got 'Are you sure...'; sending 'yes'")
566 sub.sendline("yes")
567 continue
568 elif match == 1: # "password:"
569 if password_prompt_count == 0:
570 logging.debug("Got password prompt; sending '%s'" % password)
571 sub.sendline(password)
572 password_prompt_count += 1
573 _timeout = timeout
574 continue
575 else:
576 logging.debug("Got password prompt again")
577 sub.close()
578 return False
579 elif match == 2: # "lost connection"
580 logging.debug("Got 'lost connection'")
581 sub.close()
582 return False
lmrc196d492010-05-07 14:14:26 +0000583 elif match == 3: # "Exit status"
584 sub.close()
585 if "Exit status 0" in text:
586 logging.debug("SCP command completed successfully")
587 return True
588 else:
589 logging.debug("SCP command fail with exit status %s" % text)
590 return False
591 elif match == 4: # "stalled"
592 logging.debug("SCP connection is stalled")
593 continue
594
lmr6f669ce2009-05-31 19:02:42 +0000595 else: # match == None
lmrc196d492010-05-07 14:14:26 +0000596 if sub.is_alive():
597 continue
598 logging.debug("SCP process terminated")
lmrdc3a5b12009-07-23 01:40:40 +0000599 status = sub.get_status()
lmr6f669ce2009-05-31 19:02:42 +0000600 sub.close()
lmrdc3a5b12009-07-23 01:40:40 +0000601 return status == 0
lmr6f669ce2009-05-31 19:02:42 +0000602
603
604def scp_to_remote(host, port, username, password, local_path, remote_path,
lmrc196d492010-05-07 14:14:26 +0000605 timeout=600):
lmr6f669ce2009-05-31 19:02:42 +0000606 """
607 Copy files to a remote host (guest).
608
lmr912c74b2009-08-17 20:43:27 +0000609 @param host: Hostname or IP address
610 @param username: Username (if required)
611 @param password: Password (if required)
lmr6f669ce2009-05-31 19:02:42 +0000612 @param local_path: Path on the local machine where we are copying from
613 @param remote_path: Path on the remote machine where we are copying to
614 @param timeout: Time in seconds that we will wait before giving up to
615 copy the files.
616
617 @return: True on success and False on failure.
618 """
lmrc196d492010-05-07 14:14:26 +0000619 command = ("scp -v -o UserKnownHostsFile=/dev/null "
lmr1b7e1702009-11-10 16:30:39 +0000620 "-o PreferredAuthentications=password -r -P %s %s %s@%s:%s" %
lmrd16a67d2009-06-10 19:52:59 +0000621 (port, local_path, username, host, remote_path))
lmr6f669ce2009-05-31 19:02:42 +0000622 return remote_scp(command, password, timeout)
623
624
625def scp_from_remote(host, port, username, password, remote_path, local_path,
lmrc196d492010-05-07 14:14:26 +0000626 timeout=600):
lmr6f669ce2009-05-31 19:02:42 +0000627 """
628 Copy files from a remote host (guest).
629
lmr912c74b2009-08-17 20:43:27 +0000630 @param host: Hostname or IP address
631 @param username: Username (if required)
632 @param password: Password (if required)
lmr6f669ce2009-05-31 19:02:42 +0000633 @param local_path: Path on the local machine where we are copying from
634 @param remote_path: Path on the remote machine where we are copying to
635 @param timeout: Time in seconds that we will wait before giving up to copy
636 the files.
637
638 @return: True on success and False on failure.
639 """
lmrc196d492010-05-07 14:14:26 +0000640 command = ("scp -v -o UserKnownHostsFile=/dev/null "
lmr1b7e1702009-11-10 16:30:39 +0000641 "-o PreferredAuthentications=password -r -P %s %s@%s:%s %s" %
lmrd16a67d2009-06-10 19:52:59 +0000642 (port, username, host, remote_path, local_path))
lmr6f669ce2009-05-31 19:02:42 +0000643 return remote_scp(command, password, timeout)
644
645
lmr59f9e2d2009-10-05 18:47:16 +0000646def ssh(host, port, username, password, prompt, linesep="\n", timeout=10):
lmr6f669ce2009-05-31 19:02:42 +0000647 """
648 Log into a remote host (guest) using SSH.
649
lmr912c74b2009-08-17 20:43:27 +0000650 @param host: Hostname or IP address
651 @param username: Username (if required)
652 @param password: Password (if required)
653 @param prompt: Shell prompt (regular expression)
lmr6f669ce2009-05-31 19:02:42 +0000654 @timeout: Time in seconds that we will wait before giving up on logging
655 into the host.
656
657 @return: kvm_spawn object on success and None on failure.
658 """
lmr1b7e1702009-11-10 16:30:39 +0000659 command = ("ssh -o UserKnownHostsFile=/dev/null "
660 "-o PreferredAuthentications=password -p %s %s@%s" %
lmrd16a67d2009-06-10 19:52:59 +0000661 (port, username, host))
lmr59f9e2d2009-10-05 18:47:16 +0000662 return remote_login(command, password, prompt, linesep, timeout)
lmr6f669ce2009-05-31 19:02:42 +0000663
664
lmr59f9e2d2009-10-05 18:47:16 +0000665def telnet(host, port, username, password, prompt, linesep="\n", timeout=10):
lmr6f669ce2009-05-31 19:02:42 +0000666 """
667 Log into a remote host (guest) using Telnet.
668
lmr912c74b2009-08-17 20:43:27 +0000669 @param host: Hostname or IP address
670 @param username: Username (if required)
671 @param password: Password (if required)
672 @param prompt: Shell prompt (regular expression)
lmr6f669ce2009-05-31 19:02:42 +0000673 @timeout: Time in seconds that we will wait before giving up on logging
674 into the host.
675
676 @return: kvm_spawn object on success and None on failure.
677 """
678 command = "telnet -l %s %s %s" % (username, host, port)
lmr59f9e2d2009-10-05 18:47:16 +0000679 return remote_login(command, password, prompt, linesep, timeout)
lmr6f669ce2009-05-31 19:02:42 +0000680
681
lmr59f9e2d2009-10-05 18:47:16 +0000682def netcat(host, port, username, password, prompt, linesep="\n", timeout=10):
lmr9f6ebf12009-08-17 20:44:10 +0000683 """
684 Log into a remote host (guest) using Netcat.
685
686 @param host: Hostname or IP address
687 @param username: Username (if required)
688 @param password: Password (if required)
689 @param prompt: Shell prompt (regular expression)
690 @timeout: Time in seconds that we will wait before giving up on logging
691 into the host.
692
693 @return: kvm_spawn object on success and None on failure.
694 """
695 command = "nc %s %s" % (host, port)
lmr59f9e2d2009-10-05 18:47:16 +0000696 return remote_login(command, password, prompt, linesep, timeout)
lmr9f6ebf12009-08-17 20:44:10 +0000697
698
lmr6f669ce2009-05-31 19:02:42 +0000699# The following are utility functions related to ports.
700
lmr6f669ce2009-05-31 19:02:42 +0000701def is_port_free(port):
702 """
703 Return True if the given port is available for use.
704
705 @param port: Port number
706 """
707 try:
708 s = socket.socket()
709 #s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
710 s.bind(("localhost", port))
711 free = True
712 except socket.error:
713 free = False
714 s.close()
715 return free
716
717
718def find_free_port(start_port, end_port):
719 """
lmra29a5cb2010-03-18 02:39:34 +0000720 Return a host free port in the range [start_port, end_port].
lmr6f669ce2009-05-31 19:02:42 +0000721
722 @param start_port: First port that will be checked.
723 @param end_port: Port immediately after the last one that will be checked.
724 """
725 for i in range(start_port, end_port):
726 if is_port_free(i):
727 return i
728 return None
729
730
731def find_free_ports(start_port, end_port, count):
732 """
lmra29a5cb2010-03-18 02:39:34 +0000733 Return count of host free ports in the range [start_port, end_port].
lmr6f669ce2009-05-31 19:02:42 +0000734
735 @count: Initial number of ports known to be free in the range.
736 @param start_port: First port that will be checked.
737 @param end_port: Port immediately after the last one that will be checked.
738 """
739 ports = []
740 i = start_port
741 while i < end_port and count > 0:
742 if is_port_free(i):
743 ports.append(i)
744 count -= 1
745 i += 1
746 return ports
747
748
749# The following are miscellaneous utility functions.
750
lmrb4954e02009-08-17 20:44:42 +0000751def get_path(base_path, user_path):
752 """
753 Translate a user specified path to a real path.
754 If user_path is relative, append it to base_path.
755 If user_path is absolute, return it as is.
756
757 @param base_path: The base path of relative user specified paths.
758 @param user_path: The user specified path.
759 """
760 if os.path.isabs(user_path):
761 return user_path
762 else:
763 return os.path.join(base_path, user_path)
764
765
lmr6f669ce2009-05-31 19:02:42 +0000766def generate_random_string(length):
767 """
768 Return a random string using alphanumeric characters.
769
770 @length: length of the string that will be generated.
771 """
lmr45fc0c22009-09-15 05:16:45 +0000772 r = random.SystemRandom()
lmr6f669ce2009-05-31 19:02:42 +0000773 str = ""
774 chars = string.letters + string.digits
775 while length > 0:
lmr45fc0c22009-09-15 05:16:45 +0000776 str += r.choice(chars)
lmr6f669ce2009-05-31 19:02:42 +0000777 length -= 1
778 return str
779
780
lmr0bee2342010-02-24 00:01:37 +0000781def generate_tmp_file_name(file, ext=None, dir='/tmp/'):
782 """
783 Returns a temporary file name. The file is not created.
784 """
785 while True:
786 file_name = (file + '-' + time.strftime("%Y%m%d-%H%M%S-") +
787 generate_random_string(4))
788 if ext:
789 file_name += '.' + ext
790 file_name = os.path.join(dir, file_name)
791 if not os.path.exists(file_name):
792 break
793
794 return file_name
795
796
lmr6f669ce2009-05-31 19:02:42 +0000797def format_str_for_message(str):
798 """
799 Format str so that it can be appended to a message.
800 If str consists of one line, prefix it with a space.
801 If str consists of multiple lines, prefix it with a newline.
802
803 @param str: string that will be formatted.
804 """
lmr57355592009-08-07 21:55:49 +0000805 lines = str.splitlines()
806 num_lines = len(lines)
807 str = "\n".join(lines)
lmr6f669ce2009-05-31 19:02:42 +0000808 if num_lines == 0:
809 return ""
810 elif num_lines == 1:
811 return " " + str
812 else:
813 return "\n" + str
814
815
816def wait_for(func, timeout, first=0.0, step=1.0, text=None):
817 """
818 If func() evaluates to True before timeout expires, return the
819 value of func(). Otherwise return None.
820
821 @brief: Wait until func() evaluates to True.
822
823 @param timeout: Timeout in seconds
824 @param first: Time to sleep before first attempt
825 @param steps: Time to sleep between attempts in seconds
826 @param text: Text to print while waiting, for debug purposes
827 """
828 start_time = time.time()
829 end_time = time.time() + timeout
830
831 time.sleep(first)
832
833 while time.time() < end_time:
834 if text:
835 logging.debug("%s (%f secs)" % (text, time.time() - start_time))
836
837 output = func()
838 if output:
839 return output
840
841 time.sleep(step)
842
843 logging.debug("Timeout elapsed")
844 return None
845
846
lmr03ba22e2009-10-23 12:07:44 +0000847def get_hash_from_file(hash_path, dvd_basename):
848 """
849 Get the a hash from a given DVD image from a hash file
850 (Hash files are usually named MD5SUM or SHA1SUM and are located inside the
851 download directories of the DVDs)
852
853 @param hash_path: Local path to a hash file.
854 @param cd_image: Basename of a CD image
855 """
856 hash_file = open(hash_path, 'r')
857 for line in hash_file.readlines():
858 if dvd_basename in line:
859 return line.split()[0]
860
861
lmr43beef12009-12-11 21:03:36 +0000862def run_tests(test_list, job):
863 """
864 Runs the sequence of KVM tests based on the list of dictionaries
865 generated by the configuration system, handling dependencies.
866
867 @param test_list: List with all dictionary test parameters.
868 @param job: Autotest job object.
869
870 @return: True, if all tests ran passed, False if any of them failed.
871 """
872 status_dict = {}
lmr43beef12009-12-11 21:03:36 +0000873 failed = False
lmr5df6eb42010-03-23 15:34:16 +0000874
lmr43beef12009-12-11 21:03:36 +0000875 for dict in test_list:
876 if dict.get("skip") == "yes":
877 continue
878 dependencies_satisfied = True
879 for dep in dict.get("depend"):
880 for test_name in status_dict.keys():
881 if not dep in test_name:
882 continue
883 if not status_dict[test_name]:
884 dependencies_satisfied = False
885 break
886 if dependencies_satisfied:
887 test_iterations = int(dict.get("iterations", 1))
lmr7da99b22010-01-12 22:30:28 +0000888 test_tag = dict.get("shortname")
lmrd960a722010-04-01 02:32:59 +0000889 # Setting up profilers during test execution.
890 profilers = dict.get("profilers", "").split()
891 for profiler in profilers:
892 job.profilers.add(profiler)
lmr7da99b22010-01-12 22:30:28 +0000893
lmr7da99b22010-01-12 22:30:28 +0000894 # We need only one execution, profiled, hence we're passing
895 # the profile_only parameter to job.run_test().
896 current_status = job.run_test("kvm", params=dict, tag=test_tag,
897 iterations=test_iterations,
lmrd960a722010-04-01 02:32:59 +0000898 profile_only= bool(profilers) or None)
899
900 for profiler in profilers:
901 job.profilers.delete(profiler)
lmr7da99b22010-01-12 22:30:28 +0000902
lmr43beef12009-12-11 21:03:36 +0000903 if not current_status:
904 failed = True
905 else:
906 current_status = False
907 status_dict[dict.get("name")] = current_status
908
909 return not failed
910
911
912def create_report(report_dir, results_dir):
913 """
914 Creates a neatly arranged HTML results report in the results dir.
915
916 @param report_dir: Directory where the report script is located.
917 @param results_dir: Directory where the results will be output.
918 """
919 reporter = os.path.join(report_dir, 'html_report.py')
920 html_file = os.path.join(results_dir, 'results.html')
921 os.system('%s -r %s -f %s -R' % (reporter, results_dir, html_file))
lmr31af3a12010-01-18 16:46:52 +0000922
923
924def get_full_pci_id(pci_id):
925 """
926 Get full PCI ID of pci_id.
927
928 @param pci_id: PCI ID of a device.
929 """
930 cmd = "lspci -D | awk '/%s/ {print $1}'" % pci_id
931 status, full_id = commands.getstatusoutput(cmd)
932 if status != 0:
933 return None
934 return full_id
935
936
937def get_vendor_from_pci_id(pci_id):
938 """
939 Check out the device vendor ID according to pci_id.
940
941 @param pci_id: PCI ID of a device.
942 """
943 cmd = "lspci -n | awk '/%s/ {print $3}'" % pci_id
944 return re.sub(":", " ", commands.getoutput(cmd))
945
946
lmr84154412010-02-03 18:34:43 +0000947class KvmLoggingConfig(logging_config.LoggingConfig):
948 """
949 Used with the sole purpose of providing convenient logging setup
950 for the KVM test auxiliary programs.
951 """
952 def configure_logging(self, results_dir=None, verbose=False):
953 super(KvmLoggingConfig, self).configure_logging(use_console=True,
954 verbose=verbose)
955
956
lmr31af3a12010-01-18 16:46:52 +0000957class PciAssignable(object):
958 """
959 Request PCI assignable devices on host. It will check whether to request
960 PF (physical Functions) or VF (Virtual Functions).
961 """
lmr83d3b1a2010-01-19 08:14:36 +0000962 def __init__(self, type="vf", driver=None, driver_option=None,
lmr31af3a12010-01-18 16:46:52 +0000963 names=None, devices_requested=None):
964 """
965 Initialize parameter 'type' which could be:
lmr83d3b1a2010-01-19 08:14:36 +0000966 vf: Virtual Functions
967 pf: Physical Function (actual hardware)
lmr31af3a12010-01-18 16:46:52 +0000968 mixed: Both includes VFs and PFs
969
970 If pass through Physical NIC cards, we need to specify which devices
971 to be assigned, e.g. 'eth1 eth2'.
972
973 If pass through Virtual Functions, we need to specify how many vfs
974 are going to be assigned, e.g. passthrough_count = 8 and max_vfs in
975 config file.
976
977 @param type: PCI device type.
978 @param driver: Kernel module for the PCI assignable device.
979 @param driver_option: Module option to specify the maximum number of
980 VFs (eg 'max_vfs=7')
981 @param names: Physical NIC cards correspondent network interfaces,
982 e.g.'eth1 eth2 ...'
983 @param devices_requested: Number of devices being requested.
984 """
985 self.type = type
986 self.driver = driver
987 self.driver_option = driver_option
988 if names:
989 self.name_list = names.split()
990 if devices_requested:
991 self.devices_requested = int(devices_requested)
992 else:
993 self.devices_requested = None
994
995
996 def _get_pf_pci_id(self, name, search_str):
997 """
998 Get the PF PCI ID according to name.
999
1000 @param name: Name of the PCI device.
1001 @param search_str: Search string to be used on lspci.
1002 """
1003 cmd = "ethtool -i %s | awk '/bus-info/ {print $2}'" % name
1004 s, pci_id = commands.getstatusoutput(cmd)
1005 if not (s or "Cannot get driver information" in pci_id):
1006 return pci_id[5:]
1007 cmd = "lspci | awk '/%s/ {print $1}'" % search_str
1008 pci_ids = [id for id in commands.getoutput(cmd).splitlines()]
1009 nic_id = int(re.search('[0-9]+', name).group(0))
1010 if (len(pci_ids) - 1) < nic_id:
1011 return None
1012 return pci_ids[nic_id]
1013
1014
1015 def _release_dev(self, pci_id):
1016 """
1017 Release a single PCI device.
1018
1019 @param pci_id: PCI ID of a given PCI device.
1020 """
1021 base_dir = "/sys/bus/pci"
1022 full_id = get_full_pci_id(pci_id)
1023 vendor_id = get_vendor_from_pci_id(pci_id)
1024 drv_path = os.path.join(base_dir, "devices/%s/driver" % full_id)
1025 if 'pci-stub' in os.readlink(drv_path):
1026 cmd = "echo '%s' > %s/new_id" % (vendor_id, drv_path)
1027 if os.system(cmd):
1028 return False
1029
1030 stub_path = os.path.join(base_dir, "drivers/pci-stub")
1031 cmd = "echo '%s' > %s/unbind" % (full_id, stub_path)
1032 if os.system(cmd):
1033 return False
1034
1035 driver = self.dev_drivers[pci_id]
1036 cmd = "echo '%s' > %s/bind" % (full_id, driver)
1037 if os.system(cmd):
1038 return False
1039
1040 return True
1041
1042
1043 def get_vf_devs(self):
1044 """
1045 Catch all VFs PCI IDs.
1046
1047 @return: List with all PCI IDs for the Virtual Functions avaliable
1048 """
1049 if not self.sr_iov_setup():
1050 return []
1051
1052 cmd = "lspci | awk '/Virtual Function/ {print $1}'"
1053 return commands.getoutput(cmd).split()
1054
1055
1056 def get_pf_devs(self):
1057 """
1058 Catch all PFs PCI IDs.
1059
1060 @return: List with all PCI IDs for the physical hardware requested
1061 """
1062 pf_ids = []
1063 for name in self.name_list:
1064 pf_id = self._get_pf_pci_id(name, "Ethernet")
1065 if not pf_id:
1066 continue
1067 pf_ids.append(pf_id)
1068 return pf_ids
1069
1070
1071 def get_devs(self, count):
1072 """
1073 Check out all devices' PCI IDs according to their name.
1074
1075 @param count: count number of PCI devices needed for pass through
1076 @return: a list of all devices' PCI IDs
1077 """
lmr83d3b1a2010-01-19 08:14:36 +00001078 if self.type == "vf":
lmr31af3a12010-01-18 16:46:52 +00001079 vf_ids = self.get_vf_devs()
lmr83d3b1a2010-01-19 08:14:36 +00001080 elif self.type == "pf":
lmr31af3a12010-01-18 16:46:52 +00001081 vf_ids = self.get_pf_devs()
1082 elif self.type == "mixed":
1083 vf_ids = self.get_vf_devs()
1084 vf_ids.extend(self.get_pf_devs())
1085 return vf_ids[0:count]
1086
1087
1088 def get_vfs_count(self):
1089 """
1090 Get VFs count number according to lspci.
1091 """
lmr224a5412010-03-17 12:00:36 +00001092 # FIXME: Need to think out a method of identify which
1093 # 'virtual function' belongs to which physical card considering
1094 # that if the host has more than one 82576 card. PCI_ID?
lmr31af3a12010-01-18 16:46:52 +00001095 cmd = "lspci | grep 'Virtual Function' | wc -l"
lmr224a5412010-03-17 12:00:36 +00001096 return int(commands.getoutput(cmd))
lmr31af3a12010-01-18 16:46:52 +00001097
1098
1099 def check_vfs_count(self):
1100 """
1101 Check VFs count number according to the parameter driver_options.
1102 """
lmr224a5412010-03-17 12:00:36 +00001103 # Network card 82576 has two network interfaces and each can be
1104 # virtualized up to 7 virtual functions, therefore we multiply
1105 # two for the value of driver_option 'max_vfs'.
1106 expected_count = int((re.findall("(\d)", self.driver_option)[0])) * 2
1107 return (self.get_vfs_count == expected_count)
lmr31af3a12010-01-18 16:46:52 +00001108
1109
1110 def is_binded_to_stub(self, full_id):
1111 """
1112 Verify whether the device with full_id is already binded to pci-stub.
1113
1114 @param full_id: Full ID for the given PCI device
1115 """
1116 base_dir = "/sys/bus/pci"
1117 stub_path = os.path.join(base_dir, "drivers/pci-stub")
1118 if os.path.exists(os.path.join(stub_path, full_id)):
1119 return True
1120 return False
1121
1122
1123 def sr_iov_setup(self):
1124 """
1125 Ensure the PCI device is working in sr_iov mode.
1126
1127 Check if the PCI hardware device drive is loaded with the appropriate,
1128 parameters (number of VFs), and if it's not, perform setup.
1129
1130 @return: True, if the setup was completed successfuly, False otherwise.
1131 """
1132 re_probe = False
1133 s, o = commands.getstatusoutput('lsmod | grep %s' % self.driver)
1134 if s:
1135 re_probe = True
1136 elif not self.check_vfs_count():
1137 os.system("modprobe -r %s" % self.driver)
1138 re_probe = True
lmr224a5412010-03-17 12:00:36 +00001139 else:
1140 return True
lmr31af3a12010-01-18 16:46:52 +00001141
1142 # Re-probe driver with proper number of VFs
1143 if re_probe:
1144 cmd = "modprobe %s %s" % (self.driver, self.driver_option)
lmr224a5412010-03-17 12:00:36 +00001145 logging.info("Loading the driver '%s' with option '%s'" %
1146 (self.driver, self.driver_option))
lmr31af3a12010-01-18 16:46:52 +00001147 s, o = commands.getstatusoutput(cmd)
1148 if s:
1149 return False
lmr31af3a12010-01-18 16:46:52 +00001150 return True
1151
1152
1153 def request_devs(self):
1154 """
1155 Implement setup process: unbind the PCI device and then bind it
1156 to the pci-stub driver.
1157
1158 @return: a list of successfully requested devices' PCI IDs.
1159 """
1160 base_dir = "/sys/bus/pci"
1161 stub_path = os.path.join(base_dir, "drivers/pci-stub")
1162
1163 self.pci_ids = self.get_devs(self.devices_requested)
lmrbc72f082010-02-08 10:02:40 +00001164 logging.debug("The following pci_ids were found: %s", self.pci_ids)
lmr31af3a12010-01-18 16:46:52 +00001165 requested_pci_ids = []
1166 self.dev_drivers = {}
1167
1168 # Setup all devices specified for assignment to guest
1169 for pci_id in self.pci_ids:
1170 full_id = get_full_pci_id(pci_id)
1171 if not full_id:
1172 continue
1173 drv_path = os.path.join(base_dir, "devices/%s/driver" % full_id)
1174 dev_prev_driver= os.path.realpath(os.path.join(drv_path,
1175 os.readlink(drv_path)))
1176 self.dev_drivers[pci_id] = dev_prev_driver
1177
1178 # Judge whether the device driver has been binded to stub
1179 if not self.is_binded_to_stub(full_id):
lmrbc72f082010-02-08 10:02:40 +00001180 logging.debug("Binding device %s to stub", full_id)
lmr31af3a12010-01-18 16:46:52 +00001181 vendor_id = get_vendor_from_pci_id(pci_id)
1182 stub_new_id = os.path.join(stub_path, 'new_id')
1183 unbind_dev = os.path.join(drv_path, 'unbind')
1184 stub_bind = os.path.join(stub_path, 'bind')
1185
1186 info_write_to_files = [(vendor_id, stub_new_id),
1187 (full_id, unbind_dev),
1188 (full_id, stub_bind)]
1189
1190 for content, file in info_write_to_files:
1191 try:
lmrbc72f082010-02-08 10:02:40 +00001192 utils.open_write_close(file, content)
lmr31af3a12010-01-18 16:46:52 +00001193 except IOError:
lmrbc72f082010-02-08 10:02:40 +00001194 logging.debug("Failed to write %s to file %s", content,
1195 file)
lmr31af3a12010-01-18 16:46:52 +00001196 continue
1197
1198 if not self.is_binded_to_stub(full_id):
lmrbc72f082010-02-08 10:02:40 +00001199 logging.error("Binding device %s to stub failed", pci_id)
lmr31af3a12010-01-18 16:46:52 +00001200 continue
1201 else:
lmrbc72f082010-02-08 10:02:40 +00001202 logging.debug("Device %s already binded to stub", pci_id)
lmr31af3a12010-01-18 16:46:52 +00001203 requested_pci_ids.append(pci_id)
1204 self.pci_ids = requested_pci_ids
1205 return self.pci_ids
1206
1207
1208 def release_devs(self):
1209 """
1210 Release all PCI devices currently assigned to VMs back to the
1211 virtualization host.
1212 """
1213 try:
1214 for pci_id in self.dev_drivers:
1215 if not self._release_dev(pci_id):
lmrbc72f082010-02-08 10:02:40 +00001216 logging.error("Failed to release device %s to host", pci_id)
lmr31af3a12010-01-18 16:46:52 +00001217 else:
lmrbc72f082010-02-08 10:02:40 +00001218 logging.info("Released device %s successfully", pci_id)
lmr31af3a12010-01-18 16:46:52 +00001219 except:
1220 return