[Libvir] [PATCH] (for discussion only) KVM migration v1

Richard W.M. Jones rjones at redhat.com
Tue Apr 8 18:54:01 UTC 2008


This patch implements KVM to KVM migration.  It is for discussion
only, partly because it doesn't work for some reason I can't quite
work out at the moment.

We implement a second version of the migration protocol.  This second
version has two differences:

(1) Prepare step is replaced by Prepare2, which passes the domain XML.
As explained previously this is required for KVM because we use this
on the target host to recreate the precise qemu-kvm command line as on
the source host.

(2) Finish step is replaced by Finish2.  There are two differences
here: firstly Finish2 is always called on the destination host, even
if the migration failed.  Secondly the return code from the migration
(Perform step) is passed to Finish2.  This is required for KVM
migration because if the migration failed we need to tear down the
empty qemu shell, otherwise failed migrations could leave effectively
zombie qemu processes around.

libvirt.c:virDomainMigrate function has been changed so that it can
support either form of migration protocol, and the Xen driver
continues to use version 1.  The changes here are pretty minor, and
there are no changes to the Xen driver.

The additional code involves implementing Prepare2 / Finish2 in the
remote protocol, and of course implementing migration in the qemu
driver itself.

A final word about the parameters to virDomainMigrate.

 - flags is ignored.  All KVM migrations are "live", it doesn't matter
   if you supply the live flag or not.

 - desturi may optionally be used to control the TCP port used for
   migration.  If desturi is NULL then a TCP port is chosen at
   random (or in future, some suitable secure method will be used
   instead).  If desturi is set to "tcp://hostname:port" then the
   given port number is used, and hostname is expected to be the
   hostname or IP address of the target server.

You cannot do localhost->localhost migrations (even though this is
supported by KVM) because libvirtd doesn't like you creating two VMs
with the same UUID, even if only temporarily.  So to test this you
really need two machines, or at least two instances of libvirtd
configured not to stomp on each other.

Rich.

-- 
Richard Jones, Emerging Technologies, Red Hat  http://et.redhat.com/~rjones
virt-top is 'top' for virtual machines.  Tiny program with many
powerful monitoring features, net stats, disk stats, logging, etc.
http://et.redhat.com/~rjones/virt-top
-------------- next part --------------
Index: qemud/remote.c
===================================================================
RCS file: /data/cvs/libvirt/qemud/remote.c,v
retrieving revision 1.28
diff -u -r1.28 remote.c
--- qemud/remote.c	4 Apr 2008 15:09:19 -0000	1.28
+++ qemud/remote.c	8 Apr 2008 18:43:58 -0000
@@ -1285,6 +1285,66 @@
 }
 
 static int
+remoteDispatchDomainMigratePrepare2 (struct qemud_server *server ATTRIBUTE_UNUSED,
+                                     struct qemud_client *client,
+                                     remote_message_header *req,
+                                     remote_domain_migrate_prepare2_args *args,
+                                     remote_domain_migrate_prepare2_ret *ret)
+{
+    int r;
+    char *cookie = NULL;
+    int cookielen = 0;
+    char *uri_in;
+    char **uri_out;
+    char *dname;
+    CHECK_CONN (client);
+
+    uri_in = args->uri_in == NULL ? NULL : *args->uri_in;
+    dname = args->dname == NULL ? NULL : *args->dname;
+
+    /* Wacky world of XDR ... */
+    uri_out = calloc (1, sizeof (*uri_out));
+
+    r = __virDomainMigratePrepare2 (client->conn, &cookie, &cookielen,
+                                    uri_in, uri_out,
+                                    args->flags, dname, args->resource,
+                                    args->dom_xml);
+    if (r == -1) return -1;
+
+    /* remoteDispatchClientRequest will free cookie, uri_out and
+     * the string if there is one.
+     */
+    ret->cookie.cookie_len = cookielen;
+    ret->cookie.cookie_val = cookie;
+    ret->uri_out = *uri_out == NULL ? NULL : uri_out;
+
+    return 0;
+}
+
+static int
+remoteDispatchDomainMigrateFinish2 (struct qemud_server *server ATTRIBUTE_UNUSED,
+                                    struct qemud_client *client,
+                                    remote_message_header *req,
+                                    remote_domain_migrate_finish2_args *args,
+                                    remote_domain_migrate_finish2_ret *ret)
+{
+    virDomainPtr ddom;
+    CHECK_CONN (client);
+
+    ddom = __virDomainMigrateFinish2 (client->conn, args->dname,
+                                      args->cookie.cookie_val,
+                                      args->cookie.cookie_len,
+                                      args->uri,
+                                      args->flags,
+                                      args->retcode);
+    if (ddom == NULL) return -1;
+
+    make_nonnull_domain (&ret->ddom, ddom);
+
+    return 0;
+}
+
+static int
 remoteDispatchListDefinedDomains (struct qemud_server *server ATTRIBUTE_UNUSED,
                                   struct qemud_client *client,
                                   remote_message_header *req,
Index: qemud/remote_dispatch_localvars.h
===================================================================
RCS file: /data/cvs/libvirt/qemud/remote_dispatch_localvars.h,v
retrieving revision 1.9
diff -u -r1.9 remote_dispatch_localvars.h
--- qemud/remote_dispatch_localvars.h	20 Feb 2008 15:22:35 -0000	1.9
+++ qemud/remote_dispatch_localvars.h	8 Apr 2008 18:43:58 -0000
@@ -119,9 +119,13 @@
 remote_list_networks_args lv_remote_list_networks_args;
 remote_list_networks_ret lv_remote_list_networks_ret;
 remote_storage_pool_undefine_args lv_remote_storage_pool_undefine_args;
+remote_domain_migrate_finish2_args lv_remote_domain_migrate_finish2_args;
+remote_domain_migrate_finish2_ret lv_remote_domain_migrate_finish2_ret;
 remote_domain_set_autostart_args lv_remote_domain_set_autostart_args;
 remote_storage_pool_get_autostart_args lv_remote_storage_pool_get_autostart_args;
 remote_storage_pool_get_autostart_ret lv_remote_storage_pool_get_autostart_ret;
+remote_domain_migrate_prepare2_args lv_remote_domain_migrate_prepare2_args;
+remote_domain_migrate_prepare2_ret lv_remote_domain_migrate_prepare2_ret;
 remote_storage_vol_get_path_args lv_remote_storage_vol_get_path_args;
 remote_storage_vol_get_path_ret lv_remote_storage_vol_get_path_ret;
 remote_domain_lookup_by_id_args lv_remote_domain_lookup_by_id_args;
Index: qemud/remote_dispatch_proc_switch.h
===================================================================
RCS file: /data/cvs/libvirt/qemud/remote_dispatch_proc_switch.h,v
retrieving revision 1.9
diff -u -r1.9 remote_dispatch_proc_switch.h
--- qemud/remote_dispatch_proc_switch.h	20 Feb 2008 15:22:35 -0000	1.9
+++ qemud/remote_dispatch_proc_switch.h	8 Apr 2008 18:43:59 -0000
@@ -224,6 +224,15 @@
 	ret = (char *) &lv_remote_domain_migrate_finish_ret;
 	memset (&lv_remote_domain_migrate_finish_ret, 0, sizeof lv_remote_domain_migrate_finish_ret);
 	break;
+case REMOTE_PROC_DOMAIN_MIGRATE_FINISH2:
+	fn = (dispatch_fn) remoteDispatchDomainMigrateFinish2;
+	args_filter = (xdrproc_t) xdr_remote_domain_migrate_finish2_args;
+	args = (char *) &lv_remote_domain_migrate_finish2_args;
+	memset (&lv_remote_domain_migrate_finish2_args, 0, sizeof lv_remote_domain_migrate_finish2_args);
+	ret_filter = (xdrproc_t) xdr_remote_domain_migrate_finish2_ret;
+	ret = (char *) &lv_remote_domain_migrate_finish2_ret;
+	memset (&lv_remote_domain_migrate_finish2_ret, 0, sizeof lv_remote_domain_migrate_finish2_ret);
+	break;
 case REMOTE_PROC_DOMAIN_MIGRATE_PERFORM:
 	fn = (dispatch_fn) remoteDispatchDomainMigratePerform;
 	args_filter = (xdrproc_t) xdr_remote_domain_migrate_perform_args;
@@ -239,6 +248,15 @@
 	ret = (char *) &lv_remote_domain_migrate_prepare_ret;
 	memset (&lv_remote_domain_migrate_prepare_ret, 0, sizeof lv_remote_domain_migrate_prepare_ret);
 	break;
+case REMOTE_PROC_DOMAIN_MIGRATE_PREPARE2:
+	fn = (dispatch_fn) remoteDispatchDomainMigratePrepare2;
+	args_filter = (xdrproc_t) xdr_remote_domain_migrate_prepare2_args;
+	args = (char *) &lv_remote_domain_migrate_prepare2_args;
+	memset (&lv_remote_domain_migrate_prepare2_args, 0, sizeof lv_remote_domain_migrate_prepare2_args);
+	ret_filter = (xdrproc_t) xdr_remote_domain_migrate_prepare2_ret;
+	ret = (char *) &lv_remote_domain_migrate_prepare2_ret;
+	memset (&lv_remote_domain_migrate_prepare2_ret, 0, sizeof lv_remote_domain_migrate_prepare2_ret);
+	break;
 case REMOTE_PROC_DOMAIN_PIN_VCPU:
 	fn = (dispatch_fn) remoteDispatchDomainPinVcpu;
 	args_filter = (xdrproc_t) xdr_remote_domain_pin_vcpu_args;
Index: qemud/remote_dispatch_prototypes.h
===================================================================
RCS file: /data/cvs/libvirt/qemud/remote_dispatch_prototypes.h,v
retrieving revision 1.10
diff -u -r1.10 remote_dispatch_prototypes.h
--- qemud/remote_dispatch_prototypes.h	20 Feb 2008 15:22:35 -0000	1.10
+++ qemud/remote_dispatch_prototypes.h	8 Apr 2008 18:44:00 -0000
@@ -30,8 +30,10 @@
 static int remoteDispatchDomainLookupByName (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_lookup_by_name_args *args, remote_domain_lookup_by_name_ret *ret);
 static int remoteDispatchDomainLookupByUuid (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_lookup_by_uuid_args *args, remote_domain_lookup_by_uuid_ret *ret);
 static int remoteDispatchDomainMigrateFinish (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_migrate_finish_args *args, remote_domain_migrate_finish_ret *ret);
+static int remoteDispatchDomainMigrateFinish2 (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_migrate_finish2_args *args, remote_domain_migrate_finish2_ret *ret);
 static int remoteDispatchDomainMigratePerform (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_migrate_perform_args *args, void *ret);
 static int remoteDispatchDomainMigratePrepare (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_migrate_prepare_args *args, remote_domain_migrate_prepare_ret *ret);
+static int remoteDispatchDomainMigratePrepare2 (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_migrate_prepare2_args *args, remote_domain_migrate_prepare2_ret *ret);
 static int remoteDispatchDomainPinVcpu (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_pin_vcpu_args *args, void *ret);
 static int remoteDispatchDomainReboot (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_reboot_args *args, void *ret);
 static int remoteDispatchDomainRestore (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_restore_args *args, void *ret);
Index: qemud/remote_protocol.c
===================================================================
RCS file: /data/cvs/libvirt/qemud/remote_protocol.c,v
retrieving revision 1.9
diff -u -r1.9 remote_protocol.c
--- qemud/remote_protocol.c	20 Feb 2008 15:22:35 -0000	1.9
+++ qemud/remote_protocol.c	8 Apr 2008 18:44:01 -0000
@@ -863,6 +863,62 @@
 }
 
 bool_t
+xdr_remote_domain_migrate_prepare2_args (XDR *xdrs, remote_domain_migrate_prepare2_args *objp)
+{
+
+	 if (!xdr_remote_string (xdrs, &objp->uri_in))
+		 return FALSE;
+	 if (!xdr_u_quad_t (xdrs, &objp->flags))
+		 return FALSE;
+	 if (!xdr_remote_string (xdrs, &objp->dname))
+		 return FALSE;
+	 if (!xdr_u_quad_t (xdrs, &objp->resource))
+		 return FALSE;
+	 if (!xdr_remote_nonnull_string (xdrs, &objp->dom_xml))
+		 return FALSE;
+	return TRUE;
+}
+
+bool_t
+xdr_remote_domain_migrate_prepare2_ret (XDR *xdrs, remote_domain_migrate_prepare2_ret *objp)
+{
+	char **objp_cpp0 = (char **) (void *) &objp->cookie.cookie_val;
+
+	 if (!xdr_bytes (xdrs, objp_cpp0, (u_int *) &objp->cookie.cookie_len, REMOTE_MIGRATE_COOKIE_MAX))
+		 return FALSE;
+	 if (!xdr_remote_string (xdrs, &objp->uri_out))
+		 return FALSE;
+	return TRUE;
+}
+
+bool_t
+xdr_remote_domain_migrate_finish2_args (XDR *xdrs, remote_domain_migrate_finish2_args *objp)
+{
+	char **objp_cpp0 = (char **) (void *) &objp->cookie.cookie_val;
+
+	 if (!xdr_remote_nonnull_string (xdrs, &objp->dname))
+		 return FALSE;
+	 if (!xdr_bytes (xdrs, objp_cpp0, (u_int *) &objp->cookie.cookie_len, REMOTE_MIGRATE_COOKIE_MAX))
+		 return FALSE;
+	 if (!xdr_remote_nonnull_string (xdrs, &objp->uri))
+		 return FALSE;
+	 if (!xdr_u_quad_t (xdrs, &objp->flags))
+		 return FALSE;
+	 if (!xdr_int (xdrs, &objp->retcode))
+		 return FALSE;
+	return TRUE;
+}
+
+bool_t
+xdr_remote_domain_migrate_finish2_ret (XDR *xdrs, remote_domain_migrate_finish2_ret *objp)
+{
+
+	 if (!xdr_remote_nonnull_domain (xdrs, &objp->ddom))
+		 return FALSE;
+	return TRUE;
+}
+
+bool_t
 xdr_remote_list_defined_domains_args (XDR *xdrs, remote_list_defined_domains_args *objp)
 {
 
Index: qemud/remote_protocol.h
===================================================================
RCS file: /data/cvs/libvirt/qemud/remote_protocol.h,v
retrieving revision 1.10
diff -u -r1.10 remote_protocol.h
--- qemud/remote_protocol.h	20 Feb 2008 15:22:35 -0000	1.10
+++ qemud/remote_protocol.h	8 Apr 2008 18:44:02 -0000
@@ -449,6 +449,41 @@
 };
 typedef struct remote_domain_migrate_finish_ret remote_domain_migrate_finish_ret;
 
+struct remote_domain_migrate_prepare2_args {
+	remote_string uri_in;
+	u_quad_t flags;
+	remote_string dname;
+	u_quad_t resource;
+	remote_nonnull_string dom_xml;
+};
+typedef struct remote_domain_migrate_prepare2_args remote_domain_migrate_prepare2_args;
+
+struct remote_domain_migrate_prepare2_ret {
+	struct {
+		u_int cookie_len;
+		char *cookie_val;
+	} cookie;
+	remote_string uri_out;
+};
+typedef struct remote_domain_migrate_prepare2_ret remote_domain_migrate_prepare2_ret;
+
+struct remote_domain_migrate_finish2_args {
+	remote_nonnull_string dname;
+	struct {
+		u_int cookie_len;
+		char *cookie_val;
+	} cookie;
+	remote_nonnull_string uri;
+	u_quad_t flags;
+	int retcode;
+};
+typedef struct remote_domain_migrate_finish2_args remote_domain_migrate_finish2_args;
+
+struct remote_domain_migrate_finish2_ret {
+	remote_nonnull_domain ddom;
+};
+typedef struct remote_domain_migrate_finish2_ret remote_domain_migrate_finish2_ret;
+
 struct remote_list_defined_domains_args {
 	int maxnames;
 };
@@ -1116,6 +1151,8 @@
 	REMOTE_PROC_STORAGE_VOL_GET_INFO = 98,
 	REMOTE_PROC_STORAGE_VOL_DUMP_XML = 99,
 	REMOTE_PROC_STORAGE_VOL_GET_PATH = 100,
+	REMOTE_PROC_DOMAIN_MIGRATE_PREPARE2 = 101,
+	REMOTE_PROC_DOMAIN_MIGRATE_FINISH2 = 102,
 };
 typedef enum remote_procedure remote_procedure;
 
@@ -1215,6 +1252,10 @@
 extern  bool_t xdr_remote_domain_migrate_perform_args (XDR *, remote_domain_migrate_perform_args*);
 extern  bool_t xdr_remote_domain_migrate_finish_args (XDR *, remote_domain_migrate_finish_args*);
 extern  bool_t xdr_remote_domain_migrate_finish_ret (XDR *, remote_domain_migrate_finish_ret*);
+extern  bool_t xdr_remote_domain_migrate_prepare2_args (XDR *, remote_domain_migrate_prepare2_args*);
+extern  bool_t xdr_remote_domain_migrate_prepare2_ret (XDR *, remote_domain_migrate_prepare2_ret*);
+extern  bool_t xdr_remote_domain_migrate_finish2_args (XDR *, remote_domain_migrate_finish2_args*);
+extern  bool_t xdr_remote_domain_migrate_finish2_ret (XDR *, remote_domain_migrate_finish2_ret*);
 extern  bool_t xdr_remote_list_defined_domains_args (XDR *, remote_list_defined_domains_args*);
 extern  bool_t xdr_remote_list_defined_domains_ret (XDR *, remote_list_defined_domains_ret*);
 extern  bool_t xdr_remote_num_of_defined_domains_ret (XDR *, remote_num_of_defined_domains_ret*);
@@ -1387,6 +1428,10 @@
 extern bool_t xdr_remote_domain_migrate_perform_args ();
 extern bool_t xdr_remote_domain_migrate_finish_args ();
 extern bool_t xdr_remote_domain_migrate_finish_ret ();
+extern bool_t xdr_remote_domain_migrate_prepare2_args ();
+extern bool_t xdr_remote_domain_migrate_prepare2_ret ();
+extern bool_t xdr_remote_domain_migrate_finish2_args ();
+extern bool_t xdr_remote_domain_migrate_finish2_ret ();
 extern bool_t xdr_remote_list_defined_domains_args ();
 extern bool_t xdr_remote_list_defined_domains_ret ();
 extern bool_t xdr_remote_num_of_defined_domains_ret ();
Index: qemud/remote_protocol.x
===================================================================
RCS file: /data/cvs/libvirt/qemud/remote_protocol.x,v
retrieving revision 1.10
diff -u -r1.10 remote_protocol.x
--- qemud/remote_protocol.x	20 Feb 2008 15:22:35 -0000	1.10
+++ qemud/remote_protocol.x	8 Apr 2008 18:44:03 -0000
@@ -466,6 +466,31 @@
     remote_nonnull_domain ddom;
 };
 
+struct remote_domain_migrate_prepare2_args {
+    remote_string uri_in;
+    unsigned hyper flags;
+    remote_string dname;
+    unsigned hyper resource;
+    remote_nonnull_string dom_xml;
+};
+
+struct remote_domain_migrate_prepare2_ret {
+    opaque cookie<REMOTE_MIGRATE_COOKIE_MAX>;
+    remote_string uri_out;
+};
+
+struct remote_domain_migrate_finish2_args {
+    remote_nonnull_string dname;
+    opaque cookie<REMOTE_MIGRATE_COOKIE_MAX>;
+    remote_nonnull_string uri;
+    unsigned hyper flags;
+    int retcode;
+};
+
+struct remote_domain_migrate_finish2_ret {
+    remote_nonnull_domain ddom;
+};
+
 struct remote_list_defined_domains_args {
     int maxnames;
 };
@@ -1017,7 +1042,10 @@
     REMOTE_PROC_STORAGE_VOL_LOOKUP_BY_PATH = 97,
     REMOTE_PROC_STORAGE_VOL_GET_INFO = 98,
     REMOTE_PROC_STORAGE_VOL_DUMP_XML = 99,
-    REMOTE_PROC_STORAGE_VOL_GET_PATH = 100
+    REMOTE_PROC_STORAGE_VOL_GET_PATH = 100,
+
+    REMOTE_PROC_DOMAIN_MIGRATE_PREPARE2 = 101,
+    REMOTE_PROC_DOMAIN_MIGRATE_FINISH2 = 102
 };
 
 /* Custom RPC structure. */
Index: src/driver.h
===================================================================
RCS file: /data/cvs/libvirt/src/driver.h,v
retrieving revision 1.44
diff -u -r1.44 driver.h
--- src/driver.h	21 Mar 2008 15:03:37 -0000	1.44
+++ src/driver.h	8 Apr 2008 18:44:03 -0000
@@ -57,6 +57,11 @@
     /* Driver is not local. */
 #define VIR_DRV_FEATURE_REMOTE 2
 
+    /* Driver supports V2-style virDomainMigrate, ie. domainMigratePrepare2/
+     * domainMigratePerform/domainMigrateFinish2.
+     */
+#define VIR_DRV_FEATURE_MIGRATION_V2 3
+
 /* Internal feature-detection macro.  Don't call drv->supports_feature
  * directly, because it may be NULL, use this macro instead.
  *
@@ -267,6 +272,28 @@
     (*virDrvNodeGetFreeMemory)
 		    (virConnectPtr conn);
 
+typedef int
+    (*virDrvDomainMigratePrepare2)
+                    (virConnectPtr dconn,
+                     char **cookie,
+                     int *cookielen,
+                     const char *uri_in,
+                     char **uri_out,
+                     unsigned long flags,
+                     const char *dname,
+                     unsigned long resource,
+                     const char *dom_xml);
+
+typedef virDomainPtr
+    (*virDrvDomainMigrateFinish2)
+                    (virConnectPtr dconn,
+                     const char *dname,
+                     const char *cookie,
+                     int cookielen,
+                     const char *uri,
+                     unsigned long flags,
+                     int retcode);
+
 /**
  * _virDriver:
  *
@@ -337,6 +364,8 @@
     virDrvDomainInterfaceStats  domainInterfaceStats;
     virDrvNodeGetCellsFreeMemory	nodeGetCellsFreeMemory;
     virDrvNodeGetFreeMemory		getFreeMemory;
+    virDrvDomainMigratePrepare2	domainMigratePrepare2;
+    virDrvDomainMigrateFinish2	domainMigrateFinish2;
 };
 
 typedef int
Index: src/internal.h
===================================================================
RCS file: /data/cvs/libvirt/src/internal.h,v
retrieving revision 1.65
diff -u -r1.65 internal.h
--- src/internal.h	31 Mar 2008 14:38:13 -0000	1.65
+++ src/internal.h	8 Apr 2008 18:44:03 -0000
@@ -325,6 +325,9 @@
 int __virDomainMigratePerform (virDomainPtr domain, const char *cookie, int cookielen, const char *uri, unsigned long flags, const char *dname, unsigned long bandwidth);
 virDomainPtr __virDomainMigrateFinish (virConnectPtr dconn, const char *dname, const char *cookie, int cookielen, const char *uri, unsigned long flags);
 
+int __virDomainMigratePrepare2 (virConnectPtr dconn, char **cookie, int *cookielen, const char *uri_in, char **uri_out, unsigned long flags, const char *dname, unsigned long bandwidth, const char *dom_xml);
+virDomainPtr __virDomainMigrateFinish2 (virConnectPtr dconn, const char *dname, const char *cookie, int cookielen, const char *uri, unsigned long flags, int retcode);
+
 #ifdef __cplusplus
 }
 #endif                          /* __cplusplus */
Index: src/libvirt.c
===================================================================
RCS file: /data/cvs/libvirt/src/libvirt.c,v
retrieving revision 1.134
diff -u -r1.134 libvirt.c
--- src/libvirt.c	4 Apr 2008 15:09:19 -0000	1.134
+++ src/libvirt.c	8 Apr 2008 18:44:06 -0000
@@ -2128,7 +2128,8 @@
     virDomainPtr ddomain = NULL;
     char *uri_out = NULL;
     char *cookie = NULL;
-    int cookielen = 0, ret;
+    char *dom_xml = NULL;
+    int cookielen = 0, ret, version = 0;
     DEBUG("domain=%p, dconn=%p, flags=%lu, dname=%s, uri=%s, bandwidth=%lu",
           domain, dconn, flags, dname, uri, bandwidth);
 
@@ -2143,10 +2144,17 @@
     }
 
     /* Check that migration is supported by both drivers. */
-    if (!VIR_DRV_SUPPORTS_FEATURE (conn->driver, conn,
-                                   VIR_DRV_FEATURE_MIGRATION_V1) ||
-        !VIR_DRV_SUPPORTS_FEATURE (dconn->driver, dconn,
-                                   VIR_DRV_FEATURE_MIGRATION_V1)) {
+    if (VIR_DRV_SUPPORTS_FEATURE (conn->driver, conn,
+                                  VIR_DRV_FEATURE_MIGRATION_V1) &&
+        VIR_DRV_SUPPORTS_FEATURE (dconn->driver, dconn,
+                                  VIR_DRV_FEATURE_MIGRATION_V1))
+        version = 1;
+    else if (VIR_DRV_SUPPORTS_FEATURE (conn->driver, conn,
+                                       VIR_DRV_FEATURE_MIGRATION_V2) &&
+             VIR_DRV_SUPPORTS_FEATURE (dconn->driver, dconn,
+                                       VIR_DRV_FEATURE_MIGRATION_V2))
+        version = 2;
+    else {
         virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
         return NULL;
     }
@@ -2162,17 +2170,49 @@
      * the URI by setting uri_out.  If it does not wish to modify
      * the URI, it should leave uri_out as NULL.
      */
-    ret = dconn->driver->domainMigratePrepare
-        (dconn, &cookie, &cookielen, uri, &uri_out, flags, dname, bandwidth);
-    if (ret == -1) goto done;
-    if (uri == NULL && uri_out == NULL) {
-        virLibConnError (conn, VIR_ERR_INTERNAL_ERROR,
-                         _("domainMigratePrepare did not set uri"));
-        goto done;
-    }
-    if (uri_out) uri = uri_out; /* Did domainMigratePrepare change URI? */
+    if (version == 1) {
+        ret = dconn->driver->domainMigratePrepare
+            (dconn, &cookie, &cookielen, uri, &uri_out, flags, dname,
+             bandwidth);
+        if (ret == -1) goto done;
+        if (uri == NULL && uri_out == NULL) {
+            virLibConnError (conn, VIR_ERR_INTERNAL_ERROR,
+                             _("domainMigratePrepare did not set uri"));
+            goto done;
+        }
+        if (uri_out) uri = uri_out; /* Did domainMigratePrepare change URI? */
+
+        assert (uri != NULL);
+    }
+    else /* if (version == 2) */ {
+        /* In version 2 of the protocol, the prepare step is slightly
+         * different.  We fetch the domain XML of the source domain
+         * and pass it to Prepare2.
+         */
+        if (!conn->driver->domainDumpXML) {
+            virLibConnError (conn, VIR_ERR_INTERNAL_ERROR, __FUNCTION__);
+            return NULL;
+        }
+        dom_xml = conn->driver->domainDumpXML (domain,
+                                               VIR_DOMAIN_XML_SECURE);
+
+        if (!dom_xml)
+            return NULL;
+
+        ret = dconn->driver->domainMigratePrepare2
+            (dconn, &cookie, &cookielen, uri, &uri_out, flags, dname,
+             bandwidth, dom_xml);
+        free (dom_xml);
+        if (ret == -1) goto done;
+        if (uri == NULL && uri_out == NULL) {
+            virLibConnError (conn, VIR_ERR_INTERNAL_ERROR,
+                             _("domainMigratePrepare2 did not set uri"));
+            goto done;
+        }
+        if (uri_out) uri = uri_out; /* Did domainMigratePrepare2 change URI? */
 
-    assert (uri != NULL);
+        assert (uri != NULL);
+    }
 
     /* Perform the migration.  The driver isn't supposed to return
      * until the migration is complete.
@@ -2180,18 +2220,28 @@
     ret = conn->driver->domainMigratePerform
         (domain, cookie, cookielen, uri, flags, dname, bandwidth);
 
-    if (ret == -1) goto done;
-
-    /* Get the destination domain and return it or error.
-     * 'domain' no longer actually exists at this point (or so we hope), but
-     * we still use the object in memory in order to get the name.
-     */
-    dname = dname ? dname : domain->name;
-    if (dconn->driver->domainMigrateFinish)
-        ddomain = dconn->driver->domainMigrateFinish
-            (dconn, dname, cookie, cookielen, uri, flags);
-    else
-        ddomain = virDomainLookupByName (dconn, dname);
+    if (version == 1) {
+        if (ret == -1) goto done;
+        /* Get the destination domain and return it or error.
+         * 'domain' no longer actually exists at this point
+         * (or so we hope), but we still use the object in memory
+         * in order to get the name.
+         */
+        dname = dname ? dname : domain->name;
+        if (dconn->driver->domainMigrateFinish)
+            ddomain = dconn->driver->domainMigrateFinish
+                (dconn, dname, cookie, cookielen, uri, flags);
+        else
+            ddomain = virDomainLookupByName (dconn, dname);
+    } else /* if (version == 2) */ {
+        /* In version 2 of the migration protocol, we pass the
+         * status code from the sender to the destination host,
+         * so it can do any cleanup if the migration failed.
+         */
+        dname = dname ? dname : domain->name;
+        ddomain = dconn->driver->domainMigrateFinish2
+            (dconn, dname, cookie, cookielen, uri, flags, ret);
+    }
 
  done:
     free (uri_out);
@@ -2286,6 +2336,67 @@
 }
 
 
+/* Not for public use.  This function is part of the internal
+ * implementation of migration in the remote case.
+ */
+int
+__virDomainMigratePrepare2 (virConnectPtr dconn,
+                            char **cookie,
+                            int *cookielen,
+                            const char *uri_in,
+                            char **uri_out,
+                            unsigned long flags,
+                            const char *dname,
+                            unsigned long bandwidth,
+                            const char *dom_xml)
+{
+    DEBUG("dconn=%p, cookie=%p, cookielen=%p, uri_in=%s, uri_out=%p, flags=%lu, dname=%s, bandwidth=%lu, dom_xml=%s", dconn, cookie, cookielen, uri_in, uri_out, flags, dname, bandwidth, dom_xml);
+
+    if (!VIR_IS_CONNECT (dconn)) {
+        virLibConnError (NULL, VIR_ERR_INVALID_CONN, __FUNCTION__);
+        return -1;
+    }
+
+    if (dconn->driver->domainMigratePrepare2)
+        return dconn->driver->domainMigratePrepare2 (dconn, cookie, cookielen,
+                                                     uri_in, uri_out,
+                                                     flags, dname, bandwidth,
+                                                     dom_xml);
+
+    virLibConnError (dconn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+    return -1;
+}
+
+/* Not for public use.  This function is part of the internal
+ * implementation of migration in the remote case.
+ */
+virDomainPtr
+__virDomainMigrateFinish2 (virConnectPtr dconn,
+                           const char *dname,
+                           const char *cookie,
+                           int cookielen,
+                           const char *uri,
+                           unsigned long flags,
+                           int retcode)
+{
+    DEBUG("dconn=%p, dname=%s, cookie=%p, cookielen=%d, uri=%s, flags=%lu, retcode=%d", dconn, dname, cookie, cookielen, uri, flags, retcode);
+
+    if (!VIR_IS_CONNECT (dconn)) {
+        virLibConnError (NULL, VIR_ERR_INVALID_CONN, __FUNCTION__);
+        return NULL;
+    }
+
+    if (dconn->driver->domainMigrateFinish2)
+        return dconn->driver->domainMigrateFinish2 (dconn, dname,
+                                                    cookie, cookielen,
+                                                    uri, flags,
+                                                    retcode);
+
+    virLibConnError (dconn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+    return NULL;
+}
+
+
 /**
  * virNodeGetInfo:
  * @conn: pointer to the hypervisor connection
Index: src/libvirt_sym.version
===================================================================
RCS file: /data/cvs/libvirt/src/libvirt_sym.version,v
retrieving revision 1.38
diff -u -r1.38 libvirt_sym.version
--- src/libvirt_sym.version	28 Feb 2008 17:06:32 -0000	1.38
+++ src/libvirt_sym.version	8 Apr 2008 18:44:07 -0000
@@ -176,6 +176,8 @@
 	__virDomainMigratePrepare;
 	__virDomainMigratePerform;
 	__virDomainMigrateFinish;
+	__virDomainMigratePrepare2;
+	__virDomainMigrateFinish2;
 
         __virFileReadAll;
         __virStrToLong_i;
Index: src/qemu_conf.h
===================================================================
RCS file: /data/cvs/libvirt/src/qemu_conf.h,v
retrieving revision 1.21
diff -u -r1.21 qemu_conf.h
--- src/qemu_conf.h	28 Mar 2008 20:38:21 -0000	1.21
+++ src/qemu_conf.h	8 Apr 2008 18:44:07 -0000
@@ -326,6 +326,9 @@
     virCapsPtr caps;
 };
 
+/* Port numbers used for KVM migration. */
+#define QEMUD_MIGRATION_FIRST_PORT 49152
+#define QEMUD_MIGRATION_NUM_PORTS 64
 
 static inline int
 qemudIsActiveVM(const struct qemud_vm *vm)
Index: src/qemu_driver.c
===================================================================
RCS file: /data/cvs/libvirt/src/qemu_driver.c,v
retrieving revision 1.65
diff -u -r1.65 qemu_driver.c
--- src/qemu_driver.c	8 Apr 2008 12:27:53 -0000	1.65
+++ src/qemu_driver.c	8 Apr 2008 18:44:09 -0000
@@ -1520,6 +1520,16 @@
     return 0;
 }
 
+/* Which features are supported by this driver? */
+static int
+qemudSupportsFeature (virConnectPtr conn ATTRIBUTE_UNUSED, int feature)
+{
+    switch (feature) {
+    case VIR_DRV_FEATURE_MIGRATION_V2: return 1;
+    default: return 0;
+    }
+}
+
 static const char *qemudGetType(virConnectPtr conn ATTRIBUTE_UNUSED) {
     return "QEMU";
 }
@@ -2711,6 +2721,234 @@
 #endif
 }
 
+/* Migration support. */
+
+/* Prepare is the first step, and it runs on the destination host.
+ *
+ * This starts an empty VM listening on a TCP port.
+*/
+static int
+qemudDomainMigratePrepare2 (virConnectPtr dconn,
+                            char **cookie ATTRIBUTE_UNUSED,
+                            int *cookielen ATTRIBUTE_UNUSED,
+                            const char *uri_in,
+                            char **uri_out,
+                            unsigned long flags ATTRIBUTE_UNUSED,
+                            const char *dname,
+                            unsigned long resource ATTRIBUTE_UNUSED,
+                            const char *dom_xml)
+{
+    static int port = 0;
+    struct qemud_driver *driver = (struct qemud_driver *)dconn->privateData;
+    struct qemud_vm_def *def;
+    struct qemud_vm *vm;
+    int this_port;
+    char hostname [HOST_NAME_MAX+1];
+    const char *p;
+    const int uri_length_max =
+        6 /* "tcp://" */ + 
+        sizeof (hostname) /* hostname */ +
+        1 + 5 + 1 /* :port\0 */;
+
+    if (!dom_xml) {
+        qemudReportError (dconn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                          "%s", _("no domain XML passed"));
+        return -1;
+    }
+
+    /* The URI passed in may be NULL or a string "tcp://somehostname:port".
+     *
+     * If the URI passed in is NULL then we allocate a port number
+     * from our pool of port numbers and return a URI of
+     * "tcp://ourhostname:port".
+     *
+     * If the URI passed in is not NULL then we try to parse out the
+     * port number and use that (note that the hostname is assumed
+     * to be a correct hostname which refers to the target machine).
+     */
+    if (uri_in == NULL) {
+        this_port = QEMUD_MIGRATION_FIRST_PORT + port++;
+        if (port == QEMUD_MIGRATION_NUM_PORTS) port = 0;
+
+        /* Get hostname */
+        if (gethostname (hostname, HOST_NAME_MAX+1) == -1) {
+            qemudReportError (dconn, NULL, NULL, VIR_ERR_SYSTEM_ERROR,
+                              "%s", strerror (errno));
+            return -1;
+        }
+
+        /* Caller frees */
+        *uri_out = malloc (uri_length_max);
+        if (!*uri_out) {
+            qemudReportError (dconn, NULL, NULL, VIR_ERR_NO_MEMORY,
+                              "%s", strerror (errno));
+            return -1;
+        }
+        snprintf (*uri_out, uri_length_max,
+                  "tcp://%s:%d", hostname, this_port);
+    } else {
+        /* Check the URI starts with "tcp://".  We will escape the
+         * URI when passing it to the qemu monitor, so bad
+         * characters in hostname part don't matter.
+         */
+        if (!STREQLEN (uri_in, "tcp://", 6)) {
+            qemudReportError (dconn, NULL, NULL, VIR_ERR_INVALID_ARG,
+                  "%s", _("only tcp URIs are supported for KVM migrations"));
+            return -1;
+        }
+
+        /* Get the port number. */
+        p = strrchr (uri_in, ':');
+        p++; /* definitely has a ':' in it, see above */
+        this_port = virParseNumber (&p);
+        if (this_port == -1 || p-uri_in != strlen (uri_in)) {
+            qemudReportError (dconn, NULL, NULL, VIR_ERR_INVALID_ARG,
+                              "%s", _("URI did not have ':port' at the end"));
+            return -1;
+        }
+    }
+
+    /* Parse the domain XML. */
+    if (!(def = qemudParseVMDef (dconn, driver, dom_xml, NULL))) {
+        qemudReportError (dconn, NULL, NULL, VIR_ERR_OPERATION_FAILED,
+                          "%s", _("failed to parse XML"));
+        return -1;
+    }
+
+    /* Target domain name, maybe renamed. */
+    dname = dname ? dname : def->name;
+
+    /* Ensure the name and UUID don't already exist in an active VM */
+    vm = qemudFindVMByUUID (driver, def->uuid);
+    if (!vm) vm = qemudFindVMByName (driver, dname);
+    if (vm) {
+        qemudReportError (dconn, NULL, NULL, VIR_ERR_OPERATION_FAILED,
+                          _("domain is already active as '%s'"), vm->def->name);
+        return -1;
+    }
+
+    if (!(vm = qemudAssignVMDef (dconn, driver, def))) {
+        qemudReportError (dconn, NULL, NULL, VIR_ERR_OPERATION_FAILED,
+                          "%s", _("failed to assign new VM"));
+        qemudFreeVMDef (def);
+        return -1;
+    }
+
+    /* Start the QEMU daemon, with the same command-line arguments plus
+     * -incoming tcp://0:port
+     */
+    snprintf (vm->migrateFrom, sizeof (vm->migrateFrom),
+              "tcp://0:%d", this_port);
+    if (qemudStartVMDaemon (dconn, driver, vm) < 0) {
+        qemudReportError (dconn, NULL, NULL, VIR_ERR_OPERATION_FAILED,
+                          "%s", _("failed to start listening VM"));
+        if (!vm->configFile[0])
+            qemudRemoveInactiveVM(driver, vm);
+        return -1;
+    }
+
+    return 0;
+}
+
+/* Perform is the second step, and it runs on the source host. */
+static int
+qemudDomainMigratePerform (virDomainPtr dom,
+                           const char *cookie ATTRIBUTE_UNUSED,
+                           int cookielen ATTRIBUTE_UNUSED,
+                           const char *uri,
+                           unsigned long flags ATTRIBUTE_UNUSED,
+                           const char *dname ATTRIBUTE_UNUSED,
+                           unsigned long resource)
+{
+    struct qemud_driver *driver = (struct qemud_driver *)dom->conn->privateData;
+    struct qemud_vm *vm = qemudFindVMByID (driver, dom->id);
+    char *safe_uri;
+    char cmd[HOST_NAME_MAX+50];
+    char *info;
+
+    if (!vm) {
+        qemudReportError (dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
+                          _("no domain with matching id %d"), dom->id);
+        return -1;
+    }
+
+    if (!qemudIsActiveVM (vm)) {
+        qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
+                          "%s", _("domain is not running"));
+        return -1;
+    }
+
+    if (resource > 0) {
+        /* Issue migrate_set_speed command.  Don't worry if it fails. */
+        snprintf (cmd, sizeof cmd, "migrate_set_speed %lum", resource);
+        qemudMonitorCommand (driver, vm, cmd, &info);
+
+        DEBUG ("migrate_set_speed reply: %s", info);
+        free (info);
+    }
+
+    /* Issue the migrate command. */
+    safe_uri = qemudEscapeMonitorArg (uri);
+    if (!safe_uri) {
+        qemudReportError (dom->conn, dom, NULL, VIR_ERR_SYSTEM_ERROR,
+                          "%s", strerror (errno));
+        return -1;
+    }
+    snprintf (cmd, sizeof cmd, "migrate \"%s\"", safe_uri);
+    free (safe_uri);
+
+    if (qemudMonitorCommand (driver, vm, cmd, &info) < 0) {
+        qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
+                          "%s", _("migrate operation failed"));
+        return -1;
+    }
+
+    DEBUG ("migrate reply: %s", info);
+    free (info);
+
+    /* Clean up the source domain. */
+    qemudShutdownVMDaemon (dom->conn, driver, vm);
+    if (!vm->configFile[0])
+        qemudRemoveInactiveVM (driver, vm);
+
+    return 0;
+}
+
+/* Finish is the third and final step, and it runs on the destination host. */
+static virDomainPtr
+qemudDomainMigrateFinish2 (virConnectPtr dconn,
+                           const char *dname,
+                           const char *cookie ATTRIBUTE_UNUSED,
+                           int cookielen ATTRIBUTE_UNUSED,
+                           const char *uri ATTRIBUTE_UNUSED,
+                           unsigned long flags ATTRIBUTE_UNUSED,
+                           int retcode)
+{
+    struct qemud_driver *driver = (struct qemud_driver *)dconn->privateData;
+    struct qemud_vm *vm = qemudFindVMByName (driver, dname);
+    virDomainPtr dom;
+
+    if (!vm) {
+        qemudReportError (dconn, NULL, NULL, VIR_ERR_INVALID_DOMAIN,
+                          _("no domain with matching name %s"), dname);
+        return NULL;
+    }
+
+    /* Did the migration go as planned?  If yes, return the domain
+     * object, but if no, clean up the empty qemu process.
+     */
+    if (retcode == 0) {
+        dom = virGetDomain (dconn, vm->def->name, vm->def->uuid);
+        if (dom) dom->id = vm->id;
+        return dom;
+    } else {
+        qemudShutdownVMDaemon (dconn, driver, vm);
+        if (!vm->configFile[0])
+            qemudRemoveInactiveVM (driver, vm);
+        return NULL;
+    }
+}
+
 static virNetworkPtr qemudNetworkLookupByUUID(virConnectPtr conn ATTRIBUTE_UNUSED,
                                      const unsigned char *uuid) {
     struct qemud_driver *driver = (struct qemud_driver *)conn->networkPrivateData;
@@ -3015,7 +3253,7 @@
     qemudProbe, /* probe */
     qemudOpen, /* open */
     qemudClose, /* close */
-    NULL, /* supports_feature */
+    qemudSupportsFeature, /* supports_feature */
     qemudGetType, /* type */
     qemudGetVersion, /* version */
     qemudGetHostname, /* hostname */
@@ -3059,13 +3297,15 @@
     NULL, /* domainGetSchedulerType */
     NULL, /* domainGetSchedulerParameters */
     NULL, /* domainSetSchedulerParameters */
-    NULL, /* domainMigratePrepare */
-    NULL, /* domainMigratePerform */
+    NULL, /* domainMigratePrepare (v1) */
+    qemudDomainMigratePerform, /* domainMigratePerform */
     NULL, /* domainMigrateFinish */
     qemudDomainBlockStats, /* domainBlockStats */
     qemudDomainInterfaceStats, /* domainInterfaceStats */
     NULL, /* nodeGetCellsFreeMemory */
     NULL, /* getFreeMemory */
+    qemudDomainMigratePrepare2, /* domainMigratePrepare2 */
+    qemudDomainMigrateFinish2, /* domainMigrateFinish2 */
 };
 
 static virNetworkDriver qemuNetworkDriver = {
Index: src/remote_internal.c
===================================================================
RCS file: /data/cvs/libvirt/src/remote_internal.c,v
retrieving revision 1.67
diff -u -r1.67 remote_internal.c
--- src/remote_internal.c	4 Apr 2008 07:58:29 -0000	1.67
+++ src/remote_internal.c	8 Apr 2008 18:44:12 -0000
@@ -1942,6 +1942,73 @@
 }
 
 static int
+remoteDomainMigratePrepare2 (virConnectPtr dconn,
+                             char **cookie, int *cookielen,
+                             const char *uri_in, char **uri_out,
+                             unsigned long flags, const char *dname,
+                             unsigned long resource,
+                             const char *dom_xml)
+{
+    remote_domain_migrate_prepare2_args args;
+    remote_domain_migrate_prepare2_ret ret;
+    GET_PRIVATE (dconn, -1);
+
+    args.uri_in = uri_in == NULL ? NULL : (char **) &uri_in;
+    args.flags = flags;
+    args.dname = dname == NULL ? NULL : (char **) &dname;
+    args.resource = resource;
+    args.dom_xml = (char *) dom_xml;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (dconn, priv, 0, REMOTE_PROC_DOMAIN_MIGRATE_PREPARE2,
+              (xdrproc_t) xdr_remote_domain_migrate_prepare2_args, (char *) &args,
+              (xdrproc_t) xdr_remote_domain_migrate_prepare2_ret, (char *) &ret) == -1)
+        return -1;
+
+    if (ret.cookie.cookie_len > 0) {
+        *cookie = ret.cookie.cookie_val; /* Caller frees. */
+        *cookielen = ret.cookie.cookie_len;
+    }
+    if (ret.uri_out)
+        *uri_out = *ret.uri_out; /* Caller frees. */
+
+    return 0;
+}
+
+static virDomainPtr
+remoteDomainMigrateFinish2 (virConnectPtr dconn,
+                            const char *dname,
+                            const char *cookie,
+                            int cookielen,
+                            const char *uri,
+                            unsigned long flags,
+                            int retcode)
+{
+    virDomainPtr ddom;
+    remote_domain_migrate_finish2_args args;
+    remote_domain_migrate_finish2_ret ret;
+    GET_PRIVATE (dconn, NULL);
+
+    args.dname = (char *) dname;
+    args.cookie.cookie_len = cookielen;
+    args.cookie.cookie_val = (char *) cookie;
+    args.uri = (char *) uri;
+    args.flags = flags;
+    args.retcode = retcode;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (dconn, priv, 0, REMOTE_PROC_DOMAIN_MIGRATE_FINISH2,
+              (xdrproc_t) xdr_remote_domain_migrate_finish2_args, (char *) &args,
+              (xdrproc_t) xdr_remote_domain_migrate_finish2_ret, (char *) &ret) == -1)
+        return NULL;
+
+    ddom = get_nonnull_domain (dconn, ret.ddom);
+    xdr_free ((xdrproc_t) &xdr_remote_domain_migrate_finish2_ret, (char *) &ret);
+
+    return ddom;
+}
+
+static int
 remoteListDefinedDomains (virConnectPtr conn, char **const names, int maxnames)
 {
     int i;
@@ -4730,6 +4797,8 @@
     .domainBlockStats = remoteDomainBlockStats,
     .domainInterfaceStats = remoteDomainInterfaceStats,
     .nodeGetCellsFreeMemory = NULL,
+    .domainMigratePrepare2 = remoteDomainMigratePrepare2,
+    .domainMigrateFinish2 = remoteDomainMigrateFinish2,
 };
 
 static virNetworkDriver network_driver = {
Index: src/test.c
===================================================================
RCS file: /data/cvs/libvirt/src/test.c,v
retrieving revision 1.71
diff -u -r1.71 test.c
--- src/test.c	8 Apr 2008 09:26:24 -0000	1.71
+++ src/test.c	8 Apr 2008 18:44:14 -0000
@@ -2068,6 +2068,8 @@
     NULL, /* domainInterfaceStats */
     testNodeGetCellsFreeMemory, /* nodeGetCellsFreeMemory */
     NULL, /* getFreeMemory */
+    NULL, /* domainMigratePrepare2 */
+    NULL, /* domainMigrateFinish2 */
 };
 
 static virNetworkDriver testNetworkDriver = {
Index: src/virsh.c
===================================================================
RCS file: /data/cvs/libvirt/src/virsh.c,v
retrieving revision 1.142
diff -u -r1.142 virsh.c
--- src/virsh.c	4 Apr 2008 11:20:45 -0000	1.142
+++ src/virsh.c	8 Apr 2008 18:44:18 -0000
@@ -2208,6 +2208,7 @@
     {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("domain name, id or uuid")},
     {"desturi", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("connection URI of the destination host")},
     {"migrateuri", VSH_OT_DATA, 0, gettext_noop("migration URI, usually can be omitted")},
+    {"dname", VSH_OT_DATA, 0, gettext_noop("rename to new name during migration (if supported)")},
     {NULL, 0, 0, NULL}
 };
 
@@ -2217,6 +2218,7 @@
     virDomainPtr dom = NULL;
     const char *desturi;
     const char *migrateuri;
+    const char *dname;
     int flags = 0, found, ret = FALSE;
     virConnectPtr dconn = NULL;
     virDomainPtr ddom = NULL;
@@ -2236,6 +2238,9 @@
     migrateuri = vshCommandOptString (cmd, "migrateuri", &found);
     if (!found) migrateuri = NULL;
 
+    dname = vshCommandOptString (cmd, "dname", &found);
+    if (!found) migrateuri = dname;
+
     if (vshCommandOptBool (cmd, "live"))
         flags |= VIR_MIGRATE_LIVE;
 
@@ -2244,7 +2249,7 @@
     if (!dconn) goto done;
 
     /* Migrate. */
-    ddom = virDomainMigrate (dom, dconn, flags, NULL, migrateuri, 0);
+    ddom = virDomainMigrate (dom, dconn, flags, dname, migrateuri, 0);
     if (!ddom) goto done;
 
     ret = TRUE;


More information about the libvir-list mailing list