[libvirt] Re: [Libvir] KVM migration
Richard W.M. Jones
rjones at redhat.com
Mon May 19 10:50:25 UTC 2008
On Fri, May 16, 2008 at 12:12:00AM +0200, Soren Hansen wrote:
> On Mon, Apr 07, 2008 at 04:40:41PM +0100, Richard W.M. Jones wrote:
> > It turns out that the general migration strategy I defined[1] in
> > reference to Xen, ie. the 3 steps of Prepare/Perform/Finish, isn't
> > sufficient to support KVM migration.
> [...]
>
> What ever happened to this? Is it being worked on somewhere by someone?
I've been concentrating on our toolset for the Red Hat Summit next
month is what :-) Here's an updated patch.
Rich.
--
Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones
virt-p2v converts physical machines to virtual machines. Boot with a
live CD or over the network (PXE) and turn machines into Xen guests.
http://et.redhat.com/~rjones/virt-p2v
-------------- next part --------------
Index: qemud/remote.c
===================================================================
RCS file: /data/cvs/libvirt/qemud/remote.c,v
retrieving revision 1.31
diff -u -r1.31 remote.c
--- qemud/remote.c 15 May 2008 06:12:32 -0000 1.31
+++ qemud/remote.c 19 May 2008 10:51:20 -0000
@@ -1284,6 +1284,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_protocol.x
===================================================================
RCS file: /data/cvs/libvirt/qemud/remote_protocol.x,v
retrieving revision 1.11
diff -u -r1.11 remote_protocol.x
--- qemud/remote_protocol.x 10 Apr 2008 16:53:29 -0000 1.11
+++ qemud/remote_protocol.x 19 May 2008 10:51:21 -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.47
diff -u -r1.47 driver.h
--- src/driver.h 13 May 2008 06:30:58 -0000 1.47
+++ src/driver.h 19 May 2008 10:51:21 -0000
@@ -59,6 +59,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.
*
@@ -269,6 +274,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:
*
@@ -339,6 +366,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.73
diff -u -r1.73 internal.h
--- src/internal.h 13 May 2008 06:30:58 -0000 1.73
+++ src/internal.h 19 May 2008 10:51:22 -0000
@@ -357,6 +357,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.139
diff -u -r1.139 libvirt.c
--- src/libvirt.c 14 May 2008 19:51:24 -0000 1.139
+++ src/libvirt.c 19 May 2008 10:51:25 -0000
@@ -2136,7 +2136,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);
@@ -2151,10 +2152,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;
}
@@ -2170,17 +2178,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.
@@ -2188,18 +2228,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);
@@ -2294,6 +2344,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.40
diff -u -r1.40 libvirt_sym.version
--- src/libvirt_sym.version 13 May 2008 06:30:58 -0000 1.40
+++ src/libvirt_sym.version 19 May 2008 10:51:25 -0000
@@ -177,6 +177,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.31
diff -u -r1.31 qemu_conf.h
--- src/qemu_conf.h 15 May 2008 16:21:11 -0000 1.31
+++ src/qemu_conf.h 19 May 2008 10:51:25 -0000
@@ -415,6 +415,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.76
diff -u -r1.76 qemu_driver.c
--- src/qemu_driver.c 16 May 2008 16:51:30 -0000 1.76
+++ src/qemu_driver.c 19 May 2008 10:51:27 -0000
@@ -1575,6 +1575,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";
}
@@ -2829,6 +2839,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;
@@ -3133,7 +3371,7 @@
qemudProbe, /* probe */
qemudOpen, /* open */
qemudClose, /* close */
- NULL, /* supports_feature */
+ qemudSupportsFeature, /* supports_feature */
qemudGetType, /* type */
qemudGetVersion, /* version */
qemudGetHostname, /* hostname */
@@ -3177,13 +3415,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.74
diff -u -r1.74 remote_internal.c
--- src/remote_internal.c 15 May 2008 14:21:34 -0000 1.74
+++ src/remote_internal.c 19 May 2008 10:51:31 -0000
@@ -1941,6 +1941,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;
@@ -4729,6 +4796,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.74
diff -u -r1.74 test.c
--- src/test.c 14 May 2008 19:51:24 -0000 1.74
+++ src/test.c 19 May 2008 10:51:32 -0000
@@ -2060,6 +2060,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.151
diff -u -r1.151 virsh.c
--- src/virsh.c 16 May 2008 09:37:44 -0000 1.151
+++ src/virsh.c 19 May 2008 10:51:36 -0000
@@ -2205,6 +2205,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}
};
@@ -2214,6 +2215,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;
@@ -2233,6 +2235,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;
@@ -2241,7 +2246,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