|
- /*
- * 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
- * <http://www.gnu.org/licenses/>.
- */
-
- #include <config.h>
- #include "virsh-domain.h"
- #include "virsh-util.h"
-
- #include <fcntl.h>
- #include <signal.h>
- #include <sys/time.h>
-
- #include <libxml/parser.h>
- #include <libxml/tree.h>
- #include <libxml/xpath.h>
- #include <libxml/xmlsave.h>
-
- #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"
- #include "virutil.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 <file>.")
- },
- {.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, "<disk type='%s'",
- isFile ? "file" : "block");
- if (type)
- virBufferAsprintf(&buf, " device='%s'", type);
- if (vshCommandOptBool(cmd, "rawio"))
- virBufferAddLit(&buf, " rawio='yes'");
- virBufferAddLit(&buf, ">\n");
- virBufferAdjustIndent(&buf, 2);
-
- if (driver || subdriver || iothread || cache || io) {
- virBufferAddLit(&buf, "<driver");
-
- if (driver)
- virBufferAsprintf(&buf, " name='%s'", driver);
- if (subdriver)
- virBufferAsprintf(&buf, " type='%s'", subdriver);
- if (iothread)
- virBufferAsprintf(&buf, " iothread='%s'", iothread);
- if (cache)
- virBufferAsprintf(&buf, " cache='%s'", cache);
- if (io)
- virBufferAsprintf(&buf, " io='%s'", io);
-
- virBufferAddLit(&buf, "/>\n");
- }
-
- if (source)
- virBufferAsprintf(&buf, "<source %s='%s'/>\n",
- isFile ? "file" : "dev", source);
- virBufferAsprintf(&buf, "<target dev='%s'", target);
- if (targetbus)
- virBufferAsprintf(&buf, " bus='%s'", targetbus);
- virBufferAddLit(&buf, "/>\n");
-
- if (mode)
- virBufferAsprintf(&buf, "<%s/>\n", mode);
-
- if (serial)
- virBufferAsprintf(&buf, "<serial>%s</serial>\n", serial);
-
- if (alias)
- virBufferAsprintf(&buf, "<alias name='%s'/>\n", alias);
-
- if (wwn)
- virBufferAsprintf(&buf, "<wwn>%s</wwn>\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,
- "<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 if (diskAddr.type == DISK_ADDR_TYPE_CCW) {
- virBufferAsprintf(&buf,
- "<address type='ccw' cssid='0x%02x'"
- " ssid='0x%01x' devno='0x%04x' />\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,
- "<address type='drive' controller='%u'"
- " bus='%u' unit='%llu' />\n",
- diskAddr.addr.scsi.controller, diskAddr.addr.scsi.bus,
- diskAddr.addr.scsi.unit);
- } else if (diskAddr.type == DISK_ADDR_TYPE_USB) {
- virBufferAsprintf(&buf,
- "<address type='usb' bus='%u' port='%u' />\n",
- diskAddr.addr.usb.bus, diskAddr.addr.usb.port);
- } else if (diskAddr.type == DISK_ADDR_TYPE_SATA) {
- virBufferAsprintf(&buf,
- "<address type='drive' controller='%u'"
- " bus='%u' unit='%llu' />\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,
- "<address type='drive' controller='%u'"
- " bus='%u' unit='%u' />\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, "</disk>\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, "<interface type='%s'", type);
-
- if (managed)
- virBufferAddLit(&buf, " managed='yes'>\n");
- else
- virBufferAddLit(&buf, ">\n");
- virBufferAdjustIndent(&buf, 2);
-
- switch (typ) {
- case VIR_DOMAIN_NET_TYPE_NETWORK:
- case VIR_DOMAIN_NET_TYPE_BRIDGE:
- virBufferAsprintf(&buf, "<source %s='%s'/>\n",
- virDomainNetTypeToString(typ), source);
- break;
- case VIR_DOMAIN_NET_TYPE_DIRECT:
- virBufferAsprintf(&buf, "<source dev='%s'/>\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, "<source>\n");
- virBufferAdjustIndent(&buf, 2);
- virBufferAsprintf(&buf, "<address type='pci' domain='0x%04x'"
- " bus='0x%02x' slot='0x%02x' function='0x%d'/>\n",
- pciAddr.domain, pciAddr.bus,
- pciAddr.slot, pciAddr.function);
- virBufferAdjustIndent(&buf, -2);
- virBufferAddLit(&buf, "</source>\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, "<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 (alias != NULL)
- virBufferAsprintf(&buf, "<alias name='%s'/>\n", alias);
-
- if (inboundStr || outboundStr) {
- virBufferAddLit(&buf, "<bandwidth>\n");
- virBufferAdjustIndent(&buf, 2);
- if (inboundStr && (inbound.average || inbound.floor)) {
- virBufferAddLit(&buf, "<inbound");
- if (inbound.average > 0)
- virBufferAsprintf(&buf, " average='%llu'", inbound.average);
- if (inbound.peak > 0)
- virBufferAsprintf(&buf, " peak='%llu'", inbound.peak);
- if (inbound.burst > 0)
- virBufferAsprintf(&buf, " burst='%llu'", inbound.burst);
- if (inbound.floor > 0)
- virBufferAsprintf(&buf, " floor='%llu'", inbound.floor);
- virBufferAddLit(&buf, "/>\n");
- }
- if (outboundStr && outbound.average > 0) {
- virBufferAsprintf(&buf, "<outbound average='%llu'", outbound.average);
- if (outbound.peak > 0)
- virBufferAsprintf(&buf, " peak='%llu'", outbound.peak);
- if (outbound.burst > 0)
- virBufferAsprintf(&buf, " burst='%llu'", outbound.burst);
- virBufferAddLit(&buf, "/>\n");
- }
- virBufferAdjustIndent(&buf, -2);
- virBufferAddLit(&buf, "</bandwidth>\n");
- }
-
- virBufferAdjustIndent(&buf, -2);
- virBufferAddLit(&buf, "</interface>\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 interface"));
- } else {
- vshPrintExtra(ctl, "%s", _("Interface attached successfully\n"));
- functionReturn = true;
- }
-
- cleanup:
- VIR_FREE(xml);
- virshDomainFree(dom);
- virBufferFreeAndReset(&buf);
- return functionReturn;
- }
-
- /*
- * "autostart" command
- */
- static const vshCmdInfo info_autostart[] = {
- {.name = "help",
- .data = N_("autostart a domain")
- },
- {.name = "desc",
- .data = N_("Configure a domain to be automatically started at boot.")
- },
- {.name = NULL}
- };
-
- static const vshCmdOptDef opts_autostart[] = {
- VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_PERSISTENT),
- {.name = "disable",
- .type = VSH_OT_BOOL,
- .help = N_("disable autostarting")
- },
- {.name = NULL}
- };
-
- static bool
- cmdAutostart(vshControl *ctl, const vshCmd *cmd)
- {
- virDomainPtr dom;
- const char *name;
- int autostart;
-
- if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
- return false;
-
- autostart = !vshCommandOptBool(cmd, "disable");
-
- if (virDomainSetAutostart(dom, autostart) < 0) {
- if (autostart)
- vshError(ctl, _("Failed to mark domain %s as autostarted"), name);
- else
- vshError(ctl, _("Failed to unmark domain %s as autostarted"), name);
- virshDomainFree(dom);
- return false;
- }
-
- if (autostart)
- vshPrintExtra(ctl, _("Domain %s marked as autostarted\n"), name);
- else
- vshPrintExtra(ctl, _("Domain %s unmarked as autostarted\n"), name);
-
- virshDomainFree(dom);
- return true;
- }
-
- /*
- * "blkdeviotune" command
- */
- static const vshCmdInfo info_blkdeviotune[] = {
- {.name = "help",
- .data = N_("Set or query a block device I/O tuning parameters.")
- },
- {.name = "desc",
- .data = N_("Set or query disk I/O parameters such as block throttling.")
- },
- {.name = NULL}
- };
-
- static const vshCmdOptDef opts_blkdeviotune[] = {
- VIRSH_COMMON_OPT_DOMAIN_FULL(0),
- {.name = "device",
- .type = VSH_OT_DATA,
- .flags = VSH_OFLAG_REQ,
- .completer = virshDomainDiskTargetCompleter,
- .help = N_("block device")
- },
- {.name = "total_bytes_sec",
- .type = VSH_OT_ALIAS,
- .help = "total-bytes-sec"
- },
- {.name = "total-bytes-sec",
- .type = VSH_OT_INT,
- .help = N_("total throughput limit, as scaled integer (default bytes)")
- },
- {.name = "read_bytes_sec",
- .type = VSH_OT_ALIAS,
- .help = "read-bytes-sec"
- },
- {.name = "read-bytes-sec",
- .type = VSH_OT_INT,
- .help = N_("read throughput limit, as scaled integer (default bytes)")
- },
- {.name = "write_bytes_sec",
- .type = VSH_OT_ALIAS,
- .help = "write-bytes-sec"
- },
- {.name = "write-bytes-sec",
- .type = VSH_OT_INT,
- .help = N_("write throughput limit, as scaled integer (default bytes)")
- },
- {.name = "total_iops_sec",
- .type = VSH_OT_ALIAS,
- .help = "total-iops-sec"
- },
- {.name = "total-iops-sec",
- .type = VSH_OT_INT,
- .help = N_("total I/O operations limit per second")
- },
- {.name = "read_iops_sec",
- .type = VSH_OT_ALIAS,
- .help = "read-iops-sec"
- },
- {.name = "read-iops-sec",
- .type = VSH_OT_INT,
- .help = N_("read I/O operations limit per second")
- },
- {.name = "write_iops_sec",
- .type = VSH_OT_ALIAS,
- .help = "write-iops-sec"
- },
- {.name = "write-iops-sec",
- .type = VSH_OT_INT,
- .help = N_("write I/O operations limit per second")
- },
- {.name = "total_bytes_sec_max",
- .type = VSH_OT_ALIAS,
- .help = "total-bytes-sec-max"
- },
- {.name = "total-bytes-sec-max",
- .type = VSH_OT_INT,
- .help = N_("total max, as scaled integer (default bytes)")
- },
- {.name = "read_bytes_sec_max",
- .type = VSH_OT_ALIAS,
- .help = "read-bytes-sec-max"
- },
- {.name = "read-bytes-sec-max",
- .type = VSH_OT_INT,
- .help = N_("read max, as scaled integer (default bytes)")
- },
- {.name = "write_bytes_sec_max",
- .type = VSH_OT_ALIAS,
- .help = "write-bytes-sec-max"
- },
- {.name = "write-bytes-sec-max",
- .type = VSH_OT_INT,
- .help = N_("write max, as scaled integer (default bytes)")
- },
- {.name = "total_iops_sec_max",
- .type = VSH_OT_ALIAS,
- .help = "total-iops-sec-max"
- },
- {.name = "total-iops-sec-max",
- .type = VSH_OT_INT,
- .help = N_("total I/O operations max")
- },
- {.name = "read_iops_sec_max",
- .type = VSH_OT_ALIAS,
- .help = "read-iops-sec-max"
- },
- {.name = "read-iops-sec-max",
- .type = VSH_OT_INT,
- .help = N_("read I/O operations max")
- },
- {.name = "write_iops_sec_max",
- .type = VSH_OT_ALIAS,
- .help = "write-iops-sec-max"
- },
- {.name = "write-iops-sec-max",
- .type = VSH_OT_INT,
- .help = N_("write I/O operations max")
- },
- {.name = "size_iops_sec",
- .type = VSH_OT_ALIAS,
- .help = "size-iops-sec"
- },
- {.name = "size-iops-sec",
- .type = VSH_OT_INT,
- .help = N_("I/O size in bytes")
- },
- {.name = "group_name",
- .type = VSH_OT_ALIAS,
- .help = "group-name"
- },
- {.name = "group-name",
- .type = VSH_OT_STRING,
- .help = N_("group name to share I/O quota between multiple drives")
- },
- {.name = "total_bytes_sec_max_length",
- .type = VSH_OT_ALIAS,
- .help = "total-bytes-sec-max-length"
- },
- {.name = "total-bytes-sec-max-length",
- .type = VSH_OT_INT,
- .help = N_("duration in seconds to allow total max bytes")
- },
- {.name = "read_bytes_sec_max_length",
- .type = VSH_OT_ALIAS,
- .help = "read-bytes-sec-max-length"
- },
- {.name = "read-bytes-sec-max-length",
- .type = VSH_OT_INT,
- .help = N_("duration in seconds to allow read max bytes")
- },
- {.name = "write_bytes_sec_max_length",
- .type = VSH_OT_ALIAS,
- .help = "write-bytes-sec-max-length"
- },
- {.name = "write-bytes-sec-max-length",
- .type = VSH_OT_INT,
- .help = N_("duration in seconds to allow write max bytes")
- },
- {.name = "total_iops_sec_max_length",
- .type = VSH_OT_ALIAS,
- .help = "total-iops-sec-max-length"
- },
- {.name = "total-iops-sec-max-length",
- .type = VSH_OT_INT,
- .help = N_("duration in seconds to allow total I/O operations max")
- },
- {.name = "read_iops_sec_max_length",
- .type = VSH_OT_ALIAS,
- .help = "read-iops-sec-max-length"
- },
- {.name = "read-iops-sec-max-length",
- .type = VSH_OT_INT,
- .help = N_("duration in seconds to allow read I/O operations max")
- },
- {.name = "write_iops_sec_max_length",
- .type = VSH_OT_ALIAS,
- .help = "write-iops-sec-max-length"
- },
- {.name = "write-iops-sec-max-length",
- .type = VSH_OT_INT,
- .help = N_("duration in seconds to allow write I/O operations max")
- },
- VIRSH_COMMON_OPT_DOMAIN_CONFIG,
- VIRSH_COMMON_OPT_DOMAIN_LIVE,
- VIRSH_COMMON_OPT_DOMAIN_CURRENT,
- {.name = NULL}
- };
-
- static bool
- cmdBlkdeviotune(vshControl *ctl, const vshCmd *cmd)
- {
- virDomainPtr dom = NULL;
- const char *name, *disk;
- const char *group_name = NULL;
- unsigned long long value;
- int nparams = 0;
- int maxparams = 0;
- virTypedParameterPtr params = NULL;
- unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
- size_t i;
- int rv = 0;
- bool current = vshCommandOptBool(cmd, "current");
- bool config = vshCommandOptBool(cmd, "config");
- bool live = vshCommandOptBool(cmd, "live");
- bool ret = false;
-
- VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
- VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
-
- if (config)
- flags |= VIR_DOMAIN_AFFECT_CONFIG;
- if (live)
- flags |= VIR_DOMAIN_AFFECT_LIVE;
-
- if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
- goto cleanup;
-
- if (vshCommandOptStringReq(ctl, cmd, "device", &disk) < 0)
- goto cleanup;
-
- #define VSH_ADD_IOTUNE_SCALED(PARAM, CONST) \
- if ((rv = vshCommandOptScaledInt(ctl, cmd, #PARAM, &value, \
- 1, ULLONG_MAX)) < 0) { \
- goto interror; \
- } else if (rv > 0) { \
- if (virTypedParamsAddULLong(¶ms, &nparams, &maxparams, \
- VIR_DOMAIN_BLOCK_IOTUNE_##CONST, \
- value) < 0) \
- goto save_error; \
- }
-
- VSH_ADD_IOTUNE_SCALED(total-bytes-sec, TOTAL_BYTES_SEC);
- VSH_ADD_IOTUNE_SCALED(read-bytes-sec, READ_BYTES_SEC);
- VSH_ADD_IOTUNE_SCALED(write-bytes-sec, WRITE_BYTES_SEC);
- VSH_ADD_IOTUNE_SCALED(total-bytes-sec-max, TOTAL_BYTES_SEC_MAX);
- VSH_ADD_IOTUNE_SCALED(read-bytes-sec-max, READ_BYTES_SEC_MAX);
- VSH_ADD_IOTUNE_SCALED(write-bytes-sec-max, WRITE_BYTES_SEC_MAX);
- #undef VSH_ADD_IOTUNE_SCALED
-
- #define VSH_ADD_IOTUNE(PARAM, CONST) \
- if ((rv = vshCommandOptULongLong(ctl, cmd, #PARAM, &value)) < 0) { \
- goto interror; \
- } else if (rv > 0) { \
- if (virTypedParamsAddULLong(¶ms, &nparams, &maxparams, \
- VIR_DOMAIN_BLOCK_IOTUNE_##CONST, \
- value) < 0) \
- goto save_error; \
- }
-
- VSH_ADD_IOTUNE(total-iops-sec, TOTAL_IOPS_SEC);
- VSH_ADD_IOTUNE(read-iops-sec, READ_IOPS_SEC);
- VSH_ADD_IOTUNE(write-iops-sec, WRITE_IOPS_SEC);
- VSH_ADD_IOTUNE(total-iops-sec-max, TOTAL_IOPS_SEC_MAX);
- VSH_ADD_IOTUNE(read-iops-sec-max, READ_IOPS_SEC_MAX);
- VSH_ADD_IOTUNE(write-iops-sec-max, WRITE_IOPS_SEC_MAX);
- VSH_ADD_IOTUNE(size-iops-sec, SIZE_IOPS_SEC);
-
- VSH_ADD_IOTUNE(total-bytes-sec-max-length, TOTAL_BYTES_SEC_MAX_LENGTH);
- VSH_ADD_IOTUNE(read-bytes-sec-max-length, READ_BYTES_SEC_MAX_LENGTH);
- VSH_ADD_IOTUNE(write-bytes-sec-max-length, WRITE_BYTES_SEC_MAX_LENGTH);
- VSH_ADD_IOTUNE(total-iops-sec-max-length, TOTAL_IOPS_SEC_MAX_LENGTH);
- VSH_ADD_IOTUNE(read-iops-sec-max-length, READ_IOPS_SEC_MAX_LENGTH);
- VSH_ADD_IOTUNE(write-iops-sec-max-length, WRITE_IOPS_SEC_MAX_LENGTH);
- #undef VSH_ADD_IOTUNE
-
- if (vshCommandOptStringReq(ctl, cmd, "group-name", &group_name) < 0) {
- vshError(ctl, "%s", _("Unable to parse group-name parameter"));
- goto cleanup;
- }
-
- if (group_name) {
- if (virTypedParamsAddString(¶ms, &nparams, &maxparams,
- VIR_DOMAIN_BLOCK_IOTUNE_GROUP_NAME,
- group_name) < 0)
- goto save_error;
- }
-
-
- if (nparams == 0) {
- if (virDomainGetBlockIoTune(dom, NULL, NULL, &nparams, flags) != 0) {
- vshError(ctl, "%s",
- _("Unable to get number of block I/O throttle parameters"));
- goto cleanup;
- }
-
- if (nparams == 0) {
- ret = true;
- goto cleanup;
- }
-
- params = vshCalloc(ctl, nparams, sizeof(*params));
-
- if (virDomainGetBlockIoTune(dom, disk, params, &nparams, flags) != 0) {
- vshError(ctl, "%s",
- _("Unable to get block I/O throttle parameters"));
- goto cleanup;
- }
-
- for (i = 0; i < nparams; i++) {
- char *str = vshGetTypedParamValue(ctl, ¶ms[i]);
- vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
- VIR_FREE(str);
- }
- } else {
- if (virDomainSetBlockIoTune(dom, disk, params, nparams, flags) < 0)
- goto error;
- }
-
- ret = true;
-
- cleanup:
- virTypedParamsFree(params, nparams);
- virshDomainFree(dom);
- return ret;
-
- save_error:
- vshSaveLibvirtError();
- error:
- vshError(ctl, "%s", _("Unable to change block I/O throttle"));
- goto cleanup;
-
- interror:
- vshError(ctl, "%s", _("Unable to parse integer parameter"));
- goto cleanup;
- }
-
- /*
- * "blkiotune" command
- */
- static const vshCmdInfo info_blkiotune[] = {
- {.name = "help",
- .data = N_("Get or set blkio parameters")
- },
- {.name = "desc",
- .data = N_("Get or set the current blkio parameters for a guest"
- " domain.\n"
- " To get the blkio parameters use following command: \n\n"
- " virsh # blkiotune <domain>")
- },
- {.name = NULL}
- };
-
- static const vshCmdOptDef opts_blkiotune[] = {
- VIRSH_COMMON_OPT_DOMAIN_FULL(0),
- {.name = "weight",
- .type = VSH_OT_INT,
- .help = N_("IO Weight")
- },
- {.name = "device-weights",
- .type = VSH_OT_STRING,
- .help = N_("per-device IO Weights, in the form of /path/to/device,weight,...")
- },
- {.name = "device-read-iops-sec",
- .type = VSH_OT_STRING,
- .help = N_("per-device read I/O limit per second, in the form of /path/to/device,read_iops_sec,...")
- },
- {.name = "device-write-iops-sec",
- .type = VSH_OT_STRING,
- .help = N_("per-device write I/O limit per second, in the form of /path/to/device,write_iops_sec,...")
- },
- {.name = "device-read-bytes-sec",
- .type = VSH_OT_STRING,
- .help = N_("per-device bytes read per second, in the form of /path/to/device,read_bytes_sec,...")
- },
- {.name = "device-write-bytes-sec",
- .type = VSH_OT_STRING,
- .help = N_("per-device bytes wrote per second, in the form of /path/to/device,write_bytes_sec,...")
- },
- VIRSH_COMMON_OPT_DOMAIN_CONFIG,
- VIRSH_COMMON_OPT_DOMAIN_LIVE,
- VIRSH_COMMON_OPT_DOMAIN_CURRENT,
- {.name = NULL}
- };
-
- static bool
- cmdBlkiotune(vshControl * ctl, const vshCmd * cmd)
- {
- virDomainPtr dom;
- const char *device_weight = NULL;
- const char *device_riops = NULL;
- const char *device_wiops = NULL;
- const char *device_rbps = NULL;
- const char *device_wbps = NULL;
- int weight = 0;
- int nparams = 0;
- int maxparams = 0;
- int rv = 0;
- size_t i;
- virTypedParameterPtr params = NULL;
- 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");
-
- VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
- VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
-
- if (config)
- flags |= VIR_DOMAIN_AFFECT_CONFIG;
- if (live)
- flags |= VIR_DOMAIN_AFFECT_LIVE;
-
- if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
- return false;
-
- if ((rv = vshCommandOptInt(ctl, cmd, "weight", &weight)) < 0) {
- goto cleanup;
- } else if (rv > 0) {
- if (weight <= 0) {
- vshError(ctl, _("Invalid value of %d for I/O weight"), weight);
- goto cleanup;
- }
- if (virTypedParamsAddUInt(¶ms, &nparams, &maxparams,
- VIR_DOMAIN_BLKIO_WEIGHT, weight) < 0)
- goto save_error;
- }
-
- rv = vshCommandOptStringQuiet(ctl, cmd, "device-weights", &device_weight);
- if (rv < 0) {
- vshError(ctl, "%s", _("Unable to parse string parameter"));
- goto cleanup;
- } else if (rv > 0) {
- if (virTypedParamsAddString(¶ms, &nparams, &maxparams,
- VIR_DOMAIN_BLKIO_DEVICE_WEIGHT,
- device_weight) < 0)
- goto save_error;
- }
-
- rv = vshCommandOptStringQuiet(ctl, cmd, "device-read-iops-sec", &device_riops);
- if (rv < 0) {
- vshError(ctl, "%s", _("Unable to parse string parameter"));
- goto cleanup;
- } else if (rv > 0) {
- if (virTypedParamsAddString(¶ms, &nparams, &maxparams,
- VIR_DOMAIN_BLKIO_DEVICE_READ_IOPS,
- device_riops) < 0)
- goto save_error;
- }
-
- rv = vshCommandOptStringQuiet(ctl, cmd, "device-write-iops-sec", &device_wiops);
- if (rv < 0) {
- vshError(ctl, "%s", _("Unable to parse string parameter"));
- goto cleanup;
- } else if (rv > 0) {
- if (virTypedParamsAddString(¶ms, &nparams, &maxparams,
- VIR_DOMAIN_BLKIO_DEVICE_WRITE_IOPS,
- device_wiops) < 0)
- goto save_error;
- }
-
- rv = vshCommandOptStringQuiet(ctl, cmd, "device-read-bytes-sec", &device_rbps);
- if (rv < 0) {
- vshError(ctl, "%s", _("Unable to parse string parameter"));
- goto cleanup;
- } else if (rv > 0) {
- if (virTypedParamsAddString(¶ms, &nparams, &maxparams,
- VIR_DOMAIN_BLKIO_DEVICE_READ_BPS,
- device_rbps) < 0)
- goto save_error;
- }
-
- rv = vshCommandOptStringQuiet(ctl, cmd, "device-write-bytes-sec", &device_wbps);
- if (rv < 0) {
- vshError(ctl, "%s", _("Unable to parse string parameter"));
- goto cleanup;
- } else if (rv > 0) {
- if (virTypedParamsAddString(¶ms, &nparams, &maxparams,
- VIR_DOMAIN_BLKIO_DEVICE_WRITE_BPS,
- device_wbps) < 0)
- goto save_error;
- }
-
- if (nparams == 0) {
- /* get the number of blkio parameters */
- if (virDomainGetBlkioParameters(dom, NULL, &nparams, flags) != 0) {
- vshError(ctl, "%s",
- _("Unable to get number of blkio parameters"));
- goto cleanup;
- }
-
- if (nparams == 0) {
- /* nothing to output */
- ret = true;
- goto cleanup;
- }
-
- /* now go get all the blkio parameters */
- params = vshCalloc(ctl, nparams, sizeof(*params));
- if (virDomainGetBlkioParameters(dom, params, &nparams, flags) != 0) {
- vshError(ctl, "%s", _("Unable to get blkio parameters"));
- goto cleanup;
- }
-
- for (i = 0; i < nparams; i++) {
- char *str = vshGetTypedParamValue(ctl, ¶ms[i]);
- vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
- VIR_FREE(str);
- }
- } else {
- /* set the blkio parameters */
- if (virDomainSetBlkioParameters(dom, params, nparams, flags) < 0)
- goto error;
- }
-
- ret = true;
-
- cleanup:
- virTypedParamsFree(params, nparams);
- virshDomainFree(dom);
- return ret;
-
- save_error:
- vshSaveLibvirtError();
- error:
- vshError(ctl, "%s", _("Unable to change blkio parameters"));
- goto cleanup;
- }
-
-
- static void
- virshPrintJobProgress(const char *label, unsigned long long remaining,
- unsigned long long total)
- {
- int progress;
-
- if (remaining == 0) {
- /* migration has completed */
- progress = 100;
- } else {
- /* use float to avoid overflow */
- progress = (int)(100.0 - remaining * 100.0 / total);
- if (progress >= 100) {
- /* migration has not completed, do not print [100 %] */
- progress = 99;
- }
- }
-
- /* see comments in vshError about why we must flush */
- fflush(stdout);
- fprintf(stderr, "\r%s: [%3d %%]", label, progress);
- fflush(stderr);
- }
-
- static volatile sig_atomic_t intCaught;
-
- #ifndef WIN32
- static void virshCatchInt(int sig G_GNUC_UNUSED,
- siginfo_t *siginfo G_GNUC_UNUSED,
- void *context G_GNUC_UNUSED)
- {
- intCaught = 1;
- }
- #endif /* !WIN32 */
-
-
- typedef struct _virshBlockJobWaitData virshBlockJobWaitData;
- typedef virshBlockJobWaitData *virshBlockJobWaitDataPtr;
- struct _virshBlockJobWaitData {
- vshControl *ctl;
- virDomainPtr dom;
- const char *dev;
- const char *job_name;
-
- bool verbose;
- unsigned int timeout;
- bool async_abort;
-
- int cb_id;
- int cb_id2;
- int status;
- };
-
-
- static void
- virshBlockJobStatusHandler(virConnectPtr conn G_GNUC_UNUSED,
- virDomainPtr dom G_GNUC_UNUSED,
- const char *disk,
- int type G_GNUC_UNUSED,
- int status,
- void *opaque)
- {
- virshBlockJobWaitDataPtr data = opaque;
-
- if (STREQ_NULLABLE(disk, data->dev))
- data->status = status;
- }
-
-
- /**
- * virshBlockJobWaitInit:
- * @ctl: vsh control structure
- * @dom: domain object
- * @dev: block device name to wait for
- * @job_name: block job name to display in user-facing messages
- * @verbose: enable progress reporting
- * @timeout: number of milliseconds to wait before aborting the job
- * @async_abort: abort the job asynchronously
- *
- * Prepares virsh for waiting for completion of a block job. This function
- * registers event handlers for block job events and prepares the data structures
- * for them. A call to virshBlockJobWait then waits for completion of the given
- * block job. This function should be tolerant to different versions of daemon
- * and the reporting capabilities of those.
- *
- * Returns the data structure that holds data needed for block job waiting or
- * NULL in case of error.
- */
- static virshBlockJobWaitDataPtr
- virshBlockJobWaitInit(vshControl *ctl,
- virDomainPtr dom,
- const char *dev,
- const char *job_name,
- bool verbose,
- unsigned int timeout,
- bool async_abort)
- {
- virshBlockJobWaitDataPtr ret;
- virshControlPtr priv = ctl->privData;
-
- if (VIR_ALLOC(ret) < 0)
- return NULL;
-
- ret->ctl = ctl;
- ret->dom = dom;
- ret->dev = dev;
- ret->job_name = job_name;
-
- ret->async_abort = async_abort;
- ret->timeout = timeout;
- ret->verbose = verbose;
-
- ret->status = -1;
-
- virConnectDomainEventGenericCallback cb =
- VIR_DOMAIN_EVENT_CALLBACK(virshBlockJobStatusHandler);
-
- if ((ret->cb_id = virConnectDomainEventRegisterAny(priv->conn, dom,
- VIR_DOMAIN_EVENT_ID_BLOCK_JOB,
- cb, ret, NULL)) < 0)
- vshResetLibvirtError();
-
- if ((ret->cb_id2 = virConnectDomainEventRegisterAny(priv->conn, dom,
- VIR_DOMAIN_EVENT_ID_BLOCK_JOB_2,
- cb, ret, NULL)) < 0)
- vshResetLibvirtError();
-
- return ret;
- }
-
-
- static void
- virshBlockJobWaitFree(virshBlockJobWaitDataPtr data)
- {
- virshControlPtr priv = NULL;
-
- if (!data)
- return;
-
- priv = data->ctl->privData;
- if (data->cb_id >= 0)
- virConnectDomainEventDeregisterAny(priv->conn, data->cb_id);
- if (data->cb_id2 >= 0)
- virConnectDomainEventDeregisterAny(priv->conn, data->cb_id2);
-
- VIR_FREE(data);
- }
-
-
- /**
- * virshBlockJobWait:
- * @data: private data initialized by virshBlockJobWaitInit
- *
- * Waits for the block job to complete. This function prefers to wait for a
- * matching VIR_DOMAIN_EVENT_ID_BLOCK_JOB or VIR_DOMAIN_EVENT_ID_BLOCK_JOB_2
- * event from libvirt; however, it has a fallback mode should either of these
- * events not be available.
- *
- * This function returns values from the virConnectDomainEventBlockJobStatus
- * enum or -1 in case of an internal error.
- *
- * If the fallback mode is activated the returned event is
- * VIR_DOMAIN_BLOCK_JOB_COMPLETED if the block job vanishes or
- * VIR_DOMAIN_BLOCK_JOB_READY if the block job reaches 100%.
- */
- static int
- virshBlockJobWait(virshBlockJobWaitDataPtr data)
- {
- /* For two phase jobs like active commit or block copy, the marker reaches
- * 100% and an event fires. In case where virsh would not be able to match
- * the event to the given block job we will wait for the number of retries
- * before claiming that we entered synchronised phase */
- unsigned int retries = 5;
- #ifndef WIN32
- struct sigaction sig_action;
- struct sigaction old_sig_action;
- sigset_t sigmask, oldsigmask;
- #endif /* !WIN32 */
- unsigned long long start = 0;
- unsigned long long curr = 0;
-
- unsigned int abort_flags = 0;
- int ret = -1;
- virDomainBlockJobInfo info, last;
- int result;
-
- if (!data)
- return 0;
-
- if (data->async_abort)
- abort_flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC;
-
- #ifndef WIN32
- sigemptyset(&sigmask);
- sigaddset(&sigmask, SIGINT);
-
- intCaught = 0;
- sig_action.sa_sigaction = virshCatchInt;
- sig_action.sa_flags = SA_SIGINFO;
- sigemptyset(&sig_action.sa_mask);
- sigaction(SIGINT, &sig_action, &old_sig_action);
- #endif /* !WIN32 */
-
- if (data->timeout && virTimeMillisNow(&start) < 0) {
- vshSaveLibvirtError();
- goto cleanup;
- }
-
- last.cur = last.end = 0;
-
- while (true) {
- #ifndef WIN32
- pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask);
- #endif /* !WIN32 */
- result = virDomainGetBlockJobInfo(data->dom, data->dev, &info, 0);
- #ifndef WIN32
- pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
- #endif /* !WIN32 */
-
- if (result < 0) {
- vshError(data->ctl, _("failed to query job for disk %s"), data->dev);
- goto cleanup;
- }
-
- /* If either callback could be registered and we've got an event, we can
- * can end the waiting loop */
- if ((data->cb_id >= 0 || data->cb_id2 >= 0) && data->status != -1) {
- ret = data->status;
- break;
- }
-
- /* Fallback behaviour is only needed if one or both callbacks could not
- * be registered */
- if (data->cb_id < 0 || data->cb_id2 < 0) {
- /* If the block job vanishes, synthesize a COMPLETED event */
- if (result == 0) {
- ret = VIR_DOMAIN_BLOCK_JOB_COMPLETED;
- break;
- }
-
- /* If the block job hits 100%, wait a little while for a possible
- * event from libvirt unless both callbacks could not be registered
- * in order to synthesize our own READY event */
- if (info.end == info.cur &&
- ((data->cb_id < 0 && data->cb_id2 < 0) || --retries == 0)) {
- ret = VIR_DOMAIN_BLOCK_JOB_READY;
- break;
- }
- }
-
- if (data->verbose && (info.cur != last.cur || info.end != last.end))
- virshPrintJobProgress(data->job_name, info.end - info.cur,
- info.end);
- last = info;
-
- if (data->timeout && virTimeMillisNow(&curr) < 0) {
- vshSaveLibvirtError();
- goto cleanup;
- }
-
- if (intCaught || (data->timeout && (curr - start > data->timeout))) {
- if (virDomainBlockJobAbort(data->dom, data->dev, abort_flags) < 0) {
- vshError(data->ctl, _("failed to abort job for disk '%s'"),
- data->dev);
- goto cleanup;
- }
-
- ret = VIR_DOMAIN_BLOCK_JOB_CANCELED;
- break;
- }
-
- g_usleep(500 * 1000);
- }
-
- /* print 100% completed */
- if (data->verbose &&
- (ret == VIR_DOMAIN_BLOCK_JOB_COMPLETED ||
- ret == VIR_DOMAIN_BLOCK_JOB_READY))
- virshPrintJobProgress(data->job_name, 0, 1);
-
- cleanup:
- #ifndef WIN32
- sigaction(SIGINT, &old_sig_action, NULL);
- #endif /* !WIN32 */
- return ret;
- }
-
-
- /*
- * "blockcommit" command
- */
- static const vshCmdInfo info_blockcommit[] = {
- {.name = "help",
- .data = N_("Start a block commit operation.")
- },
- {.name = "desc",
- .data = N_("Commit changes from a snapshot down to its backing image.")
- },
- {.name = NULL}
- };
-
- static const vshCmdOptDef opts_blockcommit[] = {
- VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
- {.name = "path",
- .type = VSH_OT_DATA,
- .flags = VSH_OFLAG_REQ,
- .completer = virshDomainDiskTargetCompleter,
- .help = N_("fully-qualified path of disk")
- },
- {.name = "bandwidth",
- .type = VSH_OT_INT,
- .help = N_("bandwidth limit in MiB/s")
- },
- {.name = "base",
- .type = VSH_OT_STRING,
- .help = N_("path of base file to commit into (default bottom of chain)")
- },
- {.name = "shallow",
- .type = VSH_OT_BOOL,
- .help = N_("use backing file of top as base")
- },
- {.name = "top",
- .type = VSH_OT_STRING,
- .help = N_("path of top file to commit from (default top of chain)")
- },
- {.name = "active",
- .type = VSH_OT_BOOL,
- .help = N_("trigger two-stage active commit of top file")
- },
- {.name = "delete",
- .type = VSH_OT_BOOL,
- .help = N_("delete files that were successfully committed")
- },
- {.name = "wait",
- .type = VSH_OT_BOOL,
- .help = N_("wait for job to complete "
- "(with --active, wait for job to sync)")
- },
- {.name = "verbose",
- .type = VSH_OT_BOOL,
- .help = N_("with --wait, display the progress")
- },
- {.name = "timeout",
- .type = VSH_OT_INT,
- .help = N_("implies --wait, abort if copy exceeds timeout (in seconds)")
- },
- {.name = "pivot",
- .type = VSH_OT_BOOL,
- .help = N_("implies --active --wait, pivot when commit is synced")
- },
- {.name = "keep-overlay",
- .type = VSH_OT_BOOL,
- .help = N_("implies --active --wait, quit when commit is synced")
- },
- {.name = "async",
- .type = VSH_OT_BOOL,
- .help = N_("with --wait, don't wait for cancel to finish")
- },
- {.name = "keep-relative",
- .type = VSH_OT_BOOL,
- .help = N_("keep the backing chain relatively referenced")
- },
- {.name = "bytes",
- .type = VSH_OT_BOOL,
- .help = N_("the bandwidth limit is in bytes/s rather than MiB/s")
- },
- {.name = NULL}
- };
-
- static bool
- cmdBlockcommit(vshControl *ctl, const vshCmd *cmd)
- {
- virDomainPtr dom = NULL;
- bool ret = false;
- bool verbose = vshCommandOptBool(cmd, "verbose");
- bool pivot = vshCommandOptBool(cmd, "pivot");
- bool finish = vshCommandOptBool(cmd, "keep-overlay");
- bool active = vshCommandOptBool(cmd, "active") || pivot || finish;
- bool blocking = vshCommandOptBool(cmd, "wait") || pivot || finish;
- bool async = vshCommandOptBool(cmd, "async");
- bool bytes = vshCommandOptBool(cmd, "bytes");
- int timeout = 0;
- const char *path = NULL;
- const char *base = NULL;
- const char *top = NULL;
- int abort_flags = 0;
- unsigned int flags = 0;
- unsigned long bandwidth = 0;
- virshBlockJobWaitDataPtr bjWait = NULL;
-
- VSH_EXCLUSIVE_OPTIONS("pivot", "keep-overlay");
-
- if (vshCommandOptStringReq(ctl, cmd, "path", &path) < 0)
- return false;
-
- if (vshCommandOptStringReq(ctl, cmd, "base", &base) < 0)
- return false;
-
- if (vshCommandOptStringReq(ctl, cmd, "top", &top) < 0)
- return false;
-
- if (vshBlockJobOptionBandwidth(ctl, cmd, bytes, &bandwidth) < 0)
- return false;
-
- if (bytes)
- flags |= VIR_DOMAIN_BLOCK_COMMIT_BANDWIDTH_BYTES;
-
- if (vshCommandOptBool(cmd, "shallow"))
- flags |= VIR_DOMAIN_BLOCK_COMMIT_SHALLOW;
-
- if (vshCommandOptBool(cmd, "delete"))
- flags |= VIR_DOMAIN_BLOCK_COMMIT_DELETE;
-
- if (active)
- flags |= VIR_DOMAIN_BLOCK_COMMIT_ACTIVE;
-
- if (vshCommandOptBool(cmd, "keep-relative"))
- flags |= VIR_DOMAIN_BLOCK_COMMIT_RELATIVE;
-
- if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0)
- return false;
-
- if (timeout)
- blocking = true;
-
- if (!blocking) {
- if (verbose) {
- vshError(ctl, "%s", _("--verbose requires at least one of --timeout, "
- "--wait, --pivot, or --keep-overlay"));
- return false;
- }
-
- if (async) {
- vshError(ctl, "%s", _("--async requires at least one of --timeout, "
- "--wait, --pivot, or --keep-overlay"));
- return false;
- }
- }
-
- if (async)
- abort_flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC;
-
- if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
- return false;
-
- if (blocking &&
- !(bjWait = virshBlockJobWaitInit(ctl, dom, path, _("Block commit"),
- verbose, timeout, async)))
- goto cleanup;
-
- if (virDomainBlockCommit(dom, path, base, top, bandwidth, flags) < 0)
- goto cleanup;
-
- if (!blocking) {
- if (active)
- vshPrintExtra(ctl, "%s", _("Active Block Commit started"));
- else
- vshPrintExtra(ctl, "%s", _("Block Commit started"));
-
- ret = true;
- goto cleanup;
- }
-
- /* Execution continues here only if --wait or friends were specified */
- switch (virshBlockJobWait(bjWait)) {
- case -1:
- goto cleanup;
-
- case VIR_DOMAIN_BLOCK_JOB_CANCELED:
- vshPrintExtra(ctl, "\n%s", _("Commit aborted"));
- goto cleanup;
- break;
-
- case VIR_DOMAIN_BLOCK_JOB_FAILED:
- vshPrintExtra(ctl, "\n%s", _("Commit failed"));
- goto cleanup;
- break;
-
- case VIR_DOMAIN_BLOCK_JOB_READY:
- case VIR_DOMAIN_BLOCK_JOB_COMPLETED:
- break;
- }
-
- if (active) {
- if (pivot) {
- abort_flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT;
- if (virDomainBlockJobAbort(dom, path, abort_flags) < 0) {
- vshError(ctl, _("failed to pivot job for disk %s"), path);
- goto cleanup;
- }
-
- vshPrintExtra(ctl, "\n%s", _("Successfully pivoted"));
- } else if (finish) {
- if (virDomainBlockJobAbort(dom, path, abort_flags) < 0) {
- vshError(ctl, _("failed to finish job for disk %s"), path);
- goto cleanup;
- }
-
- vshPrintExtra(ctl, "\n%s", _("Commit complete, overlay "
- "image kept"));
- } else {
- vshPrintExtra(ctl, "\n%s", _("Now in synchronized phase"));
- }
- } else {
- vshPrintExtra(ctl, "\n%s", _("Commit complete"));
- }
-
- ret = true;
- cleanup:
- virshDomainFree(dom);
- virshBlockJobWaitFree(bjWait);
- return ret;
- }
-
- /*
- * "blockcopy" command
- */
- static const vshCmdInfo info_blockcopy[] = {
- {.name = "help",
- .data = N_("Start a block copy operation.")
- },
- {.name = "desc",
- .data = N_("Copy a disk backing image chain to dest.")
- },
- {.name = NULL}
- };
-
- static const vshCmdOptDef opts_blockcopy[] = {
- VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
- {.name = "path",
- .type = VSH_OT_DATA,
- .flags = VSH_OFLAG_REQ,
- .completer = virshDomainDiskTargetCompleter,
- .help = N_("fully-qualified path of source disk")
- },
- {.name = "dest",
- .type = VSH_OT_STRING,
- .help = N_("path of the copy to create")
- },
- {.name = "bandwidth",
- .type = VSH_OT_INT,
- .help = N_("bandwidth limit in MiB/s")
- },
- {.name = "shallow",
- .type = VSH_OT_BOOL,
- .help = N_("make the copy share a backing chain")
- },
- {.name = "reuse-external",
- .type = VSH_OT_BOOL,
- .help = N_("reuse existing destination")
- },
- {.name = "raw",
- .type = VSH_OT_ALIAS,
- .help = "format=raw"
- },
- {.name = "blockdev",
- .type = VSH_OT_BOOL,
- .help = N_("copy destination is block device instead of regular file")
- },
- {.name = "wait",
- .type = VSH_OT_BOOL,
- .help = N_("wait for job to reach mirroring phase")
- },
- {.name = "verbose",
- .type = VSH_OT_BOOL,
- .help = N_("with --wait, display the progress")
- },
- {.name = "timeout",
- .type = VSH_OT_INT,
- .help = N_("implies --wait, abort if copy exceeds timeout (in seconds)")
- },
- {.name = "pivot",
- .type = VSH_OT_BOOL,
- .help = N_("implies --wait, pivot when mirroring starts")
- },
- {.name = "finish",
- .type = VSH_OT_BOOL,
- .help = N_("implies --wait, quit when mirroring starts")
- },
- {.name = "async",
- .type = VSH_OT_BOOL,
- .help = N_("with --wait, don't wait for cancel to finish")
- },
- {.name = "xml",
- .type = VSH_OT_STRING,
- .help = N_("filename containing XML description of the copy destination")
- },
- {.name = "format",
- .type = VSH_OT_STRING,
- .help = N_("format of the destination file")
- },
- {.name = "granularity",
- .type = VSH_OT_INT,
- .help = N_("power-of-two granularity to use during the copy")
- },
- {.name = "buf-size",
- .type = VSH_OT_INT,
- .help = N_("maximum amount of in-flight data during the copy")
- },
- {.name = "bytes",
- .type = VSH_OT_BOOL,
- .help = N_("the bandwidth limit is in bytes/s rather than MiB/s")
- },
- {.name = "transient-job",
- .type = VSH_OT_BOOL,
- .help = N_("the copy job is not persisted if VM is turned off")
- },
- {.name = NULL}
- };
-
- static bool
- cmdBlockcopy(vshControl *ctl, const vshCmd *cmd)
- {
- virDomainPtr dom = NULL;
- const char *dest = NULL;
- const char *format = NULL;
- unsigned long bandwidth = 0;
- unsigned int granularity = 0;
- unsigned long long buf_size = 0;
- unsigned int flags = 0;
- bool ret = false;
- bool verbose = vshCommandOptBool(cmd, "verbose");
- bool pivot = vshCommandOptBool(cmd, "pivot");
- bool finish = vshCommandOptBool(cmd, "finish");
- bool blockdev = vshCommandOptBool(cmd, "blockdev");
- bool blocking = vshCommandOptBool(cmd, "wait") || finish || pivot;
- bool async = vshCommandOptBool(cmd, "async");
- bool bytes = vshCommandOptBool(cmd, "bytes");
- bool transientjob = vshCommandOptBool(cmd, "transient-job");
- int timeout = 0;
- const char *path = NULL;
- int abort_flags = 0;
- const char *xml = NULL;
- char *xmlstr = NULL;
- virTypedParameterPtr params = NULL;
- virshBlockJobWaitDataPtr bjWait = NULL;
- int nparams = 0;
-
- if (vshCommandOptStringReq(ctl, cmd, "path", &path) < 0)
- return false;
- if (vshCommandOptStringReq(ctl, cmd, "dest", &dest) < 0)
- return false;
- if (vshCommandOptStringReq(ctl, cmd, "xml", &xml) < 0)
- return false;
- if (vshCommandOptStringReq(ctl, cmd, "format", &format) < 0)
- return false;
- if (vshBlockJobOptionBandwidth(ctl, cmd, bytes, &bandwidth) < 0)
- return false;
- if (vshCommandOptUInt(ctl, cmd, "granularity", &granularity) < 0)
- return false;
- if (vshCommandOptULongLong(ctl, cmd, "buf-size", &buf_size) < 0)
- return false;
- /* Exploit that some VIR_DOMAIN_BLOCK_REBASE_* and
- * VIR_DOMAIN_BLOCK_COPY_* flags have the same values. */
- if (vshCommandOptBool(cmd, "shallow"))
- flags |= VIR_DOMAIN_BLOCK_REBASE_SHALLOW;
- if (vshCommandOptBool(cmd, "reuse-external"))
- flags |= VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT;
- if (transientjob)
- flags |= VIR_DOMAIN_BLOCK_COPY_TRANSIENT_JOB;
- if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0)
- return false;
-
- if (timeout)
- blocking = true;
-
- if (async)
- abort_flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC;
-
- VSH_EXCLUSIVE_OPTIONS_VAR(dest, xml);
- VSH_EXCLUSIVE_OPTIONS_VAR(format, xml);
- VSH_EXCLUSIVE_OPTIONS_VAR(blockdev, xml);
- VSH_EXCLUSIVE_OPTIONS_VAR(pivot, finish);
-
- if (!dest && !xml) {
- vshError(ctl, "%s", _("need either --dest or --xml"));
- return false;
- }
-
- if (!blocking) {
- if (verbose) {
- vshError(ctl, "%s", _("--verbose requires at least one of --timeout, "
- "--wait, --pivot, or --finish"));
- return false;
- }
-
- if (async) {
- vshError(ctl, "%s", _("--async requires at least one of --timeout, "
- "--wait, --pivot, or --finish"));
- return false;
- }
- }
-
- if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
- goto cleanup;
-
- if (blocking &&
- !(bjWait = virshBlockJobWaitInit(ctl, dom, path, _("Block Copy"),
- verbose, timeout, async)))
- goto cleanup;
-
- if (xml) {
- if (virFileReadAll(xml, VSH_MAX_XML_FILE, &xmlstr) < 0) {
- vshReportError(ctl);
- goto cleanup;
- }
- }
-
- if (granularity || buf_size || (format && STRNEQ(format, "raw")) || xml ||
- transientjob) {
- /* New API */
- if (bandwidth || granularity || buf_size) {
- params = vshCalloc(ctl, 3, sizeof(*params));
- if (bandwidth) {
- if (!bytes) {
- /* bandwidth is ulong MiB/s, but the typed parameter is
- * ullong bytes/s; make sure we don't overflow */
- unsigned long long limit = MIN(ULONG_MAX, ULLONG_MAX >> 20);
- if (bandwidth > limit) {
- vshError(ctl, _("bandwidth must be less than %llu"), limit);
- goto cleanup;
- }
-
- bandwidth <<= 20ULL;
- }
- if (virTypedParameterAssign(¶ms[nparams++],
- VIR_DOMAIN_BLOCK_COPY_BANDWIDTH,
- VIR_TYPED_PARAM_ULLONG,
- bandwidth) < 0)
- goto cleanup;
- }
- if (granularity &&
- virTypedParameterAssign(¶ms[nparams++],
- VIR_DOMAIN_BLOCK_COPY_GRANULARITY,
- VIR_TYPED_PARAM_UINT,
- granularity) < 0)
- goto cleanup;
- if (buf_size &&
- virTypedParameterAssign(¶ms[nparams++],
- VIR_DOMAIN_BLOCK_COPY_BUF_SIZE,
- VIR_TYPED_PARAM_ULLONG,
- buf_size) < 0)
- goto cleanup;
- }
-
- if (!xmlstr) {
- virBuffer buf = VIR_BUFFER_INITIALIZER;
- virBufferAsprintf(&buf, "<disk type='%s'>\n",
- blockdev ? "block" : "file");
- virBufferAdjustIndent(&buf, 2);
- virBufferAsprintf(&buf, "<source %s", blockdev ? "dev" : "file");
- virBufferEscapeString(&buf, "='%s'/>\n", dest);
- virBufferEscapeString(&buf, "<driver type='%s'/>\n", format);
- virBufferAdjustIndent(&buf, -2);
- virBufferAddLit(&buf, "</disk>\n");
- xmlstr = virBufferContentAndReset(&buf);
- }
-
- if (virDomainBlockCopy(dom, path, xmlstr, params, nparams, flags) < 0)
- goto cleanup;
- } else {
- /* Old API */
- flags |= VIR_DOMAIN_BLOCK_REBASE_COPY;
- if (blockdev)
- flags |= VIR_DOMAIN_BLOCK_REBASE_COPY_DEV;
- if (STREQ_NULLABLE(format, "raw"))
- flags |= VIR_DOMAIN_BLOCK_REBASE_COPY_RAW;
- if (bytes)
- flags |= VIR_DOMAIN_BLOCK_REBASE_BANDWIDTH_BYTES;
-
- if (virDomainBlockRebase(dom, path, dest, bandwidth, flags) < 0)
- goto cleanup;
- }
-
- if (!blocking) {
- vshPrintExtra(ctl, "%s", _("Block Copy started"));
- ret = true;
- goto cleanup;
- }
-
- /* Execution continues here only if --wait or friends were specified */
- switch (virshBlockJobWait(bjWait)) {
- case -1:
- goto cleanup;
-
- case VIR_DOMAIN_BLOCK_JOB_CANCELED:
- vshPrintExtra(ctl, "\n%s", _("Copy aborted"));
- goto cleanup;
- break;
-
- case VIR_DOMAIN_BLOCK_JOB_FAILED:
- vshPrintExtra(ctl, "\n%s", _("Copy failed"));
- goto cleanup;
- break;
-
- case VIR_DOMAIN_BLOCK_JOB_READY:
- case VIR_DOMAIN_BLOCK_JOB_COMPLETED:
- break;
- }
-
- if (pivot) {
- abort_flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT;
- if (virDomainBlockJobAbort(dom, path, abort_flags) < 0) {
- vshError(ctl, _("failed to pivot job for disk %s"), path);
- goto cleanup;
- }
-
- vshPrintExtra(ctl, "\n%s", _("Successfully pivoted"));
- } else if (finish) {
- if (virDomainBlockJobAbort(dom, path, abort_flags) < 0) {
- vshError(ctl, _("failed to finish job for disk %s"), path);
- goto cleanup;
- }
-
- vshPrintExtra(ctl, "\n%s", _("Successfully copied"));
- } else {
- vshPrintExtra(ctl, "\n%s", _("Now in mirroring phase"));
- }
-
- ret = true;
-
- cleanup:
- VIR_FREE(xmlstr);
- virTypedParamsFree(params, nparams);
- virshDomainFree(dom);
- virshBlockJobWaitFree(bjWait);
- return ret;
- }
-
- /*
- * "blockjob" command
- */
- static const vshCmdInfo info_blockjob[] = {
- {.name = "help",
- .data = N_("Manage active block operations")
- },
- {.name = "desc",
- .data = N_("Query, adjust speed, or cancel active block operations.")
- },
- {.name = NULL}
- };
-
- static const vshCmdOptDef opts_blockjob[] = {
- VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
- {.name = "path",
- .type = VSH_OT_DATA,
- .flags = VSH_OFLAG_REQ,
- .completer = virshDomainDiskTargetCompleter,
- .help = N_("fully-qualified path of disk")
- },
- {.name = "abort",
- .type = VSH_OT_BOOL,
- .help = N_("abort the active job on the specified disk")
- },
- {.name = "async",
- .type = VSH_OT_BOOL,
- .help = N_("implies --abort; request but don't wait for job end")
- },
- {.name = "pivot",
- .type = VSH_OT_BOOL,
- .help = N_("implies --abort; conclude and pivot a copy or commit job")
- },
- {.name = "info",
- .type = VSH_OT_BOOL,
- .help = N_("get active job information for the specified disk")
- },
- {.name = "bytes",
- .type = VSH_OT_BOOL,
- .help = N_("get/set bandwidth in bytes rather than MiB/s")
- },
- {.name = "raw",
- .type = VSH_OT_BOOL,
- .help = N_("implies --info; output details rather than human summary")
- },
- {.name = "bandwidth",
- .type = VSH_OT_INT,
- .help = N_("set the bandwidth limit in MiB/s")
- },
- {.name = NULL}
- };
-
- VIR_ENUM_DECL(virshDomainBlockJob);
- VIR_ENUM_IMPL(virshDomainBlockJob,
- VIR_DOMAIN_BLOCK_JOB_TYPE_LAST,
- N_("Unknown job"),
- N_("Block Pull"),
- N_("Block Copy"),
- N_("Block Commit"),
- N_("Active Block Commit"),
- N_("Backup"),
- );
-
- static const char *
- virshDomainBlockJobToString(int type)
- {
- const char *str = virshDomainBlockJobTypeToString(type);
- return str ? _(str) : _("Unknown job");
- }
-
-
- static bool
- virshBlockJobInfo(vshControl *ctl,
- virDomainPtr dom,
- const char *path,
- bool raw,
- bool bytes)
- {
- virDomainBlockJobInfo info;
- virshControlPtr priv = ctl->privData;
- unsigned long long speed;
- unsigned int flags = 0;
- int rc = -1;
-
- /* If bytes were requested, or if raw mode is not forcing a MiB/s
- * query and cache can't prove failure, then query bytes/sec. */
- if (bytes || !(raw || priv->blockJobNoBytes)) {
- flags |= VIR_DOMAIN_BLOCK_JOB_INFO_BANDWIDTH_BYTES;
- rc = virDomainGetBlockJobInfo(dom, path, &info, flags);
- if (rc < 0) {
- /* Check for particular errors, let all the rest be fatal. */
- switch (last_error->code) {
- case VIR_ERR_INVALID_ARG:
- priv->blockJobNoBytes = true;
- G_GNUC_FALLTHROUGH;
- case VIR_ERR_OVERFLOW:
- if (!bytes && !raw) {
- /* try again with MiB/s, unless forcing bytes */
- vshResetLibvirtError();
- break;
- }
- G_GNUC_FALLTHROUGH;
- default:
- return false;
- }
- }
- speed = info.bandwidth;
- }
- /* If we don't already have a query result, query for MiB/s */
- if (rc < 0) {
- flags &= ~VIR_DOMAIN_BLOCK_JOB_INFO_BANDWIDTH_BYTES;
- if ((rc = virDomainGetBlockJobInfo(dom, path, &info, flags)) < 0)
- return false;
- speed = info.bandwidth;
- /* Scale to bytes/s unless in raw mode */
- if (!raw) {
- speed <<= 20;
- if (speed >> 20 != info.bandwidth) {
- vshError(ctl, _("overflow in converting %ld MiB/s to bytes\n"),
- info.bandwidth);
- return false;
- }
- }
- }
-
- if (rc == 0) {
- if (!raw)
- vshPrintExtra(ctl, _("No current block job for %s"), path);
- return true;
- }
-
- if (raw) {
- vshPrint(ctl, _(" type=%s\n bandwidth=%lu\n cur=%llu\n end=%llu\n"),
- virshDomainBlockJobTypeToString(info.type),
- info.bandwidth, info.cur, info.end);
- } else {
- virshPrintJobProgress(virshDomainBlockJobToString(info.type),
- info.end - info.cur, info.end);
- if (speed) {
- const char *unit;
- double val = vshPrettyCapacity(speed, &unit);
- vshPrint(ctl, _(" Bandwidth limit: %llu bytes/s (%-.3lf %s/s)"),
- speed, val, unit);
- }
- vshPrint(ctl, "\n");
- }
-
- return true;
- }
-
-
- static bool
- virshBlockJobSetSpeed(vshControl *ctl,
- const vshCmd *cmd,
- virDomainPtr dom,
- const char *path,
- bool bytes)
- {
- unsigned long bandwidth;
- unsigned int flags = 0;
-
- if (bytes)
- flags |= VIR_DOMAIN_BLOCK_JOB_SPEED_BANDWIDTH_BYTES;
-
- if (vshBlockJobOptionBandwidth(ctl, cmd, bytes, &bandwidth) < 0)
- return false;
-
- if (virDomainBlockJobSetSpeed(dom, path, bandwidth, flags) < 0)
- return false;
-
- return true;
- }
-
-
- static bool
- virshBlockJobAbort(virDomainPtr dom,
- const char *path,
- bool pivot,
- bool async)
- {
- unsigned int flags = 0;
-
- if (async)
- flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC;
- if (pivot)
- flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT;
-
- if (virDomainBlockJobAbort(dom, path, flags) < 0)
- return false;
-
- return true;
- }
-
-
- static bool
- cmdBlockjob(vshControl *ctl, const vshCmd *cmd)
- {
- bool ret = false;
- bool raw = vshCommandOptBool(cmd, "raw");
- bool bytes = vshCommandOptBool(cmd, "bytes");
- bool abortMode = vshCommandOptBool(cmd, "abort");
- bool pivot = vshCommandOptBool(cmd, "pivot");
- bool async = vshCommandOptBool(cmd, "async");
- bool info = vshCommandOptBool(cmd, "info");
- bool bandwidth = vshCommandOptBool(cmd, "bandwidth");
- virDomainPtr dom = NULL;
- const char *path;
-
- VSH_EXCLUSIVE_OPTIONS("raw", "abort");
- VSH_EXCLUSIVE_OPTIONS_VAR(raw, pivot);
- VSH_EXCLUSIVE_OPTIONS_VAR(raw, async);
- VSH_EXCLUSIVE_OPTIONS_VAR(raw, bandwidth);
-
- VSH_EXCLUSIVE_OPTIONS("info", "abort");
- VSH_EXCLUSIVE_OPTIONS_VAR(info, pivot);
- VSH_EXCLUSIVE_OPTIONS_VAR(info, async);
- VSH_EXCLUSIVE_OPTIONS_VAR(info, bandwidth);
-
- VSH_EXCLUSIVE_OPTIONS("bytes", "abort");
- VSH_EXCLUSIVE_OPTIONS_VAR(bytes, pivot);
- VSH_EXCLUSIVE_OPTIONS_VAR(bytes, async);
-
- if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
- goto cleanup;
-
- /* XXX Allow path to be optional to list info on all devices at once */
- if (vshCommandOptStringReq(ctl, cmd, "path", &path) < 0)
- goto cleanup;
-
- if (bandwidth)
- ret = virshBlockJobSetSpeed(ctl, cmd, dom, path, bytes);
- else if (abortMode || pivot || async)
- ret = virshBlockJobAbort(dom, path, pivot, async);
- else
- ret = virshBlockJobInfo(ctl, dom, path, raw, bytes);
-
- cleanup:
- virshDomainFree(dom);
- return ret;
- }
-
- /*
- * "blockpull" command
- */
- static const vshCmdInfo info_blockpull[] = {
- {.name = "help",
- .data = N_("Populate a disk from its backing image.")
- },
- {.name = "desc",
- .data = N_("Populate a disk from its backing image.")
- },
- {.name = NULL}
- };
-
- static const vshCmdOptDef opts_blockpull[] = {
- VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
- {.name = "path",
- .type = VSH_OT_DATA,
- .flags = VSH_OFLAG_REQ,
- .completer = virshDomainDiskTargetCompleter,
- .help = N_("fully-qualified path of disk")
- },
- {.name = "bandwidth",
- .type = VSH_OT_INT,
- .help = N_("bandwidth limit in MiB/s")
- },
- {.name = "base",
- .type = VSH_OT_STRING,
- .help = N_("path of backing file in chain for a partial pull")
- },
- {.name = "wait",
- .type = VSH_OT_BOOL,
- .help = N_("wait for job to finish")
- },
- {.name = "verbose",
- .type = VSH_OT_BOOL,
- .help = N_("with --wait, display the progress")
- },
- {.name = "timeout",
- .type = VSH_OT_INT,
- .help = N_("with --wait, abort if pull exceeds timeout (in seconds)")
- },
- {.name = "async",
- .type = VSH_OT_BOOL,
- .help = N_("with --wait, don't wait for cancel to finish")
- },
- {.name = "keep-relative",
- .type = VSH_OT_BOOL,
- .help = N_("keep the backing chain relatively referenced")
- },
- {.name = "bytes",
- .type = VSH_OT_BOOL,
- .help = N_("the bandwidth limit is in bytes/s rather than MiB/s")
- },
- {.name = NULL}
- };
-
- static bool
- cmdBlockpull(vshControl *ctl, const vshCmd *cmd)
- {
- virDomainPtr dom = NULL;
- bool ret = false;
- bool blocking = vshCommandOptBool(cmd, "wait");
- bool verbose = vshCommandOptBool(cmd, "verbose");
- bool async = vshCommandOptBool(cmd, "async");
- bool bytes = vshCommandOptBool(cmd, "bytes");
- int timeout = 0;
- const char *path = NULL;
- const char *base = NULL;
- unsigned long bandwidth = 0;
- unsigned int flags = 0;
- virshBlockJobWaitDataPtr bjWait = NULL;
-
- VSH_REQUIRE_OPTION("verbose", "wait");
- VSH_REQUIRE_OPTION("async", "wait");
-
- if (vshCommandOptStringReq(ctl, cmd, "path", &path) < 0)
- return false;
-
- if (vshCommandOptStringReq(ctl, cmd, "base", &base) < 0)
- return false;
-
- if (vshBlockJobOptionBandwidth(ctl, cmd, bytes, &bandwidth) < 0)
- return false;
-
- if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0)
- return false;
-
- if (vshCommandOptBool(cmd, "keep-relative"))
- flags |= VIR_DOMAIN_BLOCK_REBASE_RELATIVE;
-
- if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
- return false;
-
- if (blocking &&
- !(bjWait = virshBlockJobWaitInit(ctl, dom, path, _("Block Pull"),
- verbose, timeout, async)))
- goto cleanup;
-
- if (base || flags) {
- if (bytes)
- flags |= VIR_DOMAIN_BLOCK_REBASE_BANDWIDTH_BYTES;
-
- if (virDomainBlockRebase(dom, path, base, bandwidth, flags) < 0)
- goto cleanup;
- } else {
- if (bytes)
- flags |= VIR_DOMAIN_BLOCK_PULL_BANDWIDTH_BYTES;
-
- if (virDomainBlockPull(dom, path, bandwidth, flags) < 0)
- goto cleanup;
- }
-
- if (!blocking) {
- vshPrintExtra(ctl, "%s", _("Block Pull started"));
- ret = true;
- goto cleanup;
- }
-
- /* Execution continues here only if --wait or friends were specified */
- switch (virshBlockJobWait(bjWait)) {
- case -1:
- goto cleanup;
-
- case VIR_DOMAIN_BLOCK_JOB_CANCELED:
- vshPrintExtra(ctl, "\n%s", _("Pull aborted"));
- goto cleanup;
- break;
-
- case VIR_DOMAIN_BLOCK_JOB_FAILED:
- vshPrintExtra(ctl, "\n%s", _("Pull failed"));
- goto cleanup;
- break;
-
- case VIR_DOMAIN_BLOCK_JOB_READY:
- case VIR_DOMAIN_BLOCK_JOB_COMPLETED:
- vshPrintExtra(ctl, "\n%s", _("Pull complete"));
- break;
- }
-
- ret = true;
-
- cleanup:
- virshDomainFree(dom);
- virshBlockJobWaitFree(bjWait);
- return ret;
- }
-
- /*
- * "blockresize" command
- */
- static const vshCmdInfo info_blockresize[] = {
- {.name = "help",
- .data = N_("Resize block device of domain.")
- },
- {.name = "desc",
- .data = N_("Resize block device of domain.")
- },
- {.name = NULL}
- };
-
- static const vshCmdOptDef opts_blockresize[] = {
- VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
- {.name = "path",
- .type = VSH_OT_DATA,
- .flags = VSH_OFLAG_REQ,
- .completer = virshDomainDiskTargetCompleter,
- .help = N_("Fully-qualified path of block device")
- },
- {.name = "size",
- .type = VSH_OT_INT,
- .flags = VSH_OFLAG_REQ,
- .help = N_("New size of the block device, as scaled integer (default KiB)")
- },
- {.name = NULL}
- };
-
- static bool
- cmdBlockresize(vshControl *ctl, const vshCmd *cmd)
- {
- virDomainPtr dom;
- const char *path = NULL;
- unsigned long long size = 0;
- unsigned int flags = 0;
- bool ret = false;
-
- if (vshCommandOptStringReq(ctl, cmd, "path", (const char **) &path) < 0)
- return false;
-
- if (vshCommandOptScaledInt(ctl, cmd, "size", &size, 1024, ULLONG_MAX) < 0)
- return false;
-
- /* Prefer the older interface of KiB. */
- if (size % 1024 == 0)
- size /= 1024;
- else
- flags |= VIR_DOMAIN_BLOCK_RESIZE_BYTES;
-
- if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
- return false;
-
- if (virDomainBlockResize(dom, path, size, flags) < 0) {
- vshError(ctl, _("Failed to resize block device '%s'"), path);
- } else {
- vshPrintExtra(ctl, _("Block device '%s' is resized"), path);
- ret = true;
- }
-
- virshDomainFree(dom);
- return ret;
- }
-
- #ifndef WIN32
- /*
- * "console" command
- */
- static const vshCmdInfo info_console[] = {
- {.name = "help",
- .data = N_("connect to the guest console")
- },
- {.name = "desc",
- .data = N_("Connect the virtual serial console for the guest")
- },
- {.name = NULL}
- };
-
- static const vshCmdOptDef opts_console[] = {
- VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
- {.name = "devname", /* sc_prohibit_devname */
- .type = VSH_OT_STRING,
- .help = N_("character device name")
- },
- {.name = "force",
- .type = VSH_OT_BOOL,
- .help = N_("force console connection (disconnect already connected sessions)")
- },
- {.name = "safe",
- .type = VSH_OT_BOOL,
- .help = N_("only connect if safe console handling is supported")
- },
- {.name = NULL}
- };
-
- static bool
- cmdRunConsole(vshControl *ctl, virDomainPtr dom,
- const char *name,
- unsigned int flags)
- {
- int state;
- virshControlPtr priv = ctl->privData;
-
- if ((state = virshDomainState(ctl, dom, NULL)) < 0) {
- vshError(ctl, "%s", _("Unable to get domain status"));
- return false;
- }
-
- if (state == VIR_DOMAIN_SHUTOFF) {
- vshError(ctl, "%s", _("The domain is not running"));
- return false;
- }
-
- if (!isatty(STDIN_FILENO)) {
- vshError(ctl, "%s", _("Cannot run interactive console without a controlling TTY"));
- return false;
- }
-
- vshPrintExtra(ctl, _("Connected to domain %s\n"), virDomainGetName(dom));
- vshPrintExtra(ctl, _("Escape character is %s"), priv->escapeChar);
- if (priv->escapeChar[0] == '^')
- vshPrintExtra(ctl, " (Ctrl + %c)", priv->escapeChar[1]);
- vshPrintExtra(ctl, "\n");
- fflush(stdout);
- if (virshRunConsole(ctl, dom, name, flags) == 0)
- return true;
-
- return false;
- }
-
- static bool
- cmdConsole(vshControl *ctl, const vshCmd *cmd)
- {
- virDomainPtr dom;
- bool ret = false;
- bool force = vshCommandOptBool(cmd, "force");
- bool safe = vshCommandOptBool(cmd, "safe");
- unsigned int flags = 0;
- const char *name = NULL;
-
- if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
- return false;
-
- if (vshCommandOptStringReq(ctl, cmd, "devname", &name) < 0) /* sc_prohibit_devname */
- goto cleanup;
-
- if (force)
- flags |= VIR_DOMAIN_CONSOLE_FORCE;
- if (safe)
- flags |= VIR_DOMAIN_CONSOLE_SAFE;
-
- ret = cmdRunConsole(ctl, dom, name, flags);
-
- cleanup:
- virshDomainFree(dom);
- return ret;
- }
- #endif /* WIN32 */
-
- /* "domif-setlink" command
- */
- static const vshCmdInfo info_domif_setlink[] = {
- {.name = "help",
- .data = N_("set link state of a virtual interface")
- },
- {.name = "desc",
- .data = N_("Set link state of a domain's virtual interface. This command "
- "wraps usage of update-device command.")
- },
- {.name = NULL}
- };
-
- static const vshCmdOptDef opts_domif_setlink[] = {
- VIRSH_COMMON_OPT_DOMAIN_FULL(0),
- {.name = "interface",
- .type = VSH_OT_DATA,
- .flags = VSH_OFLAG_REQ,
- .completer = virshDomainInterfaceCompleter,
- .help = N_("interface device (MAC Address)")
- },
- {.name = "state",
- .type = VSH_OT_DATA,
- .flags = VSH_OFLAG_REQ,
- .completer = virshDomainInterfaceStateCompleter,
- .help = N_("new state of the device")
- },
- {.name = "persistent",
- .type = VSH_OT_ALIAS,
- .help = "config"
- },
- VIRSH_COMMON_OPT_DOMAIN_CONFIG,
- {.name = NULL}
- };
-
- static bool
- cmdDomIfSetLink(vshControl *ctl, const vshCmd *cmd)
- {
- virDomainPtr dom;
- const char *iface;
- const char *state;
- char *value;
- virMacAddr macaddr;
- const char *element;
- const char *attr;
- bool config;
- bool ret = false;
- unsigned int flags = 0;
- unsigned int xmlflags = 0;
- size_t i;
- xmlDocPtr xml = NULL;
- xmlXPathContextPtr ctxt = NULL;
- xmlXPathObjectPtr obj = NULL;
- xmlNodePtr cur = NULL;
- char *xml_buf = NULL;
-
- if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
- return false;
-
- if (vshCommandOptStringReq(ctl, cmd, "interface", &iface) < 0 ||
- vshCommandOptStringReq(ctl, cmd, "state", &state) < 0)
- goto cleanup;
-
- config = vshCommandOptBool(cmd, "config");
-
- if (STRNEQ(state, "up") && STRNEQ(state, "down")) {
- vshError(ctl, _("invalid link state '%s'"), state);
- goto cleanup;
- }
-
- if (config) {
- flags = VIR_DOMAIN_AFFECT_CONFIG;
- xmlflags |= VIR_DOMAIN_XML_INACTIVE;
- } else {
- flags = VIR_DOMAIN_AFFECT_LIVE;
- }
-
- if (virDomainIsActive(dom) == 0)
- flags = VIR_DOMAIN_AFFECT_CONFIG;
-
- if (virshDomainGetXMLFromDom(ctl, dom, xmlflags, &xml, &ctxt) < 0)
- goto cleanup;
-
- obj = xmlXPathEval(BAD_CAST "/domain/devices/interface", ctxt);
- if (obj == NULL || obj->type != XPATH_NODESET ||
- obj->nodesetval == NULL || obj->nodesetval->nodeNr == 0) {
- vshError(ctl, _("Failed to extract interface information or no interfaces found"));
- goto cleanup;
- }
-
- if (virMacAddrParse(iface, &macaddr) == 0) {
- element = "mac";
- attr = "address";
- } else {
- element = "target";
- attr = "dev";
- }
-
- /* find interface with matching mac addr */
- for (i = 0; i < obj->nodesetval->nodeNr; i++) {
- cur = obj->nodesetval->nodeTab[i]->children;
-
- while (cur) {
- if (cur->type == XML_ELEMENT_NODE &&
- virXMLNodeNameEqual(cur, element)) {
- value = virXMLPropString(cur, attr);
-
- if (STRCASEEQ(value, iface)) {
- VIR_FREE(value);
- goto hit;
- }
- VIR_FREE(value);
- }
- cur = cur->next;
- }
- }
-
- vshError(ctl, _("interface (%s: %s) not found"), element, iface);
- goto cleanup;
-
- hit:
- /* find and modify/add link state node */
- /* try to find <link> element */
- cur = obj->nodesetval->nodeTab[i]->children;
-
- while (cur) {
- if (cur->type == XML_ELEMENT_NODE &&
- virXMLNodeNameEqual(cur, "link")) {
- /* found, just modify the property */
- xmlSetProp(cur, BAD_CAST "state", BAD_CAST state);
-
- break;
- }
- cur = cur->next;
- }
-
- if (!cur) {
- /* element <link> not found, add one */
- cur = xmlNewChild(obj->nodesetval->nodeTab[i],
- NULL,
- BAD_CAST "link",
- NULL);
- if (!cur)
- goto cleanup;
-
- if (xmlNewProp(cur, BAD_CAST "state", BAD_CAST state) == NULL)
- goto cleanup;
- }
-
- if (!(xml_buf = virXMLNodeToString(xml, obj->nodesetval->nodeTab[i]))) {
- vshSaveLibvirtError();
- vshError(ctl, _("Failed to create XML"));
- goto cleanup;
- }
-
- if (virDomainUpdateDeviceFlags(dom, xml_buf, flags) < 0) {
- vshError(ctl, _("Failed to update interface link state"));
- goto cleanup;
- } else {
- vshPrintExtra(ctl, "%s", _("Device updated successfully\n"));
- ret = true;
- }
-
- cleanup:
- xmlXPathFreeObject(obj);
- xmlXPathFreeContext(ctxt);
- xmlFreeDoc(xml);
- VIR_FREE(xml_buf);
- virshDomainFree(dom);
-
- return ret;
- }
-
- /* "domiftune" command
- */
- static const vshCmdInfo info_domiftune[] = {
- {.name = "help",
- .data = N_("get/set parameters of a virtual interface")
- },
- {.name = "desc",
- .data = N_("Get/set parameters of a domain's virtual interface.")
- },
- {.name = NULL}
- };
-
- static const vshCmdOptDef opts_domiftune[] = {
- VIRSH_COMMON_OPT_DOMAIN_FULL(0),
- {.name = "interface",
- .type = VSH_OT_DATA,
- .flags = VSH_OFLAG_REQ,
- .completer = virshDomainInterfaceCompleter,
- .help = N_("interface device (MAC Address)")
- },
- {.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_CONFIG,
- VIRSH_COMMON_OPT_DOMAIN_LIVE,
- VIRSH_COMMON_OPT_DOMAIN_CURRENT,
- {.name = NULL}
- };
-
- static bool
- cmdDomIftune(vshControl *ctl, const vshCmd *cmd)
- {
- virDomainPtr dom;
- const char *name = NULL, *device = NULL,
- *inboundStr = NULL, *outboundStr = NULL;
- unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
- int nparams = 0;
- int maxparams = 0;
- virTypedParameterPtr params = NULL;
- bool ret = false;
- bool current = vshCommandOptBool(cmd, "current");
- bool config = vshCommandOptBool(cmd, "config");
- bool live = vshCommandOptBool(cmd, "live");
- virNetDevBandwidthRate inbound, outbound;
- size_t i;
-
- VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
- VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
-
- if (config)
- flags |= VIR_DOMAIN_AFFECT_CONFIG;
- if (live)
- flags |= VIR_DOMAIN_AFFECT_LIVE;
-
- if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
- return false;
-
- if (vshCommandOptStringReq(ctl, cmd, "interface", &device) < 0)
- goto cleanup;
-
- if (vshCommandOptStringReq(ctl, cmd, "inbound", &inboundStr) < 0 ||
- vshCommandOptStringReq(ctl, cmd, "outbound", &outboundStr) < 0)
- goto cleanup;
-
- memset(&inbound, 0, sizeof(inbound));
- memset(&outbound, 0, sizeof(outbound));
-
- if (inboundStr) {
- if (virshParseRateStr(ctl, inboundStr, &inbound) < 0)
- goto cleanup;
- /* we parse the rate as unsigned long long, but the API
- * only accepts UINT */
- if (inbound.average > UINT_MAX || inbound.peak > UINT_MAX ||
- inbound.burst > UINT_MAX) {
- vshError(ctl, _("inbound rate larger than maximum %u"),
- UINT_MAX);
- goto cleanup;
- }
-
- if ((!inbound.average && (inbound.burst || inbound.peak)) &&
- !inbound.floor) {
- vshError(ctl, _("either inbound average or floor is mandatory"));
- goto cleanup;
- }
-
- if (virTypedParamsAddUInt(¶ms, &nparams, &maxparams,
- VIR_DOMAIN_BANDWIDTH_IN_AVERAGE,
- inbound.average) < 0)
- goto save_error;
-
- if (inbound.peak &&
- virTypedParamsAddUInt(¶ms, &nparams, &maxparams,
- VIR_DOMAIN_BANDWIDTH_IN_PEAK,
- inbound.peak) < 0)
- goto save_error;
-
- if (inbound.burst &&
- virTypedParamsAddUInt(¶ms, &nparams, &maxparams,
- VIR_DOMAIN_BANDWIDTH_IN_BURST,
- inbound.burst) < 0)
- goto save_error;
-
- if (inbound.floor &&
- virTypedParamsAddUInt(¶ms, &nparams, &maxparams,
- VIR_DOMAIN_BANDWIDTH_IN_FLOOR,
- inbound.floor) < 0)
- goto save_error;
- }
-
- if (outboundStr) {
- if (virshParseRateStr(ctl, outboundStr, &outbound) < 0)
- goto cleanup;
- if (outbound.average > UINT_MAX || outbound.peak > UINT_MAX ||
- outbound.burst > UINT_MAX) {
- vshError(ctl, _("outbound rate larger than maximum %u"),
- UINT_MAX);
- goto cleanup;
- }
- if (outbound.average == 0 && (outbound.burst || outbound.peak)) {
- vshError(ctl, _("outbound average is mandatory"));
- goto cleanup;
- }
-
- if (outbound.floor) {
- vshError(ctl, _("outbound floor is unsupported yet"));
- goto cleanup;
- }
-
- if (virTypedParamsAddUInt(¶ms, &nparams, &maxparams,
- VIR_DOMAIN_BANDWIDTH_OUT_AVERAGE,
- outbound.average) < 0)
- goto save_error;
-
- if (outbound.peak &&
- virTypedParamsAddUInt(¶ms, &nparams, &maxparams,
- VIR_DOMAIN_BANDWIDTH_OUT_PEAK,
- outbound.peak) < 0)
- goto save_error;
-
- if (outbound.burst &&
- virTypedParamsAddUInt(¶ms, &nparams, &maxparams,
- VIR_DOMAIN_BANDWIDTH_OUT_BURST,
- outbound.burst) < 0)
- goto save_error;
- }
-
- if (nparams == 0) {
- /* get the number of interface parameters */
- if (virDomainGetInterfaceParameters(dom, device, NULL, &nparams, flags) != 0) {
- vshError(ctl, "%s",
- _("Unable to get number of interface parameters"));
- goto cleanup;
- }
-
- if (nparams == 0) {
- /* nothing to output */
- ret = true;
- goto cleanup;
- }
-
- /* get all interface parameters */
- params = vshCalloc(ctl, nparams, sizeof(*params));
- if (virDomainGetInterfaceParameters(dom, device, params, &nparams, flags) != 0) {
- vshError(ctl, "%s", _("Unable to get interface parameters"));
- goto cleanup;
- }
-
- for (i = 0; i < nparams; i++) {
- char *str = vshGetTypedParamValue(ctl, ¶ms[i]);
- vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
- VIR_FREE(str);
- }
- } else {
- if (virDomainSetInterfaceParameters(dom, device, params,
- nparams, flags) != 0)
- goto error;
- }
-
- ret = true;
-
- cleanup:
- virTypedPa
|