Add an option for virsh undefine command, to remove associated storage
volumes while undefining a domain. This patch alows the user to remove
associated (libvirt managed ) storage volumes while undefining a domain.
The new option --storage for the undefine command takes a string
argument that consists of comma separated list of target or source path
of volumes to be undefined. Volumes are removed after the domain has
been successfuly undefined,
If a volume is not part of a storage pool, the user is warned to remove
the volume in question himself.
Option --wipe-storage may be specified along with this, that ensures
the image is wiped before removing.
Option --remove-all-storage enables the user to remove all storage. The
name is chosen long as the users shoudl be aware what they're about to
do.
---
Changes to v2 based on reveiw by Eric:
(
http://www.redhat.com/archives/libvir-list/2011-September/msg00481.html )
- First undefine the domain, then undefine volumes
- Add option to remove all volumes
- Fix typos and strange wordings
- Change retur value to failure if some operations on volumes fail.
tools/virsh.c | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
tools/virsh.pod | 18 +++++++
2 files changed, 161 insertions(+), 0 deletions(-)
diff --git a/tools/virsh.c b/tools/virsh.c
index d58b827..281ffa9 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -1924,6 +1924,13 @@ 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")},
+ {"storage", VSH_OT_DATA, VSH_OFLAG_NONE,
+ N_("remove associated storage volumes (comma separated list of targets "
+ "or source paths) (see domblklist)")},
+ {"remove-all-storage", VSH_OT_BOOL, 0,
+ N_("remove all associated storage volumes (use with caution)")},
+ {"wipe-storage", VSH_OT_BOOL, VSH_OFLAG_NONE,
+ N_("wipe data on the removed volumes")},
{"snapshots-metadata", VSH_OT_BOOL, 0,
N_("remove all domain snapshot metadata, if inactive")},
{NULL, 0, 0, NULL}
@@ -1940,6 +1947,9 @@ cmdUndefine(vshControl *ctl, const vshCmd *cmd)
/* User-requested actions. */
bool managed_save = vshCommandOptBool(cmd, "managed-save");
bool snapshots_metadata = vshCommandOptBool(cmd, "snapshots-metadata");
+ bool wipe_storage = vshCommandOptBool(cmd, "wipe-storage");
+ bool remove_storage = false;
+ bool remove_all_storage = vshCommandOptBool(cmd, "remove-all-storage");
/* Positive if these items exist. */
int has_managed_save = 0;
int has_snapshots_metadata = 0;
@@ -1949,6 +1959,22 @@ cmdUndefine(vshControl *ctl, const vshCmd *cmd)
bool snapshots_safe = false;
int rc = -1;
int running;
+ /* list of volumes to remove along with this domain */
+ char *volumes = NULL;
+ const char *volume;
+ char *saveptr = NULL;
+ char *def = NULL;
+ char *source = NULL;
+ char *target = NULL;
+ int i;
+
+ xmlNodePtr *vol_nodes = NULL;
+ int nvolumes = 0;
+ xmlXPathContextPtr ctxt = NULL;
+ xmlDocPtr doc = NULL;
+ virStorageVolPtr vol = NULL;
+ bool vol_del_failed = false;
+
if (managed_save) {
flags |= VIR_DOMAIN_UNDEFINE_MANAGED_SAVE;
@@ -1965,6 +1991,18 @@ cmdUndefine(vshControl *ctl, const vshCmd *cmd)
if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
return false;
+ /* check if a string that should contain list of volumes to remove is present */
+ if (vshCommandOptString(cmd, "storage", (const char **)&volumes) >
0) {
+ /* caution volumes has to be re-assigned as it would be doulbe freed */
+ volumes = vshStrdup(ctl, volumes);
+
+ if (remove_all_storage) {
+ vshError(ctl, _("Specified both --storage and
--remove-all-storage"));
+ goto cleanup;
+ }
+ remove_storage = true;
+ }
+
/* 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
@@ -2027,6 +2065,20 @@ cmdUndefine(vshControl *ctl, const vshCmd *cmd)
snapshots_safe = true;
}
+ /* Stash domain description for later use */
+ if (remove_storage || remove_all_storage) {
+ if (running) {
+ vshError(ctl, _("Storage volume deletion is supported only on stopped
domains"));
+ goto cleanup;
+ }
+
+ if (!(def = virDomainGetXMLDesc(dom, 0))) {
+ vshError(ctl, _("Could not retrieve domain XML description"));
+ goto cleanup;
+ }
+ }
+
+
/* 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
@@ -2076,9 +2128,100 @@ out:
ret = true;
} else {
vshError(ctl, _("Failed to undefine domain %s"), name);
+ goto cleanup;
+ }
+
+ /* try to undefine storage volumes associated with this domain, if it's requested
*/
+ if (remove_storage || remove_all_storage) {
+ ret = false;
+ doc = virXMLParseStringCtxt(def, _("(domain_definition)"), &ctxt);
+ if (!doc)
+ goto cleanup;
+
+ nvolumes = virXPathNodeSet("./devices/disk", ctxt, &vol_nodes);
+
+ if (nvolumes < 0)
+ goto cleanup;
+
+ for (i = 0; i < nvolumes; i++) {
+ ctxt->node = vol_nodes[i];
+ VIR_FREE(target);
+ VIR_FREE(source);
+ if (vol) {
+ virStorageVolFree(vol);
+ vol = NULL;
+ }
+
+ /* get volume source and target paths */
+ if (!(target = virXPathString("string(./target/@dev)", ctxt))) {
+ vshError(ctl, _("Failed to enumerate devices"));
+ goto cleanup;
+ }
+
+ if (!(source = virXPathString("string("
+ "./source/@file|"
+ "./source/@dir|"
+ "./source/@name|"
+ "./source/@dev)", ctxt)) &&
+ virGetLastError())
+ goto cleanup;
+
+ if (volumes) {
+ volume = strtok_r(volumes, ",", &saveptr);
+ while (volume) {
+ if (STREQ_NULLABLE(volume, target) ||
+ STREQ_NULLABLE(volume, source))
+ break;
+ volume = strtok_r(NULL, ",", &saveptr);
+ }
+ if (!volume)
+ continue;
+ }
+
+ if (!source)
+ continue;
+
+ if (!(vol = virStorageVolLookupByPath(ctl->conn, source))) {
+ vshPrint(ctl,
+ _("Storage volume '%s' is not managed by libvirt.
"
+ "Remove it manually.\n"), source);
+ virResetLastError();
+ continue;
+ }
+
+ if (wipe_storage) {
+ vshPrint(ctl, _("Wiping volume '%s' ... "), source);
+ fflush(stdout);
+ if (virStorageVolWipe(vol, 0) < 0) {
+ vshError(ctl, _("Failed! Volume not removed."));
+ vol_del_failed = true;
+ continue;
+ } else {
+ vshPrint(ctl, _("Done.\n"));
+ }
+ }
+
+ /* delete the volume */
+ if (virStorageVolDelete(vol, 0) < 0) {
+ vshError(ctl, _("Failed to remove storage volume
'%s'"),
+ source);
+ vol_del_failed = true;
+ }
+ vshPrint(ctl, _("Volume '%s' removed.\n"), source);
+ }
+
+ if (!vol_del_failed)
+ ret = true;
}
cleanup:
+ VIR_FREE(source);
+ VIR_FREE(target);
+ VIR_FREE(volumes);
+ VIR_FREE(def);
+ VIR_FREE(vol_nodes);
+ xmlFreeDoc(doc);
+ xmlXPathFreeContext(ctxt);
virDomainFree(dom);
return ret;
}
diff --git a/tools/virsh.pod b/tools/virsh.pod
index fe92714..c0b55bd 100644
--- a/tools/virsh.pod
+++ b/tools/virsh.pod
@@ -1179,6 +1179,7 @@ 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<undefine> I<domain-id> [I<--managed-save>]
[I<--snapshots-metadata>]
+[I<--storage> B<volumes> I<--wipe-storage>
I<--remove-all-storage>]
Undefine a domain. If the domain is running, this converts it to a
transient domain, without stopping it. If the domain is inactive,
@@ -1194,6 +1195,23 @@ domain. Without the flag, attempts to undefine an inactive domain
with
snapshot metadata will fail. If the domain is active, this flag is
ignored.
+The I<--storage> flag takes a parameter B<volumes>, that is a comma
separated
+list of volume target names or source paths of storage volumes to be removed
+along with the undefined domain. Volumes can be undefined and thus removed only
+on inactive domains. Volume deletion is only attempted after the domain is
+undefined; if not all of the requested volumes could be deleted, the the
+error message indicates what still remains behind. If a volume path is not
+found in the domain definition, it's treated as if the volume was successfuly
+deleteted.
+(See B<domblklist> for list of target names associated to a domain).
+Example: --storage vda,vdb,vdc
+
+The I<--remove-all-storage> flag specifies, that all domain's storage volumes
+should be deleted.
+
+The flag I<--wipe-storage> specifies that the storage volumes should be
+wiped before removal.
+
NOTE: For an inactive domain, the domain name or UUID must be used as the
I<domain-id>.
--
1.7.3.4