mirror of https://gitee.com/openkylin/libvirt.git
virsh: Add option to undefine storage with domains
Add an option for virsh undefine command, to remove associated storage volumes while undefining a domain. This patch allows 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 successfully 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 should be aware what they're about to do.
This commit is contained in:
parent
daa8c96233
commit
3bb6bcfc79
180
tools/virsh.c
180
tools/virsh.c
|
@ -1924,6 +1924,13 @@ static const vshCmdInfo info_undefine[] = {
|
||||||
static const vshCmdOptDef opts_undefine[] = {
|
static const vshCmdOptDef opts_undefine[] = {
|
||||||
{"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name or uuid")},
|
{"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name or uuid")},
|
||||||
{"managed-save", VSH_OT_BOOL, 0, N_("remove domain managed state file")},
|
{"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,
|
{"snapshots-metadata", VSH_OT_BOOL, 0,
|
||||||
N_("remove all domain snapshot metadata, if inactive")},
|
N_("remove all domain snapshot metadata, if inactive")},
|
||||||
{NULL, 0, 0, NULL}
|
{NULL, 0, 0, NULL}
|
||||||
|
@ -1940,6 +1947,9 @@ cmdUndefine(vshControl *ctl, const vshCmd *cmd)
|
||||||
/* User-requested actions. */
|
/* User-requested actions. */
|
||||||
bool managed_save = vshCommandOptBool(cmd, "managed-save");
|
bool managed_save = vshCommandOptBool(cmd, "managed-save");
|
||||||
bool snapshots_metadata = vshCommandOptBool(cmd, "snapshots-metadata");
|
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. */
|
/* Positive if these items exist. */
|
||||||
int has_managed_save = 0;
|
int has_managed_save = 0;
|
||||||
int has_snapshots_metadata = 0;
|
int has_snapshots_metadata = 0;
|
||||||
|
@ -1949,6 +1959,23 @@ cmdUndefine(vshControl *ctl, const vshCmd *cmd)
|
||||||
bool snapshots_safe = false;
|
bool snapshots_safe = false;
|
||||||
int rc = -1;
|
int rc = -1;
|
||||||
int running;
|
int running;
|
||||||
|
/* list of volumes to remove along with this domain */
|
||||||
|
const char *volumes_arg = NULL;
|
||||||
|
char *volumes = NULL;
|
||||||
|
char **volume_tokens = NULL;
|
||||||
|
char *volume_tok = NULL;
|
||||||
|
int nvolume_tokens = 0;
|
||||||
|
char *def = NULL;
|
||||||
|
char *source = NULL;
|
||||||
|
char *target = NULL;
|
||||||
|
int vol_i;
|
||||||
|
int tok_i;
|
||||||
|
xmlDocPtr doc = NULL;
|
||||||
|
xmlXPathContextPtr ctxt = NULL;
|
||||||
|
xmlNodePtr *vol_nodes = NULL;
|
||||||
|
int nvolumes = 0;
|
||||||
|
virStorageVolPtr vol = NULL;
|
||||||
|
bool vol_del_failed = false;
|
||||||
|
|
||||||
if (managed_save) {
|
if (managed_save) {
|
||||||
flags |= VIR_DOMAIN_UNDEFINE_MANAGED_SAVE;
|
flags |= VIR_DOMAIN_UNDEFINE_MANAGED_SAVE;
|
||||||
|
@ -1965,6 +1992,17 @@ cmdUndefine(vshControl *ctl, const vshCmd *cmd)
|
||||||
if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
|
if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
/* check if a string that should contain list of volumes to remove is present */
|
||||||
|
if (vshCommandOptString(cmd, "storage", &volumes_arg) > 0) {
|
||||||
|
volumes = vshStrdup(ctl, volumes_arg);
|
||||||
|
|
||||||
|
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
|
/* Do some flag manipulation. The goal here is to disable bits
|
||||||
* from flags to reduce the likelihood of a server rejecting
|
* from flags to reduce the likelihood of a server rejecting
|
||||||
* unknown flag bits, as well as to track conditions which are
|
* unknown flag bits, as well as to track conditions which are
|
||||||
|
@ -2027,6 +2065,19 @@ cmdUndefine(vshControl *ctl, const vshCmd *cmd)
|
||||||
snapshots_safe = true;
|
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
|
/* Generally we want to try the new API first. However, while
|
||||||
* virDomainUndefineFlags was introduced at the same time as
|
* virDomainUndefineFlags was introduced at the same time as
|
||||||
* VIR_DOMAIN_UNDEFINE_MANAGED_SAVE in 0.9.4, the
|
* VIR_DOMAIN_UNDEFINE_MANAGED_SAVE in 0.9.4, the
|
||||||
|
@ -2076,9 +2127,138 @@ out:
|
||||||
ret = true;
|
ret = true;
|
||||||
} else {
|
} else {
|
||||||
vshError(ctl, _("Failed to undefine domain %s"), name);
|
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;
|
||||||
|
|
||||||
|
/* tokenize the string from user and save it's parts into an array */
|
||||||
|
if (volumes) {
|
||||||
|
/* count the delimiters */
|
||||||
|
volume_tok = volumes;
|
||||||
|
nvolume_tokens = 1; /* we need at least one member */
|
||||||
|
while (*volume_tok) {
|
||||||
|
if (*volume_tok == ',')
|
||||||
|
nvolume_tokens++;
|
||||||
|
volume_tok++;
|
||||||
|
}
|
||||||
|
|
||||||
|
volume_tokens = vshCalloc(ctl, nvolume_tokens, sizeof(char *));
|
||||||
|
|
||||||
|
/* tokenize the input string */
|
||||||
|
nvolume_tokens = 0;
|
||||||
|
volume_tok = volumes;
|
||||||
|
do {
|
||||||
|
volume_tokens[nvolume_tokens] = strsep(&volume_tok, ",");
|
||||||
|
nvolume_tokens++;
|
||||||
|
} while (volume_tok);
|
||||||
|
}
|
||||||
|
|
||||||
|
doc = virXMLParseStringCtxt(def, _("(domain_definition)"), &ctxt);
|
||||||
|
if (!doc)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
nvolumes = virXPathNodeSet("./devices/disk", ctxt, &vol_nodes);
|
||||||
|
|
||||||
|
if (nvolumes < 0)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
for (vol_i = 0; vol_i < nvolumes; vol_i++) {
|
||||||
|
ctxt->node = vol_nodes[vol_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;
|
||||||
|
|
||||||
|
/* lookup if volume was selected by user */
|
||||||
|
if (volumes) {
|
||||||
|
volume_tok = NULL;
|
||||||
|
for (tok_i = 0; tok_i < nvolume_tokens; tok_i++) {
|
||||||
|
if (volume_tokens[tok_i] &&
|
||||||
|
(STREQ_NULLABLE(volume_tokens[tok_i], target) ||
|
||||||
|
STREQ_NULLABLE(volume_tokens[tok_i], source))) {
|
||||||
|
volume_tok = volume_tokens[tok_i];
|
||||||
|
volume_tokens[tok_i] = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!volume_tok)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!source)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!(vol = virStorageVolLookupByPath(ctl->conn, source))) {
|
||||||
|
vshPrint(ctl,
|
||||||
|
_("Storage volume '%s'(%s) is not managed by libvirt. "
|
||||||
|
"Remove it manually.\n"), target, source);
|
||||||
|
virResetLastError();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wipe_storage) {
|
||||||
|
vshPrint(ctl, _("Wiping volume '%s'(%s) ... "), target, 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'(%s)"),
|
||||||
|
target, source);
|
||||||
|
vol_del_failed = true;
|
||||||
|
}
|
||||||
|
vshPrint(ctl, _("Volume '%s' removed.\n"), volume_tok?volume_tok:source);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* print volumes specified by user that were not found in domain definition */
|
||||||
|
if (volumes) {
|
||||||
|
for (tok_i = 0; tok_i < nvolume_tokens; tok_i++) {
|
||||||
|
if (volume_tokens[tok_i])
|
||||||
|
vshPrint(ctl, _("Volume '%s' was not found in domain's "
|
||||||
|
"definition.\n"),
|
||||||
|
volume_tokens[tok_i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!vol_del_failed)
|
||||||
|
ret = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
|
VIR_FREE(source);
|
||||||
|
VIR_FREE(target);
|
||||||
|
VIR_FREE(volumes);
|
||||||
|
VIR_FREE(volume_tokens);
|
||||||
|
VIR_FREE(def);
|
||||||
|
VIR_FREE(vol_nodes);
|
||||||
|
xmlFreeDoc(doc);
|
||||||
|
xmlXPathFreeContext(ctxt);
|
||||||
virDomainFree(dom);
|
virDomainFree(dom);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1181,6 +1181,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.
|
is not available the processes will provide an exit code of 1.
|
||||||
|
|
||||||
=item B<undefine> I<domain-id> [I<--managed-save>] [I<--snapshots-metadata>]
|
=item B<undefine> I<domain-id> [I<--managed-save>] [I<--snapshots-metadata>]
|
||||||
|
[ {I<--storage> B<volumes> | I<--remove-all-storage>} I<--wipe-storage>]
|
||||||
|
|
||||||
Undefine a domain. If the domain is running, this converts it to a
|
Undefine a domain. If the domain is running, this converts it to a
|
||||||
transient domain, without stopping it. If the domain is inactive,
|
transient domain, without stopping it. If the domain is inactive,
|
||||||
|
@ -1196,6 +1197,23 @@ domain. Without the flag, attempts to undefine an inactive domain with
|
||||||
snapshot metadata will fail. If the domain is active, this flag is
|
snapshot metadata will fail. If the domain is active, this flag is
|
||||||
ignored.
|
ignored.
|
||||||
|
|
||||||
|
The I<--storage> flag takes a parameter B<volumes>, which 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
|
||||||
|
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 successfully
|
||||||
|
deleted.
|
||||||
|
(See B<domblklist> for list of target names associated to a domain).
|
||||||
|
Example: --storage vda,/path/to/storage.img
|
||||||
|
|
||||||
|
The I<--remove-all-storage> flag specifies that all of the 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
|
NOTE: For an inactive domain, the domain name or UUID must be used as the
|
||||||
I<domain-id>.
|
I<domain-id>.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue