For now, disk migration via mirroring is not implemented. But when
we do implement it, we have to deal with the fact that qemu does
not provide an easy way to re-start a qemu process with mirroring
still intact (it _might_ be possible by using qemu -S then an
initial 'drive-mirror' with disk reuse before starting the domain,
but that gets hairy). Even something like 'virDomainSave' becomes
hairy, if you realize the implications that 'virDomainRestore' would
be stuck with recreating the same mirror layout.
But if we step back and look at the bigger picture, we realize that
the initial client of live storage migration via disk mirroring is
oVirt, which always uses transient domains, and that if a transient
domain is destroyed while a mirror exists, oVirt can easily restart
the storage migration by creating a new domain that visits just the
source storage, with no loss in data.
We can make life a lot easier by being cowards, and forbidding
certain operations on a domain. This patch guarantees that there
will be at most one snapshot with disk mirroring, that it will
always be the current snapshot, and that the user cannot redefine
that snapshot nor hot-plug or hot-unplug any domain disks -
for now, the only way to delete such a snapshot is by destroying
the transient domain it is attached to. A future patch will add
the ability to also delete such snapshots under a condition of a
flag which says how to end the mirroring with the 'drive-reopen'
monitor command; and once that is in place, then we can finally
implement creation of the mirroring via 'drive-mirror'. With
just this patch applied, the only way you can ever get
virDomainHasDiskMirror to return true is by manually playing with
the libvirt internal directory and then restarting libvirtd.
* src/conf/domain_conf.h (virDomainSnapshotHasDiskMirror)
(virDomainHasDiskMirror): New prototypes.
* src/conf/domain_conf.c (virDomainSnapshotHasDiskMirror)
(virDomainHasDiskMirror): Implement them.
* src/libvirt_private.syms (domain_conf.h): Export them.
* src/qemu/qemu_driver.c (qemuDomainSaveInternal)
(qemuDomainSnapshotCreateXML, qemuDomainRevertToSnapshot)
(qemuDomainSnapshotDelete, qemuDomainAttachDeviceDiskLive)
(qemuDomainDetachDeviceDiskLive): Use it.
(qemuDomainSnapshotLoad): Allow libvirtd restarts with active disk
mirroring, but sanity check for only a current image with mirrors.
---
src/conf/domain_conf.c | 20 +++++++++++++++++
src/conf/domain_conf.h | 2 +
src/libvirt_private.syms | 2 +
src/qemu/qemu_driver.c | 53 +++++++++++++++++++++++++++++++++++++++++++++-
4 files changed, 76 insertions(+), 1 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index f84304c..85abf32 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -13652,6 +13652,26 @@ cleanup:
return ret;
}
+/* Determine if the given snapshot contains a disk mirror. */
+bool
+virDomainSnapshotHasDiskMirror(virDomainSnapshotDefPtr snapshot)
+{
+ int i;
+ for (i = 0; i < snapshot->ndisks; i++)
+ if (snapshot->disks[i].mirror)
+ return true;
+ return false;
+}
+
+/* Determine if the given domain has a current snapshot that contains
+ * a disk mirror. */
+bool virDomainHasDiskMirror(virDomainObjPtr vm)
+{
+ if (!vm->current_snapshot)
+ return false;
+ return virDomainSnapshotHasDiskMirror(vm->current_snapshot->def);
+}
+
char *virDomainSnapshotDefFormat(const char *domain_uuid,
virDomainSnapshotDefPtr def,
unsigned int flags,
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 01a504b..1ea7332 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -1706,6 +1706,7 @@ char *virDomainSnapshotDefFormat(const char *domain_uuid,
int virDomainSnapshotAlignDisks(virDomainSnapshotDefPtr snapshot,
int default_snapshot,
bool require_match);
+bool virDomainSnapshotHasDiskMirror(virDomainSnapshotDefPtr snapshot);
virDomainSnapshotObjPtr virDomainSnapshotAssignDef(virDomainSnapshotObjListPtr
snapshots,
const virDomainSnapshotDefPtr def);
@@ -1931,6 +1932,7 @@ virDomainDiskDefPtr
virDomainDiskRemove(virDomainDefPtr def, size_t i);
virDomainDiskDefPtr
virDomainDiskRemoveByName(virDomainDefPtr def, const char *name);
+bool virDomainHasDiskMirror(virDomainObjPtr vm);
int virDomainNetIndexByMac(virDomainDefPtr def, const unsigned char *mac);
int virDomainNetInsert(virDomainDefPtr def, virDomainNetDefPtr net);
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 9a718b4..6739733 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -358,6 +358,7 @@ virDomainGraphicsSpiceZlibCompressionTypeFromString;
virDomainGraphicsSpiceZlibCompressionTypeToString;
virDomainGraphicsTypeFromString;
virDomainGraphicsTypeToString;
+virDomainHasDiskMirror;
virDomainHostdevDefAlloc;
virDomainHostdevDefClear;
virDomainHostdevDefFree;
@@ -450,6 +451,7 @@ virDomainSnapshotDropParent;
virDomainSnapshotFindByName;
virDomainSnapshotForEachChild;
virDomainSnapshotForEachDescendant;
+virDomainSnapshotHasDiskMirror;
virDomainSnapshotObjListGetNames;
virDomainSnapshotObjListGetNamesFrom;
virDomainSnapshotObjListNum;
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 6dd1b32..89aa56c 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -320,7 +320,8 @@ static void qemuDomainSnapshotLoad(void *payload,
char ebuf[1024];
unsigned int flags = (VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE |
VIR_DOMAIN_SNAPSHOT_PARSE_DISKS |
- VIR_DOMAIN_SNAPSHOT_PARSE_INTERNAL);
+ VIR_DOMAIN_SNAPSHOT_PARSE_INTERNAL |
+ VIR_DOMAIN_SNAPSHOT_PARSE_MIRROR);
virDomainObjLock(vm);
if (virAsprintf(&snapDir, "%s/%s", baseDir, vm->def->name) <
0) {
@@ -373,6 +374,16 @@ static void qemuDomainSnapshotLoad(void *payload,
VIR_FREE(xmlStr);
continue;
}
+ if (!def->current && virDomainSnapshotHasDiskMirror(def)) {
+ /* Someone must have hand-modified the directory; ignore them. */
+ VIR_ERROR(_("Disk mirroring unexpected since snapshot file '%s'
"
+ "does not claim to be the current snapshot"),
+ fullpath);
+ virDomainSnapshotDefFree(def);
+ VIR_FREE(fullpath);
+ VIR_FREE(xmlStr);
+ continue;
+ }
snap = virDomainSnapshotAssignDef(&vm->snapshots, def);
if (snap == NULL) {
@@ -2536,6 +2547,11 @@ qemuDomainSaveInternal(struct qemud_driver *driver, virDomainPtr
dom,
"%s", _("domain is marked for auto
destroy"));
goto cleanup;
}
+ if (virDomainHasDiskMirror(vm)) {
+ qemuReportError(VIR_ERR_OPERATION_INVALID, "%s",
+ _("domain has active disk mirrors"));
+ goto cleanup;
+ }
memset(&header, 0, sizeof(header));
memcpy(header.magic, QEMUD_SAVE_PARTIAL, sizeof(header.magic));
@@ -5067,6 +5083,12 @@ qemuDomainAttachDeviceDiskLive(virConnectPtr conn,
virCgroupPtr cgroup = NULL;
int ret = -1;
+ if (virDomainHasDiskMirror(vm)) {
+ qemuReportError(VIR_ERR_OPERATION_INVALID, "%s",
+ _("domain has active disk mirrors"));
+ goto end;
+ }
+
if (disk->driverName != NULL && !STREQ(disk->driverName,
"qemu")) {
qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unsupported driver name '%s' for disk
'%s'"),
@@ -5217,6 +5239,12 @@ qemuDomainDetachDeviceDiskLive(struct qemud_driver *driver,
virDomainDiskDefPtr disk = dev->data.disk;
int ret = -1;
+ if (virDomainHasDiskMirror(vm)) {
+ qemuReportError(VIR_ERR_OPERATION_INVALID, "%s",
+ _("domain has active disk mirrors"));
+ return -1;
+ }
+
switch (disk->device) {
case VIR_DOMAIN_DISK_DEVICE_DISK:
case VIR_DOMAIN_DISK_DEVICE_LUN:
@@ -10239,6 +10267,12 @@ qemuDomainSnapshotCreateXML(virDomainPtr domain,
"%s", _("domain is marked for auto
destroy"));
goto cleanup;
}
+ if (virDomainHasDiskMirror(vm)) {
+ qemuReportError(VIR_ERR_OPERATION_INVALID, "%s",
+ _("domain has active disk mirrors"));
+ goto cleanup;
+ }
+
if (!vm->persistent && (flags & VIR_DOMAIN_SNAPSHOT_CREATE_HALT)) {
qemuReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("cannot halt after transient domain snapshot"));
@@ -10847,6 +10881,12 @@ static int qemuDomainRevertToSnapshot(virDomainSnapshotPtr
snapshot,
goto cleanup;
}
+ if (virDomainHasDiskMirror(vm)) {
+ qemuReportError(VIR_ERR_OPERATION_INVALID, "%s",
+ _("domain has active disk mirrors"));
+ goto cleanup;
+ }
+
snap = virDomainSnapshotFindByName(&vm->snapshots, snapshot->name);
if (!snap) {
qemuReportError(VIR_ERR_NO_DOMAIN_SNAPSHOT,
@@ -11216,6 +11256,17 @@ static int qemuDomainSnapshotDelete(virDomainSnapshotPtr
snapshot,
goto cleanup;
}
+ if (virDomainHasDiskMirror(vm)) {
+ if (snap != vm->current_snapshot) {
+ qemuReportError(VIR_ERR_OPERATION_INVALID, "%s",
+ _("domain has active disk mirrors"));
+ goto cleanup;
+ }
+ qemuReportError(VIR_ERR_OPERATION_INVALID, "%s",
+ _("deletion of active disk mirrors unimplemented"));
+ goto cleanup;
+ }
+
if (!(flags & VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY)) {
if (!(flags & VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN_ONLY) &&
snap->def->state == VIR_DOMAIN_DISK_SNAPSHOT)
--
1.7.7.6