[libvirt] [PATCH 1/2] virsh: move At(De)tachInterface defintions below At(De)tachDisks

KAMEZAWA Hiroyuki kamezawa.hiroyu at jp.fujitsu.com
Wed Dec 14 03:25:08 UTC 2011


From: KAMEZAWA Hiroyuki <kamezawa.hiroyu at jp.fujitsu.com>

This is for reusing PCIAddress parser etc..in AttachInterface.
This patch just moves defs. No functional/logical changes at all.
---
 tools/virsh.c |  916 ++++++++++++++++++++++++++++----------------------------
 1 files changed, 458 insertions(+), 458 deletions(-)

diff --git a/tools/virsh.c b/tools/virsh.c
index f6571f7..2cb3a19 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -12310,75 +12310,193 @@ cmdUpdateDevice(vshControl *ctl, const vshCmd *cmd)
 
 
 /*
- * "attach-interface" command
+ * "attach-disk" command
  */
-static const vshCmdInfo info_attach_interface[] = {
-    {"help", N_("attach network interface")},
-    {"desc", N_("Attach new network interface.")},
+static const vshCmdInfo info_attach_disk[] = {
+    {"help", N_("attach disk device")},
+    {"desc", N_("Attach new disk device.")},
     {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_BOOL, 0, N_("persist interface attachment")},
-    {"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_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_BOOL, 0, N_("persist disk attachment")},
+    {"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")},
+    {"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}
 };
 
-/* 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)
+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)
 {
-    const char *average = NULL;
-    char *peak = NULL, *burst = NULL;
+    char *domain, *bus, *slot, *function;
 
-    average = rateStr;
-    if (!average)
+    if (!pciAddr)
         return -1;
-    if (virStrToLong_ull(average, &peak, 10, &rate->average) < 0)
+    if (!str)
         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;
-        }
+    domain = (char *)str;
 
-        /* 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;
-        }
-    }
+    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
-cmdAttachInterface(vshControl *ctl, const vshCmd *cmd)
+cmdAttachDisk(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;
+    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;
-    bool functionReturn = false;
     unsigned int flags;
+    const char *stype = NULL;
     virBuffer buf = VIR_BUFFER_INITIALIZER;
     char *xml;
 
@@ -12388,97 +12506,130 @@ cmdAttachInterface(vshControl *ctl, const vshCmd *cmd)
     if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
         goto cleanup;
 
-    if (vshCommandOptString(cmd, "type", &type) <= 0)
+    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, "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");
+    if (vshCommandOptString(cmd, "target", &target) <= 0)
         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);
+    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 (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 (!stype) {
+        if (driver && (STREQ(driver, "file") || STREQ(driver, "tap")))
+            isFile = true;
+    } else if (STREQ(stype, "file")) {
+        isFile = true;
+    } else if (STRNEQ(stype, "block")) {
+        vshError(ctl, _("Unknown source type: '%s'"), stype);
+        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"));
+
+    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 interface */
-    virBufferAsprintf(&buf, "<interface type='%s'>\n", type);
+    /* Make XML of disk */
+    virBufferAsprintf(&buf, "<disk type='%s'",
+                      (isFile) ? "file" : "block");
+    if (type)
+        virBufferAsprintf(&buf, " device='%s'", type);
+    virBufferAddLit(&buf, ">\n");
 
-    if (typ == 1)
-        virBufferAsprintf(&buf, "  <source network='%s'/>\n", source);
-    else if (typ == 2)
-        virBufferAsprintf(&buf, "  <source bridge='%s'/>\n", source);
+    if (driver || subdriver)
+        virBufferAsprintf(&buf, "  <driver");
 
-    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 (driver)
+        virBufferAsprintf(&buf, " name='%s'", driver);
+    if (subdriver)
+        virBufferAsprintf(&buf, " type='%s'", subdriver);
+    if (cache)
+        virBufferAsprintf(&buf, " cache='%s'", cache);
 
-    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 (driver || subdriver || 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 (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");
+
+        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;
+            }
         }
-        virBufferAsprintf(&buf, "  </bandwidth>\n");
     }
 
-    virBufferAddLit(&buf, "</interface>\n");
+    virBufferAddLit(&buf, "</disk>\n");
 
     if (virBufferError(&buf)) {
         vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
-        goto cleanup;
+        return false;
     }
 
     xml = virBufferContentAndReset(&buf);
@@ -12495,9 +12646,9 @@ cmdAttachInterface(vshControl *ctl, const vshCmd *cmd)
     VIR_FREE(xml);
 
     if (ret != 0) {
-        vshError(ctl, "%s", _("Failed to attach interface"));
+        vshError(ctl, "%s", _("Failed to attach disk"));
     } else {
-        vshPrint(ctl, "%s", _("Interface attached successfully\n"));
+        vshPrint(ctl, "%s", _("Disk attached successfully\n"));
         functionReturn = true;
     }
 
@@ -12509,37 +12660,35 @@ cmdAttachInterface(vshControl *ctl, const vshCmd *cmd)
 }
 
 /*
- * "detach-interface" command
+ * "detach-disk" command
  */
-static const vshCmdInfo info_detach_interface[] = {
-    {"help", N_("detach network interface")},
-    {"desc", N_("Detach network interface.")},
+static const vshCmdInfo info_detach_disk[] = {
+    {"help", N_("detach disk device")},
+    {"desc", N_("Detach disk device.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_detach_interface[] = {
+static const vshCmdOptDef opts_detach_disk[] = {
     {"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_BOOL, 0, N_("persist interface detachment")},
+    {"target", VSH_OT_DATA, VSH_OFLAG_REQ, N_("target of disk device")},
+    {"persistent", VSH_OT_BOOL, 0, N_("persist disk detachment")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdDetachInterface(vshControl *ctl, const vshCmd *cmd)
+cmdDetachDisk(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;
+    virDomainPtr dom = NULL;
+    const char *target = NULL;
     char *doc;
-    char buf[64];
-    int i = 0, diff_mac;
+    int i = 0, diff_tgt;
     int ret;
-    int functionReturn = false;
+    bool functionReturn = false;
     unsigned int flags;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
@@ -12548,13 +12697,8 @@ cmdDetachInterface(vshControl *ctl, const vshCmd *cmd)
     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"));
+    if (vshCommandOptString(cmd, "target", &target) <= 0)
         goto cleanup;
-    }
 
     doc = virDomainGetXMLDesc(dom, 0);
     if (!doc)
@@ -12563,44 +12707,34 @@ cmdDetachInterface(vshControl *ctl, const vshCmd *cmd)
     xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt);
     VIR_FREE(doc);
     if (!xml) {
-        vshError(ctl, "%s", _("Failed to get interface information"));
+        vshError(ctl, "%s", _("Failed to get disk information"));
         goto cleanup;
     }
 
-    snprintf(buf, sizeof(buf), "/domain/devices/interface[@type='%s']", type);
-    obj = xmlXPathEval(BAD_CAST buf, ctxt);
+    obj = xmlXPathEval(BAD_CAST "/domain/devices/disk", 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);
+        vshError(ctl, "%s", _("Failed to get disk information"));
         goto cleanup;
     }
 
-    if (!mac)
-        goto hit;
-
-    /* search mac */
+    /* search target */
     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) {
+                xmlStrEqual(cur->name, BAD_CAST "target")) {
+                char *tmp_tgt = virXMLPropString(cur, "dev");
+                diff_tgt = STREQ(tmp_tgt, target);
+                VIR_FREE(tmp_tgt);
+                if (diff_tgt) {
                     goto hit;
                 }
             }
             cur = cur->next;
         }
     }
-    vshError(ctl, _("No found interface whose MAC address is %s"), mac);
+    vshError(ctl, _("No found disk whose target is %s"), target);
     goto cleanup;
 
  hit:
@@ -12627,210 +12761,92 @@ cmdDetachInterface(vshControl *ctl, const vshCmd *cmd)
     }
 
     if (ret != 0) {
-        vshError(ctl, "%s", _("Failed to detach interface"));
+        vshError(ctl, "%s", _("Failed to detach disk"));
     } else {
-        vshPrint(ctl, "%s", _("Interface detached successfully\n"));
+        vshPrint(ctl, "%s", _("Disk detached successfully\n"));
         functionReturn = true;
     }
 
  cleanup:
-    if (dom)
-        virDomainFree(dom);
     xmlXPathFreeObject(obj);
     xmlXPathFreeContext(ctxt);
     xmlFreeDoc(xml);
     xmlBufferFree(xml_buf);
+    if (dom)
+        virDomainFree(dom);
     return functionReturn;
 }
 
 /*
- * "attach-disk" command
+ * "attach-interface" command
  */
-static const vshCmdInfo info_attach_disk[] = {
-    {"help", N_("attach disk device")},
-    {"desc", N_("Attach new disk device.")},
+static const vshCmdInfo info_attach_interface[] = {
+    {"help", N_("attach network interface")},
+    {"desc", N_("Attach new network interface.")},
     {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_BOOL, 0, N_("persist disk attachment")},
-    {"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")},
-    {"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_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_BOOL, 0, N_("persist interface attachment")},
+    {"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}
 };
 
-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)
+/* 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)
 {
-    char *controller, *bus, *unit;
+    const char *average = NULL;
+    char *peak = NULL, *burst = NULL;
 
-    if (!ideAddr)
+    average = rateStr;
+    if (!average)
         return -1;
-    if (!str)
+    if (virStrToLong_ull(average, &peak, 10, &rate->average) < 0)
         return -1;
 
-    controller = (char *)str;
-
-    if (virStrToLong_ui(controller, &bus, 0, &ideAddr->controller) != 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;
+        }
 
-    bus++;
-    if (virStrToLong_ui(bus, &unit, 0, &ideAddr->bus) != 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;
+        }
+    }
 
-    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;
+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;
-    const char *stype = NULL;
     virBuffer buf = VIR_BUFFER_INITIALIZER;
     char *xml;
 
@@ -12840,130 +12856,97 @@ cmdAttachDisk(vshControl *ctl, const vshCmd *cmd)
     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)
+    if (vshCommandOptString(cmd, "type", &type) <= 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"));
+    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;
     }
 
-    if (!stype) {
-        if (driver && (STREQ(driver, "file") || STREQ(driver, "tap")))
-            isFile = true;
-    } else if (STREQ(stype, "file")) {
-        isFile = true;
-    } else if (STRNEQ(stype, "block")) {
-        vshError(ctl, _("Unknown source type: '%s'"), stype);
+    /* 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 (mode) {
-        if (STRNEQ(mode, "readonly") && STRNEQ(mode, "shareable")) {
-            vshError(ctl, _("No support for %s in command 'attach-disk'"),
-                     mode);
+    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 disk */
-    virBufferAsprintf(&buf, "<disk type='%s'",
-                      (isFile) ? "file" : "block");
-    if (type)
-        virBufferAsprintf(&buf, " device='%s'", type);
-    virBufferAddLit(&buf, ">\n");
-
-    if (driver || subdriver)
-        virBufferAsprintf(&buf, "  <driver");
-
-    if (driver)
-        virBufferAsprintf(&buf, " name='%s'", driver);
-    if (subdriver)
-        virBufferAsprintf(&buf, " type='%s'", subdriver);
-    if (cache)
-        virBufferAsprintf(&buf, " cache='%s'", cache);
-
-    if (driver || subdriver || 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);
+    /* Make XML of interface */
+    virBufferAsprintf(&buf, "<interface type='%s'>\n", type);
 
-    if (serial)
-        virBufferAsprintf(&buf, "  <serial>%s</serial>\n", serial);
+    if (typ == 1)
+        virBufferAsprintf(&buf, "  <source network='%s'/>\n", source);
+    else if (typ == 2)
+        virBufferAsprintf(&buf, "  <source bridge='%s'/>\n", source);
 
-    if (vshCommandOptBool(cmd, "shareable"))
-        virBufferAsprintf(&buf, "  <shareable/>\n");
+    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 (straddr) {
-        if (str2DiskAddress(straddr, &diskAddr) != 0) {
-            vshError(ctl, _("Invalid address."));
-            goto cleanup;
+    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 (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 (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, "</disk>\n");
+    virBufferAddLit(&buf, "</interface>\n");
 
     if (virBufferError(&buf)) {
         vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
-        return false;
+        goto cleanup;
     }
 
     xml = virBufferContentAndReset(&buf);
@@ -12980,9 +12963,9 @@ cmdAttachDisk(vshControl *ctl, const vshCmd *cmd)
     VIR_FREE(xml);
 
     if (ret != 0) {
-        vshError(ctl, "%s", _("Failed to attach disk"));
+        vshError(ctl, "%s", _("Failed to attach interface"));
     } else {
-        vshPrint(ctl, "%s", _("Disk attached successfully\n"));
+        vshPrint(ctl, "%s", _("Interface attached successfully\n"));
         functionReturn = true;
     }
 
@@ -12994,35 +12977,37 @@ cmdAttachDisk(vshControl *ctl, const vshCmd *cmd)
 }
 
 /*
- * "detach-disk" command
+ * "detach-interface" command
  */
-static const vshCmdInfo info_detach_disk[] = {
-    {"help", N_("detach disk device")},
-    {"desc", N_("Detach disk device.")},
+static const vshCmdInfo info_detach_interface[] = {
+    {"help", N_("detach network interface")},
+    {"desc", N_("Detach network interface.")},
     {NULL, NULL}
 };
 
-static const vshCmdOptDef opts_detach_disk[] = {
+static const vshCmdOptDef opts_detach_interface[] = {
     {"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_BOOL, 0, N_("persist disk detachment")},
+    {"type",   VSH_OT_DATA, VSH_OFLAG_REQ, N_("network interface type")},
+    {"mac",    VSH_OT_STRING, 0, N_("MAC address")},
+    {"persistent", VSH_OT_BOOL, 0, N_("persist interface detachment")},
     {NULL, 0, 0, NULL}
 };
 
 static bool
-cmdDetachDisk(vshControl *ctl, const vshCmd *cmd)
+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;
-    virDomainPtr dom = NULL;
-    const char *target = NULL;
+    const char *mac =NULL, *type = NULL;
     char *doc;
-    int i = 0, diff_tgt;
+    char buf[64];
+    int i = 0, diff_mac;
     int ret;
-    bool functionReturn = false;
+    int functionReturn = false;
     unsigned int flags;
 
     if (!vshConnectionUsability(ctl, ctl->conn))
@@ -13031,8 +13016,13 @@ cmdDetachDisk(vshControl *ctl, const vshCmd *cmd)
     if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
         goto cleanup;
 
-    if (vshCommandOptString(cmd, "target", &target) <= 0)
+    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)
@@ -13041,34 +13031,44 @@ cmdDetachDisk(vshControl *ctl, const vshCmd *cmd)
     xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt);
     VIR_FREE(doc);
     if (!xml) {
-        vshError(ctl, "%s", _("Failed to get disk information"));
+        vshError(ctl, "%s", _("Failed to get interface information"));
         goto cleanup;
     }
 
-    obj = xmlXPathEval(BAD_CAST "/domain/devices/disk", ctxt);
+    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, "%s", _("Failed to get disk information"));
+        vshError(ctl, _("No found interface whose type is %s"), type);
         goto cleanup;
     }
 
-    /* search target */
+    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 "target")) {
-                char *tmp_tgt = virXMLPropString(cur, "dev");
-                diff_tgt = STREQ(tmp_tgt, target);
-                VIR_FREE(tmp_tgt);
-                if (diff_tgt) {
+                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 disk whose target is %s"), target);
+    vshError(ctl, _("No found interface whose MAC address is %s"), mac);
     goto cleanup;
 
  hit:
@@ -13095,19 +13095,19 @@ cmdDetachDisk(vshControl *ctl, const vshCmd *cmd)
     }
 
     if (ret != 0) {
-        vshError(ctl, "%s", _("Failed to detach disk"));
+        vshError(ctl, "%s", _("Failed to detach interface"));
     } else {
-        vshPrint(ctl, "%s", _("Disk detached successfully\n"));
+        vshPrint(ctl, "%s", _("Interface detached successfully\n"));
         functionReturn = true;
     }
 
  cleanup:
+    if (dom)
+        virDomainFree(dom);
     xmlXPathFreeObject(obj);
     xmlXPathFreeContext(ctxt);
     xmlFreeDoc(xml);
     xmlBufferFree(xml_buf);
-    if (dom)
-        virDomainFree(dom);
     return functionReturn;
 }
 
-- 
1.7.4.1





More information about the libvir-list mailing list