| import time, os, logging |
| from autotest_lib.client.common_lib import utils, error |
| import kvm_utils, ppm_utils, scan_results |
| |
| """ |
| KVM test definitions. |
| |
| @copyright: 2008-2009 Red Hat Inc. |
| """ |
| |
| |
| def run_boot(test, params, env): |
| """ |
| KVM reboot test: |
| 1) Log into a guest |
| 2) Send a reboot command to the guest |
| 3) Wait until it's up. |
| 4) Log into the guest to verify it's up again. |
| |
| @param test: kvm test object |
| @param params: Dictionary with the test parameters |
| @param env: Dictionary with test environment. |
| """ |
| vm = kvm_utils.env_get_vm(env, params.get("main_vm")) |
| if not vm: |
| raise error.TestError("VM object not found in environment") |
| if not vm.is_alive(): |
| raise error.TestError("VM seems to be dead; Test requires a living VM") |
| |
| logging.info("Waiting for guest to be up...") |
| |
| session = kvm_utils.wait_for(vm.ssh_login, 240, 0, 2) |
| if not session: |
| raise error.TestFail("Could not log into guest") |
| |
| logging.info("Logged in") |
| |
| if params.get("reboot") == "yes": |
| # Send the VM's reboot command |
| session.sendline(vm.get_params().get("cmd_reboot")) |
| logging.info("Reboot command sent; waiting for guest to go down...") |
| |
| if not kvm_utils.wait_for(lambda: not session.is_responsive(), |
| 120, 0, 1): |
| raise error.TestFail("Guest refuses to go down") |
| |
| session.close() |
| |
| logging.info("Guest is down; waiting for it to go up again...") |
| |
| session = kvm_utils.wait_for(vm.ssh_login, 120, 0, 2) |
| if not session: |
| raise error.TestFail("Could not log into guest after reboot") |
| |
| logging.info("Guest is up again") |
| |
| session.close() |
| |
| |
| def run_migration(test, params, env): |
| """ |
| KVM migration test: |
| |
| 1) Get two live VMs. One will be the 'source', the other will be the |
| 'destination'. |
| 2) Verify if the source VM supports migration. If it does, proceed with |
| the test |
| 3) Send a migration command to the source vm and wait until it's finished. |
| 4) Kill off the source vm |
| 3) Log into the destination vm after the migration is finished. |
| 4) Compare the output of a reference command executed on the source with |
| the output of the same command on the destination machine |
| |
| @param test: kvm test object. |
| @param params: Dictionary with test parameters. |
| @param env: Dictionary with the test environment. |
| """ |
| src_vm_name = params.get("migration_src") |
| vm = kvm_utils.env_get_vm(env, src_vm_name) |
| if not vm: |
| raise error.TestError("VM '%s' not found in environment" % src_vm_name) |
| if not vm.is_alive(): |
| raise error.TestError("VM '%s' seems to be dead; Test requires a" |
| " living VM" % src_vm_name) |
| |
| dest_vm_name = params.get("migration_dst") |
| dest_vm = kvm_utils.env_get_vm(env, dest_vm_name) |
| if not dest_vm: |
| raise error.TestError("VM '%s' not found in environment" % dest_vm_name) |
| if not dest_vm.is_alive(): |
| raise error.TestError("VM '%s' seems to be dead; Test requires a" |
| " living VM" % dest_vm_name) |
| |
| pre_scrdump_filename = os.path.join(test.debugdir, "migration_pre.ppm") |
| post_scrdump_filename = os.path.join(test.debugdir, "migration_post.ppm") |
| |
| # See if migration is supported |
| s, o = vm.send_monitor_cmd("help info") |
| if not "info migrate" in o: |
| raise error.TestError("Migration is not supported") |
| |
| # Log into guest and get the output of migration_test_command |
| logging.info("Waiting for guest to be up...") |
| |
| session = kvm_utils.wait_for(vm.ssh_login, 240, 0, 2) |
| if not session: |
| raise error.TestFail("Could not log into guest") |
| |
| logging.info("Logged in") |
| |
| reference_output = session.get_command_output(params.get("migration_test_" |
| "command")) |
| session.close() |
| |
| # Define the migration command |
| cmd = "migrate -d tcp:localhost:%d" % dest_vm.migration_port |
| logging.debug("Migration command: %s" % cmd) |
| |
| # Migrate |
| s, o = vm.send_monitor_cmd(cmd) |
| if s: |
| logging.error("Migration command failed (command: %r, output: %r)" % |
| (cmd, o)) |
| raise error.TestFail("Migration command failed") |
| |
| # Define some helper functions |
| def mig_finished(): |
| s, o = vm.send_monitor_cmd("info migrate") |
| if s: |
| return False |
| if "Migration status: active" in o: |
| return False |
| return True |
| |
| def mig_succeeded(): |
| s, o = vm.send_monitor_cmd("info migrate") |
| if s == 0 and "Migration status: completed" in o: |
| return True |
| return False |
| |
| def mig_failed(): |
| s, o = vm.send_monitor_cmd("info migrate") |
| if s == 0 and "Migration status: failed" in o: |
| return True |
| return False |
| |
| # Wait for migration to finish |
| if not kvm_utils.wait_for(mig_finished, 90, 2, 2, |
| "Waiting for migration to finish..."): |
| raise error.TestFail("Timeout elapsed while waiting for migration to" |
| "finish") |
| |
| # Report migration status |
| if mig_succeeded(): |
| logging.info("Migration finished successfully") |
| else: |
| if mig_failed(): |
| message = "Migration failed" |
| else: |
| message = "Migration ended with unknown status" |
| raise error.TestFail(message) |
| |
| # Get 'post' screendump |
| dest_vm.send_monitor_cmd("screendump %s" % post_scrdump_filename) |
| |
| # Get 'pre' screendump |
| vm.send_monitor_cmd("screendump %s" % pre_scrdump_filename) |
| |
| # Kill the source VM |
| vm.send_monitor_cmd("quit", block=False) |
| |
| # Hack: it seems that the first attempt to communicate with the SSH port |
| # following migration always fails (or succeeds after a very long time). |
| # So just connect to the port once so the following call to ssh_login |
| # succeeds. |
| dest_vm.is_sshd_running(timeout=0.0) |
| |
| # Log into guest and get the output of migration_test_command |
| logging.info("Logging into guest after migration...") |
| |
| session = dest_vm.ssh_login() |
| if not session: |
| raise error.TestFail("Could not log into guest after migration") |
| |
| logging.info("Logged in after migration") |
| |
| output = session.get_command_output(params.get("migration_test_command")) |
| session.close() |
| |
| # Compare output to reference output |
| if output != reference_output: |
| logging.info("Command output before migration differs from command" |
| " output after migration") |
| logging.info("Command: %s" % params.get("migration_test_command")) |
| logging.info("Output before:" + |
| kvm_utils.format_str_for_message(reference_output)) |
| logging.info("Output after:" + kvm_utils.format_str_for_message(output)) |
| raise error.TestFail("Command produced different output before and" |
| " after migration") |
| |
| |
| def run_autotest(test, params, env): |
| """ |
| Run an autotest test inside a guest. |
| |
| @param test: kvm test object. |
| @param params: Dictionary with test parameters. |
| @param env: Dictionary with the test environment. |
| """ |
| vm = kvm_utils.env_get_vm(env, params.get("main_vm")) |
| if not vm: |
| raise error.TestError("VM object not found in environment") |
| if not vm.is_alive(): |
| raise error.TestError("VM seems to be dead; Test requires a living VM") |
| |
| logging.info("Logging into guest...") |
| |
| session = kvm_utils.wait_for(vm.ssh_login, 240, 0, 2) |
| if not session: |
| raise error.TestFail("Could not log into guest") |
| |
| logging.info("Logged in") |
| |
| # Collect some info |
| test_name = params.get("test_name") |
| test_timeout = int(params.get("test_timeout", 300)) |
| test_control_file = params.get("test_control_file", "control") |
| tarred_autotest_path = "/tmp/autotest.tar.bz2" |
| tarred_test_path = "/tmp/%s.tar.bz2" % test_name |
| |
| # tar the contents of bindir/autotest |
| cmd = "cd %s; tar cvjf %s autotest/*" |
| cmd += " --exclude=autotest/tests" |
| cmd += " --exclude=autotest/results" |
| cmd += " --exclude=autotest/tmp" |
| cmd += " --exclude=autotest/control" |
| cmd += " --exclude=*.pyc" |
| cmd += " --exclude=*.svn" |
| cmd += " --exclude=*.git" |
| kvm_utils.run_bg(cmd % (test.bindir, tarred_autotest_path), timeout=30) |
| |
| # tar the contents of bindir/autotest/tests/<test_name> |
| cmd = "cd %s; tar cvjf %s %s/*" |
| cmd += " --exclude=*.pyc" |
| cmd += " --exclude=*.svn" |
| cmd += " --exclude=*.git" |
| kvm_utils.run_bg(cmd % (os.path.join(test.bindir, "autotest", "tests"), |
| tarred_test_path, test_name), timeout=30) |
| |
| # Check if we need to copy autotest.tar.bz2 |
| copy = False |
| output = session.get_command_output("ls -l autotest.tar.bz2") |
| if "such file" in output: |
| copy = True |
| else: |
| size = int(output.split()[4]) |
| if size != os.path.getsize(tarred_autotest_path): |
| copy = True |
| # Perform the copy |
| if copy: |
| logging.info("Copying autotest.tar.bz2 to guest" |
| " (file is missing or has a different size)...") |
| if not vm.scp_to_remote(tarred_autotest_path, ""): |
| raise error.TestFail("Could not copy autotest.tar.bz2 to guest") |
| |
| # Check if we need to copy <test_name>.tar.bz2 |
| copy = False |
| output = session.get_command_output("ls -l %s.tar.bz2" % test_name) |
| if "such file" in output: |
| copy = True |
| else: |
| size = int(output.split()[4]) |
| if size != os.path.getsize(tarred_test_path): |
| copy = True |
| # Perform the copy |
| if copy: |
| logging.info("Copying %s.tar.bz2 to guest (file is missing or has a" |
| " different size)..." % test_name) |
| if not vm.scp_to_remote(tarred_test_path, ""): |
| raise error.TestFail("Could not copy %s.tar.bz2 to guest" % |
| test_name) |
| |
| # Extract autotest.tar.bz2 |
| logging.info("Extracting autotest.tar.bz2...") |
| status = session.get_command_status("tar xvfj autotest.tar.bz2") |
| if status != 0: |
| raise error.TestFail("Could not extract autotest.tar.bz2") |
| |
| # mkdir autotest/tests |
| session.sendline("mkdir autotest/tests") |
| |
| # Extract <test_name>.tar.bz2 into autotest/tests |
| logging.info("Extracting %s.tar.bz2..." % test_name) |
| status = session.get_command_status("tar xvfj %s.tar.bz2 -C " |
| "autotest/tests" % test_name) |
| if status != 0: |
| raise error.TestFail("Could not extract %s.tar.bz2" % test_name) |
| |
| # Run the test |
| logging.info("Running test '%s'..." % test_name) |
| session.sendline("cd autotest/tests/%s" % test_name) |
| session.sendline("rm -f ./%s.state" % test_control_file) |
| session.read_up_to_prompt() |
| session.sendline("../../bin/autotest ./%s" % test_control_file) |
| logging.info("---------------- Test output ----------------") |
| match, output = session.read_up_to_prompt(timeout=test_timeout, |
| print_func=logging.info) |
| logging.info("---------------- End of test output ----------------") |
| if not match: |
| raise error.TestFail("Timeout elapsed while waiting for test to" |
| " complete") |
| |
| session.close() |
| |
| # Parse test results |
| result_list = scan_results.parse_results(output) |
| |
| # Report test results and check for FAIL/ERROR status |
| logging.info("Results (test, status, duration, info):") |
| status_error = False |
| status_fail = False |
| if result_list == []: |
| status_fail = True |
| message_fail = ("Test '%s' did not produce any recognizable " |
| "results" % test_name) |
| for result in result_list: |
| logging.info(str(result)) |
| if result[1] == "FAIL": |
| status_fail = True |
| message_fail = "Test '%s' ended with FAIL" |
| " (info: '%s')" % (result[0], result[3]) |
| if result[1] == "ERROR": |
| status_error = True |
| message_error = "Test '%s' ended with ERROR" |
| " (info: '%s')" % (result[0], result[3]) |
| if result[1] == "ABORT": |
| status_error = True |
| message_error = "Test '%s' ended with ABORT" |
| " (info: '%s')" % (result[0], result[3]) |
| |
| # Copy test results to the local bindir/guest_results |
| logging.info("Copying results back from guest...") |
| guest_results_dir = os.path.join(test.outputdir, "guest_results") |
| if not os.path.exists(guest_results_dir): |
| os.mkdir(guest_results_dir) |
| if not vm.scp_from_remote("autotest/results/default/*", guest_results_dir): |
| logging.error("Could not copy results back from guest") |
| |
| # Fail the test if necessary |
| if status_fail: |
| raise error.TestFail(message_fail) |
| elif status_error: |
| raise error.TestError(message_error) |
| |
| |
| def internal_yum_update(session, command, prompt, timeout): |
| """ |
| Helper function to perform the yum update test. |
| |
| @param session: SSH session stablished to the host |
| @param command: Command to be sent to the SSH connection |
| @param prompt: Machine prompt |
| @param timeout: How long to wait until we get an appropriate output from |
| the SSH session. |
| """ |
| session.sendline(command) |
| end_time = time.time() + timeout |
| while time.time() < end_time: |
| (match, text) = session.read_until_last_line_matches( |
| ["[Ii]s this [Oo][Kk]", prompt], timeout=timeout) |
| if match == 0: |
| logging.info("Got 'Is this ok'; sending 'y'") |
| session.sendline("y") |
| elif match == 1: |
| logging.info("Got shell prompt") |
| return True |
| else: |
| logging.info("Timeout or process exited") |
| return False |
| |
| |
| def run_yum_update(test, params, env): |
| """ |
| Runs yum update and yum update kernel on the remote host (yum enabled |
| hosts only). |
| |
| @param test: kvm test object. |
| @param params: Dictionary with test parameters. |
| @param env: Dictionary with the test environment. |
| """ |
| vm = kvm_utils.env_get_vm(env, params.get("main_vm")) |
| if not vm: |
| message = "VM object not found in environment" |
| logging.error(message) |
| raise error.TestError(message) |
| if not vm.is_alive(): |
| message = "VM seems to be dead; Test requires a living VM" |
| logging.error(message) |
| raise error.TestError(message) |
| |
| logging.info("Logging into guest...") |
| |
| session = kvm_utils.wait_for(vm.ssh_login, 120, 0, 2) |
| if not session: |
| message = "Could not log into guest" |
| logging.error(message) |
| raise error.TestFail(message) |
| |
| logging.info("Logged in") |
| |
| internal_yum_update(session, "yum update", params.get("ssh_prompt"), 600) |
| internal_yum_update(session, "yum update kernel", |
| params.get("ssh_prompt"), 600) |
| |
| session.close() |
| |
| |
| def run_linux_s3(test, params, env): |
| """ |
| Suspend a guest Linux OS to memory. |
| |
| @param test: kvm test object. |
| @param params: Dictionary with test parameters. |
| @param env: Dictionary with the test environment. |
| """ |
| vm = kvm_utils.env_get_vm(env, params.get("main_vm")) |
| if not vm: |
| raise error.TestError("VM object not found in environment") |
| if not vm.is_alive(): |
| raise error.TestError("VM seems to be dead; Test requires a living VM") |
| |
| logging.info("Waiting for guest to be up...") |
| |
| session = kvm_utils.wait_for(vm.ssh_login, 240, 0, 2) |
| if not session: |
| raise error.TestFail("Could not log into guest") |
| |
| logging.info("Logged in") |
| logging.info("Checking that VM supports S3") |
| |
| status = session.get_command_status("grep -q mem /sys/power/state") |
| if status == None: |
| logging.error("Failed to check if S3 exists") |
| elif status != 0: |
| raise error.TestFail("Guest does not support S3") |
| |
| logging.info("Waiting for a while for X to start") |
| time.sleep(10) |
| |
| src_tty = session.get_command_output("fgconsole").strip() |
| logging.info("Current virtual terminal is %s" % src_tty) |
| if src_tty not in map(str, range(1,10)): |
| raise error.TestFail("Got a strange current vt (%s)" % src_tty) |
| |
| dst_tty = "1" |
| if src_tty == "1": |
| dst_tty = "2" |
| |
| logging.info("Putting VM into S3") |
| command = "chvt %s && echo mem > /sys/power/state && chvt %s" % (dst_tty, |
| src_tty) |
| status = session.get_command_status(command, timeout=120) |
| if status != 0: |
| raise error.TestFail("Suspend to mem failed") |
| |
| logging.info("VM resumed after S3") |
| |
| session.close() |