[libvirt] [PATCH v1 10/11] qemu_migration: Check size prerequisites

Michal Privoznik mprivozn at redhat.com
Tue Nov 27 18:50:04 UTC 2012


With new NBD storage migration approach there are several
requirements that need to be meet for successful use of
the feature. One of them is - the file representing a disk,
needs to have at least same size as on the source. Hence,
we must transfer a list of pairs [disk source, size] and
check on destination that this requirement is met and/or
take actions to meet it.
---
 src/qemu/qemu_migration.c |  162 ++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 159 insertions(+), 3 deletions(-)

diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
index 2c66d8c..f8e52fb 100644
--- a/src/qemu/qemu_migration.c
+++ b/src/qemu/qemu_migration.c
@@ -27,6 +27,9 @@
 #include <gnutls/x509.h>
 #include <fcntl.h>
 #include <poll.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
 
 #include "qemu_migration.h"
 #include "qemu_monitor.h"
@@ -131,6 +134,15 @@ struct _qemuMigrationCookieNBD {
                  Negative one is meant to be sent when translating from
                  perform to finish phase to let destination know it's
                  safe to stop NBD server.*/
+
+    /* The list of pairs [disk-alias,disk-size] (in Bytes).
+     * This is needed because the same disk size is one of
+     * prerequisites for NBD storage migration. */
+    size_t ndisks;
+    struct {
+        char *src; /* disk alias */
+        size_t bytes;
+    } *disk;
 };
 
 typedef struct _qemuMigrationCookie qemuMigrationCookie;
@@ -193,6 +205,21 @@ qemuMigrationCookieNetworkFree(qemuMigrationCookieNetworkPtr network)
 }
 
 
+static void
+qemuMigrationCookieNBDFree(qemuMigrationCookieNBDPtr nbd)
+{
+    size_t i;
+
+    if (!nbd)
+        return;
+
+    for (i = 0; i < nbd->ndisks; i++)
+        VIR_FREE(nbd->disk[i].src);
+    VIR_FREE(nbd->disk);
+    VIR_FREE(nbd);
+}
+
+
 static void qemuMigrationCookieFree(qemuMigrationCookiePtr mig)
 {
     if (!mig)
@@ -204,12 +231,13 @@ static void qemuMigrationCookieFree(qemuMigrationCookiePtr mig)
     if (mig->flags & QEMU_MIGRATION_COOKIE_NETWORK)
         qemuMigrationCookieNetworkFree(mig->network);
 
+    qemuMigrationCookieNBDFree(mig->nbd);
+
     VIR_FREE(mig->localHostname);
     VIR_FREE(mig->remoteHostname);
     VIR_FREE(mig->name);
     VIR_FREE(mig->lockState);
     VIR_FREE(mig->lockDriver);
-    VIR_FREE(mig->nbd);
     VIR_FREE(mig);
 }
 
@@ -512,8 +540,12 @@ qemuMigrationCookieAddNetwork(qemuMigrationCookiePtr mig,
 
 static int
 qemuMigrationCookieAddNBD(qemuMigrationCookiePtr mig,
+                          virDomainObjPtr vm,
                           int nbdPort)
 {
+    qemuDomainObjPrivatePtr priv = vm->privateData;
+    size_t i;
+
     /* It is not a bug if there already is a NBD data */
     if (!mig->nbd &&
         VIR_ALLOC(mig->nbd) < 0) {
@@ -521,6 +553,38 @@ qemuMigrationCookieAddNBD(qemuMigrationCookiePtr mig,
         return -1;
     }
 
+    /* in Begin phase add info about disks */
+    if (priv->job.phase == QEMU_MIGRATION_PHASE_BEGIN3 &&
+        vm->def->ndisks) {
+        if (VIR_ALLOC_N(mig->nbd->disk, vm->def->ndisks) < 0) {
+            virReportOOMError();
+            return -1;
+        }
+
+        for (i = 0; i < vm->def->ndisks; i++) {
+            virDomainDiskDefPtr disk = vm->def->disks[i];
+            struct stat sb;
+
+            if (!disk->src)
+                continue;
+
+            if (virAsprintf(&mig->nbd->disk[mig->nbd->ndisks].src,
+                            "%s", disk->src) < 0) {
+                virReportOOMError();
+                return -1;
+            }
+
+            if (stat(disk->src, &sb) < 0) {
+                virReportSystemError(errno,
+                                     _("Unable to stat '%s'"),
+                                     disk->src);
+                return -1;
+            }
+
+            mig->nbd->disk[mig->nbd->ndisks++].bytes = sb.st_size;
+        }
+    }
+
     mig->nbd->port = nbdPort;
     mig->flags |= QEMU_MIGRATION_COOKIE_NBD;
 
@@ -634,7 +698,16 @@ qemuMigrationCookieXMLFormat(struct qemud_driver *driver,
         virBufferAddLit(buf, "  <nbd");
         if (mig->nbd->port)
             virBufferAsprintf(buf, " port='%d'", mig->nbd->port);
-        virBufferAddLit(buf, "/>\n");
+        if (mig->nbd->ndisks) {
+            virBufferAddLit(buf, ">\n");
+            for (i = 0; i < mig->nbd->ndisks; i++)
+                virBufferAsprintf(buf, "    <disk src='%s' size='%zu'/>\n",
+                                  mig->nbd->disk[i].src,
+                                  mig->nbd->disk[i].bytes);
+            virBufferAddLit(buf, "  </nbd>\n");
+        } else {
+            virBufferAddLit(buf, "/>\n");
+        }
     }
 
     virBufferAddLit(buf, "</qemu-migration>\n");
@@ -939,6 +1012,34 @@ qemuMigrationCookieXMLParse(qemuMigrationCookiePtr mig,
                            port);
             goto error;
         }
+
+        if ((n = virXPathNodeSet("./nbd/disk", ctxt, &nodes)) > 0) {
+            xmlNodePtr oldNode = ctxt->node;
+            if (VIR_ALLOC_N(mig->nbd->disk, n) < 0) {
+                virReportOOMError();
+                goto error;
+            }
+            mig->nbd->ndisks = n;
+
+            for (i = 0; i < n; i++) {
+                ctxt->node = nodes[i];
+
+                mig->nbd->disk[i].src = virXPathString("string(./@src)", ctxt);
+
+                tmp = virXPathString("string(./@size)", ctxt);
+                if (virStrToLong_ull(tmp, NULL, 10, (unsigned long long *)
+                                     &mig->nbd->disk[i].bytes) < 0) {
+                    virReportError(VIR_ERR_INTERNAL_ERROR,
+                                   _("Malformed size attribute '%s'"),
+                                   tmp);
+                    VIR_FREE(tmp);
+                    goto error;
+                }
+                VIR_FREE(tmp);
+            }
+            VIR_FREE(nodes);
+            ctxt->node = oldNode;
+        }
     }
 
     return 0;
@@ -1007,7 +1108,7 @@ qemuMigrationBakeCookie(qemuMigrationCookiePtr mig,
     }
 
     if (flags & QEMU_MIGRATION_COOKIE_NBD &&
-        qemuMigrationCookieAddNBD(mig, nbdPort) < 0)
+        qemuMigrationCookieAddNBD(mig, dom, nbdPort) < 0)
         return -1;
 
     if (!(*cookieout = qemuMigrationCookieXMLFormatStr(driver, mig)))
@@ -1334,6 +1435,57 @@ error:
     goto cleanup;
 }
 
+static int
+qemuMigrationPreCreateStorage(qemuMigrationCookiePtr mig)
+{
+    int ret = -1;
+    size_t i;
+    struct stat sb;
+    int fd = -1;
+
+    if (!mig->nbd || !mig->nbd->ndisks) {
+        /* nothing to do here */
+        return 0;
+    }
+
+    for (i = 0; i < mig->nbd->ndisks; i++) {
+        const char *src = mig->nbd->disk[i].src;
+        size_t bytes = mig->nbd->disk[i].bytes;
+        VIR_DEBUG("Checking '%s' for its size (requested %zuB)", src, bytes);
+
+        if ((fd = virFileOpenAs(src, O_RDWR | O_CREAT, 0660,
+                                -1, -1, VIR_FILE_OPEN_NOFORK)) < 0) {
+            virReportSystemError(errno, _("Unable to create '%s'"), src);
+            goto cleanup;
+        }
+
+        if (fstat(fd, &sb) < 0) {
+            virReportSystemError(errno, _("Unable to stat '%s'"), src);
+            goto cleanup;
+        }
+
+        VIR_DEBUG("File '%s' is %zuB big", src, sb.st_size);
+        if (sb.st_size < bytes &&
+            ftruncate(fd, bytes) < 0) {
+            virReportSystemError(errno, _("Unable to ftruncate '%s'"), src);
+            goto cleanup;
+        }
+
+        VIR_FORCE_CLOSE(fd);
+    }
+
+    ret = 0;
+cleanup:
+    VIR_FORCE_CLOSE(fd);
+    /* free from migration data to prevent
+     * infinite sending from src to dst and back */
+    for (i = 0; i < mig->nbd->ndisks; i++)
+        VIR_FREE(mig->nbd->disk[i].src);
+    VIR_FREE(mig->nbd->disk);
+    mig->nbd->ndisks = 0;
+    return ret;
+}
+
 /* Validate whether the domain is safe to migrate.  If vm is NULL,
  * then this is being run in the v2 Prepare stage on the destination
  * (where we only have the target xml); if vm is provided, then this
@@ -1932,6 +2084,10 @@ qemuMigrationPrepareAny(struct qemud_driver *driver,
                                        QEMU_MIGRATION_COOKIE_NBD)))
         goto cleanup;
 
+    /* pre-create all storage */
+    if (qemuMigrationPreCreateStorage(mig) < 0)
+        goto cleanup;
+
     if (qemuMigrationJobStart(driver, vm, QEMU_ASYNC_JOB_MIGRATION_IN) < 0)
         goto cleanup;
     qemuMigrationJobSetPhase(driver, vm, QEMU_MIGRATION_PHASE_PREPARE);
-- 
1.7.8.6




More information about the libvir-list mailing list