add offline migration to migrate a domain which is only defined but not active, this implementation has nothing to do with type of VM(qemu, xen etc.), it transfers data by stream. >From 634687c6ee81c784b633e4a86f3613e109ecda7c Mon Sep 17 00:00:00 2001 From: liguang <lig.fnst@xxxxxxxxxxxxxx> Date: Thu, 9 Aug 2012 16:04:29 +0800 Subject: [PATCH] implement offline migration Signed-off-by: liguang <lig.fnst@xxxxxxxxxxxxxx> --- daemon/remote.c | 46 +++++++++++++++++++++++++++ docs/hvsupport.pl | 2 + include/libvirt/libvirt.h.in | 6 +++ python/generator.py | 1 + src/driver.h | 5 +++ src/libvirt.c | 22 +++++++++++++ src/libvirt_public.syms | 1 + src/remote/remote_driver.c | 70 ++++++++++++++++++++++++++++++++++++++++++ src/remote/remote_protocol.x | 10 +++++- tools/virsh-domain.c | 69 +++++++++++++++++++++++++++++++++++++++++ 10 files changed, 231 insertions(+), 1 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index d25717c..5c175b2 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -21,6 +21,9 @@ */ #include <config.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> #include "virterror_internal.h" @@ -48,6 +51,7 @@ #include "virdbus.h" #include "remote_protocol.h" #include "qemu_protocol.h" +#include "fdstream.h" #define VIR_FROM_THIS VIR_FROM_RPC @@ -1617,6 +1621,48 @@ no_memory: goto cleanup; } +static int remoteDispatchDomainMigrateOffline( + virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + remote_domain_migrate_offline_args *args, + remote_domain_migrate_offline_ret *ret ATTRIBUTE_UNUSED) +{ + int rv = -1; + virStreamPtr st = NULL; + daemonClientStreamPtr stream = NULL; + daemonClientPrivatePtr priv = + virNetServerClientGetPrivateData(client); + + if (!priv->conn) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); + goto cleanup; + } + + st = virStreamNew(priv->conn, VIR_STREAM_NONBLOCK); + + if (!(stream = daemonCreateClientStream(client, st, remoteProgram, &msg->header))) + goto cleanup; + + if (virFDStreamCreateFile(st, + args->name, + 0, 0, + O_WRONLY, 0) < 0) + goto cleanup; + + + if (daemonAddClientStream(client, stream, false) < 0) + goto cleanup; + + rv = 0; + +cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + return rv; +} + static int remoteDispatchDomainMigratePrepare(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, diff --git a/docs/hvsupport.pl b/docs/hvsupport.pl index 4871739..47fc505 100755 --- a/docs/hvsupport.pl +++ b/docs/hvsupport.pl @@ -128,6 +128,8 @@ $apis{virDomainMigratePrepareTunnel3} = "0.9.2"; $apis{virDomainMigratePerform3} = "0.9.2"; $apis{virDomainMigrateFinish3} = "0.9.2"; $apis{virDomainMigrateConfirm3} = "0.9.2"; +$apis{virDomainMigrateOffline} = "0.10.1"; + diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index d21d029..e4a6e14 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -977,6 +977,7 @@ typedef enum { * whole migration process; this will be used automatically * when supported */ VIR_MIGRATE_UNSAFE = (1 << 9), /* force migration even if it is considered unsafe */ + VIR_MIGRATE_OFFLINE = (1 << 10), /* offline migration */ } virDomainMigrateFlags; /* Domain migration. */ @@ -1012,6 +1013,11 @@ int virDomainMigrateGetMaxSpeed(virDomainPtr domain, unsigned long *bandwidth, unsigned int flags); +int +virDomainMigrateOffline(virConnectPtr dconn, + char *file); + + /** * VIR_NODEINFO_MAXCPUS: * @nodeinfo: virNodeInfo instance diff --git a/python/generator.py b/python/generator.py index 6559ece..42e266b 100755 --- a/python/generator.py +++ b/python/generator.py @@ -427,6 +427,7 @@ skip_impl = ( 'virDomainGetDiskErrors', 'virConnectUnregisterCloseCallback', 'virConnectRegisterCloseCallback', + 'virDomainMigrateOffline', ) qemu_skip_impl = ( diff --git a/src/driver.h b/src/driver.h index aab9766..d17fa22 100644 --- a/src/driver.h +++ b/src/driver.h @@ -865,6 +865,10 @@ typedef char * int type, const char *uri, unsigned int flags); +typedef int + (*virDrvDomainMigrateOffline)(virConnectPtr dconn, + const char *file); + /** * _virDriver: @@ -1048,6 +1052,7 @@ struct _virDriver { virDrvDomainGetDiskErrors domainGetDiskErrors; virDrvDomainSetMetadata domainSetMetadata; virDrvDomainGetMetadata domainGetMetadata; + virDrvDomainMigrateOffline domainMigrateOffline; }; typedef int diff --git a/src/libvirt.c b/src/libvirt.c index 3c4bf8c..497f48d 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -5003,6 +5003,28 @@ virDomainMigratePeer2Peer (virDomainPtr domain, } } +/** + * virDomainMigrateOffline: + * @dconn: target connection handler + * @file: the file to push to target + * + * to handle offline migration + * Returns -1 if error, else 0 + */ +int +virDomainMigrateOffline(virConnectPtr dconn, + char *file) +{ + VIR_DEBUG("dconn=%p, file=%s", dconn, NULLSTR(file)); + + if (!VIR_IS_CONNECT (dconn)) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + return dconn->driver->domainMigrateOffline(dconn, file); +} /* * In normal migration, the libvirt client co-ordinates communication diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index e3ba119..6615a45 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -549,6 +549,7 @@ LIBVIRT_0.10.0 { virDomainGetHostname; virConnectRegisterCloseCallback; virConnectUnregisterCloseCallback; + virDomainMigrateOffline; } LIBVIRT_0.9.13; # .... define new API here using predicted next version number .... diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index afd367b..a35599d 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -22,8 +22,12 @@ */ #include <config.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> #include <unistd.h> +#include <stdio.h> #include <assert.h> #include "virnetclient.h" @@ -5061,6 +5065,71 @@ done: return rv; } +static int +doRemoteReadFile(virStreamPtr st ATTRIBUTE_UNUSED, + char *buf, size_t nbytes, void *opaque) +{ + int *fd = opaque; + + return read(*fd, buf, nbytes); +} + +static int +remoteDomainMigrateOffline(virConnectPtr dconn, + const char *name) +{ + int rv = -1, fd = -1; + virStreamPtr st = virStreamNew(dconn, 0); + remote_domain_migrate_offline_args args; + remote_domain_migrate_offline_ret ret; + struct private_data *priv = dconn->privateData; + virNetClientStreamPtr netst = NULL; + + remoteDriverLock(priv); + + args.name = (char *)name; + memset(&ret, 0, sizeof(ret)); + + if (!(netst = virNetClientStreamNew(priv->remoteProgram, REMOTE_PROC_DOMAIN_MIGRATE_OFFLINE, priv->counter))) + goto done; + if (virNetClientAddStream(priv->client, netst) < 0) { + virNetClientStreamFree(netst); + goto done; + } + st->driver = &remoteStreamDrv; + st->privateData = netst; + + if ((fd = open(name, O_RDONLY)) < 0) + goto done; + if (fd == -1) + goto done; + + if (call (dconn, priv, 0, REMOTE_PROC_DOMAIN_MIGRATE_OFFLINE, + (xdrproc_t) xdr_remote_domain_migrate_offline_args, (char *) &args, + (xdrproc_t) xdr_remote_domain_migrate_offline_ret, (char *) &ret) == -1) { + virNetClientRemoveStream(priv->client, netst); + virNetClientStreamFree(netst); + st->driver = NULL; + st->privateData = NULL; + goto done; + } + + remoteDriverUnlock(priv); + + if (virStreamSendAll(st, doRemoteReadFile, &fd) < 0) + goto done; + if (virStreamFinish(st) < 0) + goto done; + if (VIR_CLOSE(fd) < 0) + goto done; + + rv = 0; + +done: + return rv; +} + + static void remoteDomainEventQueue(struct private_data *priv, virDomainEventPtr event) { @@ -5302,6 +5371,7 @@ static virDriver remote_driver = { .domainEventDeregister = remoteDomainEventDeregister, /* 0.5.0 */ .domainMigratePrepare2 = remoteDomainMigratePrepare2, /* 0.5.0 */ .domainMigrateFinish2 = remoteDomainMigrateFinish2, /* 0.5.0 */ + .domainMigrateOffline = remoteDomainMigrateOffline, /* 0.10.1 */ .nodeDeviceDettach = remoteNodeDeviceDettach, /* 0.6.1 */ .nodeDeviceReAttach = remoteNodeDeviceReAttach, /* 0.6.1 */ .nodeDeviceReset = remoteNodeDeviceReset, /* 0.6.1 */ diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 200fe75..3d4ff8f 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -2527,6 +2527,13 @@ struct remote_connect_list_all_domains_ret { unsigned int ret; }; +struct remote_domain_migrate_offline_args { + remote_nonnull_string name; +}; + +struct remote_domain_migrate_offline_ret { + int retval; +}; /*----- Protocol. -----*/ @@ -2854,7 +2861,8 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_LIST_ALL_SNAPSHOTS = 274, /* skipgen skipgen priority:high */ REMOTE_PROC_DOMAIN_SNAPSHOT_LIST_ALL_CHILDREN = 275, /* skipgen skipgen priority:high */ REMOTE_PROC_DOMAIN_EVENT_BALLOON_CHANGE = 276, /* autogen autogen */ - REMOTE_PROC_DOMAIN_GET_HOSTNAME = 277 /* autogen autogen */ + REMOTE_PROC_DOMAIN_GET_HOSTNAME = 277, /* autogen autogen */ + REMOTE_PROC_DOMAIN_MIGRATE_OFFLINE = 278 /* skipgen skipgen priority:low*/ /* * Notice how the entries are grouped in sets of 10 ? diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 33b1727..8f9b662 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -6304,9 +6304,66 @@ static const vshCmdOptDef opts_migrate[] = { {"dname", VSH_OT_DATA, 0, N_("rename to new name during migration (if supported)")}, {"timeout", VSH_OT_INT, 0, N_("force guest to suspend if live migration exceeds timeout (in seconds)")}, {"xml", VSH_OT_STRING, 0, N_("filename containing updated XML for the target")}, + {"offline", VSH_OT_BOOL, 0, N_("migration when there's no domain active")}, {NULL, 0, 0, NULL} }; +static int +push_file(char dst[] ATTRIBUTE_UNUSED, char *file, virConnectPtr dconn) +{ + int ret = -1; + + ret = virDomainMigrateOffline(dconn, file); + + return ret; +} + +static void +vshMigrateOffline(vshControl *ctl, char *file, char dst[]) +{ + xmlDocPtr xml = NULL; + xmlXPathContextPtr ctxt = NULL; + xmlNodePtr *disks = NULL; + virConnectPtr dconn = NULL; + int i = 0, ret = 0; + char *src[] = {NULL}; + + if (!vshConnectionUsability(ctl, ctl->conn)) + return; + + xml = virXMLParseFileCtxt(file, &ctxt); + if (!xml) { + vshError(NULL, "%s", _("Fail to get domain information from")); + goto cleanup; + } + + ret = virXPathNodeSet("./devices/disk", ctxt, &disks); + if (ret < 0) { + vshError(NULL, "%s", _("Fail to get disk node")); + goto cleanup; + } + + dconn = virConnectOpen(dst); + if (!dconn) + goto cleanup; + vshPrint(ctl, "pushing %s to %s\n", file, dst); + if (push_file(dst, file, dconn) < 0) + goto cleanup; + for (i = 0 ; i < ret ; i++) { + ctxt->node = disks[i]; + src[i] = virXPathString("string(./source/@file" + "|./source/@dir" + "|./source/@name)", ctxt); + vshPrint(ctl, "pushing %s to %s\n", src[i], dst); + if (push_file(dst, src[i], dconn) < 0) + break; + } + +cleanup: + xmlXPathFreeContext(ctxt); + xmlFreeDoc(xml); +} + static void doMigrate(void *opaque) { @@ -6373,12 +6430,24 @@ doMigrate(void *opaque) if (vshCommandOptBool(cmd, "unsafe")) flags |= VIR_MIGRATE_UNSAFE; + if (vshCommandOptBool(cmd, "offline")) { + flags |= VIR_MIGRATE_OFFLINE; + if (xmlfile == NULL) + vshError(ctl, _("please specify xmlfile for offline migration")); + } + if (xmlfile && virFileReadAll(xmlfile, 8192, &xml) < 0) { vshError(ctl, _("file '%s' doesn't exist"), xmlfile); goto out; } + if (flags & VIR_MIGRATE_OFFLINE) { + vshMigrateOffline(ctl, (char *)xmlfile, (char *)desturi); + goto out; + } + + if ((flags & VIR_MIGRATE_PEER2PEER) || vshCommandOptBool(cmd, "direct")) { /* For peer2peer migration or direct migration we only expect one URI -- 1.7.2.5 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list