With this, we remove all dependencies on virt modules in virt_utils, eliminating a source of circular deps. Also, move some of the auxiliary multi host migration classes to virt_storage, where they are more appropriate.
Signed-off-by: Lucas Meneghel Rodrigues <l...@redhat.com> --- client/tests/kvm/tests/migration_multi_host.py | 4 +- client/tests/kvm/tests/migration_multi_host_fd.py | 4 +- .../migration_multi_host_with_file_transfer.py | 7 +- .../migration_multi_host_with_speed_measurement.py | 6 +- client/virt/virt_storage.py | 21 +- client/virt/virt_test_utils.py | 474 ++++++++++++++++++- client/virt/virt_utils.py | 492 +------------------- 7 files changed, 505 insertions(+), 503 deletions(-) diff --git a/client/tests/kvm/tests/migration_multi_host.py b/client/tests/kvm/tests/migration_multi_host.py index 187c0a0..2557933 100644 --- a/client/tests/kvm/tests/migration_multi_host.py +++ b/client/tests/kvm/tests/migration_multi_host.py @@ -1,4 +1,4 @@ -from autotest.client.virt import virt_utils +from autotest.client.virt import virt_test_utils def run_migration_multi_host(test, params, env): @@ -12,7 +12,7 @@ def run_migration_multi_host(test, params, env): @param params: Dictionary with test parameters. @param env: Dictionary with the test environment. """ - class TestMultihostMigration(virt_utils.MultihostMigration): + class TestMultihostMigration(virt_test_utils.MultihostMigration): def __init__(self, test, params, env): super(TestMultihostMigration, self).__init__(test, params, env) diff --git a/client/tests/kvm/tests/migration_multi_host_fd.py b/client/tests/kvm/tests/migration_multi_host_fd.py index 6f3c72b..04bb758 100644 --- a/client/tests/kvm/tests/migration_multi_host_fd.py +++ b/client/tests/kvm/tests/migration_multi_host_fd.py @@ -1,5 +1,5 @@ import logging, socket, time, errno, os, fcntl -from autotest.client.virt import virt_utils +from autotest.client.virt import virt_test_utils, virt_utils from autotest.client.shared.syncdata import SyncData def run_migration_multi_host_fd(test, params, env): @@ -14,7 +14,7 @@ def run_migration_multi_host_fd(test, params, env): @param params: Dictionary with test parameters. @param env: Dictionary with the test environment. """ - class TestMultihostMigrationFd(virt_utils.MultihostMigration): + class TestMultihostMigrationFd(virt_test_utils.MultihostMigration): def __init__(self, test, params, env): super(TestMultihostMigrationFd, self).__init__(test, params, env) diff --git a/client/tests/kvm/tests/migration_multi_host_with_file_transfer.py b/client/tests/kvm/tests/migration_multi_host_with_file_transfer.py index 976b268..bbe025f 100644 --- a/client/tests/kvm/tests/migration_multi_host_with_file_transfer.py +++ b/client/tests/kvm/tests/migration_multi_host_with_file_transfer.py @@ -2,7 +2,8 @@ import logging, threading from autotest.client import utils as client_utils from autotest.client.shared import utils, error from autotest.client.shared.syncdata import SyncData -from autotest.client.virt import virt_env_process, virt_utils, virt_remote +from autotest.client.virt import virt_env_process, virt_test_utils, virt_remote +from autotest.client.virt import virt_utils @error.context_aware @@ -65,7 +66,7 @@ def run_migration_multi_host_with_file_transfer(test, params, env): #Count of migration during file transfer. migrate_count = int(params.get("migrate_count", "3")) - class TestMultihostMigration(virt_utils.MultihostMigration): + class TestMultihostMigration(virt_test_utils.MultihostMigration): def __init__(self, test, params, env): super(TestMultihostMigration, self).__init__(test, params, env) self.vm = None @@ -85,7 +86,7 @@ def run_migration_multi_host_with_file_transfer(test, params, env): @param mig_data: object with migration data. """ for vm in mig_data.vms: - if not virt_utils.guest_active(vm): + if not virt_test_utils.guest_active(vm): raise error.TestFail("Guest not active after migration") logging.info("Migrated guest appears to be running") diff --git a/client/tests/kvm/tests/migration_multi_host_with_speed_measurement.py b/client/tests/kvm/tests/migration_multi_host_with_speed_measurement.py index 887cfe3..9ef255e 100644 --- a/client/tests/kvm/tests/migration_multi_host_with_speed_measurement.py +++ b/client/tests/kvm/tests/migration_multi_host_with_speed_measurement.py @@ -1,8 +1,8 @@ import os, re, logging, time, socket -from autotest.client.virt import virt_utils from autotest.client.shared import error, utils from autotest.client.shared.barrier import listen_server from autotest.client.shared.syncdata import SyncData +from autotest.client.virt import virt_test_utils, virt_utils def run_migration_multi_host_with_speed_measurement(test, params, env): @@ -71,7 +71,7 @@ def run_migration_multi_host_with_speed_measurement(test, params, env): return mig_stat - class TestMultihostMigration(virt_utils.MultihostMigration): + class TestMultihostMigration(virt_test_utils.MultihostMigration): def __init__(self, test, params, env): super(TestMultihostMigration, self).__init__(test, params, env) self.mig_stat = None @@ -117,7 +117,7 @@ def run_migration_multi_host_with_speed_measurement(test, params, env): session = vm.wait_for_login(timeout=self.login_timeout) virt_utils.install_cpuflags_util_on_vm(test, vm, install_path, - extra_flags="-msse3 -msse2") + extra_flags="-msse3 -msse2") cmd = ("%s/cpuflags-test --stressmem %d" % (os.path.join(install_path, "test_cpu_flags"), vm_mem / 2)) diff --git a/client/virt/virt_storage.py b/client/virt/virt_storage.py index a56f50a..1c89521 100644 --- a/client/virt/virt_storage.py +++ b/client/virt/virt_storage.py @@ -9,7 +9,26 @@ import logging, os, shutil, re from autotest.client import utils import virt_utils, virt_vm -# Functions for handling virtual machine image files + +def preprocess_images(bindir, params, env): + # Clone master image form vms. + for vm_name in params.get("vms").split(): + vm = env.get_vm(vm_name) + if vm: + vm.destroy(free_mac_addresses=False) + vm_params = params.object_params(vm_name) + for image in vm_params.get("master_images_clone").split(): + image_obj = QemuImg(params, bindir, image) + image_obj.clone_image(params, vm_name, image, bindir) + + +def postprocess_images(bindir, params): + for vm in params.get("vms").split(): + vm_params = params.object_params(vm) + for image in vm_params.get("master_images_clone").split(): + image_obj = QemuImg(params, bindir, image) + image_obj.rm_clone_image(params, vm, image, bindir) + def get_image_blkdebug_filename(params, root_dir): """ diff --git a/client/virt/virt_test_utils.py b/client/virt/virt_test_utils.py index 60781b8..efcabbb 100644 --- a/client/virt/virt_test_utils.py +++ b/client/virt/virt_test_utils.py @@ -27,7 +27,8 @@ from Queue import Queue from autotest.client.shared import error, global_config from autotest.client import utils from autotest.client.tools import scan_results -import aexpect, virt_utils, virt_vm, virt_remote +from autotest.client.shared.syncdata import SyncData, SyncListenServer +import aexpect, virt_utils, virt_vm, virt_remote, virt_storage, virt_env_process GLOBAL_CONFIG = global_config.global_config @@ -298,6 +299,475 @@ def migrate(vm, env=None, mig_timeout=3600, mig_protocol="tcp", return vm +def guest_active(vm): + o = vm.monitor.info("status") + if isinstance(o, str): + return "status: running" in o + else: + if "status" in o: + return o.get("status") == "running" + else: + return o.get("running") + + +class MigrationData(object): + def __init__(self, params, srchost, dsthost, vms_name, params_append): + """ + Class that contains data needed for one migration. + """ + self.params = params.copy() + self.params.update(params_append) + + self.source = False + if params.get("hostid") == srchost: + self.source = True + + self.destination = False + if params.get("hostid") == dsthost: + self.destination = True + + self.src = srchost + self.dst = dsthost + self.hosts = [srchost, dsthost] + self.mig_id = {'src': srchost, 'dst': dsthost, "vms": vms_name} + self.vms_name = vms_name + self.vms = [] + self.vm_ports = None + + + def is_src(self): + """ + @return: True if host is source. + """ + return self.source + + + def is_dst(self): + """ + @return: True if host is destination. + """ + return self.destination + + +class MultihostMigration(object): + """ + Class that provides a framework for multi-host migration. + + Migration can be run both synchronously and asynchronously. + To specify what is going to happen during the multi-host + migration, it is necessary to reimplement the method + migration_scenario. It is possible to start multiple migrations + in separate threads, since self.migrate is thread safe. + + Only one test using multihost migration framework should be + started on one machine otherwise it is necessary to solve the + problem with listen server port. + + Multihost migration starts SyncListenServer through which + all messages are transfered, since the multiple hosts can + be in diferent states. + + Class SyncData is used to transfer data over network or + synchronize the migration process. Synchronization sessions + are recognized by session_id. + + It is important to note that, in order to have multi-host + migration, one needs shared guest image storage. The simplest + case is when the guest images are on an NFS server. + + Example: + class TestMultihostMigration(virt_utils.MultihostMigration): + def __init__(self, test, params, env): + super(testMultihostMigration, self).__init__(test, params, env) + + def migration_scenario(self): + srchost = self.params.get("hosts")[0] + dsthost = self.params.get("hosts")[1] + + def worker(mig_data): + vm = env.get_vm("vm1") + session = vm.wait_for_login(timeout=self.login_timeout) + session.sendline("nohup dd if=/dev/zero of=/dev/null &") + session.cmd("killall -0 dd") + + def check_worker(mig_data): + vm = env.get_vm("vm1") + session = vm.wait_for_login(timeout=self.login_timeout) + session.cmd("killall -9 dd") + + # Almost synchronized migration, waiting to end it. + # Work is started only on first VM. + self.migrate_wait(["vm1", "vm2"], srchost, dsthost, + worker, check_worker) + + # Migration started in different threads. + # It allows to start multiple migrations simultaneously. + mig1 = self.migrate(["vm1"], srchost, dsthost, + worker, check_worker) + mig2 = self.migrate(["vm2"], srchost, dsthost) + mig2.join() + mig1.join() + + mig = TestMultihostMigration(test, params, env) + mig.run() + """ + def __init__(self, test, params, env, preprocess_env=True): + self.test = test + self.params = params + self.env = env + self.hosts = params.get("hosts") + self.hostid = params.get('hostid', "") + self.comm_port = int(params.get("comm_port", 13234)) + vms_count = len(params["vms"].split()) + + self.login_timeout = int(params.get("login_timeout", 360)) + self.disk_prepare_timeout = int(params.get("disk_prepare_timeout", + 160 * vms_count)) + self.finish_timeout = int(params.get("finish_timeout", + 120 * vms_count)) + + self.new_params = None + + if params.get("clone_master") == "yes": + self.clone_master = True + else: + self.clone_master = False + + self.mig_timeout = int(params.get("mig_timeout")) + # Port used to communicate info between source and destination + self.regain_ip_cmd = params.get("regain_ip_cmd", "dhclient") + + self.vm_lock = threading.Lock() + + self.sync_server = None + if self.clone_master: + self.sync_server = SyncListenServer() + + if preprocess_env: + self.preprocess_env() + self._hosts_barrier(self.hosts, self.hosts, 'disk_prepared', + self.disk_prepare_timeout) + + + def migration_scenario(self): + """ + Multi Host migration_scenario is started from method run where the + exceptions are checked. It is not necessary to take care of + cleaning up after test crash or finish. + """ + raise NotImplementedError + + + def migrate_vms_src(self, mig_data): + """ + Migrate vms source. + + @param mig_Data: Data for migration. + + For change way how machine migrates is necessary + re implement this method. + """ + def mig_wrapper(vm, dsthost, vm_ports): + vm.migrate(dest_host=dsthost, remote_port=vm_ports[vm.name]) + + logging.info("Start migrating now...") + multi_mig = [] + for vm in mig_data.vms: + multi_mig.append((mig_wrapper, (vm, mig_data.dst, + mig_data.vm_ports))) + virt_utils.parallel(multi_mig) + + + def migrate_vms_dest(self, mig_data): + """ + Migrate vms destination. This function is started on dest host during + migration. + + @param mig_Data: Data for migration. + """ + pass + + + def __del__(self): + if self.sync_server: + self.sync_server.close() + + + def master_id(self): + return self.hosts[0] + + + def _hosts_barrier(self, hosts, session_id, tag, timeout): + logging.debug("Barrier timeout: %d tags: %s" % (timeout, tag)) + tags = SyncData(self.master_id(), self.hostid, hosts, + "%s,%s,barrier" % (str(session_id), tag), + self.sync_server).sync(tag, timeout) + logging.debug("Barrier tag %s" % (tags)) + + + def preprocess_env(self): + """ + Prepare env to start vms. + """ + virt_storage.preprocess_images(self.test.bindir, self.params, self.env) + + + def _check_vms_source(self, mig_data): + for vm in mig_data.vms: + vm.wait_for_login(timeout=self.login_timeout) + + sync = SyncData(self.master_id(), self.hostid, mig_data.hosts, + mig_data.mig_id, self.sync_server) + mig_data.vm_ports = sync.sync(timeout=120)[mig_data.dst] + logging.info("Received from destination the migration port %s", + str(mig_data.vm_ports)) + + + def _check_vms_dest(self, mig_data): + mig_data.vm_ports = {} + for vm in mig_data.vms: + logging.info("Communicating to source migration port %s", + vm.migration_port) + mig_data.vm_ports[vm.name] = vm.migration_port + + SyncData(self.master_id(), self.hostid, + mig_data.hosts, mig_data.mig_id, + self.sync_server).sync(mig_data.vm_ports, timeout=120) + + + def _prepare_params(self, mig_data): + """ + Prepare separate params for vm migration. + + @param vms_name: List of vms. + """ + new_params = mig_data.params.copy() + new_params["vms"] = " ".join(mig_data.vms_name) + return new_params + + + def _check_vms(self, mig_data): + """ + Check if vms are started correctly. + + @param vms: list of vms. + @param source: Must be True if is source machine. + """ + logging.info("Try check vms %s" % (mig_data.vms_name)) + for vm in mig_data.vms_name: + if not self.env.get_vm(vm) in mig_data.vms: + mig_data.vms.append(self.env.get_vm(vm)) + for vm in mig_data.vms: + logging.info("Check vm %s on host %s" % (vm.name, self.hostid)) + vm.verify_alive() + + if mig_data.is_src(): + self._check_vms_source(mig_data) + else: + self._check_vms_dest(mig_data) + + + def prepare_for_migration(self, mig_data, migration_mode): + """ + Prepare destination of migration for migration. + + @param mig_data: Class with data necessary for migration. + @param migration_mode: Migration mode for prepare machine. + """ + new_params = self._prepare_params(mig_data) + + new_params['migration_mode'] = migration_mode + new_params['start_vm'] = 'yes' + self.vm_lock.acquire() + virt_env_process.process(self.test, new_params, self.env, + virt_env_process.preprocess_image, + virt_env_process.preprocess_vm) + self.vm_lock.release() + + self._check_vms(mig_data) + + + def migrate_vms(self, mig_data): + """ + Migrate vms. + """ + if mig_data.is_src(): + self.migrate_vms_src(mig_data) + else: + self.migrate_vms_dest(mig_data) + + + def check_vms(self, mig_data): + """ + Check vms after migrate. + + @param mig_data: object with migration data. + """ + for vm in mig_data.vms: + if not guest_active(vm): + raise error.TestFail("Guest not active after migration") + + logging.info("Migrated guest appears to be running") + + logging.info("Logging into migrated guest after migration...") + for vm in mig_data.vms: + session_serial = vm.wait_for_serial_login(timeout= + self.login_timeout) + #There is sometime happen that system sends some message on + #serial console and IP renew command block test. Because + #there must be added "sleep" in IP renew command. + session_serial.cmd(self.regain_ip_cmd) + vm.wait_for_login(timeout=self.login_timeout) + + + def postprocess_env(self): + """ + Kill vms and delete cloned images. + """ + virt_storage.postprocess_images(self.test.bindir, self.params) + + + def migrate(self, vms_name, srchost, dsthost, start_work=None, + check_work=None, mig_mode="tcp", params_append=None): + """ + Migrate machine from srchost to dsthost. It executes start_work on + source machine before migration and executes check_work on dsthost + after migration. + + Migration execution progress: + + source host | dest host + -------------------------------------------------------- + prepare guest on both sides of migration + - start machine and check if machine works + - synchronize transfer data needed for migration + -------------------------------------------------------- + start work on source guests | wait for migration + -------------------------------------------------------- + migrate guest to dest host. + wait on finish migration synchronization + -------------------------------------------------------- + | check work on vms + -------------------------------------------------------- + wait for sync on finish migration + + @param vms_name: List of vms. + @param srchost: src host id. + @param dsthost: dst host id. + @param start_work: Function started before migration. + @param check_work: Function started after migration. + @param mig_mode: Migration mode. + @param params_append: Append params to self.params only for migration. + """ + def migrate_wrap(vms_name, srchost, dsthost, start_work=None, + check_work=None, params_append=None): + logging.info("Starting migrate vms %s from host %s to %s" % + (vms_name, srchost, dsthost)) + error = None + mig_data = MigrationData(self.params, srchost, dsthost, + vms_name, params_append) + try: + try: + if mig_data.is_src(): + self.prepare_for_migration(mig_data, None) + elif self.hostid == dsthost: + self.prepare_for_migration(mig_data, mig_mode) + else: + return + + if mig_data.is_src(): + if start_work: + start_work(mig_data) + + self.migrate_vms(mig_data) + + timeout = 30 + if not mig_data.is_src(): + timeout = self.mig_timeout + self._hosts_barrier(mig_data.hosts, mig_data.mig_id, + 'mig_finished', timeout) + + if mig_data.is_dst(): + self.check_vms(mig_data) + if check_work: + check_work(mig_data) + + except: + error = True + raise + finally: + if not error: + self._hosts_barrier(self.hosts, + mig_data.mig_id, + 'test_finihed', + self.finish_timeout) + + def wait_wrap(vms_name, srchost, dsthost): + mig_data = MigrationData(self.params, srchost, dsthost, vms_name, + None) + timeout = (self.login_timeout + self.mig_timeout + + self.finish_timeout) + + self._hosts_barrier(self.hosts, mig_data.mig_id, + 'test_finihed', timeout) + + if (self.hostid in [srchost, dsthost]): + mig_thread = utils.InterruptedThread(migrate_wrap, (vms_name, + srchost, + dsthost, + start_work, + check_work, + params_append)) + else: + mig_thread = utils.InterruptedThread(wait_wrap, (vms_name, + srchost, + dsthost)) + mig_thread.start() + return mig_thread + + + def migrate_wait(self, vms_name, srchost, dsthost, start_work=None, + check_work=None, mig_mode="tcp", params_append=None): + """ + Migrate machine from srchost to dsthost and wait for finish. + It executes start_work on source machine before migration and executes + check_work on dsthost after migration. + + @param vms_name: List of vms. + @param srchost: src host id. + @param dsthost: dst host id. + @param start_work: Function which is started before migration. + @param check_work: Function which is started after + done of migration. + """ + self.migrate(vms_name, srchost, dsthost, start_work, check_work, + mig_mode, params_append).join() + + + def cleanup(self): + """ + Cleanup env after test. + """ + if self.clone_master: + self.sync_server.close() + self.postprocess_env() + + + def run(self): + """ + Start multihost migration scenario. + After scenario is finished or if scenario crashed it calls postprocess + machines and cleanup env. + """ + try: + self.migration_scenario() + + self._hosts_barrier(self.hosts, self.hosts, 'all_test_finihed', + self.finish_timeout) + finally: + self.cleanup() + + def stop_windows_service(session, service, timeout=120): """ Stop a Windows service using sc. @@ -910,6 +1380,7 @@ def run_virt_sub_test(test, params, env, sub_type=None, tag=None): params = params.object_params(tag) run_func(test, params, env) + def pin_vm_threads(vm, node): """ Pin VM threads to single cpu of a numa node @@ -921,6 +1392,7 @@ def pin_vm_threads(vm, node): for i in vm.vcpu_threads: logging.info("pin vcpu thread(%s) to cpu(%s)" % (i, node.pin_cpu(i))) + def service_setup(vm, session, dir): params = vm.get_params() diff --git a/client/virt/virt_utils.py b/client/virt/virt_utils.py index 1284fc2..46d4598 100644 --- a/client/virt/virt_utils.py +++ b/client/virt/virt_utils.py @@ -5,13 +5,11 @@ Virtualization test utility functions. """ import time, string, random, socket, os, signal, re, logging, commands, cPickle -import fcntl, shelve, ConfigParser, threading, sys, UserDict, inspect, tarfile +import fcntl, shelve, ConfigParser, sys, UserDict, inspect, tarfile import struct, shutil, glob, HTMLParser, urllib, traceback, platform from autotest.client import utils, os_dep from autotest.client.shared import error, logging_config from autotest.client.shared import logging_manager, git -from autotest.client.shared.syncdata import SyncData, SyncListenServer -import virt_env_process, virt_storage try: import koji @@ -4199,494 +4197,6 @@ def generate_mac_address_simple(): return mac -def guest_active(vm): - o = vm.monitor.info("status") - if isinstance(o, str): - return "status: running" in o - else: - if "status" in o: - return o.get("status") == "running" - else: - return o.get("running") - - -def preprocess_images(bindir, params, env): - # Clone master image form vms. - for vm_name in params.get("vms").split(): - vm = env.get_vm(vm_name) - if vm: - vm.destroy(free_mac_addresses=False) - vm_params = params.object_params(vm_name) - for image in vm_params.get("master_images_clone").split(): - image_obj = virt_storage.QemuImg(params, bindir, image) - image_obj.clone_image(params, vm_name, image, bindir) - - -def postprocess_images(bindir, params): - for vm in params.get("vms").split(): - vm_params = params.object_params(vm) - for image in vm_params.get("master_images_clone").split(): - image_obj = virt_storage.QemuImg(params, bindir, image) - image_obj.rm_clone_image(params, vm, image, bindir) - - -class MigrationData(object): - def __init__(self, params, srchost, dsthost, vms_name, params_append): - """ - Class that contains data needed for one migration. - """ - self.params = params.copy() - self.params.update(params_append) - - self.source = False - if params.get("hostid") == srchost: - self.source = True - - self.destination = False - if params.get("hostid") == dsthost: - self.destination = True - - self.src = srchost - self.dst = dsthost - self.hosts = [srchost, dsthost] - self.mig_id = {'src': srchost, 'dst': dsthost, "vms": vms_name} - self.vms_name = vms_name - self.vms = [] - self.vm_ports = None - - - def is_src(self): - """ - @return: True if host is source. - """ - return self.source - - - def is_dst(self): - """ - @return: True if host is destination. - """ - return self.destination - - -class MultihostMigration(object): - """ - Class that provides a framework for multi-host migration. - - Migration can be run both synchronously and asynchronously. - To specify what is going to happen during the multi-host - migration, it is necessary to reimplement the method - migration_scenario. It is possible to start multiple migrations - in separate threads, since self.migrate is thread safe. - - Only one test using multihost migration framework should be - started on one machine otherwise it is necessary to solve the - problem with listen server port. - - Multihost migration starts SyncListenServer through which - all messages are transfered, since the multiple hosts can - be in diferent states. - - Class SyncData is used to transfer data over network or - synchronize the migration process. Synchronization sessions - are recognized by session_id. - - It is important to note that, in order to have multi-host - migration, one needs shared guest image storage. The simplest - case is when the guest images are on an NFS server. - - Example: - class TestMultihostMigration(virt_utils.MultihostMigration): - def __init__(self, test, params, env): - super(testMultihostMigration, self).__init__(test, params, env) - - def migration_scenario(self): - srchost = self.params.get("hosts")[0] - dsthost = self.params.get("hosts")[1] - - def worker(mig_data): - vm = env.get_vm("vm1") - session = vm.wait_for_login(timeout=self.login_timeout) - session.sendline("nohup dd if=/dev/zero of=/dev/null &") - session.cmd("killall -0 dd") - - def check_worker(mig_data): - vm = env.get_vm("vm1") - session = vm.wait_for_login(timeout=self.login_timeout) - session.cmd("killall -9 dd") - - # Almost synchronized migration, waiting to end it. - # Work is started only on first VM. - self.migrate_wait(["vm1", "vm2"], srchost, dsthost, - worker, check_worker) - - # Migration started in different threads. - # It allows to start multiple migrations simultaneously. - mig1 = self.migrate(["vm1"], srchost, dsthost, - worker, check_worker) - mig2 = self.migrate(["vm2"], srchost, dsthost) - mig2.join() - mig1.join() - - mig = TestMultihostMigration(test, params, env) - mig.run() - """ - def __init__(self, test, params, env, preprocess_env=True): - self.test = test - self.params = params - self.env = env - self.hosts = params.get("hosts") - self.hostid = params.get('hostid', "") - self.comm_port = int(params.get("comm_port", 13234)) - vms_count = len(params["vms"].split()) - - self.login_timeout = int(params.get("login_timeout", 360)) - self.disk_prepare_timeout = int(params.get("disk_prepare_timeout", - 160 * vms_count)) - self.finish_timeout = int(params.get("finish_timeout", - 120 * vms_count)) - - self.new_params = None - - if params.get("clone_master") == "yes": - self.clone_master = True - else: - self.clone_master = False - - self.mig_timeout = int(params.get("mig_timeout")) - # Port used to communicate info between source and destination - self.regain_ip_cmd = params.get("regain_ip_cmd", "dhclient") - - self.vm_lock = threading.Lock() - - self.sync_server = None - if self.clone_master: - self.sync_server = SyncListenServer() - - if preprocess_env: - self.preprocess_env() - self._hosts_barrier(self.hosts, self.hosts, 'disk_prepared', - self.disk_prepare_timeout) - - - def migration_scenario(self): - """ - Multi Host migration_scenario is started from method run where the - exceptions are checked. It is not necessary to take care of - cleaning up after test crash or finish. - """ - raise NotImplementedError - - - def migrate_vms_src(self, mig_data): - """ - Migrate vms source. - - @param mig_Data: Data for migration. - - For change way how machine migrates is necessary - re implement this method. - """ - def mig_wrapper(vm, dsthost, vm_ports): - vm.migrate(dest_host=dsthost, remote_port=vm_ports[vm.name]) - - logging.info("Start migrating now...") - multi_mig = [] - for vm in mig_data.vms: - multi_mig.append((mig_wrapper, (vm, mig_data.dst, - mig_data.vm_ports))) - parallel(multi_mig) - - - def migrate_vms_dest(self, mig_data): - """ - Migrate vms destination. This function is started on dest host during - migration. - - @param mig_Data: Data for migration. - """ - pass - - - def __del__(self): - if self.sync_server: - self.sync_server.close() - - - def master_id(self): - return self.hosts[0] - - - def _hosts_barrier(self, hosts, session_id, tag, timeout): - logging.debug("Barrier timeout: %d tags: %s" % (timeout, tag)) - tags = SyncData(self.master_id(), self.hostid, hosts, - "%s,%s,barrier" % (str(session_id), tag), - self.sync_server).sync(tag, timeout) - logging.debug("Barrier tag %s" % (tags)) - - - def preprocess_env(self): - """ - Prepare env to start vms. - """ - preprocess_images(self.test.bindir, self.params, self.env) - - - def _check_vms_source(self, mig_data): - for vm in mig_data.vms: - vm.wait_for_login(timeout=self.login_timeout) - - sync = SyncData(self.master_id(), self.hostid, mig_data.hosts, - mig_data.mig_id, self.sync_server) - mig_data.vm_ports = sync.sync(timeout=120)[mig_data.dst] - logging.info("Received from destination the migration port %s", - str(mig_data.vm_ports)) - - - def _check_vms_dest(self, mig_data): - mig_data.vm_ports = {} - for vm in mig_data.vms: - logging.info("Communicating to source migration port %s", - vm.migration_port) - mig_data.vm_ports[vm.name] = vm.migration_port - - SyncData(self.master_id(), self.hostid, - mig_data.hosts, mig_data.mig_id, - self.sync_server).sync(mig_data.vm_ports, timeout=120) - - - def _prepare_params(self, mig_data): - """ - Prepare separate params for vm migration. - - @param vms_name: List of vms. - """ - new_params = mig_data.params.copy() - new_params["vms"] = " ".join(mig_data.vms_name) - return new_params - - - def _check_vms(self, mig_data): - """ - Check if vms are started correctly. - - @param vms: list of vms. - @param source: Must be True if is source machine. - """ - logging.info("Try check vms %s" % (mig_data.vms_name)) - for vm in mig_data.vms_name: - if not self.env.get_vm(vm) in mig_data.vms: - mig_data.vms.append(self.env.get_vm(vm)) - for vm in mig_data.vms: - logging.info("Check vm %s on host %s" % (vm.name, self.hostid)) - vm.verify_alive() - - if mig_data.is_src(): - self._check_vms_source(mig_data) - else: - self._check_vms_dest(mig_data) - - - def prepare_for_migration(self, mig_data, migration_mode): - """ - Prepare destination of migration for migration. - - @param mig_data: Class with data necessary for migration. - @param migration_mode: Migration mode for prepare machine. - """ - new_params = self._prepare_params(mig_data) - - new_params['migration_mode'] = migration_mode - new_params['start_vm'] = 'yes' - self.vm_lock.acquire() - virt_env_process.process(self.test, new_params, self.env, - virt_env_process.preprocess_image, - virt_env_process.preprocess_vm) - self.vm_lock.release() - - self._check_vms(mig_data) - - - def migrate_vms(self, mig_data): - """ - Migrate vms. - """ - if mig_data.is_src(): - self.migrate_vms_src(mig_data) - else: - self.migrate_vms_dest(mig_data) - - - def check_vms(self, mig_data): - """ - Check vms after migrate. - - @param mig_data: object with migration data. - """ - for vm in mig_data.vms: - if not guest_active(vm): - raise error.TestFail("Guest not active after migration") - - logging.info("Migrated guest appears to be running") - - logging.info("Logging into migrated guest after migration...") - for vm in mig_data.vms: - session_serial = vm.wait_for_serial_login(timeout= - self.login_timeout) - #There is sometime happen that system sends some message on - #serial console and IP renew command block test. Because - #there must be added "sleep" in IP renew command. - session_serial.cmd(self.regain_ip_cmd) - vm.wait_for_login(timeout=self.login_timeout) - - - def postprocess_env(self): - """ - Kill vms and delete cloned images. - """ - postprocess_images(self.test.bindir, self.params) - - - def migrate(self, vms_name, srchost, dsthost, start_work=None, - check_work=None, mig_mode="tcp", params_append=None): - """ - Migrate machine from srchost to dsthost. It executes start_work on - source machine before migration and executes check_work on dsthost - after migration. - - Migration execution progress: - - source host | dest host - -------------------------------------------------------- - prepare guest on both sides of migration - - start machine and check if machine works - - synchronize transfer data needed for migration - -------------------------------------------------------- - start work on source guests | wait for migration - -------------------------------------------------------- - migrate guest to dest host. - wait on finish migration synchronization - -------------------------------------------------------- - | check work on vms - -------------------------------------------------------- - wait for sync on finish migration - - @param vms_name: List of vms. - @param srchost: src host id. - @param dsthost: dst host id. - @param start_work: Function started before migration. - @param check_work: Function started after migration. - @param mig_mode: Migration mode. - @param params_append: Append params to self.params only for migration. - """ - def migrate_wrap(vms_name, srchost, dsthost, start_work=None, - check_work=None, params_append=None): - logging.info("Starting migrate vms %s from host %s to %s" % - (vms_name, srchost, dsthost)) - error = None - mig_data = MigrationData(self.params, srchost, dsthost, - vms_name, params_append) - try: - try: - if mig_data.is_src(): - self.prepare_for_migration(mig_data, None) - elif self.hostid == dsthost: - self.prepare_for_migration(mig_data, mig_mode) - else: - return - - if mig_data.is_src(): - if start_work: - start_work(mig_data) - - self.migrate_vms(mig_data) - - timeout = 30 - if not mig_data.is_src(): - timeout = self.mig_timeout - self._hosts_barrier(mig_data.hosts, mig_data.mig_id, - 'mig_finished', timeout) - - if mig_data.is_dst(): - self.check_vms(mig_data) - if check_work: - check_work(mig_data) - - except: - error = True - raise - finally: - if not error: - self._hosts_barrier(self.hosts, - mig_data.mig_id, - 'test_finihed', - self.finish_timeout) - - def wait_wrap(vms_name, srchost, dsthost): - mig_data = MigrationData(self.params, srchost, dsthost, vms_name, - None) - timeout = (self.login_timeout + self.mig_timeout + - self.finish_timeout) - - self._hosts_barrier(self.hosts, mig_data.mig_id, - 'test_finihed', timeout) - - if (self.hostid in [srchost, dsthost]): - mig_thread = utils.InterruptedThread(migrate_wrap, (vms_name, - srchost, - dsthost, - start_work, - check_work, - params_append)) - else: - mig_thread = utils.InterruptedThread(wait_wrap, (vms_name, - srchost, - dsthost)) - mig_thread.start() - return mig_thread - - - def migrate_wait(self, vms_name, srchost, dsthost, start_work=None, - check_work=None, mig_mode="tcp", params_append=None): - """ - Migrate machine from srchost to dsthost and wait for finish. - It executes start_work on source machine before migration and executes - check_work on dsthost after migration. - - @param vms_name: List of vms. - @param srchost: src host id. - @param dsthost: dst host id. - @param start_work: Function which is started before migration. - @param check_work: Function which is started after - done of migration. - """ - self.migrate(vms_name, srchost, dsthost, start_work, check_work, - mig_mode, params_append).join() - - - def cleanup(self): - """ - Cleanup env after test. - """ - if self.clone_master: - self.sync_server.close() - self.postprocess_env() - - - def run(self): - """ - Start multihost migration scenario. - After scenario is finished or if scenario crashed it calls postprocess - machines and cleanup env. - """ - try: - self.migration_scenario() - - self._hosts_barrier(self.hosts, self.hosts, 'all_test_finihed', - self.finish_timeout) - finally: - self.cleanup() - def get_ip_address_by_interface(ifname): """ returns ip address by interface -- 1.7.10.2 _______________________________________________ Autotest mailing list Autotest@test.kernel.org http://test.kernel.org/cgi-bin/mailman/listinfo/autotest