| 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, | 
|  |