Signed-off-by: Nikolay Shirokovskiy <nshirokovskiy@xxxxxxxxxxxxx> Signed-off-by: Maxim Nestratov <mnestratov@xxxxxxxxxxxxx> --- po/POTFILES.in | 2 + tools/Makefile.am | 2 + tools/virsh-fsitem.c | 1292 ++++++++++++++++++++++++++++++++++++++++ tools/virsh-fsitem.h | 39 ++ tools/virsh-fspool.c | 1586 ++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh-fspool.h | 38 ++ tools/virsh.c | 4 + tools/virsh.h | 9 + tools/virsh.pod | 252 +++++++- 9 files changed, 3223 insertions(+), 1 deletion(-) create mode 100644 tools/virsh-fsitem.c create mode 100644 tools/virsh-fsitem.h create mode 100644 tools/virsh-fspool.c create mode 100644 tools/virsh-fspool.h diff --git a/po/POTFILES.in b/po/POTFILES.in index 0fc3b79..5ae945b 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -289,8 +289,10 @@ tools/virsh-console.c tools/virsh-domain-monitor.c tools/virsh-domain.c tools/virsh-edit.c +tools/virsh-fspool.c tools/virsh-host.c tools/virsh-interface.c +tools/virsh-fsitem.c tools/virsh-network.c tools/virsh-nodedev.c tools/virsh-nwfilter.c diff --git a/tools/Makefile.am b/tools/Makefile.am index e7e42c3..5891bdf 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -198,9 +198,11 @@ virsh_SOURCES = \ virsh-nodedev.c virsh-nodedev.h \ virsh-nwfilter.c virsh-nwfilter.h \ virsh-pool.c virsh-pool.h \ + virsh-fspool.c virsh-fspool.h \ virsh-secret.c virsh-secret.h \ virsh-snapshot.c virsh-snapshot.h \ virsh-volume.c virsh-volume.h \ + virsh-fsitem.c virsh-fsitem.h \ $(NULL) virsh_LDFLAGS = \ diff --git a/tools/virsh-fsitem.c b/tools/virsh-fsitem.c new file mode 100644 index 0000000..53f373f --- /dev/null +++ b/tools/virsh-fsitem.c @@ -0,0 +1,1292 @@ +/* + * virsh-fsitem.c: Commands to manage storage item + * + * Copyright (C) 2016 Parallels IP Holdings GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Olga Krishtal <okrishtal@xxxxxxxxxxxxx> + * + */ +#include <config.h> +#include "virsh-fsitem.h" + +#include <fcntl.h> + +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/xpath.h> +#include <libxml/xmlsave.h> + +#include "internal.h" +#include "virbuffer.h" +#include "viralloc.h" +#include "virutil.h" +#include "virfile.h" +#include "virsh-fspool.h" +#include "virxml.h" +#include "virstring.h" + +#define VIRSH_COMMON_OPT_FSPOOL_FULL \ + VIRSH_COMMON_OPT_FSPOOL(N_("fspool name or uuid")) \ + +#define VIRSH_COMMON_OPT_FSPOOL_NAME \ + VIRSH_COMMON_OPT_FSPOOL(N_("fspool name")) \ + +#define VIRSH_COMMON_OPT_FSPOOL_OPTIONAL \ + {.name = "fspool", \ + .type = VSH_OT_STRING, \ + .help = N_("fspool name or uuid") \ + } \ + +#define VIRSH_COMMON_OPT_ITEM \ + {.name = "item", \ + .type = VSH_OT_DATA, \ + .flags = VSH_OFLAG_REQ, \ + .help = N_("item name, key or path") \ + } \ + +virFSItemPtr +virshCommandOptItemBy(vshControl *ctl, const vshCmd *cmd, + const char *optname, + const char *fspooloptname, + const char **name, unsigned int flags) +{ + virFSItemPtr item = NULL; + virFSPoolPtr fspool = NULL; + const char *n = NULL, *p = NULL; + virshControlPtr priv = ctl->privData; + + virCheckFlags(VIRSH_BYUUID | VIRSH_BYNAME, NULL); + + if (vshCommandOptStringReq(ctl, cmd, optname, &n) < 0) + return NULL; + + if (fspooloptname != NULL && + vshCommandOptStringReq(ctl, cmd, fspooloptname, &p) < 0) + return NULL; + + if (p) { + if (!(fspool = virshCommandOptFSPoolBy(ctl, cmd, fspooloptname, name, flags))) + return NULL; + + if (virFSPoolIsActive(fspool) != 1) { + vshError(ctl, _("fspool '%s' is not active"), p); + virFSPoolFree(fspool); + return NULL; + } + } + + vshDebug(ctl, VSH_ERR_DEBUG, "%s: found option <%s>: %s\n", + cmd->def->name, optname, n); + + if (name) + *name = n; + + /* try it by name */ + if (fspool && (flags & VIRSH_BYNAME)) { + vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as item name\n", + cmd->def->name, optname); + item = virFSItemLookupByName(fspool, n); + } + /* try it by key */ + if (!item && (flags & VIRSH_BYUUID)) { + vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as item key\n", + cmd->def->name, optname); + item = virFSItemLookupByKey(priv->conn, n); + } + /* try it by path */ + if (!item && (flags & VIRSH_BYUUID)) { + vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as item path\n", + cmd->def->name, optname); + item = virFSItemLookupByPath(priv->conn, n); + } + + if (!item) { + if (fspool || !fspooloptname) + vshError(ctl, _("failed to get item '%s'"), n); + else + vshError(ctl, _("failed to get item '%s', specifying --%s " + "might help"), n, fspooloptname); + } + + /* If the fspool was specified, then make sure that the returned + * item is from the given fspool */ + if (fspool && item) { + virFSPoolPtr itemfspool = NULL; + + if ((itemfspool = virFSPoolLookupByItem(item))) { + if (STRNEQ(virFSPoolGetName(itemfspool), + virFSPoolGetName(fspool))) { + vshResetLibvirtError(); + vshError(ctl, + _("Requested item '%s' is not in fspool '%s'"), + n, virFSPoolGetName(fspool)); + virFSItemFree(item); + item = NULL; + } + virFSPoolFree(itemfspool); + } + } + + if (fspool) + virFSPoolFree(fspool); + + return item; +} + +/* + * "item-create-as" command + */ +static const vshCmdInfo info_item_create_as[] = { + {.name = "help", + .data = N_("create a item from a set of args") + }, + {.name = "desc", + .data = N_("Create a item.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_item_create_as[] = { + VIRSH_COMMON_OPT_FSPOOL_NAME, + {.name = "name", + .type = VSH_OT_DATA, + .flags = VSH_OFLAG_REQ, + .help = N_("name of the item") + }, + {.name = "capacity", + .type = VSH_OT_DATA, + .flags = VSH_OFLAG_REQ, + .help = N_("size of the item, as scaled integer (default bytes)") + }, + {.name = "allocation", + .type = VSH_OT_STRING, + .help = N_("initial allocation size, as scaled integer (default bytes)") + }, + {.name = "format", + .type = VSH_OT_STRING, + .help = N_("file format type raw,bochs,qcow,qcow2,qed,vmdk") + }, + {.name = "print-xml", + .type = VSH_OT_BOOL, + .help = N_("print XML document, but don't define/create") + }, + {.name = NULL} +}; + +static int +virshItemSize(const char *data, unsigned long long *val) +{ + char *end; + if (virStrToLong_ull(data, &end, 10, val) < 0) + return -1; + return virScaleInteger(val, end, 1, ULLONG_MAX); +} + +static bool +cmdItemCreateAs(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr fspool; + virFSItemPtr item = NULL; + char *xml = NULL; + bool printXML = vshCommandOptBool(cmd, "print-xml"); + const char *name, *capacityStr = NULL, *allocationStr = NULL, *format = NULL; + unsigned long long capacity, allocation = 0; + virBuffer buf = VIR_BUFFER_INITIALIZER; + unsigned long flags = 0; + bool ret = false; + + if (!(fspool = virshCommandOptFSPool(ctl, cmd, "fspool", NULL))) + return false; + + if (vshCommandOptStringReq(ctl, cmd, "name", &name) < 0) + goto cleanup; + + if (vshCommandOptStringReq(ctl, cmd, "capacity", &capacityStr) < 0) + goto cleanup; + + if (virshItemSize(capacityStr, &capacity) < 0) { + vshError(ctl, _("Malformed size %s"), capacityStr); + goto cleanup; + } + + if (vshCommandOptStringQuiet(ctl, cmd, "allocation", &allocationStr) > 0 && + virshItemSize(allocationStr, &allocation) < 0) { + vshError(ctl, _("Malformed size %s"), allocationStr); + goto cleanup; + } + + if (vshCommandOptStringReq(ctl, cmd, "format", &format)) + goto cleanup; + + virBufferAddLit(&buf, "<item>\n"); + virBufferAdjustIndent(&buf, 2); + virBufferAsprintf(&buf, "<name>%s</name>\n", name); + virBufferAsprintf(&buf, "<capacity>%llu</capacity>\n", capacity); + if (allocationStr) + virBufferAsprintf(&buf, "<allocation>%llu</allocation>\n", allocation); + + if (format) { + virBufferAddLit(&buf, "<target>\n"); + virBufferAdjustIndent(&buf, 2); + virBufferAsprintf(&buf, "<format type='%s'/>\n", format); + virBufferAdjustIndent(&buf, -2); + virBufferAddLit(&buf, "</target>\n"); + } + + virBufferAdjustIndent(&buf, -2); + virBufferAddLit(&buf, "</item>\n"); + + if (virBufferError(&buf)) { + vshPrint(ctl, "%s", _("Failed to allocate XML buffer")); + goto cleanup; + } + xml = virBufferContentAndReset(&buf); + + if (printXML) { + vshPrint(ctl, "%s", xml); + } else { + if (!(item = virFSItemCreateXML(fspool, xml, flags))) { + vshError(ctl, _("Failed to create item %s"), name); + goto cleanup; + } + vshPrint(ctl, _("Item %s created\n"), name); + } + + ret = true; + + cleanup: + virBufferFreeAndReset(&buf); + if (item) + virFSItemFree(item); + virFSPoolFree(fspool); + VIR_FREE(xml); + return ret; +} + +/* + * "item-create" command + */ +static const vshCmdInfo info_item_create[] = { + {.name = "help", + .data = N_("create a item from an XML file") + }, + {.name = "desc", + .data = N_("Create a item.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_item_create[] = { + VIRSH_COMMON_OPT_FSPOOL_NAME, + VIRSH_COMMON_OPT_FILE(N_("file containing an XML item description")), + {.name = NULL} +}; + +static bool +cmdItemCreate(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr fspool; + virFSItemPtr item; + const char *from = NULL; + bool ret = false; + unsigned int flags = 0; + char *buffer = NULL; + + if (!(fspool = virshCommandOptFSPool(ctl, cmd, "fspool", NULL))) + return false; + + if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0) + goto cleanup; + + if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0) { + vshSaveLibvirtError(); + goto cleanup; + } + + if ((item = virFSItemCreateXML(fspool, buffer, flags))) { + vshPrint(ctl, _("Item %s created from %s\n"), + virFSItemGetName(item), from); + virFSItemFree(item); + ret = true; + } else { + vshError(ctl, _("Failed to create item from %s"), from); + } + + cleanup: + VIR_FREE(buffer); + virFSPoolFree(fspool); + return ret; +} + +/* + * "item-create-from" command + */ +static const vshCmdInfo info_item_create_from[] = { + {.name = "help", + .data = N_("create a item, using another item as input") + }, + {.name = "desc", + .data = N_("Create a item from an existing item.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_item_create_from[] = { + VIRSH_COMMON_OPT_FSPOOL_FULL, + VIRSH_COMMON_OPT_FILE(N_("file containing an XML item description")), + VIRSH_COMMON_OPT_ITEM, + {.name = "inputfspool", + .type = VSH_OT_STRING, + .help = N_("fspool name or uuid of the input item's fspool") + }, + {.name = NULL} +}; + +static bool +cmdItemCreateFrom(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr fspool = NULL; + virFSItemPtr newitem = NULL, inputitem = NULL; + const char *from = NULL; + bool ret = false; + char *buffer = NULL; + unsigned int flags = 0; + + if (!(fspool = virshCommandOptFSPool(ctl, cmd, "fspool", NULL))) + goto cleanup; + + if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0) + goto cleanup; + + if (!(inputitem = virshCommandOptItem(ctl, cmd, "item", "inputfspool", NULL))) + goto cleanup; + + if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0) { + vshReportError(ctl); + goto cleanup; + } + + newitem = virFSItemCreateXMLFrom(fspool, buffer, inputitem, flags); + + if (newitem != NULL) { + vshPrint(ctl, _("Item %s created from input item %s\n"), + virFSItemGetName(newitem), virFSItemGetName(inputitem)); + } else { + vshError(ctl, _("Failed to create item from %s"), from); + goto cleanup; + } + + ret = true; + cleanup: + VIR_FREE(buffer); + if (fspool) + virFSPoolFree(fspool); + if (inputitem) + virFSItemFree(inputitem); + if (newitem) + virFSItemFree(newitem); + return ret; +} + +static xmlChar * +virshMakeCloneXML(const char *origxml, const char *newname) +{ + + xmlDocPtr doc = NULL; + xmlXPathContextPtr ctxt = NULL; + xmlXPathObjectPtr obj = NULL; + xmlChar *newxml = NULL; + int size; + + doc = virXMLParseStringCtxt(origxml, _("(item_definition)"), &ctxt); + if (!doc) + goto cleanup; + + obj = xmlXPathEval(BAD_CAST "/item/name", ctxt); + if (obj == NULL || obj->nodesetval == NULL || + obj->nodesetval->nodeTab == NULL) + goto cleanup; + + xmlNodeSetContent(obj->nodesetval->nodeTab[0], (const xmlChar *)newname); + xmlDocDumpMemory(doc, &newxml, &size); + + cleanup: + xmlXPathFreeObject(obj); + xmlXPathFreeContext(ctxt); + xmlFreeDoc(doc); + return newxml; +} + +/* + * "item-clone" command + */ +static const vshCmdInfo info_item_clone[] = { + {.name = "help", + .data = N_("clone a item.") + }, + {.name = "desc", + .data = N_("Clone an existing item within the parent fspool.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_item_clone[] = { + VIRSH_COMMON_OPT_ITEM, + {.name = "newname", + .type = VSH_OT_DATA, + .flags = VSH_OFLAG_REQ, + .help = N_("clone name") + }, + {.name = NULL} +}; + +static bool +cmdItemClone(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr origfspool = NULL; + virFSItemPtr origitem = NULL, newitem = NULL; + const char *name = NULL; + char *origxml = NULL; + xmlChar *newxml = NULL; + bool ret = false; + unsigned int flags = 0; + + if (!(origitem = virshCommandOptItem(ctl, cmd, "item", "fspool", NULL))) + goto cleanup; + + origfspool = virFSPoolLookupByItem(origitem); + if (!origfspool) { + vshError(ctl, "%s", _("failed to get parent fspool")); + goto cleanup; + } + + if (vshCommandOptStringReq(ctl, cmd, "newname", &name) < 0) + goto cleanup; + + origxml = virFSItemGetXMLDesc(origitem, 0); + if (!origxml) + goto cleanup; + + newxml = virshMakeCloneXML(origxml, name); + if (!newxml) { + vshPrint(ctl, "%s", _("Failed to allocate XML buffer")); + goto cleanup; + } + + newitem = virFSItemCreateXMLFrom(origfspool, (char *) newxml, origitem, flags); + + if (newitem != NULL) { + vshPrint(ctl, _("Item %s cloned from %s\n"), + virFSItemGetName(newitem), virFSItemGetName(origitem)); + } else { + vshError(ctl, _("Failed to clone item from %s"), + virFSItemGetName(origitem)); + goto cleanup; + } + + ret = true; + + cleanup: + VIR_FREE(origxml); + xmlFree(newxml); + if (origitem) + virFSItemFree(origitem); + if (newitem) + virFSItemFree(newitem); + if (origfspool) + virFSPoolFree(origfspool); + return ret; +} + +/* + * "item-delete" command + */ +static const vshCmdInfo info_item_delete[] = { + {.name = "help", + .data = N_("delete a item") + }, + {.name = "desc", + .data = N_("Delete a given item.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_item_delete[] = { + VIRSH_COMMON_OPT_ITEM, + VIRSH_COMMON_OPT_FSPOOL_OPTIONAL, + {.name = NULL} +}; + +static bool +cmdItemDelete(vshControl *ctl, const vshCmd *cmd) +{ + virFSItemPtr item; + bool ret = true; + const char *name; + unsigned int flags = 0; + + if (!(item = virshCommandOptItem(ctl, cmd, "item", "fspool", &name))) + return false; + + if (virFSItemDelete(item, flags) == 0) { + vshPrint(ctl, _("Item %s deleted\n"), name); + } else { + vshError(ctl, _("Failed to delete item %s"), name); + ret = false; + } + + virFSItemFree(item); + return ret; +} + +VIR_ENUM_DECL(virshFSItem) +VIR_ENUM_IMPL(virshFSItem, + VIR_FSITEM_LAST, + N_("dir")) + +static const char * +virshItemTypeToString(int type) +{ + const char *str = virshFSItemTypeToString(type); + return str ? _(str) : _("unknown"); +} + + +/* + * "item-info" command + */ +static const vshCmdInfo info_item_info[] = { + {.name = "help", + .data = N_("storage item information") + }, + {.name = "desc", + .data = N_("Returns basic information about the storage item.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_item_info[] = { + VIRSH_COMMON_OPT_ITEM, + VIRSH_COMMON_OPT_FSPOOL_OPTIONAL, + {.name = "bytes", + .type = VSH_OT_BOOL, + .help = N_("sizes are represented in bytes rather than pretty units") + }, + {.name = NULL} +}; + +static bool +cmdItemInfo(vshControl *ctl, const vshCmd *cmd) +{ + virFSItemInfo info; + virFSItemPtr item; + bool bytes = vshCommandOptBool(cmd, "bytes"); + bool ret = true; + + if (!(item = virshCommandOptItem(ctl, cmd, "item", "fspool", NULL))) + return false; + + vshPrint(ctl, "%-15s %s\n", _("Name:"), virFSItemGetName(item)); + + if (virFSItemGetInfo(item, &info) == 0) { + double val; + const char *unit; + + vshPrint(ctl, "%-15s %s\n", _("Type:"), + virshItemTypeToString(info.type)); + + if (bytes) { + vshPrint(ctl, "%-15s %llu %s\n", _("Capacity:"), + info.capacity, _("bytes")); + } else { + val = vshPrettyCapacity(info.capacity, &unit); + vshPrint(ctl, "%-15s %2.2lf %s\n", _("Capacity:"), val, unit); + } + + if (bytes) { + vshPrint(ctl, "%-15s %llu %s\n", _("Allocation:"), + info.allocation, _("bytes")); + } else { + val = vshPrettyCapacity(info.allocation, &unit); + vshPrint(ctl, "%-15s %2.2lf %s\n", _("Allocation:"), val, unit); + } + } else { + ret = false; + } + + virFSItemFree(item); + return ret; +} + +/* + * "item-dumpxml" command + */ +static const vshCmdInfo info_item_dumpxml[] = { + {.name = "help", + .data = N_("item information in XML") + }, + {.name = "desc", + .data = N_("Output the item information as an XML dump to stdout.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_item_dumpxml[] = { + VIRSH_COMMON_OPT_ITEM, + VIRSH_COMMON_OPT_FSPOOL_OPTIONAL, + {.name = NULL} +}; + +static bool +cmdItemDumpXML(vshControl *ctl, const vshCmd *cmd) +{ + virFSItemPtr item; + bool ret = true; + char *dump; + + if (!(item = virshCommandOptItem(ctl, cmd, "item", "fspool", NULL))) + return false; + + dump = virFSItemGetXMLDesc(item, 0); + if (dump != NULL) { + vshPrint(ctl, "%s", dump); + VIR_FREE(dump); + } else { + ret = false; + } + + virFSItemFree(item); + return ret; +} + +static int +virshFSItemSorter(const void *a, const void *b) +{ + virFSItemPtr *va = (virFSItemPtr *) a; + virFSItemPtr *vb = (virFSItemPtr *) b; + + if (*va && !*vb) + return -1; + + if (!*va) + return *vb != NULL; + + return vshStrcasecmp(virFSItemGetName(*va), + virFSItemGetName(*vb)); +} + +struct virshFSItemList { + virFSItemPtr *items; + size_t nitems; +}; +typedef struct virshFSItemList *virshFSItemListPtr; + +static void +virshFSItemListFree(virshFSItemListPtr list) +{ + size_t i; + + if (list && list->items) { + for (i = 0; i < list->nitems; i++) { + if (list->items[i]) + virFSItemFree(list->items[i]); + } + VIR_FREE(list->items); + } + VIR_FREE(list); +} + +static virshFSItemListPtr +virshFSItemListCollect(vshControl *ctl, + virFSPoolPtr fspool, + unsigned int flags) +{ + virshFSItemListPtr list = vshMalloc(ctl, sizeof(*list)); + size_t i; + char **names = NULL; + virFSItemPtr item = NULL; + bool success = false; + size_t deleted = 0; + int nitems = 0; + int ret = -1; + + /* try the list with flags support (0.10.2 and later) */ + if ((ret = virFSPoolListAllItems(fspool, + &list->items, + flags)) >= 0) { + list->nitems = ret; + goto finished; + } + + /* check if the command is actually supported */ + if (last_error && last_error->code == VIR_ERR_NO_SUPPORT) + goto fallback; + + /* there was an error during the call */ + vshError(ctl, "%s", _("Failed to list items")); + goto cleanup; + + fallback: + /* fall back to old method (0.10.1 and older) */ + vshResetLibvirtError(); + + /* Determine the number of items in the fspool */ + if ((nitems = virFSPoolNumOfItems(fspool)) < 0) { + vshError(ctl, "%s", _("Failed to list storage items")); + goto cleanup; + } + + if (nitems == 0) { + success = true; + return list; + } + + /* Retrieve the list of item names in the fspool */ + names = vshCalloc(ctl, nitems, sizeof(*names)); + if ((nitems = virFSPoolListItems(fspool, names, nitems)) < 0) { + vshError(ctl, "%s", _("Failed to list storage items")); + goto cleanup; + } + + list->items = vshMalloc(ctl, sizeof(virFSItemPtr) * (nitems)); + list->nitems = 0; + + /* get the items */ + for (i = 0; i < nitems; i++) { + if (!(item = virFSItemLookupByName(fspool, names[i]))) + continue; + list->items[list->nitems++] = item; + } + + /* truncate the list for not found items */ + deleted = nitems - list->nitems; + + finished: + /* sort the list */ + if (list->items && list->nitems) + qsort(list->items, list->nitems, sizeof(*list->items), virshFSItemSorter); + + if (deleted) + VIR_SHRINK_N(list->items, list->nitems, deleted); + + success = true; + + cleanup: + if (nitems > 0) + for (i = 0; i < nitems; i++) + VIR_FREE(names[i]); + VIR_FREE(names); + + if (!success) { + virshFSItemListFree(list); + list = NULL; + } + + return list; +} + +/* + * "item-list" command + */ +static const vshCmdInfo info_item_list[] = { + {.name = "help", + .data = N_("list items") + }, + {.name = "desc", + .data = N_("Returns list of items by fspool.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_item_list[] = { + VIRSH_COMMON_OPT_FSPOOL_FULL, + {.name = "details", + .type = VSH_OT_BOOL, + .help = N_("display extended details for items") + }, + {.name = NULL} +}; + +static bool +cmdItemList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) +{ + virFSItemInfo itemInfo; + virFSPoolPtr fspool; + char *outputStr = NULL; + const char *unit; + double val; + bool details = vshCommandOptBool(cmd, "details"); + size_t i; + bool ret = false; + int stringLength = 0; + size_t allocStrLength = 0, capStrLength = 0; + size_t nameStrLength = 0, pathStrLength = 0; + size_t typeStrLength = 0; + struct itemInfoText { + char *allocation; + char *capacity; + char *path; + char *type; + }; + struct itemInfoText *itemInfoTexts = NULL; + virshFSItemListPtr list = NULL; + + /* Look up the fspool information given to us by the user */ + if (!(fspool = virshCommandOptFSPool(ctl, cmd, "fspool", NULL))) + return false; + + if (!(list = virshFSItemListCollect(ctl, fspool, 0))) + goto cleanup; + + if (list->nitems > 0) + itemInfoTexts = vshCalloc(ctl, list->nitems, sizeof(*itemInfoTexts)); + + /* Collect the rest of the item information for display */ + for (i = 0; i < list->nitems; i++) { + /* Retrieve item info */ + virFSItemPtr item = list->items[i]; + + /* Retrieve the item path */ + if ((itemInfoTexts[i].path = virFSItemGetPath(item)) == NULL) { + /* Something went wrong retrieving a item path, cope with it */ + itemInfoTexts[i].path = vshStrdup(ctl, _("unknown")); + } + + /* If requested, retrieve item type and sizing information */ + if (details) { + if (virFSItemGetInfo(item, &itemInfo) != 0) { + /* Something went wrong retrieving item info, cope with it */ + itemInfoTexts[i].allocation = vshStrdup(ctl, _("unknown")); + itemInfoTexts[i].capacity = vshStrdup(ctl, _("unknown")); + itemInfoTexts[i].type = vshStrdup(ctl, _("unknown")); + } else { + /* Convert the returned item info into output strings */ + + /* Item type */ + itemInfoTexts[i].type = vshStrdup(ctl, + virshItemTypeToString(itemInfo.type)); + + val = vshPrettyCapacity(itemInfo.capacity, &unit); + if (virAsprintf(&itemInfoTexts[i].capacity, + "%.2lf %s", val, unit) < 0) + goto cleanup; + + val = vshPrettyCapacity(itemInfo.allocation, &unit); + if (virAsprintf(&itemInfoTexts[i].allocation, + "%.2lf %s", val, unit) < 0) + goto cleanup; + } + + /* Remember the largest length for each output string. + * This lets us displaying header and item information rows + * using a single, properly sized, printf style output string. + */ + + /* Keep the length of name string if longest so far */ + stringLength = strlen(virFSItemGetName(list->items[i])); + if (stringLength > nameStrLength) + nameStrLength = stringLength; + + /* Keep the length of path string if longest so far */ + stringLength = strlen(itemInfoTexts[i].path); + if (stringLength > pathStrLength) + pathStrLength = stringLength; + + /* Keep the length of type string if longest so far */ + stringLength = strlen(itemInfoTexts[i].type); + if (stringLength > typeStrLength) + typeStrLength = stringLength; + + /* Keep the length of capacity string if longest so far */ + stringLength = strlen(itemInfoTexts[i].capacity); + if (stringLength > capStrLength) + capStrLength = stringLength; + + /* Keep the length of allocation string if longest so far */ + stringLength = strlen(itemInfoTexts[i].allocation); + if (stringLength > allocStrLength) + allocStrLength = stringLength; + } + } + + /* If the --details option wasn't selected, we output the item + * info using the fixed string format from previous versions to + * maintain backward compatibility. + */ + + /* Output basic info then return if --details option not selected */ + if (!details) { + /* The old output format */ + vshPrintExtra(ctl, " %-20s %-40s\n", _("Name"), _("Path")); + vshPrintExtra(ctl, "---------------------------------------" + "---------------------------------------\n"); + for (i = 0; i < list->nitems; i++) { + vshPrint(ctl, " %-20s %-40s\n", virFSItemGetName(list->items[i]), + itemInfoTexts[i].path); + } + + /* Cleanup and return */ + ret = true; + goto cleanup; + } + + /* We only get here if the --details option was selected. */ + + /* Use the length of name header string if it's longest */ + stringLength = strlen(_("Name")); + if (stringLength > nameStrLength) + nameStrLength = stringLength; + + /* Use the length of path header string if it's longest */ + stringLength = strlen(_("Path")); + if (stringLength > pathStrLength) + pathStrLength = stringLength; + + /* Use the length of type header string if it's longest */ + stringLength = strlen(_("Type")); + if (stringLength > typeStrLength) + typeStrLength = stringLength; + + /* Use the length of capacity header string if it's longest */ + stringLength = strlen(_("Capacity")); + if (stringLength > capStrLength) + capStrLength = stringLength; + + /* Use the length of allocation header string if it's longest */ + stringLength = strlen(_("Allocation")); + if (stringLength > allocStrLength) + allocStrLength = stringLength; + + /* Display the string lengths for debugging */ + vshDebug(ctl, VSH_ERR_DEBUG, + "Longest name string = %zu chars\n", nameStrLength); + vshDebug(ctl, VSH_ERR_DEBUG, + "Longest path string = %zu chars\n", pathStrLength); + vshDebug(ctl, VSH_ERR_DEBUG, + "Longest type string = %zu chars\n", typeStrLength); + vshDebug(ctl, VSH_ERR_DEBUG, + "Longest capacity string = %zu chars\n", capStrLength); + vshDebug(ctl, VSH_ERR_DEBUG, + "Longest allocation string = %zu chars\n", allocStrLength); + + if (virAsprintf(&outputStr, + " %%-%lus %%-%lus %%-%lus %%%lus %%%lus\n", + (unsigned long) nameStrLength, + (unsigned long) pathStrLength, + (unsigned long) typeStrLength, + (unsigned long) capStrLength, + (unsigned long) allocStrLength) < 0) + goto cleanup; + + /* Display the header */ + vshPrint(ctl, outputStr, _("Name"), _("Path"), _("Type"), + ("Capacity"), _("Allocation")); + for (i = nameStrLength + pathStrLength + typeStrLength + + capStrLength + allocStrLength + + 10; i > 0; i--) + vshPrintExtra(ctl, "-"); + vshPrintExtra(ctl, "\n"); + + /* Display the item info rows */ + for (i = 0; i < list->nitems; i++) { + vshPrint(ctl, outputStr, + virFSItemGetName(list->items[i]), + itemInfoTexts[i].path, + itemInfoTexts[i].type, + itemInfoTexts[i].capacity, + itemInfoTexts[i].allocation); + } + + /* Cleanup and return */ + ret = true; + + cleanup: + + /* Safely free the memory allocated in this function */ + if (list && list->nitems) { + for (i = 0; i < list->nitems; i++) { + /* Cleanup the memory for one item info structure per loop */ + VIR_FREE(itemInfoTexts[i].path); + VIR_FREE(itemInfoTexts[i].type); + VIR_FREE(itemInfoTexts[i].capacity); + VIR_FREE(itemInfoTexts[i].allocation); + } + } + + /* Cleanup remaining memory */ + VIR_FREE(outputStr); + VIR_FREE(itemInfoTexts); + virFSPoolFree(fspool); + virshFSItemListFree(list); + + /* Return the desired value */ + return ret; +} + +/* + * "item-name" command + */ +static const vshCmdInfo info_item_name[] = { + {.name = "help", + .data = N_("returns the item name for a given item key or path") + }, + {.name = "desc", + .data = "" + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_item_name[] = { + {.name = "item", + .type = VSH_OT_DATA, + .flags = VSH_OFLAG_REQ, + .help = N_("item key or path") + }, + {.name = NULL} +}; + +static bool +cmdItemName(vshControl *ctl, const vshCmd *cmd) +{ + virFSItemPtr item; + + if (!(item = virshCommandOptItemBy(ctl, cmd, "item", NULL, NULL, + VIRSH_BYUUID))) + return false; + + vshPrint(ctl, "%s\n", virFSItemGetName(item)); + virFSItemFree(item); + return true; +} + +/* + * "item-fspool" command + */ +static const vshCmdInfo info_item_fspool[] = { + {.name = "help", + .data = N_("returns the storage fspool for a given item key or path") + }, + {.name = "desc", + .data = "" + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_item_fspool[] = { + {.name = "item", + .type = VSH_OT_DATA, + .flags = VSH_OFLAG_REQ, + .help = N_("item key or path") + }, + {.name = "uuid", + .type = VSH_OT_BOOL, + .help = N_("return the fspool uuid rather than fspool name") + }, + {.name = NULL} +}; + +static bool +cmdItemPool(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr fspool; + virFSItemPtr item; + char uuid[VIR_UUID_STRING_BUFLEN]; + + /* Use the supplied string to locate the item */ + if (!(item = virshCommandOptItemBy(ctl, cmd, "item", NULL, NULL, + VIRSH_BYUUID))) { + return false; + } + + /* Look up the parent storage fspool for the item */ + fspool = virFSPoolLookupByItem(item); + if (fspool == NULL) { + vshError(ctl, "%s", _("failed to get parent fspool")); + virFSItemFree(item); + return false; + } + + /* Return the requested details of the parent storage fspool */ + if (vshCommandOptBool(cmd, "uuid")) { + /* Retrieve and return fspool UUID string */ + if (virFSPoolGetUUIDString(fspool, &uuid[0]) == 0) + vshPrint(ctl, "%s\n", uuid); + } else { + /* Return the storage fspool name */ + vshPrint(ctl, "%s\n", virFSPoolGetName(fspool)); + } + + /* Cleanup */ + virFSItemFree(item); + virFSPoolFree(fspool); + return true; +} + +/* + * "item-key" command + */ +static const vshCmdInfo info_item_key[] = { + {.name = "help", + .data = N_("returns the item key for a given item name or path") + }, + {.name = "desc", + .data = "" + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_item_key[] = { + {.name = "item", + .type = VSH_OT_DATA, + .flags = VSH_OFLAG_REQ, + .help = N_("item name or path") + }, + VIRSH_COMMON_OPT_FSPOOL_OPTIONAL, + {.name = NULL} +}; + +static bool +cmdItemKey(vshControl *ctl, const vshCmd *cmd) +{ + virFSItemPtr item; + + if (!(item = virshCommandOptItem(ctl, cmd, "item", "fspool", NULL))) + return false; + + vshPrint(ctl, "%s\n", virFSItemGetKey(item)); + virFSItemFree(item); + return true; +} + +/* + * "item-path" command + */ +static const vshCmdInfo info_item_path[] = { + {.name = "help", + .data = N_("returns the item path for a given item name or key") + }, + {.name = "desc", + .data = "" + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_item_path[] = { + {.name = "item", + .type = VSH_OT_DATA, + .flags = VSH_OFLAG_REQ, + .help = N_("item name or key") + }, + VIRSH_COMMON_OPT_FSPOOL_OPTIONAL, + {.name = NULL} +}; + +static bool +cmdItemPath(vshControl *ctl, const vshCmd *cmd) +{ + virFSItemPtr item; + char * FSItemPath; + + if (!(item = virshCommandOptItem(ctl, cmd, "item", "fspool", NULL))) + return false; + + if ((FSItemPath = virFSItemGetPath(item)) == NULL) { + virFSItemFree(item); + return false; + } + + vshPrint(ctl, "%s\n", FSItemPath); + VIR_FREE(FSItemPath); + virFSItemFree(item); + return true; +} + +const vshCmdDef fsItemCmds[] = { + {.name = "item-clone", + .handler = cmdItemClone, + .opts = opts_item_clone, + .info = info_item_clone, + .flags = 0 + }, + {.name = "item-create-as", + .handler = cmdItemCreateAs, + .opts = opts_item_create_as, + .info = info_item_create_as, + .flags = 0 + }, + {.name = "item-create", + .handler = cmdItemCreate, + .opts = opts_item_create, + .info = info_item_create, + .flags = 0 + }, + {.name = "item-create-from", + .handler = cmdItemCreateFrom, + .opts = opts_item_create_from, + .info = info_item_create_from, + .flags = 0 + }, + {.name = "item-delete", + .handler = cmdItemDelete, + .opts = opts_item_delete, + .info = info_item_delete, + .flags = 0 + }, + {.name = "item-dumpxml", + .handler = cmdItemDumpXML, + .opts = opts_item_dumpxml, + .info = info_item_dumpxml, + .flags = 0 + }, + {.name = "item-info", + .handler = cmdItemInfo, + .opts = opts_item_info, + .info = info_item_info, + .flags = 0 + }, + {.name = "item-key", + .handler = cmdItemKey, + .opts = opts_item_key, + .info = info_item_key, + .flags = 0 + }, + {.name = "item-list", + .handler = cmdItemList, + .opts = opts_item_list, + .info = info_item_list, + .flags = 0 + }, + {.name = "item-name", + .handler = cmdItemName, + .opts = opts_item_name, + .info = info_item_name, + .flags = 0 + }, + {.name = "item-path", + .handler = cmdItemPath, + .opts = opts_item_path, + .info = info_item_path, + .flags = 0 + }, + {.name = "item-fspool", + .handler = cmdItemPool, + .opts = opts_item_fspool, + .info = info_item_fspool, + .flags = 0 + }, + {.name = NULL} +}; diff --git a/tools/virsh-fsitem.h b/tools/virsh-fsitem.h new file mode 100644 index 0000000..0607ac5 --- /dev/null +++ b/tools/virsh-fsitem.h @@ -0,0 +1,39 @@ +/* + * virsh-fsitem.h: Commands to manage fsitems + * + * Copyright (C) 2016 Parallels IP Holdings GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#ifndef VIRSH_FSITEM_H +# define VIRSH_FSITEM_H + +# include "virsh.h" + +virFSItemPtr virshCommandOptItemBy(vshControl *ctl, const vshCmd *cmd, + const char *optname, + const char *fspooloptname, + const char **name, unsigned int flags); + +/* default is lookup by Name and UUID */ +# define virshCommandOptItem(_ctl, _cmd, _optname, _fspooloptname, _name) \ + virshCommandOptItemBy(_ctl, _cmd, _optname, _fspooloptname, _name, \ + VIRSH_BYUUID | VIRSH_BYNAME) + +extern const vshCmdDef fsItemCmds[]; + +#endif /* VIRSH_FSITEM_H */ diff --git a/tools/virsh-fspool.c b/tools/virsh-fspool.c new file mode 100644 index 0000000..cc0e4c9 --- /dev/null +++ b/tools/virsh-fspool.c @@ -0,0 +1,1586 @@ +/* + * virsh-fspool.c: Commands to manage fspool + * + * Copyright (C) 2016 Parallels IP Holdings GmbH + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Olga Krishtal <okrishtal@xxxxxxxxxxxxx> + * + */ + +#include <config.h> +#include "virsh-fspool.h" + +#include "internal.h" +#include "virbuffer.h" +#include "viralloc.h" +#include "virfile.h" +#include "conf/fs_conf.h" +#include "virstring.h" + +#define VIRSH_COMMON_OPT_FSPOOL_FULL \ + VIRSH_COMMON_OPT_FSPOOL(N_("fspool name or uuid")) \ + +#define VIRSH_COMMON_OPT_FSPOOL_BUILD \ + {.name = "build", \ + .type = VSH_OT_BOOL, \ + .flags = 0, \ + .help = N_("build the fspool as normal") \ + } \ + +#define VIRSH_COMMON_OPT_FSPOOL_NO_OVERWRITE \ + {.name = "no-overwrite", \ + .type = VSH_OT_BOOL, \ + .flags = 0, \ + .help = N_("do not overwrite an existing fspool of this type") \ + } \ + +#define VIRSH_COMMON_OPT_FSPOOL_OVERWRITE \ + {.name = "overwrite", \ + .type = VSH_OT_BOOL, \ + .flags = 0, \ + .help = N_("overwrite any existing data") \ + } \ + +#define VIRSH_COMMON_OPT_FSPOOL_X_AS \ + {.name = "name", \ + .type = VSH_OT_DATA, \ + .flags = VSH_OFLAG_REQ, \ + .help = N_("name of the fspool") \ + }, \ + {.name = "type", \ + .type = VSH_OT_DATA, \ + .flags = VSH_OFLAG_REQ, \ + .help = N_("type of the fspool") \ + }, \ + {.name = "print-xml", \ + .type = VSH_OT_BOOL, \ + .help = N_("print XML document, but don't define/create") \ + }, \ + {.name = "source-host", \ + .type = VSH_OT_STRING, \ + .help = N_("source-host for underlying storage") \ + }, \ + {.name = "source-path", \ + .type = VSH_OT_STRING, \ + .help = N_("source path for underlying storage") \ + }, \ + {.name = "source-name", \ + .type = VSH_OT_STRING, \ + .help = N_("source name for underlying storage") \ + }, \ + {.name = "target", \ + .type = VSH_OT_STRING, \ + .help = N_("target for underlying storage") \ + }, \ + {.name = "source-format", \ + .type = VSH_OT_STRING, \ + .help = N_("format for underlying storage") \ + } \ + +virFSPoolPtr +virshCommandOptFSPoolBy(vshControl *ctl, const vshCmd *cmd, const char *optname, + const char **name, unsigned int flags) +{ + virFSPoolPtr fspool = NULL; + const char *n = NULL; + virshControlPtr priv = ctl->privData; + + virCheckFlags(VIRSH_BYUUID | VIRSH_BYNAME, NULL); + + if (vshCommandOptStringReq(ctl, cmd, optname, &n) < 0) + return NULL; + + vshDebug(ctl, VSH_ERR_INFO, "%s: found option <%s>: %s\n", + cmd->def->name, optname, n); + + if (name) + *name = n; + + /* try it by UUID */ + if ((flags & VIRSH_BYUUID) && strlen(n) == VIR_UUID_STRING_BUFLEN-1) { + vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as fspool UUID\n", + cmd->def->name, optname); + fspool = virFSPoolLookupByUUIDString(priv->conn, n); + } + /* try it by NAME */ + if (!fspool && (flags & VIRSH_BYNAME)) { + vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as fspool NAME\n", + cmd->def->name, optname); + fspool = virFSPoolLookupByName(priv->conn, n); + } + + if (!fspool) + vshError(ctl, _("failed to get fspool '%s'"), n); + + return fspool; +} + +/* + * "fspool-create" command + */ +static const vshCmdInfo info_fspool_create[] = { + {.name = "help", + .data = N_("create a fspool from an XML file") + }, + {.name = "desc", + .data = N_("Create a fspool.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_fspool_create[] = { + VIRSH_COMMON_OPT_FILE(N_("file containing an XML fspool description")), + VIRSH_COMMON_OPT_FSPOOL_BUILD, + VIRSH_COMMON_OPT_FSPOOL_NO_OVERWRITE, + VIRSH_COMMON_OPT_FSPOOL_OVERWRITE, + + {.name = NULL} +}; + +static bool +cmdFSPoolCreate(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr fspool; + const char *from = NULL; + bool ret = true; + char *buffer; + bool build; + bool overwrite; + bool no_overwrite; + unsigned int flags = 0; + virshControlPtr priv = ctl->privData; + + if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0) + return false; + + build = vshCommandOptBool(cmd, "build"); + overwrite = vshCommandOptBool(cmd, "overwrite"); + no_overwrite = vshCommandOptBool(cmd, "no-overwrite"); + + VSH_EXCLUSIVE_OPTIONS_EXPR("overwrite", overwrite, + "no-overwrite", no_overwrite); + + if (build) + flags |= VIR_FSPOOL_CREATE_WITH_BUILD; + if (overwrite) + flags |= VIR_FSPOOL_CREATE_WITH_BUILD_OVERWRITE; + if (no_overwrite) + flags |= VIR_FSPOOL_CREATE_WITH_BUILD_NO_OVERWRITE; + + if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0) + return false; + + fspool = virFSPoolCreateXML(priv->conn, buffer, flags); + VIR_FREE(buffer); + + if (fspool != NULL) { + vshPrint(ctl, _("FSpool %s created from %s\n"), + virFSPoolGetName(fspool), from); + virFSPoolFree(fspool); + } else { + vshError(ctl, _("Failed to create fspool from %s"), from); + ret = false; + } + return ret; +} + +static const vshCmdOptDef opts_fspool_define_as[] = { + VIRSH_COMMON_OPT_FSPOOL_X_AS, + + {.name = NULL} +}; + +static int +virshBuildFSPoolXML(vshControl *ctl, + const vshCmd *cmd, + const char **retname, + char **xml) +{ + const char *name = NULL, *type = NULL, *srcHost = NULL, *srcPath = NULL, + *srcName = NULL, *srcFormat = NULL, *target = NULL; + virBuffer buf = VIR_BUFFER_INITIALIZER; + + if (vshCommandOptStringReq(ctl, cmd, "name", &name) < 0) + goto cleanup; + if (vshCommandOptStringReq(ctl, cmd, "type", &type) < 0) + goto cleanup; + + if (vshCommandOptStringReq(ctl, cmd, "source-host", &srcHost) < 0 || + vshCommandOptStringReq(ctl, cmd, "source-path", &srcPath) < 0 || + vshCommandOptStringReq(ctl, cmd, "source-name", &srcName) < 0 || + vshCommandOptStringReq(ctl, cmd, "source-format", &srcFormat) < 0 || + vshCommandOptStringReq(ctl, cmd, "target", &target) < 0) + goto cleanup; + + virBufferAsprintf(&buf, "<fspool type='%s'>\n", type); + virBufferAdjustIndent(&buf, 2); + virBufferAsprintf(&buf, "<name>%s</name>\n", name); + if (srcHost || srcPath || srcFormat || srcName) { + virBufferAddLit(&buf, "<source>\n"); + virBufferAdjustIndent(&buf, 2); + + if (srcHost) + virBufferAsprintf(&buf, "<host name='%s'/>\n", srcHost); + if (srcPath) + virBufferAsprintf(&buf, "<dir path='%s'/>\n", srcPath); + if (srcFormat) + virBufferAsprintf(&buf, "<format type='%s'/>\n", srcFormat); + if (srcName) + virBufferAsprintf(&buf, "<name>%s</name>\n", srcName); + + virBufferAdjustIndent(&buf, -2); + virBufferAddLit(&buf, "</source>\n"); + } + if (target) { + virBufferAddLit(&buf, "<target>\n"); + virBufferAdjustIndent(&buf, 2); + virBufferAsprintf(&buf, "<path>%s</path>\n", target); + virBufferAdjustIndent(&buf, -2); + virBufferAddLit(&buf, "</target>\n"); + } + virBufferAdjustIndent(&buf, -2); + virBufferAddLit(&buf, "</fspool>\n"); + + if (virBufferError(&buf)) { + vshPrint(ctl, "%s", _("Failed to allocate XML buffer")); + return false; + } + + *xml = virBufferContentAndReset(&buf); + *retname = name; + return true; + + cleanup: + virBufferFreeAndReset(&buf); + return false; +} + +/* + * "fspool-autostart" command + */ +static const vshCmdInfo info_fspool_autostart[] = { + {.name = "help", + .data = N_("autostart a fspool") + }, + {.name = "desc", + .data = N_("Configure a fspool to be automatically started at boot.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_fspool_autostart[] = { + VIRSH_COMMON_OPT_FSPOOL_FULL, + + {.name = "disable", + .type = VSH_OT_BOOL, + .help = N_("disable autostarting") + }, + {.name = NULL} +}; + +static bool +cmdFSPoolAutostart(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr fspool; + const char *name; + int autostart; + + if (!(fspool = virshCommandOptFSPool(ctl, cmd, "fspool", &name))) + return false; + + autostart = !vshCommandOptBool(cmd, "disable"); + + if (virFSPoolSetAutostart(fspool, autostart) < 0) { + if (autostart) + vshError(ctl, _("failed to mark fspool %s as autostarted"), name); + else + vshError(ctl, _("failed to unmark fspool %s as autostarted"), name); + virFSPoolFree(fspool); + return false; + } + + if (autostart) + vshPrint(ctl, _("Fspool %s marked as autostarted\n"), name); + else + vshPrint(ctl, _("Fspool %s unmarked as autostarted\n"), name); + + virFSPoolFree(fspool); + return true; +} + +/* + * "fspool-create-as" command + */ +static const vshCmdInfo info_fspool_create_as[] = { + {.name = "help", + .data = N_("create a fspool from a set of args") + }, + {.name = "desc", + .data = N_("Create a fspool.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_fspool_create_as[] = { + VIRSH_COMMON_OPT_FSPOOL_X_AS, + VIRSH_COMMON_OPT_FSPOOL_BUILD, + VIRSH_COMMON_OPT_FSPOOL_NO_OVERWRITE, + VIRSH_COMMON_OPT_FSPOOL_OVERWRITE, + + {.name = NULL} +}; + +static bool +cmdFSPoolCreateAs(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr fspool; + const char *name; + char *xml; + bool printXML = vshCommandOptBool(cmd, "print-xml"); + bool build; + bool overwrite; + bool no_overwrite; + unsigned int flags = 0; + virshControlPtr priv = ctl->privData; + + build = vshCommandOptBool(cmd, "build"); + overwrite = vshCommandOptBool(cmd, "overwrite"); + no_overwrite = vshCommandOptBool(cmd, "no-overwrite"); + + VSH_EXCLUSIVE_OPTIONS_EXPR("overwrite", overwrite, + "no-overwrite", no_overwrite); + + if (build) + flags |= VIR_FSPOOL_CREATE_WITH_BUILD; + if (overwrite) + flags |= VIR_FSPOOL_CREATE_WITH_BUILD_OVERWRITE; + if (no_overwrite) + flags |= VIR_FSPOOL_CREATE_WITH_BUILD_NO_OVERWRITE; + + if (!virshBuildFSPoolXML(ctl, cmd, &name, &xml)) + return false; + + if (printXML) { + vshPrint(ctl, "%s", xml); + VIR_FREE(xml); + } else { + fspool = virFSPoolCreateXML(priv->conn, xml, flags); + VIR_FREE(xml); + + if (fspool != NULL) { + vshPrint(ctl, _("FSool %s created\n"), name); + virFSPoolFree(fspool); + } else { + vshError(ctl, _("Failed to create fspool %s"), name); + return false; + } + } + return true; +} + +/* + * "fspool-define" command + */ +static const vshCmdInfo info_fspool_define[] = { + {.name = "help", + .data = N_("define an inactive persistent fspool or modify " + "an existing persistent one from an XML file") + }, + {.name = "desc", + .data = N_("Define or modify a persistent fspool.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_fspool_define[] = { + VIRSH_COMMON_OPT_FILE(N_("file containing an XML fspool description")), + + {.name = NULL} +}; + +static bool +cmdFSPoolDefine(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr fspool; + const char *from = NULL; + bool ret = true; + char *buffer; + virshControlPtr priv = ctl->privData; + + if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0) + return false; + + if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0) + return false; + + fspool = virFSPoolDefineXML(priv->conn, buffer, 0); + VIR_FREE(buffer); + + if (fspool != NULL) { + vshPrint(ctl, _("FSool %s defined from %s\n"), + virFSPoolGetName(fspool), from); + virFSPoolFree(fspool); + } else { + vshError(ctl, _("Failed to define fspool from %s"), from); + ret = false; + } + return ret; +} + +/* + * "fspool-define-as" command + */ +static const vshCmdInfo info_fspool_define_as[] = { + {.name = "help", + .data = N_("define a fspool from a set of args") + }, + {.name = "desc", + .data = N_("Define a fspool.") + }, + {.name = NULL} +}; + +static bool +cmdFSPoolDefineAs(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr fspool; + const char *name; + char *xml; + bool printXML = vshCommandOptBool(cmd, "print-xml"); + virshControlPtr priv = ctl->privData; + + if (!virshBuildFSPoolXML(ctl, cmd, &name, &xml)) + return false; + + if (printXML) { + vshPrint(ctl, "%s", xml); + VIR_FREE(xml); + } else { + fspool = virFSPoolDefineXML(priv->conn, xml, 0); + VIR_FREE(xml); + + if (fspool != NULL) { + vshPrint(ctl, _("FSpool %s defined\n"), name); + virFSPoolFree(fspool); + } else { + vshError(ctl, _("Failed to define fspool %s"), name); + return false; + } + } + return true; +} + +/* + * "fspool-build" command + */ +static const vshCmdInfo info_fspool_build[] = { + {.name = "help", + .data = N_("build a fspool") + }, + {.name = "desc", + .data = N_("Build a given fspool.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_fspool_build[] = { + VIRSH_COMMON_OPT_FSPOOL_FULL, + VIRSH_COMMON_OPT_FSPOOL_NO_OVERWRITE, + VIRSH_COMMON_OPT_FSPOOL_OVERWRITE, + + {.name = NULL} +}; + +static bool +cmdFSPoolBuild(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr fspool; + bool ret = true; + const char *name; + unsigned int flags = 0; + + if (!(fspool = virshCommandOptFSPool(ctl, cmd, "fspool", &name))) + return false; + + if (vshCommandOptBool(cmd, "no-overwrite")) + flags |= VIR_FSPOOL_BUILD_NO_OVERWRITE; + + if (vshCommandOptBool(cmd, "overwrite")) + flags |= VIR_FSPOOL_BUILD_OVERWRITE; + + if (virFSPoolBuild(fspool, flags) == 0) { + vshPrint(ctl, _("FSpool %s built\n"), name); + } else { + vshError(ctl, _("Failed to build fspool %s"), name); + ret = false; + } + + virFSPoolFree(fspool); + + return ret; +} + +/* + * "fspool-destroy" command + */ +static const vshCmdInfo info_fspool_destroy[] = { + {.name = "help", + .data = N_("stop a fspool") + }, + {.name = "desc", + .data = N_("Forcefully stop a given fspool.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_fspool_destroy[] = { + VIRSH_COMMON_OPT_FSPOOL_FULL, + + {.name = NULL} +}; + +static bool +cmdFSPoolDestroy(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr fspool; + bool ret = true; + const char *name; + + if (!(fspool = virshCommandOptFSPool(ctl, cmd, "fspool", &name))) + return false; + + if (virFSPoolDestroy(fspool) == 0) { + vshPrint(ctl, _("FSpool %s destroyed\n"), name); + } else { + vshError(ctl, _("Failed to destroy fspool %s"), name); + ret = false; + } + + virFSPoolFree(fspool); + return ret; +} + +/* + * "fspool-delete" command + */ +static const vshCmdInfo info_fspool_delete[] = { + {.name = "help", + .data = N_("delete a fspool") + }, + {.name = "desc", + .data = N_("Delete a given fspool.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_fspool_delete[] = { + VIRSH_COMMON_OPT_FSPOOL_FULL, + + {.name = NULL} +}; + +static bool +cmdFSPoolDelete(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr fspool; + bool ret = true; + const char *name; + + if (!(fspool = virshCommandOptFSPool(ctl, cmd, "fspool", &name))) + return false; + + if (virFSPoolDelete(fspool, 0) == 0) { + vshPrint(ctl, _("Pool %s deleted\n"), name); + } else { + vshError(ctl, _("Failed to delete fspool %s"), name); + ret = false; + } + + virFSPoolFree(fspool); + return ret; +} + +/* + * "fspool-refresh" command + */ +static const vshCmdInfo info_fspool_refresh[] = { + {.name = "help", + .data = N_("refresh a fspool") + }, + {.name = "desc", + .data = N_("Refresh a given fspool.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_fspool_refresh[] = { + VIRSH_COMMON_OPT_FSPOOL_FULL, + + {.name = NULL} +}; + +static bool +cmdFSPoolRefresh(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr fspool; + bool ret = true; + const char *name; + + if (!(fspool = virshCommandOptFSPool(ctl, cmd, "fspool", &name))) + return false; + + if (virFSPoolRefresh(fspool, 0) == 0) { + vshPrint(ctl, _("Pool %s refreshed\n"), name); + } else { + vshError(ctl, _("Failed to refresh fspool %s"), name); + ret = false; + } + virFSPoolFree(fspool); + + return ret; +} + +/* + * "fspool-dumpxml" command + */ +static const vshCmdInfo info_fspool_dumpxml[] = { + {.name = "help", + .data = N_("fspool information in XML") + }, + {.name = "desc", + .data = N_("Output the fspool information as an XML dump to stdout.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_fspool_dumpxml[] = { + VIRSH_COMMON_OPT_FSPOOL_FULL, + + {.name = "inactive", + .type = VSH_OT_BOOL, + .help = N_("show inactive defined XML") + }, + {.name = NULL} +}; + +static bool +cmdFSPoolDumpXML(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr fspool; + bool ret = true; + bool inactive = vshCommandOptBool(cmd, "inactive"); + unsigned int flags = 0; + char *dump; + + if (inactive) + flags |= VIR_FS_XML_INACTIVE; + + if (!(fspool = virshCommandOptFSPool(ctl, cmd, "fspool", NULL))) + return false; + + dump = virFSPoolGetXMLDesc(fspool, flags); + if (dump != NULL) { + vshPrint(ctl, "%s", dump); + VIR_FREE(dump); + } else { + ret = false; + } + + virFSPoolFree(fspool); + return ret; +} + +static int +virshFSPoolSorter(const void *a, const void *b) +{ + virFSPoolPtr *pa = (virFSPoolPtr *) a; + virFSPoolPtr *pb = (virFSPoolPtr *) b; + + if (*pa && !*pb) + return -1; + + if (!*pa) + return *pb != NULL; + + return vshStrcasecmp(virFSPoolGetName(*pa), + virFSPoolGetName(*pb)); +} + +struct virshFSPoolList { + virFSPoolPtr *fspools; + size_t nfspools; +}; +typedef struct virshFSPoolList *virshFSPoolListPtr; + +static void +virshFSPoolListFree(virshFSPoolListPtr list) +{ + size_t i; + + if (list && list->fspools) { + for (i = 0; i < list->nfspools; i++) { + if (list->fspools[i]) + virFSPoolFree(list->fspools[i]); + } + VIR_FREE(list->fspools); + } + VIR_FREE(list); +} + +static virshFSPoolListPtr +virshFSPoolListCollect(vshControl *ctl, + unsigned int flags) +{ + virshFSPoolListPtr list = vshMalloc(ctl, sizeof(*list)); + int ret; + virshControlPtr priv = ctl->privData; + + /* try the list with flags support (0.10.2 and later) */ + if ((ret = virConnectListAllFSPools(priv->conn, + &list->fspools, + flags)) < 0) { + vshError(ctl, "%s", _("Failed to list fspools")); + return NULL; + } + + list->nfspools = ret; + + /* sort the list */ + if (list->fspools && list->nfspools) + qsort(list->fspools, list->nfspools, + sizeof(*list->fspools), virshFSPoolSorter); + + return list; +} + + +VIR_ENUM_DECL(virshFSPoolState) +VIR_ENUM_IMPL(virshFSPoolState, + VIR_FSPOOL_STATE_LAST, + N_("inactive"), + N_("building"), + N_("running")) + +static const char * +virshFSPoolStateToString(int state) +{ + const char *str = virshFSPoolStateTypeToString(state); + return str ? _(str) : _("unknown"); +} + + +/* + * "fspool-list" command + */ +static const vshCmdInfo info_fspool_list[] = { + {.name = "help", + .data = N_("list fspools") + }, + {.name = "desc", + .data = N_("Returns list of fspools.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_fspool_list[] = { + {.name = "inactive", + .type = VSH_OT_BOOL, + .help = N_("list inactive fspools") + }, + {.name = "all", + .type = VSH_OT_BOOL, + .help = N_("list inactive & active fspools") + }, + {.name = "transient", + .type = VSH_OT_BOOL, + .help = N_("list transient fspools") + }, + {.name = "persistent", + .type = VSH_OT_BOOL, + .help = N_("list persistent fspools") + }, + {.name = "autostart", + .type = VSH_OT_BOOL, + .help = N_("list fspools with autostart enabled") + }, + {.name = "no-autostart", + .type = VSH_OT_BOOL, + .help = N_("list fspools with autostart disabled") + }, + {.name = "type", + .type = VSH_OT_STRING, + .help = N_("only list fspool of specified type(s) (if supported)") + }, + {.name = "details", + .type = VSH_OT_BOOL, + .help = N_("display extended details for fspools") + }, + {.name = NULL} +}; + +static bool +cmdFSPoolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) +{ + virFSPoolInfo info; + size_t i; + bool ret = false; + size_t stringLength = 0, nameStrLength = 0; + size_t autostartStrLength = 0, persistStrLength = 0; + size_t stateStrLength = 0, capStrLength = 0; + size_t allocStrLength = 0, availStrLength = 0; + struct fspoolInfoText { + char *state; + char *autostart; + char *persistent; + char *capacity; + char *allocation; + char *available; + }; + struct fspoolInfoText *fspoolInfoTexts = NULL; + unsigned int flags = VIR_CONNECT_LIST_FSPOOLS_ACTIVE; + virshFSPoolListPtr list = NULL; + const char *type = NULL; + bool details = vshCommandOptBool(cmd, "details"); + bool inactive, all; + char *outputStr = NULL; + + inactive = vshCommandOptBool(cmd, "inactive"); + all = vshCommandOptBool(cmd, "all"); + + if (inactive) + flags = VIR_CONNECT_LIST_FSPOOLS_INACTIVE; + + if (all) + flags = VIR_CONNECT_LIST_FSPOOLS_ACTIVE | + VIR_CONNECT_LIST_FSPOOLS_INACTIVE; + + if (vshCommandOptBool(cmd, "autostart")) + flags |= VIR_CONNECT_LIST_FSPOOLS_AUTOSTART; + + if (vshCommandOptBool(cmd, "no-autostart")) + flags |= VIR_CONNECT_LIST_FSPOOLS_NO_AUTOSTART; + + if (vshCommandOptBool(cmd, "persistent")) + flags |= VIR_CONNECT_LIST_FSPOOLS_PERSISTENT; + + if (vshCommandOptBool(cmd, "transient")) + flags |= VIR_CONNECT_LIST_FSPOOLS_TRANSIENT; + + if (vshCommandOptStringReq(ctl, cmd, "type", &type) < 0) + return false; + + if (type) { + int fspoolType = -1; + char **fspoolTypes = NULL; + int nfspoolTypes = 0; + + if ((nfspoolTypes = vshStringToArray(type, &fspoolTypes)) < 0) + return false; + + for (i = 0; i < nfspoolTypes; i++) { + if ((fspoolType = virFSPoolTypeFromString(fspoolTypes[i])) < 0) { + vshError(ctl, _("Invalid fspool type '%s'"), fspoolTypes[i]); + virStringFreeList(fspoolTypes); + return false; + } + + switch ((virFSPoolType) fspoolType) { + case VIR_FSPOOL_DIR: + flags |= VIR_CONNECT_LIST_FSPOOLS_DIR; + break; + case VIR_FSPOOL_LAST: + break; + } + } + virStringFreeList(fspoolTypes); + } + + if (!(list = virshFSPoolListCollect(ctl, flags))) + goto cleanup; + + fspoolInfoTexts = vshCalloc(ctl, list->nfspools, sizeof(*fspoolInfoTexts)); + + /* Collect the storage fspool information for display */ + for (i = 0; i < list->nfspools; i++) { + int autostart = 0, persistent = 0; + + /* Retrieve the autostart status of the fspool */ + if (virFSPoolGetAutostart(list->fspools[i], &autostart) < 0) + fspoolInfoTexts[i].autostart = vshStrdup(ctl, _("no autostart")); + else + fspoolInfoTexts[i].autostart = vshStrdup(ctl, autostart ? + _("yes") : _("no")); + + /* Retrieve the persistence status of the fspool */ + if (details) { + persistent = virFSPoolIsPersistent(list->fspools[i]); + vshDebug(ctl, VSH_ERR_DEBUG, "Persistent flag value: %d\n", + persistent); + if (persistent < 0) + fspoolInfoTexts[i].persistent = vshStrdup(ctl, _("unknown")); + else + fspoolInfoTexts[i].persistent = vshStrdup(ctl, persistent ? + _("yes") : _("no")); + + /* Keep the length of persistent string if longest so far */ + stringLength = strlen(fspoolInfoTexts[i].persistent); + if (stringLength > persistStrLength) + persistStrLength = stringLength; + } + + /* Collect further extended information about the fspool */ + if (virFSPoolGetInfo(list->fspools[i], &info) != 0) { + /* Something went wrong retrieving fspool info, cope with it */ + vshError(ctl, "%s", _("Could not retrieve fspool information")); + fspoolInfoTexts[i].state = vshStrdup(ctl, _("unknown")); + if (details) { + fspoolInfoTexts[i].capacity = vshStrdup(ctl, _("unknown")); + fspoolInfoTexts[i].allocation = vshStrdup(ctl, _("unknown")); + fspoolInfoTexts[i].available = vshStrdup(ctl, _("unknown")); + } + } else { + /* Decide which state string to display */ + if (details) { + const char *state = virshFSPoolStateToString(info.state); + + fspoolInfoTexts[i].state = vshStrdup(ctl, state); + + /* Create the fspool size related strings */ + if (info.state == VIR_FSPOOL_RUNNING) { + double val; + const char *unit; + + val = vshPrettyCapacity(info.capacity, &unit); + if (virAsprintf(&fspoolInfoTexts[i].capacity, + "%.2lf %s", val, unit) < 0) + goto cleanup; + + val = vshPrettyCapacity(info.allocation, &unit); + if (virAsprintf(&fspoolInfoTexts[i].allocation, + "%.2lf %s", val, unit) < 0) + goto cleanup; + + val = vshPrettyCapacity(info.available, &unit); + if (virAsprintf(&fspoolInfoTexts[i].available, + "%.2lf %s", val, unit) < 0) + goto cleanup; + } else { + /* Capacity related information isn't available */ + fspoolInfoTexts[i].capacity = vshStrdup(ctl, _("-")); + fspoolInfoTexts[i].allocation = vshStrdup(ctl, _("-")); + fspoolInfoTexts[i].available = vshStrdup(ctl, _("-")); + } + + /* Keep the length of capacity string if longest so far */ + stringLength = strlen(fspoolInfoTexts[i].capacity); + if (stringLength > capStrLength) + capStrLength = stringLength; + + /* Keep the length of allocation string if longest so far */ + stringLength = strlen(fspoolInfoTexts[i].allocation); + if (stringLength > allocStrLength) + allocStrLength = stringLength; + + /* Keep the length of available string if longest so far */ + stringLength = strlen(fspoolInfoTexts[i].available); + if (stringLength > availStrLength) + availStrLength = stringLength; + } else { + /* --details option was not specified, only active/inactive + * state strings are used */ + if (virFSPoolIsActive(list->fspools[i])) + fspoolInfoTexts[i].state = vshStrdup(ctl, _("active")); + else + fspoolInfoTexts[i].state = vshStrdup(ctl, _("inactive")); + } + } + + /* Keep the length of name string if longest so far */ + stringLength = strlen(virFSPoolGetName(list->fspools[i])); + if (stringLength > nameStrLength) + nameStrLength = stringLength; + + /* Keep the length of state string if longest so far */ + stringLength = strlen(fspoolInfoTexts[i].state); + if (stringLength > stateStrLength) + stateStrLength = stringLength; + + /* Keep the length of autostart string if longest so far */ + stringLength = strlen(fspoolInfoTexts[i].autostart); + if (stringLength > autostartStrLength) + autostartStrLength = stringLength; + } + + /* If the --details option wasn't selected, we output the fspool + * info using the fixed string format from previous versions to + * maintain backward compatibility. + */ + + /* Output basic info then return if --details option not selected */ + if (!details) { + /* Output old style header */ + vshPrintExtra(ctl, " %-20s %-10s %-10s\n", _("Name"), _("State"), + _("Autostart")); + vshPrintExtra(ctl, "-------------------------------------------\n"); + + /* Output old style fspool info */ + for (i = 0; i < list->nfspools; i++) { + const char *name = virFSPoolGetName(list->fspools[i]); + vshPrint(ctl, " %-20s %-10s %-10s\n", + name, + fspoolInfoTexts[i].state, + fspoolInfoTexts[i].autostart); + } + + /* Cleanup and return */ + ret = true; + goto cleanup; + } + + /* We only get here if the --details option was selected. */ + + /* Use the length of name header string if it's longest */ + stringLength = strlen(_("Name")); + if (stringLength > nameStrLength) + nameStrLength = stringLength; + + /* Use the length of state header string if it's longest */ + stringLength = strlen(_("State")); + if (stringLength > stateStrLength) + stateStrLength = stringLength; + + /* Use the length of autostart header string if it's longest */ + stringLength = strlen(_("Autostart")); + if (stringLength > autostartStrLength) + autostartStrLength = stringLength; + + /* Use the length of persistent header string if it's longest */ + stringLength = strlen(_("Persistent")); + if (stringLength > persistStrLength) + persistStrLength = stringLength; + + /* Use the length of capacity header string if it's longest */ + stringLength = strlen(_("Capacity")); + if (stringLength > capStrLength) + capStrLength = stringLength; + + /* Use the length of allocation header string if it's longest */ + stringLength = strlen(_("Allocation")); + if (stringLength > allocStrLength) + allocStrLength = stringLength; + + /* Use the length of available header string if it's longest */ + stringLength = strlen(_("Available")); + if (stringLength > availStrLength) + availStrLength = stringLength; + + /* Display the string lengths for debugging. */ + vshDebug(ctl, VSH_ERR_DEBUG, "Longest name string = %lu chars\n", + (unsigned long) nameStrLength); + vshDebug(ctl, VSH_ERR_DEBUG, "Longest state string = %lu chars\n", + (unsigned long) stateStrLength); + vshDebug(ctl, VSH_ERR_DEBUG, "Longest autostart string = %lu chars\n", + (unsigned long) autostartStrLength); + vshDebug(ctl, VSH_ERR_DEBUG, "Longest persistent string = %lu chars\n", + (unsigned long) persistStrLength); + vshDebug(ctl, VSH_ERR_DEBUG, "Longest capacity string = %lu chars\n", + (unsigned long) capStrLength); + vshDebug(ctl, VSH_ERR_DEBUG, "Longest allocation string = %lu chars\n", + (unsigned long) allocStrLength); + vshDebug(ctl, VSH_ERR_DEBUG, "Longest available string = %lu chars\n", + (unsigned long) availStrLength); + + /* Create the output template. Each column is sized according to + * the longest string. + */ + if (virAsprintf(&outputStr, + " %%-%lus %%-%lus %%-%lus %%-%lus %%%lus %%%lus %%%lus\n", + (unsigned long) nameStrLength, + (unsigned long) stateStrLength, + (unsigned long) autostartStrLength, + (unsigned long) persistStrLength, + (unsigned long) capStrLength, + (unsigned long) allocStrLength, + (unsigned long) availStrLength) < 0) + goto cleanup; + + /* Display the header */ + vshPrint(ctl, outputStr, _("Name"), _("State"), _("Autostart"), + _("Persistent"), _("Capacity"), _("Allocation"), _("Available")); + for (i = nameStrLength + stateStrLength + autostartStrLength + + persistStrLength + capStrLength + + allocStrLength + availStrLength + + 14; i > 0; i--) + vshPrintExtra(ctl, "-"); + vshPrintExtra(ctl, "\n"); + + /* Display the fspool info rows */ + for (i = 0; i < list->nfspools; i++) { + vshPrint(ctl, outputStr, + virFSPoolGetName(list->fspools[i]), + fspoolInfoTexts[i].state, + fspoolInfoTexts[i].autostart, + fspoolInfoTexts[i].persistent, + fspoolInfoTexts[i].capacity, + fspoolInfoTexts[i].allocation, + fspoolInfoTexts[i].available); + } + + /* Cleanup and return */ + ret = true; + + cleanup: + VIR_FREE(outputStr); + if (list && list->nfspools) { + for (i = 0; i < list->nfspools; i++) { + VIR_FREE(fspoolInfoTexts[i].state); + VIR_FREE(fspoolInfoTexts[i].autostart); + VIR_FREE(fspoolInfoTexts[i].persistent); + VIR_FREE(fspoolInfoTexts[i].capacity); + VIR_FREE(fspoolInfoTexts[i].allocation); + VIR_FREE(fspoolInfoTexts[i].available); + } + } + VIR_FREE(fspoolInfoTexts); + + virshFSPoolListFree(list); + return ret; +} + +/* + * "fspool-info" command + */ +static const vshCmdInfo info_fspool_info[] = { + {.name = "help", + .data = N_("storage fspool information") + }, + {.name = "desc", + .data = N_("Returns basic information about the storage fspool.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_fspool_info[] = { + VIRSH_COMMON_OPT_FSPOOL_FULL, + + {.name = NULL} +}; + +static bool +cmdFSPoolInfo(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolInfo info; + virFSPoolPtr fspool; + int autostart = 0; + int persistent = 0; + bool ret = true; + char uuid[VIR_UUID_STRING_BUFLEN]; + + if (!(fspool = virshCommandOptFSPool(ctl, cmd, "fspool", NULL))) + return false; + + vshPrint(ctl, "%-15s %s\n", _("Name:"), virFSPoolGetName(fspool)); + + if (virFSPoolGetUUIDString(fspool, &uuid[0]) == 0) + vshPrint(ctl, "%-15s %s\n", _("UUID:"), uuid); + + if (virFSPoolGetInfo(fspool, &info) == 0) { + double val; + const char *unit; + vshPrint(ctl, "%-15s %s\n", _("State:"), + virshFSPoolStateToString(info.state)); + + /* Check and display whether the fspool is persistent or not */ + persistent = virFSPoolIsPersistent(fspool); + vshDebug(ctl, VSH_ERR_DEBUG, "Pool persistent flag value: %d\n", + persistent); + if (persistent < 0) + vshPrint(ctl, "%-15s %s\n", _("Persistent:"), _("unknown")); + else + vshPrint(ctl, "%-15s %s\n", _("Persistent:"), persistent ? _("yes") : _("no")); + + /* Check and display whether the fspool is autostarted or not */ + if (virFSPoolGetAutostart(fspool, &autostart) < 0) + vshPrint(ctl, "%-15s %s\n", _("Autostart:"), _("no autostart")); + else + vshPrint(ctl, "%-15s %s\n", _("Autostart:"), autostart ? _("yes") : _("no")); + + if (info.state == VIR_FSPOOL_RUNNING) { + val = vshPrettyCapacity(info.capacity, &unit); + vshPrint(ctl, "%-15s %2.2lf %s\n", _("Capacity:"), val, unit); + + val = vshPrettyCapacity(info.allocation, &unit); + vshPrint(ctl, "%-15s %2.2lf %s\n", _("Allocation:"), val, unit); + + val = vshPrettyCapacity(info.available, &unit); + vshPrint(ctl, "%-15s %2.2lf %s\n", _("Available:"), val, unit); + } + } else { + ret = false; + } + + virFSPoolFree(fspool); + return ret; +} + +/* + * "fspool-name" command + */ +static const vshCmdInfo info_fspool_name[] = { + {.name = "help", + .data = N_("convert a fspool UUID to fspool name") + }, + {.name = "desc", + .data = "" + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_fspool_name[] = { + VIRSH_COMMON_OPT_FSPOOL_FULL, + + {.name = NULL} +}; + +static bool +cmdFSPoolName(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr fspool; + + if (!(fspool = virshCommandOptFSPoolBy(ctl, cmd, "fspool", NULL, VIRSH_BYUUID))) + return false; + + vshPrint(ctl, "%s\n", virFSPoolGetName(fspool)); + virFSPoolFree(fspool); + return true; +} + +/* + * "fspool-start" command + */ +static const vshCmdInfo info_fspool_start[] = { + {.name = "help", + .data = N_("start a (previously defined) inactive fspool") + }, + {.name = "desc", + .data = N_("Start a fspool.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_fspool_start[] = { + VIRSH_COMMON_OPT_FSPOOL_FULL, + VIRSH_COMMON_OPT_FSPOOL_BUILD, + VIRSH_COMMON_OPT_FSPOOL_NO_OVERWRITE, + VIRSH_COMMON_OPT_FSPOOL_OVERWRITE, + + {.name = NULL} +}; + +static bool +cmdFSPoolStart(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr fspool; + bool ret = true; + const char *name = NULL; + bool build; + bool overwrite; + bool no_overwrite; + unsigned int flags = 0; + + if (!(fspool = virshCommandOptFSPool(ctl, cmd, "fspool", &name))) + return false; + + build = vshCommandOptBool(cmd, "build"); + overwrite = vshCommandOptBool(cmd, "overwrite"); + no_overwrite = vshCommandOptBool(cmd, "no-overwrite"); + + VSH_EXCLUSIVE_OPTIONS_EXPR("overwrite", overwrite, + "no-overwrite", no_overwrite); + + if (build) + flags |= VIR_FSPOOL_CREATE_WITH_BUILD; + if (overwrite) + flags |= VIR_FSPOOL_CREATE_WITH_BUILD_OVERWRITE; + if (no_overwrite) + flags |= VIR_FSPOOL_CREATE_WITH_BUILD_NO_OVERWRITE; + + if (virFSPoolCreate(fspool, flags) == 0) { + vshPrint(ctl, _("FSpool %s started\n"), name); + } else { + vshError(ctl, _("Failed to start fspool %s"), name); + ret = false; + } + + virFSPoolFree(fspool); + return ret; +} + +/* + * "fspool-undefine" command + */ +static const vshCmdInfo info_fspool_undefine[] = { + {.name = "help", + .data = N_("undefine an inactive fspool") + }, + {.name = "desc", + .data = N_("Undefine the configuration for an inactive fspool.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_fspool_undefine[] = { + VIRSH_COMMON_OPT_FSPOOL_FULL, + + {.name = NULL} +}; + +static bool +cmdFSPoolUndefine(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr fspool; + bool ret = true; + const char *name; + + if (!(fspool = virshCommandOptFSPool(ctl, cmd, "fspool", &name))) + return false; + + if (virFSPoolUndefine(fspool) == 0) { + vshPrint(ctl, _("Pool %s has been undefined\n"), name); + } else { + vshError(ctl, _("Failed to undefine fspool %s"), name); + ret = false; + } + + virFSPoolFree(fspool); + return ret; +} + +/* + * "fspool-uuid" command + */ +static const vshCmdInfo info_fspool_uuid[] = { + {.name = "help", + .data = N_("convert a fspool name to fspool UUID") + }, + {.name = "desc", + .data = "" + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_fspool_uuid[] = { + VIRSH_COMMON_OPT_FSPOOL_FULL, + + {.name = NULL} +}; + +static bool +cmdFSPoolUuid(vshControl *ctl, const vshCmd *cmd) +{ + virFSPoolPtr fspool; + char uuid[VIR_UUID_STRING_BUFLEN]; + + if (!(fspool = virshCommandOptFSPoolBy(ctl, cmd, "fspool", NULL, VIRSH_BYNAME))) + return false; + + if (virFSPoolGetUUIDString(fspool, uuid) != -1) + vshPrint(ctl, "%s\n", uuid); + else + vshError(ctl, "%s", _("failed to get fspool UUID")); + + virFSPoolFree(fspool); + return true; +} + +/* + * "fspool-edit" command + */ +static const vshCmdInfo info_fspool_edit[] = { + {.name = "help", + .data = N_("edit XML configuration for a fspool") + }, + {.name = "desc", + .data = N_("Edit the XML configuration for a fspool.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_fspool_edit[] = { + VIRSH_COMMON_OPT_FSPOOL_FULL, + + {.name = NULL} +}; + +static bool +cmdFSPoolEdit(vshControl *ctl, const vshCmd *cmd) +{ + bool ret = false; + virFSPoolPtr fspool = NULL; + virFSPoolPtr fspool_edited = NULL; + unsigned int flags = VIR_FS_XML_INACTIVE; + char *tmp_desc = NULL; + virshControlPtr priv = ctl->privData; + + fspool = virshCommandOptFSPool(ctl, cmd, "fspool", NULL); + if (fspool == NULL) + goto cleanup; + + /* Some old daemons don't support _INACTIVE flag */ + if (!(tmp_desc = virFSPoolGetXMLDesc(fspool, flags))) { + if (last_error->code == VIR_ERR_INVALID_ARG) { + flags &= ~VIR_FS_XML_INACTIVE; + vshResetLibvirtError(); + } else { + goto cleanup; + } + } else { + VIR_FREE(tmp_desc); + } + +#define EDIT_GET_XML virFSPoolGetXMLDesc(fspool, flags) +#define EDIT_NOT_CHANGED \ + do { \ + vshPrint(ctl, _("Pool %s XML configuration not changed.\n"), \ + virFSPoolGetName(fspool)); \ + ret = true; \ + goto edit_cleanup; \ + } while (0) +#define EDIT_DEFINE \ + (fspool_edited = virFSPoolDefineXML(priv->conn, doc_edited, 0)) +#include "virsh-edit.c" + + vshPrint(ctl, _("Pool %s XML configuration edited.\n"), + virFSPoolGetName(fspool_edited)); + + ret = true; + + cleanup: + if (fspool) + virFSPoolFree(fspool); + if (fspool_edited) + virFSPoolFree(fspool_edited); + + return ret; +} + +const vshCmdDef fsPoolCmds[] = { + {.name = "fspool-autostart", + .handler = cmdFSPoolAutostart, + .opts = opts_fspool_autostart, + .info = info_fspool_autostart, + .flags = 0 + }, + {.name = "fspool-build", + .handler = cmdFSPoolBuild, + .opts = opts_fspool_build, + .info = info_fspool_build, + .flags = 0 + }, + {.name = "fspool-create-as", + .handler = cmdFSPoolCreateAs, + .opts = opts_fspool_create_as, + .info = info_fspool_create_as, + .flags = 0 + }, + {.name = "fspool-create", + .handler = cmdFSPoolCreate, + .opts = opts_fspool_create, + .info = info_fspool_create, + .flags = 0 + }, + {.name = "fspool-define-as", + .handler = cmdFSPoolDefineAs, + .opts = opts_fspool_define_as, + .info = info_fspool_define_as, + .flags = 0 + }, + {.name = "fspool-define", + .handler = cmdFSPoolDefine, + .opts = opts_fspool_define, + .info = info_fspool_define, + .flags = 0 + }, + {.name = "fspool-delete", + .handler = cmdFSPoolDelete, + .opts = opts_fspool_delete, + .info = info_fspool_delete, + .flags = 0 + }, + {.name = "fspool-destroy", + .handler = cmdFSPoolDestroy, + .opts = opts_fspool_destroy, + .info = info_fspool_destroy, + .flags = 0 + }, + {.name = "fspool-dumpxml", + .handler = cmdFSPoolDumpXML, + .opts = opts_fspool_dumpxml, + .info = info_fspool_dumpxml, + .flags = 0 + }, + {.name = "fspool-edit", + .handler = cmdFSPoolEdit, + .opts = opts_fspool_edit, + .info = info_fspool_edit, + .flags = 0 + }, + {.name = "fspool-info", + .handler = cmdFSPoolInfo, + .opts = opts_fspool_info, + .info = info_fspool_info, + .flags = 0 + }, + {.name = "fspool-list", + .handler = cmdFSPoolList, + .opts = opts_fspool_list, + .info = info_fspool_list, + .flags = 0 + }, + {.name = "fspool-name", + .handler = cmdFSPoolName, + .opts = opts_fspool_name, + .info = info_fspool_name, + .flags = 0 + }, + {.name = "fspool-refresh", + .handler = cmdFSPoolRefresh, + .opts = opts_fspool_refresh, + .info = info_fspool_refresh, + .flags = 0 + }, + {.name = "fspool-undefine", + .handler = cmdFSPoolUndefine, + .opts = opts_fspool_undefine, + .info = info_fspool_undefine, + .flags = 0 + }, + {.name = "fspool-uuid", + .handler = cmdFSPoolUuid, + .opts = opts_fspool_uuid, + .info = info_fspool_uuid, + .flags = 0 + }, + {.name = "fspool-start", + .handler = cmdFSPoolStart, + .opts = opts_fspool_start, + .info = info_fspool_start, + .flags = 0 + }, + {.name = NULL} +}; diff --git a/tools/virsh-fspool.h b/tools/virsh-fspool.h new file mode 100644 index 0000000..9eb60f1 --- /dev/null +++ b/tools/virsh-fspool.h @@ -0,0 +1,38 @@ +/* + * virsh-fspool.h: Commands to manage fspool + * + * Copyright (C) 2016 Parallels IP Holdings GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#ifndef VIRSH_FSPOOL_H +# define VIRSH_FSPOOL_H + +# include "virsh.h" + +virFSPoolPtr +virshCommandOptFSPoolBy(vshControl *ctl, const vshCmd *cmd, const char *optname, + const char **name, unsigned int flags); + +/* default is lookup by Name and UUID */ +# define virshCommandOptFSPool(_ctl, _cmd, _optname, _name) \ + virshCommandOptFSPoolBy(_ctl, _cmd, _optname, _name, \ + VIRSH_BYUUID | VIRSH_BYNAME) + +extern const vshCmdDef fsPoolCmds[]; + +#endif /* VIRSH_FSPOOL_H */ diff --git a/tools/virsh.c b/tools/virsh.c index cb60edc..97aa85a 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -68,9 +68,11 @@ #include "virsh-nodedev.h" #include "virsh-nwfilter.h" #include "virsh-pool.h" +#include "virsh-fspool.h" #include "virsh-secret.h" #include "virsh-snapshot.h" #include "virsh-volume.h" +#include "virsh-fsitem.h" /* Gnulib doesn't guarantee SA_SIGINFO support. */ #ifndef SA_SIGINFO @@ -921,6 +923,8 @@ static const vshCmdGrp cmdGroups[] = { {VIRSH_CMD_GRP_SNAPSHOT, "snapshot", snapshotCmds}, {VIRSH_CMD_GRP_STORAGE_POOL, "pool", storagePoolCmds}, {VIRSH_CMD_GRP_STORAGE_VOL, "volume", storageVolCmds}, + {VIRSH_CMD_GRP_FSPOOL, "fspool", fsPoolCmds}, + {VIRSH_CMD_GRP_FSITEM, "item", fsItemCmds}, {VIRSH_CMD_GRP_VIRSH, "virsh", virshCmds}, {NULL, NULL, NULL} }; diff --git a/tools/virsh.h b/tools/virsh.h index fd552bb..5b7a636 100644 --- a/tools/virsh.h +++ b/tools/virsh.h @@ -51,6 +51,8 @@ # define VIRSH_CMD_GRP_DOM_MONITORING "Domain Monitoring" # define VIRSH_CMD_GRP_STORAGE_POOL "Storage Pool" # define VIRSH_CMD_GRP_STORAGE_VOL "Storage Volume" +# define VIRSH_CMD_GRP_FSPOOL "Fspool" +# define VIRSH_CMD_GRP_FSITEM "Item" # define VIRSH_CMD_GRP_NETWORK "Networking" # define VIRSH_CMD_GRP_NODEDEV "Node Device" # define VIRSH_CMD_GRP_IFACE "Interface" @@ -70,6 +72,13 @@ .help = _helpstr \ } \ +# define VIRSH_COMMON_OPT_FSPOOL(_helpstr) \ + {.name = "fspool", \ + .type = VSH_OT_DATA, \ + .flags = VSH_OFLAG_REQ, \ + .help = _helpstr \ + } + # define VIRSH_COMMON_OPT_DOMAIN(_helpstr) \ {.name = "domain", \ .type = VSH_OT_DATA, \ diff --git a/tools/virsh.pod b/tools/virsh.pod index 5027180..edde034 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -699,7 +699,7 @@ Immediately terminate the domain I<domain>. This doesn't give the domain OS any chance to react, and it's the equivalent of ripping the power cord out on a physical machine. In most cases you will want to use the B<shutdown> command instead. However, this does not delete any -storage volumes used by the guest, and if the domain is persistent, it +storage volumes or fspool items used by the guest, and if the domain is persistent, it can be restarted later. If I<domain> is transient, then the metadata of any snapshots will @@ -3820,6 +3820,256 @@ B<blockresize> for live resizing. =back +=head1 FSPOOL COMMANDS + +The following commands manipulate filesystem pools. Many of the commands +for filesystem pools are similar to the ones used for storage pools. + +=over 4 + +=item B<fspool-autostart> I<pool-or-uuid> [I<--disable>] + +Configure whether I<fspool> should automatically start at boot. + +=item B<fspool-build> I<pool-or-uuid> [I<--overwrite>] [I<--no-overwrite>] + +Build a given fspool. + +=item B<pool-create> I<file> +[I<--build>] [[I<--overwrite>] | [I<--no-overwrite>]] + +Create and start a fspool object from the XML I<file>. + +[I<--build>] perform a B<fspool-build> after creation in order +to remove the need for a follow-up command to build the fspool. + +=item B<pool-create-as> I<name> I<type> [I<--print-xml>] +[I<--source-host hostname>] [I<--source-path path>] +[I<--source-name name>] [I<--target path>] [I<--source-format format>] +[I<--build>] [[I<--overwrite>] | [I<--no-overwrite>]] + +Create and start a fspool object I<name> from the raw parameters. If +I<--print-xml> is specified, then print the XML of the fspool object +without creating the fspool. Otherwise, the fspool has the specified +I<type>. +[I<--source-host hostname>] provides the source hostname for fspools backed +by fspool from a remote server (fspool netfs, etc). + +[I<--source-path path>] provides the source directory path for fspools backed +by directories (fspool type dir). + +[I<--source-name name>] provides the source name for fspools backed by storage +from a named element. + +[I<--target path>] is the path for the mapping of the fspool into +the host file system. + +[I<--source-format format>] provides information about the format of the +fspool filesystem type. + +[I<--build>] [[I<--overwrite>] | [I<--no-overwrite>]] perform a +B<fspool-build> after creation in order to remove the need for a +follow-up command to build the fspool. The I<--overwrite> and +I<--no-overwrite> flags follow the same rules as B<fspool-build>. If +just I<--build> is provided, then B<fspool-build> is called with no flags. + +=item B<fspool-define> I<file> + +Define an inactive persistent fspool or modify an existing persistent one +from the XML I<file>. + +=item B<pool-define-as> I<name> I<type> [I<--print-xml>] +[I<--source-host hostname>] [I<--source-path path>] +[I<--source-name name>] [I<--target path>] [I<--source-format format>] + +Create, but do not start, a fspool object I<name> from the raw parameters. +If I<--print-xml> is specified, then print the XML of the pool object +without defining the pool. Otherwise, the pool has the specified +I<type>. + +Use the same arguments as B<fspool-create-as>, except for the I<--build>, +I<--overwrite>, and I<--no-overwrite> options. + +=item B<fspool-destroy> I<fspool-or-uuid> + +Destroy (stop) a given I<fspool> object. Libvirt will no longer manage the fspool +object, but the raw data contained in +the fspool is not changed, and can be later recovered with +B<fspool-create>. + +=item B<fspool-delete> I<fspool-or-uuid> + +Destroy the resources used by a given I<fspool> object. This operation +is non-recoverable. The I<fspool> object will still exist after this +command, ready for the creation of new fspool item. + +=item B<fspool-dumpxml> [I<--inactive>] I<fspool-or-uuid> + +Returns the XML information about the I<fspool> object. +I<--inactive> tells virsh to dump fspool configuration that will be used +on next start of the fspool as opposed to the current fspool configuration. + +=item B<fspool-edit> I<fspool-or-uuid> + +Edit the XML configuration file for a fspool. + +This is equivalent to: + + virsh fspool-dumpxml fspool > fspool.xml + vi fspool.xml (or make changes with your other text editor) + virsh pool-define pool.xml + +except that it does some error checking. + +The editor used can be supplied by the C<$VISUAL> or C<$EDITOR> environment +variables, and defaults to C<vi>. + +=item B<fspool-info> I<fspool-or-uuid> + +Returns basic information about the I<fspool> object. + +=item B<pool-fslist> [I<--inactive>] [I<--all>] + [I<--persistent>] [I<--transient>] + [I<--autostart>] [I<--no-autostart>] + [[I<--details>] [<type>] + +List fspool objects known to libvirt. By default, only active pools +are listed; I<--inactive> lists just the inactive pools, and I<--all> +lists all pools. + +In addition, there are several sets of filtering flags. I<--persistent> is to +list the persistent fspools, I<--transient> is to list the transient fspools. +I<--autostart> lists the autostarting fspools, I<--no-autostart> lists the fspools +with autostarting disabled. + +You may also want to list fspools with specified types using I<type>, the +pool types must be separated by comma, e.g. --type dir. + +The I<--details> option instructs virsh to additionally +display fspool persistence and capacity related information where available. + +=item B<fspool-name> I<uuid> + +Convert the I<uuid> to a fspool name. + +=item B<fspool-refresh> I<fspool-or-uuid> + +Refresh the list of items contained in I<fspool>. + +=item B<fspool-start> I<fspool-or-uuid> +[I<--build>] [[I<--overwrite>] | [I<--no-overwrite>]] + +Start the I<fspool>, which is previously defined but inactive. + +=item B<fspool-undefine> I<fspool-or-uuid> + +Undefine the configuration for an inactive I<fspool>. + +=item B<fspool-uuid> I<fspool> + +Returns the UUID of the named I<fspool>. + +=back + +=head1 FS ITEMS COMMANDS + +=over 4 + +=item B<item-create> I<fspool-or-uuid> I<FILE> + +Create a item from an XML <file>. +I<fspool-or-uuid> is the name or UUID of the fspool to create the item in. +I<FILE> is the XML <file> with the item definition. An easy way to create the +XML <file> is to use the B<item-dumpxml> command to obtain the definition of a +pre-existing item. + +=item B<vol-create-from> I<pool-or-uuid> I<FILE> [I<--inputpool> +I<pool-or-uuid>] I<vol-name-or-key-or-path> [I<--prealloc-metadata>] +[I<--reflink>] + +Create a volume, using another item as input. +I<fspool-or-uuid> is the name or UUID of the fspool to create the item in. +I<FILE> is the XML <file> with the volume definition. +I<--inputfspool> I<fspool-or-uuid> is the name or uuid of the fspool the +source item is in. +I<item-name-or-key-or-path> is the name or key or path of the source item. + +=item B<item-create-as> I<fspool-or-uuid> I<name> I<capacity> +[I<--allocation> I<size>] [I<--format> I<string>][I<--print-xml>] + +Create a item from a set of arguments unless I<--print-xml> is specified, in +which case just the XML of the item object is printed out without any actual +object creation. +I<fspool-or-uuid> is the name or UUID of the fspool to create the item +in. +I<name> is the name of the new item. +I<capacity> is the size of the item to be created, as a scaled integer +(see B<NOTES> above), defaulting to bytes if there is no suffix. +I<--allocation> I<size> is the initial size to be allocated in the item, +also as a scaled integer defaulting to bytes. + +=item B<item-clone> [I<--fspool> I<fspool-or-uuid>] I<item-name-or-key-or-path> +I<name> + +Clone an existing item within the parent fspool. Less powerful, +but easier to type, version of B<item-create-from>. +I<--fspool> I<fspool-or-uuid> is the name or UUID of the fspool +that contains the source item, and will contain the new item. +I<item-name-or-key-or-path> is the name or key or path of the source item. +I<name> is the name of the new item. + +=item B<item-delete> [I<--fspool> I<fspool-or-uuid>] I<item-name-or-key-or-path> + +Delete a given item. +I<--pool> I<fspool-or-uuid> is the name or UUID of the fspool the item +is in. +I<item-name-or-key-or-path> is the name or key or path of the item to delete. + +=item B<item-dumpxml> [I<--fspool> I<fspool-or-uuid>] I<item-name-or-key-or-path> + +Output the item information as an XML dump to stdout. +I<--fspool> I<fspool-or-uuid> is the name or UUID of the fspool the item +is in. I<item-name-or-key-or-path> is the name or key or path of the item +to output the XML of. + +=item B<item-info> [I<--fspool> I<fspool-or-uuid>] I<item-name-or-key-or-path> +[I<--bytes>] + +Returns basic information about the given item. +I<--fspool> I<fspool-or-uuid> is the name or UUID of the fspool the item +is in. I<item-name-or-key-or-path> is the name or key or path of the item +to return information for. If I<--bytes> is specified the sizes are not +converted to human friendly units. + +=item B<item-list> [I<--pool> I<pool-or-uuid>] [I<--details>] + +Return the list of volumes in the given fspool. +I<--fspool> I<fspool-or-uuid> is the name or UUID of the fspool. +The I<--details> option instructs virsh to additionally display item +type and capacity related information where available. + + +=item B<item-path> [I<--fspool> I<fspool-or-uuid>] I<item-name-or-key> + +Return the path for a given item. +I<--fspool> I<fspool-or-uuid> is the name or UUID of the fspool the item +is in. +I<item-name-or-key> is the name or key of the item to return the path for. + +=item B<item-name> I<item-key-or-path> + +Return the name for a given item. +I<item-key-or-path> is the key or path of the item to return the name for. + +=item B<item-key> [I<--fspool> I<fspool-or-uuid>] I<item-name-or-path> + +Return the item key for a given volume. +I<--fspool> I<fspool-or-uuid> is the name or UUID of the fspool the item +is in. I<item-name-or-path> is the name or path of the item to return the +item key for. + +=back + =head1 SECRET COMMANDS The following commands manipulate "secrets" (e.g. passwords, passphrases and -- 1.8.3.1 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list