From e88872e9a9610bd903f42b3aa9995acbe36d95be Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Thu, 11 Aug 2011 20:24:19 -0600 Subject: [PATCH] snapshot: teach virsh about new undefine flags Similar to 'undefine --managed-save' (commit 83e849c1), we must assume that the old API is unsafe; however, we cannot emulate metadata-only deletion on older servers. Additionally, we have the wrinkle that while virDomainUndefineFlags and managed save cleanup were introduced in 0.9.4, it wasn't until 0.9.5 that snapshots block undefine of a domain. Do the best we can given the server we are talking to. * tools/virsh.c (cmdUndefine): Add --snapshots-metadata flag. * tools/virsh.pod (undefine, destroy, shutdown): Document effect of snapshots. --- tools/virsh.c | 170 ++++++++++++++++++++++++++++++++++-------------- tools/virsh.pod | 25 ++++++- 2 files changed, 145 insertions(+), 50 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index fb6e5aed22..c7240e51bc 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -1434,6 +1434,8 @@ static const vshCmdInfo info_undefine[] = { static const vshCmdOptDef opts_undefine[] = { {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name or uuid")}, {"managed-save", VSH_OT_BOOL, 0, N_("remove domain managed state file")}, + {"snapshots-metadata", VSH_OT_BOOL, 0, + N_("remove all domain snapshot metadata, if inactive")}, {NULL, 0, 0, NULL} }; @@ -1441,18 +1443,31 @@ static bool cmdUndefine(vshControl *ctl, const vshCmd *cmd) { virDomainPtr dom; - bool ret = true; + bool ret = false; const char *name = NULL; + /* Flags to attempt. */ unsigned int flags = 0; - int managed_save = vshCommandOptBool(cmd, "managed-save"); + /* User-requested actions. */ + bool managed_save = vshCommandOptBool(cmd, "managed-save"); + bool snapshots_metadata = vshCommandOptBool(cmd, "snapshots-metadata"); + /* Positive if these items exist. */ int has_managed_save = 0; + int has_snapshots_metadata = 0; + int has_snapshots = 0; + /* True if undefine will not strand data, even on older servers. */ + bool managed_save_safe = false; + bool snapshots_safe = false; int rc = -1; + int running; - if (managed_save) + if (managed_save) { flags |= VIR_DOMAIN_UNDEFINE_MANAGED_SAVE; - - if (!managed_save) - flags = -1; + managed_save_safe = true; + } + if (snapshots_metadata) { + flags |= VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA; + snapshots_safe = true; + } if (!vshConnectionUsability(ctl, ctl->conn)) return false; @@ -1460,61 +1475,120 @@ cmdUndefine(vshControl *ctl, const vshCmd *cmd) if (!(dom = vshCommandOptDomain(ctl, cmd, &name))) return false; - has_managed_save = virDomainHasManagedSaveImage(dom, 0); - if (has_managed_save < 0) { - if (last_error->code != VIR_ERR_NO_SUPPORT) { - virshReportError(ctl); - virDomainFree(dom); - return false; - } else { - virFreeError(last_error); - last_error = NULL; - } + /* Do some flag manipulation. The goal here is to disable bits + * from flags to reduce the likelihood of a server rejecting + * unknown flag bits, as well as to track conditions which are + * safe by default for the given hypervisor and server version. */ + running = virDomainIsActive(dom); + if (running < 0) { + virshReportError(ctl); + goto cleanup; } - - if (flags == -1) { - if (has_managed_save == 1) { - vshError(ctl, - _("Refusing to undefine while domain managed save " - "image exists")); - virDomainFree(dom); - return false; - } - - rc = virDomainUndefine(dom); - } else { - rc = virDomainUndefineFlags(dom, flags); - - /* It might fail when virDomainUndefineFlags is not - * supported on older libvirt, try to undefine the - * domain with combo virDomainManagedSaveRemove and - * virDomainUndefine. - */ - if (rc < 0) { + if (!running) { + /* Undefine with snapshots only fails for inactive domains, + * and managed save only exists on inactive domains; if + * running, then we don't want to remove anything. */ + has_managed_save = virDomainHasManagedSaveImage(dom, 0); + if (has_managed_save < 0) { if (last_error->code != VIR_ERR_NO_SUPPORT) { virshReportError(ctl); - goto end; - } else { + goto cleanup; + } + virFreeError(last_error); + last_error = NULL; + has_managed_save = 0; + } + + has_snapshots = virDomainSnapshotNum(dom, 0); + if (has_snapshots < 0) { + if (last_error->code != VIR_ERR_NO_SUPPORT) { + virshReportError(ctl); + goto cleanup; + } + virFreeError(last_error); + last_error = NULL; + has_snapshots = 0; + } + if (has_snapshots) { + has_snapshots_metadata + = virDomainSnapshotNum(dom, VIR_DOMAIN_SNAPSHOT_LIST_METADATA); + if (has_snapshots_metadata < 0) { + /* The server did not know the new flag, assume that all + snapshots have metadata. */ virFreeError(last_error); last_error = NULL; + has_snapshots_metadata = has_snapshots; + } else { + /* The server knew the new flag, all aspects of + * undefineFlags are safe. */ + managed_save_safe = snapshots_safe = true; } + } + } + if (!has_managed_save) { + flags &= ~VIR_DOMAIN_UNDEFINE_MANAGED_SAVE; + managed_save_safe = true; + } + if (has_snapshots == 0) { + snapshots_safe = true; + } + if (has_snapshots_metadata == 0) { + flags &= ~VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA; + snapshots_safe = true; + } - if ((has_managed_save == 1) && - virDomainManagedSaveRemove(dom, 0) < 0) - goto end; + /* Generally we want to try the new API first. However, while + * virDomainUndefineFlags was introduced at the same time as + * VIR_DOMAIN_UNDEFINE_MANAGED_SAVE in 0.9.4, the + * VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA flag was not present + * until 0.9.5; skip to piecewise emulation if we couldn't prove + * above that the new API is safe. */ + if (managed_save_safe && snapshots_safe) { + rc = virDomainUndefineFlags(dom, flags); + if (rc == 0 || (last_error->code != VIR_ERR_NO_SUPPORT && + last_error->code != VIR_ERR_INVALID_ARG)) + goto out; + virFreeError(last_error); + last_error = NULL; + } - rc = virDomainUndefine(dom); + /* The new API is unsupported or unsafe; fall back to doing things + * piecewise. */ + if (has_managed_save) { + if (!managed_save) { + vshError(ctl, "%s", + _("Refusing to undefine while domain managed save " + "image exists")); + goto cleanup; + } + if (virDomainManagedSaveRemove(dom, 0) < 0) { + virshReportError(ctl); + goto cleanup; } } -end: - if (rc == 0) { - vshPrint(ctl, _("Domain %s has been undefined\n"), name); - } else { - vshError(ctl, _("Failed to undefine domain %s"), name); - ret = false; + /* No way to emulate deletion of just snapshot metadata + * without support for the newer flags. Oh well. */ + if (has_snapshots_metadata) { + vshError(ctl, + snapshots_metadata ? + _("Unable to remove metadata of %d snapshots") : + _("Refusing to undefine while %d snapshots exist"), + has_snapshots_metadata); + goto cleanup; } + rc = virDomainUndefine(dom); + +out: + if (rc == 0) { + vshPrint(ctl, _("Domain %s has been undefined\n"), name); + ret = true; + } else { + vshError(ctl, _("Failed to undefine domain %s"), name); + } + +cleanup: virDomainFree(dom); return ret; } diff --git a/tools/virsh.pod b/tools/virsh.pod index 07439a88ba..30c0721ad3 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -496,6 +496,11 @@ the B command instead. However, this does not delete any storage volumes used by the guest, and if the domain is persistent, it can be restarted later. +If I is transient, then the metadata of any snapshots will +be lost once the guest stops running, but the snapshot contents still +exist, and a new domain with the same name and UUID can restore the +snapshot metadata with B. + =item B I I Get device block stats for a running domain. @@ -998,6 +1003,11 @@ services must be shutdown in the domain. The exact behavior of a domain when it shuts down is set by the I parameter in the domain's XML definition. +If I is transient, then the metadata of any snapshots will +be lost once the guest stops running, but the snapshot contents still +exist, and a new domain with the same name and UUID can restore the +snapshot metadata with B. + =item B I [I<--console>] [I<--paused>] [I<--autodestroy>] [I<--bypass-cache>] [I<--force-boot>] @@ -1029,16 +1039,22 @@ hypervisor. Output the device used for the TTY console of the domain. If the information is not available the processes will provide an exit code of 1. -=item B I [I<--managed-save>] +=item B I [I<--managed-save>] [I<--snapshots-metadata] Undefine a domain. If the domain is running, this converts it to a transient domain, without stopping it. If the domain is inactive, the domain configuration is removed. -The I<--managed-save> flag guarantees that any managed save image(see +The I<--managed-save> flag guarantees that any managed save image (see the B command) is also cleaned up. Without the flag, attempts to undefine a domain with a managed save image will fail. +The I<--snapshots-metadata> flag guarantees that any snapshots (see the +B command) are also cleaned up when undefining an inactive +domain. Without the flag, attempts to undefine an inactive domain with +snapshot metadata will fail. If the domain is active, this flag is +ignored. + NOTE: For an inactive domain, the domain name or UUID must be used as the I. @@ -1711,6 +1727,11 @@ treat the snapshot as current, and cannot revert to the snapshot unless I<--redefine> is later used to teach libvirt about the metadata again). +Existence of snapshot metadata will prevent attempts to B +a persistent domain. However, for transient domains, snapshot +metadata is silently lost when the domain quits running (whether +by command such as B or by internal guest action). + =item B I {[I<--print-xml>] | [I<--no-metadata>]} [I] [I]