blob: e814bee422b0ee1f599e1f56ce874ba9753a1c5e [file] [log] [blame]
lmr5c226e32009-07-27 13:13:01 +00001import time, os, logging, re, commands
lmr6f669ce2009-05-31 19:02:42 +00002from autotest_lib.client.common_lib import utils, error
lmrd85c55b2009-07-23 01:42:10 +00003import kvm_utils, kvm_subprocess, ppm_utils, scan_results
lmr6f669ce2009-05-31 19:02:42 +00004
5"""
6KVM test definitions.
7
8@copyright: 2008-2009 Red Hat Inc.
9"""
10
11
12def run_boot(test, params, env):
13 """
14 KVM reboot test:
15 1) Log into a guest
16 2) Send a reboot command to the guest
17 3) Wait until it's up.
18 4) Log into the guest to verify it's up again.
19
20 @param test: kvm test object
21 @param params: Dictionary with the test parameters
22 @param env: Dictionary with test environment.
23 """
24 vm = kvm_utils.env_get_vm(env, params.get("main_vm"))
25 if not vm:
26 raise error.TestError("VM object not found in environment")
27 if not vm.is_alive():
28 raise error.TestError("VM seems to be dead; Test requires a living VM")
29
30 logging.info("Waiting for guest to be up...")
31
32 session = kvm_utils.wait_for(vm.ssh_login, 240, 0, 2)
33 if not session:
34 raise error.TestFail("Could not log into guest")
35
36 logging.info("Logged in")
37
38 if params.get("reboot") == "yes":
39 # Send the VM's reboot command
40 session.sendline(vm.get_params().get("cmd_reboot"))
41 logging.info("Reboot command sent; waiting for guest to go down...")
42
43 if not kvm_utils.wait_for(lambda: not session.is_responsive(),
44 120, 0, 1):
45 raise error.TestFail("Guest refuses to go down")
46
47 session.close()
48
49 logging.info("Guest is down; waiting for it to go up again...")
50
lmr8691f422009-07-28 02:52:30 +000051 session = kvm_utils.wait_for(vm.ssh_login, 240, 0, 2)
lmr6f669ce2009-05-31 19:02:42 +000052 if not session:
53 raise error.TestFail("Could not log into guest after reboot")
54
55 logging.info("Guest is up again")
56
57 session.close()
58
59
lmr6faadb22009-06-18 14:57:45 +000060def run_shutdown(test, params, env):
61 """
62 KVM shutdown test:
63 1) Log into a guest
64 2) Send a shutdown command to the guest
65 3) Wait until it's down
66
67 @param test: kvm test object
68 @param params: Dictionary with the test parameters
69 @param env: Dictionary with test environment
70 """
71 vm = kvm_utils.env_get_vm(env, params.get("main_vm"))
72 if not vm:
73 raise error.TestError("VM object not found in environment")
74 if not vm.is_alive():
75 raise error.TestError("VM seems to be dead; Test requires a living VM")
76
77 logging.info("Waiting for guest to be up...")
78
79 session = kvm_utils.wait_for(vm.ssh_login, 240, 0, 2)
80 if not session:
81 raise error.TestFail("Could not log into guest")
82
83 logging.info("Logged in")
84
85 # Send the VM's shutdown command
86 session.sendline(vm.get_params().get("cmd_shutdown"))
87 session.close()
88
89 logging.info("Shutdown command sent; waiting for guest to go down...")
90
lmr8691f422009-07-28 02:52:30 +000091 if not kvm_utils.wait_for(vm.is_dead, 240, 0, 1):
lmr6faadb22009-06-18 14:57:45 +000092 raise error.TestFail("Guest refuses to go down")
93
94 logging.info("Guest is down")
95
96
lmr6f669ce2009-05-31 19:02:42 +000097def run_migration(test, params, env):
98 """
99 KVM migration test:
100
101 1) Get two live VMs. One will be the 'source', the other will be the
102 'destination'.
103 2) Verify if the source VM supports migration. If it does, proceed with
104 the test
105 3) Send a migration command to the source vm and wait until it's finished.
106 4) Kill off the source vm
107 3) Log into the destination vm after the migration is finished.
108 4) Compare the output of a reference command executed on the source with
109 the output of the same command on the destination machine
110
111 @param test: kvm test object.
112 @param params: Dictionary with test parameters.
113 @param env: Dictionary with the test environment.
114 """
115 src_vm_name = params.get("migration_src")
116 vm = kvm_utils.env_get_vm(env, src_vm_name)
117 if not vm:
118 raise error.TestError("VM '%s' not found in environment" % src_vm_name)
119 if not vm.is_alive():
120 raise error.TestError("VM '%s' seems to be dead; Test requires a"
121 " living VM" % src_vm_name)
122
123 dest_vm_name = params.get("migration_dst")
124 dest_vm = kvm_utils.env_get_vm(env, dest_vm_name)
125 if not dest_vm:
126 raise error.TestError("VM '%s' not found in environment" % dest_vm_name)
127 if not dest_vm.is_alive():
128 raise error.TestError("VM '%s' seems to be dead; Test requires a"
129 " living VM" % dest_vm_name)
130
131 pre_scrdump_filename = os.path.join(test.debugdir, "migration_pre.ppm")
132 post_scrdump_filename = os.path.join(test.debugdir, "migration_post.ppm")
133
134 # See if migration is supported
135 s, o = vm.send_monitor_cmd("help info")
136 if not "info migrate" in o:
137 raise error.TestError("Migration is not supported")
138
139 # Log into guest and get the output of migration_test_command
140 logging.info("Waiting for guest to be up...")
141
142 session = kvm_utils.wait_for(vm.ssh_login, 240, 0, 2)
143 if not session:
144 raise error.TestFail("Could not log into guest")
145
146 logging.info("Logged in")
147
148 reference_output = session.get_command_output(params.get("migration_test_"
149 "command"))
150 session.close()
151
152 # Define the migration command
153 cmd = "migrate -d tcp:localhost:%d" % dest_vm.migration_port
154 logging.debug("Migration command: %s" % cmd)
155
156 # Migrate
157 s, o = vm.send_monitor_cmd(cmd)
158 if s:
159 logging.error("Migration command failed (command: %r, output: %r)" %
160 (cmd, o))
161 raise error.TestFail("Migration command failed")
162
163 # Define some helper functions
164 def mig_finished():
165 s, o = vm.send_monitor_cmd("info migrate")
166 if s:
167 return False
168 if "Migration status: active" in o:
169 return False
170 return True
171
172 def mig_succeeded():
173 s, o = vm.send_monitor_cmd("info migrate")
174 if s == 0 and "Migration status: completed" in o:
175 return True
176 return False
177
178 def mig_failed():
179 s, o = vm.send_monitor_cmd("info migrate")
180 if s == 0 and "Migration status: failed" in o:
181 return True
182 return False
183
184 # Wait for migration to finish
185 if not kvm_utils.wait_for(mig_finished, 90, 2, 2,
186 "Waiting for migration to finish..."):
187 raise error.TestFail("Timeout elapsed while waiting for migration to"
188 "finish")
189
190 # Report migration status
191 if mig_succeeded():
192 logging.info("Migration finished successfully")
193 else:
194 if mig_failed():
195 message = "Migration failed"
196 else:
197 message = "Migration ended with unknown status"
198 raise error.TestFail(message)
199
200 # Get 'post' screendump
201 dest_vm.send_monitor_cmd("screendump %s" % post_scrdump_filename)
202
203 # Get 'pre' screendump
204 vm.send_monitor_cmd("screendump %s" % pre_scrdump_filename)
205
206 # Kill the source VM
207 vm.send_monitor_cmd("quit", block=False)
208
209 # Hack: it seems that the first attempt to communicate with the SSH port
210 # following migration always fails (or succeeds after a very long time).
211 # So just connect to the port once so the following call to ssh_login
212 # succeeds.
213 dest_vm.is_sshd_running(timeout=0.0)
214
215 # Log into guest and get the output of migration_test_command
216 logging.info("Logging into guest after migration...")
217
218 session = dest_vm.ssh_login()
219 if not session:
220 raise error.TestFail("Could not log into guest after migration")
221
222 logging.info("Logged in after migration")
223
224 output = session.get_command_output(params.get("migration_test_command"))
225 session.close()
226
227 # Compare output to reference output
228 if output != reference_output:
229 logging.info("Command output before migration differs from command"
230 " output after migration")
231 logging.info("Command: %s" % params.get("migration_test_command"))
232 logging.info("Output before:" +
233 kvm_utils.format_str_for_message(reference_output))
234 logging.info("Output after:" + kvm_utils.format_str_for_message(output))
235 raise error.TestFail("Command produced different output before and"
236 " after migration")
237
238
239def run_autotest(test, params, env):
240 """
241 Run an autotest test inside a guest.
242
243 @param test: kvm test object.
244 @param params: Dictionary with test parameters.
245 @param env: Dictionary with the test environment.
246 """
247 vm = kvm_utils.env_get_vm(env, params.get("main_vm"))
248 if not vm:
249 raise error.TestError("VM object not found in environment")
250 if not vm.is_alive():
251 raise error.TestError("VM seems to be dead; Test requires a living VM")
252
253 logging.info("Logging into guest...")
254
255 session = kvm_utils.wait_for(vm.ssh_login, 240, 0, 2)
256 if not session:
257 raise error.TestFail("Could not log into guest")
258
259 logging.info("Logged in")
260
261 # Collect some info
262 test_name = params.get("test_name")
263 test_timeout = int(params.get("test_timeout", 300))
264 test_control_file = params.get("test_control_file", "control")
265 tarred_autotest_path = "/tmp/autotest.tar.bz2"
266 tarred_test_path = "/tmp/%s.tar.bz2" % test_name
267
268 # tar the contents of bindir/autotest
269 cmd = "cd %s; tar cvjf %s autotest/*"
270 cmd += " --exclude=autotest/tests"
271 cmd += " --exclude=autotest/results"
272 cmd += " --exclude=autotest/tmp"
273 cmd += " --exclude=autotest/control"
274 cmd += " --exclude=*.pyc"
275 cmd += " --exclude=*.svn"
276 cmd += " --exclude=*.git"
lmrd85c55b2009-07-23 01:42:10 +0000277 kvm_subprocess.run_fg(cmd % (test.bindir, tarred_autotest_path), timeout=30)
lmr6f669ce2009-05-31 19:02:42 +0000278
279 # tar the contents of bindir/autotest/tests/<test_name>
280 cmd = "cd %s; tar cvjf %s %s/*"
281 cmd += " --exclude=*.pyc"
282 cmd += " --exclude=*.svn"
283 cmd += " --exclude=*.git"
lmrd85c55b2009-07-23 01:42:10 +0000284 kvm_subprocess.run_fg(cmd %
285 (os.path.join(test.bindir, "autotest", "tests"),
286 tarred_test_path, test_name), timeout=30)
lmr6f669ce2009-05-31 19:02:42 +0000287
288 # Check if we need to copy autotest.tar.bz2
289 copy = False
290 output = session.get_command_output("ls -l autotest.tar.bz2")
291 if "such file" in output:
292 copy = True
293 else:
294 size = int(output.split()[4])
295 if size != os.path.getsize(tarred_autotest_path):
296 copy = True
297 # Perform the copy
298 if copy:
299 logging.info("Copying autotest.tar.bz2 to guest"
300 " (file is missing or has a different size)...")
301 if not vm.scp_to_remote(tarred_autotest_path, ""):
302 raise error.TestFail("Could not copy autotest.tar.bz2 to guest")
303
304 # Check if we need to copy <test_name>.tar.bz2
305 copy = False
306 output = session.get_command_output("ls -l %s.tar.bz2" % test_name)
307 if "such file" in output:
308 copy = True
309 else:
310 size = int(output.split()[4])
311 if size != os.path.getsize(tarred_test_path):
312 copy = True
313 # Perform the copy
314 if copy:
315 logging.info("Copying %s.tar.bz2 to guest (file is missing or has a"
316 " different size)..." % test_name)
317 if not vm.scp_to_remote(tarred_test_path, ""):
318 raise error.TestFail("Could not copy %s.tar.bz2 to guest" %
319 test_name)
320
321 # Extract autotest.tar.bz2
322 logging.info("Extracting autotest.tar.bz2...")
323 status = session.get_command_status("tar xvfj autotest.tar.bz2")
324 if status != 0:
325 raise error.TestFail("Could not extract autotest.tar.bz2")
326
327 # mkdir autotest/tests
328 session.sendline("mkdir autotest/tests")
329
330 # Extract <test_name>.tar.bz2 into autotest/tests
331 logging.info("Extracting %s.tar.bz2..." % test_name)
332 status = session.get_command_status("tar xvfj %s.tar.bz2 -C "
333 "autotest/tests" % test_name)
334 if status != 0:
335 raise error.TestFail("Could not extract %s.tar.bz2" % test_name)
336
lmr16cf1652009-06-15 21:32:49 +0000337 # Cleaning up old remaining results
338 session.sendline("rm -rf autotest/results/*")
339 # Copying the selected control file (located inside
340 # test.bindir/autotest_control to the autotest dir
341 control_file_path = os.path.join(test.bindir, "autotest_control",
342 test_control_file)
343 if not vm.scp_to_remote(control_file_path, "autotest/control"):
344 raise error.TestFail("Could not copy the test control file to guest")
lmr6f669ce2009-05-31 19:02:42 +0000345 # Run the test
346 logging.info("Running test '%s'..." % test_name)
lmr16cf1652009-06-15 21:32:49 +0000347 session.sendline("cd autotest")
348 session.sendline("rm -f control.state")
lmr6f669ce2009-05-31 19:02:42 +0000349 session.read_up_to_prompt()
lmr16cf1652009-06-15 21:32:49 +0000350 session.sendline("bin/autotest control")
lmr6f669ce2009-05-31 19:02:42 +0000351 logging.info("---------------- Test output ----------------")
lmr16cf1652009-06-15 21:32:49 +0000352 match = session.read_up_to_prompt(timeout=test_timeout,
353 print_func=logging.info)[0]
lmr6f669ce2009-05-31 19:02:42 +0000354 logging.info("---------------- End of test output ----------------")
355 if not match:
lmr16cf1652009-06-15 21:32:49 +0000356 raise error.TestFail("Timeout elapsed while waiting for test to "
357 "complete")
358 # Get the results generated by autotest
359 output = session.get_command_output("cat results/*/status")
lmr6f669ce2009-05-31 19:02:42 +0000360
361 # Parse test results
362 result_list = scan_results.parse_results(output)
363
364 # Report test results and check for FAIL/ERROR status
365 logging.info("Results (test, status, duration, info):")
366 status_error = False
367 status_fail = False
368 if result_list == []:
369 status_fail = True
lmrd16a67d2009-06-10 19:52:59 +0000370 message_fail = ("Test '%s' did not produce any recognizable "
371 "results" % test_name)
lmr6f669ce2009-05-31 19:02:42 +0000372 for result in result_list:
373 logging.info(str(result))
374 if result[1] == "FAIL":
375 status_fail = True
lmr50bece42009-06-15 20:54:43 +0000376 message_fail = ("Test '%s' ended with FAIL "
377 "(info: '%s')" % (result[0], result[3]))
lmr6f669ce2009-05-31 19:02:42 +0000378 if result[1] == "ERROR":
379 status_error = True
lmr50bece42009-06-15 20:54:43 +0000380 message_error = ("Test '%s' ended with ERROR "
381 "(info: '%s')" % (result[0], result[3]))
lmr6f669ce2009-05-31 19:02:42 +0000382 if result[1] == "ABORT":
383 status_error = True
lmr50bece42009-06-15 20:54:43 +0000384 message_error = ("Test '%s' ended with ABORT "
385 "(info: '%s')" % (result[0], result[3]))
lmr6f669ce2009-05-31 19:02:42 +0000386
387 # Copy test results to the local bindir/guest_results
388 logging.info("Copying results back from guest...")
389 guest_results_dir = os.path.join(test.outputdir, "guest_results")
390 if not os.path.exists(guest_results_dir):
391 os.mkdir(guest_results_dir)
392 if not vm.scp_from_remote("autotest/results/default/*", guest_results_dir):
393 logging.error("Could not copy results back from guest")
394
395 # Fail the test if necessary
396 if status_fail:
397 raise error.TestFail(message_fail)
398 elif status_error:
399 raise error.TestError(message_error)
400
401
402def internal_yum_update(session, command, prompt, timeout):
403 """
404 Helper function to perform the yum update test.
405
406 @param session: SSH session stablished to the host
407 @param command: Command to be sent to the SSH connection
408 @param prompt: Machine prompt
409 @param timeout: How long to wait until we get an appropriate output from
410 the SSH session.
411 """
412 session.sendline(command)
413 end_time = time.time() + timeout
414 while time.time() < end_time:
415 (match, text) = session.read_until_last_line_matches(
416 ["[Ii]s this [Oo][Kk]", prompt], timeout=timeout)
417 if match == 0:
418 logging.info("Got 'Is this ok'; sending 'y'")
419 session.sendline("y")
420 elif match == 1:
421 logging.info("Got shell prompt")
422 return True
423 else:
424 logging.info("Timeout or process exited")
425 return False
426
427
428def run_yum_update(test, params, env):
429 """
430 Runs yum update and yum update kernel on the remote host (yum enabled
431 hosts only).
432
433 @param test: kvm test object.
434 @param params: Dictionary with test parameters.
435 @param env: Dictionary with the test environment.
436 """
437 vm = kvm_utils.env_get_vm(env, params.get("main_vm"))
438 if not vm:
439 message = "VM object not found in environment"
440 logging.error(message)
441 raise error.TestError(message)
442 if not vm.is_alive():
443 message = "VM seems to be dead; Test requires a living VM"
444 logging.error(message)
445 raise error.TestError(message)
446
447 logging.info("Logging into guest...")
448
lmr8691f422009-07-28 02:52:30 +0000449 session = kvm_utils.wait_for(vm.ssh_login, 240, 0, 2)
lmr6f669ce2009-05-31 19:02:42 +0000450 if not session:
451 message = "Could not log into guest"
452 logging.error(message)
453 raise error.TestFail(message)
454
455 logging.info("Logged in")
456
457 internal_yum_update(session, "yum update", params.get("ssh_prompt"), 600)
458 internal_yum_update(session, "yum update kernel",
459 params.get("ssh_prompt"), 600)
460
461 session.close()
462
463
464def run_linux_s3(test, params, env):
465 """
466 Suspend a guest Linux OS to memory.
467
468 @param test: kvm test object.
469 @param params: Dictionary with test parameters.
470 @param env: Dictionary with the test environment.
471 """
472 vm = kvm_utils.env_get_vm(env, params.get("main_vm"))
473 if not vm:
474 raise error.TestError("VM object not found in environment")
475 if not vm.is_alive():
476 raise error.TestError("VM seems to be dead; Test requires a living VM")
477
478 logging.info("Waiting for guest to be up...")
479
480 session = kvm_utils.wait_for(vm.ssh_login, 240, 0, 2)
481 if not session:
482 raise error.TestFail("Could not log into guest")
483
484 logging.info("Logged in")
485 logging.info("Checking that VM supports S3")
486
487 status = session.get_command_status("grep -q mem /sys/power/state")
488 if status == None:
489 logging.error("Failed to check if S3 exists")
490 elif status != 0:
491 raise error.TestFail("Guest does not support S3")
492
493 logging.info("Waiting for a while for X to start")
494 time.sleep(10)
495
496 src_tty = session.get_command_output("fgconsole").strip()
497 logging.info("Current virtual terminal is %s" % src_tty)
498 if src_tty not in map(str, range(1,10)):
499 raise error.TestFail("Got a strange current vt (%s)" % src_tty)
500
501 dst_tty = "1"
502 if src_tty == "1":
503 dst_tty = "2"
504
505 logging.info("Putting VM into S3")
506 command = "chvt %s && echo mem > /sys/power/state && chvt %s" % (dst_tty,
507 src_tty)
508 status = session.get_command_status(command, timeout=120)
509 if status != 0:
510 raise error.TestFail("Suspend to mem failed")
511
512 logging.info("VM resumed after S3")
513
514 session.close()
lmra04d7c92009-06-19 13:05:39 +0000515
516
517def run_stress_boot(tests, params, env):
518 """
519 Boots VMs until one of them becomes unresponsive, and records the maximum
520 number of VMs successfully started:
521 1) boot the first vm
522 2) boot the second vm cloned from the first vm, check whether it boots up
523 and all booted vms can ssh-login
524 3) go on until cannot create VM anymore or cannot allocate memory for VM
525
526 @param test: kvm test object
527 @param params: Dictionary with the test parameters
528 @param env: Dictionary with test environment.
529 """
530 # boot the first vm
531 vm = kvm_utils.env_get_vm(env, params.get("main_vm"))
lmra04d7c92009-06-19 13:05:39 +0000532 if not vm:
533 raise error.TestError("VM object not found in environment")
534 if not vm.is_alive():
535 raise error.TestError("VM seems to be dead; Test requires a living VM")
536
537 logging.info("Waiting for first guest to be up...")
538
539 session = kvm_utils.wait_for(vm.ssh_login, 240, 0, 2)
540 if not session:
541 raise error.TestFail("Could not log into first guest")
542
543 num = 2
544 vms = []
545 sessions = [session]
546
547 # boot the VMs
548 while num <= int(params.get("max_vms")):
549 try:
550 vm_name = "vm" + str(num)
551
552 # clone vm according to the first one
553 vm_params = params.copy()
554 vm_params['image_snapshot'] = "yes"
555 vm_params['kill_vm'] = "yes"
556 vm_params['kill_vm_gracefully'] = "no"
557 curr_vm = vm.clone(vm_name, vm_params)
558 kvm_utils.env_register_vm(env, vm_name, curr_vm)
559 params['vms'] += " " + vm_name
560
561 #vms.append(curr_vm)
562 logging.info("Booting guest #%d" % num)
563 if not curr_vm.create():
564 raise error.TestFail("Cannot create VM #%d" % num)
565
566 curr_vm_session = kvm_utils.wait_for(curr_vm.ssh_login, 240, 0, 2)
567 if not curr_vm_session:
568 raise error.TestFail("Could not log into guest #%d" % num)
569
570 logging.info("Guest #%d boots up successfully" % num)
571 sessions.append(curr_vm_session)
572
573 # check whether all previous ssh sessions are responsive
574 for i, vm_session in enumerate(sessions):
575 if vm_session.get_command_status(params.get("alive_test_cmd")):
576 raise error.TestFail("Session #%d is not responsive" % i)
577 num += 1
578
579 except (error.TestFail, OSError):
580 for se in sessions:
581 se.close()
582 logging.info("Total number booted: %d" % (num - 1))
583 raise
584 else:
585 for se in sessions:
586 se.close()
587 logging.info("Total number booted: %d" % (num -1))
lmr5c226e32009-07-27 13:13:01 +0000588
589
590def run_timedrift(test, params, env):
591 """
592 Time drift test (mainly for Windows guests):
593
594 1) Log into a guest.
595 2) Take a time reading from the guest and host.
596 3) Run load on the guest and host.
597 4) Take a second time reading.
598 5) Stop the load and rest for a while.
599 6) Take a third time reading.
600 7) If the drift immediately after load is higher than a user-
601 specified value (in %), fail.
602 If the drift after the rest period is higher than a user-specified value,
603 fail.
604
605 @param test: KVM test object.
606 @param params: Dictionary with test parameters.
607 @param env: Dictionary with the test environment.
608 """
lmr25e89fc2009-08-07 20:29:16 +0000609 # Helper functions
610 def set_cpu_affinity(pid, mask):
611 """
612 Set the CPU affinity of all threads of the process with PID pid.
613
614 @param pid: The process ID.
615 @param mask: The CPU affinity mask.
616 @return: A dict containing the previous mask for each thread.
617 """
618 tids = commands.getoutput("ps -L --pid=%s -o lwp=" % pid).split()
619 prev_masks = {}
620 for tid in tids:
621 prev_mask = commands.getoutput("taskset -p %s" % tid).split()[-1]
622 prev_masks[tid] = prev_mask
623 commands.getoutput("taskset -p %s %s" % (mask, tid))
624 return prev_masks
625
626 def restore_cpu_affinity(prev_masks):
627 """
628 Restore the CPU affinity of several threads.
629
630 @param prev_masks: A dict containing TIDs as keys and masks as values.
631 """
632 for tid, mask in prev_masks.items():
633 commands.getoutput("taskset -p %s %s" % (mask, tid))
634
lmrff2a7c52009-08-10 00:09:22 +0000635 def get_time(session, time_command, time_filter_re, time_format):
lmr25e89fc2009-08-07 20:29:16 +0000636 """
637 Returns the host time and guest time.
638
lmrff2a7c52009-08-10 00:09:22 +0000639 @param session: A shell session.
640 @param time_command: Command to issue to get the current guest time.
641 @param time_filter_re: Regex filter to apply on the output of
642 time_command in order to get the current time.
643 @param time_format: Format string to pass to time.strptime() with the
644 result of the regex filter.
lmr25e89fc2009-08-07 20:29:16 +0000645 @return: A tuple containing the host time and guest time.
646 """
647 host_time = time.time()
648 session.sendline(time_command)
649 (match, s) = session.read_up_to_prompt()
650 s = re.findall(time_filter_re, s)[0]
651 guest_time = time.mktime(time.strptime(s, time_format))
652 return (host_time, guest_time)
653
lmr5c226e32009-07-27 13:13:01 +0000654 vm = kvm_utils.env_get_vm(env, params.get("main_vm"))
655 if not vm:
656 raise error.TestError("VM object not found in environment")
657 if not vm.is_alive():
658 raise error.TestError("VM seems to be dead; Test requires a living VM")
659
660 logging.info("Waiting for guest to be up...")
661
662 session = kvm_utils.wait_for(vm.ssh_login, 240, 0, 2)
663 if not session:
664 raise error.TestFail("Could not log into guest")
665
666 logging.info("Logged in")
667
668 # Collect test parameters:
669 # Command to run to get the current time
670 time_command = params.get("time_command")
671 # Filter which should match a string to be passed to time.strptime()
672 time_filter_re = params.get("time_filter_re")
673 # Time format for time.strptime()
674 time_format = params.get("time_format")
675 guest_load_command = params.get("guest_load_command")
676 guest_load_stop_command = params.get("guest_load_stop_command")
677 host_load_command = params.get("host_load_command")
678 guest_load_instances = int(params.get("guest_load_instances", "1"))
679 host_load_instances = int(params.get("host_load_instances", "0"))
680 # CPU affinity mask for taskset
681 cpu_mask = params.get("cpu_mask", "0xFF")
682 load_duration = float(params.get("load_duration", "30"))
683 rest_duration = float(params.get("rest_duration", "10"))
684 drift_threshold = float(params.get("drift_threshold", "200"))
685 drift_threshold_after_rest = float(params.get("drift_threshold_after_rest",
686 "200"))
687
688 guest_load_sessions = []
689 host_load_sessions = []
690
lmr5c226e32009-07-27 13:13:01 +0000691 # Set the VM's CPU affinity
lmr25e89fc2009-08-07 20:29:16 +0000692 prev_affinity = set_cpu_affinity(vm.get_pid(), cpu_mask)
lmr5c226e32009-07-27 13:13:01 +0000693
694 try:
695 # Get time before load
lmrff2a7c52009-08-10 00:09:22 +0000696 (host_time_0, guest_time_0) = get_time(session, time_command,
697 time_filter_re, time_format)
lmr5c226e32009-07-27 13:13:01 +0000698
699 # Run some load on the guest
700 logging.info("Starting load on guest...")
701 for i in range(guest_load_instances):
702 load_session = vm.ssh_login()
703 if not load_session:
704 raise error.TestFail("Could not log into guest")
705 load_session.set_output_prefix("(guest load %d) " % i)
706 load_session.set_output_func(logging.debug)
707 load_session.sendline(guest_load_command)
708 guest_load_sessions.append(load_session)
709
710 # Run some load on the host
711 logging.info("Starting load on host...")
712 for i in range(host_load_instances):
713 host_load_sessions.append(
714 kvm_subprocess.run_bg(host_load_command,
715 output_func=logging.debug,
716 output_prefix="(host load %d) " % i,
717 timeout=0.5))
718 # Set the CPU affinity of the shell running the load process
719 pid = host_load_sessions[-1].get_shell_pid()
lmr25e89fc2009-08-07 20:29:16 +0000720 set_cpu_affinity(pid, cpu_mask)
lmr5c226e32009-07-27 13:13:01 +0000721 # Try setting the CPU affinity of the load process itself
722 pid = host_load_sessions[-1].get_pid()
723 if pid:
lmr25e89fc2009-08-07 20:29:16 +0000724 set_cpu_affinity(pid, cpu_mask)
lmr5c226e32009-07-27 13:13:01 +0000725
726 # Sleep for a while (during load)
727 logging.info("Sleeping for %s seconds..." % load_duration)
728 time.sleep(load_duration)
729
730 # Get time delta after load
lmrff2a7c52009-08-10 00:09:22 +0000731 (host_time_1, guest_time_1) = get_time(session, time_command,
732 time_filter_re, time_format)
lmr5c226e32009-07-27 13:13:01 +0000733
734 # Report results
735 host_delta = host_time_1 - host_time_0
736 guest_delta = guest_time_1 - guest_time_0
737 drift = 100.0 * (host_delta - guest_delta) / host_delta
738 logging.info("Host duration: %.2f" % host_delta)
739 logging.info("Guest duration: %.2f" % guest_delta)
740 logging.info("Drift: %.2f%%" % drift)
741
742 finally:
743 logging.info("Cleaning up...")
744 # Restore the VM's CPU affinity
lmr25e89fc2009-08-07 20:29:16 +0000745 restore_cpu_affinity(prev_affinity)
lmr5c226e32009-07-27 13:13:01 +0000746 # Stop the guest load
747 if guest_load_stop_command:
748 session.get_command_output(guest_load_stop_command)
749 # Close all load shell sessions
750 for load_session in guest_load_sessions:
751 load_session.close()
752 for load_session in host_load_sessions:
753 load_session.close()
754
755 # Sleep again (rest)
756 logging.info("Sleeping for %s seconds..." % rest_duration)
757 time.sleep(rest_duration)
758
759 # Get time after rest
lmrff2a7c52009-08-10 00:09:22 +0000760 (host_time_2, guest_time_2) = get_time(session, time_command,
761 time_filter_re, time_format)
lmr5c226e32009-07-27 13:13:01 +0000762
763 # Report results
764 host_delta_total = host_time_2 - host_time_0
765 guest_delta_total = guest_time_2 - guest_time_0
766 drift_total = 100.0 * (host_delta_total - guest_delta_total) / host_delta
767 logging.info("Total host duration including rest: %.2f" % host_delta_total)
768 logging.info("Total guest duration including rest: %.2f" % guest_delta_total)
769 logging.info("Total drift after rest: %.2f%%" % drift_total)
770
lmr5b829602009-08-07 20:36:06 +0000771 session.close()
772
lmr5c226e32009-07-27 13:13:01 +0000773 # Fail the test if necessary
774 if drift > drift_threshold:
775 raise error.TestFail("Time drift too large: %.2f%%" % drift)
776 if drift > drift_threshold_after_rest:
777 raise error.TestFail("Time drift too large after rest period: %.2f%%"
778 % drift_total)