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(a)cn.fujitsu.com>
Date: Thu, 9 Aug 2012 16:04:29 +0800
Subject: [PATCH] implement offline migration
Signed-off-by: liguang <lig.fnst(a)cn.fujitsu.com>
---
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