From mboxrd@z Thu Jan 1 00:00:00 1970 From: Yolkfull Chow Subject: Re: [Autotest] [PATCH] Add a server-side test - kvm_migration Date: Tue, 8 Dec 2009 14:14:27 +0800 Message-ID: <20091208061427.GA2210@aFu.nay.redhat.com> References: <1259914726-3675-1-git-send-email-yzhou@redhat.com> Reply-To: Yolkfull Chow Mime-Version: 1.0 Content-Type: text/plain; charset=iso-8859-1 Content-Transfer-Encoding: QUOTED-PRINTABLE Cc: autotest@test.kernel.org, skannery@in.ibm.com, kvm-devel To: sudhir kumar Return-path: Received: from mx1.redhat.com ([209.132.183.28]:39121 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932073AbZLHGOe (ORCPT ); Tue, 8 Dec 2009 01:14:34 -0500 Content-Disposition: inline In-Reply-To: Sender: kvm-owner@vger.kernel.org List-ID: On Mon, Dec 07, 2009 at 03:35:54PM +0530, sudhir kumar wrote: > Resending with proper cc list :( >=20 > On Mon, Dec 7, 2009 at 2:43 PM, sudhir kumar wr= ote: > > Thanks for initiating the server side implementation of migration. = =46ew > > comments below > > > > On Fri, Dec 4, 2009 at 1:48 PM, Yolkfull Chow wr= ote: > >> This patch will add a server-side test namely kvm_migration. Curre= ntly, > >> it will use existing KVM client test framework and add a new file > >> kvm_migration.py to help judge executing routine: source machine o= r dest > >> machine. > >> > >> * One thing need to be considered/improved: > >> Whether we parse the kvm_tests.cfg on server machine or on client = machines? > >> If parse it on client machines, we need to fix one problem that ad= ding > >> 'start_vm_for_migration' parameter into dict which generated on de= st machine. > > I think we can not manage with client side parsing without adding t= oo > > much complexity. So let us continue parsing on the server side only > > for remote migration. Also as the patch does, keep the local migrat= ion > > under the client also. I do not like adding test variants in > > migration_control.srv. Comments below... > >> > >> So far I choose parsing kvm_tests.cfg on server machine, and then = add > >> 'start_vm_for_migration' into dict cloned from original test dict = for dest > >> machine. > >> > >> * In order to run this test so far, we need to setup NFS for both > >> source and dest machines. > >> > >> Signed-off-by: Yolkfull Chow > >> --- > >> =A0client/tests/kvm/kvm_migration.py =A0 =A0 =A0| =A0165 +++++++++= +++++++++++++++++++++++ > >> =A0client/tests/kvm/kvm_test_utils.py =A0 =A0 | =A0 27 +++--- > >> =A0client/tests/kvm/kvm_tests.cfg.sample =A0| =A0 =A02 + > >> =A0client/tests/kvm_migration =A0 =A0 =A0 =A0 =A0 =A0 | =A0 =A01 + > >> =A0server/tests/kvm/migration_control.srv | =A0137 +++++++++++++++= +++++++++++ > >> =A05 files changed, 320 insertions(+), 12 deletions(-) > >> =A0create mode 100644 client/tests/kvm/kvm_migration.py > >> =A0create mode 120000 client/tests/kvm_migration > >> =A0create 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..52cd3cd > >> --- /dev/null > >> +++ b/client/tests/kvm/kvm_migration.py > >> @@ -0,0 +1,165 @@ > >> +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_uti= ls > >> + > >> + > >> +class kvm_migration(test.test): > >> + =A0 =A0""" > >> + =A0 =A0KVM migration test. > >> + > >> + =A0 =A0@copyright: Red Hat 2008-2009 > >> + =A0 =A0@see: http://www.linux-kvm.org/page/KVM-Autotest/Client_I= nstall > >> + =A0 =A0 =A0 =A0 =A0 =A0(Online doc - Getting started with KVM te= sting) > >> + > >> + =A0 =A0Migration execution progress: > >> + > >> + =A0 =A0source host =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dest = host > >> + =A0 =A0---------------------------------------------------------= --------- > >> + =A0 =A0log into guest > >> + =A0 =A0---------------------------------------------------------= --------- > >> + =A0 =A0start socket server > >> + > >> + =A0 =A0---- wait 30 secs -------------- wait login_timeout+30 se= cs------- > >> + > >> + =A0 =A0accept connection =A0 =A0 =A0 =A0 =A0 =A0 connect to sock= et server,send mig_port > >> + =A0 =A0---------------------------------------------------------= --------- > >> + =A0 =A0start migration > >> + > >> + =A0 =A0---- wait 30 secs -------------- wait mig_timeout+30 secs= --------- > >> + > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0try to log into migrated guest > >> + =A0 =A0---------------------------------------------------------= --------- > >> + > >> + =A0 =A0""" > >> + =A0 =A0version =3D 1 > >> + =A0 =A0def initialize(self): > >> + =A0 =A0 =A0 =A0pass > >> + > >> + > >> + =A0 =A0def run_once(self, params): > >> + =A0 =A0 =A0 =A0""" > >> + =A0 =A0 =A0 =A0Setup remote machine and then execute migration. > >> + =A0 =A0 =A0 =A0""" > >> + =A0 =A0 =A0 =A0# Check whether remote machine is ready > >> + =A0 =A0 =A0 =A0dsthost =3D params.get("dsthost") > >> + =A0 =A0 =A0 =A0srchost =3D params.get("srchost") > >> + =A0 =A0 =A0 =A0image_path =3D os.path.join(self.bindir, "images"= ) > >> + > >> + =A0 =A0 =A0 =A0rootdir =3D params.get("rootdir") > >> + =A0 =A0 =A0 =A0iso =3D os.path.join(rootdir, 'iso') > >> + =A0 =A0 =A0 =A0images =3D os.path.join(rootdir, 'images') > >> + =A0 =A0 =A0 =A0qemu =3D os.path.join(rootdir, 'qemu') > >> + =A0 =A0 =A0 =A0qemu_img =3D os.path.join(rootdir, 'qemu-img') > >> + > >> + =A0 =A0 =A0 =A0def link_if_not_exist(ldir, target, link_name): > >> + =A0 =A0 =A0 =A0 =A0 =A0t =3D target > >> + =A0 =A0 =A0 =A0 =A0 =A0l =3D os.path.join(ldir, link_name) > >> + =A0 =A0 =A0 =A0 =A0 =A0if not os.path.exists(l): > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0os.symlink(t,l) > >> + =A0 =A0 =A0 =A0link_if_not_exist(self.bindir, '../../', 'autotes= t') > >> + =A0 =A0 =A0 =A0link_if_not_exist(self.bindir, iso, 'isos') > >> + =A0 =A0 =A0 =A0link_if_not_exist(self.bindir, images, 'images') > >> + =A0 =A0 =A0 =A0link_if_not_exist(self.bindir, qemu, 'qemu') > >> + =A0 =A0 =A0 =A0link_if_not_exist(self.bindir, qemu_img, 'qemu-im= g') > >> + > >> + =A0 =A0 =A0 =A0# Report the parameters we've received and write = them as keyvals > >> + =A0 =A0 =A0 =A0logging.debug("Test parameters:") > >> + =A0 =A0 =A0 =A0keys =3D params.keys() > >> + =A0 =A0 =A0 =A0keys.sort() > >> + =A0 =A0 =A0 =A0for key in keys: > >> + =A0 =A0 =A0 =A0 =A0 =A0logging.debug(" =A0 =A0%s =3D %s", key, p= arams[key]) > >> + =A0 =A0 =A0 =A0 =A0 =A0self.write_test_keyval({key: params[key]}= ) > >> + > >> + =A0 =A0 =A0 =A0# Open the environment file > >> + =A0 =A0 =A0 =A0env_filename =3D os.path.join(self.bindir, params= =2Eget("env", "env")) > >> + =A0 =A0 =A0 =A0env =3D kvm_utils.load_env(env_filename, {}) > >> + =A0 =A0 =A0 =A0logging.debug("Contents of environment: %s" % str= (env)) > >> + > >> + =A0 =A0 =A0 =A0# Preprocess > >> + =A0 =A0 =A0 =A0kvm_preprocessing.preprocess(self, params, env) > >> + =A0 =A0 =A0 =A0kvm_utils.dump_env(env, env_filename) > >> + > >> + =A0 =A0 =A0 =A0try: > >> + =A0 =A0 =A0 =A0 =A0 =A0try: > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0# Get the living VM > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0vm =3D kvm_test_utils.get_living_= vm(env, params.get("main_vm")) > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0migration_test_command =3D params= =2Eget("migration_test_command") > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0login_timeout =3D int(params.get(= "login_timeout")) > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0mig_timeout =3D int(params.get("m= ig_timeout")) > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0source_addr =3D (srchost, 50006) > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0all =3D [srchost, dsthost] > >> + > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0# Check whether migration is supp= orted > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0s, o =3D vm.send_monitor_cmd("hel= p info") > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if not "info migrate" in o: > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0raise error.TestError("Mi= gration is not supported") > >> + > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if params.get("role") =3D=3D "sou= rce": > >> + > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0session =3D kvm_test_util= s.wait_for_login(vm, > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0timeout=3Dlogin_time= out) > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ref_output =3D session.ge= t_command_output(migration_test_command) > >> + > >> + > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0# Listen on a port to get= the migration port received from > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0# dest machine > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0s =3D socket.socket() > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0s.bind(source_addr) > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0s.listen(1) > >> + > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0# Wait 30 seconds for sou= rce and dest to reach this point > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0self.job.barrier(srchost,= 'socket_started',30).rendezvous(*all) > >> + > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0conn, addr =3D s.accept() > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0mig_port =3D int(conn.rec= v(6)) > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0s.close() > >> + > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0logging.info("Start migra= ting now...") > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0kvm_test_utils.migrate(vm= , dsthost, mig_port, env) > >> + > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0# Wait up to 30 seconds f= or dest to reach this point > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0self.job.barrier(srchost,= 'mig_finished',30).rendezvous(*all) > >> + > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0session.close() > >> + > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0elif params.get("role") =3D=3D "d= est": > >> + > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0# Wait up to login_timeou= t+30 seconds for the source to > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0# reach this point > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0self.job.barrier(dsthost,= 'socket_started', > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 login_timeout+30).rendezvous(*all) > >> + > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0s =3D socket.socket() > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0s.connect(source_addr) > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0s.send("%d" % vm.migratio= n_port) > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0s.close() > >> + > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0# Wait up to mig_timeout+= 30 seconds for the source to > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0# reach this point: migra= tion finished > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0self.job.barrier(dsthost,= 'mig_finished', > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 mig_timeout+30).rendezvous(*all) > >> + > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0try: > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0session =3D kvm_t= est_utils.wait_for_login(vm) > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0except: > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0raise error.TestF= ail("Could not log into migrated guest") > >> + > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if session.get_command_st= atus(migration_test_command) !=3D 0: > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0raise error.TestF= ail("migration_test_command failed!") > > I think we should compare this output with the contents of ref_outp= ut > > from the source vm ? Yes, it had been added in my latest improvement. > >> + > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0else: > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0raise error.TestError('In= valid role specified') > >> + > >> + =A0 =A0 =A0 =A0 =A0 =A0except Exception, e: > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0logging.error("Test failed: %s", = e) > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0logging.debug("Postprocessing on = error...") > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0kvm_preprocessing.postprocess_on_= error(self, params, env) > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0kvm_utils.dump_env(env, env_filen= ame) > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0raise > >> + > >> + =A0 =A0 =A0 =A0finally: > >> + =A0 =A0 =A0 =A0 =A0 =A0# Postprocess > >> + =A0 =A0 =A0 =A0 =A0 =A0kvm_preprocessing.postprocess(self, param= s, env) > >> + =A0 =A0 =A0 =A0 =A0 =A0logging.debug("Contents of environment: %= s", str(env)) > >> + =A0 =A0 =A0 =A0 =A0 =A0kvm_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..3e3bd49 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=3D"shell", slee= p_before_reset=3D10, nic_index=3D0, > >> =A0 =A0 return session > >> > >> > >> -def migrate(vm, env=3DNone): > >> +def migrate(vm, rem_host, rem_port, env=3DNone, mig_timeout=3D90)= : > > can we use mig_port instead of rem_port or rem_mig_port ? > >> =A0 =A0 """ > >> =A0 =A0 Migrate a VM locally and re-register it in the environment= =2E > >> > >> @@ -133,13 +133,14 @@ def migrate(vm, env=3DNone): > >> =A0 =A0 if not "info migrate" in o: > >> =A0 =A0 =A0 =A0 raise error.TestError("Migration is not supported"= ) > >> > >> - =A0 =A0# Clone the source VM and ask the clone to wait for incom= ing migration > >> - =A0 =A0dest_vm =3D vm.clone() > >> - =A0 =A0dest_vm.create(for_migration=3DTrue) > >> + =A0 =A0if rem_host =3D=3D "localhost": > >> + =A0 =A0 =A0 =A0# Clone the source VM and ask the clone to wait f= or incoming migration > >> + =A0 =A0 =A0 =A0dest_vm =3D vm.clone() > >> + =A0 =A0 =A0 =A0dest_vm.create(for_migration=3DTrue) > >> > >> =A0 =A0 try: > >> =A0 =A0 =A0 =A0 # Define the migration command > >> - =A0 =A0 =A0 =A0cmd =3D "migrate -d tcp:localhost:%d" % dest_vm.m= igration_port > >> + =A0 =A0 =A0 =A0cmd =3D "migrate -d tcp:%s:%d" % (rem_host, rem_p= ort) > >> =A0 =A0 =A0 =A0 logging.debug("Migrating with command: %s" % cmd) > >> > >> =A0 =A0 =A0 =A0 # Migrate > >> @@ -150,7 +151,7 @@ def migrate(vm, env=3DNone): > >> =A0 =A0 =A0 =A0 =A0 =A0 raise error.TestFail("Migration command fa= iled") > >> > >> =A0 =A0 =A0 =A0 # Wait for migration to finish > >> - =A0 =A0 =A0 =A0if not kvm_utils.wait_for(mig_finished, 90, 2, 2, > >> + =A0 =A0 =A0 =A0if not kvm_utils.wait_for(mig_finished, mig_timeo= ut, 2, 2, > >> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= "Waiting for migration to finish..."): > >> =A0 =A0 =A0 =A0 =A0 =A0 raise error.TestFail("Timeout elapsed whil= e waiting for migration " > >> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= "to finish") > >> @@ -166,15 +167,17 @@ def migrate(vm, env=3DNone): > >> =A0 =A0 =A0 =A0 # Kill the source VM > >> =A0 =A0 =A0 =A0 vm.destroy(gracefully=3DFalse) > >> > >> - =A0 =A0 =A0 =A0# Replace the source VM with the new cloned VM > >> - =A0 =A0 =A0 =A0if env is not None: > >> - =A0 =A0 =A0 =A0 =A0 =A0kvm_utils.env_register_vm(env, vm.name, d= est_vm) > >> + =A0 =A0 =A0 =A0if rem_host =3D=3D "localhost": > >> + =A0 =A0 =A0 =A0 =A0 =A0# Replace the source VM with the new clon= ed VM > >> + =A0 =A0 =A0 =A0 =A0 =A0if env is not None: > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0kvm_utils.env_register_vm(env, vm= =2Ename, dest_vm) > >> > >> - =A0 =A0 =A0 =A0# Return the new cloned VM > >> - =A0 =A0 =A0 =A0return dest_vm > >> + =A0 =A0 =A0 =A0 =A0 =A0# Return the new cloned VM > >> + =A0 =A0 =A0 =A0 =A0 =A0return dest_vm > >> > >> =A0 =A0 except: > >> - =A0 =A0 =A0 =A0dest_vm.destroy() > >> + =A0 =A0 =A0 =A0if rem_host =3D=3D "localhost": > >> + =A0 =A0 =A0 =A0 =A0 =A0dest_vm.destroy() > >> =A0 =A0 =A0 =A0 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: > >> =A0 =A0 =A0 =A0 migration_bg_command =3D "cd /tmp; nohup tcpdump -= q -t ip host localhost" > >> =A0 =A0 =A0 =A0 migration_bg_check_command =3D pgrep tcpdump > >> =A0 =A0 =A0 =A0 migration_bg_kill_command =3D pkill tcpdump > >> + =A0 =A0 =A0 =A0mig_timeout =3D 90 > >> + =A0 =A0 =A0 =A0login_timeout =3D 240 > >> =A0 =A0 =A0 =A0 kill_vm_on_error =3D yes > >> =A0 =A0 =A0 =A0 iterations =3D 2 > >> =A0 =A0 =A0 =A0 used_mem =3D 1024 > >> diff --git a/client/tests/kvm_migration b/client/tests/kvm_migrati= on > >> 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..9774c43 > >> --- /dev/null > >> +++ b/server/tests/kvm/migration_control.srv > >> @@ -0,0 +1,137 @@ > >> +AUTHOR =3D "Yolkfull Chow " > >> +TIME =3D "SHORT" > >> +NAME =3D "Migration across Multi-machine" > >> +TEST_CATEGORY =3D "Functional" > >> +TEST_CLASS =3D 'Virtualization' > >> +TEST_TYPE =3D "Server" > >> +DOC =3D """ > >> +Migrate KVM guest between two hosts. > >> + > >> +Arguments to run_test: > >> + > >> +@dict - a dictionary containing all parameters that migration nee= d. > >> +""" > >> + > >> +import sys, os, commands, glob, shutil > >> +from autotest_lib.server import utils > >> + > >> +AUTOTEST_DIR =3D '/root/devel/upstream/server-migration' > > ?? > >> + > >> +CLIENT_TEST_DIR =3D os.path.join(AUTOTEST_DIR, 'client/tests/') > >> +KVM_DIR =3D os.path.join(CLIENT_TEST_DIR, 'kvm') > >> + > >> +sample_files =3D glob.glob(os.path.join(KVM_DIR, '*.cfg.sample')) > >> +for sample_file in sample_files: > >> + =A0 =A0sample_file_name =3D os.path.basename(sample_file) > >> + =A0 =A0cfg_file_name =3D ".".join(sample_file_name.split(".")[:-= 1]) > >> + =A0 =A0cfg_file_path =3D os.path.join(KVM_DIR, cfg_file_name) > >> + =A0 =A0sample_file_path =3D os.path.join(KVM_DIR, sample_file) > >> + =A0 =A0shutil.copyfile(sample_file_path, cfg_file_path) > > We are writing the .cfg.sample files to .cfg files here. I feel the > > user is supposed to do that as there may be needed some changes(lik= e > > allowing RHEL to run all the tests etc) I don't think user should/need log into each client machine and do so m= any duplicated works once before running a server-side test. > >> + > >> +sys.path.append(KVM_DIR) > >> +import common, kvm_config > >> + > >> +rootdir =3D '/tmp/kvm_autotest_root' > >> + > >> +def run(pair): > >> + =A0 =A0print "KVM migration running on srchost [%s] and desthost= [%s]\n" % ( > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0pair[0], pair[1]) > >> + > >> + =A0 =A0# Edit this to limit the migration that you want > >> + =A0 =A0test_variants =3D """ > >> +only full > >> +only qcow2 > >> +only ide > >> +only default > >> +only up > >> +only Fedora.11.32 > >> +no install setup > >> +no hugepages > >> +only migrate > >> +only rtl8139 > >> +""" > > Somehow this idea does not look to be the best. We should not write > > test configuration(i.e. variants) at multiple places. May be we can > > change the migration into two variants: local_migration and > > remote_migration. Client can run the local_migration but should > > exclude remote_migration. On the other side server should be able t= o > > run both of them. > > (NB: I do not have any hands on execution experience with autotest > > server, all my comments are based on my understanding built by code > > browsing) As metioned above, what user could touch (in WebUI) before actually run= ning a server-side test is the server control file. As a result of that, we can only limit the test via this method AFAIK. Please give the furthur implementation suggestion if you have a better = idea. Thanks, > >> + > >> + =A0 =A0source =3D hosts.create_host(pair[0]) > >> + =A0 =A0dest =3D hosts.create_host(pair[1]) > >> + > >> + =A0 =A0source_at =3D autotest.Autotest(source) > >> + =A0 =A0source_at.install(source) > >> + =A0 =A0dest_at =3D autotest.Autotest(dest) > >> + =A0 =A0dest_at.install(dest) > >> + > >> + =A0 =A0# FIXME: should parse config file on client machines? > >> + =A0 =A0filename =3D os.path.join(KVM_DIR, "kvm_tests.cfg.sample"= ) > >> + =A0 =A0sample_f =3D open(filename, 'r') > >> + =A0 =A0new_contents =3D sample_f.readlines()[:-1] > >> + =A0 =A0sample_f.close() > >> + > >> + =A0 =A0new_contents =3D "".join(new_contents) > > We are copying contents of .cfg.sample file while at this stage bot= h > > .cfg and .cfg.sample are same ? Of course not. The last line which is used to control final test is com= mented and therefore string that user can edit in server control file=20 is added to kvm_tests.cfg. In this way, user could limit the test. > >> + =A0 =A0new_contents +=3D test_variants > >> + > >> + =A0 =A0filename =3D os.path.join(KVM_DIR, 'kvm_tests.cfg') > >> + =A0 =A0f =3D open(filename, 'w') > >> + =A0 =A0f.write(new_contents) > >> + =A0 =A0f.close() > >> + > >> + =A0 =A0if not os.path.exists(filename): > >> + =A0 =A0 =A0 =A0print "[ERROR] Test config file doesn't found" > >> + =A0 =A0 =A0 =A0sys.exit(1) > >> + > >> + =A0 =A0# Get test set (dictionary list) from the configuration f= ile > >> + =A0 =A0cfg =3D kvm_config.config(filename) > >> + > >> + =A0 =A0filename =3D os.path.join(KVM_DIR, "kvm_address_pools.cfg= ") > >> + =A0 =A0if os.path.exists(filename): > >> + =A0 =A0 =A0 =A0cfg.parse_file(filename) > >> + =A0 =A0 =A0 =A0hostname =3D os.uname()[1].split(".")[0] > >> + =A0 =A0 =A0 =A0if cfg.filter("^" + hostname): > >> + =A0 =A0 =A0 =A0 =A0 =A0cfg.parse_string("only ^%s" % hostname) > >> + =A0 =A0 =A0 =A0else: > >> + =A0 =A0 =A0 =A0 =A0 =A0cfg.parse_string("only ^default_host") > >> + > >> + =A0 =A0list =3D cfg.get_list() > >> + > >> + =A0 =A0# Control file template for client machine > >> + =A0 =A0control_string =3D "job.run_test('kvm_migration', params=3D= %s)" > >> + > >> + =A0 =A0for vm_dict in list: > >> + > >> + =A0 =A0 =A0 =A0vm_dict['srchost'] =3D source.ip > >> + =A0 =A0 =A0 =A0vm_dict['dsthost'] =3D dest.ip > >> + =A0 =A0 =A0 =A0vm_dict['display'] =3D 'vnc' > >> + =A0 =A0 =A0 =A0vm_dict['rootdir'] =3D rootdir > >> + > >> + =A0 =A0 =A0 =A0source_dict =3D vm_dict.copy() > >> + =A0 =A0 =A0 =A0dest_dict =3D vm_dict.copy() > >> + > >> + =A0 =A0 =A0 =A0source_dict['role'] =3D "source" > >> + > >> + =A0 =A0 =A0 =A0dest_dict['role'] =3D "dest" > >> + =A0 =A0 =A0 =A0dest_dict['start_vm_for_migration'] =3D "yes" > >> + > >> + =A0 =A0 =A0 =A0# Report the parameters we've received > >> + =A0 =A0 =A0 =A0print "Test parameters:" > >> + =A0 =A0 =A0 =A0keys =3D vm_dict.keys() > >> + =A0 =A0 =A0 =A0keys.sort() > >> + =A0 =A0 =A0 =A0for key in keys: > >> + =A0 =A0 =A0 =A0 =A0 =A0print " =A0 =A0" + str(key) + " =3D " + s= tr(vm_dict[key]) > >> + > >> + =A0 =A0 =A0 =A0source_control_file =3D ''.join([control_string %= source_dict]) > >> + =A0 =A0 =A0 =A0dest_control_file =3D ''.join([control_string % d= est_dict]) > >> + > >> + =A0 =A0 =A0 =A0dest_command =3D subcommand(dest_at.run, > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0[dest_control_file, dest.hostname]) > >> + =A0 =A0 =A0 =A0source_command =3D subcommand(source_at.run, > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0[source_control_file, source.hostname]) > >> + > >> + =A0 =A0 =A0 =A0parallel([dest_command, source_command]) > >> + > >> +# Grab the pairs (and failures) > >> +(pairs, failures) =3D utils.form_ntuples_from_machines(machines, = 2) > >> + > >> +# Log the failures > >> +for failure in failures: > >> + =A0 =A0job.record("FAIL", failure[0], "kvm", failure[1]) > >> + > >> +# Now run through each pair and run > >> +job.parallel_simple(run, pairs, log=3DFalse) > >> -- > >> 1.6.5.2 > >> > >> _______________________________________________ > >> Autotest mailing list > >> Autotest@test.kernel.org > >> http://test.kernel.org/cgi-bin/mailman/listinfo/autotest > >> > > > > Rest of the patch looks to be clean to me. Thanks for review. :) Yolkfull Chow > > > > -- > > Sudhir Kumar > > >=20 >=20 >=20 > --=20 > Sudhir Kumar