This patch will add a server-side test namely kvm_migration. Currently, it will use existing KVM client test framework and add a new file kvm_migration.py to help judge executing routine: source machine or dest machine. * Things need to be improved: 1) a method/mechanism to let source machine that initiating migrate informed when is dest machine(VM is started with listening mode) is ready. IMHO, we could use socket communication to send 'listening port'(-incoming tcp:0:$PORT) to source host and then source will start migrating. 2) second issue is, how we can edit the kvm_tests.cfg file to control different migration via web front-END. AFAIK, we can edit the control file(migration_control.srv) but we cann't touch kvm_tests.cfg. * In order to run this test so far, we need: 1) set up NFS on source host (pair[1] machine) and mount on dest host (pair[0]) 2) edit the kvm_tests.cfg, kvm_address_pool.cfg, kvm_cdkey.cfg etc whatsoever that kvm client-side test run needs 3) issue command: autoserv -m source_ip,dest_ip migration_control.srv NOTE: This is only the trial version. Need so much comments/suggestions. Thanks in advance. Cheers, Signed-off-by: Yolkfull Chow <yzhou@xxxxxxxxxx> --- client/tests/kvm/kvm_migration.py | 146 ++++++++++++++++++++++++++++++++ client/tests/kvm/kvm_preprocessing.py | 2 +- client/tests/kvm/kvm_test_utils.py | 27 +++--- client/tests/kvm/kvm_tests.cfg.sample | 1 + client/tests/kvm/kvm_vm.py | 4 +- client/tests/kvm/tests/migration.py | 2 +- client/tests/kvm_migration | 1 + server/tests/kvm/migration_control.srv | 98 +++++++++++++++++++++ 8 files changed, 265 insertions(+), 16 deletions(-) create mode 100644 client/tests/kvm/kvm_migration.py create mode 120000 client/tests/kvm_migration create mode 100644 server/tests/kvm/migration_control.srv diff --git a/client/tests/kvm/kvm_migration.py b/client/tests/kvm/kvm_migration.py new file mode 100644 index 0000000..4c08b5a --- /dev/null +++ b/client/tests/kvm/kvm_migration.py @@ -0,0 +1,146 @@ +import sys, os, time, logging, commands +from autotest_lib.client.bin import test +from autotest_lib.client.common_lib import error +import kvm_utils, kvm_preprocessing, common, kvm_vm, kvm_test_utils + + +class kvm_migration(test.test): + """ + KVM migration test. + + @copyright: Red Hat 2008-2009 + @see: http://www.linux-kvm.org/page/KVM-Autotest/Client_Install + (Online doc - Getting started with KVM testing) + """ + version = 1 + def initialize(self): + pass + + + def setup(self): + """ + Setup environment like NFS mount etc. + """ + pass + + + def run_once(self, params): + """ + Setup remote machine and then execute migration. + """ + # Check whether remote machine is ready + dsthost = params.get("dsthost") + srchost = params.get("srchost") + image_path = os.path.join(self.bindir, "images") + + rootdir = params.get("rootdir") + iso = os.path.join(rootdir, 'iso') + images = os.path.join(rootdir, 'images') + qemu = os.path.join(rootdir, 'qemu') + qemu_img = os.path.join(rootdir, 'qemu-img') + + def link_if_not_exist(ldir, target, link_name): + t = target + l = os.path.join(ldir, link_name) + if not os.path.exists(l): + os.symlink(t,l) + link_if_not_exist(self.bindir, '../../', 'autotest') + link_if_not_exist(self.bindir, iso, 'isos') + link_if_not_exist(self.bindir, images, 'images') + link_if_not_exist(self.bindir, qemu, 'qemu') + link_if_not_exist(self.bindir, qemu_img, 'qemu-img') + + try: + image_real_path = os.readlink(image_path) + except OSError: + raise error.TestError("Readlink of image dir failed") + + def setup_dest(srchost, path): + """ + Mount NFS directory from source host. + """ + cmd = "mount |grep -q %s" % srchost + if os.system(cmd): + mnt_cmd = "mount %s:%s %s" % (srchost, + path, + path) + s, o = commands.getstatusoutput(mnt_cmd) + if s != 0: + raise error.TestError("Mount srchost failed: %s" % o) + + def setup_source(path): + """ + Setup NFS mount point. + """ + export_string = "%s *(rw,no_root_squash)" % path + export_file = '/etc/exports' + f = open(export_file) + if not export_string in f.read().strip(): + try: + f.write(export_string) + except IOError: + raise error.TestError("Failed to write to exports file") + + cmd = "service nfs restart && exportfs -a" + if os.system(cmd): + raise error.TestError("Failed to restart NFS on source") + + # if params.get("role") == "dest": + # setup_dest(srchost, image_real_path) + # elif params.get("role") == "source": + # setup_source(image_real_path) + + # Report the parameters we've received and write them as keyvals + logging.debug("Test parameters:") + keys = params.keys() + keys.sort() + for key in keys: + logging.debug(" %s = %s", key, params[key]) + self.write_test_keyval({key: params[key]}) + + # Open the environment file + env_filename = os.path.join(self.bindir, params.get("env", "env")) + env = kvm_utils.load_env(env_filename, {}) + logging.debug("Contents of environment: %s" % str(env)) + + + # Preprocess + kvm_preprocessing.preprocess(self, params, env) + kvm_utils.dump_env(env, env_filename) + + try: + try: + # Get the living VM + vm = kvm_test_utils.get_living_vm(env, params.get("main_vm")) + + if params.get("role") == "source": + s, o = vm.send_monitor_cmd("help info") + if not "info migrate" in o: + raise error.TestError("Migration is not supported") + + session = kvm_test_utils.wait_for_login(vm) + migration_test_command = params.get("migration_test_command") + reference_output = session.get_command_output(migration_test_command) + + kvm_test_utils.migrate(vm, dsthost, vm.migration_port, env) + session.close() + elif params.get("role") == "dest": + try: + mig_timeout = int(params.get("mig_timeout")) + session = kvm_test_utils.wait_for_login(vm, + timeout=mig_timeout) + except: + raise error.TestFail("Could not log into migrated guest") + + except Exception, e: + logging.error("Test failed: %s", e) + logging.debug("Postprocessing on error...") + kvm_preprocessing.postprocess_on_error(self, params, env) + kvm_utils.dump_env(env, env_filename) + raise + + finally: + # Postprocess + kvm_preprocessing.postprocess(self, params, env) + logging.debug("Contents of environment: %s", str(env)) + kvm_utils.dump_env(env, env_filename) diff --git a/client/tests/kvm/kvm_preprocessing.py b/client/tests/kvm/kvm_preprocessing.py index 5bae2bd..a0cde02 100644 --- a/client/tests/kvm/kvm_preprocessing.py +++ b/client/tests/kvm/kvm_preprocessing.py @@ -74,7 +74,7 @@ def preprocess_vm(test, params, env, name): logging.debug("VM's qemu command differs from requested one; " "restarting it...") start_vm = True - + if start_vm and not vm.create(name, params, test.bindir, for_migration): raise error.TestError("Could not start VM") diff --git a/client/tests/kvm/kvm_test_utils.py b/client/tests/kvm/kvm_test_utils.py index bf8aed2..64b8616 100644 --- a/client/tests/kvm/kvm_test_utils.py +++ b/client/tests/kvm/kvm_test_utils.py @@ -105,7 +105,7 @@ def reboot(vm, session, method="shell", sleep_before_reset=10, nic_index=0, return session -def migrate(vm, env=None): +def migrate(vm, rem_host, rem_port, env=None, mig_timeout=90): """ Migrate a VM locally and re-register it in the environment. @@ -132,13 +132,14 @@ def migrate(vm, env=None): if not "info migrate" in o: raise error.TestError("Migration is not supported") - # Clone the source VM and ask the clone to wait for incoming migration - dest_vm = vm.clone() - dest_vm.create(for_migration=True) + if rem_host == "localhost": + # Clone the source VM and ask the clone to wait for incoming migration + dest_vm = vm.clone() + dest_vm.create(for_migration=True) try: # Define the migration command - cmd = "migrate -d tcp:localhost:%d" % dest_vm.migration_port + cmd = "migrate -d tcp:%s:%d" % (rem_host, rem_port) logging.debug("Migrating with command: %s" % cmd) # Migrate @@ -149,7 +150,7 @@ def migrate(vm, env=None): raise error.TestFail("Migration command failed") # Wait for migration to finish - if not kvm_utils.wait_for(mig_finished, 90, 2, 2, + if not kvm_utils.wait_for(mig_finished, mig_timeout, 2, 2, "Waiting for migration to finish..."): raise error.TestFail("Timeout elapsed while waiting for migration " "to finish") @@ -165,15 +166,17 @@ def migrate(vm, env=None): # 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 rem_host == "localhost": + # Replace the source VM with the new cloned VM + if env is not None: + kvm_utils.env_register_vm(env, vm.name, dest_vm) - # Return the new cloned VM - return dest_vm + # Return the new cloned VM + return dest_vm except: - dest_vm.destroy() + if rem_host == "localhost": + dest_vm.destroy() raise diff --git a/client/tests/kvm/kvm_tests.cfg.sample b/client/tests/kvm/kvm_tests.cfg.sample index 5e15b30..0c4a76c 100644 --- a/client/tests/kvm/kvm_tests.cfg.sample +++ b/client/tests/kvm/kvm_tests.cfg.sample @@ -81,6 +81,7 @@ variants: migration_bg_command = "cd /tmp; nohup tcpdump -q -t ip host localhost" migration_bg_check_command = pgrep tcpdump migration_bg_kill_command = pkill tcpdump + mig_timeout = 90 kill_vm_on_error = yes iterations = 2 used_mem = 1024 diff --git a/client/tests/kvm/kvm_vm.py b/client/tests/kvm/kvm_vm.py index 100b567..d135842 100755 --- a/client/tests/kvm/kvm_vm.py +++ b/client/tests/kvm/kvm_vm.py @@ -396,9 +396,9 @@ class VM: qemu_command = self.make_qemu_command() # Is this VM supposed to accept incoming migrations? + # Find available migration port + self.migration_port = kvm_utils.find_free_port(5200, 6000) if for_migration: - # Find available migration port - self.migration_port = kvm_utils.find_free_port(5200, 6000) # Add -incoming option to the qemu command qemu_command += " -incoming tcp:0:%d" % self.migration_port diff --git a/client/tests/kvm/tests/migration.py b/client/tests/kvm/tests/migration.py index b8f171c..3fd8d5f 100644 --- a/client/tests/kvm/tests/migration.py +++ b/client/tests/kvm/tests/migration.py @@ -43,7 +43,7 @@ def run_migration(test, params, env): session2.close() # Migrate the VM - dest_vm = kvm_test_utils.migrate(vm, env) + dest_vm = kvm_test_utils.migrate(vm, "localhost", vm.migration_port, env) # Log into the guest again logging.info("Logging into guest after migration...") diff --git a/client/tests/kvm_migration b/client/tests/kvm_migration new file mode 120000 index 0000000..9186877 --- /dev/null +++ b/client/tests/kvm_migration @@ -0,0 +1 @@ +kvm \ No newline at end of file diff --git a/server/tests/kvm/migration_control.srv b/server/tests/kvm/migration_control.srv new file mode 100644 index 0000000..d8b87f1 --- /dev/null +++ b/server/tests/kvm/migration_control.srv @@ -0,0 +1,98 @@ +AUTHOR = "Yolkfull Chow <yzhou@xxxxxxxxxx>" +TIME = "SHORT" +NAME = "Migration across Multi-machine" +TEST_CATEGORY = "Functional" +TEST_CLASS = 'Virtualization' +TEST_TYPE = "Server" +DOC = """ +Migrate KVM guest between two hosts. + +Arguments to run_test: + +@dict - a dictionary containing all parameters that migration need. +""" + +import sys, os, commands +from autotest_lib.server import utils + +KVM_DIR = os.path.join('/root/devel/upstream/server-mig', 'client/tests/kvm') +sys.path.append(KVM_DIR) +import common, kvm_config + +rootdir = '/tmp/kvm_autotest_root' + +def run(pair): + print "KVM migration running on srchost [%s] and desthost [%s]\n" % ( + pair[0], pair[1]) + + source = hosts.create_host(pair[0]) + dest = hosts.create_host(pair[1]) + + source_at = autotest.Autotest(source) + #source_at.install(source) + dest_at = autotest.Autotest(dest) + #dest_at.install(dest) + + # ---------------------------------------------------------- + # Get test set (dictionary list) from the configuration file + # ---------------------------------------------------------- + filename = os.path.join(KVM_DIR, "kvm_tests.cfg") + if not os.path.exists(filename): + print "[ERROR] Test config file doesn't found" + cfg = kvm_config.config(filename) + + filename = os.path.join(KVM_DIR, "kvm_address_pools.cfg") + if os.path.exists(filename): + cfg.parse_file(filename) + hostname = os.uname()[1].split(".")[0] + if cfg.filter("^" + hostname): + cfg.parse_string("only ^%s" % hostname) + else: + cfg.parse_string("only ^default_host") + + list = cfg.get_list() + + # Control file template for client machine + control_string = "job.run_test('kvm_migration', params=%s)" + + for vm_dict in list: + + vm_dict['srchost'] = source.ip + vm_dict['dsthost'] = dest.ip + vm_dict['display'] = 'vnc' + vm_dict['rootdir'] = rootdir + + source_dict = vm_dict.copy() + dest_dict = vm_dict.copy() + + source_dict['role'] = "source" + + dest_dict['role'] = "dest" + dest_dict['start_vm_for_migration'] = "yes" + + # Report the parameters we've received + print "Test parameters:" + keys = vm_dict.keys() + keys.sort() + for key in keys: + print " " + str(key) + " = " + str(vm_dict[key]) + + source_control_file = ''.join([control_string % source_dict]) + dest_control_file = ''.join([control_string % dest_dict]) + + dest_command = subcommand(dest_at.run, + [dest_control_file, dest.hostname]) + source_command = subcommand(source_at.run, + [source_control_file, source.hostname]) + + parallel([dest_command, source_command]) + +# grab the pairs (and failures) +(pairs, failures) = utils.form_ntuples_from_machines(machines, 2) + +# log the failures +for failure in failures: + job.record("FAIL", failure[0], "kvm", failure[1]) + +# now run through each pair and run +job.parallel_simple(run, pairs, log=False) -- 1.6.5.2 -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html