diff --git a/src/conf/snapshot_conf.c b/src/conf/snapshot_conf.c index 4b5b908d66..9bf3c78353 100644 --- a/src/conf/snapshot_conf.c +++ b/src/conf/snapshot_conf.c @@ -158,6 +158,11 @@ virDomainSnapshotDiskDefParseXML(xmlNodePtr node, return -1; } + if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE) { + def->snapshotDeleteInProgress = !!virXPathNode("./snapshotDeleteInProgress", + ctxt); + } + if ((cur = virXPathNode("./source", ctxt)) && virDomainStorageSourceParse(cur, ctxt, src, flags, xmlopt) < 0) return -1; @@ -744,6 +749,9 @@ virDomainSnapshotDiskDefFormat(virBuffer *buf, virBufferAsprintf(&attrBuf, " snapshot='%s'", virDomainSnapshotLocationTypeToString(disk->snapshot)); + if (disk->snapshotDeleteInProgress) + virBufferAddLit(&childBuf, "\n"); + if (disk->src->path || disk->src->format != 0) { g_auto(virBuffer) driverAttrBuf = VIR_BUFFER_INITIALIZER; g_auto(virBuffer) driverChildBuf = VIR_BUFFER_INIT_CHILD(&childBuf); diff --git a/src/conf/snapshot_conf.h b/src/conf/snapshot_conf.h index fec4a5a912..96c77ef42b 100644 --- a/src/conf/snapshot_conf.h +++ b/src/conf/snapshot_conf.h @@ -52,6 +52,7 @@ typedef struct _virDomainSnapshotDiskDef virDomainSnapshotDiskDef; struct _virDomainSnapshotDiskDef { char *name; /* name matching the snapshot == VIR_DOMAIN_SNAPSHOT_LOCATION_NO) continue; + if (snapDisk->snapshotDeleteInProgress) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("snapshot disk '%s' was target of not completed snapshot delete"), + snapDisk->name); + return NULL; + } + data = g_new0(qemuSnapshotDeleteExternalData, 1); data->snapDisk = snapDisk; @@ -2628,6 +2635,53 @@ qemuSnapshotDeleteBlockJobFinishing(virDomainObj *vm, } +/** + * qemuSnapshotSetInvalid: + * @vm: vm object + * @snap: snapshot object that contains parent disk + * @disk: disk from the snapshot we are deleting + * @invalid: boolean to set/unset invalid state + * + * @snap should point to a ancestor snapshot from the snapshot tree that + * affected the @disk which doesn't have to be the direct parent. + * + * When setting snapshot with parent disk as invalid due to snapshot being + * deleted we should not mark the whole snapshot as invalid but only the + * affected disks because the snapshot can contain other disks that we are + * not modifying at the moment. + * + * Return 0 on success, -1 on error. + * */ +static int +qemuSnapshotSetInvalid(virDomainObj *vm, + virDomainMomentObj *snap, + virDomainSnapshotDiskDef *disk, + bool invalid) +{ + ssize_t i; + qemuDomainObjPrivate *priv = vm->privateData; + virQEMUDriver *driver = priv->driver; + g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver); + virDomainSnapshotDef *snapdef = NULL; + + if (!snap) + return 0; + + snapdef = virDomainSnapshotObjGetDef(snap); + if (!snapdef) + return 0; + + for (i = 0; i < snapdef->ndisks; i++) { + virDomainSnapshotDiskDef *snapDisk = &(snapdef->disks[i]); + + if (STREQ(snapDisk->name, disk->name)) + snapDisk->snapshotDeleteInProgress = invalid; + } + + return qemuDomainSnapshotWriteMetadata(vm, snap, driver->xmlopt, cfg->snapshotDir); +} + + static int qemuSnapshotDiscardExternal(virDomainObj *vm, GSList *externalData) @@ -2644,6 +2698,9 @@ qemuSnapshotDiscardExternal(virDomainObj *vm, autofinalize = VIR_TRISTATE_BOOL_YES; } + if (qemuSnapshotSetInvalid(vm, data->parentSnap, data->snapDisk, true) < 0) + goto error; + data->job = qemuBlockCommit(vm, data->domDisk, data->parentDiskSrc, @@ -2694,6 +2751,9 @@ qemuSnapshotDiscardExternal(virDomainObj *vm, } qemuBlockJobSyncEnd(vm, data->job, VIR_ASYNC_JOB_SNAPSHOT); + + if (qemuSnapshotSetInvalid(vm, data->parentSnap, data->snapDisk, false) < 0) + goto error; } return 0;