[libvirt] [PATCH 0/3] snapshot: auto-cleanup metadata of transient domains

I intended to do this ever since I changed the API of virDomainUndefine to reject undefine of persistent guests with snapshots, but am only now getting around to it. This missed 0.9.5, but I think it is worth including in 0.9.6 even if we have a short release cycle to work around the qemu shutdown issue. Eric Blake (3): snapshot: fix logic bug in qemu undefine snapshot: prepare to remove transient snapshot metadata snapshot: remove snapshot metadata on transient exit src/conf/domain_conf.c | 4 +- src/qemu/qemu_domain.c | 260 ++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_domain.h | 36 ++++++ src/qemu/qemu_driver.c | 291 +++----------------------------------------- src/qemu/qemu_migration.c | 8 +- src/qemu/qemu_process.c | 10 +- 6 files changed, 327 insertions(+), 282 deletions(-) -- 1.7.4.4

Commit 19f8c98 introduced VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA, with the intent that omitting the flag makes undefine fail, and including the flag deletes metadata. But it used the wrong logic. Also, hoist the transient domain sooner, so that we don't accidentally remove metadata of a transient domain. * src/qemu/qemu_driver.c (qemuDomainUndefineFlags): Check correct flag value. --- src/qemu/qemu_driver.c | 16 ++++++++-------- 1 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 67c43ab..4a24019 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -5095,7 +5095,7 @@ cleanup: static int qemuDomainUndefineFlags(virDomainPtr dom, - unsigned int flags) + unsigned int flags) { struct qemud_driver *driver = dom->conn->privateData; virDomainObjPtr vm; @@ -5118,11 +5118,17 @@ qemuDomainUndefineFlags(virDomainPtr dom, goto cleanup; } + if (!vm->persistent) { + qemuReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("cannot undefine transient domain")); + goto cleanup; + } + if (!virDomainObjIsActive(vm) && (nsnapshots = virDomainSnapshotObjListNum(&vm->snapshots, 0))) { struct snap_remove rem; - if (flags & VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA) { + if (!(flags & VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA)) { qemuReportError(VIR_ERR_OPERATION_INVALID, _("cannot delete inactive domain with %d " "snapshots"), @@ -5139,12 +5145,6 @@ qemuDomainUndefineFlags(virDomainPtr dom, goto cleanup; } - if (!vm->persistent) { - qemuReportError(VIR_ERR_OPERATION_INVALID, - "%s", _("cannot undefine transient domain")); - goto cleanup; - } - name = qemuDomainManagedSavePath(driver, vm); if (name == NULL) goto cleanup; -- 1.7.4.4

On Wed, Sep 21, 2011 at 01:08:50PM -0600, Eric Blake wrote:
Commit 19f8c98 introduced VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA, with the intent that omitting the flag makes undefine fail, and including the flag deletes metadata. But it used the wrong logic. Also, hoist the transient domain sooner, so that we don't accidentally remove metadata of a transient domain.
* src/qemu/qemu_driver.c (qemuDomainUndefineFlags): Check correct flag value. --- src/qemu/qemu_driver.c | 16 ++++++++-------- 1 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 67c43ab..4a24019 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -5095,7 +5095,7 @@ cleanup:
static int qemuDomainUndefineFlags(virDomainPtr dom, - unsigned int flags) + unsigned int flags) { struct qemud_driver *driver = dom->conn->privateData; virDomainObjPtr vm; @@ -5118,11 +5118,17 @@ qemuDomainUndefineFlags(virDomainPtr dom, goto cleanup; }
+ if (!vm->persistent) { + qemuReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("cannot undefine transient domain")); + goto cleanup; + } + if (!virDomainObjIsActive(vm) && (nsnapshots = virDomainSnapshotObjListNum(&vm->snapshots, 0))) { struct snap_remove rem;
- if (flags & VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA) { + if (!(flags & VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA)) { qemuReportError(VIR_ERR_OPERATION_INVALID, _("cannot delete inactive domain with %d " "snapshots"), @@ -5139,12 +5145,6 @@ qemuDomainUndefineFlags(virDomainPtr dom, goto cleanup; }
- if (!vm->persistent) { - qemuReportError(VIR_ERR_OPERATION_INVALID, - "%s", _("cannot undefine transient domain")); - goto cleanup; - } - name = qemuDomainManagedSavePath(driver, vm); if (name == NULL) goto cleanup;
ACK, Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

This patch is mostly code motion - moving some functions out of qemu_driver and into qemu_domain so they can be reused by multiple qemu_* files (since qemu_driver.h must not grow). It also adds a new helper function, qemuDomainRemoveInactive, which will be used in the next patch. * src/qemu/qemu_domain.h (qemuFindQemuImgBinary) (qemuDomainSnapshotWriteMetadata, qemuDomainSnapshotForEachQcow2) (qemuDomainSnapshotDiscard, qemuDomainSnapshotDiscardAll) (qemuDomainRemoveInactive): New prototypes. (struct qemu_snap_remove): New struct. * src/qemu/qemu_domain.c (qemuDomainRemoveInactive) (qemuDomainSnapshotDiscardAllMetadata): New functions. (qemuFindQemuImgBinary, qemuDomainSnapshotWriteMetadata) (qemuDomainSnapshotForEachQcow2, qemuDomainSnapshotDiscard) (qemuDomainSnapshotDiscardAll): Move here... * src/qemu/qemu_driver.c (qemuFindQemuImgBinary) (qemuDomainSnapshotWriteMetadata, qemuDomainSnapshotForEachQcow2) (qemuDomainSnapshotDiscard, qemuDomainSnapshotDiscardAll): ...from here. (qemuDomainUndefineFlags): Update caller. * src/conf/domain_conf.c (virDomainRemoveInactive): Doc fixes. --- src/conf/domain_conf.c | 4 +- src/qemu/qemu_domain.c | 260 +++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_domain.h | 36 +++++++ src/qemu/qemu_driver.c | 249 +-------------------------------------------- src/qemu/qemu_process.c | 2 +- 5 files changed, 301 insertions(+), 250 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index eebcba0..7463d7c 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1438,9 +1438,9 @@ virDomainObjGetPersistentDef(virCapsPtr caps, } /* - * The caller must hold a lock on the driver owning 'doms', + * The caller must hold a lock on the driver owning 'doms', * and must also have locked 'dom', to ensure no one else - * is either waiting for 'dom' or still usingn it + * is either waiting for 'dom' or still using it */ void virDomainRemoveInactive(virDomainObjListPtr doms, virDomainObjPtr dom) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index d2cc2f0..9436245 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -1296,3 +1296,263 @@ cleanup: VIR_FREE(message); return ret; } + +/* Locate an appropriate 'qemu-img' binary. */ +const char * +qemuFindQemuImgBinary(struct qemud_driver *driver) +{ + if (!driver->qemuImgBinary) { + driver->qemuImgBinary = virFindFileInPath("kvm-img"); + if (!driver->qemuImgBinary) + driver->qemuImgBinary = virFindFileInPath("qemu-img"); + if (!driver->qemuImgBinary) + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("unable to find kvm-img or qemu-img")); + } + + return driver->qemuImgBinary; +} + +int +qemuDomainSnapshotWriteMetadata(virDomainObjPtr vm, + virDomainSnapshotObjPtr snapshot, + char *snapshotDir) +{ + int fd = -1; + char *newxml = NULL; + int ret = -1; + char *snapDir = NULL; + char *snapFile = NULL; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + char *tmp; + + virUUIDFormat(vm->def->uuid, uuidstr); + newxml = virDomainSnapshotDefFormat(uuidstr, snapshot->def, + VIR_DOMAIN_XML_SECURE, 1); + if (newxml == NULL) { + virReportOOMError(); + return -1; + } + + if (virAsprintf(&snapDir, "%s/%s", snapshotDir, vm->def->name) < 0) { + virReportOOMError(); + goto cleanup; + } + if (virFileMakePath(snapDir) < 0) { + virReportSystemError(errno, _("cannot create snapshot directory '%s'"), + snapDir); + goto cleanup; + } + + if (virAsprintf(&snapFile, "%s/%s.xml", snapDir, snapshot->def->name) < 0) { + virReportOOMError(); + goto cleanup; + } + fd = open(snapFile, O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR); + if (fd < 0) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("failed to create snapshot file '%s'"), snapFile); + goto cleanup; + } + + if (virAsprintf(&tmp, "snapshot-edit %s", vm->def->name) < 0) { + virReportOOMError(); + goto cleanup; + } + virEmitXMLWarning(fd, snapshot->def->name, tmp); + VIR_FREE(tmp); + + if (safewrite(fd, newxml, strlen(newxml)) != strlen(newxml)) { + virReportSystemError(errno, _("Failed to write snapshot data to %s"), + snapFile); + goto cleanup; + } + + ret = 0; + +cleanup: + VIR_FREE(snapFile); + VIR_FREE(snapDir); + VIR_FREE(newxml); + VIR_FORCE_CLOSE(fd); + return ret; +} + +/* The domain is expected to be locked and inactive. Return -1 on normal + * failure, 1 if we skipped a disk due to try_all. */ +int +qemuDomainSnapshotForEachQcow2(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainSnapshotObjPtr snap, + const char *op, + bool try_all) +{ + const char *qemuimgarg[] = { NULL, "snapshot", NULL, NULL, NULL, NULL }; + int i; + bool skipped = false; + + qemuimgarg[0] = qemuFindQemuImgBinary(driver); + if (qemuimgarg[0] == NULL) { + /* qemuFindQemuImgBinary set the error */ + return -1; + } + + qemuimgarg[2] = op; + qemuimgarg[3] = snap->def->name; + + for (i = 0; i < vm->def->ndisks; i++) { + /* FIXME: we also need to handle LVM here */ + /* FIXME: if we fail halfway through this loop, we are in an + * inconsistent state. I'm not quite sure what to do about that + */ + if (vm->def->disks[i]->device == VIR_DOMAIN_DISK_DEVICE_DISK) { + if (!vm->def->disks[i]->driverType || + STRNEQ(vm->def->disks[i]->driverType, "qcow2")) { + if (try_all) { + /* Continue on even in the face of error, since other + * disks in this VM may have the same snapshot name. + */ + VIR_WARN("skipping snapshot action on %s", + vm->def->disks[i]->dst); + skipped = true; + continue; + } + qemuReportError(VIR_ERR_OPERATION_INVALID, + _("Disk device '%s' does not support" + " snapshotting"), + vm->def->disks[i]->dst); + return -1; + } + + qemuimgarg[4] = vm->def->disks[i]->src; + + if (virRun(qemuimgarg, NULL) < 0) { + if (try_all) { + VIR_WARN("skipping snapshot action on %s", + vm->def->disks[i]->info.alias); + skipped = true; + continue; + } + return -1; + } + } + } + + return skipped ? 1 : 0; +} + +/* Discard one snapshot (or its metadata), without reparenting any children. */ +int +qemuDomainSnapshotDiscard(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainSnapshotObjPtr snap, + bool update_current, + bool metadata_only) +{ + char *snapFile = NULL; + int ret = -1; + qemuDomainObjPrivatePtr priv; + virDomainSnapshotObjPtr parentsnap = NULL; + + if (!metadata_only) { + if (!virDomainObjIsActive(vm)) { + /* Ignore any skipped disks */ + if (qemuDomainSnapshotForEachQcow2(driver, vm, snap, "-d", + true) < 0) + goto cleanup; + } else { + priv = vm->privateData; + qemuDomainObjEnterMonitorWithDriver(driver, vm); + /* we continue on even in the face of error */ + qemuMonitorDeleteSnapshot(priv->mon, snap->def->name); + qemuDomainObjExitMonitorWithDriver(driver, vm); + } + } + + if (virAsprintf(&snapFile, "%s/%s/%s.xml", driver->snapshotDir, + vm->def->name, snap->def->name) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (snap == vm->current_snapshot) { + if (update_current && snap->def->parent) { + parentsnap = virDomainSnapshotFindByName(&vm->snapshots, + snap->def->parent); + if (!parentsnap) { + VIR_WARN("missing parent snapshot matching name '%s'", + snap->def->parent); + } else { + parentsnap->def->current = true; + if (qemuDomainSnapshotWriteMetadata(vm, parentsnap, + driver->snapshotDir) < 0) { + VIR_WARN("failed to set parent snapshot '%s' as current", + snap->def->parent); + parentsnap->def->current = false; + parentsnap = NULL; + } + } + } + vm->current_snapshot = parentsnap; + } + + if (unlink(snapFile) < 0) + VIR_WARN("Failed to unlink %s", snapFile); + virDomainSnapshotObjListRemove(&vm->snapshots, snap); + + ret = 0; + +cleanup: + VIR_FREE(snapFile); + + return ret; +} + +/* Hash iterator callback to discard multiple snapshots. */ +void qemuDomainSnapshotDiscardAll(void *payload, + const void *name ATTRIBUTE_UNUSED, + void *data) +{ + virDomainSnapshotObjPtr snap = payload; + struct qemu_snap_remove *curr = data; + int err; + + if (snap->def->current) + curr->current = true; + err = qemuDomainSnapshotDiscard(curr->driver, curr->vm, snap, false, + curr->metadata_only); + if (err && !curr->err) + curr->err = err; +} + +int +qemuDomainSnapshotDiscardAllMetadata(struct qemud_driver *driver, + virDomainObjPtr vm) +{ + struct qemu_snap_remove rem; + + rem.driver = driver; + rem.vm = vm; + rem.metadata_only = true; + rem.err = 0; + virHashForEach(vm->snapshots.objs, qemuDomainSnapshotDiscardAll, &rem); + + /* XXX also do rmdir ? */ + return rem.err; +} + +/* + * The caller must hold a lock on both driver and vm, and there must + * be no remaining references to vm. + */ +void +qemuDomainRemoveInactive(struct qemud_driver *driver, + virDomainObjPtr vm) +{ + /* Remove any snapshot metadata prior to removing the domain */ + if (qemuDomainSnapshotDiscardAllMetadata(driver, vm) < 0) { + VIR_WARN("unable to remove all snapshots for domain %s", + vm->def->name); + } + virDomainRemoveInactive(&driver->domains, vm); +} diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index cde3ada..00cfa3a 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -237,4 +237,40 @@ int qemuDomainAppendLog(struct qemud_driver *driver, int logFD, const char *fmt, ...) ATTRIBUTE_FMT_PRINTF(4, 5); +const char *qemuFindQemuImgBinary(struct qemud_driver *driver); + +int qemuDomainSnapshotWriteMetadata(virDomainObjPtr vm, + virDomainSnapshotObjPtr snapshot, + char *snapshotDir); + +int qemuDomainSnapshotForEachQcow2(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainSnapshotObjPtr snap, + const char *op, + bool try_all); + +int qemuDomainSnapshotDiscard(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainSnapshotObjPtr snap, + bool update_current, + bool metadata_only); + +struct qemu_snap_remove { + struct qemud_driver *driver; + virDomainObjPtr vm; + int err; + bool metadata_only; + bool current; +}; + +void qemuDomainSnapshotDiscardAll(void *payload, + const void *name, + void *data); + +int qemuDomainSnapshotDiscardAllMetadata(struct qemud_driver *driver, + virDomainObjPtr vm); + +void qemuDomainRemoveInactive(struct qemud_driver *driver, + virDomainObjPtr vm); + #endif /* __QEMU_DOMAIN_H__ */ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 4a24019..4ff3281 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -1596,243 +1596,6 @@ cleanup: } -/* Locate an appropriate 'qemu-img' binary. */ -static const char * -qemuFindQemuImgBinary(struct qemud_driver *driver) -{ - if (!driver->qemuImgBinary) { - driver->qemuImgBinary = virFindFileInPath("kvm-img"); - if (!driver->qemuImgBinary) - driver->qemuImgBinary = virFindFileInPath("qemu-img"); - if (!driver->qemuImgBinary) - qemuReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("unable to find kvm-img or qemu-img")); - } - - return driver->qemuImgBinary; -} - -static int -qemuDomainSnapshotWriteMetadata(virDomainObjPtr vm, - virDomainSnapshotObjPtr snapshot, - char *snapshotDir) -{ - int fd = -1; - char *newxml = NULL; - int ret = -1; - char *snapDir = NULL; - char *snapFile = NULL; - char uuidstr[VIR_UUID_STRING_BUFLEN]; - char *tmp; - - virUUIDFormat(vm->def->uuid, uuidstr); - newxml = virDomainSnapshotDefFormat(uuidstr, snapshot->def, - VIR_DOMAIN_XML_SECURE, 1); - if (newxml == NULL) { - virReportOOMError(); - return -1; - } - - if (virAsprintf(&snapDir, "%s/%s", snapshotDir, vm->def->name) < 0) { - virReportOOMError(); - goto cleanup; - } - if (virFileMakePath(snapDir) < 0) { - virReportSystemError(errno, _("cannot create snapshot directory '%s'"), - snapDir); - goto cleanup; - } - - if (virAsprintf(&snapFile, "%s/%s.xml", snapDir, snapshot->def->name) < 0) { - virReportOOMError(); - goto cleanup; - } - fd = open(snapFile, O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR); - if (fd < 0) { - qemuReportError(VIR_ERR_OPERATION_FAILED, - _("failed to create snapshot file '%s'"), snapFile); - goto cleanup; - } - - if (virAsprintf(&tmp, "snapshot-edit %s", vm->def->name) < 0) { - virReportOOMError(); - goto cleanup; - } - virEmitXMLWarning(fd, snapshot->def->name, tmp); - VIR_FREE(tmp); - - if (safewrite(fd, newxml, strlen(newxml)) != strlen(newxml)) { - virReportSystemError(errno, _("Failed to write snapshot data to %s"), - snapFile); - goto cleanup; - } - - ret = 0; - -cleanup: - VIR_FREE(snapFile); - VIR_FREE(snapDir); - VIR_FREE(newxml); - VIR_FORCE_CLOSE(fd); - return ret; -} - -/* The domain is expected to be locked and inactive. Return -1 on normal - * failure, 1 if we skipped a disk due to try_all. */ -static int -qemuDomainSnapshotForEachQcow2(struct qemud_driver *driver, - virDomainObjPtr vm, - virDomainSnapshotObjPtr snap, - const char *op, - bool try_all) -{ - const char *qemuimgarg[] = { NULL, "snapshot", NULL, NULL, NULL, NULL }; - int i; - bool skipped = false; - - qemuimgarg[0] = qemuFindQemuImgBinary(driver); - if (qemuimgarg[0] == NULL) { - /* qemuFindQemuImgBinary set the error */ - return -1; - } - - qemuimgarg[2] = op; - qemuimgarg[3] = snap->def->name; - - for (i = 0; i < vm->def->ndisks; i++) { - /* FIXME: we also need to handle LVM here */ - /* FIXME: if we fail halfway through this loop, we are in an - * inconsistent state. I'm not quite sure what to do about that - */ - if (vm->def->disks[i]->device == VIR_DOMAIN_DISK_DEVICE_DISK) { - if (!vm->def->disks[i]->driverType || - STRNEQ(vm->def->disks[i]->driverType, "qcow2")) { - if (try_all) { - /* Continue on even in the face of error, since other - * disks in this VM may have the same snapshot name. - */ - VIR_WARN("skipping snapshot action on %s", - vm->def->disks[i]->dst); - skipped = true; - continue; - } - qemuReportError(VIR_ERR_OPERATION_INVALID, - _("Disk device '%s' does not support" - " snapshotting"), - vm->def->disks[i]->dst); - return -1; - } - - qemuimgarg[4] = vm->def->disks[i]->src; - - if (virRun(qemuimgarg, NULL) < 0) { - if (try_all) { - VIR_WARN("skipping snapshot action on %s", - vm->def->disks[i]->info.alias); - skipped = true; - continue; - } - return -1; - } - } - } - - return skipped ? 1 : 0; -} - -/* Discard one snapshot (or its metadata), without reparenting any children. */ -static int -qemuDomainSnapshotDiscard(struct qemud_driver *driver, - virDomainObjPtr vm, - virDomainSnapshotObjPtr snap, - bool update_current, - bool metadata_only) -{ - char *snapFile = NULL; - int ret = -1; - qemuDomainObjPrivatePtr priv; - virDomainSnapshotObjPtr parentsnap = NULL; - - if (!metadata_only) { - if (!virDomainObjIsActive(vm)) { - /* Ignore any skipped disks */ - if (qemuDomainSnapshotForEachQcow2(driver, vm, snap, "-d", - true) < 0) - goto cleanup; - } else { - priv = vm->privateData; - qemuDomainObjEnterMonitorWithDriver(driver, vm); - /* we continue on even in the face of error */ - qemuMonitorDeleteSnapshot(priv->mon, snap->def->name); - qemuDomainObjExitMonitorWithDriver(driver, vm); - } - } - - if (virAsprintf(&snapFile, "%s/%s/%s.xml", driver->snapshotDir, - vm->def->name, snap->def->name) < 0) { - virReportOOMError(); - goto cleanup; - } - - if (snap == vm->current_snapshot) { - if (update_current && snap->def->parent) { - parentsnap = virDomainSnapshotFindByName(&vm->snapshots, - snap->def->parent); - if (!parentsnap) { - VIR_WARN("missing parent snapshot matching name '%s'", - snap->def->parent); - } else { - parentsnap->def->current = true; - if (qemuDomainSnapshotWriteMetadata(vm, parentsnap, - driver->snapshotDir) < 0) { - VIR_WARN("failed to set parent snapshot '%s' as current", - snap->def->parent); - parentsnap->def->current = false; - parentsnap = NULL; - } - } - } - vm->current_snapshot = parentsnap; - } - - if (unlink(snapFile) < 0) - VIR_WARN("Failed to unlink %s", snapFile); - virDomainSnapshotObjListRemove(&vm->snapshots, snap); - - ret = 0; - -cleanup: - VIR_FREE(snapFile); - - return ret; -} - -struct snap_remove { - struct qemud_driver *driver; - virDomainObjPtr vm; - int err; - bool metadata_only; - bool current; -}; - -/* Hash iterator callback to discard multiple snapshots. */ -static void -qemuDomainSnapshotDiscardAll(void *payload, - const void *name ATTRIBUTE_UNUSED, - void *data) -{ - virDomainSnapshotObjPtr snap = payload; - struct snap_remove *curr = data; - int err; - - if (snap->def->current) - curr->current = true; - err = qemuDomainSnapshotDiscard(curr->driver, curr->vm, snap, false, - curr->metadata_only); - if (err && !curr->err) - curr->err = err; -} - /* Count how many snapshots in a set have external disk snapshots. */ static void qemuDomainSnapshotCountExternal(void *payload, @@ -5126,8 +4889,6 @@ qemuDomainUndefineFlags(virDomainPtr dom, if (!virDomainObjIsActive(vm) && (nsnapshots = virDomainSnapshotObjListNum(&vm->snapshots, 0))) { - struct snap_remove rem; - if (!(flags & VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA)) { qemuReportError(VIR_ERR_OPERATION_INVALID, _("cannot delete inactive domain with %d " @@ -5135,13 +4896,7 @@ qemuDomainUndefineFlags(virDomainPtr dom, nsnapshots); goto cleanup; } - - rem.driver = driver; - rem.vm = vm; - rem.metadata_only = true; - rem.err = 0; - virHashForEach(vm->snapshots.objs, qemuDomainSnapshotDiscardAll, &rem); - if (rem.err < 0) + if (qemuDomainSnapshotDiscardAllMetadata(driver, vm) < 0) goto cleanup; } @@ -10132,7 +9887,7 @@ static int qemuDomainSnapshotDelete(virDomainSnapshotPtr snapshot, int ret = -1; virDomainSnapshotObjPtr snap = NULL; char uuidstr[VIR_UUID_STRING_BUFLEN]; - struct snap_remove rem; + struct qemu_snap_remove rem; struct snap_reparent rep; bool metadata_only = !!(flags & VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY); int external = 0; diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index bd49b21..8797a56 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -106,7 +106,7 @@ qemuProcessRemoveDomainStatus(struct qemud_driver *driver, extern struct qemud_driver *qemu_driver; /* - * This is a callback registered with a qemuMonitorPtr instance, + * This is a callback registered with a qemuMonitorPtr instance, * and to be invoked when the monitor console hits an end of file * condition, or error, thus indicating VM shutdown should be * performed -- 1.7.4.4

Commit 282fe1f0 documented that transient domains will auto-delete any snapshot metadata when the last reference to the domain is removed, and that management apps are in charge of grabbing any snapshot metadata prior to that point. However, this was not actually implemented for qemu until now. * src/qemu/qemu_driver.c (qemudDomainCreate) (qemuDomainDestroyFlags, qemuDomainSaveInternal) (qemudDomainCoreDump, qemuDomainRestoreFlags, qemudDomainDefine) (qemuDomainUndefineFlags, qemuDomainMigrateConfirm3) (qemuDomainRevertToSnapshot): Clean up snapshot metadata. * src/qemu/qemu_migration.c (qemuMigrationPrepareAny) (qemuMigrationPerformJob, qemuMigrationPerformPhase) (qemuMigrationFinish): Likewise. * src/qemu/qemu_process.c (qemuProcessHandleMonitorEOF) (qemuProcessReconnect, qemuProcessReconnectHelper) (qemuProcessAutoDestroyDom): Likewise. --- src/qemu/qemu_driver.c | 26 ++++++++++---------------- src/qemu/qemu_migration.c | 8 ++++---- src/qemu/qemu_process.c | 8 ++++---- 3 files changed, 18 insertions(+), 24 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 4ff3281..0d0bea2 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -1318,8 +1318,7 @@ static virDomainPtr qemudDomainCreate(virConnectPtr conn, const char *xml, -1, NULL, NULL, VIR_VM_OP_CREATE) < 0) { virDomainAuditStart(vm, "booted", false); if (qemuDomainObjEndJob(driver, vm) > 0) - virDomainRemoveInactive(&driver->domains, - vm); + qemuDomainRemoveInactive(driver, vm); vm = NULL; goto cleanup; } @@ -1658,8 +1657,7 @@ qemuDomainDestroyFlags(virDomainPtr dom, if (!vm->persistent) { if (qemuDomainObjEndJob(driver, vm) > 0) - virDomainRemoveInactive(&driver->domains, - vm); + qemuDomainRemoveInactive(driver, vm); vm = NULL; } ret = 0; @@ -2529,8 +2527,7 @@ qemuDomainSaveInternal(struct qemud_driver *driver, virDomainPtr dom, VIR_DOMAIN_EVENT_STOPPED_SAVED); if (!vm->persistent) { if (qemuDomainObjEndAsyncJob(driver, vm) > 0) - virDomainRemoveInactive(&driver->domains, - vm); + qemuDomainRemoveInactive(driver, vm); vm = NULL; } @@ -2947,8 +2944,7 @@ endjob: if (qemuDomainObjEndAsyncJob(driver, vm) == 0) vm = NULL; else if ((ret == 0) && (flags & VIR_DUMP_CRASH) && !vm->persistent) { - virDomainRemoveInactive(&driver->domains, - vm); + qemuDomainRemoveInactive(driver, vm); vm = NULL; } @@ -4151,7 +4147,7 @@ qemuDomainRestoreFlags(virConnectPtr conn, if (qemuDomainObjEndJob(driver, vm) == 0) vm = NULL; else if (ret < 0 && !vm->persistent) { - virDomainRemoveInactive(&driver->domains, vm); + qemuDomainRemoveInactive(driver, vm); vm = NULL; } @@ -4830,8 +4826,7 @@ static virDomainPtr qemudDomainDefine(virConnectPtr conn, const char *xml) { if (virDomainSaveConfig(driver->configDir, vm->newDef ? vm->newDef : vm->def) < 0) { VIR_INFO("Defining domain '%s'", vm->def->name); - virDomainRemoveInactive(&driver->domains, - vm); + qemuDomainRemoveInactive(driver, vm); vm = NULL; goto cleanup; } @@ -4936,8 +4931,7 @@ qemuDomainUndefineFlags(virDomainPtr dom, if (virDomainObjIsActive(vm)) { vm->persistent = 0; } else { - virDomainRemoveInactive(&driver->domains, - vm); + qemuDomainRemoveInactive(driver, vm); vm = NULL; } @@ -8243,7 +8237,7 @@ qemuDomainMigrateConfirm3(virDomainPtr domain, (!vm->persistent || (flags & VIR_MIGRATE_UNDEFINE_SOURCE))) { if (flags & VIR_MIGRATE_UNDEFINE_SOURCE) virDomainDeleteConfig(driver->configDir, driver->autostartDir, vm); - virDomainRemoveInactive(&driver->domains, vm); + qemuDomainRemoveInactive(driver, vm); vm = NULL; } @@ -9774,7 +9768,7 @@ static int qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot, if (qemuDomainSnapshotRevertInactive(driver, vm, snap) < 0) { if (!vm->persistent) { if (qemuDomainObjEndJob(driver, vm) > 0) - virDomainRemoveInactive(&driver->domains, vm); + qemuDomainRemoveInactive(driver, vm); vm = NULL; goto cleanup; } @@ -9797,7 +9791,7 @@ static int qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot, if (rc < 0) { if (!vm->persistent) { if (qemuDomainObjEndJob(driver, vm) > 0) - virDomainRemoveInactive(&driver->domains, vm); + qemuDomainRemoveInactive(driver, vm); vm = NULL; goto cleanup; } diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index d9f8d93..4fd2e9f 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -1127,7 +1127,7 @@ endjob: if (qemuMigrationJobFinish(driver, vm) == 0) { vm = NULL; } else if (!vm->persistent) { - virDomainRemoveInactive(&driver->domains, vm); + qemuDomainRemoveInactive(driver, vm); vm = NULL; } goto cleanup; @@ -2272,7 +2272,7 @@ endjob: (ret == 0 && (flags & VIR_MIGRATE_UNDEFINE_SOURCE)))) { if (flags & VIR_MIGRATE_UNDEFINE_SOURCE) virDomainDeleteConfig(driver->configDir, driver->autostartDir, vm); - virDomainRemoveInactive(&driver->domains, vm); + qemuDomainRemoveInactive(driver, vm); vm = NULL; } @@ -2351,7 +2351,7 @@ endjob: if (refs == 0) { vm = NULL; } else if (!virDomainObjIsActive(vm) && !vm->persistent) { - virDomainRemoveInactive(&driver->domains, vm); + qemuDomainRemoveInactive(driver, vm); vm = NULL; } @@ -2615,7 +2615,7 @@ endjob: if (qemuMigrationJobFinish(driver, vm) == 0) { vm = NULL; } else if (!vm->persistent && !virDomainObjIsActive(vm)) { - virDomainRemoveInactive(&driver->domains, vm); + qemuDomainRemoveInactive(driver, vm); vm = NULL; } diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 8797a56..c27a5e2 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -150,7 +150,7 @@ qemuProcessHandleMonitorEOF(qemuMonitorPtr mon ATTRIBUTE_UNUSED, virDomainAuditStop(vm, auditReason); if (!vm->persistent) - virDomainRemoveInactive(&driver->domains, vm); + qemuDomainRemoveInactive(driver, vm); else virDomainObjUnlock(vm); @@ -2674,7 +2674,7 @@ error: * user tries to start it again later */ qemuProcessStop(driver, obj, 0, VIR_DOMAIN_SHUTOFF_FAILED); if (!obj->persistent) - virDomainRemoveInactive(&driver->domains, obj); + qemuDomainRemoveInactive(driver, obj); else virDomainObjUnlock(obj); } @@ -2751,7 +2751,7 @@ qemuProcessReconnectHelper(void *payload, * Kill qemu */ qemuProcessStop(src->driver, obj, 0, VIR_DOMAIN_SHUTOFF_FAILED); if (!obj->persistent) - virDomainRemoveInactive(&src->driver->domains, obj); + qemuDomainRemoveInactive(src->driver, obj); else virDomainObjUnlock(obj); } @@ -3698,7 +3698,7 @@ static void qemuProcessAutoDestroyDom(void *payload, if (qemuDomainObjEndJob(data->driver, dom) == 0) dom = NULL; if (dom && !dom->persistent) - virDomainRemoveInactive(&data->driver->domains, dom); + qemuDomainRemoveInactive(data->driver, dom); cleanup: if (dom) -- 1.7.4.4

The previous patch removed all snapshots, but not the directory where the snapshots lived, which is still a form of stale data. * src/qemu/qemu_domain.c (qemuDomainRemoveInactive): Wipe any snapshot directory. --- I could probably also just squash this into 2/3. src/qemu/qemu_domain.c | 12 +++++++++++- 1 files changed, 11 insertions(+), 1 deletions(-) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 9436245..4023648 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -1537,7 +1537,6 @@ qemuDomainSnapshotDiscardAllMetadata(struct qemud_driver *driver, rem.err = 0; virHashForEach(vm->snapshots.objs, qemuDomainSnapshotDiscardAll, &rem); - /* XXX also do rmdir ? */ return rem.err; } @@ -1549,10 +1548,21 @@ void qemuDomainRemoveInactive(struct qemud_driver *driver, virDomainObjPtr vm) { + char *snapDir; + /* Remove any snapshot metadata prior to removing the domain */ if (qemuDomainSnapshotDiscardAllMetadata(driver, vm) < 0) { VIR_WARN("unable to remove all snapshots for domain %s", vm->def->name); } + else if (virAsprintf(&snapDir, "%s/%s", driver->snapshotDir, + vm->def->name) < 0) { + VIR_WARN("unable to remove snapshot directory %s/%s", + driver->snapshotDir, vm->def->name); + } else { + if (rmdir(snapDir) < 0 && errno != ENOENT) + VIR_WARN("unable to remove snapshot directory %s", snapDir); + VIR_FREE(snapDir); + } virDomainRemoveInactive(&driver->domains, vm); } -- 1.7.4.4

On Wed, Sep 21, 2011 at 01:08:49PM -0600, Eric Blake wrote:
I intended to do this ever since I changed the API of virDomainUndefine to reject undefine of persistent guests with snapshots, but am only now getting around to it. This missed 0.9.5, but I think it is worth including in 0.9.6 even if we have a short release cycle to work around the qemu shutdown issue.
Eric Blake (3): snapshot: fix logic bug in qemu undefine snapshot: prepare to remove transient snapshot metadata snapshot: remove snapshot metadata on transient exit
src/conf/domain_conf.c | 4 +- src/qemu/qemu_domain.c | 260 ++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_domain.h | 36 ++++++ src/qemu/qemu_driver.c | 291 +++----------------------------------------- src/qemu/qemu_migration.c | 8 +- src/qemu/qemu_process.c | 10 +- 6 files changed, 327 insertions(+), 282 deletions(-)
ACK to the serie, pushed now ! I just had a small conflict on 3/4 due to a comment context aligned differently, thanks :-) Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/
participants (2)
-
Daniel Veillard
-
Eric Blake