[libvirt] [PATCH RFC v3 11/15] virsh: filesystem pools commands

Olga Krishtal okrishtal at virtuozzo.com
Fri Dec 2 15:38:16 UTC 2016


Signed-off-by: Nikolay Shirokovskiy <nshirokovskiy at virtuozzo.com>
Signed-off-by: Maxim Nestratov <mnestratov at virtuozzo.com>
---
 po/POTFILES.in       |    2 +
 tools/Makefile.am    |    2 +
 tools/virsh-fsitem.c | 1293 +++++++++++++++++++++++++++++++++++++++++
 tools/virsh-fsitem.h |   39 ++
 tools/virsh-fspool.c | 1574 ++++++++++++++++++++++++++++++++++++++++++++++++++
 tools/virsh-fspool.h |   38 ++
 tools/virsh.c        |    4 +
 tools/virsh.h        |    9 +
 tools/virsh.pod      |  252 +++++++-
 9 files changed, 3212 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 7ad9174..2d4c191 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 319abb2..ff68bd2 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..87fb327
--- /dev/null
+++ b/tools/virsh-fsitem.c
@@ -0,0 +1,1293 @@
+/*
+ * 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 at virtuozzo.com>
+ *
+ */
+#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")
+    },
+    VIRSH_COMMON_OPT_FSPOOL_OPTIONAL,
+    {.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..5034fce
--- /dev/null
+++ b/tools/virsh-fspool.c
@@ -0,0 +1,1574 @@
+/*
+ * 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 at virtuozzo.com>
+ *
+ */
+
+#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 overwrite;
+    bool no_overwrite;
+    unsigned int flags = 0;
+    virshControlPtr priv = ctl->privData;
+
+    if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0)
+        return false;
+
+    overwrite = vshCommandOptBool(cmd, "overwrite");
+    no_overwrite = vshCommandOptBool(cmd, "no-overwrite");
+
+    VSH_EXCLUSIVE_OPTIONS_EXPR("overwrite", overwrite,
+                               "no-overwrite", no_overwrite);
+
+    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 overwrite;
+    bool no_overwrite;
+    unsigned int flags = 0;
+    virshControlPtr priv = ctl->privData;
+
+    overwrite = vshCommandOptBool(cmd, "overwrite");
+    no_overwrite = vshCommandOptBool(cmd, "no-overwrite");
+
+    VSH_EXCLUSIVE_OPTIONS_EXPR("overwrite", overwrite,
+                               "no-overwrite", no_overwrite);
+
+    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 overwrite;
+    bool no_overwrite;
+    unsigned int flags = 0;
+
+    if (!(fspool = virshCommandOptFSPool(ctl, cmd, "fspool", &name)))
+         return false;
+
+    overwrite = vshCommandOptBool(cmd, "overwrite");
+    no_overwrite = vshCommandOptBool(cmd, "no-overwrite");
+
+    VSH_EXCLUSIVE_OPTIONS_EXPR("overwrite", overwrite,
+                               "no-overwrite", no_overwrite);
+
+    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 1068447..7d7e619 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
@@ -878,6 +880,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 f278fec..1181391 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
@@ -3904,6 +3904,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




More information about the libvir-list mailing list