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.

Improvement based on Version #2:
 * Log into migrated guest from source client machine because the previous
   session got before migration should be still responsive.
 * Compare the output of `migration_test_command' before and after migration.
 * Add checking status of migrated guest via monitor command 'info status'.
 * As suggested by Sudhir, rename 'rem_port' to 'mig_port'. I also rename
   'rem_host' to be 'dest_host'.

Signed-off-by: Yolkfull Chow <yz...@redhat.com>
---
 client/tests/kvm/kvm_migration.py      |  170 ++++++++++++++++++++++++++++++++
 client/tests/kvm/kvm_test_utils.py     |   27 +++---
 client/tests/kvm/kvm_tests.cfg.sample  |    2 +
 client/tests/kvm_migration             |    1 +
 server/tests/kvm/migration_control.srv |  139 ++++++++++++++++++++++++++
 5 files changed, 327 insertions(+), 12 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..7845e6b
--- /dev/null
+++ b/client/tests/kvm/kvm_migration.py
@@ -0,0 +1,170 @@
+import sys, os, time, logging, commands, socket
+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)
+
+    Migration execution progress:
+
+    source host                     dest host
+    ------------------------------------------------------------------
+    log into guest
+    ------------------------------------------------------------------
+    start socket server
+
+    ---- wait 30 secs -------------- wait login_timeout+30 secs-------
+    
+    accept connection             connect to socket server,send mig_port
+    ------------------------------------------------------------------
+    start migration
+
+    ---- wait 30 secs -------------- wait mig_timeout+30 secs---------
+
+    try to log into migrated guest   check VM's status via monitor cmd
+    ------------------------------------------------------------------
+    
+    """
+    version = 1
+    def initialize(self):
+        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')
+
+        # 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"))
+                migration_test_command = params.get("migration_test_command")
+                login_timeout = int(params.get("login_timeout"))
+                mig_timeout = int(params.get("mig_timeout"))
+                source_addr = (srchost, 50006)
+                all = [srchost, dsthost]
+
+                # Check whether migration is supported
+                s, o = vm.send_monitor_cmd("help info")
+                if not "info migrate" in o:
+                    raise error.TestError("Migration is not supported")
+
+                if params.get("role") == "source":
+
+                    session = kvm_test_utils.wait_for_login(vm,
+                                                          
timeout=login_timeout)
+                    ref_output = 
session.get_command_output(migration_test_command)
+
+                    # Listen on a port to get the migration port received from 
+                    # dest machine
+                    s = socket.socket()
+                    s.bind(source_addr)
+                    s.listen(1)
+
+                    # Wait 30 seconds for source and dest to reach this point
+                    
self.job.barrier(srchost,'socket_started',30).rendezvous(*all)
+
+                    conn, addr = s.accept()
+                    mig_port = int(conn.recv(6))
+                    s.close()
+
+                    logging.info("Start migrating now...")
+                    kvm_test_utils.migrate(vm, dsthost, mig_port, env)
+
+                    # Wait up to 30 seconds for dest to reach this point
+                    
self.job.barrier(srchost,'mig_finished',30).rendezvous(*all)
+
+                    output = session.get_command_output(migration_test_command)
+                    if output != ref_output:
+                        msg = "Output of migration_test_command mismatch!"
+                        logging.error(msg)
+                        logging.error("Before migration: \n%s" % ref_output)
+                        logging.error("After migration: \n%s" % output)
+                        raise error.TestFail(msg)
+                    
+                    session.close()
+
+                elif params.get("role") == "dest":
+
+                    # Wait up to login_timeout+30 seconds for the source to
+                    # reach this point
+                    self.job.barrier(dsthost, 'socket_started',
+                                     login_timeout+30).rendezvous(*all)
+
+                    s = socket.socket()
+                    s.connect(source_addr)
+                    s.send("%d" % vm.migration_port)
+                    s.close()
+
+                    # Wait up to mig_timeout+30 seconds for the source to
+                    # reach this point: migration finished
+                    self.job.barrier(dsthost, 'mig_finished',
+                                     mig_timeout+30).rendezvous(*all)
+
+                    s, o = vm.send_monitor_cmd('info status')
+                    if s or not 'VM status: running' in o:
+                        raise error.TestFail(
+                            "Check migrated guest's status failed")
+                    logging.info("Migrated guest is running")
+
+                else:
+                    raise error.TestError('Invalid role specified')
+
+            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_test_utils.py 
b/client/tests/kvm/kvm_test_utils.py
index 02ec0cf..bd734cb 100644
--- a/client/tests/kvm/kvm_test_utils.py
+++ b/client/tests/kvm/kvm_test_utils.py
@@ -106,7 +106,7 @@ def reboot(vm, session, method="shell", 
sleep_before_reset=10, nic_index=0,
     return session
 
 
-def migrate(vm, env=None):
+def migrate(vm, dest_host, mig_port, env=None, mig_timeout=90):
     """
     Migrate a VM locally and re-register it in the environment.
 
@@ -133,13 +133,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 dest_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" % (dest_host, mig_port)
         logging.debug("Migrating with command: %s" % cmd)
 
         # Migrate
@@ -150,7 +151,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")
@@ -166,15 +167,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 dest_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 dest_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 9fb1ba8..788b870 100644
--- a/client/tests/kvm/kvm_tests.cfg.sample
+++ b/client/tests/kvm/kvm_tests.cfg.sample
@@ -81,6 +81,8 @@ 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
+        login_timeout = 240
         kill_vm_on_error = yes
         iterations = 2
         used_mem = 1024
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..5b4822e
--- /dev/null
+++ b/server/tests/kvm/migration_control.srv
@@ -0,0 +1,139 @@
+AUTHOR = "Yolkfull Chow <yz...@redhat.com>"
+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, glob, shutil
+from autotest_lib.server import utils
+
+# Specify the directory of autotest before you start this test
+AUTOTEST_DIR = '/root/devel/upstream/server-migration'
+
+# Specify the root directory that on client machines
+rootdir = '/tmp/kvm_autotest_root'
+
+KVM_DIR = os.path.join(AUTOTEST_DIR, 'client/tests/kvm')
+sys.path.append(KVM_DIR)
+
+import common, kvm_config
+
+# Copy all .cfg.sample files to be .cfg files
+sample_files = glob.glob(os.path.join(KVM_DIR, '*.cfg.sample'))
+for sample_file in sample_files:
+    sample_file_name = os.path.basename(sample_file)
+    cfg_file_name = ".".join(sample_file_name.split(".")[:-1])
+    cfg_file_path = os.path.join(KVM_DIR, cfg_file_name)
+    sample_file_path = os.path.join(KVM_DIR, sample_file)
+    shutil.copyfile(sample_file_path, cfg_file_path)
+
+
+
+def run(pair):
+    print "KVM migration running on srchost [%s] and desthost [%s]\n" % (
+                                                        pair[0], pair[1])
+
+    # Edit this to limit the migration test dict
+    test_variants = """
+only full
+only qcow2
+only ide
+only default
+only up
+kill_vm_on_error = no
+kill_vm = no
+only Fedora.11.32
+no install setup
+no hugepages
+only migrate
+only rtl8139
+"""
+
+    source = hosts.create_host(pair[0])
+    dest = hosts.create_host(pair[1])
+    source_at = autotest.Autotest(source)
+    dest_at = autotest.Autotest(dest)
+
+    # FIXME: should parse config file on client machines?
+    filename = os.path.join(KVM_DIR, "kvm_tests.cfg.sample")
+    sample_f = open(filename, 'r')
+    new_contents = sample_f.readlines()[:-1]
+    sample_f.close()
+
+    new_contents = "".join(new_contents)
+    new_contents += test_variants
+
+    filename = os.path.join(KVM_DIR, 'kvm_tests.cfg')
+    f = open(filename, 'w')
+    f.write(new_contents)
+    f.close()
+
+    if not os.path.exists(filename):
+        print "[ERROR] Config file kvm_tests.cfg is not found"
+        sys.exit(1)
+
+    # Get test set (dictionary list) from the configuration file
+    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
+        vm_dict['nic_mode'] = 'tap'
+
+        source_dict = vm_dict.copy()
+        source_dict['role'] = "source"
+
+        dest_dict = vm_dict.copy()
+        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 majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to