/* * virsh-domain.c: Commands to manage domain * * Copyright (C) 2005, 2007-2016 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 * . */ #include #include "virsh-domain.h" #include "virsh-util.h" #include #include #include #include #include #include #include #include "internal.h" #include "virbitmap.h" #include "virbuffer.h" #include "conf/domain_conf.h" #include "viralloc.h" #include "vircommand.h" #include "virfile.h" #include "virjson.h" #include "virkeycode.h" #include "virmacaddr.h" #include "virnetdevbandwidth.h" #include "virprocess.h" #include "virstring.h" #include "virsh-console.h" #include "virsh-domain-monitor.h" #include "virerror.h" #include "virtime.h" #include "virtypedparam.h" #include "virxml.h" #include "virsh-nodedev.h" #include "viruri.h" #include "vsh-table.h" #include "virenum.h" #define VIRSH_COMMON_OPT_DOMAIN_PERSISTENT \ {.name = "persistent", \ .type = VSH_OT_BOOL, \ .help = N_("make live change persistent") \ } #define VIRSH_COMMON_OPT_DOMAIN_CONFIG \ VIRSH_COMMON_OPT_CONFIG(N_("affect next boot")) #define VIRSH_COMMON_OPT_DOMAIN_LIVE \ VIRSH_COMMON_OPT_LIVE(N_("affect running domain")) #define VIRSH_COMMON_OPT_DOMAIN_CURRENT \ VIRSH_COMMON_OPT_CURRENT(N_("affect current domain")) static virDomainPtr virshDomainDefine(virConnectPtr conn, const char *xml, unsigned int flags) { virDomainPtr dom; if (flags) { dom = virDomainDefineXMLFlags(conn, xml, flags); /* If validate is the only flag, just drop it and * try again. */ if (!dom) { if ((virGetLastErrorCode() == VIR_ERR_NO_SUPPORT) && (flags == VIR_DOMAIN_DEFINE_VALIDATE)) dom = virDomainDefineXML(conn, xml); } } else { dom = virDomainDefineXML(conn, xml); } return dom; } VIR_ENUM_DECL(virshDomainVcpuState); VIR_ENUM_IMPL(virshDomainVcpuState, VIR_VCPU_LAST, N_("offline"), N_("running"), N_("blocked")); static const char * virshDomainVcpuStateToString(int state) { const char *str = virshDomainVcpuStateTypeToString(state); return str ? _(str) : _("no state"); } /* * Determine number of CPU nodes present by trying * virNodeGetCPUMap and falling back to virNodeGetInfo * if needed. */ static int virshNodeGetCPUCount(virConnectPtr conn) { int ret; virNodeInfo nodeinfo; if ((ret = virNodeGetCPUMap(conn, NULL, NULL, 0)) < 0) { /* fall back to nodeinfo */ vshResetLibvirtError(); if (virNodeGetInfo(conn, &nodeinfo) == 0) ret = VIR_NODEINFO_MAXCPUS(nodeinfo); } return ret; } /* * "attach-device" command */ static const vshCmdInfo info_attach_device[] = { {.name = "help", .data = N_("attach device from an XML file") }, {.name = "desc", .data = N_("Attach device from an XML .") }, {.name = NULL} }; static const vshCmdOptDef opts_attach_device[] = { VIRSH_COMMON_OPT_DOMAIN_FULL(0), VIRSH_COMMON_OPT_FILE(N_("XML file")), VIRSH_COMMON_OPT_DOMAIN_PERSISTENT, VIRSH_COMMON_OPT_DOMAIN_CONFIG, VIRSH_COMMON_OPT_DOMAIN_LIVE, VIRSH_COMMON_OPT_DOMAIN_CURRENT, {.name = NULL} }; static bool cmdAttachDevice(vshControl *ctl, const vshCmd *cmd) { virDomainPtr dom; const char *from = NULL; char *buffer; int rv; bool ret = false; unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT; bool current = vshCommandOptBool(cmd, "current"); bool config = vshCommandOptBool(cmd, "config"); bool live = vshCommandOptBool(cmd, "live"); bool persistent = vshCommandOptBool(cmd, "persistent"); VSH_EXCLUSIVE_OPTIONS_VAR(persistent, current); VSH_EXCLUSIVE_OPTIONS_VAR(current, live); VSH_EXCLUSIVE_OPTIONS_VAR(current, config); if (config || persistent) flags |= VIR_DOMAIN_AFFECT_CONFIG; if (live) flags |= VIR_DOMAIN_AFFECT_LIVE; if (!(dom = virshCommandOptDomain(ctl, cmd, NULL))) return false; if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0) goto cleanup; if (persistent && virDomainIsActive(dom) == 1) flags |= VIR_DOMAIN_AFFECT_LIVE; if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0) { vshReportError(ctl); goto cleanup; } if (flags || current) rv = virDomainAttachDeviceFlags(dom, buffer, flags); else rv = virDomainAttachDevice(dom, buffer); VIR_FREE(buffer); if (rv < 0) { vshError(ctl, _("Failed to attach device from %s"), from); goto cleanup; } vshPrintExtra(ctl, "%s", _("Device attached successfully\n")); ret = true; cleanup: virshDomainFree(dom); return ret; } /* * "attach-disk" command */ static const vshCmdInfo info_attach_disk[] = { {.name = "help", .data = N_("attach disk device") }, {.name = "desc", .data = N_("Attach new disk device.") }, {.name = NULL} }; static const vshCmdOptDef opts_attach_disk[] = { VIRSH_COMMON_OPT_DOMAIN_FULL(0), {.name = "source", .type = VSH_OT_DATA, .flags = VSH_OFLAG_REQ | VSH_OFLAG_EMPTY_OK, .help = N_("source of disk device") }, {.name = "target", .type = VSH_OT_DATA, .flags = VSH_OFLAG_REQ, .help = N_("target of disk device") }, {.name = "targetbus", .type = VSH_OT_STRING, .help = N_("target bus of disk device") }, {.name = "driver", .type = VSH_OT_STRING, .help = N_("driver of disk device") }, {.name = "subdriver", .type = VSH_OT_STRING, .help = N_("subdriver of disk device") }, {.name = "iothread", .type = VSH_OT_STRING, .help = N_("IOThread to be used by supported device") }, {.name = "cache", .type = VSH_OT_STRING, .help = N_("cache mode of disk device") }, {.name = "io", .type = VSH_OT_STRING, .help = N_("io policy of disk device") }, {.name = "type", .type = VSH_OT_STRING, .help = N_("target device type") }, {.name = "shareable", .type = VSH_OT_ALIAS, .help = "mode=shareable" }, {.name = "mode", .type = VSH_OT_STRING, .help = N_("mode of device reading and writing") }, {.name = "sourcetype", .type = VSH_OT_STRING, .help = N_("type of source (block|file)") }, {.name = "serial", .type = VSH_OT_STRING, .help = N_("serial of disk device") }, {.name = "wwn", .type = VSH_OT_STRING, .help = N_("wwn of disk device") }, {.name = "alias", .type = VSH_OT_STRING, .help = N_("custom alias name of disk device") }, {.name = "rawio", .type = VSH_OT_BOOL, .help = N_("needs rawio capability") }, {.name = "address", .type = VSH_OT_STRING, .help = N_("address of disk device") }, {.name = "multifunction", .type = VSH_OT_BOOL, .help = N_("use multifunction pci under specified address") }, {.name = "print-xml", .type = VSH_OT_BOOL, .help = N_("print XML document rather than attach the disk") }, VIRSH_COMMON_OPT_DOMAIN_PERSISTENT, VIRSH_COMMON_OPT_DOMAIN_CONFIG, VIRSH_COMMON_OPT_DOMAIN_LIVE, VIRSH_COMMON_OPT_DOMAIN_CURRENT, {.name = NULL} }; enum { DISK_ADDR_TYPE_INVALID, DISK_ADDR_TYPE_PCI, DISK_ADDR_TYPE_SCSI, DISK_ADDR_TYPE_IDE, DISK_ADDR_TYPE_CCW, DISK_ADDR_TYPE_USB, DISK_ADDR_TYPE_SATA, }; struct PCIAddress { unsigned int domain; unsigned int bus; unsigned int slot; unsigned int function; }; struct SCSIAddress { unsigned int controller; unsigned int bus; unsigned long long unit; }; struct IDEAddress { unsigned int controller; unsigned int bus; unsigned int unit; }; struct CCWAddress { unsigned int cssid; unsigned int ssid; unsigned int devno; }; struct USBAddress { unsigned int bus; unsigned int port; }; struct SATAAddress { unsigned int controller; unsigned int bus; unsigned long long unit; }; struct DiskAddress { int type; union { struct PCIAddress pci; struct SCSIAddress scsi; struct IDEAddress ide; struct CCWAddress ccw; struct USBAddress usb; struct SATAAddress sata; } 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_uip(domain, &bus, 16, &pciAddr->domain) != 0) return -1; bus++; if (virStrToLong_uip(bus, &slot, 16, &pciAddr->bus) != 0) return -1; slot++; if (virStrToLong_uip(slot, &function, 16, &pciAddr->slot) != 0) return -1; function++; if (virStrToLong_uip(function, NULL, 16, &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_uip(controller, &bus, 10, &scsiAddr->controller) != 0) return -1; bus++; if (virStrToLong_uip(bus, &unit, 10, &scsiAddr->bus) != 0) return -1; unit++; if (virStrToLong_ullp(unit, NULL, 10, &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_uip(controller, &bus, 10, &ideAddr->controller) != 0) return -1; bus++; if (virStrToLong_uip(bus, &unit, 10, &ideAddr->bus) != 0) return -1; unit++; if (virStrToLong_uip(unit, NULL, 10, &ideAddr->unit) != 0) return -1; return 0; } static int str2CCWAddress(const char *str, struct CCWAddress *ccwAddr) { char *cssid, *ssid, *devno; if (!ccwAddr) return -1; if (!str) return -1; cssid = (char *)str; if (virStrToLong_uip(cssid, &ssid, 16, &ccwAddr->cssid) != 0) return -1; ssid++; if (virStrToLong_uip(ssid, &devno, 16, &ccwAddr->ssid) != 0) return -1; devno++; if (virStrToLong_uip(devno, NULL, 16, &ccwAddr->devno) != 0) return -1; return 0; } static int str2USBAddress(const char *str, struct USBAddress *usbAddr) { char *bus, *port; if (!usbAddr) return -1; if (!str) return -1; bus = (char *)str; if (virStrToLong_uip(bus, &port, 10, &usbAddr->bus) != 0) return -1; port++; if (virStrToLong_uip(port, NULL, 10, &usbAddr->port) != 0) return -1; return 0; } static int str2SATAAddress(const char *str, struct SATAAddress *sataAddr) { char *controller, *bus, *unit; if (!sataAddr) return -1; if (!str) return -1; controller = (char *)str; if (virStrToLong_uip(controller, &bus, 10, &sataAddr->controller) != 0) return -1; bus++; if (virStrToLong_uip(bus, &unit, 10, &sataAddr->bus) != 0) return -1; unit++; if (virStrToLong_ullp(unit, NULL, 10, &sataAddr->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) * ccw disk address: ccw:0xfe.0.0000 (cssid:ssid:devno) * usb disk address: usb:00.00 (bus:port) * sata disk address: sata: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); } else if (STREQLEN(type, "ccw", addr - type)) { diskAddr->type = DISK_ADDR_TYPE_CCW; return str2CCWAddress(addr + 1, &diskAddr->addr.ccw); } else if (STREQLEN(type, "usb", addr - type)) { diskAddr->type = DISK_ADDR_TYPE_USB; return str2USBAddress(addr + 1, &diskAddr->addr.usb); } else if (STREQLEN(type, "sata", addr - type)) { diskAddr->type = DISK_ADDR_TYPE_SATA; return str2SATAAddress(addr + 1, &diskAddr->addr.sata); } 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, *iothread = NULL, *cache = NULL, *io = NULL, *serial = NULL, *straddr = NULL, *wwn = NULL, *targetbus = NULL, *alias = NULL; struct DiskAddress diskAddr; bool isFile = false, functionReturn = false; int ret; unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT; const char *stype = NULL; virBuffer buf = VIR_BUFFER_INITIALIZER; char *xml = NULL; struct stat st; bool current = vshCommandOptBool(cmd, "current"); bool config = vshCommandOptBool(cmd, "config"); bool live = vshCommandOptBool(cmd, "live"); bool persistent = vshCommandOptBool(cmd, "persistent"); VSH_EXCLUSIVE_OPTIONS_VAR(persistent, current); VSH_EXCLUSIVE_OPTIONS_VAR(current, live); VSH_EXCLUSIVE_OPTIONS_VAR(current, config); if (config || persistent) flags |= VIR_DOMAIN_AFFECT_CONFIG; if (live) flags |= VIR_DOMAIN_AFFECT_LIVE; if (vshCommandOptStringReq(ctl, cmd, "source", &source) < 0 || vshCommandOptStringReq(ctl, cmd, "target", &target) < 0 || vshCommandOptStringReq(ctl, cmd, "driver", &driver) < 0 || vshCommandOptStringReq(ctl, cmd, "subdriver", &subdriver) < 0 || vshCommandOptStringReq(ctl, cmd, "type", &type) < 0 || vshCommandOptStringReq(ctl, cmd, "mode", &mode) < 0 || vshCommandOptStringReq(ctl, cmd, "iothread", &iothread) < 0 || vshCommandOptStringReq(ctl, cmd, "cache", &cache) < 0 || vshCommandOptStringReq(ctl, cmd, "io", &io) < 0 || vshCommandOptStringReq(ctl, cmd, "serial", &serial) < 0 || vshCommandOptStringReq(ctl, cmd, "wwn", &wwn) < 0 || vshCommandOptStringReq(ctl, cmd, "address", &straddr) < 0 || vshCommandOptStringReq(ctl, cmd, "targetbus", &targetbus) < 0 || vshCommandOptStringReq(ctl, cmd, "alias", &alias) < 0 || vshCommandOptStringReq(ctl, cmd, "sourcetype", &stype) < 0) 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; } } if (wwn && !virValidateWWN(wwn)) goto cleanup; /* Make XML of disk */ virBufferAsprintf(&buf, "\n"); virBufferAdjustIndent(&buf, 2); if (driver || subdriver || iothread || cache || io) { virBufferAddLit(&buf, "\n"); } if (source) virBufferAsprintf(&buf, "\n", isFile ? "file" : "dev", source); virBufferAsprintf(&buf, "\n"); if (mode) virBufferAsprintf(&buf, "<%s/>\n", mode); if (serial) virBufferAsprintf(&buf, "%s\n", serial); if (alias) virBufferAsprintf(&buf, "\n", alias); if (wwn) virBufferAsprintf(&buf, "%s\n", wwn); 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, "
\n"); } else if (diskAddr.type == DISK_ADDR_TYPE_CCW) { virBufferAsprintf(&buf, "
\n", diskAddr.addr.ccw.cssid, diskAddr.addr.ccw.ssid, diskAddr.addr.ccw.devno); } else { vshError(ctl, "%s", _("expecting a pci:0000.00.00.00 or ccw:00.0.0000 address.")); goto cleanup; } } else if (STRPREFIX((const char *)target, "sd")) { if (diskAddr.type == DISK_ADDR_TYPE_SCSI) { virBufferAsprintf(&buf, "
\n", diskAddr.addr.scsi.controller, diskAddr.addr.scsi.bus, diskAddr.addr.scsi.unit); } else if (diskAddr.type == DISK_ADDR_TYPE_USB) { virBufferAsprintf(&buf, "
\n", diskAddr.addr.usb.bus, diskAddr.addr.usb.port); } else if (diskAddr.type == DISK_ADDR_TYPE_SATA) { virBufferAsprintf(&buf, "
\n", diskAddr.addr.sata.controller, diskAddr.addr.sata.bus, diskAddr.addr.sata.unit); } else { vshError(ctl, "%s", _("expecting a scsi:00.00.00 or usb:00.00 or sata:00.00.00 address.")); goto cleanup; } } else if (STRPREFIX((const char *)target, "hd")) { if (diskAddr.type == DISK_ADDR_TYPE_IDE) { virBufferAsprintf(&buf, "
\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; } } } virBufferAdjustIndent(&buf, -2); virBufferAddLit(&buf, "\n"); xml = virBufferContentAndReset(&buf); if (vshCommandOptBool(cmd, "print-xml")) { vshPrint(ctl, "%s", xml); functionReturn = true; goto cleanup; } if (!(dom = virshCommandOptDomain(ctl, cmd, NULL))) goto cleanup; if (persistent && virDomainIsActive(dom) == 1) flags |= VIR_DOMAIN_AFFECT_LIVE; if (flags || current) ret = virDomainAttachDeviceFlags(dom, xml, flags); else ret = virDomainAttachDevice(dom, xml); if (ret != 0) { vshError(ctl, "%s", _("Failed to attach disk")); } else { vshPrintExtra(ctl, "%s", _("Disk attached successfully\n")); functionReturn = true; } cleanup: VIR_FREE(xml); virshDomainFree(dom); virBufferFreeAndReset(&buf); return functionReturn; } /* * "attach-interface" command */ static const vshCmdInfo info_attach_interface[] = { {.name = "help", .data = N_("attach network interface") }, {.name = "desc", .data = N_("Attach new network interface.") }, {.name = NULL} }; static const vshCmdOptDef opts_attach_interface[] = { VIRSH_COMMON_OPT_DOMAIN_FULL(0), {.name = "type", .type = VSH_OT_DATA, .flags = VSH_OFLAG_REQ, .help = N_("network interface type") }, {.name = "source", .type = VSH_OT_DATA, .flags = VSH_OFLAG_REQ, .help = N_("source of network interface") }, {.name = "target", .type = VSH_OT_STRING, .help = N_("target network name") }, {.name = "mac", .type = VSH_OT_STRING, .help = N_("MAC address") }, {.name = "script", .type = VSH_OT_STRING, .help = N_("script used to bridge network interface") }, {.name = "model", .type = VSH_OT_STRING, .help = N_("model type") }, {.name = "alias", .type = VSH_OT_STRING, .help = N_("custom alias name of interface device") }, {.name = "inbound", .type = VSH_OT_STRING, .help = N_("control domain's incoming traffics") }, {.name = "outbound", .type = VSH_OT_STRING, .help = N_("control domain's outgoing traffics") }, VIRSH_COMMON_OPT_DOMAIN_PERSISTENT, VIRSH_COMMON_OPT_DOMAIN_CONFIG, VIRSH_COMMON_OPT_DOMAIN_LIVE, VIRSH_COMMON_OPT_DOMAIN_CURRENT, {.name = "print-xml", .type = VSH_OT_BOOL, .help = N_("print XML document rather than attach the interface") }, {.name = "managed", .type = VSH_OT_BOOL, .help = N_("libvirt will automatically detach/attach the device from/to host") }, {.name = NULL} }; /* parse inbound and outbound which are in the format of * 'average,peak,burst,floor', in which peak and burst are optional, * thus 'average,,burst' and 'average,peak' are also legal. */ #define VIRSH_PARSE_RATE_FIELD(index, name) \ do { \ if (index < ntok && \ *tok[index] != '\0' && \ virStrToLong_ullp(tok[index], NULL, 10, &rate->name) < 0) { \ vshError(ctl, _("field '%s' is malformed"), #name); \ goto cleanup; \ } \ } while (0) static int virshParseRateStr(vshControl *ctl, const char *rateStr, virNetDevBandwidthRatePtr rate) { char **tok = NULL; size_t ntok; int ret = -1; if (!(tok = virStringSplitCount(rateStr, ",", 0, &ntok))) return -1; if (ntok > 4) { vshError(ctl, _("Rate string '%s' has too many fields"), rateStr); goto cleanup; } VIRSH_PARSE_RATE_FIELD(0, average); VIRSH_PARSE_RATE_FIELD(1, peak); VIRSH_PARSE_RATE_FIELD(2, burst); VIRSH_PARSE_RATE_FIELD(3, floor); ret = 0; cleanup: virStringListFree(tok); return ret; } #undef VIRSH_PARSE_RATE_FIELD 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, *alias = NULL; virNetDevBandwidthRate inbound, outbound; virDomainNetType typ; int ret; bool functionReturn = false; virBuffer buf = VIR_BUFFER_INITIALIZER; char *xml = NULL; unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT; bool current = vshCommandOptBool(cmd, "current"); bool config = vshCommandOptBool(cmd, "config"); bool live = vshCommandOptBool(cmd, "live"); bool persistent = vshCommandOptBool(cmd, "persistent"); bool managed = vshCommandOptBool(cmd, "managed"); VSH_EXCLUSIVE_OPTIONS_VAR(persistent, current); VSH_EXCLUSIVE_OPTIONS_VAR(current, live); VSH_EXCLUSIVE_OPTIONS_VAR(current, config); if (config || persistent) flags |= VIR_DOMAIN_AFFECT_CONFIG; if (live) flags |= VIR_DOMAIN_AFFECT_LIVE; if (vshCommandOptStringReq(ctl, cmd, "type", &type) < 0 || vshCommandOptStringReq(ctl, cmd, "source", &source) < 0 || vshCommandOptStringReq(ctl, cmd, "target", &target) < 0 || vshCommandOptStringReq(ctl, cmd, "mac", &mac) < 0 || vshCommandOptStringReq(ctl, cmd, "script", &script) < 0 || vshCommandOptStringReq(ctl, cmd, "model", &model) < 0 || vshCommandOptStringReq(ctl, cmd, "alias", &alias) < 0 || vshCommandOptStringReq(ctl, cmd, "inbound", &inboundStr) < 0 || vshCommandOptStringReq(ctl, cmd, "outbound", &outboundStr) < 0) goto cleanup; /* check interface type */ if ((int)(typ = virDomainNetTypeFromString(type)) < 0) { vshError(ctl, _("No support for %s in command 'attach-interface'"), type); goto cleanup; } if (inboundStr) { memset(&inbound, 0, sizeof(inbound)); if (virshParseRateStr(ctl, inboundStr, &inbound) < 0) goto cleanup; if (!inbound.average && !inbound.floor) { vshError(ctl, _("either inbound average or floor is mandatory")); goto cleanup; } } if (outboundStr) { memset(&outbound, 0, sizeof(outbound)); if (virshParseRateStr(ctl, outboundStr, &outbound) < 0) goto cleanup; if (outbound.average == 0) { vshError(ctl, _("outbound average is mandatory")); goto cleanup; } if (outbound.floor) { vshError(ctl, _("outbound floor is unsupported yet")); goto cleanup; } } /* Make XML of interface */ virBufferAsprintf(&buf, "\n"); else virBufferAddLit(&buf, ">\n"); virBufferAdjustIndent(&buf, 2); switch (typ) { case VIR_DOMAIN_NET_TYPE_NETWORK: case VIR_DOMAIN_NET_TYPE_BRIDGE: virBufferAsprintf(&buf, "\n", virDomainNetTypeToString(typ), source); break; case VIR_DOMAIN_NET_TYPE_DIRECT: virBufferAsprintf(&buf, "\n", source); break; case VIR_DOMAIN_NET_TYPE_HOSTDEV: { struct PCIAddress pciAddr = {0, 0, 0, 0}; if (str2PCIAddress(source, &pciAddr) < 0) { vshError(ctl, _("cannot parse pci address '%s' for network " "interface"), source); goto cleanup; } virBufferAddLit(&buf, "\n"); virBufferAdjustIndent(&buf, 2); virBufferAsprintf(&buf, "
\n", pciAddr.domain, pciAddr.bus, pciAddr.slot, pciAddr.function); virBufferAdjustIndent(&buf, -2); virBufferAddLit(&buf, "\n"); break; } case VIR_DOMAIN_NET_TYPE_USER: case VIR_DOMAIN_NET_TYPE_ETHERNET: case VIR_DOMAIN_NET_TYPE_VHOSTUSER: case VIR_DOMAIN_NET_TYPE_SERVER: case VIR_DOMAIN_NET_TYPE_CLIENT: case VIR_DOMAIN_NET_TYPE_MCAST: case VIR_DOMAIN_NET_TYPE_UDP: case VIR_DOMAIN_NET_TYPE_INTERNAL: case VIR_DOMAIN_NET_TYPE_LAST: vshError(ctl, _("No support for %s in command 'attach-interface'"), type); goto cleanup; break; } if (target != NULL) virBufferAsprintf(&buf, "\n", target); if (mac != NULL) virBufferAsprintf(&buf, "\n", mac); if (script != NULL) virBufferAsprintf(&buf, "