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