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 | 15 ++++++++++++++-
src/conf/domain_conf.h | 1 +
src/libvirt_private.syms | 1 +
src/qemu/qemu_driver.c | 42 +++++++++++++++++++++++++++++++++++++++++-
4 files changed, 57 insertions(+), 2 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index fd9fd2c..30cea8d 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -7194,7 +7194,9 @@ virDomainHasDiskMirror(virDomainObjPtr vm)
for (i = 0; i < vm->def->ndisks; i++)
if (vm->def->disks[i]->mirror)
return true;
- return false;
+ if (!vm->current_snapshot)
+ return false;
+ return virDomainSnapshotHasDiskMirror(vm->current_snapshot->def);
}
int virDomainNetInsert(virDomainDefPtr def, virDomainNetDefPtr net)
@@ -13833,6 +13835,17 @@ 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;
+}
+
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 a25cd1a..e56d7c9 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -1731,6 +1731,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);
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 4ed2f0c..8ecd0eb 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -447,6 +447,7 @@ virDomainSnapshotDropParent;
virDomainSnapshotFindByName;
virDomainSnapshotForEachChild;
virDomainSnapshotForEachDescendant;
+virDomainSnapshotHasDiskMirror;
virDomainSnapshotObjListGetNames;
virDomainSnapshotObjListGetNamesFrom;
virDomainSnapshotObjListNum;
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index dd49cb9..5b1ed87 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) {
@@ -5117,6 +5128,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'"),
@@ -5267,6 +5284,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:
@@ -10914,6 +10937,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,
@@ -11288,6 +11317,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