]> xenbits.xensource.com Git - libvirt.git/commitdiff
virsh: Split cmds of domain group from virsh.c
authorOsier Yang <jyang@redhat.com>
Wed, 25 Jul 2012 15:37:18 +0000 (23:37 +0800)
committerOsier Yang <jyang@redhat.com>
Thu, 26 Jul 2012 03:57:18 +0000 (11:57 +0800)
This splits commands to manage domain into virsh-domain.c,The helpers
not for common use are moved into them too. Standard copyright is added
for the new file.

* tools/virsh.c:
  - Remove commands for domain group, and one helper
    (vshDomainVcpuStateToString)
  - vshStreamSink is moved before commands's definition for it's
    also used by commands not of domain group, such as volUpload.

* tools/virsh-domain.c:
  - New file, commands for domain group and the one helper are
    moved into it.

* po/POTFILES.in:
  - Add virsh-domain.c

* cfg.mk:
  - Skip to check config.h including for virsh-domain.c

cfg.mk
po/POTFILES.in
tools/virsh-domain.c [new file with mode: 0644]
tools/virsh.c

diff --git a/cfg.mk b/cfg.mk
index 87256d97db5424c9f2f91a93781a07782ce10d68..47d2ea818bb4882033ade2f11ae13adaefb6c716 100644 (file)
--- a/cfg.mk
+++ b/cfg.mk
@@ -773,7 +773,7 @@ exclude_file_name_regexp--sc_prohibit_xmlURI = ^src/util/viruri\.c$$
 
 exclude_file_name_regexp--sc_prohibit_return_as_function = \.py$$
 
-_virsh_includes=(edit|domain-monitor)
+_virsh_includes=(edit|domain-monitor|domain)
 exclude_file_name_regexp--sc_require_config_h = ^(examples/|tools/virsh-$(_virsh_includes)\.c$$)
 
 exclude_file_name_regexp--sc_require_config_h_first = ^(examples/|tools/virsh-$(_virsh_includes)\.c$$)
index 64da663de257d3258c3c49efee33bdfaf39a9b17..fb9891dc0127d20d777cae27052775b628426b70 100644 (file)
@@ -178,6 +178,7 @@ tools/console.c
 tools/libvirt-guests.init.sh
 tools/virsh.c
 tools/virsh-domain-monitor.c
+tools/virsh-domain.c
 tools/virsh-edit.c
 tools/virt-host-validate-common.c
 tools/virt-host-validate-lxc.c
diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c
new file mode 100644 (file)
index 0000000..da918ea
--- /dev/null
@@ -0,0 +1,8049 @@
+/*
+ * virsh-domain.c: Commands to manage domain
+ *
+ * Copyright (C) 2005, 2007-2012 Red Hat, Inc.
+ *
+ * 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/>.
+ *
+ *  Daniel Veillard <veillard@redhat.com>
+ *  Karel Zak <kzak@redhat.com>
+ *  Daniel P. Berrange <berrange@redhat.com>
+ *
+ */
+
+static const char *
+vshDomainVcpuStateToString(int state)
+{
+    switch (state) {
+    case VIR_VCPU_OFFLINE:
+        return N_("offline");
+    case VIR_VCPU_BLOCKED:
+        return N_("idle");
+    case VIR_VCPU_RUNNING:
+        return N_("running");
+    default:
+        ;/*FALLTHROUGH*/
+    }
+    return N_("no state");
+}
+
+/*
+ * "attach-device" command
+ */
+static const vshCmdInfo info_attach_device[] = {
+    {"help", N_("attach device from an XML file")},
+    {"desc", N_("Attach device from an XML <file>.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_attach_device[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"file",   VSH_OT_DATA, VSH_OFLAG_REQ, N_("XML file")},
+    {"persistent", VSH_OT_ALIAS, 0, "config"},
+    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdAttachDevice(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom;
+    const char *from = NULL;
+    char *buffer;
+    int ret;
+    unsigned int flags;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        return false;
+
+    if (vshCommandOptString(cmd, "file", &from) <= 0) {
+        virDomainFree(dom);
+        return false;
+    }
+
+    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
+        virshReportError(ctl);
+        virDomainFree(dom);
+        return false;
+    }
+
+    if (vshCommandOptBool(cmd, "config")) {
+        flags = VIR_DOMAIN_AFFECT_CONFIG;
+        if (virDomainIsActive(dom) == 1)
+           flags |= VIR_DOMAIN_AFFECT_LIVE;
+        ret = virDomainAttachDeviceFlags(dom, buffer, flags);
+    } else {
+        ret = virDomainAttachDevice(dom, buffer);
+    }
+    VIR_FREE(buffer);
+
+    if (ret < 0) {
+        vshError(ctl, _("Failed to attach device from %s"), from);
+        virDomainFree(dom);
+        return false;
+    } else {
+        vshPrint(ctl, "%s", _("Device attached successfully\n"));
+    }
+
+    virDomainFree(dom);
+    return true;
+}
+
+/*
+ * "attach-disk" command
+ */
+static const vshCmdInfo info_attach_disk[] = {
+    {"help", N_("attach disk device")},
+    {"desc", N_("Attach new disk device.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_attach_disk[] = {
+    {"domain",  VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"source",  VSH_OT_DATA, VSH_OFLAG_REQ | VSH_OFLAG_EMPTY_OK,
+     N_("source of disk device")},
+    {"target",  VSH_OT_DATA, VSH_OFLAG_REQ, N_("target of disk device")},
+    {"driver",    VSH_OT_STRING, 0, N_("driver of disk device")},
+    {"subdriver", VSH_OT_STRING, 0, N_("subdriver of disk device")},
+    {"cache",     VSH_OT_STRING, 0, N_("cache mode of disk device")},
+    {"type",    VSH_OT_STRING, 0, N_("target device type")},
+    {"mode",    VSH_OT_STRING, 0, N_("mode of device reading and writing")},
+    {"persistent", VSH_OT_ALIAS, 0, "config"},
+    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
+    {"sourcetype", VSH_OT_STRING, 0, N_("type of source (block|file)")},
+    {"serial", VSH_OT_STRING, 0, N_("serial of disk device")},
+    {"shareable", VSH_OT_BOOL, 0, N_("shareable between domains")},
+    {"rawio", VSH_OT_BOOL, 0, N_("needs rawio capability")},
+    {"address", VSH_OT_STRING, 0, N_("address of disk device")},
+    {"multifunction", VSH_OT_BOOL, 0,
+     N_("use multifunction pci under specified address")},
+    {NULL, 0, 0, NULL}
+};
+
+enum {
+    DISK_ADDR_TYPE_INVALID,
+    DISK_ADDR_TYPE_PCI,
+    DISK_ADDR_TYPE_SCSI,
+    DISK_ADDR_TYPE_IDE,
+};
+
+struct PCIAddress {
+    unsigned int domain;
+    unsigned int bus;
+    unsigned int slot;
+    unsigned int function;
+};
+
+struct SCSIAddress {
+    unsigned int controller;
+    unsigned int bus;
+    unsigned int unit;
+};
+
+struct IDEAddress {
+    unsigned int controller;
+    unsigned int bus;
+    unsigned int unit;
+};
+
+struct DiskAddress {
+    int type;
+    union {
+        struct PCIAddress pci;
+        struct SCSIAddress scsi;
+        struct IDEAddress ide;
+    } addr;
+};
+
+static int str2PCIAddress(const char *str, struct PCIAddress *pciAddr)
+{
+    char *domain, *bus, *slot, *function;
+
+    if (!pciAddr)
+        return -1;
+    if (!str)
+        return -1;
+
+    domain = (char *)str;
+
+    if (virStrToLong_ui(domain, &bus, 0, &pciAddr->domain) != 0)
+        return -1;
+
+    bus++;
+    if (virStrToLong_ui(bus, &slot, 0, &pciAddr->bus) != 0)
+        return -1;
+
+    slot++;
+    if (virStrToLong_ui(slot, &function, 0, &pciAddr->slot) != 0)
+        return -1;
+
+    function++;
+    if (virStrToLong_ui(function, NULL, 0, &pciAddr->function) != 0)
+        return -1;
+
+    return 0;
+}
+
+static int str2SCSIAddress(const char *str, struct SCSIAddress *scsiAddr)
+{
+    char *controller, *bus, *unit;
+
+    if (!scsiAddr)
+        return -1;
+    if (!str)
+        return -1;
+
+    controller = (char *)str;
+
+    if (virStrToLong_ui(controller, &bus, 0, &scsiAddr->controller) != 0)
+        return -1;
+
+    bus++;
+    if (virStrToLong_ui(bus, &unit, 0, &scsiAddr->bus) != 0)
+        return -1;
+
+    unit++;
+    if (virStrToLong_ui(unit, NULL, 0, &scsiAddr->unit) != 0)
+        return -1;
+
+    return 0;
+}
+
+static int str2IDEAddress(const char *str, struct IDEAddress *ideAddr)
+{
+    char *controller, *bus, *unit;
+
+    if (!ideAddr)
+        return -1;
+    if (!str)
+        return -1;
+
+    controller = (char *)str;
+
+    if (virStrToLong_ui(controller, &bus, 0, &ideAddr->controller) != 0)
+        return -1;
+
+    bus++;
+    if (virStrToLong_ui(bus, &unit, 0, &ideAddr->bus) != 0)
+        return -1;
+
+    unit++;
+    if (virStrToLong_ui(unit, NULL, 0, &ideAddr->unit) != 0)
+        return -1;
+
+    return 0;
+}
+
+/* pci address pci:0000.00.0x0a.0 (domain:bus:slot:function)
+ * ide disk address: ide:00.00.0 (controller:bus:unit)
+ * scsi disk address: scsi:00.00.0 (controller:bus:unit)
+ */
+
+static int str2DiskAddress(const char *str, struct DiskAddress *diskAddr)
+{
+    char *type, *addr;
+
+    if (!diskAddr)
+        return -1;
+    if (!str)
+        return -1;
+
+    type = (char *)str;
+    addr = strchr(type, ':');
+    if (!addr)
+        return -1;
+
+    if (STREQLEN(type, "pci", addr - type)) {
+        diskAddr->type = DISK_ADDR_TYPE_PCI;
+        return str2PCIAddress(addr + 1, &diskAddr->addr.pci);
+    } else if (STREQLEN(type, "scsi", addr - type)) {
+        diskAddr->type = DISK_ADDR_TYPE_SCSI;
+        return str2SCSIAddress(addr + 1, &diskAddr->addr.scsi);
+    } else if (STREQLEN(type, "ide", addr - type)) {
+        diskAddr->type = DISK_ADDR_TYPE_IDE;
+        return str2IDEAddress(addr + 1, &diskAddr->addr.ide);
+    }
+
+    return -1;
+}
+
+static bool
+cmdAttachDisk(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom = NULL;
+    const char *source = NULL, *target = NULL, *driver = NULL,
+                *subdriver = NULL, *type = NULL, *mode = NULL,
+                *cache = NULL, *serial = NULL, *straddr = NULL;
+    struct DiskAddress diskAddr;
+    bool isFile = false, functionReturn = false;
+    int ret;
+    unsigned int flags;
+    const char *stype = NULL;
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+    char *xml;
+    struct stat st;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        goto cleanup;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        goto cleanup;
+
+    if (vshCommandOptString(cmd, "source", &source) <= 0)
+        goto cleanup;
+    /* Allow empty string as a placeholder that implies no source, for
+     * use in adding a cdrom drive with no disk.  */
+    if (!*source)
+        source = NULL;
+
+    if (vshCommandOptString(cmd, "target", &target) <= 0)
+        goto cleanup;
+
+    if (vshCommandOptString(cmd, "driver", &driver) < 0 ||
+        vshCommandOptString(cmd, "subdriver", &subdriver) < 0 ||
+        vshCommandOptString(cmd, "type", &type) < 0 ||
+        vshCommandOptString(cmd, "mode", &mode) < 0 ||
+        vshCommandOptString(cmd, "cache", &cache) < 0 ||
+        vshCommandOptString(cmd, "serial", &serial) < 0 ||
+        vshCommandOptString(cmd, "address", &straddr) < 0 ||
+        vshCommandOptString(cmd, "sourcetype", &stype) < 0) {
+        vshError(ctl, "%s", _("missing option"));
+        goto cleanup;
+    }
+
+    if (!stype) {
+        if (driver && (STREQ(driver, "file") || STREQ(driver, "tap"))) {
+            isFile = true;
+        } else {
+            if (source && !stat(source, &st))
+                isFile = S_ISREG(st.st_mode) ? true : false;
+        }
+    } else if (STREQ(stype, "file")) {
+        isFile = true;
+    } else if (STRNEQ(stype, "block")) {
+        vshError(ctl, _("Unknown source type: '%s'"), stype);
+        goto cleanup;
+    }
+
+    if (mode) {
+        if (STRNEQ(mode, "readonly") && STRNEQ(mode, "shareable")) {
+            vshError(ctl, _("No support for %s in command 'attach-disk'"),
+                     mode);
+            goto cleanup;
+        }
+    }
+
+    /* Make XML of disk */
+    virBufferAsprintf(&buf, "<disk type='%s'",
+                      (isFile) ? "file" : "block");
+    if (type)
+        virBufferAsprintf(&buf, " device='%s'", type);
+    if (vshCommandOptBool(cmd, "rawio"))
+        virBufferAddLit(&buf, " rawio='yes'");
+    virBufferAddLit(&buf, ">\n");
+
+    if (driver || subdriver || cache) {
+        virBufferAsprintf(&buf, "  <driver");
+
+        if (driver)
+            virBufferAsprintf(&buf, " name='%s'", driver);
+        if (subdriver)
+            virBufferAsprintf(&buf, " type='%s'", subdriver);
+        if (cache)
+            virBufferAsprintf(&buf, " cache='%s'", cache);
+
+        virBufferAddLit(&buf, "/>\n");
+    }
+
+    if (source)
+        virBufferAsprintf(&buf, "  <source %s='%s'/>\n",
+                          (isFile) ? "file" : "dev",
+                          source);
+    virBufferAsprintf(&buf, "  <target dev='%s'/>\n", target);
+    if (mode)
+        virBufferAsprintf(&buf, "  <%s/>\n", mode);
+
+    if (serial)
+        virBufferAsprintf(&buf, "  <serial>%s</serial>\n", serial);
+
+    if (vshCommandOptBool(cmd, "shareable"))
+        virBufferAsprintf(&buf, "  <shareable/>\n");
+
+    if (straddr) {
+        if (str2DiskAddress(straddr, &diskAddr) != 0) {
+            vshError(ctl, _("Invalid address."));
+            goto cleanup;
+        }
+
+        if (STRPREFIX((const char *)target, "vd")) {
+            if (diskAddr.type == DISK_ADDR_TYPE_PCI) {
+                virBufferAsprintf(&buf,
+                                  "  <address type='pci' domain='0x%04x'"
+                                  " bus ='0x%02x' slot='0x%02x' function='0x%0x'",
+                                  diskAddr.addr.pci.domain, diskAddr.addr.pci.bus,
+                                  diskAddr.addr.pci.slot, diskAddr.addr.pci.function);
+                if (vshCommandOptBool(cmd, "multifunction"))
+                    virBufferAddLit(&buf, " multifunction='on'");
+                virBufferAddLit(&buf, "/>\n");
+            } else {
+                vshError(ctl, "%s", _("expecting a pci:0000.00.00.00 address."));
+                goto cleanup;
+            }
+        } else if (STRPREFIX((const char *)target, "sd")) {
+            if (diskAddr.type == DISK_ADDR_TYPE_SCSI) {
+                virBufferAsprintf(&buf,
+                                  "  <address type='drive' controller='%d'"
+                                  " bus='%d' unit='%d' />\n",
+                                  diskAddr.addr.scsi.controller, diskAddr.addr.scsi.bus,
+                                  diskAddr.addr.scsi.unit);
+            } else {
+                vshError(ctl, "%s", _("expecting a scsi:00.00.00 address."));
+                goto cleanup;
+            }
+        } else if (STRPREFIX((const char *)target, "hd")) {
+            if (diskAddr.type == DISK_ADDR_TYPE_IDE) {
+                virBufferAsprintf(&buf,
+                                  "  <address type='drive' controller='%d'"
+                                  " bus='%d' unit='%d' />\n",
+                                  diskAddr.addr.ide.controller, diskAddr.addr.ide.bus,
+                                  diskAddr.addr.ide.unit);
+            } else {
+                vshError(ctl, "%s", _("expecting an ide:00.00.00 address."));
+                goto cleanup;
+            }
+        }
+    }
+
+    virBufferAddLit(&buf, "</disk>\n");
+
+    if (virBufferError(&buf)) {
+        vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
+        return false;
+    }
+
+    xml = virBufferContentAndReset(&buf);
+
+    if (vshCommandOptBool(cmd, "config")) {
+        flags = VIR_DOMAIN_AFFECT_CONFIG;
+        if (virDomainIsActive(dom) == 1)
+            flags |= VIR_DOMAIN_AFFECT_LIVE;
+        ret = virDomainAttachDeviceFlags(dom, xml, flags);
+    } else {
+        ret = virDomainAttachDevice(dom, xml);
+    }
+
+    VIR_FREE(xml);
+
+    if (ret != 0) {
+        vshError(ctl, "%s", _("Failed to attach disk"));
+    } else {
+        vshPrint(ctl, "%s", _("Disk attached successfully\n"));
+        functionReturn = true;
+    }
+
+ cleanup:
+    if (dom)
+        virDomainFree(dom);
+    virBufferFreeAndReset(&buf);
+    return functionReturn;
+}
+
+/*
+ * "attach-interface" command
+ */
+static const vshCmdInfo info_attach_interface[] = {
+    {"help", N_("attach network interface")},
+    {"desc", N_("Attach new network interface.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_attach_interface[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"type",   VSH_OT_DATA, VSH_OFLAG_REQ, N_("network interface type")},
+    {"source", VSH_OT_DATA, VSH_OFLAG_REQ, N_("source of network interface")},
+    {"target", VSH_OT_DATA, 0, N_("target network name")},
+    {"mac",    VSH_OT_DATA, 0, N_("MAC address")},
+    {"script", VSH_OT_DATA, 0, N_("script used to bridge network interface")},
+    {"model", VSH_OT_DATA, 0, N_("model type")},
+    {"persistent", VSH_OT_ALIAS, 0, "config"},
+    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
+    {"inbound", VSH_OT_DATA, VSH_OFLAG_NONE, N_("control domain's incoming traffics")},
+    {"outbound", VSH_OT_DATA, VSH_OFLAG_NONE, N_("control domain's outgoing traffics")},
+    {NULL, 0, 0, NULL}
+};
+
+/* parse inbound and outbound which are in the format of
+ * 'average,peak,burst', in which peak and burst are optional,
+ * thus 'average,,burst' and 'average,peak' are also legal. */
+static int parseRateStr(const char *rateStr, virNetDevBandwidthRatePtr rate)
+{
+    const char *average = NULL;
+    char *peak = NULL, *burst = NULL;
+
+    average = rateStr;
+    if (!average)
+        return -1;
+    if (virStrToLong_ull(average, &peak, 10, &rate->average) < 0)
+        return -1;
+
+    /* peak will be updated to point to the end of rateStr in case
+     * of 'average' */
+    if (peak && *peak != '\0') {
+        burst = strchr(peak + 1, ',');
+        if (!(burst && (burst - peak == 1))) {
+            if (virStrToLong_ull(peak + 1, &burst, 10, &rate->peak) < 0)
+                return -1;
+        }
+
+        /* burst will be updated to point to the end of rateStr in case
+         * of 'average,peak' */
+        if (burst && *burst != '\0') {
+            if (virStrToLong_ull(burst + 1, NULL, 10, &rate->burst) < 0)
+                return -1;
+        }
+    }
+
+
+    return 0;
+}
+
+static bool
+cmdAttachInterface(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom = NULL;
+    const char *mac = NULL, *target = NULL, *script = NULL,
+                *type = NULL, *source = NULL, *model = NULL,
+                *inboundStr = NULL, *outboundStr = NULL;
+    virNetDevBandwidthRate inbound, outbound;
+    int typ;
+    int ret;
+    bool functionReturn = false;
+    unsigned int flags;
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+    char *xml;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        goto cleanup;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        goto cleanup;
+
+    if (vshCommandOptString(cmd, "type", &type) <= 0)
+        goto cleanup;
+
+    if (vshCommandOptString(cmd, "source", &source) < 0 ||
+        vshCommandOptString(cmd, "target", &target) < 0 ||
+        vshCommandOptString(cmd, "mac", &mac) < 0 ||
+        vshCommandOptString(cmd, "script", &script) < 0 ||
+        vshCommandOptString(cmd, "model", &model) < 0 ||
+        vshCommandOptString(cmd, "inbound", &inboundStr) < 0 ||
+        vshCommandOptString(cmd, "outbound", &outboundStr) < 0) {
+        vshError(ctl, "missing argument");
+        goto cleanup;
+    }
+
+    /* check interface type */
+    if (STREQ(type, "network")) {
+        typ = 1;
+    } else if (STREQ(type, "bridge")) {
+        typ = 2;
+    } else {
+        vshError(ctl, _("No support for %s in command 'attach-interface'"),
+                 type);
+        goto cleanup;
+    }
+
+    if (inboundStr) {
+        memset(&inbound, 0, sizeof(inbound));
+        if (parseRateStr(inboundStr, &inbound) < 0) {
+            vshError(ctl, _("inbound format is incorrect"));
+            goto cleanup;
+        }
+        if (inbound.average == 0) {
+            vshError(ctl, _("inbound average is mandatory"));
+            goto cleanup;
+        }
+    }
+    if (outboundStr) {
+        memset(&outbound, 0, sizeof(outbound));
+        if (parseRateStr(outboundStr, &outbound) < 0) {
+            vshError(ctl, _("outbound format is incorrect"));
+            goto cleanup;
+        }
+        if (outbound.average == 0) {
+            vshError(ctl, _("outbound average is mandatory"));
+            goto cleanup;
+        }
+    }
+
+    /* Make XML of interface */
+    virBufferAsprintf(&buf, "<interface type='%s'>\n", type);
+
+    if (typ == 1)
+        virBufferAsprintf(&buf, "  <source network='%s'/>\n", source);
+    else if (typ == 2)
+        virBufferAsprintf(&buf, "  <source bridge='%s'/>\n", source);
+
+    if (target != NULL)
+        virBufferAsprintf(&buf, "  <target dev='%s'/>\n", target);
+    if (mac != NULL)
+        virBufferAsprintf(&buf, "  <mac address='%s'/>\n", mac);
+    if (script != NULL)
+        virBufferAsprintf(&buf, "  <script path='%s'/>\n", script);
+    if (model != NULL)
+        virBufferAsprintf(&buf, "  <model type='%s'/>\n", model);
+
+    if (inboundStr || outboundStr) {
+        virBufferAsprintf(&buf, "  <bandwidth>\n");
+        if (inboundStr && inbound.average > 0) {
+            virBufferAsprintf(&buf, "    <inbound average='%llu'", inbound.average);
+            if (inbound.peak > 0)
+                virBufferAsprintf(&buf, " peak='%llu'", inbound.peak);
+            if (inbound.burst > 0)
+                virBufferAsprintf(&buf, " burst='%llu'", inbound.burst);
+            virBufferAsprintf(&buf, "/>\n");
+        }
+        if (outboundStr && outbound.average > 0) {
+            virBufferAsprintf(&buf, "    <outbound average='%llu'", outbound.average);
+            if (outbound.peak > 0)
+                virBufferAsprintf(&buf, " peak='%llu'", outbound.peak);
+            if (outbound.burst > 0)
+                virBufferAsprintf(&buf, " burst='%llu'", outbound.burst);
+            virBufferAsprintf(&buf, "/>\n");
+        }
+        virBufferAsprintf(&buf, "  </bandwidth>\n");
+    }
+
+    virBufferAddLit(&buf, "</interface>\n");
+
+    if (virBufferError(&buf)) {
+        vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
+        goto cleanup;
+    }
+
+    xml = virBufferContentAndReset(&buf);
+
+    if (vshCommandOptBool(cmd, "config")) {
+        flags = VIR_DOMAIN_AFFECT_CONFIG;
+        if (virDomainIsActive(dom) == 1)
+            flags |= VIR_DOMAIN_AFFECT_LIVE;
+        ret = virDomainAttachDeviceFlags(dom, xml, flags);
+    } else {
+        ret = virDomainAttachDevice(dom, xml);
+    }
+
+    VIR_FREE(xml);
+
+    if (ret != 0) {
+        vshError(ctl, "%s", _("Failed to attach interface"));
+    } else {
+        vshPrint(ctl, "%s", _("Interface attached successfully\n"));
+        functionReturn = true;
+    }
+
+ cleanup:
+    if (dom)
+        virDomainFree(dom);
+    virBufferFreeAndReset(&buf);
+    return functionReturn;
+}
+
+/*
+ * "autostart" command
+ */
+static const vshCmdInfo info_autostart[] = {
+    {"help", N_("autostart a domain")},
+    {"desc",
+     N_("Configure a domain to be automatically started at boot.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_autostart[] = {
+    {"domain",  VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"disable", VSH_OT_BOOL, 0, N_("disable autostarting")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdAutostart(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom;
+    const char *name;
+    int autostart;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+        return false;
+
+    autostart = !vshCommandOptBool(cmd, "disable");
+
+    if (virDomainSetAutostart(dom, autostart) < 0) {
+        if (autostart)
+            vshError(ctl, _("Failed to mark domain %s as autostarted"), name);
+        else
+            vshError(ctl, _("Failed to unmark domain %s as autostarted"), name);
+        virDomainFree(dom);
+        return false;
+    }
+
+    if (autostart)
+        vshPrint(ctl, _("Domain %s marked as autostarted\n"), name);
+    else
+        vshPrint(ctl, _("Domain %s unmarked as autostarted\n"), name);
+
+    virDomainFree(dom);
+    return true;
+}
+
+/*
+ * "blkdeviotune" command
+ */
+static const vshCmdInfo info_blkdeviotune[] = {
+    {"help", N_("Set or query a block device I/O tuning parameters.")},
+    {"desc", N_("Set or query disk I/O parameters such as block throttling.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_blkdeviotune[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("block device")},
+    {"total_bytes_sec", VSH_OT_ALIAS, 0, "total-bytes-sec"},
+    {"total-bytes-sec", VSH_OT_INT, VSH_OFLAG_NONE,
+     N_("total throughput limit in bytes per second")},
+    {"read_bytes_sec", VSH_OT_ALIAS, 0, "read-bytes-sec"},
+    {"read-bytes-sec", VSH_OT_INT, VSH_OFLAG_NONE,
+     N_("read throughput limit in bytes per second")},
+    {"write_bytes_sec", VSH_OT_ALIAS, 0, "write-bytes-sec"},
+    {"write-bytes-sec", VSH_OT_INT, VSH_OFLAG_NONE,
+     N_("write throughput limit in bytes per second")},
+    {"total_iops_sec", VSH_OT_ALIAS, 0, "total-iops-sec"},
+    {"total-iops-sec", VSH_OT_INT, VSH_OFLAG_NONE,
+     N_("total I/O operations limit per second")},
+    {"read_iops_sec", VSH_OT_ALIAS, 0, "read-iops-sec"},
+    {"read-iops-sec", VSH_OT_INT, VSH_OFLAG_NONE,
+     N_("read I/O operations limit per second")},
+    {"write_iops_sec", VSH_OT_ALIAS, 0, "write-iops-sec"},
+    {"write-iops-sec", VSH_OT_INT, VSH_OFLAG_NONE,
+     N_("write I/O operations limit per second")},
+    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
+    {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
+    {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdBlkdeviotune(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom = NULL;
+    const char *name, *disk;
+    unsigned long long total_bytes_sec = 0, read_bytes_sec = 0, write_bytes_sec = 0;
+    unsigned long long total_iops_sec = 0, read_iops_sec = 0, write_iops_sec = 0;
+    int nparams = 0;
+    virTypedParameterPtr params = NULL;
+    unsigned int flags = 0, i = 0;
+    int rv = 0;
+    bool current = vshCommandOptBool(cmd, "current");
+    bool config = vshCommandOptBool(cmd, "config");
+    bool live = vshCommandOptBool(cmd, "live");
+    bool ret = false;
+
+    if (current) {
+        if (live || config) {
+            vshError(ctl, "%s", _("--current must be specified exclusively"));
+            return false;
+        }
+        flags = VIR_DOMAIN_AFFECT_CURRENT;
+    } else {
+        if (config)
+            flags |= VIR_DOMAIN_AFFECT_CONFIG;
+        if (live)
+            flags |= VIR_DOMAIN_AFFECT_LIVE;
+    }
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        goto cleanup;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+        goto cleanup;
+
+    if (vshCommandOptString(cmd, "device", &disk) < 0)
+        goto cleanup;
+
+    if ((rv = vshCommandOptULongLong(cmd, "total-bytes-sec",
+                                     &total_bytes_sec)) < 0) {
+        vshError(ctl, "%s",
+                 _("Unable to parse integer parameter"));
+        goto cleanup;
+    } else if (rv > 0) {
+        nparams++;
+    }
+
+    if ((rv = vshCommandOptULongLong(cmd, "read-bytes-sec",
+                                     &read_bytes_sec)) < 0) {
+        vshError(ctl, "%s",
+                 _("Unable to parse integer parameter"));
+        goto cleanup;
+    } else if (rv > 0) {
+        nparams++;
+    }
+
+    if ((rv = vshCommandOptULongLong(cmd, "write-bytes-sec",
+                                     &write_bytes_sec)) < 0) {
+        vshError(ctl, "%s",
+                 _("Unable to parse integer parameter"));
+        goto cleanup;
+    } else if (rv > 0) {
+        nparams++;
+    }
+
+    if ((rv = vshCommandOptULongLong(cmd, "total-iops-sec",
+                                     &total_iops_sec)) < 0) {
+        vshError(ctl, "%s",
+                 _("Unable to parse integer parameter"));
+        goto cleanup;
+    } else if (rv > 0) {
+        nparams++;
+    }
+
+    if ((rv = vshCommandOptULongLong(cmd, "read-iops-sec",
+                                     &read_iops_sec)) < 0) {
+        vshError(ctl, "%s",
+                 _("Unable to parse integer parameter"));
+        goto cleanup;
+    } else if (rv > 0) {
+        nparams++;
+    }
+
+    if ((rv = vshCommandOptULongLong(cmd, "write-iops-sec",
+                                     &write_iops_sec)) < 0) {
+        vshError(ctl, "%s",
+                 _("Unable to parse integer parameter"));
+        goto cleanup;
+    } else if (rv > 0) {
+        nparams++;
+    }
+
+    if (nparams == 0) {
+
+        if (virDomainGetBlockIoTune(dom, NULL, NULL, &nparams, flags) != 0) {
+            vshError(ctl, "%s",
+                     _("Unable to get number of block I/O throttle parameters"));
+            goto cleanup;
+        }
+
+        if (nparams == 0) {
+            ret = true;
+            goto cleanup;
+        }
+
+        params = vshCalloc(ctl, nparams, sizeof(*params));
+
+        if (virDomainGetBlockIoTune(dom, disk, params, &nparams, flags) != 0) {
+            vshError(ctl, "%s",
+                     _("Unable to get block I/O throttle parameters"));
+            goto cleanup;
+        }
+
+        for (i = 0; i < nparams; i++) {
+            char *str = vshGetTypedParamValue(ctl, &params[i]);
+            vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
+            VIR_FREE(str);
+        }
+
+        ret = true;
+        goto cleanup;
+    } else {
+        /* Set the block I/O throttle, match by opt since parameters can be 0 */
+        params = vshCalloc(ctl, nparams, sizeof(*params));
+        i = 0;
+
+        if (i < nparams && vshCommandOptBool(cmd, "total-bytes-sec") &&
+            virTypedParameterAssign(&params[i++],
+                                    VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_BYTES_SEC,
+                                    VIR_TYPED_PARAM_ULLONG,
+                                    total_bytes_sec) < 0)
+            goto error;
+
+        if (i < nparams && vshCommandOptBool(cmd, "read-bytes-sec") &&
+            virTypedParameterAssign(&params[i++],
+                                    VIR_DOMAIN_BLOCK_IOTUNE_READ_BYTES_SEC,
+                                    VIR_TYPED_PARAM_ULLONG,
+                                    read_bytes_sec) < 0)
+            goto error;
+
+        if (i < nparams && vshCommandOptBool(cmd, "write-bytes-sec") &&
+            virTypedParameterAssign(&params[i++],
+                                    VIR_DOMAIN_BLOCK_IOTUNE_WRITE_BYTES_SEC,
+                                    VIR_TYPED_PARAM_ULLONG,
+                                    write_bytes_sec) < 0)
+            goto error;
+
+        if (i < nparams && vshCommandOptBool(cmd, "total-iops-sec") &&
+            virTypedParameterAssign(&params[i++],
+                                    VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_IOPS_SEC,
+                                    VIR_TYPED_PARAM_ULLONG,
+                                    total_iops_sec) < 0)
+            goto error;
+
+        if (i < nparams && vshCommandOptBool(cmd, "read-iops-sec") &&
+            virTypedParameterAssign(&params[i++],
+                                    VIR_DOMAIN_BLOCK_IOTUNE_READ_IOPS_SEC,
+                                    VIR_TYPED_PARAM_ULLONG,
+                                    read_iops_sec) < 0)
+            goto error;
+
+        if (i < nparams && vshCommandOptBool(cmd, "write-iops-sec") &&
+            virTypedParameterAssign(&params[i++],
+                                    VIR_DOMAIN_BLOCK_IOTUNE_WRITE_IOPS_SEC,
+                                    VIR_TYPED_PARAM_ULLONG,
+                                    write_iops_sec) < 0)
+            goto error;
+
+        if (virDomainSetBlockIoTune(dom, disk, params, nparams, flags) < 0)
+            goto error;
+    }
+
+    ret = true;
+
+cleanup:
+    VIR_FREE(params);
+    virDomainFree(dom);
+    return ret;
+
+error:
+    vshError(ctl, "%s", _("Unable to change block I/O throttle"));
+    goto cleanup;
+}
+
+/*
+ * "blkiotune" command
+ */
+static const vshCmdInfo info_blkiotune[] = {
+    {"help", N_("Get or set blkio parameters")},
+    {"desc", N_("Get or set the current blkio parameters for a guest"
+                " domain.\n"
+                "    To get the blkio parameters use following command: \n\n"
+                "    virsh # blkiotune <domain>")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_blkiotune[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"weight", VSH_OT_INT, VSH_OFLAG_NONE,
+     N_("IO Weight in range [100, 1000]")},
+    {"device-weights", VSH_OT_STRING, VSH_OFLAG_NONE,
+     N_("per-device IO Weights, in the form of /path/to/device,weight,...")},
+    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
+    {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
+    {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdBlkiotune(vshControl * ctl, const vshCmd * cmd)
+{
+    virDomainPtr dom;
+    const char *device_weight = NULL;
+    int weight = 0;
+    int nparams = 0;
+    int rv = 0;
+    unsigned int i = 0;
+    virTypedParameterPtr params = NULL, temp = NULL;
+    bool ret = false;
+    unsigned int flags = 0;
+    bool current = vshCommandOptBool(cmd, "current");
+    bool config = vshCommandOptBool(cmd, "config");
+    bool live = vshCommandOptBool(cmd, "live");
+
+    if (current) {
+        if (live || config) {
+            vshError(ctl, "%s", _("--current must be specified exclusively"));
+            return false;
+        }
+        flags = VIR_DOMAIN_AFFECT_CURRENT;
+    } else {
+        if (config)
+            flags |= VIR_DOMAIN_AFFECT_CONFIG;
+        if (live)
+            flags |= VIR_DOMAIN_AFFECT_LIVE;
+    }
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        return false;
+
+    if ((rv = vshCommandOptInt(cmd, "weight", &weight)) < 0) {
+        vshError(ctl, "%s",
+                 _("Unable to parse integer parameter"));
+        goto cleanup;
+    }
+
+    if (rv > 0) {
+        nparams++;
+        if (weight <= 0) {
+            vshError(ctl, _("Invalid value of %d for I/O weight"), weight);
+            goto cleanup;
+        }
+    }
+
+    rv = vshCommandOptString(cmd, "device-weights", &device_weight);
+    if (rv < 0) {
+        vshError(ctl, "%s",
+                 _("Unable to parse string parameter"));
+        goto cleanup;
+    }
+    if (rv > 0) {
+        nparams++;
+    }
+
+    if (nparams == 0) {
+        /* get the number of blkio parameters */
+        if (virDomainGetBlkioParameters(dom, NULL, &nparams, flags) != 0) {
+            vshError(ctl, "%s",
+                     _("Unable to get number of blkio parameters"));
+            goto cleanup;
+        }
+
+        if (nparams == 0) {
+            /* nothing to output */
+            ret = true;
+            goto cleanup;
+        }
+
+        /* now go get all the blkio parameters */
+        params = vshCalloc(ctl, nparams, sizeof(*params));
+        if (virDomainGetBlkioParameters(dom, params, &nparams, flags) != 0) {
+            vshError(ctl, "%s", _("Unable to get blkio parameters"));
+            goto cleanup;
+        }
+
+        for (i = 0; i < nparams; i++) {
+            char *str = vshGetTypedParamValue(ctl, &params[i]);
+            vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
+            VIR_FREE(str);
+        }
+    } else {
+        /* set the blkio parameters */
+        params = vshCalloc(ctl, nparams, sizeof(*params));
+
+        for (i = 0; i < nparams; i++) {
+            temp = &params[i];
+            temp->type = VIR_TYPED_PARAM_UINT;
+
+            if (weight) {
+                temp->value.ui = weight;
+                if (!virStrcpy(temp->field, VIR_DOMAIN_BLKIO_WEIGHT,
+                               sizeof(temp->field)))
+                    goto cleanup;
+                weight = 0;
+            } else if (device_weight) {
+                temp->value.s = vshStrdup(ctl, device_weight);
+                temp->type = VIR_TYPED_PARAM_STRING;
+                if (!virStrcpy(temp->field, VIR_DOMAIN_BLKIO_DEVICE_WEIGHT,
+                               sizeof(temp->field)))
+                    goto cleanup;
+                device_weight = NULL;
+            }
+        }
+
+        if (virDomainSetBlkioParameters(dom, params, nparams, flags) < 0) {
+            vshError(ctl, "%s", _("Unable to change blkio parameters"));
+            goto cleanup;
+        }
+    }
+
+    ret = true;
+
+  cleanup:
+    virTypedParameterArrayClear(params, nparams);
+    VIR_FREE(params);
+    virDomainFree(dom);
+    return ret;
+}
+
+typedef enum {
+    VSH_CMD_BLOCK_JOB_ABORT = 0,
+    VSH_CMD_BLOCK_JOB_INFO = 1,
+    VSH_CMD_BLOCK_JOB_SPEED = 2,
+    VSH_CMD_BLOCK_JOB_PULL = 3,
+    VSH_CMD_BLOCK_JOB_COPY = 4,
+} vshCmdBlockJobMode;
+
+static int
+blockJobImpl(vshControl *ctl, const vshCmd *cmd,
+             virDomainBlockJobInfoPtr info, int mode,
+             virDomainPtr *pdom)
+{
+    virDomainPtr dom = NULL;
+    const char *name, *path;
+    unsigned long bandwidth = 0;
+    int ret = -1;
+    const char *base = NULL;
+    unsigned int flags = 0;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        goto cleanup;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+        goto cleanup;
+
+    if (vshCommandOptString(cmd, "path", &path) < 0)
+        goto cleanup;
+
+    if (vshCommandOptUL(cmd, "bandwidth", &bandwidth) < 0) {
+        vshError(ctl, "%s", _("bandwidth must be a number"));
+        goto cleanup;
+    }
+
+    switch ((vshCmdBlockJobMode) mode) {
+    case  VSH_CMD_BLOCK_JOB_ABORT:
+        if (vshCommandOptBool(cmd, "async"))
+            flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC;
+        if (vshCommandOptBool(cmd, "pivot"))
+            flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT;
+        ret = virDomainBlockJobAbort(dom, path, flags);
+        break;
+    case VSH_CMD_BLOCK_JOB_INFO:
+        ret = virDomainGetBlockJobInfo(dom, path, info, 0);
+        break;
+    case VSH_CMD_BLOCK_JOB_SPEED:
+        ret = virDomainBlockJobSetSpeed(dom, path, bandwidth, 0);
+        break;
+    case VSH_CMD_BLOCK_JOB_PULL:
+        if (vshCommandOptString(cmd, "base", &base) < 0)
+            goto cleanup;
+        if (base)
+            ret = virDomainBlockRebase(dom, path, base, bandwidth, 0);
+        else
+            ret = virDomainBlockPull(dom, path, bandwidth, 0);
+        break;
+    case VSH_CMD_BLOCK_JOB_COPY:
+        flags |= VIR_DOMAIN_BLOCK_REBASE_COPY;
+        if (vshCommandOptBool(cmd, "shallow"))
+            flags |= VIR_DOMAIN_BLOCK_REBASE_SHALLOW;
+        if (vshCommandOptBool(cmd, "reuse-external"))
+            flags |= VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT;
+        if (vshCommandOptBool(cmd, "raw"))
+            flags |= VIR_DOMAIN_BLOCK_REBASE_COPY_RAW;
+        if (vshCommandOptString(cmd, "dest", &base) < 0)
+            goto cleanup;
+        ret = virDomainBlockRebase(dom, path, base, bandwidth, flags);
+    }
+
+cleanup:
+    if (pdom && ret == 0)
+        *pdom = dom;
+    else if (dom)
+        virDomainFree(dom);
+    return ret;
+}
+
+static void
+print_job_progress(const char *label, unsigned long long remaining,
+                   unsigned long long total)
+{
+    int progress;
+
+    if (total == 0)
+        /* migration has not been started */
+        return;
+
+    if (remaining == 0) {
+        /* migration has completed */
+        progress = 100;
+    } else {
+        /* use float to avoid overflow */
+        progress = (int)(100.0 - remaining * 100.0 / total);
+        if (progress >= 100) {
+            /* migration has not completed, do not print [100 %] */
+            progress = 99;
+        }
+    }
+
+    /* see comments in vshError about why we must flush */
+    fflush(stdout);
+    fprintf(stderr, "\r%s: [%3d %%]", label, progress);
+    fflush(stderr);
+}
+
+/*
+ * "blockcopy" command
+ */
+static const vshCmdInfo info_block_copy[] = {
+    {"help", N_("Start a block copy operation.")},
+    {"desc", N_("Populate a disk from its backing image.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_block_copy[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"path", VSH_OT_DATA, VSH_OFLAG_REQ, N_("fully-qualified path of disk")},
+    {"dest", VSH_OT_DATA, VSH_OFLAG_REQ, N_("path of the copy to create")},
+    {"bandwidth", VSH_OT_DATA, VSH_OFLAG_NONE, N_("bandwidth limit in MiB/s")},
+    {"shallow", VSH_OT_BOOL, 0, N_("make the copy share a backing chain")},
+    {"reuse-external", VSH_OT_BOOL, 0, N_("reuse existing destination")},
+    {"raw", VSH_OT_BOOL, 0, N_("use raw destination file")},
+    {"wait", VSH_OT_BOOL, 0, N_("wait for job to reach mirroring phase")},
+    {"verbose", VSH_OT_BOOL, 0, N_("with --wait, display the progress")},
+    {"timeout", VSH_OT_INT, VSH_OFLAG_NONE,
+     N_("with --wait, abort if copy exceeds timeout (in seconds)")},
+    {"pivot", VSH_OT_BOOL, 0, N_("with --wait, pivot when mirroring starts")},
+    {"finish", VSH_OT_BOOL, 0, N_("with --wait, quit when mirroring starts")},
+    {"async", VSH_OT_BOOL, 0,
+     N_("with --wait, don't wait for cancel to finish")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdBlockCopy(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom = NULL;
+    bool ret = false;
+    bool blocking = vshCommandOptBool(cmd, "wait");
+    bool verbose = vshCommandOptBool(cmd, "verbose");
+    bool pivot = vshCommandOptBool(cmd, "pivot");
+    bool finish = vshCommandOptBool(cmd, "finish");
+    int timeout = 0;
+    struct sigaction sig_action;
+    struct sigaction old_sig_action;
+    sigset_t sigmask;
+    struct timeval start;
+    struct timeval curr;
+    const char *path = NULL;
+    bool quit = false;
+    int abort_flags = 0;
+
+    if (blocking) {
+        if (pivot && finish) {
+            vshError(ctl, "%s", _("cannot mix --pivot and --finish"));
+            return false;
+        }
+        if (vshCommandOptInt(cmd, "timeout", &timeout) > 0) {
+            if (timeout < 1) {
+                vshError(ctl, "%s", _("migrate: Invalid timeout"));
+                return false;
+            }
+
+            /* Ensure that we can multiply by 1000 without overflowing. */
+            if (timeout > INT_MAX / 1000) {
+                vshError(ctl, "%s", _("migrate: Timeout is too big"));
+                return false;
+            }
+        }
+        if (vshCommandOptString(cmd, "path", &path) < 0)
+            return false;
+        if (vshCommandOptBool(cmd, "async"))
+            abort_flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC;
+
+        sigemptyset(&sigmask);
+        sigaddset(&sigmask, SIGINT);
+
+        intCaught = 0;
+        sig_action.sa_sigaction = vshCatchInt;
+        sig_action.sa_flags = SA_SIGINFO;
+        sigemptyset(&sig_action.sa_mask);
+        sigaction(SIGINT, &sig_action, &old_sig_action);
+
+        GETTIMEOFDAY(&start);
+    } else if (verbose || vshCommandOptBool(cmd, "timeout") ||
+               vshCommandOptBool(cmd, "async") || pivot || finish) {
+        vshError(ctl, "%s", _("blocking control options require --wait"));
+        return false;
+    }
+
+    if (blockJobImpl(ctl, cmd, NULL, VSH_CMD_BLOCK_JOB_COPY, &dom) < 0)
+        goto cleanup;
+
+    if (!blocking) {
+        vshPrint(ctl, "%s", _("Block Copy started"));
+        ret = true;
+        goto cleanup;
+    }
+
+    while (blocking) {
+        virDomainBlockJobInfo info;
+        int result = virDomainGetBlockJobInfo(dom, path, &info, 0);
+
+        if (result <= 0) {
+            vshError(ctl, _("failed to query job for disk %s"), path);
+            goto cleanup;
+        }
+        if (verbose)
+            print_job_progress(_("Block Copy"), info.end - info.cur, info.end);
+        if (info.cur == info.end)
+            break;
+
+        GETTIMEOFDAY(&curr);
+        if (intCaught || (timeout &&
+                          (((int)(curr.tv_sec - start.tv_sec)  * 1000 +
+                            (int)(curr.tv_usec - start.tv_usec) / 1000) >
+                           timeout * 1000))) {
+            vshDebug(ctl, VSH_ERR_DEBUG,
+                     intCaught ? "interrupted" : "timeout");
+            intCaught = 0;
+            timeout = 0;
+            quit = true;
+            if (virDomainBlockJobAbort(dom, path, abort_flags) < 0) {
+                vshError(ctl, _("failed to abort job for disk %s"), path);
+                goto cleanup;
+            }
+            if (abort_flags & VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC)
+                break;
+        } else {
+            usleep(500 * 1000);
+        }
+    }
+
+    if (pivot) {
+        abort_flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT;
+        if (virDomainBlockJobAbort(dom, path, abort_flags) < 0) {
+            vshError(ctl, _("failed to pivot job for disk %s"), path);
+            goto cleanup;
+        }
+    } else if (finish && virDomainBlockJobAbort(dom, path, abort_flags) < 0) {
+        vshError(ctl, _("failed to finish job for disk %s"), path);
+        goto cleanup;
+    }
+    vshPrint(ctl, "\n%s",
+             quit ? _("Copy aborted") :
+             pivot ? _("Successfully pivoted") :
+             finish ? _("Successfully copied") :
+             _("Now in mirroring phase"));
+
+    ret = true;
+cleanup:
+    if (dom)
+        virDomainFree(dom);
+    if (blocking)
+        sigaction(SIGINT, &old_sig_action, NULL);
+    return ret;
+}
+
+/*
+ * "blockjob" command
+ */
+static const vshCmdInfo info_block_job[] = {
+    {"help", N_("Manage active block operations")},
+    {"desc", N_("Query, adjust speed, or cancel active block operations.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_block_job[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"path", VSH_OT_DATA, VSH_OFLAG_REQ, N_("fully-qualified path of disk")},
+    {"abort", VSH_OT_BOOL, VSH_OFLAG_NONE,
+     N_("abort the active job on the specified disk")},
+    {"async", VSH_OT_BOOL, VSH_OFLAG_NONE,
+     N_("don't wait for --abort to complete")},
+    {"pivot", VSH_OT_BOOL, VSH_OFLAG_NONE,
+     N_("conclude and pivot a copy job")},
+    {"info", VSH_OT_BOOL, VSH_OFLAG_NONE,
+     N_("get active job information for the specified disk")},
+    {"bandwidth", VSH_OT_DATA, VSH_OFLAG_NONE,
+     N_("set the Bandwidth limit in MiB/s")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdBlockJob(vshControl *ctl, const vshCmd *cmd)
+{
+    int mode;
+    virDomainBlockJobInfo info;
+    const char *type;
+    int ret;
+    bool abortMode = (vshCommandOptBool(cmd, "abort") ||
+                      vshCommandOptBool(cmd, "async") ||
+                      vshCommandOptBool(cmd, "pivot"));
+    bool infoMode = vshCommandOptBool(cmd, "info");
+    bool bandwidth = vshCommandOptBool(cmd, "bandwidth");
+
+    if (abortMode + infoMode + bandwidth > 1) {
+        vshError(ctl, "%s",
+                 _("conflict between --abort, --info, and --bandwidth modes"));
+        return false;
+    }
+
+    if (abortMode)
+        mode = VSH_CMD_BLOCK_JOB_ABORT;
+    else if (bandwidth)
+        mode = VSH_CMD_BLOCK_JOB_SPEED;
+    else
+        mode = VSH_CMD_BLOCK_JOB_INFO;
+
+    ret = blockJobImpl(ctl, cmd, &info, mode, NULL);
+    if (ret < 0)
+        return false;
+
+    if (ret == 0 || mode != VSH_CMD_BLOCK_JOB_INFO)
+        return true;
+
+    switch (info.type) {
+    case VIR_DOMAIN_BLOCK_JOB_TYPE_PULL:
+        type = _("Block Pull");
+        break;
+    case VIR_DOMAIN_BLOCK_JOB_TYPE_COPY:
+        type = _("Block Copy");
+        break;
+    default:
+        type = _("Unknown job");
+        break;
+    }
+
+    print_job_progress(type, info.end - info.cur, info.end);
+    if (info.bandwidth != 0)
+        vshPrint(ctl, _("    Bandwidth limit: %lu MiB/s\n"), info.bandwidth);
+    return true;
+}
+
+/*
+ * "blockpull" command
+ */
+static const vshCmdInfo info_block_pull[] = {
+    {"help", N_("Populate a disk from its backing image.")},
+    {"desc", N_("Populate a disk from its backing image.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_block_pull[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"path", VSH_OT_DATA, VSH_OFLAG_REQ, N_("fully-qualified path of disk")},
+    {"bandwidth", VSH_OT_DATA, VSH_OFLAG_NONE, N_("bandwidth limit in MiB/s")},
+    {"base", VSH_OT_DATA, VSH_OFLAG_NONE,
+     N_("path of backing file in chain for a partial pull")},
+    {"wait", VSH_OT_BOOL, 0, N_("wait for job to finish")},
+    {"verbose", VSH_OT_BOOL, 0, N_("with --wait, display the progress")},
+    {"timeout", VSH_OT_INT, VSH_OFLAG_NONE,
+     N_("with --wait, abort if pull exceeds timeout (in seconds)")},
+    {"async", VSH_OT_BOOL, 0,
+     N_("with --wait, don't wait for cancel to finish")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdBlockPull(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom = NULL;
+    bool ret = false;
+    bool blocking = vshCommandOptBool(cmd, "wait");
+    bool verbose = vshCommandOptBool(cmd, "verbose");
+    int timeout = 0;
+    struct sigaction sig_action;
+    struct sigaction old_sig_action;
+    sigset_t sigmask;
+    struct timeval start;
+    struct timeval curr;
+    const char *path = NULL;
+    bool quit = false;
+    int abort_flags = 0;
+
+    if (blocking) {
+        if (vshCommandOptInt(cmd, "timeout", &timeout) > 0) {
+            if (timeout < 1) {
+                vshError(ctl, "%s", _("invalid timeout"));
+                return false;
+            }
+
+            /* Ensure that we can multiply by 1000 without overflowing. */
+            if (timeout > INT_MAX / 1000) {
+                vshError(ctl, "%s", _("timeout is too big"));
+                return false;
+            }
+        }
+        if (vshCommandOptString(cmd, "path", &path) < 0)
+            return false;
+        if (vshCommandOptBool(cmd, "async"))
+            abort_flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC;
+
+        sigemptyset(&sigmask);
+        sigaddset(&sigmask, SIGINT);
+
+        intCaught = 0;
+        sig_action.sa_sigaction = vshCatchInt;
+        sig_action.sa_flags = SA_SIGINFO;
+        sigemptyset(&sig_action.sa_mask);
+        sigaction(SIGINT, &sig_action, &old_sig_action);
+
+        GETTIMEOFDAY(&start);
+    } else if (verbose || vshCommandOptBool(cmd, "timeout") ||
+               vshCommandOptBool(cmd, "async")) {
+        vshError(ctl, "%s", _("blocking control options require --wait"));
+        return false;
+    }
+
+    if (blockJobImpl(ctl, cmd, NULL, VSH_CMD_BLOCK_JOB_PULL, &dom) < 0)
+        goto cleanup;
+
+    if (!blocking) {
+        vshPrint(ctl, "%s", _("Block Pull started"));
+        ret = true;
+        goto cleanup;
+    }
+
+    while (blocking) {
+        virDomainBlockJobInfo info;
+        int result = virDomainGetBlockJobInfo(dom, path, &info, 0);
+
+        if (result < 0) {
+            vshError(ctl, _("failed to query job for disk %s"), path);
+            goto cleanup;
+        }
+        if (result == 0)
+            break;
+
+        if (verbose)
+            print_job_progress(_("Block Pull"), info.end - info.cur, info.end);
+
+        GETTIMEOFDAY(&curr);
+        if (intCaught || (timeout &&
+                          (((int)(curr.tv_sec - start.tv_sec)  * 1000 +
+                            (int)(curr.tv_usec - start.tv_usec) / 1000) >
+                           timeout * 1000))) {
+            vshDebug(ctl, VSH_ERR_DEBUG,
+                     intCaught ? "interrupted" : "timeout");
+            intCaught = 0;
+            timeout = 0;
+            quit = true;
+            if (virDomainBlockJobAbort(dom, path, abort_flags) < 0) {
+                vshError(ctl, _("failed to abort job for disk %s"), path);
+                goto cleanup;
+            }
+            if (abort_flags & VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC)
+                break;
+        } else {
+            usleep(500 * 1000);
+        }
+    }
+
+    if (verbose && !quit) {
+        /* printf [100 %] */
+        print_job_progress(_("Block Pull"), 0, 1);
+    }
+    vshPrint(ctl, "\n%s", quit ? _("Pull aborted") : _("Pull complete"));
+
+    ret = true;
+cleanup:
+    if (dom)
+        virDomainFree(dom);
+    if (blocking)
+        sigaction(SIGINT, &old_sig_action, NULL);
+    return ret;
+}
+
+/*
+ * "blockresize" command
+ */
+static const vshCmdInfo info_block_resize[] = {
+    {"help", N_("Resize block device of domain.")},
+    {"desc", N_("Resize block device of domain.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_block_resize[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"path", VSH_OT_DATA, VSH_OFLAG_REQ,
+     N_("Fully-qualified path of block device")},
+    {"size", VSH_OT_INT, VSH_OFLAG_REQ,
+     N_("New size of the block device, as scaled integer (default KiB)")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdBlockResize(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom;
+    const char *path = NULL;
+    unsigned long long size = 0;
+    unsigned int flags = 0;
+    int ret = false;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (vshCommandOptString(cmd, "path", (const char **) &path) < 0) {
+        vshError(ctl, "%s", _("Path must not be empty"));
+        return false;
+    }
+
+    if (vshCommandOptScaledInt(cmd, "size", &size, 1024, ULLONG_MAX) < 0) {
+        vshError(ctl, "%s", _("Unable to parse integer"));
+        return false;
+    }
+
+    /* Prefer the older interface of KiB.  */
+    if (size % 1024 == 0)
+        size /= 1024;
+    else
+        flags |= VIR_DOMAIN_BLOCK_RESIZE_BYTES;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        return false;
+
+    if (virDomainBlockResize(dom, path, size, flags) < 0) {
+        vshError(ctl, _("Failed to resize block device '%s'"), path);
+    } else {
+        vshPrint(ctl, _("Block device '%s' is resized"), path);
+        ret = true;
+    }
+
+    virDomainFree(dom);
+    return ret;
+}
+
+#ifndef WIN32
+/*
+ * "console" command
+ */
+static const vshCmdInfo info_console[] = {
+    {"help", N_("connect to the guest console")},
+    {"desc",
+     N_("Connect the virtual serial console for the guest")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_console[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"devname", VSH_OT_STRING, 0, N_("character device name")},
+    {"force", VSH_OT_BOOL, 0,
+      N_("force console connection (disconnect already connected sessions)")},
+    {"safe", VSH_OT_BOOL, 0,
+      N_("only connect if safe console handling is supported")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdRunConsole(vshControl *ctl, virDomainPtr dom,
+              const char *name,
+              unsigned int flags)
+{
+    bool ret = false;
+    int state;
+
+    if ((state = vshDomainState(ctl, dom, NULL)) < 0) {
+        vshError(ctl, "%s", _("Unable to get domain status"));
+        goto cleanup;
+    }
+
+    if (state == VIR_DOMAIN_SHUTOFF) {
+        vshError(ctl, "%s", _("The domain is not running"));
+        goto cleanup;
+    }
+
+    if (!isatty(STDIN_FILENO)) {
+        vshError(ctl, "%s", _("Cannot run interactive console without a controlling TTY"));
+        goto cleanup;
+    }
+
+    vshPrintExtra(ctl, _("Connected to domain %s\n"), virDomainGetName(dom));
+    vshPrintExtra(ctl, _("Escape character is %s\n"), ctl->escapeChar);
+    fflush(stdout);
+    if (vshRunConsole(dom, name, ctl->escapeChar, flags) == 0)
+        ret = true;
+
+ cleanup:
+
+    return ret;
+}
+#endif /* WIN32 */
+
+static bool
+cmdConsole(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom;
+    bool ret = false;
+    bool force = vshCommandOptBool(cmd, "force");
+    bool safe = vshCommandOptBool(cmd, "safe");
+    unsigned int flags = 0;
+    const char *name = NULL;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        return false;
+
+    if (vshCommandOptString(cmd, "devname", &name) < 0) {
+        vshError(ctl, "%s", _("Invalid devname"));
+        goto cleanup;
+    }
+
+    if (force)
+        flags |= VIR_DOMAIN_CONSOLE_FORCE;
+    if (safe)
+        flags |= VIR_DOMAIN_CONSOLE_SAFE;
+
+    ret = cmdRunConsole(ctl, dom, name, flags);
+
+cleanup:
+    virDomainFree(dom);
+    return ret;
+}
+
+/* "domif-setlink" command
+ */
+static const vshCmdInfo info_domif_setlink[] = {
+    {"help", N_("set link state of a virtual interface")},
+    {"desc", N_("Set link state of a domain's virtual interface. This command wraps usage of update-device command.")},
+    {NULL,NULL}
+};
+
+static const vshCmdOptDef opts_domif_setlink[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface device (MAC Address)")},
+    {"state", VSH_OT_DATA, VSH_OFLAG_REQ, N_("new state of the device")},
+    {"persistent", VSH_OT_ALIAS, 0, "config"},
+    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDomIfSetLink(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom;
+    const char *iface;
+    const char *state;
+    const char *value;
+    const char *desc;
+    virMacAddr macaddr;
+    const char *element;
+    const char *attr;
+    bool config;
+    bool ret = false;
+    unsigned int flags = 0;
+    int i;
+    xmlDocPtr xml = NULL;
+    xmlXPathContextPtr ctxt = NULL;
+    xmlXPathObjectPtr obj = NULL;
+    xmlNodePtr cur = NULL;
+    xmlBufferPtr xml_buf = NULL;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        return false;
+
+    if (vshCommandOptString(cmd, "interface", &iface) <= 0)
+        goto cleanup;
+
+    if (vshCommandOptString(cmd, "state", &state) <= 0)
+        goto cleanup;
+
+    config = vshCommandOptBool(cmd, "config");
+
+    if (STRNEQ(state, "up") && STRNEQ(state, "down")) {
+        vshError(ctl, _("invalid link state '%s'"), state);
+        goto cleanup;
+    }
+
+    /* get persistent or live description of network device */
+    desc = virDomainGetXMLDesc(dom, config ? VIR_DOMAIN_XML_INACTIVE : 0);
+    if (desc == NULL) {
+        vshError(ctl, _("Failed to get domain description xml"));
+        goto cleanup;
+    }
+
+    if (config)
+        flags = VIR_DOMAIN_AFFECT_CONFIG;
+    else
+        flags = VIR_DOMAIN_AFFECT_LIVE;
+
+    if (virDomainIsActive(dom) == 0)
+        flags = VIR_DOMAIN_AFFECT_CONFIG;
+
+    /* extract current network device description */
+    xml = virXMLParseStringCtxt(desc, _("(domain_definition)"), &ctxt);
+    VIR_FREE(desc);
+    if (!xml) {
+        vshError(ctl, _("Failed to parse domain description xml"));
+        goto cleanup;
+    }
+
+    obj = xmlXPathEval(BAD_CAST "/domain/devices/interface", ctxt);
+    if (obj == NULL || obj->type != XPATH_NODESET ||
+        obj->nodesetval == NULL || obj->nodesetval->nodeNr == 0) {
+        vshError(ctl, _("Failed to extract interface information or no interfaces found"));
+        goto cleanup;
+    }
+
+    if (virMacAddrParse(iface, &macaddr) == 0) {
+        element = "mac";
+        attr = "address";
+    } else {
+        element = "target";
+        attr = "dev";
+    }
+
+    /* find interface with matching mac addr */
+    for (i = 0; i < obj->nodesetval->nodeNr; i++) {
+        cur = obj->nodesetval->nodeTab[i]->children;
+
+        while (cur) {
+            if (cur->type == XML_ELEMENT_NODE &&
+                xmlStrEqual(cur->name, BAD_CAST element)) {
+                value = virXMLPropString(cur, attr);
+
+                if (STRCASEEQ(value, iface)) {
+                    VIR_FREE(value);
+                    goto hit;
+                }
+                VIR_FREE(value);
+            }
+            cur = cur->next;
+        }
+    }
+
+    vshError(ctl, _("interface (%s: %s) not found"), element, iface);
+    goto cleanup;
+
+hit:
+    /* find and modify/add link state node */
+    /* try to find <link> element */
+    cur = obj->nodesetval->nodeTab[i]->children;
+
+    while (cur) {
+        if (cur->type == XML_ELEMENT_NODE &&
+            xmlStrEqual(cur->name, BAD_CAST "link")) {
+            /* found, just modify the property */
+            xmlSetProp(cur, BAD_CAST "state", BAD_CAST state);
+
+            break;
+        }
+        cur = cur->next;
+    }
+
+    if (!cur) {
+        /* element <link> not found, add one */
+        cur = xmlNewChild(obj->nodesetval->nodeTab[i],
+                          NULL,
+                          BAD_CAST "link",
+                          NULL);
+        if (!cur)
+            goto cleanup;
+
+        if (xmlNewProp(cur, BAD_CAST "state", BAD_CAST state) == NULL)
+            goto cleanup;
+    }
+
+    xml_buf = xmlBufferCreate();
+    if (!xml_buf) {
+        vshError(ctl, _("Failed to allocate memory"));
+        goto cleanup;
+    }
+
+    if (xmlNodeDump(xml_buf, xml, obj->nodesetval->nodeTab[i], 0, 0) < 0 ) {
+        vshError(ctl, _("Failed to create XML"));
+        goto cleanup;
+    }
+
+    if (virDomainUpdateDeviceFlags(dom, (char *)xmlBufferContent(xml_buf), flags) < 0) {
+        vshError(ctl, _("Failed to update interface link state"));
+        goto cleanup;
+    } else {
+        vshPrint(ctl, "%s", _("Device updated successfully\n"));
+        ret = true;
+    }
+
+cleanup:
+    xmlXPathFreeObject(obj);
+    xmlXPathFreeContext(ctxt);
+    xmlFreeDoc(xml);
+    xmlBufferFree(xml_buf);
+
+    if (dom)
+        virDomainFree(dom);
+
+    return ret;
+}
+
+/* "domiftune" command
+ */
+static const vshCmdInfo info_domiftune[] = {
+    {"help", N_("get/set parameters of a virtual interface")},
+    {"desc", N_("Get/set parameters of a domain's virtual interface.")},
+    {NULL,NULL}
+};
+
+static const vshCmdOptDef opts_domiftune[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface device (MAC Address)")},
+    {"inbound", VSH_OT_DATA, VSH_OFLAG_NONE, N_("control domain's incoming traffics")},
+    {"outbound", VSH_OT_DATA, VSH_OFLAG_NONE, N_("control domain's outgoing traffics")},
+    {"config", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("affect next boot")},
+    {"live", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("affect running domain")},
+    {"current", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("affect current domain")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDomIftune(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom;
+    const char *name = NULL, *device = NULL,
+               *inboundStr = NULL, *outboundStr = NULL;
+    unsigned int flags = 0;
+    int nparams = 0;
+    virTypedParameterPtr params = NULL;
+    bool ret = false;
+    bool current = vshCommandOptBool(cmd, "current");
+    bool config = vshCommandOptBool(cmd, "config");
+    bool live = vshCommandOptBool(cmd, "live");
+    virNetDevBandwidthRate inbound, outbound;
+    int i;
+
+    if (current) {
+        if (live || config) {
+            vshError(ctl, "%s", _("--current must be specified exclusively"));
+            return false;
+        }
+        flags = VIR_DOMAIN_AFFECT_CURRENT;
+    } else {
+        if (config)
+            flags |= VIR_DOMAIN_AFFECT_CONFIG;
+        if (live)
+            flags |= VIR_DOMAIN_AFFECT_LIVE;
+    }
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+        return false;
+
+    if (vshCommandOptString(cmd, "interface", &device) <= 0) {
+        virDomainFree(dom);
+        return false;
+    }
+
+    if (vshCommandOptString(cmd, "inbound", &inboundStr) < 0 ||
+        vshCommandOptString(cmd, "outbound", &outboundStr) < 0) {
+        vshError(ctl, "missing argument");
+        goto cleanup;
+    }
+
+    memset(&inbound, 0, sizeof(inbound));
+    memset(&outbound, 0, sizeof(outbound));
+
+    if (inboundStr) {
+        if (parseRateStr(inboundStr, &inbound) < 0) {
+            vshError(ctl, _("inbound format is incorrect"));
+            goto cleanup;
+        }
+        if (inbound.average == 0) {
+            vshError(ctl, _("inbound average is mandatory"));
+            goto cleanup;
+        }
+        nparams++; /* average */
+        if (inbound.peak) nparams++;
+        if (inbound.burst) nparams++;
+    }
+    if (outboundStr) {
+        if (parseRateStr(outboundStr, &outbound) < 0) {
+            vshError(ctl, _("outbound format is incorrect"));
+            goto cleanup;
+        }
+        if (outbound.average == 0) {
+            vshError(ctl, _("outbound average is mandatory"));
+            goto cleanup;
+        }
+        nparams++; /* average */
+        if (outbound.peak) nparams++;
+        if (outbound.burst) nparams++;
+    }
+
+    if (nparams == 0) {
+        /* get the number of interface parameters */
+        if (virDomainGetInterfaceParameters(dom, device, NULL, &nparams, flags) != 0) {
+            vshError(ctl, "%s",
+                     _("Unable to get number of interface parameters"));
+            goto cleanup;
+        }
+
+        if (nparams == 0) {
+            /* nothing to output */
+            ret = true;
+            goto cleanup;
+        }
+
+        /* get all interface parameters */
+        params = vshCalloc(ctl, nparams, sizeof(*params));
+        if (!params) {
+            virReportOOMError();
+            goto cleanup;
+        }
+        if (virDomainGetInterfaceParameters(dom, device, params, &nparams, flags) != 0) {
+            vshError(ctl, "%s", _("Unable to get interface parameters"));
+            goto cleanup;
+        }
+
+        for (i = 0; i < nparams; i++) {
+            char *str = vshGetTypedParamValue(ctl, &params[i]);
+            vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
+            VIR_FREE(str);
+        }
+    } else {
+        /* set the interface parameters */
+        params = vshCalloc(ctl, nparams, sizeof(*params));
+        if (!params) {
+            virReportOOMError();
+            goto cleanup;
+        }
+
+        for (i = 0; i < nparams; i++)
+            params[i].type = VIR_TYPED_PARAM_UINT;
+
+        i = 0;
+        if (inbound.average && i < nparams) {
+            if (!virStrcpy(params[i].field, VIR_DOMAIN_BANDWIDTH_IN_AVERAGE,
+                           sizeof(params[i].field)))
+                goto cleanup;
+            params[i].value.ui = inbound.average;
+            i++;
+        }
+        if (inbound.peak && i < nparams) {
+            if (!virStrcpy(params[i].field, VIR_DOMAIN_BANDWIDTH_IN_PEAK,
+                           sizeof(params[i].field)))
+                goto cleanup;
+            params[i].value.ui = inbound.peak;
+            i++;
+        }
+        if (inbound.burst && i < nparams) {
+            if (!virStrcpy(params[i].field, VIR_DOMAIN_BANDWIDTH_IN_BURST,
+                           sizeof(params[i].field)))
+                goto cleanup;
+            params[i].value.ui = inbound.burst;
+            i++;
+        }
+        if (outbound.average && i < nparams) {
+            if (!virStrcpy(params[i].field, VIR_DOMAIN_BANDWIDTH_OUT_AVERAGE,
+                           sizeof(params[i].field)))
+                goto cleanup;
+            params[i].value.ui = outbound.average;
+            i++;
+        }
+        if (outbound.peak && i < nparams) {
+            if (!virStrcpy(params[i].field, VIR_DOMAIN_BANDWIDTH_OUT_PEAK,
+                           sizeof(params[i].field)))
+                goto cleanup;
+            params[i].value.ui = outbound.peak;
+            i++;
+        }
+        if (outbound.burst && i < nparams) {
+            if (!virStrcpy(params[i].field, VIR_DOMAIN_BANDWIDTH_OUT_BURST,
+                           sizeof(params[i].field)))
+                goto cleanup;
+            params[i].value.ui = outbound.burst;
+            i++;
+        }
+
+        if (virDomainSetInterfaceParameters(dom, device, params, nparams, flags) != 0) {
+            vshError(ctl, "%s", _("Unable to set interface parameters"));
+            goto cleanup;
+        }
+    }
+
+    ret = true;
+
+cleanup:
+    virTypedParameterArrayClear(params, nparams);
+    VIR_FREE(params);
+    virDomainFree(dom);
+    return ret;
+}
+
+/*
+ * "suspend" command
+ */
+static const vshCmdInfo info_suspend[] = {
+    {"help", N_("suspend a domain")},
+    {"desc", N_("Suspend a running domain.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_suspend[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdSuspend(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom;
+    const char *name;
+    bool ret = true;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+        return false;
+
+    if (virDomainSuspend(dom) == 0) {
+        vshPrint(ctl, _("Domain %s suspended\n"), name);
+    } else {
+        vshError(ctl, _("Failed to suspend domain %s"), name);
+        ret = false;
+    }
+
+    virDomainFree(dom);
+    return ret;
+}
+
+/*
+ * "dompmsuspend" command
+ */
+static const vshCmdInfo info_dom_pm_suspend[] = {
+    {"help", N_("suspend a domain gracefully using power management "
+                "functions")},
+    {"desc", N_("Suspends a running domain using guest OS's power management. "
+                "(Note: This requires a guest agent configured and running in "
+                "the guest OS).")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_dom_pm_suspend[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"duration", VSH_OT_INT, VSH_OFLAG_REQ_OPT, N_("duration in seconds")},
+    {"target", VSH_OT_STRING, VSH_OFLAG_REQ, N_("mem(Suspend-to-RAM), "
+                                                "disk(Suspend-to-Disk), "
+                                                "hybrid(Hybrid-Suspend)")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDomPMSuspend(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom;
+    const char *name;
+    bool ret = false;
+    const char *target = NULL;
+    unsigned int suspendTarget;
+    unsigned long long duration = 0;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+        return false;
+
+    if (vshCommandOptULongLong(cmd, "duration", &duration) < 0) {
+        vshError(ctl, _("Invalid duration argument"));
+        goto cleanup;
+    }
+
+    if (vshCommandOptString(cmd, "target", &target) < 0) {
+        vshError(ctl, _("Invalid target argument"));
+        goto cleanup;
+    }
+
+    if (STREQ(target, "mem"))
+        suspendTarget = VIR_NODE_SUSPEND_TARGET_MEM;
+    else if (STREQ(target, "disk"))
+        suspendTarget = VIR_NODE_SUSPEND_TARGET_DISK;
+    else if (STREQ(target, "hybrid"))
+        suspendTarget = VIR_NODE_SUSPEND_TARGET_HYBRID;
+    else {
+        vshError(ctl, "%s", _("Invalid target"));
+        goto cleanup;
+    }
+
+    if (virDomainPMSuspendForDuration(dom, suspendTarget, duration, 0) < 0) {
+        vshError(ctl, _("Domain %s could not be suspended"),
+                 virDomainGetName(dom));
+        goto cleanup;
+    }
+
+    vshPrint(ctl, _("Domain %s successfully suspended"),
+             virDomainGetName(dom));
+
+    ret = true;
+
+cleanup:
+    virDomainFree(dom);
+    return ret;
+}
+
+/*
+ * "dompmwakeup" command
+ */
+
+static const vshCmdInfo info_dom_pm_wakeup[] = {
+    {"help", N_("wakeup a domain suspended by dompmsuspend command")},
+    {"desc", N_("Wakeup a domain previously suspended "
+                "by dompmsuspend command.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_dom_pm_wakeup[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDomPMWakeup(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom;
+    const char *name;
+    bool ret = false;
+    unsigned int flags = 0;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+        return false;
+
+    if (virDomainPMWakeup(dom, flags) < 0) {
+        vshError(ctl, _("Domain %s could not be woken up"),
+                 virDomainGetName(dom));
+        goto cleanup;
+    }
+
+    vshPrint(ctl, _("Domain %s successfully woken up"),
+             virDomainGetName(dom));
+
+    ret = true;
+
+cleanup:
+    virDomainFree(dom);
+    return ret;
+}
+
+/*
+ * "undefine" command
+ */
+static const vshCmdInfo info_undefine[] = {
+    {"help", N_("undefine a domain")},
+    {"desc",
+     N_("Undefine an inactive domain, or convert persistent to transient.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_undefine[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name or uuid")},
+    {"managed-save", VSH_OT_BOOL, 0, N_("remove domain managed state file")},
+    {"storage", VSH_OT_DATA, VSH_OFLAG_NONE,
+     N_("remove associated storage volumes (comma separated list of targets "
+        "or source paths) (see domblklist)")},
+    {"remove-all-storage", VSH_OT_BOOL, 0,
+     N_("remove all associated storage volumes (use with caution)")},
+    {"wipe-storage", VSH_OT_BOOL, VSH_OFLAG_NONE,
+     N_("wipe data on the removed volumes")},
+    {"snapshots-metadata", VSH_OT_BOOL, 0,
+     N_("remove all domain snapshot metadata, if inactive")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdUndefine(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom;
+    bool ret = false;
+    const char *name = NULL;
+    /* Flags to attempt.  */
+    unsigned int flags = 0;
+    /* User-requested actions.  */
+    bool managed_save = vshCommandOptBool(cmd, "managed-save");
+    bool snapshots_metadata = vshCommandOptBool(cmd, "snapshots-metadata");
+    bool wipe_storage = vshCommandOptBool(cmd, "wipe-storage");
+    bool remove_storage = false;
+    bool remove_all_storage = vshCommandOptBool(cmd, "remove-all-storage");
+    /* Positive if these items exist.  */
+    int has_managed_save = 0;
+    int has_snapshots_metadata = 0;
+    int has_snapshots = 0;
+    /* True if undefine will not strand data, even on older servers.  */
+    bool managed_save_safe = false;
+    bool snapshots_safe = false;
+    int rc = -1;
+    int running;
+    /* list of volumes to remove along with this domain */
+    const char *volumes_arg = NULL;
+    char *volumes = NULL;
+    char **volume_tokens = NULL;
+    char *volume_tok = NULL;
+    int nvolume_tokens = 0;
+    char *def = NULL;
+    char *source = NULL;
+    char *target = NULL;
+    int vol_i;
+    int tok_i;
+    xmlDocPtr doc = NULL;
+    xmlXPathContextPtr ctxt = NULL;
+    xmlNodePtr *vol_nodes = NULL;
+    int nvolumes = 0;
+    virStorageVolPtr vol = NULL;
+    bool vol_del_failed = false;
+
+    if (managed_save) {
+        flags |= VIR_DOMAIN_UNDEFINE_MANAGED_SAVE;
+        managed_save_safe = true;
+    }
+    if (snapshots_metadata) {
+        flags |= VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA;
+        snapshots_safe = true;
+    }
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+        return false;
+
+    /* check if a string that should contain list of volumes to remove is present */
+    if (vshCommandOptString(cmd, "storage", &volumes_arg) > 0) {
+        volumes = vshStrdup(ctl, volumes_arg);
+
+        if (remove_all_storage) {
+            vshError(ctl, _("Specified both --storage and --remove-all-storage"));
+            goto cleanup;
+        }
+        remove_storage = true;
+   }
+
+    /* Do some flag manipulation.  The goal here is to disable bits
+     * from flags to reduce the likelihood of a server rejecting
+     * unknown flag bits, as well as to track conditions which are
+     * safe by default for the given hypervisor and server version.  */
+    running = virDomainIsActive(dom);
+    if (running < 0) {
+        virshReportError(ctl);
+        goto cleanup;
+    }
+    if (!running) {
+        /* Undefine with snapshots only fails for inactive domains,
+         * and managed save only exists on inactive domains; if
+         * running, then we don't want to remove anything.  */
+        has_managed_save = virDomainHasManagedSaveImage(dom, 0);
+        if (has_managed_save < 0) {
+            if (last_error->code != VIR_ERR_NO_SUPPORT) {
+                virshReportError(ctl);
+                goto cleanup;
+            }
+            virFreeError(last_error);
+            last_error = NULL;
+            has_managed_save = 0;
+        }
+
+        has_snapshots = virDomainSnapshotNum(dom, 0);
+        if (has_snapshots < 0) {
+            if (last_error->code != VIR_ERR_NO_SUPPORT) {
+                virshReportError(ctl);
+                goto cleanup;
+            }
+            virFreeError(last_error);
+            last_error = NULL;
+            has_snapshots = 0;
+        }
+        if (has_snapshots) {
+            has_snapshots_metadata
+                = virDomainSnapshotNum(dom, VIR_DOMAIN_SNAPSHOT_LIST_METADATA);
+            if (has_snapshots_metadata < 0) {
+                /* The server did not know the new flag, assume that all
+                   snapshots have metadata.  */
+                virFreeError(last_error);
+                last_error = NULL;
+                has_snapshots_metadata = has_snapshots;
+            } else {
+                /* The server knew the new flag, all aspects of
+                 * undefineFlags are safe.  */
+                managed_save_safe = snapshots_safe = true;
+            }
+        }
+    }
+    if (!has_managed_save) {
+        flags &= ~VIR_DOMAIN_UNDEFINE_MANAGED_SAVE;
+        managed_save_safe = true;
+    }
+    if (has_snapshots == 0) {
+        snapshots_safe = true;
+    }
+    if (has_snapshots_metadata == 0) {
+        flags &= ~VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA;
+        snapshots_safe = true;
+    }
+
+    /* Stash domain description for later use */
+    if (remove_storage || remove_all_storage) {
+        if (running) {
+            vshError(ctl, _("Storage volume deletion is supported only on stopped domains"));
+            goto cleanup;
+        }
+
+        if (!(def = virDomainGetXMLDesc(dom, 0))) {
+            vshError(ctl, _("Could not retrieve domain XML description"));
+            goto cleanup;
+        }
+    }
+
+    /* Generally we want to try the new API first.  However, while
+     * virDomainUndefineFlags was introduced at the same time as
+     * VIR_DOMAIN_UNDEFINE_MANAGED_SAVE in 0.9.4, the
+     * VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA flag was not present
+     * until 0.9.5; skip to piecewise emulation if we couldn't prove
+     * above that the new API is safe.  */
+    if (managed_save_safe && snapshots_safe) {
+        rc = virDomainUndefineFlags(dom, flags);
+        if (rc == 0 || (last_error->code != VIR_ERR_NO_SUPPORT &&
+                        last_error->code != VIR_ERR_INVALID_ARG))
+            goto out;
+        virFreeError(last_error);
+        last_error = NULL;
+    }
+
+    /* The new API is unsupported or unsafe; fall back to doing things
+     * piecewise.  */
+    if (has_managed_save) {
+        if (!managed_save) {
+            vshError(ctl, "%s",
+                     _("Refusing to undefine while domain managed save "
+                       "image exists"));
+            goto cleanup;
+        }
+        if (virDomainManagedSaveRemove(dom, 0) < 0) {
+            virshReportError(ctl);
+            goto cleanup;
+        }
+    }
+
+    /* No way to emulate deletion of just snapshot metadata
+     * without support for the newer flags.  Oh well.  */
+    if (has_snapshots_metadata) {
+        vshError(ctl,
+                 snapshots_metadata ?
+                 _("Unable to remove metadata of %d snapshots") :
+                 _("Refusing to undefine while %d snapshots exist"),
+                 has_snapshots_metadata);
+        goto cleanup;
+    }
+
+    rc = virDomainUndefine(dom);
+
+out:
+    if (rc == 0) {
+        vshPrint(ctl, _("Domain %s has been undefined\n"), name);
+        ret = true;
+    } else {
+        vshError(ctl, _("Failed to undefine domain %s"), name);
+        goto cleanup;
+    }
+
+    /* try to undefine storage volumes associated with this domain, if it's requested */
+    if (remove_storage || remove_all_storage) {
+        ret = false;
+
+        /* tokenize the string from user and save it's parts into an array */
+        if (volumes) {
+            /* count the delimiters */
+            volume_tok = volumes;
+            nvolume_tokens = 1; /* we need at least one member */
+            while (*volume_tok) {
+                if (*volume_tok == ',')
+                    nvolume_tokens++;
+                volume_tok++;
+            }
+
+            volume_tokens = vshCalloc(ctl, nvolume_tokens,  sizeof(char *));
+
+            /* tokenize the input string */
+            nvolume_tokens = 0;
+            volume_tok = volumes;
+            do {
+                volume_tokens[nvolume_tokens] = strsep(&volume_tok, ",");
+                nvolume_tokens++;
+            } while (volume_tok);
+        }
+
+        doc = virXMLParseStringCtxt(def, _("(domain_definition)"), &ctxt);
+        if (!doc)
+            goto cleanup;
+
+        nvolumes = virXPathNodeSet("./devices/disk", ctxt, &vol_nodes);
+
+        if (nvolumes < 0)
+            goto cleanup;
+
+        for (vol_i = 0; vol_i < nvolumes; vol_i++) {
+            ctxt->node = vol_nodes[vol_i];
+            VIR_FREE(target);
+            VIR_FREE(source);
+            if (vol) {
+                virStorageVolFree(vol);
+                vol = NULL;
+            }
+
+            /* get volume source and target paths */
+            if (!(target = virXPathString("string(./target/@dev)", ctxt))) {
+                vshError(ctl, _("Failed to enumerate devices"));
+                goto cleanup;
+            }
+
+            if (!(source = virXPathString("string("
+                                          "./source/@file|"
+                                          "./source/@dir|"
+                                          "./source/@name|"
+                                          "./source/@dev)", ctxt)) &&
+                virGetLastError())
+                goto cleanup;
+
+            /* lookup if volume was selected by user */
+            if (volumes) {
+                volume_tok = NULL;
+                for (tok_i = 0; tok_i < nvolume_tokens; tok_i++) {
+                    if (volume_tokens[tok_i] &&
+                        (STREQ_NULLABLE(volume_tokens[tok_i], target) ||
+                         STREQ_NULLABLE(volume_tokens[tok_i], source))) {
+                        volume_tok = volume_tokens[tok_i];
+                        volume_tokens[tok_i] = NULL;
+                        break;
+                    }
+                }
+                if (!volume_tok)
+                    continue;
+            }
+
+            if (!source)
+                continue;
+
+            if (!(vol = virStorageVolLookupByPath(ctl->conn, source))) {
+                vshPrint(ctl,
+                         _("Storage volume '%s'(%s) is not managed by libvirt. "
+                           "Remove it manually.\n"), target, source);
+                virResetLastError();
+                continue;
+            }
+
+            if (wipe_storage) {
+                vshPrint(ctl, _("Wiping volume '%s'(%s) ... "), target, source);
+                fflush(stdout);
+                if (virStorageVolWipe(vol, 0) < 0) {
+                    vshError(ctl, _("Failed! Volume not removed."));
+                    vol_del_failed = true;
+                    continue;
+                } else {
+                    vshPrint(ctl, _("Done.\n"));
+                }
+            }
+
+            /* delete the volume */
+            if (virStorageVolDelete(vol, 0) < 0) {
+                vshError(ctl, _("Failed to remove storage volume '%s'(%s)"),
+                         target, source);
+                vol_del_failed = true;
+            }
+            vshPrint(ctl, _("Volume '%s' removed.\n"), volume_tok?volume_tok:source);
+        }
+
+        /* print volumes specified by user that were not found in domain definition */
+        if (volumes) {
+            for (tok_i = 0; tok_i < nvolume_tokens; tok_i++) {
+                if (volume_tokens[tok_i])
+                    vshPrint(ctl, _("Volume '%s' was not found in domain's "
+                                    "definition.\n"),
+                             volume_tokens[tok_i]);
+            }
+        }
+
+        if (!vol_del_failed)
+            ret = true;
+    }
+
+cleanup:
+    VIR_FREE(source);
+    VIR_FREE(target);
+    VIR_FREE(volumes);
+    VIR_FREE(volume_tokens);
+    VIR_FREE(def);
+    VIR_FREE(vol_nodes);
+    if (vol)
+        virStorageVolFree(vol);
+    xmlFreeDoc(doc);
+    xmlXPathFreeContext(ctxt);
+    virDomainFree(dom);
+    return ret;
+}
+
+/*
+ * "start" command
+ */
+static const vshCmdInfo info_start[] = {
+    {"help", N_("start a (previously defined) inactive domain")},
+    {"desc", N_("Start a domain, either from the last managedsave\n"
+                "    state, or via a fresh boot if no managedsave state\n"
+                "    is present.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_start[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("name of the inactive domain")},
+#ifndef WIN32
+    {"console", VSH_OT_BOOL, 0, N_("attach to console after creation")},
+#endif
+    {"paused", VSH_OT_BOOL, 0, N_("leave the guest paused after creation")},
+    {"autodestroy", VSH_OT_BOOL, 0,
+     N_("automatically destroy the guest when virsh disconnects")},
+    {"bypass-cache", VSH_OT_BOOL, 0,
+     N_("avoid file system cache when loading")},
+    {"force-boot", VSH_OT_BOOL, 0,
+     N_("force fresh boot by discarding any managed save")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdStart(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom;
+    bool ret = false;
+#ifndef WIN32
+    bool console = vshCommandOptBool(cmd, "console");
+#endif
+    unsigned int flags = VIR_DOMAIN_NONE;
+    int rc;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomainBy(ctl, cmd, NULL,
+                                      VSH_BYNAME | VSH_BYUUID)))
+        return false;
+
+    if (virDomainGetID(dom) != (unsigned int)-1) {
+        vshError(ctl, "%s", _("Domain is already active"));
+        virDomainFree(dom);
+        return false;
+    }
+
+    if (vshCommandOptBool(cmd, "paused"))
+        flags |= VIR_DOMAIN_START_PAUSED;
+    if (vshCommandOptBool(cmd, "autodestroy"))
+        flags |= VIR_DOMAIN_START_AUTODESTROY;
+    if (vshCommandOptBool(cmd, "bypass-cache"))
+        flags |= VIR_DOMAIN_START_BYPASS_CACHE;
+    if (vshCommandOptBool(cmd, "force-boot"))
+        flags |= VIR_DOMAIN_START_FORCE_BOOT;
+
+    /* We can emulate force boot, even for older servers that reject it.  */
+    if (flags & VIR_DOMAIN_START_FORCE_BOOT) {
+        if (virDomainCreateWithFlags(dom, flags) == 0)
+            goto started;
+        if (last_error->code != VIR_ERR_NO_SUPPORT &&
+            last_error->code != VIR_ERR_INVALID_ARG) {
+            virshReportError(ctl);
+            goto cleanup;
+        }
+        virFreeError(last_error);
+        last_error = NULL;
+        rc = virDomainHasManagedSaveImage(dom, 0);
+        if (rc < 0) {
+            /* No managed save image to remove */
+            virFreeError(last_error);
+            last_error = NULL;
+        } else if (rc > 0) {
+            if (virDomainManagedSaveRemove(dom, 0) < 0) {
+                virshReportError(ctl);
+                goto cleanup;
+            }
+        }
+        flags &= ~VIR_DOMAIN_START_FORCE_BOOT;
+    }
+
+    /* Prefer older API unless we have to pass a flag.  */
+    if ((flags ? virDomainCreateWithFlags(dom, flags)
+         : virDomainCreate(dom)) < 0) {
+        vshError(ctl, _("Failed to start domain %s"), virDomainGetName(dom));
+        goto cleanup;
+    }
+
+started:
+    vshPrint(ctl, _("Domain %s started\n"),
+             virDomainGetName(dom));
+#ifndef WIN32
+    if (console && !cmdRunConsole(ctl, dom, NULL, 0))
+        goto cleanup;
+#endif
+
+    ret = true;
+
+cleanup:
+    virDomainFree(dom);
+    return ret;
+}
+
+/*
+ * "save" command
+ */
+static const vshCmdInfo info_save[] = {
+    {"help", N_("save a domain state to a file")},
+    {"desc", N_("Save the RAM state of a running domain.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_save[] = {
+    {"bypass-cache", VSH_OT_BOOL, 0, N_("avoid file system cache when saving")},
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("where to save the data")},
+    {"xml", VSH_OT_STRING, 0,
+     N_("filename containing updated XML for the target")},
+    {"running", VSH_OT_BOOL, 0, N_("set domain to be running on restore")},
+    {"paused", VSH_OT_BOOL, 0, N_("set domain to be paused on restore")},
+    {"verbose", VSH_OT_BOOL, 0, N_("display the progress of save")},
+    {NULL, 0, 0, NULL}
+};
+
+static void
+doSave(void *opaque)
+{
+    vshCtrlData *data = opaque;
+    vshControl *ctl = data->ctl;
+    const vshCmd *cmd = data->cmd;
+    char ret = '1';
+    virDomainPtr dom = NULL;
+    const char *name = NULL;
+    const char *to = NULL;
+    unsigned int flags = 0;
+    const char *xmlfile = NULL;
+    char *xml = NULL;
+    sigset_t sigmask, oldsigmask;
+
+    sigemptyset(&sigmask);
+    sigaddset(&sigmask, SIGINT);
+    if (pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask) < 0)
+        goto out_sig;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        goto out;
+
+    if (vshCommandOptString(cmd, "file", &to) <= 0)
+        goto out;
+
+    if (vshCommandOptBool(cmd, "bypass-cache"))
+        flags |= VIR_DOMAIN_SAVE_BYPASS_CACHE;
+    if (vshCommandOptBool(cmd, "running"))
+        flags |= VIR_DOMAIN_SAVE_RUNNING;
+    if (vshCommandOptBool(cmd, "paused"))
+        flags |= VIR_DOMAIN_SAVE_PAUSED;
+
+    if (vshCommandOptString(cmd, "xml", &xmlfile) < 0) {
+        vshError(ctl, "%s", _("malformed xml argument"));
+        goto out;
+    }
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+        goto out;
+
+    if (xmlfile &&
+        virFileReadAll(xmlfile, 8192, &xml) < 0)
+        goto out;
+
+    if (((flags || xml)
+         ? virDomainSaveFlags(dom, to, xml, flags)
+         : virDomainSave(dom, to)) < 0) {
+        vshError(ctl, _("Failed to save domain %s to %s"), name, to);
+        goto out;
+    }
+
+    ret = '0';
+
+out:
+    pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
+out_sig:
+    if (dom) virDomainFree(dom);
+    VIR_FREE(xml);
+    ignore_value(safewrite(data->writefd, &ret, sizeof(ret)));
+}
+
+static bool
+cmdSave(vshControl *ctl, const vshCmd *cmd)
+{
+    bool ret = false;
+    virDomainPtr dom = NULL;
+    int p[2] = {-1. -1};
+    virThread workerThread;
+    bool verbose = false;
+    vshCtrlData data;
+    const char *to = NULL;
+    const char *name = NULL;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+        return false;
+
+    if (vshCommandOptString(cmd, "file", &to) <= 0)
+        goto cleanup;
+
+    if (vshCommandOptBool(cmd, "verbose"))
+        verbose = true;
+
+    if (pipe(p) < 0)
+        goto cleanup;
+
+    data.ctl = ctl;
+    data.cmd = cmd;
+    data.writefd = p[1];
+
+    if (virThreadCreate(&workerThread,
+                        true,
+                        doSave,
+                        &data) < 0)
+        goto cleanup;
+
+    ret = vshWatchJob(ctl, dom, verbose, p[0], 0, NULL, NULL, _("Save"));
+
+    virThreadJoin(&workerThread);
+
+    if (ret)
+        vshPrint(ctl, _("\nDomain %s saved to %s\n"), name, to);
+
+cleanup:
+    if (dom)
+        virDomainFree(dom);
+    return ret;
+}
+
+/*
+ * "save-image-dumpxml" command
+ */
+static const vshCmdInfo info_save_image_dumpxml[] = {
+    {"help", N_("saved state domain information in XML")},
+    {"desc", N_("Output the domain information for a saved state file,\n"
+                "as an XML dump to stdout.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_save_image_dumpxml[] = {
+    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("saved state file to read")},
+    {"security-info", VSH_OT_BOOL, 0, N_("include security sensitive information in XML dump")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdSaveImageDumpxml(vshControl *ctl, const vshCmd *cmd)
+{
+    const char *file = NULL;
+    bool ret = false;
+    unsigned int flags = 0;
+    char *xml = NULL;
+
+    if (vshCommandOptBool(cmd, "security-info"))
+        flags |= VIR_DOMAIN_XML_SECURE;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (vshCommandOptString(cmd, "file", &file) <= 0)
+        return false;
+
+    xml = virDomainSaveImageGetXMLDesc(ctl->conn, file, flags);
+    if (!xml)
+        goto cleanup;
+
+    vshPrint(ctl, "%s", xml);
+    ret = true;
+
+cleanup:
+    VIR_FREE(xml);
+    return ret;
+}
+
+/*
+ * "save-image-define" command
+ */
+static const vshCmdInfo info_save_image_define[] = {
+    {"help", N_("redefine the XML for a domain's saved state file")},
+    {"desc", N_("Replace the domain XML associated with a saved state file")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_save_image_define[] = {
+    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("saved state file to modify")},
+    {"xml", VSH_OT_STRING, VSH_OFLAG_REQ,
+     N_("filename containing updated XML for the target")},
+    {"running", VSH_OT_BOOL, 0, N_("set domain to be running on restore")},
+    {"paused", VSH_OT_BOOL, 0, N_("set domain to be paused on restore")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdSaveImageDefine(vshControl *ctl, const vshCmd *cmd)
+{
+    const char *file = NULL;
+    bool ret = false;
+    const char *xmlfile = NULL;
+    char *xml = NULL;
+    unsigned int flags = 0;
+
+    if (vshCommandOptBool(cmd, "running"))
+        flags |= VIR_DOMAIN_SAVE_RUNNING;
+    if (vshCommandOptBool(cmd, "paused"))
+        flags |= VIR_DOMAIN_SAVE_PAUSED;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (vshCommandOptString(cmd, "file", &file) <= 0)
+        return false;
+
+    if (vshCommandOptString(cmd, "xml", &xmlfile) <= 0) {
+        vshError(ctl, "%s", _("malformed or missing xml argument"));
+        return false;
+    }
+
+    if (virFileReadAll(xmlfile, 8192, &xml) < 0)
+        goto cleanup;
+
+    if (virDomainSaveImageDefineXML(ctl->conn, file, xml, flags) < 0) {
+        vshError(ctl, _("Failed to update %s"), file);
+        goto cleanup;
+    }
+
+    vshPrint(ctl, _("State file %s updated.\n"), file);
+    ret = true;
+
+cleanup:
+    VIR_FREE(xml);
+    return ret;
+}
+
+/*
+ * "save-image-edit" command
+ */
+static const vshCmdInfo info_save_image_edit[] = {
+    {"help", N_("edit XML for a domain's saved state file")},
+    {"desc", N_("Edit the domain XML associated with a saved state file")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_save_image_edit[] = {
+    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("saved state file to edit")},
+    {"running", VSH_OT_BOOL, 0, N_("set domain to be running on restore")},
+    {"paused", VSH_OT_BOOL, 0, N_("set domain to be paused on restore")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdSaveImageEdit(vshControl *ctl, const vshCmd *cmd)
+{
+    const char *file = NULL;
+    bool ret = false;
+    unsigned int getxml_flags = VIR_DOMAIN_XML_SECURE;
+    unsigned int define_flags = 0;
+
+    if (vshCommandOptBool(cmd, "running"))
+        define_flags |= VIR_DOMAIN_SAVE_RUNNING;
+    if (vshCommandOptBool(cmd, "paused"))
+        define_flags |= VIR_DOMAIN_SAVE_PAUSED;
+
+    /* Normally, we let the API reject mutually exclusive flags.
+     * However, in the edit cycle, we let the user retry if the define
+     * step fails, but the define step will always fail on invalid
+     * flags, so we reject it up front to avoid looping.  */
+    if (define_flags == (VIR_DOMAIN_SAVE_RUNNING | VIR_DOMAIN_SAVE_PAUSED)) {
+        vshError(ctl, "%s", _("--running and --saved are mutually exclusive"));
+        return false;
+    }
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (vshCommandOptString(cmd, "file", &file) <= 0)
+        return false;
+
+#define EDIT_GET_XML \
+    virDomainSaveImageGetXMLDesc(ctl->conn, file, getxml_flags)
+#define EDIT_NOT_CHANGED \
+    vshPrint(ctl, _("Saved image %s XML configuration " \
+                    "not changed.\n"), file);           \
+    ret = true; goto edit_cleanup;
+#define EDIT_DEFINE \
+    virDomainSaveImageDefineXML(ctl->conn, file, doc_edited, define_flags)
+#define EDIT_FREE /* */
+#include "virsh-edit.c"
+
+    vshPrint(ctl, _("State file %s edited.\n"), file);
+    ret = true;
+
+cleanup:
+    return ret;
+}
+
+/*
+ * "managedsave" command
+ */
+static const vshCmdInfo info_managedsave[] = {
+    {"help", N_("managed save of a domain state")},
+    {"desc", N_("Save and destroy a running domain, so it can be restarted from\n"
+                "    the same state at a later time.  When the virsh 'start'\n"
+                "    command is next run for the domain, it will automatically\n"
+                "    be started from this saved state.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_managedsave[] = {
+    {"bypass-cache", VSH_OT_BOOL, 0, N_("avoid file system cache when saving")},
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"running", VSH_OT_BOOL, 0, N_("set domain to be running on next start")},
+    {"paused", VSH_OT_BOOL, 0, N_("set domain to be paused on next start")},
+    {"verbose", VSH_OT_BOOL, 0, N_("display the progress of save")},
+    {NULL, 0, 0, NULL}
+};
+
+static void
+doManagedsave(void *opaque)
+{
+    char ret = '1';
+    vshCtrlData *data = opaque;
+    vshControl *ctl = data->ctl;
+    const vshCmd *cmd = data->cmd;
+    virDomainPtr dom = NULL;
+    const char *name;
+    unsigned int flags = 0;
+    sigset_t sigmask, oldsigmask;
+
+    sigemptyset(&sigmask);
+    sigaddset(&sigmask, SIGINT);
+    if (pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask) < 0)
+        goto out_sig;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        goto out;
+
+    if (vshCommandOptBool(cmd, "bypass-cache"))
+        flags |= VIR_DOMAIN_SAVE_BYPASS_CACHE;
+    if (vshCommandOptBool(cmd, "running"))
+        flags |= VIR_DOMAIN_SAVE_RUNNING;
+    if (vshCommandOptBool(cmd, "paused"))
+        flags |= VIR_DOMAIN_SAVE_PAUSED;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+        goto out;
+
+    if (virDomainManagedSave(dom, flags) < 0) {
+        vshError(ctl, _("Failed to save domain %s state"), name);
+        goto out;
+    }
+
+    ret = '0';
+out:
+    pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
+out_sig:
+    if (dom)
+        virDomainFree(dom);
+    ignore_value(safewrite(data->writefd, &ret, sizeof(ret)));
+}
+
+static bool
+cmdManagedSave(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom;
+    int p[2] = { -1, -1};
+    bool ret = false;
+    bool verbose = false;
+    const char *name = NULL;
+    vshCtrlData data;
+    virThread workerThread;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+        return false;
+
+    if (vshCommandOptBool(cmd, "verbose"))
+        verbose = true;
+
+    if (pipe(p) < 0)
+        goto cleanup;
+
+    data.ctl = ctl;
+    data.cmd = cmd;
+    data.writefd = p[1];
+
+    if (virThreadCreate(&workerThread,
+                        true,
+                        doManagedsave,
+                        &data) < 0)
+        goto cleanup;
+
+    ret = vshWatchJob(ctl, dom, verbose, p[0], 0,
+                      NULL, NULL, _("Managedsave"));
+
+    virThreadJoin(&workerThread);
+
+    if (ret)
+        vshPrint(ctl, _("\nDomain %s state saved by libvirt\n"), name);
+
+cleanup:
+    virDomainFree(dom);
+    VIR_FORCE_CLOSE(p[0]);
+    VIR_FORCE_CLOSE(p[1]);
+    return ret;
+}
+
+/*
+ * "managedsave-remove" command
+ */
+static const vshCmdInfo info_managedsaveremove[] = {
+    {"help", N_("Remove managed save of a domain")},
+    {"desc", N_("Remove an existing managed save state file from a domain")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_managedsaveremove[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdManagedSaveRemove(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom;
+    const char *name;
+    bool ret = false;
+    int hassave;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+        return false;
+
+    hassave = virDomainHasManagedSaveImage(dom, 0);
+    if (hassave < 0) {
+        vshError(ctl, "%s", _("Failed to check for domain managed save image"));
+        goto cleanup;
+    }
+
+    if (hassave) {
+        if (virDomainManagedSaveRemove(dom, 0) < 0) {
+            vshError(ctl, _("Failed to remove managed save image for domain %s"),
+                     name);
+            goto cleanup;
+        }
+        else
+            vshPrint(ctl, _("Removed managedsave image for domain %s"), name);
+    }
+    else
+        vshPrint(ctl, _("Domain %s has no manage save image; removal skipped"),
+                 name);
+
+    ret = true;
+
+cleanup:
+    virDomainFree(dom);
+    return ret;
+}
+
+/*
+ * "schedinfo" command
+ */
+static const vshCmdInfo info_schedinfo[] = {
+    {"help", N_("show/set scheduler parameters")},
+    {"desc", N_("Show/Set scheduler parameters.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_schedinfo[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"set", VSH_OT_STRING, VSH_OFLAG_NONE, N_("parameter=value")},
+    {"weight", VSH_OT_INT, VSH_OFLAG_NONE, N_("weight for XEN_CREDIT")},
+    {"cap", VSH_OT_INT, VSH_OFLAG_NONE, N_("cap for XEN_CREDIT")},
+    {"current", VSH_OT_BOOL, 0, N_("get/set current scheduler info")},
+    {"config", VSH_OT_BOOL, 0, N_("get/set value to be used on next boot")},
+    {"live", VSH_OT_BOOL, 0, N_("get/set value from running domain")},
+    {NULL, 0, 0, NULL}
+};
+
+static int
+cmdSchedInfoUpdate(vshControl *ctl, const vshCmd *cmd,
+                   virTypedParameterPtr param)
+{
+    const char *data = NULL;
+
+    /* Legacy 'weight' parameter */
+    if (STREQ(param->field, "weight") &&
+        param->type == VIR_TYPED_PARAM_UINT &&
+        vshCommandOptBool(cmd, "weight")) {
+        int val;
+        if (vshCommandOptInt(cmd, "weight", &val) <= 0) {
+            vshError(ctl, "%s", _("Invalid value of weight"));
+            return -1;
+        } else {
+            param->value.ui = val;
+        }
+        return 1;
+    }
+
+    /* Legacy 'cap' parameter */
+    if (STREQ(param->field, "cap") &&
+        param->type == VIR_TYPED_PARAM_UINT &&
+        vshCommandOptBool(cmd, "cap")) {
+        int val;
+        if (vshCommandOptInt(cmd, "cap", &val) <= 0) {
+            vshError(ctl, "%s", _("Invalid value of cap"));
+            return -1;
+        } else {
+            param->value.ui = val;
+        }
+        return 1;
+    }
+
+    if (vshCommandOptString(cmd, "set", &data) > 0) {
+        char *val = strchr(data, '=');
+        int match = 0;
+        if (!val) {
+            vshError(ctl, "%s", _("Invalid syntax for --set, expecting name=value"));
+            return -1;
+        }
+        *val = '\0';
+        match = STREQ(data, param->field);
+        *val = '=';
+        val++;
+
+        if (!match)
+            return 0;
+
+        switch (param->type) {
+        case VIR_TYPED_PARAM_INT:
+            if (virStrToLong_i(val, NULL, 10, &param->value.i) < 0) {
+                vshError(ctl, "%s",
+                         _("Invalid value for parameter, expecting an int"));
+                return -1;
+            }
+            break;
+        case VIR_TYPED_PARAM_UINT:
+            if (virStrToLong_ui(val, NULL, 10, &param->value.ui) < 0) {
+                vshError(ctl, "%s",
+                         _("Invalid value for parameter, expecting an unsigned int"));
+                return -1;
+            }
+            break;
+        case VIR_TYPED_PARAM_LLONG:
+            if (virStrToLong_ll(val, NULL, 10, &param->value.l) < 0) {
+                vshError(ctl, "%s",
+                         _("Invalid value for parameter, expecting a long long"));
+                return -1;
+            }
+            break;
+        case VIR_TYPED_PARAM_ULLONG:
+            if (virStrToLong_ull(val, NULL, 10, &param->value.ul) < 0) {
+                vshError(ctl, "%s",
+                         _("Invalid value for parameter, expecting an unsigned long long"));
+                return -1;
+            }
+            break;
+        case VIR_TYPED_PARAM_DOUBLE:
+            if (virStrToDouble(val, NULL, &param->value.d) < 0) {
+                vshError(ctl, "%s", _("Invalid value for parameter, expecting a double"));
+                return -1;
+            }
+            break;
+        case VIR_TYPED_PARAM_BOOLEAN:
+            param->value.b = STREQ(val, "0") ? 0 : 1;
+        }
+        return 1;
+    }
+
+    return 0;
+}
+
+static bool
+cmdSchedinfo(vshControl *ctl, const vshCmd *cmd)
+{
+    char *schedulertype;
+    virDomainPtr dom;
+    virTypedParameterPtr params = NULL;
+    int nparams = 0;
+    int update = 0;
+    int i, ret;
+    bool ret_val = false;
+    unsigned int flags = 0;
+    bool current = vshCommandOptBool(cmd, "current");
+    bool config = vshCommandOptBool(cmd, "config");
+    bool live = vshCommandOptBool(cmd, "live");
+
+    if (current) {
+        if (live || config) {
+            vshError(ctl, "%s", _("--current must be specified exclusively"));
+            return false;
+        }
+        flags = VIR_DOMAIN_AFFECT_CURRENT;
+    } else {
+        if (config)
+            flags |= VIR_DOMAIN_AFFECT_CONFIG;
+        if (live)
+            flags |= VIR_DOMAIN_AFFECT_LIVE;
+    }
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        return false;
+
+    /* Print SchedulerType */
+    schedulertype = virDomainGetSchedulerType(dom, &nparams);
+    if (schedulertype != NULL) {
+        vshPrint(ctl, "%-15s: %s\n", _("Scheduler"),
+             schedulertype);
+        VIR_FREE(schedulertype);
+    } else {
+        vshPrint(ctl, "%-15s: %s\n", _("Scheduler"), _("Unknown"));
+        goto cleanup;
+    }
+
+    if (nparams) {
+        params = vshMalloc(ctl, sizeof(*params) * nparams);
+
+        memset(params, 0, sizeof(*params) * nparams);
+        if (flags || current) {
+            /* We cannot query both live and config at once, so settle
+               on current in that case.  If we are setting, then the
+               two values should match when we re-query; otherwise, we
+               report the error later.  */
+            ret = virDomainGetSchedulerParametersFlags(dom, params, &nparams,
+                                                       ((live && config) ? 0
+                                                        : flags));
+        } else {
+            ret = virDomainGetSchedulerParameters(dom, params, &nparams);
+        }
+        if (ret == -1)
+            goto cleanup;
+
+        /* See if any params are being set */
+        for (i = 0; i < nparams; i++) {
+            ret = cmdSchedInfoUpdate(ctl, cmd, &(params[i]));
+            if (ret == -1)
+                goto cleanup;
+
+            if (ret == 1)
+                update = 1;
+        }
+
+        /* Update parameters & refresh data */
+        if (update) {
+            if (flags || current)
+                ret = virDomainSetSchedulerParametersFlags(dom, params,
+                                                           nparams, flags);
+            else
+                ret = virDomainSetSchedulerParameters(dom, params, nparams);
+            if (ret == -1)
+                goto cleanup;
+
+            if (flags || current)
+                ret = virDomainGetSchedulerParametersFlags(dom, params,
+                                                           &nparams,
+                                                           ((live && config) ? 0
+                                                            : flags));
+            else
+                ret = virDomainGetSchedulerParameters(dom, params, &nparams);
+            if (ret == -1)
+                goto cleanup;
+        } else {
+            /* See if we've tried to --set var=val.  If so, the fact that
+               we reach this point (with update == 0) means that "var" did
+               not match any of the settable parameters.  Report the error.  */
+            const char *var_value_pair = NULL;
+            if (vshCommandOptString(cmd, "set", &var_value_pair) > 0) {
+                vshError(ctl, _("invalid scheduler option: %s"),
+                         var_value_pair);
+                goto cleanup;
+            }
+            /* When not doing --set, --live and --config do not mix.  */
+            if (live && config) {
+                vshError(ctl, "%s",
+                         _("cannot query both live and config at once"));
+                goto cleanup;
+            }
+        }
+
+        ret_val = true;
+        for (i = 0; i < nparams; i++) {
+            char *str = vshGetTypedParamValue(ctl, &params[i]);
+            vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
+            VIR_FREE(str);
+        }
+    }
+
+ cleanup:
+    VIR_FREE(params);
+    virDomainFree(dom);
+    return ret_val;
+}
+
+/*
+ * "restore" command
+ */
+static const vshCmdInfo info_restore[] = {
+    {"help", N_("restore a domain from a saved state in a file")},
+    {"desc", N_("Restore a domain.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_restore[] = {
+    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("the state to restore")},
+    {"bypass-cache", VSH_OT_BOOL, 0,
+     N_("avoid file system cache when restoring")},
+    {"xml", VSH_OT_STRING, 0,
+     N_("filename containing updated XML for the target")},
+    {"running", VSH_OT_BOOL, 0, N_("restore domain into running state")},
+    {"paused", VSH_OT_BOOL, 0, N_("restore domain into paused state")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdRestore(vshControl *ctl, const vshCmd *cmd)
+{
+    const char *from = NULL;
+    bool ret = false;
+    unsigned int flags = 0;
+    const char *xmlfile = NULL;
+    char *xml = NULL;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (vshCommandOptString(cmd, "file", &from) <= 0)
+        return false;
+
+    if (vshCommandOptBool(cmd, "bypass-cache"))
+        flags |= VIR_DOMAIN_SAVE_BYPASS_CACHE;
+    if (vshCommandOptBool(cmd, "running"))
+        flags |= VIR_DOMAIN_SAVE_RUNNING;
+    if (vshCommandOptBool(cmd, "paused"))
+        flags |= VIR_DOMAIN_SAVE_PAUSED;
+
+    if (vshCommandOptString(cmd, "xml", &xmlfile) < 0) {
+        vshError(ctl, "%s", _("malformed xml argument"));
+        return false;
+    }
+
+    if (xmlfile &&
+        virFileReadAll(xmlfile, 8192, &xml) < 0)
+        goto cleanup;
+
+    if (((flags || xml)
+         ? virDomainRestoreFlags(ctl->conn, from, xml, flags)
+         : virDomainRestore(ctl->conn, from)) < 0) {
+        vshError(ctl, _("Failed to restore domain from %s"), from);
+        goto cleanup;
+    }
+
+    vshPrint(ctl, _("Domain restored from %s\n"), from);
+    ret = true;
+
+cleanup:
+    VIR_FREE(xml);
+    return ret;
+}
+
+/*
+ * "dump" command
+ */
+static const vshCmdInfo info_dump[] = {
+    {"help", N_("dump the core of a domain to a file for analysis")},
+    {"desc", N_("Core dump a domain.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_dump[] = {
+    {"live", VSH_OT_BOOL, 0, N_("perform a live core dump if supported")},
+    {"crash", VSH_OT_BOOL, 0, N_("crash the domain after core dump")},
+    {"bypass-cache", VSH_OT_BOOL, 0,
+     N_("avoid file system cache when saving")},
+    {"reset", VSH_OT_BOOL, 0, N_("reset the domain after core dump")},
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("where to dump the core")},
+    {"verbose", VSH_OT_BOOL, 0, N_("display the progress of dump")},
+    {"memory-only", VSH_OT_BOOL, 0, N_("dump domain's memory only")},
+    {NULL, 0, 0, NULL}
+};
+
+static void
+doDump(void *opaque)
+{
+    char ret = '1';
+    vshCtrlData *data = opaque;
+    vshControl *ctl = data->ctl;
+    const vshCmd *cmd = data->cmd;
+    virDomainPtr dom = NULL;
+    sigset_t sigmask, oldsigmask;
+    const char *name = NULL;
+    const char *to = NULL;
+    unsigned int flags = 0;
+
+    sigemptyset(&sigmask);
+    sigaddset(&sigmask, SIGINT);
+    if (pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask) < 0)
+        goto out_sig;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        goto out;
+
+    if (vshCommandOptString(cmd, "file", &to) <= 0)
+        goto out;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+        goto out;
+
+    if (vshCommandOptBool(cmd, "live"))
+        flags |= VIR_DUMP_LIVE;
+    if (vshCommandOptBool(cmd, "crash"))
+        flags |= VIR_DUMP_CRASH;
+    if (vshCommandOptBool(cmd, "bypass-cache"))
+        flags |= VIR_DUMP_BYPASS_CACHE;
+    if (vshCommandOptBool(cmd, "reset"))
+        flags |= VIR_DUMP_RESET;
+    if (vshCommandOptBool(cmd, "memory-only"))
+        flags |= VIR_DUMP_MEMORY_ONLY;
+
+    if (virDomainCoreDump(dom, to, flags) < 0) {
+        vshError(ctl, _("Failed to core dump domain %s to %s"), name, to);
+        goto out;
+    }
+
+    ret = '0';
+out:
+    pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
+out_sig:
+    if (dom)
+        virDomainFree(dom);
+    ignore_value(safewrite(data->writefd, &ret, sizeof(ret)));
+}
+
+static bool
+cmdDump(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom;
+    int p[2] = { -1, -1};
+    bool ret = false;
+    bool verbose = false;
+    const char *name = NULL;
+    const char *to = NULL;
+    vshCtrlData data;
+    virThread workerThread;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+        return false;
+
+    if (vshCommandOptString(cmd, "file", &to) <= 0)
+        return false;
+
+    if (vshCommandOptBool(cmd, "verbose"))
+        verbose = true;
+
+    if (pipe(p) < 0)
+        goto cleanup;
+
+    data.ctl = ctl;
+    data.cmd = cmd;
+    data.writefd = p[1];
+
+    if (virThreadCreate(&workerThread,
+                        true,
+                        doDump,
+                        &data) < 0)
+        goto cleanup;
+
+    ret = vshWatchJob(ctl, dom, verbose, p[0], 0, NULL, NULL, _("Dump"));
+
+    virThreadJoin(&workerThread);
+
+    if (ret)
+        vshPrint(ctl, _("\nDomain %s dumped to %s\n"), name, to);
+
+cleanup:
+    virDomainFree(dom);
+    VIR_FORCE_CLOSE(p[0]);
+    VIR_FORCE_CLOSE(p[1]);
+    return ret;
+}
+
+static const vshCmdInfo info_screenshot[] = {
+    {"help", N_("take a screenshot of a current domain console and store it "
+                "into a file")},
+    {"desc", N_("screenshot of a current domain console")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_screenshot[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"file", VSH_OT_DATA, VSH_OFLAG_NONE, N_("where to store the screenshot")},
+    {"screen", VSH_OT_INT, VSH_OFLAG_NONE, N_("ID of a screen to take screenshot of")},
+    {NULL, 0, 0, NULL}
+};
+
+/**
+ * Generate string: '<domain name>-<timestamp>[<extension>]'
+ */
+static char *
+vshGenFileName(vshControl *ctl, virDomainPtr dom, const char *mime)
+{
+    char timestr[100];
+    struct timeval cur_time;
+    struct tm time_info;
+    const char *ext = NULL;
+    char *ret = NULL;
+
+    /* We should be already connected, but doesn't
+     * hurt to check */
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return NULL;
+
+    if (!dom) {
+        vshError(ctl, "%s", _("Invalid domain supplied"));
+        return NULL;
+    }
+
+    if (STREQ(mime, "image/x-portable-pixmap"))
+        ext = ".ppm";
+    else if (STREQ(mime, "image/png"))
+        ext = ".png";
+    /* add mime type here */
+
+    gettimeofday(&cur_time, NULL);
+    localtime_r(&cur_time.tv_sec, &time_info);
+    strftime(timestr, sizeof(timestr), "%Y-%m-%d-%H:%M:%S", &time_info);
+
+    if (virAsprintf(&ret, "%s-%s%s", virDomainGetName(dom),
+                    timestr, ext ? ext : "") < 0) {
+        vshError(ctl, "%s", _("Out of memory"));
+        return NULL;
+    }
+
+    return ret;
+}
+
+static bool
+cmdScreenshot(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom;
+    const char *name = NULL;
+    char *file = NULL;
+    int fd = -1;
+    virStreamPtr st = NULL;
+    unsigned int screen = 0;
+    unsigned int flags = 0; /* currently unused */
+    int ret = false;
+    bool created = false;
+    bool generated = false;
+    char *mime = NULL;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (vshCommandOptString(cmd, "file", (const char **) &file) < 0) {
+        vshError(ctl, "%s", _("file must not be empty"));
+        return false;
+    }
+
+    if (vshCommandOptUInt(cmd, "screen", &screen) < 0) {
+        vshError(ctl, "%s", _("invalid screen ID"));
+        return false;
+    }
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+        return false;
+
+    st = virStreamNew(ctl->conn, 0);
+
+    mime = virDomainScreenshot(dom, st, screen, flags);
+    if (!mime) {
+        vshError(ctl, _("could not take a screenshot of %s"), name);
+        goto cleanup;
+    }
+
+    if (!file) {
+        if (!(file=vshGenFileName(ctl, dom, mime)))
+            return false;
+        generated = true;
+    }
+
+    if ((fd = open(file, O_WRONLY|O_CREAT|O_EXCL, 0666)) < 0) {
+        if (errno != EEXIST ||
+            (fd = open(file, O_WRONLY|O_TRUNC, 0666)) < 0) {
+            vshError(ctl, _("cannot create file %s"), file);
+            goto cleanup;
+        }
+    } else {
+        created = true;
+    }
+
+    if (virStreamRecvAll(st, vshStreamSink, &fd) < 0) {
+        vshError(ctl, _("could not receive data from domain %s"), name);
+        goto cleanup;
+    }
+
+    if (VIR_CLOSE(fd) < 0) {
+        vshError(ctl, _("cannot close file %s"), file);
+        goto cleanup;
+    }
+
+    if (virStreamFinish(st) < 0) {
+        vshError(ctl, _("cannot close stream on domain %s"), name);
+        goto cleanup;
+    }
+
+    vshPrint(ctl, _("Screenshot saved to %s, with type of %s"), file, mime);
+    ret = true;
+
+cleanup:
+    if (!ret && created)
+        unlink(file);
+    if (generated)
+        VIR_FREE(file);
+    virDomainFree(dom);
+    if (st)
+        virStreamFree(st);
+    VIR_FORCE_CLOSE(fd);
+    VIR_FREE(mime);
+    return ret;
+}
+
+/*
+ * "resume" command
+ */
+static const vshCmdInfo info_resume[] = {
+    {"help", N_("resume a domain")},
+    {"desc", N_("Resume a previously suspended domain.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_resume[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdResume(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom;
+    bool ret = true;
+    const char *name;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+        return false;
+
+    if (virDomainResume(dom) == 0) {
+        vshPrint(ctl, _("Domain %s resumed\n"), name);
+    } else {
+        vshError(ctl, _("Failed to resume domain %s"), name);
+        ret = false;
+    }
+
+    virDomainFree(dom);
+    return ret;
+}
+
+/*
+ * "shutdown" command
+ */
+static const vshCmdInfo info_shutdown[] = {
+    {"help", N_("gracefully shutdown a domain")},
+    {"desc", N_("Run shutdown in the target domain.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_shutdown[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"mode", VSH_OT_STRING, VSH_OFLAG_NONE, N_("shutdown mode: acpi|agent")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdShutdown(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom;
+    bool ret = true;
+    const char *name;
+    const char *mode = NULL;
+    int flags = 0;
+    int rv;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (vshCommandOptString(cmd, "mode", &mode) < 0) {
+        vshError(ctl, "%s", _("Invalid type"));
+        return false;
+    }
+
+    if (mode) {
+        if (STREQ(mode, "acpi")) {
+            flags |= VIR_DOMAIN_SHUTDOWN_ACPI_POWER_BTN;
+        } else if (STREQ(mode, "agent")) {
+            flags |= VIR_DOMAIN_SHUTDOWN_GUEST_AGENT;
+        } else {
+            vshError(ctl, _("Unknown mode %s value, expecting 'acpi' or 'agent'"), mode);
+            return false;
+        }
+    }
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+        return false;
+
+    if (flags)
+        rv = virDomainShutdownFlags(dom, flags);
+    else
+        rv = virDomainShutdown(dom);
+    if (rv == 0) {
+        vshPrint(ctl, _("Domain %s is being shutdown\n"), name);
+    } else {
+        vshError(ctl, _("Failed to shutdown domain %s"), name);
+        ret = false;
+    }
+
+    virDomainFree(dom);
+    return ret;
+}
+
+/*
+ * "reboot" command
+ */
+static const vshCmdInfo info_reboot[] = {
+    {"help", N_("reboot a domain")},
+    {"desc", N_("Run a reboot command in the target domain.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_reboot[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"mode", VSH_OT_STRING, VSH_OFLAG_NONE, N_("shutdown mode: acpi|agent")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdReboot(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom;
+    bool ret = true;
+    const char *name;
+    const char *mode = NULL;
+    int flags = 0;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (vshCommandOptString(cmd, "mode", &mode) < 0) {
+        vshError(ctl, "%s", _("Invalid type"));
+        return false;
+    }
+
+    if (mode) {
+        if (STREQ(mode, "acpi")) {
+            flags |= VIR_DOMAIN_SHUTDOWN_ACPI_POWER_BTN;
+        } else if (STREQ(mode, "agent")) {
+            flags |= VIR_DOMAIN_SHUTDOWN_GUEST_AGENT;
+        } else {
+            vshError(ctl, _("Unknown mode %s value, expecting 'acpi' or 'agent'"), mode);
+            return false;
+        }
+    }
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+        return false;
+
+    if (virDomainReboot(dom, flags) == 0) {
+        vshPrint(ctl, _("Domain %s is being rebooted\n"), name);
+    } else {
+        vshError(ctl, _("Failed to reboot domain %s"), name);
+        ret = false;
+    }
+
+    virDomainFree(dom);
+    return ret;
+}
+
+/*
+ * "reset" command
+ */
+static const vshCmdInfo info_reset[] = {
+    {"help", N_("reset a domain")},
+    {"desc", N_("Reset the target domain as if by power button")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_reset[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdReset(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom;
+    bool ret = true;
+    const char *name;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+        return false;
+
+    if (virDomainReset(dom, 0) == 0) {
+        vshPrint(ctl, _("Domain %s was reset\n"), name);
+    } else {
+        vshError(ctl, _("Failed to reset domain %s"), name);
+        ret = false;
+    }
+
+    virDomainFree(dom);
+    return ret;
+}
+
+/*
+ * "domjobinfo" command
+ */
+static const vshCmdInfo info_domjobinfo[] = {
+    {"help", N_("domain job information")},
+    {"desc", N_("Returns information about jobs running on a domain.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_domjobinfo[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDomjobinfo(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainJobInfo info;
+    virDomainPtr dom;
+    bool ret = true;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        return false;
+
+    if (virDomainGetJobInfo(dom, &info) == 0) {
+        const char *unit;
+        double val;
+
+        vshPrint(ctl, "%-17s ", _("Job type:"));
+        switch (info.type) {
+        case VIR_DOMAIN_JOB_BOUNDED:
+            vshPrint(ctl, "%-12s\n", _("Bounded"));
+            break;
+
+        case VIR_DOMAIN_JOB_UNBOUNDED:
+            vshPrint(ctl, "%-12s\n", _("Unbounded"));
+            break;
+
+        case VIR_DOMAIN_JOB_NONE:
+        default:
+            vshPrint(ctl, "%-12s\n", _("None"));
+            goto cleanup;
+        }
+
+        vshPrint(ctl, "%-17s %-12llu ms\n", _("Time elapsed:"), info.timeElapsed);
+        if (info.type == VIR_DOMAIN_JOB_BOUNDED)
+            vshPrint(ctl, "%-17s %-12llu ms\n", _("Time remaining:"), info.timeRemaining);
+        if (info.dataTotal || info.dataRemaining || info.dataProcessed) {
+            val = prettyCapacity(info.dataProcessed, &unit);
+            vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data processed:"), val, unit);
+            val = prettyCapacity(info.dataRemaining, &unit);
+            vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data remaining:"), val, unit);
+            val = prettyCapacity(info.dataTotal, &unit);
+            vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data total:"), val, unit);
+        }
+        if (info.memTotal || info.memRemaining || info.memProcessed) {
+            val = prettyCapacity(info.memProcessed, &unit);
+            vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory processed:"), val, unit);
+            val = prettyCapacity(info.memRemaining, &unit);
+            vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory remaining:"), val, unit);
+            val = prettyCapacity(info.memTotal, &unit);
+            vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory total:"), val, unit);
+        }
+        if (info.fileTotal || info.fileRemaining || info.fileProcessed) {
+            val = prettyCapacity(info.fileProcessed, &unit);
+            vshPrint(ctl, "%-17s %-.3lf %s\n", _("File processed:"), val, unit);
+            val = prettyCapacity(info.fileRemaining, &unit);
+            vshPrint(ctl, "%-17s %-.3lf %s\n", _("File remaining:"), val, unit);
+            val = prettyCapacity(info.fileTotal, &unit);
+            vshPrint(ctl, "%-17s %-.3lf %s\n", _("File total:"), val, unit);
+        }
+    } else {
+        ret = false;
+    }
+cleanup:
+    virDomainFree(dom);
+    return ret;
+}
+
+/*
+ * "domjobabort" command
+ */
+static const vshCmdInfo info_domjobabort[] = {
+    {"help", N_("abort active domain job")},
+    {"desc", N_("Aborts the currently running domain job")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_domjobabort[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDomjobabort(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom;
+    bool ret = true;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        return false;
+
+    if (virDomainAbortJob(dom) < 0)
+        ret = false;
+
+    virDomainFree(dom);
+    return ret;
+}
+
+/*
+ * "maxvcpus" command
+ */
+static const vshCmdInfo info_maxvcpus[] = {
+    {"help", N_("connection vcpu maximum")},
+    {"desc", N_("Show maximum number of virtual CPUs for guests on this connection.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_maxvcpus[] = {
+    {"type", VSH_OT_STRING, 0, N_("domain type")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdMaxvcpus(vshControl *ctl, const vshCmd *cmd)
+{
+    const char *type = NULL;
+    int vcpus;
+
+    if (vshCommandOptString(cmd, "type", &type) < 0) {
+        vshError(ctl, "%s", _("Invalid type"));
+        return false;
+    }
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    vcpus = virConnectGetMaxVcpus(ctl->conn, type);
+    if (vcpus < 0)
+        return false;
+    vshPrint(ctl, "%d\n", vcpus);
+
+    return true;
+}
+
+/*
+ * "vcpucount" command
+ */
+static const vshCmdInfo info_vcpucount[] = {
+    {"help", N_("domain vcpu counts")},
+    {"desc", N_("Returns the number of virtual CPUs used by the domain.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_vcpucount[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"maximum", VSH_OT_BOOL, 0, N_("get maximum cap on vcpus")},
+    {"active", VSH_OT_BOOL, 0, N_("get number of currently active vcpus")},
+    {"live", VSH_OT_BOOL, 0, N_("get value from running domain")},
+    {"config", VSH_OT_BOOL, 0, N_("get value to be used on next boot")},
+    {"current", VSH_OT_BOOL, 0,
+     N_("get value according to current domain state")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdVcpucount(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom;
+    bool ret = true;
+    bool maximum = vshCommandOptBool(cmd, "maximum");
+    bool active = vshCommandOptBool(cmd, "active");
+    bool config = vshCommandOptBool(cmd, "config");
+    bool live = vshCommandOptBool(cmd, "live");
+    bool current = vshCommandOptBool(cmd, "current");
+    bool all = maximum + active + current + config + live == 0;
+    int count;
+
+    /* We want one of each pair of mutually exclusive options; that
+     * is, use of flags requires exactly two options.  We reject the
+     * use of more than 2 flags later on.  */
+    if (maximum + active + current + config + live == 1) {
+        if (maximum || active) {
+            vshError(ctl,
+                     _("when using --%s, one of --config, --live, or --current "
+                       "must be specified"),
+                     maximum ? "maximum" : "active");
+        } else {
+            vshError(ctl,
+                     _("when using --%s, either --maximum or --active must be "
+                       "specified"),
+                     (current ? "current" : config ? "config" : "live"));
+        }
+        return false;
+    }
+
+    /* Backwards compatibility: prior to 0.9.4,
+     * VIR_DOMAIN_AFFECT_CURRENT was unsupported, and --current meant
+     * the opposite of --maximum.  Translate the old '--current
+     * --live' into the new '--active --live', while treating the new
+     * '--maximum --current' correctly rather than rejecting it as
+     * '--maximum --active'.  */
+    if (!maximum && !active && current) {
+        current = false;
+        active = true;
+    }
+
+    if (maximum && active) {
+        vshError(ctl, "%s",
+                 _("--maximum and --active cannot both be specified"));
+        return false;
+    }
+    if (current + config + live > 1) {
+        vshError(ctl, "%s",
+                 _("--config, --live, and --current are mutually exclusive"));
+        return false;
+    }
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        return false;
+
+    /* In all cases, try the new API first; if it fails because we are
+     * talking to an older client, generally we try a fallback API
+     * before giving up.  --current requires the new API, since we
+     * don't know whether the domain is running or inactive.  */
+    if (current) {
+        count = virDomainGetVcpusFlags(dom,
+                                       maximum ? VIR_DOMAIN_VCPU_MAXIMUM : 0);
+        if (count < 0) {
+            virshReportError(ctl);
+            ret = false;
+        } else {
+            vshPrint(ctl, "%d\n", count);
+        }
+    }
+
+    if (all || (maximum && config)) {
+        count = virDomainGetVcpusFlags(dom, (VIR_DOMAIN_VCPU_MAXIMUM |
+                                             VIR_DOMAIN_AFFECT_CONFIG));
+        if (count < 0 && (last_error->code == VIR_ERR_NO_SUPPORT
+                          || last_error->code == VIR_ERR_INVALID_ARG)) {
+            char *tmp;
+            char *xml = virDomainGetXMLDesc(dom, VIR_DOMAIN_XML_INACTIVE);
+            if (xml && (tmp = strstr(xml, "<vcpu"))) {
+                tmp = strchr(tmp, '>');
+                if (!tmp || virStrToLong_i(tmp + 1, &tmp, 10, &count) < 0)
+                    count = -1;
+            }
+            virFreeError(last_error);
+            last_error = NULL;
+            VIR_FREE(xml);
+        }
+
+        if (count < 0) {
+            virshReportError(ctl);
+            ret = false;
+        } else if (all) {
+            vshPrint(ctl, "%-12s %-12s %3d\n", _("maximum"), _("config"),
+                     count);
+        } else {
+            vshPrint(ctl, "%d\n", count);
+        }
+        virFreeError(last_error);
+        last_error = NULL;
+    }
+
+    if (all || (maximum && live)) {
+        count = virDomainGetVcpusFlags(dom, (VIR_DOMAIN_VCPU_MAXIMUM |
+                                             VIR_DOMAIN_AFFECT_LIVE));
+        if (count < 0 && (last_error->code == VIR_ERR_NO_SUPPORT
+                          || last_error->code == VIR_ERR_INVALID_ARG)) {
+            count = virDomainGetMaxVcpus(dom);
+        }
+
+        if (count < 0) {
+            virshReportError(ctl);
+            ret = false;
+        } else if (all) {
+            vshPrint(ctl, "%-12s %-12s %3d\n", _("maximum"), _("live"),
+                     count);
+        } else {
+            vshPrint(ctl, "%d\n", count);
+        }
+        virFreeError(last_error);
+        last_error = NULL;
+    }
+
+    if (all || (active && config)) {
+        count = virDomainGetVcpusFlags(dom, VIR_DOMAIN_AFFECT_CONFIG);
+        if (count < 0 && (last_error->code == VIR_ERR_NO_SUPPORT
+                          || last_error->code == VIR_ERR_INVALID_ARG)) {
+            char *tmp, *end;
+            char *xml = virDomainGetXMLDesc(dom, VIR_DOMAIN_XML_INACTIVE);
+            if (xml && (tmp = strstr(xml, "<vcpu"))) {
+                end = strchr(tmp, '>');
+                if (end) {
+                    *end = '\0';
+                    tmp = strstr(tmp, "current=");
+                    if (!tmp)
+                        tmp = end + 1;
+                    else {
+                        tmp += strlen("current=");
+                        tmp += *tmp == '\'' || *tmp == '"';
+                    }
+                }
+                if (!tmp || virStrToLong_i(tmp, &tmp, 10, &count) < 0)
+                    count = -1;
+            }
+            VIR_FREE(xml);
+        }
+
+        if (count < 0) {
+            virshReportError(ctl);
+            ret = false;
+        } else if (all) {
+            vshPrint(ctl, "%-12s %-12s %3d\n", _("current"), _("config"),
+                     count);
+        } else {
+            vshPrint(ctl, "%d\n", count);
+        }
+        virFreeError(last_error);
+        last_error = NULL;
+    }
+
+    if (all || (active && live)) {
+        count = virDomainGetVcpusFlags(dom, VIR_DOMAIN_AFFECT_LIVE);
+        if (count < 0 && (last_error->code == VIR_ERR_NO_SUPPORT
+                          || last_error->code == VIR_ERR_INVALID_ARG)) {
+            virDomainInfo info;
+            if (virDomainGetInfo(dom, &info) == 0)
+                count = info.nrVirtCpu;
+        }
+
+        if (count < 0) {
+            virshReportError(ctl);
+            ret = false;
+        } else if (all) {
+            vshPrint(ctl, "%-12s %-12s %3d\n", _("current"), _("live"),
+                     count);
+        } else {
+            vshPrint(ctl, "%d\n", count);
+        }
+        virFreeError(last_error);
+        last_error = NULL;
+    }
+
+    virDomainFree(dom);
+    return ret;
+}
+
+/*
+ * "vcpuinfo" command
+ */
+static const vshCmdInfo info_vcpuinfo[] = {
+    {"help", N_("detailed domain vcpu information")},
+    {"desc", N_("Returns basic information about the domain virtual CPUs.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_vcpuinfo[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdVcpuinfo(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainInfo info;
+    virDomainPtr dom;
+    virNodeInfo nodeinfo;
+    virVcpuInfoPtr cpuinfo;
+    unsigned char *cpumaps;
+    int ncpus, maxcpu;
+    size_t cpumaplen;
+    bool ret = true;
+    int n, m;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        return false;
+
+    if (virNodeGetInfo(ctl->conn, &nodeinfo) != 0) {
+        virDomainFree(dom);
+        return false;
+    }
+
+    if (virDomainGetInfo(dom, &info) != 0) {
+        virDomainFree(dom);
+        return false;
+    }
+
+    cpuinfo = vshMalloc(ctl, sizeof(virVcpuInfo)*info.nrVirtCpu);
+    maxcpu = VIR_NODEINFO_MAXCPUS(nodeinfo);
+    cpumaplen = VIR_CPU_MAPLEN(maxcpu);
+    cpumaps = vshMalloc(ctl, info.nrVirtCpu * cpumaplen);
+
+    if ((ncpus = virDomainGetVcpus(dom,
+                                   cpuinfo, info.nrVirtCpu,
+                                   cpumaps, cpumaplen)) >= 0) {
+        for (n = 0 ; n < ncpus ; n++) {
+            vshPrint(ctl, "%-15s %d\n", _("VCPU:"), n);
+            vshPrint(ctl, "%-15s %d\n", _("CPU:"), cpuinfo[n].cpu);
+            vshPrint(ctl, "%-15s %s\n", _("State:"),
+                     _(vshDomainVcpuStateToString(cpuinfo[n].state)));
+            if (cpuinfo[n].cpuTime != 0) {
+                double cpuUsed = cpuinfo[n].cpuTime;
+
+                cpuUsed /= 1000000000.0;
+
+                vshPrint(ctl, "%-15s %.1lfs\n", _("CPU time:"), cpuUsed);
+            }
+            vshPrint(ctl, "%-15s ", _("CPU Affinity:"));
+            for (m = 0; m < maxcpu; m++) {
+                vshPrint(ctl, "%c", VIR_CPU_USABLE(cpumaps, cpumaplen, n, m) ? 'y' : '-');
+            }
+            vshPrint(ctl, "\n");
+            if (n < (ncpus - 1)) {
+                vshPrint(ctl, "\n");
+            }
+        }
+    } else {
+        if (info.state == VIR_DOMAIN_SHUTOFF &&
+            (ncpus = virDomainGetVcpuPinInfo(dom, info.nrVirtCpu,
+                                             cpumaps, cpumaplen,
+                                             VIR_DOMAIN_AFFECT_CONFIG)) >= 0) {
+
+            /* fallback plan to use virDomainGetVcpuPinInfo */
+
+            for (n = 0; n < ncpus; n++) {
+                vshPrint(ctl, "%-15s %d\n", _("VCPU:"), n);
+                vshPrint(ctl, "%-15s %s\n", _("CPU:"), _("N/A"));
+                vshPrint(ctl, "%-15s %s\n", _("State:"), _("N/A"));
+                vshPrint(ctl, "%-15s %s\n", _("CPU time"), _("N/A"));
+                vshPrint(ctl, "%-15s ", _("CPU Affinity:"));
+                for (m = 0; m < maxcpu; m++) {
+                    vshPrint(ctl, "%c",
+                             VIR_CPU_USABLE(cpumaps, cpumaplen, n, m) ? 'y' : '-');
+                }
+                vshPrint(ctl, "\n");
+                if (n < (ncpus - 1)) {
+                    vshPrint(ctl, "\n");
+                }
+            }
+        } else {
+            ret = false;
+        }
+    }
+
+    VIR_FREE(cpumaps);
+    VIR_FREE(cpuinfo);
+    virDomainFree(dom);
+    return ret;
+}
+
+/*
+ * "vcpupin" command
+ */
+static const vshCmdInfo info_vcpupin[] = {
+    {"help", N_("control or query domain vcpu affinity")},
+    {"desc", N_("Pin domain VCPUs to host physical CPUs.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_vcpupin[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"vcpu", VSH_OT_INT, 0, N_("vcpu number")},
+    {"cpulist", VSH_OT_DATA, VSH_OFLAG_EMPTY_OK,
+     N_("host cpu number(s) to set, or omit option to query")},
+    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
+    {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
+    {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdVcpuPin(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainInfo info;
+    virDomainPtr dom;
+    virNodeInfo nodeinfo;
+    int vcpu = -1;
+    const char *cpulist = NULL;
+    bool ret = true;
+    unsigned char *cpumap = NULL;
+    unsigned char *cpumaps = NULL;
+    size_t cpumaplen;
+    bool bit, lastbit, isInvert;
+    int i, cpu, lastcpu, maxcpu, ncpus;
+    bool unuse = false;
+    const char *cur;
+    bool config = vshCommandOptBool(cmd, "config");
+    bool live = vshCommandOptBool(cmd, "live");
+    bool current = vshCommandOptBool(cmd, "current");
+    bool query = false; /* Query mode if no cpulist */
+    unsigned int flags = 0;
+
+    if (current) {
+        if (live || config) {
+            vshError(ctl, "%s", _("--current must be specified exclusively"));
+            return false;
+        }
+        flags = VIR_DOMAIN_AFFECT_CURRENT;
+    } else {
+        if (config)
+            flags |= VIR_DOMAIN_AFFECT_CONFIG;
+        if (live)
+            flags |= VIR_DOMAIN_AFFECT_LIVE;
+        /* neither option is specified */
+        if (!live && !config)
+            flags = -1;
+    }
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        return false;
+
+    if (vshCommandOptString(cmd, "cpulist", &cpulist) < 0) {
+        vshError(ctl, "%s", _("vcpupin: Missing cpulist."));
+        virDomainFree(dom);
+        return false;
+    }
+    query = !cpulist;
+
+    /* In query mode, "vcpu" is optional */
+    if (vshCommandOptInt(cmd, "vcpu", &vcpu) < !query) {
+        vshError(ctl, "%s",
+                 _("vcpupin: Invalid or missing vCPU number."));
+        virDomainFree(dom);
+        return false;
+    }
+
+    if (virNodeGetInfo(ctl->conn, &nodeinfo) != 0) {
+        virDomainFree(dom);
+        return false;
+    }
+
+    if (virDomainGetInfo(dom, &info) != 0) {
+        vshError(ctl, "%s", _("vcpupin: failed to get domain information."));
+        virDomainFree(dom);
+        return false;
+    }
+
+    if (vcpu >= info.nrVirtCpu) {
+        vshError(ctl, "%s", _("vcpupin: Invalid vCPU number."));
+        virDomainFree(dom);
+        return false;
+    }
+
+    maxcpu = VIR_NODEINFO_MAXCPUS(nodeinfo);
+    cpumaplen = VIR_CPU_MAPLEN(maxcpu);
+
+    /* Query mode: show CPU affinity information then exit.*/
+    if (query) {
+        /* When query mode and neither "live", "config" nor "current"
+         * is specified, set VIR_DOMAIN_AFFECT_CURRENT as flags */
+        if (flags == -1)
+            flags = VIR_DOMAIN_AFFECT_CURRENT;
+
+        cpumaps = vshMalloc(ctl, info.nrVirtCpu * cpumaplen);
+        if ((ncpus = virDomainGetVcpuPinInfo(dom, info.nrVirtCpu,
+                                             cpumaps, cpumaplen, flags)) >= 0) {
+
+            vshPrint(ctl, "%s %s\n", _("VCPU:"), _("CPU Affinity"));
+            vshPrint(ctl, "----------------------------------\n");
+            for (i = 0; i < ncpus; i++) {
+
+               if (vcpu != -1 && i != vcpu)
+                   continue;
+
+               bit = lastbit = isInvert = false;
+               lastcpu = -1;
+
+               vshPrint(ctl, "%4d: ", i);
+               for (cpu = 0; cpu < maxcpu; cpu++) {
+
+                   bit = VIR_CPU_USABLE(cpumaps, cpumaplen, i, cpu);
+
+                   isInvert = (bit ^ lastbit);
+                   if (bit && isInvert) {
+                       if (lastcpu == -1)
+                           vshPrint(ctl, "%d", cpu);
+                       else
+                           vshPrint(ctl, ",%d", cpu);
+                       lastcpu = cpu;
+                   }
+                   if (!bit && isInvert && lastcpu != cpu - 1)
+                       vshPrint(ctl, "-%d", cpu - 1);
+                   lastbit = bit;
+               }
+               if (bit && !isInvert) {
+                  vshPrint(ctl, "-%d", maxcpu - 1);
+               }
+               vshPrint(ctl, "\n");
+            }
+
+        } else {
+            ret = false;
+        }
+        VIR_FREE(cpumaps);
+        goto cleanup;
+    }
+
+    /* Pin mode: pinning specified vcpu to specified physical cpus*/
+
+    cpumap = vshCalloc(ctl, cpumaplen, sizeof(cpumap));
+    /* Parse cpulist */
+    cur = cpulist;
+    if (*cur == 0) {
+        goto parse_error;
+    } else if (*cur == 'r') {
+        for (cpu = 0; cpu < maxcpu; cpu++)
+            VIR_USE_CPU(cpumap, cpu);
+        cur = "";
+    }
+
+    while (*cur != 0) {
+
+        /* the char '^' denotes exclusive */
+        if (*cur == '^') {
+            cur++;
+            unuse = true;
+        }
+
+        /* parse physical CPU number */
+        if (!c_isdigit(*cur))
+            goto parse_error;
+        cpu  = virParseNumber(&cur);
+        if (cpu < 0) {
+            goto parse_error;
+        }
+        if (cpu >= maxcpu) {
+            vshError(ctl, _("Physical CPU %d doesn't exist."), cpu);
+            goto parse_error;
+        }
+        virSkipSpaces(&cur);
+
+        if (*cur == ',' || *cur == 0) {
+            if (unuse) {
+                VIR_UNUSE_CPU(cpumap, cpu);
+            } else {
+                VIR_USE_CPU(cpumap, cpu);
+            }
+        } else if (*cur == '-') {
+            /* the char '-' denotes range */
+            if (unuse) {
+                goto parse_error;
+            }
+            cur++;
+            virSkipSpaces(&cur);
+            /* parse the end of range */
+            lastcpu = virParseNumber(&cur);
+            if (lastcpu < cpu) {
+                goto parse_error;
+            }
+            if (lastcpu >= maxcpu) {
+                vshError(ctl, _("Physical CPU %d doesn't exist."), maxcpu);
+                goto parse_error;
+            }
+            for (i = cpu; i <= lastcpu; i++) {
+                VIR_USE_CPU(cpumap, i);
+            }
+            virSkipSpaces(&cur);
+        }
+
+        if (*cur == ',') {
+            cur++;
+            virSkipSpaces(&cur);
+            unuse = false;
+        } else if (*cur == 0) {
+            break;
+        } else {
+            goto parse_error;
+        }
+    }
+
+    if (flags == -1) {
+        if (virDomainPinVcpu(dom, vcpu, cpumap, cpumaplen) != 0) {
+            ret = false;
+        }
+    } else {
+        if (virDomainPinVcpuFlags(dom, vcpu, cpumap, cpumaplen, flags) != 0) {
+            ret = false;
+        }
+    }
+
+cleanup:
+    VIR_FREE(cpumap);
+    virDomainFree(dom);
+    return ret;
+
+parse_error:
+    vshError(ctl, "%s", _("cpulist: Invalid format."));
+    ret = false;
+    goto cleanup;
+}
+
+/*
+ * "setvcpus" command
+ */
+static const vshCmdInfo info_setvcpus[] = {
+    {"help", N_("change number of virtual CPUs")},
+    {"desc", N_("Change the number of virtual CPUs in the guest domain.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_setvcpus[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"count", VSH_OT_INT, VSH_OFLAG_REQ, N_("number of virtual CPUs")},
+    {"maximum", VSH_OT_BOOL, 0, N_("set maximum limit on next boot")},
+    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
+    {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
+    {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdSetvcpus(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom;
+    int count = 0;
+    bool ret = true;
+    bool maximum = vshCommandOptBool(cmd, "maximum");
+    bool config = vshCommandOptBool(cmd, "config");
+    bool live = vshCommandOptBool(cmd, "live");
+    bool current = vshCommandOptBool(cmd, "current");
+    unsigned int flags = 0;
+
+    if (current) {
+        if (live || config) {
+            vshError(ctl, "%s", _("--current must be specified exclusively"));
+            return false;
+        }
+        flags = VIR_DOMAIN_AFFECT_CURRENT;
+    } else {
+        if (config)
+            flags |= VIR_DOMAIN_AFFECT_CONFIG;
+        if (live)
+            flags |= VIR_DOMAIN_AFFECT_LIVE;
+        /* neither option is specified */
+        if (!live && !config && !maximum)
+            flags = -1;
+    }
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        return false;
+
+    if (vshCommandOptInt(cmd, "count", &count) < 0 || count <= 0) {
+        vshError(ctl, "%s", _("Invalid number of virtual CPUs"));
+        goto cleanup;
+    }
+
+    if (flags == -1) {
+        if (virDomainSetVcpus(dom, count) != 0) {
+            ret = false;
+        }
+    } else {
+        /* If the --maximum flag was given, we need to ensure only the
+           --config flag is in effect as well */
+        if (maximum) {
+            vshDebug(ctl, VSH_ERR_DEBUG, "--maximum flag was given\n");
+
+            flags |= VIR_DOMAIN_VCPU_MAXIMUM;
+
+            /* If neither the --config nor --live flags were given, OR
+               if just the --live flag was given, we need to error out
+               warning the user that the --maximum flag can only be used
+               with the --config flag */
+            if (live || !config) {
+
+                /* Warn the user about the invalid flag combination */
+                vshError(ctl, _("--maximum must be used with --config only"));
+                ret = false;
+                goto cleanup;
+            }
+        }
+
+        /* Apply the virtual cpu changes */
+        if (virDomainSetVcpusFlags(dom, count, flags) < 0) {
+            ret = false;
+        }
+    }
+
+  cleanup:
+    virDomainFree(dom);
+    return ret;
+}
+
+/*
+ * "cpu-compare" command
+ */
+static const vshCmdInfo info_cpu_compare[] = {
+    {"help", N_("compare host CPU with a CPU described by an XML file")},
+    {"desc", N_("compare CPU with host CPU")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_cpu_compare[] = {
+    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML CPU description")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdCPUCompare(vshControl *ctl, const vshCmd *cmd)
+{
+    const char *from = NULL;
+    bool ret = false;
+    char *buffer;
+    int result;
+    const char *snippet;
+
+    xmlDocPtr xml = NULL;
+    xmlXPathContextPtr ctxt = NULL;
+    xmlBufferPtr xml_buf = NULL;
+    xmlNodePtr node;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (vshCommandOptString(cmd, "file", &from) <= 0)
+        return false;
+
+    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
+        vshError(ctl, _("Failed to read file '%s' to compare"),
+                 from);
+        return false;
+    }
+
+    /* try to extract the CPU element from as it would appear in a domain XML*/
+    if (!(xml = virXMLParseStringCtxt(buffer, from, &ctxt)))
+        goto cleanup;
+
+    if ((node = virXPathNode("/cpu|"
+                             "/domain/cpu|"
+                              "/capabilities/host/cpu", ctxt))) {
+        if (!(xml_buf = xmlBufferCreate())) {
+            vshError(ctl, _("Can't create XML buffer to extract CPU element."));
+            goto cleanup;
+        }
+
+        if (xmlNodeDump(xml_buf, xml, node, 0, 0) < 0) {
+            vshError(ctl, _("Failed to extract CPU element snippet from domain XML."));
+            goto cleanup;
+        }
+
+        snippet = (const char *) xmlBufferContent(xml_buf);
+    } else {
+        vshError(ctl, _("File '%s' does not contain a <cpu> element or is not "
+                        "a valid domain or capabilities XML"), from);
+        goto cleanup;
+    }
+
+    result = virConnectCompareCPU(ctl->conn, snippet, 0);
+
+    switch (result) {
+    case VIR_CPU_COMPARE_INCOMPATIBLE:
+        vshPrint(ctl, _("CPU described in %s is incompatible with host CPU\n"),
+                 from);
+        goto cleanup;
+        break;
+
+    case VIR_CPU_COMPARE_IDENTICAL:
+        vshPrint(ctl, _("CPU described in %s is identical to host CPU\n"),
+                 from);
+        break;
+
+    case VIR_CPU_COMPARE_SUPERSET:
+        vshPrint(ctl, _("Host CPU is a superset of CPU described in %s\n"),
+                 from);
+        break;
+
+    case VIR_CPU_COMPARE_ERROR:
+    default:
+        vshError(ctl, _("Failed to compare host CPU with %s"), from);
+        goto cleanup;
+    }
+
+    ret = true;
+
+cleanup:
+    VIR_FREE(buffer);
+    xmlBufferFree(xml_buf);
+    xmlXPathFreeContext(ctxt);
+    xmlFreeDoc(xml);
+
+    return ret;
+}
+
+/*
+ * "cpu-baseline" command
+ */
+static const vshCmdInfo info_cpu_baseline[] = {
+    {"help", N_("compute baseline CPU")},
+    {"desc", N_("Compute baseline CPU for a set of given CPUs.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_cpu_baseline[] = {
+    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing XML CPU descriptions")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdCPUBaseline(vshControl *ctl, const vshCmd *cmd)
+{
+    const char *from = NULL;
+    bool ret = false;
+    char *buffer;
+    char *result = NULL;
+    const char **list = NULL;
+    int count = 0;
+
+    xmlDocPtr xml = NULL;
+    xmlNodePtr *node_list = NULL;
+    xmlXPathContextPtr ctxt = NULL;
+    xmlBufferPtr xml_buf = NULL;
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+    int i;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (vshCommandOptString(cmd, "file", &from) <= 0)
+        return false;
+
+    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
+        return false;
+
+    /* add a separate container around the xml */
+    virBufferStrcat(&buf, "<container>", buffer, "</container>", NULL);
+    if (virBufferError(&buf))
+        goto no_memory;
+
+    VIR_FREE(buffer);
+    buffer = virBufferContentAndReset(&buf);
+
+
+    if (!(xml = virXMLParseStringCtxt(buffer, from, &ctxt)))
+        goto cleanup;
+
+    if ((count = virXPathNodeSet("//cpu[not(ancestor::cpus)]",
+                                 ctxt, &node_list)) == -1)
+        goto cleanup;
+
+    if (count == 0) {
+        vshError(ctl, _("No host CPU specified in '%s'"), from);
+        goto cleanup;
+    }
+
+    list = vshCalloc(ctl, count, sizeof(const char *));
+
+    if (!(xml_buf = xmlBufferCreate()))
+        goto no_memory;
+
+    for (i = 0; i < count; i++) {
+        xmlBufferEmpty(xml_buf);
+
+        if (xmlNodeDump(xml_buf, xml,  node_list[i], 0, 0) < 0) {
+            vshError(ctl, _("Failed to extract <cpu> element"));
+            goto cleanup;
+        }
+
+        list[i] = vshStrdup(ctl, (const char *)xmlBufferContent(xml_buf));
+    }
+
+    result = virConnectBaselineCPU(ctl->conn, list, count, 0);
+
+    if (result) {
+        vshPrint(ctl, "%s", result);
+        ret = true;
+    }
+
+cleanup:
+    xmlXPathFreeContext(ctxt);
+    xmlFreeDoc(xml);
+    xmlBufferFree(xml_buf);
+    VIR_FREE(result);
+    if (list != NULL && count > 0) {
+        for (i = 0; i < count; i++)
+            VIR_FREE(list[i]);
+    }
+    VIR_FREE(list);
+    VIR_FREE(buffer);
+
+    return ret;
+
+no_memory:
+    vshError(ctl, "%s", _("Out of memory"));
+    ret = false;
+    goto cleanup;
+}
+
+/*
+ * "cpu-stats" command
+ */
+static const vshCmdInfo info_cpu_stats[] = {
+    {"help", N_("show domain cpu statistics")},
+    {"desc",
+     N_("Display per-CPU and total statistics about the domain's CPUs")},
+    {NULL, NULL},
+};
+
+static const vshCmdOptDef opts_cpu_stats[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"total", VSH_OT_BOOL, 0, N_("Show total statistics only")},
+    {"start", VSH_OT_INT, 0, N_("Show statistics from this CPU")},
+    {"count", VSH_OT_INT, 0, N_("Number of shown CPUs at most")},
+    {NULL, 0, 0, NULL},
+};
+
+static bool
+cmdCPUStats(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom;
+    virTypedParameterPtr params = NULL;
+    int i, j, pos, max_id, cpu = -1, show_count = -1, nparams;
+    bool show_total = false, show_per_cpu = false;
+    unsigned int flags = 0;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        return false;
+
+    show_total = vshCommandOptBool(cmd, "total");
+    if (vshCommandOptInt(cmd, "start", &cpu) > 0)
+        show_per_cpu = true;
+    if (vshCommandOptInt(cmd, "count", &show_count) > 0)
+        show_per_cpu = true;
+
+    /* default show per_cpu and total */
+    if (!show_total && !show_per_cpu) {
+        show_total = true;
+        show_per_cpu = true;
+    }
+
+    if (!show_per_cpu) /* show total stats only */
+        goto do_show_total;
+
+    /* check cpu, show_count, and ignore wrong argument */
+    if (cpu < 0)
+        cpu = 0;
+
+    /* get number of cpus on the node */
+    if ((max_id = virDomainGetCPUStats(dom, NULL, 0, 0, 0, flags)) < 0)
+        goto failed_stats;
+    if (show_count < 0 || show_count > max_id)
+        show_count = max_id;
+
+    /* get percpu information */
+    if ((nparams = virDomainGetCPUStats(dom, NULL, 0, 0, 1, flags)) < 0)
+        goto failed_stats;
+
+    if (!nparams) {
+        vshPrint(ctl, "%s", _("No per-CPU stats available"));
+        goto do_show_total;
+    }
+
+    if (VIR_ALLOC_N(params, nparams * MIN(show_count, 128)) < 0)
+        goto failed_params;
+
+    while (show_count) {
+        int ncpus = MIN(show_count, 128);
+
+        if (virDomainGetCPUStats(dom, params, nparams, cpu, ncpus, flags) < 0)
+            goto failed_stats;
+
+        for (i = 0; i < ncpus; i++) {
+            if (params[i * nparams].type == 0) /* this cpu is not in the map */
+                continue;
+            vshPrint(ctl, "CPU%d:\n", cpu + i);
+
+            for (j = 0; j < nparams; j++) {
+                pos = i * nparams + j;
+                vshPrint(ctl, "\t%-12s ", params[pos].field);
+                if ((STREQ(params[pos].field, VIR_DOMAIN_CPU_STATS_CPUTIME) ||
+                     STREQ(params[pos].field, VIR_DOMAIN_CPU_STATS_VCPUTIME)) &&
+                    params[j].type == VIR_TYPED_PARAM_ULLONG) {
+                    vshPrint(ctl, "%9lld.%09lld seconds\n",
+                             params[pos].value.ul / 1000000000,
+                             params[pos].value.ul % 1000000000);
+                } else {
+                    const char *s = vshGetTypedParamValue(ctl, &params[pos]);
+                    vshPrint(ctl, _("%s\n"), s);
+                    VIR_FREE(s);
+                }
+            }
+        }
+        cpu += ncpus;
+        show_count -= ncpus;
+        virTypedParameterArrayClear(params, nparams * ncpus);
+    }
+    VIR_FREE(params);
+
+do_show_total:
+    if (!show_total)
+        goto cleanup;
+
+    /* get supported num of parameter for total statistics */
+    if ((nparams = virDomainGetCPUStats(dom, NULL, 0, -1, 1, flags)) < 0)
+        goto failed_stats;
+
+    if (!nparams) {
+        vshPrint(ctl, "%s", _("No total stats available"));
+        goto cleanup;
+    }
+
+    if (VIR_ALLOC_N(params, nparams))
+        goto failed_params;
+
+    /* passing start_cpu == -1 gives us domain's total status */
+    if ((nparams = virDomainGetCPUStats(dom, params, nparams, -1, 1, flags)) < 0)
+        goto failed_stats;
+
+    vshPrint(ctl, _("Total:\n"));
+    for (i = 0; i < nparams; i++) {
+        vshPrint(ctl, "\t%-12s ", params[i].field);
+        if ((STREQ(params[i].field, VIR_DOMAIN_CPU_STATS_CPUTIME) ||
+             STREQ(params[i].field, VIR_DOMAIN_CPU_STATS_USERTIME) ||
+             STREQ(params[i].field, VIR_DOMAIN_CPU_STATS_SYSTEMTIME)) &&
+            params[i].type == VIR_TYPED_PARAM_ULLONG) {
+            vshPrint(ctl, "%9lld.%09lld seconds\n",
+                     params[i].value.ul / 1000000000,
+                     params[i].value.ul % 1000000000);
+        } else {
+            char *s = vshGetTypedParamValue(ctl, &params[i]);
+            vshPrint(ctl, "%s\n", s);
+            VIR_FREE(s);
+        }
+    }
+    virTypedParameterArrayClear(params, nparams);
+    VIR_FREE(params);
+
+cleanup:
+    virDomainFree(dom);
+    return true;
+
+failed_params:
+    virReportOOMError();
+    virDomainFree(dom);
+    return false;
+
+failed_stats:
+    vshError(ctl, _("Failed to virDomainGetCPUStats()\n"));
+    VIR_FREE(params);
+    virDomainFree(dom);
+    return false;
+}
+
+/*
+ * "create" command
+ */
+static const vshCmdInfo info_create[] = {
+    {"help", N_("create a domain from an XML file")},
+    {"desc", N_("Create a domain.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_create[] = {
+    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML domain description")},
+#ifndef WIN32
+    {"console", VSH_OT_BOOL, 0, N_("attach to console after creation")},
+#endif
+    {"paused", VSH_OT_BOOL, 0, N_("leave the guest paused after creation")},
+    {"autodestroy", VSH_OT_BOOL, 0, N_("automatically destroy the guest when virsh disconnects")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdCreate(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom;
+    const char *from = NULL;
+    bool ret = true;
+    char *buffer;
+#ifndef WIN32
+    bool console = vshCommandOptBool(cmd, "console");
+#endif
+    unsigned int flags = VIR_DOMAIN_NONE;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (vshCommandOptString(cmd, "file", &from) <= 0)
+        return false;
+
+    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
+        return false;
+
+    if (vshCommandOptBool(cmd, "paused"))
+        flags |= VIR_DOMAIN_START_PAUSED;
+    if (vshCommandOptBool(cmd, "autodestroy"))
+        flags |= VIR_DOMAIN_START_AUTODESTROY;
+
+    dom = virDomainCreateXML(ctl->conn, buffer, flags);
+    VIR_FREE(buffer);
+
+    if (dom != NULL) {
+        vshPrint(ctl, _("Domain %s created from %s\n"),
+                 virDomainGetName(dom), from);
+#ifndef WIN32
+        if (console)
+            cmdRunConsole(ctl, dom, NULL, 0);
+#endif
+        virDomainFree(dom);
+    } else {
+        vshError(ctl, _("Failed to create domain from %s"), from);
+        ret = false;
+    }
+    return ret;
+}
+
+/*
+ * "define" command
+ */
+static const vshCmdInfo info_define[] = {
+    {"help", N_("define (but don't start) a domain from an XML file")},
+    {"desc", N_("Define a domain.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_define[] = {
+    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML domain description")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDefine(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom;
+    const char *from = NULL;
+    bool ret = true;
+    char *buffer;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (vshCommandOptString(cmd, "file", &from) <= 0)
+        return false;
+
+    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
+        return false;
+
+    dom = virDomainDefineXML(ctl->conn, buffer);
+    VIR_FREE(buffer);
+
+    if (dom != NULL) {
+        vshPrint(ctl, _("Domain %s defined from %s\n"),
+                 virDomainGetName(dom), from);
+        virDomainFree(dom);
+    } else {
+        vshError(ctl, _("Failed to define domain from %s"), from);
+        ret = false;
+    }
+    return ret;
+}
+
+/*
+ * "destroy" command
+ */
+static const vshCmdInfo info_destroy[] = {
+    {"help", N_("destroy (stop) a domain")},
+    {"desc",
+     N_("Forcefully stop a given domain, but leave its resources intact.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_destroy[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"graceful", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("terminate gracefully")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDestroy(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom;
+    bool ret = true;
+    const char *name;
+    unsigned int flags = 0;
+    int result;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+        return false;
+
+    if (vshCommandOptBool(cmd, "graceful"))
+       flags |= VIR_DOMAIN_DESTROY_GRACEFUL;
+
+    if (flags)
+       result = virDomainDestroyFlags(dom, VIR_DOMAIN_DESTROY_GRACEFUL);
+    else
+       result = virDomainDestroy(dom);
+
+    if (result == 0) {
+        vshPrint(ctl, _("Domain %s destroyed\n"), name);
+    } else {
+        vshError(ctl, _("Failed to destroy domain %s"), name);
+        ret = false;
+    }
+
+    virDomainFree(dom);
+    return ret;
+}
+
+/*
+ * "desc" command for managing domain description and title
+ */
+static const vshCmdInfo info_desc[] = {
+    {"help", N_("show or set domain's description or title")},
+    {"desc", N_("Allows to show or modify description or title of a domain.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_desc[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"live", VSH_OT_BOOL, 0, N_("modify/get running state")},
+    {"config", VSH_OT_BOOL, 0, N_("modify/get persistent configuration")},
+    {"current", VSH_OT_BOOL, 0, N_("modify/get current state configuration")},
+    {"title", VSH_OT_BOOL, 0, N_("modify/get the title instead of description")},
+    {"edit", VSH_OT_BOOL, 0, N_("open an editor to modify the description")},
+    {"new-desc", VSH_OT_ARGV, 0, N_("message")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDesc(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
+{
+    virDomainPtr dom;
+    bool config = vshCommandOptBool(cmd, "config");
+    bool live = vshCommandOptBool(cmd, "live");
+    bool current = vshCommandOptBool(cmd, "current");
+
+    bool title = vshCommandOptBool(cmd, "title");
+    bool edit = vshCommandOptBool(cmd, "edit");
+
+    int state;
+    int type;
+    char *desc = NULL;
+    char *desc_edited = NULL;
+    char *tmp = NULL;
+    char *tmpstr;
+    const vshCmdOpt *opt = NULL;
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+    bool pad = false;
+    bool ret = false;
+    unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
+
+    if (current) {
+        if (live || config) {
+            vshError(ctl, "%s", _("--current must be specified exclusively"));
+            return false;
+        }
+        flags = VIR_DOMAIN_AFFECT_CURRENT;
+    } else {
+        if (config)
+            flags |= VIR_DOMAIN_AFFECT_CONFIG;
+        if (live)
+            flags |= VIR_DOMAIN_AFFECT_LIVE;
+    }
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        return false;
+
+    if ((state = vshDomainState(ctl, dom, NULL)) < 0)
+        goto cleanup;
+
+    while ((opt = vshCommandOptArgv(cmd, opt))) {
+        if (pad)
+            virBufferAddChar(&buf, ' ');
+        pad = true;
+        virBufferAdd(&buf, opt->data, -1);
+    }
+
+    if (title)
+        type = VIR_DOMAIN_METADATA_TITLE;
+    else
+        type = VIR_DOMAIN_METADATA_DESCRIPTION;
+
+    if (virBufferError(&buf)) {
+        vshPrint(ctl, "%s", _("Failed to collect new description/title"));
+        goto cleanup;
+    }
+    desc = virBufferContentAndReset(&buf);
+
+    if (edit || desc) {
+        if (!desc) {
+                desc = vshGetDomainDescription(ctl, dom, title,
+                                           config?VIR_DOMAIN_XML_INACTIVE:0);
+                if (!desc)
+                    goto cleanup;
+        }
+
+        if (edit) {
+            /* Create and open the temporary file. */
+            if (!(tmp = editWriteToTempFile(ctl, desc)))
+                goto cleanup;
+
+            /* Start the editor. */
+            if (editFile(ctl, tmp) == -1)
+                goto cleanup;
+
+            /* Read back the edited file. */
+            if (!(desc_edited = editReadBackFile(ctl, tmp)))
+                goto cleanup;
+
+            /* strip a possible newline at the end of file; some
+             * editors enforce a newline, this makes editing the title
+             * more convenient */
+            if (title &&
+                (tmpstr = strrchr(desc_edited, '\n')) &&
+                *(tmpstr+1) == '\0')
+                *tmpstr = '\0';
+
+            /* Compare original XML with edited.  Has it changed at all? */
+            if (STREQ(desc, desc_edited)) {
+                vshPrint(ctl, _("Domain description not changed.\n"));
+                ret = true;
+                goto cleanup;
+            }
+
+            VIR_FREE(desc);
+            desc = desc_edited;
+            desc_edited = NULL;
+        }
+
+        if (virDomainSetMetadata(dom, type, desc, NULL, NULL, flags) < 0) {
+            vshError(ctl, "%s",
+                     _("Failed to set new domain description"));
+            goto cleanup;
+        }
+        vshPrint(ctl, "%s", _("Domain description updated successfully"));
+    } else {
+        desc = vshGetDomainDescription(ctl, dom, title,
+                                       config?VIR_DOMAIN_XML_INACTIVE:0);
+        if (!desc)
+            goto cleanup;
+
+        if (strlen(desc) > 0)
+            vshPrint(ctl, "%s", desc);
+        else
+            vshPrint(ctl, _("No description for domain: %s"),
+                     virDomainGetName(dom));
+    }
+
+    ret = true;
+cleanup:
+    VIR_FREE(desc_edited);
+    VIR_FREE(desc);
+    if (tmp) {
+        unlink(tmp);
+        VIR_FREE(tmp);
+    }
+    if (dom)
+        virDomainFree(dom);
+    return ret;
+}
+
+/*
+ * "inject-nmi" command
+ */
+static const vshCmdInfo info_inject_nmi[] = {
+    {"help", N_("Inject NMI to the guest")},
+    {"desc", N_("Inject NMI to the guest domain.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_inject_nmi[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdInjectNMI(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom;
+    int ret = true;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        return false;
+
+    if (virDomainInjectNMI(dom, 0) < 0)
+            ret = false;
+
+    virDomainFree(dom);
+    return ret;
+}
+
+/*
+ * "send-key" command
+ */
+static const vshCmdInfo info_send_key[] = {
+    {"help", N_("Send keycodes to the guest")},
+    {"desc", N_("Send keycodes (integers or symbolic names) to the guest")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_send_key[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"codeset", VSH_OT_STRING, VSH_OFLAG_REQ_OPT,
+     N_("the codeset of keycodes, default:linux")},
+    {"holdtime", VSH_OT_INT, VSH_OFLAG_REQ_OPT,
+     N_("the time (in milliseconds) how long the keys will be held")},
+    {"keycode", VSH_OT_ARGV, VSH_OFLAG_REQ, N_("the key code")},
+    {NULL, 0, 0, NULL}
+};
+
+static int
+get_integer_keycode(const char *key_name)
+{
+    unsigned int val;
+
+    if (virStrToLong_ui(key_name, NULL, 0, &val) < 0 || val > 0xffff || !val)
+        return -1;
+    return val;
+}
+
+static bool
+cmdSendKey(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom;
+    int ret = false;
+    const char *codeset_option;
+    int codeset;
+    int holdtime;
+    int count = 0;
+    const vshCmdOpt *opt = NULL;
+    int keycode;
+    unsigned int keycodes[VIR_DOMAIN_SEND_KEY_MAX_KEYS];
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        return false;
+
+    if (vshCommandOptString(cmd, "codeset", &codeset_option) <= 0)
+        codeset_option = "linux";
+
+    if (vshCommandOptInt(cmd, "holdtime", &holdtime) <= 0)
+        holdtime = 0;
+
+    codeset = virKeycodeSetTypeFromString(codeset_option);
+    if ((int)codeset < 0) {
+        vshError(ctl, _("unknown codeset: '%s'"), codeset_option);
+        goto cleanup;
+    }
+
+    while ((opt = vshCommandOptArgv(cmd, opt))) {
+        if (count == VIR_DOMAIN_SEND_KEY_MAX_KEYS) {
+            vshError(ctl, _("too many keycodes"));
+            goto cleanup;
+        }
+
+        if ((keycode = get_integer_keycode(opt->data)) <= 0) {
+            if ((keycode = virKeycodeValueFromString(codeset, opt->data)) <= 0) {
+                vshError(ctl, _("invalid keycode: '%s'"), opt->data);
+                goto cleanup;
+            }
+        }
+
+        keycodes[count] = keycode;
+        count++;
+    }
+
+    if (!(virDomainSendKey(dom, codeset, holdtime, keycodes, count, 0) < 0))
+        ret = true;
+
+cleanup:
+    virDomainFree(dom);
+    return ret;
+}
+
+/*
+ * "setmem" command
+ */
+static const vshCmdInfo info_setmem[] = {
+    {"help", N_("change memory allocation")},
+    {"desc", N_("Change the current memory allocation in the guest domain.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_setmem[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"kilobytes", VSH_OT_ALIAS, 0, "size"},
+    {"size", VSH_OT_INT, VSH_OFLAG_REQ,
+     N_("new memory size, as scaled integer (default KiB)")},
+    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
+    {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
+    {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdSetmem(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom;
+    unsigned long long bytes = 0;
+    unsigned long long max;
+    unsigned long kibibytes = 0;
+    bool ret = true;
+    bool config = vshCommandOptBool(cmd, "config");
+    bool live = vshCommandOptBool(cmd, "live");
+    bool current = vshCommandOptBool(cmd, "current");
+    unsigned int flags = 0;
+
+    if (current) {
+        if (live || config) {
+            vshError(ctl, "%s", _("--current must be specified exclusively"));
+            return false;
+        }
+        flags = VIR_DOMAIN_AFFECT_CURRENT;
+    } else {
+        if (config)
+            flags |= VIR_DOMAIN_AFFECT_CONFIG;
+        if (live)
+            flags |= VIR_DOMAIN_AFFECT_LIVE;
+        /* neither option is specified */
+        if (!live && !config)
+            flags = -1;
+    }
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        return false;
+
+    /* The API expects 'unsigned long' KiB, so depending on whether we
+     * are 32-bit or 64-bit determines the maximum we can use.  */
+    if (sizeof(kibibytes) < sizeof(max))
+        max = 1024ull * ULONG_MAX;
+    else
+        max = ULONG_MAX;
+    if (vshCommandOptScaledInt(cmd, "size", &bytes, 1024, max) < 0) {
+        vshError(ctl, "%s", _("memory size has to be a number"));
+        virDomainFree(dom);
+        return false;
+    }
+    kibibytes = VIR_DIV_UP(bytes, 1024);
+
+    if (flags == -1) {
+        if (virDomainSetMemory(dom, kibibytes) != 0) {
+            ret = false;
+        }
+    } else {
+        if (virDomainSetMemoryFlags(dom, kibibytes, flags) < 0) {
+            ret = false;
+        }
+    }
+
+    virDomainFree(dom);
+    return ret;
+}
+
+/*
+ * "setmaxmem" command
+ */
+static const vshCmdInfo info_setmaxmem[] = {
+    {"help", N_("change maximum memory limit")},
+    {"desc", N_("Change the maximum memory allocation limit in the guest domain.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_setmaxmem[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"kilobytes", VSH_OT_ALIAS, 0, "size"},
+    {"size", VSH_OT_INT, VSH_OFLAG_REQ,
+     N_("new maximum memory size, as scaled integer (default KiB)")},
+    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
+    {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
+    {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdSetmaxmem(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom;
+    unsigned long long bytes = 0;
+    unsigned long long max;
+    unsigned long kibibytes = 0;
+    bool ret = true;
+    bool config = vshCommandOptBool(cmd, "config");
+    bool live = vshCommandOptBool(cmd, "live");
+    bool current = vshCommandOptBool(cmd, "current");
+    unsigned int flags = VIR_DOMAIN_MEM_MAXIMUM;
+
+    if (current) {
+        if (live || config) {
+            vshError(ctl, "%s", _("--current must be specified exclusively"));
+            return false;
+        }
+    } else {
+        if (config)
+            flags |= VIR_DOMAIN_AFFECT_CONFIG;
+        if (live)
+            flags |= VIR_DOMAIN_AFFECT_LIVE;
+        /* neither option is specified */
+        if (!live && !config)
+            flags = -1;
+    }
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        return false;
+
+    /* The API expects 'unsigned long' KiB, so depending on whether we
+     * are 32-bit or 64-bit determines the maximum we can use.  */
+    if (sizeof(kibibytes) < sizeof(max))
+        max = 1024ull * ULONG_MAX;
+    else
+        max = ULONG_MAX;
+    if (vshCommandOptScaledInt(cmd, "size", &bytes, 1024, max) < 0) {
+        vshError(ctl, "%s", _("memory size has to be a number"));
+        virDomainFree(dom);
+        return false;
+    }
+    kibibytes = VIR_DIV_UP(bytes, 1024);
+
+    if (flags == -1) {
+        if (virDomainSetMaxMemory(dom, kibibytes) != 0) {
+            vshError(ctl, "%s", _("Unable to change MaxMemorySize"));
+            ret = false;
+        }
+    } else {
+        if (virDomainSetMemoryFlags(dom, kibibytes, flags) < 0) {
+            vshError(ctl, "%s", _("Unable to change MaxMemorySize"));
+            ret = false;
+        }
+    }
+
+    virDomainFree(dom);
+    return ret;
+}
+
+/*
+ * "memtune" command
+ */
+static const vshCmdInfo info_memtune[] = {
+    {"help", N_("Get or set memory parameters")},
+    {"desc", N_("Get or set the current memory parameters for a guest"
+                " domain.\n"
+                "    To get the memory parameters use following command: \n\n"
+                "    virsh # memtune <domain>")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_memtune[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"hard-limit", VSH_OT_INT, VSH_OFLAG_NONE,
+     N_("Max memory, as scaled integer (default KiB)")},
+    {"soft-limit", VSH_OT_INT, VSH_OFLAG_NONE,
+     N_("Memory during contention, as scaled integer (default KiB)")},
+    {"swap-hard-limit", VSH_OT_INT, VSH_OFLAG_NONE,
+     N_("Max memory plus swap, as scaled integer (default KiB)")},
+    {"min-guarantee", VSH_OT_INT, VSH_OFLAG_NONE,
+     N_("Min guaranteed memory, as scaled integer (default KiB)")},
+    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
+    {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
+    {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
+    {NULL, 0, 0, NULL}
+};
+
+static int
+vshMemtuneGetSize(const vshCmd *cmd, const char *name, long long *value)
+{
+    int ret;
+    unsigned long long tmp;
+    const char *str;
+    char *end;
+
+    ret = vshCommandOptString(cmd, name, &str);
+    if (ret <= 0)
+        return ret;
+    if (virStrToLong_ll(str, &end, 10, value) < 0)
+        return -1;
+    if (*value < 0) {
+        *value = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
+        return 1;
+    }
+    tmp = *value;
+    if (virScaleInteger(&tmp, end, 1024, LLONG_MAX) < 0)
+        return -1;
+    *value = VIR_DIV_UP(tmp, 1024);
+    return 0;
+}
+
+static bool
+cmdMemtune(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom;
+    long long hard_limit = 0, soft_limit = 0, swap_hard_limit = 0;
+    long long min_guarantee = 0;
+    int nparams = 0;
+    unsigned int i = 0;
+    virTypedParameterPtr params = NULL, temp = NULL;
+    bool ret = false;
+    unsigned int flags = 0;
+    bool current = vshCommandOptBool(cmd, "current");
+    bool config = vshCommandOptBool(cmd, "config");
+    bool live = vshCommandOptBool(cmd, "live");
+
+    if (current) {
+        if (live || config) {
+            vshError(ctl, "%s", _("--current must be specified exclusively"));
+            return false;
+        }
+        flags = VIR_DOMAIN_AFFECT_CURRENT;
+    } else {
+        if (config)
+            flags |= VIR_DOMAIN_AFFECT_CONFIG;
+        if (live)
+            flags |= VIR_DOMAIN_AFFECT_LIVE;
+    }
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        return false;
+
+    if (vshMemtuneGetSize(cmd, "hard-limit", &hard_limit) < 0 ||
+        vshMemtuneGetSize(cmd, "soft-limit", &soft_limit) < 0 ||
+        vshMemtuneGetSize(cmd, "swap-hard-limit", &swap_hard_limit) < 0 ||
+        vshMemtuneGetSize(cmd, "min-guarantee", &min_guarantee) < 0) {
+        vshError(ctl, "%s",
+                 _("Unable to parse integer parameter"));
+        goto cleanup;
+    }
+
+    if (hard_limit)
+        nparams++;
+
+    if (soft_limit)
+        nparams++;
+
+    if (swap_hard_limit)
+        nparams++;
+
+    if (min_guarantee)
+        nparams++;
+
+    if (nparams == 0) {
+        /* get the number of memory parameters */
+        if (virDomainGetMemoryParameters(dom, NULL, &nparams, flags) != 0) {
+            vshError(ctl, "%s",
+                     _("Unable to get number of memory parameters"));
+            goto cleanup;
+        }
+
+        if (nparams == 0) {
+            /* nothing to output */
+            ret = true;
+            goto cleanup;
+        }
+
+        /* now go get all the memory parameters */
+        params = vshCalloc(ctl, nparams, sizeof(*params));
+        if (virDomainGetMemoryParameters(dom, params, &nparams, flags) != 0) {
+            vshError(ctl, "%s", _("Unable to get memory parameters"));
+            goto cleanup;
+        }
+
+        for (i = 0; i < nparams; i++) {
+            if (params[i].type == VIR_TYPED_PARAM_ULLONG &&
+                params[i].value.ul == VIR_DOMAIN_MEMORY_PARAM_UNLIMITED) {
+                vshPrint(ctl, "%-15s: %s\n", params[i].field, _("unlimited"));
+            } else {
+                char *str = vshGetTypedParamValue(ctl, &params[i]);
+                vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
+                VIR_FREE(str);
+            }
+        }
+
+        ret = true;
+    } else {
+        /* set the memory parameters */
+        params = vshCalloc(ctl, nparams, sizeof(*params));
+
+        for (i = 0; i < nparams; i++) {
+            temp = &params[i];
+
+            /*
+             * Some magic here, this is used to fill the params structure with
+             * the valid arguments passed, after filling the particular
+             * argument we purposely make them 0, so on the next pass it goes
+             * to the next valid argument and so on.
+             */
+            if (soft_limit) {
+                if (virTypedParameterAssign(temp,
+                                            VIR_DOMAIN_MEMORY_SOFT_LIMIT,
+                                            VIR_TYPED_PARAM_ULLONG,
+                                            soft_limit) < 0)
+                    goto error;
+                soft_limit = 0;
+            } else if (hard_limit) {
+                if (virTypedParameterAssign(temp,
+                                            VIR_DOMAIN_MEMORY_HARD_LIMIT,
+                                            VIR_TYPED_PARAM_ULLONG,
+                                            hard_limit) < 0)
+                    goto error;
+                hard_limit = 0;
+            } else if (swap_hard_limit) {
+                if (virTypedParameterAssign(temp,
+                                            VIR_DOMAIN_MEMORY_SWAP_HARD_LIMIT,
+                                            VIR_TYPED_PARAM_ULLONG,
+                                            swap_hard_limit) < 0)
+                    goto error;
+                swap_hard_limit = 0;
+            } else if (min_guarantee) {
+                if (virTypedParameterAssign(temp,
+                                            VIR_DOMAIN_MEMORY_MIN_GUARANTEE,
+                                            VIR_TYPED_PARAM_ULLONG,
+                                            min_guarantee) < 0)
+                    goto error;
+                min_guarantee = 0;
+            }
+
+            /* If the user has passed -1, we interpret it as unlimited */
+            if (temp->value.ul == -1)
+                temp->value.ul = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
+        }
+        if (virDomainSetMemoryParameters(dom, params, nparams, flags) != 0)
+            goto error;
+        else
+            ret = true;
+    }
+
+cleanup:
+    VIR_FREE(params);
+    virDomainFree(dom);
+    return ret;
+
+error:
+    vshError(ctl, "%s", _("Unable to change memory parameters"));
+    goto cleanup;
+}
+
+/*
+ * "numatune" command
+ */
+static const vshCmdInfo info_numatune[] = {
+    {"help", N_("Get or set numa parameters")},
+    {"desc", N_("Get or set the current numa parameters for a guest"
+                " domain.\n"
+                "    To get the numa parameters use following command: \n\n"
+                "    virsh # numatune <domain>")},
+    {NULL, NULL}
+
+};
+
+static const vshCmdOptDef opts_numatune[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"mode", VSH_OT_DATA, VSH_OFLAG_NONE,
+     N_("NUMA mode, one of strict, preferred and interleave")},
+    {"nodeset", VSH_OT_DATA, VSH_OFLAG_NONE,
+     N_("NUMA node selections to set")},
+    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
+    {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
+    {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdNumatune(vshControl * ctl, const vshCmd * cmd)
+{
+    virDomainPtr dom;
+    int nparams = 0;
+    unsigned int i = 0;
+    virTypedParameterPtr params = NULL, temp = NULL;
+    const char *nodeset = NULL;
+    bool ret = false;
+    unsigned int flags = 0;
+    bool current = vshCommandOptBool(cmd, "current");
+    bool config = vshCommandOptBool(cmd, "config");
+    bool live = vshCommandOptBool(cmd, "live");
+    const char *mode = NULL;
+
+    if (current) {
+        if (live || config) {
+            vshError(ctl, "%s", _("--current must be specified exclusively"));
+            return false;
+        }
+        flags = VIR_DOMAIN_AFFECT_CURRENT;
+    } else {
+        if (config)
+            flags |= VIR_DOMAIN_AFFECT_CONFIG;
+        if (live)
+            flags |= VIR_DOMAIN_AFFECT_LIVE;
+    }
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        return false;
+
+    if (vshCommandOptString(cmd, "nodeset", &nodeset) < 0) {
+        vshError(ctl, "%s", _("Unable to parse nodeset."));
+        virDomainFree(dom);
+        return false;
+    }
+    if (nodeset)
+        nparams++;
+    if (vshCommandOptString(cmd, "mode", &mode) < 0) {
+        vshError(ctl, "%s", _("Unable to parse mode."));
+        virDomainFree(dom);
+        return false;
+    }
+    if (mode)
+        nparams++;
+
+    if (nparams == 0) {
+        /* get the number of numa parameters */
+        if (virDomainGetNumaParameters(dom, NULL, &nparams, flags) != 0) {
+            vshError(ctl, "%s",
+                     _("Unable to get number of memory parameters"));
+            goto cleanup;
+        }
+
+        if (nparams == 0) {
+            /* nothing to output */
+            ret = true;
+            goto cleanup;
+        }
+
+        /* now go get all the numa parameters */
+        params = vshCalloc(ctl, nparams, sizeof(*params));
+        if (virDomainGetNumaParameters(dom, params, &nparams, flags) != 0) {
+            vshError(ctl, "%s", _("Unable to get numa parameters"));
+            goto cleanup;
+        }
+
+        for (i = 0; i < nparams; i++) {
+            if (params[i].type == VIR_TYPED_PARAM_INT &&
+                STREQ(params[i].field, VIR_DOMAIN_NUMA_MODE)) {
+                vshPrint(ctl, "%-15s: %s\n", params[i].field,
+                         virDomainNumatuneMemModeTypeToString(params[i].value.i));
+            } else {
+                char *str = vshGetTypedParamValue(ctl, &params[i]);
+                vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
+                VIR_FREE(str);
+            }
+        }
+
+        ret = true;
+    } else {
+        /* set the numa parameters */
+        params = vshCalloc(ctl, nparams, sizeof(*params));
+
+        for (i = 0; i < nparams; i++) {
+            temp = &params[i];
+
+            /*
+             * Some magic here, this is used to fill the params structure with
+             * the valid arguments passed, after filling the particular
+             * argument we purposely make them 0, so on the next pass it goes
+             * to the next valid argument and so on.
+             */
+            if (mode) {
+                /* Accept string or integer, in case server
+                 * understands newer integer than what strings we were
+                 * compiled with */
+                if ((temp->value.i =
+                    virDomainNumatuneMemModeTypeFromString(mode)) < 0 &&
+                    virStrToLong_i(mode, NULL, 0, &temp->value.i) < 0) {
+                    vshError(ctl, _("Invalid mode: %s"), mode);
+                    goto cleanup;
+                }
+                if (!virStrcpy(temp->field, VIR_DOMAIN_NUMA_MODE,
+                               sizeof(temp->field)))
+                    goto cleanup;
+                temp->type = VIR_TYPED_PARAM_INT;
+                mode = NULL;
+            } else if (nodeset) {
+                temp->value.s = vshStrdup(ctl, nodeset);
+                temp->type = VIR_TYPED_PARAM_STRING;
+                if (!virStrcpy(temp->field, VIR_DOMAIN_NUMA_NODESET,
+                               sizeof(temp->field)))
+                    goto cleanup;
+                nodeset = NULL;
+            }
+        }
+        if (virDomainSetNumaParameters(dom, params, nparams, flags) != 0)
+            vshError(ctl, "%s", _("Unable to change numa parameters"));
+        else
+            ret = true;
+    }
+
+  cleanup:
+    virTypedParameterArrayClear(params, nparams);
+    VIR_FREE(params);
+    virDomainFree(dom);
+    return ret;
+}
+
+/*
+ * "dumpxml" command
+ */
+static const vshCmdInfo info_dumpxml[] = {
+    {"help", N_("domain information in XML")},
+    {"desc", N_("Output the domain information as an XML dump to stdout.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_dumpxml[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"inactive", VSH_OT_BOOL, 0, N_("show inactive defined XML")},
+    {"security-info", VSH_OT_BOOL, 0, N_("include security sensitive information in XML dump")},
+    {"update-cpu", VSH_OT_BOOL, 0, N_("update guest CPU according to host CPU")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDumpXML(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom;
+    bool ret = true;
+    char *dump;
+    unsigned int flags = 0;
+    bool inactive = vshCommandOptBool(cmd, "inactive");
+    bool secure = vshCommandOptBool(cmd, "security-info");
+    bool update = vshCommandOptBool(cmd, "update-cpu");
+
+    if (inactive)
+        flags |= VIR_DOMAIN_XML_INACTIVE;
+    if (secure)
+        flags |= VIR_DOMAIN_XML_SECURE;
+    if (update)
+        flags |= VIR_DOMAIN_XML_UPDATE_CPU;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        return false;
+
+    dump = virDomainGetXMLDesc(dom, flags);
+    if (dump != NULL) {
+        vshPrint(ctl, "%s", dump);
+        VIR_FREE(dump);
+    } else {
+        ret = false;
+    }
+
+    virDomainFree(dom);
+    return ret;
+}
+
+/*
+ * "domxml-from-native" command
+ */
+static const vshCmdInfo info_domxmlfromnative[] = {
+    {"help", N_("Convert native config to domain XML")},
+    {"desc", N_("Convert native guest configuration format to domain XML format.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_domxmlfromnative[] = {
+    {"format", VSH_OT_DATA, VSH_OFLAG_REQ, N_("source config data format")},
+    {"config", VSH_OT_DATA, VSH_OFLAG_REQ, N_("config data file to import from")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDomXMLFromNative(vshControl *ctl, const vshCmd *cmd)
+{
+    bool ret = true;
+    const char *format = NULL;
+    const char *configFile = NULL;
+    char *configData;
+    char *xmlData;
+    unsigned int flags = 0;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (vshCommandOptString(cmd, "format", &format) < 0 ||
+        vshCommandOptString(cmd, "config", &configFile) < 0)
+        return false;
+
+    if (virFileReadAll(configFile, 1024*1024, &configData) < 0)
+        return false;
+
+    xmlData = virConnectDomainXMLFromNative(ctl->conn, format, configData, flags);
+    if (xmlData != NULL) {
+        vshPrint(ctl, "%s", xmlData);
+        VIR_FREE(xmlData);
+    } else {
+        ret = false;
+    }
+
+    VIR_FREE(configData);
+    return ret;
+}
+
+/*
+ * "domxml-to-native" command
+ */
+static const vshCmdInfo info_domxmltonative[] = {
+    {"help", N_("Convert domain XML to native config")},
+    {"desc", N_("Convert domain XML config to a native guest configuration format.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_domxmltonative[] = {
+    {"format", VSH_OT_DATA, VSH_OFLAG_REQ, N_("target config data type format")},
+    {"xml", VSH_OT_DATA, VSH_OFLAG_REQ, N_("xml data file to export from")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDomXMLToNative(vshControl *ctl, const vshCmd *cmd)
+{
+    bool ret = true;
+    const char *format = NULL;
+    const char *xmlFile = NULL;
+    char *configData;
+    char *xmlData;
+    unsigned int flags = 0;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (vshCommandOptString(cmd, "format", &format) < 0
+        || vshCommandOptString(cmd, "xml", &xmlFile) < 0)
+        return false;
+
+    if (virFileReadAll(xmlFile, 1024*1024, &xmlData) < 0)
+        return false;
+
+    configData = virConnectDomainXMLToNative(ctl->conn, format, xmlData, flags);
+    if (configData != NULL) {
+        vshPrint(ctl, "%s", configData);
+        VIR_FREE(configData);
+    } else {
+        ret = false;
+    }
+
+    VIR_FREE(xmlData);
+    return ret;
+}
+
+/*
+ * "domname" command
+ */
+static const vshCmdInfo info_domname[] = {
+    {"help", N_("convert a domain id or UUID to domain name")},
+    {"desc", ""},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_domname[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain id or uuid")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDomname(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+    if (!(dom = vshCommandOptDomainBy(ctl, cmd, NULL,
+                                      VSH_BYID|VSH_BYUUID)))
+        return false;
+
+    vshPrint(ctl, "%s\n", virDomainGetName(dom));
+    virDomainFree(dom);
+    return true;
+}
+
+/*
+ * "domid" command
+ */
+static const vshCmdInfo info_domid[] = {
+    {"help", N_("convert a domain name or UUID to domain id")},
+    {"desc", ""},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_domid[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name or uuid")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDomid(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom;
+    unsigned int id;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+    if (!(dom = vshCommandOptDomainBy(ctl, cmd, NULL,
+                                      VSH_BYNAME|VSH_BYUUID)))
+        return false;
+
+    id = virDomainGetID(dom);
+    if (id == ((unsigned int)-1))
+        vshPrint(ctl, "%s\n", "-");
+    else
+        vshPrint(ctl, "%d\n", id);
+    virDomainFree(dom);
+    return true;
+}
+
+/*
+ * "domuuid" command
+ */
+static const vshCmdInfo info_domuuid[] = {
+    {"help", N_("convert a domain name or id to domain UUID")},
+    {"desc", ""},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_domuuid[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain id or name")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDomuuid(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom;
+    char uuid[VIR_UUID_STRING_BUFLEN];
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+    if (!(dom = vshCommandOptDomainBy(ctl, cmd, NULL,
+                                      VSH_BYNAME|VSH_BYID)))
+        return false;
+
+    if (virDomainGetUUIDString(dom, uuid) != -1)
+        vshPrint(ctl, "%s\n", uuid);
+    else
+        vshError(ctl, "%s", _("failed to get domain UUID"));
+
+    virDomainFree(dom);
+    return true;
+}
+
+/*
+ * "migrate" command
+ */
+static const vshCmdInfo info_migrate[] = {
+    {"help", N_("migrate domain to another host")},
+    {"desc", N_("Migrate domain to another host.  Add --live for live migration.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_migrate[] = {
+    {"live", VSH_OT_BOOL, 0, N_("live migration")},
+    {"p2p", VSH_OT_BOOL, 0, N_("peer-2-peer migration")},
+    {"direct", VSH_OT_BOOL, 0, N_("direct migration")},
+    {"tunneled", VSH_OT_ALIAS, 0, "tunnelled"},
+    {"tunnelled", VSH_OT_BOOL, 0, N_("tunnelled migration")},
+    {"persistent", VSH_OT_BOOL, 0, N_("persist VM on destination")},
+    {"undefinesource", VSH_OT_BOOL, 0, N_("undefine VM on source")},
+    {"suspend", VSH_OT_BOOL, 0, N_("do not restart the domain on the destination host")},
+    {"copy-storage-all", VSH_OT_BOOL, 0, N_("migration with non-shared storage with full disk copy")},
+    {"copy-storage-inc", VSH_OT_BOOL, 0, N_("migration with non-shared storage with incremental copy (same base image shared between source and destination)")},
+    {"change-protection", VSH_OT_BOOL, 0,
+     N_("prevent any configuration changes to domain until migration ends)")},
+    {"unsafe", VSH_OT_BOOL, 0, N_("force migration even if it may be unsafe")},
+    {"verbose", VSH_OT_BOOL, 0, N_("display the progress of migration")},
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"desturi", VSH_OT_DATA, VSH_OFLAG_REQ, N_("connection URI of the destination host as seen from the client(normal migration) or source(p2p migration)")},
+    {"migrateuri", VSH_OT_DATA, 0, N_("migration URI, usually can be omitted")},
+    {"dname", VSH_OT_DATA, 0, N_("rename to new name during migration (if supported)")},
+    {"timeout", VSH_OT_INT, 0, N_("force guest to suspend if live migration exceeds timeout (in seconds)")},
+    {"xml", VSH_OT_STRING, 0, N_("filename containing updated XML for the target")},
+    {NULL, 0, 0, NULL}
+};
+
+static void
+doMigrate(void *opaque)
+{
+    char ret = '1';
+    virDomainPtr dom = NULL;
+    const char *desturi = NULL;
+    const char *migrateuri = NULL;
+    const char *dname = NULL;
+    unsigned int flags = 0;
+    vshCtrlData *data = opaque;
+    vshControl *ctl = data->ctl;
+    const vshCmd *cmd = data->cmd;
+    const char *xmlfile = NULL;
+    char *xml = NULL;
+    sigset_t sigmask, oldsigmask;
+
+    sigemptyset(&sigmask);
+    sigaddset(&sigmask, SIGINT);
+    if (pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask) < 0)
+        goto out_sig;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        goto out;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        goto out;
+
+    if (vshCommandOptString(cmd, "desturi", &desturi) <= 0 ||
+        vshCommandOptString(cmd, "migrateuri", &migrateuri) < 0 ||
+        vshCommandOptString(cmd, "dname", &dname) < 0) {
+        vshError(ctl, "%s", _("missing argument"));
+        goto out;
+    }
+
+    if (vshCommandOptString(cmd, "xml", &xmlfile) < 0) {
+        vshError(ctl, "%s", _("malformed xml argument"));
+        goto out;
+    }
+
+    if (vshCommandOptBool(cmd, "live"))
+        flags |= VIR_MIGRATE_LIVE;
+    if (vshCommandOptBool(cmd, "p2p"))
+        flags |= VIR_MIGRATE_PEER2PEER;
+    if (vshCommandOptBool(cmd, "tunnelled"))
+        flags |= VIR_MIGRATE_TUNNELLED;
+
+    if (vshCommandOptBool(cmd, "persistent"))
+        flags |= VIR_MIGRATE_PERSIST_DEST;
+    if (vshCommandOptBool(cmd, "undefinesource"))
+        flags |= VIR_MIGRATE_UNDEFINE_SOURCE;
+
+    if (vshCommandOptBool(cmd, "suspend"))
+        flags |= VIR_MIGRATE_PAUSED;
+
+    if (vshCommandOptBool(cmd, "copy-storage-all"))
+        flags |= VIR_MIGRATE_NON_SHARED_DISK;
+
+    if (vshCommandOptBool(cmd, "copy-storage-inc"))
+        flags |= VIR_MIGRATE_NON_SHARED_INC;
+
+    if (vshCommandOptBool(cmd, "change-protection"))
+        flags |= VIR_MIGRATE_CHANGE_PROTECTION;
+
+    if (vshCommandOptBool(cmd, "unsafe"))
+        flags |= VIR_MIGRATE_UNSAFE;
+
+    if (xmlfile &&
+        virFileReadAll(xmlfile, 8192, &xml) < 0) {
+        vshError(ctl, _("file '%s' doesn't exist"), xmlfile);
+        goto out;
+    }
+
+    if ((flags & VIR_MIGRATE_PEER2PEER) ||
+        vshCommandOptBool(cmd, "direct")) {
+        /* For peer2peer migration or direct migration we only expect one URI
+         * a libvirt URI, or a hypervisor specific URI. */
+
+        if (migrateuri != NULL) {
+            vshError(ctl, "%s", _("migrate: Unexpected migrateuri for peer2peer/direct migration"));
+            goto out;
+        }
+
+        if (virDomainMigrateToURI2(dom, desturi, NULL, xml, flags, dname, 0) == 0)
+            ret = '0';
+    } else {
+        /* For traditional live migration, connect to the destination host directly. */
+        virConnectPtr dconn = NULL;
+        virDomainPtr ddom = NULL;
+
+        dconn = virConnectOpenAuth(desturi, virConnectAuthPtrDefault, 0);
+        if (!dconn) goto out;
+
+        ddom = virDomainMigrate2(dom, dconn, xml, flags, dname, migrateuri, 0);
+        if (ddom) {
+            virDomainFree(ddom);
+            ret = '0';
+        }
+        virConnectClose(dconn);
+    }
+
+out:
+    pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
+out_sig:
+    if (dom) virDomainFree(dom);
+    VIR_FREE(xml);
+    ignore_value(safewrite(data->writefd, &ret, sizeof(ret)));
+}
+
+static void
+vshMigrationTimeout(vshControl *ctl,
+                    virDomainPtr dom,
+                    void *opaque ATTRIBUTE_UNUSED)
+{
+    vshDebug(ctl, VSH_ERR_DEBUG, "suspending the domain, "
+             "since migration timed out\n");
+    virDomainSuspend(dom);
+}
+
+static bool
+vshWatchJob(vshControl *ctl,
+            virDomainPtr dom,
+            bool verbose,
+            int pipe_fd,
+            int timeout,
+            jobWatchTimeoutFunc timeout_func,
+            void *opaque,
+            const char *label)
+{
+    struct sigaction sig_action;
+    struct sigaction old_sig_action;
+    struct pollfd pollfd;
+    struct timeval start, curr;
+    virDomainJobInfo jobinfo;
+    int ret = -1;
+    char retchar;
+    bool functionReturn = false;
+    sigset_t sigmask, oldsigmask;
+
+    sigemptyset(&sigmask);
+    sigaddset(&sigmask, SIGINT);
+
+    intCaught = 0;
+    sig_action.sa_sigaction = vshCatchInt;
+    sig_action.sa_flags = SA_SIGINFO;
+    sigemptyset(&sig_action.sa_mask);
+    sigaction(SIGINT, &sig_action, &old_sig_action);
+
+    pollfd.fd = pipe_fd;
+    pollfd.events = POLLIN;
+    pollfd.revents = 0;
+
+    GETTIMEOFDAY(&start);
+    while (1) {
+repoll:
+        ret = poll(&pollfd, 1, 500);
+        if (ret > 0) {
+            if (pollfd.revents & POLLIN &&
+                saferead(pipe_fd, &retchar, sizeof(retchar)) > 0 &&
+                retchar == '0') {
+                if (verbose) {
+                    /* print [100 %] */
+                    print_job_progress(label, 0, 1);
+                }
+                break;
+            }
+            goto cleanup;
+        }
+
+        if (ret < 0) {
+            if (errno == EINTR) {
+                if (intCaught) {
+                    virDomainAbortJob(dom);
+                    intCaught = 0;
+                } else {
+                    goto repoll;
+                }
+            }
+            goto cleanup;
+        }
+
+        GETTIMEOFDAY(&curr);
+        if (timeout && (((int)(curr.tv_sec - start.tv_sec)  * 1000 +
+                         (int)(curr.tv_usec - start.tv_usec) / 1000) >
+                        timeout * 1000)) {
+            /* suspend the domain when migration timeouts. */
+            vshDebug(ctl, VSH_ERR_DEBUG, "%s timeout", label);
+            if (timeout_func)
+                (timeout_func)(ctl, dom, opaque);
+            timeout = 0;
+        }
+
+        if (verbose) {
+            pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask);
+            ret = virDomainGetJobInfo(dom, &jobinfo);
+            pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
+            if (ret == 0)
+                print_job_progress(label, jobinfo.dataRemaining,
+                                   jobinfo.dataTotal);
+        }
+    }
+
+    functionReturn = true;
+
+cleanup:
+    sigaction(SIGINT, &old_sig_action, NULL);
+    return functionReturn;
+}
+
+static bool
+cmdMigrate(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom = NULL;
+    int p[2] = {-1, -1};
+    virThread workerThread;
+    bool verbose = false;
+    bool functionReturn = false;
+    int timeout = 0;
+    bool live_flag = false;
+    vshCtrlData data;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        return false;
+
+    if (vshCommandOptBool(cmd, "verbose"))
+        verbose = true;
+
+    if (vshCommandOptBool(cmd, "live"))
+        live_flag = true;
+    if (vshCommandOptInt(cmd, "timeout", &timeout) > 0) {
+        if (! live_flag) {
+            vshError(ctl, "%s",
+                     _("migrate: Unexpected timeout for offline migration"));
+            goto cleanup;
+        }
+
+        if (timeout < 1) {
+            vshError(ctl, "%s", _("migrate: Invalid timeout"));
+            goto cleanup;
+        }
+
+        /* Ensure that we can multiply by 1000 without overflowing. */
+        if (timeout > INT_MAX / 1000) {
+            vshError(ctl, "%s", _("migrate: Timeout is too big"));
+            goto cleanup;
+        }
+    }
+
+    if (pipe(p) < 0)
+        goto cleanup;
+
+    data.ctl = ctl;
+    data.cmd = cmd;
+    data.writefd = p[1];
+
+    if (virThreadCreate(&workerThread,
+                        true,
+                        doMigrate,
+                        &data) < 0)
+        goto cleanup;
+    functionReturn = vshWatchJob(ctl, dom, verbose, p[0], timeout,
+                                 vshMigrationTimeout, NULL, _("Migration"));
+
+    virThreadJoin(&workerThread);
+
+cleanup:
+    virDomainFree(dom);
+    VIR_FORCE_CLOSE(p[0]);
+    VIR_FORCE_CLOSE(p[1]);
+    return functionReturn;
+}
+
+/*
+ * "migrate-setmaxdowntime" command
+ */
+static const vshCmdInfo info_migrate_setmaxdowntime[] = {
+    {"help", N_("set maximum tolerable downtime")},
+    {"desc", N_("Set maximum tolerable downtime of a domain which is being live-migrated to another host.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_migrate_setmaxdowntime[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"downtime", VSH_OT_INT, VSH_OFLAG_REQ, N_("maximum tolerable downtime (in milliseconds) for migration")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdMigrateSetMaxDowntime(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom = NULL;
+    long long downtime = 0;
+    bool ret = false;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        return false;
+
+    if (vshCommandOptLongLong(cmd, "downtime", &downtime) < 0 ||
+        downtime < 1) {
+        vshError(ctl, "%s", _("migrate: Invalid downtime"));
+        goto done;
+    }
+
+    if (virDomainMigrateSetMaxDowntime(dom, downtime, 0))
+        goto done;
+
+    ret = true;
+
+done:
+    virDomainFree(dom);
+    return ret;
+}
+
+/*
+ * "migrate-setspeed" command
+ */
+static const vshCmdInfo info_migrate_setspeed[] = {
+    {"help", N_("Set the maximum migration bandwidth")},
+    {"desc", N_("Set the maximum migration bandwidth (in MiB/s) for a domain "
+                "which is being migrated to another host.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_migrate_setspeed[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"bandwidth", VSH_OT_INT, VSH_OFLAG_REQ,
+     N_("migration bandwidth limit in MiB/s")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdMigrateSetMaxSpeed(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom = NULL;
+    unsigned long bandwidth = 0;
+    bool ret = false;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        return false;
+
+    if (vshCommandOptUL(cmd, "bandwidth", &bandwidth) < 0) {
+        vshError(ctl, "%s", _("migrate: Invalid bandwidth"));
+        goto done;
+    }
+
+    if (virDomainMigrateSetMaxSpeed(dom, bandwidth, 0) < 0)
+        goto done;
+
+    ret = true;
+
+done:
+    virDomainFree(dom);
+    return ret;
+}
+
+/*
+ * "migrate-getspeed" command
+ */
+static const vshCmdInfo info_migrate_getspeed[] = {
+    {"help", N_("Get the maximum migration bandwidth")},
+    {"desc", N_("Get the maximum migration bandwidth (in MiB/s) for a domain.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_migrate_getspeed[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdMigrateGetMaxSpeed(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom = NULL;
+    unsigned long bandwidth;
+    bool ret = false;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        return false;
+
+    if (virDomainMigrateGetMaxSpeed(dom, &bandwidth, 0) < 0)
+        goto done;
+
+    vshPrint(ctl, "%lu\n", bandwidth);
+
+    ret = true;
+
+done:
+    virDomainFree(dom);
+    return ret;
+}
+
+/*
+ * "domdisplay" command
+ */
+static const vshCmdInfo info_domdisplay[] = {
+    {"help", N_("domain display connection URI")},
+    {"desc", N_("Output the IP address and port number for the graphical display.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_domdisplay[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"include-password", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("includes the password into the connection URI if available")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDomDisplay(vshControl *ctl, const vshCmd *cmd)
+{
+    xmlDocPtr xml = NULL;
+    xmlXPathContextPtr ctxt = NULL;
+    virDomainPtr dom;
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+    bool ret = false;
+    char *doc;
+    char *xpath;
+    char *listen_addr;
+    int port, tls_port = 0;
+    char *passwd = NULL;
+    char *output = NULL;
+    const char *scheme[] = { "vnc", "spice", "rdp", NULL };
+    int iter = 0;
+    int tmp;
+    int flags = 0;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        return false;
+
+    if (!virDomainIsActive(dom)) {
+        vshError(ctl, _("Domain is not running"));
+        goto cleanup;
+    }
+
+    if (vshCommandOptBool(cmd, "include-password"))
+        flags |= VIR_DOMAIN_XML_SECURE;
+
+    doc = virDomainGetXMLDesc(dom, flags);
+
+    if (!doc)
+        goto cleanup;
+
+    xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt);
+    VIR_FREE(doc);
+    if (!xml)
+        goto cleanup;
+
+    /* Attempt to grab our display info */
+    for (iter = 0; scheme[iter] != NULL; iter++) {
+        /* Create our XPATH lookup for the current display's port */
+        virAsprintf(&xpath, "string(/domain/devices/graphics[@type='%s']"
+                "/@port)", scheme[iter]);
+        if (!xpath) {
+            virReportOOMError();
+            goto cleanup;
+        }
+
+        /* Attempt to get the port number for the current graphics scheme */
+        tmp = virXPathInt(xpath, ctxt, &port);
+        VIR_FREE(xpath);
+
+        /* If there is no port number for this type, then jump to the next
+         * scheme
+         */
+        if (tmp)
+            continue;
+
+        /* Create our XPATH lookup for the current display's address */
+        virAsprintf(&xpath, "string(/domain/devices/graphics[@type='%s']"
+                "/@listen)", scheme[iter]);
+        if (!xpath) {
+            virReportOOMError();
+            goto cleanup;
+        }
+
+        /* Attempt to get the listening addr if set for the current
+         * graphics scheme
+         */
+        listen_addr = virXPathString(xpath, ctxt);
+        VIR_FREE(xpath);
+
+        /* Per scheme data mangling */
+        if (STREQ(scheme[iter], "vnc")) {
+            /* VNC protocol handlers take their port number as 'port' - 5900 */
+            port -= 5900;
+        } else if (STREQ(scheme[iter], "spice")) {
+            /* Create our XPATH lookup for the SPICE TLS Port */
+            virAsprintf(&xpath, "string(/domain/devices/graphics[@type='%s']"
+                    "/@tlsPort)", scheme[iter]);
+            if (!xpath) {
+                virReportOOMError();
+                goto cleanup;
+            }
+
+            /* Attempt to get the TLS port number for SPICE */
+            tmp = virXPathInt(xpath, ctxt, &tls_port);
+            VIR_FREE(xpath);
+            if (tmp)
+                tls_port = 0;
+
+            if (vshCommandOptBool(cmd, "include-password")) {
+                /* Create our XPATH lookup for the SPICE password */
+                virAsprintf(&xpath, "string(/domain/devices/graphics"
+                        "[@type='%s']/@passwd)", scheme[iter]);
+                if (!xpath) {
+                    virReportOOMError();
+                    goto cleanup;
+                }
+
+                /* Attempt to get the SPICE password */
+                passwd = virXPathString(xpath, ctxt);
+                VIR_FREE(xpath);
+            }
+        }
+
+        /* Build up the full URI, starting with the scheme */
+        virBufferAsprintf(&buf, "%s://", scheme[iter]);
+
+        /* Then host name or IP */
+        if (!listen_addr || STREQ((const char *)listen_addr, "0.0.0.0"))
+            virBufferAddLit(&buf, "localhost");
+        else
+            virBufferAsprintf(&buf, "%s", listen_addr);
+
+        VIR_FREE(listen_addr);
+
+        /* Add the port */
+        if (STREQ(scheme[iter], "spice"))
+            virBufferAsprintf(&buf, "?port=%d", port);
+        else
+            virBufferAsprintf(&buf, ":%d", port);
+
+        /* TLS Port */
+        if (tls_port)
+            virBufferAsprintf(&buf, "&tls-port=%d", tls_port);
+
+        /* Password */
+        if (passwd) {
+            virBufferAsprintf(&buf, "&password=%s", passwd);
+            VIR_FREE(passwd);
+        }
+
+        /* Ensure we can print our URI */
+        if (virBufferError(&buf)) {
+            vshPrint(ctl, "%s", _("Failed to create display URI"));
+            goto cleanup;
+        }
+
+        /* Print out our full URI */
+        output = virBufferContentAndReset(&buf);
+        vshPrint(ctl, "%s", output);
+        VIR_FREE(output);
+
+        /* We got what we came for so return successfully */
+        ret = true;
+        break;
+    }
+
+cleanup:
+    xmlXPathFreeContext(ctxt);
+    xmlFreeDoc(xml);
+    virDomainFree(dom);
+    return ret;
+}
+
+/*
+ * "vncdisplay" command
+ */
+static const vshCmdInfo info_vncdisplay[] = {
+    {"help", N_("vnc display")},
+    {"desc", N_("Output the IP address and port number for the VNC display.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_vncdisplay[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdVNCDisplay(vshControl *ctl, const vshCmd *cmd)
+{
+    xmlDocPtr xml = NULL;
+    xmlXPathContextPtr ctxt = NULL;
+    virDomainPtr dom;
+    bool ret = false;
+    int port = 0;
+    char *doc = NULL;
+    char *listen_addr = NULL;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        return false;
+
+    /* Check if the domain is active and don't rely on -1 for this */
+    if (!virDomainIsActive(dom)) {
+        vshError(ctl, _("Domain is not running"));
+        goto cleanup;
+    }
+
+    if (!(doc = virDomainGetXMLDesc(dom, 0)))
+        goto cleanup;
+
+    if (!(xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt)))
+        goto cleanup;
+
+    /* Get the VNC port */
+    if (virXPathInt("string(/domain/devices/graphics[@type='vnc']/@port)",
+                    ctxt, &port)) {
+        vshError(ctl, _("Failed to get VNC port. Is this domain using VNC?"));
+        goto cleanup;
+    }
+
+    listen_addr = virXPathString("string(/domain/devices/graphics"
+                                 "[@type='vnc']/@listen)", ctxt);
+    if (listen_addr == NULL || STREQ(listen_addr, "0.0.0.0"))
+        vshPrint(ctl, ":%d\n", port-5900);
+    else
+        vshPrint(ctl, "%s:%d\n", listen_addr, port-5900);
+
+    ret = true;
+
+ cleanup:
+    VIR_FREE(doc);
+    VIR_FREE(listen_addr);
+    xmlXPathFreeContext(ctxt);
+    xmlFreeDoc(xml);
+    virDomainFree(dom);
+    return ret;
+}
+
+/*
+ * "ttyconsole" command
+ */
+static const vshCmdInfo info_ttyconsole[] = {
+    {"help", N_("tty console")},
+    {"desc", N_("Output the device for the TTY console.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_ttyconsole[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdTTYConsole(vshControl *ctl, const vshCmd *cmd)
+{
+    xmlDocPtr xml = NULL;
+    xmlXPathObjectPtr obj = NULL;
+    xmlXPathContextPtr ctxt = NULL;
+    virDomainPtr dom;
+    bool ret = false;
+    char *doc;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        return false;
+
+    doc = virDomainGetXMLDesc(dom, 0);
+    if (!doc)
+        goto cleanup;
+
+    xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt);
+    VIR_FREE(doc);
+    if (!xml)
+        goto cleanup;
+
+    obj = xmlXPathEval(BAD_CAST "string(/domain/devices/console/@tty)", ctxt);
+    if (obj == NULL || obj->type != XPATH_STRING ||
+        obj->stringval == NULL || obj->stringval[0] == 0) {
+        goto cleanup;
+    }
+    vshPrint(ctl, "%s\n", (const char *)obj->stringval);
+    ret = true;
+
+ cleanup:
+    xmlXPathFreeObject(obj);
+    xmlXPathFreeContext(ctxt);
+    xmlFreeDoc(xml);
+    virDomainFree(dom);
+    return ret;
+}
+
+/*
+ * "domhostname" command
+ */
+static const vshCmdInfo info_domhostname[] = {
+    {"help", N_("print the domain's hostname")},
+    {"desc", ""},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_domhostname[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDomHostname(vshControl *ctl, const vshCmd *cmd)
+{
+    char *hostname;
+    virDomainPtr dom;
+    bool ret = false;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        return false;
+
+    hostname = virDomainGetHostname(dom, 0);
+    if (hostname == NULL) {
+        vshError(ctl, "%s", _("failed to get hostname"));
+        goto error;
+    }
+
+    vshPrint(ctl, "%s\n", hostname);
+    ret = true;
+
+error:
+    VIR_FREE(hostname);
+    virDomainFree(dom);
+    return ret;
+}
+
+/**
+ * Check if n1 is superset of n2, meaning n1 contains all elements and
+ * attributes as n2 at least. Including children.
+ * @n1 first node
+ * @n2 second node
+ * returns true in case n1 covers n2, false otherwise.
+ */
+ATTRIBUTE_UNUSED
+static bool
+vshNodeIsSuperset(xmlNodePtr n1, xmlNodePtr n2)
+{
+    xmlNodePtr child1, child2;
+    xmlAttrPtr attr;
+    char *prop1, *prop2;
+    bool found;
+    bool visited;
+    bool ret = false;
+    long n1_child_size, n2_child_size, n1_iter;
+    virBitmapPtr bitmap;
+
+    if (!n1 && !n2)
+        return true;
+
+    if (!n1 || !n2)
+        return false;
+
+    if (!xmlStrEqual(n1->name, n2->name))
+        return false;
+
+    /* Iterate over n2 attributes and check if n1 contains them*/
+    attr = n2->properties;
+    while (attr) {
+        if (attr->type == XML_ATTRIBUTE_NODE) {
+            prop1 = virXMLPropString(n1, (const char *) attr->name);
+            prop2 = virXMLPropString(n2, (const char *) attr->name);
+            if (STRNEQ_NULLABLE(prop1, prop2)) {
+                xmlFree(prop1);
+                xmlFree(prop2);
+                return false;
+            }
+            xmlFree(prop1);
+            xmlFree(prop2);
+        }
+        attr = attr->next;
+    }
+
+    n1_child_size = virXMLChildElementCount(n1);
+    n2_child_size = virXMLChildElementCount(n2);
+    if (n1_child_size < 0 || n2_child_size < 0 ||
+        n1_child_size < n2_child_size)
+        return false;
+
+    if (n1_child_size == 0 && n2_child_size == 0)
+        return true;
+
+    if (!(bitmap = virBitmapAlloc(n1_child_size))) {
+        virReportOOMError();
+        return false;
+    }
+
+    child2 = n2->children;
+    while (child2) {
+        if (child2->type != XML_ELEMENT_NODE) {
+            child2 = child2->next;
+            continue;
+        }
+
+        child1 = n1->children;
+        n1_iter = 0;
+        found = false;
+        while (child1) {
+            if (child1->type != XML_ELEMENT_NODE) {
+                child1 = child1->next;
+                continue;
+            }
+
+            if (virBitmapGetBit(bitmap, n1_iter, &visited) < 0) {
+                vshError(NULL, "%s", _("Bad child elements counting."));
+                goto cleanup;
+            }
+
+            if (visited) {
+                child1 = child1->next;
+                n1_iter++;
+                continue;
+            }
+
+            if (xmlStrEqual(child1->name, child2->name)) {
+                found = true;
+                if (virBitmapSetBit(bitmap, n1_iter) < 0) {
+                    vshError(NULL, "%s", _("Bad child elements counting."));
+                    goto cleanup;
+                }
+
+                if (!vshNodeIsSuperset(child1, child2))
+                    goto cleanup;
+
+                break;
+            }
+
+            child1 = child1->next;
+            n1_iter++;
+        }
+
+        if (!found)
+            goto cleanup;
+
+        child2 = child2->next;
+    }
+
+    ret = true;
+
+cleanup:
+    virBitmapFree(bitmap);
+    return ret;
+}
+
+/**
+ * vshCompleteXMLFromDomain:
+ * @ctl vshControl for error messages printing
+ * @dom domain
+ * @oldXML device XML before
+ * @newXML and after completion
+ *
+ * For given domain and (probably incomplete) device XML specification try to
+ * find such device in domain and complete missing parts. This is however
+ * possible only when given device XML is sufficiently precise so it addresses
+ * only one device.
+ *
+ * Returns -2 when no such device exists in domain, -3 when given XML selects many
+ *          (is too ambiguous), 0 in case of success. Otherwise returns -1. @newXML
+ *          is touched only in case of success.
+ */
+ATTRIBUTE_UNUSED
+static int
+vshCompleteXMLFromDomain(vshControl *ctl, virDomainPtr dom, char *oldXML,
+                         char **newXML)
+{
+    int funcRet = -1;
+    char *domXML = NULL;
+    xmlDocPtr domDoc = NULL, devDoc = NULL;
+    xmlNodePtr node = NULL;
+    xmlXPathContextPtr domCtxt = NULL, devCtxt = NULL;
+    xmlNodePtr *devices = NULL;
+    xmlSaveCtxtPtr sctxt = NULL;
+    int devices_size;
+    char *xpath = NULL;
+    xmlBufferPtr buf = NULL;
+    int i = 0;
+    int indx = -1;
+
+    if (!(domXML = virDomainGetXMLDesc(dom, 0))) {
+        vshError(ctl, _("couldn't get XML description of domain %s"),
+                 virDomainGetName(dom));
+        goto cleanup;
+    }
+
+    domDoc = virXMLParseStringCtxt(domXML, _("(domain_definition)"), &domCtxt);
+    if (!domDoc) {
+        vshError(ctl, _("Failed to parse domain definition xml"));
+        goto cleanup;
+    }
+
+    devDoc = virXMLParseStringCtxt(oldXML, _("(device_definition)"), &devCtxt);
+    if (!devDoc) {
+        vshError(ctl, _("Failed to parse device definition xml"));
+        goto cleanup;
+    }
+
+    node = xmlDocGetRootElement(devDoc);
+
+    buf = xmlBufferCreate();
+    if (!buf) {
+        vshError(ctl, "%s", _("out of memory"));
+        goto cleanup;
+    }
+
+    /* Get all possible devices */
+    virAsprintf(&xpath, "/domain/devices/%s", node->name);
+    if (!xpath) {
+        virReportOOMError();
+        goto cleanup;
+    }
+    devices_size = virXPathNodeSet(xpath, domCtxt, &devices);
+
+    if (devices_size < 0) {
+        /* error */
+        vshError(ctl, "%s", _("error when selecting nodes"));
+        goto cleanup;
+    } else if (devices_size == 0) {
+        /* no such device */
+        funcRet = -2;
+        goto cleanup;
+    }
+
+    /* and refine */
+    for (i = 0; i < devices_size; i++) {
+        if (vshNodeIsSuperset(devices[i], node)) {
+            if (indx >= 0) {
+                funcRet = -3; /* ambiguous */
+                goto cleanup;
+            }
+            indx = i;
+        }
+    }
+
+    if (indx < 0) {
+        funcRet = -2; /* no such device */
+        goto cleanup;
+    }
+
+    vshDebug(ctl, VSH_ERR_DEBUG, "Found device at pos %d\n", indx);
+
+    if (newXML) {
+        sctxt = xmlSaveToBuffer(buf, NULL, 0);
+        if (!sctxt) {
+            vshError(ctl, "%s", _("failed to create document saving context"));
+            goto cleanup;
+        }
+
+        xmlSaveTree(sctxt, devices[indx]);
+        xmlSaveClose(sctxt);
+        *newXML = (char *) xmlBufferContent(buf);
+        if (!*newXML) {
+            virReportOOMError();
+            goto cleanup;
+        }
+        buf->content = NULL;
+    }
+
+    vshDebug(ctl, VSH_ERR_DEBUG, "Old xml:\n%s\nNew xml:\n%s\n", oldXML,
+             newXML ? NULLSTR(*newXML) : "(null)");
+
+    funcRet = 0;
+
+cleanup:
+    xmlBufferFree(buf);
+    VIR_FREE(devices);
+    xmlXPathFreeContext(devCtxt);
+    xmlXPathFreeContext(domCtxt);
+    xmlFreeDoc(devDoc);
+    xmlFreeDoc(domDoc);
+    VIR_FREE(domXML);
+    VIR_FREE(xpath);
+    return funcRet;
+}
+
+/*
+ * "detach-device" command
+ */
+static const vshCmdInfo info_detach_device[] = {
+    {"help", N_("detach device from an XML file")},
+    {"desc", N_("Detach device from an XML <file>")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_detach_device[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"file",   VSH_OT_DATA, VSH_OFLAG_REQ, N_("XML file")},
+    {"persistent", VSH_OT_ALIAS, 0, "config"},
+    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDetachDevice(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom = NULL;
+    const char *from = NULL;
+    char *buffer = NULL;
+    int ret;
+    bool funcRet = false;
+    unsigned int flags;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        return false;
+
+    if (vshCommandOptString(cmd, "file", &from) <= 0)
+        goto cleanup;
+
+    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
+        virshReportError(ctl);
+        goto cleanup;
+    }
+
+    if (vshCommandOptBool(cmd, "config")) {
+        flags = VIR_DOMAIN_AFFECT_CONFIG;
+        if (virDomainIsActive(dom) == 1)
+           flags |= VIR_DOMAIN_AFFECT_LIVE;
+        ret = virDomainDetachDeviceFlags(dom, buffer, flags);
+    } else {
+        ret = virDomainDetachDevice(dom, buffer);
+    }
+
+    if (ret < 0) {
+        vshError(ctl, _("Failed to detach device from %s"), from);
+        goto cleanup;
+    }
+
+    vshPrint(ctl, "%s", _("Device detached successfully\n"));
+    funcRet = true;
+
+cleanup:
+    VIR_FREE(buffer);
+    virDomainFree(dom);
+    return funcRet;
+}
+
+/*
+ * "update-device" command
+ */
+static const vshCmdInfo info_update_device[] = {
+    {"help", N_("update device from an XML file")},
+    {"desc", N_("Update device from an XML <file>.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_update_device[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"file",   VSH_OT_DATA, VSH_OFLAG_REQ, N_("XML file")},
+    {"persistent", VSH_OT_ALIAS, 0, "config"},
+    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
+    {"force",  VSH_OT_BOOL, 0, N_("force device update")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdUpdateDevice(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom;
+    const char *from = NULL;
+    char *buffer;
+    int ret;
+    unsigned int flags;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        return false;
+
+    if (vshCommandOptString(cmd, "file", &from) <= 0) {
+        virDomainFree(dom);
+        return false;
+    }
+
+    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
+        virshReportError(ctl);
+        virDomainFree(dom);
+        return false;
+    }
+
+    if (vshCommandOptBool(cmd, "config")) {
+        flags = VIR_DOMAIN_AFFECT_CONFIG;
+        if (virDomainIsActive(dom) == 1)
+           flags |= VIR_DOMAIN_AFFECT_LIVE;
+    } else {
+        flags = VIR_DOMAIN_AFFECT_LIVE;
+    }
+
+    if (vshCommandOptBool(cmd, "force"))
+        flags |= VIR_DOMAIN_DEVICE_MODIFY_FORCE;
+
+    ret = virDomainUpdateDeviceFlags(dom, buffer, flags);
+    VIR_FREE(buffer);
+
+    if (ret < 0) {
+        vshError(ctl, _("Failed to update device from %s"), from);
+        virDomainFree(dom);
+        return false;
+    } else {
+        vshPrint(ctl, "%s", _("Device updated successfully\n"));
+    }
+
+    virDomainFree(dom);
+    return true;
+}
+
+/*
+ * "detach-interface" command
+ */
+static const vshCmdInfo info_detach_interface[] = {
+    {"help", N_("detach network interface")},
+    {"desc", N_("Detach network interface.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_detach_interface[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"type",   VSH_OT_DATA, VSH_OFLAG_REQ, N_("network interface type")},
+    {"mac",    VSH_OT_STRING, 0, N_("MAC address")},
+    {"persistent", VSH_OT_ALIAS, 0, "config"},
+    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDetachInterface(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom = NULL;
+    xmlDocPtr xml = NULL;
+    xmlXPathObjectPtr obj=NULL;
+    xmlXPathContextPtr ctxt = NULL;
+    xmlNodePtr cur = NULL;
+    xmlBufferPtr xml_buf = NULL;
+    const char *mac =NULL, *type = NULL;
+    char *doc;
+    char buf[64];
+    int i = 0, diff_mac;
+    int ret;
+    int functionReturn = false;
+    unsigned int flags;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        goto cleanup;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        goto cleanup;
+
+    if (vshCommandOptString(cmd, "type", &type) <= 0)
+        goto cleanup;
+
+    if (vshCommandOptString(cmd, "mac", &mac) < 0) {
+        vshError(ctl, "%s", _("missing option"));
+        goto cleanup;
+    }
+
+    doc = virDomainGetXMLDesc(dom, 0);
+    if (!doc)
+        goto cleanup;
+
+    xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt);
+    VIR_FREE(doc);
+    if (!xml) {
+        vshError(ctl, "%s", _("Failed to get interface information"));
+        goto cleanup;
+    }
+
+    snprintf(buf, sizeof(buf), "/domain/devices/interface[@type='%s']", type);
+    obj = xmlXPathEval(BAD_CAST buf, ctxt);
+    if (obj == NULL || obj->type != XPATH_NODESET ||
+        obj->nodesetval == NULL || obj->nodesetval->nodeNr == 0) {
+        vshError(ctl, _("No found interface whose type is %s"), type);
+        goto cleanup;
+    }
+
+    if (!mac && obj->nodesetval->nodeNr > 1) {
+        vshError(ctl, _("Domain has %d interfaces. Please specify which one "
+                        "to detach using --mac"), obj->nodesetval->nodeNr);
+        goto cleanup;
+    }
+
+    if (!mac)
+        goto hit;
+
+    /* search mac */
+    for (; i < obj->nodesetval->nodeNr; i++) {
+        cur = obj->nodesetval->nodeTab[i]->children;
+        while (cur != NULL) {
+            if (cur->type == XML_ELEMENT_NODE &&
+                xmlStrEqual(cur->name, BAD_CAST "mac")) {
+                char *tmp_mac = virXMLPropString(cur, "address");
+                diff_mac = virMacAddrCompare(tmp_mac, mac);
+                VIR_FREE(tmp_mac);
+                if (!diff_mac) {
+                    goto hit;
+                }
+            }
+            cur = cur->next;
+        }
+    }
+    vshError(ctl, _("No found interface whose MAC address is %s"), mac);
+    goto cleanup;
+
+ hit:
+    xml_buf = xmlBufferCreate();
+    if (!xml_buf) {
+        vshError(ctl, "%s", _("Failed to allocate memory"));
+        goto cleanup;
+    }
+
+    if (xmlNodeDump(xml_buf, xml, obj->nodesetval->nodeTab[i], 0, 0) < 0) {
+        vshError(ctl, "%s", _("Failed to create XML"));
+        goto cleanup;
+    }
+
+    if (vshCommandOptBool(cmd, "config")) {
+        flags = VIR_DOMAIN_AFFECT_CONFIG;
+        if (virDomainIsActive(dom) == 1)
+            flags |= VIR_DOMAIN_AFFECT_LIVE;
+        ret = virDomainDetachDeviceFlags(dom,
+                                         (char *)xmlBufferContent(xml_buf),
+                                         flags);
+    } else {
+        ret = virDomainDetachDevice(dom, (char *)xmlBufferContent(xml_buf));
+    }
+
+    if (ret != 0) {
+        vshError(ctl, "%s", _("Failed to detach interface"));
+    } else {
+        vshPrint(ctl, "%s", _("Interface detached successfully\n"));
+        functionReturn = true;
+    }
+
+ cleanup:
+    if (dom)
+        virDomainFree(dom);
+    xmlXPathFreeObject(obj);
+    xmlXPathFreeContext(ctxt);
+    xmlFreeDoc(xml);
+    xmlBufferFree(xml_buf);
+    return functionReturn;
+}
+
+typedef enum {
+    VSH_FIND_DISK_NORMAL,
+    VSH_FIND_DISK_CHANGEABLE,
+} vshFindDiskType;
+
+/* Helper function to find disk device in XML doc.  Returns the disk
+ * node on success, or NULL on failure. Caller must free the result
+ * @path: Fully-qualified path or target of disk device.
+ * @type: Either VSH_FIND_DISK_NORMAL or VSH_FIND_DISK_CHANGEABLE.
+ */
+static xmlNodePtr
+vshFindDisk(const char *doc,
+            const char *path,
+            int type)
+{
+    xmlDocPtr xml = NULL;
+    xmlXPathObjectPtr obj= NULL;
+    xmlXPathContextPtr ctxt = NULL;
+    xmlNodePtr cur = NULL;
+    xmlNodePtr ret = NULL;
+    int i = 0;
+
+    xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt);
+    if (!xml) {
+        vshError(NULL, "%s", _("Failed to get disk information"));
+        goto cleanup;
+    }
+
+    obj = xmlXPathEval(BAD_CAST "/domain/devices/disk", ctxt);
+    if (obj == NULL ||
+        obj->type != XPATH_NODESET ||
+        obj->nodesetval == NULL ||
+        obj->nodesetval->nodeNr == 0) {
+        vshError(NULL, "%s", _("Failed to get disk information"));
+        goto cleanup;
+    }
+
+    /* search disk using @path */
+    for (; i < obj->nodesetval->nodeNr; i++) {
+        bool is_supported = true;
+
+        if (type == VSH_FIND_DISK_CHANGEABLE) {
+            xmlNodePtr n = obj->nodesetval->nodeTab[i];
+            is_supported = false;
+
+            /* Check if the disk is CDROM or floppy disk */
+            if (xmlStrEqual(n->name, BAD_CAST "disk")) {
+                char *device_value = virXMLPropString(n, "device");
+
+                if (STREQ(device_value, "cdrom") ||
+                    STREQ(device_value, "floppy"))
+                    is_supported = true;
+
+                VIR_FREE(device_value);
+            }
+
+            if (!is_supported)
+                continue;
+        }
+
+        cur = obj->nodesetval->nodeTab[i]->children;
+        while (cur != NULL) {
+            if (cur->type == XML_ELEMENT_NODE) {
+                char *tmp = NULL;
+
+                if (xmlStrEqual(cur->name, BAD_CAST "source")) {
+                    if ((tmp = virXMLPropString(cur, "file")) ||
+                        (tmp = virXMLPropString(cur, "dev")) ||
+                        (tmp = virXMLPropString(cur, "dir")) ||
+                        (tmp = virXMLPropString(cur, "name"))) {
+                    }
+                } else if (xmlStrEqual(cur->name, BAD_CAST "target")) {
+                    tmp = virXMLPropString(cur, "dev");
+                }
+
+                if (STREQ_NULLABLE(tmp, path)) {
+                    ret = xmlCopyNode(obj->nodesetval->nodeTab[i], 1);
+                    VIR_FREE(tmp);
+                    goto cleanup;
+                }
+                VIR_FREE(tmp);
+            }
+            cur = cur->next;
+        }
+    }
+
+    vshError(NULL, _("No found disk whose source path or target is %s"), path);
+
+cleanup:
+    xmlXPathFreeObject(obj);
+    xmlXPathFreeContext(ctxt);
+    xmlFreeDoc(xml);
+    return ret;
+}
+
+typedef enum {
+    VSH_PREPARE_DISK_XML_NONE = 0,
+    VSH_PREPARE_DISK_XML_EJECT,
+    VSH_PREPARE_DISK_XML_INSERT,
+    VSH_PREPARE_DISK_XML_UPDATE,
+} vshPrepareDiskXMLType;
+
+/* Helper function to prepare disk XML. Could be used for disk
+ * detaching, media changing(ejecting, inserting, updating)
+ * for changeable disk. Returns the processed XML as string on
+ * success, or NULL on failure. Caller must free the result.
+ */
+static char *
+vshPrepareDiskXML(xmlNodePtr disk_node,
+                  const char *source,
+                  const char *path,
+                  int type)
+{
+    xmlNodePtr cur = NULL;
+    xmlBufferPtr xml_buf = NULL;
+    const char *disk_type = NULL;
+    const char *device_type = NULL;
+    xmlNodePtr new_node = NULL;
+    char *ret = NULL;
+
+    if (!disk_node)
+        return NULL;
+
+    xml_buf = xmlBufferCreate();
+    if (!xml_buf) {
+        vshError(NULL, "%s", _("Failed to allocate memory"));
+        return NULL;
+    }
+
+    device_type = virXMLPropString(disk_node, "device");
+
+    if (STREQ_NULLABLE(device_type, "cdrom") ||
+        STREQ_NULLABLE(device_type, "floppy")) {
+        bool has_source = false;
+        disk_type = virXMLPropString(disk_node, "type");
+
+        cur = disk_node->children;
+        while (cur != NULL) {
+            if (cur->type == XML_ELEMENT_NODE &&
+                xmlStrEqual(cur->name, BAD_CAST "source")) {
+                has_source = true;
+                break;
+            }
+            cur = cur->next;
+        }
+
+        if (!has_source) {
+            if (type == VSH_PREPARE_DISK_XML_EJECT) {
+                vshError(NULL, _("The disk device '%s' doesn't have media"),
+                         path);
+                goto error;
+            }
+
+            if (source) {
+                new_node = xmlNewNode(NULL, BAD_CAST "source");
+                xmlNewProp(new_node, (const xmlChar *)disk_type,
+                           (const xmlChar *)source);
+                xmlAddChild(disk_node, new_node);
+            } else if (type == VSH_PREPARE_DISK_XML_INSERT) {
+                vshError(NULL, _("No source is specified for inserting media"));
+                goto error;
+            } else if (type == VSH_PREPARE_DISK_XML_UPDATE) {
+                vshError(NULL, _("No source is specified for updating media"));
+                goto error;
+            }
+        }
+
+        if (has_source) {
+            if (type == VSH_PREPARE_DISK_XML_INSERT) {
+                vshError(NULL, _("The disk device '%s' already has media"),
+                         path);
+                goto error;
+            }
+
+            /* Remove the source if it tends to eject/update media. */
+            xmlUnlinkNode(cur);
+            xmlFreeNode(cur);
+
+            if (source && (type == VSH_PREPARE_DISK_XML_UPDATE)) {
+                new_node = xmlNewNode(NULL, BAD_CAST "source");
+                xmlNewProp(new_node, (const xmlChar *)disk_type,
+                           (const xmlChar *)source);
+                xmlAddChild(disk_node, new_node);
+            }
+        }
+    }
+
+    if (xmlNodeDump(xml_buf, NULL, disk_node, 0, 0) < 0) {
+        vshError(NULL, "%s", _("Failed to create XML"));
+        goto error;
+    }
+
+    goto cleanup;
+
+cleanup:
+    VIR_FREE(device_type);
+    VIR_FREE(disk_type);
+    if (xml_buf) {
+        int len = xmlBufferLength(xml_buf);
+        if (VIR_ALLOC_N(ret, len + 1) < 0) {
+            virReportOOMError();
+            return NULL;
+        }
+        memcpy(ret, (char *)xmlBufferContent(xml_buf), len);
+        ret[len] = '\0';
+        xmlBufferFree(xml_buf);
+    }
+    return ret;
+
+error:
+    xmlBufferFree(xml_buf);
+    xml_buf = NULL;
+    goto cleanup;
+}
+
+
+/*
+ * "detach-disk" command
+ */
+static const vshCmdInfo info_detach_disk[] = {
+    {"help", N_("detach disk device")},
+    {"desc", N_("Detach disk device.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_detach_disk[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"target", VSH_OT_DATA, VSH_OFLAG_REQ, N_("target of disk device")},
+    {"persistent", VSH_OT_ALIAS, 0, "config"},
+    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDetachDisk(vshControl *ctl, const vshCmd *cmd)
+{
+    char *disk_xml = NULL;
+    virDomainPtr dom = NULL;
+    const char *target = NULL;
+    char *doc = NULL;
+    int ret;
+    bool functionReturn = false;
+    unsigned int flags;
+    xmlNodePtr disk_node = NULL;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        goto cleanup;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        goto cleanup;
+
+    if (vshCommandOptString(cmd, "target", &target) <= 0)
+        goto cleanup;
+
+    doc = virDomainGetXMLDesc(dom, 0);
+    if (!doc)
+        goto cleanup;
+
+    if (!(disk_node = vshFindDisk(doc, target, VSH_FIND_DISK_NORMAL)))
+        goto cleanup;
+
+    if (!(disk_xml = vshPrepareDiskXML(disk_node, NULL, NULL,
+                                       VSH_PREPARE_DISK_XML_NONE)))
+        goto cleanup;
+
+    if (vshCommandOptBool(cmd, "config")) {
+        flags = VIR_DOMAIN_AFFECT_CONFIG;
+        if (virDomainIsActive(dom) == 1)
+            flags |= VIR_DOMAIN_AFFECT_LIVE;
+        ret = virDomainDetachDeviceFlags(dom,
+                                         disk_xml,
+                                         flags);
+    } else {
+        ret = virDomainDetachDevice(dom, disk_xml);
+    }
+
+    if (ret != 0) {
+        vshError(ctl, "%s", _("Failed to detach disk"));
+    } else {
+        vshPrint(ctl, "%s", _("Disk detached successfully\n"));
+        functionReturn = true;
+    }
+
+ cleanup:
+    xmlFreeNode(disk_node);
+    VIR_FREE(disk_xml);
+    VIR_FREE(doc);
+    if (dom)
+        virDomainFree(dom);
+    return functionReturn;
+}
+
+/*
+ * "edit" command
+ */
+static const vshCmdInfo info_edit[] = {
+    {"help", N_("edit XML configuration for a domain")},
+    {"desc", N_("Edit the XML configuration for a domain.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_edit[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdEdit(vshControl *ctl, const vshCmd *cmd)
+{
+    bool ret = false;
+    virDomainPtr dom = NULL;
+    virDomainPtr dom_edited = NULL;
+    unsigned int flags = VIR_DOMAIN_XML_SECURE | VIR_DOMAIN_XML_INACTIVE;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        goto cleanup;
+
+    dom = vshCommandOptDomain(ctl, cmd, NULL);
+    if (dom == NULL)
+        goto cleanup;
+
+#define EDIT_GET_XML virDomainGetXMLDesc(dom, flags)
+#define EDIT_NOT_CHANGED \
+    vshPrint(ctl, _("Domain %s XML configuration not changed.\n"),  \
+             virDomainGetName(dom));                                \
+    ret = true; goto edit_cleanup;
+#define EDIT_DEFINE \
+    (dom_edited = virDomainDefineXML(ctl->conn, doc_edited))
+#define EDIT_FREE \
+    if (dom_edited) \
+        virDomainFree(dom_edited);
+#include "virsh-edit.c"
+
+    vshPrint(ctl, _("Domain %s XML configuration edited.\n"),
+             virDomainGetName(dom_edited));
+
+    ret = true;
+
+ cleanup:
+    if (dom)
+        virDomainFree(dom);
+    if (dom_edited)
+        virDomainFree(dom_edited);
+
+    return ret;
+}
+
+/*
+ * "change-media" command
+ */
+static const vshCmdInfo info_change_media[] = {
+    {"help", N_("Change media of CD or floppy drive")},
+    {"desc", N_("Change media of CD or floppy drive.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_change_media[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"path", VSH_OT_DATA, VSH_OFLAG_REQ, N_("Fully-qualified path or "
+                                            "target of disk device")},
+    {"source", VSH_OT_DATA, 0, N_("source of the media")},
+    {"eject", VSH_OT_BOOL, 0, N_("Eject the media")},
+    {"insert", VSH_OT_BOOL, 0, N_("Insert the media")},
+    {"update", VSH_OT_BOOL, 0, N_("Update the media")},
+    {"current", VSH_OT_BOOL, 0, N_("can be either or both of --live and --config, "
+                                   "depends on implementation of hypervisor driver")},
+    {"live", VSH_OT_BOOL, 0, N_("alter live configuration of running domain")},
+    {"config", VSH_OT_BOOL, 0, N_("alter persistent configuration, effect observed on next boot")},
+    {"force",  VSH_OT_BOOL, 0, N_("force media insertion")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdChangeMedia(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom = NULL;
+    const char *source = NULL;
+    const char *path = NULL;
+    const char *doc = NULL;
+    xmlNodePtr disk_node = NULL;
+    const char *disk_xml = NULL;
+    int flags = 0;
+    bool config, live, current, force = false;
+    bool eject, insert, update = false;
+    bool ret = false;
+    int prepare_type = 0;
+    const char *action = NULL;
+
+    config = vshCommandOptBool(cmd, "config");
+    live = vshCommandOptBool(cmd, "live");
+    current = vshCommandOptBool(cmd, "current");
+    force = vshCommandOptBool(cmd, "force");
+    eject = vshCommandOptBool(cmd, "eject");
+    insert = vshCommandOptBool(cmd, "insert");
+    update = vshCommandOptBool(cmd, "update");
+
+    if (eject + insert + update > 1) {
+        vshError(ctl, "%s", _("--eject, --insert, and --update must be specified "
+                            "exclusively."));
+        return false;
+    }
+
+    if (eject) {
+        prepare_type = VSH_PREPARE_DISK_XML_EJECT;
+        action = "eject";
+    }
+
+    if (insert) {
+        prepare_type = VSH_PREPARE_DISK_XML_INSERT;
+        action = "insert";
+    }
+
+    if (update || (!eject && !insert)) {
+        prepare_type = VSH_PREPARE_DISK_XML_UPDATE;
+        action = "update";
+    }
+
+    if (current) {
+        if (live || config) {
+            vshError(ctl, "%s", _("--current must be specified exclusively"));
+            return false;
+        }
+        flags = VIR_DOMAIN_AFFECT_CURRENT;
+    } else {
+        if (config)
+            flags |= VIR_DOMAIN_AFFECT_CONFIG;
+        if (live)
+            flags |= VIR_DOMAIN_AFFECT_LIVE;
+    }
+
+    if (force)
+        flags |= VIR_DOMAIN_DEVICE_MODIFY_FORCE;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        goto cleanup;
+
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        goto cleanup;
+
+    if (vshCommandOptString(cmd, "path", &path) <= 0)
+        goto cleanup;
+
+    if (vshCommandOptString(cmd, "source", &source) < 0)
+        goto cleanup;
+
+    if (insert && !source) {
+        vshError(ctl, "%s", _("No disk source specified for inserting"));
+        goto cleanup;
+    }
+
+    if (flags & VIR_DOMAIN_AFFECT_CONFIG)
+        doc = virDomainGetXMLDesc(dom, VIR_DOMAIN_XML_INACTIVE);
+    else
+        doc = virDomainGetXMLDesc(dom, 0);
+    if (!doc)
+        goto cleanup;
+
+    if (!(disk_node = vshFindDisk(doc, path, VSH_FIND_DISK_CHANGEABLE)))
+        goto cleanup;
+
+    if (!(disk_xml = vshPrepareDiskXML(disk_node, source, path, prepare_type)))
+        goto cleanup;
+
+    if (virDomainUpdateDeviceFlags(dom, disk_xml, flags) != 0) {
+        vshError(ctl, _("Failed to complete action %s on media"), action);
+        goto cleanup;
+    }
+
+    vshPrint(ctl, _("succeeded to complete action %s on media\n"), action);
+    ret = true;
+
+cleanup:
+    VIR_FREE(doc);
+    xmlFreeNode(disk_node);
+    VIR_FREE(disk_xml);
+    if (dom)
+        virDomainFree(dom);
+    return ret;
+}
index a7a8a6b3a998c6be6977be3e476eae28c9062c58..af6bce00a408cac61162d6d2ca76f12cf53acc3f 100644 (file)
@@ -724,6 +724,14 @@ vshAskReedit(vshControl *ctl, const char *msg ATTRIBUTE_UNUSED)
 }
 #endif /* WIN32 */
 
+static int vshStreamSink(virStreamPtr st ATTRIBUTE_UNUSED,
+                         const char *bytes, size_t nbytes, void *opaque)
+{
+    int *fd = opaque;
+
+    return safewrite(*fd, bytes, nbytes);
+}
+
 /* ---------------
  * Commands
  * ---------------
@@ -783,55 +791,6 @@ cmdHelp(vshControl *ctl, const vshCmd *cmd)
     }
 }
 
-/*
- * "autostart" command
- */
-static const vshCmdInfo info_autostart[] = {
-    {"help", N_("autostart a domain")},
-    {"desc",
-     N_("Configure a domain to be automatically started at boot.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_autostart[] = {
-    {"domain",  VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"disable", VSH_OT_BOOL, 0, N_("disable autostarting")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdAutostart(vshControl *ctl, const vshCmd *cmd)
-{
-    virDomainPtr dom;
-    const char *name;
-    int autostart;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
-        return false;
-
-    autostart = !vshCommandOptBool(cmd, "disable");
-
-    if (virDomainSetAutostart(dom, autostart) < 0) {
-        if (autostart)
-            vshError(ctl, _("Failed to mark domain %s as autostarted"), name);
-        else
-            vshError(ctl, _("Failed to unmark domain %s as autostarted"), name);
-        virDomainFree(dom);
-        return false;
-    }
-
-    if (autostart)
-        vshPrint(ctl, _("Domain %s marked as autostarted\n"), name);
-    else
-        vshPrint(ctl, _("Domain %s unmarked as autostarted\n"), name);
-
-    virDomainFree(dom);
-    return true;
-}
-
 /*
  * "connect" command
  */
@@ -884,2078 +843,1304 @@ cmdConnect(vshControl *ctl, const vshCmd *cmd)
     return !!ctl->conn;
 }
 
-#ifndef WIN32
-
 /*
- * "console" command
+ * "freecell" command
  */
-static const vshCmdInfo info_console[] = {
-    {"help", N_("connect to the guest console")},
-    {"desc",
-     N_("Connect the virtual serial console for the guest")},
+static const vshCmdInfo info_freecell[] = {
+    {"help", N_("NUMA free memory")},
+    {"desc", N_("display available free memory for the NUMA cell.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_console[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"devname", VSH_OT_STRING, 0, N_("character device name")},
-    {"force", VSH_OT_BOOL, 0,
-      N_("force console connection (disconnect already connected sessions)")},
-    {"safe", VSH_OT_BOOL, 0,
-      N_("only connect if safe console handling is supported")},
+static const vshCmdOptDef opts_freecell[] = {
+    {"cellno", VSH_OT_INT, 0, N_("NUMA cell number")},
+    {"all", VSH_OT_BOOL, 0, N_("show free memory for all NUMA cells")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdRunConsole(vshControl *ctl, virDomainPtr dom,
-              const char *name,
-              unsigned int flags)
+cmdFreecell(vshControl *ctl, const vshCmd *cmd)
 {
-    bool ret = false;
-    int state;
+    bool func_ret = false;
+    int ret;
+    int cell = -1, cell_given;
+    unsigned long long memory;
+    xmlNodePtr *nodes = NULL;
+    unsigned long nodes_cnt;
+    unsigned long *nodes_id = NULL;
+    unsigned long long *nodes_free = NULL;
+    int all_given;
+    int i;
+    char *cap_xml = NULL;
+    xmlDocPtr xml = NULL;
+    xmlXPathContextPtr ctxt = NULL;
 
-    if ((state = vshDomainState(ctl, dom, NULL)) < 0) {
-        vshError(ctl, "%s", _("Unable to get domain status"));
-        goto cleanup;
-    }
 
-    if (state == VIR_DOMAIN_SHUTOFF) {
-        vshError(ctl, "%s", _("The domain is not running"));
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if ( (cell_given = vshCommandOptInt(cmd, "cellno", &cell)) < 0) {
+        vshError(ctl, "%s", _("cell number has to be a number"));
         goto cleanup;
     }
+    all_given = vshCommandOptBool(cmd, "all");
 
-    if (!isatty(STDIN_FILENO)) {
-        vshError(ctl, "%s", _("Cannot run interactive console without a controlling TTY"));
+    if (all_given && cell_given) {
+        vshError(ctl, "%s", _("--cellno and --all are mutually exclusive. "
+                              "Please choose only one."));
         goto cleanup;
     }
 
-    vshPrintExtra(ctl, _("Connected to domain %s\n"), virDomainGetName(dom));
-    vshPrintExtra(ctl, _("Escape character is %s\n"), ctl->escapeChar);
-    fflush(stdout);
-    if (vshRunConsole(dom, name, ctl->escapeChar, flags) == 0)
-        ret = true;
+    if (all_given) {
+        cap_xml = virConnectGetCapabilities(ctl->conn);
+        if (!cap_xml) {
+            vshError(ctl, "%s", _("unable to get node capabilities"));
+            goto cleanup;
+        }
 
- cleanup:
+        xml = virXMLParseStringCtxt(cap_xml, _("(capabilities)"), &ctxt);
+        if (!xml) {
+            vshError(ctl, "%s", _("unable to get node capabilities"));
+            goto cleanup;
+        }
+        nodes_cnt = virXPathNodeSet("/capabilities/host/topology/cells/cell",
+                                    ctxt, &nodes);
 
-    return ret;
+        if (nodes_cnt == -1) {
+            vshError(ctl, "%s", _("could not get information about "
+                                  "NUMA topology"));
+            goto cleanup;
+        }
+
+        nodes_free = vshCalloc(ctl, nodes_cnt, sizeof(*nodes_free));
+        nodes_id = vshCalloc(ctl, nodes_cnt, sizeof(*nodes_id));
+
+        for (i = 0; i < nodes_cnt; i++) {
+            unsigned long id;
+            char *val = virXMLPropString(nodes[i], "id");
+            if (virStrToLong_ul(val, NULL, 10, &id)) {
+                vshError(ctl, "%s", _("conversion from string failed"));
+                VIR_FREE(val);
+                goto cleanup;
+            }
+            VIR_FREE(val);
+            nodes_id[i]=id;
+            ret = virNodeGetCellsFreeMemory(ctl->conn, &(nodes_free[i]), id, 1);
+            if (ret != 1) {
+                vshError(ctl, _("failed to get free memory for NUMA node "
+                                "number: %lu"), id);
+                goto cleanup;
+            }
+        }
+
+        memory = 0;
+        for (cell = 0; cell < nodes_cnt; cell++) {
+            vshPrint(ctl, "%5lu: %10llu KiB\n", nodes_id[cell],
+                    (nodes_free[cell]/1024));
+            memory += nodes_free[cell];
+        }
+
+        vshPrintExtra(ctl, "--------------------\n");
+        vshPrintExtra(ctl, "%5s: %10llu KiB\n", _("Total"), memory/1024);
+    } else {
+        if (!cell_given) {
+            memory = virNodeGetFreeMemory(ctl->conn);
+            if (memory == 0)
+                goto cleanup;
+        } else {
+            ret = virNodeGetCellsFreeMemory(ctl->conn, &memory, cell, 1);
+            if (ret != 1)
+                goto cleanup;
+        }
+
+        if (cell == -1)
+            vshPrint(ctl, "%s: %llu KiB\n", _("Total"), (memory/1024));
+        else
+            vshPrint(ctl, "%d: %llu KiB\n", cell, (memory/1024));
+    }
+
+    func_ret = true;
+
+cleanup:
+    xmlXPathFreeContext(ctxt);
+    xmlFreeDoc(xml);
+    VIR_FREE(nodes);
+    VIR_FREE(nodes_free);
+    VIR_FREE(nodes_id);
+    VIR_FREE(cap_xml);
+    return func_ret;
 }
 
+/*
+ * "nodeinfo" command
+ */
+static const vshCmdInfo info_nodeinfo[] = {
+    {"help", N_("node information")},
+    {"desc", N_("Returns basic information about the node.")},
+    {NULL, NULL}
+};
+
 static bool
-cmdConsole(vshControl *ctl, const vshCmd *cmd)
+cmdNodeinfo(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
 {
-    virDomainPtr dom;
-    bool ret = false;
-    bool force = vshCommandOptBool(cmd, "force");
-    bool safe = vshCommandOptBool(cmd, "safe");
-    unsigned int flags = 0;
-    const char *name = NULL;
+    virNodeInfo info;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+    if (virNodeGetInfo(ctl->conn, &info) < 0) {
+        vshError(ctl, "%s", _("failed to get node information"));
         return false;
-
-    if (vshCommandOptString(cmd, "devname", &name) < 0) {
-        vshError(ctl, "%s", _("Invalid devname"));
-        goto cleanup;
     }
+    vshPrint(ctl, "%-20s %s\n", _("CPU model:"), info.model);
+    vshPrint(ctl, "%-20s %d\n", _("CPU(s):"), info.cpus);
+    vshPrint(ctl, "%-20s %d MHz\n", _("CPU frequency:"), info.mhz);
+    vshPrint(ctl, "%-20s %d\n", _("CPU socket(s):"), info.sockets);
+    vshPrint(ctl, "%-20s %d\n", _("Core(s) per socket:"), info.cores);
+    vshPrint(ctl, "%-20s %d\n", _("Thread(s) per core:"), info.threads);
+    vshPrint(ctl, "%-20s %d\n", _("NUMA cell(s):"), info.nodes);
+    vshPrint(ctl, "%-20s %lu KiB\n", _("Memory size:"), info.memory);
 
-    if (force)
-        flags |= VIR_DOMAIN_CONSOLE_FORCE;
-    if (safe)
-        flags |= VIR_DOMAIN_CONSOLE_SAFE;
-
-    ret = cmdRunConsole(ctl, dom, name, flags);
-
-cleanup:
-    virDomainFree(dom);
-    return ret;
+    return true;
 }
 
-#endif /* WIN32 */
-
 /*
- * "desc" command for managing domain description and title
+ * "nodecpustats" command
  */
-static const vshCmdInfo info_desc[] = {
-    {"help", N_("show or set domain's description or title")},
-    {"desc", N_("Allows to show or modify description or title of a domain.")},
+static const vshCmdInfo info_nodecpustats[] = {
+    {"help", N_("Prints cpu stats of the node.")},
+    {"desc", N_("Returns cpu stats of the node, in nanoseconds.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_desc[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"live", VSH_OT_BOOL, 0, N_("modify/get running state")},
-    {"config", VSH_OT_BOOL, 0, N_("modify/get persistent configuration")},
-    {"current", VSH_OT_BOOL, 0, N_("modify/get current state configuration")},
-    {"title", VSH_OT_BOOL, 0, N_("modify/get the title instead of description")},
-    {"edit", VSH_OT_BOOL, 0, N_("open an editor to modify the description")},
-    {"new-desc", VSH_OT_ARGV, 0, N_("message")},
+static const vshCmdOptDef opts_node_cpustats[] = {
+    {"cpu", VSH_OT_INT, 0, N_("prints specified cpu statistics only.")},
+    {"percent", VSH_OT_BOOL, 0, N_("prints by percentage during 1 second.")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdDesc(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
+cmdNodeCpuStats(vshControl *ctl, const vshCmd *cmd)
 {
-    virDomainPtr dom;
-    bool config = vshCommandOptBool(cmd, "config");
-    bool live = vshCommandOptBool(cmd, "live");
-    bool current = vshCommandOptBool(cmd, "current");
-
-    bool title = vshCommandOptBool(cmd, "title");
-    bool edit = vshCommandOptBool(cmd, "edit");
-
-    int state;
-    int type;
-    char *desc = NULL;
-    char *desc_edited = NULL;
-    char *tmp = NULL;
-    char *tmpstr;
-    const vshCmdOpt *opt = NULL;
-    virBuffer buf = VIR_BUFFER_INITIALIZER;
-    bool pad = false;
+    int i, j;
+    bool flag_utilization = false;
+    bool flag_percent = vshCommandOptBool(cmd, "percent");
+    int cpuNum = VIR_NODE_CPU_STATS_ALL_CPUS;
+    virNodeCPUStatsPtr params;
+    int nparams = 0;
     bool ret = false;
-    unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
-
-    if (current) {
-        if (live || config) {
-            vshError(ctl, "%s", _("--current must be specified exclusively"));
-            return false;
-        }
-        flags = VIR_DOMAIN_AFFECT_CURRENT;
-    } else {
-        if (config)
-            flags |= VIR_DOMAIN_AFFECT_CONFIG;
-        if (live)
-            flags |= VIR_DOMAIN_AFFECT_LIVE;
-    }
+    struct cpu_stats {
+        unsigned long long user;
+        unsigned long long sys;
+        unsigned long long idle;
+        unsigned long long iowait;
+        unsigned long long util;
+    } cpu_stats[2];
+    double user_time, sys_time, idle_time, iowait_time, total_time;
+    double usage;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+    if (vshCommandOptInt(cmd, "cpu", &cpuNum) < 0) {
+        vshError(ctl, "%s", _("Invalid value of cpuNum"));
         return false;
-
-    if ((state = vshDomainState(ctl, dom, NULL)) < 0)
-        goto cleanup;
-
-    while ((opt = vshCommandOptArgv(cmd, opt))) {
-        if (pad)
-            virBufferAddChar(&buf, ' ');
-        pad = true;
-        virBufferAdd(&buf, opt->data, -1);
     }
 
-    if (title)
-        type = VIR_DOMAIN_METADATA_TITLE;
-    else
-        type = VIR_DOMAIN_METADATA_DESCRIPTION;
-
-    if (virBufferError(&buf)) {
-        vshPrint(ctl, "%s", _("Failed to collect new description/title"));
-        goto cleanup;
+    if (virNodeGetCPUStats(ctl->conn, cpuNum, NULL, &nparams, 0) != 0) {
+        vshError(ctl, "%s",
+                 _("Unable to get number of cpu stats"));
+        return false;
+    }
+    if (nparams == 0) {
+        /* nothing to output */
+        return true;
     }
-    desc = virBufferContentAndReset(&buf);
 
-    if (edit || desc) {
-        if (!desc) {
-                desc = vshGetDomainDescription(ctl, dom, title,
-                                           config?VIR_DOMAIN_XML_INACTIVE:0);
-                if (!desc)
-                    goto cleanup;
-        }
+    memset(cpu_stats, 0, sizeof(cpu_stats));
+    params = vshCalloc(ctl, nparams, sizeof(*params));
 
-        if (edit) {
-            /* Create and open the temporary file. */
-            if (!(tmp = editWriteToTempFile(ctl, desc)))
-                goto cleanup;
+    for (i = 0; i < 2; i++) {
+        if (i > 0)
+            sleep(1);
 
-            /* Start the editor. */
-            if (editFile(ctl, tmp) == -1)
-                goto cleanup;
+        if (virNodeGetCPUStats(ctl->conn, cpuNum, params, &nparams, 0) != 0) {
+            vshError(ctl, "%s", _("Unable to get node cpu stats"));
+            goto cleanup;
+        }
 
-            /* Read back the edited file. */
-            if (!(desc_edited = editReadBackFile(ctl, tmp)))
-                goto cleanup;
+        for (j = 0; j < nparams; j++) {
+            unsigned long long value = params[j].value;
 
-            /* strip a possible newline at the end of file; some
-             * editors enforce a newline, this makes editing the title
-             * more convenient */
-            if (title &&
-                (tmpstr = strrchr(desc_edited, '\n')) &&
-                *(tmpstr+1) == '\0')
-                *tmpstr = '\0';
-
-            /* Compare original XML with edited.  Has it changed at all? */
-            if (STREQ(desc, desc_edited)) {
-                vshPrint(ctl, _("Domain description not changed.\n"));
-                ret = true;
-                goto cleanup;
+            if (STREQ(params[j].field, VIR_NODE_CPU_STATS_KERNEL)) {
+                cpu_stats[i].sys = value;
+            } else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_USER)) {
+                cpu_stats[i].user = value;
+            } else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_IDLE)) {
+                cpu_stats[i].idle = value;
+            } else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_IOWAIT)) {
+                cpu_stats[i].iowait = value;
+            } else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_UTILIZATION)) {
+                cpu_stats[i].util = value;
+                flag_utilization = true;
             }
-
-            VIR_FREE(desc);
-            desc = desc_edited;
-            desc_edited = NULL;
         }
 
-        if (virDomainSetMetadata(dom, type, desc, NULL, NULL, flags) < 0) {
-            vshError(ctl, "%s",
-                     _("Failed to set new domain description"));
-            goto cleanup;
+        if (flag_utilization || !flag_percent)
+            break;
+    }
+
+    if (!flag_percent) {
+        if (!flag_utilization) {
+            vshPrint(ctl, "%-15s %20llu\n", _("user:"), cpu_stats[0].user);
+            vshPrint(ctl, "%-15s %20llu\n", _("system:"), cpu_stats[0].sys);
+            vshPrint(ctl, "%-15s %20llu\n", _("idle:"), cpu_stats[0].idle);
+            vshPrint(ctl, "%-15s %20llu\n", _("iowait:"), cpu_stats[0].iowait);
         }
-        vshPrint(ctl, "%s", _("Domain description updated successfully"));
     } else {
-        desc = vshGetDomainDescription(ctl, dom, title,
-                                       config?VIR_DOMAIN_XML_INACTIVE:0);
-        if (!desc)
-            goto cleanup;
+        if (flag_utilization) {
+            usage = cpu_stats[0].util;
 
-        if (strlen(desc) > 0)
-            vshPrint(ctl, "%s", desc);
-        else
-            vshPrint(ctl, _("No description for domain: %s"),
-                     virDomainGetName(dom));
+            vshPrint(ctl, "%-15s %5.1lf%%\n", _("usage:"), usage);
+            vshPrint(ctl, "%-15s %5.1lf%%\n", _("idle:"), 100 - usage);
+        } else {
+            user_time   = cpu_stats[1].user   - cpu_stats[0].user;
+            sys_time    = cpu_stats[1].sys    - cpu_stats[0].sys;
+            idle_time   = cpu_stats[1].idle   - cpu_stats[0].idle;
+            iowait_time = cpu_stats[1].iowait - cpu_stats[0].iowait;
+            total_time  = user_time + sys_time + idle_time + iowait_time;
+
+            usage = (user_time + sys_time) / total_time * 100;
+
+            vshPrint(ctl, "%-15s %5.1lf%%\n",
+                     _("usage:"), usage);
+            vshPrint(ctl, "%-15s %5.1lf%%\n",
+                     _("user:"), user_time / total_time * 100);
+            vshPrint(ctl, "%-15s %5.1lf%%\n",
+                     _("system:"), sys_time  / total_time * 100);
+            vshPrint(ctl, "%-15s %5.1lf%%\n",
+                     _("idle:"), idle_time     / total_time * 100);
+            vshPrint(ctl, "%-15s %5.1lf%%\n",
+                     _("iowait:"), iowait_time   / total_time * 100);
+        }
     }
 
     ret = true;
-cleanup:
-    VIR_FREE(desc_edited);
-    VIR_FREE(desc);
-    if (tmp) {
-        unlink(tmp);
-        VIR_FREE(tmp);
-    }
-    if (dom)
-        virDomainFree(dom);
+
+  cleanup:
+    VIR_FREE(params);
     return ret;
 }
 
-/* "domif-setlink" command
+/*
+ * "nodememstats" command
  */
-static const vshCmdInfo info_domif_setlink[] = {
-    {"help", N_("set link state of a virtual interface")},
-    {"desc", N_("Set link state of a domain's virtual interface. This command wraps usage of update-device command.")},
-    {NULL,NULL}
+static const vshCmdInfo info_nodememstats[] = {
+    {"help", N_("Prints memory stats of the node.")},
+    {"desc", N_("Returns memory stats of the node, in kilobytes.")},
+    {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_domif_setlink[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface device (MAC Address)")},
-    {"state", VSH_OT_DATA, VSH_OFLAG_REQ, N_("new state of the device")},
-    {"persistent", VSH_OT_ALIAS, 0, "config"},
-    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
+static const vshCmdOptDef opts_node_memstats[] = {
+    {"cell", VSH_OT_INT, 0, N_("prints specified cell statistics only.")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdDomIfSetLink(vshControl *ctl, const vshCmd *cmd)
+cmdNodeMemStats(vshControl *ctl, const vshCmd *cmd)
 {
-    virDomainPtr dom;
-    const char *iface;
-    const char *state;
-    const char *value;
-    const char *desc;
-    virMacAddr macaddr;
-    const char *element;
-    const char *attr;
-    bool config;
+    int nparams = 0;
+    unsigned int i = 0;
+    int cellNum = VIR_NODE_MEMORY_STATS_ALL_CELLS;
+    virNodeMemoryStatsPtr params = NULL;
     bool ret = false;
-    unsigned int flags = 0;
-    int i;
-    xmlDocPtr xml = NULL;
-    xmlXPathContextPtr ctxt = NULL;
-    xmlXPathObjectPtr obj = NULL;
-    xmlNodePtr cur = NULL;
-    xmlBufferPtr xml_buf = NULL;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+    if (vshCommandOptInt(cmd, "cell", &cellNum) < 0) {
+        vshError(ctl, "%s", _("Invalid value of cellNum"));
         return false;
-
-    if (vshCommandOptString(cmd, "interface", &iface) <= 0)
-        goto cleanup;
-
-    if (vshCommandOptString(cmd, "state", &state) <= 0)
-        goto cleanup;
-
-    config = vshCommandOptBool(cmd, "config");
-
-    if (STRNEQ(state, "up") && STRNEQ(state, "down")) {
-        vshError(ctl, _("invalid link state '%s'"), state);
-        goto cleanup;
     }
 
-    /* get persistent or live description of network device */
-    desc = virDomainGetXMLDesc(dom, config ? VIR_DOMAIN_XML_INACTIVE : 0);
-    if (desc == NULL) {
-        vshError(ctl, _("Failed to get domain description xml"));
+    /* get the number of memory parameters */
+    if (virNodeGetMemoryStats(ctl->conn, cellNum, NULL, &nparams, 0) != 0) {
+        vshError(ctl, "%s",
+                 _("Unable to get number of memory stats"));
         goto cleanup;
     }
 
-    if (config)
-        flags = VIR_DOMAIN_AFFECT_CONFIG;
-    else
-        flags = VIR_DOMAIN_AFFECT_LIVE;
-
-    if (virDomainIsActive(dom) == 0)
-        flags = VIR_DOMAIN_AFFECT_CONFIG;
-
-    /* extract current network device description */
-    xml = virXMLParseStringCtxt(desc, _("(domain_definition)"), &ctxt);
-    VIR_FREE(desc);
-    if (!xml) {
-        vshError(ctl, _("Failed to parse domain description xml"));
+    if (nparams == 0) {
+        /* nothing to output */
+        ret = true;
         goto cleanup;
     }
 
-    obj = xmlXPathEval(BAD_CAST "/domain/devices/interface", ctxt);
-    if (obj == NULL || obj->type != XPATH_NODESET ||
-        obj->nodesetval == NULL || obj->nodesetval->nodeNr == 0) {
-        vshError(ctl, _("Failed to extract interface information or no interfaces found"));
+    /* now go get all the memory parameters */
+    params = vshCalloc(ctl, nparams, sizeof(*params));
+    if (virNodeGetMemoryStats(ctl->conn, cellNum, params, &nparams, 0) != 0) {
+        vshError(ctl, "%s", _("Unable to get memory stats"));
         goto cleanup;
     }
 
-    if (virMacAddrParse(iface, &macaddr) == 0) {
-        element = "mac";
-        attr = "address";
-    } else {
-        element = "target";
-        attr = "dev";
-    }
+    for (i = 0; i < nparams; i++)
+        vshPrint(ctl, "%-7s: %20llu KiB\n", params[i].field, params[i].value);
 
-    /* find interface with matching mac addr */
-    for (i = 0; i < obj->nodesetval->nodeNr; i++) {
-        cur = obj->nodesetval->nodeTab[i]->children;
+    ret = true;
 
-        while (cur) {
-            if (cur->type == XML_ELEMENT_NODE &&
-                xmlStrEqual(cur->name, BAD_CAST element)) {
-                value = virXMLPropString(cur, attr);
+  cleanup:
+    VIR_FREE(params);
+    return ret;
+}
 
-                if (STRCASEEQ(value, iface)) {
-                    VIR_FREE(value);
-                    goto hit;
-                }
-                VIR_FREE(value);
-            }
-            cur = cur->next;
-        }
-    }
+/*
+ * "nodesuspend" command
+ */
+static const vshCmdInfo info_nodesuspend[] = {
+    {"help", N_("suspend the host node for a given time duration")},
+    {"desc", N_("Suspend the host node for a given time duration "
+                               "and attempt to resume thereafter.")},
+    {NULL, NULL}
+};
 
-    vshError(ctl, _("interface (%s: %s) not found"), element, iface);
-    goto cleanup;
+static const vshCmdOptDef opts_node_suspend[] = {
+    {"target", VSH_OT_DATA, VSH_OFLAG_REQ, N_("mem(Suspend-to-RAM), "
+                                               "disk(Suspend-to-Disk), hybrid(Hybrid-Suspend)")},
+    {"duration", VSH_OT_INT, VSH_OFLAG_REQ, N_("Suspend duration in seconds")},
+    {"flags", VSH_OT_INT, VSH_OFLAG_NONE, N_("Suspend flags, 0 for default")},
+    {NULL, 0, 0, NULL}
+};
 
-hit:
-    /* find and modify/add link state node */
-    /* try to find <link> element */
-    cur = obj->nodesetval->nodeTab[i]->children;
+static bool
+cmdNodeSuspend(vshControl *ctl, const vshCmd *cmd)
+{
+    const char *target = NULL;
+    unsigned int suspendTarget;
+    long long duration;
+    unsigned int flags = 0;
 
-    while (cur) {
-        if (cur->type == XML_ELEMENT_NODE &&
-            xmlStrEqual(cur->name, BAD_CAST "link")) {
-            /* found, just modify the property */
-            xmlSetProp(cur, BAD_CAST "state", BAD_CAST state);
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
 
-            break;
-        }
-        cur = cur->next;
+    if (vshCommandOptString(cmd, "target", &target) < 0) {
+        vshError(ctl, _("Invalid target argument"));
+        return false;
     }
 
-    if (!cur) {
-        /* element <link> not found, add one */
-        cur = xmlNewChild(obj->nodesetval->nodeTab[i],
-                          NULL,
-                          BAD_CAST "link",
-                          NULL);
-        if (!cur)
-            goto cleanup;
+    if (vshCommandOptLongLong(cmd, "duration", &duration) < 0) {
+        vshError(ctl, _("Invalid duration argument"));
+        return false;
+    }
 
-        if (xmlNewProp(cur, BAD_CAST "state", BAD_CAST state) == NULL)
-            goto cleanup;
+    if (vshCommandOptUInt(cmd, "flags", &flags) < 0) {
+        vshError(ctl, _("Invalid flags argument"));
+        return false;
     }
 
-    xml_buf = xmlBufferCreate();
-    if (!xml_buf) {
-        vshError(ctl, _("Failed to allocate memory"));
-        goto cleanup;
+    if (STREQ(target, "mem"))
+        suspendTarget = VIR_NODE_SUSPEND_TARGET_MEM;
+    else if (STREQ(target, "disk"))
+        suspendTarget = VIR_NODE_SUSPEND_TARGET_DISK;
+    else if (STREQ(target, "hybrid"))
+        suspendTarget = VIR_NODE_SUSPEND_TARGET_HYBRID;
+    else {
+        vshError(ctl, "%s", _("Invalid target"));
+        return false;
     }
 
-    if (xmlNodeDump(xml_buf, xml, obj->nodesetval->nodeTab[i], 0, 0) < 0 ) {
-        vshError(ctl, _("Failed to create XML"));
-        goto cleanup;
+    if (duration <= 0) {
+        vshError(ctl, "%s", _("Invalid duration"));
+        return false;
     }
 
-    if (virDomainUpdateDeviceFlags(dom, (char *)xmlBufferContent(xml_buf), flags) < 0) {
-        vshError(ctl, _("Failed to update interface link state"));
-        goto cleanup;
-    } else {
-        vshPrint(ctl, "%s", _("Device updated successfully\n"));
-        ret = true;
+    if (virNodeSuspendForDuration(ctl->conn, suspendTarget, duration,
+                                  flags) < 0) {
+        vshError(ctl, "%s", _("The host was not suspended"));
+        return false;
     }
+    return true;
+}
 
-cleanup:
-    xmlXPathFreeObject(obj);
-    xmlXPathFreeContext(ctxt);
-    xmlFreeDoc(xml);
-    xmlBufferFree(xml_buf);
 
-    if (dom)
-        virDomainFree(dom);
+/*
+ * "capabilities" command
+ */
+static const vshCmdInfo info_capabilities[] = {
+    {"help", N_("capabilities")},
+    {"desc", N_("Returns capabilities of hypervisor/driver.")},
+    {NULL, NULL}
+};
 
-    return ret;
+static bool
+cmdCapabilities(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
+{
+    char *caps;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    if ((caps = virConnectGetCapabilities(ctl->conn)) == NULL) {
+        vshError(ctl, "%s", _("failed to get capabilities"));
+        return false;
+    }
+    vshPrint(ctl, "%s\n", caps);
+    VIR_FREE(caps);
+
+    return true;
 }
 
-/* "domiftune" command
+/*
+ * "net-autostart" command
  */
-static const vshCmdInfo info_domiftune[] = {
-    {"help", N_("get/set parameters of a virtual interface")},
-    {"desc", N_("Get/set parameters of a domain's virtual interface.")},
-    {NULL,NULL}
+static const vshCmdInfo info_network_autostart[] = {
+    {"help", N_("autostart a network")},
+    {"desc",
+     N_("Configure a network to be automatically started at boot.")},
+    {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_domiftune[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface device (MAC Address)")},
-    {"inbound", VSH_OT_DATA, VSH_OFLAG_NONE, N_("control domain's incoming traffics")},
-    {"outbound", VSH_OT_DATA, VSH_OFLAG_NONE, N_("control domain's outgoing traffics")},
-    {"config", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("affect next boot")},
-    {"live", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("affect running domain")},
-    {"current", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("affect current domain")},
+static const vshCmdOptDef opts_network_autostart[] = {
+    {"network",  VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
+    {"disable", VSH_OT_BOOL, 0, N_("disable autostarting")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdDomIftune(vshControl *ctl, const vshCmd *cmd)
+cmdNetworkAutostart(vshControl *ctl, const vshCmd *cmd)
 {
-    virDomainPtr dom;
-    const char *name = NULL, *device = NULL,
-               *inboundStr = NULL, *outboundStr = NULL;
-    unsigned int flags = 0;
-    int nparams = 0;
-    virTypedParameterPtr params = NULL;
-    bool ret = false;
-    bool current = vshCommandOptBool(cmd, "current");
-    bool config = vshCommandOptBool(cmd, "config");
-    bool live = vshCommandOptBool(cmd, "live");
-    virNetDevBandwidthRate inbound, outbound;
-    int i;
-
-    if (current) {
-        if (live || config) {
-            vshError(ctl, "%s", _("--current must be specified exclusively"));
-            return false;
-        }
-        flags = VIR_DOMAIN_AFFECT_CURRENT;
-    } else {
-        if (config)
-            flags |= VIR_DOMAIN_AFFECT_CONFIG;
-        if (live)
-            flags |= VIR_DOMAIN_AFFECT_LIVE;
-    }
+    virNetworkPtr network;
+    const char *name;
+    int autostart;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+    if (!(network = vshCommandOptNetwork(ctl, cmd, &name)))
         return false;
 
-    if (vshCommandOptString(cmd, "interface", &device) <= 0) {
-        virDomainFree(dom);
-        return false;
-    }
-
-    if (vshCommandOptString(cmd, "inbound", &inboundStr) < 0 ||
-        vshCommandOptString(cmd, "outbound", &outboundStr) < 0) {
-        vshError(ctl, "missing argument");
-        goto cleanup;
-    }
-
-    memset(&inbound, 0, sizeof(inbound));
-    memset(&outbound, 0, sizeof(outbound));
-
-    if (inboundStr) {
-        if (parseRateStr(inboundStr, &inbound) < 0) {
-            vshError(ctl, _("inbound format is incorrect"));
-            goto cleanup;
-        }
-        if (inbound.average == 0) {
-            vshError(ctl, _("inbound average is mandatory"));
-            goto cleanup;
-        }
-        nparams++; /* average */
-        if (inbound.peak) nparams++;
-        if (inbound.burst) nparams++;
-    }
-    if (outboundStr) {
-        if (parseRateStr(outboundStr, &outbound) < 0) {
-            vshError(ctl, _("outbound format is incorrect"));
-            goto cleanup;
-        }
-        if (outbound.average == 0) {
-            vshError(ctl, _("outbound average is mandatory"));
-            goto cleanup;
-        }
-        nparams++; /* average */
-        if (outbound.peak) nparams++;
-        if (outbound.burst) nparams++;
-    }
-
-    if (nparams == 0) {
-        /* get the number of interface parameters */
-        if (virDomainGetInterfaceParameters(dom, device, NULL, &nparams, flags) != 0) {
-            vshError(ctl, "%s",
-                     _("Unable to get number of interface parameters"));
-            goto cleanup;
-        }
-
-        if (nparams == 0) {
-            /* nothing to output */
-            ret = true;
-            goto cleanup;
-        }
-
-        /* get all interface parameters */
-        params = vshCalloc(ctl, nparams, sizeof(*params));
-        if (!params) {
-            virReportOOMError();
-            goto cleanup;
-        }
-        if (virDomainGetInterfaceParameters(dom, device, params, &nparams, flags) != 0) {
-            vshError(ctl, "%s", _("Unable to get interface parameters"));
-            goto cleanup;
-        }
-
-        for (i = 0; i < nparams; i++) {
-            char *str = vshGetTypedParamValue(ctl, &params[i]);
-            vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
-            VIR_FREE(str);
-        }
-    } else {
-        /* set the interface parameters */
-        params = vshCalloc(ctl, nparams, sizeof(*params));
-        if (!params) {
-            virReportOOMError();
-            goto cleanup;
-        }
-
-        for (i = 0; i < nparams; i++)
-            params[i].type = VIR_TYPED_PARAM_UINT;
-
-        i = 0;
-        if (inbound.average && i < nparams) {
-            if (!virStrcpy(params[i].field, VIR_DOMAIN_BANDWIDTH_IN_AVERAGE,
-                           sizeof(params[i].field)))
-                goto cleanup;
-            params[i].value.ui = inbound.average;
-            i++;
-        }
-        if (inbound.peak && i < nparams) {
-            if (!virStrcpy(params[i].field, VIR_DOMAIN_BANDWIDTH_IN_PEAK,
-                           sizeof(params[i].field)))
-                goto cleanup;
-            params[i].value.ui = inbound.peak;
-            i++;
-        }
-        if (inbound.burst && i < nparams) {
-            if (!virStrcpy(params[i].field, VIR_DOMAIN_BANDWIDTH_IN_BURST,
-                           sizeof(params[i].field)))
-                goto cleanup;
-            params[i].value.ui = inbound.burst;
-            i++;
-        }
-        if (outbound.average && i < nparams) {
-            if (!virStrcpy(params[i].field, VIR_DOMAIN_BANDWIDTH_OUT_AVERAGE,
-                           sizeof(params[i].field)))
-                goto cleanup;
-            params[i].value.ui = outbound.average;
-            i++;
-        }
-        if (outbound.peak && i < nparams) {
-            if (!virStrcpy(params[i].field, VIR_DOMAIN_BANDWIDTH_OUT_PEAK,
-                           sizeof(params[i].field)))
-                goto cleanup;
-            params[i].value.ui = outbound.peak;
-            i++;
-        }
-        if (outbound.burst && i < nparams) {
-            if (!virStrcpy(params[i].field, VIR_DOMAIN_BANDWIDTH_OUT_BURST,
-                           sizeof(params[i].field)))
-                goto cleanup;
-            params[i].value.ui = outbound.burst;
-            i++;
-        }
+    autostart = !vshCommandOptBool(cmd, "disable");
 
-        if (virDomainSetInterfaceParameters(dom, device, params, nparams, flags) != 0) {
-            vshError(ctl, "%s", _("Unable to set interface parameters"));
-            goto cleanup;
-        }
+    if (virNetworkSetAutostart(network, autostart) < 0) {
+        if (autostart)
+            vshError(ctl, _("failed to mark network %s as autostarted"), name);
+        else
+            vshError(ctl, _("failed to unmark network %s as autostarted"), name);
+        virNetworkFree(network);
+        return false;
     }
 
-    ret = true;
+    if (autostart)
+        vshPrint(ctl, _("Network %s marked as autostarted\n"), name);
+    else
+        vshPrint(ctl, _("Network %s unmarked as autostarted\n"), name);
 
-cleanup:
-    virTypedParameterArrayClear(params, nparams);
-    VIR_FREE(params);
-    virDomainFree(dom);
-    return ret;
+    virNetworkFree(network);
+    return true;
 }
 
 /*
- * "suspend" command
+ * "net-create" command
  */
-static const vshCmdInfo info_suspend[] = {
-    {"help", N_("suspend a domain")},
-    {"desc", N_("Suspend a running domain.")},
+static const vshCmdInfo info_network_create[] = {
+    {"help", N_("create a network from an XML file")},
+    {"desc", N_("Create a network.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_suspend[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+static const vshCmdOptDef opts_network_create[] = {
+    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML network description")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdSuspend(vshControl *ctl, const vshCmd *cmd)
+cmdNetworkCreate(vshControl *ctl, const vshCmd *cmd)
 {
-    virDomainPtr dom;
-    const char *name;
+    virNetworkPtr network;
+    const char *from = NULL;
     bool ret = true;
+    char *buffer;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+    if (vshCommandOptString(cmd, "file", &from) <= 0)
+        return false;
+
+    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
         return false;
 
-    if (virDomainSuspend(dom) == 0) {
-        vshPrint(ctl, _("Domain %s suspended\n"), name);
+    network = virNetworkCreateXML(ctl->conn, buffer);
+    VIR_FREE(buffer);
+
+    if (network != NULL) {
+        vshPrint(ctl, _("Network %s created from %s\n"),
+                 virNetworkGetName(network), from);
+        virNetworkFree(network);
     } else {
-        vshError(ctl, _("Failed to suspend domain %s"), name);
+        vshError(ctl, _("Failed to create network from %s"), from);
         ret = false;
     }
-
-    virDomainFree(dom);
     return ret;
 }
 
+
 /*
- * "dompmsuspend" command
+ * "net-define" command
  */
-static const vshCmdInfo info_dom_pm_suspend[] = {
-    {"help", N_("suspend a domain gracefully using power management "
-                "functions")},
-    {"desc", N_("Suspends a running domain using guest OS's power management. "
-                "(Note: This requires a guest agent configured and running in "
-                "the guest OS).")},
+static const vshCmdInfo info_network_define[] = {
+    {"help", N_("define (but don't start) a network from an XML file")},
+    {"desc", N_("Define a network.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_dom_pm_suspend[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"duration", VSH_OT_INT, VSH_OFLAG_REQ_OPT, N_("duration in seconds")},
-    {"target", VSH_OT_STRING, VSH_OFLAG_REQ, N_("mem(Suspend-to-RAM), "
-                                                "disk(Suspend-to-Disk), "
-                                                "hybrid(Hybrid-Suspend)")},
+static const vshCmdOptDef opts_network_define[] = {
+    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML network description")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdDomPMSuspend(vshControl *ctl, const vshCmd *cmd)
+cmdNetworkDefine(vshControl *ctl, const vshCmd *cmd)
 {
-    virDomainPtr dom;
-    const char *name;
-    bool ret = false;
-    const char *target = NULL;
-    unsigned int suspendTarget;
-    unsigned long long duration = 0;
+    virNetworkPtr network;
+    const char *from = NULL;
+    bool ret = true;
+    char *buffer;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+    if (vshCommandOptString(cmd, "file", &from) <= 0)
         return false;
 
-    if (vshCommandOptULongLong(cmd, "duration", &duration) < 0) {
-        vshError(ctl, _("Invalid duration argument"));
-        goto cleanup;
-    }
-
-    if (vshCommandOptString(cmd, "target", &target) < 0) {
-        vshError(ctl, _("Invalid target argument"));
-        goto cleanup;
-    }
+    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
+        return false;
 
-    if (STREQ(target, "mem"))
-        suspendTarget = VIR_NODE_SUSPEND_TARGET_MEM;
-    else if (STREQ(target, "disk"))
-        suspendTarget = VIR_NODE_SUSPEND_TARGET_DISK;
-    else if (STREQ(target, "hybrid"))
-        suspendTarget = VIR_NODE_SUSPEND_TARGET_HYBRID;
-    else {
-        vshError(ctl, "%s", _("Invalid target"));
-        goto cleanup;
-    }
+    network = virNetworkDefineXML(ctl->conn, buffer);
+    VIR_FREE(buffer);
 
-    if (virDomainPMSuspendForDuration(dom, suspendTarget, duration, 0) < 0) {
-        vshError(ctl, _("Domain %s could not be suspended"),
-                 virDomainGetName(dom));
-        goto cleanup;
+    if (network != NULL) {
+        vshPrint(ctl, _("Network %s defined from %s\n"),
+                 virNetworkGetName(network), from);
+        virNetworkFree(network);
+    } else {
+        vshError(ctl, _("Failed to define network from %s"), from);
+        ret = false;
     }
-
-    vshPrint(ctl, _("Domain %s successfully suspended"),
-             virDomainGetName(dom));
-
-    ret = true;
-
-cleanup:
-    virDomainFree(dom);
     return ret;
 }
 
+
 /*
- * "dompmwakeup" command
+ * "net-destroy" command
  */
-
-static const vshCmdInfo info_dom_pm_wakeup[] = {
-    {"help", N_("wakeup a domain suspended by dompmsuspend command")},
-    {"desc", N_("Wakeup a domain previously suspended "
-                "by dompmsuspend command.")},
+static const vshCmdInfo info_network_destroy[] = {
+    {"help", N_("destroy (stop) a network")},
+    {"desc", N_("Forcefully stop a given network.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_dom_pm_wakeup[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+static const vshCmdOptDef opts_network_destroy[] = {
+    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdDomPMWakeup(vshControl *ctl, const vshCmd *cmd)
+cmdNetworkDestroy(vshControl *ctl, const vshCmd *cmd)
 {
-    virDomainPtr dom;
+    virNetworkPtr network;
+    bool ret = true;
     const char *name;
-    bool ret = false;
-    unsigned int flags = 0;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+    if (!(network = vshCommandOptNetwork(ctl, cmd, &name)))
         return false;
 
-    if (virDomainPMWakeup(dom, flags) < 0) {
-        vshError(ctl, _("Domain %s could not be woken up"),
-                 virDomainGetName(dom));
-        goto cleanup;
+    if (virNetworkDestroy(network) == 0) {
+        vshPrint(ctl, _("Network %s destroyed\n"), name);
+    } else {
+        vshError(ctl, _("Failed to destroy network %s"), name);
+        ret = false;
     }
 
-    vshPrint(ctl, _("Domain %s successfully woken up"),
-             virDomainGetName(dom));
-
-    ret = true;
-
-cleanup:
-    virDomainFree(dom);
+    virNetworkFree(network);
     return ret;
 }
 
+
 /*
- * "create" command
+ * "net-dumpxml" command
  */
-static const vshCmdInfo info_create[] = {
-    {"help", N_("create a domain from an XML file")},
-    {"desc", N_("Create a domain.")},
+static const vshCmdInfo info_network_dumpxml[] = {
+    {"help", N_("network information in XML")},
+    {"desc", N_("Output the network information as an XML dump to stdout.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_create[] = {
-    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML domain description")},
-#ifndef WIN32
-    {"console", VSH_OT_BOOL, 0, N_("attach to console after creation")},
-#endif
-    {"paused", VSH_OT_BOOL, 0, N_("leave the guest paused after creation")},
-    {"autodestroy", VSH_OT_BOOL, 0, N_("automatically destroy the guest when virsh disconnects")},
+static const vshCmdOptDef opts_network_dumpxml[] = {
+    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
+    {"inactive", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("network information of an inactive domain")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdCreate(vshControl *ctl, const vshCmd *cmd)
+cmdNetworkDumpXML(vshControl *ctl, const vshCmd *cmd)
 {
-    virDomainPtr dom;
-    const char *from = NULL;
+    virNetworkPtr network;
     bool ret = true;
-    char *buffer;
-#ifndef WIN32
-    bool console = vshCommandOptBool(cmd, "console");
-#endif
-    unsigned int flags = VIR_DOMAIN_NONE;
+    char *dump;
+    unsigned int flags = 0;
+    int inactive;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (vshCommandOptString(cmd, "file", &from) <= 0)
-        return false;
-
-    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
+    if (!(network = vshCommandOptNetwork(ctl, cmd, NULL)))
         return false;
 
-    if (vshCommandOptBool(cmd, "paused"))
-        flags |= VIR_DOMAIN_START_PAUSED;
-    if (vshCommandOptBool(cmd, "autodestroy"))
-        flags |= VIR_DOMAIN_START_AUTODESTROY;
+    inactive = vshCommandOptBool(cmd, "inactive");
+    if (inactive)
+        flags |= VIR_NETWORK_XML_INACTIVE;
 
-    dom = virDomainCreateXML(ctl->conn, buffer, flags);
-    VIR_FREE(buffer);
+    dump = virNetworkGetXMLDesc(network, flags);
 
-    if (dom != NULL) {
-        vshPrint(ctl, _("Domain %s created from %s\n"),
-                 virDomainGetName(dom), from);
-#ifndef WIN32
-        if (console)
-            cmdRunConsole(ctl, dom, NULL, 0);
-#endif
-        virDomainFree(dom);
+    if (dump != NULL) {
+        vshPrint(ctl, "%s", dump);
+        VIR_FREE(dump);
     } else {
-        vshError(ctl, _("Failed to create domain from %s"), from);
         ret = false;
     }
+
+    virNetworkFree(network);
     return ret;
 }
 
 /*
- * "define" command
+ * "net-info" command
  */
-static const vshCmdInfo info_define[] = {
-    {"help", N_("define (but don't start) a domain from an XML file")},
-    {"desc", N_("Define a domain.")},
+static const vshCmdInfo info_network_info[] = {
+    {"help", N_("network information")},
+    {"desc", N_("Returns basic information about the network")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_define[] = {
-    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML domain description")},
+static const vshCmdOptDef opts_network_info[] = {
+    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdDefine(vshControl *ctl, const vshCmd *cmd)
+cmdNetworkInfo(vshControl *ctl, const vshCmd *cmd)
 {
-    virDomainPtr dom;
-    const char *from = NULL;
-    bool ret = true;
-    char *buffer;
+    virNetworkPtr network;
+    char uuid[VIR_UUID_STRING_BUFLEN];
+    int autostart;
+    int persistent = -1;
+    int active = -1;
+    char *bridge = NULL;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (vshCommandOptString(cmd, "file", &from) <= 0)
+    if (!(network = vshCommandOptNetwork(ctl, cmd, NULL)))
         return false;
 
-    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
-        return false;
+    vshPrint(ctl, "%-15s %s\n", _("Name"), virNetworkGetName(network));
 
-    dom = virDomainDefineXML(ctl->conn, buffer);
-    VIR_FREE(buffer);
+    if (virNetworkGetUUIDString(network, uuid) == 0)
+        vshPrint(ctl, "%-15s %s\n", _("UUID"), uuid);
 
-    if (dom != NULL) {
-        vshPrint(ctl, _("Domain %s defined from %s\n"),
-                 virDomainGetName(dom), from);
-        virDomainFree(dom);
-    } else {
-        vshError(ctl, _("Failed to define domain from %s"), from);
-        ret = false;
-    }
-    return ret;
-}
+    active = virNetworkIsActive(network);
+    if (active >= 0)
+        vshPrint(ctl, "%-15s %s\n", _("Active:"), active? _("yes") : _("no"));
 
-/*
- * "undefine" command
- */
-static const vshCmdInfo info_undefine[] = {
-    {"help", N_("undefine a domain")},
-    {"desc",
-     N_("Undefine an inactive domain, or convert persistent to transient.")},
+    persistent = virNetworkIsPersistent(network);
+    if (persistent < 0)
+        vshPrint(ctl, "%-15s %s\n", _("Persistent:"), _("unknown"));
+    else
+        vshPrint(ctl, "%-15s %s\n", _("Persistent:"), persistent ? _("yes") : _("no"));
+
+    if (virNetworkGetAutostart(network, &autostart) < 0)
+        vshPrint(ctl, "%-15s %s\n", _("Autostart:"), _("no autostart"));
+    else
+        vshPrint(ctl, "%-15s %s\n", _("Autostart:"), autostart ? _("yes") : _("no"));
+
+    bridge = virNetworkGetBridgeName(network);
+    if (bridge)
+        vshPrint(ctl, "%-15s %s\n", _("Bridge:"), bridge);
+
+    VIR_FREE(bridge);
+    virNetworkFree(network);
+    return true;
+}
+
+/*
+ * "iface-edit" command
+ */
+static const vshCmdInfo info_interface_edit[] = {
+    {"help", N_("edit XML configuration for a physical host interface")},
+    {"desc", N_("Edit the XML configuration for a physical host interface.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_undefine[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name or uuid")},
-    {"managed-save", VSH_OT_BOOL, 0, N_("remove domain managed state file")},
-    {"storage", VSH_OT_DATA, VSH_OFLAG_NONE,
-     N_("remove associated storage volumes (comma separated list of targets "
-        "or source paths) (see domblklist)")},
-    {"remove-all-storage", VSH_OT_BOOL, 0,
-     N_("remove all associated storage volumes (use with caution)")},
-    {"wipe-storage", VSH_OT_BOOL, VSH_OFLAG_NONE,
-     N_("wipe data on the removed volumes")},
-    {"snapshots-metadata", VSH_OT_BOOL, 0,
-     N_("remove all domain snapshot metadata, if inactive")},
+static const vshCmdOptDef opts_interface_edit[] = {
+    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name or MAC address")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdUndefine(vshControl *ctl, const vshCmd *cmd)
+cmdInterfaceEdit(vshControl *ctl, const vshCmd *cmd)
 {
-    virDomainPtr dom;
     bool ret = false;
-    const char *name = NULL;
-    /* Flags to attempt.  */
-    unsigned int flags = 0;
-    /* User-requested actions.  */
-    bool managed_save = vshCommandOptBool(cmd, "managed-save");
-    bool snapshots_metadata = vshCommandOptBool(cmd, "snapshots-metadata");
-    bool wipe_storage = vshCommandOptBool(cmd, "wipe-storage");
-    bool remove_storage = false;
-    bool remove_all_storage = vshCommandOptBool(cmd, "remove-all-storage");
-    /* Positive if these items exist.  */
-    int has_managed_save = 0;
-    int has_snapshots_metadata = 0;
-    int has_snapshots = 0;
-    /* True if undefine will not strand data, even on older servers.  */
-    bool managed_save_safe = false;
-    bool snapshots_safe = false;
-    int rc = -1;
-    int running;
-    /* list of volumes to remove along with this domain */
-    const char *volumes_arg = NULL;
-    char *volumes = NULL;
-    char **volume_tokens = NULL;
-    char *volume_tok = NULL;
-    int nvolume_tokens = 0;
-    char *def = NULL;
-    char *source = NULL;
-    char *target = NULL;
-    int vol_i;
-    int tok_i;
-    xmlDocPtr doc = NULL;
-    xmlXPathContextPtr ctxt = NULL;
-    xmlNodePtr *vol_nodes = NULL;
-    int nvolumes = 0;
-    virStorageVolPtr vol = NULL;
-    bool vol_del_failed = false;
-
-    if (managed_save) {
-        flags |= VIR_DOMAIN_UNDEFINE_MANAGED_SAVE;
-        managed_save_safe = true;
-    }
-    if (snapshots_metadata) {
-        flags |= VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA;
-        snapshots_safe = true;
-    }
+    virInterfacePtr iface = NULL;
+    virInterfacePtr iface_edited = NULL;
+    unsigned int flags = VIR_INTERFACE_XML_INACTIVE;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
-        return false;
-
-    /* check if a string that should contain list of volumes to remove is present */
-    if (vshCommandOptString(cmd, "storage", &volumes_arg) > 0) {
-        volumes = vshStrdup(ctl, volumes_arg);
-
-        if (remove_all_storage) {
-            vshError(ctl, _("Specified both --storage and --remove-all-storage"));
-            goto cleanup;
-        }
-        remove_storage = true;
-   }
-
-    /* Do some flag manipulation.  The goal here is to disable bits
-     * from flags to reduce the likelihood of a server rejecting
-     * unknown flag bits, as well as to track conditions which are
-     * safe by default for the given hypervisor and server version.  */
-    running = virDomainIsActive(dom);
-    if (running < 0) {
-        virshReportError(ctl);
-        goto cleanup;
-    }
-    if (!running) {
-        /* Undefine with snapshots only fails for inactive domains,
-         * and managed save only exists on inactive domains; if
-         * running, then we don't want to remove anything.  */
-        has_managed_save = virDomainHasManagedSaveImage(dom, 0);
-        if (has_managed_save < 0) {
-            if (last_error->code != VIR_ERR_NO_SUPPORT) {
-                virshReportError(ctl);
-                goto cleanup;
-            }
-            virFreeError(last_error);
-            last_error = NULL;
-            has_managed_save = 0;
-        }
-
-        has_snapshots = virDomainSnapshotNum(dom, 0);
-        if (has_snapshots < 0) {
-            if (last_error->code != VIR_ERR_NO_SUPPORT) {
-                virshReportError(ctl);
-                goto cleanup;
-            }
-            virFreeError(last_error);
-            last_error = NULL;
-            has_snapshots = 0;
-        }
-        if (has_snapshots) {
-            has_snapshots_metadata
-                = virDomainSnapshotNum(dom, VIR_DOMAIN_SNAPSHOT_LIST_METADATA);
-            if (has_snapshots_metadata < 0) {
-                /* The server did not know the new flag, assume that all
-                   snapshots have metadata.  */
-                virFreeError(last_error);
-                last_error = NULL;
-                has_snapshots_metadata = has_snapshots;
-            } else {
-                /* The server knew the new flag, all aspects of
-                 * undefineFlags are safe.  */
-                managed_save_safe = snapshots_safe = true;
-            }
-        }
-    }
-    if (!has_managed_save) {
-        flags &= ~VIR_DOMAIN_UNDEFINE_MANAGED_SAVE;
-        managed_save_safe = true;
-    }
-    if (has_snapshots == 0) {
-        snapshots_safe = true;
-    }
-    if (has_snapshots_metadata == 0) {
-        flags &= ~VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA;
-        snapshots_safe = true;
-    }
-
-    /* Stash domain description for later use */
-    if (remove_storage || remove_all_storage) {
-        if (running) {
-            vshError(ctl, _("Storage volume deletion is supported only on stopped domains"));
-            goto cleanup;
-        }
-
-        if (!(def = virDomainGetXMLDesc(dom, 0))) {
-            vshError(ctl, _("Could not retrieve domain XML description"));
-            goto cleanup;
-        }
-    }
-
-    /* Generally we want to try the new API first.  However, while
-     * virDomainUndefineFlags was introduced at the same time as
-     * VIR_DOMAIN_UNDEFINE_MANAGED_SAVE in 0.9.4, the
-     * VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA flag was not present
-     * until 0.9.5; skip to piecewise emulation if we couldn't prove
-     * above that the new API is safe.  */
-    if (managed_save_safe && snapshots_safe) {
-        rc = virDomainUndefineFlags(dom, flags);
-        if (rc == 0 || (last_error->code != VIR_ERR_NO_SUPPORT &&
-                        last_error->code != VIR_ERR_INVALID_ARG))
-            goto out;
-        virFreeError(last_error);
-        last_error = NULL;
-    }
-
-    /* The new API is unsupported or unsafe; fall back to doing things
-     * piecewise.  */
-    if (has_managed_save) {
-        if (!managed_save) {
-            vshError(ctl, "%s",
-                     _("Refusing to undefine while domain managed save "
-                       "image exists"));
-            goto cleanup;
-        }
-        if (virDomainManagedSaveRemove(dom, 0) < 0) {
-            virshReportError(ctl);
-            goto cleanup;
-        }
-    }
-
-    /* No way to emulate deletion of just snapshot metadata
-     * without support for the newer flags.  Oh well.  */
-    if (has_snapshots_metadata) {
-        vshError(ctl,
-                 snapshots_metadata ?
-                 _("Unable to remove metadata of %d snapshots") :
-                 _("Refusing to undefine while %d snapshots exist"),
-                 has_snapshots_metadata);
         goto cleanup;
-    }
 
-    rc = virDomainUndefine(dom);
-
-out:
-    if (rc == 0) {
-        vshPrint(ctl, _("Domain %s has been undefined\n"), name);
-        ret = true;
-    } else {
-        vshError(ctl, _("Failed to undefine domain %s"), name);
+    iface = vshCommandOptInterface(ctl, cmd, NULL);
+    if (iface == NULL)
         goto cleanup;
-    }
-
-    /* try to undefine storage volumes associated with this domain, if it's requested */
-    if (remove_storage || remove_all_storage) {
-        ret = false;
-
-        /* tokenize the string from user and save it's parts into an array */
-        if (volumes) {
-            /* count the delimiters */
-            volume_tok = volumes;
-            nvolume_tokens = 1; /* we need at least one member */
-            while (*volume_tok) {
-                if (*volume_tok == ',')
-                    nvolume_tokens++;
-                volume_tok++;
-            }
-
-            volume_tokens = vshCalloc(ctl, nvolume_tokens,  sizeof(char *));
-
-            /* tokenize the input string */
-            nvolume_tokens = 0;
-            volume_tok = volumes;
-            do {
-                volume_tokens[nvolume_tokens] = strsep(&volume_tok, ",");
-                nvolume_tokens++;
-            } while (volume_tok);
-        }
-
-        doc = virXMLParseStringCtxt(def, _("(domain_definition)"), &ctxt);
-        if (!doc)
-            goto cleanup;
-
-        nvolumes = virXPathNodeSet("./devices/disk", ctxt, &vol_nodes);
-
-        if (nvolumes < 0)
-            goto cleanup;
-
-        for (vol_i = 0; vol_i < nvolumes; vol_i++) {
-            ctxt->node = vol_nodes[vol_i];
-            VIR_FREE(target);
-            VIR_FREE(source);
-            if (vol) {
-                virStorageVolFree(vol);
-                vol = NULL;
-            }
-
-            /* get volume source and target paths */
-            if (!(target = virXPathString("string(./target/@dev)", ctxt))) {
-                vshError(ctl, _("Failed to enumerate devices"));
-                goto cleanup;
-            }
-
-            if (!(source = virXPathString("string("
-                                          "./source/@file|"
-                                          "./source/@dir|"
-                                          "./source/@name|"
-                                          "./source/@dev)", ctxt)) &&
-                virGetLastError())
-                goto cleanup;
-
-            /* lookup if volume was selected by user */
-            if (volumes) {
-                volume_tok = NULL;
-                for (tok_i = 0; tok_i < nvolume_tokens; tok_i++) {
-                    if (volume_tokens[tok_i] &&
-                        (STREQ_NULLABLE(volume_tokens[tok_i], target) ||
-                         STREQ_NULLABLE(volume_tokens[tok_i], source))) {
-                        volume_tok = volume_tokens[tok_i];
-                        volume_tokens[tok_i] = NULL;
-                        break;
-                    }
-                }
-                if (!volume_tok)
-                    continue;
-            }
-
-            if (!source)
-                continue;
-
-            if (!(vol = virStorageVolLookupByPath(ctl->conn, source))) {
-                vshPrint(ctl,
-                         _("Storage volume '%s'(%s) is not managed by libvirt. "
-                           "Remove it manually.\n"), target, source);
-                virResetLastError();
-                continue;
-            }
-
-            if (wipe_storage) {
-                vshPrint(ctl, _("Wiping volume '%s'(%s) ... "), target, source);
-                fflush(stdout);
-                if (virStorageVolWipe(vol, 0) < 0) {
-                    vshError(ctl, _("Failed! Volume not removed."));
-                    vol_del_failed = true;
-                    continue;
-                } else {
-                    vshPrint(ctl, _("Done.\n"));
-                }
-            }
 
-            /* delete the volume */
-            if (virStorageVolDelete(vol, 0) < 0) {
-                vshError(ctl, _("Failed to remove storage volume '%s'(%s)"),
-                         target, source);
-                vol_del_failed = true;
-            }
-            vshPrint(ctl, _("Volume '%s' removed.\n"), volume_tok?volume_tok:source);
-        }
+#define EDIT_GET_XML virInterfaceGetXMLDesc(iface, flags)
+#define EDIT_NOT_CHANGED \
+    vshPrint(ctl, _("Interface %s XML configuration not changed.\n"),   \
+             virInterfaceGetName(iface));                               \
+    ret = true; goto edit_cleanup;
+#define EDIT_DEFINE \
+    (iface_edited = virInterfaceDefineXML(ctl->conn, doc_edited, 0))
+#define EDIT_FREE \
+    if (iface_edited)   \
+        virInterfaceFree(iface_edited);
+#include "virsh-edit.c"
 
-        /* print volumes specified by user that were not found in domain definition */
-        if (volumes) {
-            for (tok_i = 0; tok_i < nvolume_tokens; tok_i++) {
-                if (volume_tokens[tok_i])
-                    vshPrint(ctl, _("Volume '%s' was not found in domain's "
-                                    "definition.\n"),
-                             volume_tokens[tok_i]);
-            }
-        }
+    vshPrint(ctl, _("Interface %s XML configuration edited.\n"),
+             virInterfaceGetName(iface_edited));
 
-        if (!vol_del_failed)
-            ret = true;
-    }
+    ret = true;
 
 cleanup:
-    VIR_FREE(source);
-    VIR_FREE(target);
-    VIR_FREE(volumes);
-    VIR_FREE(volume_tokens);
-    VIR_FREE(def);
-    VIR_FREE(vol_nodes);
-    if (vol)
-        virStorageVolFree(vol);
-    xmlFreeDoc(doc);
-    xmlXPathFreeContext(ctxt);
-    virDomainFree(dom);
+    if (iface)
+        virInterfaceFree(iface);
+    if (iface_edited)
+        virInterfaceFree(iface_edited);
+
     return ret;
 }
 
-
 /*
- * "start" command
+ * "net-list" command
  */
-static const vshCmdInfo info_start[] = {
-    {"help", N_("start a (previously defined) inactive domain")},
-    {"desc", N_("Start a domain, either from the last managedsave\n"
-                "    state, or via a fresh boot if no managedsave state\n"
-                "    is present.")},
+static const vshCmdInfo info_network_list[] = {
+    {"help", N_("list networks")},
+    {"desc", N_("Returns list of networks.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_start[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("name of the inactive domain")},
-#ifndef WIN32
-    {"console", VSH_OT_BOOL, 0, N_("attach to console after creation")},
-#endif
-    {"paused", VSH_OT_BOOL, 0, N_("leave the guest paused after creation")},
-    {"autodestroy", VSH_OT_BOOL, 0,
-     N_("automatically destroy the guest when virsh disconnects")},
-    {"bypass-cache", VSH_OT_BOOL, 0,
-     N_("avoid file system cache when loading")},
-    {"force-boot", VSH_OT_BOOL, 0,
-     N_("force fresh boot by discarding any managed save")},
+static const vshCmdOptDef opts_network_list[] = {
+    {"inactive", VSH_OT_BOOL, 0, N_("list inactive networks")},
+    {"all", VSH_OT_BOOL, 0, N_("list inactive & active networks")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdStart(vshControl *ctl, const vshCmd *cmd)
+cmdNetworkList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
 {
-    virDomainPtr dom;
-    bool ret = false;
-#ifndef WIN32
-    bool console = vshCommandOptBool(cmd, "console");
-#endif
-    unsigned int flags = VIR_DOMAIN_NONE;
-    int rc;
+    bool inactive = vshCommandOptBool(cmd, "inactive");
+    bool all = vshCommandOptBool(cmd, "all");
+    bool active = !inactive || all;
+    int maxactive = 0, maxinactive = 0, i;
+    char **activeNames = NULL, **inactiveNames = NULL;
+    inactive |= all;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (!(dom = vshCommandOptDomainBy(ctl, cmd, NULL,
-                                      VSH_BYNAME | VSH_BYUUID)))
-        return false;
+    if (active) {
+        maxactive = virConnectNumOfNetworks(ctl->conn);
+        if (maxactive < 0) {
+            vshError(ctl, "%s", _("Failed to list active networks"));
+            return false;
+        }
+        if (maxactive) {
+            activeNames = vshMalloc(ctl, sizeof(char *) * maxactive);
 
-    if (virDomainGetID(dom) != (unsigned int)-1) {
-        vshError(ctl, "%s", _("Domain is already active"));
-        virDomainFree(dom);
-        return false;
-    }
+            if ((maxactive = virConnectListNetworks(ctl->conn, activeNames,
+                                                    maxactive)) < 0) {
+                vshError(ctl, "%s", _("Failed to list active networks"));
+                VIR_FREE(activeNames);
+                return false;
+            }
 
-    if (vshCommandOptBool(cmd, "paused"))
-        flags |= VIR_DOMAIN_START_PAUSED;
-    if (vshCommandOptBool(cmd, "autodestroy"))
-        flags |= VIR_DOMAIN_START_AUTODESTROY;
-    if (vshCommandOptBool(cmd, "bypass-cache"))
-        flags |= VIR_DOMAIN_START_BYPASS_CACHE;
-    if (vshCommandOptBool(cmd, "force-boot"))
-        flags |= VIR_DOMAIN_START_FORCE_BOOT;
-
-    /* We can emulate force boot, even for older servers that reject it.  */
-    if (flags & VIR_DOMAIN_START_FORCE_BOOT) {
-        if (virDomainCreateWithFlags(dom, flags) == 0)
-            goto started;
-        if (last_error->code != VIR_ERR_NO_SUPPORT &&
-            last_error->code != VIR_ERR_INVALID_ARG) {
-            virshReportError(ctl);
-            goto cleanup;
+            qsort(&activeNames[0], maxactive, sizeof(char *), vshNameSorter);
         }
-        virFreeError(last_error);
-        last_error = NULL;
-        rc = virDomainHasManagedSaveImage(dom, 0);
-        if (rc < 0) {
-            /* No managed save image to remove */
-            virFreeError(last_error);
-            last_error = NULL;
-        } else if (rc > 0) {
-            if (virDomainManagedSaveRemove(dom, 0) < 0) {
-                virshReportError(ctl);
-                goto cleanup;
+    }
+    if (inactive) {
+        maxinactive = virConnectNumOfDefinedNetworks(ctl->conn);
+        if (maxinactive < 0) {
+            vshError(ctl, "%s", _("Failed to list inactive networks"));
+            VIR_FREE(activeNames);
+            return false;
+        }
+        if (maxinactive) {
+            inactiveNames = vshMalloc(ctl, sizeof(char *) * maxinactive);
+
+            if ((maxinactive =
+                     virConnectListDefinedNetworks(ctl->conn, inactiveNames,
+                                                   maxinactive)) < 0) {
+                vshError(ctl, "%s", _("Failed to list inactive networks"));
+                VIR_FREE(activeNames);
+                VIR_FREE(inactiveNames);
+                return false;
             }
+
+            qsort(&inactiveNames[0], maxinactive, sizeof(char*), vshNameSorter);
         }
-        flags &= ~VIR_DOMAIN_START_FORCE_BOOT;
     }
+    vshPrintExtra(ctl, "%-20s %-10s %s\n", _("Name"), _("State"),
+                  _("Autostart"));
+    vshPrintExtra(ctl, "-----------------------------------------\n");
 
-    /* Prefer older API unless we have to pass a flag.  */
-    if ((flags ? virDomainCreateWithFlags(dom, flags)
-         : virDomainCreate(dom)) < 0) {
-        vshError(ctl, _("Failed to start domain %s"), virDomainGetName(dom));
-        goto cleanup;
-    }
+    for (i = 0; i < maxactive; i++) {
+        virNetworkPtr network =
+            virNetworkLookupByName(ctl->conn, activeNames[i]);
+        const char *autostartStr;
+        int autostart = 0;
 
-started:
-    vshPrint(ctl, _("Domain %s started\n"),
-             virDomainGetName(dom));
-#ifndef WIN32
-    if (console && !cmdRunConsole(ctl, dom, NULL, 0))
-        goto cleanup;
-#endif
+        /* this kind of work with networks is not atomic operation */
+        if (!network) {
+            VIR_FREE(activeNames[i]);
+            continue;
+        }
 
-    ret = true;
+        if (virNetworkGetAutostart(network, &autostart) < 0)
+            autostartStr = _("no autostart");
+        else
+            autostartStr = autostart ? _("yes") : _("no");
 
-cleanup:
-    virDomainFree(dom);
-    return ret;
+        vshPrint(ctl, "%-20s %-10s %-10s\n",
+                 virNetworkGetName(network),
+                 _("active"),
+                 autostartStr);
+        virNetworkFree(network);
+        VIR_FREE(activeNames[i]);
+    }
+    for (i = 0; i < maxinactive; i++) {
+        virNetworkPtr network = virNetworkLookupByName(ctl->conn, inactiveNames[i]);
+        const char *autostartStr;
+        int autostart = 0;
+
+        /* this kind of work with networks is not atomic operation */
+        if (!network) {
+            VIR_FREE(inactiveNames[i]);
+            continue;
+        }
+
+        if (virNetworkGetAutostart(network, &autostart) < 0)
+            autostartStr = _("no autostart");
+        else
+            autostartStr = autostart ? _("yes") : _("no");
+
+        vshPrint(ctl, "%-20s %-10s %-10s\n",
+                 inactiveNames[i],
+                 _("inactive"),
+                 autostartStr);
+
+        virNetworkFree(network);
+        VIR_FREE(inactiveNames[i]);
+    }
+    VIR_FREE(activeNames);
+    VIR_FREE(inactiveNames);
+    return true;
 }
 
+
 /*
- * "save" command
+ * "net-name" command
  */
-static const vshCmdInfo info_save[] = {
-    {"help", N_("save a domain state to a file")},
-    {"desc", N_("Save the RAM state of a running domain.")},
+static const vshCmdInfo info_network_name[] = {
+    {"help", N_("convert a network UUID to network name")},
+    {"desc", ""},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_save[] = {
-    {"bypass-cache", VSH_OT_BOOL, 0, N_("avoid file system cache when saving")},
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("where to save the data")},
-    {"xml", VSH_OT_STRING, 0,
-     N_("filename containing updated XML for the target")},
-    {"running", VSH_OT_BOOL, 0, N_("set domain to be running on restore")},
-    {"paused", VSH_OT_BOOL, 0, N_("set domain to be paused on restore")},
-    {"verbose", VSH_OT_BOOL, 0, N_("display the progress of save")},
+static const vshCmdOptDef opts_network_name[] = {
+    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network uuid")},
     {NULL, 0, 0, NULL}
 };
 
-static void
-doSave(void *opaque)
-{
-    vshCtrlData *data = opaque;
-    vshControl *ctl = data->ctl;
-    const vshCmd *cmd = data->cmd;
-    char ret = '1';
-    virDomainPtr dom = NULL;
-    const char *name = NULL;
-    const char *to = NULL;
-    unsigned int flags = 0;
-    const char *xmlfile = NULL;
-    char *xml = NULL;
-    sigset_t sigmask, oldsigmask;
-
-    sigemptyset(&sigmask);
-    sigaddset(&sigmask, SIGINT);
-    if (pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask) < 0)
-        goto out_sig;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        goto out;
-
-    if (vshCommandOptString(cmd, "file", &to) <= 0)
-        goto out;
-
-    if (vshCommandOptBool(cmd, "bypass-cache"))
-        flags |= VIR_DOMAIN_SAVE_BYPASS_CACHE;
-    if (vshCommandOptBool(cmd, "running"))
-        flags |= VIR_DOMAIN_SAVE_RUNNING;
-    if (vshCommandOptBool(cmd, "paused"))
-        flags |= VIR_DOMAIN_SAVE_PAUSED;
-
-    if (vshCommandOptString(cmd, "xml", &xmlfile) < 0) {
-        vshError(ctl, "%s", _("malformed xml argument"));
-        goto out;
-    }
-
-    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
-        goto out;
-
-    if (xmlfile &&
-        virFileReadAll(xmlfile, 8192, &xml) < 0)
-        goto out;
-
-    if (((flags || xml)
-         ? virDomainSaveFlags(dom, to, xml, flags)
-         : virDomainSave(dom, to)) < 0) {
-        vshError(ctl, _("Failed to save domain %s to %s"), name, to);
-        goto out;
-    }
-
-    ret = '0';
-
-out:
-    pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
-out_sig:
-    if (dom) virDomainFree(dom);
-    VIR_FREE(xml);
-    ignore_value(safewrite(data->writefd, &ret, sizeof(ret)));
-}
-
 static bool
-cmdSave(vshControl *ctl, const vshCmd *cmd)
+cmdNetworkName(vshControl *ctl, const vshCmd *cmd)
 {
-    bool ret = false;
-    virDomainPtr dom = NULL;
-    int p[2] = {-1. -1};
-    virThread workerThread;
-    bool verbose = false;
-    vshCtrlData data;
-    const char *to = NULL;
-    const char *name = NULL;
+    virNetworkPtr network;
 
-    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+    if (!(network = vshCommandOptNetworkBy(ctl, cmd, NULL,
+                                           VSH_BYUUID)))
         return false;
 
-    if (vshCommandOptString(cmd, "file", &to) <= 0)
-        goto cleanup;
-
-    if (vshCommandOptBool(cmd, "verbose"))
-        verbose = true;
-
-    if (pipe(p) < 0)
-        goto cleanup;
-
-    data.ctl = ctl;
-    data.cmd = cmd;
-    data.writefd = p[1];
-
-    if (virThreadCreate(&workerThread,
-                        true,
-                        doSave,
-                        &data) < 0)
-        goto cleanup;
-
-    ret = vshWatchJob(ctl, dom, verbose, p[0], 0, NULL, NULL, _("Save"));
-
-    virThreadJoin(&workerThread);
-
-    if (ret)
-        vshPrint(ctl, _("\nDomain %s saved to %s\n"), name, to);
-
-cleanup:
-    if (dom)
-        virDomainFree(dom);
-    return ret;
+    vshPrint(ctl, "%s\n", virNetworkGetName(network));
+    virNetworkFree(network);
+    return true;
 }
 
+
 /*
- * "save-image-dumpxml" command
+ * "net-start" command
  */
-static const vshCmdInfo info_save_image_dumpxml[] = {
-    {"help", N_("saved state domain information in XML")},
-    {"desc", N_("Output the domain information for a saved state file,\n"
-                "as an XML dump to stdout.")},
+static const vshCmdInfo info_network_start[] = {
+    {"help", N_("start a (previously defined) inactive network")},
+    {"desc", N_("Start a network.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_save_image_dumpxml[] = {
-    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("saved state file to read")},
-    {"security-info", VSH_OT_BOOL, 0, N_("include security sensitive information in XML dump")},
+static const vshCmdOptDef opts_network_start[] = {
+    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdSaveImageDumpxml(vshControl *ctl, const vshCmd *cmd)
+cmdNetworkStart(vshControl *ctl, const vshCmd *cmd)
 {
-    const char *file = NULL;
-    bool ret = false;
-    unsigned int flags = 0;
-    char *xml = NULL;
-
-    if (vshCommandOptBool(cmd, "security-info"))
-        flags |= VIR_DOMAIN_XML_SECURE;
+    virNetworkPtr network;
+    bool ret = true;
+    const char *name = NULL;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (vshCommandOptString(cmd, "file", &file) <= 0)
-        return false;
-
-    xml = virDomainSaveImageGetXMLDesc(ctl->conn, file, flags);
-    if (!xml)
-        goto cleanup;
-
-    vshPrint(ctl, "%s", xml);
-    ret = true;
+    if (!(network = vshCommandOptNetwork(ctl, cmd, &name)))
+         return false;
 
-cleanup:
-    VIR_FREE(xml);
+    if (virNetworkCreate(network) == 0) {
+        vshPrint(ctl, _("Network %s started\n"), name);
+    } else {
+        vshError(ctl, _("Failed to start network %s"), name);
+        ret = false;
+    }
+    virNetworkFree(network);
     return ret;
 }
 
+
 /*
- * "save-image-define" command
+ * "net-undefine" command
  */
-static const vshCmdInfo info_save_image_define[] = {
-    {"help", N_("redefine the XML for a domain's saved state file")},
-    {"desc", N_("Replace the domain XML associated with a saved state file")},
+static const vshCmdInfo info_network_undefine[] = {
+    {"help", N_("undefine an inactive network")},
+    {"desc", N_("Undefine the configuration for an inactive network.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_save_image_define[] = {
-    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("saved state file to modify")},
-    {"xml", VSH_OT_STRING, VSH_OFLAG_REQ,
-     N_("filename containing updated XML for the target")},
-    {"running", VSH_OT_BOOL, 0, N_("set domain to be running on restore")},
-    {"paused", VSH_OT_BOOL, 0, N_("set domain to be paused on restore")},
+static const vshCmdOptDef opts_network_undefine[] = {
+    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdSaveImageDefine(vshControl *ctl, const vshCmd *cmd)
+cmdNetworkUndefine(vshControl *ctl, const vshCmd *cmd)
 {
-    const char *file = NULL;
-    bool ret = false;
-    const char *xmlfile = NULL;
-    char *xml = NULL;
-    unsigned int flags = 0;
-
-    if (vshCommandOptBool(cmd, "running"))
-        flags |= VIR_DOMAIN_SAVE_RUNNING;
-    if (vshCommandOptBool(cmd, "paused"))
-        flags |= VIR_DOMAIN_SAVE_PAUSED;
+    virNetworkPtr network;
+    bool ret = true;
+    const char *name;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (vshCommandOptString(cmd, "file", &file) <= 0)
-        return false;
-
-    if (vshCommandOptString(cmd, "xml", &xmlfile) <= 0) {
-        vshError(ctl, "%s", _("malformed or missing xml argument"));
+    if (!(network = vshCommandOptNetwork(ctl, cmd, &name)))
         return false;
-    }
 
-    if (virFileReadAll(xmlfile, 8192, &xml) < 0)
-        goto cleanup;
-
-    if (virDomainSaveImageDefineXML(ctl->conn, file, xml, flags) < 0) {
-        vshError(ctl, _("Failed to update %s"), file);
-        goto cleanup;
+    if (virNetworkUndefine(network) == 0) {
+        vshPrint(ctl, _("Network %s has been undefined\n"), name);
+    } else {
+        vshError(ctl, _("Failed to undefine network %s"), name);
+        ret = false;
     }
 
-    vshPrint(ctl, _("State file %s updated.\n"), file);
-    ret = true;
-
-cleanup:
-    VIR_FREE(xml);
+    virNetworkFree(network);
     return ret;
 }
 
+
 /*
- * "save-image-edit" command
+ * "net-uuid" command
  */
-static const vshCmdInfo info_save_image_edit[] = {
-    {"help", N_("edit XML for a domain's saved state file")},
-    {"desc", N_("Edit the domain XML associated with a saved state file")},
+static const vshCmdInfo info_network_uuid[] = {
+    {"help", N_("convert a network name to network UUID")},
+    {"desc", ""},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_save_image_edit[] = {
-    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("saved state file to edit")},
-    {"running", VSH_OT_BOOL, 0, N_("set domain to be running on restore")},
-    {"paused", VSH_OT_BOOL, 0, N_("set domain to be paused on restore")},
+static const vshCmdOptDef opts_network_uuid[] = {
+    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdSaveImageEdit(vshControl *ctl, const vshCmd *cmd)
+cmdNetworkUuid(vshControl *ctl, const vshCmd *cmd)
 {
-    const char *file = NULL;
-    bool ret = false;
-    unsigned int getxml_flags = VIR_DOMAIN_XML_SECURE;
-    unsigned int define_flags = 0;
-
-    if (vshCommandOptBool(cmd, "running"))
-        define_flags |= VIR_DOMAIN_SAVE_RUNNING;
-    if (vshCommandOptBool(cmd, "paused"))
-        define_flags |= VIR_DOMAIN_SAVE_PAUSED;
-
-    /* Normally, we let the API reject mutually exclusive flags.
-     * However, in the edit cycle, we let the user retry if the define
-     * step fails, but the define step will always fail on invalid
-     * flags, so we reject it up front to avoid looping.  */
-    if (define_flags == (VIR_DOMAIN_SAVE_RUNNING | VIR_DOMAIN_SAVE_PAUSED)) {
-        vshError(ctl, "%s", _("--running and --saved are mutually exclusive"));
-        return false;
-    }
+    virNetworkPtr network;
+    char uuid[VIR_UUID_STRING_BUFLEN];
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (vshCommandOptString(cmd, "file", &file) <= 0)
+    if (!(network = vshCommandOptNetworkBy(ctl, cmd, NULL,
+                                           VSH_BYNAME)))
         return false;
 
-#define EDIT_GET_XML \
-    virDomainSaveImageGetXMLDesc(ctl->conn, file, getxml_flags)
-#define EDIT_NOT_CHANGED \
-    vshPrint(ctl, _("Saved image %s XML configuration " \
-                    "not changed.\n"), file);           \
-    ret = true; goto edit_cleanup;
-#define EDIT_DEFINE \
-    virDomainSaveImageDefineXML(ctl->conn, file, doc_edited, define_flags)
-#define EDIT_FREE /* */
-#include "virsh-edit.c"
-
-    vshPrint(ctl, _("State file %s edited.\n"), file);
-    ret = true;
+    if (virNetworkGetUUIDString(network, uuid) != -1)
+        vshPrint(ctl, "%s\n", uuid);
+    else
+        vshError(ctl, "%s", _("failed to get network UUID"));
 
-cleanup:
-    return ret;
+    virNetworkFree(network);
+    return true;
 }
 
+
+/**************************************************************************/
 /*
- * "managedsave" command
+ * "iface-list" command
  */
-static const vshCmdInfo info_managedsave[] = {
-    {"help", N_("managed save of a domain state")},
-    {"desc", N_("Save and destroy a running domain, so it can be restarted from\n"
-                "    the same state at a later time.  When the virsh 'start'\n"
-                "    command is next run for the domain, it will automatically\n"
-                "    be started from this saved state.")},
+static const vshCmdInfo info_interface_list[] = {
+    {"help", N_("list physical host interfaces")},
+    {"desc", N_("Returns list of physical host interfaces.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_managedsave[] = {
-    {"bypass-cache", VSH_OT_BOOL, 0, N_("avoid file system cache when saving")},
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"running", VSH_OT_BOOL, 0, N_("set domain to be running on next start")},
-    {"paused", VSH_OT_BOOL, 0, N_("set domain to be paused on next start")},
-    {"verbose", VSH_OT_BOOL, 0, N_("display the progress of save")},
+static const vshCmdOptDef opts_interface_list[] = {
+    {"inactive", VSH_OT_BOOL, 0, N_("list inactive interfaces")},
+    {"all", VSH_OT_BOOL, 0, N_("list inactive & active interfaces")},
     {NULL, 0, 0, NULL}
 };
-
-static void
-doManagedsave(void *opaque)
+static bool
+cmdInterfaceList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
 {
-    char ret = '1';
-    vshCtrlData *data = opaque;
-    vshControl *ctl = data->ctl;
-    const vshCmd *cmd = data->cmd;
-    virDomainPtr dom = NULL;
-    const char *name;
-    unsigned int flags = 0;
-    sigset_t sigmask, oldsigmask;
-
-    sigemptyset(&sigmask);
-    sigaddset(&sigmask, SIGINT);
-    if (pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask) < 0)
-        goto out_sig;
+    bool inactive = vshCommandOptBool(cmd, "inactive");
+    bool all = vshCommandOptBool(cmd, "all");
+    bool active = !inactive || all;
+    int maxactive = 0, maxinactive = 0, i;
+    char **activeNames = NULL, **inactiveNames = NULL;
+    inactive |= all;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
-        goto out;
+        return false;
 
-    if (vshCommandOptBool(cmd, "bypass-cache"))
-        flags |= VIR_DOMAIN_SAVE_BYPASS_CACHE;
-    if (vshCommandOptBool(cmd, "running"))
-        flags |= VIR_DOMAIN_SAVE_RUNNING;
-    if (vshCommandOptBool(cmd, "paused"))
-        flags |= VIR_DOMAIN_SAVE_PAUSED;
+    if (active) {
+        maxactive = virConnectNumOfInterfaces(ctl->conn);
+        if (maxactive < 0) {
+            vshError(ctl, "%s", _("Failed to list active interfaces"));
+            return false;
+        }
+        if (maxactive) {
+            activeNames = vshMalloc(ctl, sizeof(char *) * maxactive);
 
-    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
-        goto out;
+            if ((maxactive = virConnectListInterfaces(ctl->conn, activeNames,
+                                                    maxactive)) < 0) {
+                vshError(ctl, "%s", _("Failed to list active interfaces"));
+                VIR_FREE(activeNames);
+                return false;
+            }
 
-    if (virDomainManagedSave(dom, flags) < 0) {
-        vshError(ctl, _("Failed to save domain %s state"), name);
-        goto out;
+            qsort(&activeNames[0], maxactive, sizeof(char *), vshNameSorter);
+        }
     }
+    if (inactive) {
+        maxinactive = virConnectNumOfDefinedInterfaces(ctl->conn);
+        if (maxinactive < 0) {
+            vshError(ctl, "%s", _("Failed to list inactive interfaces"));
+            VIR_FREE(activeNames);
+            return false;
+        }
+        if (maxinactive) {
+            inactiveNames = vshMalloc(ctl, sizeof(char *) * maxinactive);
 
-    ret = '0';
-out:
-    pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
-out_sig:
-    if (dom)
-        virDomainFree(dom);
-    ignore_value(safewrite(data->writefd, &ret, sizeof(ret)));
-}
-
-static bool
-cmdManagedSave(vshControl *ctl, const vshCmd *cmd)
-{
-    virDomainPtr dom;
-    int p[2] = { -1, -1};
-    bool ret = false;
-    bool verbose = false;
-    const char *name = NULL;
-    vshCtrlData data;
-    virThread workerThread;
-
-    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
-        return false;
-
-    if (vshCommandOptBool(cmd, "verbose"))
-        verbose = true;
+            if ((maxinactive =
+                     virConnectListDefinedInterfaces(ctl->conn, inactiveNames,
+                                                     maxinactive)) < 0) {
+                vshError(ctl, "%s", _("Failed to list inactive interfaces"));
+                VIR_FREE(activeNames);
+                VIR_FREE(inactiveNames);
+                return false;
+            }
 
-    if (pipe(p) < 0)
-        goto cleanup;
+            qsort(&inactiveNames[0], maxinactive, sizeof(char*), vshNameSorter);
+        }
+    }
+    vshPrintExtra(ctl, "%-20s %-10s %s\n", _("Name"), _("State"),
+                  _("MAC Address"));
+    vshPrintExtra(ctl, "--------------------------------------------\n");
 
-    data.ctl = ctl;
-    data.cmd = cmd;
-    data.writefd = p[1];
+    for (i = 0; i < maxactive; i++) {
+        virInterfacePtr iface =
+            virInterfaceLookupByName(ctl->conn, activeNames[i]);
 
-    if (virThreadCreate(&workerThread,
-                        true,
-                        doManagedsave,
-                        &data) < 0)
-        goto cleanup;
+        /* this kind of work with interfaces is not atomic */
+        if (!iface) {
+            VIR_FREE(activeNames[i]);
+            continue;
+        }
 
-    ret = vshWatchJob(ctl, dom, verbose, p[0], 0,
-                      NULL, NULL, _("Managedsave"));
+        vshPrint(ctl, "%-20s %-10s %s\n",
+                 virInterfaceGetName(iface),
+                 _("active"),
+                 virInterfaceGetMACString(iface));
+        virInterfaceFree(iface);
+        VIR_FREE(activeNames[i]);
+    }
+    for (i = 0; i < maxinactive; i++) {
+        virInterfacePtr iface =
+            virInterfaceLookupByName(ctl->conn, inactiveNames[i]);
 
-    virThreadJoin(&workerThread);
+        /* this kind of work with interfaces is not atomic */
+        if (!iface) {
+            VIR_FREE(inactiveNames[i]);
+            continue;
+        }
 
-    if (ret)
-        vshPrint(ctl, _("\nDomain %s state saved by libvirt\n"), name);
+        vshPrint(ctl, "%-20s %-10s %s\n",
+                 virInterfaceGetName(iface),
+                 _("inactive"),
+                 virInterfaceGetMACString(iface));
+        virInterfaceFree(iface);
+        VIR_FREE(inactiveNames[i]);
+    }
+    VIR_FREE(activeNames);
+    VIR_FREE(inactiveNames);
+    return true;
 
-cleanup:
-    virDomainFree(dom);
-    VIR_FORCE_CLOSE(p[0]);
-    VIR_FORCE_CLOSE(p[1]);
-    return ret;
 }
 
 /*
- * "managedsave-remove" command
+ * "iface-name" command
  */
-static const vshCmdInfo info_managedsaveremove[] = {
-    {"help", N_("Remove managed save of a domain")},
-    {"desc", N_("Remove an existing managed save state file from a domain")},
+static const vshCmdInfo info_interface_name[] = {
+    {"help", N_("convert an interface MAC address to interface name")},
+    {"desc", ""},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_managedsaveremove[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+static const vshCmdOptDef opts_interface_name[] = {
+    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface mac")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdManagedSaveRemove(vshControl *ctl, const vshCmd *cmd)
+cmdInterfaceName(vshControl *ctl, const vshCmd *cmd)
 {
-    virDomainPtr dom;
-    const char *name;
-    bool ret = false;
-    int hassave;
+    virInterfacePtr iface;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
-
-    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+    if (!(iface = vshCommandOptInterfaceBy(ctl, cmd, NULL, NULL,
+                                           VSH_BYMAC)))
         return false;
 
-    hassave = virDomainHasManagedSaveImage(dom, 0);
-    if (hassave < 0) {
-        vshError(ctl, "%s", _("Failed to check for domain managed save image"));
-        goto cleanup;
-    }
-
-    if (hassave) {
-        if (virDomainManagedSaveRemove(dom, 0) < 0) {
-            vshError(ctl, _("Failed to remove managed save image for domain %s"),
-                     name);
-            goto cleanup;
-        }
-        else
-            vshPrint(ctl, _("Removed managedsave image for domain %s"), name);
-    }
-    else
-        vshPrint(ctl, _("Domain %s has no manage save image; removal skipped"),
-                 name);
-
-    ret = true;
-
-cleanup:
-    virDomainFree(dom);
-    return ret;
+    vshPrint(ctl, "%s\n", virInterfaceGetName(iface));
+    virInterfaceFree(iface);
+    return true;
 }
 
 /*
- * "schedinfo" command
+ * "iface-mac" command
  */
-static const vshCmdInfo info_schedinfo[] = {
-    {"help", N_("show/set scheduler parameters")},
-    {"desc", N_("Show/Set scheduler parameters.")},
+static const vshCmdInfo info_interface_mac[] = {
+    {"help", N_("convert an interface name to interface MAC address")},
+    {"desc", ""},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_schedinfo[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"set", VSH_OT_STRING, VSH_OFLAG_NONE, N_("parameter=value")},
-    {"weight", VSH_OT_INT, VSH_OFLAG_NONE, N_("weight for XEN_CREDIT")},
-    {"cap", VSH_OT_INT, VSH_OFLAG_NONE, N_("cap for XEN_CREDIT")},
-    {"current", VSH_OT_BOOL, 0, N_("get/set current scheduler info")},
-    {"config", VSH_OT_BOOL, 0, N_("get/set value to be used on next boot")},
-    {"live", VSH_OT_BOOL, 0, N_("get/set value from running domain")},
+static const vshCmdOptDef opts_interface_mac[] = {
+    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name")},
     {NULL, 0, 0, NULL}
 };
 
-static int
-cmdSchedInfoUpdate(vshControl *ctl, const vshCmd *cmd,
-                   virTypedParameterPtr param)
+static bool
+cmdInterfaceMAC(vshControl *ctl, const vshCmd *cmd)
 {
-    const char *data = NULL;
-
-    /* Legacy 'weight' parameter */
-    if (STREQ(param->field, "weight") &&
-        param->type == VIR_TYPED_PARAM_UINT &&
-        vshCommandOptBool(cmd, "weight")) {
-        int val;
-        if (vshCommandOptInt(cmd, "weight", &val) <= 0) {
-            vshError(ctl, "%s", _("Invalid value of weight"));
-            return -1;
-        } else {
-            param->value.ui = val;
-        }
-        return 1;
-    }
-
-    /* Legacy 'cap' parameter */
-    if (STREQ(param->field, "cap") &&
-        param->type == VIR_TYPED_PARAM_UINT &&
-        vshCommandOptBool(cmd, "cap")) {
-        int val;
-        if (vshCommandOptInt(cmd, "cap", &val) <= 0) {
-            vshError(ctl, "%s", _("Invalid value of cap"));
-            return -1;
-        } else {
-            param->value.ui = val;
-        }
-        return 1;
-    }
-
-    if (vshCommandOptString(cmd, "set", &data) > 0) {
-        char *val = strchr(data, '=');
-        int match = 0;
-        if (!val) {
-            vshError(ctl, "%s", _("Invalid syntax for --set, expecting name=value"));
-            return -1;
-        }
-        *val = '\0';
-        match = STREQ(data, param->field);
-        *val = '=';
-        val++;
-
-        if (!match)
-            return 0;
+    virInterfacePtr iface;
 
-        switch (param->type) {
-        case VIR_TYPED_PARAM_INT:
-            if (virStrToLong_i(val, NULL, 10, &param->value.i) < 0) {
-                vshError(ctl, "%s",
-                         _("Invalid value for parameter, expecting an int"));
-                return -1;
-            }
-            break;
-        case VIR_TYPED_PARAM_UINT:
-            if (virStrToLong_ui(val, NULL, 10, &param->value.ui) < 0) {
-                vshError(ctl, "%s",
-                         _("Invalid value for parameter, expecting an unsigned int"));
-                return -1;
-            }
-            break;
-        case VIR_TYPED_PARAM_LLONG:
-            if (virStrToLong_ll(val, NULL, 10, &param->value.l) < 0) {
-                vshError(ctl, "%s",
-                         _("Invalid value for parameter, expecting a long long"));
-                return -1;
-            }
-            break;
-        case VIR_TYPED_PARAM_ULLONG:
-            if (virStrToLong_ull(val, NULL, 10, &param->value.ul) < 0) {
-                vshError(ctl, "%s",
-                         _("Invalid value for parameter, expecting an unsigned long long"));
-                return -1;
-            }
-            break;
-        case VIR_TYPED_PARAM_DOUBLE:
-            if (virStrToDouble(val, NULL, &param->value.d) < 0) {
-                vshError(ctl, "%s", _("Invalid value for parameter, expecting a double"));
-                return -1;
-            }
-            break;
-        case VIR_TYPED_PARAM_BOOLEAN:
-            param->value.b = STREQ(val, "0") ? 0 : 1;
-        }
-        return 1;
-    }
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+    if (!(iface = vshCommandOptInterfaceBy(ctl, cmd, NULL, NULL,
+                                           VSH_BYNAME)))
+        return false;
 
-    return 0;
+    vshPrint(ctl, "%s\n", virInterfaceGetMACString(iface));
+    virInterfaceFree(iface);
+    return true;
 }
 
+/*
+ * "iface-dumpxml" command
+ */
+static const vshCmdInfo info_interface_dumpxml[] = {
+    {"help", N_("interface information in XML")},
+    {"desc", N_("Output the physical host interface information as an XML dump to stdout.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_interface_dumpxml[] = {
+    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name or MAC address")},
+    {"inactive", VSH_OT_BOOL, 0, N_("show inactive defined XML")},
+    {NULL, 0, 0, NULL}
+};
 
 static bool
-cmdSchedinfo(vshControl *ctl, const vshCmd *cmd)
+cmdInterfaceDumpXML(vshControl *ctl, const vshCmd *cmd)
 {
-    char *schedulertype;
-    virDomainPtr dom;
-    virTypedParameterPtr params = NULL;
-    int nparams = 0;
-    int update = 0;
-    int i, ret;
-    bool ret_val = false;
+    virInterfacePtr iface;
+    bool ret = true;
+    char *dump;
     unsigned int flags = 0;
-    bool current = vshCommandOptBool(cmd, "current");
-    bool config = vshCommandOptBool(cmd, "config");
-    bool live = vshCommandOptBool(cmd, "live");
+    bool inactive = vshCommandOptBool(cmd, "inactive");
 
-    if (current) {
-        if (live || config) {
-            vshError(ctl, "%s", _("--current must be specified exclusively"));
-            return false;
-        }
-        flags = VIR_DOMAIN_AFFECT_CURRENT;
-    } else {
-        if (config)
-            flags |= VIR_DOMAIN_AFFECT_CONFIG;
-        if (live)
-            flags |= VIR_DOMAIN_AFFECT_LIVE;
-    }
+    if (inactive)
+        flags |= VIR_INTERFACE_XML_INACTIVE;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+    if (!(iface = vshCommandOptInterface(ctl, cmd, NULL)))
         return false;
 
-    /* Print SchedulerType */
-    schedulertype = virDomainGetSchedulerType(dom, &nparams);
-    if (schedulertype != NULL) {
-        vshPrint(ctl, "%-15s: %s\n", _("Scheduler"),
-             schedulertype);
-        VIR_FREE(schedulertype);
+    dump = virInterfaceGetXMLDesc(iface, flags);
+    if (dump != NULL) {
+        vshPrint(ctl, "%s", dump);
+        VIR_FREE(dump);
     } else {
-        vshPrint(ctl, "%-15s: %s\n", _("Scheduler"), _("Unknown"));
-        goto cleanup;
-    }
-
-    if (nparams) {
-        params = vshMalloc(ctl, sizeof(*params) * nparams);
-
-        memset(params, 0, sizeof(*params) * nparams);
-        if (flags || current) {
-            /* We cannot query both live and config at once, so settle
-               on current in that case.  If we are setting, then the
-               two values should match when we re-query; otherwise, we
-               report the error later.  */
-            ret = virDomainGetSchedulerParametersFlags(dom, params, &nparams,
-                                                       ((live && config) ? 0
-                                                        : flags));
-        } else {
-            ret = virDomainGetSchedulerParameters(dom, params, &nparams);
-        }
-        if (ret == -1)
-            goto cleanup;
-
-        /* See if any params are being set */
-        for (i = 0; i < nparams; i++) {
-            ret = cmdSchedInfoUpdate(ctl, cmd, &(params[i]));
-            if (ret == -1)
-                goto cleanup;
-
-            if (ret == 1)
-                update = 1;
-        }
-
-        /* Update parameters & refresh data */
-        if (update) {
-            if (flags || current)
-                ret = virDomainSetSchedulerParametersFlags(dom, params,
-                                                           nparams, flags);
-            else
-                ret = virDomainSetSchedulerParameters(dom, params, nparams);
-            if (ret == -1)
-                goto cleanup;
-
-            if (flags || current)
-                ret = virDomainGetSchedulerParametersFlags(dom, params,
-                                                           &nparams,
-                                                           ((live && config) ? 0
-                                                            : flags));
-            else
-                ret = virDomainGetSchedulerParameters(dom, params, &nparams);
-            if (ret == -1)
-                goto cleanup;
-        } else {
-            /* See if we've tried to --set var=val.  If so, the fact that
-               we reach this point (with update == 0) means that "var" did
-               not match any of the settable parameters.  Report the error.  */
-            const char *var_value_pair = NULL;
-            if (vshCommandOptString(cmd, "set", &var_value_pair) > 0) {
-                vshError(ctl, _("invalid scheduler option: %s"),
-                         var_value_pair);
-                goto cleanup;
-            }
-            /* When not doing --set, --live and --config do not mix.  */
-            if (live && config) {
-                vshError(ctl, "%s",
-                         _("cannot query both live and config at once"));
-                goto cleanup;
-            }
-        }
-
-        ret_val = true;
-        for (i = 0; i < nparams; i++) {
-            char *str = vshGetTypedParamValue(ctl, &params[i]);
-            vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
-            VIR_FREE(str);
-        }
+        ret = false;
     }
 
- cleanup:
-    VIR_FREE(params);
-    virDomainFree(dom);
-    return ret_val;
+    virInterfaceFree(iface);
+    return ret;
 }
 
 /*
- * "restore" command
+ * "iface-define" command
  */
-static const vshCmdInfo info_restore[] = {
-    {"help", N_("restore a domain from a saved state in a file")},
-    {"desc", N_("Restore a domain.")},
+static const vshCmdInfo info_interface_define[] = {
+    {"help", N_("define (but don't start) a physical host interface from an XML file")},
+    {"desc", N_("Define a physical host interface.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_restore[] = {
-    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("the state to restore")},
-    {"bypass-cache", VSH_OT_BOOL, 0,
-     N_("avoid file system cache when restoring")},
-    {"xml", VSH_OT_STRING, 0,
-     N_("filename containing updated XML for the target")},
-    {"running", VSH_OT_BOOL, 0, N_("restore domain into running state")},
-    {"paused", VSH_OT_BOOL, 0, N_("restore domain into paused state")},
+static const vshCmdOptDef opts_interface_define[] = {
+    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML interface description")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdRestore(vshControl *ctl, const vshCmd *cmd)
+cmdInterfaceDefine(vshControl *ctl, const vshCmd *cmd)
 {
+    virInterfacePtr iface;
     const char *from = NULL;
-    bool ret = false;
-    unsigned int flags = 0;
-    const char *xmlfile = NULL;
-    char *xml = NULL;
+    bool ret = true;
+    char *buffer;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
@@ -2963,8395 +2148,1765 @@ cmdRestore(vshControl *ctl, const vshCmd *cmd)
     if (vshCommandOptString(cmd, "file", &from) <= 0)
         return false;
 
-    if (vshCommandOptBool(cmd, "bypass-cache"))
-        flags |= VIR_DOMAIN_SAVE_BYPASS_CACHE;
-    if (vshCommandOptBool(cmd, "running"))
-        flags |= VIR_DOMAIN_SAVE_RUNNING;
-    if (vshCommandOptBool(cmd, "paused"))
-        flags |= VIR_DOMAIN_SAVE_PAUSED;
-
-    if (vshCommandOptString(cmd, "xml", &xmlfile) < 0) {
-        vshError(ctl, "%s", _("malformed xml argument"));
+    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
         return false;
-    }
 
-    if (xmlfile &&
-        virFileReadAll(xmlfile, 8192, &xml) < 0)
-        goto cleanup;
+    iface = virInterfaceDefineXML(ctl->conn, buffer, 0);
+    VIR_FREE(buffer);
 
-    if (((flags || xml)
-         ? virDomainRestoreFlags(ctl->conn, from, xml, flags)
-         : virDomainRestore(ctl->conn, from)) < 0) {
-        vshError(ctl, _("Failed to restore domain from %s"), from);
-        goto cleanup;
+    if (iface != NULL) {
+        vshPrint(ctl, _("Interface %s defined from %s\n"),
+                 virInterfaceGetName(iface), from);
+        virInterfaceFree(iface);
+    } else {
+        vshError(ctl, _("Failed to define interface from %s"), from);
+        ret = false;
     }
-
-    vshPrint(ctl, _("Domain restored from %s\n"), from);
-    ret = true;
-
-cleanup:
-    VIR_FREE(xml);
-    return ret;
-}
+    return ret;
+}
 
 /*
- * "dump" command
+ * "iface-undefine" command
  */
-static const vshCmdInfo info_dump[] = {
-    {"help", N_("dump the core of a domain to a file for analysis")},
-    {"desc", N_("Core dump a domain.")},
+static const vshCmdInfo info_interface_undefine[] = {
+    {"help", N_("undefine a physical host interface (remove it from configuration)")},
+    {"desc", N_("undefine an interface.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_dump[] = {
-    {"live", VSH_OT_BOOL, 0, N_("perform a live core dump if supported")},
-    {"crash", VSH_OT_BOOL, 0, N_("crash the domain after core dump")},
-    {"bypass-cache", VSH_OT_BOOL, 0,
-     N_("avoid file system cache when saving")},
-    {"reset", VSH_OT_BOOL, 0, N_("reset the domain after core dump")},
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("where to dump the core")},
-    {"verbose", VSH_OT_BOOL, 0, N_("display the progress of dump")},
-    {"memory-only", VSH_OT_BOOL, 0, N_("dump domain's memory only")},
+static const vshCmdOptDef opts_interface_undefine[] = {
+    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name or MAC address")},
     {NULL, 0, 0, NULL}
 };
 
-static void
-doDump(void *opaque)
-{
-    char ret = '1';
-    vshCtrlData *data = opaque;
-    vshControl *ctl = data->ctl;
-    const vshCmd *cmd = data->cmd;
-    virDomainPtr dom = NULL;
-    sigset_t sigmask, oldsigmask;
-    const char *name = NULL;
-    const char *to = NULL;
-    unsigned int flags = 0;
-
-    sigemptyset(&sigmask);
-    sigaddset(&sigmask, SIGINT);
-    if (pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask) < 0)
-        goto out_sig;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        goto out;
-
-    if (vshCommandOptString(cmd, "file", &to) <= 0)
-        goto out;
-
-    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
-        goto out;
-
-    if (vshCommandOptBool(cmd, "live"))
-        flags |= VIR_DUMP_LIVE;
-    if (vshCommandOptBool(cmd, "crash"))
-        flags |= VIR_DUMP_CRASH;
-    if (vshCommandOptBool(cmd, "bypass-cache"))
-        flags |= VIR_DUMP_BYPASS_CACHE;
-    if (vshCommandOptBool(cmd, "reset"))
-        flags |= VIR_DUMP_RESET;
-    if (vshCommandOptBool(cmd, "memory-only"))
-        flags |= VIR_DUMP_MEMORY_ONLY;
-
-    if (virDomainCoreDump(dom, to, flags) < 0) {
-        vshError(ctl, _("Failed to core dump domain %s to %s"), name, to);
-        goto out;
-    }
-
-    ret = '0';
-out:
-    pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
-out_sig:
-    if (dom)
-        virDomainFree(dom);
-    ignore_value(safewrite(data->writefd, &ret, sizeof(ret)));
-}
-
 static bool
-cmdDump(vshControl *ctl, const vshCmd *cmd)
+cmdInterfaceUndefine(vshControl *ctl, const vshCmd *cmd)
 {
-    virDomainPtr dom;
-    int p[2] = { -1, -1};
-    bool ret = false;
-    bool verbose = false;
-    const char *name = NULL;
-    const char *to = NULL;
-    vshCtrlData data;
-    virThread workerThread;
+    virInterfacePtr iface;
+    bool ret = true;
+    const char *name;
 
-    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+    if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (vshCommandOptString(cmd, "file", &to) <= 0)
+    if (!(iface = vshCommandOptInterface(ctl, cmd, &name)))
         return false;
 
-    if (vshCommandOptBool(cmd, "verbose"))
-        verbose = true;
-
-    if (pipe(p) < 0)
-        goto cleanup;
-
-    data.ctl = ctl;
-    data.cmd = cmd;
-    data.writefd = p[1];
-
-    if (virThreadCreate(&workerThread,
-                        true,
-                        doDump,
-                        &data) < 0)
-        goto cleanup;
-
-    ret = vshWatchJob(ctl, dom, verbose, p[0], 0, NULL, NULL, _("Dump"));
-
-    virThreadJoin(&workerThread);
-
-    if (ret)
-        vshPrint(ctl, _("\nDomain %s dumped to %s\n"), name, to);
+    if (virInterfaceUndefine(iface) == 0) {
+        vshPrint(ctl, _("Interface %s undefined\n"), name);
+    } else {
+        vshError(ctl, _("Failed to undefine interface %s"), name);
+        ret = false;
+    }
 
-cleanup:
-    virDomainFree(dom);
-    VIR_FORCE_CLOSE(p[0]);
-    VIR_FORCE_CLOSE(p[1]);
+    virInterfaceFree(iface);
     return ret;
 }
 
-static const vshCmdInfo info_screenshot[] = {
-    {"help", N_("take a screenshot of a current domain console and store it "
-                "into a file")},
-    {"desc", N_("screenshot of a current domain console")},
+/*
+ * "iface-start" command
+ */
+static const vshCmdInfo info_interface_start[] = {
+    {"help", N_("start a physical host interface (enable it / \"if-up\")")},
+    {"desc", N_("start a physical host interface.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_screenshot[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"file", VSH_OT_DATA, VSH_OFLAG_NONE, N_("where to store the screenshot")},
-    {"screen", VSH_OT_INT, VSH_OFLAG_NONE, N_("ID of a screen to take screenshot of")},
+static const vshCmdOptDef opts_interface_start[] = {
+    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name or MAC address")},
     {NULL, 0, 0, NULL}
 };
 
-static int vshStreamSink(virStreamPtr st ATTRIBUTE_UNUSED,
-                         const char *bytes, size_t nbytes, void *opaque)
-{
-    int *fd = opaque;
-
-    return safewrite(*fd, bytes, nbytes);
-}
-
-/**
- * Generate string: '<domain name>-<timestamp>[<extension>]'
- */
-static char *
-vshGenFileName(vshControl *ctl, virDomainPtr dom, const char *mime)
-{
-    char timestr[100];
-    struct timeval cur_time;
-    struct tm time_info;
-    const char *ext = NULL;
-    char *ret = NULL;
-
-    /* We should be already connected, but doesn't
-     * hurt to check */
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return NULL;
-
-    if (!dom) {
-        vshError(ctl, "%s", _("Invalid domain supplied"));
-        return NULL;
-    }
-
-    if (STREQ(mime, "image/x-portable-pixmap"))
-        ext = ".ppm";
-    else if (STREQ(mime, "image/png"))
-        ext = ".png";
-    /* add mime type here */
-
-    gettimeofday(&cur_time, NULL);
-    localtime_r(&cur_time.tv_sec, &time_info);
-    strftime(timestr, sizeof(timestr), "%Y-%m-%d-%H:%M:%S", &time_info);
-
-    if (virAsprintf(&ret, "%s-%s%s", virDomainGetName(dom),
-                    timestr, ext ? ext : "") < 0) {
-        vshError(ctl, "%s", _("Out of memory"));
-        return NULL;
-    }
-
-    return ret;
-}
-
 static bool
-cmdScreenshot(vshControl *ctl, const vshCmd *cmd)
+cmdInterfaceStart(vshControl *ctl, const vshCmd *cmd)
 {
-    virDomainPtr dom;
-    const char *name = NULL;
-    char *file = NULL;
-    int fd = -1;
-    virStreamPtr st = NULL;
-    unsigned int screen = 0;
-    unsigned int flags = 0; /* currently unused */
-    int ret = false;
-    bool created = false;
-    bool generated = false;
-    char *mime = NULL;
+    virInterfacePtr iface;
+    bool ret = true;
+    const char *name;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (vshCommandOptString(cmd, "file", (const char **) &file) < 0) {
-        vshError(ctl, "%s", _("file must not be empty"));
-        return false;
-    }
-
-    if (vshCommandOptUInt(cmd, "screen", &screen) < 0) {
-        vshError(ctl, "%s", _("invalid screen ID"));
-        return false;
-    }
-
-    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+    if (!(iface = vshCommandOptInterface(ctl, cmd, &name)))
         return false;
 
-    st = virStreamNew(ctl->conn, 0);
-
-    mime = virDomainScreenshot(dom, st, screen, flags);
-    if (!mime) {
-        vshError(ctl, _("could not take a screenshot of %s"), name);
-        goto cleanup;
-    }
-
-    if (!file) {
-        if (!(file=vshGenFileName(ctl, dom, mime)))
-            return false;
-        generated = true;
-    }
-
-    if ((fd = open(file, O_WRONLY|O_CREAT|O_EXCL, 0666)) < 0) {
-        if (errno != EEXIST ||
-            (fd = open(file, O_WRONLY|O_TRUNC, 0666)) < 0) {
-            vshError(ctl, _("cannot create file %s"), file);
-            goto cleanup;
-        }
+    if (virInterfaceCreate(iface, 0) == 0) {
+        vshPrint(ctl, _("Interface %s started\n"), name);
     } else {
-        created = true;
-    }
-
-    if (virStreamRecvAll(st, vshStreamSink, &fd) < 0) {
-        vshError(ctl, _("could not receive data from domain %s"), name);
-        goto cleanup;
-    }
-
-    if (VIR_CLOSE(fd) < 0) {
-        vshError(ctl, _("cannot close file %s"), file);
-        goto cleanup;
-    }
-
-    if (virStreamFinish(st) < 0) {
-        vshError(ctl, _("cannot close stream on domain %s"), name);
-        goto cleanup;
+        vshError(ctl, _("Failed to start interface %s"), name);
+        ret = false;
     }
 
-    vshPrint(ctl, _("Screenshot saved to %s, with type of %s"), file, mime);
-    ret = true;
-
-cleanup:
-    if (!ret && created)
-        unlink(file);
-    if (generated)
-        VIR_FREE(file);
-    virDomainFree(dom);
-    if (st)
-        virStreamFree(st);
-    VIR_FORCE_CLOSE(fd);
-    VIR_FREE(mime);
+    virInterfaceFree(iface);
     return ret;
 }
 
 /*
- * "resume" command
+ * "iface-destroy" command
  */
-static const vshCmdInfo info_resume[] = {
-    {"help", N_("resume a domain")},
-    {"desc", N_("Resume a previously suspended domain.")},
+static const vshCmdInfo info_interface_destroy[] = {
+    {"help", N_("destroy a physical host interface (disable it / \"if-down\")")},
+    {"desc", N_("forcefully stop a physical host interface.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_resume[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+static const vshCmdOptDef opts_interface_destroy[] = {
+    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name or MAC address")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdResume(vshControl *ctl, const vshCmd *cmd)
+cmdInterfaceDestroy(vshControl *ctl, const vshCmd *cmd)
 {
-    virDomainPtr dom;
+    virInterfacePtr iface;
     bool ret = true;
     const char *name;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+    if (!(iface = vshCommandOptInterface(ctl, cmd, &name)))
         return false;
 
-    if (virDomainResume(dom) == 0) {
-        vshPrint(ctl, _("Domain %s resumed\n"), name);
+    if (virInterfaceDestroy(iface, 0) == 0) {
+        vshPrint(ctl, _("Interface %s destroyed\n"), name);
     } else {
-        vshError(ctl, _("Failed to resume domain %s"), name);
+        vshError(ctl, _("Failed to destroy interface %s"), name);
         ret = false;
     }
 
-    virDomainFree(dom);
+    virInterfaceFree(iface);
     return ret;
 }
 
 /*
- * "shutdown" command
+ * "iface-begin" command
  */
-static const vshCmdInfo info_shutdown[] = {
-    {"help", N_("gracefully shutdown a domain")},
-    {"desc", N_("Run shutdown in the target domain.")},
+static const vshCmdInfo info_interface_begin[] = {
+    {"help", N_("create a snapshot of current interfaces settings, "
+                "which can be later committed (iface-commit) or "
+                "restored (iface-rollback)")},
+    {"desc", N_("Create a restore point for interfaces settings")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_shutdown[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"mode", VSH_OT_STRING, VSH_OFLAG_NONE, N_("shutdown mode: acpi|agent")},
+static const vshCmdOptDef opts_interface_begin[] = {
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdShutdown(vshControl *ctl, const vshCmd *cmd)
+cmdInterfaceBegin(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
 {
-    virDomainPtr dom;
-    bool ret = true;
-    const char *name;
-    const char *mode = NULL;
-    int flags = 0;
-    int rv;
-
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (vshCommandOptString(cmd, "mode", &mode) < 0) {
-        vshError(ctl, "%s", _("Invalid type"));
-        return false;
-    }
-
-    if (mode) {
-        if (STREQ(mode, "acpi")) {
-            flags |= VIR_DOMAIN_SHUTDOWN_ACPI_POWER_BTN;
-        } else if (STREQ(mode, "agent")) {
-            flags |= VIR_DOMAIN_SHUTDOWN_GUEST_AGENT;
-        } else {
-            vshError(ctl, _("Unknown mode %s value, expecting 'acpi' or 'agent'"), mode);
-            return false;
-        }
-    }
-
-    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+    if (virInterfaceChangeBegin(ctl->conn, 0) < 0) {
+        vshError(ctl, "%s", _("Failed to begin network config change transaction"));
         return false;
-
-    if (flags)
-        rv = virDomainShutdownFlags(dom, flags);
-    else
-        rv = virDomainShutdown(dom);
-    if (rv == 0) {
-        vshPrint(ctl, _("Domain %s is being shutdown\n"), name);
-    } else {
-        vshError(ctl, _("Failed to shutdown domain %s"), name);
-        ret = false;
     }
 
-    virDomainFree(dom);
-    return ret;
+    vshPrint(ctl, "%s", _("Network config change transaction started\n"));
+    return true;
 }
 
 /*
- * "reboot" command
+ * "iface-commit" command
  */
-static const vshCmdInfo info_reboot[] = {
-    {"help", N_("reboot a domain")},
-    {"desc", N_("Run a reboot command in the target domain.")},
+static const vshCmdInfo info_interface_commit[] = {
+    {"help", N_("commit changes made since iface-begin and free restore point")},
+    {"desc", N_("commit changes and free restore point")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_reboot[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"mode", VSH_OT_STRING, VSH_OFLAG_NONE, N_("shutdown mode: acpi|agent")},
+static const vshCmdOptDef opts_interface_commit[] = {
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdReboot(vshControl *ctl, const vshCmd *cmd)
+cmdInterfaceCommit(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
 {
-    virDomainPtr dom;
-    bool ret = true;
-    const char *name;
-    const char *mode = NULL;
-    int flags = 0;
-
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (vshCommandOptString(cmd, "mode", &mode) < 0) {
-        vshError(ctl, "%s", _("Invalid type"));
-        return false;
-    }
-
-    if (mode) {
-        if (STREQ(mode, "acpi")) {
-            flags |= VIR_DOMAIN_SHUTDOWN_ACPI_POWER_BTN;
-        } else if (STREQ(mode, "agent")) {
-            flags |= VIR_DOMAIN_SHUTDOWN_GUEST_AGENT;
-        } else {
-            vshError(ctl, _("Unknown mode %s value, expecting 'acpi' or 'agent'"), mode);
-            return false;
-        }
-    }
-
-    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+    if (virInterfaceChangeCommit(ctl->conn, 0) < 0) {
+        vshError(ctl, "%s", _("Failed to commit network config change transaction"));
         return false;
-
-    if (virDomainReboot(dom, flags) == 0) {
-        vshPrint(ctl, _("Domain %s is being rebooted\n"), name);
-    } else {
-        vshError(ctl, _("Failed to reboot domain %s"), name);
-        ret = false;
     }
 
-    virDomainFree(dom);
-    return ret;
+    vshPrint(ctl, "%s", _("Network config change transaction committed\n"));
+    return true;
 }
 
 /*
- * "reset" command
+ * "iface-rollback" command
  */
-static const vshCmdInfo info_reset[] = {
-    {"help", N_("reset a domain")},
-    {"desc", N_("Reset the target domain as if by power button")},
+static const vshCmdInfo info_interface_rollback[] = {
+    {"help", N_("rollback to previous saved configuration created via iface-begin")},
+    {"desc", N_("rollback to previous restore point")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_reset[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+static const vshCmdOptDef opts_interface_rollback[] = {
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdReset(vshControl *ctl, const vshCmd *cmd)
+cmdInterfaceRollback(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
 {
-    virDomainPtr dom;
-    bool ret = true;
-    const char *name;
-
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+    if (virInterfaceChangeRollback(ctl->conn, 0) < 0) {
+        vshError(ctl, "%s", _("Failed to rollback network config change transaction"));
         return false;
-
-    if (virDomainReset(dom, 0) == 0) {
-        vshPrint(ctl, _("Domain %s was reset\n"), name);
-    } else {
-        vshError(ctl, _("Failed to reset domain %s"), name);
-        ret = false;
     }
 
-    virDomainFree(dom);
-    return ret;
+    vshPrint(ctl, "%s", _("Network config change transaction rolled back\n"));
+    return true;
 }
 
 /*
- * "destroy" command
+ * "iface-bridge" command
  */
-static const vshCmdInfo info_destroy[] = {
-    {"help", N_("destroy (stop) a domain")},
-    {"desc",
-     N_("Forcefully stop a given domain, but leave its resources intact.")},
-    {NULL, NULL}
+static const vshCmdInfo info_interface_bridge[] = {
+    {"help", N_("create a bridge device and attach an existing network device to it")},
+    {"desc", N_("bridge an existing network device")},
+    {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_destroy[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"graceful", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("terminate gracefully")},
+static const vshCmdOptDef opts_interface_bridge[] = {
+    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("existing interface name")},
+    {"bridge", VSH_OT_DATA, VSH_OFLAG_REQ, N_("new bridge device name")},
+    {"no-stp", VSH_OT_BOOL, 0, N_("do not enable STP for this bridge")},
+    {"delay", VSH_OT_INT, 0,
+     N_("number of seconds to squelch traffic on newly connected ports")},
+    {"no-start", VSH_OT_BOOL, 0, N_("don't start the bridge immediately")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdDestroy(vshControl *ctl, const vshCmd *cmd)
+cmdInterfaceBridge(vshControl *ctl, const vshCmd *cmd)
 {
-    virDomainPtr dom;
-    bool ret = true;
-    const char *name;
-    unsigned int flags = 0;
-    int result;
+    bool ret = false;
+    virInterfacePtr if_handle = NULL, br_handle = NULL;
+    const char *if_name, *br_name;
+    char *if_type = NULL, *if2_name = NULL, *delay_str = NULL;
+    bool stp = false, nostart = false;
+    unsigned int delay = 0;
+    char *if_xml = NULL;
+    xmlChar *br_xml = NULL;
+    int br_xml_size;
+    xmlDocPtr xml_doc = NULL;
+    xmlXPathContextPtr ctxt = NULL;
+    xmlNodePtr top_node, br_node, if_node, cur;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
+        goto cleanup;
 
-    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
-        return false;
+    /* Get a handle to the original device */
+    if (!(if_handle = vshCommandOptInterfaceBy(ctl, cmd, "interface",
+                                               &if_name, VSH_BYNAME))) {
+        goto cleanup;
+    }
 
-    if (vshCommandOptBool(cmd, "graceful"))
-       flags |= VIR_DOMAIN_DESTROY_GRACEFUL;
+    /* Name for new bridge device */
+    if (vshCommandOptString(cmd, "bridge", &br_name) <= 0) {
+        vshError(ctl, "%s", _("Missing bridge device name in command"));
+        goto cleanup;
+    }
 
-    if (flags)
-       result = virDomainDestroyFlags(dom, VIR_DOMAIN_DESTROY_GRACEFUL);
-    else
-       result = virDomainDestroy(dom);
+    /* make sure "new" device doesn't already exist */
+    if ((br_handle = virInterfaceLookupByName(ctl->conn, br_name))) {
+        vshError(ctl, _("Network device %s already exists"), br_name);
+        goto cleanup;
+    }
 
-    if (result == 0) {
-        vshPrint(ctl, _("Domain %s destroyed\n"), name);
-    } else {
-        vshError(ctl, _("Failed to destroy domain %s"), name);
-        ret = false;
+    /* use "no-stp" because we want "stp" to default true */
+    stp = !vshCommandOptBool(cmd, "no-stp");
+
+    if (vshCommandOptUInt(cmd, "delay", &delay) < 0) {
+        vshError(ctl, "%s", _("Unable to parse delay parameter"));
+        goto cleanup;
     }
 
-    virDomainFree(dom);
-    return ret;
-}
+    nostart = vshCommandOptBool(cmd, "no-start");
 
-/*
- * "domjobinfo" command
- */
-static const vshCmdInfo info_domjobinfo[] = {
-    {"help", N_("domain job information")},
-    {"desc", N_("Returns information about jobs running on a domain.")},
-    {NULL, NULL}
-};
+    /* Get the original interface into an xmlDoc */
+    if (!(if_xml = virInterfaceGetXMLDesc(if_handle, VIR_INTERFACE_XML_INACTIVE)))
+        goto cleanup;
+    if (!(xml_doc = virXMLParseStringCtxt(if_xml,
+                                          _("(interface definition)"), &ctxt))) {
+        vshError(ctl, _("Failed to parse configuration of %s"), if_name);
+        goto cleanup;
+    }
+    top_node = ctxt->node;
 
-static const vshCmdOptDef opts_domjobinfo[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {NULL, 0, 0, NULL}
-};
+    /* Verify that the original device isn't already a bridge. */
+    if (!(if_type = virXMLPropString(top_node, "type"))) {
+        vshError(ctl, _("Existing device %s has no type"), if_name);
+        goto cleanup;
+    }
 
+    if (STREQ(if_type, "bridge")) {
+        vshError(ctl, _("Existing device %s is already a bridge"), if_name);
+        goto cleanup;
+    }
 
-static bool
-cmdDomjobinfo(vshControl *ctl, const vshCmd *cmd)
-{
-    virDomainJobInfo info;
-    virDomainPtr dom;
-    bool ret = true;
+    /* verify the name in the XML matches the device name */
+    if (!(if2_name = virXMLPropString(top_node, "name")) ||
+        STRNEQ(if2_name, if_name)) {
+        vshError(ctl, _("Interface name from config %s doesn't match given supplied name %s"),
+                 if2_name, if_name);
+        goto cleanup;
+    }
 
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
+    /* Create a <bridge> node under <interface>. */
+    if (!(br_node = xmlNewChild(top_node, NULL, BAD_CAST "bridge", NULL))) {
+        vshError(ctl, "%s", _("Failed to create bridge node in xml document"));
+        goto cleanup;
+    }
 
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
-        return false;
+    /* Set stp and delay attributes in <bridge> according to the
+     * commandline options.
+     */
+    if (!xmlSetProp(br_node, BAD_CAST "stp", BAD_CAST (stp ? "on" : "off"))) {
+        vshError(ctl, "%s", _("Failed to set stp attribute in xml document"));
+        goto cleanup;
+    }
 
-    if (virDomainGetJobInfo(dom, &info) == 0) {
-        const char *unit;
-        double val;
+    if ((delay || stp) &&
+        ((virAsprintf(&delay_str, "%d", delay) < 0) ||
+         !xmlSetProp(br_node, BAD_CAST "delay", BAD_CAST delay_str))) {
+        vshError(ctl, _("Failed to set bridge delay %d in xml document"), delay);
+        goto cleanup;
+    }
 
-        vshPrint(ctl, "%-17s ", _("Job type:"));
-        switch (info.type) {
-        case VIR_DOMAIN_JOB_BOUNDED:
-            vshPrint(ctl, "%-12s\n", _("Bounded"));
-            break;
+    /* Change the type of the outer/master interface to "bridge" and the
+     * name to the provided bridge name.
+     */
+    if (!xmlSetProp(top_node, BAD_CAST "type", BAD_CAST "bridge")) {
+        vshError(ctl, "%s", _("Failed to set bridge interface type to 'bridge' in xml document"));
+        goto cleanup;
+    }
 
-        case VIR_DOMAIN_JOB_UNBOUNDED:
-            vshPrint(ctl, "%-12s\n", _("Unbounded"));
-            break;
+    if (!xmlSetProp(top_node, BAD_CAST "name", BAD_CAST br_name)) {
+        vshError(ctl, _("Failed to set master bridge interface name to '%s' in xml document"),
+            br_name);
+        goto cleanup;
+    }
 
-        case VIR_DOMAIN_JOB_NONE:
-        default:
-            vshPrint(ctl, "%-12s\n", _("None"));
-            goto cleanup;
-        }
+    /* Create an <interface> node under <bridge> that uses the
+     * original interface's type and name.
+     */
+    if (!(if_node = xmlNewChild(br_node, NULL, BAD_CAST "interface", NULL))) {
+        vshError(ctl, "%s", _("Failed to create interface node under bridge node in xml document"));
+        goto cleanup;
+    }
 
-        vshPrint(ctl, "%-17s %-12llu ms\n", _("Time elapsed:"), info.timeElapsed);
-        if (info.type == VIR_DOMAIN_JOB_BOUNDED)
-            vshPrint(ctl, "%-17s %-12llu ms\n", _("Time remaining:"), info.timeRemaining);
-        if (info.dataTotal || info.dataRemaining || info.dataProcessed) {
-            val = prettyCapacity(info.dataProcessed, &unit);
-            vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data processed:"), val, unit);
-            val = prettyCapacity(info.dataRemaining, &unit);
-            vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data remaining:"), val, unit);
-            val = prettyCapacity(info.dataTotal, &unit);
-            vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data total:"), val, unit);
-        }
-        if (info.memTotal || info.memRemaining || info.memProcessed) {
-            val = prettyCapacity(info.memProcessed, &unit);
-            vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory processed:"), val, unit);
-            val = prettyCapacity(info.memRemaining, &unit);
-            vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory remaining:"), val, unit);
-            val = prettyCapacity(info.memTotal, &unit);
-            vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory total:"), val, unit);
-        }
-        if (info.fileTotal || info.fileRemaining || info.fileProcessed) {
-            val = prettyCapacity(info.fileProcessed, &unit);
-            vshPrint(ctl, "%-17s %-.3lf %s\n", _("File processed:"), val, unit);
-            val = prettyCapacity(info.fileRemaining, &unit);
-            vshPrint(ctl, "%-17s %-.3lf %s\n", _("File remaining:"), val, unit);
-            val = prettyCapacity(info.fileTotal, &unit);
-            vshPrint(ctl, "%-17s %-.3lf %s\n", _("File total:"), val, unit);
+    /* set the type of the inner/slave interface to the original
+     * if_type, and the name to the original if_name.
+     */
+    if (!xmlSetProp(if_node, BAD_CAST "type", BAD_CAST if_type)) {
+        vshError(ctl, _("Failed to set new slave interface type to '%s' in xml document"),
+                 if_name);
+        goto cleanup;
+    }
+
+    if (!xmlSetProp(if_node, BAD_CAST "name", BAD_CAST if_name)) {
+        vshError(ctl, _("Failed to set new slave interface name to '%s' in xml document"),
+            br_name);
+        goto cleanup;
+    }
+
+    /* Cycle through all the nodes under the original <interface>,
+     * moving all <mac>, <bond> and <vlan> nodes down into the new
+     * lower level <interface>.
+     */
+    cur = top_node->children;
+    while (cur) {
+        xmlNodePtr old = cur;
+
+        cur = cur->next;
+        if ((old->type == XML_ELEMENT_NODE) &&
+            (xmlStrEqual(old->name, BAD_CAST "mac") ||  /* ethernet stuff to move down */
+             xmlStrEqual(old->name, BAD_CAST "bond") || /* bond stuff to move down */
+             xmlStrEqual(old->name, BAD_CAST "vlan"))) { /* vlan stuff to move down */
+            xmlUnlinkNode(old);
+            if (!xmlAddChild(if_node, old)) {
+                vshError(ctl, _("Failed to move '%s' element in xml document"), old->name);
+                xmlFreeNode(old);
+                goto cleanup;
+            }
         }
-    } else {
-        ret = false;
     }
-cleanup:
-    virDomainFree(dom);
-    return ret;
-}
 
-/*
- * "domjobabort" command
- */
-static const vshCmdInfo info_domjobabort[] = {
-    {"help", N_("abort active domain job")},
-    {"desc", N_("Aborts the currently running domain job")},
-    {NULL, NULL}
-};
+    /* The document should now be fully converted; write it out to a string. */
+    xmlDocDumpMemory(xml_doc, &br_xml, &br_xml_size);
 
-static const vshCmdOptDef opts_domjobabort[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {NULL, 0, 0, NULL}
-};
+    if (!br_xml || br_xml_size <= 0) {
+        vshError(ctl, _("Failed to format new xml document for bridge %s"), br_name);
+        goto cleanup;
+    }
 
-static bool
-cmdDomjobabort(vshControl *ctl, const vshCmd *cmd)
-{
-    virDomainPtr dom;
-    bool ret = true;
 
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
+    /* br_xml is the new interface to define. It will automatically undefine the
+     * independent original interface.
+     */
+    if (!(br_handle = virInterfaceDefineXML(ctl->conn, (char *) br_xml, 0))) {
+        vshError(ctl, _("Failed to define new bridge interface %s"),
+                 br_name);
+        goto cleanup;
+    }
 
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
-        return false;
+    vshPrint(ctl, _("Created bridge %s with attached device %s\n"),
+             br_name, if_name);
 
-    if (virDomainAbortJob(dom) < 0)
-        ret = false;
+    /* start it up unless requested not to */
+    if (!nostart) {
+        if (virInterfaceCreate(br_handle, 0) < 0) {
+            vshError(ctl, _("Failed to start bridge interface %s"), br_name);
+            goto cleanup;
+        }
+        vshPrint(ctl, _("Bridge interface %s started\n"), br_name);
+    }
 
-    virDomainFree(dom);
+    ret = true;
+ cleanup:
+    if (if_handle)
+       virInterfaceFree(if_handle);
+    if (br_handle)
+       virInterfaceFree(br_handle);
+    VIR_FREE(if_xml);
+    VIR_FREE(br_xml);
+    VIR_FREE(if_type);
+    VIR_FREE(if2_name);
+    VIR_FREE(delay_str);
+    xmlXPathFreeContext(ctxt);
+    xmlFreeDoc(xml_doc);
     return ret;
 }
 
 /*
- * "freecell" command
+ * "iface-unbridge" command
  */
-static const vshCmdInfo info_freecell[] = {
-    {"help", N_("NUMA free memory")},
-    {"desc", N_("display available free memory for the NUMA cell.")},
+static const vshCmdInfo info_interface_unbridge[] = {
+    {"help", N_("undefine a bridge device after detaching its slave device")},
+    {"desc", N_("unbridge a network device")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_freecell[] = {
-    {"cellno", VSH_OT_INT, 0, N_("NUMA cell number")},
-    {"all", VSH_OT_BOOL, 0, N_("show free memory for all NUMA cells")},
+static const vshCmdOptDef opts_interface_unbridge[] = {
+    {"bridge", VSH_OT_DATA, VSH_OFLAG_REQ, N_("current bridge device name")},
+    {"no-start", VSH_OT_BOOL, 0,
+     N_("don't start the un-slaved interface immediately (not recommended)")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdFreecell(vshControl *ctl, const vshCmd *cmd)
+cmdInterfaceUnbridge(vshControl *ctl, const vshCmd *cmd)
 {
-    bool func_ret = false;
-    int ret;
-    int cell = -1, cell_given;
-    unsigned long long memory;
-    xmlNodePtr *nodes = NULL;
-    unsigned long nodes_cnt;
-    unsigned long *nodes_id = NULL;
-    unsigned long long *nodes_free = NULL;
-    int all_given;
-    int i;
-    char *cap_xml = NULL;
-    xmlDocPtr xml = NULL;
+    bool ret = false;
+    virInterfacePtr if_handle = NULL, br_handle = NULL;
+    const char *br_name;
+    char *if_type = NULL, *if_name = NULL;
+    bool nostart = false;
+    char *br_xml = NULL;
+    xmlChar *if_xml = NULL;
+    int if_xml_size;
+    xmlDocPtr xml_doc = NULL;
     xmlXPathContextPtr ctxt = NULL;
-
+    xmlNodePtr top_node, br_node, if_node, cur;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
+        goto cleanup;
 
-    if ( (cell_given = vshCommandOptInt(cmd, "cellno", &cell)) < 0) {
-        vshError(ctl, "%s", _("cell number has to be a number"));
+    /* Get a handle to the original device */
+    if (!(br_handle = vshCommandOptInterfaceBy(ctl, cmd, "bridge",
+                                               &br_name, VSH_BYNAME))) {
         goto cleanup;
     }
-    all_given = vshCommandOptBool(cmd, "all");
 
-    if (all_given && cell_given) {
-        vshError(ctl, "%s", _("--cellno and --all are mutually exclusive. "
-                              "Please choose only one."));
+    nostart = vshCommandOptBool(cmd, "no-start");
+
+    /* Get the bridge xml into an xmlDoc */
+    if (!(br_xml = virInterfaceGetXMLDesc(br_handle, VIR_INTERFACE_XML_INACTIVE)))
+        goto cleanup;
+    if (!(xml_doc = virXMLParseStringCtxt(br_xml,
+                                          _("(bridge interface definition)"),
+                                          &ctxt))) {
+        vshError(ctl, _("Failed to parse configuration of %s"), br_name);
         goto cleanup;
     }
+    top_node = ctxt->node;
 
-    if (all_given) {
-        cap_xml = virConnectGetCapabilities(ctl->conn);
-        if (!cap_xml) {
-            vshError(ctl, "%s", _("unable to get node capabilities"));
-            goto cleanup;
-        }
+    /* Verify that the device really is a bridge. */
+    if (!(if_type = virXMLPropString(top_node, "type"))) {
+        vshError(ctl, _("Existing device %s has no type"), br_name);
+        goto cleanup;
+    }
 
-        xml = virXMLParseStringCtxt(cap_xml, _("(capabilities)"), &ctxt);
-        if (!xml) {
-            vshError(ctl, "%s", _("unable to get node capabilities"));
-            goto cleanup;
-        }
-        nodes_cnt = virXPathNodeSet("/capabilities/host/topology/cells/cell",
-                                    ctxt, &nodes);
+    if (STRNEQ(if_type, "bridge")) {
+        vshError(ctl, _("Device %s is not a bridge"), br_name);
+        goto cleanup;
+    }
+    VIR_FREE(if_type);
 
-        if (nodes_cnt == -1) {
-            vshError(ctl, "%s", _("could not get information about "
-                                  "NUMA topology"));
-            goto cleanup;
-        }
+    /* verify the name in the XML matches the device name */
+    if (!(if_name = virXMLPropString(top_node, "name")) ||
+        STRNEQ(if_name, br_name)) {
+        vshError(ctl, _("Interface name from config %s doesn't match given supplied name %s"),
+                 if_name, br_name);
+        goto cleanup;
+    }
+    VIR_FREE(if_name);
 
-        nodes_free = vshCalloc(ctl, nodes_cnt, sizeof(*nodes_free));
-        nodes_id = vshCalloc(ctl, nodes_cnt, sizeof(*nodes_id));
+    /* Find the <bridge> node under <interface>. */
+    if (!(br_node = virXPathNode("./bridge", ctxt))) {
+        vshError(ctl, "%s", _("No bridge node in xml document"));
+        goto cleanup;
+    }
 
-        for (i = 0; i < nodes_cnt; i++) {
-            unsigned long id;
-            char *val = virXMLPropString(nodes[i], "id");
-            if (virStrToLong_ul(val, NULL, 10, &id)) {
-                vshError(ctl, "%s", _("conversion from string failed"));
-                VIR_FREE(val);
-                goto cleanup;
-            }
-            VIR_FREE(val);
-            nodes_id[i]=id;
-            ret = virNodeGetCellsFreeMemory(ctl->conn, &(nodes_free[i]), id, 1);
-            if (ret != 1) {
-                vshError(ctl, _("failed to get free memory for NUMA node "
-                                "number: %lu"), id);
+    if ((if_node = virXPathNode("./bridge/interface[2]", ctxt))) {
+        vshError(ctl, "%s", _("Multiple interfaces attached to bridge"));
+        goto cleanup;
+    }
+
+    if (!(if_node = virXPathNode("./bridge/interface", ctxt))) {
+        vshError(ctl, "%s", _("No interface attached to bridge"));
+        goto cleanup;
+    }
+
+    /* Change the type and name of the outer/master interface to
+     * the type/name of the attached slave interface.
+     */
+    if (!(if_name = virXMLPropString(if_node, "name"))) {
+        vshError(ctl, _("Device attached to bridge %s has no name"), br_name);
+        goto cleanup;
+    }
+
+    if (!(if_type = virXMLPropString(if_node, "type"))) {
+        vshError(ctl, _("Attached device %s has no type"), if_name);
+        goto cleanup;
+    }
+
+    if (!xmlSetProp(top_node, BAD_CAST "type", BAD_CAST if_type)) {
+        vshError(ctl, _("Failed to set interface type to '%s' in xml document"),
+                 if_type);
+        goto cleanup;
+    }
+
+    if (!xmlSetProp(top_node, BAD_CAST "name", BAD_CAST if_name)) {
+        vshError(ctl, _("Failed to set interface name to '%s' in xml document"),
+                 if_name);
+        goto cleanup;
+    }
+
+    /* Cycle through all the nodes under the attached <interface>,
+     * moving all <mac>, <bond> and <vlan> nodes up into the toplevel
+     * <interface>.
+     */
+    cur = if_node->children;
+    while (cur) {
+        xmlNodePtr old = cur;
+
+        cur = cur->next;
+        if ((old->type == XML_ELEMENT_NODE) &&
+            (xmlStrEqual(old->name, BAD_CAST "mac") ||  /* ethernet stuff to move down */
+             xmlStrEqual(old->name, BAD_CAST "bond") || /* bond stuff to move down */
+             xmlStrEqual(old->name, BAD_CAST "vlan"))) { /* vlan stuff to move down */
+            xmlUnlinkNode(old);
+            if (!xmlAddChild(top_node, old)) {
+                vshError(ctl, _("Failed to move '%s' element in xml document"), old->name);
+                xmlFreeNode(old);
                 goto cleanup;
             }
         }
+    }
 
-        memory = 0;
-        for (cell = 0; cell < nodes_cnt; cell++) {
-            vshPrint(ctl, "%5lu: %10llu KiB\n", nodes_id[cell],
-                    (nodes_free[cell]/1024));
-            memory += nodes_free[cell];
-        }
+    /* The document should now be fully converted; write it out to a string. */
+    xmlDocDumpMemory(xml_doc, &if_xml, &if_xml_size);
 
-        vshPrintExtra(ctl, "--------------------\n");
-        vshPrintExtra(ctl, "%5s: %10llu KiB\n", _("Total"), memory/1024);
-    } else {
-        if (!cell_given) {
-            memory = virNodeGetFreeMemory(ctl->conn);
-            if (memory == 0)
-                goto cleanup;
-        } else {
-            ret = virNodeGetCellsFreeMemory(ctl->conn, &memory, cell, 1);
-            if (ret != 1)
-                goto cleanup;
-        }
+    if (!if_xml || if_xml_size <= 0) {
+        vshError(ctl, _("Failed to format new xml document for un-enslaved interface %s"),
+                 if_name);
+        goto cleanup;
+    }
 
-        if (cell == -1)
-            vshPrint(ctl, "%s: %llu KiB\n", _("Total"), (memory/1024));
-        else
-            vshPrint(ctl, "%d: %llu KiB\n", cell, (memory/1024));
+    /* Destroy and Undefine the bridge device, since we otherwise
+     * can't safely define the unattached device.
+     */
+    if (virInterfaceDestroy(br_handle, 0) < 0) {
+        vshError(ctl, _("Failed to destroy bridge interface %s"), br_name);
+        goto cleanup;
+    }
+    if (virInterfaceUndefine(br_handle) < 0) {
+        vshError(ctl, _("Failed to undefine bridge interface %s"), br_name);
+        goto cleanup;
     }
 
-    func_ret = true;
+    /* if_xml is the new interface to define.
+     */
+    if (!(if_handle = virInterfaceDefineXML(ctl->conn, (char *) if_xml, 0))) {
+        vshError(ctl, _("Failed to define new interface %s"), if_name);
+        goto cleanup;
+    }
 
-cleanup:
+    vshPrint(ctl, _("Device %s un-attached from bridge %s\n"),
+             if_name, br_name);
+
+    /* unless requested otherwise, undefine the bridge device */
+    if (!nostart) {
+        if (virInterfaceCreate(if_handle, 0) < 0) {
+            vshError(ctl, _("Failed to start interface %s"), if_name);
+            goto cleanup;
+        }
+        vshPrint(ctl, _("Interface %s started\n"), if_name);
+    }
+
+    ret = true;
+ cleanup:
+    if (if_handle)
+       virInterfaceFree(if_handle);
+    if (br_handle)
+       virInterfaceFree(br_handle);
+    VIR_FREE(if_xml);
+    VIR_FREE(br_xml);
+    VIR_FREE(if_type);
+    VIR_FREE(if_name);
     xmlXPathFreeContext(ctxt);
-    xmlFreeDoc(xml);
-    VIR_FREE(nodes);
-    VIR_FREE(nodes_free);
-    VIR_FREE(nodes_id);
-    VIR_FREE(cap_xml);
-    return func_ret;
+    xmlFreeDoc(xml_doc);
+    return ret;
 }
 
 /*
- * "maxvcpus" command
+ * "nwfilter-define" command
  */
-static const vshCmdInfo info_maxvcpus[] = {
-    {"help", N_("connection vcpu maximum")},
-    {"desc", N_("Show maximum number of virtual CPUs for guests on this connection.")},
+static const vshCmdInfo info_nwfilter_define[] = {
+    {"help", N_("define or update a network filter from an XML file")},
+    {"desc", N_("Define a new network filter or update an existing one.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_maxvcpus[] = {
-    {"type", VSH_OT_STRING, 0, N_("domain type")},
+static const vshCmdOptDef opts_nwfilter_define[] = {
+    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML network filter description")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdMaxvcpus(vshControl *ctl, const vshCmd *cmd)
+cmdNWFilterDefine(vshControl *ctl, const vshCmd *cmd)
 {
-    const char *type = NULL;
-    int vcpus;
-
-    if (vshCommandOptString(cmd, "type", &type) < 0) {
-        vshError(ctl, "%s", _("Invalid type"));
-        return false;
-    }
+    virNWFilterPtr nwfilter;
+    const char *from = NULL;
+    bool ret = true;
+    char *buffer;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    vcpus = virConnectGetMaxVcpus(ctl->conn, type);
-    if (vcpus < 0)
+    if (vshCommandOptString(cmd, "file", &from) <= 0)
         return false;
-    vshPrint(ctl, "%d\n", vcpus);
 
-    return true;
-}
+    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
+        return false;
 
-/*
- * "vcpucount" command
- */
-static const vshCmdInfo info_vcpucount[] = {
-    {"help", N_("domain vcpu counts")},
-    {"desc", N_("Returns the number of virtual CPUs used by the domain.")},
-    {NULL, NULL}
-};
+    nwfilter = virNWFilterDefineXML(ctl->conn, buffer);
+    VIR_FREE(buffer);
 
-static const vshCmdOptDef opts_vcpucount[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"maximum", VSH_OT_BOOL, 0, N_("get maximum cap on vcpus")},
-    {"active", VSH_OT_BOOL, 0, N_("get number of currently active vcpus")},
-    {"live", VSH_OT_BOOL, 0, N_("get value from running domain")},
-    {"config", VSH_OT_BOOL, 0, N_("get value to be used on next boot")},
-    {"current", VSH_OT_BOOL, 0,
-     N_("get value according to current domain state")},
+    if (nwfilter != NULL) {
+        vshPrint(ctl, _("Network filter %s defined from %s\n"),
+                 virNWFilterGetName(nwfilter), from);
+        virNWFilterFree(nwfilter);
+    } else {
+        vshError(ctl, _("Failed to define network filter from %s"), from);
+        ret = false;
+    }
+    return ret;
+}
+
+
+/*
+ * "nwfilter-undefine" command
+ */
+static const vshCmdInfo info_nwfilter_undefine[] = {
+    {"help", N_("undefine a network filter")},
+    {"desc", N_("Undefine a given network filter.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_nwfilter_undefine[] = {
+    {"nwfilter", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network filter name or uuid")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdVcpucount(vshControl *ctl, const vshCmd *cmd)
+cmdNWFilterUndefine(vshControl *ctl, const vshCmd *cmd)
 {
-    virDomainPtr dom;
+    virNWFilterPtr nwfilter;
     bool ret = true;
-    bool maximum = vshCommandOptBool(cmd, "maximum");
-    bool active = vshCommandOptBool(cmd, "active");
-    bool config = vshCommandOptBool(cmd, "config");
-    bool live = vshCommandOptBool(cmd, "live");
-    bool current = vshCommandOptBool(cmd, "current");
-    bool all = maximum + active + current + config + live == 0;
-    int count;
-
-    /* We want one of each pair of mutually exclusive options; that
-     * is, use of flags requires exactly two options.  We reject the
-     * use of more than 2 flags later on.  */
-    if (maximum + active + current + config + live == 1) {
-        if (maximum || active) {
-            vshError(ctl,
-                     _("when using --%s, one of --config, --live, or --current "
-                       "must be specified"),
-                     maximum ? "maximum" : "active");
-        } else {
-            vshError(ctl,
-                     _("when using --%s, either --maximum or --active must be "
-                       "specified"),
-                     (current ? "current" : config ? "config" : "live"));
-        }
-        return false;
-    }
-
-    /* Backwards compatibility: prior to 0.9.4,
-     * VIR_DOMAIN_AFFECT_CURRENT was unsupported, and --current meant
-     * the opposite of --maximum.  Translate the old '--current
-     * --live' into the new '--active --live', while treating the new
-     * '--maximum --current' correctly rather than rejecting it as
-     * '--maximum --active'.  */
-    if (!maximum && !active && current) {
-        current = false;
-        active = true;
-    }
-
-    if (maximum && active) {
-        vshError(ctl, "%s",
-                 _("--maximum and --active cannot both be specified"));
-        return false;
-    }
-    if (current + config + live > 1) {
-        vshError(ctl, "%s",
-                 _("--config, --live, and --current are mutually exclusive"));
-        return false;
-    }
+    const char *name;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+    if (!(nwfilter = vshCommandOptNWFilter(ctl, cmd, &name)))
         return false;
 
-    /* In all cases, try the new API first; if it fails because we are
-     * talking to an older client, generally we try a fallback API
-     * before giving up.  --current requires the new API, since we
-     * don't know whether the domain is running or inactive.  */
-    if (current) {
-        count = virDomainGetVcpusFlags(dom,
-                                       maximum ? VIR_DOMAIN_VCPU_MAXIMUM : 0);
-        if (count < 0) {
-            virshReportError(ctl);
-            ret = false;
-        } else {
-            vshPrint(ctl, "%d\n", count);
-        }
-    }
-
-    if (all || (maximum && config)) {
-        count = virDomainGetVcpusFlags(dom, (VIR_DOMAIN_VCPU_MAXIMUM |
-                                             VIR_DOMAIN_AFFECT_CONFIG));
-        if (count < 0 && (last_error->code == VIR_ERR_NO_SUPPORT
-                          || last_error->code == VIR_ERR_INVALID_ARG)) {
-            char *tmp;
-            char *xml = virDomainGetXMLDesc(dom, VIR_DOMAIN_XML_INACTIVE);
-            if (xml && (tmp = strstr(xml, "<vcpu"))) {
-                tmp = strchr(tmp, '>');
-                if (!tmp || virStrToLong_i(tmp + 1, &tmp, 10, &count) < 0)
-                    count = -1;
-            }
-            virFreeError(last_error);
-            last_error = NULL;
-            VIR_FREE(xml);
-        }
-
-        if (count < 0) {
-            virshReportError(ctl);
-            ret = false;
-        } else if (all) {
-            vshPrint(ctl, "%-12s %-12s %3d\n", _("maximum"), _("config"),
-                     count);
-        } else {
-            vshPrint(ctl, "%d\n", count);
-        }
-        virFreeError(last_error);
-        last_error = NULL;
-    }
-
-    if (all || (maximum && live)) {
-        count = virDomainGetVcpusFlags(dom, (VIR_DOMAIN_VCPU_MAXIMUM |
-                                             VIR_DOMAIN_AFFECT_LIVE));
-        if (count < 0 && (last_error->code == VIR_ERR_NO_SUPPORT
-                          || last_error->code == VIR_ERR_INVALID_ARG)) {
-            count = virDomainGetMaxVcpus(dom);
-        }
-
-        if (count < 0) {
-            virshReportError(ctl);
-            ret = false;
-        } else if (all) {
-            vshPrint(ctl, "%-12s %-12s %3d\n", _("maximum"), _("live"),
-                     count);
-        } else {
-            vshPrint(ctl, "%d\n", count);
-        }
-        virFreeError(last_error);
-        last_error = NULL;
-    }
-
-    if (all || (active && config)) {
-        count = virDomainGetVcpusFlags(dom, VIR_DOMAIN_AFFECT_CONFIG);
-        if (count < 0 && (last_error->code == VIR_ERR_NO_SUPPORT
-                          || last_error->code == VIR_ERR_INVALID_ARG)) {
-            char *tmp, *end;
-            char *xml = virDomainGetXMLDesc(dom, VIR_DOMAIN_XML_INACTIVE);
-            if (xml && (tmp = strstr(xml, "<vcpu"))) {
-                end = strchr(tmp, '>');
-                if (end) {
-                    *end = '\0';
-                    tmp = strstr(tmp, "current=");
-                    if (!tmp)
-                        tmp = end + 1;
-                    else {
-                        tmp += strlen("current=");
-                        tmp += *tmp == '\'' || *tmp == '"';
-                    }
-                }
-                if (!tmp || virStrToLong_i(tmp, &tmp, 10, &count) < 0)
-                    count = -1;
-            }
-            VIR_FREE(xml);
-        }
-
-        if (count < 0) {
-            virshReportError(ctl);
-            ret = false;
-        } else if (all) {
-            vshPrint(ctl, "%-12s %-12s %3d\n", _("current"), _("config"),
-                     count);
-        } else {
-            vshPrint(ctl, "%d\n", count);
-        }
-        virFreeError(last_error);
-        last_error = NULL;
-    }
-
-    if (all || (active && live)) {
-        count = virDomainGetVcpusFlags(dom, VIR_DOMAIN_AFFECT_LIVE);
-        if (count < 0 && (last_error->code == VIR_ERR_NO_SUPPORT
-                          || last_error->code == VIR_ERR_INVALID_ARG)) {
-            virDomainInfo info;
-            if (virDomainGetInfo(dom, &info) == 0)
-                count = info.nrVirtCpu;
-        }
-
-        if (count < 0) {
-            virshReportError(ctl);
-            ret = false;
-        } else if (all) {
-            vshPrint(ctl, "%-12s %-12s %3d\n", _("current"), _("live"),
-                     count);
-        } else {
-            vshPrint(ctl, "%d\n", count);
-        }
-        virFreeError(last_error);
-        last_error = NULL;
+    if (virNWFilterUndefine(nwfilter) == 0) {
+        vshPrint(ctl, _("Network filter %s undefined\n"), name);
+    } else {
+        vshError(ctl, _("Failed to undefine network filter %s"), name);
+        ret = false;
     }
 
-    virDomainFree(dom);
+    virNWFilterFree(nwfilter);
     return ret;
 }
 
+
 /*
- * "vcpuinfo" command
+ * "nwfilter-dumpxml" command
  */
-static const vshCmdInfo info_vcpuinfo[] = {
-    {"help", N_("detailed domain vcpu information")},
-    {"desc", N_("Returns basic information about the domain virtual CPUs.")},
+static const vshCmdInfo info_nwfilter_dumpxml[] = {
+    {"help", N_("network filter information in XML")},
+    {"desc", N_("Output the network filter information as an XML dump to stdout.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_vcpuinfo[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+static const vshCmdOptDef opts_nwfilter_dumpxml[] = {
+    {"nwfilter", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network filter name or uuid")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdVcpuinfo(vshControl *ctl, const vshCmd *cmd)
+cmdNWFilterDumpXML(vshControl *ctl, const vshCmd *cmd)
 {
-    virDomainInfo info;
-    virDomainPtr dom;
-    virNodeInfo nodeinfo;
-    virVcpuInfoPtr cpuinfo;
-    unsigned char *cpumaps;
-    int ncpus, maxcpu;
-    size_t cpumaplen;
+    virNWFilterPtr nwfilter;
     bool ret = true;
-    int n, m;
+    char *dump;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
-        return false;
-
-    if (virNodeGetInfo(ctl->conn, &nodeinfo) != 0) {
-        virDomainFree(dom);
-        return false;
-    }
-
-    if (virDomainGetInfo(dom, &info) != 0) {
-        virDomainFree(dom);
+    if (!(nwfilter = vshCommandOptNWFilter(ctl, cmd, NULL)))
         return false;
-    }
-
-    cpuinfo = vshMalloc(ctl, sizeof(virVcpuInfo)*info.nrVirtCpu);
-    maxcpu = VIR_NODEINFO_MAXCPUS(nodeinfo);
-    cpumaplen = VIR_CPU_MAPLEN(maxcpu);
-    cpumaps = vshMalloc(ctl, info.nrVirtCpu * cpumaplen);
-
-    if ((ncpus = virDomainGetVcpus(dom,
-                                   cpuinfo, info.nrVirtCpu,
-                                   cpumaps, cpumaplen)) >= 0) {
-        for (n = 0 ; n < ncpus ; n++) {
-            vshPrint(ctl, "%-15s %d\n", _("VCPU:"), n);
-            vshPrint(ctl, "%-15s %d\n", _("CPU:"), cpuinfo[n].cpu);
-            vshPrint(ctl, "%-15s %s\n", _("State:"),
-                     _(vshDomainVcpuStateToString(cpuinfo[n].state)));
-            if (cpuinfo[n].cpuTime != 0) {
-                double cpuUsed = cpuinfo[n].cpuTime;
-
-                cpuUsed /= 1000000000.0;
 
-                vshPrint(ctl, "%-15s %.1lfs\n", _("CPU time:"), cpuUsed);
-            }
-            vshPrint(ctl, "%-15s ", _("CPU Affinity:"));
-            for (m = 0; m < maxcpu; m++) {
-                vshPrint(ctl, "%c", VIR_CPU_USABLE(cpumaps, cpumaplen, n, m) ? 'y' : '-');
-            }
-            vshPrint(ctl, "\n");
-            if (n < (ncpus - 1)) {
-                vshPrint(ctl, "\n");
-            }
-        }
+    dump = virNWFilterGetXMLDesc(nwfilter, 0);
+    if (dump != NULL) {
+        vshPrint(ctl, "%s", dump);
+        VIR_FREE(dump);
     } else {
-        if (info.state == VIR_DOMAIN_SHUTOFF &&
-            (ncpus = virDomainGetVcpuPinInfo(dom, info.nrVirtCpu,
-                                             cpumaps, cpumaplen,
-                                             VIR_DOMAIN_AFFECT_CONFIG)) >= 0) {
-
-            /* fallback plan to use virDomainGetVcpuPinInfo */
-
-            for (n = 0; n < ncpus; n++) {
-                vshPrint(ctl, "%-15s %d\n", _("VCPU:"), n);
-                vshPrint(ctl, "%-15s %s\n", _("CPU:"), _("N/A"));
-                vshPrint(ctl, "%-15s %s\n", _("State:"), _("N/A"));
-                vshPrint(ctl, "%-15s %s\n", _("CPU time"), _("N/A"));
-                vshPrint(ctl, "%-15s ", _("CPU Affinity:"));
-                for (m = 0; m < maxcpu; m++) {
-                    vshPrint(ctl, "%c",
-                             VIR_CPU_USABLE(cpumaps, cpumaplen, n, m) ? 'y' : '-');
-                }
-                vshPrint(ctl, "\n");
-                if (n < (ncpus - 1)) {
-                    vshPrint(ctl, "\n");
-                }
-            }
-        } else {
-            ret = false;
-        }
+        ret = false;
     }
 
-    VIR_FREE(cpumaps);
-    VIR_FREE(cpuinfo);
-    virDomainFree(dom);
+    virNWFilterFree(nwfilter);
     return ret;
 }
 
 /*
- * "vcpupin" command
+ * "nwfilter-list" command
  */
-static const vshCmdInfo info_vcpupin[] = {
-    {"help", N_("control or query domain vcpu affinity")},
-    {"desc", N_("Pin domain VCPUs to host physical CPUs.")},
+static const vshCmdInfo info_nwfilter_list[] = {
+    {"help", N_("list network filters")},
+    {"desc", N_("Returns list of network filters.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_vcpupin[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"vcpu", VSH_OT_INT, 0, N_("vcpu number")},
-    {"cpulist", VSH_OT_DATA, VSH_OFLAG_EMPTY_OK,
-     N_("host cpu number(s) to set, or omit option to query")},
-    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
-    {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
-    {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
+static const vshCmdOptDef opts_nwfilter_list[] = {
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdVcpuPin(vshControl *ctl, const vshCmd *cmd)
+cmdNWFilterList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
 {
-    virDomainInfo info;
-    virDomainPtr dom;
-    virNodeInfo nodeinfo;
-    int vcpu = -1;
-    const char *cpulist = NULL;
-    bool ret = true;
-    unsigned char *cpumap = NULL;
-    unsigned char *cpumaps = NULL;
-    size_t cpumaplen;
-    bool bit, lastbit, isInvert;
-    int i, cpu, lastcpu, maxcpu, ncpus;
-    bool unuse = false;
-    const char *cur;
-    bool config = vshCommandOptBool(cmd, "config");
-    bool live = vshCommandOptBool(cmd, "live");
-    bool current = vshCommandOptBool(cmd, "current");
-    bool query = false; /* Query mode if no cpulist */
-    unsigned int flags = 0;
-
-    if (current) {
-        if (live || config) {
-            vshError(ctl, "%s", _("--current must be specified exclusively"));
-            return false;
-        }
-        flags = VIR_DOMAIN_AFFECT_CURRENT;
-    } else {
-        if (config)
-            flags |= VIR_DOMAIN_AFFECT_CONFIG;
-        if (live)
-            flags |= VIR_DOMAIN_AFFECT_LIVE;
-        /* neither option is specified */
-        if (!live && !config)
-            flags = -1;
-    }
+    int numfilters, i;
+    char **names;
+    char uuid[VIR_UUID_STRING_BUFLEN];
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
-        return false;
-
-    if (vshCommandOptString(cmd, "cpulist", &cpulist) < 0) {
-        vshError(ctl, "%s", _("vcpupin: Missing cpulist."));
-        virDomainFree(dom);
-        return false;
-    }
-    query = !cpulist;
-
-    /* In query mode, "vcpu" is optional */
-    if (vshCommandOptInt(cmd, "vcpu", &vcpu) < !query) {
-        vshError(ctl, "%s",
-                 _("vcpupin: Invalid or missing vCPU number."));
-        virDomainFree(dom);
-        return false;
-    }
-
-    if (virNodeGetInfo(ctl->conn, &nodeinfo) != 0) {
-        virDomainFree(dom);
+    numfilters = virConnectNumOfNWFilters(ctl->conn);
+    if (numfilters < 0) {
+        vshError(ctl, "%s", _("Failed to list network filters"));
         return false;
     }
 
-    if (virDomainGetInfo(dom, &info) != 0) {
-        vshError(ctl, "%s", _("vcpupin: failed to get domain information."));
-        virDomainFree(dom);
-        return false;
-    }
+    names = vshMalloc(ctl, sizeof(char *) * numfilters);
 
-    if (vcpu >= info.nrVirtCpu) {
-        vshError(ctl, "%s", _("vcpupin: Invalid vCPU number."));
-        virDomainFree(dom);
+    if ((numfilters = virConnectListNWFilters(ctl->conn, names,
+                                              numfilters)) < 0) {
+        vshError(ctl, "%s", _("Failed to list network filters"));
+        VIR_FREE(names);
         return false;
     }
 
-    maxcpu = VIR_NODEINFO_MAXCPUS(nodeinfo);
-    cpumaplen = VIR_CPU_MAPLEN(maxcpu);
-
-    /* Query mode: show CPU affinity information then exit.*/
-    if (query) {
-        /* When query mode and neither "live", "config" nor "current"
-         * is specified, set VIR_DOMAIN_AFFECT_CURRENT as flags */
-        if (flags == -1)
-            flags = VIR_DOMAIN_AFFECT_CURRENT;
-
-        cpumaps = vshMalloc(ctl, info.nrVirtCpu * cpumaplen);
-        if ((ncpus = virDomainGetVcpuPinInfo(dom, info.nrVirtCpu,
-                                             cpumaps, cpumaplen, flags)) >= 0) {
-
-            vshPrint(ctl, "%s %s\n", _("VCPU:"), _("CPU Affinity"));
-            vshPrint(ctl, "----------------------------------\n");
-            for (i = 0; i < ncpus; i++) {
-
-               if (vcpu != -1 && i != vcpu)
-                   continue;
-
-               bit = lastbit = isInvert = false;
-               lastcpu = -1;
-
-               vshPrint(ctl, "%4d: ", i);
-               for (cpu = 0; cpu < maxcpu; cpu++) {
-
-                   bit = VIR_CPU_USABLE(cpumaps, cpumaplen, i, cpu);
-
-                   isInvert = (bit ^ lastbit);
-                   if (bit && isInvert) {
-                       if (lastcpu == -1)
-                           vshPrint(ctl, "%d", cpu);
-                       else
-                           vshPrint(ctl, ",%d", cpu);
-                       lastcpu = cpu;
-                   }
-                   if (!bit && isInvert && lastcpu != cpu - 1)
-                       vshPrint(ctl, "-%d", cpu - 1);
-                   lastbit = bit;
-               }
-               if (bit && !isInvert) {
-                  vshPrint(ctl, "-%d", maxcpu - 1);
-               }
-               vshPrint(ctl, "\n");
-            }
-
-        } else {
-            ret = false;
-        }
-        VIR_FREE(cpumaps);
-        goto cleanup;
-    }
-
-    /* Pin mode: pinning specified vcpu to specified physical cpus*/
-
-    cpumap = vshCalloc(ctl, cpumaplen, sizeof(cpumap));
-    /* Parse cpulist */
-    cur = cpulist;
-    if (*cur == 0) {
-        goto parse_error;
-    } else if (*cur == 'r') {
-        for (cpu = 0; cpu < maxcpu; cpu++)
-            VIR_USE_CPU(cpumap, cpu);
-        cur = "";
-    }
-
-    while (*cur != 0) {
-
-        /* the char '^' denotes exclusive */
-        if (*cur == '^') {
-            cur++;
-            unuse = true;
-        }
+    qsort(&names[0], numfilters, sizeof(char *), vshNameSorter);
 
-        /* parse physical CPU number */
-        if (!c_isdigit(*cur))
-            goto parse_error;
-        cpu  = virParseNumber(&cur);
-        if (cpu < 0) {
-            goto parse_error;
-        }
-        if (cpu >= maxcpu) {
-            vshError(ctl, _("Physical CPU %d doesn't exist."), cpu);
-            goto parse_error;
-        }
-        virSkipSpaces(&cur);
+    vshPrintExtra(ctl, "%-36s  %-20s \n", _("UUID"), _("Name"));
+    vshPrintExtra(ctl,
+       "----------------------------------------------------------------\n");
 
-        if (*cur == ',' || *cur == 0) {
-            if (unuse) {
-                VIR_UNUSE_CPU(cpumap, cpu);
-            } else {
-                VIR_USE_CPU(cpumap, cpu);
-            }
-        } else if (*cur == '-') {
-            /* the char '-' denotes range */
-            if (unuse) {
-                goto parse_error;
-            }
-            cur++;
-            virSkipSpaces(&cur);
-            /* parse the end of range */
-            lastcpu = virParseNumber(&cur);
-            if (lastcpu < cpu) {
-                goto parse_error;
-            }
-            if (lastcpu >= maxcpu) {
-                vshError(ctl, _("Physical CPU %d doesn't exist."), maxcpu);
-                goto parse_error;
-            }
-            for (i = cpu; i <= lastcpu; i++) {
-                VIR_USE_CPU(cpumap, i);
-            }
-            virSkipSpaces(&cur);
-        }
+    for (i = 0; i < numfilters; i++) {
+        virNWFilterPtr nwfilter =
+            virNWFilterLookupByName(ctl->conn, names[i]);
 
-        if (*cur == ',') {
-            cur++;
-            virSkipSpaces(&cur);
-            unuse = false;
-        } else if (*cur == 0) {
-            break;
-        } else {
-            goto parse_error;
+        /* this kind of work with networks is not atomic operation */
+        if (!nwfilter) {
+            VIR_FREE(names[i]);
+            continue;
         }
-    }
 
-    if (flags == -1) {
-        if (virDomainPinVcpu(dom, vcpu, cpumap, cpumaplen) != 0) {
-            ret = false;
-        }
-    } else {
-        if (virDomainPinVcpuFlags(dom, vcpu, cpumap, cpumaplen, flags) != 0) {
-            ret = false;
-        }
+        virNWFilterGetUUIDString(nwfilter, uuid);
+        vshPrint(ctl, "%-36s  %-20s\n",
+                 uuid,
+                 virNWFilterGetName(nwfilter));
+        virNWFilterFree(nwfilter);
+        VIR_FREE(names[i]);
     }
 
-cleanup:
-    VIR_FREE(cpumap);
-    virDomainFree(dom);
-    return ret;
-
-parse_error:
-    vshError(ctl, "%s", _("cpulist: Invalid format."));
-    ret = false;
-    goto cleanup;
+    VIR_FREE(names);
+    return true;
 }
 
+
 /*
- * "setvcpus" command
+ * "nwfilter-edit" command
  */
-static const vshCmdInfo info_setvcpus[] = {
-    {"help", N_("change number of virtual CPUs")},
-    {"desc", N_("Change the number of virtual CPUs in the guest domain.")},
+static const vshCmdInfo info_nwfilter_edit[] = {
+    {"help", N_("edit XML configuration for a network filter")},
+    {"desc", N_("Edit the XML configuration for a network filter.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_setvcpus[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"count", VSH_OT_INT, VSH_OFLAG_REQ, N_("number of virtual CPUs")},
-    {"maximum", VSH_OT_BOOL, 0, N_("set maximum limit on next boot")},
-    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
-    {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
-    {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
+static const vshCmdOptDef opts_nwfilter_edit[] = {
+    {"nwfilter", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network filter name or uuid")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdSetvcpus(vshControl *ctl, const vshCmd *cmd)
+cmdNWFilterEdit(vshControl *ctl, const vshCmd *cmd)
 {
-    virDomainPtr dom;
-    int count = 0;
-    bool ret = true;
-    bool maximum = vshCommandOptBool(cmd, "maximum");
-    bool config = vshCommandOptBool(cmd, "config");
-    bool live = vshCommandOptBool(cmd, "live");
-    bool current = vshCommandOptBool(cmd, "current");
-    unsigned int flags = 0;
-
-    if (current) {
-        if (live || config) {
-            vshError(ctl, "%s", _("--current must be specified exclusively"));
-            return false;
-        }
-        flags = VIR_DOMAIN_AFFECT_CURRENT;
-    } else {
-        if (config)
-            flags |= VIR_DOMAIN_AFFECT_CONFIG;
-        if (live)
-            flags |= VIR_DOMAIN_AFFECT_LIVE;
-        /* neither option is specified */
-        if (!live && !config && !maximum)
-            flags = -1;
-    }
+    bool ret = false;
+    virNWFilterPtr nwfilter = NULL;
+    virNWFilterPtr nwfilter_edited = NULL;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
-        return false;
-
-    if (vshCommandOptInt(cmd, "count", &count) < 0 || count <= 0) {
-        vshError(ctl, "%s", _("Invalid number of virtual CPUs"));
         goto cleanup;
-    }
 
-    if (flags == -1) {
-        if (virDomainSetVcpus(dom, count) != 0) {
-            ret = false;
-        }
-    } else {
-        /* If the --maximum flag was given, we need to ensure only the
-           --config flag is in effect as well */
-        if (maximum) {
-            vshDebug(ctl, VSH_ERR_DEBUG, "--maximum flag was given\n");
+    nwfilter = vshCommandOptNWFilter(ctl, cmd, NULL);
+    if (nwfilter == NULL)
+        goto cleanup;
 
-            flags |= VIR_DOMAIN_VCPU_MAXIMUM;
+#define EDIT_GET_XML virNWFilterGetXMLDesc(nwfilter, 0)
+#define EDIT_NOT_CHANGED \
+    vshPrint(ctl, _("Network filter %s XML "            \
+                    "configuration not changed.\n"),    \
+             virNWFilterGetName(nwfilter));             \
+    ret = true; goto edit_cleanup;
+#define EDIT_DEFINE \
+    (nwfilter_edited = virNWFilterDefineXML(ctl->conn, doc_edited))
+#define EDIT_FREE \
+    if (nwfilter_edited)    \
+        virNWFilterFree(nwfilter);
+#include "virsh-edit.c"
 
-            /* If neither the --config nor --live flags were given, OR
-               if just the --live flag was given, we need to error out
-               warning the user that the --maximum flag can only be used
-               with the --config flag */
-            if (live || !config) {
+    vshPrint(ctl, _("Network filter %s XML configuration edited.\n"),
+             virNWFilterGetName(nwfilter_edited));
 
-                /* Warn the user about the invalid flag combination */
-                vshError(ctl, _("--maximum must be used with --config only"));
-                ret = false;
-                goto cleanup;
-            }
-        }
+    ret = true;
 
-        /* Apply the virtual cpu changes */
-        if (virDomainSetVcpusFlags(dom, count, flags) < 0) {
-            ret = false;
-        }
-    }
+cleanup:
+    if (nwfilter)
+        virNWFilterFree(nwfilter);
+    if (nwfilter_edited)
+        virNWFilterFree(nwfilter_edited);
 
-  cleanup:
-    virDomainFree(dom);
     return ret;
 }
 
+
+/**************************************************************************/
 /*
- * "cpu-stats" command
+ * "pool-autostart" command
  */
-static const vshCmdInfo info_cpu_stats[] = {
-    {"help", N_("show domain cpu statistics")},
+static const vshCmdInfo info_pool_autostart[] = {
+    {"help", N_("autostart a pool")},
     {"desc",
-     N_("Display per-CPU and total statistics about the domain's CPUs")},
-    {NULL, NULL},
+     N_("Configure a pool to be automatically started at boot.")},
+    {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_cpu_stats[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"total", VSH_OT_BOOL, 0, N_("Show total statistics only")},
-    {"start", VSH_OT_INT, 0, N_("Show statistics from this CPU")},
-    {"count", VSH_OT_INT, 0, N_("Number of shown CPUs at most")},
-    {NULL, 0, 0, NULL},
+static const vshCmdOptDef opts_pool_autostart[] = {
+    {"pool",  VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
+    {"disable", VSH_OT_BOOL, 0, N_("disable autostarting")},
+    {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdCPUStats(vshControl *ctl, const vshCmd *cmd)
+cmdPoolAutostart(vshControl *ctl, const vshCmd *cmd)
 {
-    virDomainPtr dom;
-    virTypedParameterPtr params = NULL;
-    int i, j, pos, max_id, cpu = -1, show_count = -1, nparams;
-    bool show_total = false, show_per_cpu = false;
-    unsigned int flags = 0;
+    virStoragePoolPtr pool;
+    const char *name;
+    int autostart;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
         return false;
 
-    show_total = vshCommandOptBool(cmd, "total");
-    if (vshCommandOptInt(cmd, "start", &cpu) > 0)
-        show_per_cpu = true;
-    if (vshCommandOptInt(cmd, "count", &show_count) > 0)
-        show_per_cpu = true;
-
-    /* default show per_cpu and total */
-    if (!show_total && !show_per_cpu) {
-        show_total = true;
-        show_per_cpu = true;
-    }
-
-    if (!show_per_cpu) /* show total stats only */
-        goto do_show_total;
-
-    /* check cpu, show_count, and ignore wrong argument */
-    if (cpu < 0)
-        cpu = 0;
-
-    /* get number of cpus on the node */
-    if ((max_id = virDomainGetCPUStats(dom, NULL, 0, 0, 0, flags)) < 0)
-        goto failed_stats;
-    if (show_count < 0 || show_count > max_id)
-        show_count = max_id;
-
-    /* get percpu information */
-    if ((nparams = virDomainGetCPUStats(dom, NULL, 0, 0, 1, flags)) < 0)
-        goto failed_stats;
-
-    if (!nparams) {
-        vshPrint(ctl, "%s", _("No per-CPU stats available"));
-        goto do_show_total;
-    }
-
-    if (VIR_ALLOC_N(params, nparams * MIN(show_count, 128)) < 0)
-        goto failed_params;
-
-    while (show_count) {
-        int ncpus = MIN(show_count, 128);
-
-        if (virDomainGetCPUStats(dom, params, nparams, cpu, ncpus, flags) < 0)
-            goto failed_stats;
-
-        for (i = 0; i < ncpus; i++) {
-            if (params[i * nparams].type == 0) /* this cpu is not in the map */
-                continue;
-            vshPrint(ctl, "CPU%d:\n", cpu + i);
-
-            for (j = 0; j < nparams; j++) {
-                pos = i * nparams + j;
-                vshPrint(ctl, "\t%-12s ", params[pos].field);
-                if ((STREQ(params[pos].field, VIR_DOMAIN_CPU_STATS_CPUTIME) ||
-                     STREQ(params[pos].field, VIR_DOMAIN_CPU_STATS_VCPUTIME)) &&
-                    params[j].type == VIR_TYPED_PARAM_ULLONG) {
-                    vshPrint(ctl, "%9lld.%09lld seconds\n",
-                             params[pos].value.ul / 1000000000,
-                             params[pos].value.ul % 1000000000);
-                } else {
-                    const char *s = vshGetTypedParamValue(ctl, &params[pos]);
-                    vshPrint(ctl, _("%s\n"), s);
-                    VIR_FREE(s);
-                }
-            }
-        }
-        cpu += ncpus;
-        show_count -= ncpus;
-        virTypedParameterArrayClear(params, nparams * ncpus);
-    }
-    VIR_FREE(params);
-
-do_show_total:
-    if (!show_total)
-        goto cleanup;
-
-    /* get supported num of parameter for total statistics */
-    if ((nparams = virDomainGetCPUStats(dom, NULL, 0, -1, 1, flags)) < 0)
-        goto failed_stats;
+    autostart = !vshCommandOptBool(cmd, "disable");
 
-    if (!nparams) {
-        vshPrint(ctl, "%s", _("No total stats available"));
-        goto cleanup;
+    if (virStoragePoolSetAutostart(pool, autostart) < 0) {
+        if (autostart)
+            vshError(ctl, _("failed to mark pool %s as autostarted"), name);
+        else
+            vshError(ctl, _("failed to unmark pool %s as autostarted"), name);
+        virStoragePoolFree(pool);
+        return false;
     }
 
-    if (VIR_ALLOC_N(params, nparams))
-        goto failed_params;
-
-    /* passing start_cpu == -1 gives us domain's total status */
-    if ((nparams = virDomainGetCPUStats(dom, params, nparams, -1, 1, flags)) < 0)
-        goto failed_stats;
-
-    vshPrint(ctl, _("Total:\n"));
-    for (i = 0; i < nparams; i++) {
-        vshPrint(ctl, "\t%-12s ", params[i].field);
-        if ((STREQ(params[i].field, VIR_DOMAIN_CPU_STATS_CPUTIME) ||
-             STREQ(params[i].field, VIR_DOMAIN_CPU_STATS_USERTIME) ||
-             STREQ(params[i].field, VIR_DOMAIN_CPU_STATS_SYSTEMTIME)) &&
-            params[i].type == VIR_TYPED_PARAM_ULLONG) {
-            vshPrint(ctl, "%9lld.%09lld seconds\n",
-                     params[i].value.ul / 1000000000,
-                     params[i].value.ul % 1000000000);
-        } else {
-            char *s = vshGetTypedParamValue(ctl, &params[i]);
-            vshPrint(ctl, "%s\n", s);
-            VIR_FREE(s);
-        }
-    }
-    virTypedParameterArrayClear(params, nparams);
-    VIR_FREE(params);
+    if (autostart)
+        vshPrint(ctl, _("Pool %s marked as autostarted\n"), name);
+    else
+        vshPrint(ctl, _("Pool %s unmarked as autostarted\n"), name);
 
-cleanup:
-    virDomainFree(dom);
+    virStoragePoolFree(pool);
     return true;
-
-failed_params:
-    virReportOOMError();
-    virDomainFree(dom);
-    return false;
-
-failed_stats:
-    vshError(ctl, _("Failed to virDomainGetCPUStats()\n"));
-    VIR_FREE(params);
-    virDomainFree(dom);
-    return false;
 }
 
 /*
- * "inject-nmi" command
+ * "pool-create" command
  */
-static const vshCmdInfo info_inject_nmi[] = {
-    {"help", N_("Inject NMI to the guest")},
-    {"desc", N_("Inject NMI to the guest domain.")},
+static const vshCmdInfo info_pool_create[] = {
+    {"help", N_("create a pool from an XML file")},
+    {"desc", N_("Create a pool.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_inject_nmi[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+static const vshCmdOptDef opts_pool_create[] = {
+    {"file", VSH_OT_DATA, VSH_OFLAG_REQ,
+     N_("file containing an XML pool description")},
     {NULL, 0, 0, NULL}
 };
 
-
 static bool
-cmdInjectNMI(vshControl *ctl, const vshCmd *cmd)
+cmdPoolCreate(vshControl *ctl, const vshCmd *cmd)
 {
-    virDomainPtr dom;
-    int ret = true;
+    virStoragePoolPtr pool;
+    const char *from = NULL;
+    bool ret = true;
+    char *buffer;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+    if (vshCommandOptString(cmd, "file", &from) <= 0)
+        return false;
+
+    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
         return false;
 
-    if (virDomainInjectNMI(dom, 0) < 0)
-            ret = false;
+    pool = virStoragePoolCreateXML(ctl->conn, buffer, 0);
+    VIR_FREE(buffer);
 
-    virDomainFree(dom);
+    if (pool != NULL) {
+        vshPrint(ctl, _("Pool %s created from %s\n"),
+                 virStoragePoolGetName(pool), from);
+        virStoragePoolFree(pool);
+    } else {
+        vshError(ctl, _("Failed to create pool from %s"), from);
+        ret = false;
+    }
     return ret;
 }
 
+
 /*
- * "send-key" command
+ * "nodedev-create" command
  */
-static const vshCmdInfo info_send_key[] = {
-    {"help", N_("Send keycodes to the guest")},
-    {"desc", N_("Send keycodes (integers or symbolic names) to the guest")},
+static const vshCmdInfo info_node_device_create[] = {
+    {"help", N_("create a device defined "
+                          "by an XML file on the node")},
+    {"desc", N_("Create a device on the node.  Note that this "
+                          "command creates devices on the physical host "
+                          "that can then be assigned to a virtual machine.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_send_key[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"codeset", VSH_OT_STRING, VSH_OFLAG_REQ_OPT,
-     N_("the codeset of keycodes, default:linux")},
-    {"holdtime", VSH_OT_INT, VSH_OFLAG_REQ_OPT,
-     N_("the time (in milliseconds) how long the keys will be held")},
-    {"keycode", VSH_OT_ARGV, VSH_OFLAG_REQ, N_("the key code")},
+static const vshCmdOptDef opts_node_device_create[] = {
+    {"file", VSH_OT_DATA, VSH_OFLAG_REQ,
+     N_("file containing an XML description of the device")},
     {NULL, 0, 0, NULL}
 };
 
-static int
-get_integer_keycode(const char *key_name)
-{
-    unsigned int val;
-
-    if (virStrToLong_ui(key_name, NULL, 0, &val) < 0 || val > 0xffff || !val)
-        return -1;
-    return val;
-}
-
 static bool
-cmdSendKey(vshControl *ctl, const vshCmd *cmd)
+cmdNodeDeviceCreate(vshControl *ctl, const vshCmd *cmd)
 {
-    virDomainPtr dom;
-    int ret = false;
-    const char *codeset_option;
-    int codeset;
-    int holdtime;
-    int count = 0;
-    const vshCmdOpt *opt = NULL;
-    int keycode;
-    unsigned int keycodes[VIR_DOMAIN_SEND_KEY_MAX_KEYS];
+    virNodeDevicePtr dev = NULL;
+    const char *from = NULL;
+    bool ret = true;
+    char *buffer;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+    if (vshCommandOptString(cmd, "file", &from) <= 0)
         return false;
 
-    if (vshCommandOptString(cmd, "codeset", &codeset_option) <= 0)
-        codeset_option = "linux";
-
-    if (vshCommandOptInt(cmd, "holdtime", &holdtime) <= 0)
-        holdtime = 0;
-
-    codeset = virKeycodeSetTypeFromString(codeset_option);
-    if ((int)codeset < 0) {
-        vshError(ctl, _("unknown codeset: '%s'"), codeset_option);
-        goto cleanup;
-    }
-
-    while ((opt = vshCommandOptArgv(cmd, opt))) {
-        if (count == VIR_DOMAIN_SEND_KEY_MAX_KEYS) {
-            vshError(ctl, _("too many keycodes"));
-            goto cleanup;
-        }
+    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
+        return false;
 
-        if ((keycode = get_integer_keycode(opt->data)) <= 0) {
-            if ((keycode = virKeycodeValueFromString(codeset, opt->data)) <= 0) {
-                vshError(ctl, _("invalid keycode: '%s'"), opt->data);
-                goto cleanup;
-            }
-        }
+    dev = virNodeDeviceCreateXML(ctl->conn, buffer, 0);
+    VIR_FREE(buffer);
 
-        keycodes[count] = keycode;
-        count++;
+    if (dev != NULL) {
+        vshPrint(ctl, _("Node device %s created from %s\n"),
+                 virNodeDeviceGetName(dev), from);
+        virNodeDeviceFree(dev);
+    } else {
+        vshError(ctl, _("Failed to create node device from %s"), from);
+        ret = false;
     }
 
-    if (!(virDomainSendKey(dom, codeset, holdtime, keycodes, count, 0) < 0))
-        ret = true;
-
-cleanup:
-    virDomainFree(dom);
     return ret;
 }
 
+
 /*
- * "setmem" command
+ * "nodedev-destroy" command
  */
-static const vshCmdInfo info_setmem[] = {
-    {"help", N_("change memory allocation")},
-    {"desc", N_("Change the current memory allocation in the guest domain.")},
+static const vshCmdInfo info_node_device_destroy[] = {
+    {"help", N_("destroy (stop) a device on the node")},
+    {"desc", N_("Destroy a device on the node.  Note that this "
+                "command destroys devices on the physical host")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_setmem[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"kilobytes", VSH_OT_ALIAS, 0, "size"},
-    {"size", VSH_OT_INT, VSH_OFLAG_REQ,
-     N_("new memory size, as scaled integer (default KiB)")},
-    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
-    {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
-    {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
+static const vshCmdOptDef opts_node_device_destroy[] = {
+    {"name", VSH_OT_DATA, VSH_OFLAG_REQ,
+     N_("name of the device to be destroyed")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdSetmem(vshControl *ctl, const vshCmd *cmd)
+cmdNodeDeviceDestroy(vshControl *ctl, const vshCmd *cmd)
 {
-    virDomainPtr dom;
-    unsigned long long bytes = 0;
-    unsigned long long max;
-    unsigned long kibibytes = 0;
+    virNodeDevicePtr dev = NULL;
     bool ret = true;
-    bool config = vshCommandOptBool(cmd, "config");
-    bool live = vshCommandOptBool(cmd, "live");
-    bool current = vshCommandOptBool(cmd, "current");
-    unsigned int flags = 0;
-
-    if (current) {
-        if (live || config) {
-            vshError(ctl, "%s", _("--current must be specified exclusively"));
-            return false;
-        }
-        flags = VIR_DOMAIN_AFFECT_CURRENT;
-    } else {
-        if (config)
-            flags |= VIR_DOMAIN_AFFECT_CONFIG;
-        if (live)
-            flags |= VIR_DOMAIN_AFFECT_LIVE;
-        /* neither option is specified */
-        if (!live && !config)
-            flags = -1;
-    }
+    const char *name = NULL;
 
-    if (!vshConnectionUsability(ctl, ctl->conn))
+    if (!vshConnectionUsability(ctl, ctl->conn)) {
         return false;
+    }
 
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+    if (vshCommandOptString(cmd, "name", &name) <= 0)
         return false;
 
-    /* The API expects 'unsigned long' KiB, so depending on whether we
-     * are 32-bit or 64-bit determines the maximum we can use.  */
-    if (sizeof(kibibytes) < sizeof(max))
-        max = 1024ull * ULONG_MAX;
-    else
-        max = ULONG_MAX;
-    if (vshCommandOptScaledInt(cmd, "size", &bytes, 1024, max) < 0) {
-        vshError(ctl, "%s", _("memory size has to be a number"));
-        virDomainFree(dom);
-        return false;
-    }
-    kibibytes = VIR_DIV_UP(bytes, 1024);
+    dev = virNodeDeviceLookupByName(ctl->conn, name);
 
-    if (flags == -1) {
-        if (virDomainSetMemory(dom, kibibytes) != 0) {
-            ret = false;
-        }
+    if (virNodeDeviceDestroy(dev) == 0) {
+        vshPrint(ctl, _("Destroyed node device '%s'\n"), name);
     } else {
-        if (virDomainSetMemoryFlags(dom, kibibytes, flags) < 0) {
-            ret = false;
-        }
+        vshError(ctl, _("Failed to destroy node device '%s'"), name);
+        ret = false;
     }
 
-    virDomainFree(dom);
+    virNodeDeviceFree(dev);
     return ret;
 }
 
+
 /*
- * "setmaxmem" command
+ * XML Building helper for pool-define-as and pool-create-as
  */
-static const vshCmdInfo info_setmaxmem[] = {
-    {"help", N_("change maximum memory limit")},
-    {"desc", N_("Change the maximum memory allocation limit in the guest domain.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_setmaxmem[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"kilobytes", VSH_OT_ALIAS, 0, "size"},
-    {"size", VSH_OT_INT, VSH_OFLAG_REQ,
-     N_("new maximum memory size, as scaled integer (default KiB)")},
-    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
-    {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
-    {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
+static const vshCmdOptDef opts_pool_X_as[] = {
+    {"name", VSH_OT_DATA, VSH_OFLAG_REQ, N_("name of the pool")},
+    {"print-xml", VSH_OT_BOOL, 0, N_("print XML document, but don't define/create")},
+    {"type", VSH_OT_DATA, VSH_OFLAG_REQ, N_("type of the pool")},
+    {"source-host", VSH_OT_DATA, 0, N_("source-host for underlying storage")},
+    {"source-path", VSH_OT_DATA, 0, N_("source path for underlying storage")},
+    {"source-dev", VSH_OT_DATA, 0, N_("source device for underlying storage")},
+    {"source-name", VSH_OT_DATA, 0, N_("source name for underlying storage")},
+    {"target", VSH_OT_DATA, 0, N_("target for underlying storage")},
+    {"source-format", VSH_OT_STRING, 0, N_("format for underlying storage")},
     {NULL, 0, 0, NULL}
 };
 
-static bool
-cmdSetmaxmem(vshControl *ctl, const vshCmd *cmd)
-{
-    virDomainPtr dom;
-    unsigned long long bytes = 0;
-    unsigned long long max;
-    unsigned long kibibytes = 0;
-    bool ret = true;
-    bool config = vshCommandOptBool(cmd, "config");
-    bool live = vshCommandOptBool(cmd, "live");
-    bool current = vshCommandOptBool(cmd, "current");
-    unsigned int flags = VIR_DOMAIN_MEM_MAXIMUM;
+static int buildPoolXML(const vshCmd *cmd, const char **retname, char **xml) {
 
-    if (current) {
-        if (live || config) {
-            vshError(ctl, "%s", _("--current must be specified exclusively"));
-            return false;
-        }
-    } else {
-        if (config)
-            flags |= VIR_DOMAIN_AFFECT_CONFIG;
-        if (live)
-            flags |= VIR_DOMAIN_AFFECT_LIVE;
-        /* neither option is specified */
-        if (!live && !config)
-            flags = -1;
+    const char *name = NULL, *type = NULL, *srcHost = NULL, *srcPath = NULL,
+               *srcDev = NULL, *srcName = NULL, *srcFormat = NULL, *target = NULL;
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+
+    if (vshCommandOptString(cmd, "name", &name) <= 0)
+        goto cleanup;
+    if (vshCommandOptString(cmd, "type", &type) <= 0)
+        goto cleanup;
+
+    if (vshCommandOptString(cmd, "source-host", &srcHost) < 0 ||
+        vshCommandOptString(cmd, "source-path", &srcPath) < 0 ||
+        vshCommandOptString(cmd, "source-dev", &srcDev) < 0 ||
+        vshCommandOptString(cmd, "source-name", &srcName) < 0 ||
+        vshCommandOptString(cmd, "source-format", &srcFormat) < 0 ||
+        vshCommandOptString(cmd, "target", &target) < 0) {
+        vshError(NULL, "%s", _("missing argument"));
+        goto cleanup;
     }
 
-    if (!vshConnectionUsability(ctl, ctl->conn))
+    virBufferAsprintf(&buf, "<pool type='%s'>\n", type);
+    virBufferAsprintf(&buf, "  <name>%s</name>\n", name);
+    if (srcHost || srcPath || srcDev || srcFormat || srcName) {
+        virBufferAddLit(&buf, "  <source>\n");
+
+        if (srcHost)
+            virBufferAsprintf(&buf, "    <host name='%s'/>\n", srcHost);
+        if (srcPath)
+            virBufferAsprintf(&buf, "    <dir path='%s'/>\n", srcPath);
+        if (srcDev)
+            virBufferAsprintf(&buf, "    <device path='%s'/>\n", srcDev);
+        if (srcFormat)
+            virBufferAsprintf(&buf, "    <format type='%s'/>\n", srcFormat);
+        if (srcName)
+            virBufferAsprintf(&buf, "    <name>%s</name>\n", srcName);
+
+        virBufferAddLit(&buf, "  </source>\n");
+    }
+    if (target) {
+        virBufferAddLit(&buf, "  <target>\n");
+        virBufferAsprintf(&buf, "    <path>%s</path>\n", target);
+        virBufferAddLit(&buf, "  </target>\n");
+    }
+    virBufferAddLit(&buf, "</pool>\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;
+}
+
+/*
+ * "pool-create-as" command
+ */
+static const vshCmdInfo info_pool_create_as[] = {
+    {"help", N_("create a pool from a set of args")},
+    {"desc", N_("Create a pool.")},
+    {NULL, NULL}
+};
+
+static bool
+cmdPoolCreateAs(vshControl *ctl, const vshCmd *cmd)
+{
+    virStoragePoolPtr pool;
+    const char *name;
+    char *xml;
+    bool printXML = vshCommandOptBool(cmd, "print-xml");
 
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+    if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    /* The API expects 'unsigned long' KiB, so depending on whether we
-     * are 32-bit or 64-bit determines the maximum we can use.  */
-    if (sizeof(kibibytes) < sizeof(max))
-        max = 1024ull * ULONG_MAX;
-    else
-        max = ULONG_MAX;
-    if (vshCommandOptScaledInt(cmd, "size", &bytes, 1024, max) < 0) {
-        vshError(ctl, "%s", _("memory size has to be a number"));
-        virDomainFree(dom);
+    if (!buildPoolXML(cmd, &name, &xml))
         return false;
-    }
-    kibibytes = VIR_DIV_UP(bytes, 1024);
 
-    if (flags == -1) {
-        if (virDomainSetMaxMemory(dom, kibibytes) != 0) {
-            vshError(ctl, "%s", _("Unable to change MaxMemorySize"));
-            ret = false;
-        }
+    if (printXML) {
+        vshPrint(ctl, "%s", xml);
+        VIR_FREE(xml);
     } else {
-        if (virDomainSetMemoryFlags(dom, kibibytes, flags) < 0) {
-            vshError(ctl, "%s", _("Unable to change MaxMemorySize"));
-            ret = false;
+        pool = virStoragePoolCreateXML(ctl->conn, xml, 0);
+        VIR_FREE(xml);
+
+        if (pool != NULL) {
+            vshPrint(ctl, _("Pool %s created\n"), name);
+            virStoragePoolFree(pool);
+        } else {
+            vshError(ctl, _("Failed to create pool %s"), name);
+            return false;
         }
     }
-
-    virDomainFree(dom);
-    return ret;
+    return true;
 }
 
+
 /*
- * "blkiotune" command
+ * "pool-define" command
  */
-static const vshCmdInfo info_blkiotune[] = {
-    {"help", N_("Get or set blkio parameters")},
-    {"desc", N_("Get or set the current blkio parameters for a guest"
-                " domain.\n"
-                "    To get the blkio parameters use following command: \n\n"
-                "    virsh # blkiotune <domain>")},
+static const vshCmdInfo info_pool_define[] = {
+    {"help", N_("define (but don't start) a pool from an XML file")},
+    {"desc", N_("Define a pool.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_blkiotune[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"weight", VSH_OT_INT, VSH_OFLAG_NONE,
-     N_("IO Weight in range [100, 1000]")},
-    {"device-weights", VSH_OT_STRING, VSH_OFLAG_NONE,
-     N_("per-device IO Weights, in the form of /path/to/device,weight,...")},
-    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
-    {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
-    {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
+static const vshCmdOptDef opts_pool_define[] = {
+    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML pool description")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdBlkiotune(vshControl * ctl, const vshCmd * cmd)
+cmdPoolDefine(vshControl *ctl, const vshCmd *cmd)
 {
-    virDomainPtr dom;
-    const char *device_weight = NULL;
-    int weight = 0;
-    int nparams = 0;
-    int rv = 0;
-    unsigned int i = 0;
-    virTypedParameterPtr params = NULL, temp = NULL;
-    bool ret = false;
-    unsigned int flags = 0;
-    bool current = vshCommandOptBool(cmd, "current");
-    bool config = vshCommandOptBool(cmd, "config");
-    bool live = vshCommandOptBool(cmd, "live");
-
-    if (current) {
-        if (live || config) {
-            vshError(ctl, "%s", _("--current must be specified exclusively"));
-            return false;
-        }
-        flags = VIR_DOMAIN_AFFECT_CURRENT;
-    } else {
-        if (config)
-            flags |= VIR_DOMAIN_AFFECT_CONFIG;
-        if (live)
-            flags |= VIR_DOMAIN_AFFECT_LIVE;
-    }
+    virStoragePoolPtr pool;
+    const char *from = NULL;
+    bool ret = true;
+    char *buffer;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+    if (vshCommandOptString(cmd, "file", &from) <= 0)
         return false;
 
-    if ((rv = vshCommandOptInt(cmd, "weight", &weight)) < 0) {
-        vshError(ctl, "%s",
-                 _("Unable to parse integer parameter"));
-        goto cleanup;
-    }
+    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
+        return false;
 
-    if (rv > 0) {
-        nparams++;
-        if (weight <= 0) {
-            vshError(ctl, _("Invalid value of %d for I/O weight"), weight);
-            goto cleanup;
-        }
-    }
+    pool = virStoragePoolDefineXML(ctl->conn, buffer, 0);
+    VIR_FREE(buffer);
 
-    rv = vshCommandOptString(cmd, "device-weights", &device_weight);
-    if (rv < 0) {
-        vshError(ctl, "%s",
-                 _("Unable to parse string parameter"));
-        goto cleanup;
-    }
-    if (rv > 0) {
-        nparams++;
+    if (pool != NULL) {
+        vshPrint(ctl, _("Pool %s defined from %s\n"),
+                 virStoragePoolGetName(pool), from);
+        virStoragePoolFree(pool);
+    } else {
+        vshError(ctl, _("Failed to define pool from %s"), from);
+        ret = false;
     }
+    return ret;
+}
 
-    if (nparams == 0) {
-        /* get the number of blkio parameters */
-        if (virDomainGetBlkioParameters(dom, NULL, &nparams, flags) != 0) {
-            vshError(ctl, "%s",
-                     _("Unable to get number of blkio parameters"));
-            goto cleanup;
-        }
 
-        if (nparams == 0) {
-            /* nothing to output */
-            ret = true;
-            goto cleanup;
-        }
+/*
+ * "pool-define-as" command
+ */
+static const vshCmdInfo info_pool_define_as[] = {
+    {"help", N_("define a pool from a set of args")},
+    {"desc", N_("Define a pool.")},
+    {NULL, NULL}
+};
 
-        /* now go get all the blkio parameters */
-        params = vshCalloc(ctl, nparams, sizeof(*params));
-        if (virDomainGetBlkioParameters(dom, params, &nparams, flags) != 0) {
-            vshError(ctl, "%s", _("Unable to get blkio parameters"));
-            goto cleanup;
-        }
+static bool
+cmdPoolDefineAs(vshControl *ctl, const vshCmd *cmd)
+{
+    virStoragePoolPtr pool;
+    const char *name;
+    char *xml;
+    bool printXML = vshCommandOptBool(cmd, "print-xml");
 
-        for (i = 0; i < nparams; i++) {
-            char *str = vshGetTypedParamValue(ctl, &params[i]);
-            vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
-            VIR_FREE(str);
-        }
-    } else {
-        /* set the blkio parameters */
-        params = vshCalloc(ctl, nparams, sizeof(*params));
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
 
-        for (i = 0; i < nparams; i++) {
-            temp = &params[i];
-            temp->type = VIR_TYPED_PARAM_UINT;
+    if (!buildPoolXML(cmd, &name, &xml))
+        return false;
 
-            if (weight) {
-                temp->value.ui = weight;
-                if (!virStrcpy(temp->field, VIR_DOMAIN_BLKIO_WEIGHT,
-                               sizeof(temp->field)))
-                    goto cleanup;
-                weight = 0;
-            } else if (device_weight) {
-                temp->value.s = vshStrdup(ctl, device_weight);
-                temp->type = VIR_TYPED_PARAM_STRING;
-                if (!virStrcpy(temp->field, VIR_DOMAIN_BLKIO_DEVICE_WEIGHT,
-                               sizeof(temp->field)))
-                    goto cleanup;
-                device_weight = NULL;
-            }
-        }
+    if (printXML) {
+        vshPrint(ctl, "%s", xml);
+        VIR_FREE(xml);
+    } else {
+        pool = virStoragePoolDefineXML(ctl->conn, xml, 0);
+        VIR_FREE(xml);
 
-        if (virDomainSetBlkioParameters(dom, params, nparams, flags) < 0) {
-            vshError(ctl, "%s", _("Unable to change blkio parameters"));
-            goto cleanup;
+        if (pool != NULL) {
+            vshPrint(ctl, _("Pool %s defined\n"), name);
+            virStoragePoolFree(pool);
+        } else {
+            vshError(ctl, _("Failed to define pool %s"), name);
+            return false;
         }
     }
-
-    ret = true;
-
-  cleanup:
-    virTypedParameterArrayClear(params, nparams);
-    VIR_FREE(params);
-    virDomainFree(dom);
-    return ret;
+    return true;
 }
 
+
 /*
- * "memtune" command
+ * "pool-build" command
  */
-static const vshCmdInfo info_memtune[] = {
-    {"help", N_("Get or set memory parameters")},
-    {"desc", N_("Get or set the current memory parameters for a guest"
-                " domain.\n"
-                "    To get the memory parameters use following command: \n\n"
-                "    virsh # memtune <domain>")},
+static const vshCmdInfo info_pool_build[] = {
+    {"help", N_("build a pool")},
+    {"desc", N_("Build a given pool.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_memtune[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"hard-limit", VSH_OT_INT, VSH_OFLAG_NONE,
-     N_("Max memory, as scaled integer (default KiB)")},
-    {"soft-limit", VSH_OT_INT, VSH_OFLAG_NONE,
-     N_("Memory during contention, as scaled integer (default KiB)")},
-    {"swap-hard-limit", VSH_OT_INT, VSH_OFLAG_NONE,
-     N_("Max memory plus swap, as scaled integer (default KiB)")},
-    {"min-guarantee", VSH_OT_INT, VSH_OFLAG_NONE,
-     N_("Min guaranteed memory, as scaled integer (default KiB)")},
-    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
-    {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
-    {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
+static const vshCmdOptDef opts_pool_build[] = {
+    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
+    {"no-overwrite", VSH_OT_BOOL, 0, N_("do not overwrite an existing pool of this type")},
+    {"overwrite", VSH_OT_BOOL, 0, N_("overwrite any existing data")},
     {NULL, 0, 0, NULL}
 };
 
-static int
-vshMemtuneGetSize(const vshCmd *cmd, const char *name, long long *value)
-{
-    int ret;
-    unsigned long long tmp;
-    const char *str;
-    char *end;
-
-    ret = vshCommandOptString(cmd, name, &str);
-    if (ret <= 0)
-        return ret;
-    if (virStrToLong_ll(str, &end, 10, value) < 0)
-        return -1;
-    if (*value < 0) {
-        *value = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
-        return 1;
-    }
-    tmp = *value;
-    if (virScaleInteger(&tmp, end, 1024, LLONG_MAX) < 0)
-        return -1;
-    *value = VIR_DIV_UP(tmp, 1024);
-    return 0;
-}
-
 static bool
-cmdMemtune(vshControl *ctl, const vshCmd *cmd)
+cmdPoolBuild(vshControl *ctl, const vshCmd *cmd)
 {
-    virDomainPtr dom;
-    long long hard_limit = 0, soft_limit = 0, swap_hard_limit = 0;
-    long long min_guarantee = 0;
-    int nparams = 0;
-    unsigned int i = 0;
-    virTypedParameterPtr params = NULL, temp = NULL;
-    bool ret = false;
+    virStoragePoolPtr pool;
+    bool ret = true;
+    const char *name;
     unsigned int flags = 0;
-    bool current = vshCommandOptBool(cmd, "current");
-    bool config = vshCommandOptBool(cmd, "config");
-    bool live = vshCommandOptBool(cmd, "live");
-
-    if (current) {
-        if (live || config) {
-            vshError(ctl, "%s", _("--current must be specified exclusively"));
-            return false;
-        }
-        flags = VIR_DOMAIN_AFFECT_CURRENT;
-    } else {
-        if (config)
-            flags |= VIR_DOMAIN_AFFECT_CONFIG;
-        if (live)
-            flags |= VIR_DOMAIN_AFFECT_LIVE;
-    }
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
         return false;
 
-    if (vshMemtuneGetSize(cmd, "hard-limit", &hard_limit) < 0 ||
-        vshMemtuneGetSize(cmd, "soft-limit", &soft_limit) < 0 ||
-        vshMemtuneGetSize(cmd, "swap-hard-limit", &swap_hard_limit) < 0 ||
-        vshMemtuneGetSize(cmd, "min-guarantee", &min_guarantee) < 0) {
-        vshError(ctl, "%s",
-                 _("Unable to parse integer parameter"));
-        goto cleanup;
+    if (vshCommandOptBool(cmd, "no-overwrite")) {
+        flags |= VIR_STORAGE_POOL_BUILD_NO_OVERWRITE;
     }
 
-    if (hard_limit)
-        nparams++;
+    if (vshCommandOptBool(cmd, "overwrite")) {
+        flags |= VIR_STORAGE_POOL_BUILD_OVERWRITE;
+    }
 
-    if (soft_limit)
-        nparams++;
+    if (virStoragePoolBuild(pool, flags) == 0) {
+        vshPrint(ctl, _("Pool %s built\n"), name);
+    } else {
+        vshError(ctl, _("Failed to build pool %s"), name);
+        ret = false;
+    }
 
-    if (swap_hard_limit)
-        nparams++;
+    virStoragePoolFree(pool);
 
-    if (min_guarantee)
-        nparams++;
+    return ret;
+}
 
-    if (nparams == 0) {
-        /* get the number of memory parameters */
-        if (virDomainGetMemoryParameters(dom, NULL, &nparams, flags) != 0) {
-            vshError(ctl, "%s",
-                     _("Unable to get number of memory parameters"));
-            goto cleanup;
-        }
-
-        if (nparams == 0) {
-            /* nothing to output */
-            ret = true;
-            goto cleanup;
-        }
-
-        /* now go get all the memory parameters */
-        params = vshCalloc(ctl, nparams, sizeof(*params));
-        if (virDomainGetMemoryParameters(dom, params, &nparams, flags) != 0) {
-            vshError(ctl, "%s", _("Unable to get memory parameters"));
-            goto cleanup;
-        }
+/*
+ * "pool-destroy" command
+ */
+static const vshCmdInfo info_pool_destroy[] = {
+    {"help", N_("destroy (stop) a pool")},
+    {"desc",
+     N_("Forcefully stop a given pool. Raw data in the pool is untouched")},
+    {NULL, NULL}
+};
 
-        for (i = 0; i < nparams; i++) {
-            if (params[i].type == VIR_TYPED_PARAM_ULLONG &&
-                params[i].value.ul == VIR_DOMAIN_MEMORY_PARAM_UNLIMITED) {
-                vshPrint(ctl, "%-15s: %s\n", params[i].field, _("unlimited"));
-            } else {
-                char *str = vshGetTypedParamValue(ctl, &params[i]);
-                vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
-                VIR_FREE(str);
-            }
-        }
+static const vshCmdOptDef opts_pool_destroy[] = {
+    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
+    {NULL, 0, 0, NULL}
+};
 
-        ret = true;
-    } else {
-        /* set the memory parameters */
-        params = vshCalloc(ctl, nparams, sizeof(*params));
+static bool
+cmdPoolDestroy(vshControl *ctl, const vshCmd *cmd)
+{
+    virStoragePoolPtr pool;
+    bool ret = true;
+    const char *name;
 
-        for (i = 0; i < nparams; i++) {
-            temp = &params[i];
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
 
-            /*
-             * Some magic here, this is used to fill the params structure with
-             * the valid arguments passed, after filling the particular
-             * argument we purposely make them 0, so on the next pass it goes
-             * to the next valid argument and so on.
-             */
-            if (soft_limit) {
-                if (virTypedParameterAssign(temp,
-                                            VIR_DOMAIN_MEMORY_SOFT_LIMIT,
-                                            VIR_TYPED_PARAM_ULLONG,
-                                            soft_limit) < 0)
-                    goto error;
-                soft_limit = 0;
-            } else if (hard_limit) {
-                if (virTypedParameterAssign(temp,
-                                            VIR_DOMAIN_MEMORY_HARD_LIMIT,
-                                            VIR_TYPED_PARAM_ULLONG,
-                                            hard_limit) < 0)
-                    goto error;
-                hard_limit = 0;
-            } else if (swap_hard_limit) {
-                if (virTypedParameterAssign(temp,
-                                            VIR_DOMAIN_MEMORY_SWAP_HARD_LIMIT,
-                                            VIR_TYPED_PARAM_ULLONG,
-                                            swap_hard_limit) < 0)
-                    goto error;
-                swap_hard_limit = 0;
-            } else if (min_guarantee) {
-                if (virTypedParameterAssign(temp,
-                                            VIR_DOMAIN_MEMORY_MIN_GUARANTEE,
-                                            VIR_TYPED_PARAM_ULLONG,
-                                            min_guarantee) < 0)
-                    goto error;
-                min_guarantee = 0;
-            }
+    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
+        return false;
 
-            /* If the user has passed -1, we interpret it as unlimited */
-            if (temp->value.ul == -1)
-                temp->value.ul = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
-        }
-        if (virDomainSetMemoryParameters(dom, params, nparams, flags) != 0)
-            goto error;
-        else
-            ret = true;
+    if (virStoragePoolDestroy(pool) == 0) {
+        vshPrint(ctl, _("Pool %s destroyed\n"), name);
+    } else {
+        vshError(ctl, _("Failed to destroy pool %s"), name);
+        ret = false;
     }
 
-cleanup:
-    VIR_FREE(params);
-    virDomainFree(dom);
+    virStoragePoolFree(pool);
     return ret;
-
-error:
-    vshError(ctl, "%s", _("Unable to change memory parameters"));
-    goto cleanup;
 }
 
+
 /*
- * "numatune" command
+ * "pool-delete" command
  */
-static const vshCmdInfo info_numatune[] = {
-    {"help", N_("Get or set numa parameters")},
-    {"desc", N_("Get or set the current numa parameters for a guest"
-                " domain.\n"
-                "    To get the numa parameters use following command: \n\n"
-                "    virsh # numatune <domain>")},
+static const vshCmdInfo info_pool_delete[] = {
+    {"help", N_("delete a pool")},
+    {"desc", N_("Delete a given pool.")},
     {NULL, NULL}
-
 };
 
-static const vshCmdOptDef opts_numatune[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"mode", VSH_OT_DATA, VSH_OFLAG_NONE,
-     N_("NUMA mode, one of strict, preferred and interleave")},
-    {"nodeset", VSH_OT_DATA, VSH_OFLAG_NONE,
-     N_("NUMA node selections to set")},
-    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
-    {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
-    {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
+static const vshCmdOptDef opts_pool_delete[] = {
+    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdNumatune(vshControl * ctl, const vshCmd * cmd)
+cmdPoolDelete(vshControl *ctl, const vshCmd *cmd)
 {
-    virDomainPtr dom;
-    int nparams = 0;
-    unsigned int i = 0;
-    virTypedParameterPtr params = NULL, temp = NULL;
-    const char *nodeset = NULL;
-    bool ret = false;
-    unsigned int flags = 0;
-    bool current = vshCommandOptBool(cmd, "current");
-    bool config = vshCommandOptBool(cmd, "config");
-    bool live = vshCommandOptBool(cmd, "live");
-    const char *mode = NULL;
-
-    if (current) {
-        if (live || config) {
-            vshError(ctl, "%s", _("--current must be specified exclusively"));
-            return false;
-        }
-        flags = VIR_DOMAIN_AFFECT_CURRENT;
-    } else {
-        if (config)
-            flags |= VIR_DOMAIN_AFFECT_CONFIG;
-        if (live)
-            flags |= VIR_DOMAIN_AFFECT_LIVE;
-    }
+    virStoragePoolPtr pool;
+    bool ret = true;
+    const char *name;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
-        return false;
-
-    if (vshCommandOptString(cmd, "nodeset", &nodeset) < 0) {
-        vshError(ctl, "%s", _("Unable to parse nodeset."));
-        virDomainFree(dom);
-        return false;
-    }
-    if (nodeset)
-        nparams++;
-    if (vshCommandOptString(cmd, "mode", &mode) < 0) {
-        vshError(ctl, "%s", _("Unable to parse mode."));
-        virDomainFree(dom);
+    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
         return false;
-    }
-    if (mode)
-        nparams++;
-
-    if (nparams == 0) {
-        /* get the number of numa parameters */
-        if (virDomainGetNumaParameters(dom, NULL, &nparams, flags) != 0) {
-            vshError(ctl, "%s",
-                     _("Unable to get number of memory parameters"));
-            goto cleanup;
-        }
-
-        if (nparams == 0) {
-            /* nothing to output */
-            ret = true;
-            goto cleanup;
-        }
-
-        /* now go get all the numa parameters */
-        params = vshCalloc(ctl, nparams, sizeof(*params));
-        if (virDomainGetNumaParameters(dom, params, &nparams, flags) != 0) {
-            vshError(ctl, "%s", _("Unable to get numa parameters"));
-            goto cleanup;
-        }
 
-        for (i = 0; i < nparams; i++) {
-            if (params[i].type == VIR_TYPED_PARAM_INT &&
-                STREQ(params[i].field, VIR_DOMAIN_NUMA_MODE)) {
-                vshPrint(ctl, "%-15s: %s\n", params[i].field,
-                         virDomainNumatuneMemModeTypeToString(params[i].value.i));
-            } else {
-                char *str = vshGetTypedParamValue(ctl, &params[i]);
-                vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
-                VIR_FREE(str);
-            }
-        }
-
-        ret = true;
+    if (virStoragePoolDelete(pool, 0) == 0) {
+        vshPrint(ctl, _("Pool %s deleted\n"), name);
     } else {
-        /* set the numa parameters */
-        params = vshCalloc(ctl, nparams, sizeof(*params));
-
-        for (i = 0; i < nparams; i++) {
-            temp = &params[i];
-
-            /*
-             * Some magic here, this is used to fill the params structure with
-             * the valid arguments passed, after filling the particular
-             * argument we purposely make them 0, so on the next pass it goes
-             * to the next valid argument and so on.
-             */
-            if (mode) {
-                /* Accept string or integer, in case server
-                 * understands newer integer than what strings we were
-                 * compiled with */
-                if ((temp->value.i =
-                    virDomainNumatuneMemModeTypeFromString(mode)) < 0 &&
-                    virStrToLong_i(mode, NULL, 0, &temp->value.i) < 0) {
-                    vshError(ctl, _("Invalid mode: %s"), mode);
-                    goto cleanup;
-                }
-                if (!virStrcpy(temp->field, VIR_DOMAIN_NUMA_MODE,
-                               sizeof(temp->field)))
-                    goto cleanup;
-                temp->type = VIR_TYPED_PARAM_INT;
-                mode = NULL;
-            } else if (nodeset) {
-                temp->value.s = vshStrdup(ctl, nodeset);
-                temp->type = VIR_TYPED_PARAM_STRING;
-                if (!virStrcpy(temp->field, VIR_DOMAIN_NUMA_NODESET,
-                               sizeof(temp->field)))
-                    goto cleanup;
-                nodeset = NULL;
-            }
-        }
-        if (virDomainSetNumaParameters(dom, params, nparams, flags) != 0)
-            vshError(ctl, "%s", _("Unable to change numa parameters"));
-        else
-            ret = true;
+        vshError(ctl, _("Failed to delete pool %s"), name);
+        ret = false;
     }
 
-  cleanup:
-    virTypedParameterArrayClear(params, nparams);
-    VIR_FREE(params);
-    virDomainFree(dom);
+    virStoragePoolFree(pool);
     return ret;
 }
 
+
 /*
- * "nodeinfo" command
+ * "pool-refresh" command
  */
-static const vshCmdInfo info_nodeinfo[] = {
-    {"help", N_("node information")},
-    {"desc", N_("Returns basic information about the node.")},
+static const vshCmdInfo info_pool_refresh[] = {
+    {"help", N_("refresh a pool")},
+    {"desc", N_("Refresh a given pool.")},
     {NULL, NULL}
 };
 
+static const vshCmdOptDef opts_pool_refresh[] = {
+    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
+    {NULL, 0, 0, NULL}
+};
+
 static bool
-cmdNodeinfo(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
+cmdPoolRefresh(vshControl *ctl, const vshCmd *cmd)
 {
-    virNodeInfo info;
+    virStoragePoolPtr pool;
+    bool ret = true;
+    const char *name;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (virNodeGetInfo(ctl->conn, &info) < 0) {
-        vshError(ctl, "%s", _("failed to get node information"));
+    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
         return false;
+
+    if (virStoragePoolRefresh(pool, 0) == 0) {
+        vshPrint(ctl, _("Pool %s refreshed\n"), name);
+    } else {
+        vshError(ctl, _("Failed to refresh pool %s"), name);
+        ret = false;
     }
-    vshPrint(ctl, "%-20s %s\n", _("CPU model:"), info.model);
-    vshPrint(ctl, "%-20s %d\n", _("CPU(s):"), info.cpus);
-    vshPrint(ctl, "%-20s %d MHz\n", _("CPU frequency:"), info.mhz);
-    vshPrint(ctl, "%-20s %d\n", _("CPU socket(s):"), info.sockets);
-    vshPrint(ctl, "%-20s %d\n", _("Core(s) per socket:"), info.cores);
-    vshPrint(ctl, "%-20s %d\n", _("Thread(s) per core:"), info.threads);
-    vshPrint(ctl, "%-20s %d\n", _("NUMA cell(s):"), info.nodes);
-    vshPrint(ctl, "%-20s %lu KiB\n", _("Memory size:"), info.memory);
+    virStoragePoolFree(pool);
 
-    return true;
+    return ret;
 }
 
+
 /*
- * "nodecpustats" command
+ * "pool-dumpxml" command
  */
-static const vshCmdInfo info_nodecpustats[] = {
-    {"help", N_("Prints cpu stats of the node.")},
-    {"desc", N_("Returns cpu stats of the node, in nanoseconds.")},
+static const vshCmdInfo info_pool_dumpxml[] = {
+    {"help", N_("pool information in XML")},
+    {"desc", N_("Output the pool information as an XML dump to stdout.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_node_cpustats[] = {
-    {"cpu", VSH_OT_INT, 0, N_("prints specified cpu statistics only.")},
-    {"percent", VSH_OT_BOOL, 0, N_("prints by percentage during 1 second.")},
+static const vshCmdOptDef opts_pool_dumpxml[] = {
+    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
+    {"inactive", VSH_OT_BOOL, 0, N_("show inactive defined XML")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdNodeCpuStats(vshControl *ctl, const vshCmd *cmd)
+cmdPoolDumpXML(vshControl *ctl, const vshCmd *cmd)
 {
-    int i, j;
-    bool flag_utilization = false;
-    bool flag_percent = vshCommandOptBool(cmd, "percent");
-    int cpuNum = VIR_NODE_CPU_STATS_ALL_CPUS;
-    virNodeCPUStatsPtr params;
-    int nparams = 0;
-    bool ret = false;
-    struct cpu_stats {
-        unsigned long long user;
-        unsigned long long sys;
-        unsigned long long idle;
-        unsigned long long iowait;
-        unsigned long long util;
-    } cpu_stats[2];
-    double user_time, sys_time, idle_time, iowait_time, total_time;
-    double usage;
+    virStoragePoolPtr pool;
+    bool ret = true;
+    bool inactive = vshCommandOptBool(cmd, "inactive");
+    unsigned int flags = 0;
+    char *dump;
+
+    if (inactive)
+        flags |= VIR_STORAGE_XML_INACTIVE;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (vshCommandOptInt(cmd, "cpu", &cpuNum) < 0) {
-        vshError(ctl, "%s", _("Invalid value of cpuNum"));
+    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", NULL)))
         return false;
-    }
 
-    if (virNodeGetCPUStats(ctl->conn, cpuNum, NULL, &nparams, 0) != 0) {
-        vshError(ctl, "%s",
-                 _("Unable to get number of cpu stats"));
-        return false;
-    }
-    if (nparams == 0) {
-        /* nothing to output */
-        return true;
+    dump = virStoragePoolGetXMLDesc(pool, flags);
+    if (dump != NULL) {
+        vshPrint(ctl, "%s", dump);
+        VIR_FREE(dump);
+    } else {
+        ret = false;
     }
 
-    memset(cpu_stats, 0, sizeof(cpu_stats));
-    params = vshCalloc(ctl, nparams, sizeof(*params));
+    virStoragePoolFree(pool);
+    return ret;
+}
 
-    for (i = 0; i < 2; i++) {
-        if (i > 0)
-            sleep(1);
 
-        if (virNodeGetCPUStats(ctl->conn, cpuNum, params, &nparams, 0) != 0) {
-            vshError(ctl, "%s", _("Unable to get node cpu stats"));
-            goto cleanup;
-        }
+/*
+ * "pool-list" command
+ */
+static const vshCmdInfo info_pool_list[] = {
+    {"help", N_("list pools")},
+    {"desc", N_("Returns list of pools.")},
+    {NULL, NULL}
+};
 
-        for (j = 0; j < nparams; j++) {
-            unsigned long long value = params[j].value;
-
-            if (STREQ(params[j].field, VIR_NODE_CPU_STATS_KERNEL)) {
-                cpu_stats[i].sys = value;
-            } else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_USER)) {
-                cpu_stats[i].user = value;
-            } else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_IDLE)) {
-                cpu_stats[i].idle = value;
-            } else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_IOWAIT)) {
-                cpu_stats[i].iowait = value;
-            } else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_UTILIZATION)) {
-                cpu_stats[i].util = value;
-                flag_utilization = true;
-            }
-        }
-
-        if (flag_utilization || !flag_percent)
-            break;
-    }
-
-    if (!flag_percent) {
-        if (!flag_utilization) {
-            vshPrint(ctl, "%-15s %20llu\n", _("user:"), cpu_stats[0].user);
-            vshPrint(ctl, "%-15s %20llu\n", _("system:"), cpu_stats[0].sys);
-            vshPrint(ctl, "%-15s %20llu\n", _("idle:"), cpu_stats[0].idle);
-            vshPrint(ctl, "%-15s %20llu\n", _("iowait:"), cpu_stats[0].iowait);
-        }
-    } else {
-        if (flag_utilization) {
-            usage = cpu_stats[0].util;
-
-            vshPrint(ctl, "%-15s %5.1lf%%\n", _("usage:"), usage);
-            vshPrint(ctl, "%-15s %5.1lf%%\n", _("idle:"), 100 - usage);
-        } else {
-            user_time   = cpu_stats[1].user   - cpu_stats[0].user;
-            sys_time    = cpu_stats[1].sys    - cpu_stats[0].sys;
-            idle_time   = cpu_stats[1].idle   - cpu_stats[0].idle;
-            iowait_time = cpu_stats[1].iowait - cpu_stats[0].iowait;
-            total_time  = user_time + sys_time + idle_time + iowait_time;
-
-            usage = (user_time + sys_time) / total_time * 100;
-
-            vshPrint(ctl, "%-15s %5.1lf%%\n",
-                     _("usage:"), usage);
-            vshPrint(ctl, "%-15s %5.1lf%%\n",
-                     _("user:"), user_time / total_time * 100);
-            vshPrint(ctl, "%-15s %5.1lf%%\n",
-                     _("system:"), sys_time  / total_time * 100);
-            vshPrint(ctl, "%-15s %5.1lf%%\n",
-                     _("idle:"), idle_time     / total_time * 100);
-            vshPrint(ctl, "%-15s %5.1lf%%\n",
-                     _("iowait:"), iowait_time   / total_time * 100);
-        }
-    }
-
-    ret = true;
-
-  cleanup:
-    VIR_FREE(params);
-    return ret;
-}
-
-/*
- * "nodememstats" command
- */
-static const vshCmdInfo info_nodememstats[] = {
-    {"help", N_("Prints memory stats of the node.")},
-    {"desc", N_("Returns memory stats of the node, in kilobytes.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_node_memstats[] = {
-    {"cell", VSH_OT_INT, 0, N_("prints specified cell statistics only.")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNodeMemStats(vshControl *ctl, const vshCmd *cmd)
-{
-    int nparams = 0;
-    unsigned int i = 0;
-    int cellNum = VIR_NODE_MEMORY_STATS_ALL_CELLS;
-    virNodeMemoryStatsPtr params = NULL;
-    bool ret = false;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (vshCommandOptInt(cmd, "cell", &cellNum) < 0) {
-        vshError(ctl, "%s", _("Invalid value of cellNum"));
-        return false;
-    }
-
-    /* get the number of memory parameters */
-    if (virNodeGetMemoryStats(ctl->conn, cellNum, NULL, &nparams, 0) != 0) {
-        vshError(ctl, "%s",
-                 _("Unable to get number of memory stats"));
-        goto cleanup;
-    }
-
-    if (nparams == 0) {
-        /* nothing to output */
-        ret = true;
-        goto cleanup;
-    }
-
-    /* now go get all the memory parameters */
-    params = vshCalloc(ctl, nparams, sizeof(*params));
-    if (virNodeGetMemoryStats(ctl->conn, cellNum, params, &nparams, 0) != 0) {
-        vshError(ctl, "%s", _("Unable to get memory stats"));
-        goto cleanup;
-    }
-
-    for (i = 0; i < nparams; i++)
-        vshPrint(ctl, "%-7s: %20llu KiB\n", params[i].field, params[i].value);
-
-    ret = true;
-
-  cleanup:
-    VIR_FREE(params);
-    return ret;
-}
-
-/*
- * "nodesuspend" command
- */
-static const vshCmdInfo info_nodesuspend[] = {
-    {"help", N_("suspend the host node for a given time duration")},
-    {"desc", N_("Suspend the host node for a given time duration "
-                               "and attempt to resume thereafter.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_node_suspend[] = {
-    {"target", VSH_OT_DATA, VSH_OFLAG_REQ, N_("mem(Suspend-to-RAM), "
-                                               "disk(Suspend-to-Disk), hybrid(Hybrid-Suspend)")},
-    {"duration", VSH_OT_INT, VSH_OFLAG_REQ, N_("Suspend duration in seconds")},
-    {"flags", VSH_OT_INT, VSH_OFLAG_NONE, N_("Suspend flags, 0 for default")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNodeSuspend(vshControl *ctl, const vshCmd *cmd)
-{
-    const char *target = NULL;
-    unsigned int suspendTarget;
-    long long duration;
-    unsigned int flags = 0;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (vshCommandOptString(cmd, "target", &target) < 0) {
-        vshError(ctl, _("Invalid target argument"));
-        return false;
-    }
-
-    if (vshCommandOptLongLong(cmd, "duration", &duration) < 0) {
-        vshError(ctl, _("Invalid duration argument"));
-        return false;
-    }
-
-    if (vshCommandOptUInt(cmd, "flags", &flags) < 0) {
-        vshError(ctl, _("Invalid flags argument"));
-        return false;
-    }
-
-    if (STREQ(target, "mem"))
-        suspendTarget = VIR_NODE_SUSPEND_TARGET_MEM;
-    else if (STREQ(target, "disk"))
-        suspendTarget = VIR_NODE_SUSPEND_TARGET_DISK;
-    else if (STREQ(target, "hybrid"))
-        suspendTarget = VIR_NODE_SUSPEND_TARGET_HYBRID;
-    else {
-        vshError(ctl, "%s", _("Invalid target"));
-        return false;
-    }
-
-    if (duration <= 0) {
-        vshError(ctl, "%s", _("Invalid duration"));
-        return false;
-    }
-
-    if (virNodeSuspendForDuration(ctl->conn, suspendTarget, duration,
-                                  flags) < 0) {
-        vshError(ctl, "%s", _("The host was not suspended"));
-        return false;
-    }
-    return true;
-}
-
-
-/*
- * "capabilities" command
- */
-static const vshCmdInfo info_capabilities[] = {
-    {"help", N_("capabilities")},
-    {"desc", N_("Returns capabilities of hypervisor/driver.")},
-    {NULL, NULL}
-};
-
-static bool
-cmdCapabilities(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
-{
-    char *caps;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if ((caps = virConnectGetCapabilities(ctl->conn)) == NULL) {
-        vshError(ctl, "%s", _("failed to get capabilities"));
-        return false;
-    }
-    vshPrint(ctl, "%s\n", caps);
-    VIR_FREE(caps);
-
-    return true;
-}
-
-/*
- * "dumpxml" command
- */
-static const vshCmdInfo info_dumpxml[] = {
-    {"help", N_("domain information in XML")},
-    {"desc", N_("Output the domain information as an XML dump to stdout.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_dumpxml[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"inactive", VSH_OT_BOOL, 0, N_("show inactive defined XML")},
-    {"security-info", VSH_OT_BOOL, 0, N_("include security sensitive information in XML dump")},
-    {"update-cpu", VSH_OT_BOOL, 0, N_("update guest CPU according to host CPU")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdDumpXML(vshControl *ctl, const vshCmd *cmd)
-{
-    virDomainPtr dom;
-    bool ret = true;
-    char *dump;
-    unsigned int flags = 0;
-    bool inactive = vshCommandOptBool(cmd, "inactive");
-    bool secure = vshCommandOptBool(cmd, "security-info");
-    bool update = vshCommandOptBool(cmd, "update-cpu");
-
-    if (inactive)
-        flags |= VIR_DOMAIN_XML_INACTIVE;
-    if (secure)
-        flags |= VIR_DOMAIN_XML_SECURE;
-    if (update)
-        flags |= VIR_DOMAIN_XML_UPDATE_CPU;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
-        return false;
-
-    dump = virDomainGetXMLDesc(dom, flags);
-    if (dump != NULL) {
-        vshPrint(ctl, "%s", dump);
-        VIR_FREE(dump);
-    } else {
-        ret = false;
-    }
-
-    virDomainFree(dom);
-    return ret;
-}
-
-/*
- * "domxml-from-native" command
- */
-static const vshCmdInfo info_domxmlfromnative[] = {
-    {"help", N_("Convert native config to domain XML")},
-    {"desc", N_("Convert native guest configuration format to domain XML format.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_domxmlfromnative[] = {
-    {"format", VSH_OT_DATA, VSH_OFLAG_REQ, N_("source config data format")},
-    {"config", VSH_OT_DATA, VSH_OFLAG_REQ, N_("config data file to import from")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdDomXMLFromNative(vshControl *ctl, const vshCmd *cmd)
-{
-    bool ret = true;
-    const char *format = NULL;
-    const char *configFile = NULL;
-    char *configData;
-    char *xmlData;
-    unsigned int flags = 0;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (vshCommandOptString(cmd, "format", &format) < 0 ||
-        vshCommandOptString(cmd, "config", &configFile) < 0)
-        return false;
-
-    if (virFileReadAll(configFile, 1024*1024, &configData) < 0)
-        return false;
-
-    xmlData = virConnectDomainXMLFromNative(ctl->conn, format, configData, flags);
-    if (xmlData != NULL) {
-        vshPrint(ctl, "%s", xmlData);
-        VIR_FREE(xmlData);
-    } else {
-        ret = false;
-    }
-
-    VIR_FREE(configData);
-    return ret;
-}
-
-/*
- * "domxml-to-native" command
- */
-static const vshCmdInfo info_domxmltonative[] = {
-    {"help", N_("Convert domain XML to native config")},
-    {"desc", N_("Convert domain XML config to a native guest configuration format.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_domxmltonative[] = {
-    {"format", VSH_OT_DATA, VSH_OFLAG_REQ, N_("target config data type format")},
-    {"xml", VSH_OT_DATA, VSH_OFLAG_REQ, N_("xml data file to export from")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdDomXMLToNative(vshControl *ctl, const vshCmd *cmd)
-{
-    bool ret = true;
-    const char *format = NULL;
-    const char *xmlFile = NULL;
-    char *configData;
-    char *xmlData;
-    unsigned int flags = 0;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (vshCommandOptString(cmd, "format", &format) < 0
-        || vshCommandOptString(cmd, "xml", &xmlFile) < 0)
-        return false;
-
-    if (virFileReadAll(xmlFile, 1024*1024, &xmlData) < 0)
-        return false;
-
-    configData = virConnectDomainXMLToNative(ctl->conn, format, xmlData, flags);
-    if (configData != NULL) {
-        vshPrint(ctl, "%s", configData);
-        VIR_FREE(configData);
-    } else {
-        ret = false;
-    }
-
-    VIR_FREE(xmlData);
-    return ret;
-}
-
-/*
- * "domname" command
- */
-static const vshCmdInfo info_domname[] = {
-    {"help", N_("convert a domain id or UUID to domain name")},
-    {"desc", ""},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_domname[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain id or uuid")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdDomname(vshControl *ctl, const vshCmd *cmd)
-{
-    virDomainPtr dom;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-    if (!(dom = vshCommandOptDomainBy(ctl, cmd, NULL,
-                                      VSH_BYID|VSH_BYUUID)))
-        return false;
-
-    vshPrint(ctl, "%s\n", virDomainGetName(dom));
-    virDomainFree(dom);
-    return true;
-}
-
-/*
- * "domid" command
- */
-static const vshCmdInfo info_domid[] = {
-    {"help", N_("convert a domain name or UUID to domain id")},
-    {"desc", ""},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_domid[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name or uuid")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdDomid(vshControl *ctl, const vshCmd *cmd)
-{
-    virDomainPtr dom;
-    unsigned int id;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-    if (!(dom = vshCommandOptDomainBy(ctl, cmd, NULL,
-                                      VSH_BYNAME|VSH_BYUUID)))
-        return false;
-
-    id = virDomainGetID(dom);
-    if (id == ((unsigned int)-1))
-        vshPrint(ctl, "%s\n", "-");
-    else
-        vshPrint(ctl, "%d\n", id);
-    virDomainFree(dom);
-    return true;
-}
-
-/*
- * "domuuid" command
- */
-static const vshCmdInfo info_domuuid[] = {
-    {"help", N_("convert a domain name or id to domain UUID")},
-    {"desc", ""},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_domuuid[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain id or name")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdDomuuid(vshControl *ctl, const vshCmd *cmd)
-{
-    virDomainPtr dom;
-    char uuid[VIR_UUID_STRING_BUFLEN];
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-    if (!(dom = vshCommandOptDomainBy(ctl, cmd, NULL,
-                                      VSH_BYNAME|VSH_BYID)))
-        return false;
-
-    if (virDomainGetUUIDString(dom, uuid) != -1)
-        vshPrint(ctl, "%s\n", uuid);
-    else
-        vshError(ctl, "%s", _("failed to get domain UUID"));
-
-    virDomainFree(dom);
-    return true;
-}
-
-/*
- * "migrate" command
- */
-static const vshCmdInfo info_migrate[] = {
-    {"help", N_("migrate domain to another host")},
-    {"desc", N_("Migrate domain to another host.  Add --live for live migration.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_migrate[] = {
-    {"live", VSH_OT_BOOL, 0, N_("live migration")},
-    {"p2p", VSH_OT_BOOL, 0, N_("peer-2-peer migration")},
-    {"direct", VSH_OT_BOOL, 0, N_("direct migration")},
-    {"tunneled", VSH_OT_ALIAS, 0, "tunnelled"},
-    {"tunnelled", VSH_OT_BOOL, 0, N_("tunnelled migration")},
-    {"persistent", VSH_OT_BOOL, 0, N_("persist VM on destination")},
-    {"undefinesource", VSH_OT_BOOL, 0, N_("undefine VM on source")},
-    {"suspend", VSH_OT_BOOL, 0, N_("do not restart the domain on the destination host")},
-    {"copy-storage-all", VSH_OT_BOOL, 0, N_("migration with non-shared storage with full disk copy")},
-    {"copy-storage-inc", VSH_OT_BOOL, 0, N_("migration with non-shared storage with incremental copy (same base image shared between source and destination)")},
-    {"change-protection", VSH_OT_BOOL, 0,
-     N_("prevent any configuration changes to domain until migration ends)")},
-    {"unsafe", VSH_OT_BOOL, 0, N_("force migration even if it may be unsafe")},
-    {"verbose", VSH_OT_BOOL, 0, N_("display the progress of migration")},
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"desturi", VSH_OT_DATA, VSH_OFLAG_REQ, N_("connection URI of the destination host as seen from the client(normal migration) or source(p2p migration)")},
-    {"migrateuri", VSH_OT_DATA, 0, N_("migration URI, usually can be omitted")},
-    {"dname", VSH_OT_DATA, 0, N_("rename to new name during migration (if supported)")},
-    {"timeout", VSH_OT_INT, 0, N_("force guest to suspend if live migration exceeds timeout (in seconds)")},
-    {"xml", VSH_OT_STRING, 0, N_("filename containing updated XML for the target")},
-    {NULL, 0, 0, NULL}
-};
-
-static void
-doMigrate(void *opaque)
-{
-    char ret = '1';
-    virDomainPtr dom = NULL;
-    const char *desturi = NULL;
-    const char *migrateuri = NULL;
-    const char *dname = NULL;
-    unsigned int flags = 0;
-    vshCtrlData *data = opaque;
-    vshControl *ctl = data->ctl;
-    const vshCmd *cmd = data->cmd;
-    const char *xmlfile = NULL;
-    char *xml = NULL;
-    sigset_t sigmask, oldsigmask;
-
-    sigemptyset(&sigmask);
-    sigaddset(&sigmask, SIGINT);
-    if (pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask) < 0)
-        goto out_sig;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        goto out;
-
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
-        goto out;
-
-    if (vshCommandOptString(cmd, "desturi", &desturi) <= 0 ||
-        vshCommandOptString(cmd, "migrateuri", &migrateuri) < 0 ||
-        vshCommandOptString(cmd, "dname", &dname) < 0) {
-        vshError(ctl, "%s", _("missing argument"));
-        goto out;
-    }
-
-    if (vshCommandOptString(cmd, "xml", &xmlfile) < 0) {
-        vshError(ctl, "%s", _("malformed xml argument"));
-        goto out;
-    }
-
-    if (vshCommandOptBool(cmd, "live"))
-        flags |= VIR_MIGRATE_LIVE;
-    if (vshCommandOptBool(cmd, "p2p"))
-        flags |= VIR_MIGRATE_PEER2PEER;
-    if (vshCommandOptBool(cmd, "tunnelled"))
-        flags |= VIR_MIGRATE_TUNNELLED;
-
-    if (vshCommandOptBool(cmd, "persistent"))
-        flags |= VIR_MIGRATE_PERSIST_DEST;
-    if (vshCommandOptBool(cmd, "undefinesource"))
-        flags |= VIR_MIGRATE_UNDEFINE_SOURCE;
-
-    if (vshCommandOptBool(cmd, "suspend"))
-        flags |= VIR_MIGRATE_PAUSED;
-
-    if (vshCommandOptBool(cmd, "copy-storage-all"))
-        flags |= VIR_MIGRATE_NON_SHARED_DISK;
-
-    if (vshCommandOptBool(cmd, "copy-storage-inc"))
-        flags |= VIR_MIGRATE_NON_SHARED_INC;
-
-    if (vshCommandOptBool(cmd, "change-protection"))
-        flags |= VIR_MIGRATE_CHANGE_PROTECTION;
-
-    if (vshCommandOptBool(cmd, "unsafe"))
-        flags |= VIR_MIGRATE_UNSAFE;
-
-    if (xmlfile &&
-        virFileReadAll(xmlfile, 8192, &xml) < 0) {
-        vshError(ctl, _("file '%s' doesn't exist"), xmlfile);
-        goto out;
-    }
-
-    if ((flags & VIR_MIGRATE_PEER2PEER) ||
-        vshCommandOptBool(cmd, "direct")) {
-        /* For peer2peer migration or direct migration we only expect one URI
-         * a libvirt URI, or a hypervisor specific URI. */
-
-        if (migrateuri != NULL) {
-            vshError(ctl, "%s", _("migrate: Unexpected migrateuri for peer2peer/direct migration"));
-            goto out;
-        }
-
-        if (virDomainMigrateToURI2(dom, desturi, NULL, xml, flags, dname, 0) == 0)
-            ret = '0';
-    } else {
-        /* For traditional live migration, connect to the destination host directly. */
-        virConnectPtr dconn = NULL;
-        virDomainPtr ddom = NULL;
-
-        dconn = virConnectOpenAuth(desturi, virConnectAuthPtrDefault, 0);
-        if (!dconn) goto out;
-
-        ddom = virDomainMigrate2(dom, dconn, xml, flags, dname, migrateuri, 0);
-        if (ddom) {
-            virDomainFree(ddom);
-            ret = '0';
-        }
-        virConnectClose(dconn);
-    }
-
-out:
-    pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
-out_sig:
-    if (dom) virDomainFree(dom);
-    VIR_FREE(xml);
-    ignore_value(safewrite(data->writefd, &ret, sizeof(ret)));
-}
-
-static void
-print_job_progress(const char *label, unsigned long long remaining,
-                   unsigned long long total)
-{
-    int progress;
-
-    if (total == 0)
-        /* migration has not been started */
-        return;
-
-    if (remaining == 0) {
-        /* migration has completed */
-        progress = 100;
-    } else {
-        /* use float to avoid overflow */
-        progress = (int)(100.0 - remaining * 100.0 / total);
-        if (progress >= 100) {
-            /* migration has not completed, do not print [100 %] */
-            progress = 99;
-        }
-    }
-
-    /* see comments in vshError about why we must flush */
-    fflush(stdout);
-    fprintf(stderr, "\r%s: [%3d %%]", label, progress);
-    fflush(stderr);
-}
-
-static void
-vshMigrationTimeout(vshControl *ctl,
-                    virDomainPtr dom,
-                    void *opaque ATTRIBUTE_UNUSED)
-{
-    vshDebug(ctl, VSH_ERR_DEBUG, "suspending the domain, "
-             "since migration timed out\n");
-    virDomainSuspend(dom);
-}
-
-static bool
-vshWatchJob(vshControl *ctl,
-            virDomainPtr dom,
-            bool verbose,
-            int pipe_fd,
-            int timeout,
-            jobWatchTimeoutFunc timeout_func,
-            void *opaque,
-            const char *label)
-{
-    struct sigaction sig_action;
-    struct sigaction old_sig_action;
-    struct pollfd pollfd;
-    struct timeval start, curr;
-    virDomainJobInfo jobinfo;
-    int ret = -1;
-    char retchar;
-    bool functionReturn = false;
-    sigset_t sigmask, oldsigmask;
-
-    sigemptyset(&sigmask);
-    sigaddset(&sigmask, SIGINT);
-
-    intCaught = 0;
-    sig_action.sa_sigaction = vshCatchInt;
-    sig_action.sa_flags = SA_SIGINFO;
-    sigemptyset(&sig_action.sa_mask);
-    sigaction(SIGINT, &sig_action, &old_sig_action);
-
-    pollfd.fd = pipe_fd;
-    pollfd.events = POLLIN;
-    pollfd.revents = 0;
-
-    GETTIMEOFDAY(&start);
-    while (1) {
-repoll:
-        ret = poll(&pollfd, 1, 500);
-        if (ret > 0) {
-            if (pollfd.revents & POLLIN &&
-                saferead(pipe_fd, &retchar, sizeof(retchar)) > 0 &&
-                retchar == '0') {
-                if (verbose) {
-                    /* print [100 %] */
-                    print_job_progress(label, 0, 1);
-                }
-                break;
-            }
-            goto cleanup;
-        }
-
-        if (ret < 0) {
-            if (errno == EINTR) {
-                if (intCaught) {
-                    virDomainAbortJob(dom);
-                    intCaught = 0;
-                } else {
-                    goto repoll;
-                }
-            }
-            goto cleanup;
-        }
-
-        GETTIMEOFDAY(&curr);
-        if (timeout && (((int)(curr.tv_sec - start.tv_sec)  * 1000 +
-                         (int)(curr.tv_usec - start.tv_usec) / 1000) >
-                        timeout * 1000)) {
-            /* suspend the domain when migration timeouts. */
-            vshDebug(ctl, VSH_ERR_DEBUG, "%s timeout", label);
-            if (timeout_func)
-                (timeout_func)(ctl, dom, opaque);
-            timeout = 0;
-        }
-
-        if (verbose) {
-            pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask);
-            ret = virDomainGetJobInfo(dom, &jobinfo);
-            pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
-            if (ret == 0)
-                print_job_progress(label, jobinfo.dataRemaining,
-                                   jobinfo.dataTotal);
-        }
-    }
-
-    functionReturn = true;
-
-cleanup:
-    sigaction(SIGINT, &old_sig_action, NULL);
-    return functionReturn;
-}
-
-static bool
-cmdMigrate(vshControl *ctl, const vshCmd *cmd)
-{
-    virDomainPtr dom = NULL;
-    int p[2] = {-1, -1};
-    virThread workerThread;
-    bool verbose = false;
-    bool functionReturn = false;
-    int timeout = 0;
-    bool live_flag = false;
-    vshCtrlData data;
-
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
-        return false;
-
-    if (vshCommandOptBool(cmd, "verbose"))
-        verbose = true;
-
-    if (vshCommandOptBool(cmd, "live"))
-        live_flag = true;
-    if (vshCommandOptInt(cmd, "timeout", &timeout) > 0) {
-        if (! live_flag) {
-            vshError(ctl, "%s",
-                     _("migrate: Unexpected timeout for offline migration"));
-            goto cleanup;
-        }
-
-        if (timeout < 1) {
-            vshError(ctl, "%s", _("migrate: Invalid timeout"));
-            goto cleanup;
-        }
-
-        /* Ensure that we can multiply by 1000 without overflowing. */
-        if (timeout > INT_MAX / 1000) {
-            vshError(ctl, "%s", _("migrate: Timeout is too big"));
-            goto cleanup;
-        }
-    }
-
-    if (pipe(p) < 0)
-        goto cleanup;
-
-    data.ctl = ctl;
-    data.cmd = cmd;
-    data.writefd = p[1];
-
-    if (virThreadCreate(&workerThread,
-                        true,
-                        doMigrate,
-                        &data) < 0)
-        goto cleanup;
-    functionReturn = vshWatchJob(ctl, dom, verbose, p[0], timeout,
-                                 vshMigrationTimeout, NULL, _("Migration"));
-
-    virThreadJoin(&workerThread);
-
-cleanup:
-    virDomainFree(dom);
-    VIR_FORCE_CLOSE(p[0]);
-    VIR_FORCE_CLOSE(p[1]);
-    return functionReturn;
-}
-
-/*
- * "migrate-setmaxdowntime" command
- */
-static const vshCmdInfo info_migrate_setmaxdowntime[] = {
-    {"help", N_("set maximum tolerable downtime")},
-    {"desc", N_("Set maximum tolerable downtime of a domain which is being live-migrated to another host.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_migrate_setmaxdowntime[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"downtime", VSH_OT_INT, VSH_OFLAG_REQ, N_("maximum tolerable downtime (in milliseconds) for migration")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdMigrateSetMaxDowntime(vshControl *ctl, const vshCmd *cmd)
-{
-    virDomainPtr dom = NULL;
-    long long downtime = 0;
-    bool ret = false;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
-        return false;
-
-    if (vshCommandOptLongLong(cmd, "downtime", &downtime) < 0 ||
-        downtime < 1) {
-        vshError(ctl, "%s", _("migrate: Invalid downtime"));
-        goto done;
-    }
-
-    if (virDomainMigrateSetMaxDowntime(dom, downtime, 0))
-        goto done;
-
-    ret = true;
-
-done:
-    virDomainFree(dom);
-    return ret;
-}
-
-/*
- * "migrate-setspeed" command
- */
-static const vshCmdInfo info_migrate_setspeed[] = {
-    {"help", N_("Set the maximum migration bandwidth")},
-    {"desc", N_("Set the maximum migration bandwidth (in MiB/s) for a domain "
-                "which is being migrated to another host.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_migrate_setspeed[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"bandwidth", VSH_OT_INT, VSH_OFLAG_REQ,
-     N_("migration bandwidth limit in MiB/s")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdMigrateSetMaxSpeed(vshControl *ctl, const vshCmd *cmd)
-{
-    virDomainPtr dom = NULL;
-    unsigned long bandwidth = 0;
-    bool ret = false;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
-        return false;
-
-    if (vshCommandOptUL(cmd, "bandwidth", &bandwidth) < 0) {
-        vshError(ctl, "%s", _("migrate: Invalid bandwidth"));
-        goto done;
-    }
-
-    if (virDomainMigrateSetMaxSpeed(dom, bandwidth, 0) < 0)
-        goto done;
-
-    ret = true;
-
-done:
-    virDomainFree(dom);
-    return ret;
-}
-
-/*
- * "migrate-getspeed" command
- */
-static const vshCmdInfo info_migrate_getspeed[] = {
-    {"help", N_("Get the maximum migration bandwidth")},
-    {"desc", N_("Get the maximum migration bandwidth (in MiB/s) for a domain.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_migrate_getspeed[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdMigrateGetMaxSpeed(vshControl *ctl, const vshCmd *cmd)
-{
-    virDomainPtr dom = NULL;
-    unsigned long bandwidth;
-    bool ret = false;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
-        return false;
-
-    if (virDomainMigrateGetMaxSpeed(dom, &bandwidth, 0) < 0)
-        goto done;
-
-    vshPrint(ctl, "%lu\n", bandwidth);
-
-    ret = true;
-
-done:
-    virDomainFree(dom);
-    return ret;
-}
-
-typedef enum {
-    VSH_CMD_BLOCK_JOB_ABORT = 0,
-    VSH_CMD_BLOCK_JOB_INFO = 1,
-    VSH_CMD_BLOCK_JOB_SPEED = 2,
-    VSH_CMD_BLOCK_JOB_PULL = 3,
-    VSH_CMD_BLOCK_JOB_COPY = 4,
-} vshCmdBlockJobMode;
-
-static int
-blockJobImpl(vshControl *ctl, const vshCmd *cmd,
-             virDomainBlockJobInfoPtr info, int mode,
-             virDomainPtr *pdom)
-{
-    virDomainPtr dom = NULL;
-    const char *name, *path;
-    unsigned long bandwidth = 0;
-    int ret = -1;
-    const char *base = NULL;
-    unsigned int flags = 0;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        goto cleanup;
-
-    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
-        goto cleanup;
-
-    if (vshCommandOptString(cmd, "path", &path) < 0)
-        goto cleanup;
-
-    if (vshCommandOptUL(cmd, "bandwidth", &bandwidth) < 0) {
-        vshError(ctl, "%s", _("bandwidth must be a number"));
-        goto cleanup;
-    }
-
-    switch ((vshCmdBlockJobMode) mode) {
-    case  VSH_CMD_BLOCK_JOB_ABORT:
-        if (vshCommandOptBool(cmd, "async"))
-            flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC;
-        if (vshCommandOptBool(cmd, "pivot"))
-            flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT;
-        ret = virDomainBlockJobAbort(dom, path, flags);
-        break;
-    case VSH_CMD_BLOCK_JOB_INFO:
-        ret = virDomainGetBlockJobInfo(dom, path, info, 0);
-        break;
-    case VSH_CMD_BLOCK_JOB_SPEED:
-        ret = virDomainBlockJobSetSpeed(dom, path, bandwidth, 0);
-        break;
-    case VSH_CMD_BLOCK_JOB_PULL:
-        if (vshCommandOptString(cmd, "base", &base) < 0)
-            goto cleanup;
-        if (base)
-            ret = virDomainBlockRebase(dom, path, base, bandwidth, 0);
-        else
-            ret = virDomainBlockPull(dom, path, bandwidth, 0);
-        break;
-    case VSH_CMD_BLOCK_JOB_COPY:
-        flags |= VIR_DOMAIN_BLOCK_REBASE_COPY;
-        if (vshCommandOptBool(cmd, "shallow"))
-            flags |= VIR_DOMAIN_BLOCK_REBASE_SHALLOW;
-        if (vshCommandOptBool(cmd, "reuse-external"))
-            flags |= VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT;
-        if (vshCommandOptBool(cmd, "raw"))
-            flags |= VIR_DOMAIN_BLOCK_REBASE_COPY_RAW;
-        if (vshCommandOptString(cmd, "dest", &base) < 0)
-            goto cleanup;
-        ret = virDomainBlockRebase(dom, path, base, bandwidth, flags);
-    }
-
-cleanup:
-    if (pdom && ret == 0)
-        *pdom = dom;
-    else if (dom)
-        virDomainFree(dom);
-    return ret;
-}
-
-/*
- * "blockcopy" command
- */
-static const vshCmdInfo info_block_copy[] = {
-    {"help", N_("Start a block copy operation.")},
-    {"desc", N_("Populate a disk from its backing image.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_block_copy[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"path", VSH_OT_DATA, VSH_OFLAG_REQ, N_("fully-qualified path of disk")},
-    {"dest", VSH_OT_DATA, VSH_OFLAG_REQ, N_("path of the copy to create")},
-    {"bandwidth", VSH_OT_DATA, VSH_OFLAG_NONE, N_("bandwidth limit in MiB/s")},
-    {"shallow", VSH_OT_BOOL, 0, N_("make the copy share a backing chain")},
-    {"reuse-external", VSH_OT_BOOL, 0, N_("reuse existing destination")},
-    {"raw", VSH_OT_BOOL, 0, N_("use raw destination file")},
-    {"wait", VSH_OT_BOOL, 0, N_("wait for job to reach mirroring phase")},
-    {"verbose", VSH_OT_BOOL, 0, N_("with --wait, display the progress")},
-    {"timeout", VSH_OT_INT, VSH_OFLAG_NONE,
-     N_("with --wait, abort if copy exceeds timeout (in seconds)")},
-    {"pivot", VSH_OT_BOOL, 0, N_("with --wait, pivot when mirroring starts")},
-    {"finish", VSH_OT_BOOL, 0, N_("with --wait, quit when mirroring starts")},
-    {"async", VSH_OT_BOOL, 0,
-     N_("with --wait, don't wait for cancel to finish")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdBlockCopy(vshControl *ctl, const vshCmd *cmd)
-{
-    virDomainPtr dom = NULL;
-    bool ret = false;
-    bool blocking = vshCommandOptBool(cmd, "wait");
-    bool verbose = vshCommandOptBool(cmd, "verbose");
-    bool pivot = vshCommandOptBool(cmd, "pivot");
-    bool finish = vshCommandOptBool(cmd, "finish");
-    int timeout = 0;
-    struct sigaction sig_action;
-    struct sigaction old_sig_action;
-    sigset_t sigmask;
-    struct timeval start;
-    struct timeval curr;
-    const char *path = NULL;
-    bool quit = false;
-    int abort_flags = 0;
-
-    if (blocking) {
-        if (pivot && finish) {
-            vshError(ctl, "%s", _("cannot mix --pivot and --finish"));
-            return false;
-        }
-        if (vshCommandOptInt(cmd, "timeout", &timeout) > 0) {
-            if (timeout < 1) {
-                vshError(ctl, "%s", _("migrate: Invalid timeout"));
-                return false;
-            }
-
-            /* Ensure that we can multiply by 1000 without overflowing. */
-            if (timeout > INT_MAX / 1000) {
-                vshError(ctl, "%s", _("migrate: Timeout is too big"));
-                return false;
-            }
-        }
-        if (vshCommandOptString(cmd, "path", &path) < 0)
-            return false;
-        if (vshCommandOptBool(cmd, "async"))
-            abort_flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC;
-
-        sigemptyset(&sigmask);
-        sigaddset(&sigmask, SIGINT);
-
-        intCaught = 0;
-        sig_action.sa_sigaction = vshCatchInt;
-        sig_action.sa_flags = SA_SIGINFO;
-        sigemptyset(&sig_action.sa_mask);
-        sigaction(SIGINT, &sig_action, &old_sig_action);
-
-        GETTIMEOFDAY(&start);
-    } else if (verbose || vshCommandOptBool(cmd, "timeout") ||
-               vshCommandOptBool(cmd, "async") || pivot || finish) {
-        vshError(ctl, "%s", _("blocking control options require --wait"));
-        return false;
-    }
-
-    if (blockJobImpl(ctl, cmd, NULL, VSH_CMD_BLOCK_JOB_COPY, &dom) < 0)
-        goto cleanup;
-
-    if (!blocking) {
-        vshPrint(ctl, "%s", _("Block Copy started"));
-        ret = true;
-        goto cleanup;
-    }
-
-    while (blocking) {
-        virDomainBlockJobInfo info;
-        int result = virDomainGetBlockJobInfo(dom, path, &info, 0);
-
-        if (result <= 0) {
-            vshError(ctl, _("failed to query job for disk %s"), path);
-            goto cleanup;
-        }
-        if (verbose)
-            print_job_progress(_("Block Copy"), info.end - info.cur, info.end);
-        if (info.cur == info.end)
-            break;
-
-        GETTIMEOFDAY(&curr);
-        if (intCaught || (timeout &&
-                          (((int)(curr.tv_sec - start.tv_sec)  * 1000 +
-                            (int)(curr.tv_usec - start.tv_usec) / 1000) >
-                           timeout * 1000))) {
-            vshDebug(ctl, VSH_ERR_DEBUG,
-                     intCaught ? "interrupted" : "timeout");
-            intCaught = 0;
-            timeout = 0;
-            quit = true;
-            if (virDomainBlockJobAbort(dom, path, abort_flags) < 0) {
-                vshError(ctl, _("failed to abort job for disk %s"), path);
-                goto cleanup;
-            }
-            if (abort_flags & VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC)
-                break;
-        } else {
-            usleep(500 * 1000);
-        }
-    }
-
-    if (pivot) {
-        abort_flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT;
-        if (virDomainBlockJobAbort(dom, path, abort_flags) < 0) {
-            vshError(ctl, _("failed to pivot job for disk %s"), path);
-            goto cleanup;
-        }
-    } else if (finish && virDomainBlockJobAbort(dom, path, abort_flags) < 0) {
-        vshError(ctl, _("failed to finish job for disk %s"), path);
-        goto cleanup;
-    }
-    vshPrint(ctl, "\n%s",
-             quit ? _("Copy aborted") :
-             pivot ? _("Successfully pivoted") :
-             finish ? _("Successfully copied") :
-             _("Now in mirroring phase"));
-
-    ret = true;
-cleanup:
-    if (dom)
-        virDomainFree(dom);
-    if (blocking)
-        sigaction(SIGINT, &old_sig_action, NULL);
-    return ret;
-}
-
-/*
- * "blockpull" command
- */
-static const vshCmdInfo info_block_pull[] = {
-    {"help", N_("Populate a disk from its backing image.")},
-    {"desc", N_("Populate a disk from its backing image.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_block_pull[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"path", VSH_OT_DATA, VSH_OFLAG_REQ, N_("fully-qualified path of disk")},
-    {"bandwidth", VSH_OT_DATA, VSH_OFLAG_NONE, N_("bandwidth limit in MiB/s")},
-    {"base", VSH_OT_DATA, VSH_OFLAG_NONE,
-     N_("path of backing file in chain for a partial pull")},
-    {"wait", VSH_OT_BOOL, 0, N_("wait for job to finish")},
-    {"verbose", VSH_OT_BOOL, 0, N_("with --wait, display the progress")},
-    {"timeout", VSH_OT_INT, VSH_OFLAG_NONE,
-     N_("with --wait, abort if pull exceeds timeout (in seconds)")},
-    {"async", VSH_OT_BOOL, 0,
-     N_("with --wait, don't wait for cancel to finish")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdBlockPull(vshControl *ctl, const vshCmd *cmd)
-{
-    virDomainPtr dom = NULL;
-    bool ret = false;
-    bool blocking = vshCommandOptBool(cmd, "wait");
-    bool verbose = vshCommandOptBool(cmd, "verbose");
-    int timeout = 0;
-    struct sigaction sig_action;
-    struct sigaction old_sig_action;
-    sigset_t sigmask;
-    struct timeval start;
-    struct timeval curr;
-    const char *path = NULL;
-    bool quit = false;
-    int abort_flags = 0;
-
-    if (blocking) {
-        if (vshCommandOptInt(cmd, "timeout", &timeout) > 0) {
-            if (timeout < 1) {
-                vshError(ctl, "%s", _("invalid timeout"));
-                return false;
-            }
-
-            /* Ensure that we can multiply by 1000 without overflowing. */
-            if (timeout > INT_MAX / 1000) {
-                vshError(ctl, "%s", _("timeout is too big"));
-                return false;
-            }
-        }
-        if (vshCommandOptString(cmd, "path", &path) < 0)
-            return false;
-        if (vshCommandOptBool(cmd, "async"))
-            abort_flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC;
-
-        sigemptyset(&sigmask);
-        sigaddset(&sigmask, SIGINT);
-
-        intCaught = 0;
-        sig_action.sa_sigaction = vshCatchInt;
-        sig_action.sa_flags = SA_SIGINFO;
-        sigemptyset(&sig_action.sa_mask);
-        sigaction(SIGINT, &sig_action, &old_sig_action);
-
-        GETTIMEOFDAY(&start);
-    } else if (verbose || vshCommandOptBool(cmd, "timeout") ||
-               vshCommandOptBool(cmd, "async")) {
-        vshError(ctl, "%s", _("blocking control options require --wait"));
-        return false;
-    }
-
-    if (blockJobImpl(ctl, cmd, NULL, VSH_CMD_BLOCK_JOB_PULL, &dom) < 0)
-        goto cleanup;
-
-    if (!blocking) {
-        vshPrint(ctl, "%s", _("Block Pull started"));
-        ret = true;
-        goto cleanup;
-    }
-
-    while (blocking) {
-        virDomainBlockJobInfo info;
-        int result = virDomainGetBlockJobInfo(dom, path, &info, 0);
-
-        if (result < 0) {
-            vshError(ctl, _("failed to query job for disk %s"), path);
-            goto cleanup;
-        }
-        if (result == 0)
-            break;
-
-        if (verbose)
-            print_job_progress(_("Block Pull"), info.end - info.cur, info.end);
-
-        GETTIMEOFDAY(&curr);
-        if (intCaught || (timeout &&
-                          (((int)(curr.tv_sec - start.tv_sec)  * 1000 +
-                            (int)(curr.tv_usec - start.tv_usec) / 1000) >
-                           timeout * 1000))) {
-            vshDebug(ctl, VSH_ERR_DEBUG,
-                     intCaught ? "interrupted" : "timeout");
-            intCaught = 0;
-            timeout = 0;
-            quit = true;
-            if (virDomainBlockJobAbort(dom, path, abort_flags) < 0) {
-                vshError(ctl, _("failed to abort job for disk %s"), path);
-                goto cleanup;
-            }
-            if (abort_flags & VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC)
-                break;
-        } else {
-            usleep(500 * 1000);
-        }
-    }
-
-    if (verbose && !quit) {
-        /* printf [100 %] */
-        print_job_progress(_("Block Pull"), 0, 1);
-    }
-    vshPrint(ctl, "\n%s", quit ? _("Pull aborted") : _("Pull complete"));
-
-    ret = true;
-cleanup:
-    if (dom)
-        virDomainFree(dom);
-    if (blocking)
-        sigaction(SIGINT, &old_sig_action, NULL);
-    return ret;
-}
-
-/*
- * "blockjob" command
- */
-static const vshCmdInfo info_block_job[] = {
-    {"help", N_("Manage active block operations")},
-    {"desc", N_("Query, adjust speed, or cancel active block operations.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_block_job[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"path", VSH_OT_DATA, VSH_OFLAG_REQ, N_("fully-qualified path of disk")},
-    {"abort", VSH_OT_BOOL, VSH_OFLAG_NONE,
-     N_("abort the active job on the specified disk")},
-    {"async", VSH_OT_BOOL, VSH_OFLAG_NONE,
-     N_("don't wait for --abort to complete")},
-    {"pivot", VSH_OT_BOOL, VSH_OFLAG_NONE,
-     N_("conclude and pivot a copy job")},
-    {"info", VSH_OT_BOOL, VSH_OFLAG_NONE,
-     N_("get active job information for the specified disk")},
-    {"bandwidth", VSH_OT_DATA, VSH_OFLAG_NONE,
-     N_("set the Bandwidth limit in MiB/s")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdBlockJob(vshControl *ctl, const vshCmd *cmd)
-{
-    int mode;
-    virDomainBlockJobInfo info;
-    const char *type;
-    int ret;
-    bool abortMode = (vshCommandOptBool(cmd, "abort") ||
-                      vshCommandOptBool(cmd, "async") ||
-                      vshCommandOptBool(cmd, "pivot"));
-    bool infoMode = vshCommandOptBool(cmd, "info");
-    bool bandwidth = vshCommandOptBool(cmd, "bandwidth");
-
-    if (abortMode + infoMode + bandwidth > 1) {
-        vshError(ctl, "%s",
-                 _("conflict between --abort, --info, and --bandwidth modes"));
-        return false;
-    }
-
-    if (abortMode)
-        mode = VSH_CMD_BLOCK_JOB_ABORT;
-    else if (bandwidth)
-        mode = VSH_CMD_BLOCK_JOB_SPEED;
-    else
-        mode = VSH_CMD_BLOCK_JOB_INFO;
-
-    ret = blockJobImpl(ctl, cmd, &info, mode, NULL);
-    if (ret < 0)
-        return false;
-
-    if (ret == 0 || mode != VSH_CMD_BLOCK_JOB_INFO)
-        return true;
-
-    switch (info.type) {
-    case VIR_DOMAIN_BLOCK_JOB_TYPE_PULL:
-        type = _("Block Pull");
-        break;
-    case VIR_DOMAIN_BLOCK_JOB_TYPE_COPY:
-        type = _("Block Copy");
-        break;
-    default:
-        type = _("Unknown job");
-        break;
-    }
-
-    print_job_progress(type, info.end - info.cur, info.end);
-    if (info.bandwidth != 0)
-        vshPrint(ctl, _("    Bandwidth limit: %lu MiB/s\n"), info.bandwidth);
-    return true;
-}
-
-/*
- * "blockresize" command
- */
-static const vshCmdInfo info_block_resize[] = {
-    {"help", N_("Resize block device of domain.")},
-    {"desc", N_("Resize block device of domain.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_block_resize[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"path", VSH_OT_DATA, VSH_OFLAG_REQ,
-     N_("Fully-qualified path of block device")},
-    {"size", VSH_OT_INT, VSH_OFLAG_REQ,
-     N_("New size of the block device, as scaled integer (default KiB)")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdBlockResize(vshControl *ctl, const vshCmd *cmd)
-{
-    virDomainPtr dom;
-    const char *path = NULL;
-    unsigned long long size = 0;
-    unsigned int flags = 0;
-    int ret = false;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (vshCommandOptString(cmd, "path", (const char **) &path) < 0) {
-        vshError(ctl, "%s", _("Path must not be empty"));
-        return false;
-    }
-
-    if (vshCommandOptScaledInt(cmd, "size", &size, 1024, ULLONG_MAX) < 0) {
-        vshError(ctl, "%s", _("Unable to parse integer"));
-        return false;
-    }
-
-    /* Prefer the older interface of KiB.  */
-    if (size % 1024 == 0)
-        size /= 1024;
-    else
-        flags |= VIR_DOMAIN_BLOCK_RESIZE_BYTES;
-
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
-        return false;
-
-    if (virDomainBlockResize(dom, path, size, flags) < 0) {
-        vshError(ctl, _("Failed to resize block device '%s'"), path);
-    } else {
-        vshPrint(ctl, _("Block device '%s' is resized"), path);
-        ret = true;
-    }
-
-    virDomainFree(dom);
-    return ret;
-}
-
-/*
- * "net-autostart" command
- */
-static const vshCmdInfo info_network_autostart[] = {
-    {"help", N_("autostart a network")},
-    {"desc",
-     N_("Configure a network to be automatically started at boot.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_network_autostart[] = {
-    {"network",  VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
-    {"disable", VSH_OT_BOOL, 0, N_("disable autostarting")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNetworkAutostart(vshControl *ctl, const vshCmd *cmd)
-{
-    virNetworkPtr network;
-    const char *name;
-    int autostart;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!(network = vshCommandOptNetwork(ctl, cmd, &name)))
-        return false;
-
-    autostart = !vshCommandOptBool(cmd, "disable");
-
-    if (virNetworkSetAutostart(network, autostart) < 0) {
-        if (autostart)
-            vshError(ctl, _("failed to mark network %s as autostarted"), name);
-        else
-            vshError(ctl, _("failed to unmark network %s as autostarted"), name);
-        virNetworkFree(network);
-        return false;
-    }
-
-    if (autostart)
-        vshPrint(ctl, _("Network %s marked as autostarted\n"), name);
-    else
-        vshPrint(ctl, _("Network %s unmarked as autostarted\n"), name);
-
-    virNetworkFree(network);
-    return true;
-}
-
-/*
- * "blkdeviotune" command
- */
-static const vshCmdInfo info_blkdeviotune[] = {
-    {"help", N_("Set or query a block device I/O tuning parameters.")},
-    {"desc", N_("Set or query disk I/O parameters such as block throttling.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_blkdeviotune[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("block device")},
-    {"total_bytes_sec", VSH_OT_ALIAS, 0, "total-bytes-sec"},
-    {"total-bytes-sec", VSH_OT_INT, VSH_OFLAG_NONE,
-     N_("total throughput limit in bytes per second")},
-    {"read_bytes_sec", VSH_OT_ALIAS, 0, "read-bytes-sec"},
-    {"read-bytes-sec", VSH_OT_INT, VSH_OFLAG_NONE,
-     N_("read throughput limit in bytes per second")},
-    {"write_bytes_sec", VSH_OT_ALIAS, 0, "write-bytes-sec"},
-    {"write-bytes-sec", VSH_OT_INT, VSH_OFLAG_NONE,
-     N_("write throughput limit in bytes per second")},
-    {"total_iops_sec", VSH_OT_ALIAS, 0, "total-iops-sec"},
-    {"total-iops-sec", VSH_OT_INT, VSH_OFLAG_NONE,
-     N_("total I/O operations limit per second")},
-    {"read_iops_sec", VSH_OT_ALIAS, 0, "read-iops-sec"},
-    {"read-iops-sec", VSH_OT_INT, VSH_OFLAG_NONE,
-     N_("read I/O operations limit per second")},
-    {"write_iops_sec", VSH_OT_ALIAS, 0, "write-iops-sec"},
-    {"write-iops-sec", VSH_OT_INT, VSH_OFLAG_NONE,
-     N_("write I/O operations limit per second")},
-    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
-    {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
-    {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdBlkdeviotune(vshControl *ctl, const vshCmd *cmd)
-{
-    virDomainPtr dom = NULL;
-    const char *name, *disk;
-    unsigned long long total_bytes_sec = 0, read_bytes_sec = 0, write_bytes_sec = 0;
-    unsigned long long total_iops_sec = 0, read_iops_sec = 0, write_iops_sec = 0;
-    int nparams = 0;
-    virTypedParameterPtr params = NULL;
-    unsigned int flags = 0, i = 0;
-    int rv = 0;
-    bool current = vshCommandOptBool(cmd, "current");
-    bool config = vshCommandOptBool(cmd, "config");
-    bool live = vshCommandOptBool(cmd, "live");
-    bool ret = false;
-
-    if (current) {
-        if (live || config) {
-            vshError(ctl, "%s", _("--current must be specified exclusively"));
-            return false;
-        }
-        flags = VIR_DOMAIN_AFFECT_CURRENT;
-    } else {
-        if (config)
-            flags |= VIR_DOMAIN_AFFECT_CONFIG;
-        if (live)
-            flags |= VIR_DOMAIN_AFFECT_LIVE;
-    }
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        goto cleanup;
-
-    if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
-        goto cleanup;
-
-    if (vshCommandOptString(cmd, "device", &disk) < 0)
-        goto cleanup;
-
-    if ((rv = vshCommandOptULongLong(cmd, "total-bytes-sec",
-                                     &total_bytes_sec)) < 0) {
-        vshError(ctl, "%s",
-                 _("Unable to parse integer parameter"));
-        goto cleanup;
-    } else if (rv > 0) {
-        nparams++;
-    }
-
-    if ((rv = vshCommandOptULongLong(cmd, "read-bytes-sec",
-                                     &read_bytes_sec)) < 0) {
-        vshError(ctl, "%s",
-                 _("Unable to parse integer parameter"));
-        goto cleanup;
-    } else if (rv > 0) {
-        nparams++;
-    }
-
-    if ((rv = vshCommandOptULongLong(cmd, "write-bytes-sec",
-                                     &write_bytes_sec)) < 0) {
-        vshError(ctl, "%s",
-                 _("Unable to parse integer parameter"));
-        goto cleanup;
-    } else if (rv > 0) {
-        nparams++;
-    }
-
-    if ((rv = vshCommandOptULongLong(cmd, "total-iops-sec",
-                                     &total_iops_sec)) < 0) {
-        vshError(ctl, "%s",
-                 _("Unable to parse integer parameter"));
-        goto cleanup;
-    } else if (rv > 0) {
-        nparams++;
-    }
-
-    if ((rv = vshCommandOptULongLong(cmd, "read-iops-sec",
-                                     &read_iops_sec)) < 0) {
-        vshError(ctl, "%s",
-                 _("Unable to parse integer parameter"));
-        goto cleanup;
-    } else if (rv > 0) {
-        nparams++;
-    }
-
-    if ((rv = vshCommandOptULongLong(cmd, "write-iops-sec",
-                                     &write_iops_sec)) < 0) {
-        vshError(ctl, "%s",
-                 _("Unable to parse integer parameter"));
-        goto cleanup;
-    } else if (rv > 0) {
-        nparams++;
-    }
-
-    if (nparams == 0) {
-
-        if (virDomainGetBlockIoTune(dom, NULL, NULL, &nparams, flags) != 0) {
-            vshError(ctl, "%s",
-                     _("Unable to get number of block I/O throttle parameters"));
-            goto cleanup;
-        }
-
-        if (nparams == 0) {
-            ret = true;
-            goto cleanup;
-        }
-
-        params = vshCalloc(ctl, nparams, sizeof(*params));
-
-        if (virDomainGetBlockIoTune(dom, disk, params, &nparams, flags) != 0) {
-            vshError(ctl, "%s",
-                     _("Unable to get block I/O throttle parameters"));
-            goto cleanup;
-        }
-
-        for (i = 0; i < nparams; i++) {
-            char *str = vshGetTypedParamValue(ctl, &params[i]);
-            vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
-            VIR_FREE(str);
-        }
-
-        ret = true;
-        goto cleanup;
-    } else {
-        /* Set the block I/O throttle, match by opt since parameters can be 0 */
-        params = vshCalloc(ctl, nparams, sizeof(*params));
-        i = 0;
-
-        if (i < nparams && vshCommandOptBool(cmd, "total-bytes-sec") &&
-            virTypedParameterAssign(&params[i++],
-                                    VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_BYTES_SEC,
-                                    VIR_TYPED_PARAM_ULLONG,
-                                    total_bytes_sec) < 0)
-            goto error;
-
-        if (i < nparams && vshCommandOptBool(cmd, "read-bytes-sec") &&
-            virTypedParameterAssign(&params[i++],
-                                    VIR_DOMAIN_BLOCK_IOTUNE_READ_BYTES_SEC,
-                                    VIR_TYPED_PARAM_ULLONG,
-                                    read_bytes_sec) < 0)
-            goto error;
-
-        if (i < nparams && vshCommandOptBool(cmd, "write-bytes-sec") &&
-            virTypedParameterAssign(&params[i++],
-                                    VIR_DOMAIN_BLOCK_IOTUNE_WRITE_BYTES_SEC,
-                                    VIR_TYPED_PARAM_ULLONG,
-                                    write_bytes_sec) < 0)
-            goto error;
-
-        if (i < nparams && vshCommandOptBool(cmd, "total-iops-sec") &&
-            virTypedParameterAssign(&params[i++],
-                                    VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_IOPS_SEC,
-                                    VIR_TYPED_PARAM_ULLONG,
-                                    total_iops_sec) < 0)
-            goto error;
-
-        if (i < nparams && vshCommandOptBool(cmd, "read-iops-sec") &&
-            virTypedParameterAssign(&params[i++],
-                                    VIR_DOMAIN_BLOCK_IOTUNE_READ_IOPS_SEC,
-                                    VIR_TYPED_PARAM_ULLONG,
-                                    read_iops_sec) < 0)
-            goto error;
-
-        if (i < nparams && vshCommandOptBool(cmd, "write-iops-sec") &&
-            virTypedParameterAssign(&params[i++],
-                                    VIR_DOMAIN_BLOCK_IOTUNE_WRITE_IOPS_SEC,
-                                    VIR_TYPED_PARAM_ULLONG,
-                                    write_iops_sec) < 0)
-            goto error;
-
-        if (virDomainSetBlockIoTune(dom, disk, params, nparams, flags) < 0)
-            goto error;
-    }
-
-    ret = true;
-
-cleanup:
-    VIR_FREE(params);
-    virDomainFree(dom);
-    return ret;
-
-error:
-    vshError(ctl, "%s", _("Unable to change block I/O throttle"));
-    goto cleanup;
-}
-
-/*
- * "net-create" command
- */
-static const vshCmdInfo info_network_create[] = {
-    {"help", N_("create a network from an XML file")},
-    {"desc", N_("Create a network.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_network_create[] = {
-    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML network description")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNetworkCreate(vshControl *ctl, const vshCmd *cmd)
-{
-    virNetworkPtr network;
-    const char *from = NULL;
-    bool ret = true;
-    char *buffer;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (vshCommandOptString(cmd, "file", &from) <= 0)
-        return false;
-
-    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
-        return false;
-
-    network = virNetworkCreateXML(ctl->conn, buffer);
-    VIR_FREE(buffer);
-
-    if (network != NULL) {
-        vshPrint(ctl, _("Network %s created from %s\n"),
-                 virNetworkGetName(network), from);
-        virNetworkFree(network);
-    } else {
-        vshError(ctl, _("Failed to create network from %s"), from);
-        ret = false;
-    }
-    return ret;
-}
-
-
-/*
- * "net-define" command
- */
-static const vshCmdInfo info_network_define[] = {
-    {"help", N_("define (but don't start) a network from an XML file")},
-    {"desc", N_("Define a network.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_network_define[] = {
-    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML network description")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNetworkDefine(vshControl *ctl, const vshCmd *cmd)
-{
-    virNetworkPtr network;
-    const char *from = NULL;
-    bool ret = true;
-    char *buffer;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (vshCommandOptString(cmd, "file", &from) <= 0)
-        return false;
-
-    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
-        return false;
-
-    network = virNetworkDefineXML(ctl->conn, buffer);
-    VIR_FREE(buffer);
-
-    if (network != NULL) {
-        vshPrint(ctl, _("Network %s defined from %s\n"),
-                 virNetworkGetName(network), from);
-        virNetworkFree(network);
-    } else {
-        vshError(ctl, _("Failed to define network from %s"), from);
-        ret = false;
-    }
-    return ret;
-}
-
-
-/*
- * "net-destroy" command
- */
-static const vshCmdInfo info_network_destroy[] = {
-    {"help", N_("destroy (stop) a network")},
-    {"desc", N_("Forcefully stop a given network.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_network_destroy[] = {
-    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNetworkDestroy(vshControl *ctl, const vshCmd *cmd)
-{
-    virNetworkPtr network;
-    bool ret = true;
-    const char *name;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!(network = vshCommandOptNetwork(ctl, cmd, &name)))
-        return false;
-
-    if (virNetworkDestroy(network) == 0) {
-        vshPrint(ctl, _("Network %s destroyed\n"), name);
-    } else {
-        vshError(ctl, _("Failed to destroy network %s"), name);
-        ret = false;
-    }
-
-    virNetworkFree(network);
-    return ret;
-}
-
-
-/*
- * "net-dumpxml" command
- */
-static const vshCmdInfo info_network_dumpxml[] = {
-    {"help", N_("network information in XML")},
-    {"desc", N_("Output the network information as an XML dump to stdout.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_network_dumpxml[] = {
-    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
-    {"inactive", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("network information of an inactive domain")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNetworkDumpXML(vshControl *ctl, const vshCmd *cmd)
-{
-    virNetworkPtr network;
-    bool ret = true;
-    char *dump;
-    unsigned int flags = 0;
-    int inactive;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!(network = vshCommandOptNetwork(ctl, cmd, NULL)))
-        return false;
-
-    inactive = vshCommandOptBool(cmd, "inactive");
-    if (inactive)
-        flags |= VIR_NETWORK_XML_INACTIVE;
-
-    dump = virNetworkGetXMLDesc(network, flags);
-
-    if (dump != NULL) {
-        vshPrint(ctl, "%s", dump);
-        VIR_FREE(dump);
-    } else {
-        ret = false;
-    }
-
-    virNetworkFree(network);
-    return ret;
-}
-
-/*
- * "net-info" command
- */
-static const vshCmdInfo info_network_info[] = {
-    {"help", N_("network information")},
-    {"desc", N_("Returns basic information about the network")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_network_info[] = {
-    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNetworkInfo(vshControl *ctl, const vshCmd *cmd)
-{
-    virNetworkPtr network;
-    char uuid[VIR_UUID_STRING_BUFLEN];
-    int autostart;
-    int persistent = -1;
-    int active = -1;
-    char *bridge = NULL;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!(network = vshCommandOptNetwork(ctl, cmd, NULL)))
-        return false;
-
-    vshPrint(ctl, "%-15s %s\n", _("Name"), virNetworkGetName(network));
-
-    if (virNetworkGetUUIDString(network, uuid) == 0)
-        vshPrint(ctl, "%-15s %s\n", _("UUID"), uuid);
-
-    active = virNetworkIsActive(network);
-    if (active >= 0)
-        vshPrint(ctl, "%-15s %s\n", _("Active:"), active? _("yes") : _("no"));
-
-    persistent = virNetworkIsPersistent(network);
-    if (persistent < 0)
-        vshPrint(ctl, "%-15s %s\n", _("Persistent:"), _("unknown"));
-    else
-        vshPrint(ctl, "%-15s %s\n", _("Persistent:"), persistent ? _("yes") : _("no"));
-
-    if (virNetworkGetAutostart(network, &autostart) < 0)
-        vshPrint(ctl, "%-15s %s\n", _("Autostart:"), _("no autostart"));
-    else
-        vshPrint(ctl, "%-15s %s\n", _("Autostart:"), autostart ? _("yes") : _("no"));
-
-    bridge = virNetworkGetBridgeName(network);
-    if (bridge)
-        vshPrint(ctl, "%-15s %s\n", _("Bridge:"), bridge);
-
-    VIR_FREE(bridge);
-    virNetworkFree(network);
-    return true;
-}
-
-/*
- * "iface-edit" command
- */
-static const vshCmdInfo info_interface_edit[] = {
-    {"help", N_("edit XML configuration for a physical host interface")},
-    {"desc", N_("Edit the XML configuration for a physical host interface.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_interface_edit[] = {
-    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name or MAC address")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdInterfaceEdit(vshControl *ctl, const vshCmd *cmd)
-{
-    bool ret = false;
-    virInterfacePtr iface = NULL;
-    virInterfacePtr iface_edited = NULL;
-    unsigned int flags = VIR_INTERFACE_XML_INACTIVE;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        goto cleanup;
-
-    iface = vshCommandOptInterface(ctl, cmd, NULL);
-    if (iface == NULL)
-        goto cleanup;
-
-#define EDIT_GET_XML virInterfaceGetXMLDesc(iface, flags)
-#define EDIT_NOT_CHANGED \
-    vshPrint(ctl, _("Interface %s XML configuration not changed.\n"),   \
-             virInterfaceGetName(iface));                               \
-    ret = true; goto edit_cleanup;
-#define EDIT_DEFINE \
-    (iface_edited = virInterfaceDefineXML(ctl->conn, doc_edited, 0))
-#define EDIT_FREE \
-    if (iface_edited)   \
-        virInterfaceFree(iface_edited);
-#include "virsh-edit.c"
-
-    vshPrint(ctl, _("Interface %s XML configuration edited.\n"),
-             virInterfaceGetName(iface_edited));
-
-    ret = true;
-
-cleanup:
-    if (iface)
-        virInterfaceFree(iface);
-    if (iface_edited)
-        virInterfaceFree(iface_edited);
-
-    return ret;
-}
-
-/*
- * "net-list" command
- */
-static const vshCmdInfo info_network_list[] = {
-    {"help", N_("list networks")},
-    {"desc", N_("Returns list of networks.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_network_list[] = {
-    {"inactive", VSH_OT_BOOL, 0, N_("list inactive networks")},
-    {"all", VSH_OT_BOOL, 0, N_("list inactive & active networks")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNetworkList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
-{
-    bool inactive = vshCommandOptBool(cmd, "inactive");
-    bool all = vshCommandOptBool(cmd, "all");
-    bool active = !inactive || all;
-    int maxactive = 0, maxinactive = 0, i;
-    char **activeNames = NULL, **inactiveNames = NULL;
-    inactive |= all;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (active) {
-        maxactive = virConnectNumOfNetworks(ctl->conn);
-        if (maxactive < 0) {
-            vshError(ctl, "%s", _("Failed to list active networks"));
-            return false;
-        }
-        if (maxactive) {
-            activeNames = vshMalloc(ctl, sizeof(char *) * maxactive);
-
-            if ((maxactive = virConnectListNetworks(ctl->conn, activeNames,
-                                                    maxactive)) < 0) {
-                vshError(ctl, "%s", _("Failed to list active networks"));
-                VIR_FREE(activeNames);
-                return false;
-            }
-
-            qsort(&activeNames[0], maxactive, sizeof(char *), vshNameSorter);
-        }
-    }
-    if (inactive) {
-        maxinactive = virConnectNumOfDefinedNetworks(ctl->conn);
-        if (maxinactive < 0) {
-            vshError(ctl, "%s", _("Failed to list inactive networks"));
-            VIR_FREE(activeNames);
-            return false;
-        }
-        if (maxinactive) {
-            inactiveNames = vshMalloc(ctl, sizeof(char *) * maxinactive);
-
-            if ((maxinactive =
-                     virConnectListDefinedNetworks(ctl->conn, inactiveNames,
-                                                   maxinactive)) < 0) {
-                vshError(ctl, "%s", _("Failed to list inactive networks"));
-                VIR_FREE(activeNames);
-                VIR_FREE(inactiveNames);
-                return false;
-            }
-
-            qsort(&inactiveNames[0], maxinactive, sizeof(char*), vshNameSorter);
-        }
-    }
-    vshPrintExtra(ctl, "%-20s %-10s %s\n", _("Name"), _("State"),
-                  _("Autostart"));
-    vshPrintExtra(ctl, "-----------------------------------------\n");
-
-    for (i = 0; i < maxactive; i++) {
-        virNetworkPtr network =
-            virNetworkLookupByName(ctl->conn, activeNames[i]);
-        const char *autostartStr;
-        int autostart = 0;
-
-        /* this kind of work with networks is not atomic operation */
-        if (!network) {
-            VIR_FREE(activeNames[i]);
-            continue;
-        }
-
-        if (virNetworkGetAutostart(network, &autostart) < 0)
-            autostartStr = _("no autostart");
-        else
-            autostartStr = autostart ? _("yes") : _("no");
-
-        vshPrint(ctl, "%-20s %-10s %-10s\n",
-                 virNetworkGetName(network),
-                 _("active"),
-                 autostartStr);
-        virNetworkFree(network);
-        VIR_FREE(activeNames[i]);
-    }
-    for (i = 0; i < maxinactive; i++) {
-        virNetworkPtr network = virNetworkLookupByName(ctl->conn, inactiveNames[i]);
-        const char *autostartStr;
-        int autostart = 0;
-
-        /* this kind of work with networks is not atomic operation */
-        if (!network) {
-            VIR_FREE(inactiveNames[i]);
-            continue;
-        }
-
-        if (virNetworkGetAutostart(network, &autostart) < 0)
-            autostartStr = _("no autostart");
-        else
-            autostartStr = autostart ? _("yes") : _("no");
-
-        vshPrint(ctl, "%-20s %-10s %-10s\n",
-                 inactiveNames[i],
-                 _("inactive"),
-                 autostartStr);
-
-        virNetworkFree(network);
-        VIR_FREE(inactiveNames[i]);
-    }
-    VIR_FREE(activeNames);
-    VIR_FREE(inactiveNames);
-    return true;
-}
-
-
-/*
- * "net-name" command
- */
-static const vshCmdInfo info_network_name[] = {
-    {"help", N_("convert a network UUID to network name")},
-    {"desc", ""},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_network_name[] = {
-    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network uuid")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNetworkName(vshControl *ctl, const vshCmd *cmd)
-{
-    virNetworkPtr network;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-    if (!(network = vshCommandOptNetworkBy(ctl, cmd, NULL,
-                                           VSH_BYUUID)))
-        return false;
-
-    vshPrint(ctl, "%s\n", virNetworkGetName(network));
-    virNetworkFree(network);
-    return true;
-}
-
-
-/*
- * "net-start" command
- */
-static const vshCmdInfo info_network_start[] = {
-    {"help", N_("start a (previously defined) inactive network")},
-    {"desc", N_("Start a network.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_network_start[] = {
-    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNetworkStart(vshControl *ctl, const vshCmd *cmd)
-{
-    virNetworkPtr network;
-    bool ret = true;
-    const char *name = NULL;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!(network = vshCommandOptNetwork(ctl, cmd, &name)))
-         return false;
-
-    if (virNetworkCreate(network) == 0) {
-        vshPrint(ctl, _("Network %s started\n"), name);
-    } else {
-        vshError(ctl, _("Failed to start network %s"), name);
-        ret = false;
-    }
-    virNetworkFree(network);
-    return ret;
-}
-
-
-/*
- * "net-undefine" command
- */
-static const vshCmdInfo info_network_undefine[] = {
-    {"help", N_("undefine an inactive network")},
-    {"desc", N_("Undefine the configuration for an inactive network.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_network_undefine[] = {
-    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNetworkUndefine(vshControl *ctl, const vshCmd *cmd)
-{
-    virNetworkPtr network;
-    bool ret = true;
-    const char *name;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!(network = vshCommandOptNetwork(ctl, cmd, &name)))
-        return false;
-
-    if (virNetworkUndefine(network) == 0) {
-        vshPrint(ctl, _("Network %s has been undefined\n"), name);
-    } else {
-        vshError(ctl, _("Failed to undefine network %s"), name);
-        ret = false;
-    }
-
-    virNetworkFree(network);
-    return ret;
-}
-
-
-/*
- * "net-uuid" command
- */
-static const vshCmdInfo info_network_uuid[] = {
-    {"help", N_("convert a network name to network UUID")},
-    {"desc", ""},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_network_uuid[] = {
-    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNetworkUuid(vshControl *ctl, const vshCmd *cmd)
-{
-    virNetworkPtr network;
-    char uuid[VIR_UUID_STRING_BUFLEN];
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!(network = vshCommandOptNetworkBy(ctl, cmd, NULL,
-                                           VSH_BYNAME)))
-        return false;
-
-    if (virNetworkGetUUIDString(network, uuid) != -1)
-        vshPrint(ctl, "%s\n", uuid);
-    else
-        vshError(ctl, "%s", _("failed to get network UUID"));
-
-    virNetworkFree(network);
-    return true;
-}
-
-
-/**************************************************************************/
-/*
- * "iface-list" command
- */
-static const vshCmdInfo info_interface_list[] = {
-    {"help", N_("list physical host interfaces")},
-    {"desc", N_("Returns list of physical host interfaces.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_interface_list[] = {
-    {"inactive", VSH_OT_BOOL, 0, N_("list inactive interfaces")},
-    {"all", VSH_OT_BOOL, 0, N_("list inactive & active interfaces")},
-    {NULL, 0, 0, NULL}
-};
-static bool
-cmdInterfaceList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
-{
-    bool inactive = vshCommandOptBool(cmd, "inactive");
-    bool all = vshCommandOptBool(cmd, "all");
-    bool active = !inactive || all;
-    int maxactive = 0, maxinactive = 0, i;
-    char **activeNames = NULL, **inactiveNames = NULL;
-    inactive |= all;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (active) {
-        maxactive = virConnectNumOfInterfaces(ctl->conn);
-        if (maxactive < 0) {
-            vshError(ctl, "%s", _("Failed to list active interfaces"));
-            return false;
-        }
-        if (maxactive) {
-            activeNames = vshMalloc(ctl, sizeof(char *) * maxactive);
-
-            if ((maxactive = virConnectListInterfaces(ctl->conn, activeNames,
-                                                    maxactive)) < 0) {
-                vshError(ctl, "%s", _("Failed to list active interfaces"));
-                VIR_FREE(activeNames);
-                return false;
-            }
-
-            qsort(&activeNames[0], maxactive, sizeof(char *), vshNameSorter);
-        }
-    }
-    if (inactive) {
-        maxinactive = virConnectNumOfDefinedInterfaces(ctl->conn);
-        if (maxinactive < 0) {
-            vshError(ctl, "%s", _("Failed to list inactive interfaces"));
-            VIR_FREE(activeNames);
-            return false;
-        }
-        if (maxinactive) {
-            inactiveNames = vshMalloc(ctl, sizeof(char *) * maxinactive);
-
-            if ((maxinactive =
-                     virConnectListDefinedInterfaces(ctl->conn, inactiveNames,
-                                                     maxinactive)) < 0) {
-                vshError(ctl, "%s", _("Failed to list inactive interfaces"));
-                VIR_FREE(activeNames);
-                VIR_FREE(inactiveNames);
-                return false;
-            }
-
-            qsort(&inactiveNames[0], maxinactive, sizeof(char*), vshNameSorter);
-        }
-    }
-    vshPrintExtra(ctl, "%-20s %-10s %s\n", _("Name"), _("State"),
-                  _("MAC Address"));
-    vshPrintExtra(ctl, "--------------------------------------------\n");
-
-    for (i = 0; i < maxactive; i++) {
-        virInterfacePtr iface =
-            virInterfaceLookupByName(ctl->conn, activeNames[i]);
-
-        /* this kind of work with interfaces is not atomic */
-        if (!iface) {
-            VIR_FREE(activeNames[i]);
-            continue;
-        }
-
-        vshPrint(ctl, "%-20s %-10s %s\n",
-                 virInterfaceGetName(iface),
-                 _("active"),
-                 virInterfaceGetMACString(iface));
-        virInterfaceFree(iface);
-        VIR_FREE(activeNames[i]);
-    }
-    for (i = 0; i < maxinactive; i++) {
-        virInterfacePtr iface =
-            virInterfaceLookupByName(ctl->conn, inactiveNames[i]);
-
-        /* this kind of work with interfaces is not atomic */
-        if (!iface) {
-            VIR_FREE(inactiveNames[i]);
-            continue;
-        }
-
-        vshPrint(ctl, "%-20s %-10s %s\n",
-                 virInterfaceGetName(iface),
-                 _("inactive"),
-                 virInterfaceGetMACString(iface));
-        virInterfaceFree(iface);
-        VIR_FREE(inactiveNames[i]);
-    }
-    VIR_FREE(activeNames);
-    VIR_FREE(inactiveNames);
-    return true;
-
-}
-
-/*
- * "iface-name" command
- */
-static const vshCmdInfo info_interface_name[] = {
-    {"help", N_("convert an interface MAC address to interface name")},
-    {"desc", ""},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_interface_name[] = {
-    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface mac")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdInterfaceName(vshControl *ctl, const vshCmd *cmd)
-{
-    virInterfacePtr iface;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-    if (!(iface = vshCommandOptInterfaceBy(ctl, cmd, NULL, NULL,
-                                           VSH_BYMAC)))
-        return false;
-
-    vshPrint(ctl, "%s\n", virInterfaceGetName(iface));
-    virInterfaceFree(iface);
-    return true;
-}
-
-/*
- * "iface-mac" command
- */
-static const vshCmdInfo info_interface_mac[] = {
-    {"help", N_("convert an interface name to interface MAC address")},
-    {"desc", ""},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_interface_mac[] = {
-    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdInterfaceMAC(vshControl *ctl, const vshCmd *cmd)
-{
-    virInterfacePtr iface;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-    if (!(iface = vshCommandOptInterfaceBy(ctl, cmd, NULL, NULL,
-                                           VSH_BYNAME)))
-        return false;
-
-    vshPrint(ctl, "%s\n", virInterfaceGetMACString(iface));
-    virInterfaceFree(iface);
-    return true;
-}
-
-/*
- * "iface-dumpxml" command
- */
-static const vshCmdInfo info_interface_dumpxml[] = {
-    {"help", N_("interface information in XML")},
-    {"desc", N_("Output the physical host interface information as an XML dump to stdout.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_interface_dumpxml[] = {
-    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name or MAC address")},
-    {"inactive", VSH_OT_BOOL, 0, N_("show inactive defined XML")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdInterfaceDumpXML(vshControl *ctl, const vshCmd *cmd)
-{
-    virInterfacePtr iface;
-    bool ret = true;
-    char *dump;
-    unsigned int flags = 0;
-    bool inactive = vshCommandOptBool(cmd, "inactive");
-
-    if (inactive)
-        flags |= VIR_INTERFACE_XML_INACTIVE;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!(iface = vshCommandOptInterface(ctl, cmd, NULL)))
-        return false;
-
-    dump = virInterfaceGetXMLDesc(iface, flags);
-    if (dump != NULL) {
-        vshPrint(ctl, "%s", dump);
-        VIR_FREE(dump);
-    } else {
-        ret = false;
-    }
-
-    virInterfaceFree(iface);
-    return ret;
-}
-
-/*
- * "iface-define" command
- */
-static const vshCmdInfo info_interface_define[] = {
-    {"help", N_("define (but don't start) a physical host interface from an XML file")},
-    {"desc", N_("Define a physical host interface.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_interface_define[] = {
-    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML interface description")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdInterfaceDefine(vshControl *ctl, const vshCmd *cmd)
-{
-    virInterfacePtr iface;
-    const char *from = NULL;
-    bool ret = true;
-    char *buffer;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (vshCommandOptString(cmd, "file", &from) <= 0)
-        return false;
-
-    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
-        return false;
-
-    iface = virInterfaceDefineXML(ctl->conn, buffer, 0);
-    VIR_FREE(buffer);
-
-    if (iface != NULL) {
-        vshPrint(ctl, _("Interface %s defined from %s\n"),
-                 virInterfaceGetName(iface), from);
-        virInterfaceFree(iface);
-    } else {
-        vshError(ctl, _("Failed to define interface from %s"), from);
-        ret = false;
-    }
-    return ret;
-}
-
-/*
- * "iface-undefine" command
- */
-static const vshCmdInfo info_interface_undefine[] = {
-    {"help", N_("undefine a physical host interface (remove it from configuration)")},
-    {"desc", N_("undefine an interface.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_interface_undefine[] = {
-    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name or MAC address")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdInterfaceUndefine(vshControl *ctl, const vshCmd *cmd)
-{
-    virInterfacePtr iface;
-    bool ret = true;
-    const char *name;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!(iface = vshCommandOptInterface(ctl, cmd, &name)))
-        return false;
-
-    if (virInterfaceUndefine(iface) == 0) {
-        vshPrint(ctl, _("Interface %s undefined\n"), name);
-    } else {
-        vshError(ctl, _("Failed to undefine interface %s"), name);
-        ret = false;
-    }
-
-    virInterfaceFree(iface);
-    return ret;
-}
-
-/*
- * "iface-start" command
- */
-static const vshCmdInfo info_interface_start[] = {
-    {"help", N_("start a physical host interface (enable it / \"if-up\")")},
-    {"desc", N_("start a physical host interface.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_interface_start[] = {
-    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name or MAC address")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdInterfaceStart(vshControl *ctl, const vshCmd *cmd)
-{
-    virInterfacePtr iface;
-    bool ret = true;
-    const char *name;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!(iface = vshCommandOptInterface(ctl, cmd, &name)))
-        return false;
-
-    if (virInterfaceCreate(iface, 0) == 0) {
-        vshPrint(ctl, _("Interface %s started\n"), name);
-    } else {
-        vshError(ctl, _("Failed to start interface %s"), name);
-        ret = false;
-    }
-
-    virInterfaceFree(iface);
-    return ret;
-}
-
-/*
- * "iface-destroy" command
- */
-static const vshCmdInfo info_interface_destroy[] = {
-    {"help", N_("destroy a physical host interface (disable it / \"if-down\")")},
-    {"desc", N_("forcefully stop a physical host interface.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_interface_destroy[] = {
-    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name or MAC address")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdInterfaceDestroy(vshControl *ctl, const vshCmd *cmd)
-{
-    virInterfacePtr iface;
-    bool ret = true;
-    const char *name;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!(iface = vshCommandOptInterface(ctl, cmd, &name)))
-        return false;
-
-    if (virInterfaceDestroy(iface, 0) == 0) {
-        vshPrint(ctl, _("Interface %s destroyed\n"), name);
-    } else {
-        vshError(ctl, _("Failed to destroy interface %s"), name);
-        ret = false;
-    }
-
-    virInterfaceFree(iface);
-    return ret;
-}
-
-/*
- * "iface-begin" command
- */
-static const vshCmdInfo info_interface_begin[] = {
-    {"help", N_("create a snapshot of current interfaces settings, "
-                "which can be later committed (iface-commit) or "
-                "restored (iface-rollback)")},
-    {"desc", N_("Create a restore point for interfaces settings")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_interface_begin[] = {
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdInterfaceBegin(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
-{
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (virInterfaceChangeBegin(ctl->conn, 0) < 0) {
-        vshError(ctl, "%s", _("Failed to begin network config change transaction"));
-        return false;
-    }
-
-    vshPrint(ctl, "%s", _("Network config change transaction started\n"));
-    return true;
-}
-
-/*
- * "iface-commit" command
- */
-static const vshCmdInfo info_interface_commit[] = {
-    {"help", N_("commit changes made since iface-begin and free restore point")},
-    {"desc", N_("commit changes and free restore point")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_interface_commit[] = {
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdInterfaceCommit(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
-{
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (virInterfaceChangeCommit(ctl->conn, 0) < 0) {
-        vshError(ctl, "%s", _("Failed to commit network config change transaction"));
-        return false;
-    }
-
-    vshPrint(ctl, "%s", _("Network config change transaction committed\n"));
-    return true;
-}
-
-/*
- * "iface-rollback" command
- */
-static const vshCmdInfo info_interface_rollback[] = {
-    {"help", N_("rollback to previous saved configuration created via iface-begin")},
-    {"desc", N_("rollback to previous restore point")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_interface_rollback[] = {
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdInterfaceRollback(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
-{
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (virInterfaceChangeRollback(ctl->conn, 0) < 0) {
-        vshError(ctl, "%s", _("Failed to rollback network config change transaction"));
-        return false;
-    }
-
-    vshPrint(ctl, "%s", _("Network config change transaction rolled back\n"));
-    return true;
-}
-
-/*
- * "iface-bridge" command
- */
-static const vshCmdInfo info_interface_bridge[] = {
-    {"help", N_("create a bridge device and attach an existing network device to it")},
-    {"desc", N_("bridge an existing network device")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_interface_bridge[] = {
-    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("existing interface name")},
-    {"bridge", VSH_OT_DATA, VSH_OFLAG_REQ, N_("new bridge device name")},
-    {"no-stp", VSH_OT_BOOL, 0, N_("do not enable STP for this bridge")},
-    {"delay", VSH_OT_INT, 0,
-     N_("number of seconds to squelch traffic on newly connected ports")},
-    {"no-start", VSH_OT_BOOL, 0, N_("don't start the bridge immediately")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdInterfaceBridge(vshControl *ctl, const vshCmd *cmd)
-{
-    bool ret = false;
-    virInterfacePtr if_handle = NULL, br_handle = NULL;
-    const char *if_name, *br_name;
-    char *if_type = NULL, *if2_name = NULL, *delay_str = NULL;
-    bool stp = false, nostart = false;
-    unsigned int delay = 0;
-    char *if_xml = NULL;
-    xmlChar *br_xml = NULL;
-    int br_xml_size;
-    xmlDocPtr xml_doc = NULL;
-    xmlXPathContextPtr ctxt = NULL;
-    xmlNodePtr top_node, br_node, if_node, cur;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        goto cleanup;
-
-    /* Get a handle to the original device */
-    if (!(if_handle = vshCommandOptInterfaceBy(ctl, cmd, "interface",
-                                               &if_name, VSH_BYNAME))) {
-        goto cleanup;
-    }
-
-    /* Name for new bridge device */
-    if (vshCommandOptString(cmd, "bridge", &br_name) <= 0) {
-        vshError(ctl, "%s", _("Missing bridge device name in command"));
-        goto cleanup;
-    }
-
-    /* make sure "new" device doesn't already exist */
-    if ((br_handle = virInterfaceLookupByName(ctl->conn, br_name))) {
-        vshError(ctl, _("Network device %s already exists"), br_name);
-        goto cleanup;
-    }
-
-    /* use "no-stp" because we want "stp" to default true */
-    stp = !vshCommandOptBool(cmd, "no-stp");
-
-    if (vshCommandOptUInt(cmd, "delay", &delay) < 0) {
-        vshError(ctl, "%s", _("Unable to parse delay parameter"));
-        goto cleanup;
-    }
-
-    nostart = vshCommandOptBool(cmd, "no-start");
-
-    /* Get the original interface into an xmlDoc */
-    if (!(if_xml = virInterfaceGetXMLDesc(if_handle, VIR_INTERFACE_XML_INACTIVE)))
-        goto cleanup;
-    if (!(xml_doc = virXMLParseStringCtxt(if_xml,
-                                          _("(interface definition)"), &ctxt))) {
-        vshError(ctl, _("Failed to parse configuration of %s"), if_name);
-        goto cleanup;
-    }
-    top_node = ctxt->node;
-
-    /* Verify that the original device isn't already a bridge. */
-    if (!(if_type = virXMLPropString(top_node, "type"))) {
-        vshError(ctl, _("Existing device %s has no type"), if_name);
-        goto cleanup;
-    }
-
-    if (STREQ(if_type, "bridge")) {
-        vshError(ctl, _("Existing device %s is already a bridge"), if_name);
-        goto cleanup;
-    }
-
-    /* verify the name in the XML matches the device name */
-    if (!(if2_name = virXMLPropString(top_node, "name")) ||
-        STRNEQ(if2_name, if_name)) {
-        vshError(ctl, _("Interface name from config %s doesn't match given supplied name %s"),
-                 if2_name, if_name);
-        goto cleanup;
-    }
-
-    /* Create a <bridge> node under <interface>. */
-    if (!(br_node = xmlNewChild(top_node, NULL, BAD_CAST "bridge", NULL))) {
-        vshError(ctl, "%s", _("Failed to create bridge node in xml document"));
-        goto cleanup;
-    }
-
-    /* Set stp and delay attributes in <bridge> according to the
-     * commandline options.
-     */
-    if (!xmlSetProp(br_node, BAD_CAST "stp", BAD_CAST (stp ? "on" : "off"))) {
-        vshError(ctl, "%s", _("Failed to set stp attribute in xml document"));
-        goto cleanup;
-    }
-
-    if ((delay || stp) &&
-        ((virAsprintf(&delay_str, "%d", delay) < 0) ||
-         !xmlSetProp(br_node, BAD_CAST "delay", BAD_CAST delay_str))) {
-        vshError(ctl, _("Failed to set bridge delay %d in xml document"), delay);
-        goto cleanup;
-    }
-
-    /* Change the type of the outer/master interface to "bridge" and the
-     * name to the provided bridge name.
-     */
-    if (!xmlSetProp(top_node, BAD_CAST "type", BAD_CAST "bridge")) {
-        vshError(ctl, "%s", _("Failed to set bridge interface type to 'bridge' in xml document"));
-        goto cleanup;
-    }
-
-    if (!xmlSetProp(top_node, BAD_CAST "name", BAD_CAST br_name)) {
-        vshError(ctl, _("Failed to set master bridge interface name to '%s' in xml document"),
-            br_name);
-        goto cleanup;
-    }
-
-    /* Create an <interface> node under <bridge> that uses the
-     * original interface's type and name.
-     */
-    if (!(if_node = xmlNewChild(br_node, NULL, BAD_CAST "interface", NULL))) {
-        vshError(ctl, "%s", _("Failed to create interface node under bridge node in xml document"));
-        goto cleanup;
-    }
-
-    /* set the type of the inner/slave interface to the original
-     * if_type, and the name to the original if_name.
-     */
-    if (!xmlSetProp(if_node, BAD_CAST "type", BAD_CAST if_type)) {
-        vshError(ctl, _("Failed to set new slave interface type to '%s' in xml document"),
-                 if_name);
-        goto cleanup;
-    }
-
-    if (!xmlSetProp(if_node, BAD_CAST "name", BAD_CAST if_name)) {
-        vshError(ctl, _("Failed to set new slave interface name to '%s' in xml document"),
-            br_name);
-        goto cleanup;
-    }
-
-    /* Cycle through all the nodes under the original <interface>,
-     * moving all <mac>, <bond> and <vlan> nodes down into the new
-     * lower level <interface>.
-     */
-    cur = top_node->children;
-    while (cur) {
-        xmlNodePtr old = cur;
-
-        cur = cur->next;
-        if ((old->type == XML_ELEMENT_NODE) &&
-            (xmlStrEqual(old->name, BAD_CAST "mac") ||  /* ethernet stuff to move down */
-             xmlStrEqual(old->name, BAD_CAST "bond") || /* bond stuff to move down */
-             xmlStrEqual(old->name, BAD_CAST "vlan"))) { /* vlan stuff to move down */
-            xmlUnlinkNode(old);
-            if (!xmlAddChild(if_node, old)) {
-                vshError(ctl, _("Failed to move '%s' element in xml document"), old->name);
-                xmlFreeNode(old);
-                goto cleanup;
-            }
-        }
-    }
-
-    /* The document should now be fully converted; write it out to a string. */
-    xmlDocDumpMemory(xml_doc, &br_xml, &br_xml_size);
-
-    if (!br_xml || br_xml_size <= 0) {
-        vshError(ctl, _("Failed to format new xml document for bridge %s"), br_name);
-        goto cleanup;
-    }
-
-
-    /* br_xml is the new interface to define. It will automatically undefine the
-     * independent original interface.
-     */
-    if (!(br_handle = virInterfaceDefineXML(ctl->conn, (char *) br_xml, 0))) {
-        vshError(ctl, _("Failed to define new bridge interface %s"),
-                 br_name);
-        goto cleanup;
-    }
-
-    vshPrint(ctl, _("Created bridge %s with attached device %s\n"),
-             br_name, if_name);
-
-    /* start it up unless requested not to */
-    if (!nostart) {
-        if (virInterfaceCreate(br_handle, 0) < 0) {
-            vshError(ctl, _("Failed to start bridge interface %s"), br_name);
-            goto cleanup;
-        }
-        vshPrint(ctl, _("Bridge interface %s started\n"), br_name);
-    }
-
-    ret = true;
- cleanup:
-    if (if_handle)
-       virInterfaceFree(if_handle);
-    if (br_handle)
-       virInterfaceFree(br_handle);
-    VIR_FREE(if_xml);
-    VIR_FREE(br_xml);
-    VIR_FREE(if_type);
-    VIR_FREE(if2_name);
-    VIR_FREE(delay_str);
-    xmlXPathFreeContext(ctxt);
-    xmlFreeDoc(xml_doc);
-    return ret;
-}
-
-/*
- * "iface-unbridge" command
- */
-static const vshCmdInfo info_interface_unbridge[] = {
-    {"help", N_("undefine a bridge device after detaching its slave device")},
-    {"desc", N_("unbridge a network device")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_interface_unbridge[] = {
-    {"bridge", VSH_OT_DATA, VSH_OFLAG_REQ, N_("current bridge device name")},
-    {"no-start", VSH_OT_BOOL, 0,
-     N_("don't start the un-slaved interface immediately (not recommended)")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdInterfaceUnbridge(vshControl *ctl, const vshCmd *cmd)
-{
-    bool ret = false;
-    virInterfacePtr if_handle = NULL, br_handle = NULL;
-    const char *br_name;
-    char *if_type = NULL, *if_name = NULL;
-    bool nostart = false;
-    char *br_xml = NULL;
-    xmlChar *if_xml = NULL;
-    int if_xml_size;
-    xmlDocPtr xml_doc = NULL;
-    xmlXPathContextPtr ctxt = NULL;
-    xmlNodePtr top_node, br_node, if_node, cur;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        goto cleanup;
-
-    /* Get a handle to the original device */
-    if (!(br_handle = vshCommandOptInterfaceBy(ctl, cmd, "bridge",
-                                               &br_name, VSH_BYNAME))) {
-        goto cleanup;
-    }
-
-    nostart = vshCommandOptBool(cmd, "no-start");
-
-    /* Get the bridge xml into an xmlDoc */
-    if (!(br_xml = virInterfaceGetXMLDesc(br_handle, VIR_INTERFACE_XML_INACTIVE)))
-        goto cleanup;
-    if (!(xml_doc = virXMLParseStringCtxt(br_xml,
-                                          _("(bridge interface definition)"),
-                                          &ctxt))) {
-        vshError(ctl, _("Failed to parse configuration of %s"), br_name);
-        goto cleanup;
-    }
-    top_node = ctxt->node;
-
-    /* Verify that the device really is a bridge. */
-    if (!(if_type = virXMLPropString(top_node, "type"))) {
-        vshError(ctl, _("Existing device %s has no type"), br_name);
-        goto cleanup;
-    }
-
-    if (STRNEQ(if_type, "bridge")) {
-        vshError(ctl, _("Device %s is not a bridge"), br_name);
-        goto cleanup;
-    }
-    VIR_FREE(if_type);
-
-    /* verify the name in the XML matches the device name */
-    if (!(if_name = virXMLPropString(top_node, "name")) ||
-        STRNEQ(if_name, br_name)) {
-        vshError(ctl, _("Interface name from config %s doesn't match given supplied name %s"),
-                 if_name, br_name);
-        goto cleanup;
-    }
-    VIR_FREE(if_name);
-
-    /* Find the <bridge> node under <interface>. */
-    if (!(br_node = virXPathNode("./bridge", ctxt))) {
-        vshError(ctl, "%s", _("No bridge node in xml document"));
-        goto cleanup;
-    }
-
-    if ((if_node = virXPathNode("./bridge/interface[2]", ctxt))) {
-        vshError(ctl, "%s", _("Multiple interfaces attached to bridge"));
-        goto cleanup;
-    }
-
-    if (!(if_node = virXPathNode("./bridge/interface", ctxt))) {
-        vshError(ctl, "%s", _("No interface attached to bridge"));
-        goto cleanup;
-    }
-
-    /* Change the type and name of the outer/master interface to
-     * the type/name of the attached slave interface.
-     */
-    if (!(if_name = virXMLPropString(if_node, "name"))) {
-        vshError(ctl, _("Device attached to bridge %s has no name"), br_name);
-        goto cleanup;
-    }
-
-    if (!(if_type = virXMLPropString(if_node, "type"))) {
-        vshError(ctl, _("Attached device %s has no type"), if_name);
-        goto cleanup;
-    }
-
-    if (!xmlSetProp(top_node, BAD_CAST "type", BAD_CAST if_type)) {
-        vshError(ctl, _("Failed to set interface type to '%s' in xml document"),
-                 if_type);
-        goto cleanup;
-    }
-
-    if (!xmlSetProp(top_node, BAD_CAST "name", BAD_CAST if_name)) {
-        vshError(ctl, _("Failed to set interface name to '%s' in xml document"),
-                 if_name);
-        goto cleanup;
-    }
-
-    /* Cycle through all the nodes under the attached <interface>,
-     * moving all <mac>, <bond> and <vlan> nodes up into the toplevel
-     * <interface>.
-     */
-    cur = if_node->children;
-    while (cur) {
-        xmlNodePtr old = cur;
-
-        cur = cur->next;
-        if ((old->type == XML_ELEMENT_NODE) &&
-            (xmlStrEqual(old->name, BAD_CAST "mac") ||  /* ethernet stuff to move down */
-             xmlStrEqual(old->name, BAD_CAST "bond") || /* bond stuff to move down */
-             xmlStrEqual(old->name, BAD_CAST "vlan"))) { /* vlan stuff to move down */
-            xmlUnlinkNode(old);
-            if (!xmlAddChild(top_node, old)) {
-                vshError(ctl, _("Failed to move '%s' element in xml document"), old->name);
-                xmlFreeNode(old);
-                goto cleanup;
-            }
-        }
-    }
-
-    /* The document should now be fully converted; write it out to a string. */
-    xmlDocDumpMemory(xml_doc, &if_xml, &if_xml_size);
-
-    if (!if_xml || if_xml_size <= 0) {
-        vshError(ctl, _("Failed to format new xml document for un-enslaved interface %s"),
-                 if_name);
-        goto cleanup;
-    }
-
-    /* Destroy and Undefine the bridge device, since we otherwise
-     * can't safely define the unattached device.
-     */
-    if (virInterfaceDestroy(br_handle, 0) < 0) {
-        vshError(ctl, _("Failed to destroy bridge interface %s"), br_name);
-        goto cleanup;
-    }
-    if (virInterfaceUndefine(br_handle) < 0) {
-        vshError(ctl, _("Failed to undefine bridge interface %s"), br_name);
-        goto cleanup;
-    }
-
-    /* if_xml is the new interface to define.
-     */
-    if (!(if_handle = virInterfaceDefineXML(ctl->conn, (char *) if_xml, 0))) {
-        vshError(ctl, _("Failed to define new interface %s"), if_name);
-        goto cleanup;
-    }
-
-    vshPrint(ctl, _("Device %s un-attached from bridge %s\n"),
-             if_name, br_name);
-
-    /* unless requested otherwise, undefine the bridge device */
-    if (!nostart) {
-        if (virInterfaceCreate(if_handle, 0) < 0) {
-            vshError(ctl, _("Failed to start interface %s"), if_name);
-            goto cleanup;
-        }
-        vshPrint(ctl, _("Interface %s started\n"), if_name);
-    }
-
-    ret = true;
- cleanup:
-    if (if_handle)
-       virInterfaceFree(if_handle);
-    if (br_handle)
-       virInterfaceFree(br_handle);
-    VIR_FREE(if_xml);
-    VIR_FREE(br_xml);
-    VIR_FREE(if_type);
-    VIR_FREE(if_name);
-    xmlXPathFreeContext(ctxt);
-    xmlFreeDoc(xml_doc);
-    return ret;
-}
-
-/*
- * "nwfilter-define" command
- */
-static const vshCmdInfo info_nwfilter_define[] = {
-    {"help", N_("define or update a network filter from an XML file")},
-    {"desc", N_("Define a new network filter or update an existing one.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_nwfilter_define[] = {
-    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML network filter description")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNWFilterDefine(vshControl *ctl, const vshCmd *cmd)
-{
-    virNWFilterPtr nwfilter;
-    const char *from = NULL;
-    bool ret = true;
-    char *buffer;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (vshCommandOptString(cmd, "file", &from) <= 0)
-        return false;
-
-    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
-        return false;
-
-    nwfilter = virNWFilterDefineXML(ctl->conn, buffer);
-    VIR_FREE(buffer);
-
-    if (nwfilter != NULL) {
-        vshPrint(ctl, _("Network filter %s defined from %s\n"),
-                 virNWFilterGetName(nwfilter), from);
-        virNWFilterFree(nwfilter);
-    } else {
-        vshError(ctl, _("Failed to define network filter from %s"), from);
-        ret = false;
-    }
-    return ret;
-}
-
-
-/*
- * "nwfilter-undefine" command
- */
-static const vshCmdInfo info_nwfilter_undefine[] = {
-    {"help", N_("undefine a network filter")},
-    {"desc", N_("Undefine a given network filter.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_nwfilter_undefine[] = {
-    {"nwfilter", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network filter name or uuid")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNWFilterUndefine(vshControl *ctl, const vshCmd *cmd)
-{
-    virNWFilterPtr nwfilter;
-    bool ret = true;
-    const char *name;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!(nwfilter = vshCommandOptNWFilter(ctl, cmd, &name)))
-        return false;
-
-    if (virNWFilterUndefine(nwfilter) == 0) {
-        vshPrint(ctl, _("Network filter %s undefined\n"), name);
-    } else {
-        vshError(ctl, _("Failed to undefine network filter %s"), name);
-        ret = false;
-    }
-
-    virNWFilterFree(nwfilter);
-    return ret;
-}
-
-
-/*
- * "nwfilter-dumpxml" command
- */
-static const vshCmdInfo info_nwfilter_dumpxml[] = {
-    {"help", N_("network filter information in XML")},
-    {"desc", N_("Output the network filter information as an XML dump to stdout.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_nwfilter_dumpxml[] = {
-    {"nwfilter", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network filter name or uuid")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNWFilterDumpXML(vshControl *ctl, const vshCmd *cmd)
-{
-    virNWFilterPtr nwfilter;
-    bool ret = true;
-    char *dump;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!(nwfilter = vshCommandOptNWFilter(ctl, cmd, NULL)))
-        return false;
-
-    dump = virNWFilterGetXMLDesc(nwfilter, 0);
-    if (dump != NULL) {
-        vshPrint(ctl, "%s", dump);
-        VIR_FREE(dump);
-    } else {
-        ret = false;
-    }
-
-    virNWFilterFree(nwfilter);
-    return ret;
-}
-
-/*
- * "nwfilter-list" command
- */
-static const vshCmdInfo info_nwfilter_list[] = {
-    {"help", N_("list network filters")},
-    {"desc", N_("Returns list of network filters.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_nwfilter_list[] = {
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNWFilterList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
-{
-    int numfilters, i;
-    char **names;
-    char uuid[VIR_UUID_STRING_BUFLEN];
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    numfilters = virConnectNumOfNWFilters(ctl->conn);
-    if (numfilters < 0) {
-        vshError(ctl, "%s", _("Failed to list network filters"));
-        return false;
-    }
-
-    names = vshMalloc(ctl, sizeof(char *) * numfilters);
-
-    if ((numfilters = virConnectListNWFilters(ctl->conn, names,
-                                              numfilters)) < 0) {
-        vshError(ctl, "%s", _("Failed to list network filters"));
-        VIR_FREE(names);
-        return false;
-    }
-
-    qsort(&names[0], numfilters, sizeof(char *), vshNameSorter);
-
-    vshPrintExtra(ctl, "%-36s  %-20s \n", _("UUID"), _("Name"));
-    vshPrintExtra(ctl,
-       "----------------------------------------------------------------\n");
-
-    for (i = 0; i < numfilters; i++) {
-        virNWFilterPtr nwfilter =
-            virNWFilterLookupByName(ctl->conn, names[i]);
-
-        /* this kind of work with networks is not atomic operation */
-        if (!nwfilter) {
-            VIR_FREE(names[i]);
-            continue;
-        }
-
-        virNWFilterGetUUIDString(nwfilter, uuid);
-        vshPrint(ctl, "%-36s  %-20s\n",
-                 uuid,
-                 virNWFilterGetName(nwfilter));
-        virNWFilterFree(nwfilter);
-        VIR_FREE(names[i]);
-    }
-
-    VIR_FREE(names);
-    return true;
-}
-
-
-/*
- * "nwfilter-edit" command
- */
-static const vshCmdInfo info_nwfilter_edit[] = {
-    {"help", N_("edit XML configuration for a network filter")},
-    {"desc", N_("Edit the XML configuration for a network filter.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_nwfilter_edit[] = {
-    {"nwfilter", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network filter name or uuid")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNWFilterEdit(vshControl *ctl, const vshCmd *cmd)
-{
-    bool ret = false;
-    virNWFilterPtr nwfilter = NULL;
-    virNWFilterPtr nwfilter_edited = NULL;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        goto cleanup;
-
-    nwfilter = vshCommandOptNWFilter(ctl, cmd, NULL);
-    if (nwfilter == NULL)
-        goto cleanup;
-
-#define EDIT_GET_XML virNWFilterGetXMLDesc(nwfilter, 0)
-#define EDIT_NOT_CHANGED \
-    vshPrint(ctl, _("Network filter %s XML "            \
-                    "configuration not changed.\n"),    \
-             virNWFilterGetName(nwfilter));             \
-    ret = true; goto edit_cleanup;
-#define EDIT_DEFINE \
-    (nwfilter_edited = virNWFilterDefineXML(ctl->conn, doc_edited))
-#define EDIT_FREE \
-    if (nwfilter_edited)    \
-        virNWFilterFree(nwfilter);
-#include "virsh-edit.c"
-
-    vshPrint(ctl, _("Network filter %s XML configuration edited.\n"),
-             virNWFilterGetName(nwfilter_edited));
-
-    ret = true;
-
-cleanup:
-    if (nwfilter)
-        virNWFilterFree(nwfilter);
-    if (nwfilter_edited)
-        virNWFilterFree(nwfilter_edited);
-
-    return ret;
-}
-
-
-/**************************************************************************/
-/*
- * "pool-autostart" command
- */
-static const vshCmdInfo info_pool_autostart[] = {
-    {"help", N_("autostart a pool")},
-    {"desc",
-     N_("Configure a pool to be automatically started at boot.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_pool_autostart[] = {
-    {"pool",  VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
-    {"disable", VSH_OT_BOOL, 0, N_("disable autostarting")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdPoolAutostart(vshControl *ctl, const vshCmd *cmd)
-{
-    virStoragePoolPtr pool;
-    const char *name;
-    int autostart;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
-        return false;
-
-    autostart = !vshCommandOptBool(cmd, "disable");
-
-    if (virStoragePoolSetAutostart(pool, autostart) < 0) {
-        if (autostart)
-            vshError(ctl, _("failed to mark pool %s as autostarted"), name);
-        else
-            vshError(ctl, _("failed to unmark pool %s as autostarted"), name);
-        virStoragePoolFree(pool);
-        return false;
-    }
-
-    if (autostart)
-        vshPrint(ctl, _("Pool %s marked as autostarted\n"), name);
-    else
-        vshPrint(ctl, _("Pool %s unmarked as autostarted\n"), name);
-
-    virStoragePoolFree(pool);
-    return true;
-}
-
-/*
- * "pool-create" command
- */
-static const vshCmdInfo info_pool_create[] = {
-    {"help", N_("create a pool from an XML file")},
-    {"desc", N_("Create a pool.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_pool_create[] = {
-    {"file", VSH_OT_DATA, VSH_OFLAG_REQ,
-     N_("file containing an XML pool description")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdPoolCreate(vshControl *ctl, const vshCmd *cmd)
-{
-    virStoragePoolPtr pool;
-    const char *from = NULL;
-    bool ret = true;
-    char *buffer;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (vshCommandOptString(cmd, "file", &from) <= 0)
-        return false;
-
-    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
-        return false;
-
-    pool = virStoragePoolCreateXML(ctl->conn, buffer, 0);
-    VIR_FREE(buffer);
-
-    if (pool != NULL) {
-        vshPrint(ctl, _("Pool %s created from %s\n"),
-                 virStoragePoolGetName(pool), from);
-        virStoragePoolFree(pool);
-    } else {
-        vshError(ctl, _("Failed to create pool from %s"), from);
-        ret = false;
-    }
-    return ret;
-}
-
-
-/*
- * "nodedev-create" command
- */
-static const vshCmdInfo info_node_device_create[] = {
-    {"help", N_("create a device defined "
-                          "by an XML file on the node")},
-    {"desc", N_("Create a device on the node.  Note that this "
-                          "command creates devices on the physical host "
-                          "that can then be assigned to a virtual machine.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_node_device_create[] = {
-    {"file", VSH_OT_DATA, VSH_OFLAG_REQ,
-     N_("file containing an XML description of the device")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNodeDeviceCreate(vshControl *ctl, const vshCmd *cmd)
-{
-    virNodeDevicePtr dev = NULL;
-    const char *from = NULL;
-    bool ret = true;
-    char *buffer;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (vshCommandOptString(cmd, "file", &from) <= 0)
-        return false;
-
-    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
-        return false;
-
-    dev = virNodeDeviceCreateXML(ctl->conn, buffer, 0);
-    VIR_FREE(buffer);
-
-    if (dev != NULL) {
-        vshPrint(ctl, _("Node device %s created from %s\n"),
-                 virNodeDeviceGetName(dev), from);
-        virNodeDeviceFree(dev);
-    } else {
-        vshError(ctl, _("Failed to create node device from %s"), from);
-        ret = false;
-    }
-
-    return ret;
-}
-
-
-/*
- * "nodedev-destroy" command
- */
-static const vshCmdInfo info_node_device_destroy[] = {
-    {"help", N_("destroy (stop) a device on the node")},
-    {"desc", N_("Destroy a device on the node.  Note that this "
-                "command destroys devices on the physical host")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_node_device_destroy[] = {
-    {"name", VSH_OT_DATA, VSH_OFLAG_REQ,
-     N_("name of the device to be destroyed")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNodeDeviceDestroy(vshControl *ctl, const vshCmd *cmd)
-{
-    virNodeDevicePtr dev = NULL;
-    bool ret = true;
-    const char *name = NULL;
-
-    if (!vshConnectionUsability(ctl, ctl->conn)) {
-        return false;
-    }
-
-    if (vshCommandOptString(cmd, "name", &name) <= 0)
-        return false;
-
-    dev = virNodeDeviceLookupByName(ctl->conn, name);
-
-    if (virNodeDeviceDestroy(dev) == 0) {
-        vshPrint(ctl, _("Destroyed node device '%s'\n"), name);
-    } else {
-        vshError(ctl, _("Failed to destroy node device '%s'"), name);
-        ret = false;
-    }
-
-    virNodeDeviceFree(dev);
-    return ret;
-}
-
-
-/*
- * XML Building helper for pool-define-as and pool-create-as
- */
-static const vshCmdOptDef opts_pool_X_as[] = {
-    {"name", VSH_OT_DATA, VSH_OFLAG_REQ, N_("name of the pool")},
-    {"print-xml", VSH_OT_BOOL, 0, N_("print XML document, but don't define/create")},
-    {"type", VSH_OT_DATA, VSH_OFLAG_REQ, N_("type of the pool")},
-    {"source-host", VSH_OT_DATA, 0, N_("source-host for underlying storage")},
-    {"source-path", VSH_OT_DATA, 0, N_("source path for underlying storage")},
-    {"source-dev", VSH_OT_DATA, 0, N_("source device for underlying storage")},
-    {"source-name", VSH_OT_DATA, 0, N_("source name for underlying storage")},
-    {"target", VSH_OT_DATA, 0, N_("target for underlying storage")},
-    {"source-format", VSH_OT_STRING, 0, N_("format for underlying storage")},
-    {NULL, 0, 0, NULL}
-};
-
-static int buildPoolXML(const vshCmd *cmd, const char **retname, char **xml) {
-
-    const char *name = NULL, *type = NULL, *srcHost = NULL, *srcPath = NULL,
-               *srcDev = NULL, *srcName = NULL, *srcFormat = NULL, *target = NULL;
-    virBuffer buf = VIR_BUFFER_INITIALIZER;
-
-    if (vshCommandOptString(cmd, "name", &name) <= 0)
-        goto cleanup;
-    if (vshCommandOptString(cmd, "type", &type) <= 0)
-        goto cleanup;
-
-    if (vshCommandOptString(cmd, "source-host", &srcHost) < 0 ||
-        vshCommandOptString(cmd, "source-path", &srcPath) < 0 ||
-        vshCommandOptString(cmd, "source-dev", &srcDev) < 0 ||
-        vshCommandOptString(cmd, "source-name", &srcName) < 0 ||
-        vshCommandOptString(cmd, "source-format", &srcFormat) < 0 ||
-        vshCommandOptString(cmd, "target", &target) < 0) {
-        vshError(NULL, "%s", _("missing argument"));
-        goto cleanup;
-    }
-
-    virBufferAsprintf(&buf, "<pool type='%s'>\n", type);
-    virBufferAsprintf(&buf, "  <name>%s</name>\n", name);
-    if (srcHost || srcPath || srcDev || srcFormat || srcName) {
-        virBufferAddLit(&buf, "  <source>\n");
-
-        if (srcHost)
-            virBufferAsprintf(&buf, "    <host name='%s'/>\n", srcHost);
-        if (srcPath)
-            virBufferAsprintf(&buf, "    <dir path='%s'/>\n", srcPath);
-        if (srcDev)
-            virBufferAsprintf(&buf, "    <device path='%s'/>\n", srcDev);
-        if (srcFormat)
-            virBufferAsprintf(&buf, "    <format type='%s'/>\n", srcFormat);
-        if (srcName)
-            virBufferAsprintf(&buf, "    <name>%s</name>\n", srcName);
-
-        virBufferAddLit(&buf, "  </source>\n");
-    }
-    if (target) {
-        virBufferAddLit(&buf, "  <target>\n");
-        virBufferAsprintf(&buf, "    <path>%s</path>\n", target);
-        virBufferAddLit(&buf, "  </target>\n");
-    }
-    virBufferAddLit(&buf, "</pool>\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;
-}
-
-/*
- * "pool-create-as" command
- */
-static const vshCmdInfo info_pool_create_as[] = {
-    {"help", N_("create a pool from a set of args")},
-    {"desc", N_("Create a pool.")},
-    {NULL, NULL}
-};
-
-static bool
-cmdPoolCreateAs(vshControl *ctl, const vshCmd *cmd)
-{
-    virStoragePoolPtr pool;
-    const char *name;
-    char *xml;
-    bool printXML = vshCommandOptBool(cmd, "print-xml");
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!buildPoolXML(cmd, &name, &xml))
-        return false;
-
-    if (printXML) {
-        vshPrint(ctl, "%s", xml);
-        VIR_FREE(xml);
-    } else {
-        pool = virStoragePoolCreateXML(ctl->conn, xml, 0);
-        VIR_FREE(xml);
-
-        if (pool != NULL) {
-            vshPrint(ctl, _("Pool %s created\n"), name);
-            virStoragePoolFree(pool);
-        } else {
-            vshError(ctl, _("Failed to create pool %s"), name);
-            return false;
-        }
-    }
-    return true;
-}
-
-
-/*
- * "pool-define" command
- */
-static const vshCmdInfo info_pool_define[] = {
-    {"help", N_("define (but don't start) a pool from an XML file")},
-    {"desc", N_("Define a pool.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_pool_define[] = {
-    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML pool description")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdPoolDefine(vshControl *ctl, const vshCmd *cmd)
-{
-    virStoragePoolPtr pool;
-    const char *from = NULL;
-    bool ret = true;
-    char *buffer;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (vshCommandOptString(cmd, "file", &from) <= 0)
-        return false;
-
-    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
-        return false;
-
-    pool = virStoragePoolDefineXML(ctl->conn, buffer, 0);
-    VIR_FREE(buffer);
-
-    if (pool != NULL) {
-        vshPrint(ctl, _("Pool %s defined from %s\n"),
-                 virStoragePoolGetName(pool), from);
-        virStoragePoolFree(pool);
-    } else {
-        vshError(ctl, _("Failed to define pool from %s"), from);
-        ret = false;
-    }
-    return ret;
-}
-
-
-/*
- * "pool-define-as" command
- */
-static const vshCmdInfo info_pool_define_as[] = {
-    {"help", N_("define a pool from a set of args")},
-    {"desc", N_("Define a pool.")},
-    {NULL, NULL}
-};
-
-static bool
-cmdPoolDefineAs(vshControl *ctl, const vshCmd *cmd)
-{
-    virStoragePoolPtr pool;
-    const char *name;
-    char *xml;
-    bool printXML = vshCommandOptBool(cmd, "print-xml");
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!buildPoolXML(cmd, &name, &xml))
-        return false;
-
-    if (printXML) {
-        vshPrint(ctl, "%s", xml);
-        VIR_FREE(xml);
-    } else {
-        pool = virStoragePoolDefineXML(ctl->conn, xml, 0);
-        VIR_FREE(xml);
-
-        if (pool != NULL) {
-            vshPrint(ctl, _("Pool %s defined\n"), name);
-            virStoragePoolFree(pool);
-        } else {
-            vshError(ctl, _("Failed to define pool %s"), name);
-            return false;
-        }
-    }
-    return true;
-}
-
-
-/*
- * "pool-build" command
- */
-static const vshCmdInfo info_pool_build[] = {
-    {"help", N_("build a pool")},
-    {"desc", N_("Build a given pool.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_pool_build[] = {
-    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
-    {"no-overwrite", VSH_OT_BOOL, 0, N_("do not overwrite an existing pool of this type")},
-    {"overwrite", VSH_OT_BOOL, 0, N_("overwrite any existing data")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdPoolBuild(vshControl *ctl, const vshCmd *cmd)
-{
-    virStoragePoolPtr pool;
-    bool ret = true;
-    const char *name;
-    unsigned int flags = 0;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
-        return false;
-
-    if (vshCommandOptBool(cmd, "no-overwrite")) {
-        flags |= VIR_STORAGE_POOL_BUILD_NO_OVERWRITE;
-    }
-
-    if (vshCommandOptBool(cmd, "overwrite")) {
-        flags |= VIR_STORAGE_POOL_BUILD_OVERWRITE;
-    }
-
-    if (virStoragePoolBuild(pool, flags) == 0) {
-        vshPrint(ctl, _("Pool %s built\n"), name);
-    } else {
-        vshError(ctl, _("Failed to build pool %s"), name);
-        ret = false;
-    }
-
-    virStoragePoolFree(pool);
-
-    return ret;
-}
-
-/*
- * "pool-destroy" command
- */
-static const vshCmdInfo info_pool_destroy[] = {
-    {"help", N_("destroy (stop) a pool")},
-    {"desc",
-     N_("Forcefully stop a given pool. Raw data in the pool is untouched")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_pool_destroy[] = {
-    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdPoolDestroy(vshControl *ctl, const vshCmd *cmd)
-{
-    virStoragePoolPtr pool;
-    bool ret = true;
-    const char *name;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
-        return false;
-
-    if (virStoragePoolDestroy(pool) == 0) {
-        vshPrint(ctl, _("Pool %s destroyed\n"), name);
-    } else {
-        vshError(ctl, _("Failed to destroy pool %s"), name);
-        ret = false;
-    }
-
-    virStoragePoolFree(pool);
-    return ret;
-}
-
-
-/*
- * "pool-delete" command
- */
-static const vshCmdInfo info_pool_delete[] = {
-    {"help", N_("delete a pool")},
-    {"desc", N_("Delete a given pool.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_pool_delete[] = {
-    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdPoolDelete(vshControl *ctl, const vshCmd *cmd)
-{
-    virStoragePoolPtr pool;
-    bool ret = true;
-    const char *name;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
-        return false;
-
-    if (virStoragePoolDelete(pool, 0) == 0) {
-        vshPrint(ctl, _("Pool %s deleted\n"), name);
-    } else {
-        vshError(ctl, _("Failed to delete pool %s"), name);
-        ret = false;
-    }
-
-    virStoragePoolFree(pool);
-    return ret;
-}
-
-
-/*
- * "pool-refresh" command
- */
-static const vshCmdInfo info_pool_refresh[] = {
-    {"help", N_("refresh a pool")},
-    {"desc", N_("Refresh a given pool.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_pool_refresh[] = {
-    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdPoolRefresh(vshControl *ctl, const vshCmd *cmd)
-{
-    virStoragePoolPtr pool;
-    bool ret = true;
-    const char *name;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
-        return false;
-
-    if (virStoragePoolRefresh(pool, 0) == 0) {
-        vshPrint(ctl, _("Pool %s refreshed\n"), name);
-    } else {
-        vshError(ctl, _("Failed to refresh pool %s"), name);
-        ret = false;
-    }
-    virStoragePoolFree(pool);
-
-    return ret;
-}
-
-
-/*
- * "pool-dumpxml" command
- */
-static const vshCmdInfo info_pool_dumpxml[] = {
-    {"help", N_("pool information in XML")},
-    {"desc", N_("Output the pool information as an XML dump to stdout.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_pool_dumpxml[] = {
-    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
-    {"inactive", VSH_OT_BOOL, 0, N_("show inactive defined XML")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdPoolDumpXML(vshControl *ctl, const vshCmd *cmd)
-{
-    virStoragePoolPtr pool;
-    bool ret = true;
-    bool inactive = vshCommandOptBool(cmd, "inactive");
-    unsigned int flags = 0;
-    char *dump;
-
-    if (inactive)
-        flags |= VIR_STORAGE_XML_INACTIVE;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", NULL)))
-        return false;
-
-    dump = virStoragePoolGetXMLDesc(pool, flags);
-    if (dump != NULL) {
-        vshPrint(ctl, "%s", dump);
-        VIR_FREE(dump);
-    } else {
-        ret = false;
-    }
-
-    virStoragePoolFree(pool);
-    return ret;
-}
-
-
-/*
- * "pool-list" command
- */
-static const vshCmdInfo info_pool_list[] = {
-    {"help", N_("list pools")},
-    {"desc", N_("Returns list of pools.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_pool_list[] = {
-    {"inactive", VSH_OT_BOOL, 0, N_("list inactive pools")},
-    {"all", VSH_OT_BOOL, 0, N_("list inactive & active pools")},
-    {"details", VSH_OT_BOOL, 0, N_("display extended details for pools")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdPoolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
-{
-    virStoragePoolInfo info;
-    char **poolNames = NULL;
-    int i, ret;
-    bool functionReturn;
-    int numActivePools = 0, numInactivePools = 0, numAllPools = 0;
-    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 poolInfoText {
-        char *state;
-        char *autostart;
-        char *persistent;
-        char *capacity;
-        char *allocation;
-        char *available;
-    };
-    struct poolInfoText *poolInfoTexts = NULL;
-
-    /* Determine the options passed by the user */
-    bool all = vshCommandOptBool(cmd, "all");
-    bool details = vshCommandOptBool(cmd, "details");
-    bool inactive = vshCommandOptBool(cmd, "inactive");
-    bool active = !inactive || all;
-    inactive |= all;
-
-    /* Check the connection to libvirtd daemon is still working */
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    /* Retrieve the number of active storage pools */
-    if (active) {
-        numActivePools = virConnectNumOfStoragePools(ctl->conn);
-        if (numActivePools < 0) {
-            vshError(ctl, "%s", _("Failed to list active pools"));
-            return false;
-        }
-    }
-
-    /* Retrieve the number of inactive storage pools */
-    if (inactive) {
-        numInactivePools = virConnectNumOfDefinedStoragePools(ctl->conn);
-        if (numInactivePools < 0) {
-            vshError(ctl, "%s", _("Failed to list inactive pools"));
-            return false;
-        }
-    }
-
-    /* Determine the total number of pools to list */
-    numAllPools = numActivePools + numInactivePools;
-
-    /* Allocate memory for arrays of storage pool names and info */
-    poolNames = vshCalloc(ctl, numAllPools, sizeof(*poolNames));
-    poolInfoTexts =
-        vshCalloc(ctl, numAllPools, sizeof(*poolInfoTexts));
-
-    /* Retrieve a list of active storage pool names */
-    if (active) {
-        if (virConnectListStoragePools(ctl->conn,
-                                       poolNames, numActivePools) < 0) {
-            vshError(ctl, "%s", _("Failed to list active pools"));
-            VIR_FREE(poolInfoTexts);
-            VIR_FREE(poolNames);
-            return false;
-        }
-    }
-
-    /* Add the inactive storage pools to the end of the name list */
-    if (inactive) {
-        if (virConnectListDefinedStoragePools(ctl->conn,
-                                              &poolNames[numActivePools],
-                                              numInactivePools) < 0) {
-            vshError(ctl, "%s", _("Failed to list inactive pools"));
-            VIR_FREE(poolInfoTexts);
-            VIR_FREE(poolNames);
-            return false;
-        }
-    }
-
-    /* Sort the storage pool names */
-    qsort(poolNames, numAllPools, sizeof(*poolNames), vshNameSorter);
-
-    /* Collect the storage pool information for display */
-    for (i = 0; i < numAllPools; i++) {
-        int autostart = 0, persistent = 0;
-
-        /* Retrieve a pool object, looking it up by name */
-        virStoragePoolPtr pool = virStoragePoolLookupByName(ctl->conn,
-                                                            poolNames[i]);
-        if (!pool) {
-            VIR_FREE(poolNames[i]);
-            continue;
-        }
-
-        /* Retrieve the autostart status of the pool */
-        if (virStoragePoolGetAutostart(pool, &autostart) < 0)
-            poolInfoTexts[i].autostart = vshStrdup(ctl, _("no autostart"));
-        else
-            poolInfoTexts[i].autostart = vshStrdup(ctl, autostart ?
-                                                    _("yes") : _("no"));
-
-        /* Retrieve the persistence status of the pool */
-        if (details) {
-            persistent = virStoragePoolIsPersistent(pool);
-            vshDebug(ctl, VSH_ERR_DEBUG, "Persistent flag value: %d\n",
-                     persistent);
-            if (persistent < 0)
-                poolInfoTexts[i].persistent = vshStrdup(ctl, _("unknown"));
-            else
-                poolInfoTexts[i].persistent = vshStrdup(ctl, persistent ?
-                                                         _("yes") : _("no"));
-
-            /* Keep the length of persistent string if longest so far */
-            stringLength = strlen(poolInfoTexts[i].persistent);
-            if (stringLength > persistStrLength)
-                persistStrLength = stringLength;
-        }
-
-        /* Collect further extended information about the pool */
-        if (virStoragePoolGetInfo(pool, &info) != 0) {
-            /* Something went wrong retrieving pool info, cope with it */
-            vshError(ctl, "%s", _("Could not retrieve pool information"));
-            poolInfoTexts[i].state = vshStrdup(ctl, _("unknown"));
-            if (details) {
-                poolInfoTexts[i].capacity = vshStrdup(ctl, _("unknown"));
-                poolInfoTexts[i].allocation = vshStrdup(ctl, _("unknown"));
-                poolInfoTexts[i].available = vshStrdup(ctl, _("unknown"));
-            }
-        } else {
-            /* Decide which state string to display */
-            if (details) {
-                /* --details option was specified, we're using detailed state
-                 * strings */
-                switch (info.state) {
-                case VIR_STORAGE_POOL_INACTIVE:
-                    poolInfoTexts[i].state = vshStrdup(ctl, _("inactive"));
-                    break;
-                case VIR_STORAGE_POOL_BUILDING:
-                    poolInfoTexts[i].state = vshStrdup(ctl, _("building"));
-                    break;
-                case VIR_STORAGE_POOL_RUNNING:
-                    poolInfoTexts[i].state = vshStrdup(ctl, _("running"));
-                    break;
-                case VIR_STORAGE_POOL_DEGRADED:
-                    poolInfoTexts[i].state = vshStrdup(ctl, _("degraded"));
-                    break;
-                case VIR_STORAGE_POOL_INACCESSIBLE:
-                    poolInfoTexts[i].state = vshStrdup(ctl, _("inaccessible"));
-                    break;
-                }
-
-                /* Create the pool size related strings */
-                if (info.state == VIR_STORAGE_POOL_RUNNING ||
-                    info.state == VIR_STORAGE_POOL_DEGRADED) {
-                    double val;
-                    const char *unit;
-
-                    /* Create the capacity output string */
-                    val = prettyCapacity(info.capacity, &unit);
-                    ret = virAsprintf(&poolInfoTexts[i].capacity,
-                                      "%.2lf %s", val, unit);
-                    if (ret < 0) {
-                        /* An error occurred creating the string, return */
-                        goto asprintf_failure;
-                    }
-
-                    /* Create the allocation output string */
-                    val = prettyCapacity(info.allocation, &unit);
-                    ret = virAsprintf(&poolInfoTexts[i].allocation,
-                                      "%.2lf %s", val, unit);
-                    if (ret < 0) {
-                        /* An error occurred creating the string, return */
-                        goto asprintf_failure;
-                    }
-
-                    /* Create the available space output string */
-                    val = prettyCapacity(info.available, &unit);
-                    ret = virAsprintf(&poolInfoTexts[i].available,
-                                      "%.2lf %s", val, unit);
-                    if (ret < 0) {
-                        /* An error occurred creating the string, return */
-                        goto asprintf_failure;
-                    }
-                } else {
-                    /* Capacity related information isn't available */
-                    poolInfoTexts[i].capacity = vshStrdup(ctl, _("-"));
-                    poolInfoTexts[i].allocation = vshStrdup(ctl, _("-"));
-                    poolInfoTexts[i].available = vshStrdup(ctl, _("-"));
-                }
-
-                /* Keep the length of capacity string if longest so far */
-                stringLength = strlen(poolInfoTexts[i].capacity);
-                if (stringLength > capStrLength)
-                    capStrLength = stringLength;
-
-                /* Keep the length of allocation string if longest so far */
-                stringLength = strlen(poolInfoTexts[i].allocation);
-                if (stringLength > allocStrLength)
-                    allocStrLength = stringLength;
-
-                /* Keep the length of available string if longest so far */
-                stringLength = strlen(poolInfoTexts[i].available);
-                if (stringLength > availStrLength)
-                    availStrLength = stringLength;
-            } else {
-                /* --details option was not specified, only active/inactive
-                * state strings are used */
-                if (info.state == VIR_STORAGE_POOL_INACTIVE)
-                    poolInfoTexts[i].state = vshStrdup(ctl, _("inactive"));
-                else
-                    poolInfoTexts[i].state = vshStrdup(ctl, _("active"));
-            }
-        }
-
-        /* Keep the length of name string if longest so far */
-        stringLength = strlen(poolNames[i]);
-        if (stringLength > nameStrLength)
-            nameStrLength = stringLength;
-
-        /* Keep the length of state string if longest so far */
-        stringLength = strlen(poolInfoTexts[i].state);
-        if (stringLength > stateStrLength)
-            stateStrLength = stringLength;
-
-        /* Keep the length of autostart string if longest so far */
-        stringLength = strlen(poolInfoTexts[i].autostart);
-        if (stringLength > autostartStrLength)
-            autostartStrLength = stringLength;
-
-        /* Free the pool object */
-        virStoragePoolFree(pool);
-    }
-
-    /* If the --details option wasn't selected, we output the pool
-     * 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 pool info */
-        for (i = 0; i < numAllPools; i++) {
-            vshPrint(ctl, "%-20s %-10s %-10s\n",
-                 poolNames[i],
-                 poolInfoTexts[i].state,
-                 poolInfoTexts[i].autostart);
-        }
-
-        /* Cleanup and return */
-        functionReturn = 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.
-     */
-    char *outputStr;
-    ret = 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);
-    if (ret < 0) {
-        /* An error occurred creating the string, return */
-        goto asprintf_failure;
-    }
-
-    /* Display the header */
-    vshPrint(ctl, outputStr, _("Name"), _("State"), _("Autostart"),
-             _("Persistent"), _("Capacity"), _("Allocation"), _("Available"));
-    for (i = nameStrLength + stateStrLength + autostartStrLength
-                           + persistStrLength + capStrLength
-                           + allocStrLength + availStrLength
-                           + 12; i > 0; i--)
-        vshPrintExtra(ctl, "-");
-    vshPrintExtra(ctl, "\n");
-
-    /* Display the pool info rows */
-    for (i = 0; i < numAllPools; i++) {
-        vshPrint(ctl, outputStr,
-                 poolNames[i],
-                 poolInfoTexts[i].state,
-                 poolInfoTexts[i].autostart,
-                 poolInfoTexts[i].persistent,
-                 poolInfoTexts[i].capacity,
-                 poolInfoTexts[i].allocation,
-                 poolInfoTexts[i].available);
-    }
-
-    /* Cleanup and return */
-    functionReturn = true;
-    goto cleanup;
-
-asprintf_failure:
-
-    /* Display an appropriate error message then cleanup and return */
-    switch (errno) {
-    case ENOMEM:
-        /* Couldn't allocate memory */
-        vshError(ctl, "%s", _("Out of memory"));
-        break;
-    default:
-        /* Some other error */
-        vshError(ctl, _("virAsprintf failed (errno %d)"), errno);
-    }
-    functionReturn = false;
-
-cleanup:
-
-    /* Safely free the memory allocated in this function */
-    for (i = 0; i < numAllPools; i++) {
-        /* Cleanup the memory for one pool info structure */
-        VIR_FREE(poolInfoTexts[i].state);
-        VIR_FREE(poolInfoTexts[i].autostart);
-        VIR_FREE(poolInfoTexts[i].persistent);
-        VIR_FREE(poolInfoTexts[i].capacity);
-        VIR_FREE(poolInfoTexts[i].allocation);
-        VIR_FREE(poolInfoTexts[i].available);
-        VIR_FREE(poolNames[i]);
-    }
-
-    /* Cleanup the memory for the initial arrays*/
-    VIR_FREE(poolInfoTexts);
-    VIR_FREE(poolNames);
-
-    /* Return the desired value */
-    return functionReturn;
-}
-
-/*
- * "find-storage-pool-sources-as" command
- */
-static const vshCmdInfo info_find_storage_pool_sources_as[] = {
-    {"help", N_("find potential storage pool sources")},
-    {"desc", N_("Returns XML <sources> document.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_find_storage_pool_sources_as[] = {
-    {"type", VSH_OT_DATA, VSH_OFLAG_REQ,
-     N_("type of storage pool sources to find")},
-    {"host", VSH_OT_DATA, VSH_OFLAG_NONE, N_("optional host to query")},
-    {"port", VSH_OT_DATA, VSH_OFLAG_NONE, N_("optional port to query")},
-    {"initiator", VSH_OT_DATA, VSH_OFLAG_NONE, N_("optional initiator IQN to use for query")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdPoolDiscoverSourcesAs(vshControl * ctl, const vshCmd * cmd ATTRIBUTE_UNUSED)
-{
-    const char *type = NULL, *host = NULL;
-    char *srcSpec = NULL;
-    char *srcList;
-    const char *initiator = NULL;
-
-    if (vshCommandOptString(cmd, "type", &type) <= 0 ||
-        vshCommandOptString(cmd, "host", &host) < 0 ||
-        vshCommandOptString(cmd, "initiator", &initiator) < 0) {
-        vshError(ctl,"%s", _("missing argument"));
-        return false;
-    }
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (host) {
-        const char *port = NULL;
-        virBuffer buf = VIR_BUFFER_INITIALIZER;
-
-        if (vshCommandOptString(cmd, "port", &port) < 0) {
-            vshError(ctl, "%s", _("missing argument"));
-            virBufferFreeAndReset(&buf);
-            return false;
-        }
-        virBufferAddLit(&buf, "<source>\n");
-        virBufferAsprintf(&buf, "  <host name='%s'", host);
-        if (port)
-            virBufferAsprintf(&buf, " port='%s'", port);
-        virBufferAddLit(&buf, "/>\n");
-        if (initiator) {
-            virBufferAddLit(&buf, "  <initiator>\n");
-            virBufferAsprintf(&buf, "    <iqn name='%s'/>\n", initiator);
-            virBufferAddLit(&buf, "  </initiator>\n");
-        }
-        virBufferAddLit(&buf, "</source>\n");
-        if (virBufferError(&buf)) {
-            vshError(ctl, "%s", _("Out of memory"));
-            return false;
-        }
-        srcSpec = virBufferContentAndReset(&buf);
-    }
-
-    srcList = virConnectFindStoragePoolSources(ctl->conn, type, srcSpec, 0);
-    VIR_FREE(srcSpec);
-    if (srcList == NULL) {
-        vshError(ctl, _("Failed to find any %s pool sources"), type);
-        return false;
-    }
-    vshPrint(ctl, "%s", srcList);
-    VIR_FREE(srcList);
-
-    return true;
-}
-
-
-/*
- * "find-storage-pool-sources" command
- */
-static const vshCmdInfo info_find_storage_pool_sources[] = {
-    {"help", N_("discover potential storage pool sources")},
-    {"desc", N_("Returns XML <sources> document.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_find_storage_pool_sources[] = {
-    {"type", VSH_OT_DATA, VSH_OFLAG_REQ,
-     N_("type of storage pool sources to discover")},
-    {"srcSpec", VSH_OT_DATA, VSH_OFLAG_NONE,
-     N_("optional file of source xml to query for pools")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdPoolDiscoverSources(vshControl * ctl, const vshCmd * cmd ATTRIBUTE_UNUSED)
-{
-    const char *type = NULL, *srcSpecFile = NULL;
-    char *srcSpec = NULL, *srcList;
-
-    if (vshCommandOptString(cmd, "type", &type) <= 0)
-        return false;
-
-    if (vshCommandOptString(cmd, "srcSpec", &srcSpecFile) < 0) {
-        vshError(ctl, "%s", _("missing option"));
-        return false;
-    }
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (srcSpecFile && virFileReadAll(srcSpecFile, VIRSH_MAX_XML_FILE, &srcSpec) < 0)
-        return false;
-
-    srcList = virConnectFindStoragePoolSources(ctl->conn, type, srcSpec, 0);
-    VIR_FREE(srcSpec);
-    if (srcList == NULL) {
-        vshError(ctl, _("Failed to find any %s pool sources"), type);
-        return false;
-    }
-    vshPrint(ctl, "%s", srcList);
-    VIR_FREE(srcList);
-
-    return true;
-}
-
-
-/*
- * "pool-info" command
- */
-static const vshCmdInfo info_pool_info[] = {
-    {"help", N_("storage pool information")},
-    {"desc", N_("Returns basic information about the storage pool.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_pool_info[] = {
-    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdPoolInfo(vshControl *ctl, const vshCmd *cmd)
-{
-    virStoragePoolInfo info;
-    virStoragePoolPtr pool;
-    int autostart = 0;
-    int persistent = 0;
-    bool ret = true;
-    char uuid[VIR_UUID_STRING_BUFLEN];
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", NULL)))
-        return false;
-
-    vshPrint(ctl, "%-15s %s\n", _("Name:"), virStoragePoolGetName(pool));
-
-    if (virStoragePoolGetUUIDString(pool, &uuid[0])==0)
-        vshPrint(ctl, "%-15s %s\n", _("UUID:"), uuid);
-
-    if (virStoragePoolGetInfo(pool, &info) == 0) {
-        double val;
-        const char *unit;
-        switch (info.state) {
-        case VIR_STORAGE_POOL_INACTIVE:
-            vshPrint(ctl, "%-15s %s\n", _("State:"),
-                     _("inactive"));
-            break;
-        case VIR_STORAGE_POOL_BUILDING:
-            vshPrint(ctl, "%-15s %s\n", _("State:"),
-                     _("building"));
-            break;
-        case VIR_STORAGE_POOL_RUNNING:
-            vshPrint(ctl, "%-15s %s\n", _("State:"),
-                     _("running"));
-            break;
-        case VIR_STORAGE_POOL_DEGRADED:
-            vshPrint(ctl, "%-15s %s\n", _("State:"),
-                     _("degraded"));
-            break;
-        case VIR_STORAGE_POOL_INACCESSIBLE:
-            vshPrint(ctl, "%-15s %s\n", _("State:"),
-                     _("inaccessible"));
-            break;
-        }
-
-        /* Check and display whether the pool is persistent or not */
-        persistent = virStoragePoolIsPersistent(pool);
-        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 pool is autostarted or not */
-        virStoragePoolGetAutostart(pool, &autostart);
-        vshDebug(ctl, VSH_ERR_DEBUG, "Pool autostart flag value: %d\n",
-                 autostart);
-        if (autostart < 0)
-            vshPrint(ctl, "%-15s %s\n", _("Autostart:"), _("no autostart"));
-        else
-            vshPrint(ctl, "%-15s %s\n", _("Autostart:"), autostart ? _("yes") : _("no"));
-
-        if (info.state == VIR_STORAGE_POOL_RUNNING ||
-            info.state == VIR_STORAGE_POOL_DEGRADED) {
-            val = prettyCapacity(info.capacity, &unit);
-            vshPrint(ctl, "%-15s %2.2lf %s\n", _("Capacity:"), val, unit);
-
-            val = prettyCapacity(info.allocation, &unit);
-            vshPrint(ctl, "%-15s %2.2lf %s\n", _("Allocation:"), val, unit);
-
-            val = prettyCapacity(info.available, &unit);
-            vshPrint(ctl, "%-15s %2.2lf %s\n", _("Available:"), val, unit);
-        }
-    } else {
-        ret = false;
-    }
-
-    virStoragePoolFree(pool);
-    return ret;
-}
-
-
-/*
- * "pool-name" command
- */
-static const vshCmdInfo info_pool_name[] = {
-    {"help", N_("convert a pool UUID to pool name")},
-    {"desc", ""},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_pool_name[] = {
-    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool uuid")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdPoolName(vshControl *ctl, const vshCmd *cmd)
-{
-    virStoragePoolPtr pool;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-    if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL,
-                                           VSH_BYUUID)))
-        return false;
-
-    vshPrint(ctl, "%s\n", virStoragePoolGetName(pool));
-    virStoragePoolFree(pool);
-    return true;
-}
-
-
-/*
- * "pool-start" command
- */
-static const vshCmdInfo info_pool_start[] = {
-    {"help", N_("start a (previously defined) inactive pool")},
-    {"desc", N_("Start a pool.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_pool_start[] = {
-    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("name or uuid of the inactive pool")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdPoolStart(vshControl *ctl, const vshCmd *cmd)
-{
-    virStoragePoolPtr pool;
-    bool ret = true;
-    const char *name = NULL;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
-         return false;
-
-    if (virStoragePoolCreate(pool, 0) == 0) {
-        vshPrint(ctl, _("Pool %s started\n"), name);
-    } else {
-        vshError(ctl, _("Failed to start pool %s"), name);
-        ret = false;
-    }
-
-    virStoragePoolFree(pool);
-    return ret;
-}
-
-
-/*
- * "vol-create-as" command
- */
-static const vshCmdInfo info_vol_create_as[] = {
-    {"help", N_("create a volume from a set of args")},
-    {"desc", N_("Create a vol.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_vol_create_as[] = {
-    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name")},
-    {"name", VSH_OT_DATA, VSH_OFLAG_REQ, N_("name of the volume")},
-    {"capacity", VSH_OT_DATA, VSH_OFLAG_REQ,
-     N_("size of the vol, as scaled integer (default bytes)")},
-    {"allocation", VSH_OT_STRING, 0,
-     N_("initial allocation size, as scaled integer (default bytes)")},
-    {"format", VSH_OT_STRING, 0,
-     N_("file format type raw,bochs,qcow,qcow2,qed,vmdk")},
-    {"backing-vol", VSH_OT_STRING, 0,
-     N_("the backing volume if taking a snapshot")},
-    {"backing-vol-format", VSH_OT_STRING, 0,
-     N_("format of backing volume if taking a snapshot")},
-    {NULL, 0, 0, NULL}
-};
-
-static int
-vshVolSize(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
-cmdVolCreateAs(vshControl *ctl, const vshCmd *cmd)
-{
-    virStoragePoolPtr pool;
-    virStorageVolPtr vol;
-    char *xml;
-    const char *name, *capacityStr = NULL, *allocationStr = NULL, *format = NULL;
-    const char *snapshotStrVol = NULL, *snapshotStrFormat = NULL;
-    unsigned long long capacity, allocation = 0;
-    virBuffer buf = VIR_BUFFER_INITIALIZER;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL,
-                                     VSH_BYNAME)))
-        return false;
-
-    if (vshCommandOptString(cmd, "name", &name) <= 0)
-        goto cleanup;
-
-    if (vshCommandOptString(cmd, "capacity", &capacityStr) <= 0)
-        goto cleanup;
-
-    if (vshVolSize(capacityStr, &capacity) < 0) {
-        vshError(ctl, _("Malformed size %s"), capacityStr);
-        goto cleanup;
-    }
-
-    if (vshCommandOptString(cmd, "allocation", &allocationStr) > 0 &&
-        vshVolSize(allocationStr, &allocation) < 0) {
-        vshError(ctl, _("Malformed size %s"), allocationStr);
-        goto cleanup;
-    }
-
-    if (vshCommandOptString(cmd, "format", &format) < 0 ||
-        vshCommandOptString(cmd, "backing-vol", &snapshotStrVol) < 0 ||
-        vshCommandOptString(cmd, "backing-vol-format",
-                            &snapshotStrFormat) < 0) {
-        vshError(ctl, "%s", _("missing argument"));
-        goto cleanup;
-    }
-
-
-    virBufferAddLit(&buf, "<volume>\n");
-    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");
-        virBufferAsprintf(&buf, "    <format type='%s'/>\n",format);
-        virBufferAddLit(&buf, "  </target>\n");
-    }
-
-    /* Convert the snapshot parameters into backingStore XML */
-    if (snapshotStrVol) {
-        /* Lookup snapshot backing volume.  Try the backing-vol
-         *  parameter as a name */
-        vshDebug(ctl, VSH_ERR_DEBUG,
-                 "%s: Look up backing store volume '%s' as name\n",
-                 cmd->def->name, snapshotStrVol);
-        virStorageVolPtr snapVol = virStorageVolLookupByName(pool, snapshotStrVol);
-        if (snapVol)
-                vshDebug(ctl, VSH_ERR_DEBUG,
-                         "%s: Backing store volume found using '%s' as name\n",
-                         cmd->def->name, snapshotStrVol);
-
-        if (snapVol == NULL) {
-            /* Snapshot backing volume not found by name.  Try the
-             *  backing-vol parameter as a key */
-            vshDebug(ctl, VSH_ERR_DEBUG,
-                     "%s: Look up backing store volume '%s' as key\n",
-                     cmd->def->name, snapshotStrVol);
-            snapVol = virStorageVolLookupByKey(ctl->conn, snapshotStrVol);
-            if (snapVol)
-                vshDebug(ctl, VSH_ERR_DEBUG,
-                         "%s: Backing store volume found using '%s' as key\n",
-                         cmd->def->name, snapshotStrVol);
-        }
-        if (snapVol == NULL) {
-            /* Snapshot backing volume not found by key.  Try the
-             *  backing-vol parameter as a path */
-            vshDebug(ctl, VSH_ERR_DEBUG,
-                     "%s: Look up backing store volume '%s' as path\n",
-                     cmd->def->name, snapshotStrVol);
-            snapVol = virStorageVolLookupByPath(ctl->conn, snapshotStrVol);
-            if (snapVol)
-                vshDebug(ctl, VSH_ERR_DEBUG,
-                         "%s: Backing store volume found using '%s' as path\n",
-                         cmd->def->name, snapshotStrVol);
-        }
-        if (snapVol == NULL) {
-            vshError(ctl, _("failed to get vol '%s'"), snapshotStrVol);
-            goto cleanup;
-        }
-
-        char *snapshotStrVolPath;
-        if ((snapshotStrVolPath = virStorageVolGetPath(snapVol)) == NULL) {
-            virStorageVolFree(snapVol);
-            goto cleanup;
-        }
-
-        /* Create XML for the backing store */
-        virBufferAddLit(&buf, "  <backingStore>\n");
-        virBufferAsprintf(&buf, "    <path>%s</path>\n",snapshotStrVolPath);
-        if (snapshotStrFormat)
-            virBufferAsprintf(&buf, "    <format type='%s'/>\n",snapshotStrFormat);
-        virBufferAddLit(&buf, "  </backingStore>\n");
-
-        /* Cleanup snapshot allocations */
-        VIR_FREE(snapshotStrVolPath);
-        virStorageVolFree(snapVol);
-    }
-
-    virBufferAddLit(&buf, "</volume>\n");
-
-    if (virBufferError(&buf)) {
-        vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
-        goto cleanup;
-    }
-    xml = virBufferContentAndReset(&buf);
-    vol = virStorageVolCreateXML(pool, xml, 0);
-    VIR_FREE(xml);
-    virStoragePoolFree(pool);
-
-    if (vol != NULL) {
-        vshPrint(ctl, _("Vol %s created\n"), name);
-        virStorageVolFree(vol);
-        return true;
-    } else {
-        vshError(ctl, _("Failed to create vol %s"), name);
-        return false;
-    }
-
- cleanup:
-    virBufferFreeAndReset(&buf);
-    virStoragePoolFree(pool);
-    return false;
-}
-
-
-/*
- * "pool-undefine" command
- */
-static const vshCmdInfo info_pool_undefine[] = {
-    {"help", N_("undefine an inactive pool")},
-    {"desc", N_("Undefine the configuration for an inactive pool.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_pool_undefine[] = {
-    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdPoolUndefine(vshControl *ctl, const vshCmd *cmd)
-{
-    virStoragePoolPtr pool;
-    bool ret = true;
-    const char *name;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
-        return false;
-
-    if (virStoragePoolUndefine(pool) == 0) {
-        vshPrint(ctl, _("Pool %s has been undefined\n"), name);
-    } else {
-        vshError(ctl, _("Failed to undefine pool %s"), name);
-        ret = false;
-    }
-
-    virStoragePoolFree(pool);
-    return ret;
-}
-
-
-/*
- * "pool-uuid" command
- */
-static const vshCmdInfo info_pool_uuid[] = {
-    {"help", N_("convert a pool name to pool UUID")},
-    {"desc", ""},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_pool_uuid[] = {
-    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdPoolUuid(vshControl *ctl, const vshCmd *cmd)
-{
-    virStoragePoolPtr pool;
-    char uuid[VIR_UUID_STRING_BUFLEN];
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL,
-                                           VSH_BYNAME)))
-        return false;
-
-    if (virStoragePoolGetUUIDString(pool, uuid) != -1)
-        vshPrint(ctl, "%s\n", uuid);
-    else
-        vshError(ctl, "%s", _("failed to get pool UUID"));
-
-    virStoragePoolFree(pool);
-    return true;
-}
-
-
-/*
- * "vol-create" command
- */
-static const vshCmdInfo info_vol_create[] = {
-    {"help", N_("create a vol from an XML file")},
-    {"desc", N_("Create a vol.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_vol_create[] = {
-    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name")},
-    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML vol description")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdVolCreate(vshControl *ctl, const vshCmd *cmd)
-{
-    virStoragePoolPtr pool;
-    virStorageVolPtr vol;
-    const char *from = NULL;
-    bool ret = true;
-    char *buffer;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL,
-                                           VSH_BYNAME)))
-        return false;
-
-    if (vshCommandOptString(cmd, "file", &from) <= 0) {
-        virStoragePoolFree(pool);
-        return false;
-    }
-
-    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
-        virshReportError(ctl);
-        virStoragePoolFree(pool);
-        return false;
-    }
-
-    vol = virStorageVolCreateXML(pool, buffer, 0);
-    VIR_FREE(buffer);
-    virStoragePoolFree(pool);
-
-    if (vol != NULL) {
-        vshPrint(ctl, _("Vol %s created from %s\n"),
-                 virStorageVolGetName(vol), from);
-        virStorageVolFree(vol);
-    } else {
-        vshError(ctl, _("Failed to create vol from %s"), from);
-        ret = false;
-    }
-    return ret;
-}
-
-/*
- * "vol-create-from" command
- */
-static const vshCmdInfo info_vol_create_from[] = {
-    {"help", N_("create a vol, using another volume as input")},
-    {"desc", N_("Create a vol from an existing volume.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_vol_create_from[] = {
-    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
-    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML vol description")},
-    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("input vol name or key")},
-    {"inputpool", VSH_OT_STRING, 0, N_("pool name or uuid of the input volume's pool")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdVolCreateFrom(vshControl *ctl, const vshCmd *cmd)
-{
-    virStoragePoolPtr pool = NULL;
-    virStorageVolPtr newvol = NULL, inputvol = NULL;
-    const char *from = NULL;
-    bool ret = false;
-    char *buffer = NULL;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        goto cleanup;
-
-    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", NULL)))
-        goto cleanup;
-
-    if (vshCommandOptString(cmd, "file", &from) <= 0) {
-        goto cleanup;
-    }
-
-    if (!(inputvol = vshCommandOptVol(ctl, cmd, "vol", "inputpool", NULL)))
-        goto cleanup;
-
-    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
-        virshReportError(ctl);
-        goto cleanup;
-    }
-
-    newvol = virStorageVolCreateXMLFrom(pool, buffer, inputvol, 0);
-
-    if (newvol != NULL) {
-        vshPrint(ctl, _("Vol %s created from input vol %s\n"),
-                 virStorageVolGetName(newvol), virStorageVolGetName(inputvol));
-    } else {
-        vshError(ctl, _("Failed to create vol from %s"), from);
-        goto cleanup;
-    }
-
-    ret = true;
-cleanup:
-    VIR_FREE(buffer);
-    if (pool)
-        virStoragePoolFree(pool);
-    if (inputvol)
-        virStorageVolFree(inputvol);
-    if (newvol)
-        virStorageVolFree(newvol);
-    return ret;
-}
-
-static xmlChar *
-makeCloneXML(const char *origxml, const char *newname)
-{
-
-    xmlDocPtr doc = NULL;
-    xmlXPathContextPtr ctxt = NULL;
-    xmlXPathObjectPtr obj = NULL;
-    xmlChar *newxml = NULL;
-    int size;
-
-    doc = virXMLParseStringCtxt(origxml, _("(volume_definition)"), &ctxt);
-    if (!doc)
-        goto cleanup;
-
-    obj = xmlXPathEval(BAD_CAST "/volume/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;
-}
-
-/*
- * "vol-clone" command
- */
-static const vshCmdInfo info_vol_clone[] = {
-    {"help", N_("clone a volume.")},
-    {"desc", N_("Clone an existing volume.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_vol_clone[] = {
-    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("orig vol name or key")},
-    {"newname", VSH_OT_DATA, VSH_OFLAG_REQ, N_("clone name")},
-    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdVolClone(vshControl *ctl, const vshCmd *cmd)
-{
-    virStoragePoolPtr origpool = NULL;
-    virStorageVolPtr origvol = NULL, newvol = NULL;
-    const char *name = NULL;
-    char *origxml = NULL;
-    xmlChar *newxml = NULL;
-    bool ret = false;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        goto cleanup;
-
-    if (!(origvol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL)))
-        goto cleanup;
-
-    origpool = virStoragePoolLookupByVolume(origvol);
-    if (!origpool) {
-        vshError(ctl, "%s", _("failed to get parent pool"));
-        goto cleanup;
-    }
-
-    if (vshCommandOptString(cmd, "newname", &name) <= 0)
-        goto cleanup;
-
-    origxml = virStorageVolGetXMLDesc(origvol, 0);
-    if (!origxml)
-        goto cleanup;
-
-    newxml = makeCloneXML(origxml, name);
-    if (!newxml) {
-        vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
-        goto cleanup;
-    }
-
-    newvol = virStorageVolCreateXMLFrom(origpool, (char *) newxml, origvol, 0);
-
-    if (newvol != NULL) {
-        vshPrint(ctl, _("Vol %s cloned from %s\n"),
-                 virStorageVolGetName(newvol), virStorageVolGetName(origvol));
-    } else {
-        vshError(ctl, _("Failed to clone vol from %s"),
-                 virStorageVolGetName(origvol));
-        goto cleanup;
-    }
-
-    ret = true;
-
-cleanup:
-    VIR_FREE(origxml);
-    xmlFree(newxml);
-    if (origvol)
-        virStorageVolFree(origvol);
-    if (newvol)
-        virStorageVolFree(newvol);
-    if (origpool)
-        virStoragePoolFree(origpool);
-    return ret;
-}
-
-
-/*
- * "vol-upload" command
- */
-static const vshCmdInfo info_vol_upload[] = {
-    {"help", N_("upload a file into a volume")},
-    {"desc", N_("Upload a file into a volume")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_vol_upload[] = {
-    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
-    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file")},
-    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
-    {"offset", VSH_OT_INT, 0, N_("volume offset to upload to") },
-    {"length", VSH_OT_INT, 0, N_("amount of data to upload") },
-    {NULL, 0, 0, NULL}
-};
-
-static int
-cmdVolUploadSource(virStreamPtr st ATTRIBUTE_UNUSED,
-                   char *bytes, size_t nbytes, void *opaque)
-{
-    int *fd = opaque;
-
-    return saferead(*fd, bytes, nbytes);
-}
-
-static bool
-cmdVolUpload(vshControl *ctl, const vshCmd *cmd)
-{
-    const char *file = NULL;
-    virStorageVolPtr vol = NULL;
-    bool ret = false;
-    int fd = -1;
-    virStreamPtr st = NULL;
-    const char *name = NULL;
-    unsigned long long offset = 0, length = 0;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        goto cleanup;
-
-    if (vshCommandOptULongLong(cmd, "offset", &offset) < 0) {
-        vshError(ctl, _("Unable to parse integer"));
-        return false;
-    }
-
-    if (vshCommandOptULongLong(cmd, "length", &length) < 0) {
-        vshError(ctl, _("Unable to parse integer"));
-        return false;
-    }
-
-    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", &name))) {
-        return false;
-    }
-
-    if (vshCommandOptString(cmd, "file", &file) < 0) {
-        vshError(ctl, _("file must not be empty"));
-        goto cleanup;
-    }
-
-    if ((fd = open(file, O_RDONLY)) < 0) {
-        vshError(ctl, _("cannot read %s"), file);
-        goto cleanup;
-    }
-
-    st = virStreamNew(ctl->conn, 0);
-    if (virStorageVolUpload(vol, st, offset, length, 0) < 0) {
-        vshError(ctl, _("cannot upload to volume %s"), name);
-        goto cleanup;
-    }
-
-    if (virStreamSendAll(st, cmdVolUploadSource, &fd) < 0) {
-        vshError(ctl, _("cannot send data to volume %s"), name);
-        goto cleanup;
-    }
-
-    if (VIR_CLOSE(fd) < 0) {
-        vshError(ctl, _("cannot close file %s"), file);
-        virStreamAbort(st);
-        goto cleanup;
-    }
-
-    if (virStreamFinish(st) < 0) {
-        vshError(ctl, _("cannot close volume %s"), name);
-        goto cleanup;
-    }
-
-    ret = true;
-
-cleanup:
-    if (vol)
-        virStorageVolFree(vol);
-    if (st)
-        virStreamFree(st);
-    VIR_FORCE_CLOSE(fd);
-    return ret;
-}
-
-
-
-/*
- * "vol-download" command
- */
-static const vshCmdInfo info_vol_download[] = {
-    {"help", N_("Download a volume to a file")},
-    {"desc", N_("Download a volume to a file")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_vol_download[] = {
-    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
-    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file")},
-    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
-    {"offset", VSH_OT_INT, 0, N_("volume offset to download from") },
-    {"length", VSH_OT_INT, 0, N_("amount of data to download") },
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdVolDownload(vshControl *ctl, const vshCmd *cmd)
-{
-    const char *file = NULL;
-    virStorageVolPtr vol = NULL;
-    bool ret = false;
-    int fd = -1;
-    virStreamPtr st = NULL;
-    const char *name = NULL;
-    unsigned long long offset = 0, length = 0;
-    bool created = false;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (vshCommandOptULongLong(cmd, "offset", &offset) < 0) {
-        vshError(ctl, _("Unable to parse integer"));
-        return false;
-    }
-
-    if (vshCommandOptULongLong(cmd, "length", &length) < 0) {
-        vshError(ctl, _("Unable to parse integer"));
-        return false;
-    }
-
-    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", &name)))
-        return false;
-
-    if (vshCommandOptString(cmd, "file", &file) < 0) {
-        vshError(ctl, _("file must not be empty"));
-        goto cleanup;
-    }
-
-    if ((fd = open(file, O_WRONLY|O_CREAT|O_EXCL, 0666)) < 0) {
-        if (errno != EEXIST ||
-            (fd = open(file, O_WRONLY|O_TRUNC, 0666)) < 0) {
-            vshError(ctl, _("cannot create %s"), file);
-            goto cleanup;
-        }
-    } else {
-        created = true;
-    }
-
-    st = virStreamNew(ctl->conn, 0);
-    if (virStorageVolDownload(vol, st, offset, length, 0) < 0) {
-        vshError(ctl, _("cannot download from volume %s"), name);
-        goto cleanup;
-    }
-
-    if (virStreamRecvAll(st, vshStreamSink, &fd) < 0) {
-        vshError(ctl, _("cannot receive data from volume %s"), name);
-        goto cleanup;
-    }
-
-    if (VIR_CLOSE(fd) < 0) {
-        vshError(ctl, _("cannot close file %s"), file);
-        virStreamAbort(st);
-        goto cleanup;
-    }
-
-    if (virStreamFinish(st) < 0) {
-        vshError(ctl, _("cannot close volume %s"), name);
-        goto cleanup;
-    }
-
-    ret = true;
-
-cleanup:
-    VIR_FORCE_CLOSE(fd);
-    if (!ret && created)
-        unlink(file);
-    if (vol)
-        virStorageVolFree(vol);
-    if (st)
-        virStreamFree(st);
-    return ret;
-}
-
-
-/*
- * "vol-delete" command
- */
-static const vshCmdInfo info_vol_delete[] = {
-    {"help", N_("delete a vol")},
-    {"desc", N_("Delete a given vol.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_vol_delete[] = {
-    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
-    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdVolDelete(vshControl *ctl, const vshCmd *cmd)
-{
-    virStorageVolPtr vol;
-    bool ret = true;
-    const char *name;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", &name))) {
-        return false;
-    }
-
-    if (virStorageVolDelete(vol, 0) == 0) {
-        vshPrint(ctl, _("Vol %s deleted\n"), name);
-    } else {
-        vshError(ctl, _("Failed to delete vol %s"), name);
-        ret = false;
-    }
-
-    virStorageVolFree(vol);
-    return ret;
-}
-
-
-/*
- * "vol-wipe" command
- */
-static const vshCmdInfo info_vol_wipe[] = {
-    {"help", N_("wipe a vol")},
-    {"desc", N_("Ensure data previously on a volume is not accessible to future reads")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_vol_wipe[] = {
-    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
-    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
-    {"algorithm", VSH_OT_STRING, 0, N_("perform selected wiping algorithm")},
-    {NULL, 0, 0, NULL}
-};
-
-VIR_ENUM_DECL(virStorageVolWipeAlgorithm)
-VIR_ENUM_IMPL(virStorageVolWipeAlgorithm, VIR_STORAGE_VOL_WIPE_ALG_LAST,
-              "zero", "nnsa", "dod", "bsi", "gutmann", "schneier",
-              "pfitzner7", "pfitzner33", "random");
-
-static bool
-cmdVolWipe(vshControl *ctl, const vshCmd *cmd)
-{
-    virStorageVolPtr vol;
-    bool ret = false;
-    const char *name;
-    const char *algorithm_str = NULL;
-    int algorithm = VIR_STORAGE_VOL_WIPE_ALG_ZERO;
-    int funcRet;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", &name))) {
-        return false;
-    }
-
-    if (vshCommandOptString(cmd, "algorithm", &algorithm_str) < 0) {
-        vshError(ctl, "%s", _("missing argument"));
-        goto out;
-    }
-
-    if (algorithm_str &&
-        (algorithm = virStorageVolWipeAlgorithmTypeFromString(algorithm_str)) < 0) {
-        vshError(ctl, _("Unsupported algorithm '%s'"), algorithm_str);
-        goto out;
-    }
-
-    if ((funcRet = virStorageVolWipePattern(vol, algorithm, 0)) < 0) {
-        if (last_error->code == VIR_ERR_NO_SUPPORT &&
-            algorithm == VIR_STORAGE_VOL_WIPE_ALG_ZERO)
-            funcRet = virStorageVolWipe(vol, 0);
-    }
-
-    if (funcRet < 0) {
-        vshError(ctl, _("Failed to wipe vol %s"), name);
-        goto out;
-    }
-
-    vshPrint(ctl, _("Vol %s wiped\n"), name);
-    ret = true;
-out:
-    virStorageVolFree(vol);
-    return ret;
-}
-
-
-/*
- * "vol-info" command
- */
-static const vshCmdInfo info_vol_info[] = {
-    {"help", N_("storage vol information")},
-    {"desc", N_("Returns basic information about the storage vol.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_vol_info[] = {
-    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
-    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdVolInfo(vshControl *ctl, const vshCmd *cmd)
-{
-    virStorageVolInfo info;
-    virStorageVolPtr vol;
-    bool ret = true;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL)))
-        return false;
-
-    vshPrint(ctl, "%-15s %s\n", _("Name:"), virStorageVolGetName(vol));
-
-    if (virStorageVolGetInfo(vol, &info) == 0) {
-        double val;
-        const char *unit;
-        switch(info.type) {
-        case VIR_STORAGE_VOL_FILE:
-            vshPrint(ctl, "%-15s %s\n", _("Type:"), _("file"));
-            break;
-
-        case VIR_STORAGE_VOL_BLOCK:
-            vshPrint(ctl, "%-15s %s\n", _("Type:"), _("block"));
-            break;
-
-        case VIR_STORAGE_VOL_DIR:
-            vshPrint(ctl, "%-15s %s\n", _("Type:"), _("dir"));
-            break;
-
-        case VIR_STORAGE_VOL_NETWORK:
-            vshPrint(ctl, "%-15s %s\n", _("Type:"), _("network"));
-            break;
-
-        default:
-            vshPrint(ctl, "%-15s %s\n", _("Type:"), _("unknown"));
-        }
-
-        val = prettyCapacity(info.capacity, &unit);
-        vshPrint(ctl, "%-15s %2.2lf %s\n", _("Capacity:"), val, unit);
-
-        val = prettyCapacity(info.allocation, &unit);
-        vshPrint(ctl, "%-15s %2.2lf %s\n", _("Allocation:"), val, unit);
-    } else {
-        ret = false;
-    }
-
-    virStorageVolFree(vol);
-    return ret;
-}
-
-/*
- * "vol-resize" command
- */
-static const vshCmdInfo info_vol_resize[] = {
-    {"help", N_("resize a vol")},
-    {"desc", N_("Resizes a storage volume.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_vol_resize[] = {
-    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
-    {"capacity", VSH_OT_DATA, VSH_OFLAG_REQ,
-     N_("new capacity for the vol, as scaled integer (default bytes)")},
-    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
-    {"allocate", VSH_OT_BOOL, 0,
-     N_("allocate the new capacity, rather than leaving it sparse")},
-    {"delta", VSH_OT_BOOL, 0,
-     N_("use capacity as a delta to current size, rather than the new size")},
-    {"shrink", VSH_OT_BOOL, 0, N_("allow the resize to shrink the volume")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdVolResize(vshControl *ctl, const vshCmd *cmd)
-{
-    virStorageVolPtr vol;
-    const char *capacityStr = NULL;
-    unsigned long long capacity = 0;
-    unsigned int flags = 0;
-    bool ret = false;
-    bool delta = false;
-
-    if (vshCommandOptBool(cmd, "allocate"))
-        flags |= VIR_STORAGE_VOL_RESIZE_ALLOCATE;
-    if (vshCommandOptBool(cmd, "delta")) {
-        delta = true;
-        flags |= VIR_STORAGE_VOL_RESIZE_DELTA;
-    }
-    if (vshCommandOptBool(cmd, "shrink"))
-        flags |= VIR_STORAGE_VOL_RESIZE_SHRINK;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL)))
-        return false;
-
-    if (vshCommandOptString(cmd, "capacity", &capacityStr) <= 0)
-        goto cleanup;
-    virSkipSpaces(&capacityStr);
-    if (*capacityStr == '-') {
-        /* The API always requires a positive value; but we allow a
-         * negative value for convenience.  */
-        if (delta && vshCommandOptBool(cmd, "shrink")){
-            capacityStr++;
-        } else {
-            vshError(ctl, "%s",
-                     _("negative size requires --delta and --shrink"));
-            goto cleanup;
-        }
-    }
-    if (vshVolSize(capacityStr, &capacity) < 0) {
-        vshError(ctl, _("Malformed size %s"), capacityStr);
-        goto cleanup;
-    }
-
-    if (virStorageVolResize(vol, capacity, flags) == 0) {
-        vshPrint(ctl,
-                 delta ? _("Size of volume '%s' successfully changed by %s\n")
-                 : _("Size of volume '%s' successfully changed to %s\n"),
-                 virStorageVolGetName(vol), capacityStr);
-        ret = true;
-    } else {
-        vshError(ctl,
-                 delta ? _("Failed to change size of volume '%s' by %s\n")
-                 : _("Failed to change size of volume '%s' to %s\n"),
-                 virStorageVolGetName(vol), capacityStr);
-        ret = false;
-    }
-
-cleanup:
-    virStorageVolFree(vol);
-    return ret;
-}
-
-
-/*
- * "vol-dumpxml" command
- */
-static const vshCmdInfo info_vol_dumpxml[] = {
-    {"help", N_("vol information in XML")},
-    {"desc", N_("Output the vol information as an XML dump to stdout.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_vol_dumpxml[] = {
-    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
-    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdVolDumpXML(vshControl *ctl, const vshCmd *cmd)
-{
-    virStorageVolPtr vol;
-    bool ret = true;
-    char *dump;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL)))
-        return false;
-
-    dump = virStorageVolGetXMLDesc(vol, 0);
-    if (dump != NULL) {
-        vshPrint(ctl, "%s", dump);
-        VIR_FREE(dump);
-    } else {
-        ret = false;
-    }
-
-    virStorageVolFree(vol);
-    return ret;
-}
-
-
-/*
- * "vol-list" command
- */
-static const vshCmdInfo info_vol_list[] = {
-    {"help", N_("list vols")},
-    {"desc", N_("Returns list of vols by pool.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_vol_list[] = {
-    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
-    {"details", VSH_OT_BOOL, 0, N_("display extended details for volumes")},
-    {NULL, 0, 0, NULL}
-};
+static const vshCmdOptDef opts_pool_list[] = {
+    {"inactive", VSH_OT_BOOL, 0, N_("list inactive pools")},
+    {"all", VSH_OT_BOOL, 0, N_("list inactive & active pools")},
+    {"details", VSH_OT_BOOL, 0, N_("display extended details for pools")},
+    {NULL, 0, 0, NULL}
+};
 
 static bool
-cmdVolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
+cmdPoolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
 {
-    virStorageVolInfo volumeInfo;
-    virStoragePoolPtr pool;
-    char **activeNames = NULL;
-    char *outputStr = NULL;
-    const char *unit;
-    double val;
-    bool details = vshCommandOptBool(cmd, "details");
-    int numVolumes = 0, i;
-    int ret;
+    virStoragePoolInfo info;
+    char **poolNames = NULL;
+    int i, ret;
     bool functionReturn;
-    int stringLength = 0;
-    size_t allocStrLength = 0, capStrLength = 0;
-    size_t nameStrLength = 0, pathStrLength = 0;
-    size_t typeStrLength = 0;
-    struct volInfoText {
-        char *allocation;
+    int numActivePools = 0, numInactivePools = 0, numAllPools = 0;
+    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 poolInfoText {
+        char *state;
+        char *autostart;
+        char *persistent;
         char *capacity;
-        char *path;
-        char *type;
+        char *allocation;
+        char *available;
     };
-    struct volInfoText *volInfoTexts = NULL;
+    struct poolInfoText *poolInfoTexts = NULL;
+
+    /* Determine the options passed by the user */
+    bool all = vshCommandOptBool(cmd, "all");
+    bool details = vshCommandOptBool(cmd, "details");
+    bool inactive = vshCommandOptBool(cmd, "inactive");
+    bool active = !inactive || all;
+    inactive |= all;
 
     /* Check the connection to libvirtd daemon is still working */
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    /* Look up the pool information given to us by the user */
-    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", NULL)))
-        return false;
-
-    /* Determine the number of volumes in the pool */
-    numVolumes = virStoragePoolNumOfVolumes(pool);
-
-    if (numVolumes < 0) {
-        vshError(ctl, "%s", _("Failed to list storage volumes"));
-        virStoragePoolFree(pool);
-        return false;
+    /* Retrieve the number of active storage pools */
+    if (active) {
+        numActivePools = virConnectNumOfStoragePools(ctl->conn);
+        if (numActivePools < 0) {
+            vshError(ctl, "%s", _("Failed to list active pools"));
+            return false;
+        }
     }
 
-    /* Retrieve the list of volume names in the pool */
-    if (numVolumes > 0) {
-        activeNames = vshCalloc(ctl, numVolumes, sizeof(*activeNames));
-        if ((numVolumes = virStoragePoolListVolumes(pool, activeNames,
-                                                    numVolumes)) < 0) {
-            vshError(ctl, "%s", _("Failed to list active vols"));
-            VIR_FREE(activeNames);
-            virStoragePoolFree(pool);
+    /* Retrieve the number of inactive storage pools */
+    if (inactive) {
+        numInactivePools = virConnectNumOfDefinedStoragePools(ctl->conn);
+        if (numInactivePools < 0) {
+            vshError(ctl, "%s", _("Failed to list inactive pools"));
             return false;
         }
+    }
 
-        /* Sort the volume names */
-        qsort(&activeNames[0], numVolumes, sizeof(*activeNames), vshNameSorter);
+    /* Determine the total number of pools to list */
+    numAllPools = numActivePools + numInactivePools;
 
-        /* Set aside memory for volume information pointers */
-        volInfoTexts = vshCalloc(ctl, numVolumes, sizeof(*volInfoTexts));
+    /* Allocate memory for arrays of storage pool names and info */
+    poolNames = vshCalloc(ctl, numAllPools, sizeof(*poolNames));
+    poolInfoTexts =
+        vshCalloc(ctl, numAllPools, sizeof(*poolInfoTexts));
+
+    /* Retrieve a list of active storage pool names */
+    if (active) {
+        if (virConnectListStoragePools(ctl->conn,
+                                       poolNames, numActivePools) < 0) {
+            vshError(ctl, "%s", _("Failed to list active pools"));
+            VIR_FREE(poolInfoTexts);
+            VIR_FREE(poolNames);
+            return false;
+        }
     }
 
-    /* Collect the rest of the volume information for display */
-    for (i = 0; i < numVolumes; i++) {
-        /* Retrieve volume info */
-        virStorageVolPtr vol = virStorageVolLookupByName(pool,
-                                                         activeNames[i]);
+    /* Add the inactive storage pools to the end of the name list */
+    if (inactive) {
+        if (virConnectListDefinedStoragePools(ctl->conn,
+                                              &poolNames[numActivePools],
+                                              numInactivePools) < 0) {
+            vshError(ctl, "%s", _("Failed to list inactive pools"));
+            VIR_FREE(poolInfoTexts);
+            VIR_FREE(poolNames);
+            return false;
+        }
+    }
 
-        /* Retrieve the volume path */
-        if ((volInfoTexts[i].path = virStorageVolGetPath(vol)) == NULL) {
-            /* Something went wrong retrieving a volume path, cope with it */
-            volInfoTexts[i].path = vshStrdup(ctl, _("unknown"));
+    /* Sort the storage pool names */
+    qsort(poolNames, numAllPools, sizeof(*poolNames), vshNameSorter);
+
+    /* Collect the storage pool information for display */
+    for (i = 0; i < numAllPools; i++) {
+        int autostart = 0, persistent = 0;
+
+        /* Retrieve a pool object, looking it up by name */
+        virStoragePoolPtr pool = virStoragePoolLookupByName(ctl->conn,
+                                                            poolNames[i]);
+        if (!pool) {
+            VIR_FREE(poolNames[i]);
+            continue;
         }
 
-        /* If requested, retrieve volume type and sizing information */
+        /* Retrieve the autostart status of the pool */
+        if (virStoragePoolGetAutostart(pool, &autostart) < 0)
+            poolInfoTexts[i].autostart = vshStrdup(ctl, _("no autostart"));
+        else
+            poolInfoTexts[i].autostart = vshStrdup(ctl, autostart ?
+                                                    _("yes") : _("no"));
+
+        /* Retrieve the persistence status of the pool */
         if (details) {
-            if (virStorageVolGetInfo(vol, &volumeInfo) != 0) {
-                /* Something went wrong retrieving volume info, cope with it */
-                volInfoTexts[i].allocation = vshStrdup(ctl, _("unknown"));
-                volInfoTexts[i].capacity = vshStrdup(ctl, _("unknown"));
-                volInfoTexts[i].type = vshStrdup(ctl, _("unknown"));
-            } else {
-                /* Convert the returned volume info into output strings */
+            persistent = virStoragePoolIsPersistent(pool);
+            vshDebug(ctl, VSH_ERR_DEBUG, "Persistent flag value: %d\n",
+                     persistent);
+            if (persistent < 0)
+                poolInfoTexts[i].persistent = vshStrdup(ctl, _("unknown"));
+            else
+                poolInfoTexts[i].persistent = vshStrdup(ctl, persistent ?
+                                                         _("yes") : _("no"));
 
-                /* Volume type */
-                switch (volumeInfo.type) {
-                        case VIR_STORAGE_VOL_FILE:
-                            volInfoTexts[i].type = vshStrdup(ctl, _("file"));
-                            break;
-                        case VIR_STORAGE_VOL_BLOCK:
-                            volInfoTexts[i].type = vshStrdup(ctl, _("block"));
-                            break;
-                        case VIR_STORAGE_VOL_DIR:
-                            volInfoTexts[i].type = vshStrdup(ctl, _("dir"));
-                            break;
-                        default:
-                            volInfoTexts[i].type = vshStrdup(ctl, _("unknown"));
-                }
+            /* Keep the length of persistent string if longest so far */
+            stringLength = strlen(poolInfoTexts[i].persistent);
+            if (stringLength > persistStrLength)
+                persistStrLength = stringLength;
+        }
 
-                /* Create the capacity output string */
-                val = prettyCapacity(volumeInfo.capacity, &unit);
-                ret = virAsprintf(&volInfoTexts[i].capacity,
-                                  "%.2lf %s", val, unit);
-                if (ret < 0) {
-                    /* An error occurred creating the string, return */
-                    goto asprintf_failure;
+        /* Collect further extended information about the pool */
+        if (virStoragePoolGetInfo(pool, &info) != 0) {
+            /* Something went wrong retrieving pool info, cope with it */
+            vshError(ctl, "%s", _("Could not retrieve pool information"));
+            poolInfoTexts[i].state = vshStrdup(ctl, _("unknown"));
+            if (details) {
+                poolInfoTexts[i].capacity = vshStrdup(ctl, _("unknown"));
+                poolInfoTexts[i].allocation = vshStrdup(ctl, _("unknown"));
+                poolInfoTexts[i].available = vshStrdup(ctl, _("unknown"));
+            }
+        } else {
+            /* Decide which state string to display */
+            if (details) {
+                /* --details option was specified, we're using detailed state
+                 * strings */
+                switch (info.state) {
+                case VIR_STORAGE_POOL_INACTIVE:
+                    poolInfoTexts[i].state = vshStrdup(ctl, _("inactive"));
+                    break;
+                case VIR_STORAGE_POOL_BUILDING:
+                    poolInfoTexts[i].state = vshStrdup(ctl, _("building"));
+                    break;
+                case VIR_STORAGE_POOL_RUNNING:
+                    poolInfoTexts[i].state = vshStrdup(ctl, _("running"));
+                    break;
+                case VIR_STORAGE_POOL_DEGRADED:
+                    poolInfoTexts[i].state = vshStrdup(ctl, _("degraded"));
+                    break;
+                case VIR_STORAGE_POOL_INACCESSIBLE:
+                    poolInfoTexts[i].state = vshStrdup(ctl, _("inaccessible"));
+                    break;
                 }
 
-                /* Create the allocation output string */
-                val = prettyCapacity(volumeInfo.allocation, &unit);
-                ret = virAsprintf(&volInfoTexts[i].allocation,
-                                  "%.2lf %s", val, unit);
-                if (ret < 0) {
-                    /* An error occurred creating the string, return */
-                    goto asprintf_failure;
-                }
-            }
+                /* Create the pool size related strings */
+                if (info.state == VIR_STORAGE_POOL_RUNNING ||
+                    info.state == VIR_STORAGE_POOL_DEGRADED) {
+                    double val;
+                    const char *unit;
 
-            /* Remember the largest length for each output string.
-             * This lets us displaying header and volume information rows
-             * using a single, properly sized, printf style output string.
-             */
+                    /* Create the capacity output string */
+                    val = prettyCapacity(info.capacity, &unit);
+                    ret = virAsprintf(&poolInfoTexts[i].capacity,
+                                      "%.2lf %s", val, unit);
+                    if (ret < 0) {
+                        /* An error occurred creating the string, return */
+                        goto asprintf_failure;
+                    }
 
-            /* Keep the length of name string if longest so far */
-            stringLength = strlen(activeNames[i]);
-            if (stringLength > nameStrLength)
-                nameStrLength = stringLength;
+                    /* Create the allocation output string */
+                    val = prettyCapacity(info.allocation, &unit);
+                    ret = virAsprintf(&poolInfoTexts[i].allocation,
+                                      "%.2lf %s", val, unit);
+                    if (ret < 0) {
+                        /* An error occurred creating the string, return */
+                        goto asprintf_failure;
+                    }
 
-            /* Keep the length of path string if longest so far */
-            stringLength = strlen(volInfoTexts[i].path);
-            if (stringLength > pathStrLength)
-                pathStrLength = stringLength;
+                    /* Create the available space output string */
+                    val = prettyCapacity(info.available, &unit);
+                    ret = virAsprintf(&poolInfoTexts[i].available,
+                                      "%.2lf %s", val, unit);
+                    if (ret < 0) {
+                        /* An error occurred creating the string, return */
+                        goto asprintf_failure;
+                    }
+                } else {
+                    /* Capacity related information isn't available */
+                    poolInfoTexts[i].capacity = vshStrdup(ctl, _("-"));
+                    poolInfoTexts[i].allocation = vshStrdup(ctl, _("-"));
+                    poolInfoTexts[i].available = vshStrdup(ctl, _("-"));
+                }
 
-            /* Keep the length of type string if longest so far */
-            stringLength = strlen(volInfoTexts[i].type);
-            if (stringLength > typeStrLength)
-                typeStrLength = stringLength;
+                /* Keep the length of capacity string if longest so far */
+                stringLength = strlen(poolInfoTexts[i].capacity);
+                if (stringLength > capStrLength)
+                    capStrLength = stringLength;
+
+                /* Keep the length of allocation string if longest so far */
+                stringLength = strlen(poolInfoTexts[i].allocation);
+                if (stringLength > allocStrLength)
+                    allocStrLength = stringLength;
+
+                /* Keep the length of available string if longest so far */
+                stringLength = strlen(poolInfoTexts[i].available);
+                if (stringLength > availStrLength)
+                    availStrLength = stringLength;
+            } else {
+                /* --details option was not specified, only active/inactive
+                * state strings are used */
+                if (info.state == VIR_STORAGE_POOL_INACTIVE)
+                    poolInfoTexts[i].state = vshStrdup(ctl, _("inactive"));
+                else
+                    poolInfoTexts[i].state = vshStrdup(ctl, _("active"));
+            }
+        }
 
-            /* Keep the length of capacity string if longest so far */
-            stringLength = strlen(volInfoTexts[i].capacity);
-            if (stringLength > capStrLength)
-                capStrLength = stringLength;
+        /* Keep the length of name string if longest so far */
+        stringLength = strlen(poolNames[i]);
+        if (stringLength > nameStrLength)
+            nameStrLength = stringLength;
 
-            /* Keep the length of allocation string if longest so far */
-            stringLength = strlen(volInfoTexts[i].allocation);
-            if (stringLength > allocStrLength)
-                allocStrLength = stringLength;
-        }
+        /* Keep the length of state string if longest so far */
+        stringLength = strlen(poolInfoTexts[i].state);
+        if (stringLength > stateStrLength)
+            stateStrLength = stringLength;
 
-        /* Cleanup memory allocation */
-        virStorageVolFree(vol);
+        /* Keep the length of autostart string if longest so far */
+        stringLength = strlen(poolInfoTexts[i].autostart);
+        if (stringLength > autostartStrLength)
+            autostartStrLength = stringLength;
+
+        /* Free the pool object */
+        virStoragePoolFree(pool);
     }
 
-    /* If the --details option wasn't selected, we output the volume
+    /* If the --details option wasn't selected, we output the pool
      * 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"));
+        /* Output old style header */
+        vshPrintExtra(ctl, "%-20s %-10s %-10s\n", _("Name"), _("State"),
+                      _("Autostart"));
         vshPrintExtra(ctl, "-----------------------------------------\n");
-        for (i = 0; i < numVolumes; i++) {
-            vshPrint(ctl, "%-20s %-40s\n", activeNames[i],
-                     volInfoTexts[i].path);
+
+        /* Output old style pool info */
+        for (i = 0; i < numAllPools; i++) {
+            vshPrint(ctl, "%-20s %-10s %-10s\n",
+                 poolNames[i],
+                 poolInfoTexts[i].state,
+                 poolInfoTexts[i].autostart);
         }
 
         /* Cleanup and return */
@@ -11366,15 +3921,20 @@ cmdVolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
     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 state header string if it's longest */
+    stringLength = strlen(_("State"));
+    if (stringLength > stateStrLength)
+        stateStrLength = stringLength;
 
-    /* Use the length of type header string if it's longest */
-    stringLength = strlen(_("Type"));
-    if (stringLength > typeStrLength)
-        typeStrLength = 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"));
@@ -11386,48 +3946,65 @@ cmdVolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
     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);
+    /* Use the length of available header string if it's longest */
+    stringLength = strlen(_("Available"));
+    if (stringLength > availStrLength)
+        availStrLength = stringLength;
 
-    /* Create the output template */
+    /* 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.
+     */
+    char *outputStr;
     ret = virAsprintf(&outputStr,
-                      "%%-%lus  %%-%lus  %%-%lus  %%%lus  %%%lus\n",
-                      (unsigned long) nameStrLength,
-                      (unsigned long) pathStrLength,
-                      (unsigned long) typeStrLength,
-                      (unsigned long) capStrLength,
-                      (unsigned long) allocStrLength);
+              "%%-%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);
     if (ret < 0) {
         /* An error occurred creating the string, return */
         goto asprintf_failure;
     }
 
     /* Display the header */
-    vshPrint(ctl, outputStr, _("Name"), _("Path"), _("Type"),
-             ("Capacity"), _("Allocation"));
-    for (i = nameStrLength + pathStrLength + typeStrLength
-                           + capStrLength + allocStrLength
-                           + 8; i > 0; i--)
+    vshPrint(ctl, outputStr, _("Name"), _("State"), _("Autostart"),
+             _("Persistent"), _("Capacity"), _("Allocation"), _("Available"));
+    for (i = nameStrLength + stateStrLength + autostartStrLength
+                           + persistStrLength + capStrLength
+                           + allocStrLength + availStrLength
+                           + 12; i > 0; i--)
         vshPrintExtra(ctl, "-");
     vshPrintExtra(ctl, "\n");
 
-    /* Display the volume info rows */
-    for (i = 0; i < numVolumes; i++) {
+    /* Display the pool info rows */
+    for (i = 0; i < numAllPools; i++) {
         vshPrint(ctl, outputStr,
-                 activeNames[i],
-                 volInfoTexts[i].path,
-                 volInfoTexts[i].type,
-                 volInfoTexts[i].capacity,
-                 volInfoTexts[i].allocation);
+                 poolNames[i],
+                 poolInfoTexts[i].state,
+                 poolInfoTexts[i].autostart,
+                 poolInfoTexts[i].persistent,
+                 poolInfoTexts[i].capacity,
+                 poolInfoTexts[i].allocation,
+                 poolInfoTexts[i].available);
     }
 
     /* Cleanup and return */
@@ -11451,3146 +4028,2607 @@ asprintf_failure:
 cleanup:
 
     /* Safely free the memory allocated in this function */
-    for (i = 0; i < numVolumes; i++) {
-        /* Cleanup the memory for one volume info structure per loop */
-        VIR_FREE(volInfoTexts[i].path);
-        VIR_FREE(volInfoTexts[i].type);
-        VIR_FREE(volInfoTexts[i].capacity);
-        VIR_FREE(volInfoTexts[i].allocation);
-        VIR_FREE(activeNames[i]);
+    for (i = 0; i < numAllPools; i++) {
+        /* Cleanup the memory for one pool info structure */
+        VIR_FREE(poolInfoTexts[i].state);
+        VIR_FREE(poolInfoTexts[i].autostart);
+        VIR_FREE(poolInfoTexts[i].persistent);
+        VIR_FREE(poolInfoTexts[i].capacity);
+        VIR_FREE(poolInfoTexts[i].allocation);
+        VIR_FREE(poolInfoTexts[i].available);
+        VIR_FREE(poolNames[i]);
     }
 
-    /* Cleanup remaining memory */
-    VIR_FREE(outputStr);
-    VIR_FREE(volInfoTexts);
-    VIR_FREE(activeNames);
-    virStoragePoolFree(pool);
+    /* Cleanup the memory for the initial arrays*/
+    VIR_FREE(poolInfoTexts);
+    VIR_FREE(poolNames);
 
     /* Return the desired value */
     return functionReturn;
 }
 
-
 /*
- * "vol-name" command
+ * "find-storage-pool-sources-as" command
  */
-static const vshCmdInfo info_vol_name[] = {
-    {"help", N_("returns the volume name for a given volume key or path")},
-    {"desc", ""},
+static const vshCmdInfo info_find_storage_pool_sources_as[] = {
+    {"help", N_("find potential storage pool sources")},
+    {"desc", N_("Returns XML <sources> document.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_vol_name[] = {
-    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("volume key or path")},
+static const vshCmdOptDef opts_find_storage_pool_sources_as[] = {
+    {"type", VSH_OT_DATA, VSH_OFLAG_REQ,
+     N_("type of storage pool sources to find")},
+    {"host", VSH_OT_DATA, VSH_OFLAG_NONE, N_("optional host to query")},
+    {"port", VSH_OT_DATA, VSH_OFLAG_NONE, N_("optional port to query")},
+    {"initiator", VSH_OT_DATA, VSH_OFLAG_NONE, N_("optional initiator IQN to use for query")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdVolName(vshControl *ctl, const vshCmd *cmd)
+cmdPoolDiscoverSourcesAs(vshControl * ctl, const vshCmd * cmd ATTRIBUTE_UNUSED)
 {
-    virStorageVolPtr vol;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
+    const char *type = NULL, *host = NULL;
+    char *srcSpec = NULL;
+    char *srcList;
+    const char *initiator = NULL;
 
-    if (!(vol = vshCommandOptVolBy(ctl, cmd, "vol", NULL, NULL,
-                                   VSH_BYUUID)))
+    if (vshCommandOptString(cmd, "type", &type) <= 0 ||
+        vshCommandOptString(cmd, "host", &host) < 0 ||
+        vshCommandOptString(cmd, "initiator", &initiator) < 0) {
+        vshError(ctl,"%s", _("missing argument"));
         return false;
+    }
 
-    vshPrint(ctl, "%s\n", virStorageVolGetName(vol));
-    virStorageVolFree(vol);
-    return true;
-}
-
-
-/*
- * "vol-pool" command
- */
-static const vshCmdInfo info_vol_pool[] = {
-    {"help", N_("returns the storage pool for a given volume key or path")},
-    {"desc", ""},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_vol_pool[] = {
-    {"uuid", VSH_OT_BOOL, 0, N_("return the pool uuid rather than pool name")},
-    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("volume key or path")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdVolPool(vshControl *ctl, const vshCmd *cmd)
-{
-    virStoragePoolPtr pool;
-    virStorageVolPtr vol;
-    char uuid[VIR_UUID_STRING_BUFLEN];
-
-    /* Check the connection to libvirtd daemon is still working */
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    /* Use the supplied string to locate the volume */
-    if (!(vol = vshCommandOptVolBy(ctl, cmd, "vol", NULL, NULL,
-                                   VSH_BYUUID))) {
-        return false;
+    if (host) {
+        const char *port = NULL;
+        virBuffer buf = VIR_BUFFER_INITIALIZER;
+
+        if (vshCommandOptString(cmd, "port", &port) < 0) {
+            vshError(ctl, "%s", _("missing argument"));
+            virBufferFreeAndReset(&buf);
+            return false;
+        }
+        virBufferAddLit(&buf, "<source>\n");
+        virBufferAsprintf(&buf, "  <host name='%s'", host);
+        if (port)
+            virBufferAsprintf(&buf, " port='%s'", port);
+        virBufferAddLit(&buf, "/>\n");
+        if (initiator) {
+            virBufferAddLit(&buf, "  <initiator>\n");
+            virBufferAsprintf(&buf, "    <iqn name='%s'/>\n", initiator);
+            virBufferAddLit(&buf, "  </initiator>\n");
+        }
+        virBufferAddLit(&buf, "</source>\n");
+        if (virBufferError(&buf)) {
+            vshError(ctl, "%s", _("Out of memory"));
+            return false;
+        }
+        srcSpec = virBufferContentAndReset(&buf);
     }
 
-    /* Look up the parent storage pool for the volume */
-    pool = virStoragePoolLookupByVolume(vol);
-    if (pool == NULL) {
-        vshError(ctl, "%s", _("failed to get parent pool"));
-        virStorageVolFree(vol);
+    srcList = virConnectFindStoragePoolSources(ctl->conn, type, srcSpec, 0);
+    VIR_FREE(srcSpec);
+    if (srcList == NULL) {
+        vshError(ctl, _("Failed to find any %s pool sources"), type);
         return false;
     }
+    vshPrint(ctl, "%s", srcList);
+    VIR_FREE(srcList);
 
-    /* Return the requested details of the parent storage pool */
-    if (vshCommandOptBool(cmd, "uuid")) {
-        /* Retrieve and return pool UUID string */
-        if (virStoragePoolGetUUIDString(pool, &uuid[0]) == 0)
-            vshPrint(ctl, "%s\n", uuid);
-    } else {
-        /* Return the storage pool name */
-        vshPrint(ctl, "%s\n", virStoragePoolGetName(pool));
-    }
-
-    /* Cleanup */
-    virStorageVolFree(vol);
-    virStoragePoolFree(pool);
     return true;
 }
 
 
 /*
- * "vol-key" command
+ * "find-storage-pool-sources" command
  */
-static const vshCmdInfo info_vol_key[] = {
-    {"help", N_("returns the volume key for a given volume name or path")},
-    {"desc", ""},
+static const vshCmdInfo info_find_storage_pool_sources[] = {
+    {"help", N_("discover potential storage pool sources")},
+    {"desc", N_("Returns XML <sources> document.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_vol_key[] = {
-    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("volume name or path")},
-    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
+static const vshCmdOptDef opts_find_storage_pool_sources[] = {
+    {"type", VSH_OT_DATA, VSH_OFLAG_REQ,
+     N_("type of storage pool sources to discover")},
+    {"srcSpec", VSH_OT_DATA, VSH_OFLAG_NONE,
+     N_("optional file of source xml to query for pools")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdVolKey(vshControl *ctl, const vshCmd *cmd)
+cmdPoolDiscoverSources(vshControl * ctl, const vshCmd * cmd ATTRIBUTE_UNUSED)
 {
-    virStorageVolPtr vol;
+    const char *type = NULL, *srcSpecFile = NULL;
+    char *srcSpec = NULL, *srcList;
+
+    if (vshCommandOptString(cmd, "type", &type) <= 0)
+        return false;
+
+    if (vshCommandOptString(cmd, "srcSpec", &srcSpecFile) < 0) {
+        vshError(ctl, "%s", _("missing option"));
+        return false;
+    }
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL)))
+    if (srcSpecFile && virFileReadAll(srcSpecFile, VIRSH_MAX_XML_FILE, &srcSpec) < 0)
         return false;
 
-    vshPrint(ctl, "%s\n", virStorageVolGetKey(vol));
-    virStorageVolFree(vol);
+    srcList = virConnectFindStoragePoolSources(ctl->conn, type, srcSpec, 0);
+    VIR_FREE(srcSpec);
+    if (srcList == NULL) {
+        vshError(ctl, _("Failed to find any %s pool sources"), type);
+        return false;
+    }
+    vshPrint(ctl, "%s", srcList);
+    VIR_FREE(srcList);
+
     return true;
 }
 
 
-
 /*
- * "vol-path" command
+ * "pool-info" command
  */
-static const vshCmdInfo info_vol_path[] = {
-    {"help", N_("returns the volume path for a given volume name or key")},
-    {"desc", ""},
+static const vshCmdInfo info_pool_info[] = {
+    {"help", N_("storage pool information")},
+    {"desc", N_("Returns basic information about the storage pool.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_vol_path[] = {
-    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("volume name or key")},
-    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
+static const vshCmdOptDef opts_pool_info[] = {
+    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdVolPath(vshControl *ctl, const vshCmd *cmd)
+cmdPoolInfo(vshControl *ctl, const vshCmd *cmd)
 {
-    virStorageVolPtr vol;
-    char * StorageVolPath;
+    virStoragePoolInfo info;
+    virStoragePoolPtr pool;
+    int autostart = 0;
+    int persistent = 0;
+    bool ret = true;
+    char uuid[VIR_UUID_STRING_BUFLEN];
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL))) {
+    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", NULL)))
         return false;
-    }
 
-    if ((StorageVolPath = virStorageVolGetPath(vol)) == NULL) {
-        virStorageVolFree(vol);
-        return false;
+    vshPrint(ctl, "%-15s %s\n", _("Name:"), virStoragePoolGetName(pool));
+
+    if (virStoragePoolGetUUIDString(pool, &uuid[0])==0)
+        vshPrint(ctl, "%-15s %s\n", _("UUID:"), uuid);
+
+    if (virStoragePoolGetInfo(pool, &info) == 0) {
+        double val;
+        const char *unit;
+        switch (info.state) {
+        case VIR_STORAGE_POOL_INACTIVE:
+            vshPrint(ctl, "%-15s %s\n", _("State:"),
+                     _("inactive"));
+            break;
+        case VIR_STORAGE_POOL_BUILDING:
+            vshPrint(ctl, "%-15s %s\n", _("State:"),
+                     _("building"));
+            break;
+        case VIR_STORAGE_POOL_RUNNING:
+            vshPrint(ctl, "%-15s %s\n", _("State:"),
+                     _("running"));
+            break;
+        case VIR_STORAGE_POOL_DEGRADED:
+            vshPrint(ctl, "%-15s %s\n", _("State:"),
+                     _("degraded"));
+            break;
+        case VIR_STORAGE_POOL_INACCESSIBLE:
+            vshPrint(ctl, "%-15s %s\n", _("State:"),
+                     _("inaccessible"));
+            break;
+        }
+
+        /* Check and display whether the pool is persistent or not */
+        persistent = virStoragePoolIsPersistent(pool);
+        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 pool is autostarted or not */
+        virStoragePoolGetAutostart(pool, &autostart);
+        vshDebug(ctl, VSH_ERR_DEBUG, "Pool autostart flag value: %d\n",
+                 autostart);
+        if (autostart < 0)
+            vshPrint(ctl, "%-15s %s\n", _("Autostart:"), _("no autostart"));
+        else
+            vshPrint(ctl, "%-15s %s\n", _("Autostart:"), autostart ? _("yes") : _("no"));
+
+        if (info.state == VIR_STORAGE_POOL_RUNNING ||
+            info.state == VIR_STORAGE_POOL_DEGRADED) {
+            val = prettyCapacity(info.capacity, &unit);
+            vshPrint(ctl, "%-15s %2.2lf %s\n", _("Capacity:"), val, unit);
+
+            val = prettyCapacity(info.allocation, &unit);
+            vshPrint(ctl, "%-15s %2.2lf %s\n", _("Allocation:"), val, unit);
+
+            val = prettyCapacity(info.available, &unit);
+            vshPrint(ctl, "%-15s %2.2lf %s\n", _("Available:"), val, unit);
+        }
+    } else {
+        ret = false;
     }
 
-    vshPrint(ctl, "%s\n", StorageVolPath);
-    VIR_FREE(StorageVolPath);
-    virStorageVolFree(vol);
-    return true;
+    virStoragePoolFree(pool);
+    return ret;
 }
 
 
 /*
- * "secret-define" command
+ * "pool-name" command
  */
-static const vshCmdInfo info_secret_define[] = {
-    {"help", N_("define or modify a secret from an XML file")},
-    {"desc", N_("Define or modify a secret.")},
+static const vshCmdInfo info_pool_name[] = {
+    {"help", N_("convert a pool UUID to pool name")},
+    {"desc", ""},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_secret_define[] = {
-    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing secret attributes in XML")},
+static const vshCmdOptDef opts_pool_name[] = {
+    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool uuid")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdSecretDefine(vshControl *ctl, const vshCmd *cmd)
+cmdPoolName(vshControl *ctl, const vshCmd *cmd)
 {
-    const char *from = NULL;
-    char *buffer;
-    virSecretPtr res;
-    char uuid[VIR_UUID_STRING_BUFLEN];
+    virStoragePoolPtr pool;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
-
-    if (vshCommandOptString(cmd, "file", &from) <= 0)
-        return false;
-
-    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
+    if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL,
+                                           VSH_BYUUID)))
         return false;
 
-    res = virSecretDefineXML(ctl->conn, buffer, 0);
-    VIR_FREE(buffer);
-
-    if (res == NULL) {
-        vshError(ctl, _("Failed to set attributes from %s"), from);
-        return false;
-    }
-    if (virSecretGetUUIDString(res, &(uuid[0])) < 0) {
-        vshError(ctl, "%s", _("Failed to get UUID of created secret"));
-        virSecretFree(res);
-        return false;
-    }
-    vshPrint(ctl, _("Secret %s created\n"), uuid);
-    virSecretFree(res);
+    vshPrint(ctl, "%s\n", virStoragePoolGetName(pool));
+    virStoragePoolFree(pool);
     return true;
 }
 
+
 /*
- * "secret-dumpxml" command
+ * "pool-start" command
  */
-static const vshCmdInfo info_secret_dumpxml[] = {
-    {"help", N_("secret attributes in XML")},
-    {"desc", N_("Output attributes of a secret as an XML dump to stdout.")},
+static const vshCmdInfo info_pool_start[] = {
+    {"help", N_("start a (previously defined) inactive pool")},
+    {"desc", N_("Start a pool.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_secret_dumpxml[] = {
-    {"secret", VSH_OT_DATA, VSH_OFLAG_REQ, N_("secret UUID")},
+static const vshCmdOptDef opts_pool_start[] = {
+    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("name or uuid of the inactive pool")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdSecretDumpXML(vshControl *ctl, const vshCmd *cmd)
+cmdPoolStart(vshControl *ctl, const vshCmd *cmd)
 {
-    virSecretPtr secret;
-    bool ret = false;
-    char *xml;
+    virStoragePoolPtr pool;
+    bool ret = true;
+    const char *name = NULL;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    secret = vshCommandOptSecret(ctl, cmd, NULL);
-    if (secret == NULL)
-        return false;
+    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
+         return false;
 
-    xml = virSecretGetXMLDesc(secret, 0);
-    if (xml == NULL)
-        goto cleanup;
-    vshPrint(ctl, "%s", xml);
-    VIR_FREE(xml);
-    ret = true;
+    if (virStoragePoolCreate(pool, 0) == 0) {
+        vshPrint(ctl, _("Pool %s started\n"), name);
+    } else {
+        vshError(ctl, _("Failed to start pool %s"), name);
+        ret = false;
+    }
 
-cleanup:
-    virSecretFree(secret);
+    virStoragePoolFree(pool);
     return ret;
 }
 
+
 /*
- * "secret-set-value" command
+ * "vol-create-as" command
  */
-static const vshCmdInfo info_secret_set_value[] = {
-    {"help", N_("set a secret value")},
-    {"desc", N_("Set a secret value.")},
+static const vshCmdInfo info_vol_create_as[] = {
+    {"help", N_("create a volume from a set of args")},
+    {"desc", N_("Create a vol.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_secret_set_value[] = {
-    {"secret", VSH_OT_DATA, VSH_OFLAG_REQ, N_("secret UUID")},
-    {"base64", VSH_OT_DATA, VSH_OFLAG_REQ, N_("base64-encoded secret value")},
+static const vshCmdOptDef opts_vol_create_as[] = {
+    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name")},
+    {"name", VSH_OT_DATA, VSH_OFLAG_REQ, N_("name of the volume")},
+    {"capacity", VSH_OT_DATA, VSH_OFLAG_REQ,
+     N_("size of the vol, as scaled integer (default bytes)")},
+    {"allocation", VSH_OT_STRING, 0,
+     N_("initial allocation size, as scaled integer (default bytes)")},
+    {"format", VSH_OT_STRING, 0,
+     N_("file format type raw,bochs,qcow,qcow2,qed,vmdk")},
+    {"backing-vol", VSH_OT_STRING, 0,
+     N_("the backing volume if taking a snapshot")},
+    {"backing-vol-format", VSH_OT_STRING, 0,
+     N_("format of backing volume if taking a snapshot")},
     {NULL, 0, 0, NULL}
 };
 
+static int
+vshVolSize(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
-cmdSecretSetValue(vshControl *ctl, const vshCmd *cmd)
+cmdVolCreateAs(vshControl *ctl, const vshCmd *cmd)
 {
-    virSecretPtr secret;
-    size_t value_size;
-    const char *base64 = NULL;
-    char *value;
-    int res;
-    bool ret = false;
+    virStoragePoolPtr pool;
+    virStorageVolPtr vol;
+    char *xml;
+    const char *name, *capacityStr = NULL, *allocationStr = NULL, *format = NULL;
+    const char *snapshotStrVol = NULL, *snapshotStrFormat = NULL;
+    unsigned long long capacity, allocation = 0;
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    secret = vshCommandOptSecret(ctl, cmd, NULL);
-    if (secret == NULL)
+    if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL,
+                                     VSH_BYNAME)))
         return false;
 
-    if (vshCommandOptString(cmd, "base64", &base64) <= 0)
+    if (vshCommandOptString(cmd, "name", &name) <= 0)
         goto cleanup;
 
-    if (!base64_decode_alloc(base64, strlen(base64), &value, &value_size)) {
-        vshError(ctl, "%s", _("Invalid base64 data"));
+    if (vshCommandOptString(cmd, "capacity", &capacityStr) <= 0)
+        goto cleanup;
+
+    if (vshVolSize(capacityStr, &capacity) < 0) {
+        vshError(ctl, _("Malformed size %s"), capacityStr);
         goto cleanup;
     }
-    if (value == NULL) {
-        vshError(ctl, "%s", _("Failed to allocate memory"));
-        return false;
+
+    if (vshCommandOptString(cmd, "allocation", &allocationStr) > 0 &&
+        vshVolSize(allocationStr, &allocation) < 0) {
+        vshError(ctl, _("Malformed size %s"), allocationStr);
+        goto cleanup;
     }
 
-    res = virSecretSetValue(secret, (unsigned char *)value, value_size, 0);
-    memset(value, 0, value_size);
-    VIR_FREE(value);
+    if (vshCommandOptString(cmd, "format", &format) < 0 ||
+        vshCommandOptString(cmd, "backing-vol", &snapshotStrVol) < 0 ||
+        vshCommandOptString(cmd, "backing-vol-format",
+                            &snapshotStrFormat) < 0) {
+        vshError(ctl, "%s", _("missing argument"));
+        goto cleanup;
+    }
 
-    if (res != 0) {
-        vshError(ctl, "%s", _("Failed to set secret value"));
+
+    virBufferAddLit(&buf, "<volume>\n");
+    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");
+        virBufferAsprintf(&buf, "    <format type='%s'/>\n",format);
+        virBufferAddLit(&buf, "  </target>\n");
+    }
+
+    /* Convert the snapshot parameters into backingStore XML */
+    if (snapshotStrVol) {
+        /* Lookup snapshot backing volume.  Try the backing-vol
+         *  parameter as a name */
+        vshDebug(ctl, VSH_ERR_DEBUG,
+                 "%s: Look up backing store volume '%s' as name\n",
+                 cmd->def->name, snapshotStrVol);
+        virStorageVolPtr snapVol = virStorageVolLookupByName(pool, snapshotStrVol);
+        if (snapVol)
+                vshDebug(ctl, VSH_ERR_DEBUG,
+                         "%s: Backing store volume found using '%s' as name\n",
+                         cmd->def->name, snapshotStrVol);
+
+        if (snapVol == NULL) {
+            /* Snapshot backing volume not found by name.  Try the
+             *  backing-vol parameter as a key */
+            vshDebug(ctl, VSH_ERR_DEBUG,
+                     "%s: Look up backing store volume '%s' as key\n",
+                     cmd->def->name, snapshotStrVol);
+            snapVol = virStorageVolLookupByKey(ctl->conn, snapshotStrVol);
+            if (snapVol)
+                vshDebug(ctl, VSH_ERR_DEBUG,
+                         "%s: Backing store volume found using '%s' as key\n",
+                         cmd->def->name, snapshotStrVol);
+        }
+        if (snapVol == NULL) {
+            /* Snapshot backing volume not found by key.  Try the
+             *  backing-vol parameter as a path */
+            vshDebug(ctl, VSH_ERR_DEBUG,
+                     "%s: Look up backing store volume '%s' as path\n",
+                     cmd->def->name, snapshotStrVol);
+            snapVol = virStorageVolLookupByPath(ctl->conn, snapshotStrVol);
+            if (snapVol)
+                vshDebug(ctl, VSH_ERR_DEBUG,
+                         "%s: Backing store volume found using '%s' as path\n",
+                         cmd->def->name, snapshotStrVol);
+        }
+        if (snapVol == NULL) {
+            vshError(ctl, _("failed to get vol '%s'"), snapshotStrVol);
+            goto cleanup;
+        }
+
+        char *snapshotStrVolPath;
+        if ((snapshotStrVolPath = virStorageVolGetPath(snapVol)) == NULL) {
+            virStorageVolFree(snapVol);
+            goto cleanup;
+        }
+
+        /* Create XML for the backing store */
+        virBufferAddLit(&buf, "  <backingStore>\n");
+        virBufferAsprintf(&buf, "    <path>%s</path>\n",snapshotStrVolPath);
+        if (snapshotStrFormat)
+            virBufferAsprintf(&buf, "    <format type='%s'/>\n",snapshotStrFormat);
+        virBufferAddLit(&buf, "  </backingStore>\n");
+
+        /* Cleanup snapshot allocations */
+        VIR_FREE(snapshotStrVolPath);
+        virStorageVolFree(snapVol);
+    }
+
+    virBufferAddLit(&buf, "</volume>\n");
+
+    if (virBufferError(&buf)) {
+        vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
         goto cleanup;
     }
-    vshPrint(ctl, "%s", _("Secret value set\n"));
-    ret = true;
+    xml = virBufferContentAndReset(&buf);
+    vol = virStorageVolCreateXML(pool, xml, 0);
+    VIR_FREE(xml);
+    virStoragePoolFree(pool);
 
-cleanup:
-    virSecretFree(secret);
-    return ret;
+    if (vol != NULL) {
+        vshPrint(ctl, _("Vol %s created\n"), name);
+        virStorageVolFree(vol);
+        return true;
+    } else {
+        vshError(ctl, _("Failed to create vol %s"), name);
+        return false;
+    }
+
+ cleanup:
+    virBufferFreeAndReset(&buf);
+    virStoragePoolFree(pool);
+    return false;
 }
 
+
 /*
- * "secret-get-value" command
+ * "pool-undefine" command
  */
-static const vshCmdInfo info_secret_get_value[] = {
-    {"help", N_("Output a secret value")},
-    {"desc", N_("Output a secret value to stdout.")},
+static const vshCmdInfo info_pool_undefine[] = {
+    {"help", N_("undefine an inactive pool")},
+    {"desc", N_("Undefine the configuration for an inactive pool.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_secret_get_value[] = {
-    {"secret", VSH_OT_DATA, VSH_OFLAG_REQ, N_("secret UUID")},
+static const vshCmdOptDef opts_pool_undefine[] = {
+    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdSecretGetValue(vshControl *ctl, const vshCmd *cmd)
+cmdPoolUndefine(vshControl *ctl, const vshCmd *cmd)
 {
-    virSecretPtr secret;
-    char *base64;
-    unsigned char *value;
-    size_t value_size;
-    bool ret = false;
+    virStoragePoolPtr pool;
+    bool ret = true;
+    const char *name;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    secret = vshCommandOptSecret(ctl, cmd, NULL);
-    if (secret == NULL)
+    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
         return false;
 
-    value = virSecretGetValue(secret, &value_size, 0);
-    if (value == NULL)
-        goto cleanup;
-
-    base64_encode_alloc((char *)value, value_size, &base64);
-    memset(value, 0, value_size);
-    VIR_FREE(value);
-
-    if (base64 == NULL) {
-        vshError(ctl, "%s", _("Failed to allocate memory"));
-        goto cleanup;
+    if (virStoragePoolUndefine(pool) == 0) {
+        vshPrint(ctl, _("Pool %s has been undefined\n"), name);
+    } else {
+        vshError(ctl, _("Failed to undefine pool %s"), name);
+        ret = false;
     }
-    vshPrint(ctl, "%s", base64);
-    memset(base64, 0, strlen(base64));
-    VIR_FREE(base64);
-    ret = true;
 
-cleanup:
-    virSecretFree(secret);
+    virStoragePoolFree(pool);
     return ret;
 }
 
+
 /*
- * "secret-undefine" command
+ * "pool-uuid" command
  */
-static const vshCmdInfo info_secret_undefine[] = {
-    {"help", N_("undefine a secret")},
-    {"desc", N_("Undefine a secret.")},
+static const vshCmdInfo info_pool_uuid[] = {
+    {"help", N_("convert a pool name to pool UUID")},
+    {"desc", ""},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_secret_undefine[] = {
-    {"secret", VSH_OT_DATA, VSH_OFLAG_REQ, N_("secret UUID")},
+static const vshCmdOptDef opts_pool_uuid[] = {
+    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdSecretUndefine(vshControl *ctl, const vshCmd *cmd)
+cmdPoolUuid(vshControl *ctl, const vshCmd *cmd)
 {
-    virSecretPtr secret;
-    bool ret = false;
-    const char *uuid;
+    virStoragePoolPtr pool;
+    char uuid[VIR_UUID_STRING_BUFLEN];
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    secret = vshCommandOptSecret(ctl, cmd, &uuid);
-    if (secret == NULL)
+    if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL,
+                                           VSH_BYNAME)))
         return false;
 
-    if (virSecretUndefine(secret) < 0) {
-        vshError(ctl, _("Failed to delete secret %s"), uuid);
-        goto cleanup;
-    }
-    vshPrint(ctl, _("Secret %s deleted\n"), uuid);
-    ret = true;
+    if (virStoragePoolGetUUIDString(pool, uuid) != -1)
+        vshPrint(ctl, "%s\n", uuid);
+    else
+        vshError(ctl, "%s", _("failed to get pool UUID"));
 
-cleanup:
-    virSecretFree(secret);
-    return ret;
+    virStoragePoolFree(pool);
+    return true;
 }
 
+
 /*
- * "secret-list" command
+ * "vol-create" command
  */
-static const vshCmdInfo info_secret_list[] = {
-    {"help", N_("list secrets")},
-    {"desc", N_("Returns a list of secrets")},
+static const vshCmdInfo info_vol_create[] = {
+    {"help", N_("create a vol from an XML file")},
+    {"desc", N_("Create a vol.")},
     {NULL, NULL}
 };
 
+static const vshCmdOptDef opts_vol_create[] = {
+    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name")},
+    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML vol description")},
+    {NULL, 0, 0, NULL}
+};
+
 static bool
-cmdSecretList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
+cmdVolCreate(vshControl *ctl, const vshCmd *cmd)
 {
-    int maxuuids = 0, i;
-    char **uuids = NULL;
+    virStoragePoolPtr pool;
+    virStorageVolPtr vol;
+    const char *from = NULL;
+    bool ret = true;
+    char *buffer;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    maxuuids = virConnectNumOfSecrets(ctl->conn);
-    if (maxuuids < 0) {
-        vshError(ctl, "%s", _("Failed to list secrets"));
+    if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL,
+                                           VSH_BYNAME)))
         return false;
-    }
-    uuids = vshMalloc(ctl, sizeof(*uuids) * maxuuids);
 
-    maxuuids = virConnectListSecrets(ctl->conn, uuids, maxuuids);
-    if (maxuuids < 0) {
-        vshError(ctl, "%s", _("Failed to list secrets"));
-        VIR_FREE(uuids);
+    if (vshCommandOptString(cmd, "file", &from) <= 0) {
+        virStoragePoolFree(pool);
         return false;
     }
 
-    qsort(uuids, maxuuids, sizeof(char *), vshNameSorter);
-
-    vshPrintExtra(ctl, "%-36s %s\n", _("UUID"), _("Usage"));
-    vshPrintExtra(ctl, "-----------------------------------------------------------\n");
-
-    for (i = 0; i < maxuuids; i++) {
-        virSecretPtr sec = virSecretLookupByUUIDString(ctl->conn, uuids[i]);
-        const char *usageType = NULL;
-
-        if (!sec) {
-            VIR_FREE(uuids[i]);
-            continue;
-        }
+    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
+        virshReportError(ctl);
+        virStoragePoolFree(pool);
+        return false;
+    }
 
-        switch (virSecretGetUsageType(sec)) {
-        case VIR_SECRET_USAGE_TYPE_VOLUME:
-            usageType = _("Volume");
-            break;
-        }
+    vol = virStorageVolCreateXML(pool, buffer, 0);
+    VIR_FREE(buffer);
+    virStoragePoolFree(pool);
 
-        if (usageType) {
-            vshPrint(ctl, "%-36s %s %s\n",
-                     uuids[i], usageType,
-                     virSecretGetUsageID(sec));
-        } else {
-            vshPrint(ctl, "%-36s %s\n",
-                     uuids[i], _("Unused"));
-        }
-        virSecretFree(sec);
-        VIR_FREE(uuids[i]);
+    if (vol != NULL) {
+        vshPrint(ctl, _("Vol %s created from %s\n"),
+                 virStorageVolGetName(vol), from);
+        virStorageVolFree(vol);
+    } else {
+        vshError(ctl, _("Failed to create vol from %s"), from);
+        ret = false;
     }
-    VIR_FREE(uuids);
-    return true;
+    return ret;
 }
 
-
 /*
- * "version" command
+ * "vol-create-from" command
  */
-static const vshCmdInfo info_version[] = {
-    {"help", N_("show version")},
-    {"desc", N_("Display the system version information.")},
+static const vshCmdInfo info_vol_create_from[] = {
+    {"help", N_("create a vol, using another volume as input")},
+    {"desc", N_("Create a vol from an existing volume.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_version[] = {
-    {"daemon", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("report daemon version too")},
+static const vshCmdOptDef opts_vol_create_from[] = {
+    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
+    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML vol description")},
+    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("input vol name or key")},
+    {"inputpool", VSH_OT_STRING, 0, N_("pool name or uuid of the input volume's pool")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdVersion(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
+cmdVolCreateFrom(vshControl *ctl, const vshCmd *cmd)
 {
-    unsigned long hvVersion;
-    const char *hvType;
-    unsigned long libVersion;
-    unsigned long includeVersion;
-    unsigned long apiVersion;
-    unsigned long daemonVersion;
-    int ret;
-    unsigned int major;
-    unsigned int minor;
-    unsigned int rel;
+    virStoragePoolPtr pool = NULL;
+    virStorageVolPtr newvol = NULL, inputvol = NULL;
+    const char *from = NULL;
+    bool ret = false;
+    char *buffer = NULL;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    hvType = virConnectGetType(ctl->conn);
-    if (hvType == NULL) {
-        vshError(ctl, "%s", _("failed to get hypervisor type"));
-        return false;
-    }
+        goto cleanup;
 
-    includeVersion = LIBVIR_VERSION_NUMBER;
-    major = includeVersion / 1000000;
-    includeVersion %= 1000000;
-    minor = includeVersion / 1000;
-    rel = includeVersion % 1000;
-    vshPrint(ctl, _("Compiled against library: libvir %d.%d.%d\n"),
-             major, minor, rel);
+    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", NULL)))
+        goto cleanup;
 
-    ret = virGetVersion(&libVersion, hvType, &apiVersion);
-    if (ret < 0) {
-        vshError(ctl, "%s", _("failed to get the library version"));
-        return false;
+    if (vshCommandOptString(cmd, "file", &from) <= 0) {
+        goto cleanup;
     }
-    major = libVersion / 1000000;
-    libVersion %= 1000000;
-    minor = libVersion / 1000;
-    rel = libVersion % 1000;
-    vshPrint(ctl, _("Using library: libvir %d.%d.%d\n"),
-             major, minor, rel);
 
-    major = apiVersion / 1000000;
-    apiVersion %= 1000000;
-    minor = apiVersion / 1000;
-    rel = apiVersion % 1000;
-    vshPrint(ctl, _("Using API: %s %d.%d.%d\n"), hvType,
-             major, minor, rel);
+    if (!(inputvol = vshCommandOptVol(ctl, cmd, "vol", "inputpool", NULL)))
+        goto cleanup;
 
-    ret = virConnectGetVersion(ctl->conn, &hvVersion);
-    if (ret < 0) {
-        vshError(ctl, "%s", _("failed to get the hypervisor version"));
-        return false;
+    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
+        virshReportError(ctl);
+        goto cleanup;
     }
-    if (hvVersion == 0) {
-        vshPrint(ctl,
-                 _("Cannot extract running %s hypervisor version\n"), hvType);
-    } else {
-        major = hvVersion / 1000000;
-        hvVersion %= 1000000;
-        minor = hvVersion / 1000;
-        rel = hvVersion % 1000;
 
-        vshPrint(ctl, _("Running hypervisor: %s %d.%d.%d\n"),
-                 hvType, major, minor, rel);
-    }
+    newvol = virStorageVolCreateXMLFrom(pool, buffer, inputvol, 0);
 
-    if (vshCommandOptBool(cmd, "daemon")) {
-        ret = virConnectGetLibVersion(ctl->conn, &daemonVersion);
-        if (ret < 0) {
-            vshError(ctl, "%s", _("failed to get the daemon version"));
-        } else {
-            major = daemonVersion / 1000000;
-            daemonVersion %= 1000000;
-            minor = daemonVersion / 1000;
-            rel = daemonVersion % 1000;
-            vshPrint(ctl, _("Running against daemon: %d.%d.%d\n"),
-                     major, minor, rel);
-        }
+    if (newvol != NULL) {
+        vshPrint(ctl, _("Vol %s created from input vol %s\n"),
+                 virStorageVolGetName(newvol), virStorageVolGetName(inputvol));
+    } else {
+        vshError(ctl, _("Failed to create vol from %s"), from);
+        goto cleanup;
     }
 
-    return true;
+    ret = true;
+cleanup:
+    VIR_FREE(buffer);
+    if (pool)
+        virStoragePoolFree(pool);
+    if (inputvol)
+        virStorageVolFree(inputvol);
+    if (newvol)
+        virStorageVolFree(newvol);
+    return ret;
 }
 
-/* Tree listing helpers.  */
+static xmlChar *
+makeCloneXML(const char *origxml, const char *newname)
+{
 
-/* Given an index, return either the name of that device (non-NULL) or
- * of its parent (NULL if a root).  */
-typedef const char * (*vshTreeLookup)(int devid, bool parent, void *opaque);
+    xmlDocPtr doc = NULL;
+    xmlXPathContextPtr ctxt = NULL;
+    xmlXPathObjectPtr obj = NULL;
+    xmlChar *newxml = NULL;
+    int size;
 
-static int
-vshTreePrintInternal(vshControl *ctl,
-                     vshTreeLookup lookup,
-                     void *opaque,
-                     int num_devices,
-                     int devid,
-                     int lastdev,
-                     bool root,
-                     virBufferPtr indent)
+    doc = virXMLParseStringCtxt(origxml, _("(volume_definition)"), &ctxt);
+    if (!doc)
+        goto cleanup;
+
+    obj = xmlXPathEval(BAD_CAST "/volume/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;
+}
+
+/*
+ * "vol-clone" command
+ */
+static const vshCmdInfo info_vol_clone[] = {
+    {"help", N_("clone a volume.")},
+    {"desc", N_("Clone an existing volume.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_vol_clone[] = {
+    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("orig vol name or key")},
+    {"newname", VSH_OT_DATA, VSH_OFLAG_REQ, N_("clone name")},
+    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdVolClone(vshControl *ctl, const vshCmd *cmd)
 {
-    int i;
-    int nextlastdev = -1;
-    int ret = -1;
-    const char *dev = (lookup)(devid, false, opaque);
+    virStoragePoolPtr origpool = NULL;
+    virStorageVolPtr origvol = NULL, newvol = NULL;
+    const char *name = NULL;
+    char *origxml = NULL;
+    xmlChar *newxml = NULL;
+    bool ret = false;
 
-    if (virBufferError(indent))
+    if (!vshConnectionUsability(ctl, ctl->conn))
         goto cleanup;
 
-    /* Print this device, with indent if not at root */
-    vshPrint(ctl, "%s%s%s\n", virBufferCurrentContent(indent),
-             root ? "" : "+- ", dev);
+    if (!(origvol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL)))
+        goto cleanup;
 
-    /* Update indent to show '|' or ' ' for child devices */
-    if (!root) {
-        virBufferAddChar(indent, devid == lastdev ? ' ' : '|');
-        virBufferAddChar(indent, ' ');
-        if (virBufferError(indent))
-            goto cleanup;
+    origpool = virStoragePoolLookupByVolume(origvol);
+    if (!origpool) {
+        vshError(ctl, "%s", _("failed to get parent pool"));
+        goto cleanup;
     }
 
-    /* Determine the index of the last child device */
-    for (i = 0 ; i < num_devices ; i++) {
-        const char *parent = (lookup)(i, true, opaque);
+    if (vshCommandOptString(cmd, "newname", &name) <= 0)
+        goto cleanup;
 
-        if (parent && STREQ(parent, dev))
-            nextlastdev = i;
-    }
+    origxml = virStorageVolGetXMLDesc(origvol, 0);
+    if (!origxml)
+        goto cleanup;
 
-    /* If there is a child device, then print another blank line */
-    if (nextlastdev != -1)
-        vshPrint(ctl, "%s  |\n", virBufferCurrentContent(indent));
+    newxml = makeCloneXML(origxml, name);
+    if (!newxml) {
+        vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
+        goto cleanup;
+    }
 
-    /* Finally print all children */
-    virBufferAddLit(indent, "  ");
-    for (i = 0 ; i < num_devices ; i++) {
-        const char *parent = (lookup)(i, true, opaque);
+    newvol = virStorageVolCreateXMLFrom(origpool, (char *) newxml, origvol, 0);
 
-        if (parent && STREQ(parent, dev) &&
-            vshTreePrintInternal(ctl, lookup, opaque,
-                                 num_devices, i, nextlastdev,
-                                 false, indent) < 0)
-            goto cleanup;
+    if (newvol != NULL) {
+        vshPrint(ctl, _("Vol %s cloned from %s\n"),
+                 virStorageVolGetName(newvol), virStorageVolGetName(origvol));
+    } else {
+        vshError(ctl, _("Failed to clone vol from %s"),
+                 virStorageVolGetName(origvol));
+        goto cleanup;
     }
-    virBufferTrim(indent, "  ", -1);
 
-    /* If there was no child device, and we're the last in
-     * a list of devices, then print another blank line */
-    if (nextlastdev == -1 && devid == lastdev)
-        vshPrint(ctl, "%s\n", virBufferCurrentContent(indent));
+    ret = true;
 
-    if (!root)
-        virBufferTrim(indent, NULL, 2);
-    ret = 0;
 cleanup:
+    VIR_FREE(origxml);
+    xmlFree(newxml);
+    if (origvol)
+        virStorageVolFree(origvol);
+    if (newvol)
+        virStorageVolFree(newvol);
+    if (origpool)
+        virStoragePoolFree(origpool);
     return ret;
 }
 
-static int
-vshTreePrint(vshControl *ctl, vshTreeLookup lookup, void *opaque,
-             int num_devices, int devid)
-{
-    int ret;
-    virBuffer indent = VIR_BUFFER_INITIALIZER;
-
-    ret = vshTreePrintInternal(ctl, lookup, opaque, num_devices,
-                               devid, devid, true, &indent);
-    if (ret < 0)
-        vshError(ctl, "%s", _("Failed to complete tree listing"));
-    virBufferFreeAndReset(&indent);
-    return ret;
-}
-
-struct vshNodeList {
-    char **names;
-    char **parents;
-};
-
-static const char *
-vshNodeListLookup(int devid, bool parent, void *opaque)
-{
-    struct vshNodeList *arrays = opaque;
-    if (parent)
-        return arrays->parents[devid];
-    return arrays->names[devid];
-}
 
 /*
- * "nodedev-list" command
+ * "vol-upload" command
  */
-static const vshCmdInfo info_node_list_devices[] = {
-    {"help", N_("enumerate devices on this host")},
-    {"desc", ""},
+static const vshCmdInfo info_vol_upload[] = {
+    {"help", N_("upload a file into a volume")},
+    {"desc", N_("Upload a file into a volume")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_node_list_devices[] = {
-    {"tree", VSH_OT_BOOL, 0, N_("list devices in a tree")},
-    {"cap", VSH_OT_STRING, VSH_OFLAG_NONE, N_("capability name")},
+static const vshCmdOptDef opts_vol_upload[] = {
+    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
+    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file")},
+    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
+    {"offset", VSH_OT_INT, 0, N_("volume offset to upload to") },
+    {"length", VSH_OT_INT, 0, N_("amount of data to upload") },
     {NULL, 0, 0, NULL}
 };
 
+static int
+cmdVolUploadSource(virStreamPtr st ATTRIBUTE_UNUSED,
+                   char *bytes, size_t nbytes, void *opaque)
+{
+    int *fd = opaque;
+
+    return saferead(*fd, bytes, nbytes);
+}
+
 static bool
-cmdNodeListDevices(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
+cmdVolUpload(vshControl *ctl, const vshCmd *cmd)
 {
-    const char *cap = NULL;
-    char **devices;
-    int num_devices, i;
-    bool tree = vshCommandOptBool(cmd, "tree");
-    bool ret = true;
+    const char *file = NULL;
+    virStorageVolPtr vol = NULL;
+    bool ret = false;
+    int fd = -1;
+    virStreamPtr st = NULL;
+    const char *name = NULL;
+    unsigned long long offset = 0, length = 0;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
+        goto cleanup;
 
-    if (vshCommandOptString(cmd, "cap", &cap) <= 0)
-        cap = NULL;
+    if (vshCommandOptULongLong(cmd, "offset", &offset) < 0) {
+        vshError(ctl, _("Unable to parse integer"));
+        return false;
+    }
 
-    num_devices = virNodeNumOfDevices(ctl->conn, cap, 0);
-    if (num_devices < 0) {
-        vshError(ctl, "%s", _("Failed to count node devices"));
+    if (vshCommandOptULongLong(cmd, "length", &length) < 0) {
+        vshError(ctl, _("Unable to parse integer"));
         return false;
-    } else if (num_devices == 0) {
-        return true;
     }
 
-    devices = vshMalloc(ctl, sizeof(char *) * num_devices);
-    num_devices =
-        virNodeListDevices(ctl->conn, cap, devices, num_devices, 0);
-    if (num_devices < 0) {
-        vshError(ctl, "%s", _("Failed to list node devices"));
-        VIR_FREE(devices);
+    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", &name))) {
         return false;
     }
-    qsort(&devices[0], num_devices, sizeof(char*), vshNameSorter);
-    if (tree) {
-        char **parents = vshMalloc(ctl, sizeof(char *) * num_devices);
-        struct vshNodeList arrays = { devices, parents };
 
-        for (i = 0; i < num_devices; i++) {
-            virNodeDevicePtr dev = virNodeDeviceLookupByName(ctl->conn, devices[i]);
-            if (dev && STRNEQ(devices[i], "computer")) {
-                const char *parent = virNodeDeviceGetParent(dev);
-                parents[i] = parent ? vshStrdup(ctl, parent) : NULL;
-            } else {
-                parents[i] = NULL;
-            }
-            virNodeDeviceFree(dev);
-        }
-        for (i = 0 ; i < num_devices ; i++) {
-            if (parents[i] == NULL &&
-                vshTreePrint(ctl, vshNodeListLookup, &arrays, num_devices,
-                             i) < 0)
-                ret = false;
-        }
-        for (i = 0 ; i < num_devices ; i++) {
-            VIR_FREE(devices[i]);
-            VIR_FREE(parents[i]);
-        }
-        VIR_FREE(parents);
-    } else {
-        for (i = 0; i < num_devices; i++) {
-            vshPrint(ctl, "%s\n", devices[i]);
-            VIR_FREE(devices[i]);
-        }
+    if (vshCommandOptString(cmd, "file", &file) < 0) {
+        vshError(ctl, _("file must not be empty"));
+        goto cleanup;
     }
-    VIR_FREE(devices);
+
+    if ((fd = open(file, O_RDONLY)) < 0) {
+        vshError(ctl, _("cannot read %s"), file);
+        goto cleanup;
+    }
+
+    st = virStreamNew(ctl->conn, 0);
+    if (virStorageVolUpload(vol, st, offset, length, 0) < 0) {
+        vshError(ctl, _("cannot upload to volume %s"), name);
+        goto cleanup;
+    }
+
+    if (virStreamSendAll(st, cmdVolUploadSource, &fd) < 0) {
+        vshError(ctl, _("cannot send data to volume %s"), name);
+        goto cleanup;
+    }
+
+    if (VIR_CLOSE(fd) < 0) {
+        vshError(ctl, _("cannot close file %s"), file);
+        virStreamAbort(st);
+        goto cleanup;
+    }
+
+    if (virStreamFinish(st) < 0) {
+        vshError(ctl, _("cannot close volume %s"), name);
+        goto cleanup;
+    }
+
+    ret = true;
+
+cleanup:
+    if (vol)
+        virStorageVolFree(vol);
+    if (st)
+        virStreamFree(st);
+    VIR_FORCE_CLOSE(fd);
     return ret;
 }
 
+
+
 /*
- * "nodedev-dumpxml" command
- */
-static const vshCmdInfo info_node_device_dumpxml[] = {
-    {"help", N_("node device details in XML")},
-    {"desc", N_("Output the node device details as an XML dump to stdout.")},
+ * "vol-download" command
+ */
+static const vshCmdInfo info_vol_download[] = {
+    {"help", N_("Download a volume to a file")},
+    {"desc", N_("Download a volume to a file")},
     {NULL, NULL}
 };
 
-
-static const vshCmdOptDef opts_node_device_dumpxml[] = {
-    {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("device key")},
+static const vshCmdOptDef opts_vol_download[] = {
+    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
+    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file")},
+    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
+    {"offset", VSH_OT_INT, 0, N_("volume offset to download from") },
+    {"length", VSH_OT_INT, 0, N_("amount of data to download") },
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdNodeDeviceDumpXML(vshControl *ctl, const vshCmd *cmd)
+cmdVolDownload(vshControl *ctl, const vshCmd *cmd)
 {
+    const char *file = NULL;
+    virStorageVolPtr vol = NULL;
+    bool ret = false;
+    int fd = -1;
+    virStreamPtr st = NULL;
     const char *name = NULL;
-    virNodeDevicePtr device;
-    char *xml;
+    unsigned long long offset = 0, length = 0;
+    bool created = false;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
-    if (vshCommandOptString(cmd, "device", &name) <= 0)
+
+    if (vshCommandOptULongLong(cmd, "offset", &offset) < 0) {
+        vshError(ctl, _("Unable to parse integer"));
         return false;
-    if (!(device = virNodeDeviceLookupByName(ctl->conn, name))) {
-        vshError(ctl, "%s '%s'", _("Could not find matching device"), name);
+    }
+
+    if (vshCommandOptULongLong(cmd, "length", &length) < 0) {
+        vshError(ctl, _("Unable to parse integer"));
         return false;
     }
 
-    xml = virNodeDeviceGetXMLDesc(device, 0);
-    if (!xml) {
-        virNodeDeviceFree(device);
+    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", &name)))
         return false;
+
+    if (vshCommandOptString(cmd, "file", &file) < 0) {
+        vshError(ctl, _("file must not be empty"));
+        goto cleanup;
     }
 
-    vshPrint(ctl, "%s\n", xml);
-    VIR_FREE(xml);
-    virNodeDeviceFree(device);
-    return true;
+    if ((fd = open(file, O_WRONLY|O_CREAT|O_EXCL, 0666)) < 0) {
+        if (errno != EEXIST ||
+            (fd = open(file, O_WRONLY|O_TRUNC, 0666)) < 0) {
+            vshError(ctl, _("cannot create %s"), file);
+            goto cleanup;
+        }
+    } else {
+        created = true;
+    }
+
+    st = virStreamNew(ctl->conn, 0);
+    if (virStorageVolDownload(vol, st, offset, length, 0) < 0) {
+        vshError(ctl, _("cannot download from volume %s"), name);
+        goto cleanup;
+    }
+
+    if (virStreamRecvAll(st, vshStreamSink, &fd) < 0) {
+        vshError(ctl, _("cannot receive data from volume %s"), name);
+        goto cleanup;
+    }
+
+    if (VIR_CLOSE(fd) < 0) {
+        vshError(ctl, _("cannot close file %s"), file);
+        virStreamAbort(st);
+        goto cleanup;
+    }
+
+    if (virStreamFinish(st) < 0) {
+        vshError(ctl, _("cannot close volume %s"), name);
+        goto cleanup;
+    }
+
+    ret = true;
+
+cleanup:
+    VIR_FORCE_CLOSE(fd);
+    if (!ret && created)
+        unlink(file);
+    if (vol)
+        virStorageVolFree(vol);
+    if (st)
+        virStreamFree(st);
+    return ret;
 }
 
+
 /*
- * "nodedev-detach" command
+ * "vol-delete" command
  */
-static const vshCmdInfo info_node_device_detach[] = {
-    {"help", N_("detach node device from its device driver")},
-    {"desc", N_("Detach node device from its device driver before assigning to a domain.")},
+static const vshCmdInfo info_vol_delete[] = {
+    {"help", N_("delete a vol")},
+    {"desc", N_("Delete a given vol.")},
     {NULL, NULL}
 };
 
-
-static const vshCmdOptDef opts_node_device_detach[] = {
-    {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("device key")},
+static const vshCmdOptDef opts_vol_delete[] = {
+    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
+    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdNodeDeviceDetach(vshControl *ctl, const vshCmd *cmd)
+cmdVolDelete(vshControl *ctl, const vshCmd *cmd)
 {
-    const char *name = NULL;
-    virNodeDevicePtr device;
+    virStorageVolPtr vol;
     bool ret = true;
+    const char *name;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
-    if (vshCommandOptString(cmd, "device", &name) <= 0)
-        return false;
-    if (!(device = virNodeDeviceLookupByName(ctl->conn, name))) {
-        vshError(ctl, "%s '%s'", _("Could not find matching device"), name);
+
+    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", &name))) {
         return false;
     }
 
-    /* Yes, our public API is misspelled.  At least virsh can accept
-     * either spelling.  */
-    if (virNodeDeviceDettach(device) == 0) {
-        vshPrint(ctl, _("Device %s detached\n"), name);
+    if (virStorageVolDelete(vol, 0) == 0) {
+        vshPrint(ctl, _("Vol %s deleted\n"), name);
     } else {
-        vshError(ctl, _("Failed to detach device %s"), name);
+        vshError(ctl, _("Failed to delete vol %s"), name);
         ret = false;
     }
-    virNodeDeviceFree(device);
+
+    virStorageVolFree(vol);
     return ret;
 }
 
+
 /*
- * "nodedev-reattach" command
+ * "vol-wipe" command
  */
-static const vshCmdInfo info_node_device_reattach[] = {
-    {"help", N_("reattach node device to its device driver")},
-    {"desc", N_("Reattach node device to its device driver once released by the domain.")},
+static const vshCmdInfo info_vol_wipe[] = {
+    {"help", N_("wipe a vol")},
+    {"desc", N_("Ensure data previously on a volume is not accessible to future reads")},
     {NULL, NULL}
 };
 
-
-static const vshCmdOptDef opts_node_device_reattach[] = {
-    {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("device key")},
+static const vshCmdOptDef opts_vol_wipe[] = {
+    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
+    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
+    {"algorithm", VSH_OT_STRING, 0, N_("perform selected wiping algorithm")},
     {NULL, 0, 0, NULL}
 };
 
+VIR_ENUM_DECL(virStorageVolWipeAlgorithm)
+VIR_ENUM_IMPL(virStorageVolWipeAlgorithm, VIR_STORAGE_VOL_WIPE_ALG_LAST,
+              "zero", "nnsa", "dod", "bsi", "gutmann", "schneier",
+              "pfitzner7", "pfitzner33", "random");
+
 static bool
-cmdNodeDeviceReAttach(vshControl *ctl, const vshCmd *cmd)
+cmdVolWipe(vshControl *ctl, const vshCmd *cmd)
 {
-    const char *name = NULL;
-    virNodeDevicePtr device;
-    bool ret = true;
+    virStorageVolPtr vol;
+    bool ret = false;
+    const char *name;
+    const char *algorithm_str = NULL;
+    int algorithm = VIR_STORAGE_VOL_WIPE_ALG_ZERO;
+    int funcRet;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
-    if (vshCommandOptString(cmd, "device", &name) <= 0)
-        return false;
-    if (!(device = virNodeDeviceLookupByName(ctl->conn, name))) {
-        vshError(ctl, "%s '%s'", _("Could not find matching device"), name);
+
+    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", &name))) {
         return false;
     }
 
-    if (virNodeDeviceReAttach(device) == 0) {
-        vshPrint(ctl, _("Device %s re-attached\n"), name);
-    } else {
-        vshError(ctl, _("Failed to re-attach device %s"), name);
-        ret = false;
+    if (vshCommandOptString(cmd, "algorithm", &algorithm_str) < 0) {
+        vshError(ctl, "%s", _("missing argument"));
+        goto out;
     }
-    virNodeDeviceFree(device);
+
+    if (algorithm_str &&
+        (algorithm = virStorageVolWipeAlgorithmTypeFromString(algorithm_str)) < 0) {
+        vshError(ctl, _("Unsupported algorithm '%s'"), algorithm_str);
+        goto out;
+    }
+
+    if ((funcRet = virStorageVolWipePattern(vol, algorithm, 0)) < 0) {
+        if (last_error->code == VIR_ERR_NO_SUPPORT &&
+            algorithm == VIR_STORAGE_VOL_WIPE_ALG_ZERO)
+            funcRet = virStorageVolWipe(vol, 0);
+    }
+
+    if (funcRet < 0) {
+        vshError(ctl, _("Failed to wipe vol %s"), name);
+        goto out;
+    }
+
+    vshPrint(ctl, _("Vol %s wiped\n"), name);
+    ret = true;
+out:
+    virStorageVolFree(vol);
     return ret;
 }
 
+
 /*
- * "nodedev-reset" command
+ * "vol-info" command
  */
-static const vshCmdInfo info_node_device_reset[] = {
-    {"help", N_("reset node device")},
-    {"desc", N_("Reset node device before or after assigning to a domain.")},
+static const vshCmdInfo info_vol_info[] = {
+    {"help", N_("storage vol information")},
+    {"desc", N_("Returns basic information about the storage vol.")},
     {NULL, NULL}
 };
 
-
-static const vshCmdOptDef opts_node_device_reset[] = {
-    {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("device key")},
+static const vshCmdOptDef opts_vol_info[] = {
+    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
+    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdNodeDeviceReset(vshControl *ctl, const vshCmd *cmd)
+cmdVolInfo(vshControl *ctl, const vshCmd *cmd)
 {
-    const char *name = NULL;
-    virNodeDevicePtr device;
+    virStorageVolInfo info;
+    virStorageVolPtr vol;
     bool ret = true;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
-    if (vshCommandOptString(cmd, "device", &name) <= 0)
-        return false;
-    if (!(device = virNodeDeviceLookupByName(ctl->conn, name))) {
-        vshError(ctl, "%s '%s'", _("Could not find matching device"), name);
+
+    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL)))
         return false;
-    }
 
-    if (virNodeDeviceReset(device) == 0) {
-        vshPrint(ctl, _("Device %s reset\n"), name);
+    vshPrint(ctl, "%-15s %s\n", _("Name:"), virStorageVolGetName(vol));
+
+    if (virStorageVolGetInfo(vol, &info) == 0) {
+        double val;
+        const char *unit;
+        switch(info.type) {
+        case VIR_STORAGE_VOL_FILE:
+            vshPrint(ctl, "%-15s %s\n", _("Type:"), _("file"));
+            break;
+
+        case VIR_STORAGE_VOL_BLOCK:
+            vshPrint(ctl, "%-15s %s\n", _("Type:"), _("block"));
+            break;
+
+        case VIR_STORAGE_VOL_DIR:
+            vshPrint(ctl, "%-15s %s\n", _("Type:"), _("dir"));
+            break;
+
+        case VIR_STORAGE_VOL_NETWORK:
+            vshPrint(ctl, "%-15s %s\n", _("Type:"), _("network"));
+            break;
+
+        default:
+            vshPrint(ctl, "%-15s %s\n", _("Type:"), _("unknown"));
+        }
+
+        val = prettyCapacity(info.capacity, &unit);
+        vshPrint(ctl, "%-15s %2.2lf %s\n", _("Capacity:"), val, unit);
+
+        val = prettyCapacity(info.allocation, &unit);
+        vshPrint(ctl, "%-15s %2.2lf %s\n", _("Allocation:"), val, unit);
     } else {
-        vshError(ctl, _("Failed to reset device %s"), name);
         ret = false;
     }
-    virNodeDeviceFree(device);
+
+    virStorageVolFree(vol);
     return ret;
 }
 
 /*
- * "hostname" command
+ * "vol-resize" command
  */
-static const vshCmdInfo info_hostname[] = {
-    {"help", N_("print the hypervisor hostname")},
-    {"desc", ""},
+static const vshCmdInfo info_vol_resize[] = {
+    {"help", N_("resize a vol")},
+    {"desc", N_("Resizes a storage volume.")},
     {NULL, NULL}
 };
 
-static bool
-cmdHostname(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
-{
-    char *hostname;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
-
-    hostname = virConnectGetHostname(ctl->conn);
-    if (hostname == NULL) {
-        vshError(ctl, "%s", _("failed to get hostname"));
-        return false;
-    }
-
-    vshPrint (ctl, "%s\n", hostname);
-    VIR_FREE(hostname);
-
-    return true;
-}
-
-/*
- * "uri" command
- */
-static const vshCmdInfo info_uri[] = {
-    {"help", N_("print the hypervisor canonical URI")},
-    {"desc", ""},
-    {NULL, NULL}
+static const vshCmdOptDef opts_vol_resize[] = {
+    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
+    {"capacity", VSH_OT_DATA, VSH_OFLAG_REQ,
+     N_("new capacity for the vol, as scaled integer (default bytes)")},
+    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
+    {"allocate", VSH_OT_BOOL, 0,
+     N_("allocate the new capacity, rather than leaving it sparse")},
+    {"delta", VSH_OT_BOOL, 0,
+     N_("use capacity as a delta to current size, rather than the new size")},
+    {"shrink", VSH_OT_BOOL, 0, N_("allow the resize to shrink the volume")},
+    {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdURI(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
+cmdVolResize(vshControl *ctl, const vshCmd *cmd)
 {
-    char *uri;
+    virStorageVolPtr vol;
+    const char *capacityStr = NULL;
+    unsigned long long capacity = 0;
+    unsigned int flags = 0;
+    bool ret = false;
+    bool delta = false;
+
+    if (vshCommandOptBool(cmd, "allocate"))
+        flags |= VIR_STORAGE_VOL_RESIZE_ALLOCATE;
+    if (vshCommandOptBool(cmd, "delta")) {
+        delta = true;
+        flags |= VIR_STORAGE_VOL_RESIZE_DELTA;
+    }
+    if (vshCommandOptBool(cmd, "shrink"))
+        flags |= VIR_STORAGE_VOL_RESIZE_SHRINK;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    uri = virConnectGetURI(ctl->conn);
-    if (uri == NULL) {
-        vshError(ctl, "%s", _("failed to get URI"));
+    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL)))
         return false;
+
+    if (vshCommandOptString(cmd, "capacity", &capacityStr) <= 0)
+        goto cleanup;
+    virSkipSpaces(&capacityStr);
+    if (*capacityStr == '-') {
+        /* The API always requires a positive value; but we allow a
+         * negative value for convenience.  */
+        if (delta && vshCommandOptBool(cmd, "shrink")){
+            capacityStr++;
+        } else {
+            vshError(ctl, "%s",
+                     _("negative size requires --delta and --shrink"));
+            goto cleanup;
+        }
+    }
+    if (vshVolSize(capacityStr, &capacity) < 0) {
+        vshError(ctl, _("Malformed size %s"), capacityStr);
+        goto cleanup;
     }
 
-    vshPrint(ctl, "%s\n", uri);
-    VIR_FREE(uri);
+    if (virStorageVolResize(vol, capacity, flags) == 0) {
+        vshPrint(ctl,
+                 delta ? _("Size of volume '%s' successfully changed by %s\n")
+                 : _("Size of volume '%s' successfully changed to %s\n"),
+                 virStorageVolGetName(vol), capacityStr);
+        ret = true;
+    } else {
+        vshError(ctl,
+                 delta ? _("Failed to change size of volume '%s' by %s\n")
+                 : _("Failed to change size of volume '%s' to %s\n"),
+                 virStorageVolGetName(vol), capacityStr);
+        ret = false;
+    }
 
-    return true;
+cleanup:
+    virStorageVolFree(vol);
+    return ret;
 }
 
+
 /*
- * "sysinfo" command
+ * "vol-dumpxml" command
  */
-static const vshCmdInfo info_sysinfo[] = {
-    {"help", N_("print the hypervisor sysinfo")},
-    {"desc",
-     N_("output an XML string for the hypervisor sysinfo, if available")},
+static const vshCmdInfo info_vol_dumpxml[] = {
+    {"help", N_("vol information in XML")},
+    {"desc", N_("Output the vol information as an XML dump to stdout.")},
     {NULL, NULL}
 };
 
+static const vshCmdOptDef opts_vol_dumpxml[] = {
+    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
+    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
+    {NULL, 0, 0, NULL}
+};
+
 static bool
-cmdSysinfo(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
+cmdVolDumpXML(vshControl *ctl, const vshCmd *cmd)
 {
-    char *sysinfo;
+    virStorageVolPtr vol;
+    bool ret = true;
+    char *dump;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    sysinfo = virConnectGetSysinfo(ctl->conn, 0);
-    if (sysinfo == NULL) {
-        vshError(ctl, "%s", _("failed to get sysinfo"));
+    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL)))
         return false;
-    }
 
-    vshPrint(ctl, "%s", sysinfo);
-    VIR_FREE(sysinfo);
+    dump = virStorageVolGetXMLDesc(vol, 0);
+    if (dump != NULL) {
+        vshPrint(ctl, "%s", dump);
+        VIR_FREE(dump);
+    } else {
+        ret = false;
+    }
 
-    return true;
+    virStorageVolFree(vol);
+    return ret;
 }
 
+
 /*
- * "domdisplay" command
+ * "vol-list" command
  */
-static const vshCmdInfo info_domdisplay[] = {
-    {"help", N_("domain display connection URI")},
-    {"desc", N_("Output the IP address and port number for the graphical display.")},
+static const vshCmdInfo info_vol_list[] = {
+    {"help", N_("list vols")},
+    {"desc", N_("Returns list of vols by pool.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_domdisplay[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"include-password", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("includes the password into the connection URI if available")},
+static const vshCmdOptDef opts_vol_list[] = {
+    {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
+    {"details", VSH_OT_BOOL, 0, N_("display extended details for volumes")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdDomDisplay(vshControl *ctl, const vshCmd *cmd)
+cmdVolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
 {
-    xmlDocPtr xml = NULL;
-    xmlXPathContextPtr ctxt = NULL;
-    virDomainPtr dom;
-    virBuffer buf = VIR_BUFFER_INITIALIZER;
-    bool ret = false;
-    char *doc;
-    char *xpath;
-    char *listen_addr;
-    int port, tls_port = 0;
-    char *passwd = NULL;
-    char *output = NULL;
-    const char *scheme[] = { "vnc", "spice", "rdp", NULL };
-    int iter = 0;
-    int tmp;
-    int flags = 0;
+    virStorageVolInfo volumeInfo;
+    virStoragePoolPtr pool;
+    char **activeNames = NULL;
+    char *outputStr = NULL;
+    const char *unit;
+    double val;
+    bool details = vshCommandOptBool(cmd, "details");
+    int numVolumes = 0, i;
+    int ret;
+    bool functionReturn;
+    int stringLength = 0;
+    size_t allocStrLength = 0, capStrLength = 0;
+    size_t nameStrLength = 0, pathStrLength = 0;
+    size_t typeStrLength = 0;
+    struct volInfoText {
+        char *allocation;
+        char *capacity;
+        char *path;
+        char *type;
+    };
+    struct volInfoText *volInfoTexts = NULL;
 
+    /* Check the connection to libvirtd daemon is still working */
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+    /* Look up the pool information given to us by the user */
+    if (!(pool = vshCommandOptPool(ctl, cmd, "pool", NULL)))
         return false;
 
-    if (!virDomainIsActive(dom)) {
-        vshError(ctl, _("Domain is not running"));
-        goto cleanup;
+    /* Determine the number of volumes in the pool */
+    numVolumes = virStoragePoolNumOfVolumes(pool);
+
+    if (numVolumes < 0) {
+        vshError(ctl, "%s", _("Failed to list storage volumes"));
+        virStoragePoolFree(pool);
+        return false;
     }
 
-    if (vshCommandOptBool(cmd, "include-password"))
-        flags |= VIR_DOMAIN_XML_SECURE;
+    /* Retrieve the list of volume names in the pool */
+    if (numVolumes > 0) {
+        activeNames = vshCalloc(ctl, numVolumes, sizeof(*activeNames));
+        if ((numVolumes = virStoragePoolListVolumes(pool, activeNames,
+                                                    numVolumes)) < 0) {
+            vshError(ctl, "%s", _("Failed to list active vols"));
+            VIR_FREE(activeNames);
+            virStoragePoolFree(pool);
+            return false;
+        }
 
-    doc = virDomainGetXMLDesc(dom, flags);
+        /* Sort the volume names */
+        qsort(&activeNames[0], numVolumes, sizeof(*activeNames), vshNameSorter);
 
-    if (!doc)
-        goto cleanup;
+        /* Set aside memory for volume information pointers */
+        volInfoTexts = vshCalloc(ctl, numVolumes, sizeof(*volInfoTexts));
+    }
 
-    xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt);
-    VIR_FREE(doc);
-    if (!xml)
-        goto cleanup;
+    /* Collect the rest of the volume information for display */
+    for (i = 0; i < numVolumes; i++) {
+        /* Retrieve volume info */
+        virStorageVolPtr vol = virStorageVolLookupByName(pool,
+                                                         activeNames[i]);
 
-    /* Attempt to grab our display info */
-    for (iter = 0; scheme[iter] != NULL; iter++) {
-        /* Create our XPATH lookup for the current display's port */
-        virAsprintf(&xpath, "string(/domain/devices/graphics[@type='%s']"
-                "/@port)", scheme[iter]);
-        if (!xpath) {
-            virReportOOMError();
-            goto cleanup;
+        /* Retrieve the volume path */
+        if ((volInfoTexts[i].path = virStorageVolGetPath(vol)) == NULL) {
+            /* Something went wrong retrieving a volume path, cope with it */
+            volInfoTexts[i].path = vshStrdup(ctl, _("unknown"));
         }
 
-        /* Attempt to get the port number for the current graphics scheme */
-        tmp = virXPathInt(xpath, ctxt, &port);
-        VIR_FREE(xpath);
-
-        /* If there is no port number for this type, then jump to the next
-         * scheme
-         */
-        if (tmp)
-            continue;
-
-        /* Create our XPATH lookup for the current display's address */
-        virAsprintf(&xpath, "string(/domain/devices/graphics[@type='%s']"
-                "/@listen)", scheme[iter]);
-        if (!xpath) {
-            virReportOOMError();
-            goto cleanup;
-        }
+        /* If requested, retrieve volume type and sizing information */
+        if (details) {
+            if (virStorageVolGetInfo(vol, &volumeInfo) != 0) {
+                /* Something went wrong retrieving volume info, cope with it */
+                volInfoTexts[i].allocation = vshStrdup(ctl, _("unknown"));
+                volInfoTexts[i].capacity = vshStrdup(ctl, _("unknown"));
+                volInfoTexts[i].type = vshStrdup(ctl, _("unknown"));
+            } else {
+                /* Convert the returned volume info into output strings */
 
-        /* Attempt to get the listening addr if set for the current
-         * graphics scheme
-         */
-        listen_addr = virXPathString(xpath, ctxt);
-        VIR_FREE(xpath);
-
-        /* Per scheme data mangling */
-        if (STREQ(scheme[iter], "vnc")) {
-            /* VNC protocol handlers take their port number as 'port' - 5900 */
-            port -= 5900;
-        } else if (STREQ(scheme[iter], "spice")) {
-            /* Create our XPATH lookup for the SPICE TLS Port */
-            virAsprintf(&xpath, "string(/domain/devices/graphics[@type='%s']"
-                    "/@tlsPort)", scheme[iter]);
-            if (!xpath) {
-                virReportOOMError();
-                goto cleanup;
-            }
+                /* Volume type */
+                switch (volumeInfo.type) {
+                        case VIR_STORAGE_VOL_FILE:
+                            volInfoTexts[i].type = vshStrdup(ctl, _("file"));
+                            break;
+                        case VIR_STORAGE_VOL_BLOCK:
+                            volInfoTexts[i].type = vshStrdup(ctl, _("block"));
+                            break;
+                        case VIR_STORAGE_VOL_DIR:
+                            volInfoTexts[i].type = vshStrdup(ctl, _("dir"));
+                            break;
+                        default:
+                            volInfoTexts[i].type = vshStrdup(ctl, _("unknown"));
+                }
 
-            /* Attempt to get the TLS port number for SPICE */
-            tmp = virXPathInt(xpath, ctxt, &tls_port);
-            VIR_FREE(xpath);
-            if (tmp)
-                tls_port = 0;
-
-            if (vshCommandOptBool(cmd, "include-password")) {
-                /* Create our XPATH lookup for the SPICE password */
-                virAsprintf(&xpath, "string(/domain/devices/graphics"
-                        "[@type='%s']/@passwd)", scheme[iter]);
-                if (!xpath) {
-                    virReportOOMError();
-                    goto cleanup;
+                /* Create the capacity output string */
+                val = prettyCapacity(volumeInfo.capacity, &unit);
+                ret = virAsprintf(&volInfoTexts[i].capacity,
+                                  "%.2lf %s", val, unit);
+                if (ret < 0) {
+                    /* An error occurred creating the string, return */
+                    goto asprintf_failure;
                 }
 
-                /* Attempt to get the SPICE password */
-                passwd = virXPathString(xpath, ctxt);
-                VIR_FREE(xpath);
+                /* Create the allocation output string */
+                val = prettyCapacity(volumeInfo.allocation, &unit);
+                ret = virAsprintf(&volInfoTexts[i].allocation,
+                                  "%.2lf %s", val, unit);
+                if (ret < 0) {
+                    /* An error occurred creating the string, return */
+                    goto asprintf_failure;
+                }
             }
-        }
-
-        /* Build up the full URI, starting with the scheme */
-        virBufferAsprintf(&buf, "%s://", scheme[iter]);
 
-        /* Then host name or IP */
-        if (!listen_addr || STREQ((const char *)listen_addr, "0.0.0.0"))
-            virBufferAddLit(&buf, "localhost");
-        else
-            virBufferAsprintf(&buf, "%s", listen_addr);
+            /* Remember the largest length for each output string.
+             * This lets us displaying header and volume information rows
+             * using a single, properly sized, printf style output string.
+             */
 
-        VIR_FREE(listen_addr);
+            /* Keep the length of name string if longest so far */
+            stringLength = strlen(activeNames[i]);
+            if (stringLength > nameStrLength)
+                nameStrLength = stringLength;
 
-        /* Add the port */
-        if (STREQ(scheme[iter], "spice"))
-            virBufferAsprintf(&buf, "?port=%d", port);
-        else
-            virBufferAsprintf(&buf, ":%d", port);
+            /* Keep the length of path string if longest so far */
+            stringLength = strlen(volInfoTexts[i].path);
+            if (stringLength > pathStrLength)
+                pathStrLength = stringLength;
 
-        /* TLS Port */
-        if (tls_port)
-            virBufferAsprintf(&buf, "&tls-port=%d", tls_port);
+            /* Keep the length of type string if longest so far */
+            stringLength = strlen(volInfoTexts[i].type);
+            if (stringLength > typeStrLength)
+                typeStrLength = stringLength;
 
-        /* Password */
-        if (passwd) {
-            virBufferAsprintf(&buf, "&password=%s", passwd);
-            VIR_FREE(passwd);
-        }
+            /* Keep the length of capacity string if longest so far */
+            stringLength = strlen(volInfoTexts[i].capacity);
+            if (stringLength > capStrLength)
+                capStrLength = stringLength;
 
-        /* Ensure we can print our URI */
-        if (virBufferError(&buf)) {
-            vshPrint(ctl, "%s", _("Failed to create display URI"));
-            goto cleanup;
+            /* Keep the length of allocation string if longest so far */
+            stringLength = strlen(volInfoTexts[i].allocation);
+            if (stringLength > allocStrLength)
+                allocStrLength = stringLength;
         }
 
-        /* Print out our full URI */
-        output = virBufferContentAndReset(&buf);
-        vshPrint(ctl, "%s", output);
-        VIR_FREE(output);
-
-        /* We got what we came for so return successfully */
-        ret = true;
-        break;
+        /* Cleanup memory allocation */
+        virStorageVolFree(vol);
     }
 
-cleanup:
-    xmlXPathFreeContext(ctxt);
-    xmlFreeDoc(xml);
-    virDomainFree(dom);
-    return ret;
-}
-
-/*
- * "vncdisplay" command
- */
-static const vshCmdInfo info_vncdisplay[] = {
-    {"help", N_("vnc display")},
-    {"desc", N_("Output the IP address and port number for the VNC display.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_vncdisplay[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdVNCDisplay(vshControl *ctl, const vshCmd *cmd)
-{
-    xmlDocPtr xml = NULL;
-    xmlXPathContextPtr ctxt = NULL;
-    virDomainPtr dom;
-    bool ret = false;
-    int port = 0;
-    char *doc = NULL;
-    char *listen_addr = NULL;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
+    /* If the --details option wasn't selected, we output the volume
+     * info using the fixed string format from previous versions to
+     * maintain backward compatibility.
+     */
 
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
-        return false;
+    /* 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 < numVolumes; i++) {
+            vshPrint(ctl, "%-20s %-40s\n", activeNames[i],
+                     volInfoTexts[i].path);
+        }
 
-    /* Check if the domain is active and don't rely on -1 for this */
-    if (!virDomainIsActive(dom)) {
-        vshError(ctl, _("Domain is not running"));
+        /* Cleanup and return */
+        functionReturn = true;
         goto cleanup;
     }
 
-    if (!(doc = virDomainGetXMLDesc(dom, 0)))
-        goto cleanup;
+    /* We only get here if the --details option was selected. */
 
-    if (!(xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt)))
-        goto cleanup;
+    /* Use the length of name header string if it's longest */
+    stringLength = strlen(_("Name"));
+    if (stringLength > nameStrLength)
+        nameStrLength = stringLength;
 
-    /* Get the VNC port */
-    if (virXPathInt("string(/domain/devices/graphics[@type='vnc']/@port)",
-                    ctxt, &port)) {
-        vshError(ctl, _("Failed to get VNC port. Is this domain using VNC?"));
-        goto cleanup;
-    }
+    /* Use the length of path header string if it's longest */
+    stringLength = strlen(_("Path"));
+    if (stringLength > pathStrLength)
+        pathStrLength = stringLength;
 
-    listen_addr = virXPathString("string(/domain/devices/graphics"
-                                 "[@type='vnc']/@listen)", ctxt);
-    if (listen_addr == NULL || STREQ(listen_addr, "0.0.0.0"))
-        vshPrint(ctl, ":%d\n", port-5900);
-    else
-        vshPrint(ctl, "%s:%d\n", listen_addr, port-5900);
+    /* Use the length of type header string if it's longest */
+    stringLength = strlen(_("Type"));
+    if (stringLength > typeStrLength)
+        typeStrLength = stringLength;
 
-    ret = true;
+    /* Use the length of capacity header string if it's longest */
+    stringLength = strlen(_("Capacity"));
+    if (stringLength > capStrLength)
+        capStrLength = stringLength;
 
- cleanup:
-    VIR_FREE(doc);
-    VIR_FREE(listen_addr);
-    xmlXPathFreeContext(ctxt);
-    xmlFreeDoc(xml);
-    virDomainFree(dom);
-    return ret;
-}
+    /* Use the length of allocation header string if it's longest */
+    stringLength = strlen(_("Allocation"));
+    if (stringLength > allocStrLength)
+        allocStrLength = stringLength;
 
-/*
- * "ttyconsole" command
- */
-static const vshCmdInfo info_ttyconsole[] = {
-    {"help", N_("tty console")},
-    {"desc", N_("Output the device for the TTY console.")},
-    {NULL, NULL}
-};
+    /* 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);
 
-static const vshCmdOptDef opts_ttyconsole[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {NULL, 0, 0, NULL}
-};
+    /* Create the output template */
+    ret = virAsprintf(&outputStr,
+                      "%%-%lus  %%-%lus  %%-%lus  %%%lus  %%%lus\n",
+                      (unsigned long) nameStrLength,
+                      (unsigned long) pathStrLength,
+                      (unsigned long) typeStrLength,
+                      (unsigned long) capStrLength,
+                      (unsigned long) allocStrLength);
+    if (ret < 0) {
+        /* An error occurred creating the string, return */
+        goto asprintf_failure;
+    }
 
-static bool
-cmdTTYConsole(vshControl *ctl, const vshCmd *cmd)
-{
-    xmlDocPtr xml = NULL;
-    xmlXPathObjectPtr obj = NULL;
-    xmlXPathContextPtr ctxt = NULL;
-    virDomainPtr dom;
-    bool ret = false;
-    char *doc;
+    /* Display the header */
+    vshPrint(ctl, outputStr, _("Name"), _("Path"), _("Type"),
+             ("Capacity"), _("Allocation"));
+    for (i = nameStrLength + pathStrLength + typeStrLength
+                           + capStrLength + allocStrLength
+                           + 8; i > 0; i--)
+        vshPrintExtra(ctl, "-");
+    vshPrintExtra(ctl, "\n");
 
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        return false;
+    /* Display the volume info rows */
+    for (i = 0; i < numVolumes; i++) {
+        vshPrint(ctl, outputStr,
+                 activeNames[i],
+                 volInfoTexts[i].path,
+                 volInfoTexts[i].type,
+                 volInfoTexts[i].capacity,
+                 volInfoTexts[i].allocation);
+    }
 
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
-        return false;
+    /* Cleanup and return */
+    functionReturn = true;
+    goto cleanup;
 
-    doc = virDomainGetXMLDesc(dom, 0);
-    if (!doc)
-        goto cleanup;
+asprintf_failure:
 
-    xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt);
-    VIR_FREE(doc);
-    if (!xml)
-        goto cleanup;
+    /* Display an appropriate error message then cleanup and return */
+    switch (errno) {
+    case ENOMEM:
+        /* Couldn't allocate memory */
+        vshError(ctl, "%s", _("Out of memory"));
+        break;
+    default:
+        /* Some other error */
+        vshError(ctl, _("virAsprintf failed (errno %d)"), errno);
+    }
+    functionReturn = false;
 
-    obj = xmlXPathEval(BAD_CAST "string(/domain/devices/console/@tty)", ctxt);
-    if (obj == NULL || obj->type != XPATH_STRING ||
-        obj->stringval == NULL || obj->stringval[0] == 0) {
-        goto cleanup;
+cleanup:
+
+    /* Safely free the memory allocated in this function */
+    for (i = 0; i < numVolumes; i++) {
+        /* Cleanup the memory for one volume info structure per loop */
+        VIR_FREE(volInfoTexts[i].path);
+        VIR_FREE(volInfoTexts[i].type);
+        VIR_FREE(volInfoTexts[i].capacity);
+        VIR_FREE(volInfoTexts[i].allocation);
+        VIR_FREE(activeNames[i]);
     }
-    vshPrint(ctl, "%s\n", (const char *)obj->stringval);
-    ret = true;
 
- cleanup:
-    xmlXPathFreeObject(obj);
-    xmlXPathFreeContext(ctxt);
-    xmlFreeDoc(xml);
-    virDomainFree(dom);
-    return ret;
+    /* Cleanup remaining memory */
+    VIR_FREE(outputStr);
+    VIR_FREE(volInfoTexts);
+    VIR_FREE(activeNames);
+    virStoragePoolFree(pool);
+
+    /* Return the desired value */
+    return functionReturn;
 }
 
+
 /*
- * "domhostname" command
+ * "vol-name" command
  */
-static const vshCmdInfo info_domhostname[] = {
-    {"help", N_("print the domain's hostname")},
+static const vshCmdInfo info_vol_name[] = {
+    {"help", N_("returns the volume name for a given volume key or path")},
     {"desc", ""},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_domhostname[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+static const vshCmdOptDef opts_vol_name[] = {
+    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("volume key or path")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdDomHostname(vshControl *ctl, const vshCmd *cmd)
+cmdVolName(vshControl *ctl, const vshCmd *cmd)
 {
-    char *hostname;
-    virDomainPtr dom;
-    bool ret = false;
+    virStorageVolPtr vol;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
-        return false;
-
-    hostname = virDomainGetHostname(dom, 0);
-    if (hostname == NULL) {
-        vshError(ctl, "%s", _("failed to get hostname"));
-        goto error;
-    }
-
-    vshPrint(ctl, "%s\n", hostname);
-    ret = true;
+    if (!(vol = vshCommandOptVolBy(ctl, cmd, "vol", NULL, NULL,
+                                   VSH_BYUUID)))
+        return false;
 
-error:
-    VIR_FREE(hostname);
-    virDomainFree(dom);
-    return ret;
+    vshPrint(ctl, "%s\n", virStorageVolGetName(vol));
+    virStorageVolFree(vol);
+    return true;
 }
 
 
 /*
- * "attach-device" command
+ * "vol-pool" command
  */
-static const vshCmdInfo info_attach_device[] = {
-    {"help", N_("attach device from an XML file")},
-    {"desc", N_("Attach device from an XML <file>.")},
+static const vshCmdInfo info_vol_pool[] = {
+    {"help", N_("returns the storage pool for a given volume key or path")},
+    {"desc", ""},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_attach_device[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"file",   VSH_OT_DATA, VSH_OFLAG_REQ, N_("XML file")},
-    {"persistent", VSH_OT_ALIAS, 0, "config"},
-    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
+static const vshCmdOptDef opts_vol_pool[] = {
+    {"uuid", VSH_OT_BOOL, 0, N_("return the pool uuid rather than pool name")},
+    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("volume key or path")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdAttachDevice(vshControl *ctl, const vshCmd *cmd)
+cmdVolPool(vshControl *ctl, const vshCmd *cmd)
 {
-    virDomainPtr dom;
-    const char *from = NULL;
-    char *buffer;
-    int ret;
-    unsigned int flags;
+    virStoragePoolPtr pool;
+    virStorageVolPtr vol;
+    char uuid[VIR_UUID_STRING_BUFLEN];
 
+    /* Check the connection to libvirtd daemon is still working */
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
-        return false;
-
-    if (vshCommandOptString(cmd, "file", &from) <= 0) {
-        virDomainFree(dom);
+    /* Use the supplied string to locate the volume */
+    if (!(vol = vshCommandOptVolBy(ctl, cmd, "vol", NULL, NULL,
+                                   VSH_BYUUID))) {
         return false;
     }
 
-    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
-        virshReportError(ctl);
-        virDomainFree(dom);
+    /* Look up the parent storage pool for the volume */
+    pool = virStoragePoolLookupByVolume(vol);
+    if (pool == NULL) {
+        vshError(ctl, "%s", _("failed to get parent pool"));
+        virStorageVolFree(vol);
         return false;
     }
 
-    if (vshCommandOptBool(cmd, "config")) {
-        flags = VIR_DOMAIN_AFFECT_CONFIG;
-        if (virDomainIsActive(dom) == 1)
-           flags |= VIR_DOMAIN_AFFECT_LIVE;
-        ret = virDomainAttachDeviceFlags(dom, buffer, flags);
-    } else {
-        ret = virDomainAttachDevice(dom, buffer);
-    }
-    VIR_FREE(buffer);
-
-    if (ret < 0) {
-        vshError(ctl, _("Failed to attach device from %s"), from);
-        virDomainFree(dom);
-        return false;
+    /* Return the requested details of the parent storage pool */
+    if (vshCommandOptBool(cmd, "uuid")) {
+        /* Retrieve and return pool UUID string */
+        if (virStoragePoolGetUUIDString(pool, &uuid[0]) == 0)
+            vshPrint(ctl, "%s\n", uuid);
     } else {
-        vshPrint(ctl, "%s", _("Device attached successfully\n"));
+        /* Return the storage pool name */
+        vshPrint(ctl, "%s\n", virStoragePoolGetName(pool));
     }
 
-    virDomainFree(dom);
+    /* Cleanup */
+    virStorageVolFree(vol);
+    virStoragePoolFree(pool);
     return true;
 }
 
-/**
- * Check if n1 is superset of n2, meaning n1 contains all elements and
- * attributes as n2 at least. Including children.
- * @n1 first node
- * @n2 second node
- * returns true in case n1 covers n2, false otherwise.
- */
-ATTRIBUTE_UNUSED
-static bool
-vshNodeIsSuperset(xmlNodePtr n1, xmlNodePtr n2)
-{
-    xmlNodePtr child1, child2;
-    xmlAttrPtr attr;
-    char *prop1, *prop2;
-    bool found;
-    bool visited;
-    bool ret = false;
-    long n1_child_size, n2_child_size, n1_iter;
-    virBitmapPtr bitmap;
-
-    if (!n1 && !n2)
-        return true;
 
-    if (!n1 || !n2)
-        return false;
+/*
+ * "vol-key" command
+ */
+static const vshCmdInfo info_vol_key[] = {
+    {"help", N_("returns the volume key for a given volume name or path")},
+    {"desc", ""},
+    {NULL, NULL}
+};
 
-    if (!xmlStrEqual(n1->name, n2->name))
-        return false;
+static const vshCmdOptDef opts_vol_key[] = {
+    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("volume name or path")},
+    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
+    {NULL, 0, 0, NULL}
+};
 
-    /* Iterate over n2 attributes and check if n1 contains them*/
-    attr = n2->properties;
-    while (attr) {
-        if (attr->type == XML_ATTRIBUTE_NODE) {
-            prop1 = virXMLPropString(n1, (const char *) attr->name);
-            prop2 = virXMLPropString(n2, (const char *) attr->name);
-            if (STRNEQ_NULLABLE(prop1, prop2)) {
-                xmlFree(prop1);
-                xmlFree(prop2);
-                return false;
-            }
-            xmlFree(prop1);
-            xmlFree(prop2);
-        }
-        attr = attr->next;
-    }
+static bool
+cmdVolKey(vshControl *ctl, const vshCmd *cmd)
+{
+    virStorageVolPtr vol;
 
-    n1_child_size = virXMLChildElementCount(n1);
-    n2_child_size = virXMLChildElementCount(n2);
-    if (n1_child_size < 0 || n2_child_size < 0 ||
-        n1_child_size < n2_child_size)
+    if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (n1_child_size == 0 && n2_child_size == 0)
-        return true;
-
-    if (!(bitmap = virBitmapAlloc(n1_child_size))) {
-        virReportOOMError();
+    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL)))
         return false;
-    }
 
-    child2 = n2->children;
-    while (child2) {
-        if (child2->type != XML_ELEMENT_NODE) {
-            child2 = child2->next;
-            continue;
-        }
-
-        child1 = n1->children;
-        n1_iter = 0;
-        found = false;
-        while (child1) {
-            if (child1->type != XML_ELEMENT_NODE) {
-                child1 = child1->next;
-                continue;
-            }
-
-            if (virBitmapGetBit(bitmap, n1_iter, &visited) < 0) {
-                vshError(NULL, "%s", _("Bad child elements counting."));
-                goto cleanup;
-            }
-
-            if (visited) {
-                child1 = child1->next;
-                n1_iter++;
-                continue;
-            }
-
-            if (xmlStrEqual(child1->name, child2->name)) {
-                found = true;
-                if (virBitmapSetBit(bitmap, n1_iter) < 0) {
-                    vshError(NULL, "%s", _("Bad child elements counting."));
-                    goto cleanup;
-                }
-
-                if (!vshNodeIsSuperset(child1, child2))
-                    goto cleanup;
-
-                break;
-            }
-
-            child1 = child1->next;
-            n1_iter++;
-        }
-
-        if (!found)
-            goto cleanup;
-
-        child2 = child2->next;
-    }
-
-    ret = true;
-
-cleanup:
-    virBitmapFree(bitmap);
-    return ret;
+    vshPrint(ctl, "%s\n", virStorageVolGetKey(vol));
+    virStorageVolFree(vol);
+    return true;
 }
 
-/**
- * vshCompleteXMLFromDomain:
- * @ctl vshControl for error messages printing
- * @dom domain
- * @oldXML device XML before
- * @newXML and after completion
- *
- * For given domain and (probably incomplete) device XML specification try to
- * find such device in domain and complete missing parts. This is however
- * possible only when given device XML is sufficiently precise so it addresses
- * only one device.
- *
- * Returns -2 when no such device exists in domain, -3 when given XML selects many
- *          (is too ambiguous), 0 in case of success. Otherwise returns -1. @newXML
- *          is touched only in case of success.
- */
-ATTRIBUTE_UNUSED
-static int
-vshCompleteXMLFromDomain(vshControl *ctl, virDomainPtr dom, char *oldXML,
-                         char **newXML)
-{
-    int funcRet = -1;
-    char *domXML = NULL;
-    xmlDocPtr domDoc = NULL, devDoc = NULL;
-    xmlNodePtr node = NULL;
-    xmlXPathContextPtr domCtxt = NULL, devCtxt = NULL;
-    xmlNodePtr *devices = NULL;
-    xmlSaveCtxtPtr sctxt = NULL;
-    int devices_size;
-    char *xpath = NULL;
-    xmlBufferPtr buf = NULL;
-    int i = 0;
-    int indx = -1;
-
-    if (!(domXML = virDomainGetXMLDesc(dom, 0))) {
-        vshError(ctl, _("couldn't get XML description of domain %s"),
-                 virDomainGetName(dom));
-        goto cleanup;
-    }
-
-    domDoc = virXMLParseStringCtxt(domXML, _("(domain_definition)"), &domCtxt);
-    if (!domDoc) {
-        vshError(ctl, _("Failed to parse domain definition xml"));
-        goto cleanup;
-    }
-
-    devDoc = virXMLParseStringCtxt(oldXML, _("(device_definition)"), &devCtxt);
-    if (!devDoc) {
-        vshError(ctl, _("Failed to parse device definition xml"));
-        goto cleanup;
-    }
-
-    node = xmlDocGetRootElement(devDoc);
-
-    buf = xmlBufferCreate();
-    if (!buf) {
-        vshError(ctl, "%s", _("out of memory"));
-        goto cleanup;
-    }
-
-    /* Get all possible devices */
-    virAsprintf(&xpath, "/domain/devices/%s", node->name);
-    if (!xpath) {
-        virReportOOMError();
-        goto cleanup;
-    }
-    devices_size = virXPathNodeSet(xpath, domCtxt, &devices);
-
-    if (devices_size < 0) {
-        /* error */
-        vshError(ctl, "%s", _("error when selecting nodes"));
-        goto cleanup;
-    } else if (devices_size == 0) {
-        /* no such device */
-        funcRet = -2;
-        goto cleanup;
-    }
-
-    /* and refine */
-    for (i = 0; i < devices_size; i++) {
-        if (vshNodeIsSuperset(devices[i], node)) {
-            if (indx >= 0) {
-                funcRet = -3; /* ambiguous */
-                goto cleanup;
-            }
-            indx = i;
-        }
-    }
-
-    if (indx < 0) {
-        funcRet = -2; /* no such device */
-        goto cleanup;
-    }
-
-    vshDebug(ctl, VSH_ERR_DEBUG, "Found device at pos %d\n", indx);
-
-    if (newXML) {
-        sctxt = xmlSaveToBuffer(buf, NULL, 0);
-        if (!sctxt) {
-            vshError(ctl, "%s", _("failed to create document saving context"));
-            goto cleanup;
-        }
-
-        xmlSaveTree(sctxt, devices[indx]);
-        xmlSaveClose(sctxt);
-        *newXML = (char *) xmlBufferContent(buf);
-        if (!*newXML) {
-            virReportOOMError();
-            goto cleanup;
-        }
-        buf->content = NULL;
-    }
-
-    vshDebug(ctl, VSH_ERR_DEBUG, "Old xml:\n%s\nNew xml:\n%s\n", oldXML,
-             newXML ? NULLSTR(*newXML) : "(null)");
 
-    funcRet = 0;
-
-cleanup:
-    xmlBufferFree(buf);
-    VIR_FREE(devices);
-    xmlXPathFreeContext(devCtxt);
-    xmlXPathFreeContext(domCtxt);
-    xmlFreeDoc(devDoc);
-    xmlFreeDoc(domDoc);
-    VIR_FREE(domXML);
-    VIR_FREE(xpath);
-    return funcRet;
-}
 
 /*
- * "detach-device" command
+ * "vol-path" command
  */
-static const vshCmdInfo info_detach_device[] = {
-    {"help", N_("detach device from an XML file")},
-    {"desc", N_("Detach device from an XML <file>")},
+static const vshCmdInfo info_vol_path[] = {
+    {"help", N_("returns the volume path for a given volume name or key")},
+    {"desc", ""},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_detach_device[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"file",   VSH_OT_DATA, VSH_OFLAG_REQ, N_("XML file")},
-    {"persistent", VSH_OT_ALIAS, 0, "config"},
-    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
+static const vshCmdOptDef opts_vol_path[] = {
+    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("volume name or key")},
+    {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdDetachDevice(vshControl *ctl, const vshCmd *cmd)
+cmdVolPath(vshControl *ctl, const vshCmd *cmd)
 {
-    virDomainPtr dom = NULL;
-    const char *from = NULL;
-    char *buffer = NULL;
-    int ret;
-    bool funcRet = false;
-    unsigned int flags;
+    virStorageVolPtr vol;
+    char * StorageVolPath;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+    if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL))) {
         return false;
-
-    if (vshCommandOptString(cmd, "file", &from) <= 0)
-        goto cleanup;
-
-    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
-        virshReportError(ctl);
-        goto cleanup;
     }
 
-    if (vshCommandOptBool(cmd, "config")) {
-        flags = VIR_DOMAIN_AFFECT_CONFIG;
-        if (virDomainIsActive(dom) == 1)
-           flags |= VIR_DOMAIN_AFFECT_LIVE;
-        ret = virDomainDetachDeviceFlags(dom, buffer, flags);
-    } else {
-        ret = virDomainDetachDevice(dom, buffer);
-    }
-
-    if (ret < 0) {
-        vshError(ctl, _("Failed to detach device from %s"), from);
-        goto cleanup;
+    if ((StorageVolPath = virStorageVolGetPath(vol)) == NULL) {
+        virStorageVolFree(vol);
+        return false;
     }
 
-    vshPrint(ctl, "%s", _("Device detached successfully\n"));
-    funcRet = true;
-
-cleanup:
-    VIR_FREE(buffer);
-    virDomainFree(dom);
-    return funcRet;
+    vshPrint(ctl, "%s\n", StorageVolPath);
+    VIR_FREE(StorageVolPath);
+    virStorageVolFree(vol);
+    return true;
 }
 
 
 /*
- * "update-device" command
+ * "secret-define" command
  */
-static const vshCmdInfo info_update_device[] = {
-    {"help", N_("update device from an XML file")},
-    {"desc", N_("Update device from an XML <file>.")},
+static const vshCmdInfo info_secret_define[] = {
+    {"help", N_("define or modify a secret from an XML file")},
+    {"desc", N_("Define or modify a secret.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_update_device[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"file",   VSH_OT_DATA, VSH_OFLAG_REQ, N_("XML file")},
-    {"persistent", VSH_OT_ALIAS, 0, "config"},
-    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
-    {"force",  VSH_OT_BOOL, 0, N_("force device update")},
+static const vshCmdOptDef opts_secret_define[] = {
+    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing secret attributes in XML")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdUpdateDevice(vshControl *ctl, const vshCmd *cmd)
+cmdSecretDefine(vshControl *ctl, const vshCmd *cmd)
 {
-    virDomainPtr dom;
     const char *from = NULL;
     char *buffer;
-    int ret;
-    unsigned int flags;
+    virSecretPtr res;
+    char uuid[VIR_UUID_STRING_BUFLEN];
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
-        return false;
-
-    if (vshCommandOptString(cmd, "file", &from) <= 0) {
-        virDomainFree(dom);
+    if (vshCommandOptString(cmd, "file", &from) <= 0)
         return false;
-    }
 
-    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
-        virshReportError(ctl);
-        virDomainFree(dom);
+    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
         return false;
-    }
-
-    if (vshCommandOptBool(cmd, "config")) {
-        flags = VIR_DOMAIN_AFFECT_CONFIG;
-        if (virDomainIsActive(dom) == 1)
-           flags |= VIR_DOMAIN_AFFECT_LIVE;
-    } else {
-        flags = VIR_DOMAIN_AFFECT_LIVE;
-    }
-
-    if (vshCommandOptBool(cmd, "force"))
-        flags |= VIR_DOMAIN_DEVICE_MODIFY_FORCE;
 
-    ret = virDomainUpdateDeviceFlags(dom, buffer, flags);
+    res = virSecretDefineXML(ctl->conn, buffer, 0);
     VIR_FREE(buffer);
 
-    if (ret < 0) {
-        vshError(ctl, _("Failed to update device from %s"), from);
-        virDomainFree(dom);
+    if (res == NULL) {
+        vshError(ctl, _("Failed to set attributes from %s"), from);
+        return false;
+    }
+    if (virSecretGetUUIDString(res, &(uuid[0])) < 0) {
+        vshError(ctl, "%s", _("Failed to get UUID of created secret"));
+        virSecretFree(res);
         return false;
-    } else {
-        vshPrint(ctl, "%s", _("Device updated successfully\n"));
     }
-
-    virDomainFree(dom);
+    vshPrint(ctl, _("Secret %s created\n"), uuid);
+    virSecretFree(res);
     return true;
 }
 
-
 /*
- * "attach-interface" command
+ * "secret-dumpxml" command
  */
-static const vshCmdInfo info_attach_interface[] = {
-    {"help", N_("attach network interface")},
-    {"desc", N_("Attach new network interface.")},
+static const vshCmdInfo info_secret_dumpxml[] = {
+    {"help", N_("secret attributes in XML")},
+    {"desc", N_("Output attributes of a secret as an XML dump to stdout.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_attach_interface[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"type",   VSH_OT_DATA, VSH_OFLAG_REQ, N_("network interface type")},
-    {"source", VSH_OT_DATA, VSH_OFLAG_REQ, N_("source of network interface")},
-    {"target", VSH_OT_DATA, 0, N_("target network name")},
-    {"mac",    VSH_OT_DATA, 0, N_("MAC address")},
-    {"script", VSH_OT_DATA, 0, N_("script used to bridge network interface")},
-    {"model", VSH_OT_DATA, 0, N_("model type")},
-    {"persistent", VSH_OT_ALIAS, 0, "config"},
-    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
-    {"inbound", VSH_OT_DATA, VSH_OFLAG_NONE, N_("control domain's incoming traffics")},
-    {"outbound", VSH_OT_DATA, VSH_OFLAG_NONE, N_("control domain's outgoing traffics")},
+static const vshCmdOptDef opts_secret_dumpxml[] = {
+    {"secret", VSH_OT_DATA, VSH_OFLAG_REQ, N_("secret UUID")},
     {NULL, 0, 0, NULL}
 };
 
-/* parse inbound and outbound which are in the format of
- * 'average,peak,burst', in which peak and burst are optional,
- * thus 'average,,burst' and 'average,peak' are also legal. */
-static int parseRateStr(const char *rateStr, virNetDevBandwidthRatePtr rate)
-{
-    const char *average = NULL;
-    char *peak = NULL, *burst = NULL;
-
-    average = rateStr;
-    if (!average)
-        return -1;
-    if (virStrToLong_ull(average, &peak, 10, &rate->average) < 0)
-        return -1;
-
-    /* peak will be updated to point to the end of rateStr in case
-     * of 'average' */
-    if (peak && *peak != '\0') {
-        burst = strchr(peak + 1, ',');
-        if (!(burst && (burst - peak == 1))) {
-            if (virStrToLong_ull(peak + 1, &burst, 10, &rate->peak) < 0)
-                return -1;
-        }
-
-        /* burst will be updated to point to the end of rateStr in case
-         * of 'average,peak' */
-        if (burst && *burst != '\0') {
-            if (virStrToLong_ull(burst + 1, NULL, 10, &rate->burst) < 0)
-                return -1;
-        }
-    }
-
-
-    return 0;
-}
-
 static bool
-cmdAttachInterface(vshControl *ctl, const vshCmd *cmd)
+cmdSecretDumpXML(vshControl *ctl, const vshCmd *cmd)
 {
-    virDomainPtr dom = NULL;
-    const char *mac = NULL, *target = NULL, *script = NULL,
-                *type = NULL, *source = NULL, *model = NULL,
-                *inboundStr = NULL, *outboundStr = NULL;
-    virNetDevBandwidthRate inbound, outbound;
-    int typ;
-    int ret;
-    bool functionReturn = false;
-    unsigned int flags;
-    virBuffer buf = VIR_BUFFER_INITIALIZER;
+    virSecretPtr secret;
+    bool ret = false;
     char *xml;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
-        goto cleanup;
-
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
-        goto cleanup;
-
-    if (vshCommandOptString(cmd, "type", &type) <= 0)
-        goto cleanup;
-
-    if (vshCommandOptString(cmd, "source", &source) < 0 ||
-        vshCommandOptString(cmd, "target", &target) < 0 ||
-        vshCommandOptString(cmd, "mac", &mac) < 0 ||
-        vshCommandOptString(cmd, "script", &script) < 0 ||
-        vshCommandOptString(cmd, "model", &model) < 0 ||
-        vshCommandOptString(cmd, "inbound", &inboundStr) < 0 ||
-        vshCommandOptString(cmd, "outbound", &outboundStr) < 0) {
-        vshError(ctl, "missing argument");
-        goto cleanup;
-    }
-
-    /* check interface type */
-    if (STREQ(type, "network")) {
-        typ = 1;
-    } else if (STREQ(type, "bridge")) {
-        typ = 2;
-    } else {
-        vshError(ctl, _("No support for %s in command 'attach-interface'"),
-                 type);
-        goto cleanup;
-    }
-
-    if (inboundStr) {
-        memset(&inbound, 0, sizeof(inbound));
-        if (parseRateStr(inboundStr, &inbound) < 0) {
-            vshError(ctl, _("inbound format is incorrect"));
-            goto cleanup;
-        }
-        if (inbound.average == 0) {
-            vshError(ctl, _("inbound average is mandatory"));
-            goto cleanup;
-        }
-    }
-    if (outboundStr) {
-        memset(&outbound, 0, sizeof(outbound));
-        if (parseRateStr(outboundStr, &outbound) < 0) {
-            vshError(ctl, _("outbound format is incorrect"));
-            goto cleanup;
-        }
-        if (outbound.average == 0) {
-            vshError(ctl, _("outbound average is mandatory"));
-            goto cleanup;
-        }
-    }
-
-    /* Make XML of interface */
-    virBufferAsprintf(&buf, "<interface type='%s'>\n", type);
-
-    if (typ == 1)
-        virBufferAsprintf(&buf, "  <source network='%s'/>\n", source);
-    else if (typ == 2)
-        virBufferAsprintf(&buf, "  <source bridge='%s'/>\n", source);
-
-    if (target != NULL)
-        virBufferAsprintf(&buf, "  <target dev='%s'/>\n", target);
-    if (mac != NULL)
-        virBufferAsprintf(&buf, "  <mac address='%s'/>\n", mac);
-    if (script != NULL)
-        virBufferAsprintf(&buf, "  <script path='%s'/>\n", script);
-    if (model != NULL)
-        virBufferAsprintf(&buf, "  <model type='%s'/>\n", model);
-
-    if (inboundStr || outboundStr) {
-        virBufferAsprintf(&buf, "  <bandwidth>\n");
-        if (inboundStr && inbound.average > 0) {
-            virBufferAsprintf(&buf, "    <inbound average='%llu'", inbound.average);
-            if (inbound.peak > 0)
-                virBufferAsprintf(&buf, " peak='%llu'", inbound.peak);
-            if (inbound.burst > 0)
-                virBufferAsprintf(&buf, " burst='%llu'", inbound.burst);
-            virBufferAsprintf(&buf, "/>\n");
-        }
-        if (outboundStr && outbound.average > 0) {
-            virBufferAsprintf(&buf, "    <outbound average='%llu'", outbound.average);
-            if (outbound.peak > 0)
-                virBufferAsprintf(&buf, " peak='%llu'", outbound.peak);
-            if (outbound.burst > 0)
-                virBufferAsprintf(&buf, " burst='%llu'", outbound.burst);
-            virBufferAsprintf(&buf, "/>\n");
-        }
-        virBufferAsprintf(&buf, "  </bandwidth>\n");
-    }
+        return false;
 
-    virBufferAddLit(&buf, "</interface>\n");
+    secret = vshCommandOptSecret(ctl, cmd, NULL);
+    if (secret == NULL)
+        return false;
 
-    if (virBufferError(&buf)) {
-        vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
+    xml = virSecretGetXMLDesc(secret, 0);
+    if (xml == NULL)
         goto cleanup;
-    }
-
-    xml = virBufferContentAndReset(&buf);
-
-    if (vshCommandOptBool(cmd, "config")) {
-        flags = VIR_DOMAIN_AFFECT_CONFIG;
-        if (virDomainIsActive(dom) == 1)
-            flags |= VIR_DOMAIN_AFFECT_LIVE;
-        ret = virDomainAttachDeviceFlags(dom, xml, flags);
-    } else {
-        ret = virDomainAttachDevice(dom, xml);
-    }
-
+    vshPrint(ctl, "%s", xml);
     VIR_FREE(xml);
+    ret = true;
 
-    if (ret != 0) {
-        vshError(ctl, "%s", _("Failed to attach interface"));
-    } else {
-        vshPrint(ctl, "%s", _("Interface attached successfully\n"));
-        functionReturn = true;
-    }
-
- cleanup:
-    if (dom)
-        virDomainFree(dom);
-    virBufferFreeAndReset(&buf);
-    return functionReturn;
+cleanup:
+    virSecretFree(secret);
+    return ret;
 }
 
 /*
- * "detach-interface" command
+ * "secret-set-value" command
  */
-static const vshCmdInfo info_detach_interface[] = {
-    {"help", N_("detach network interface")},
-    {"desc", N_("Detach network interface.")},
+static const vshCmdInfo info_secret_set_value[] = {
+    {"help", N_("set a secret value")},
+    {"desc", N_("Set a secret value.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_detach_interface[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"type",   VSH_OT_DATA, VSH_OFLAG_REQ, N_("network interface type")},
-    {"mac",    VSH_OT_STRING, 0, N_("MAC address")},
-    {"persistent", VSH_OT_ALIAS, 0, "config"},
-    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
+static const vshCmdOptDef opts_secret_set_value[] = {
+    {"secret", VSH_OT_DATA, VSH_OFLAG_REQ, N_("secret UUID")},
+    {"base64", VSH_OT_DATA, VSH_OFLAG_REQ, N_("base64-encoded secret value")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdDetachInterface(vshControl *ctl, const vshCmd *cmd)
+cmdSecretSetValue(vshControl *ctl, const vshCmd *cmd)
 {
-    virDomainPtr dom = NULL;
-    xmlDocPtr xml = NULL;
-    xmlXPathObjectPtr obj=NULL;
-    xmlXPathContextPtr ctxt = NULL;
-    xmlNodePtr cur = NULL;
-    xmlBufferPtr xml_buf = NULL;
-    const char *mac =NULL, *type = NULL;
-    char *doc;
-    char buf[64];
-    int i = 0, diff_mac;
-    int ret;
-    int functionReturn = false;
-    unsigned int flags;
+    virSecretPtr secret;
+    size_t value_size;
+    const char *base64 = NULL;
+    char *value;
+    int res;
+    bool ret = false;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
-        goto cleanup;
-
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
-        goto cleanup;
-
-    if (vshCommandOptString(cmd, "type", &type) <= 0)
-        goto cleanup;
-
-    if (vshCommandOptString(cmd, "mac", &mac) < 0) {
-        vshError(ctl, "%s", _("missing option"));
-        goto cleanup;
-    }
-
-    doc = virDomainGetXMLDesc(dom, 0);
-    if (!doc)
-        goto cleanup;
+        return false;
 
-    xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt);
-    VIR_FREE(doc);
-    if (!xml) {
-        vshError(ctl, "%s", _("Failed to get interface information"));
-        goto cleanup;
-    }
+    secret = vshCommandOptSecret(ctl, cmd, NULL);
+    if (secret == NULL)
+        return false;
 
-    snprintf(buf, sizeof(buf), "/domain/devices/interface[@type='%s']", type);
-    obj = xmlXPathEval(BAD_CAST buf, ctxt);
-    if (obj == NULL || obj->type != XPATH_NODESET ||
-        obj->nodesetval == NULL || obj->nodesetval->nodeNr == 0) {
-        vshError(ctl, _("No found interface whose type is %s"), type);
+    if (vshCommandOptString(cmd, "base64", &base64) <= 0)
         goto cleanup;
-    }
 
-    if (!mac && obj->nodesetval->nodeNr > 1) {
-        vshError(ctl, _("Domain has %d interfaces. Please specify which one "
-                        "to detach using --mac"), obj->nodesetval->nodeNr);
+    if (!base64_decode_alloc(base64, strlen(base64), &value, &value_size)) {
+        vshError(ctl, "%s", _("Invalid base64 data"));
         goto cleanup;
     }
-
-    if (!mac)
-        goto hit;
-
-    /* search mac */
-    for (; i < obj->nodesetval->nodeNr; i++) {
-        cur = obj->nodesetval->nodeTab[i]->children;
-        while (cur != NULL) {
-            if (cur->type == XML_ELEMENT_NODE &&
-                xmlStrEqual(cur->name, BAD_CAST "mac")) {
-                char *tmp_mac = virXMLPropString(cur, "address");
-                diff_mac = virMacAddrCompare(tmp_mac, mac);
-                VIR_FREE(tmp_mac);
-                if (!diff_mac) {
-                    goto hit;
-                }
-            }
-            cur = cur->next;
-        }
-    }
-    vshError(ctl, _("No found interface whose MAC address is %s"), mac);
-    goto cleanup;
-
- hit:
-    xml_buf = xmlBufferCreate();
-    if (!xml_buf) {
+    if (value == NULL) {
         vshError(ctl, "%s", _("Failed to allocate memory"));
-        goto cleanup;
-    }
-
-    if (xmlNodeDump(xml_buf, xml, obj->nodesetval->nodeTab[i], 0, 0) < 0) {
-        vshError(ctl, "%s", _("Failed to create XML"));
-        goto cleanup;
+        return false;
     }
 
-    if (vshCommandOptBool(cmd, "config")) {
-        flags = VIR_DOMAIN_AFFECT_CONFIG;
-        if (virDomainIsActive(dom) == 1)
-            flags |= VIR_DOMAIN_AFFECT_LIVE;
-        ret = virDomainDetachDeviceFlags(dom,
-                                         (char *)xmlBufferContent(xml_buf),
-                                         flags);
-    } else {
-        ret = virDomainDetachDevice(dom, (char *)xmlBufferContent(xml_buf));
-    }
+    res = virSecretSetValue(secret, (unsigned char *)value, value_size, 0);
+    memset(value, 0, value_size);
+    VIR_FREE(value);
 
-    if (ret != 0) {
-        vshError(ctl, "%s", _("Failed to detach interface"));
-    } else {
-        vshPrint(ctl, "%s", _("Interface detached successfully\n"));
-        functionReturn = true;
+    if (res != 0) {
+        vshError(ctl, "%s", _("Failed to set secret value"));
+        goto cleanup;
     }
+    vshPrint(ctl, "%s", _("Secret value set\n"));
+    ret = true;
 
- cleanup:
-    if (dom)
-        virDomainFree(dom);
-    xmlXPathFreeObject(obj);
-    xmlXPathFreeContext(ctxt);
-    xmlFreeDoc(xml);
-    xmlBufferFree(xml_buf);
-    return functionReturn;
+cleanup:
+    virSecretFree(secret);
+    return ret;
 }
 
 /*
- * "attach-disk" command
+ * "secret-get-value" command
  */
-static const vshCmdInfo info_attach_disk[] = {
-    {"help", N_("attach disk device")},
-    {"desc", N_("Attach new disk device.")},
+static const vshCmdInfo info_secret_get_value[] = {
+    {"help", N_("Output a secret value")},
+    {"desc", N_("Output a secret value to stdout.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_attach_disk[] = {
-    {"domain",  VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"source",  VSH_OT_DATA, VSH_OFLAG_REQ | VSH_OFLAG_EMPTY_OK,
-     N_("source of disk device")},
-    {"target",  VSH_OT_DATA, VSH_OFLAG_REQ, N_("target of disk device")},
-    {"driver",    VSH_OT_STRING, 0, N_("driver of disk device")},
-    {"subdriver", VSH_OT_STRING, 0, N_("subdriver of disk device")},
-    {"cache",     VSH_OT_STRING, 0, N_("cache mode of disk device")},
-    {"type",    VSH_OT_STRING, 0, N_("target device type")},
-    {"mode",    VSH_OT_STRING, 0, N_("mode of device reading and writing")},
-    {"persistent", VSH_OT_ALIAS, 0, "config"},
-    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
-    {"sourcetype", VSH_OT_STRING, 0, N_("type of source (block|file)")},
-    {"serial", VSH_OT_STRING, 0, N_("serial of disk device")},
-    {"shareable", VSH_OT_BOOL, 0, N_("shareable between domains")},
-    {"rawio", VSH_OT_BOOL, 0, N_("needs rawio capability")},
-    {"address", VSH_OT_STRING, 0, N_("address of disk device")},
-    {"multifunction", VSH_OT_BOOL, 0,
-     N_("use multifunction pci under specified address")},
+static const vshCmdOptDef opts_secret_get_value[] = {
+    {"secret", VSH_OT_DATA, VSH_OFLAG_REQ, N_("secret UUID")},
     {NULL, 0, 0, NULL}
 };
 
-enum {
-    DISK_ADDR_TYPE_INVALID,
-    DISK_ADDR_TYPE_PCI,
-    DISK_ADDR_TYPE_SCSI,
-    DISK_ADDR_TYPE_IDE,
-};
-
-struct PCIAddress {
-    unsigned int domain;
-    unsigned int bus;
-    unsigned int slot;
-    unsigned int function;
-};
-
-struct SCSIAddress {
-    unsigned int controller;
-    unsigned int bus;
-    unsigned int unit;
-};
-
-struct IDEAddress {
-    unsigned int controller;
-    unsigned int bus;
-    unsigned int unit;
-};
-
-struct DiskAddress {
-    int type;
-    union {
-        struct PCIAddress pci;
-        struct SCSIAddress scsi;
-        struct IDEAddress ide;
-    } addr;
-};
-
-static int str2PCIAddress(const char *str, struct PCIAddress *pciAddr)
-{
-    char *domain, *bus, *slot, *function;
-
-    if (!pciAddr)
-        return -1;
-    if (!str)
-        return -1;
-
-    domain = (char *)str;
-
-    if (virStrToLong_ui(domain, &bus, 0, &pciAddr->domain) != 0)
-        return -1;
-
-    bus++;
-    if (virStrToLong_ui(bus, &slot, 0, &pciAddr->bus) != 0)
-        return -1;
-
-    slot++;
-    if (virStrToLong_ui(slot, &function, 0, &pciAddr->slot) != 0)
-        return -1;
-
-    function++;
-    if (virStrToLong_ui(function, NULL, 0, &pciAddr->function) != 0)
-        return -1;
-
-    return 0;
-}
-
-static int str2SCSIAddress(const char *str, struct SCSIAddress *scsiAddr)
-{
-    char *controller, *bus, *unit;
-
-    if (!scsiAddr)
-        return -1;
-    if (!str)
-        return -1;
-
-    controller = (char *)str;
-
-    if (virStrToLong_ui(controller, &bus, 0, &scsiAddr->controller) != 0)
-        return -1;
-
-    bus++;
-    if (virStrToLong_ui(bus, &unit, 0, &scsiAddr->bus) != 0)
-        return -1;
-
-    unit++;
-    if (virStrToLong_ui(unit, NULL, 0, &scsiAddr->unit) != 0)
-        return -1;
-
-    return 0;
-}
-
-static int str2IDEAddress(const char *str, struct IDEAddress *ideAddr)
+static bool
+cmdSecretGetValue(vshControl *ctl, const vshCmd *cmd)
 {
-    char *controller, *bus, *unit;
+    virSecretPtr secret;
+    char *base64;
+    unsigned char *value;
+    size_t value_size;
+    bool ret = false;
 
-    if (!ideAddr)
-        return -1;
-    if (!str)
-        return -1;
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
 
-    controller = (char *)str;
+    secret = vshCommandOptSecret(ctl, cmd, NULL);
+    if (secret == NULL)
+        return false;
 
-    if (virStrToLong_ui(controller, &bus, 0, &ideAddr->controller) != 0)
-        return -1;
+    value = virSecretGetValue(secret, &value_size, 0);
+    if (value == NULL)
+        goto cleanup;
 
-    bus++;
-    if (virStrToLong_ui(bus, &unit, 0, &ideAddr->bus) != 0)
-        return -1;
+    base64_encode_alloc((char *)value, value_size, &base64);
+    memset(value, 0, value_size);
+    VIR_FREE(value);
 
-    unit++;
-    if (virStrToLong_ui(unit, NULL, 0, &ideAddr->unit) != 0)
-        return -1;
+    if (base64 == NULL) {
+        vshError(ctl, "%s", _("Failed to allocate memory"));
+        goto cleanup;
+    }
+    vshPrint(ctl, "%s", base64);
+    memset(base64, 0, strlen(base64));
+    VIR_FREE(base64);
+    ret = true;
 
-    return 0;
+cleanup:
+    virSecretFree(secret);
+    return ret;
 }
 
-/* pci address pci:0000.00.0x0a.0 (domain:bus:slot:function)
- * ide disk address: ide:00.00.0 (controller:bus:unit)
- * scsi disk address: scsi:00.00.0 (controller:bus:unit)
+/*
+ * "secret-undefine" command
  */
+static const vshCmdInfo info_secret_undefine[] = {
+    {"help", N_("undefine a secret")},
+    {"desc", N_("Undefine a secret.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_secret_undefine[] = {
+    {"secret", VSH_OT_DATA, VSH_OFLAG_REQ, N_("secret UUID")},
+    {NULL, 0, 0, NULL}
+};
 
-static int str2DiskAddress(const char *str, struct DiskAddress *diskAddr)
+static bool
+cmdSecretUndefine(vshControl *ctl, const vshCmd *cmd)
 {
-    char *type, *addr;
+    virSecretPtr secret;
+    bool ret = false;
+    const char *uuid;
 
-    if (!diskAddr)
-        return -1;
-    if (!str)
-        return -1;
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
 
-    type = (char *)str;
-    addr = strchr(type, ':');
-    if (!addr)
-        return -1;
+    secret = vshCommandOptSecret(ctl, cmd, &uuid);
+    if (secret == NULL)
+        return false;
 
-    if (STREQLEN(type, "pci", addr - type)) {
-        diskAddr->type = DISK_ADDR_TYPE_PCI;
-        return str2PCIAddress(addr + 1, &diskAddr->addr.pci);
-    } else if (STREQLEN(type, "scsi", addr - type)) {
-        diskAddr->type = DISK_ADDR_TYPE_SCSI;
-        return str2SCSIAddress(addr + 1, &diskAddr->addr.scsi);
-    } else if (STREQLEN(type, "ide", addr - type)) {
-        diskAddr->type = DISK_ADDR_TYPE_IDE;
-        return str2IDEAddress(addr + 1, &diskAddr->addr.ide);
+    if (virSecretUndefine(secret) < 0) {
+        vshError(ctl, _("Failed to delete secret %s"), uuid);
+        goto cleanup;
     }
+    vshPrint(ctl, _("Secret %s deleted\n"), uuid);
+    ret = true;
 
-    return -1;
+cleanup:
+    virSecretFree(secret);
+    return ret;
 }
 
+/*
+ * "secret-list" command
+ */
+static const vshCmdInfo info_secret_list[] = {
+    {"help", N_("list secrets")},
+    {"desc", N_("Returns a list of secrets")},
+    {NULL, NULL}
+};
+
 static bool
-cmdAttachDisk(vshControl *ctl, const vshCmd *cmd)
+cmdSecretList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
 {
-    virDomainPtr dom = NULL;
-    const char *source = NULL, *target = NULL, *driver = NULL,
-                *subdriver = NULL, *type = NULL, *mode = NULL,
-                *cache = NULL, *serial = NULL, *straddr = NULL;
-    struct DiskAddress diskAddr;
-    bool isFile = false, functionReturn = false;
-    int ret;
-    unsigned int flags;
-    const char *stype = NULL;
-    virBuffer buf = VIR_BUFFER_INITIALIZER;
-    char *xml;
-    struct stat st;
+    int maxuuids = 0, i;
+    char **uuids = NULL;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
-        goto cleanup;
-
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
-        goto cleanup;
-
-    if (vshCommandOptString(cmd, "source", &source) <= 0)
-        goto cleanup;
-    /* Allow empty string as a placeholder that implies no source, for
-     * use in adding a cdrom drive with no disk.  */
-    if (!*source)
-        source = NULL;
-
-    if (vshCommandOptString(cmd, "target", &target) <= 0)
-        goto cleanup;
+        return false;
 
-    if (vshCommandOptString(cmd, "driver", &driver) < 0 ||
-        vshCommandOptString(cmd, "subdriver", &subdriver) < 0 ||
-        vshCommandOptString(cmd, "type", &type) < 0 ||
-        vshCommandOptString(cmd, "mode", &mode) < 0 ||
-        vshCommandOptString(cmd, "cache", &cache) < 0 ||
-        vshCommandOptString(cmd, "serial", &serial) < 0 ||
-        vshCommandOptString(cmd, "address", &straddr) < 0 ||
-        vshCommandOptString(cmd, "sourcetype", &stype) < 0) {
-        vshError(ctl, "%s", _("missing option"));
-        goto cleanup;
+    maxuuids = virConnectNumOfSecrets(ctl->conn);
+    if (maxuuids < 0) {
+        vshError(ctl, "%s", _("Failed to list secrets"));
+        return false;
     }
+    uuids = vshMalloc(ctl, sizeof(*uuids) * maxuuids);
 
-    if (!stype) {
-        if (driver && (STREQ(driver, "file") || STREQ(driver, "tap"))) {
-            isFile = true;
-        } else {
-            if (source && !stat(source, &st))
-                isFile = S_ISREG(st.st_mode) ? true : false;
-        }
-    } else if (STREQ(stype, "file")) {
-        isFile = true;
-    } else if (STRNEQ(stype, "block")) {
-        vshError(ctl, _("Unknown source type: '%s'"), stype);
-        goto cleanup;
+    maxuuids = virConnectListSecrets(ctl->conn, uuids, maxuuids);
+    if (maxuuids < 0) {
+        vshError(ctl, "%s", _("Failed to list secrets"));
+        VIR_FREE(uuids);
+        return false;
     }
 
-    if (mode) {
-        if (STRNEQ(mode, "readonly") && STRNEQ(mode, "shareable")) {
-            vshError(ctl, _("No support for %s in command 'attach-disk'"),
-                     mode);
-            goto cleanup;
-        }
-    }
+    qsort(uuids, maxuuids, sizeof(char *), vshNameSorter);
 
-    /* Make XML of disk */
-    virBufferAsprintf(&buf, "<disk type='%s'",
-                      (isFile) ? "file" : "block");
-    if (type)
-        virBufferAsprintf(&buf, " device='%s'", type);
-    if (vshCommandOptBool(cmd, "rawio"))
-        virBufferAddLit(&buf, " rawio='yes'");
-    virBufferAddLit(&buf, ">\n");
+    vshPrintExtra(ctl, "%-36s %s\n", _("UUID"), _("Usage"));
+    vshPrintExtra(ctl, "-----------------------------------------------------------\n");
 
-    if (driver || subdriver || cache) {
-        virBufferAsprintf(&buf, "  <driver");
+    for (i = 0; i < maxuuids; i++) {
+        virSecretPtr sec = virSecretLookupByUUIDString(ctl->conn, uuids[i]);
+        const char *usageType = NULL;
 
-        if (driver)
-            virBufferAsprintf(&buf, " name='%s'", driver);
-        if (subdriver)
-            virBufferAsprintf(&buf, " type='%s'", subdriver);
-        if (cache)
-            virBufferAsprintf(&buf, " cache='%s'", cache);
+        if (!sec) {
+            VIR_FREE(uuids[i]);
+            continue;
+        }
 
-        virBufferAddLit(&buf, "/>\n");
+        switch (virSecretGetUsageType(sec)) {
+        case VIR_SECRET_USAGE_TYPE_VOLUME:
+            usageType = _("Volume");
+            break;
+        }
+
+        if (usageType) {
+            vshPrint(ctl, "%-36s %s %s\n",
+                     uuids[i], usageType,
+                     virSecretGetUsageID(sec));
+        } else {
+            vshPrint(ctl, "%-36s %s\n",
+                     uuids[i], _("Unused"));
+        }
+        virSecretFree(sec);
+        VIR_FREE(uuids[i]);
     }
+    VIR_FREE(uuids);
+    return true;
+}
 
-    if (source)
-        virBufferAsprintf(&buf, "  <source %s='%s'/>\n",
-                          (isFile) ? "file" : "dev",
-                          source);
-    virBufferAsprintf(&buf, "  <target dev='%s'/>\n", target);
-    if (mode)
-        virBufferAsprintf(&buf, "  <%s/>\n", mode);
 
-    if (serial)
-        virBufferAsprintf(&buf, "  <serial>%s</serial>\n", serial);
+/*
+ * "version" command
+ */
+static const vshCmdInfo info_version[] = {
+    {"help", N_("show version")},
+    {"desc", N_("Display the system version information.")},
+    {NULL, NULL}
+};
 
-    if (vshCommandOptBool(cmd, "shareable"))
-        virBufferAsprintf(&buf, "  <shareable/>\n");
+static const vshCmdOptDef opts_version[] = {
+    {"daemon", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("report daemon version too")},
+    {NULL, 0, 0, NULL}
+};
 
-    if (straddr) {
-        if (str2DiskAddress(straddr, &diskAddr) != 0) {
-            vshError(ctl, _("Invalid address."));
-            goto cleanup;
-        }
+static bool
+cmdVersion(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
+{
+    unsigned long hvVersion;
+    const char *hvType;
+    unsigned long libVersion;
+    unsigned long includeVersion;
+    unsigned long apiVersion;
+    unsigned long daemonVersion;
+    int ret;
+    unsigned int major;
+    unsigned int minor;
+    unsigned int rel;
 
-        if (STRPREFIX((const char *)target, "vd")) {
-            if (diskAddr.type == DISK_ADDR_TYPE_PCI) {
-                virBufferAsprintf(&buf,
-                                  "  <address type='pci' domain='0x%04x'"
-                                  " bus ='0x%02x' slot='0x%02x' function='0x%0x'",
-                                  diskAddr.addr.pci.domain, diskAddr.addr.pci.bus,
-                                  diskAddr.addr.pci.slot, diskAddr.addr.pci.function);
-                if (vshCommandOptBool(cmd, "multifunction"))
-                    virBufferAddLit(&buf, " multifunction='on'");
-                virBufferAddLit(&buf, "/>\n");
-            } else {
-                vshError(ctl, "%s", _("expecting a pci:0000.00.00.00 address."));
-                goto cleanup;
-            }
-        } else if (STRPREFIX((const char *)target, "sd")) {
-            if (diskAddr.type == DISK_ADDR_TYPE_SCSI) {
-                virBufferAsprintf(&buf,
-                                  "  <address type='drive' controller='%d'"
-                                  " bus='%d' unit='%d' />\n",
-                                  diskAddr.addr.scsi.controller, diskAddr.addr.scsi.bus,
-                                  diskAddr.addr.scsi.unit);
-            } else {
-                vshError(ctl, "%s", _("expecting a scsi:00.00.00 address."));
-                goto cleanup;
-            }
-        } else if (STRPREFIX((const char *)target, "hd")) {
-            if (diskAddr.type == DISK_ADDR_TYPE_IDE) {
-                virBufferAsprintf(&buf,
-                                  "  <address type='drive' controller='%d'"
-                                  " bus='%d' unit='%d' />\n",
-                                  diskAddr.addr.ide.controller, diskAddr.addr.ide.bus,
-                                  diskAddr.addr.ide.unit);
-            } else {
-                vshError(ctl, "%s", _("expecting an ide:00.00.00 address."));
-                goto cleanup;
-            }
-        }
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+
+    hvType = virConnectGetType(ctl->conn);
+    if (hvType == NULL) {
+        vshError(ctl, "%s", _("failed to get hypervisor type"));
+        return false;
     }
 
-    virBufferAddLit(&buf, "</disk>\n");
+    includeVersion = LIBVIR_VERSION_NUMBER;
+    major = includeVersion / 1000000;
+    includeVersion %= 1000000;
+    minor = includeVersion / 1000;
+    rel = includeVersion % 1000;
+    vshPrint(ctl, _("Compiled against library: libvir %d.%d.%d\n"),
+             major, minor, rel);
 
-    if (virBufferError(&buf)) {
-        vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
+    ret = virGetVersion(&libVersion, hvType, &apiVersion);
+    if (ret < 0) {
+        vshError(ctl, "%s", _("failed to get the library version"));
         return false;
     }
+    major = libVersion / 1000000;
+    libVersion %= 1000000;
+    minor = libVersion / 1000;
+    rel = libVersion % 1000;
+    vshPrint(ctl, _("Using library: libvir %d.%d.%d\n"),
+             major, minor, rel);
 
-    xml = virBufferContentAndReset(&buf);
+    major = apiVersion / 1000000;
+    apiVersion %= 1000000;
+    minor = apiVersion / 1000;
+    rel = apiVersion % 1000;
+    vshPrint(ctl, _("Using API: %s %d.%d.%d\n"), hvType,
+             major, minor, rel);
 
-    if (vshCommandOptBool(cmd, "config")) {
-        flags = VIR_DOMAIN_AFFECT_CONFIG;
-        if (virDomainIsActive(dom) == 1)
-            flags |= VIR_DOMAIN_AFFECT_LIVE;
-        ret = virDomainAttachDeviceFlags(dom, xml, flags);
-    } else {
-        ret = virDomainAttachDevice(dom, xml);
+    ret = virConnectGetVersion(ctl->conn, &hvVersion);
+    if (ret < 0) {
+        vshError(ctl, "%s", _("failed to get the hypervisor version"));
+        return false;
     }
+    if (hvVersion == 0) {
+        vshPrint(ctl,
+                 _("Cannot extract running %s hypervisor version\n"), hvType);
+    } else {
+        major = hvVersion / 1000000;
+        hvVersion %= 1000000;
+        minor = hvVersion / 1000;
+        rel = hvVersion % 1000;
 
-    VIR_FREE(xml);
+        vshPrint(ctl, _("Running hypervisor: %s %d.%d.%d\n"),
+                 hvType, major, minor, rel);
+    }
 
-    if (ret != 0) {
-        vshError(ctl, "%s", _("Failed to attach disk"));
-    } else {
-        vshPrint(ctl, "%s", _("Disk attached successfully\n"));
-        functionReturn = true;
+    if (vshCommandOptBool(cmd, "daemon")) {
+        ret = virConnectGetLibVersion(ctl->conn, &daemonVersion);
+        if (ret < 0) {
+            vshError(ctl, "%s", _("failed to get the daemon version"));
+        } else {
+            major = daemonVersion / 1000000;
+            daemonVersion %= 1000000;
+            minor = daemonVersion / 1000;
+            rel = daemonVersion % 1000;
+            vshPrint(ctl, _("Running against daemon: %d.%d.%d\n"),
+                     major, minor, rel);
+        }
     }
 
- cleanup:
-    if (dom)
-        virDomainFree(dom);
-    virBufferFreeAndReset(&buf);
-    return functionReturn;
+    return true;
 }
 
-typedef enum {
-    VSH_FIND_DISK_NORMAL,
-    VSH_FIND_DISK_CHANGEABLE,
-} vshFindDiskType;
-
-/* Helper function to find disk device in XML doc.  Returns the disk
- * node on success, or NULL on failure. Caller must free the result
- * @path: Fully-qualified path or target of disk device.
- * @type: Either VSH_FIND_DISK_NORMAL or VSH_FIND_DISK_CHANGEABLE.
- */
-static xmlNodePtr
-vshFindDisk(const char *doc,
-            const char *path,
-            int type)
+/* Tree listing helpers.  */
+
+/* Given an index, return either the name of that device (non-NULL) or
+ * of its parent (NULL if a root).  */
+typedef const char * (*vshTreeLookup)(int devid, bool parent, void *opaque);
+
+static int
+vshTreePrintInternal(vshControl *ctl,
+                     vshTreeLookup lookup,
+                     void *opaque,
+                     int num_devices,
+                     int devid,
+                     int lastdev,
+                     bool root,
+                     virBufferPtr indent)
 {
-    xmlDocPtr xml = NULL;
-    xmlXPathObjectPtr obj= NULL;
-    xmlXPathContextPtr ctxt = NULL;
-    xmlNodePtr cur = NULL;
-    xmlNodePtr ret = NULL;
-    int i = 0;
+    int i;
+    int nextlastdev = -1;
+    int ret = -1;
+    const char *dev = (lookup)(devid, false, opaque);
 
-    xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt);
-    if (!xml) {
-        vshError(NULL, "%s", _("Failed to get disk information"));
+    if (virBufferError(indent))
         goto cleanup;
-    }
 
-    obj = xmlXPathEval(BAD_CAST "/domain/devices/disk", ctxt);
-    if (obj == NULL ||
-        obj->type != XPATH_NODESET ||
-        obj->nodesetval == NULL ||
-        obj->nodesetval->nodeNr == 0) {
-        vshError(NULL, "%s", _("Failed to get disk information"));
-        goto cleanup;
+    /* Print this device, with indent if not at root */
+    vshPrint(ctl, "%s%s%s\n", virBufferCurrentContent(indent),
+             root ? "" : "+- ", dev);
+
+    /* Update indent to show '|' or ' ' for child devices */
+    if (!root) {
+        virBufferAddChar(indent, devid == lastdev ? ' ' : '|');
+        virBufferAddChar(indent, ' ');
+        if (virBufferError(indent))
+            goto cleanup;
     }
 
-    /* search disk using @path */
-    for (; i < obj->nodesetval->nodeNr; i++) {
-        bool is_supported = true;
-
-        if (type == VSH_FIND_DISK_CHANGEABLE) {
-            xmlNodePtr n = obj->nodesetval->nodeTab[i];
-            is_supported = false;
-
-            /* Check if the disk is CDROM or floppy disk */
-            if (xmlStrEqual(n->name, BAD_CAST "disk")) {
-                char *device_value = virXMLPropString(n, "device");
-
-                if (STREQ(device_value, "cdrom") ||
-                    STREQ(device_value, "floppy"))
-                    is_supported = true;
-
-                VIR_FREE(device_value);
-            }
+    /* Determine the index of the last child device */
+    for (i = 0 ; i < num_devices ; i++) {
+        const char *parent = (lookup)(i, true, opaque);
 
-            if (!is_supported)
-                continue;
-        }
+        if (parent && STREQ(parent, dev))
+            nextlastdev = i;
+    }
 
-        cur = obj->nodesetval->nodeTab[i]->children;
-        while (cur != NULL) {
-            if (cur->type == XML_ELEMENT_NODE) {
-                char *tmp = NULL;
+    /* If there is a child device, then print another blank line */
+    if (nextlastdev != -1)
+        vshPrint(ctl, "%s  |\n", virBufferCurrentContent(indent));
 
-                if (xmlStrEqual(cur->name, BAD_CAST "source")) {
-                    if ((tmp = virXMLPropString(cur, "file")) ||
-                        (tmp = virXMLPropString(cur, "dev")) ||
-                        (tmp = virXMLPropString(cur, "dir")) ||
-                        (tmp = virXMLPropString(cur, "name"))) {
-                    }
-                } else if (xmlStrEqual(cur->name, BAD_CAST "target")) {
-                    tmp = virXMLPropString(cur, "dev");
-                }
+    /* Finally print all children */
+    virBufferAddLit(indent, "  ");
+    for (i = 0 ; i < num_devices ; i++) {
+        const char *parent = (lookup)(i, true, opaque);
 
-                if (STREQ_NULLABLE(tmp, path)) {
-                    ret = xmlCopyNode(obj->nodesetval->nodeTab[i], 1);
-                    VIR_FREE(tmp);
-                    goto cleanup;
-                }
-                VIR_FREE(tmp);
-            }
-            cur = cur->next;
-        }
+        if (parent && STREQ(parent, dev) &&
+            vshTreePrintInternal(ctl, lookup, opaque,
+                                 num_devices, i, nextlastdev,
+                                 false, indent) < 0)
+            goto cleanup;
     }
+    virBufferTrim(indent, "  ", -1);
 
-    vshError(NULL, _("No found disk whose source path or target is %s"), path);
+    /* If there was no child device, and we're the last in
+     * a list of devices, then print another blank line */
+    if (nextlastdev == -1 && devid == lastdev)
+        vshPrint(ctl, "%s\n", virBufferCurrentContent(indent));
 
+    if (!root)
+        virBufferTrim(indent, NULL, 2);
+    ret = 0;
 cleanup:
-    xmlXPathFreeObject(obj);
-    xmlXPathFreeContext(ctxt);
-    xmlFreeDoc(xml);
     return ret;
 }
 
-typedef enum {
-    VSH_PREPARE_DISK_XML_NONE = 0,
-    VSH_PREPARE_DISK_XML_EJECT,
-    VSH_PREPARE_DISK_XML_INSERT,
-    VSH_PREPARE_DISK_XML_UPDATE,
-} vshPrepareDiskXMLType;
-
-/* Helper function to prepare disk XML. Could be used for disk
- * detaching, media changing(ejecting, inserting, updating)
- * for changeable disk. Returns the processed XML as string on
- * success, or NULL on failure. Caller must free the result.
- */
-static char *
-vshPrepareDiskXML(xmlNodePtr disk_node,
-                  const char *source,
-                  const char *path,
-                  int type)
+static int
+vshTreePrint(vshControl *ctl, vshTreeLookup lookup, void *opaque,
+             int num_devices, int devid)
 {
-    xmlNodePtr cur = NULL;
-    xmlBufferPtr xml_buf = NULL;
-    const char *disk_type = NULL;
-    const char *device_type = NULL;
-    xmlNodePtr new_node = NULL;
-    char *ret = NULL;
-
-    if (!disk_node)
-        return NULL;
-
-    xml_buf = xmlBufferCreate();
-    if (!xml_buf) {
-        vshError(NULL, "%s", _("Failed to allocate memory"));
-        return NULL;
-    }
-
-    device_type = virXMLPropString(disk_node, "device");
-
-    if (STREQ_NULLABLE(device_type, "cdrom") ||
-        STREQ_NULLABLE(device_type, "floppy")) {
-        bool has_source = false;
-        disk_type = virXMLPropString(disk_node, "type");
-
-        cur = disk_node->children;
-        while (cur != NULL) {
-            if (cur->type == XML_ELEMENT_NODE &&
-                xmlStrEqual(cur->name, BAD_CAST "source")) {
-                has_source = true;
-                break;
-            }
-            cur = cur->next;
-        }
-
-        if (!has_source) {
-            if (type == VSH_PREPARE_DISK_XML_EJECT) {
-                vshError(NULL, _("The disk device '%s' doesn't have media"),
-                         path);
-                goto error;
-            }
-
-            if (source) {
-                new_node = xmlNewNode(NULL, BAD_CAST "source");
-                xmlNewProp(new_node, (const xmlChar *)disk_type,
-                           (const xmlChar *)source);
-                xmlAddChild(disk_node, new_node);
-            } else if (type == VSH_PREPARE_DISK_XML_INSERT) {
-                vshError(NULL, _("No source is specified for inserting media"));
-                goto error;
-            } else if (type == VSH_PREPARE_DISK_XML_UPDATE) {
-                vshError(NULL, _("No source is specified for updating media"));
-                goto error;
-            }
-        }
-
-        if (has_source) {
-            if (type == VSH_PREPARE_DISK_XML_INSERT) {
-                vshError(NULL, _("The disk device '%s' already has media"),
-                         path);
-                goto error;
-            }
-
-            /* Remove the source if it tends to eject/update media. */
-            xmlUnlinkNode(cur);
-            xmlFreeNode(cur);
-
-            if (source && (type == VSH_PREPARE_DISK_XML_UPDATE)) {
-                new_node = xmlNewNode(NULL, BAD_CAST "source");
-                xmlNewProp(new_node, (const xmlChar *)disk_type,
-                           (const xmlChar *)source);
-                xmlAddChild(disk_node, new_node);
-            }
-        }
-    }
-
-    if (xmlNodeDump(xml_buf, NULL, disk_node, 0, 0) < 0) {
-        vshError(NULL, "%s", _("Failed to create XML"));
-        goto error;
-    }
-
-    goto cleanup;
+    int ret;
+    virBuffer indent = VIR_BUFFER_INITIALIZER;
 
-cleanup:
-    VIR_FREE(device_type);
-    VIR_FREE(disk_type);
-    if (xml_buf) {
-        int len = xmlBufferLength(xml_buf);
-        if (VIR_ALLOC_N(ret, len + 1) < 0) {
-            virReportOOMError();
-            return NULL;
-        }
-        memcpy(ret, (char *)xmlBufferContent(xml_buf), len);
-        ret[len] = '\0';
-        xmlBufferFree(xml_buf);
-    }
+    ret = vshTreePrintInternal(ctl, lookup, opaque, num_devices,
+                               devid, devid, true, &indent);
+    if (ret < 0)
+        vshError(ctl, "%s", _("Failed to complete tree listing"));
+    virBufferFreeAndReset(&indent);
     return ret;
+}
 
-error:
-    xmlBufferFree(xml_buf);
-    xml_buf = NULL;
-    goto cleanup;
+struct vshNodeList {
+    char **names;
+    char **parents;
+};
+
+static const char *
+vshNodeListLookup(int devid, bool parent, void *opaque)
+{
+    struct vshNodeList *arrays = opaque;
+    if (parent)
+        return arrays->parents[devid];
+    return arrays->names[devid];
 }
 
 /*
- * "detach-disk" command
+ * "nodedev-list" command
  */
-static const vshCmdInfo info_detach_disk[] = {
-    {"help", N_("detach disk device")},
-    {"desc", N_("Detach disk device.")},
+static const vshCmdInfo info_node_list_devices[] = {
+    {"help", N_("enumerate devices on this host")},
+    {"desc", ""},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_detach_disk[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"target", VSH_OT_DATA, VSH_OFLAG_REQ, N_("target of disk device")},
-    {"persistent", VSH_OT_ALIAS, 0, "config"},
-    {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
+static const vshCmdOptDef opts_node_list_devices[] = {
+    {"tree", VSH_OT_BOOL, 0, N_("list devices in a tree")},
+    {"cap", VSH_OT_STRING, VSH_OFLAG_NONE, N_("capability name")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdDetachDisk(vshControl *ctl, const vshCmd *cmd)
+cmdNodeListDevices(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
 {
-    char *disk_xml = NULL;
-    virDomainPtr dom = NULL;
-    const char *target = NULL;
-    char *doc = NULL;
-    int ret;
-    bool functionReturn = false;
-    unsigned int flags;
-    xmlNodePtr disk_node = NULL;
+    const char *cap = NULL;
+    char **devices;
+    int num_devices, i;
+    bool tree = vshCommandOptBool(cmd, "tree");
+    bool ret = true;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
-        goto cleanup;
-
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
-        goto cleanup;
-
-    if (vshCommandOptString(cmd, "target", &target) <= 0)
-        goto cleanup;
-
-    doc = virDomainGetXMLDesc(dom, 0);
-    if (!doc)
-        goto cleanup;
+        return false;
 
-    if (!(disk_node = vshFindDisk(doc, target, VSH_FIND_DISK_NORMAL)))
-        goto cleanup;
+    if (vshCommandOptString(cmd, "cap", &cap) <= 0)
+        cap = NULL;
 
-    if (!(disk_xml = vshPrepareDiskXML(disk_node, NULL, NULL,
-                                       VSH_PREPARE_DISK_XML_NONE)))
-        goto cleanup;
+    num_devices = virNodeNumOfDevices(ctl->conn, cap, 0);
+    if (num_devices < 0) {
+        vshError(ctl, "%s", _("Failed to count node devices"));
+        return false;
+    } else if (num_devices == 0) {
+        return true;
+    }
 
-    if (vshCommandOptBool(cmd, "config")) {
-        flags = VIR_DOMAIN_AFFECT_CONFIG;
-        if (virDomainIsActive(dom) == 1)
-            flags |= VIR_DOMAIN_AFFECT_LIVE;
-        ret = virDomainDetachDeviceFlags(dom,
-                                         disk_xml,
-                                         flags);
-    } else {
-        ret = virDomainDetachDevice(dom, disk_xml);
+    devices = vshMalloc(ctl, sizeof(char *) * num_devices);
+    num_devices =
+        virNodeListDevices(ctl->conn, cap, devices, num_devices, 0);
+    if (num_devices < 0) {
+        vshError(ctl, "%s", _("Failed to list node devices"));
+        VIR_FREE(devices);
+        return false;
     }
+    qsort(&devices[0], num_devices, sizeof(char*), vshNameSorter);
+    if (tree) {
+        char **parents = vshMalloc(ctl, sizeof(char *) * num_devices);
+        struct vshNodeList arrays = { devices, parents };
 
-    if (ret != 0) {
-        vshError(ctl, "%s", _("Failed to detach disk"));
+        for (i = 0; i < num_devices; i++) {
+            virNodeDevicePtr dev = virNodeDeviceLookupByName(ctl->conn, devices[i]);
+            if (dev && STRNEQ(devices[i], "computer")) {
+                const char *parent = virNodeDeviceGetParent(dev);
+                parents[i] = parent ? vshStrdup(ctl, parent) : NULL;
+            } else {
+                parents[i] = NULL;
+            }
+            virNodeDeviceFree(dev);
+        }
+        for (i = 0 ; i < num_devices ; i++) {
+            if (parents[i] == NULL &&
+                vshTreePrint(ctl, vshNodeListLookup, &arrays, num_devices,
+                             i) < 0)
+                ret = false;
+        }
+        for (i = 0 ; i < num_devices ; i++) {
+            VIR_FREE(devices[i]);
+            VIR_FREE(parents[i]);
+        }
+        VIR_FREE(parents);
     } else {
-        vshPrint(ctl, "%s", _("Disk detached successfully\n"));
-        functionReturn = true;
+        for (i = 0; i < num_devices; i++) {
+            vshPrint(ctl, "%s\n", devices[i]);
+            VIR_FREE(devices[i]);
+        }
     }
-
- cleanup:
-    xmlFreeNode(disk_node);
-    VIR_FREE(disk_xml);
-    VIR_FREE(doc);
-    if (dom)
-        virDomainFree(dom);
-    return functionReturn;
+    VIR_FREE(devices);
+    return ret;
 }
 
 /*
- * "change-media" command
+ * "nodedev-dumpxml" command
  */
-static const vshCmdInfo info_change_media[] = {
-    {"help", N_("Change media of CD or floppy drive")},
-    {"desc", N_("Change media of CD or floppy drive.")},
+static const vshCmdInfo info_node_device_dumpxml[] = {
+    {"help", N_("node device details in XML")},
+    {"desc", N_("Output the node device details as an XML dump to stdout.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_change_media[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {"path", VSH_OT_DATA, VSH_OFLAG_REQ, N_("Fully-qualified path or "
-                                            "target of disk device")},
-    {"source", VSH_OT_DATA, 0, N_("source of the media")},
-    {"eject", VSH_OT_BOOL, 0, N_("Eject the media")},
-    {"insert", VSH_OT_BOOL, 0, N_("Insert the media")},
-    {"update", VSH_OT_BOOL, 0, N_("Update the media")},
-    {"current", VSH_OT_BOOL, 0, N_("can be either or both of --live and --config, "
-                                   "depends on implementation of hypervisor driver")},
-    {"live", VSH_OT_BOOL, 0, N_("alter live configuration of running domain")},
-    {"config", VSH_OT_BOOL, 0, N_("alter persistent configuration, effect observed on next boot")},
-    {"force",  VSH_OT_BOOL, 0, N_("force media insertion")},
+
+static const vshCmdOptDef opts_node_device_dumpxml[] = {
+    {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("device key")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdChangeMedia(vshControl *ctl, const vshCmd *cmd)
+cmdNodeDeviceDumpXML(vshControl *ctl, const vshCmd *cmd)
 {
-    virDomainPtr dom = NULL;
-    const char *source = NULL;
-    const char *path = NULL;
-    const char *doc = NULL;
-    xmlNodePtr disk_node = NULL;
-    const char *disk_xml = NULL;
-    int flags = 0;
-    bool config, live, current, force = false;
-    bool eject, insert, update = false;
-    bool ret = false;
-    int prepare_type = 0;
-    const char *action = NULL;
-
-    config = vshCommandOptBool(cmd, "config");
-    live = vshCommandOptBool(cmd, "live");
-    current = vshCommandOptBool(cmd, "current");
-    force = vshCommandOptBool(cmd, "force");
-    eject = vshCommandOptBool(cmd, "eject");
-    insert = vshCommandOptBool(cmd, "insert");
-    update = vshCommandOptBool(cmd, "update");
-
-    if (eject + insert + update > 1) {
-        vshError(ctl, "%s", _("--eject, --insert, and --update must be specified "
-                            "exclusively."));
-        return false;
-    }
-
-    if (eject) {
-        prepare_type = VSH_PREPARE_DISK_XML_EJECT;
-        action = "eject";
-    }
-
-    if (insert) {
-        prepare_type = VSH_PREPARE_DISK_XML_INSERT;
-        action = "insert";
-    }
+    const char *name = NULL;
+    virNodeDevicePtr device;
+    char *xml;
 
-    if (update || (!eject && !insert)) {
-        prepare_type = VSH_PREPARE_DISK_XML_UPDATE;
-        action = "update";
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+    if (vshCommandOptString(cmd, "device", &name) <= 0)
+        return false;
+    if (!(device = virNodeDeviceLookupByName(ctl->conn, name))) {
+        vshError(ctl, "%s '%s'", _("Could not find matching device"), name);
+        return false;
     }
 
-    if (current) {
-        if (live || config) {
-            vshError(ctl, "%s", _("--current must be specified exclusively"));
-            return false;
-        }
-        flags = VIR_DOMAIN_AFFECT_CURRENT;
-    } else {
-        if (config)
-            flags |= VIR_DOMAIN_AFFECT_CONFIG;
-        if (live)
-            flags |= VIR_DOMAIN_AFFECT_LIVE;
+    xml = virNodeDeviceGetXMLDesc(device, 0);
+    if (!xml) {
+        virNodeDeviceFree(device);
+        return false;
     }
 
-    if (force)
-        flags |= VIR_DOMAIN_DEVICE_MODIFY_FORCE;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        goto cleanup;
-
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
-        goto cleanup;
-
-    if (vshCommandOptString(cmd, "path", &path) <= 0)
-        goto cleanup;
-
-    if (vshCommandOptString(cmd, "source", &source) < 0)
-        goto cleanup;
+    vshPrint(ctl, "%s\n", xml);
+    VIR_FREE(xml);
+    virNodeDeviceFree(device);
+    return true;
+}
 
-    if (insert && !source) {
-        vshError(ctl, "%s", _("No disk source specified for inserting"));
-        goto cleanup;
-    }
+/*
+ * "nodedev-detach" command
+ */
+static const vshCmdInfo info_node_device_detach[] = {
+    {"help", N_("detach node device from its device driver")},
+    {"desc", N_("Detach node device from its device driver before assigning to a domain.")},
+    {NULL, NULL}
+};
 
-    if (flags & VIR_DOMAIN_AFFECT_CONFIG)
-        doc = virDomainGetXMLDesc(dom, VIR_DOMAIN_XML_INACTIVE);
-    else
-        doc = virDomainGetXMLDesc(dom, 0);
-    if (!doc)
-        goto cleanup;
 
-    if (!(disk_node = vshFindDisk(doc, path, VSH_FIND_DISK_CHANGEABLE)))
-        goto cleanup;
+static const vshCmdOptDef opts_node_device_detach[] = {
+    {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("device key")},
+    {NULL, 0, 0, NULL}
+};
 
-    if (!(disk_xml = vshPrepareDiskXML(disk_node, source, path, prepare_type)))
-        goto cleanup;
+static bool
+cmdNodeDeviceDetach(vshControl *ctl, const vshCmd *cmd)
+{
+    const char *name = NULL;
+    virNodeDevicePtr device;
+    bool ret = true;
 
-    if (virDomainUpdateDeviceFlags(dom, disk_xml, flags) != 0) {
-        vshError(ctl, _("Failed to complete action %s on media"), action);
-        goto cleanup;
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+    if (vshCommandOptString(cmd, "device", &name) <= 0)
+        return false;
+    if (!(device = virNodeDeviceLookupByName(ctl->conn, name))) {
+        vshError(ctl, "%s '%s'", _("Could not find matching device"), name);
+        return false;
     }
 
-    vshPrint(ctl, _("succeeded to complete action %s on media\n"), action);
-    ret = true;
-
-cleanup:
-    VIR_FREE(doc);
-    xmlFreeNode(disk_node);
-    VIR_FREE(disk_xml);
-    if (dom)
-        virDomainFree(dom);
+    /* Yes, our public API is misspelled.  At least virsh can accept
+     * either spelling.  */
+    if (virNodeDeviceDettach(device) == 0) {
+        vshPrint(ctl, _("Device %s detached\n"), name);
+    } else {
+        vshError(ctl, _("Failed to detach device %s"), name);
+        ret = false;
+    }
+    virNodeDeviceFree(device);
     return ret;
 }
 
 /*
- * "cpu-compare" command
+ * "nodedev-reattach" command
  */
-static const vshCmdInfo info_cpu_compare[] = {
-    {"help", N_("compare host CPU with a CPU described by an XML file")},
-    {"desc", N_("compare CPU with host CPU")},
+static const vshCmdInfo info_node_device_reattach[] = {
+    {"help", N_("reattach node device to its device driver")},
+    {"desc", N_("Reattach node device to its device driver once released by the domain.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_cpu_compare[] = {
-    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML CPU description")},
+
+static const vshCmdOptDef opts_node_device_reattach[] = {
+    {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("device key")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdCPUCompare(vshControl *ctl, const vshCmd *cmd)
+cmdNodeDeviceReAttach(vshControl *ctl, const vshCmd *cmd)
 {
-    const char *from = NULL;
-    bool ret = false;
-    char *buffer;
-    int result;
-    const char *snippet;
-
-    xmlDocPtr xml = NULL;
-    xmlXPathContextPtr ctxt = NULL;
-    xmlBufferPtr xml_buf = NULL;
-    xmlNodePtr node;
+    const char *name = NULL;
+    virNodeDevicePtr device;
+    bool ret = true;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
-
-    if (vshCommandOptString(cmd, "file", &from) <= 0)
+    if (vshCommandOptString(cmd, "device", &name) <= 0)
         return false;
-
-    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
-        vshError(ctl, _("Failed to read file '%s' to compare"),
-                 from);
+    if (!(device = virNodeDeviceLookupByName(ctl->conn, name))) {
+        vshError(ctl, "%s '%s'", _("Could not find matching device"), name);
         return false;
     }
 
-    /* try to extract the CPU element from as it would appear in a domain XML*/
-    if (!(xml = virXMLParseStringCtxt(buffer, from, &ctxt)))
-        goto cleanup;
-
-    if ((node = virXPathNode("/cpu|"
-                             "/domain/cpu|"
-                              "/capabilities/host/cpu", ctxt))) {
-        if (!(xml_buf = xmlBufferCreate())) {
-            vshError(ctl, _("Can't create XML buffer to extract CPU element."));
-            goto cleanup;
-        }
-
-        if (xmlNodeDump(xml_buf, xml, node, 0, 0) < 0) {
-            vshError(ctl, _("Failed to extract CPU element snippet from domain XML."));
-            goto cleanup;
-        }
-
-        snippet = (const char *) xmlBufferContent(xml_buf);
+    if (virNodeDeviceReAttach(device) == 0) {
+        vshPrint(ctl, _("Device %s re-attached\n"), name);
     } else {
-        vshError(ctl, _("File '%s' does not contain a <cpu> element or is not "
-                        "a valid domain or capabilities XML"), from);
-        goto cleanup;
+        vshError(ctl, _("Failed to re-attach device %s"), name);
+        ret = false;
     }
+    virNodeDeviceFree(device);
+    return ret;
+}
 
-    result = virConnectCompareCPU(ctl->conn, snippet, 0);
+/*
+ * "nodedev-reset" command
+ */
+static const vshCmdInfo info_node_device_reset[] = {
+    {"help", N_("reset node device")},
+    {"desc", N_("Reset node device before or after assigning to a domain.")},
+    {NULL, NULL}
+};
 
-    switch (result) {
-    case VIR_CPU_COMPARE_INCOMPATIBLE:
-        vshPrint(ctl, _("CPU described in %s is incompatible with host CPU\n"),
-                 from);
-        goto cleanup;
-        break;
 
-    case VIR_CPU_COMPARE_IDENTICAL:
-        vshPrint(ctl, _("CPU described in %s is identical to host CPU\n"),
-                 from);
-        break;
+static const vshCmdOptDef opts_node_device_reset[] = {
+    {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("device key")},
+    {NULL, 0, 0, NULL}
+};
 
-    case VIR_CPU_COMPARE_SUPERSET:
-        vshPrint(ctl, _("Host CPU is a superset of CPU described in %s\n"),
-                 from);
-        break;
+static bool
+cmdNodeDeviceReset(vshControl *ctl, const vshCmd *cmd)
+{
+    const char *name = NULL;
+    virNodeDevicePtr device;
+    bool ret = true;
 
-    case VIR_CPU_COMPARE_ERROR:
-    default:
-        vshError(ctl, _("Failed to compare host CPU with %s"), from);
-        goto cleanup;
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+    if (vshCommandOptString(cmd, "device", &name) <= 0)
+        return false;
+    if (!(device = virNodeDeviceLookupByName(ctl->conn, name))) {
+        vshError(ctl, "%s '%s'", _("Could not find matching device"), name);
+        return false;
     }
 
-    ret = true;
-
-cleanup:
-    VIR_FREE(buffer);
-    xmlBufferFree(xml_buf);
-    xmlXPathFreeContext(ctxt);
-    xmlFreeDoc(xml);
-
+    if (virNodeDeviceReset(device) == 0) {
+        vshPrint(ctl, _("Device %s reset\n"), name);
+    } else {
+        vshError(ctl, _("Failed to reset device %s"), name);
+        ret = false;
+    }
+    virNodeDeviceFree(device);
     return ret;
 }
 
 /*
- * "cpu-baseline" command
+ * "hostname" command
  */
-static const vshCmdInfo info_cpu_baseline[] = {
-    {"help", N_("compute baseline CPU")},
-    {"desc", N_("Compute baseline CPU for a set of given CPUs.")},
+static const vshCmdInfo info_hostname[] = {
+    {"help", N_("print the hypervisor hostname")},
+    {"desc", ""},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_cpu_baseline[] = {
-    {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing XML CPU descriptions")},
-    {NULL, 0, 0, NULL}
-};
-
 static bool
-cmdCPUBaseline(vshControl *ctl, const vshCmd *cmd)
+cmdHostname(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
 {
-    const char *from = NULL;
-    bool ret = false;
-    char *buffer;
-    char *result = NULL;
-    const char **list = NULL;
-    int count = 0;
-
-    xmlDocPtr xml = NULL;
-    xmlNodePtr *node_list = NULL;
-    xmlXPathContextPtr ctxt = NULL;
-    xmlBufferPtr xml_buf = NULL;
-    virBuffer buf = VIR_BUFFER_INITIALIZER;
-    int i;
+    char *hostname;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
         return false;
 
-    if (vshCommandOptString(cmd, "file", &from) <= 0)
-        return false;
-
-    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
+    hostname = virConnectGetHostname(ctl->conn);
+    if (hostname == NULL) {
+        vshError(ctl, "%s", _("failed to get hostname"));
         return false;
+    }
 
-    /* add a separate container around the xml */
-    virBufferStrcat(&buf, "<container>", buffer, "</container>", NULL);
-    if (virBufferError(&buf))
-        goto no_memory;
+    vshPrint (ctl, "%s\n", hostname);
+    VIR_FREE(hostname);
 
-    VIR_FREE(buffer);
-    buffer = virBufferContentAndReset(&buf);
+    return true;
+}
 
+/*
+ * "uri" command
+ */
+static const vshCmdInfo info_uri[] = {
+    {"help", N_("print the hypervisor canonical URI")},
+    {"desc", ""},
+    {NULL, NULL}
+};
 
-    if (!(xml = virXMLParseStringCtxt(buffer, from, &ctxt)))
-        goto cleanup;
+static bool
+cmdURI(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
+{
+    char *uri;
 
-    if ((count = virXPathNodeSet("//cpu[not(ancestor::cpus)]",
-                                 ctxt, &node_list)) == -1)
-        goto cleanup;
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
 
-    if (count == 0) {
-        vshError(ctl, _("No host CPU specified in '%s'"), from);
-        goto cleanup;
+    uri = virConnectGetURI(ctl->conn);
+    if (uri == NULL) {
+        vshError(ctl, "%s", _("failed to get URI"));
+        return false;
     }
 
-    list = vshCalloc(ctl, count, sizeof(const char *));
-
-    if (!(xml_buf = xmlBufferCreate()))
-        goto no_memory;
-
-    for (i = 0; i < count; i++) {
-        xmlBufferEmpty(xml_buf);
+    vshPrint(ctl, "%s\n", uri);
+    VIR_FREE(uri);
 
-        if (xmlNodeDump(xml_buf, xml,  node_list[i], 0, 0) < 0) {
-            vshError(ctl, _("Failed to extract <cpu> element"));
-            goto cleanup;
-        }
+    return true;
+}
 
-        list[i] = vshStrdup(ctl, (const char *)xmlBufferContent(xml_buf));
-    }
+/*
+ * "sysinfo" command
+ */
+static const vshCmdInfo info_sysinfo[] = {
+    {"help", N_("print the hypervisor sysinfo")},
+    {"desc",
+     N_("output an XML string for the hypervisor sysinfo, if available")},
+    {NULL, NULL}
+};
 
-    result = virConnectBaselineCPU(ctl->conn, list, count, 0);
+static bool
+cmdSysinfo(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
+{
+    char *sysinfo;
 
-    if (result) {
-        vshPrint(ctl, "%s", result);
-        ret = true;
-    }
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
 
-cleanup:
-    xmlXPathFreeContext(ctxt);
-    xmlFreeDoc(xml);
-    xmlBufferFree(xml_buf);
-    VIR_FREE(result);
-    if (list != NULL && count > 0) {
-        for (i = 0; i < count; i++)
-            VIR_FREE(list[i]);
+    sysinfo = virConnectGetSysinfo(ctl->conn, 0);
+    if (sysinfo == NULL) {
+        vshError(ctl, "%s", _("failed to get sysinfo"));
+        return false;
     }
-    VIR_FREE(list);
-    VIR_FREE(buffer);
 
-    return ret;
+    vshPrint(ctl, "%s", sysinfo);
+    VIR_FREE(sysinfo);
 
-no_memory:
-    vshError(ctl, "%s", _("Out of memory"));
-    ret = false;
-    goto cleanup;
+    return true;
 }
 
 /* Common code for the edit / net-edit / pool-edit functions which follow. */
@@ -14849,62 +6887,6 @@ cmdEcho(vshControl *ctl, const vshCmd *cmd)
     return true;
 }
 
-/*
- * "edit" command
- */
-static const vshCmdInfo info_edit[] = {
-    {"help", N_("edit XML configuration for a domain")},
-    {"desc", N_("Edit the XML configuration for a domain.")},
-    {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_edit[] = {
-    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
-    {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdEdit(vshControl *ctl, const vshCmd *cmd)
-{
-    bool ret = false;
-    virDomainPtr dom = NULL;
-    virDomainPtr dom_edited = NULL;
-    unsigned int flags = VIR_DOMAIN_XML_SECURE | VIR_DOMAIN_XML_INACTIVE;
-
-    if (!vshConnectionUsability(ctl, ctl->conn))
-        goto cleanup;
-
-    dom = vshCommandOptDomain(ctl, cmd, NULL);
-    if (dom == NULL)
-        goto cleanup;
-
-#define EDIT_GET_XML virDomainGetXMLDesc(dom, flags)
-#define EDIT_NOT_CHANGED \
-    vshPrint(ctl, _("Domain %s XML configuration not changed.\n"),  \
-             virDomainGetName(dom));                                \
-    ret = true; goto edit_cleanup;
-#define EDIT_DEFINE \
-    (dom_edited = virDomainDefineXML(ctl->conn, doc_edited))
-#define EDIT_FREE \
-    if (dom_edited) \
-        virDomainFree(dom_edited);
-#include "virsh-edit.c"
-
-    vshPrint(ctl, _("Domain %s XML configuration edited.\n"),
-             virDomainGetName(dom_edited));
-
-    ret = true;
-
- cleanup:
-    if (dom)
-        virDomainFree(dom);
-    if (dom_edited)
-        virDomainFree(dom_edited);
-
-    return ret;
-}
-
-
 /*
  * "net-edit" command
  */
@@ -18174,22 +10156,6 @@ vshFindTypedParamByName(const char *name, virTypedParameterPtr list, int count)
     return NULL;
 }
 
-static const char *
-vshDomainVcpuStateToString(int state)
-{
-    switch (state) {
-    case VIR_VCPU_OFFLINE:
-        return N_("offline");
-    case VIR_VCPU_BLOCKED:
-        return N_("idle");
-    case VIR_VCPU_RUNNING:
-        return N_("running");
-    default:
-        ;/*FALLTHROUGH*/
-    }
-    return N_("no state");
-}
-
 static bool
 vshConnectionUsability(vshControl *ctl, virConnectPtr conn)
 {
@@ -19062,6 +11028,8 @@ vshParseArgv(vshControl *ctl, int argc, char **argv)
     return true;
 }
 
+#include "virsh-domain.c"
+
 static const vshCmdDef domManagementCmds[] = {
     {"attach-device", cmdAttachDevice, opts_attach_device,
      info_attach_device, 0},