new file mode 100644
@@ -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)
@@ -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
@@ -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
new file mode 120000
@@ -0,0 +1 @@
+kvm
\ No newline at end of file
new file mode 100644
@@ -0,0 +1,139 @@
+AUTHOR = "Yolkfull Chow <yzhou@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)