diff --git a/cfg.mk b/cfg.mk index 87256d97db5424c9f2f91a93781a07782ce10d68..47d2ea818bb4882033ade2f11ae13adaefb6c716 100644 --- a/cfg.mk +++ b/cfg.mk @@ -773,7 +773,7 @@ exclude_file_name_regexp--sc_prohibit_xmlURI = ^src/util/viruri\.c$$ exclude_file_name_regexp--sc_prohibit_return_as_function = \.py$$ -_virsh_includes=(edit|domain-monitor) +_virsh_includes=(edit|domain-monitor|domain) exclude_file_name_regexp--sc_require_config_h = ^(examples/|tools/virsh-$(_virsh_includes)\.c$$) exclude_file_name_regexp--sc_require_config_h_first = ^(examples/|tools/virsh-$(_virsh_includes)\.c$$) diff --git a/po/POTFILES.in b/po/POTFILES.in index 64da663de257d3258c3c49efee33bdfaf39a9b17..fb9891dc0127d20d777cae27052775b628426b70 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -178,6 +178,7 @@ tools/console.c tools/libvirt-guests.init.sh tools/virsh.c tools/virsh-domain-monitor.c +tools/virsh-domain.c tools/virsh-edit.c tools/virt-host-validate-common.c tools/virt-host-validate-lxc.c diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c new file mode 100644 index 0000000000000000000000000000000000000000..da918ea79ae124045ee2d9c05fb71ee414d29b39 --- /dev/null +++ b/tools/virsh-domain.c @@ -0,0 +1,8049 @@ +/* + * virsh-domain.c: Commands to manage domain + * + * Copyright (C) 2005, 2007-2012 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; If not, see + * . + * + * Daniel Veillard + * Karel Zak + * Daniel P. Berrange + * + */ + +static const char * +vshDomainVcpuStateToString(int state) +{ + switch (state) { + case VIR_VCPU_OFFLINE: + return N_("offline"); + case VIR_VCPU_BLOCKED: + return N_("idle"); + case VIR_VCPU_RUNNING: + return N_("running"); + default: + ;/*FALLTHROUGH*/ + } + return N_("no state"); +} + +/* + * "attach-device" command + */ +static const vshCmdInfo info_attach_device[] = { + {"help", N_("attach device from an XML file")}, + {"desc", N_("Attach device from an XML .")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_attach_device[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, + {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("XML file")}, + {"persistent", VSH_OT_ALIAS, 0, "config"}, + {"config", VSH_OT_BOOL, 0, N_("affect next boot")}, + {NULL, 0, 0, NULL} +}; + +static bool +cmdAttachDevice(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom; + const char *from = NULL; + char *buffer; + int ret; + unsigned int flags; + + if (!vshConnectionUsability(ctl, ctl->conn)) + return false; + + if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) + return false; + + if (vshCommandOptString(cmd, "file", &from) <= 0) { + virDomainFree(dom); + return false; + } + + if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) { + virshReportError(ctl); + virDomainFree(dom); + return false; + } + + if (vshCommandOptBool(cmd, "config")) { + flags = VIR_DOMAIN_AFFECT_CONFIG; + if (virDomainIsActive(dom) == 1) + flags |= VIR_DOMAIN_AFFECT_LIVE; + ret = virDomainAttachDeviceFlags(dom, buffer, flags); + } else { + ret = virDomainAttachDevice(dom, buffer); + } + VIR_FREE(buffer); + + if (ret < 0) { + vshError(ctl, _("Failed to attach device from %s"), from); + virDomainFree(dom); + return false; + } else { + vshPrint(ctl, "%s", _("Device attached successfully\n")); + } + + virDomainFree(dom); + return true; +} + +/* + * "attach-disk" command + */ +static const vshCmdInfo info_attach_disk[] = { + {"help", N_("attach disk device")}, + {"desc", N_("Attach new disk device.")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_attach_disk[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, + {"source", VSH_OT_DATA, VSH_OFLAG_REQ | VSH_OFLAG_EMPTY_OK, + N_("source of disk device")}, + {"target", VSH_OT_DATA, VSH_OFLAG_REQ, N_("target of disk device")}, + {"driver", VSH_OT_STRING, 0, N_("driver of disk device")}, + {"subdriver", VSH_OT_STRING, 0, N_("subdriver of disk device")}, + {"cache", VSH_OT_STRING, 0, N_("cache mode of disk device")}, + {"type", VSH_OT_STRING, 0, N_("target device type")}, + {"mode", VSH_OT_STRING, 0, N_("mode of device reading and writing")}, + {"persistent", VSH_OT_ALIAS, 0, "config"}, + {"config", VSH_OT_BOOL, 0, N_("affect next boot")}, + {"sourcetype", VSH_OT_STRING, 0, N_("type of source (block|file)")}, + {"serial", VSH_OT_STRING, 0, N_("serial of disk device")}, + {"shareable", VSH_OT_BOOL, 0, N_("shareable between domains")}, + {"rawio", VSH_OT_BOOL, 0, N_("needs rawio capability")}, + {"address", VSH_OT_STRING, 0, N_("address of disk device")}, + {"multifunction", VSH_OT_BOOL, 0, + N_("use multifunction pci under specified address")}, + {NULL, 0, 0, NULL} +}; + +enum { + DISK_ADDR_TYPE_INVALID, + DISK_ADDR_TYPE_PCI, + DISK_ADDR_TYPE_SCSI, + DISK_ADDR_TYPE_IDE, +}; + +struct PCIAddress { + unsigned int domain; + unsigned int bus; + unsigned int slot; + unsigned int function; +}; + +struct SCSIAddress { + unsigned int controller; + unsigned int bus; + unsigned int unit; +}; + +struct IDEAddress { + unsigned int controller; + unsigned int bus; + unsigned int unit; +}; + +struct DiskAddress { + int type; + union { + struct PCIAddress pci; + struct SCSIAddress scsi; + struct IDEAddress ide; + } addr; +}; + +static int str2PCIAddress(const char *str, struct PCIAddress *pciAddr) +{ + char *domain, *bus, *slot, *function; + + if (!pciAddr) + return -1; + if (!str) + return -1; + + domain = (char *)str; + + if (virStrToLong_ui(domain, &bus, 0, &pciAddr->domain) != 0) + return -1; + + bus++; + if (virStrToLong_ui(bus, &slot, 0, &pciAddr->bus) != 0) + return -1; + + slot++; + if (virStrToLong_ui(slot, &function, 0, &pciAddr->slot) != 0) + return -1; + + function++; + if (virStrToLong_ui(function, NULL, 0, &pciAddr->function) != 0) + return -1; + + return 0; +} + +static int str2SCSIAddress(const char *str, struct SCSIAddress *scsiAddr) +{ + char *controller, *bus, *unit; + + if (!scsiAddr) + return -1; + if (!str) + return -1; + + controller = (char *)str; + + if (virStrToLong_ui(controller, &bus, 0, &scsiAddr->controller) != 0) + return -1; + + bus++; + if (virStrToLong_ui(bus, &unit, 0, &scsiAddr->bus) != 0) + return -1; + + unit++; + if (virStrToLong_ui(unit, NULL, 0, &scsiAddr->unit) != 0) + return -1; + + return 0; +} + +static int str2IDEAddress(const char *str, struct IDEAddress *ideAddr) +{ + char *controller, *bus, *unit; + + if (!ideAddr) + return -1; + if (!str) + return -1; + + controller = (char *)str; + + if (virStrToLong_ui(controller, &bus, 0, &ideAddr->controller) != 0) + return -1; + + bus++; + if (virStrToLong_ui(bus, &unit, 0, &ideAddr->bus) != 0) + return -1; + + unit++; + if (virStrToLong_ui(unit, NULL, 0, &ideAddr->unit) != 0) + return -1; + + return 0; +} + +/* pci address pci:0000.00.0x0a.0 (domain:bus:slot:function) + * ide disk address: ide:00.00.0 (controller:bus:unit) + * scsi disk address: scsi:00.00.0 (controller:bus:unit) + */ + +static int str2DiskAddress(const char *str, struct DiskAddress *diskAddr) +{ + char *type, *addr; + + if (!diskAddr) + return -1; + if (!str) + return -1; + + type = (char *)str; + addr = strchr(type, ':'); + if (!addr) + return -1; + + if (STREQLEN(type, "pci", addr - type)) { + diskAddr->type = DISK_ADDR_TYPE_PCI; + return str2PCIAddress(addr + 1, &diskAddr->addr.pci); + } else if (STREQLEN(type, "scsi", addr - type)) { + diskAddr->type = DISK_ADDR_TYPE_SCSI; + return str2SCSIAddress(addr + 1, &diskAddr->addr.scsi); + } else if (STREQLEN(type, "ide", addr - type)) { + diskAddr->type = DISK_ADDR_TYPE_IDE; + return str2IDEAddress(addr + 1, &diskAddr->addr.ide); + } + + return -1; +} + +static bool +cmdAttachDisk(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom = NULL; + const char *source = NULL, *target = NULL, *driver = NULL, + *subdriver = NULL, *type = NULL, *mode = NULL, + *cache = NULL, *serial = NULL, *straddr = NULL; + struct DiskAddress diskAddr; + bool isFile = false, functionReturn = false; + int ret; + unsigned int flags; + const char *stype = NULL; + virBuffer buf = VIR_BUFFER_INITIALIZER; + char *xml; + struct stat st; + + if (!vshConnectionUsability(ctl, ctl->conn)) + goto cleanup; + + if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) + goto cleanup; + + if (vshCommandOptString(cmd, "source", &source) <= 0) + goto cleanup; + /* Allow empty string as a placeholder that implies no source, for + * use in adding a cdrom drive with no disk. */ + if (!*source) + source = NULL; + + if (vshCommandOptString(cmd, "target", &target) <= 0) + goto cleanup; + + if (vshCommandOptString(cmd, "driver", &driver) < 0 || + vshCommandOptString(cmd, "subdriver", &subdriver) < 0 || + vshCommandOptString(cmd, "type", &type) < 0 || + vshCommandOptString(cmd, "mode", &mode) < 0 || + vshCommandOptString(cmd, "cache", &cache) < 0 || + vshCommandOptString(cmd, "serial", &serial) < 0 || + vshCommandOptString(cmd, "address", &straddr) < 0 || + vshCommandOptString(cmd, "sourcetype", &stype) < 0) { + vshError(ctl, "%s", _("missing option")); + goto cleanup; + } + + if (!stype) { + if (driver && (STREQ(driver, "file") || STREQ(driver, "tap"))) { + isFile = true; + } else { + if (source && !stat(source, &st)) + isFile = S_ISREG(st.st_mode) ? true : false; + } + } else if (STREQ(stype, "file")) { + isFile = true; + } else if (STRNEQ(stype, "block")) { + vshError(ctl, _("Unknown source type: '%s'"), stype); + goto cleanup; + } + + if (mode) { + if (STRNEQ(mode, "readonly") && STRNEQ(mode, "shareable")) { + vshError(ctl, _("No support for %s in command 'attach-disk'"), + mode); + goto cleanup; + } + } + + /* Make XML of disk */ + virBufferAsprintf(&buf, "\n"); + + if (driver || subdriver || cache) { + virBufferAsprintf(&buf, " \n"); + } + + if (source) + virBufferAsprintf(&buf, " \n", + (isFile) ? "file" : "dev", + source); + virBufferAsprintf(&buf, " \n", target); + if (mode) + virBufferAsprintf(&buf, " <%s/>\n", mode); + + if (serial) + virBufferAsprintf(&buf, " %s\n", serial); + + if (vshCommandOptBool(cmd, "shareable")) + virBufferAsprintf(&buf, " \n"); + + if (straddr) { + if (str2DiskAddress(straddr, &diskAddr) != 0) { + vshError(ctl, _("Invalid address.")); + goto cleanup; + } + + if (STRPREFIX((const char *)target, "vd")) { + if (diskAddr.type == DISK_ADDR_TYPE_PCI) { + virBufferAsprintf(&buf, + "
\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, + "
\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, + "
\n", + diskAddr.addr.ide.controller, diskAddr.addr.ide.bus, + diskAddr.addr.ide.unit); + } else { + vshError(ctl, "%s", _("expecting an ide:00.00.00 address.")); + goto cleanup; + } + } + } + + virBufferAddLit(&buf, "\n"); + + if (virBufferError(&buf)) { + vshPrint(ctl, "%s", _("Failed to allocate XML buffer")); + return false; + } + + xml = virBufferContentAndReset(&buf); + + if (vshCommandOptBool(cmd, "config")) { + flags = VIR_DOMAIN_AFFECT_CONFIG; + if (virDomainIsActive(dom) == 1) + flags |= VIR_DOMAIN_AFFECT_LIVE; + ret = virDomainAttachDeviceFlags(dom, xml, flags); + } else { + ret = virDomainAttachDevice(dom, xml); + } + + VIR_FREE(xml); + + if (ret != 0) { + vshError(ctl, "%s", _("Failed to attach disk")); + } else { + vshPrint(ctl, "%s", _("Disk attached successfully\n")); + functionReturn = true; + } + + cleanup: + if (dom) + virDomainFree(dom); + virBufferFreeAndReset(&buf); + return functionReturn; +} + +/* + * "attach-interface" command + */ +static const vshCmdInfo info_attach_interface[] = { + {"help", N_("attach network interface")}, + {"desc", N_("Attach new network interface.")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_attach_interface[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, + {"type", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network interface type")}, + {"source", VSH_OT_DATA, VSH_OFLAG_REQ, N_("source of network interface")}, + {"target", VSH_OT_DATA, 0, N_("target network name")}, + {"mac", VSH_OT_DATA, 0, N_("MAC address")}, + {"script", VSH_OT_DATA, 0, N_("script used to bridge network interface")}, + {"model", VSH_OT_DATA, 0, N_("model type")}, + {"persistent", VSH_OT_ALIAS, 0, "config"}, + {"config", VSH_OT_BOOL, 0, N_("affect next boot")}, + {"inbound", VSH_OT_DATA, VSH_OFLAG_NONE, N_("control domain's incoming traffics")}, + {"outbound", VSH_OT_DATA, VSH_OFLAG_NONE, N_("control domain's outgoing traffics")}, + {NULL, 0, 0, NULL} +}; + +/* parse inbound and outbound which are in the format of + * 'average,peak,burst', in which peak and burst are optional, + * thus 'average,,burst' and 'average,peak' are also legal. */ +static int parseRateStr(const char *rateStr, virNetDevBandwidthRatePtr rate) +{ + const char *average = NULL; + char *peak = NULL, *burst = NULL; + + average = rateStr; + if (!average) + return -1; + if (virStrToLong_ull(average, &peak, 10, &rate->average) < 0) + return -1; + + /* peak will be updated to point to the end of rateStr in case + * of 'average' */ + if (peak && *peak != '\0') { + burst = strchr(peak + 1, ','); + if (!(burst && (burst - peak == 1))) { + if (virStrToLong_ull(peak + 1, &burst, 10, &rate->peak) < 0) + return -1; + } + + /* burst will be updated to point to the end of rateStr in case + * of 'average,peak' */ + if (burst && *burst != '\0') { + if (virStrToLong_ull(burst + 1, NULL, 10, &rate->burst) < 0) + return -1; + } + } + + + return 0; +} + +static bool +cmdAttachInterface(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom = NULL; + const char *mac = NULL, *target = NULL, *script = NULL, + *type = NULL, *source = NULL, *model = NULL, + *inboundStr = NULL, *outboundStr = NULL; + virNetDevBandwidthRate inbound, outbound; + int typ; + int ret; + bool functionReturn = false; + unsigned int flags; + virBuffer buf = VIR_BUFFER_INITIALIZER; + char *xml; + + if (!vshConnectionUsability(ctl, ctl->conn)) + goto cleanup; + + if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) + goto cleanup; + + if (vshCommandOptString(cmd, "type", &type) <= 0) + goto cleanup; + + if (vshCommandOptString(cmd, "source", &source) < 0 || + vshCommandOptString(cmd, "target", &target) < 0 || + vshCommandOptString(cmd, "mac", &mac) < 0 || + vshCommandOptString(cmd, "script", &script) < 0 || + vshCommandOptString(cmd, "model", &model) < 0 || + vshCommandOptString(cmd, "inbound", &inboundStr) < 0 || + vshCommandOptString(cmd, "outbound", &outboundStr) < 0) { + vshError(ctl, "missing argument"); + goto cleanup; + } + + /* check interface type */ + if (STREQ(type, "network")) { + typ = 1; + } else if (STREQ(type, "bridge")) { + typ = 2; + } else { + vshError(ctl, _("No support for %s in command 'attach-interface'"), + type); + goto cleanup; + } + + if (inboundStr) { + memset(&inbound, 0, sizeof(inbound)); + if (parseRateStr(inboundStr, &inbound) < 0) { + vshError(ctl, _("inbound format is incorrect")); + goto cleanup; + } + if (inbound.average == 0) { + vshError(ctl, _("inbound average is mandatory")); + goto cleanup; + } + } + if (outboundStr) { + memset(&outbound, 0, sizeof(outbound)); + if (parseRateStr(outboundStr, &outbound) < 0) { + vshError(ctl, _("outbound format is incorrect")); + goto cleanup; + } + if (outbound.average == 0) { + vshError(ctl, _("outbound average is mandatory")); + goto cleanup; + } + } + + /* Make XML of interface */ + virBufferAsprintf(&buf, "\n", type); + + if (typ == 1) + virBufferAsprintf(&buf, " \n", source); + else if (typ == 2) + virBufferAsprintf(&buf, " \n", source); + + if (target != NULL) + virBufferAsprintf(&buf, " \n", target); + if (mac != NULL) + virBufferAsprintf(&buf, " \n", mac); + if (script != NULL) + virBufferAsprintf(&buf, "