On 2/23/19 4:24 PM, Eric Blake wrote:
Implement the new flags for bulk snapshot dump and redefine. This
borrows from ideas in the test driver, but is further complicated
by the fact that qemu must write snapshot XML to disk, and thus must
do additional validation after the initial parse to ensure the user
didn't attempt to rename a snapshot with "../" or similar.
Of note: all prior callers of qemuDomainSnapshotDiscardAllMetadata()
were at points where it did not matter if vm->current_snapshot and
the metaroot in vm->snapshots were left pointing to stale memory,
because we were about to delete the entire vm object; but now it is
important to reset things properly so that the domain still shows
as having no snapshots on failure.
Signed-off-by: Eric Blake <eblake(a)redhat.com>
---
src/qemu/qemu_domain.h | 2 +-
src/qemu/qemu_domain.c | 35 +++++++++++++++++------
src/qemu/qemu_driver.c | 64 ++++++++++++++++++++++++++++++++++++++++--
3 files changed, 89 insertions(+), 12 deletions(-)
NB: I couldn't get this one to git am -3 apply - I didn't chase the
conflict though.
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h
index 7c6b50184c..37c9813ec5 100644
--- a/src/qemu/qemu_domain.h
+++ b/src/qemu/qemu_domain.h
@@ -683,7 +683,7 @@ int qemuDomainSnapshotWriteMetadata(virDomainObjPtr vm,
virDomainSnapshotObjPtr snapshot,
virCapsPtr caps,
virDomainXMLOptionPtr xmlopt,
- char *snapshotDir);
+ const char *snapshotDir);
Theoretically separable.
int qemuDomainSnapshotForEachQcow2(virQEMUDriverPtr driver,
virDomainObjPtr vm,
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index cb1665c8f9..cf8b6e8eaf 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -7704,6 +7704,7 @@ qemuDomainDefCopy(virQEMUDriverPtr driver,
static int
qemuDomainDefFormatBufInternal(virQEMUDriverPtr driver,
+ virDomainObjPtr vm,
virDomainDefPtr def,
virCPUDefPtr origCPU,
unsigned int flags,
@@ -7713,8 +7714,10 @@ qemuDomainDefFormatBufInternal(virQEMUDriverPtr driver,
virDomainDefPtr copy = NULL;
virCapsPtr caps = NULL;
virQEMUCapsPtr qemuCaps = NULL;
+ bool snapshots = flags & VIR_DOMAIN_XML_SNAPSHOTS;
- virCheckFlags(VIR_DOMAIN_XML_COMMON_FLAGS | VIR_DOMAIN_XML_UPDATE_CPU, -1);
+ virCheckFlags(VIR_DOMAIN_XML_COMMON_FLAGS | VIR_DOMAIN_XML_UPDATE_CPU |
+ VIR_DOMAIN_XML_SNAPSHOTS, -1);
if (!(caps = virQEMUDriverGetCapabilities(driver, false)))
goto cleanup;
@@ -7881,7 +7884,14 @@ qemuDomainDefFormatBufInternal(virQEMUDriverPtr driver,
}
format:
- ret = virDomainDefFormatInternal(def, caps, NULL, NULL,
+ if (snapshots && !vm) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("snapshots XML requested but not provided"));
Error msg is a bit odd - the error is that a snapshot listing was
desired, but consumer didn't pass the @vm object.
+ goto cleanup;
+ }
+ ret = virDomainDefFormatInternal(def, caps,
+ snapshots ? vm->snapshots : NULL,
+ snapshots ? vm->current_snapshot : NULL,
virDomainDefFormatConvertXMLFlags(flags),
buf, driver->xmlopt);
Perhaps this one should [be | have been] turned into a
virDomainDefFormatFull (or whatever new name is chosen if one is
chosen). I think it shows the hazards of exposing *Internal to many
consumers.
@@ -7899,19 +7909,21 @@ qemuDomainDefFormatBuf(virQEMUDriverPtr driver,
unsigned int flags,
virBufferPtr buf)
{
- return qemuDomainDefFormatBufInternal(driver, def, NULL, flags, buf);
+ return qemuDomainDefFormatBufInternal(driver, NULL, def, NULL, flags, buf);
}
static char *
qemuDomainDefFormatXMLInternal(virQEMUDriverPtr driver,
+ virDomainObjPtr vm,
virDomainDefPtr def,
virCPUDefPtr origCPU,
unsigned int flags)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
- if (qemuDomainDefFormatBufInternal(driver, def, origCPU, flags, &buf) < 0)
+ if (qemuDomainDefFormatBufInternal(driver, vm, def, origCPU, flags,
+ &buf) < 0)
Existing; however, considering earlier comment about snapshot list being
filled into the buffer until error, but the *ListFormat not clearing the
buffer on error, I think another patch should be added here to do the
virBufferFreeAndReset(&buf); before return NULL.
return NULL;
return virBufferContentAndReset(&buf);
@@ -7923,7 +7935,7 @@ qemuDomainDefFormatXML(virQEMUDriverPtr driver,
virDomainDefPtr def,
unsigned int flags)
{
- return qemuDomainDefFormatXMLInternal(driver, def, NULL, flags);
+ return qemuDomainDefFormatXMLInternal(driver, NULL, def, NULL, flags);
}
@@ -7942,7 +7954,7 @@ char *qemuDomainFormatXML(virQEMUDriverPtr driver,
origCPU = priv->origCPU;
}
- return qemuDomainDefFormatXMLInternal(driver, def, origCPU, flags);
+ return qemuDomainDefFormatXMLInternal(driver, vm, def, origCPU, flags);
}
char *
@@ -7959,7 +7971,7 @@ qemuDomainDefFormatLive(virQEMUDriverPtr driver,
if (compatible)
flags |= VIR_DOMAIN_XML_MIGRATABLE;
- return qemuDomainDefFormatXMLInternal(driver, def, origCPU, flags);
+ return qemuDomainDefFormatXMLInternal(driver, NULL, def, origCPU, flags);
}
@@ -8386,7 +8398,7 @@ qemuDomainSnapshotWriteMetadata(virDomainObjPtr vm,
virDomainSnapshotObjPtr snapshot,
virCapsPtr caps,
virDomainXMLOptionPtr xmlopt,
- char *snapshotDir)
+ const char *snapshotDir)
{
char *newxml = NULL;
int ret = -1;
@@ -8605,6 +8617,7 @@ qemuDomainSnapshotDiscardAllMetadata(virQEMUDriverPtr driver,
virDomainObjPtr vm)
{
virQEMUSnapRemove rem;
+ virDomainSnapshotObjPtr snap;
rem.driver = driver;
rem.vm = vm;
@@ -8612,6 +8625,12 @@ qemuDomainSnapshotDiscardAllMetadata(virQEMUDriverPtr driver,
rem.err = 0;
virDomainSnapshotForEach(vm->snapshots, qemuDomainSnapshotDiscardAll,
&rem);
+ if (rem.current)
+ vm->current_snapshot = NULL;
+ /* Update the metaroot to match that all children were dropped */
+ snap = virDomainSnapshotFindByName(vm->snapshots, NULL);
+ snap->nchildren = 0;
+ snap->first_child = NULL;
return rem.err;
}
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index b3f4dc6f5c..4df9e18e4c 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -7337,8 +7337,8 @@ static char
virDomainObjPtr vm;
char *ret = NULL;
- virCheckFlags(VIR_DOMAIN_XML_COMMON_FLAGS | VIR_DOMAIN_XML_UPDATE_CPU,
- NULL);
+ virCheckFlags(VIR_DOMAIN_XML_COMMON_FLAGS | VIR_DOMAIN_XML_UPDATE_CPU |
+ VIR_DOMAIN_XML_SNAPSHOTS, NULL);
if (!(vm = qemuDomObjFromDomain(dom)))
goto cleanup;
@@ -15733,6 +15733,31 @@ qemuDomainSnapshotValidate(virDomainSnapshotDefPtr def, int
state,
return 0;
}
+/* Struct and hash-iterator callback used when bulk redefining snapshots */
+struct qemuDomainSnapshotBulk {
+ virDomainObjPtr vm;
+ virQEMUDriverPtr driver;
+ const char *snapshotDir;
+ unsigned int flags;
+};
+
+static int
+qemuDomainSnapshotBulkRedefine(void *payload, const void *name ATTRIBUTE_UNUSED,
one arg each line
+ void *opaque)
+{
+ virDomainSnapshotObjPtr snap = payload;
+ struct qemuDomainSnapshotBulk *data = opaque;
+
+ if (qemuDomainSnapshotValidate(snap->def, snap->def->state,
+ data->flags) < 0)
+ return -1;
+ if (qemuDomainSnapshotWriteMetadata(data->vm, snap, data->driver->caps,
+ data->driver->xmlopt,
+ data->snapshotDir) < 0)
+ return -1;
+ return 0;
+}
+
static virDomainSnapshotPtr
qemuDomainSnapshotCreateXML(virDomainPtr domain,
const char *xmlDesc,
@@ -15762,7 +15787,8 @@ qemuDomainSnapshotCreateXML(virDomainPtr domain,
VIR_DOMAIN_SNAPSHOT_CREATE_REUSE_EXT |
VIR_DOMAIN_SNAPSHOT_CREATE_QUIESCE |
VIR_DOMAIN_SNAPSHOT_CREATE_ATOMIC |
- VIR_DOMAIN_SNAPSHOT_CREATE_LIVE, NULL);
+ VIR_DOMAIN_SNAPSHOT_CREATE_LIVE |
+ VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE_LIST, NULL);
VIR_REQUIRE_FLAG_RET(VIR_DOMAIN_SNAPSHOT_CREATE_QUIESCE,
VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY,
@@ -15794,6 +15820,38 @@ qemuDomainSnapshotCreateXML(virDomainPtr domain,
goto cleanup;
}
+ if (flags & VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE_LIST) {
+ struct qemuDomainSnapshotBulk bulk = {
+ .vm = vm,
+ .driver = driver,
+ .snapshotDir = cfg->snapshotDir,
+ .flags = flags,
+ };
+
+ if (virDomainSnapshotDefParseList(xmlDesc, vm->def->uuid,
+ vm->snapshots,
&vm->current_snapshot,
+ caps, driver->xmlopt,
+ parse_flags) < 0)
+ goto cleanup;
+ /* Validate and save the snapshots to disk. Since we don't get
+ * here unless there were no snapshots beforehand, just delete
+ * everything if anything failed, ignoring further errors. */
+ if (virDomainSnapshotForEach(vm->snapshots,
+ qemuDomainSnapshotBulkRedefine,
+ &bulk) < 0) {
+ virErrorPtr orig_err = virSaveLastError();
+
+ qemuDomainSnapshotDiscardAllMetadata(driver, vm);
+ virSetError(orig_err);
+ virFreeError(orig_err);
+ goto cleanup;
+ }
+ /* Return is arbitrary, so use the first root */
+ snap = virDomainSnapshotFindByName(vm->snapshots, NULL);
Similar to test driver - this isn't used during cleanup.
John
+ snapshot = virGetDomainSnapshot(domain,
snap->first_child->def->name);
+ goto cleanup;
+ }
+
if (!vm->persistent && (flags & VIR_DOMAIN_SNAPSHOT_CREATE_HALT)) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("cannot halt after transient domain snapshot"));