[libvirt] [PATCH V2] offline migration

liguang lig.fnst at cn.fujitsu.com
Tue Aug 28 00:45:45 UTC 2012


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 at cn.fujitsu.com>
Date: Thu, 9 Aug 2012 16:04:29 +0800
Subject: [PATCH] implement offline migration

Signed-off-by: liguang <lig.fnst at 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
 




More information about the libvir-list mailing list