From patchwork Mon Jan 3 18:27:14 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michael Goldish X-Patchwork-Id: 448631 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id p03IRBb1024206 for ; Mon, 3 Jan 2011 18:27:11 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752190Ab1ACS1B (ORCPT ); Mon, 3 Jan 2011 13:27:01 -0500 Received: from mx1.redhat.com ([209.132.183.28]:41140 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752186Ab1ACS1A (ORCPT ); Mon, 3 Jan 2011 13:27:00 -0500 Received: from int-mx01.intmail.prod.int.phx2.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id p03IQxsl021680 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Mon, 3 Jan 2011 13:26:59 -0500 Received: from ns3.rdu.redhat.com (ns3.rdu.redhat.com [10.11.255.199]) by int-mx01.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id p03IQxWj023574; Mon, 3 Jan 2011 13:26:59 -0500 Received: from moof.tlv.redhat.com (dhcp-1-185.tlv.redhat.com [10.35.1.185]) by ns3.rdu.redhat.com (8.13.8/8.13.8) with ESMTP id p03IQeWf027298; Mon, 3 Jan 2011 13:26:58 -0500 From: Michael Goldish To: autotest@test.kernel.org, kvm@vger.kernel.org Cc: Michael Goldish Subject: [KVM-AUTOTEST PATCH 13/17] KVM test: make migrate() a VM method Date: Mon, 3 Jan 2011 20:27:14 +0200 Message-Id: <1294079238-21239-13-git-send-email-mgoldish@redhat.com> In-Reply-To: <1294079238-21239-1-git-send-email-mgoldish@redhat.com> References: <1294079238-21239-1-git-send-email-mgoldish@redhat.com> X-Scanned-By: MIMEDefang 2.67 on 10.5.11.11 Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter1.kernel.org [140.211.167.41]); Mon, 03 Jan 2011 18:27:11 +0000 (UTC) diff --git a/client/tests/kvm/kvm_vm.py b/client/tests/kvm/kvm_vm.py index d236359..d6ca782 100755 --- a/client/tests/kvm/kvm_vm.py +++ b/client/tests/kvm/kvm_vm.py @@ -1348,6 +1348,148 @@ class VM: return self.serial_login(internal_timeout) + def migrate(self, timeout=3600, protocol="tcp", cancel_delay=None, + offline=False, stable_check=False, clean=True, + save_path="/tmp", dest_host="localhost", remote_port=None): + """ + Migrate the VM. + + If the migration is local, the VM object's state is switched with that + of the destination VM. Otherwise, the state is switched with that of + a dead VM (returned by self.clone()). + + @param timeout: Time to wait for migration to complete. + @param protocol: Migration protocol ('tcp', 'unix' or 'exec'). + @param cancel_delay: If provided, specifies a time duration after which + migration will be canceled. Used for testing migrate_cancel. + @param offline: If True, pause the source VM before migration. + @param stable_check: If True, compare the VM's state after migration to + its state before migration and raise an exception if they + differ. + @param clean: If True, delete the saved state files (relevant only if + stable_check is also True). + @save_path: The path for state files. + @param dest_host: Destination host (defaults to 'localhost'). + @param remote_port: Port to use for remote migration. + """ + def mig_finished(): + o = self.monitor.info("migrate") + if isinstance(o, str): + return "status: active" not in o + else: + return o.get("status") != "active" + + def mig_succeeded(): + o = self.monitor.info("migrate") + if isinstance(o, str): + return "status: completed" in o + else: + return o.get("status") == "completed" + + def mig_failed(): + o = self.monitor.info("migrate") + if isinstance(o, str): + return "status: failed" in o + else: + return o.get("status") == "failed" + + def mig_cancelled(): + o = self.monitor.info("migrate") + if isinstance(o, str): + return ("Migration status: cancelled" in o or + "Migration status: canceled" in o) + else: + return (o.get("status") == "cancelled" or + o.get("status") == "canceled") + + def wait_for_migration(): + if not kvm_utils.wait_for(mig_finished, timeout, 2, 2, + "Waiting for migration to finish..."): + raise VMMigrateTimeoutError("Timeout expired while waiting " + "for migration to finish") + + local = dest_host == "localhost" + + clone = self.clone() + if local: + if stable_check: + # Pause the dest vm after creation + extra_params = clone.params.get("extra_params", "") + " -S" + clone.params["extra_params"] = extra_params + clone.create(migration_mode=protocol, mac_source=self) + + try: + if protocol == "tcp": + if local: + uri = "tcp:localhost:%d" % clone.migration_port + else: + uri = "tcp:%s:%d" % (dest_host, remote_port) + elif protocol == "unix": + uri = "unix:%s" % clone.migration_file + elif protocol == "exec": + uri = '"exec:nc localhost %s"' % clone.migration_port + + if offline: + self.monitor.cmd("stop") + + self.monitor.migrate(uri) + + if cancel_delay: + time.sleep(cancel_delay) + self.monitor.cmd("migrate_cancel") + if not kvm_utils.wait_for(mig_cancelled, 60, 2, 2, + "Waiting for migration " + "cancellation"): + raise VMMigrateCancelError("Cannot cancel migration") + return + + wait_for_migration() + + # Report migration status + if mig_succeeded(): + logging.info("Migration completed successfully") + elif mig_failed(): + raise VMMigrateFailedError("Migration failed") + else: + raise VMMigrateFailedError("Migration ended with unknown " + "status") + + # Switch self <-> clone + temp = self.clone(copy_state=True) + self.__dict__ = clone.__dict__ + clone = temp + + # From now on, clone is the source VM that will soon be destroyed + # and self is the destination VM that will remain alive. If this + # is remote migration, self is a dead VM object. + + if local and stable_check: + try: + save1 = os.path.join(save_path, "src-" + clone.instance) + save2 = os.path.join(save_path, "dst-" + self.instance) + clone.save_to_file(save1) + self.save_to_file(save2) + # Fail if we see deltas + md5_save1 = utils.hash_file(save1) + md5_save2 = utils.hash_file(save2) + if md5_save1 != md5_save2: + raise VMMigrateStateMismatchError(md5_save1, + md5_save2) + finally: + if clean: + if os.path.isfile(save1): + os.remove(save1) + if os.path.isfile(save2): + os.remove(save2) + + finally: + # If we're doing remote migration and it's completed successfully, + # self points to a dead VM object + if self.is_alive(): + self.monitor.cmd("cont") + clone.destroy(gracefully=False) + + def send_key(self, keystr): """ Send a key event to the VM.