[PATCH] Add a server-side test: kvm_migration

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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

[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux