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