Index: client/tests/kvm/kvm_test_utils.py |
diff --git a/client/tests/kvm/kvm_test_utils.py b/client/tests/kvm/kvm_test_utils.py |
index 014f26573eaf077905ad7064480b8eaddd8fdc89..b5c4a24e642e92a0b49396f25d22f2de6533edff 100644 |
--- a/client/tests/kvm/kvm_test_utils.py |
+++ b/client/tests/kvm/kvm_test_utils.py |
@@ -21,7 +21,7 @@ More specifically: |
@copyright: 2008-2009 Red Hat Inc. |
""" |
-import time, os, logging, re, commands, signal |
+import time, os, logging, re, signal |
from autotest_lib.client.common_lib import error |
from autotest_lib.client.bin import utils |
import kvm_utils, kvm_vm, kvm_subprocess, scan_results |
@@ -35,7 +35,7 @@ def get_living_vm(env, vm_name): |
@param vm_name: Name of the desired VM object. |
@return: A VM object. |
""" |
- vm = kvm_utils.env_get_vm(env, vm_name) |
+ vm = env.get_vm(vm_name) |
if not vm: |
raise error.TestError("VM '%s' not found in environment" % vm_name) |
if not vm.is_alive(): |
@@ -44,21 +44,47 @@ def get_living_vm(env, vm_name): |
return vm |
-def wait_for_login(vm, nic_index=0, timeout=240, start=0, step=2): |
+def wait_for_login(vm, nic_index=0, timeout=240, start=0, step=2, serial=None): |
""" |
Try logging into a VM repeatedly. Stop on success or when timeout expires. |
@param vm: VM object. |
@param nic_index: Index of NIC to access in the VM. |
@param timeout: Time to wait before giving up. |
+ @param serial: Whether to use a serial connection instead of a remote |
+ (ssh, rss) one. |
@return: A shell session object. |
""" |
- logging.info("Trying to log into guest '%s', timeout %ds", vm.name, timeout) |
- session = kvm_utils.wait_for(lambda: vm.remote_login(nic_index=nic_index), |
- timeout, start, step) |
+ end_time = time.time() + timeout |
+ session = None |
+ if serial: |
+ type = 'serial' |
+ logging.info("Trying to log into guest %s using serial connection," |
+ " timeout %ds", vm.name, timeout) |
+ time.sleep(start) |
+ while time.time() < end_time: |
+ try: |
+ session = vm.serial_login() |
+ break |
+ except kvm_utils.LoginError, e: |
+ logging.debug(e) |
+ time.sleep(step) |
+ else: |
+ type = 'remote' |
+ logging.info("Trying to log into guest %s using remote connection," |
+ " timeout %ds", vm.name, timeout) |
+ time.sleep(start) |
+ while time.time() < end_time: |
+ try: |
+ session = vm.login(nic_index=nic_index) |
+ break |
+ except (kvm_utils.LoginError, kvm_vm.VMError), e: |
+ logging.debug(e) |
+ time.sleep(step) |
if not session: |
- raise error.TestFail("Could not log into guest '%s'" % vm.name) |
- logging.info("Logged into guest '%s'" % vm.name) |
+ raise error.TestFail("Could not log into guest %s using %s connection" % |
+ (vm.name, type)) |
+ logging.info("Logged into guest %s using %s connection", vm.name, type) |
return session |
@@ -112,16 +138,14 @@ def reboot(vm, session, method="shell", sleep_before_reset=10, nic_index=0, |
# Try logging into the guest until timeout expires |
logging.info("Guest is down. Waiting for it to go up again, timeout %ds", |
timeout) |
- session = kvm_utils.wait_for(lambda: vm.remote_login(nic_index=nic_index), |
- timeout, 0, 2) |
- if not session: |
- raise error.TestFail("Could not log into guest after reboot") |
+ session = vm.wait_for_login(nic_index, timeout=timeout) |
logging.info("Guest is up again") |
return session |
def migrate(vm, env=None, mig_timeout=3600, mig_protocol="tcp", |
- mig_cancel=False): |
+ mig_cancel=False, offline=False, stable_check=False, |
+ clean=False, save_path=None, dest_host='localhost', mig_port=None): |
""" |
Migrate a VM locally and re-register it in the environment. |
@@ -131,7 +155,10 @@ def migrate(vm, env=None, mig_timeout=3600, mig_protocol="tcp", |
@param mig_timeout: timeout value for migration. |
@param mig_protocol: migration protocol |
@param mig_cancel: Test migrate_cancel or not when protocol is tcp. |
- @return: The post-migration VM. |
+ @param dest_host: Destination host (defaults to 'localhost'). |
+ @param mig_port: Port that will be used for migration. |
+ @return: The post-migration VM, in case of same host migration, True in |
+ case of multi-host migration. |
""" |
def mig_finished(): |
o = vm.monitor.info("migrate") |
@@ -169,38 +196,31 @@ def migrate(vm, env=None, mig_timeout=3600, mig_protocol="tcp", |
raise error.TestFail("Timeout expired while waiting for migration " |
"to finish") |
- dest_vm = vm.clone() |
+ if dest_host == 'localhost': |
+ dest_vm = vm.clone() |
- if mig_protocol == "exec": |
- # Exec is a little different from other migrate methods - first we |
- # ask the monitor the migration, then the vm state is dumped to a |
- # compressed file, then we start the dest vm with -incoming pointing |
- # to it |
- try: |
- exec_file = "/tmp/exec-%s.gz" % kvm_utils.generate_random_string(8) |
- exec_cmd = "gzip -c -d %s" % exec_file |
- uri = '"exec:gzip -c > %s"' % exec_file |
- vm.monitor.cmd("stop") |
- vm.monitor.migrate(uri) |
- wait_for_migration() |
+ if (dest_host == 'localhost') and stable_check: |
+ # Pause the dest vm after creation |
+ dest_vm.params['extra_params'] = (dest_vm.params.get('extra_params','') |
+ + ' -S') |
- if not dest_vm.create(migration_mode=mig_protocol, |
- migration_exec_cmd=exec_cmd, mac_source=vm): |
- raise error.TestError("Could not create dest VM") |
- finally: |
- logging.debug("Removing migration file %s", exec_file) |
- try: |
- os.remove(exec_file) |
- except OSError: |
- pass |
- else: |
- if not dest_vm.create(migration_mode=mig_protocol, mac_source=vm): |
- raise error.TestError("Could not create dest VM") |
+ if dest_host == 'localhost': |
+ dest_vm.create(migration_mode=mig_protocol, mac_source=vm) |
+ |
+ try: |
try: |
if mig_protocol == "tcp": |
- uri = "tcp:localhost:%d" % dest_vm.migration_port |
+ if dest_host == 'localhost': |
+ uri = "tcp:localhost:%d" % dest_vm.migration_port |
+ else: |
+ uri = 'tcp:%s:%d' % (dest_host, mig_port) |
elif mig_protocol == "unix": |
uri = "unix:%s" % dest_vm.migration_file |
+ elif mig_protocol == "exec": |
+ uri = '"exec:nc localhost %s"' % dest_vm.migration_port |
+ |
+ if offline: |
+ vm.monitor.cmd("stop") |
vm.monitor.migrate(uri) |
if mig_cancel: |
@@ -210,14 +230,43 @@ def migrate(vm, env=None, mig_timeout=3600, mig_protocol="tcp", |
"Waiting for migration " |
"cancellation"): |
raise error.TestFail("Failed to cancel migration") |
- dest_vm.destroy(gracefully=False) |
+ if offline: |
+ vm.monitor.cmd("cont") |
+ if dest_host == 'localhost': |
+ dest_vm.destroy(gracefully=False) |
return vm |
else: |
wait_for_migration() |
+ if (dest_host == 'localhost') and stable_check: |
+ save_path = None or "/tmp" |
+ save1 = os.path.join(save_path, "src") |
+ save2 = os.path.join(save_path, "dst") |
+ |
+ vm.save_to_file(save1) |
+ dest_vm.save_to_file(save2) |
+ |
+ # Fail if we see deltas |
+ md5_save1 = utils.hash_file(save1) |
+ md5_save2 = utils.hash_file(save2) |
+ if md5_save1 != md5_save2: |
+ raise error.TestFail("Mismatch of VM state before " |
+ "and after migration") |
+ |
+ if (dest_host == 'localhost') and offline: |
+ dest_vm.monitor.cmd("cont") |
except: |
- dest_vm.destroy() |
+ if dest_host == 'localhost': |
+ dest_vm.destroy() |
raise |
+ finally: |
+ if (dest_host == 'localhost') and stable_check and clean: |
+ logging.debug("Cleaning the state files") |
+ if os.path.isfile(save1): |
+ os.remove(save1) |
+ if os.path.isfile(save2): |
+ os.remove(save2) |
+ |
# Report migration status |
if mig_succeeded(): |
logging.info("Migration finished successfully") |
@@ -226,19 +275,23 @@ def migrate(vm, env=None, mig_timeout=3600, mig_protocol="tcp", |
else: |
raise error.TestFail("Migration ended with unknown status") |
- if "paused" in dest_vm.monitor.info("status"): |
- logging.debug("Destination VM is paused, resuming it...") |
- dest_vm.monitor.cmd("cont") |
+ if dest_host == 'localhost': |
+ if "paused" in dest_vm.monitor.info("status"): |
+ logging.debug("Destination VM is paused, resuming it...") |
+ dest_vm.monitor.cmd("cont") |
# Kill the source VM |
vm.destroy(gracefully=False) |
# Replace the source VM with the new cloned VM |
- if env is not None: |
- kvm_utils.env_register_vm(env, vm.name, dest_vm) |
+ if (dest_host == 'localhost') and (env is not None): |
+ env.register_vm(vm.name, dest_vm) |
# Return the new cloned VM |
- return dest_vm |
+ if dest_host == 'localhost': |
+ return dest_vm |
+ else: |
+ return vm |
def stop_windows_service(session, service, timeout=120): |
@@ -252,7 +305,7 @@ def stop_windows_service(session, service, timeout=120): |
""" |
end_time = time.time() + timeout |
while time.time() < end_time: |
- o = session.get_command_output("sc stop %s" % service, timeout=60) |
+ o = session.cmd_output("sc stop %s" % service, timeout=60) |
# FAILED 1060 means the service isn't installed. |
# FAILED 1062 means the service hasn't been started. |
if re.search(r"\bFAILED (1060|1062)\b", o, re.I): |
@@ -274,7 +327,7 @@ def start_windows_service(session, service, timeout=120): |
""" |
end_time = time.time() + timeout |
while time.time() < end_time: |
- o = session.get_command_output("sc start %s" % service, timeout=60) |
+ o = session.cmd_output("sc start %s" % service, timeout=60) |
# FAILED 1060 means the service isn't installed. |
if re.search(r"\bFAILED 1060\b", o, re.I): |
raise error.TestError("Could not start service '%s' " |
@@ -306,31 +359,26 @@ def get_time(session, time_command, time_filter_re, time_format): |
""" |
if len(re.findall("ntpdate|w32tm", time_command)) == 0: |
host_time = time.time() |
- session.sendline(time_command) |
- (match, s) = session.read_up_to_prompt() |
- if not match: |
- raise error.TestError("Could not get guest time") |
+ s = session.cmd_output(time_command) |
try: |
s = re.findall(time_filter_re, s)[0] |
except IndexError: |
- logging.debug("The time string from guest is:\n%s" % s) |
+ logging.debug("The time string from guest is:\n%s", s) |
raise error.TestError("The time string from guest is unexpected.") |
except Exception, e: |
- logging.debug("(time_filter_re, time_string): (%s, %s)" % |
- (time_filter_re, s)) |
+ logging.debug("(time_filter_re, time_string): (%s, %s)", |
+ time_filter_re, s) |
raise e |
guest_time = time.mktime(time.strptime(s, time_format)) |
else: |
- s , o = session.get_command_status_output(time_command) |
- if s != 0: |
- raise error.TestError("Could not get guest time") |
+ o = session.cmd(time_command) |
if re.match('ntpdate', time_command): |
- offset = re.findall('offset (.*) sec',o)[0] |
+ offset = re.findall('offset (.*) sec', o)[0] |
host_main, host_mantissa = re.findall(time_filter_re, o)[0] |
- host_time = time.mktime(time.strptime(host_main, time_format)) \ |
- + float("0.%s" % host_mantissa) |
+ host_time = (time.mktime(time.strptime(host_main, time_format)) + |
+ float("0.%s" % host_mantissa)) |
guest_time = host_time + float(offset) |
else: |
guest_time = re.findall(time_filter_re, o)[0] |
@@ -381,7 +429,7 @@ def get_memory_info(lvms): |
return meminfo |
-def run_autotest(vm, session, control_path, timeout, outputdir): |
+def run_autotest(vm, session, control_path, timeout, outputdir, params): |
""" |
Run an autotest control file inside a guest (linux only utility). |
@@ -391,6 +439,9 @@ def run_autotest(vm, session, control_path, timeout, outputdir): |
@param timeout: Timeout under which the autotest control file must complete. |
@param outputdir: Path on host where we should copy the guest autotest |
results to. |
+ |
+ The following params is used by the migration |
+ @param params: Test params used in the migration test |
""" |
def copy_if_hash_differs(vm, local_path, remote_path): |
""" |
@@ -400,10 +451,9 @@ def run_autotest(vm, session, control_path, timeout, outputdir): |
@param local_path: Local path. |
@param remote_path: Remote path. |
""" |
- copy = False |
local_hash = utils.hash_file(local_path) |
basename = os.path.basename(local_path) |
- output = session.get_command_output("md5sum %s" % remote_path) |
+ output = session.cmd_output("md5sum %s" % remote_path) |
if "such file" in output: |
remote_hash = "0" |
elif output: |
@@ -414,14 +464,9 @@ def run_autotest(vm, session, control_path, timeout, outputdir): |
# Let's be a little more lenient here and see if it wasn't a |
# temporary problem |
remote_hash = "0" |
- |
if remote_hash != local_hash: |
logging.debug("Copying %s to guest", basename) |
- copy = True |
- |
- if copy: |
- if not vm.copy_files_to(local_path, remote_path): |
- raise error.TestFail("Could not copy %s to guest" % local_path) |
+ vm.copy_files_to(local_path, remote_path) |
def extract(vm, remote_path, dest_dir="."): |
@@ -435,10 +480,7 @@ def run_autotest(vm, session, control_path, timeout, outputdir): |
basename = os.path.basename(remote_path) |
logging.info("Extracting %s...", basename) |
e_cmd = "tar xjvf %s -C %s" % (remote_path, dest_dir) |
- s, o = session.get_command_status_output(e_cmd, timeout=120) |
- if s != 0: |
- logging.error("Uncompress output:\n%s", o) |
- raise error.TestFail("Failed to extract %s on guest" % basename) |
+ session.cmd(e_cmd, timeout=120) |
def get_results(): |
@@ -449,9 +491,8 @@ def run_autotest(vm, session, control_path, timeout, outputdir): |
guest_results_dir = os.path.join(outputdir, "guest_autotest_results") |
if not os.path.exists(guest_results_dir): |
os.mkdir(guest_results_dir) |
- if not vm.copy_files_from("%s/results/default/*" % autotest_path, |
- guest_results_dir): |
- logging.error("Could not copy autotest results from guest") |
+ vm.copy_files_from("%s/results/default/*" % autotest_path, |
+ guest_results_dir) |
def get_results_summary(): |
@@ -459,7 +500,7 @@ def run_autotest(vm, session, control_path, timeout, outputdir): |
Get the status of the tests that were executed on the host and close |
the session where autotest was being executed. |
""" |
- output = session.get_command_output("cat results/*/status") |
+ output = session.cmd_output("cat results/*/status") |
try: |
results = scan_results.parse_results(output) |
# Report test results |
@@ -477,6 +518,11 @@ def run_autotest(vm, session, control_path, timeout, outputdir): |
raise error.TestError("Invalid path to autotest control file: %s" % |
control_path) |
+ migrate_background = params.get("migrate_background") == "yes" |
+ if migrate_background: |
+ mig_timeout = float(params.get("mig_timeout", "3600")) |
+ mig_protocol = params.get("migration_protocol", "tcp") |
+ |
compressed_autotest_path = "/tmp/autotest.tar.bz2" |
# To avoid problems, let's make the test use the current AUTODIR |
@@ -501,33 +547,56 @@ def run_autotest(vm, session, control_path, timeout, outputdir): |
# Extract autotest.tar.bz2 |
extract(vm, compressed_autotest_path, "/") |
- if not vm.copy_files_to(control_path, |
- os.path.join(autotest_path, 'control')): |
- raise error.TestFail("Could not copy the test control file to guest") |
+ vm.copy_files_to(control_path, os.path.join(autotest_path, 'control')) |
# Run the test |
logging.info("Running autotest control file %s on guest, timeout %ss", |
os.path.basename(control_path), timeout) |
- session.get_command_output("cd %s" % autotest_path) |
- session.get_command_output("rm -f control.state") |
- session.get_command_output("rm -rf results/*") |
- logging.info("---------------- Test output ----------------") |
- status = session.get_command_status("bin/autotest control", |
- timeout=timeout, |
- print_func=logging.info) |
- logging.info("------------- End of test output ------------") |
- if status is None: |
- if not vm.is_alive(): |
- raise error.TestError("Autotest job on guest failed " |
- "(VM terminated during job)") |
- if not session.is_alive(): |
+ session.cmd("cd %s" % autotest_path) |
+ try: |
+ session.cmd("rm -f control.state") |
+ session.cmd("rm -rf results/*") |
+ except kvm_subprocess.ShellError: |
+ pass |
+ try: |
+ bg = None |
+ try: |
+ logging.info("---------------- Test output ----------------") |
+ if migrate_background: |
+ mig_timeout = float(params.get("mig_timeout", "3600")) |
+ mig_protocol = params.get("migration_protocol", "tcp") |
+ |
+ bg = kvm_utils.Thread(session.cmd_output, |
+ kwargs={'cmd': "bin/autotest control", |
+ 'timeout': timeout, |
+ 'print_func': logging.info}) |
+ |
+ bg.start() |
+ |
+ while bg.is_alive(): |
+ logging.info("Tests is not ended, start a round of" |
+ "migration ...") |
+ vm.migrate(timeout=mig_timeout, protocol=mig_protocol) |
+ else: |
+ session.cmd_output("bin/autotest control", timeout=timeout, |
+ print_func=logging.info) |
+ finally: |
+ logging.info("------------- End of test output ------------") |
+ if migrate_background and bg: |
+ bg.join() |
+ except kvm_subprocess.ShellTimeoutError: |
+ if vm.is_alive(): |
get_results() |
+ get_results_summary() |
+ raise error.TestError("Timeout elapsed while waiting for job to " |
+ "complete") |
+ else: |
raise error.TestError("Autotest job on guest failed " |
- "(Remote session terminated during job)") |
+ "(VM terminated during job)") |
+ except kvm_subprocess.ShellProcessTerminatedError: |
get_results() |
- get_results_summary() |
- raise error.TestError("Timeout elapsed while waiting for job to " |
- "complete") |
+ raise error.TestError("Autotest job on guest failed " |
+ "(Remote session terminated during job)") |
results = get_results_summary() |
get_results() |
@@ -589,21 +658,25 @@ def raw_ping(command, timeout, session, output_func): |
process.close() |
return status, output |
else: |
- session.sendline(command) |
- status, output = session.read_up_to_prompt(timeout=timeout, |
- print_func=output_func) |
- if not status: |
+ output = "" |
+ try: |
+ output = session.cmd_output(command, timeout=timeout, |
+ print_func=output_func) |
+ except kvm_subprocess.ShellTimeoutError: |
# Send ctrl+c (SIGINT) through ssh session |
session.send("\003") |
- status, output2 = session.read_up_to_prompt(print_func=output_func) |
- output += output2 |
- if not status: |
+ try: |
+ output2 = session.read_up_to_prompt(print_func=output_func) |
+ output += output2 |
+ except kvm_subprocess.ExpectTimeoutError, e: |
+ output += e.output |
# We also need to use this session to query the return value |
session.send("\003") |
session.sendline(session.status_test_command) |
- s2, o2 = session.read_up_to_prompt() |
- if not s2: |
+ try: |
+ o2 = session.read_up_to_prompt() |
+ except kvm_subprocess.ExpectError: |
status = -1 |
else: |
try: |
@@ -670,7 +743,7 @@ def get_linux_ifname(session, mac_address): |
@mac_address: the macaddress of nic |
""" |
- output = session.get_command_output("ifconfig -a") |
+ output = session.cmd_output("ifconfig -a") |
try: |
ethname = re.findall("(\w+)\s+Link.*%s" % mac_address, output, |