From: KAMEZAWA Hiroyuki <kamezawa.hiroyu@xxxxxxxxxxxxxx> 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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list