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, " \n", script);
+ if (model != NULL)
+ virBufferAsprintf(&buf, " \n", model);
+
+ if (inboundStr || outboundStr) {
+ virBufferAsprintf(&buf, " \n");
+ if (inboundStr && inbound.average > 0) {
+ virBufferAsprintf(&buf, " 0)
+ virBufferAsprintf(&buf, " peak='%llu'", inbound.peak);
+ if (inbound.burst > 0)
+ virBufferAsprintf(&buf, " burst='%llu'", inbound.burst);
+ virBufferAsprintf(&buf, "/>\n");
+ }
+ if (outboundStr && outbound.average > 0) {
+ virBufferAsprintf(&buf, " 0)
+ virBufferAsprintf(&buf, " peak='%llu'", outbound.peak);
+ if (outbound.burst > 0)
+ virBufferAsprintf(&buf, " burst='%llu'", outbound.burst);
+ virBufferAsprintf(&buf, "/>\n");
+ }
+ virBufferAsprintf(&buf, " \n");
+ }
+
+ virBufferAddLit(&buf, "\n");
+
+ if (virBufferError(&buf)) {
+ vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
+ goto cleanup;
+ }
+
+ 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 interface"));
+ } else {
+ vshPrint(ctl, "%s", _("Interface attached successfully\n"));
+ functionReturn = true;
+ }
+
+ cleanup:
+ if (dom)
+ virDomainFree(dom);
+ virBufferFreeAndReset(&buf);
+ return functionReturn;
+}
+
+/*
+ * "autostart" command
+ */
+static const vshCmdInfo info_autostart[] = {
+ {"help", N_("autostart a domain")},
+ {"desc",
+ N_("Configure a domain to be automatically started at boot.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_autostart[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"disable", VSH_OT_BOOL, 0, N_("disable autostarting")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdAutostart(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom;
+ const char *name;
+ int autostart;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(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);
+ virDomainFree(dom);
+ return false;
+ }
+
+ if (autostart)
+ vshPrint(ctl, _("Domain %s marked as autostarted\n"), name);
+ else
+ vshPrint(ctl, _("Domain %s unmarked as autostarted\n"), name);
+
+ virDomainFree(dom);
+ return true;
+}
+
+/*
+ * "blkdeviotune" command
+ */
+static const vshCmdInfo info_blkdeviotune[] = {
+ {"help", N_("Set or query a block device I/O tuning parameters.")},
+ {"desc", N_("Set or query disk I/O parameters such as block throttling.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_blkdeviotune[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("block device")},
+ {"total_bytes_sec", VSH_OT_ALIAS, 0, "total-bytes-sec"},
+ {"total-bytes-sec", VSH_OT_INT, VSH_OFLAG_NONE,
+ N_("total throughput limit in bytes per second")},
+ {"read_bytes_sec", VSH_OT_ALIAS, 0, "read-bytes-sec"},
+ {"read-bytes-sec", VSH_OT_INT, VSH_OFLAG_NONE,
+ N_("read throughput limit in bytes per second")},
+ {"write_bytes_sec", VSH_OT_ALIAS, 0, "write-bytes-sec"},
+ {"write-bytes-sec", VSH_OT_INT, VSH_OFLAG_NONE,
+ N_("write throughput limit in bytes per second")},
+ {"total_iops_sec", VSH_OT_ALIAS, 0, "total-iops-sec"},
+ {"total-iops-sec", VSH_OT_INT, VSH_OFLAG_NONE,
+ N_("total I/O operations limit per second")},
+ {"read_iops_sec", VSH_OT_ALIAS, 0, "read-iops-sec"},
+ {"read-iops-sec", VSH_OT_INT, VSH_OFLAG_NONE,
+ N_("read I/O operations limit per second")},
+ {"write_iops_sec", VSH_OT_ALIAS, 0, "write-iops-sec"},
+ {"write-iops-sec", VSH_OT_INT, VSH_OFLAG_NONE,
+ N_("write I/O operations limit per second")},
+ {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
+ {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
+ {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdBlkdeviotune(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom = NULL;
+ const char *name, *disk;
+ unsigned long long total_bytes_sec = 0, read_bytes_sec = 0, write_bytes_sec = 0;
+ unsigned long long total_iops_sec = 0, read_iops_sec = 0, write_iops_sec = 0;
+ int nparams = 0;
+ virTypedParameterPtr params = NULL;
+ unsigned int flags = 0, i = 0;
+ int rv = 0;
+ bool current = vshCommandOptBool(cmd, "current");
+ bool config = vshCommandOptBool(cmd, "config");
+ bool live = vshCommandOptBool(cmd, "live");
+ bool ret = false;
+
+ if (current) {
+ if (live || config) {
+ vshError(ctl, "%s", _("--current must be specified exclusively"));
+ return false;
+ }
+ flags = VIR_DOMAIN_AFFECT_CURRENT;
+ } else {
+ if (config)
+ flags |= VIR_DOMAIN_AFFECT_CONFIG;
+ if (live)
+ flags |= VIR_DOMAIN_AFFECT_LIVE;
+ }
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ goto cleanup;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+ goto cleanup;
+
+ if (vshCommandOptString(cmd, "device", &disk) < 0)
+ goto cleanup;
+
+ if ((rv = vshCommandOptULongLong(cmd, "total-bytes-sec",
+ &total_bytes_sec)) < 0) {
+ vshError(ctl, "%s",
+ _("Unable to parse integer parameter"));
+ goto cleanup;
+ } else if (rv > 0) {
+ nparams++;
+ }
+
+ if ((rv = vshCommandOptULongLong(cmd, "read-bytes-sec",
+ &read_bytes_sec)) < 0) {
+ vshError(ctl, "%s",
+ _("Unable to parse integer parameter"));
+ goto cleanup;
+ } else if (rv > 0) {
+ nparams++;
+ }
+
+ if ((rv = vshCommandOptULongLong(cmd, "write-bytes-sec",
+ &write_bytes_sec)) < 0) {
+ vshError(ctl, "%s",
+ _("Unable to parse integer parameter"));
+ goto cleanup;
+ } else if (rv > 0) {
+ nparams++;
+ }
+
+ if ((rv = vshCommandOptULongLong(cmd, "total-iops-sec",
+ &total_iops_sec)) < 0) {
+ vshError(ctl, "%s",
+ _("Unable to parse integer parameter"));
+ goto cleanup;
+ } else if (rv > 0) {
+ nparams++;
+ }
+
+ if ((rv = vshCommandOptULongLong(cmd, "read-iops-sec",
+ &read_iops_sec)) < 0) {
+ vshError(ctl, "%s",
+ _("Unable to parse integer parameter"));
+ goto cleanup;
+ } else if (rv > 0) {
+ nparams++;
+ }
+
+ if ((rv = vshCommandOptULongLong(cmd, "write-iops-sec",
+ &write_iops_sec)) < 0) {
+ vshError(ctl, "%s",
+ _("Unable to parse integer parameter"));
+ goto cleanup;
+ } else if (rv > 0) {
+ nparams++;
+ }
+
+ 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);
+ }
+
+ ret = true;
+ goto cleanup;
+ } else {
+ /* Set the block I/O throttle, match by opt since parameters can be 0 */
+ params = vshCalloc(ctl, nparams, sizeof(*params));
+ i = 0;
+
+ if (i < nparams && vshCommandOptBool(cmd, "total-bytes-sec") &&
+ virTypedParameterAssign(¶ms[i++],
+ VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_BYTES_SEC,
+ VIR_TYPED_PARAM_ULLONG,
+ total_bytes_sec) < 0)
+ goto error;
+
+ if (i < nparams && vshCommandOptBool(cmd, "read-bytes-sec") &&
+ virTypedParameterAssign(¶ms[i++],
+ VIR_DOMAIN_BLOCK_IOTUNE_READ_BYTES_SEC,
+ VIR_TYPED_PARAM_ULLONG,
+ read_bytes_sec) < 0)
+ goto error;
+
+ if (i < nparams && vshCommandOptBool(cmd, "write-bytes-sec") &&
+ virTypedParameterAssign(¶ms[i++],
+ VIR_DOMAIN_BLOCK_IOTUNE_WRITE_BYTES_SEC,
+ VIR_TYPED_PARAM_ULLONG,
+ write_bytes_sec) < 0)
+ goto error;
+
+ if (i < nparams && vshCommandOptBool(cmd, "total-iops-sec") &&
+ virTypedParameterAssign(¶ms[i++],
+ VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_IOPS_SEC,
+ VIR_TYPED_PARAM_ULLONG,
+ total_iops_sec) < 0)
+ goto error;
+
+ if (i < nparams && vshCommandOptBool(cmd, "read-iops-sec") &&
+ virTypedParameterAssign(¶ms[i++],
+ VIR_DOMAIN_BLOCK_IOTUNE_READ_IOPS_SEC,
+ VIR_TYPED_PARAM_ULLONG,
+ read_iops_sec) < 0)
+ goto error;
+
+ if (i < nparams && vshCommandOptBool(cmd, "write-iops-sec") &&
+ virTypedParameterAssign(¶ms[i++],
+ VIR_DOMAIN_BLOCK_IOTUNE_WRITE_IOPS_SEC,
+ VIR_TYPED_PARAM_ULLONG,
+ write_iops_sec) < 0)
+ goto error;
+
+ if (virDomainSetBlockIoTune(dom, disk, params, nparams, flags) < 0)
+ goto error;
+ }
+
+ ret = true;
+
+cleanup:
+ VIR_FREE(params);
+ virDomainFree(dom);
+ return ret;
+
+error:
+ vshError(ctl, "%s", _("Unable to change block I/O throttle"));
+ goto cleanup;
+}
+
+/*
+ * "blkiotune" command
+ */
+static const vshCmdInfo info_blkiotune[] = {
+ {"help", N_("Get or set blkio parameters")},
+ {"desc", 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 ")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_blkiotune[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"weight", VSH_OT_INT, VSH_OFLAG_NONE,
+ N_("IO Weight in range [100, 1000]")},
+ {"device-weights", VSH_OT_STRING, VSH_OFLAG_NONE,
+ N_("per-device IO Weights, in the form of /path/to/device,weight,...")},
+ {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
+ {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
+ {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdBlkiotune(vshControl * ctl, const vshCmd * cmd)
+{
+ virDomainPtr dom;
+ const char *device_weight = NULL;
+ int weight = 0;
+ int nparams = 0;
+ int rv = 0;
+ unsigned int i = 0;
+ virTypedParameterPtr params = NULL, temp = NULL;
+ bool ret = false;
+ unsigned int flags = 0;
+ bool current = vshCommandOptBool(cmd, "current");
+ bool config = vshCommandOptBool(cmd, "config");
+ bool live = vshCommandOptBool(cmd, "live");
+
+ if (current) {
+ if (live || config) {
+ vshError(ctl, "%s", _("--current must be specified exclusively"));
+ return false;
+ }
+ flags = VIR_DOMAIN_AFFECT_CURRENT;
+ } else {
+ if (config)
+ flags |= VIR_DOMAIN_AFFECT_CONFIG;
+ if (live)
+ flags |= VIR_DOMAIN_AFFECT_LIVE;
+ }
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ return false;
+
+ if ((rv = vshCommandOptInt(cmd, "weight", &weight)) < 0) {
+ vshError(ctl, "%s",
+ _("Unable to parse integer parameter"));
+ goto cleanup;
+ }
+
+ if (rv > 0) {
+ nparams++;
+ if (weight <= 0) {
+ vshError(ctl, _("Invalid value of %d for I/O weight"), weight);
+ goto cleanup;
+ }
+ }
+
+ rv = vshCommandOptString(cmd, "device-weights", &device_weight);
+ if (rv < 0) {
+ vshError(ctl, "%s",
+ _("Unable to parse string parameter"));
+ goto cleanup;
+ }
+ if (rv > 0) {
+ nparams++;
+ }
+
+ 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 */
+ params = vshCalloc(ctl, nparams, sizeof(*params));
+
+ for (i = 0; i < nparams; i++) {
+ temp = ¶ms[i];
+ temp->type = VIR_TYPED_PARAM_UINT;
+
+ if (weight) {
+ temp->value.ui = weight;
+ if (!virStrcpy(temp->field, VIR_DOMAIN_BLKIO_WEIGHT,
+ sizeof(temp->field)))
+ goto cleanup;
+ weight = 0;
+ } else if (device_weight) {
+ temp->value.s = vshStrdup(ctl, device_weight);
+ temp->type = VIR_TYPED_PARAM_STRING;
+ if (!virStrcpy(temp->field, VIR_DOMAIN_BLKIO_DEVICE_WEIGHT,
+ sizeof(temp->field)))
+ goto cleanup;
+ device_weight = NULL;
+ }
+ }
+
+ if (virDomainSetBlkioParameters(dom, params, nparams, flags) < 0) {
+ vshError(ctl, "%s", _("Unable to change blkio parameters"));
+ goto cleanup;
+ }
+ }
+
+ ret = true;
+
+ cleanup:
+ virTypedParameterArrayClear(params, nparams);
+ VIR_FREE(params);
+ virDomainFree(dom);
+ return ret;
+}
+
+typedef enum {
+ VSH_CMD_BLOCK_JOB_ABORT = 0,
+ VSH_CMD_BLOCK_JOB_INFO = 1,
+ VSH_CMD_BLOCK_JOB_SPEED = 2,
+ VSH_CMD_BLOCK_JOB_PULL = 3,
+ VSH_CMD_BLOCK_JOB_COPY = 4,
+} vshCmdBlockJobMode;
+
+static int
+blockJobImpl(vshControl *ctl, const vshCmd *cmd,
+ virDomainBlockJobInfoPtr info, int mode,
+ virDomainPtr *pdom)
+{
+ virDomainPtr dom = NULL;
+ const char *name, *path;
+ unsigned long bandwidth = 0;
+ int ret = -1;
+ const char *base = NULL;
+ unsigned int flags = 0;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ goto cleanup;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+ goto cleanup;
+
+ if (vshCommandOptString(cmd, "path", &path) < 0)
+ goto cleanup;
+
+ if (vshCommandOptUL(cmd, "bandwidth", &bandwidth) < 0) {
+ vshError(ctl, "%s", _("bandwidth must be a number"));
+ goto cleanup;
+ }
+
+ switch ((vshCmdBlockJobMode) mode) {
+ case VSH_CMD_BLOCK_JOB_ABORT:
+ if (vshCommandOptBool(cmd, "async"))
+ flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC;
+ if (vshCommandOptBool(cmd, "pivot"))
+ flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT;
+ ret = virDomainBlockJobAbort(dom, path, flags);
+ break;
+ case VSH_CMD_BLOCK_JOB_INFO:
+ ret = virDomainGetBlockJobInfo(dom, path, info, 0);
+ break;
+ case VSH_CMD_BLOCK_JOB_SPEED:
+ ret = virDomainBlockJobSetSpeed(dom, path, bandwidth, 0);
+ break;
+ case VSH_CMD_BLOCK_JOB_PULL:
+ if (vshCommandOptString(cmd, "base", &base) < 0)
+ goto cleanup;
+ if (base)
+ ret = virDomainBlockRebase(dom, path, base, bandwidth, 0);
+ else
+ ret = virDomainBlockPull(dom, path, bandwidth, 0);
+ break;
+ case VSH_CMD_BLOCK_JOB_COPY:
+ flags |= VIR_DOMAIN_BLOCK_REBASE_COPY;
+ if (vshCommandOptBool(cmd, "shallow"))
+ flags |= VIR_DOMAIN_BLOCK_REBASE_SHALLOW;
+ if (vshCommandOptBool(cmd, "reuse-external"))
+ flags |= VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT;
+ if (vshCommandOptBool(cmd, "raw"))
+ flags |= VIR_DOMAIN_BLOCK_REBASE_COPY_RAW;
+ if (vshCommandOptString(cmd, "dest", &base) < 0)
+ goto cleanup;
+ ret = virDomainBlockRebase(dom, path, base, bandwidth, flags);
+ }
+
+cleanup:
+ if (pdom && ret == 0)
+ *pdom = dom;
+ else if (dom)
+ virDomainFree(dom);
+ return ret;
+}
+
+static void
+print_job_progress(const char *label, unsigned long long remaining,
+ unsigned long long total)
+{
+ int progress;
+
+ if (total == 0)
+ /* migration has not been started */
+ return;
+
+ 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);
+}
+
+/*
+ * "blockcopy" command
+ */
+static const vshCmdInfo info_block_copy[] = {
+ {"help", N_("Start a block copy operation.")},
+ {"desc", N_("Populate a disk from its backing image.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_block_copy[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"path", VSH_OT_DATA, VSH_OFLAG_REQ, N_("fully-qualified path of disk")},
+ {"dest", VSH_OT_DATA, VSH_OFLAG_REQ, N_("path of the copy to create")},
+ {"bandwidth", VSH_OT_DATA, VSH_OFLAG_NONE, N_("bandwidth limit in MiB/s")},
+ {"shallow", VSH_OT_BOOL, 0, N_("make the copy share a backing chain")},
+ {"reuse-external", VSH_OT_BOOL, 0, N_("reuse existing destination")},
+ {"raw", VSH_OT_BOOL, 0, N_("use raw destination file")},
+ {"wait", VSH_OT_BOOL, 0, N_("wait for job to reach mirroring phase")},
+ {"verbose", VSH_OT_BOOL, 0, N_("with --wait, display the progress")},
+ {"timeout", VSH_OT_INT, VSH_OFLAG_NONE,
+ N_("with --wait, abort if copy exceeds timeout (in seconds)")},
+ {"pivot", VSH_OT_BOOL, 0, N_("with --wait, pivot when mirroring starts")},
+ {"finish", VSH_OT_BOOL, 0, N_("with --wait, quit when mirroring starts")},
+ {"async", VSH_OT_BOOL, 0,
+ N_("with --wait, don't wait for cancel to finish")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdBlockCopy(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom = NULL;
+ bool ret = false;
+ bool blocking = vshCommandOptBool(cmd, "wait");
+ bool verbose = vshCommandOptBool(cmd, "verbose");
+ bool pivot = vshCommandOptBool(cmd, "pivot");
+ bool finish = vshCommandOptBool(cmd, "finish");
+ int timeout = 0;
+ struct sigaction sig_action;
+ struct sigaction old_sig_action;
+ sigset_t sigmask;
+ struct timeval start;
+ struct timeval curr;
+ const char *path = NULL;
+ bool quit = false;
+ int abort_flags = 0;
+
+ if (blocking) {
+ if (pivot && finish) {
+ vshError(ctl, "%s", _("cannot mix --pivot and --finish"));
+ return false;
+ }
+ if (vshCommandOptInt(cmd, "timeout", &timeout) > 0) {
+ if (timeout < 1) {
+ vshError(ctl, "%s", _("migrate: Invalid timeout"));
+ return false;
+ }
+
+ /* Ensure that we can multiply by 1000 without overflowing. */
+ if (timeout > INT_MAX / 1000) {
+ vshError(ctl, "%s", _("migrate: Timeout is too big"));
+ return false;
+ }
+ }
+ if (vshCommandOptString(cmd, "path", &path) < 0)
+ return false;
+ if (vshCommandOptBool(cmd, "async"))
+ abort_flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC;
+
+ sigemptyset(&sigmask);
+ sigaddset(&sigmask, SIGINT);
+
+ intCaught = 0;
+ sig_action.sa_sigaction = vshCatchInt;
+ sig_action.sa_flags = SA_SIGINFO;
+ sigemptyset(&sig_action.sa_mask);
+ sigaction(SIGINT, &sig_action, &old_sig_action);
+
+ GETTIMEOFDAY(&start);
+ } else if (verbose || vshCommandOptBool(cmd, "timeout") ||
+ vshCommandOptBool(cmd, "async") || pivot || finish) {
+ vshError(ctl, "%s", _("blocking control options require --wait"));
+ return false;
+ }
+
+ if (blockJobImpl(ctl, cmd, NULL, VSH_CMD_BLOCK_JOB_COPY, &dom) < 0)
+ goto cleanup;
+
+ if (!blocking) {
+ vshPrint(ctl, "%s", _("Block Copy started"));
+ ret = true;
+ goto cleanup;
+ }
+
+ while (blocking) {
+ virDomainBlockJobInfo info;
+ int result = virDomainGetBlockJobInfo(dom, path, &info, 0);
+
+ if (result <= 0) {
+ vshError(ctl, _("failed to query job for disk %s"), path);
+ goto cleanup;
+ }
+ if (verbose)
+ print_job_progress(_("Block Copy"), info.end - info.cur, info.end);
+ if (info.cur == info.end)
+ break;
+
+ GETTIMEOFDAY(&curr);
+ if (intCaught || (timeout &&
+ (((int)(curr.tv_sec - start.tv_sec) * 1000 +
+ (int)(curr.tv_usec - start.tv_usec) / 1000) >
+ timeout * 1000))) {
+ vshDebug(ctl, VSH_ERR_DEBUG,
+ intCaught ? "interrupted" : "timeout");
+ intCaught = 0;
+ timeout = 0;
+ quit = true;
+ if (virDomainBlockJobAbort(dom, path, abort_flags) < 0) {
+ vshError(ctl, _("failed to abort job for disk %s"), path);
+ goto cleanup;
+ }
+ if (abort_flags & VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC)
+ break;
+ } else {
+ usleep(500 * 1000);
+ }
+ }
+
+ 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;
+ }
+ } else if (finish && virDomainBlockJobAbort(dom, path, abort_flags) < 0) {
+ vshError(ctl, _("failed to finish job for disk %s"), path);
+ goto cleanup;
+ }
+ vshPrint(ctl, "\n%s",
+ quit ? _("Copy aborted") :
+ pivot ? _("Successfully pivoted") :
+ finish ? _("Successfully copied") :
+ _("Now in mirroring phase"));
+
+ ret = true;
+cleanup:
+ if (dom)
+ virDomainFree(dom);
+ if (blocking)
+ sigaction(SIGINT, &old_sig_action, NULL);
+ return ret;
+}
+
+/*
+ * "blockjob" command
+ */
+static const vshCmdInfo info_block_job[] = {
+ {"help", N_("Manage active block operations")},
+ {"desc", N_("Query, adjust speed, or cancel active block operations.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_block_job[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"path", VSH_OT_DATA, VSH_OFLAG_REQ, N_("fully-qualified path of disk")},
+ {"abort", VSH_OT_BOOL, VSH_OFLAG_NONE,
+ N_("abort the active job on the specified disk")},
+ {"async", VSH_OT_BOOL, VSH_OFLAG_NONE,
+ N_("don't wait for --abort to complete")},
+ {"pivot", VSH_OT_BOOL, VSH_OFLAG_NONE,
+ N_("conclude and pivot a copy job")},
+ {"info", VSH_OT_BOOL, VSH_OFLAG_NONE,
+ N_("get active job information for the specified disk")},
+ {"bandwidth", VSH_OT_DATA, VSH_OFLAG_NONE,
+ N_("set the Bandwidth limit in MiB/s")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdBlockJob(vshControl *ctl, const vshCmd *cmd)
+{
+ int mode;
+ virDomainBlockJobInfo info;
+ const char *type;
+ int ret;
+ bool abortMode = (vshCommandOptBool(cmd, "abort") ||
+ vshCommandOptBool(cmd, "async") ||
+ vshCommandOptBool(cmd, "pivot"));
+ bool infoMode = vshCommandOptBool(cmd, "info");
+ bool bandwidth = vshCommandOptBool(cmd, "bandwidth");
+
+ if (abortMode + infoMode + bandwidth > 1) {
+ vshError(ctl, "%s",
+ _("conflict between --abort, --info, and --bandwidth modes"));
+ return false;
+ }
+
+ if (abortMode)
+ mode = VSH_CMD_BLOCK_JOB_ABORT;
+ else if (bandwidth)
+ mode = VSH_CMD_BLOCK_JOB_SPEED;
+ else
+ mode = VSH_CMD_BLOCK_JOB_INFO;
+
+ ret = blockJobImpl(ctl, cmd, &info, mode, NULL);
+ if (ret < 0)
+ return false;
+
+ if (ret == 0 || mode != VSH_CMD_BLOCK_JOB_INFO)
+ return true;
+
+ switch (info.type) {
+ case VIR_DOMAIN_BLOCK_JOB_TYPE_PULL:
+ type = _("Block Pull");
+ break;
+ case VIR_DOMAIN_BLOCK_JOB_TYPE_COPY:
+ type = _("Block Copy");
+ break;
+ default:
+ type = _("Unknown job");
+ break;
+ }
+
+ print_job_progress(type, info.end - info.cur, info.end);
+ if (info.bandwidth != 0)
+ vshPrint(ctl, _(" Bandwidth limit: %lu MiB/s\n"), info.bandwidth);
+ return true;
+}
+
+/*
+ * "blockpull" command
+ */
+static const vshCmdInfo info_block_pull[] = {
+ {"help", N_("Populate a disk from its backing image.")},
+ {"desc", N_("Populate a disk from its backing image.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_block_pull[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"path", VSH_OT_DATA, VSH_OFLAG_REQ, N_("fully-qualified path of disk")},
+ {"bandwidth", VSH_OT_DATA, VSH_OFLAG_NONE, N_("bandwidth limit in MiB/s")},
+ {"base", VSH_OT_DATA, VSH_OFLAG_NONE,
+ N_("path of backing file in chain for a partial pull")},
+ {"wait", VSH_OT_BOOL, 0, N_("wait for job to finish")},
+ {"verbose", VSH_OT_BOOL, 0, N_("with --wait, display the progress")},
+ {"timeout", VSH_OT_INT, VSH_OFLAG_NONE,
+ N_("with --wait, abort if pull exceeds timeout (in seconds)")},
+ {"async", VSH_OT_BOOL, 0,
+ N_("with --wait, don't wait for cancel to finish")},
+ {NULL, 0, 0, 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");
+ int timeout = 0;
+ struct sigaction sig_action;
+ struct sigaction old_sig_action;
+ sigset_t sigmask;
+ struct timeval start;
+ struct timeval curr;
+ const char *path = NULL;
+ bool quit = false;
+ int abort_flags = 0;
+
+ if (blocking) {
+ if (vshCommandOptInt(cmd, "timeout", &timeout) > 0) {
+ if (timeout < 1) {
+ vshError(ctl, "%s", _("invalid timeout"));
+ return false;
+ }
+
+ /* Ensure that we can multiply by 1000 without overflowing. */
+ if (timeout > INT_MAX / 1000) {
+ vshError(ctl, "%s", _("timeout is too big"));
+ return false;
+ }
+ }
+ if (vshCommandOptString(cmd, "path", &path) < 0)
+ return false;
+ if (vshCommandOptBool(cmd, "async"))
+ abort_flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC;
+
+ sigemptyset(&sigmask);
+ sigaddset(&sigmask, SIGINT);
+
+ intCaught = 0;
+ sig_action.sa_sigaction = vshCatchInt;
+ sig_action.sa_flags = SA_SIGINFO;
+ sigemptyset(&sig_action.sa_mask);
+ sigaction(SIGINT, &sig_action, &old_sig_action);
+
+ GETTIMEOFDAY(&start);
+ } else if (verbose || vshCommandOptBool(cmd, "timeout") ||
+ vshCommandOptBool(cmd, "async")) {
+ vshError(ctl, "%s", _("blocking control options require --wait"));
+ return false;
+ }
+
+ if (blockJobImpl(ctl, cmd, NULL, VSH_CMD_BLOCK_JOB_PULL, &dom) < 0)
+ goto cleanup;
+
+ if (!blocking) {
+ vshPrint(ctl, "%s", _("Block Pull started"));
+ ret = true;
+ goto cleanup;
+ }
+
+ while (blocking) {
+ virDomainBlockJobInfo info;
+ int result = virDomainGetBlockJobInfo(dom, path, &info, 0);
+
+ if (result < 0) {
+ vshError(ctl, _("failed to query job for disk %s"), path);
+ goto cleanup;
+ }
+ if (result == 0)
+ break;
+
+ if (verbose)
+ print_job_progress(_("Block Pull"), info.end - info.cur, info.end);
+
+ GETTIMEOFDAY(&curr);
+ if (intCaught || (timeout &&
+ (((int)(curr.tv_sec - start.tv_sec) * 1000 +
+ (int)(curr.tv_usec - start.tv_usec) / 1000) >
+ timeout * 1000))) {
+ vshDebug(ctl, VSH_ERR_DEBUG,
+ intCaught ? "interrupted" : "timeout");
+ intCaught = 0;
+ timeout = 0;
+ quit = true;
+ if (virDomainBlockJobAbort(dom, path, abort_flags) < 0) {
+ vshError(ctl, _("failed to abort job for disk %s"), path);
+ goto cleanup;
+ }
+ if (abort_flags & VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC)
+ break;
+ } else {
+ usleep(500 * 1000);
+ }
+ }
+
+ if (verbose && !quit) {
+ /* printf [100 %] */
+ print_job_progress(_("Block Pull"), 0, 1);
+ }
+ vshPrint(ctl, "\n%s", quit ? _("Pull aborted") : _("Pull complete"));
+
+ ret = true;
+cleanup:
+ if (dom)
+ virDomainFree(dom);
+ if (blocking)
+ sigaction(SIGINT, &old_sig_action, NULL);
+ return ret;
+}
+
+/*
+ * "blockresize" command
+ */
+static const vshCmdInfo info_block_resize[] = {
+ {"help", N_("Resize block device of domain.")},
+ {"desc", N_("Resize block device of domain.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_block_resize[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"path", VSH_OT_DATA, VSH_OFLAG_REQ,
+ N_("Fully-qualified path of block device")},
+ {"size", VSH_OT_INT, VSH_OFLAG_REQ,
+ N_("New size of the block device, as scaled integer (default KiB)")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdBlockResize(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom;
+ const char *path = NULL;
+ unsigned long long size = 0;
+ unsigned int flags = 0;
+ int ret = false;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (vshCommandOptString(cmd, "path", (const char **) &path) < 0) {
+ vshError(ctl, "%s", _("Path must not be empty"));
+ return false;
+ }
+
+ if (vshCommandOptScaledInt(cmd, "size", &size, 1024, ULLONG_MAX) < 0) {
+ vshError(ctl, "%s", _("Unable to parse integer"));
+ return false;
+ }
+
+ /* Prefer the older interface of KiB. */
+ if (size % 1024 == 0)
+ size /= 1024;
+ else
+ flags |= VIR_DOMAIN_BLOCK_RESIZE_BYTES;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ return false;
+
+ if (virDomainBlockResize(dom, path, size, flags) < 0) {
+ vshError(ctl, _("Failed to resize block device '%s'"), path);
+ } else {
+ vshPrint(ctl, _("Block device '%s' is resized"), path);
+ ret = true;
+ }
+
+ virDomainFree(dom);
+ return ret;
+}
+
+#ifndef WIN32
+/*
+ * "console" command
+ */
+static const vshCmdInfo info_console[] = {
+ {"help", N_("connect to the guest console")},
+ {"desc",
+ N_("Connect the virtual serial console for the guest")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_console[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"devname", VSH_OT_STRING, 0, N_("character device name")},
+ {"force", VSH_OT_BOOL, 0,
+ N_("force console connection (disconnect already connected sessions)")},
+ {"safe", VSH_OT_BOOL, 0,
+ N_("only connect if safe console handling is supported")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdRunConsole(vshControl *ctl, virDomainPtr dom,
+ const char *name,
+ unsigned int flags)
+{
+ bool ret = false;
+ int state;
+
+ if ((state = vshDomainState(ctl, dom, NULL)) < 0) {
+ vshError(ctl, "%s", _("Unable to get domain status"));
+ goto cleanup;
+ }
+
+ if (state == VIR_DOMAIN_SHUTOFF) {
+ vshError(ctl, "%s", _("The domain is not running"));
+ goto cleanup;
+ }
+
+ if (!isatty(STDIN_FILENO)) {
+ vshError(ctl, "%s", _("Cannot run interactive console without a controlling TTY"));
+ goto cleanup;
+ }
+
+ vshPrintExtra(ctl, _("Connected to domain %s\n"), virDomainGetName(dom));
+ vshPrintExtra(ctl, _("Escape character is %s\n"), ctl->escapeChar);
+ fflush(stdout);
+ if (vshRunConsole(dom, name, ctl->escapeChar, flags) == 0)
+ ret = true;
+
+ cleanup:
+
+ return ret;
+}
+#endif /* WIN32 */
+
+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 (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ return false;
+
+ if (vshCommandOptString(cmd, "devname", &name) < 0) {
+ vshError(ctl, "%s", _("Invalid devname"));
+ goto cleanup;
+ }
+
+ if (force)
+ flags |= VIR_DOMAIN_CONSOLE_FORCE;
+ if (safe)
+ flags |= VIR_DOMAIN_CONSOLE_SAFE;
+
+ ret = cmdRunConsole(ctl, dom, name, flags);
+
+cleanup:
+ virDomainFree(dom);
+ return ret;
+}
+
+/* "domif-setlink" command
+ */
+static const vshCmdInfo info_domif_setlink[] = {
+ {"help", N_("set link state of a virtual interface")},
+ {"desc", N_("Set link state of a domain's virtual interface. This command wraps usage of update-device command.")},
+ {NULL,NULL}
+};
+
+static const vshCmdOptDef opts_domif_setlink[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface device (MAC Address)")},
+ {"state", VSH_OT_DATA, VSH_OFLAG_REQ, N_("new state of the device")},
+ {"persistent", VSH_OT_ALIAS, 0, "config"},
+ {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDomIfSetLink(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom;
+ const char *iface;
+ const char *state;
+ const char *value;
+ const char *desc;
+ virMacAddr macaddr;
+ const char *element;
+ const char *attr;
+ bool config;
+ bool ret = false;
+ unsigned int flags = 0;
+ int i;
+ xmlDocPtr xml = NULL;
+ xmlXPathContextPtr ctxt = NULL;
+ xmlXPathObjectPtr obj = NULL;
+ xmlNodePtr cur = NULL;
+ xmlBufferPtr xml_buf = NULL;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ return false;
+
+ if (vshCommandOptString(cmd, "interface", &iface) <= 0)
+ goto cleanup;
+
+ if (vshCommandOptString(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;
+ }
+
+ /* get persistent or live description of network device */
+ desc = virDomainGetXMLDesc(dom, config ? VIR_DOMAIN_XML_INACTIVE : 0);
+ if (desc == NULL) {
+ vshError(ctl, _("Failed to get domain description xml"));
+ goto cleanup;
+ }
+
+ if (config)
+ flags = VIR_DOMAIN_AFFECT_CONFIG;
+ else
+ flags = VIR_DOMAIN_AFFECT_LIVE;
+
+ if (virDomainIsActive(dom) == 0)
+ flags = VIR_DOMAIN_AFFECT_CONFIG;
+
+ /* extract current network device description */
+ xml = virXMLParseStringCtxt(desc, _("(domain_definition)"), &ctxt);
+ VIR_FREE(desc);
+ if (!xml) {
+ vshError(ctl, _("Failed to parse domain description xml"));
+ 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 &&
+ xmlStrEqual(cur->name, BAD_CAST 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 element */
+ cur = obj->nodesetval->nodeTab[i]->children;
+
+ while (cur) {
+ if (cur->type == XML_ELEMENT_NODE &&
+ xmlStrEqual(cur->name, BAD_CAST "link")) {
+ /* found, just modify the property */
+ xmlSetProp(cur, BAD_CAST "state", BAD_CAST state);
+
+ break;
+ }
+ cur = cur->next;
+ }
+
+ if (!cur) {
+ /* element 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;
+ }
+
+ xml_buf = xmlBufferCreate();
+ if (!xml_buf) {
+ vshError(ctl, _("Failed to allocate memory"));
+ goto cleanup;
+ }
+
+ if (xmlNodeDump(xml_buf, xml, obj->nodesetval->nodeTab[i], 0, 0) < 0 ) {
+ vshError(ctl, _("Failed to create XML"));
+ goto cleanup;
+ }
+
+ if (virDomainUpdateDeviceFlags(dom, (char *)xmlBufferContent(xml_buf), flags) < 0) {
+ vshError(ctl, _("Failed to update interface link state"));
+ goto cleanup;
+ } else {
+ vshPrint(ctl, "%s", _("Device updated successfully\n"));
+ ret = true;
+ }
+
+cleanup:
+ xmlXPathFreeObject(obj);
+ xmlXPathFreeContext(ctxt);
+ xmlFreeDoc(xml);
+ xmlBufferFree(xml_buf);
+
+ if (dom)
+ virDomainFree(dom);
+
+ return ret;
+}
+
+/* "domiftune" command
+ */
+static const vshCmdInfo info_domiftune[] = {
+ {"help", N_("get/set parameters of a virtual interface")},
+ {"desc", N_("Get/set parameters of a domain's virtual interface.")},
+ {NULL,NULL}
+};
+
+static const vshCmdOptDef opts_domiftune[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface device (MAC Address)")},
+ {"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")},
+ {"config", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("affect next boot")},
+ {"live", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("affect running domain")},
+ {"current", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("affect current domain")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDomIftune(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom;
+ const char *name = NULL, *device = NULL,
+ *inboundStr = NULL, *outboundStr = NULL;
+ unsigned int flags = 0;
+ int nparams = 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;
+ int i;
+
+ if (current) {
+ if (live || config) {
+ vshError(ctl, "%s", _("--current must be specified exclusively"));
+ return false;
+ }
+ flags = VIR_DOMAIN_AFFECT_CURRENT;
+ } else {
+ if (config)
+ flags |= VIR_DOMAIN_AFFECT_CONFIG;
+ if (live)
+ flags |= VIR_DOMAIN_AFFECT_LIVE;
+ }
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+ return false;
+
+ if (vshCommandOptString(cmd, "interface", &device) <= 0) {
+ virDomainFree(dom);
+ return false;
+ }
+
+ if (vshCommandOptString(cmd, "inbound", &inboundStr) < 0 ||
+ vshCommandOptString(cmd, "outbound", &outboundStr) < 0) {
+ vshError(ctl, "missing argument");
+ goto cleanup;
+ }
+
+ memset(&inbound, 0, sizeof(inbound));
+ memset(&outbound, 0, sizeof(outbound));
+
+ if (inboundStr) {
+ 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;
+ }
+ nparams++; /* average */
+ if (inbound.peak) nparams++;
+ if (inbound.burst) nparams++;
+ }
+ if (outboundStr) {
+ 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;
+ }
+ nparams++; /* average */
+ if (outbound.peak) nparams++;
+ if (outbound.burst) nparams++;
+ }
+
+ 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 (!params) {
+ virReportOOMError();
+ goto cleanup;
+ }
+ 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 {
+ /* set the interface parameters */
+ params = vshCalloc(ctl, nparams, sizeof(*params));
+ if (!params) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ for (i = 0; i < nparams; i++)
+ params[i].type = VIR_TYPED_PARAM_UINT;
+
+ i = 0;
+ if (inbound.average && i < nparams) {
+ if (!virStrcpy(params[i].field, VIR_DOMAIN_BANDWIDTH_IN_AVERAGE,
+ sizeof(params[i].field)))
+ goto cleanup;
+ params[i].value.ui = inbound.average;
+ i++;
+ }
+ if (inbound.peak && i < nparams) {
+ if (!virStrcpy(params[i].field, VIR_DOMAIN_BANDWIDTH_IN_PEAK,
+ sizeof(params[i].field)))
+ goto cleanup;
+ params[i].value.ui = inbound.peak;
+ i++;
+ }
+ if (inbound.burst && i < nparams) {
+ if (!virStrcpy(params[i].field, VIR_DOMAIN_BANDWIDTH_IN_BURST,
+ sizeof(params[i].field)))
+ goto cleanup;
+ params[i].value.ui = inbound.burst;
+ i++;
+ }
+ if (outbound.average && i < nparams) {
+ if (!virStrcpy(params[i].field, VIR_DOMAIN_BANDWIDTH_OUT_AVERAGE,
+ sizeof(params[i].field)))
+ goto cleanup;
+ params[i].value.ui = outbound.average;
+ i++;
+ }
+ if (outbound.peak && i < nparams) {
+ if (!virStrcpy(params[i].field, VIR_DOMAIN_BANDWIDTH_OUT_PEAK,
+ sizeof(params[i].field)))
+ goto cleanup;
+ params[i].value.ui = outbound.peak;
+ i++;
+ }
+ if (outbound.burst && i < nparams) {
+ if (!virStrcpy(params[i].field, VIR_DOMAIN_BANDWIDTH_OUT_BURST,
+ sizeof(params[i].field)))
+ goto cleanup;
+ params[i].value.ui = outbound.burst;
+ i++;
+ }
+
+ if (virDomainSetInterfaceParameters(dom, device, params, nparams, flags) != 0) {
+ vshError(ctl, "%s", _("Unable to set interface parameters"));
+ goto cleanup;
+ }
+ }
+
+ ret = true;
+
+cleanup:
+ virTypedParameterArrayClear(params, nparams);
+ VIR_FREE(params);
+ virDomainFree(dom);
+ return ret;
+}
+
+/*
+ * "suspend" command
+ */
+static const vshCmdInfo info_suspend[] = {
+ {"help", N_("suspend a domain")},
+ {"desc", N_("Suspend a running domain.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_suspend[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdSuspend(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom;
+ const char *name;
+ bool ret = true;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+ return false;
+
+ if (virDomainSuspend(dom) == 0) {
+ vshPrint(ctl, _("Domain %s suspended\n"), name);
+ } else {
+ vshError(ctl, _("Failed to suspend domain %s"), name);
+ ret = false;
+ }
+
+ virDomainFree(dom);
+ return ret;
+}
+
+/*
+ * "dompmsuspend" command
+ */
+static const vshCmdInfo info_dom_pm_suspend[] = {
+ {"help", N_("suspend a domain gracefully using power management "
+ "functions")},
+ {"desc", N_("Suspends a running domain using guest OS's power management. "
+ "(Note: This requires a guest agent configured and running in "
+ "the guest OS).")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_dom_pm_suspend[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"duration", VSH_OT_INT, VSH_OFLAG_REQ_OPT, N_("duration in seconds")},
+ {"target", VSH_OT_STRING, VSH_OFLAG_REQ, N_("mem(Suspend-to-RAM), "
+ "disk(Suspend-to-Disk), "
+ "hybrid(Hybrid-Suspend)")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDomPMSuspend(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom;
+ const char *name;
+ bool ret = false;
+ const char *target = NULL;
+ unsigned int suspendTarget;
+ unsigned long long duration = 0;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+ return false;
+
+ if (vshCommandOptULongLong(cmd, "duration", &duration) < 0) {
+ vshError(ctl, _("Invalid duration argument"));
+ goto cleanup;
+ }
+
+ if (vshCommandOptString(cmd, "target", &target) < 0) {
+ vshError(ctl, _("Invalid target argument"));
+ goto cleanup;
+ }
+
+ if (STREQ(target, "mem"))
+ suspendTarget = VIR_NODE_SUSPEND_TARGET_MEM;
+ else if (STREQ(target, "disk"))
+ suspendTarget = VIR_NODE_SUSPEND_TARGET_DISK;
+ else if (STREQ(target, "hybrid"))
+ suspendTarget = VIR_NODE_SUSPEND_TARGET_HYBRID;
+ else {
+ vshError(ctl, "%s", _("Invalid target"));
+ goto cleanup;
+ }
+
+ if (virDomainPMSuspendForDuration(dom, suspendTarget, duration, 0) < 0) {
+ vshError(ctl, _("Domain %s could not be suspended"),
+ virDomainGetName(dom));
+ goto cleanup;
+ }
+
+ vshPrint(ctl, _("Domain %s successfully suspended"),
+ virDomainGetName(dom));
+
+ ret = true;
+
+cleanup:
+ virDomainFree(dom);
+ return ret;
+}
+
+/*
+ * "dompmwakeup" command
+ */
+
+static const vshCmdInfo info_dom_pm_wakeup[] = {
+ {"help", N_("wakeup a domain suspended by dompmsuspend command")},
+ {"desc", N_("Wakeup a domain previously suspended "
+ "by dompmsuspend command.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_dom_pm_wakeup[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDomPMWakeup(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom;
+ const char *name;
+ bool ret = false;
+ unsigned int flags = 0;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+ return false;
+
+ if (virDomainPMWakeup(dom, flags) < 0) {
+ vshError(ctl, _("Domain %s could not be woken up"),
+ virDomainGetName(dom));
+ goto cleanup;
+ }
+
+ vshPrint(ctl, _("Domain %s successfully woken up"),
+ virDomainGetName(dom));
+
+ ret = true;
+
+cleanup:
+ virDomainFree(dom);
+ return ret;
+}
+
+/*
+ * "undefine" command
+ */
+static const vshCmdInfo info_undefine[] = {
+ {"help", N_("undefine a domain")},
+ {"desc",
+ N_("Undefine an inactive domain, or convert persistent to transient.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_undefine[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name or uuid")},
+ {"managed-save", VSH_OT_BOOL, 0, N_("remove domain managed state file")},
+ {"storage", VSH_OT_DATA, VSH_OFLAG_NONE,
+ N_("remove associated storage volumes (comma separated list of targets "
+ "or source paths) (see domblklist)")},
+ {"remove-all-storage", VSH_OT_BOOL, 0,
+ N_("remove all associated storage volumes (use with caution)")},
+ {"wipe-storage", VSH_OT_BOOL, VSH_OFLAG_NONE,
+ N_("wipe data on the removed volumes")},
+ {"snapshots-metadata", VSH_OT_BOOL, 0,
+ N_("remove all domain snapshot metadata, if inactive")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdUndefine(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom;
+ bool ret = false;
+ const char *name = NULL;
+ /* Flags to attempt. */
+ unsigned int flags = 0;
+ /* User-requested actions. */
+ bool managed_save = vshCommandOptBool(cmd, "managed-save");
+ bool snapshots_metadata = vshCommandOptBool(cmd, "snapshots-metadata");
+ bool wipe_storage = vshCommandOptBool(cmd, "wipe-storage");
+ bool remove_storage = false;
+ bool remove_all_storage = vshCommandOptBool(cmd, "remove-all-storage");
+ /* Positive if these items exist. */
+ int has_managed_save = 0;
+ int has_snapshots_metadata = 0;
+ int has_snapshots = 0;
+ /* True if undefine will not strand data, even on older servers. */
+ bool managed_save_safe = false;
+ bool snapshots_safe = false;
+ int rc = -1;
+ int running;
+ /* list of volumes to remove along with this domain */
+ const char *volumes_arg = NULL;
+ char *volumes = NULL;
+ char **volume_tokens = NULL;
+ char *volume_tok = NULL;
+ int nvolume_tokens = 0;
+ char *def = NULL;
+ char *source = NULL;
+ char *target = NULL;
+ int vol_i;
+ int tok_i;
+ xmlDocPtr doc = NULL;
+ xmlXPathContextPtr ctxt = NULL;
+ xmlNodePtr *vol_nodes = NULL;
+ int nvolumes = 0;
+ virStorageVolPtr vol = NULL;
+ bool vol_del_failed = false;
+
+ if (managed_save) {
+ flags |= VIR_DOMAIN_UNDEFINE_MANAGED_SAVE;
+ managed_save_safe = true;
+ }
+ if (snapshots_metadata) {
+ flags |= VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA;
+ snapshots_safe = true;
+ }
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+ return false;
+
+ /* check if a string that should contain list of volumes to remove is present */
+ if (vshCommandOptString(cmd, "storage", &volumes_arg) > 0) {
+ volumes = vshStrdup(ctl, volumes_arg);
+
+ if (remove_all_storage) {
+ vshError(ctl, _("Specified both --storage and --remove-all-storage"));
+ goto cleanup;
+ }
+ remove_storage = true;
+ }
+
+ /* Do some flag manipulation. The goal here is to disable bits
+ * from flags to reduce the likelihood of a server rejecting
+ * unknown flag bits, as well as to track conditions which are
+ * safe by default for the given hypervisor and server version. */
+ running = virDomainIsActive(dom);
+ if (running < 0) {
+ virshReportError(ctl);
+ goto cleanup;
+ }
+ if (!running) {
+ /* Undefine with snapshots only fails for inactive domains,
+ * and managed save only exists on inactive domains; if
+ * running, then we don't want to remove anything. */
+ has_managed_save = virDomainHasManagedSaveImage(dom, 0);
+ if (has_managed_save < 0) {
+ if (last_error->code != VIR_ERR_NO_SUPPORT) {
+ virshReportError(ctl);
+ goto cleanup;
+ }
+ virFreeError(last_error);
+ last_error = NULL;
+ has_managed_save = 0;
+ }
+
+ has_snapshots = virDomainSnapshotNum(dom, 0);
+ if (has_snapshots < 0) {
+ if (last_error->code != VIR_ERR_NO_SUPPORT) {
+ virshReportError(ctl);
+ goto cleanup;
+ }
+ virFreeError(last_error);
+ last_error = NULL;
+ has_snapshots = 0;
+ }
+ if (has_snapshots) {
+ has_snapshots_metadata
+ = virDomainSnapshotNum(dom, VIR_DOMAIN_SNAPSHOT_LIST_METADATA);
+ if (has_snapshots_metadata < 0) {
+ /* The server did not know the new flag, assume that all
+ snapshots have metadata. */
+ virFreeError(last_error);
+ last_error = NULL;
+ has_snapshots_metadata = has_snapshots;
+ } else {
+ /* The server knew the new flag, all aspects of
+ * undefineFlags are safe. */
+ managed_save_safe = snapshots_safe = true;
+ }
+ }
+ }
+ if (!has_managed_save) {
+ flags &= ~VIR_DOMAIN_UNDEFINE_MANAGED_SAVE;
+ managed_save_safe = true;
+ }
+ if (has_snapshots == 0) {
+ snapshots_safe = true;
+ }
+ if (has_snapshots_metadata == 0) {
+ flags &= ~VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA;
+ snapshots_safe = true;
+ }
+
+ /* Stash domain description for later use */
+ if (remove_storage || remove_all_storage) {
+ if (running) {
+ vshError(ctl, _("Storage volume deletion is supported only on stopped domains"));
+ goto cleanup;
+ }
+
+ if (!(def = virDomainGetXMLDesc(dom, 0))) {
+ vshError(ctl, _("Could not retrieve domain XML description"));
+ goto cleanup;
+ }
+ }
+
+ /* Generally we want to try the new API first. However, while
+ * virDomainUndefineFlags was introduced at the same time as
+ * VIR_DOMAIN_UNDEFINE_MANAGED_SAVE in 0.9.4, the
+ * VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA flag was not present
+ * until 0.9.5; skip to piecewise emulation if we couldn't prove
+ * above that the new API is safe. */
+ if (managed_save_safe && snapshots_safe) {
+ rc = virDomainUndefineFlags(dom, flags);
+ if (rc == 0 || (last_error->code != VIR_ERR_NO_SUPPORT &&
+ last_error->code != VIR_ERR_INVALID_ARG))
+ goto out;
+ virFreeError(last_error);
+ last_error = NULL;
+ }
+
+ /* The new API is unsupported or unsafe; fall back to doing things
+ * piecewise. */
+ if (has_managed_save) {
+ if (!managed_save) {
+ vshError(ctl, "%s",
+ _("Refusing to undefine while domain managed save "
+ "image exists"));
+ goto cleanup;
+ }
+ if (virDomainManagedSaveRemove(dom, 0) < 0) {
+ virshReportError(ctl);
+ goto cleanup;
+ }
+ }
+
+ /* No way to emulate deletion of just snapshot metadata
+ * without support for the newer flags. Oh well. */
+ if (has_snapshots_metadata) {
+ vshError(ctl,
+ snapshots_metadata ?
+ _("Unable to remove metadata of %d snapshots") :
+ _("Refusing to undefine while %d snapshots exist"),
+ has_snapshots_metadata);
+ goto cleanup;
+ }
+
+ rc = virDomainUndefine(dom);
+
+out:
+ if (rc == 0) {
+ vshPrint(ctl, _("Domain %s has been undefined\n"), name);
+ ret = true;
+ } else {
+ vshError(ctl, _("Failed to undefine domain %s"), name);
+ goto cleanup;
+ }
+
+ /* try to undefine storage volumes associated with this domain, if it's requested */
+ if (remove_storage || remove_all_storage) {
+ ret = false;
+
+ /* tokenize the string from user and save it's parts into an array */
+ if (volumes) {
+ /* count the delimiters */
+ volume_tok = volumes;
+ nvolume_tokens = 1; /* we need at least one member */
+ while (*volume_tok) {
+ if (*volume_tok == ',')
+ nvolume_tokens++;
+ volume_tok++;
+ }
+
+ volume_tokens = vshCalloc(ctl, nvolume_tokens, sizeof(char *));
+
+ /* tokenize the input string */
+ nvolume_tokens = 0;
+ volume_tok = volumes;
+ do {
+ volume_tokens[nvolume_tokens] = strsep(&volume_tok, ",");
+ nvolume_tokens++;
+ } while (volume_tok);
+ }
+
+ doc = virXMLParseStringCtxt(def, _("(domain_definition)"), &ctxt);
+ if (!doc)
+ goto cleanup;
+
+ nvolumes = virXPathNodeSet("./devices/disk", ctxt, &vol_nodes);
+
+ if (nvolumes < 0)
+ goto cleanup;
+
+ for (vol_i = 0; vol_i < nvolumes; vol_i++) {
+ ctxt->node = vol_nodes[vol_i];
+ VIR_FREE(target);
+ VIR_FREE(source);
+ if (vol) {
+ virStorageVolFree(vol);
+ vol = NULL;
+ }
+
+ /* get volume source and target paths */
+ if (!(target = virXPathString("string(./target/@dev)", ctxt))) {
+ vshError(ctl, _("Failed to enumerate devices"));
+ goto cleanup;
+ }
+
+ if (!(source = virXPathString("string("
+ "./source/@file|"
+ "./source/@dir|"
+ "./source/@name|"
+ "./source/@dev)", ctxt)) &&
+ virGetLastError())
+ goto cleanup;
+
+ /* lookup if volume was selected by user */
+ if (volumes) {
+ volume_tok = NULL;
+ for (tok_i = 0; tok_i < nvolume_tokens; tok_i++) {
+ if (volume_tokens[tok_i] &&
+ (STREQ_NULLABLE(volume_tokens[tok_i], target) ||
+ STREQ_NULLABLE(volume_tokens[tok_i], source))) {
+ volume_tok = volume_tokens[tok_i];
+ volume_tokens[tok_i] = NULL;
+ break;
+ }
+ }
+ if (!volume_tok)
+ continue;
+ }
+
+ if (!source)
+ continue;
+
+ if (!(vol = virStorageVolLookupByPath(ctl->conn, source))) {
+ vshPrint(ctl,
+ _("Storage volume '%s'(%s) is not managed by libvirt. "
+ "Remove it manually.\n"), target, source);
+ virResetLastError();
+ continue;
+ }
+
+ if (wipe_storage) {
+ vshPrint(ctl, _("Wiping volume '%s'(%s) ... "), target, source);
+ fflush(stdout);
+ if (virStorageVolWipe(vol, 0) < 0) {
+ vshError(ctl, _("Failed! Volume not removed."));
+ vol_del_failed = true;
+ continue;
+ } else {
+ vshPrint(ctl, _("Done.\n"));
+ }
+ }
+
+ /* delete the volume */
+ if (virStorageVolDelete(vol, 0) < 0) {
+ vshError(ctl, _("Failed to remove storage volume '%s'(%s)"),
+ target, source);
+ vol_del_failed = true;
+ }
+ vshPrint(ctl, _("Volume '%s' removed.\n"), volume_tok?volume_tok:source);
+ }
+
+ /* print volumes specified by user that were not found in domain definition */
+ if (volumes) {
+ for (tok_i = 0; tok_i < nvolume_tokens; tok_i++) {
+ if (volume_tokens[tok_i])
+ vshPrint(ctl, _("Volume '%s' was not found in domain's "
+ "definition.\n"),
+ volume_tokens[tok_i]);
+ }
+ }
+
+ if (!vol_del_failed)
+ ret = true;
+ }
+
+cleanup:
+ VIR_FREE(source);
+ VIR_FREE(target);
+ VIR_FREE(volumes);
+ VIR_FREE(volume_tokens);
+ VIR_FREE(def);
+ VIR_FREE(vol_nodes);
+ if (vol)
+ virStorageVolFree(vol);
+ xmlFreeDoc(doc);
+ xmlXPathFreeContext(ctxt);
+ virDomainFree(dom);
+ return ret;
+}
+
+/*
+ * "start" command
+ */
+static const vshCmdInfo info_start[] = {
+ {"help", N_("start a (previously defined) inactive domain")},
+ {"desc", N_("Start a domain, either from the last managedsave\n"
+ " state, or via a fresh boot if no managedsave state\n"
+ " is present.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_start[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("name of the inactive domain")},
+#ifndef WIN32
+ {"console", VSH_OT_BOOL, 0, N_("attach to console after creation")},
+#endif
+ {"paused", VSH_OT_BOOL, 0, N_("leave the guest paused after creation")},
+ {"autodestroy", VSH_OT_BOOL, 0,
+ N_("automatically destroy the guest when virsh disconnects")},
+ {"bypass-cache", VSH_OT_BOOL, 0,
+ N_("avoid file system cache when loading")},
+ {"force-boot", VSH_OT_BOOL, 0,
+ N_("force fresh boot by discarding any managed save")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdStart(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom;
+ bool ret = false;
+#ifndef WIN32
+ bool console = vshCommandOptBool(cmd, "console");
+#endif
+ unsigned int flags = VIR_DOMAIN_NONE;
+ int rc;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomainBy(ctl, cmd, NULL,
+ VSH_BYNAME | VSH_BYUUID)))
+ return false;
+
+ if (virDomainGetID(dom) != (unsigned int)-1) {
+ vshError(ctl, "%s", _("Domain is already active"));
+ virDomainFree(dom);
+ return false;
+ }
+
+ if (vshCommandOptBool(cmd, "paused"))
+ flags |= VIR_DOMAIN_START_PAUSED;
+ if (vshCommandOptBool(cmd, "autodestroy"))
+ flags |= VIR_DOMAIN_START_AUTODESTROY;
+ if (vshCommandOptBool(cmd, "bypass-cache"))
+ flags |= VIR_DOMAIN_START_BYPASS_CACHE;
+ if (vshCommandOptBool(cmd, "force-boot"))
+ flags |= VIR_DOMAIN_START_FORCE_BOOT;
+
+ /* We can emulate force boot, even for older servers that reject it. */
+ if (flags & VIR_DOMAIN_START_FORCE_BOOT) {
+ if (virDomainCreateWithFlags(dom, flags) == 0)
+ goto started;
+ if (last_error->code != VIR_ERR_NO_SUPPORT &&
+ last_error->code != VIR_ERR_INVALID_ARG) {
+ virshReportError(ctl);
+ goto cleanup;
+ }
+ virFreeError(last_error);
+ last_error = NULL;
+ rc = virDomainHasManagedSaveImage(dom, 0);
+ if (rc < 0) {
+ /* No managed save image to remove */
+ virFreeError(last_error);
+ last_error = NULL;
+ } else if (rc > 0) {
+ if (virDomainManagedSaveRemove(dom, 0) < 0) {
+ virshReportError(ctl);
+ goto cleanup;
+ }
+ }
+ flags &= ~VIR_DOMAIN_START_FORCE_BOOT;
+ }
+
+ /* Prefer older API unless we have to pass a flag. */
+ if ((flags ? virDomainCreateWithFlags(dom, flags)
+ : virDomainCreate(dom)) < 0) {
+ vshError(ctl, _("Failed to start domain %s"), virDomainGetName(dom));
+ goto cleanup;
+ }
+
+started:
+ vshPrint(ctl, _("Domain %s started\n"),
+ virDomainGetName(dom));
+#ifndef WIN32
+ if (console && !cmdRunConsole(ctl, dom, NULL, 0))
+ goto cleanup;
+#endif
+
+ ret = true;
+
+cleanup:
+ virDomainFree(dom);
+ return ret;
+}
+
+/*
+ * "save" command
+ */
+static const vshCmdInfo info_save[] = {
+ {"help", N_("save a domain state to a file")},
+ {"desc", N_("Save the RAM state of a running domain.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_save[] = {
+ {"bypass-cache", VSH_OT_BOOL, 0, N_("avoid file system cache when saving")},
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("where to save the data")},
+ {"xml", VSH_OT_STRING, 0,
+ N_("filename containing updated XML for the target")},
+ {"running", VSH_OT_BOOL, 0, N_("set domain to be running on restore")},
+ {"paused", VSH_OT_BOOL, 0, N_("set domain to be paused on restore")},
+ {"verbose", VSH_OT_BOOL, 0, N_("display the progress of save")},
+ {NULL, 0, 0, NULL}
+};
+
+static void
+doSave(void *opaque)
+{
+ vshCtrlData *data = opaque;
+ vshControl *ctl = data->ctl;
+ const vshCmd *cmd = data->cmd;
+ char ret = '1';
+ virDomainPtr dom = NULL;
+ const char *name = NULL;
+ const char *to = NULL;
+ unsigned int flags = 0;
+ const char *xmlfile = NULL;
+ char *xml = NULL;
+ sigset_t sigmask, oldsigmask;
+
+ sigemptyset(&sigmask);
+ sigaddset(&sigmask, SIGINT);
+ if (pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask) < 0)
+ goto out_sig;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ goto out;
+
+ if (vshCommandOptString(cmd, "file", &to) <= 0)
+ goto out;
+
+ if (vshCommandOptBool(cmd, "bypass-cache"))
+ flags |= VIR_DOMAIN_SAVE_BYPASS_CACHE;
+ if (vshCommandOptBool(cmd, "running"))
+ flags |= VIR_DOMAIN_SAVE_RUNNING;
+ if (vshCommandOptBool(cmd, "paused"))
+ flags |= VIR_DOMAIN_SAVE_PAUSED;
+
+ if (vshCommandOptString(cmd, "xml", &xmlfile) < 0) {
+ vshError(ctl, "%s", _("malformed xml argument"));
+ goto out;
+ }
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+ goto out;
+
+ if (xmlfile &&
+ virFileReadAll(xmlfile, 8192, &xml) < 0)
+ goto out;
+
+ if (((flags || xml)
+ ? virDomainSaveFlags(dom, to, xml, flags)
+ : virDomainSave(dom, to)) < 0) {
+ vshError(ctl, _("Failed to save domain %s to %s"), name, to);
+ goto out;
+ }
+
+ ret = '0';
+
+out:
+ pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
+out_sig:
+ if (dom) virDomainFree(dom);
+ VIR_FREE(xml);
+ ignore_value(safewrite(data->writefd, &ret, sizeof(ret)));
+}
+
+static bool
+cmdSave(vshControl *ctl, const vshCmd *cmd)
+{
+ bool ret = false;
+ virDomainPtr dom = NULL;
+ int p[2] = {-1. -1};
+ virThread workerThread;
+ bool verbose = false;
+ vshCtrlData data;
+ const char *to = NULL;
+ const char *name = NULL;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+ return false;
+
+ if (vshCommandOptString(cmd, "file", &to) <= 0)
+ goto cleanup;
+
+ if (vshCommandOptBool(cmd, "verbose"))
+ verbose = true;
+
+ if (pipe(p) < 0)
+ goto cleanup;
+
+ data.ctl = ctl;
+ data.cmd = cmd;
+ data.writefd = p[1];
+
+ if (virThreadCreate(&workerThread,
+ true,
+ doSave,
+ &data) < 0)
+ goto cleanup;
+
+ ret = vshWatchJob(ctl, dom, verbose, p[0], 0, NULL, NULL, _("Save"));
+
+ virThreadJoin(&workerThread);
+
+ if (ret)
+ vshPrint(ctl, _("\nDomain %s saved to %s\n"), name, to);
+
+cleanup:
+ if (dom)
+ virDomainFree(dom);
+ return ret;
+}
+
+/*
+ * "save-image-dumpxml" command
+ */
+static const vshCmdInfo info_save_image_dumpxml[] = {
+ {"help", N_("saved state domain information in XML")},
+ {"desc", N_("Output the domain information for a saved state file,\n"
+ "as an XML dump to stdout.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_save_image_dumpxml[] = {
+ {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("saved state file to read")},
+ {"security-info", VSH_OT_BOOL, 0, N_("include security sensitive information in XML dump")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdSaveImageDumpxml(vshControl *ctl, const vshCmd *cmd)
+{
+ const char *file = NULL;
+ bool ret = false;
+ unsigned int flags = 0;
+ char *xml = NULL;
+
+ if (vshCommandOptBool(cmd, "security-info"))
+ flags |= VIR_DOMAIN_XML_SECURE;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (vshCommandOptString(cmd, "file", &file) <= 0)
+ return false;
+
+ xml = virDomainSaveImageGetXMLDesc(ctl->conn, file, flags);
+ if (!xml)
+ goto cleanup;
+
+ vshPrint(ctl, "%s", xml);
+ ret = true;
+
+cleanup:
+ VIR_FREE(xml);
+ return ret;
+}
+
+/*
+ * "save-image-define" command
+ */
+static const vshCmdInfo info_save_image_define[] = {
+ {"help", N_("redefine the XML for a domain's saved state file")},
+ {"desc", N_("Replace the domain XML associated with a saved state file")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_save_image_define[] = {
+ {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("saved state file to modify")},
+ {"xml", VSH_OT_STRING, VSH_OFLAG_REQ,
+ N_("filename containing updated XML for the target")},
+ {"running", VSH_OT_BOOL, 0, N_("set domain to be running on restore")},
+ {"paused", VSH_OT_BOOL, 0, N_("set domain to be paused on restore")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdSaveImageDefine(vshControl *ctl, const vshCmd *cmd)
+{
+ const char *file = NULL;
+ bool ret = false;
+ const char *xmlfile = NULL;
+ char *xml = NULL;
+ unsigned int flags = 0;
+
+ if (vshCommandOptBool(cmd, "running"))
+ flags |= VIR_DOMAIN_SAVE_RUNNING;
+ if (vshCommandOptBool(cmd, "paused"))
+ flags |= VIR_DOMAIN_SAVE_PAUSED;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (vshCommandOptString(cmd, "file", &file) <= 0)
+ return false;
+
+ if (vshCommandOptString(cmd, "xml", &xmlfile) <= 0) {
+ vshError(ctl, "%s", _("malformed or missing xml argument"));
+ return false;
+ }
+
+ if (virFileReadAll(xmlfile, 8192, &xml) < 0)
+ goto cleanup;
+
+ if (virDomainSaveImageDefineXML(ctl->conn, file, xml, flags) < 0) {
+ vshError(ctl, _("Failed to update %s"), file);
+ goto cleanup;
+ }
+
+ vshPrint(ctl, _("State file %s updated.\n"), file);
+ ret = true;
+
+cleanup:
+ VIR_FREE(xml);
+ return ret;
+}
+
+/*
+ * "save-image-edit" command
+ */
+static const vshCmdInfo info_save_image_edit[] = {
+ {"help", N_("edit XML for a domain's saved state file")},
+ {"desc", N_("Edit the domain XML associated with a saved state file")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_save_image_edit[] = {
+ {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("saved state file to edit")},
+ {"running", VSH_OT_BOOL, 0, N_("set domain to be running on restore")},
+ {"paused", VSH_OT_BOOL, 0, N_("set domain to be paused on restore")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdSaveImageEdit(vshControl *ctl, const vshCmd *cmd)
+{
+ const char *file = NULL;
+ bool ret = false;
+ unsigned int getxml_flags = VIR_DOMAIN_XML_SECURE;
+ unsigned int define_flags = 0;
+
+ if (vshCommandOptBool(cmd, "running"))
+ define_flags |= VIR_DOMAIN_SAVE_RUNNING;
+ if (vshCommandOptBool(cmd, "paused"))
+ define_flags |= VIR_DOMAIN_SAVE_PAUSED;
+
+ /* Normally, we let the API reject mutually exclusive flags.
+ * However, in the edit cycle, we let the user retry if the define
+ * step fails, but the define step will always fail on invalid
+ * flags, so we reject it up front to avoid looping. */
+ if (define_flags == (VIR_DOMAIN_SAVE_RUNNING | VIR_DOMAIN_SAVE_PAUSED)) {
+ vshError(ctl, "%s", _("--running and --saved are mutually exclusive"));
+ return false;
+ }
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (vshCommandOptString(cmd, "file", &file) <= 0)
+ return false;
+
+#define EDIT_GET_XML \
+ virDomainSaveImageGetXMLDesc(ctl->conn, file, getxml_flags)
+#define EDIT_NOT_CHANGED \
+ vshPrint(ctl, _("Saved image %s XML configuration " \
+ "not changed.\n"), file); \
+ ret = true; goto edit_cleanup;
+#define EDIT_DEFINE \
+ virDomainSaveImageDefineXML(ctl->conn, file, doc_edited, define_flags)
+#define EDIT_FREE /* */
+#include "virsh-edit.c"
+
+ vshPrint(ctl, _("State file %s edited.\n"), file);
+ ret = true;
+
+cleanup:
+ return ret;
+}
+
+/*
+ * "managedsave" command
+ */
+static const vshCmdInfo info_managedsave[] = {
+ {"help", N_("managed save of a domain state")},
+ {"desc", N_("Save and destroy a running domain, so it can be restarted from\n"
+ " the same state at a later time. When the virsh 'start'\n"
+ " command is next run for the domain, it will automatically\n"
+ " be started from this saved state.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_managedsave[] = {
+ {"bypass-cache", VSH_OT_BOOL, 0, N_("avoid file system cache when saving")},
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"running", VSH_OT_BOOL, 0, N_("set domain to be running on next start")},
+ {"paused", VSH_OT_BOOL, 0, N_("set domain to be paused on next start")},
+ {"verbose", VSH_OT_BOOL, 0, N_("display the progress of save")},
+ {NULL, 0, 0, NULL}
+};
+
+static void
+doManagedsave(void *opaque)
+{
+ char ret = '1';
+ vshCtrlData *data = opaque;
+ vshControl *ctl = data->ctl;
+ const vshCmd *cmd = data->cmd;
+ virDomainPtr dom = NULL;
+ const char *name;
+ unsigned int flags = 0;
+ sigset_t sigmask, oldsigmask;
+
+ sigemptyset(&sigmask);
+ sigaddset(&sigmask, SIGINT);
+ if (pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask) < 0)
+ goto out_sig;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ goto out;
+
+ if (vshCommandOptBool(cmd, "bypass-cache"))
+ flags |= VIR_DOMAIN_SAVE_BYPASS_CACHE;
+ if (vshCommandOptBool(cmd, "running"))
+ flags |= VIR_DOMAIN_SAVE_RUNNING;
+ if (vshCommandOptBool(cmd, "paused"))
+ flags |= VIR_DOMAIN_SAVE_PAUSED;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+ goto out;
+
+ if (virDomainManagedSave(dom, flags) < 0) {
+ vshError(ctl, _("Failed to save domain %s state"), name);
+ goto out;
+ }
+
+ ret = '0';
+out:
+ pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
+out_sig:
+ if (dom)
+ virDomainFree(dom);
+ ignore_value(safewrite(data->writefd, &ret, sizeof(ret)));
+}
+
+static bool
+cmdManagedSave(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom;
+ int p[2] = { -1, -1};
+ bool ret = false;
+ bool verbose = false;
+ const char *name = NULL;
+ vshCtrlData data;
+ virThread workerThread;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+ return false;
+
+ if (vshCommandOptBool(cmd, "verbose"))
+ verbose = true;
+
+ if (pipe(p) < 0)
+ goto cleanup;
+
+ data.ctl = ctl;
+ data.cmd = cmd;
+ data.writefd = p[1];
+
+ if (virThreadCreate(&workerThread,
+ true,
+ doManagedsave,
+ &data) < 0)
+ goto cleanup;
+
+ ret = vshWatchJob(ctl, dom, verbose, p[0], 0,
+ NULL, NULL, _("Managedsave"));
+
+ virThreadJoin(&workerThread);
+
+ if (ret)
+ vshPrint(ctl, _("\nDomain %s state saved by libvirt\n"), name);
+
+cleanup:
+ virDomainFree(dom);
+ VIR_FORCE_CLOSE(p[0]);
+ VIR_FORCE_CLOSE(p[1]);
+ return ret;
+}
+
+/*
+ * "managedsave-remove" command
+ */
+static const vshCmdInfo info_managedsaveremove[] = {
+ {"help", N_("Remove managed save of a domain")},
+ {"desc", N_("Remove an existing managed save state file from a domain")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_managedsaveremove[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdManagedSaveRemove(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom;
+ const char *name;
+ bool ret = false;
+ int hassave;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+ return false;
+
+ hassave = virDomainHasManagedSaveImage(dom, 0);
+ if (hassave < 0) {
+ vshError(ctl, "%s", _("Failed to check for domain managed save image"));
+ goto cleanup;
+ }
+
+ if (hassave) {
+ if (virDomainManagedSaveRemove(dom, 0) < 0) {
+ vshError(ctl, _("Failed to remove managed save image for domain %s"),
+ name);
+ goto cleanup;
+ }
+ else
+ vshPrint(ctl, _("Removed managedsave image for domain %s"), name);
+ }
+ else
+ vshPrint(ctl, _("Domain %s has no manage save image; removal skipped"),
+ name);
+
+ ret = true;
+
+cleanup:
+ virDomainFree(dom);
+ return ret;
+}
+
+/*
+ * "schedinfo" command
+ */
+static const vshCmdInfo info_schedinfo[] = {
+ {"help", N_("show/set scheduler parameters")},
+ {"desc", N_("Show/Set scheduler parameters.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_schedinfo[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"set", VSH_OT_STRING, VSH_OFLAG_NONE, N_("parameter=value")},
+ {"weight", VSH_OT_INT, VSH_OFLAG_NONE, N_("weight for XEN_CREDIT")},
+ {"cap", VSH_OT_INT, VSH_OFLAG_NONE, N_("cap for XEN_CREDIT")},
+ {"current", VSH_OT_BOOL, 0, N_("get/set current scheduler info")},
+ {"config", VSH_OT_BOOL, 0, N_("get/set value to be used on next boot")},
+ {"live", VSH_OT_BOOL, 0, N_("get/set value from running domain")},
+ {NULL, 0, 0, NULL}
+};
+
+static int
+cmdSchedInfoUpdate(vshControl *ctl, const vshCmd *cmd,
+ virTypedParameterPtr param)
+{
+ const char *data = NULL;
+
+ /* Legacy 'weight' parameter */
+ if (STREQ(param->field, "weight") &&
+ param->type == VIR_TYPED_PARAM_UINT &&
+ vshCommandOptBool(cmd, "weight")) {
+ int val;
+ if (vshCommandOptInt(cmd, "weight", &val) <= 0) {
+ vshError(ctl, "%s", _("Invalid value of weight"));
+ return -1;
+ } else {
+ param->value.ui = val;
+ }
+ return 1;
+ }
+
+ /* Legacy 'cap' parameter */
+ if (STREQ(param->field, "cap") &&
+ param->type == VIR_TYPED_PARAM_UINT &&
+ vshCommandOptBool(cmd, "cap")) {
+ int val;
+ if (vshCommandOptInt(cmd, "cap", &val) <= 0) {
+ vshError(ctl, "%s", _("Invalid value of cap"));
+ return -1;
+ } else {
+ param->value.ui = val;
+ }
+ return 1;
+ }
+
+ if (vshCommandOptString(cmd, "set", &data) > 0) {
+ char *val = strchr(data, '=');
+ int match = 0;
+ if (!val) {
+ vshError(ctl, "%s", _("Invalid syntax for --set, expecting name=value"));
+ return -1;
+ }
+ *val = '\0';
+ match = STREQ(data, param->field);
+ *val = '=';
+ val++;
+
+ if (!match)
+ return 0;
+
+ switch (param->type) {
+ case VIR_TYPED_PARAM_INT:
+ if (virStrToLong_i(val, NULL, 10, ¶m->value.i) < 0) {
+ vshError(ctl, "%s",
+ _("Invalid value for parameter, expecting an int"));
+ return -1;
+ }
+ break;
+ case VIR_TYPED_PARAM_UINT:
+ if (virStrToLong_ui(val, NULL, 10, ¶m->value.ui) < 0) {
+ vshError(ctl, "%s",
+ _("Invalid value for parameter, expecting an unsigned int"));
+ return -1;
+ }
+ break;
+ case VIR_TYPED_PARAM_LLONG:
+ if (virStrToLong_ll(val, NULL, 10, ¶m->value.l) < 0) {
+ vshError(ctl, "%s",
+ _("Invalid value for parameter, expecting a long long"));
+ return -1;
+ }
+ break;
+ case VIR_TYPED_PARAM_ULLONG:
+ if (virStrToLong_ull(val, NULL, 10, ¶m->value.ul) < 0) {
+ vshError(ctl, "%s",
+ _("Invalid value for parameter, expecting an unsigned long long"));
+ return -1;
+ }
+ break;
+ case VIR_TYPED_PARAM_DOUBLE:
+ if (virStrToDouble(val, NULL, ¶m->value.d) < 0) {
+ vshError(ctl, "%s", _("Invalid value for parameter, expecting a double"));
+ return -1;
+ }
+ break;
+ case VIR_TYPED_PARAM_BOOLEAN:
+ param->value.b = STREQ(val, "0") ? 0 : 1;
+ }
+ return 1;
+ }
+
+ return 0;
+}
+
+static bool
+cmdSchedinfo(vshControl *ctl, const vshCmd *cmd)
+{
+ char *schedulertype;
+ virDomainPtr dom;
+ virTypedParameterPtr params = NULL;
+ int nparams = 0;
+ int update = 0;
+ int i, ret;
+ bool ret_val = false;
+ unsigned int flags = 0;
+ bool current = vshCommandOptBool(cmd, "current");
+ bool config = vshCommandOptBool(cmd, "config");
+ bool live = vshCommandOptBool(cmd, "live");
+
+ if (current) {
+ if (live || config) {
+ vshError(ctl, "%s", _("--current must be specified exclusively"));
+ return false;
+ }
+ flags = VIR_DOMAIN_AFFECT_CURRENT;
+ } else {
+ if (config)
+ flags |= VIR_DOMAIN_AFFECT_CONFIG;
+ if (live)
+ flags |= VIR_DOMAIN_AFFECT_LIVE;
+ }
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ return false;
+
+ /* Print SchedulerType */
+ schedulertype = virDomainGetSchedulerType(dom, &nparams);
+ if (schedulertype != NULL) {
+ vshPrint(ctl, "%-15s: %s\n", _("Scheduler"),
+ schedulertype);
+ VIR_FREE(schedulertype);
+ } else {
+ vshPrint(ctl, "%-15s: %s\n", _("Scheduler"), _("Unknown"));
+ goto cleanup;
+ }
+
+ if (nparams) {
+ params = vshMalloc(ctl, sizeof(*params) * nparams);
+
+ memset(params, 0, sizeof(*params) * nparams);
+ if (flags || current) {
+ /* We cannot query both live and config at once, so settle
+ on current in that case. If we are setting, then the
+ two values should match when we re-query; otherwise, we
+ report the error later. */
+ ret = virDomainGetSchedulerParametersFlags(dom, params, &nparams,
+ ((live && config) ? 0
+ : flags));
+ } else {
+ ret = virDomainGetSchedulerParameters(dom, params, &nparams);
+ }
+ if (ret == -1)
+ goto cleanup;
+
+ /* See if any params are being set */
+ for (i = 0; i < nparams; i++) {
+ ret = cmdSchedInfoUpdate(ctl, cmd, &(params[i]));
+ if (ret == -1)
+ goto cleanup;
+
+ if (ret == 1)
+ update = 1;
+ }
+
+ /* Update parameters & refresh data */
+ if (update) {
+ if (flags || current)
+ ret = virDomainSetSchedulerParametersFlags(dom, params,
+ nparams, flags);
+ else
+ ret = virDomainSetSchedulerParameters(dom, params, nparams);
+ if (ret == -1)
+ goto cleanup;
+
+ if (flags || current)
+ ret = virDomainGetSchedulerParametersFlags(dom, params,
+ &nparams,
+ ((live && config) ? 0
+ : flags));
+ else
+ ret = virDomainGetSchedulerParameters(dom, params, &nparams);
+ if (ret == -1)
+ goto cleanup;
+ } else {
+ /* See if we've tried to --set var=val. If so, the fact that
+ we reach this point (with update == 0) means that "var" did
+ not match any of the settable parameters. Report the error. */
+ const char *var_value_pair = NULL;
+ if (vshCommandOptString(cmd, "set", &var_value_pair) > 0) {
+ vshError(ctl, _("invalid scheduler option: %s"),
+ var_value_pair);
+ goto cleanup;
+ }
+ /* When not doing --set, --live and --config do not mix. */
+ if (live && config) {
+ vshError(ctl, "%s",
+ _("cannot query both live and config at once"));
+ goto cleanup;
+ }
+ }
+
+ ret_val = true;
+ for (i = 0; i < nparams; i++) {
+ char *str = vshGetTypedParamValue(ctl, ¶ms[i]);
+ vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
+ VIR_FREE(str);
+ }
+ }
+
+ cleanup:
+ VIR_FREE(params);
+ virDomainFree(dom);
+ return ret_val;
+}
+
+/*
+ * "restore" command
+ */
+static const vshCmdInfo info_restore[] = {
+ {"help", N_("restore a domain from a saved state in a file")},
+ {"desc", N_("Restore a domain.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_restore[] = {
+ {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("the state to restore")},
+ {"bypass-cache", VSH_OT_BOOL, 0,
+ N_("avoid file system cache when restoring")},
+ {"xml", VSH_OT_STRING, 0,
+ N_("filename containing updated XML for the target")},
+ {"running", VSH_OT_BOOL, 0, N_("restore domain into running state")},
+ {"paused", VSH_OT_BOOL, 0, N_("restore domain into paused state")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdRestore(vshControl *ctl, const vshCmd *cmd)
+{
+ const char *from = NULL;
+ bool ret = false;
+ unsigned int flags = 0;
+ const char *xmlfile = NULL;
+ char *xml = NULL;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (vshCommandOptString(cmd, "file", &from) <= 0)
+ return false;
+
+ if (vshCommandOptBool(cmd, "bypass-cache"))
+ flags |= VIR_DOMAIN_SAVE_BYPASS_CACHE;
+ if (vshCommandOptBool(cmd, "running"))
+ flags |= VIR_DOMAIN_SAVE_RUNNING;
+ if (vshCommandOptBool(cmd, "paused"))
+ flags |= VIR_DOMAIN_SAVE_PAUSED;
+
+ if (vshCommandOptString(cmd, "xml", &xmlfile) < 0) {
+ vshError(ctl, "%s", _("malformed xml argument"));
+ return false;
+ }
+
+ if (xmlfile &&
+ virFileReadAll(xmlfile, 8192, &xml) < 0)
+ goto cleanup;
+
+ if (((flags || xml)
+ ? virDomainRestoreFlags(ctl->conn, from, xml, flags)
+ : virDomainRestore(ctl->conn, from)) < 0) {
+ vshError(ctl, _("Failed to restore domain from %s"), from);
+ goto cleanup;
+ }
+
+ vshPrint(ctl, _("Domain restored from %s\n"), from);
+ ret = true;
+
+cleanup:
+ VIR_FREE(xml);
+ return ret;
+}
+
+/*
+ * "dump" command
+ */
+static const vshCmdInfo info_dump[] = {
+ {"help", N_("dump the core of a domain to a file for analysis")},
+ {"desc", N_("Core dump a domain.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_dump[] = {
+ {"live", VSH_OT_BOOL, 0, N_("perform a live core dump if supported")},
+ {"crash", VSH_OT_BOOL, 0, N_("crash the domain after core dump")},
+ {"bypass-cache", VSH_OT_BOOL, 0,
+ N_("avoid file system cache when saving")},
+ {"reset", VSH_OT_BOOL, 0, N_("reset the domain after core dump")},
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("where to dump the core")},
+ {"verbose", VSH_OT_BOOL, 0, N_("display the progress of dump")},
+ {"memory-only", VSH_OT_BOOL, 0, N_("dump domain's memory only")},
+ {NULL, 0, 0, NULL}
+};
+
+static void
+doDump(void *opaque)
+{
+ char ret = '1';
+ vshCtrlData *data = opaque;
+ vshControl *ctl = data->ctl;
+ const vshCmd *cmd = data->cmd;
+ virDomainPtr dom = NULL;
+ sigset_t sigmask, oldsigmask;
+ const char *name = NULL;
+ const char *to = NULL;
+ unsigned int flags = 0;
+
+ sigemptyset(&sigmask);
+ sigaddset(&sigmask, SIGINT);
+ if (pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask) < 0)
+ goto out_sig;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ goto out;
+
+ if (vshCommandOptString(cmd, "file", &to) <= 0)
+ goto out;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+ goto out;
+
+ if (vshCommandOptBool(cmd, "live"))
+ flags |= VIR_DUMP_LIVE;
+ if (vshCommandOptBool(cmd, "crash"))
+ flags |= VIR_DUMP_CRASH;
+ if (vshCommandOptBool(cmd, "bypass-cache"))
+ flags |= VIR_DUMP_BYPASS_CACHE;
+ if (vshCommandOptBool(cmd, "reset"))
+ flags |= VIR_DUMP_RESET;
+ if (vshCommandOptBool(cmd, "memory-only"))
+ flags |= VIR_DUMP_MEMORY_ONLY;
+
+ if (virDomainCoreDump(dom, to, flags) < 0) {
+ vshError(ctl, _("Failed to core dump domain %s to %s"), name, to);
+ goto out;
+ }
+
+ ret = '0';
+out:
+ pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
+out_sig:
+ if (dom)
+ virDomainFree(dom);
+ ignore_value(safewrite(data->writefd, &ret, sizeof(ret)));
+}
+
+static bool
+cmdDump(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom;
+ int p[2] = { -1, -1};
+ bool ret = false;
+ bool verbose = false;
+ const char *name = NULL;
+ const char *to = NULL;
+ vshCtrlData data;
+ virThread workerThread;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+ return false;
+
+ if (vshCommandOptString(cmd, "file", &to) <= 0)
+ return false;
+
+ if (vshCommandOptBool(cmd, "verbose"))
+ verbose = true;
+
+ if (pipe(p) < 0)
+ goto cleanup;
+
+ data.ctl = ctl;
+ data.cmd = cmd;
+ data.writefd = p[1];
+
+ if (virThreadCreate(&workerThread,
+ true,
+ doDump,
+ &data) < 0)
+ goto cleanup;
+
+ ret = vshWatchJob(ctl, dom, verbose, p[0], 0, NULL, NULL, _("Dump"));
+
+ virThreadJoin(&workerThread);
+
+ if (ret)
+ vshPrint(ctl, _("\nDomain %s dumped to %s\n"), name, to);
+
+cleanup:
+ virDomainFree(dom);
+ VIR_FORCE_CLOSE(p[0]);
+ VIR_FORCE_CLOSE(p[1]);
+ return ret;
+}
+
+static const vshCmdInfo info_screenshot[] = {
+ {"help", N_("take a screenshot of a current domain console and store it "
+ "into a file")},
+ {"desc", N_("screenshot of a current domain console")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_screenshot[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"file", VSH_OT_DATA, VSH_OFLAG_NONE, N_("where to store the screenshot")},
+ {"screen", VSH_OT_INT, VSH_OFLAG_NONE, N_("ID of a screen to take screenshot of")},
+ {NULL, 0, 0, NULL}
+};
+
+/**
+ * Generate string: '-[]'
+ */
+static char *
+vshGenFileName(vshControl *ctl, virDomainPtr dom, const char *mime)
+{
+ char timestr[100];
+ struct timeval cur_time;
+ struct tm time_info;
+ const char *ext = NULL;
+ char *ret = NULL;
+
+ /* We should be already connected, but doesn't
+ * hurt to check */
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return NULL;
+
+ if (!dom) {
+ vshError(ctl, "%s", _("Invalid domain supplied"));
+ return NULL;
+ }
+
+ if (STREQ(mime, "image/x-portable-pixmap"))
+ ext = ".ppm";
+ else if (STREQ(mime, "image/png"))
+ ext = ".png";
+ /* add mime type here */
+
+ gettimeofday(&cur_time, NULL);
+ localtime_r(&cur_time.tv_sec, &time_info);
+ strftime(timestr, sizeof(timestr), "%Y-%m-%d-%H:%M:%S", &time_info);
+
+ if (virAsprintf(&ret, "%s-%s%s", virDomainGetName(dom),
+ timestr, ext ? ext : "") < 0) {
+ vshError(ctl, "%s", _("Out of memory"));
+ return NULL;
+ }
+
+ return ret;
+}
+
+static bool
+cmdScreenshot(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom;
+ const char *name = NULL;
+ char *file = NULL;
+ int fd = -1;
+ virStreamPtr st = NULL;
+ unsigned int screen = 0;
+ unsigned int flags = 0; /* currently unused */
+ int ret = false;
+ bool created = false;
+ bool generated = false;
+ char *mime = NULL;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (vshCommandOptString(cmd, "file", (const char **) &file) < 0) {
+ vshError(ctl, "%s", _("file must not be empty"));
+ return false;
+ }
+
+ if (vshCommandOptUInt(cmd, "screen", &screen) < 0) {
+ vshError(ctl, "%s", _("invalid screen ID"));
+ return false;
+ }
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+ return false;
+
+ st = virStreamNew(ctl->conn, 0);
+
+ mime = virDomainScreenshot(dom, st, screen, flags);
+ if (!mime) {
+ vshError(ctl, _("could not take a screenshot of %s"), name);
+ goto cleanup;
+ }
+
+ if (!file) {
+ if (!(file=vshGenFileName(ctl, dom, mime)))
+ return false;
+ generated = true;
+ }
+
+ if ((fd = open(file, O_WRONLY|O_CREAT|O_EXCL, 0666)) < 0) {
+ if (errno != EEXIST ||
+ (fd = open(file, O_WRONLY|O_TRUNC, 0666)) < 0) {
+ vshError(ctl, _("cannot create file %s"), file);
+ goto cleanup;
+ }
+ } else {
+ created = true;
+ }
+
+ if (virStreamRecvAll(st, vshStreamSink, &fd) < 0) {
+ vshError(ctl, _("could not receive data from domain %s"), name);
+ goto cleanup;
+ }
+
+ if (VIR_CLOSE(fd) < 0) {
+ vshError(ctl, _("cannot close file %s"), file);
+ goto cleanup;
+ }
+
+ if (virStreamFinish(st) < 0) {
+ vshError(ctl, _("cannot close stream on domain %s"), name);
+ goto cleanup;
+ }
+
+ vshPrint(ctl, _("Screenshot saved to %s, with type of %s"), file, mime);
+ ret = true;
+
+cleanup:
+ if (!ret && created)
+ unlink(file);
+ if (generated)
+ VIR_FREE(file);
+ virDomainFree(dom);
+ if (st)
+ virStreamFree(st);
+ VIR_FORCE_CLOSE(fd);
+ VIR_FREE(mime);
+ return ret;
+}
+
+/*
+ * "resume" command
+ */
+static const vshCmdInfo info_resume[] = {
+ {"help", N_("resume a domain")},
+ {"desc", N_("Resume a previously suspended domain.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_resume[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdResume(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom;
+ bool ret = true;
+ const char *name;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+ return false;
+
+ if (virDomainResume(dom) == 0) {
+ vshPrint(ctl, _("Domain %s resumed\n"), name);
+ } else {
+ vshError(ctl, _("Failed to resume domain %s"), name);
+ ret = false;
+ }
+
+ virDomainFree(dom);
+ return ret;
+}
+
+/*
+ * "shutdown" command
+ */
+static const vshCmdInfo info_shutdown[] = {
+ {"help", N_("gracefully shutdown a domain")},
+ {"desc", N_("Run shutdown in the target domain.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_shutdown[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"mode", VSH_OT_STRING, VSH_OFLAG_NONE, N_("shutdown mode: acpi|agent")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdShutdown(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom;
+ bool ret = true;
+ const char *name;
+ const char *mode = NULL;
+ int flags = 0;
+ int rv;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (vshCommandOptString(cmd, "mode", &mode) < 0) {
+ vshError(ctl, "%s", _("Invalid type"));
+ return false;
+ }
+
+ if (mode) {
+ if (STREQ(mode, "acpi")) {
+ flags |= VIR_DOMAIN_SHUTDOWN_ACPI_POWER_BTN;
+ } else if (STREQ(mode, "agent")) {
+ flags |= VIR_DOMAIN_SHUTDOWN_GUEST_AGENT;
+ } else {
+ vshError(ctl, _("Unknown mode %s value, expecting 'acpi' or 'agent'"), mode);
+ return false;
+ }
+ }
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+ return false;
+
+ if (flags)
+ rv = virDomainShutdownFlags(dom, flags);
+ else
+ rv = virDomainShutdown(dom);
+ if (rv == 0) {
+ vshPrint(ctl, _("Domain %s is being shutdown\n"), name);
+ } else {
+ vshError(ctl, _("Failed to shutdown domain %s"), name);
+ ret = false;
+ }
+
+ virDomainFree(dom);
+ return ret;
+}
+
+/*
+ * "reboot" command
+ */
+static const vshCmdInfo info_reboot[] = {
+ {"help", N_("reboot a domain")},
+ {"desc", N_("Run a reboot command in the target domain.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_reboot[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"mode", VSH_OT_STRING, VSH_OFLAG_NONE, N_("shutdown mode: acpi|agent")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdReboot(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom;
+ bool ret = true;
+ const char *name;
+ const char *mode = NULL;
+ int flags = 0;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (vshCommandOptString(cmd, "mode", &mode) < 0) {
+ vshError(ctl, "%s", _("Invalid type"));
+ return false;
+ }
+
+ if (mode) {
+ if (STREQ(mode, "acpi")) {
+ flags |= VIR_DOMAIN_SHUTDOWN_ACPI_POWER_BTN;
+ } else if (STREQ(mode, "agent")) {
+ flags |= VIR_DOMAIN_SHUTDOWN_GUEST_AGENT;
+ } else {
+ vshError(ctl, _("Unknown mode %s value, expecting 'acpi' or 'agent'"), mode);
+ return false;
+ }
+ }
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+ return false;
+
+ if (virDomainReboot(dom, flags) == 0) {
+ vshPrint(ctl, _("Domain %s is being rebooted\n"), name);
+ } else {
+ vshError(ctl, _("Failed to reboot domain %s"), name);
+ ret = false;
+ }
+
+ virDomainFree(dom);
+ return ret;
+}
+
+/*
+ * "reset" command
+ */
+static const vshCmdInfo info_reset[] = {
+ {"help", N_("reset a domain")},
+ {"desc", N_("Reset the target domain as if by power button")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_reset[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdReset(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom;
+ bool ret = true;
+ const char *name;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+ return false;
+
+ if (virDomainReset(dom, 0) == 0) {
+ vshPrint(ctl, _("Domain %s was reset\n"), name);
+ } else {
+ vshError(ctl, _("Failed to reset domain %s"), name);
+ ret = false;
+ }
+
+ virDomainFree(dom);
+ return ret;
+}
+
+/*
+ * "domjobinfo" command
+ */
+static const vshCmdInfo info_domjobinfo[] = {
+ {"help", N_("domain job information")},
+ {"desc", N_("Returns information about jobs running on a domain.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_domjobinfo[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDomjobinfo(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainJobInfo info;
+ virDomainPtr dom;
+ bool ret = true;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ return false;
+
+ if (virDomainGetJobInfo(dom, &info) == 0) {
+ const char *unit;
+ double val;
+
+ vshPrint(ctl, "%-17s ", _("Job type:"));
+ switch (info.type) {
+ case VIR_DOMAIN_JOB_BOUNDED:
+ vshPrint(ctl, "%-12s\n", _("Bounded"));
+ break;
+
+ case VIR_DOMAIN_JOB_UNBOUNDED:
+ vshPrint(ctl, "%-12s\n", _("Unbounded"));
+ break;
+
+ case VIR_DOMAIN_JOB_NONE:
+ default:
+ vshPrint(ctl, "%-12s\n", _("None"));
+ goto cleanup;
+ }
+
+ vshPrint(ctl, "%-17s %-12llu ms\n", _("Time elapsed:"), info.timeElapsed);
+ if (info.type == VIR_DOMAIN_JOB_BOUNDED)
+ vshPrint(ctl, "%-17s %-12llu ms\n", _("Time remaining:"), info.timeRemaining);
+ if (info.dataTotal || info.dataRemaining || info.dataProcessed) {
+ val = prettyCapacity(info.dataProcessed, &unit);
+ vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data processed:"), val, unit);
+ val = prettyCapacity(info.dataRemaining, &unit);
+ vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data remaining:"), val, unit);
+ val = prettyCapacity(info.dataTotal, &unit);
+ vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data total:"), val, unit);
+ }
+ if (info.memTotal || info.memRemaining || info.memProcessed) {
+ val = prettyCapacity(info.memProcessed, &unit);
+ vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory processed:"), val, unit);
+ val = prettyCapacity(info.memRemaining, &unit);
+ vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory remaining:"), val, unit);
+ val = prettyCapacity(info.memTotal, &unit);
+ vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory total:"), val, unit);
+ }
+ if (info.fileTotal || info.fileRemaining || info.fileProcessed) {
+ val = prettyCapacity(info.fileProcessed, &unit);
+ vshPrint(ctl, "%-17s %-.3lf %s\n", _("File processed:"), val, unit);
+ val = prettyCapacity(info.fileRemaining, &unit);
+ vshPrint(ctl, "%-17s %-.3lf %s\n", _("File remaining:"), val, unit);
+ val = prettyCapacity(info.fileTotal, &unit);
+ vshPrint(ctl, "%-17s %-.3lf %s\n", _("File total:"), val, unit);
+ }
+ } else {
+ ret = false;
+ }
+cleanup:
+ virDomainFree(dom);
+ return ret;
+}
+
+/*
+ * "domjobabort" command
+ */
+static const vshCmdInfo info_domjobabort[] = {
+ {"help", N_("abort active domain job")},
+ {"desc", N_("Aborts the currently running domain job")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_domjobabort[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDomjobabort(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom;
+ bool ret = true;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ return false;
+
+ if (virDomainAbortJob(dom) < 0)
+ ret = false;
+
+ virDomainFree(dom);
+ return ret;
+}
+
+/*
+ * "maxvcpus" command
+ */
+static const vshCmdInfo info_maxvcpus[] = {
+ {"help", N_("connection vcpu maximum")},
+ {"desc", N_("Show maximum number of virtual CPUs for guests on this connection.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_maxvcpus[] = {
+ {"type", VSH_OT_STRING, 0, N_("domain type")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdMaxvcpus(vshControl *ctl, const vshCmd *cmd)
+{
+ const char *type = NULL;
+ int vcpus;
+
+ if (vshCommandOptString(cmd, "type", &type) < 0) {
+ vshError(ctl, "%s", _("Invalid type"));
+ return false;
+ }
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ vcpus = virConnectGetMaxVcpus(ctl->conn, type);
+ if (vcpus < 0)
+ return false;
+ vshPrint(ctl, "%d\n", vcpus);
+
+ return true;
+}
+
+/*
+ * "vcpucount" command
+ */
+static const vshCmdInfo info_vcpucount[] = {
+ {"help", N_("domain vcpu counts")},
+ {"desc", N_("Returns the number of virtual CPUs used by the domain.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_vcpucount[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"maximum", VSH_OT_BOOL, 0, N_("get maximum cap on vcpus")},
+ {"active", VSH_OT_BOOL, 0, N_("get number of currently active vcpus")},
+ {"live", VSH_OT_BOOL, 0, N_("get value from running domain")},
+ {"config", VSH_OT_BOOL, 0, N_("get value to be used on next boot")},
+ {"current", VSH_OT_BOOL, 0,
+ N_("get value according to current domain state")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdVcpucount(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom;
+ bool ret = true;
+ bool maximum = vshCommandOptBool(cmd, "maximum");
+ bool active = vshCommandOptBool(cmd, "active");
+ bool config = vshCommandOptBool(cmd, "config");
+ bool live = vshCommandOptBool(cmd, "live");
+ bool current = vshCommandOptBool(cmd, "current");
+ bool all = maximum + active + current + config + live == 0;
+ int count;
+
+ /* We want one of each pair of mutually exclusive options; that
+ * is, use of flags requires exactly two options. We reject the
+ * use of more than 2 flags later on. */
+ if (maximum + active + current + config + live == 1) {
+ if (maximum || active) {
+ vshError(ctl,
+ _("when using --%s, one of --config, --live, or --current "
+ "must be specified"),
+ maximum ? "maximum" : "active");
+ } else {
+ vshError(ctl,
+ _("when using --%s, either --maximum or --active must be "
+ "specified"),
+ (current ? "current" : config ? "config" : "live"));
+ }
+ return false;
+ }
+
+ /* Backwards compatibility: prior to 0.9.4,
+ * VIR_DOMAIN_AFFECT_CURRENT was unsupported, and --current meant
+ * the opposite of --maximum. Translate the old '--current
+ * --live' into the new '--active --live', while treating the new
+ * '--maximum --current' correctly rather than rejecting it as
+ * '--maximum --active'. */
+ if (!maximum && !active && current) {
+ current = false;
+ active = true;
+ }
+
+ if (maximum && active) {
+ vshError(ctl, "%s",
+ _("--maximum and --active cannot both be specified"));
+ return false;
+ }
+ if (current + config + live > 1) {
+ vshError(ctl, "%s",
+ _("--config, --live, and --current are mutually exclusive"));
+ return false;
+ }
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ return false;
+
+ /* In all cases, try the new API first; if it fails because we are
+ * talking to an older client, generally we try a fallback API
+ * before giving up. --current requires the new API, since we
+ * don't know whether the domain is running or inactive. */
+ if (current) {
+ count = virDomainGetVcpusFlags(dom,
+ maximum ? VIR_DOMAIN_VCPU_MAXIMUM : 0);
+ if (count < 0) {
+ virshReportError(ctl);
+ ret = false;
+ } else {
+ vshPrint(ctl, "%d\n", count);
+ }
+ }
+
+ if (all || (maximum && config)) {
+ count = virDomainGetVcpusFlags(dom, (VIR_DOMAIN_VCPU_MAXIMUM |
+ VIR_DOMAIN_AFFECT_CONFIG));
+ if (count < 0 && (last_error->code == VIR_ERR_NO_SUPPORT
+ || last_error->code == VIR_ERR_INVALID_ARG)) {
+ char *tmp;
+ char *xml = virDomainGetXMLDesc(dom, VIR_DOMAIN_XML_INACTIVE);
+ if (xml && (tmp = strstr(xml, "');
+ if (!tmp || virStrToLong_i(tmp + 1, &tmp, 10, &count) < 0)
+ count = -1;
+ }
+ virFreeError(last_error);
+ last_error = NULL;
+ VIR_FREE(xml);
+ }
+
+ if (count < 0) {
+ virshReportError(ctl);
+ ret = false;
+ } else if (all) {
+ vshPrint(ctl, "%-12s %-12s %3d\n", _("maximum"), _("config"),
+ count);
+ } else {
+ vshPrint(ctl, "%d\n", count);
+ }
+ virFreeError(last_error);
+ last_error = NULL;
+ }
+
+ if (all || (maximum && live)) {
+ count = virDomainGetVcpusFlags(dom, (VIR_DOMAIN_VCPU_MAXIMUM |
+ VIR_DOMAIN_AFFECT_LIVE));
+ if (count < 0 && (last_error->code == VIR_ERR_NO_SUPPORT
+ || last_error->code == VIR_ERR_INVALID_ARG)) {
+ count = virDomainGetMaxVcpus(dom);
+ }
+
+ if (count < 0) {
+ virshReportError(ctl);
+ ret = false;
+ } else if (all) {
+ vshPrint(ctl, "%-12s %-12s %3d\n", _("maximum"), _("live"),
+ count);
+ } else {
+ vshPrint(ctl, "%d\n", count);
+ }
+ virFreeError(last_error);
+ last_error = NULL;
+ }
+
+ if (all || (active && config)) {
+ count = virDomainGetVcpusFlags(dom, VIR_DOMAIN_AFFECT_CONFIG);
+ if (count < 0 && (last_error->code == VIR_ERR_NO_SUPPORT
+ || last_error->code == VIR_ERR_INVALID_ARG)) {
+ char *tmp, *end;
+ char *xml = virDomainGetXMLDesc(dom, VIR_DOMAIN_XML_INACTIVE);
+ if (xml && (tmp = strstr(xml, "');
+ if (end) {
+ *end = '\0';
+ tmp = strstr(tmp, "current=");
+ if (!tmp)
+ tmp = end + 1;
+ else {
+ tmp += strlen("current=");
+ tmp += *tmp == '\'' || *tmp == '"';
+ }
+ }
+ if (!tmp || virStrToLong_i(tmp, &tmp, 10, &count) < 0)
+ count = -1;
+ }
+ VIR_FREE(xml);
+ }
+
+ if (count < 0) {
+ virshReportError(ctl);
+ ret = false;
+ } else if (all) {
+ vshPrint(ctl, "%-12s %-12s %3d\n", _("current"), _("config"),
+ count);
+ } else {
+ vshPrint(ctl, "%d\n", count);
+ }
+ virFreeError(last_error);
+ last_error = NULL;
+ }
+
+ if (all || (active && live)) {
+ count = virDomainGetVcpusFlags(dom, VIR_DOMAIN_AFFECT_LIVE);
+ if (count < 0 && (last_error->code == VIR_ERR_NO_SUPPORT
+ || last_error->code == VIR_ERR_INVALID_ARG)) {
+ virDomainInfo info;
+ if (virDomainGetInfo(dom, &info) == 0)
+ count = info.nrVirtCpu;
+ }
+
+ if (count < 0) {
+ virshReportError(ctl);
+ ret = false;
+ } else if (all) {
+ vshPrint(ctl, "%-12s %-12s %3d\n", _("current"), _("live"),
+ count);
+ } else {
+ vshPrint(ctl, "%d\n", count);
+ }
+ virFreeError(last_error);
+ last_error = NULL;
+ }
+
+ virDomainFree(dom);
+ return ret;
+}
+
+/*
+ * "vcpuinfo" command
+ */
+static const vshCmdInfo info_vcpuinfo[] = {
+ {"help", N_("detailed domain vcpu information")},
+ {"desc", N_("Returns basic information about the domain virtual CPUs.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_vcpuinfo[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdVcpuinfo(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainInfo info;
+ virDomainPtr dom;
+ virNodeInfo nodeinfo;
+ virVcpuInfoPtr cpuinfo;
+ unsigned char *cpumaps;
+ int ncpus, maxcpu;
+ size_t cpumaplen;
+ bool ret = true;
+ int n, m;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ return false;
+
+ if (virNodeGetInfo(ctl->conn, &nodeinfo) != 0) {
+ virDomainFree(dom);
+ return false;
+ }
+
+ if (virDomainGetInfo(dom, &info) != 0) {
+ virDomainFree(dom);
+ return false;
+ }
+
+ cpuinfo = vshMalloc(ctl, sizeof(virVcpuInfo)*info.nrVirtCpu);
+ maxcpu = VIR_NODEINFO_MAXCPUS(nodeinfo);
+ cpumaplen = VIR_CPU_MAPLEN(maxcpu);
+ cpumaps = vshMalloc(ctl, info.nrVirtCpu * cpumaplen);
+
+ if ((ncpus = virDomainGetVcpus(dom,
+ cpuinfo, info.nrVirtCpu,
+ cpumaps, cpumaplen)) >= 0) {
+ for (n = 0 ; n < ncpus ; n++) {
+ vshPrint(ctl, "%-15s %d\n", _("VCPU:"), n);
+ vshPrint(ctl, "%-15s %d\n", _("CPU:"), cpuinfo[n].cpu);
+ vshPrint(ctl, "%-15s %s\n", _("State:"),
+ _(vshDomainVcpuStateToString(cpuinfo[n].state)));
+ if (cpuinfo[n].cpuTime != 0) {
+ double cpuUsed = cpuinfo[n].cpuTime;
+
+ cpuUsed /= 1000000000.0;
+
+ vshPrint(ctl, "%-15s %.1lfs\n", _("CPU time:"), cpuUsed);
+ }
+ vshPrint(ctl, "%-15s ", _("CPU Affinity:"));
+ for (m = 0; m < maxcpu; m++) {
+ vshPrint(ctl, "%c", VIR_CPU_USABLE(cpumaps, cpumaplen, n, m) ? 'y' : '-');
+ }
+ vshPrint(ctl, "\n");
+ if (n < (ncpus - 1)) {
+ vshPrint(ctl, "\n");
+ }
+ }
+ } else {
+ if (info.state == VIR_DOMAIN_SHUTOFF &&
+ (ncpus = virDomainGetVcpuPinInfo(dom, info.nrVirtCpu,
+ cpumaps, cpumaplen,
+ VIR_DOMAIN_AFFECT_CONFIG)) >= 0) {
+
+ /* fallback plan to use virDomainGetVcpuPinInfo */
+
+ for (n = 0; n < ncpus; n++) {
+ vshPrint(ctl, "%-15s %d\n", _("VCPU:"), n);
+ vshPrint(ctl, "%-15s %s\n", _("CPU:"), _("N/A"));
+ vshPrint(ctl, "%-15s %s\n", _("State:"), _("N/A"));
+ vshPrint(ctl, "%-15s %s\n", _("CPU time"), _("N/A"));
+ vshPrint(ctl, "%-15s ", _("CPU Affinity:"));
+ for (m = 0; m < maxcpu; m++) {
+ vshPrint(ctl, "%c",
+ VIR_CPU_USABLE(cpumaps, cpumaplen, n, m) ? 'y' : '-');
+ }
+ vshPrint(ctl, "\n");
+ if (n < (ncpus - 1)) {
+ vshPrint(ctl, "\n");
+ }
+ }
+ } else {
+ ret = false;
+ }
+ }
+
+ VIR_FREE(cpumaps);
+ VIR_FREE(cpuinfo);
+ virDomainFree(dom);
+ return ret;
+}
+
+/*
+ * "vcpupin" command
+ */
+static const vshCmdInfo info_vcpupin[] = {
+ {"help", N_("control or query domain vcpu affinity")},
+ {"desc", N_("Pin domain VCPUs to host physical CPUs.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_vcpupin[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"vcpu", VSH_OT_INT, 0, N_("vcpu number")},
+ {"cpulist", VSH_OT_DATA, VSH_OFLAG_EMPTY_OK,
+ N_("host cpu number(s) to set, or omit option to query")},
+ {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
+ {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
+ {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdVcpuPin(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainInfo info;
+ virDomainPtr dom;
+ virNodeInfo nodeinfo;
+ int vcpu = -1;
+ const char *cpulist = NULL;
+ bool ret = true;
+ unsigned char *cpumap = NULL;
+ unsigned char *cpumaps = NULL;
+ size_t cpumaplen;
+ bool bit, lastbit, isInvert;
+ int i, cpu, lastcpu, maxcpu, ncpus;
+ bool unuse = false;
+ const char *cur;
+ bool config = vshCommandOptBool(cmd, "config");
+ bool live = vshCommandOptBool(cmd, "live");
+ bool current = vshCommandOptBool(cmd, "current");
+ bool query = false; /* Query mode if no cpulist */
+ unsigned int flags = 0;
+
+ if (current) {
+ if (live || config) {
+ vshError(ctl, "%s", _("--current must be specified exclusively"));
+ return false;
+ }
+ flags = VIR_DOMAIN_AFFECT_CURRENT;
+ } else {
+ if (config)
+ flags |= VIR_DOMAIN_AFFECT_CONFIG;
+ if (live)
+ flags |= VIR_DOMAIN_AFFECT_LIVE;
+ /* neither option is specified */
+ if (!live && !config)
+ flags = -1;
+ }
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ return false;
+
+ if (vshCommandOptString(cmd, "cpulist", &cpulist) < 0) {
+ vshError(ctl, "%s", _("vcpupin: Missing cpulist."));
+ virDomainFree(dom);
+ return false;
+ }
+ query = !cpulist;
+
+ /* In query mode, "vcpu" is optional */
+ if (vshCommandOptInt(cmd, "vcpu", &vcpu) < !query) {
+ vshError(ctl, "%s",
+ _("vcpupin: Invalid or missing vCPU number."));
+ virDomainFree(dom);
+ return false;
+ }
+
+ if (virNodeGetInfo(ctl->conn, &nodeinfo) != 0) {
+ virDomainFree(dom);
+ return false;
+ }
+
+ if (virDomainGetInfo(dom, &info) != 0) {
+ vshError(ctl, "%s", _("vcpupin: failed to get domain information."));
+ virDomainFree(dom);
+ return false;
+ }
+
+ if (vcpu >= info.nrVirtCpu) {
+ vshError(ctl, "%s", _("vcpupin: Invalid vCPU number."));
+ virDomainFree(dom);
+ return false;
+ }
+
+ maxcpu = VIR_NODEINFO_MAXCPUS(nodeinfo);
+ cpumaplen = VIR_CPU_MAPLEN(maxcpu);
+
+ /* Query mode: show CPU affinity information then exit.*/
+ if (query) {
+ /* When query mode and neither "live", "config" nor "current"
+ * is specified, set VIR_DOMAIN_AFFECT_CURRENT as flags */
+ if (flags == -1)
+ flags = VIR_DOMAIN_AFFECT_CURRENT;
+
+ cpumaps = vshMalloc(ctl, info.nrVirtCpu * cpumaplen);
+ if ((ncpus = virDomainGetVcpuPinInfo(dom, info.nrVirtCpu,
+ cpumaps, cpumaplen, flags)) >= 0) {
+
+ vshPrint(ctl, "%s %s\n", _("VCPU:"), _("CPU Affinity"));
+ vshPrint(ctl, "----------------------------------\n");
+ for (i = 0; i < ncpus; i++) {
+
+ if (vcpu != -1 && i != vcpu)
+ continue;
+
+ bit = lastbit = isInvert = false;
+ lastcpu = -1;
+
+ vshPrint(ctl, "%4d: ", i);
+ for (cpu = 0; cpu < maxcpu; cpu++) {
+
+ bit = VIR_CPU_USABLE(cpumaps, cpumaplen, i, cpu);
+
+ isInvert = (bit ^ lastbit);
+ if (bit && isInvert) {
+ if (lastcpu == -1)
+ vshPrint(ctl, "%d", cpu);
+ else
+ vshPrint(ctl, ",%d", cpu);
+ lastcpu = cpu;
+ }
+ if (!bit && isInvert && lastcpu != cpu - 1)
+ vshPrint(ctl, "-%d", cpu - 1);
+ lastbit = bit;
+ }
+ if (bit && !isInvert) {
+ vshPrint(ctl, "-%d", maxcpu - 1);
+ }
+ vshPrint(ctl, "\n");
+ }
+
+ } else {
+ ret = false;
+ }
+ VIR_FREE(cpumaps);
+ goto cleanup;
+ }
+
+ /* Pin mode: pinning specified vcpu to specified physical cpus*/
+
+ cpumap = vshCalloc(ctl, cpumaplen, sizeof(cpumap));
+ /* Parse cpulist */
+ cur = cpulist;
+ if (*cur == 0) {
+ goto parse_error;
+ } else if (*cur == 'r') {
+ for (cpu = 0; cpu < maxcpu; cpu++)
+ VIR_USE_CPU(cpumap, cpu);
+ cur = "";
+ }
+
+ while (*cur != 0) {
+
+ /* the char '^' denotes exclusive */
+ if (*cur == '^') {
+ cur++;
+ unuse = true;
+ }
+
+ /* parse physical CPU number */
+ if (!c_isdigit(*cur))
+ goto parse_error;
+ cpu = virParseNumber(&cur);
+ if (cpu < 0) {
+ goto parse_error;
+ }
+ if (cpu >= maxcpu) {
+ vshError(ctl, _("Physical CPU %d doesn't exist."), cpu);
+ goto parse_error;
+ }
+ virSkipSpaces(&cur);
+
+ if (*cur == ',' || *cur == 0) {
+ if (unuse) {
+ VIR_UNUSE_CPU(cpumap, cpu);
+ } else {
+ VIR_USE_CPU(cpumap, cpu);
+ }
+ } else if (*cur == '-') {
+ /* the char '-' denotes range */
+ if (unuse) {
+ goto parse_error;
+ }
+ cur++;
+ virSkipSpaces(&cur);
+ /* parse the end of range */
+ lastcpu = virParseNumber(&cur);
+ if (lastcpu < cpu) {
+ goto parse_error;
+ }
+ if (lastcpu >= maxcpu) {
+ vshError(ctl, _("Physical CPU %d doesn't exist."), maxcpu);
+ goto parse_error;
+ }
+ for (i = cpu; i <= lastcpu; i++) {
+ VIR_USE_CPU(cpumap, i);
+ }
+ virSkipSpaces(&cur);
+ }
+
+ if (*cur == ',') {
+ cur++;
+ virSkipSpaces(&cur);
+ unuse = false;
+ } else if (*cur == 0) {
+ break;
+ } else {
+ goto parse_error;
+ }
+ }
+
+ if (flags == -1) {
+ if (virDomainPinVcpu(dom, vcpu, cpumap, cpumaplen) != 0) {
+ ret = false;
+ }
+ } else {
+ if (virDomainPinVcpuFlags(dom, vcpu, cpumap, cpumaplen, flags) != 0) {
+ ret = false;
+ }
+ }
+
+cleanup:
+ VIR_FREE(cpumap);
+ virDomainFree(dom);
+ return ret;
+
+parse_error:
+ vshError(ctl, "%s", _("cpulist: Invalid format."));
+ ret = false;
+ goto cleanup;
+}
+
+/*
+ * "setvcpus" command
+ */
+static const vshCmdInfo info_setvcpus[] = {
+ {"help", N_("change number of virtual CPUs")},
+ {"desc", N_("Change the number of virtual CPUs in the guest domain.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_setvcpus[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"count", VSH_OT_INT, VSH_OFLAG_REQ, N_("number of virtual CPUs")},
+ {"maximum", VSH_OT_BOOL, 0, N_("set maximum limit on next boot")},
+ {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
+ {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
+ {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdSetvcpus(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom;
+ int count = 0;
+ bool ret = true;
+ bool maximum = vshCommandOptBool(cmd, "maximum");
+ bool config = vshCommandOptBool(cmd, "config");
+ bool live = vshCommandOptBool(cmd, "live");
+ bool current = vshCommandOptBool(cmd, "current");
+ unsigned int flags = 0;
+
+ if (current) {
+ if (live || config) {
+ vshError(ctl, "%s", _("--current must be specified exclusively"));
+ return false;
+ }
+ flags = VIR_DOMAIN_AFFECT_CURRENT;
+ } else {
+ if (config)
+ flags |= VIR_DOMAIN_AFFECT_CONFIG;
+ if (live)
+ flags |= VIR_DOMAIN_AFFECT_LIVE;
+ /* neither option is specified */
+ if (!live && !config && !maximum)
+ flags = -1;
+ }
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ return false;
+
+ if (vshCommandOptInt(cmd, "count", &count) < 0 || count <= 0) {
+ vshError(ctl, "%s", _("Invalid number of virtual CPUs"));
+ goto cleanup;
+ }
+
+ if (flags == -1) {
+ if (virDomainSetVcpus(dom, count) != 0) {
+ ret = false;
+ }
+ } else {
+ /* If the --maximum flag was given, we need to ensure only the
+ --config flag is in effect as well */
+ if (maximum) {
+ vshDebug(ctl, VSH_ERR_DEBUG, "--maximum flag was given\n");
+
+ flags |= VIR_DOMAIN_VCPU_MAXIMUM;
+
+ /* If neither the --config nor --live flags were given, OR
+ if just the --live flag was given, we need to error out
+ warning the user that the --maximum flag can only be used
+ with the --config flag */
+ if (live || !config) {
+
+ /* Warn the user about the invalid flag combination */
+ vshError(ctl, _("--maximum must be used with --config only"));
+ ret = false;
+ goto cleanup;
+ }
+ }
+
+ /* Apply the virtual cpu changes */
+ if (virDomainSetVcpusFlags(dom, count, flags) < 0) {
+ ret = false;
+ }
+ }
+
+ cleanup:
+ virDomainFree(dom);
+ return ret;
+}
+
+/*
+ * "cpu-compare" command
+ */
+static const vshCmdInfo info_cpu_compare[] = {
+ {"help", N_("compare host CPU with a CPU described by an XML file")},
+ {"desc", N_("compare CPU with host CPU")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_cpu_compare[] = {
+ {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML CPU description")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdCPUCompare(vshControl *ctl, const vshCmd *cmd)
+{
+ const char *from = NULL;
+ bool ret = false;
+ char *buffer;
+ int result;
+ const char *snippet;
+
+ xmlDocPtr xml = NULL;
+ xmlXPathContextPtr ctxt = NULL;
+ xmlBufferPtr xml_buf = NULL;
+ xmlNodePtr node;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (vshCommandOptString(cmd, "file", &from) <= 0)
+ return false;
+
+ if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
+ vshError(ctl, _("Failed to read file '%s' to compare"),
+ from);
+ return false;
+ }
+
+ /* try to extract the CPU element from as it would appear in a domain XML*/
+ if (!(xml = virXMLParseStringCtxt(buffer, from, &ctxt)))
+ goto cleanup;
+
+ if ((node = virXPathNode("/cpu|"
+ "/domain/cpu|"
+ "/capabilities/host/cpu", ctxt))) {
+ if (!(xml_buf = xmlBufferCreate())) {
+ vshError(ctl, _("Can't create XML buffer to extract CPU element."));
+ goto cleanup;
+ }
+
+ if (xmlNodeDump(xml_buf, xml, node, 0, 0) < 0) {
+ vshError(ctl, _("Failed to extract CPU element snippet from domain XML."));
+ goto cleanup;
+ }
+
+ snippet = (const char *) xmlBufferContent(xml_buf);
+ } else {
+ vshError(ctl, _("File '%s' does not contain a element or is not "
+ "a valid domain or capabilities XML"), from);
+ goto cleanup;
+ }
+
+ result = virConnectCompareCPU(ctl->conn, snippet, 0);
+
+ switch (result) {
+ case VIR_CPU_COMPARE_INCOMPATIBLE:
+ vshPrint(ctl, _("CPU described in %s is incompatible with host CPU\n"),
+ from);
+ goto cleanup;
+ break;
+
+ case VIR_CPU_COMPARE_IDENTICAL:
+ vshPrint(ctl, _("CPU described in %s is identical to host CPU\n"),
+ from);
+ break;
+
+ case VIR_CPU_COMPARE_SUPERSET:
+ vshPrint(ctl, _("Host CPU is a superset of CPU described in %s\n"),
+ from);
+ break;
+
+ case VIR_CPU_COMPARE_ERROR:
+ default:
+ vshError(ctl, _("Failed to compare host CPU with %s"), from);
+ goto cleanup;
+ }
+
+ ret = true;
+
+cleanup:
+ VIR_FREE(buffer);
+ xmlBufferFree(xml_buf);
+ xmlXPathFreeContext(ctxt);
+ xmlFreeDoc(xml);
+
+ return ret;
+}
+
+/*
+ * "cpu-baseline" command
+ */
+static const vshCmdInfo info_cpu_baseline[] = {
+ {"help", N_("compute baseline CPU")},
+ {"desc", N_("Compute baseline CPU for a set of given CPUs.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_cpu_baseline[] = {
+ {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing XML CPU descriptions")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdCPUBaseline(vshControl *ctl, const vshCmd *cmd)
+{
+ const char *from = NULL;
+ bool ret = false;
+ char *buffer;
+ char *result = NULL;
+ const char **list = NULL;
+ int count = 0;
+
+ xmlDocPtr xml = NULL;
+ xmlNodePtr *node_list = NULL;
+ xmlXPathContextPtr ctxt = NULL;
+ xmlBufferPtr xml_buf = NULL;
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+ int i;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (vshCommandOptString(cmd, "file", &from) <= 0)
+ return false;
+
+ if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
+ return false;
+
+ /* add a separate container around the xml */
+ virBufferStrcat(&buf, "", buffer, "", NULL);
+ if (virBufferError(&buf))
+ goto no_memory;
+
+ VIR_FREE(buffer);
+ buffer = virBufferContentAndReset(&buf);
+
+
+ if (!(xml = virXMLParseStringCtxt(buffer, from, &ctxt)))
+ goto cleanup;
+
+ if ((count = virXPathNodeSet("//cpu[not(ancestor::cpus)]",
+ ctxt, &node_list)) == -1)
+ goto cleanup;
+
+ if (count == 0) {
+ vshError(ctl, _("No host CPU specified in '%s'"), from);
+ goto cleanup;
+ }
+
+ list = vshCalloc(ctl, count, sizeof(const char *));
+
+ if (!(xml_buf = xmlBufferCreate()))
+ goto no_memory;
+
+ for (i = 0; i < count; i++) {
+ xmlBufferEmpty(xml_buf);
+
+ if (xmlNodeDump(xml_buf, xml, node_list[i], 0, 0) < 0) {
+ vshError(ctl, _("Failed to extract element"));
+ goto cleanup;
+ }
+
+ list[i] = vshStrdup(ctl, (const char *)xmlBufferContent(xml_buf));
+ }
+
+ result = virConnectBaselineCPU(ctl->conn, list, count, 0);
+
+ if (result) {
+ vshPrint(ctl, "%s", result);
+ ret = true;
+ }
+
+cleanup:
+ xmlXPathFreeContext(ctxt);
+ xmlFreeDoc(xml);
+ xmlBufferFree(xml_buf);
+ VIR_FREE(result);
+ if (list != NULL && count > 0) {
+ for (i = 0; i < count; i++)
+ VIR_FREE(list[i]);
+ }
+ VIR_FREE(list);
+ VIR_FREE(buffer);
+
+ return ret;
+
+no_memory:
+ vshError(ctl, "%s", _("Out of memory"));
+ ret = false;
+ goto cleanup;
+}
+
+/*
+ * "cpu-stats" command
+ */
+static const vshCmdInfo info_cpu_stats[] = {
+ {"help", N_("show domain cpu statistics")},
+ {"desc",
+ N_("Display per-CPU and total statistics about the domain's CPUs")},
+ {NULL, NULL},
+};
+
+static const vshCmdOptDef opts_cpu_stats[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"total", VSH_OT_BOOL, 0, N_("Show total statistics only")},
+ {"start", VSH_OT_INT, 0, N_("Show statistics from this CPU")},
+ {"count", VSH_OT_INT, 0, N_("Number of shown CPUs at most")},
+ {NULL, 0, 0, NULL},
+};
+
+static bool
+cmdCPUStats(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom;
+ virTypedParameterPtr params = NULL;
+ int i, j, pos, max_id, cpu = -1, show_count = -1, nparams;
+ bool show_total = false, show_per_cpu = false;
+ unsigned int flags = 0;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ return false;
+
+ show_total = vshCommandOptBool(cmd, "total");
+ if (vshCommandOptInt(cmd, "start", &cpu) > 0)
+ show_per_cpu = true;
+ if (vshCommandOptInt(cmd, "count", &show_count) > 0)
+ show_per_cpu = true;
+
+ /* default show per_cpu and total */
+ if (!show_total && !show_per_cpu) {
+ show_total = true;
+ show_per_cpu = true;
+ }
+
+ if (!show_per_cpu) /* show total stats only */
+ goto do_show_total;
+
+ /* check cpu, show_count, and ignore wrong argument */
+ if (cpu < 0)
+ cpu = 0;
+
+ /* get number of cpus on the node */
+ if ((max_id = virDomainGetCPUStats(dom, NULL, 0, 0, 0, flags)) < 0)
+ goto failed_stats;
+ if (show_count < 0 || show_count > max_id)
+ show_count = max_id;
+
+ /* get percpu information */
+ if ((nparams = virDomainGetCPUStats(dom, NULL, 0, 0, 1, flags)) < 0)
+ goto failed_stats;
+
+ if (!nparams) {
+ vshPrint(ctl, "%s", _("No per-CPU stats available"));
+ goto do_show_total;
+ }
+
+ if (VIR_ALLOC_N(params, nparams * MIN(show_count, 128)) < 0)
+ goto failed_params;
+
+ while (show_count) {
+ int ncpus = MIN(show_count, 128);
+
+ if (virDomainGetCPUStats(dom, params, nparams, cpu, ncpus, flags) < 0)
+ goto failed_stats;
+
+ for (i = 0; i < ncpus; i++) {
+ if (params[i * nparams].type == 0) /* this cpu is not in the map */
+ continue;
+ vshPrint(ctl, "CPU%d:\n", cpu + i);
+
+ for (j = 0; j < nparams; j++) {
+ pos = i * nparams + j;
+ vshPrint(ctl, "\t%-12s ", params[pos].field);
+ if ((STREQ(params[pos].field, VIR_DOMAIN_CPU_STATS_CPUTIME) ||
+ STREQ(params[pos].field, VIR_DOMAIN_CPU_STATS_VCPUTIME)) &&
+ params[j].type == VIR_TYPED_PARAM_ULLONG) {
+ vshPrint(ctl, "%9lld.%09lld seconds\n",
+ params[pos].value.ul / 1000000000,
+ params[pos].value.ul % 1000000000);
+ } else {
+ const char *s = vshGetTypedParamValue(ctl, ¶ms[pos]);
+ vshPrint(ctl, _("%s\n"), s);
+ VIR_FREE(s);
+ }
+ }
+ }
+ cpu += ncpus;
+ show_count -= ncpus;
+ virTypedParameterArrayClear(params, nparams * ncpus);
+ }
+ VIR_FREE(params);
+
+do_show_total:
+ if (!show_total)
+ goto cleanup;
+
+ /* get supported num of parameter for total statistics */
+ if ((nparams = virDomainGetCPUStats(dom, NULL, 0, -1, 1, flags)) < 0)
+ goto failed_stats;
+
+ if (!nparams) {
+ vshPrint(ctl, "%s", _("No total stats available"));
+ goto cleanup;
+ }
+
+ if (VIR_ALLOC_N(params, nparams))
+ goto failed_params;
+
+ /* passing start_cpu == -1 gives us domain's total status */
+ if ((nparams = virDomainGetCPUStats(dom, params, nparams, -1, 1, flags)) < 0)
+ goto failed_stats;
+
+ vshPrint(ctl, _("Total:\n"));
+ for (i = 0; i < nparams; i++) {
+ vshPrint(ctl, "\t%-12s ", params[i].field);
+ if ((STREQ(params[i].field, VIR_DOMAIN_CPU_STATS_CPUTIME) ||
+ STREQ(params[i].field, VIR_DOMAIN_CPU_STATS_USERTIME) ||
+ STREQ(params[i].field, VIR_DOMAIN_CPU_STATS_SYSTEMTIME)) &&
+ params[i].type == VIR_TYPED_PARAM_ULLONG) {
+ vshPrint(ctl, "%9lld.%09lld seconds\n",
+ params[i].value.ul / 1000000000,
+ params[i].value.ul % 1000000000);
+ } else {
+ char *s = vshGetTypedParamValue(ctl, ¶ms[i]);
+ vshPrint(ctl, "%s\n", s);
+ VIR_FREE(s);
+ }
+ }
+ virTypedParameterArrayClear(params, nparams);
+ VIR_FREE(params);
+
+cleanup:
+ virDomainFree(dom);
+ return true;
+
+failed_params:
+ virReportOOMError();
+ virDomainFree(dom);
+ return false;
+
+failed_stats:
+ vshError(ctl, _("Failed to virDomainGetCPUStats()\n"));
+ VIR_FREE(params);
+ virDomainFree(dom);
+ return false;
+}
+
+/*
+ * "create" command
+ */
+static const vshCmdInfo info_create[] = {
+ {"help", N_("create a domain from an XML file")},
+ {"desc", N_("Create a domain.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_create[] = {
+ {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML domain description")},
+#ifndef WIN32
+ {"console", VSH_OT_BOOL, 0, N_("attach to console after creation")},
+#endif
+ {"paused", VSH_OT_BOOL, 0, N_("leave the guest paused after creation")},
+ {"autodestroy", VSH_OT_BOOL, 0, N_("automatically destroy the guest when virsh disconnects")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdCreate(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom;
+ const char *from = NULL;
+ bool ret = true;
+ char *buffer;
+#ifndef WIN32
+ bool console = vshCommandOptBool(cmd, "console");
+#endif
+ unsigned int flags = VIR_DOMAIN_NONE;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (vshCommandOptString(cmd, "file", &from) <= 0)
+ return false;
+
+ if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
+ return false;
+
+ if (vshCommandOptBool(cmd, "paused"))
+ flags |= VIR_DOMAIN_START_PAUSED;
+ if (vshCommandOptBool(cmd, "autodestroy"))
+ flags |= VIR_DOMAIN_START_AUTODESTROY;
+
+ dom = virDomainCreateXML(ctl->conn, buffer, flags);
+ VIR_FREE(buffer);
+
+ if (dom != NULL) {
+ vshPrint(ctl, _("Domain %s created from %s\n"),
+ virDomainGetName(dom), from);
+#ifndef WIN32
+ if (console)
+ cmdRunConsole(ctl, dom, NULL, 0);
+#endif
+ virDomainFree(dom);
+ } else {
+ vshError(ctl, _("Failed to create domain from %s"), from);
+ ret = false;
+ }
+ return ret;
+}
+
+/*
+ * "define" command
+ */
+static const vshCmdInfo info_define[] = {
+ {"help", N_("define (but don't start) a domain from an XML file")},
+ {"desc", N_("Define a domain.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_define[] = {
+ {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML domain description")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDefine(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom;
+ const char *from = NULL;
+ bool ret = true;
+ char *buffer;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (vshCommandOptString(cmd, "file", &from) <= 0)
+ return false;
+
+ if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
+ return false;
+
+ dom = virDomainDefineXML(ctl->conn, buffer);
+ VIR_FREE(buffer);
+
+ if (dom != NULL) {
+ vshPrint(ctl, _("Domain %s defined from %s\n"),
+ virDomainGetName(dom), from);
+ virDomainFree(dom);
+ } else {
+ vshError(ctl, _("Failed to define domain from %s"), from);
+ ret = false;
+ }
+ return ret;
+}
+
+/*
+ * "destroy" command
+ */
+static const vshCmdInfo info_destroy[] = {
+ {"help", N_("destroy (stop) a domain")},
+ {"desc",
+ N_("Forcefully stop a given domain, but leave its resources intact.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_destroy[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"graceful", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("terminate gracefully")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDestroy(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom;
+ bool ret = true;
+ const char *name;
+ unsigned int flags = 0;
+ int result;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+ return false;
+
+ if (vshCommandOptBool(cmd, "graceful"))
+ flags |= VIR_DOMAIN_DESTROY_GRACEFUL;
+
+ if (flags)
+ result = virDomainDestroyFlags(dom, VIR_DOMAIN_DESTROY_GRACEFUL);
+ else
+ result = virDomainDestroy(dom);
+
+ if (result == 0) {
+ vshPrint(ctl, _("Domain %s destroyed\n"), name);
+ } else {
+ vshError(ctl, _("Failed to destroy domain %s"), name);
+ ret = false;
+ }
+
+ virDomainFree(dom);
+ return ret;
+}
+
+/*
+ * "desc" command for managing domain description and title
+ */
+static const vshCmdInfo info_desc[] = {
+ {"help", N_("show or set domain's description or title")},
+ {"desc", N_("Allows to show or modify description or title of a domain.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_desc[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"live", VSH_OT_BOOL, 0, N_("modify/get running state")},
+ {"config", VSH_OT_BOOL, 0, N_("modify/get persistent configuration")},
+ {"current", VSH_OT_BOOL, 0, N_("modify/get current state configuration")},
+ {"title", VSH_OT_BOOL, 0, N_("modify/get the title instead of description")},
+ {"edit", VSH_OT_BOOL, 0, N_("open an editor to modify the description")},
+ {"new-desc", VSH_OT_ARGV, 0, N_("message")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDesc(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
+{
+ virDomainPtr dom;
+ bool config = vshCommandOptBool(cmd, "config");
+ bool live = vshCommandOptBool(cmd, "live");
+ bool current = vshCommandOptBool(cmd, "current");
+
+ bool title = vshCommandOptBool(cmd, "title");
+ bool edit = vshCommandOptBool(cmd, "edit");
+
+ int state;
+ int type;
+ char *desc = NULL;
+ char *desc_edited = NULL;
+ char *tmp = NULL;
+ char *tmpstr;
+ const vshCmdOpt *opt = NULL;
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+ bool pad = false;
+ bool ret = false;
+ unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
+
+ if (current) {
+ if (live || config) {
+ vshError(ctl, "%s", _("--current must be specified exclusively"));
+ return false;
+ }
+ flags = VIR_DOMAIN_AFFECT_CURRENT;
+ } else {
+ if (config)
+ flags |= VIR_DOMAIN_AFFECT_CONFIG;
+ if (live)
+ flags |= VIR_DOMAIN_AFFECT_LIVE;
+ }
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ return false;
+
+ if ((state = vshDomainState(ctl, dom, NULL)) < 0)
+ goto cleanup;
+
+ while ((opt = vshCommandOptArgv(cmd, opt))) {
+ if (pad)
+ virBufferAddChar(&buf, ' ');
+ pad = true;
+ virBufferAdd(&buf, opt->data, -1);
+ }
+
+ if (title)
+ type = VIR_DOMAIN_METADATA_TITLE;
+ else
+ type = VIR_DOMAIN_METADATA_DESCRIPTION;
+
+ if (virBufferError(&buf)) {
+ vshPrint(ctl, "%s", _("Failed to collect new description/title"));
+ goto cleanup;
+ }
+ desc = virBufferContentAndReset(&buf);
+
+ if (edit || desc) {
+ if (!desc) {
+ desc = vshGetDomainDescription(ctl, dom, title,
+ config?VIR_DOMAIN_XML_INACTIVE:0);
+ if (!desc)
+ goto cleanup;
+ }
+
+ if (edit) {
+ /* Create and open the temporary file. */
+ if (!(tmp = editWriteToTempFile(ctl, desc)))
+ goto cleanup;
+
+ /* Start the editor. */
+ if (editFile(ctl, tmp) == -1)
+ goto cleanup;
+
+ /* Read back the edited file. */
+ if (!(desc_edited = editReadBackFile(ctl, tmp)))
+ goto cleanup;
+
+ /* strip a possible newline at the end of file; some
+ * editors enforce a newline, this makes editing the title
+ * more convenient */
+ if (title &&
+ (tmpstr = strrchr(desc_edited, '\n')) &&
+ *(tmpstr+1) == '\0')
+ *tmpstr = '\0';
+
+ /* Compare original XML with edited. Has it changed at all? */
+ if (STREQ(desc, desc_edited)) {
+ vshPrint(ctl, _("Domain description not changed.\n"));
+ ret = true;
+ goto cleanup;
+ }
+
+ VIR_FREE(desc);
+ desc = desc_edited;
+ desc_edited = NULL;
+ }
+
+ if (virDomainSetMetadata(dom, type, desc, NULL, NULL, flags) < 0) {
+ vshError(ctl, "%s",
+ _("Failed to set new domain description"));
+ goto cleanup;
+ }
+ vshPrint(ctl, "%s", _("Domain description updated successfully"));
+ } else {
+ desc = vshGetDomainDescription(ctl, dom, title,
+ config?VIR_DOMAIN_XML_INACTIVE:0);
+ if (!desc)
+ goto cleanup;
+
+ if (strlen(desc) > 0)
+ vshPrint(ctl, "%s", desc);
+ else
+ vshPrint(ctl, _("No description for domain: %s"),
+ virDomainGetName(dom));
+ }
+
+ ret = true;
+cleanup:
+ VIR_FREE(desc_edited);
+ VIR_FREE(desc);
+ if (tmp) {
+ unlink(tmp);
+ VIR_FREE(tmp);
+ }
+ if (dom)
+ virDomainFree(dom);
+ return ret;
+}
+
+/*
+ * "inject-nmi" command
+ */
+static const vshCmdInfo info_inject_nmi[] = {
+ {"help", N_("Inject NMI to the guest")},
+ {"desc", N_("Inject NMI to the guest domain.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_inject_nmi[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdInjectNMI(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom;
+ int ret = true;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ return false;
+
+ if (virDomainInjectNMI(dom, 0) < 0)
+ ret = false;
+
+ virDomainFree(dom);
+ return ret;
+}
+
+/*
+ * "send-key" command
+ */
+static const vshCmdInfo info_send_key[] = {
+ {"help", N_("Send keycodes to the guest")},
+ {"desc", N_("Send keycodes (integers or symbolic names) to the guest")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_send_key[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"codeset", VSH_OT_STRING, VSH_OFLAG_REQ_OPT,
+ N_("the codeset of keycodes, default:linux")},
+ {"holdtime", VSH_OT_INT, VSH_OFLAG_REQ_OPT,
+ N_("the time (in milliseconds) how long the keys will be held")},
+ {"keycode", VSH_OT_ARGV, VSH_OFLAG_REQ, N_("the key code")},
+ {NULL, 0, 0, NULL}
+};
+
+static int
+get_integer_keycode(const char *key_name)
+{
+ unsigned int val;
+
+ if (virStrToLong_ui(key_name, NULL, 0, &val) < 0 || val > 0xffff || !val)
+ return -1;
+ return val;
+}
+
+static bool
+cmdSendKey(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom;
+ int ret = false;
+ const char *codeset_option;
+ int codeset;
+ int holdtime;
+ int count = 0;
+ const vshCmdOpt *opt = NULL;
+ int keycode;
+ unsigned int keycodes[VIR_DOMAIN_SEND_KEY_MAX_KEYS];
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ return false;
+
+ if (vshCommandOptString(cmd, "codeset", &codeset_option) <= 0)
+ codeset_option = "linux";
+
+ if (vshCommandOptInt(cmd, "holdtime", &holdtime) <= 0)
+ holdtime = 0;
+
+ codeset = virKeycodeSetTypeFromString(codeset_option);
+ if ((int)codeset < 0) {
+ vshError(ctl, _("unknown codeset: '%s'"), codeset_option);
+ goto cleanup;
+ }
+
+ while ((opt = vshCommandOptArgv(cmd, opt))) {
+ if (count == VIR_DOMAIN_SEND_KEY_MAX_KEYS) {
+ vshError(ctl, _("too many keycodes"));
+ goto cleanup;
+ }
+
+ if ((keycode = get_integer_keycode(opt->data)) <= 0) {
+ if ((keycode = virKeycodeValueFromString(codeset, opt->data)) <= 0) {
+ vshError(ctl, _("invalid keycode: '%s'"), opt->data);
+ goto cleanup;
+ }
+ }
+
+ keycodes[count] = keycode;
+ count++;
+ }
+
+ if (!(virDomainSendKey(dom, codeset, holdtime, keycodes, count, 0) < 0))
+ ret = true;
+
+cleanup:
+ virDomainFree(dom);
+ return ret;
+}
+
+/*
+ * "setmem" command
+ */
+static const vshCmdInfo info_setmem[] = {
+ {"help", N_("change memory allocation")},
+ {"desc", N_("Change the current memory allocation in the guest domain.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_setmem[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"kilobytes", VSH_OT_ALIAS, 0, "size"},
+ {"size", VSH_OT_INT, VSH_OFLAG_REQ,
+ N_("new memory size, as scaled integer (default KiB)")},
+ {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
+ {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
+ {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdSetmem(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom;
+ unsigned long long bytes = 0;
+ unsigned long long max;
+ unsigned long kibibytes = 0;
+ bool ret = true;
+ bool config = vshCommandOptBool(cmd, "config");
+ bool live = vshCommandOptBool(cmd, "live");
+ bool current = vshCommandOptBool(cmd, "current");
+ unsigned int flags = 0;
+
+ if (current) {
+ if (live || config) {
+ vshError(ctl, "%s", _("--current must be specified exclusively"));
+ return false;
+ }
+ flags = VIR_DOMAIN_AFFECT_CURRENT;
+ } else {
+ if (config)
+ flags |= VIR_DOMAIN_AFFECT_CONFIG;
+ if (live)
+ flags |= VIR_DOMAIN_AFFECT_LIVE;
+ /* neither option is specified */
+ if (!live && !config)
+ flags = -1;
+ }
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ return false;
+
+ /* The API expects 'unsigned long' KiB, so depending on whether we
+ * are 32-bit or 64-bit determines the maximum we can use. */
+ if (sizeof(kibibytes) < sizeof(max))
+ max = 1024ull * ULONG_MAX;
+ else
+ max = ULONG_MAX;
+ if (vshCommandOptScaledInt(cmd, "size", &bytes, 1024, max) < 0) {
+ vshError(ctl, "%s", _("memory size has to be a number"));
+ virDomainFree(dom);
+ return false;
+ }
+ kibibytes = VIR_DIV_UP(bytes, 1024);
+
+ if (flags == -1) {
+ if (virDomainSetMemory(dom, kibibytes) != 0) {
+ ret = false;
+ }
+ } else {
+ if (virDomainSetMemoryFlags(dom, kibibytes, flags) < 0) {
+ ret = false;
+ }
+ }
+
+ virDomainFree(dom);
+ return ret;
+}
+
+/*
+ * "setmaxmem" command
+ */
+static const vshCmdInfo info_setmaxmem[] = {
+ {"help", N_("change maximum memory limit")},
+ {"desc", N_("Change the maximum memory allocation limit in the guest domain.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_setmaxmem[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"kilobytes", VSH_OT_ALIAS, 0, "size"},
+ {"size", VSH_OT_INT, VSH_OFLAG_REQ,
+ N_("new maximum memory size, as scaled integer (default KiB)")},
+ {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
+ {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
+ {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdSetmaxmem(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom;
+ unsigned long long bytes = 0;
+ unsigned long long max;
+ unsigned long kibibytes = 0;
+ bool ret = true;
+ bool config = vshCommandOptBool(cmd, "config");
+ bool live = vshCommandOptBool(cmd, "live");
+ bool current = vshCommandOptBool(cmd, "current");
+ unsigned int flags = VIR_DOMAIN_MEM_MAXIMUM;
+
+ if (current) {
+ if (live || config) {
+ vshError(ctl, "%s", _("--current must be specified exclusively"));
+ return false;
+ }
+ } else {
+ if (config)
+ flags |= VIR_DOMAIN_AFFECT_CONFIG;
+ if (live)
+ flags |= VIR_DOMAIN_AFFECT_LIVE;
+ /* neither option is specified */
+ if (!live && !config)
+ flags = -1;
+ }
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ return false;
+
+ /* The API expects 'unsigned long' KiB, so depending on whether we
+ * are 32-bit or 64-bit determines the maximum we can use. */
+ if (sizeof(kibibytes) < sizeof(max))
+ max = 1024ull * ULONG_MAX;
+ else
+ max = ULONG_MAX;
+ if (vshCommandOptScaledInt(cmd, "size", &bytes, 1024, max) < 0) {
+ vshError(ctl, "%s", _("memory size has to be a number"));
+ virDomainFree(dom);
+ return false;
+ }
+ kibibytes = VIR_DIV_UP(bytes, 1024);
+
+ if (flags == -1) {
+ if (virDomainSetMaxMemory(dom, kibibytes) != 0) {
+ vshError(ctl, "%s", _("Unable to change MaxMemorySize"));
+ ret = false;
+ }
+ } else {
+ if (virDomainSetMemoryFlags(dom, kibibytes, flags) < 0) {
+ vshError(ctl, "%s", _("Unable to change MaxMemorySize"));
+ ret = false;
+ }
+ }
+
+ virDomainFree(dom);
+ return ret;
+}
+
+/*
+ * "memtune" command
+ */
+static const vshCmdInfo info_memtune[] = {
+ {"help", N_("Get or set memory parameters")},
+ {"desc", N_("Get or set the current memory parameters for a guest"
+ " domain.\n"
+ " To get the memory parameters use following command: \n\n"
+ " virsh # memtune ")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_memtune[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"hard-limit", VSH_OT_INT, VSH_OFLAG_NONE,
+ N_("Max memory, as scaled integer (default KiB)")},
+ {"soft-limit", VSH_OT_INT, VSH_OFLAG_NONE,
+ N_("Memory during contention, as scaled integer (default KiB)")},
+ {"swap-hard-limit", VSH_OT_INT, VSH_OFLAG_NONE,
+ N_("Max memory plus swap, as scaled integer (default KiB)")},
+ {"min-guarantee", VSH_OT_INT, VSH_OFLAG_NONE,
+ N_("Min guaranteed memory, as scaled integer (default KiB)")},
+ {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
+ {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
+ {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
+ {NULL, 0, 0, NULL}
+};
+
+static int
+vshMemtuneGetSize(const vshCmd *cmd, const char *name, long long *value)
+{
+ int ret;
+ unsigned long long tmp;
+ const char *str;
+ char *end;
+
+ ret = vshCommandOptString(cmd, name, &str);
+ if (ret <= 0)
+ return ret;
+ if (virStrToLong_ll(str, &end, 10, value) < 0)
+ return -1;
+ if (*value < 0) {
+ *value = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
+ return 1;
+ }
+ tmp = *value;
+ if (virScaleInteger(&tmp, end, 1024, LLONG_MAX) < 0)
+ return -1;
+ *value = VIR_DIV_UP(tmp, 1024);
+ return 0;
+}
+
+static bool
+cmdMemtune(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom;
+ long long hard_limit = 0, soft_limit = 0, swap_hard_limit = 0;
+ long long min_guarantee = 0;
+ int nparams = 0;
+ unsigned int i = 0;
+ virTypedParameterPtr params = NULL, temp = NULL;
+ bool ret = false;
+ unsigned int flags = 0;
+ bool current = vshCommandOptBool(cmd, "current");
+ bool config = vshCommandOptBool(cmd, "config");
+ bool live = vshCommandOptBool(cmd, "live");
+
+ if (current) {
+ if (live || config) {
+ vshError(ctl, "%s", _("--current must be specified exclusively"));
+ return false;
+ }
+ flags = VIR_DOMAIN_AFFECT_CURRENT;
+ } else {
+ if (config)
+ flags |= VIR_DOMAIN_AFFECT_CONFIG;
+ if (live)
+ flags |= VIR_DOMAIN_AFFECT_LIVE;
+ }
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ return false;
+
+ if (vshMemtuneGetSize(cmd, "hard-limit", &hard_limit) < 0 ||
+ vshMemtuneGetSize(cmd, "soft-limit", &soft_limit) < 0 ||
+ vshMemtuneGetSize(cmd, "swap-hard-limit", &swap_hard_limit) < 0 ||
+ vshMemtuneGetSize(cmd, "min-guarantee", &min_guarantee) < 0) {
+ vshError(ctl, "%s",
+ _("Unable to parse integer parameter"));
+ goto cleanup;
+ }
+
+ if (hard_limit)
+ nparams++;
+
+ if (soft_limit)
+ nparams++;
+
+ if (swap_hard_limit)
+ nparams++;
+
+ if (min_guarantee)
+ nparams++;
+
+ if (nparams == 0) {
+ /* get the number of memory parameters */
+ if (virDomainGetMemoryParameters(dom, NULL, &nparams, flags) != 0) {
+ vshError(ctl, "%s",
+ _("Unable to get number of memory parameters"));
+ goto cleanup;
+ }
+
+ if (nparams == 0) {
+ /* nothing to output */
+ ret = true;
+ goto cleanup;
+ }
+
+ /* now go get all the memory parameters */
+ params = vshCalloc(ctl, nparams, sizeof(*params));
+ if (virDomainGetMemoryParameters(dom, params, &nparams, flags) != 0) {
+ vshError(ctl, "%s", _("Unable to get memory parameters"));
+ goto cleanup;
+ }
+
+ for (i = 0; i < nparams; i++) {
+ if (params[i].type == VIR_TYPED_PARAM_ULLONG &&
+ params[i].value.ul == VIR_DOMAIN_MEMORY_PARAM_UNLIMITED) {
+ vshPrint(ctl, "%-15s: %s\n", params[i].field, _("unlimited"));
+ } else {
+ char *str = vshGetTypedParamValue(ctl, ¶ms[i]);
+ vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
+ VIR_FREE(str);
+ }
+ }
+
+ ret = true;
+ } else {
+ /* set the memory parameters */
+ params = vshCalloc(ctl, nparams, sizeof(*params));
+
+ for (i = 0; i < nparams; i++) {
+ temp = ¶ms[i];
+
+ /*
+ * Some magic here, this is used to fill the params structure with
+ * the valid arguments passed, after filling the particular
+ * argument we purposely make them 0, so on the next pass it goes
+ * to the next valid argument and so on.
+ */
+ if (soft_limit) {
+ if (virTypedParameterAssign(temp,
+ VIR_DOMAIN_MEMORY_SOFT_LIMIT,
+ VIR_TYPED_PARAM_ULLONG,
+ soft_limit) < 0)
+ goto error;
+ soft_limit = 0;
+ } else if (hard_limit) {
+ if (virTypedParameterAssign(temp,
+ VIR_DOMAIN_MEMORY_HARD_LIMIT,
+ VIR_TYPED_PARAM_ULLONG,
+ hard_limit) < 0)
+ goto error;
+ hard_limit = 0;
+ } else if (swap_hard_limit) {
+ if (virTypedParameterAssign(temp,
+ VIR_DOMAIN_MEMORY_SWAP_HARD_LIMIT,
+ VIR_TYPED_PARAM_ULLONG,
+ swap_hard_limit) < 0)
+ goto error;
+ swap_hard_limit = 0;
+ } else if (min_guarantee) {
+ if (virTypedParameterAssign(temp,
+ VIR_DOMAIN_MEMORY_MIN_GUARANTEE,
+ VIR_TYPED_PARAM_ULLONG,
+ min_guarantee) < 0)
+ goto error;
+ min_guarantee = 0;
+ }
+
+ /* If the user has passed -1, we interpret it as unlimited */
+ if (temp->value.ul == -1)
+ temp->value.ul = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
+ }
+ if (virDomainSetMemoryParameters(dom, params, nparams, flags) != 0)
+ goto error;
+ else
+ ret = true;
+ }
+
+cleanup:
+ VIR_FREE(params);
+ virDomainFree(dom);
+ return ret;
+
+error:
+ vshError(ctl, "%s", _("Unable to change memory parameters"));
+ goto cleanup;
+}
+
+/*
+ * "numatune" command
+ */
+static const vshCmdInfo info_numatune[] = {
+ {"help", N_("Get or set numa parameters")},
+ {"desc", N_("Get or set the current numa parameters for a guest"
+ " domain.\n"
+ " To get the numa parameters use following command: \n\n"
+ " virsh # numatune ")},
+ {NULL, NULL}
+
+};
+
+static const vshCmdOptDef opts_numatune[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"mode", VSH_OT_DATA, VSH_OFLAG_NONE,
+ N_("NUMA mode, one of strict, preferred and interleave")},
+ {"nodeset", VSH_OT_DATA, VSH_OFLAG_NONE,
+ N_("NUMA node selections to set")},
+ {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
+ {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
+ {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdNumatune(vshControl * ctl, const vshCmd * cmd)
+{
+ virDomainPtr dom;
+ int nparams = 0;
+ unsigned int i = 0;
+ virTypedParameterPtr params = NULL, temp = NULL;
+ const char *nodeset = NULL;
+ bool ret = false;
+ unsigned int flags = 0;
+ bool current = vshCommandOptBool(cmd, "current");
+ bool config = vshCommandOptBool(cmd, "config");
+ bool live = vshCommandOptBool(cmd, "live");
+ const char *mode = NULL;
+
+ if (current) {
+ if (live || config) {
+ vshError(ctl, "%s", _("--current must be specified exclusively"));
+ return false;
+ }
+ flags = VIR_DOMAIN_AFFECT_CURRENT;
+ } else {
+ if (config)
+ flags |= VIR_DOMAIN_AFFECT_CONFIG;
+ if (live)
+ flags |= VIR_DOMAIN_AFFECT_LIVE;
+ }
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ return false;
+
+ if (vshCommandOptString(cmd, "nodeset", &nodeset) < 0) {
+ vshError(ctl, "%s", _("Unable to parse nodeset."));
+ virDomainFree(dom);
+ return false;
+ }
+ if (nodeset)
+ nparams++;
+ if (vshCommandOptString(cmd, "mode", &mode) < 0) {
+ vshError(ctl, "%s", _("Unable to parse mode."));
+ virDomainFree(dom);
+ return false;
+ }
+ if (mode)
+ nparams++;
+
+ if (nparams == 0) {
+ /* get the number of numa parameters */
+ if (virDomainGetNumaParameters(dom, NULL, &nparams, flags) != 0) {
+ vshError(ctl, "%s",
+ _("Unable to get number of memory parameters"));
+ goto cleanup;
+ }
+
+ if (nparams == 0) {
+ /* nothing to output */
+ ret = true;
+ goto cleanup;
+ }
+
+ /* now go get all the numa parameters */
+ params = vshCalloc(ctl, nparams, sizeof(*params));
+ if (virDomainGetNumaParameters(dom, params, &nparams, flags) != 0) {
+ vshError(ctl, "%s", _("Unable to get numa parameters"));
+ goto cleanup;
+ }
+
+ for (i = 0; i < nparams; i++) {
+ if (params[i].type == VIR_TYPED_PARAM_INT &&
+ STREQ(params[i].field, VIR_DOMAIN_NUMA_MODE)) {
+ vshPrint(ctl, "%-15s: %s\n", params[i].field,
+ virDomainNumatuneMemModeTypeToString(params[i].value.i));
+ } else {
+ char *str = vshGetTypedParamValue(ctl, ¶ms[i]);
+ vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
+ VIR_FREE(str);
+ }
+ }
+
+ ret = true;
+ } else {
+ /* set the numa parameters */
+ params = vshCalloc(ctl, nparams, sizeof(*params));
+
+ for (i = 0; i < nparams; i++) {
+ temp = ¶ms[i];
+
+ /*
+ * Some magic here, this is used to fill the params structure with
+ * the valid arguments passed, after filling the particular
+ * argument we purposely make them 0, so on the next pass it goes
+ * to the next valid argument and so on.
+ */
+ if (mode) {
+ /* Accept string or integer, in case server
+ * understands newer integer than what strings we were
+ * compiled with */
+ if ((temp->value.i =
+ virDomainNumatuneMemModeTypeFromString(mode)) < 0 &&
+ virStrToLong_i(mode, NULL, 0, &temp->value.i) < 0) {
+ vshError(ctl, _("Invalid mode: %s"), mode);
+ goto cleanup;
+ }
+ if (!virStrcpy(temp->field, VIR_DOMAIN_NUMA_MODE,
+ sizeof(temp->field)))
+ goto cleanup;
+ temp->type = VIR_TYPED_PARAM_INT;
+ mode = NULL;
+ } else if (nodeset) {
+ temp->value.s = vshStrdup(ctl, nodeset);
+ temp->type = VIR_TYPED_PARAM_STRING;
+ if (!virStrcpy(temp->field, VIR_DOMAIN_NUMA_NODESET,
+ sizeof(temp->field)))
+ goto cleanup;
+ nodeset = NULL;
+ }
+ }
+ if (virDomainSetNumaParameters(dom, params, nparams, flags) != 0)
+ vshError(ctl, "%s", _("Unable to change numa parameters"));
+ else
+ ret = true;
+ }
+
+ cleanup:
+ virTypedParameterArrayClear(params, nparams);
+ VIR_FREE(params);
+ virDomainFree(dom);
+ return ret;
+}
+
+/*
+ * "dumpxml" command
+ */
+static const vshCmdInfo info_dumpxml[] = {
+ {"help", N_("domain information in XML")},
+ {"desc", N_("Output the domain information as an XML dump to stdout.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_dumpxml[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"inactive", VSH_OT_BOOL, 0, N_("show inactive defined XML")},
+ {"security-info", VSH_OT_BOOL, 0, N_("include security sensitive information in XML dump")},
+ {"update-cpu", VSH_OT_BOOL, 0, N_("update guest CPU according to host CPU")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDumpXML(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom;
+ bool ret = true;
+ char *dump;
+ unsigned int flags = 0;
+ bool inactive = vshCommandOptBool(cmd, "inactive");
+ bool secure = vshCommandOptBool(cmd, "security-info");
+ bool update = vshCommandOptBool(cmd, "update-cpu");
+
+ if (inactive)
+ flags |= VIR_DOMAIN_XML_INACTIVE;
+ if (secure)
+ flags |= VIR_DOMAIN_XML_SECURE;
+ if (update)
+ flags |= VIR_DOMAIN_XML_UPDATE_CPU;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ return false;
+
+ dump = virDomainGetXMLDesc(dom, flags);
+ if (dump != NULL) {
+ vshPrint(ctl, "%s", dump);
+ VIR_FREE(dump);
+ } else {
+ ret = false;
+ }
+
+ virDomainFree(dom);
+ return ret;
+}
+
+/*
+ * "domxml-from-native" command
+ */
+static const vshCmdInfo info_domxmlfromnative[] = {
+ {"help", N_("Convert native config to domain XML")},
+ {"desc", N_("Convert native guest configuration format to domain XML format.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_domxmlfromnative[] = {
+ {"format", VSH_OT_DATA, VSH_OFLAG_REQ, N_("source config data format")},
+ {"config", VSH_OT_DATA, VSH_OFLAG_REQ, N_("config data file to import from")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDomXMLFromNative(vshControl *ctl, const vshCmd *cmd)
+{
+ bool ret = true;
+ const char *format = NULL;
+ const char *configFile = NULL;
+ char *configData;
+ char *xmlData;
+ unsigned int flags = 0;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (vshCommandOptString(cmd, "format", &format) < 0 ||
+ vshCommandOptString(cmd, "config", &configFile) < 0)
+ return false;
+
+ if (virFileReadAll(configFile, 1024*1024, &configData) < 0)
+ return false;
+
+ xmlData = virConnectDomainXMLFromNative(ctl->conn, format, configData, flags);
+ if (xmlData != NULL) {
+ vshPrint(ctl, "%s", xmlData);
+ VIR_FREE(xmlData);
+ } else {
+ ret = false;
+ }
+
+ VIR_FREE(configData);
+ return ret;
+}
+
+/*
+ * "domxml-to-native" command
+ */
+static const vshCmdInfo info_domxmltonative[] = {
+ {"help", N_("Convert domain XML to native config")},
+ {"desc", N_("Convert domain XML config to a native guest configuration format.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_domxmltonative[] = {
+ {"format", VSH_OT_DATA, VSH_OFLAG_REQ, N_("target config data type format")},
+ {"xml", VSH_OT_DATA, VSH_OFLAG_REQ, N_("xml data file to export from")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDomXMLToNative(vshControl *ctl, const vshCmd *cmd)
+{
+ bool ret = true;
+ const char *format = NULL;
+ const char *xmlFile = NULL;
+ char *configData;
+ char *xmlData;
+ unsigned int flags = 0;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (vshCommandOptString(cmd, "format", &format) < 0
+ || vshCommandOptString(cmd, "xml", &xmlFile) < 0)
+ return false;
+
+ if (virFileReadAll(xmlFile, 1024*1024, &xmlData) < 0)
+ return false;
+
+ configData = virConnectDomainXMLToNative(ctl->conn, format, xmlData, flags);
+ if (configData != NULL) {
+ vshPrint(ctl, "%s", configData);
+ VIR_FREE(configData);
+ } else {
+ ret = false;
+ }
+
+ VIR_FREE(xmlData);
+ return ret;
+}
+
+/*
+ * "domname" command
+ */
+static const vshCmdInfo info_domname[] = {
+ {"help", N_("convert a domain id or UUID to domain name")},
+ {"desc", ""},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_domname[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain id or uuid")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDomname(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+ if (!(dom = vshCommandOptDomainBy(ctl, cmd, NULL,
+ VSH_BYID|VSH_BYUUID)))
+ return false;
+
+ vshPrint(ctl, "%s\n", virDomainGetName(dom));
+ virDomainFree(dom);
+ return true;
+}
+
+/*
+ * "domid" command
+ */
+static const vshCmdInfo info_domid[] = {
+ {"help", N_("convert a domain name or UUID to domain id")},
+ {"desc", ""},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_domid[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name or uuid")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDomid(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom;
+ unsigned int id;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+ if (!(dom = vshCommandOptDomainBy(ctl, cmd, NULL,
+ VSH_BYNAME|VSH_BYUUID)))
+ return false;
+
+ id = virDomainGetID(dom);
+ if (id == ((unsigned int)-1))
+ vshPrint(ctl, "%s\n", "-");
+ else
+ vshPrint(ctl, "%d\n", id);
+ virDomainFree(dom);
+ return true;
+}
+
+/*
+ * "domuuid" command
+ */
+static const vshCmdInfo info_domuuid[] = {
+ {"help", N_("convert a domain name or id to domain UUID")},
+ {"desc", ""},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_domuuid[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain id or name")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDomuuid(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom;
+ char uuid[VIR_UUID_STRING_BUFLEN];
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+ if (!(dom = vshCommandOptDomainBy(ctl, cmd, NULL,
+ VSH_BYNAME|VSH_BYID)))
+ return false;
+
+ if (virDomainGetUUIDString(dom, uuid) != -1)
+ vshPrint(ctl, "%s\n", uuid);
+ else
+ vshError(ctl, "%s", _("failed to get domain UUID"));
+
+ virDomainFree(dom);
+ return true;
+}
+
+/*
+ * "migrate" command
+ */
+static const vshCmdInfo info_migrate[] = {
+ {"help", N_("migrate domain to another host")},
+ {"desc", N_("Migrate domain to another host. Add --live for live migration.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_migrate[] = {
+ {"live", VSH_OT_BOOL, 0, N_("live migration")},
+ {"p2p", VSH_OT_BOOL, 0, N_("peer-2-peer migration")},
+ {"direct", VSH_OT_BOOL, 0, N_("direct migration")},
+ {"tunneled", VSH_OT_ALIAS, 0, "tunnelled"},
+ {"tunnelled", VSH_OT_BOOL, 0, N_("tunnelled migration")},
+ {"persistent", VSH_OT_BOOL, 0, N_("persist VM on destination")},
+ {"undefinesource", VSH_OT_BOOL, 0, N_("undefine VM on source")},
+ {"suspend", VSH_OT_BOOL, 0, N_("do not restart the domain on the destination host")},
+ {"copy-storage-all", VSH_OT_BOOL, 0, N_("migration with non-shared storage with full disk copy")},
+ {"copy-storage-inc", VSH_OT_BOOL, 0, N_("migration with non-shared storage with incremental copy (same base image shared between source and destination)")},
+ {"change-protection", VSH_OT_BOOL, 0,
+ N_("prevent any configuration changes to domain until migration ends)")},
+ {"unsafe", VSH_OT_BOOL, 0, N_("force migration even if it may be unsafe")},
+ {"verbose", VSH_OT_BOOL, 0, N_("display the progress of migration")},
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"desturi", VSH_OT_DATA, VSH_OFLAG_REQ, N_("connection URI of the destination host as seen from the client(normal migration) or source(p2p migration)")},
+ {"migrateuri", VSH_OT_DATA, 0, N_("migration URI, usually can be omitted")},
+ {"dname", VSH_OT_DATA, 0, N_("rename to new name during migration (if supported)")},
+ {"timeout", VSH_OT_INT, 0, N_("force guest to suspend if live migration exceeds timeout (in seconds)")},
+ {"xml", VSH_OT_STRING, 0, N_("filename containing updated XML for the target")},
+ {NULL, 0, 0, NULL}
+};
+
+static void
+doMigrate(void *opaque)
+{
+ char ret = '1';
+ virDomainPtr dom = NULL;
+ const char *desturi = NULL;
+ const char *migrateuri = NULL;
+ const char *dname = NULL;
+ unsigned int flags = 0;
+ vshCtrlData *data = opaque;
+ vshControl *ctl = data->ctl;
+ const vshCmd *cmd = data->cmd;
+ const char *xmlfile = NULL;
+ char *xml = NULL;
+ sigset_t sigmask, oldsigmask;
+
+ sigemptyset(&sigmask);
+ sigaddset(&sigmask, SIGINT);
+ if (pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask) < 0)
+ goto out_sig;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ goto out;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ goto out;
+
+ if (vshCommandOptString(cmd, "desturi", &desturi) <= 0 ||
+ vshCommandOptString(cmd, "migrateuri", &migrateuri) < 0 ||
+ vshCommandOptString(cmd, "dname", &dname) < 0) {
+ vshError(ctl, "%s", _("missing argument"));
+ goto out;
+ }
+
+ if (vshCommandOptString(cmd, "xml", &xmlfile) < 0) {
+ vshError(ctl, "%s", _("malformed xml argument"));
+ goto out;
+ }
+
+ if (vshCommandOptBool(cmd, "live"))
+ flags |= VIR_MIGRATE_LIVE;
+ if (vshCommandOptBool(cmd, "p2p"))
+ flags |= VIR_MIGRATE_PEER2PEER;
+ if (vshCommandOptBool(cmd, "tunnelled"))
+ flags |= VIR_MIGRATE_TUNNELLED;
+
+ if (vshCommandOptBool(cmd, "persistent"))
+ flags |= VIR_MIGRATE_PERSIST_DEST;
+ if (vshCommandOptBool(cmd, "undefinesource"))
+ flags |= VIR_MIGRATE_UNDEFINE_SOURCE;
+
+ if (vshCommandOptBool(cmd, "suspend"))
+ flags |= VIR_MIGRATE_PAUSED;
+
+ if (vshCommandOptBool(cmd, "copy-storage-all"))
+ flags |= VIR_MIGRATE_NON_SHARED_DISK;
+
+ if (vshCommandOptBool(cmd, "copy-storage-inc"))
+ flags |= VIR_MIGRATE_NON_SHARED_INC;
+
+ if (vshCommandOptBool(cmd, "change-protection"))
+ flags |= VIR_MIGRATE_CHANGE_PROTECTION;
+
+ if (vshCommandOptBool(cmd, "unsafe"))
+ flags |= VIR_MIGRATE_UNSAFE;
+
+ if (xmlfile &&
+ virFileReadAll(xmlfile, 8192, &xml) < 0) {
+ vshError(ctl, _("file '%s' doesn't exist"), xmlfile);
+ goto out;
+ }
+
+ if ((flags & VIR_MIGRATE_PEER2PEER) ||
+ vshCommandOptBool(cmd, "direct")) {
+ /* For peer2peer migration or direct migration we only expect one URI
+ * a libvirt URI, or a hypervisor specific URI. */
+
+ if (migrateuri != NULL) {
+ vshError(ctl, "%s", _("migrate: Unexpected migrateuri for peer2peer/direct migration"));
+ goto out;
+ }
+
+ if (virDomainMigrateToURI2(dom, desturi, NULL, xml, flags, dname, 0) == 0)
+ ret = '0';
+ } else {
+ /* For traditional live migration, connect to the destination host directly. */
+ virConnectPtr dconn = NULL;
+ virDomainPtr ddom = NULL;
+
+ dconn = virConnectOpenAuth(desturi, virConnectAuthPtrDefault, 0);
+ if (!dconn) goto out;
+
+ ddom = virDomainMigrate2(dom, dconn, xml, flags, dname, migrateuri, 0);
+ if (ddom) {
+ virDomainFree(ddom);
+ ret = '0';
+ }
+ virConnectClose(dconn);
+ }
+
+out:
+ pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
+out_sig:
+ if (dom) virDomainFree(dom);
+ VIR_FREE(xml);
+ ignore_value(safewrite(data->writefd, &ret, sizeof(ret)));
+}
+
+static void
+vshMigrationTimeout(vshControl *ctl,
+ virDomainPtr dom,
+ void *opaque ATTRIBUTE_UNUSED)
+{
+ vshDebug(ctl, VSH_ERR_DEBUG, "suspending the domain, "
+ "since migration timed out\n");
+ virDomainSuspend(dom);
+}
+
+static bool
+vshWatchJob(vshControl *ctl,
+ virDomainPtr dom,
+ bool verbose,
+ int pipe_fd,
+ int timeout,
+ jobWatchTimeoutFunc timeout_func,
+ void *opaque,
+ const char *label)
+{
+ struct sigaction sig_action;
+ struct sigaction old_sig_action;
+ struct pollfd pollfd;
+ struct timeval start, curr;
+ virDomainJobInfo jobinfo;
+ int ret = -1;
+ char retchar;
+ bool functionReturn = false;
+ sigset_t sigmask, oldsigmask;
+
+ sigemptyset(&sigmask);
+ sigaddset(&sigmask, SIGINT);
+
+ intCaught = 0;
+ sig_action.sa_sigaction = vshCatchInt;
+ sig_action.sa_flags = SA_SIGINFO;
+ sigemptyset(&sig_action.sa_mask);
+ sigaction(SIGINT, &sig_action, &old_sig_action);
+
+ pollfd.fd = pipe_fd;
+ pollfd.events = POLLIN;
+ pollfd.revents = 0;
+
+ GETTIMEOFDAY(&start);
+ while (1) {
+repoll:
+ ret = poll(&pollfd, 1, 500);
+ if (ret > 0) {
+ if (pollfd.revents & POLLIN &&
+ saferead(pipe_fd, &retchar, sizeof(retchar)) > 0 &&
+ retchar == '0') {
+ if (verbose) {
+ /* print [100 %] */
+ print_job_progress(label, 0, 1);
+ }
+ break;
+ }
+ goto cleanup;
+ }
+
+ if (ret < 0) {
+ if (errno == EINTR) {
+ if (intCaught) {
+ virDomainAbortJob(dom);
+ intCaught = 0;
+ } else {
+ goto repoll;
+ }
+ }
+ goto cleanup;
+ }
+
+ GETTIMEOFDAY(&curr);
+ if (timeout && (((int)(curr.tv_sec - start.tv_sec) * 1000 +
+ (int)(curr.tv_usec - start.tv_usec) / 1000) >
+ timeout * 1000)) {
+ /* suspend the domain when migration timeouts. */
+ vshDebug(ctl, VSH_ERR_DEBUG, "%s timeout", label);
+ if (timeout_func)
+ (timeout_func)(ctl, dom, opaque);
+ timeout = 0;
+ }
+
+ if (verbose) {
+ pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask);
+ ret = virDomainGetJobInfo(dom, &jobinfo);
+ pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
+ if (ret == 0)
+ print_job_progress(label, jobinfo.dataRemaining,
+ jobinfo.dataTotal);
+ }
+ }
+
+ functionReturn = true;
+
+cleanup:
+ sigaction(SIGINT, &old_sig_action, NULL);
+ return functionReturn;
+}
+
+static bool
+cmdMigrate(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom = NULL;
+ int p[2] = {-1, -1};
+ virThread workerThread;
+ bool verbose = false;
+ bool functionReturn = false;
+ int timeout = 0;
+ bool live_flag = false;
+ vshCtrlData data;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ return false;
+
+ if (vshCommandOptBool(cmd, "verbose"))
+ verbose = true;
+
+ if (vshCommandOptBool(cmd, "live"))
+ live_flag = true;
+ if (vshCommandOptInt(cmd, "timeout", &timeout) > 0) {
+ if (! live_flag) {
+ vshError(ctl, "%s",
+ _("migrate: Unexpected timeout for offline migration"));
+ goto cleanup;
+ }
+
+ if (timeout < 1) {
+ vshError(ctl, "%s", _("migrate: Invalid timeout"));
+ goto cleanup;
+ }
+
+ /* Ensure that we can multiply by 1000 without overflowing. */
+ if (timeout > INT_MAX / 1000) {
+ vshError(ctl, "%s", _("migrate: Timeout is too big"));
+ goto cleanup;
+ }
+ }
+
+ if (pipe(p) < 0)
+ goto cleanup;
+
+ data.ctl = ctl;
+ data.cmd = cmd;
+ data.writefd = p[1];
+
+ if (virThreadCreate(&workerThread,
+ true,
+ doMigrate,
+ &data) < 0)
+ goto cleanup;
+ functionReturn = vshWatchJob(ctl, dom, verbose, p[0], timeout,
+ vshMigrationTimeout, NULL, _("Migration"));
+
+ virThreadJoin(&workerThread);
+
+cleanup:
+ virDomainFree(dom);
+ VIR_FORCE_CLOSE(p[0]);
+ VIR_FORCE_CLOSE(p[1]);
+ return functionReturn;
+}
+
+/*
+ * "migrate-setmaxdowntime" command
+ */
+static const vshCmdInfo info_migrate_setmaxdowntime[] = {
+ {"help", N_("set maximum tolerable downtime")},
+ {"desc", N_("Set maximum tolerable downtime of a domain which is being live-migrated to another host.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_migrate_setmaxdowntime[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"downtime", VSH_OT_INT, VSH_OFLAG_REQ, N_("maximum tolerable downtime (in milliseconds) for migration")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdMigrateSetMaxDowntime(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom = NULL;
+ long long downtime = 0;
+ bool ret = false;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ return false;
+
+ if (vshCommandOptLongLong(cmd, "downtime", &downtime) < 0 ||
+ downtime < 1) {
+ vshError(ctl, "%s", _("migrate: Invalid downtime"));
+ goto done;
+ }
+
+ if (virDomainMigrateSetMaxDowntime(dom, downtime, 0))
+ goto done;
+
+ ret = true;
+
+done:
+ virDomainFree(dom);
+ return ret;
+}
+
+/*
+ * "migrate-setspeed" command
+ */
+static const vshCmdInfo info_migrate_setspeed[] = {
+ {"help", N_("Set the maximum migration bandwidth")},
+ {"desc", N_("Set the maximum migration bandwidth (in MiB/s) for a domain "
+ "which is being migrated to another host.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_migrate_setspeed[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"bandwidth", VSH_OT_INT, VSH_OFLAG_REQ,
+ N_("migration bandwidth limit in MiB/s")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdMigrateSetMaxSpeed(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom = NULL;
+ unsigned long bandwidth = 0;
+ bool ret = false;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ return false;
+
+ if (vshCommandOptUL(cmd, "bandwidth", &bandwidth) < 0) {
+ vshError(ctl, "%s", _("migrate: Invalid bandwidth"));
+ goto done;
+ }
+
+ if (virDomainMigrateSetMaxSpeed(dom, bandwidth, 0) < 0)
+ goto done;
+
+ ret = true;
+
+done:
+ virDomainFree(dom);
+ return ret;
+}
+
+/*
+ * "migrate-getspeed" command
+ */
+static const vshCmdInfo info_migrate_getspeed[] = {
+ {"help", N_("Get the maximum migration bandwidth")},
+ {"desc", N_("Get the maximum migration bandwidth (in MiB/s) for a domain.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_migrate_getspeed[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdMigrateGetMaxSpeed(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom = NULL;
+ unsigned long bandwidth;
+ bool ret = false;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ return false;
+
+ if (virDomainMigrateGetMaxSpeed(dom, &bandwidth, 0) < 0)
+ goto done;
+
+ vshPrint(ctl, "%lu\n", bandwidth);
+
+ ret = true;
+
+done:
+ virDomainFree(dom);
+ return ret;
+}
+
+/*
+ * "domdisplay" command
+ */
+static const vshCmdInfo info_domdisplay[] = {
+ {"help", N_("domain display connection URI")},
+ {"desc", N_("Output the IP address and port number for the graphical display.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_domdisplay[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"include-password", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("includes the password into the connection URI if available")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDomDisplay(vshControl *ctl, const vshCmd *cmd)
+{
+ xmlDocPtr xml = NULL;
+ xmlXPathContextPtr ctxt = NULL;
+ virDomainPtr dom;
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+ bool ret = false;
+ char *doc;
+ char *xpath;
+ char *listen_addr;
+ int port, tls_port = 0;
+ char *passwd = NULL;
+ char *output = NULL;
+ const char *scheme[] = { "vnc", "spice", "rdp", NULL };
+ int iter = 0;
+ int tmp;
+ int flags = 0;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ return false;
+
+ if (!virDomainIsActive(dom)) {
+ vshError(ctl, _("Domain is not running"));
+ goto cleanup;
+ }
+
+ if (vshCommandOptBool(cmd, "include-password"))
+ flags |= VIR_DOMAIN_XML_SECURE;
+
+ doc = virDomainGetXMLDesc(dom, flags);
+
+ if (!doc)
+ goto cleanup;
+
+ xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt);
+ VIR_FREE(doc);
+ if (!xml)
+ goto cleanup;
+
+ /* Attempt to grab our display info */
+ for (iter = 0; scheme[iter] != NULL; iter++) {
+ /* Create our XPATH lookup for the current display's port */
+ virAsprintf(&xpath, "string(/domain/devices/graphics[@type='%s']"
+ "/@port)", scheme[iter]);
+ if (!xpath) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ /* Attempt to get the port number for the current graphics scheme */
+ tmp = virXPathInt(xpath, ctxt, &port);
+ VIR_FREE(xpath);
+
+ /* If there is no port number for this type, then jump to the next
+ * scheme
+ */
+ if (tmp)
+ continue;
+
+ /* Create our XPATH lookup for the current display's address */
+ virAsprintf(&xpath, "string(/domain/devices/graphics[@type='%s']"
+ "/@listen)", scheme[iter]);
+ if (!xpath) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ /* Attempt to get the listening addr if set for the current
+ * graphics scheme
+ */
+ listen_addr = virXPathString(xpath, ctxt);
+ VIR_FREE(xpath);
+
+ /* Per scheme data mangling */
+ if (STREQ(scheme[iter], "vnc")) {
+ /* VNC protocol handlers take their port number as 'port' - 5900 */
+ port -= 5900;
+ } else if (STREQ(scheme[iter], "spice")) {
+ /* Create our XPATH lookup for the SPICE TLS Port */
+ virAsprintf(&xpath, "string(/domain/devices/graphics[@type='%s']"
+ "/@tlsPort)", scheme[iter]);
+ if (!xpath) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ /* Attempt to get the TLS port number for SPICE */
+ tmp = virXPathInt(xpath, ctxt, &tls_port);
+ VIR_FREE(xpath);
+ if (tmp)
+ tls_port = 0;
+
+ if (vshCommandOptBool(cmd, "include-password")) {
+ /* Create our XPATH lookup for the SPICE password */
+ virAsprintf(&xpath, "string(/domain/devices/graphics"
+ "[@type='%s']/@passwd)", scheme[iter]);
+ if (!xpath) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ /* Attempt to get the SPICE password */
+ passwd = virXPathString(xpath, ctxt);
+ VIR_FREE(xpath);
+ }
+ }
+
+ /* Build up the full URI, starting with the scheme */
+ virBufferAsprintf(&buf, "%s://", scheme[iter]);
+
+ /* Then host name or IP */
+ if (!listen_addr || STREQ((const char *)listen_addr, "0.0.0.0"))
+ virBufferAddLit(&buf, "localhost");
+ else
+ virBufferAsprintf(&buf, "%s", listen_addr);
+
+ VIR_FREE(listen_addr);
+
+ /* Add the port */
+ if (STREQ(scheme[iter], "spice"))
+ virBufferAsprintf(&buf, "?port=%d", port);
+ else
+ virBufferAsprintf(&buf, ":%d", port);
+
+ /* TLS Port */
+ if (tls_port)
+ virBufferAsprintf(&buf, "&tls-port=%d", tls_port);
+
+ /* Password */
+ if (passwd) {
+ virBufferAsprintf(&buf, "&password=%s", passwd);
+ VIR_FREE(passwd);
+ }
+
+ /* Ensure we can print our URI */
+ if (virBufferError(&buf)) {
+ vshPrint(ctl, "%s", _("Failed to create display URI"));
+ goto cleanup;
+ }
+
+ /* Print out our full URI */
+ output = virBufferContentAndReset(&buf);
+ vshPrint(ctl, "%s", output);
+ VIR_FREE(output);
+
+ /* We got what we came for so return successfully */
+ ret = true;
+ break;
+ }
+
+cleanup:
+ xmlXPathFreeContext(ctxt);
+ xmlFreeDoc(xml);
+ virDomainFree(dom);
+ return ret;
+}
+
+/*
+ * "vncdisplay" command
+ */
+static const vshCmdInfo info_vncdisplay[] = {
+ {"help", N_("vnc display")},
+ {"desc", N_("Output the IP address and port number for the VNC display.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_vncdisplay[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdVNCDisplay(vshControl *ctl, const vshCmd *cmd)
+{
+ xmlDocPtr xml = NULL;
+ xmlXPathContextPtr ctxt = NULL;
+ virDomainPtr dom;
+ bool ret = false;
+ int port = 0;
+ char *doc = NULL;
+ char *listen_addr = NULL;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ return false;
+
+ /* Check if the domain is active and don't rely on -1 for this */
+ if (!virDomainIsActive(dom)) {
+ vshError(ctl, _("Domain is not running"));
+ goto cleanup;
+ }
+
+ if (!(doc = virDomainGetXMLDesc(dom, 0)))
+ goto cleanup;
+
+ if (!(xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt)))
+ goto cleanup;
+
+ /* Get the VNC port */
+ if (virXPathInt("string(/domain/devices/graphics[@type='vnc']/@port)",
+ ctxt, &port)) {
+ vshError(ctl, _("Failed to get VNC port. Is this domain using VNC?"));
+ goto cleanup;
+ }
+
+ listen_addr = virXPathString("string(/domain/devices/graphics"
+ "[@type='vnc']/@listen)", ctxt);
+ if (listen_addr == NULL || STREQ(listen_addr, "0.0.0.0"))
+ vshPrint(ctl, ":%d\n", port-5900);
+ else
+ vshPrint(ctl, "%s:%d\n", listen_addr, port-5900);
+
+ ret = true;
+
+ cleanup:
+ VIR_FREE(doc);
+ VIR_FREE(listen_addr);
+ xmlXPathFreeContext(ctxt);
+ xmlFreeDoc(xml);
+ virDomainFree(dom);
+ return ret;
+}
+
+/*
+ * "ttyconsole" command
+ */
+static const vshCmdInfo info_ttyconsole[] = {
+ {"help", N_("tty console")},
+ {"desc", N_("Output the device for the TTY console.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_ttyconsole[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdTTYConsole(vshControl *ctl, const vshCmd *cmd)
+{
+ xmlDocPtr xml = NULL;
+ xmlXPathObjectPtr obj = NULL;
+ xmlXPathContextPtr ctxt = NULL;
+ virDomainPtr dom;
+ bool ret = false;
+ char *doc;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ return false;
+
+ doc = virDomainGetXMLDesc(dom, 0);
+ if (!doc)
+ goto cleanup;
+
+ xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt);
+ VIR_FREE(doc);
+ if (!xml)
+ goto cleanup;
+
+ obj = xmlXPathEval(BAD_CAST "string(/domain/devices/console/@tty)", ctxt);
+ if (obj == NULL || obj->type != XPATH_STRING ||
+ obj->stringval == NULL || obj->stringval[0] == 0) {
+ goto cleanup;
+ }
+ vshPrint(ctl, "%s\n", (const char *)obj->stringval);
+ ret = true;
+
+ cleanup:
+ xmlXPathFreeObject(obj);
+ xmlXPathFreeContext(ctxt);
+ xmlFreeDoc(xml);
+ virDomainFree(dom);
+ return ret;
+}
+
+/*
+ * "domhostname" command
+ */
+static const vshCmdInfo info_domhostname[] = {
+ {"help", N_("print the domain's hostname")},
+ {"desc", ""},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_domhostname[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDomHostname(vshControl *ctl, const vshCmd *cmd)
+{
+ char *hostname;
+ virDomainPtr dom;
+ bool ret = false;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ return false;
+
+ hostname = virDomainGetHostname(dom, 0);
+ if (hostname == NULL) {
+ vshError(ctl, "%s", _("failed to get hostname"));
+ goto error;
+ }
+
+ vshPrint(ctl, "%s\n", hostname);
+ ret = true;
+
+error:
+ VIR_FREE(hostname);
+ virDomainFree(dom);
+ return ret;
+}
+
+/**
+ * Check if n1 is superset of n2, meaning n1 contains all elements and
+ * attributes as n2 at least. Including children.
+ * @n1 first node
+ * @n2 second node
+ * returns true in case n1 covers n2, false otherwise.
+ */
+ATTRIBUTE_UNUSED
+static bool
+vshNodeIsSuperset(xmlNodePtr n1, xmlNodePtr n2)
+{
+ xmlNodePtr child1, child2;
+ xmlAttrPtr attr;
+ char *prop1, *prop2;
+ bool found;
+ bool visited;
+ bool ret = false;
+ long n1_child_size, n2_child_size, n1_iter;
+ virBitmapPtr bitmap;
+
+ if (!n1 && !n2)
+ return true;
+
+ if (!n1 || !n2)
+ return false;
+
+ if (!xmlStrEqual(n1->name, n2->name))
+ return false;
+
+ /* Iterate over n2 attributes and check if n1 contains them*/
+ attr = n2->properties;
+ while (attr) {
+ if (attr->type == XML_ATTRIBUTE_NODE) {
+ prop1 = virXMLPropString(n1, (const char *) attr->name);
+ prop2 = virXMLPropString(n2, (const char *) attr->name);
+ if (STRNEQ_NULLABLE(prop1, prop2)) {
+ xmlFree(prop1);
+ xmlFree(prop2);
+ return false;
+ }
+ xmlFree(prop1);
+ xmlFree(prop2);
+ }
+ attr = attr->next;
+ }
+
+ n1_child_size = virXMLChildElementCount(n1);
+ n2_child_size = virXMLChildElementCount(n2);
+ if (n1_child_size < 0 || n2_child_size < 0 ||
+ n1_child_size < n2_child_size)
+ return false;
+
+ if (n1_child_size == 0 && n2_child_size == 0)
+ return true;
+
+ if (!(bitmap = virBitmapAlloc(n1_child_size))) {
+ virReportOOMError();
+ return false;
+ }
+
+ child2 = n2->children;
+ while (child2) {
+ if (child2->type != XML_ELEMENT_NODE) {
+ child2 = child2->next;
+ continue;
+ }
+
+ child1 = n1->children;
+ n1_iter = 0;
+ found = false;
+ while (child1) {
+ if (child1->type != XML_ELEMENT_NODE) {
+ child1 = child1->next;
+ continue;
+ }
+
+ if (virBitmapGetBit(bitmap, n1_iter, &visited) < 0) {
+ vshError(NULL, "%s", _("Bad child elements counting."));
+ goto cleanup;
+ }
+
+ if (visited) {
+ child1 = child1->next;
+ n1_iter++;
+ continue;
+ }
+
+ if (xmlStrEqual(child1->name, child2->name)) {
+ found = true;
+ if (virBitmapSetBit(bitmap, n1_iter) < 0) {
+ vshError(NULL, "%s", _("Bad child elements counting."));
+ goto cleanup;
+ }
+
+ if (!vshNodeIsSuperset(child1, child2))
+ goto cleanup;
+
+ break;
+ }
+
+ child1 = child1->next;
+ n1_iter++;
+ }
+
+ if (!found)
+ goto cleanup;
+
+ child2 = child2->next;
+ }
+
+ ret = true;
+
+cleanup:
+ virBitmapFree(bitmap);
+ return ret;
+}
+
+/**
+ * vshCompleteXMLFromDomain:
+ * @ctl vshControl for error messages printing
+ * @dom domain
+ * @oldXML device XML before
+ * @newXML and after completion
+ *
+ * For given domain and (probably incomplete) device XML specification try to
+ * find such device in domain and complete missing parts. This is however
+ * possible only when given device XML is sufficiently precise so it addresses
+ * only one device.
+ *
+ * Returns -2 when no such device exists in domain, -3 when given XML selects many
+ * (is too ambiguous), 0 in case of success. Otherwise returns -1. @newXML
+ * is touched only in case of success.
+ */
+ATTRIBUTE_UNUSED
+static int
+vshCompleteXMLFromDomain(vshControl *ctl, virDomainPtr dom, char *oldXML,
+ char **newXML)
+{
+ int funcRet = -1;
+ char *domXML = NULL;
+ xmlDocPtr domDoc = NULL, devDoc = NULL;
+ xmlNodePtr node = NULL;
+ xmlXPathContextPtr domCtxt = NULL, devCtxt = NULL;
+ xmlNodePtr *devices = NULL;
+ xmlSaveCtxtPtr sctxt = NULL;
+ int devices_size;
+ char *xpath = NULL;
+ xmlBufferPtr buf = NULL;
+ int i = 0;
+ int indx = -1;
+
+ if (!(domXML = virDomainGetXMLDesc(dom, 0))) {
+ vshError(ctl, _("couldn't get XML description of domain %s"),
+ virDomainGetName(dom));
+ goto cleanup;
+ }
+
+ domDoc = virXMLParseStringCtxt(domXML, _("(domain_definition)"), &domCtxt);
+ if (!domDoc) {
+ vshError(ctl, _("Failed to parse domain definition xml"));
+ goto cleanup;
+ }
+
+ devDoc = virXMLParseStringCtxt(oldXML, _("(device_definition)"), &devCtxt);
+ if (!devDoc) {
+ vshError(ctl, _("Failed to parse device definition xml"));
+ goto cleanup;
+ }
+
+ node = xmlDocGetRootElement(devDoc);
+
+ buf = xmlBufferCreate();
+ if (!buf) {
+ vshError(ctl, "%s", _("out of memory"));
+ goto cleanup;
+ }
+
+ /* Get all possible devices */
+ virAsprintf(&xpath, "/domain/devices/%s", node->name);
+ if (!xpath) {
+ virReportOOMError();
+ goto cleanup;
+ }
+ devices_size = virXPathNodeSet(xpath, domCtxt, &devices);
+
+ if (devices_size < 0) {
+ /* error */
+ vshError(ctl, "%s", _("error when selecting nodes"));
+ goto cleanup;
+ } else if (devices_size == 0) {
+ /* no such device */
+ funcRet = -2;
+ goto cleanup;
+ }
+
+ /* and refine */
+ for (i = 0; i < devices_size; i++) {
+ if (vshNodeIsSuperset(devices[i], node)) {
+ if (indx >= 0) {
+ funcRet = -3; /* ambiguous */
+ goto cleanup;
+ }
+ indx = i;
+ }
+ }
+
+ if (indx < 0) {
+ funcRet = -2; /* no such device */
+ goto cleanup;
+ }
+
+ vshDebug(ctl, VSH_ERR_DEBUG, "Found device at pos %d\n", indx);
+
+ if (newXML) {
+ sctxt = xmlSaveToBuffer(buf, NULL, 0);
+ if (!sctxt) {
+ vshError(ctl, "%s", _("failed to create document saving context"));
+ goto cleanup;
+ }
+
+ xmlSaveTree(sctxt, devices[indx]);
+ xmlSaveClose(sctxt);
+ *newXML = (char *) xmlBufferContent(buf);
+ if (!*newXML) {
+ virReportOOMError();
+ goto cleanup;
+ }
+ buf->content = NULL;
+ }
+
+ vshDebug(ctl, VSH_ERR_DEBUG, "Old xml:\n%s\nNew xml:\n%s\n", oldXML,
+ newXML ? NULLSTR(*newXML) : "(null)");
+
+ funcRet = 0;
+
+cleanup:
+ xmlBufferFree(buf);
+ VIR_FREE(devices);
+ xmlXPathFreeContext(devCtxt);
+ xmlXPathFreeContext(domCtxt);
+ xmlFreeDoc(devDoc);
+ xmlFreeDoc(domDoc);
+ VIR_FREE(domXML);
+ VIR_FREE(xpath);
+ return funcRet;
+}
+
+/*
+ * "detach-device" command
+ */
+static const vshCmdInfo info_detach_device[] = {
+ {"help", N_("detach device from an XML file")},
+ {"desc", N_("Detach device from an XML ")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_detach_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
+cmdDetachDevice(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom = NULL;
+ const char *from = NULL;
+ char *buffer = NULL;
+ int ret;
+ bool funcRet = false;
+ unsigned int flags;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ return false;
+
+ if (vshCommandOptString(cmd, "file", &from) <= 0)
+ goto cleanup;
+
+ if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
+ virshReportError(ctl);
+ goto cleanup;
+ }
+
+ if (vshCommandOptBool(cmd, "config")) {
+ flags = VIR_DOMAIN_AFFECT_CONFIG;
+ if (virDomainIsActive(dom) == 1)
+ flags |= VIR_DOMAIN_AFFECT_LIVE;
+ ret = virDomainDetachDeviceFlags(dom, buffer, flags);
+ } else {
+ ret = virDomainDetachDevice(dom, buffer);
+ }
+
+ if (ret < 0) {
+ vshError(ctl, _("Failed to detach device from %s"), from);
+ goto cleanup;
+ }
+
+ vshPrint(ctl, "%s", _("Device detached successfully\n"));
+ funcRet = true;
+
+cleanup:
+ VIR_FREE(buffer);
+ virDomainFree(dom);
+ return funcRet;
+}
+
+/*
+ * "update-device" command
+ */
+static const vshCmdInfo info_update_device[] = {
+ {"help", N_("update device from an XML file")},
+ {"desc", N_("Update device from an XML .")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_update_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")},
+ {"force", VSH_OT_BOOL, 0, N_("force device update")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdUpdateDevice(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;
+ } else {
+ flags = VIR_DOMAIN_AFFECT_LIVE;
+ }
+
+ if (vshCommandOptBool(cmd, "force"))
+ flags |= VIR_DOMAIN_DEVICE_MODIFY_FORCE;
+
+ ret = virDomainUpdateDeviceFlags(dom, buffer, flags);
+ VIR_FREE(buffer);
+
+ if (ret < 0) {
+ vshError(ctl, _("Failed to update device from %s"), from);
+ virDomainFree(dom);
+ return false;
+ } else {
+ vshPrint(ctl, "%s", _("Device updated successfully\n"));
+ }
+
+ virDomainFree(dom);
+ return true;
+}
+
+/*
+ * "detach-interface" command
+ */
+static const vshCmdInfo info_detach_interface[] = {
+ {"help", N_("detach network interface")},
+ {"desc", N_("Detach network interface.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_detach_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")},
+ {"mac", VSH_OT_STRING, 0, N_("MAC address")},
+ {"persistent", VSH_OT_ALIAS, 0, "config"},
+ {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDetachInterface(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom = NULL;
+ xmlDocPtr xml = NULL;
+ xmlXPathObjectPtr obj=NULL;
+ xmlXPathContextPtr ctxt = NULL;
+ xmlNodePtr cur = NULL;
+ xmlBufferPtr xml_buf = NULL;
+ const char *mac =NULL, *type = NULL;
+ char *doc;
+ char buf[64];
+ int i = 0, diff_mac;
+ int ret;
+ int functionReturn = false;
+ unsigned int flags;
+
+ 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, "mac", &mac) < 0) {
+ vshError(ctl, "%s", _("missing option"));
+ goto cleanup;
+ }
+
+ doc = virDomainGetXMLDesc(dom, 0);
+ if (!doc)
+ goto cleanup;
+
+ xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt);
+ VIR_FREE(doc);
+ if (!xml) {
+ vshError(ctl, "%s", _("Failed to get interface information"));
+ goto cleanup;
+ }
+
+ snprintf(buf, sizeof(buf), "/domain/devices/interface[@type='%s']", type);
+ obj = xmlXPathEval(BAD_CAST buf, ctxt);
+ if (obj == NULL || obj->type != XPATH_NODESET ||
+ obj->nodesetval == NULL || obj->nodesetval->nodeNr == 0) {
+ vshError(ctl, _("No found interface whose type is %s"), type);
+ goto cleanup;
+ }
+
+ if (!mac && obj->nodesetval->nodeNr > 1) {
+ vshError(ctl, _("Domain has %d interfaces. Please specify which one "
+ "to detach using --mac"), obj->nodesetval->nodeNr);
+ goto cleanup;
+ }
+
+ if (!mac)
+ goto hit;
+
+ /* search mac */
+ for (; i < obj->nodesetval->nodeNr; i++) {
+ cur = obj->nodesetval->nodeTab[i]->children;
+ while (cur != NULL) {
+ if (cur->type == XML_ELEMENT_NODE &&
+ xmlStrEqual(cur->name, BAD_CAST "mac")) {
+ char *tmp_mac = virXMLPropString(cur, "address");
+ diff_mac = virMacAddrCompare(tmp_mac, mac);
+ VIR_FREE(tmp_mac);
+ if (!diff_mac) {
+ goto hit;
+ }
+ }
+ cur = cur->next;
+ }
+ }
+ vshError(ctl, _("No found interface whose MAC address is %s"), mac);
+ goto cleanup;
+
+ hit:
+ xml_buf = xmlBufferCreate();
+ if (!xml_buf) {
+ vshError(ctl, "%s", _("Failed to allocate memory"));
+ goto cleanup;
+ }
+
+ if (xmlNodeDump(xml_buf, xml, obj->nodesetval->nodeTab[i], 0, 0) < 0) {
+ vshError(ctl, "%s", _("Failed to create XML"));
+ goto cleanup;
+ }
+
+ if (vshCommandOptBool(cmd, "config")) {
+ flags = VIR_DOMAIN_AFFECT_CONFIG;
+ if (virDomainIsActive(dom) == 1)
+ flags |= VIR_DOMAIN_AFFECT_LIVE;
+ ret = virDomainDetachDeviceFlags(dom,
+ (char *)xmlBufferContent(xml_buf),
+ flags);
+ } else {
+ ret = virDomainDetachDevice(dom, (char *)xmlBufferContent(xml_buf));
+ }
+
+ if (ret != 0) {
+ vshError(ctl, "%s", _("Failed to detach interface"));
+ } else {
+ vshPrint(ctl, "%s", _("Interface detached successfully\n"));
+ functionReturn = true;
+ }
+
+ cleanup:
+ if (dom)
+ virDomainFree(dom);
+ xmlXPathFreeObject(obj);
+ xmlXPathFreeContext(ctxt);
+ xmlFreeDoc(xml);
+ xmlBufferFree(xml_buf);
+ return functionReturn;
+}
+
+typedef enum {
+ VSH_FIND_DISK_NORMAL,
+ VSH_FIND_DISK_CHANGEABLE,
+} vshFindDiskType;
+
+/* Helper function to find disk device in XML doc. Returns the disk
+ * node on success, or NULL on failure. Caller must free the result
+ * @path: Fully-qualified path or target of disk device.
+ * @type: Either VSH_FIND_DISK_NORMAL or VSH_FIND_DISK_CHANGEABLE.
+ */
+static xmlNodePtr
+vshFindDisk(const char *doc,
+ const char *path,
+ int type)
+{
+ xmlDocPtr xml = NULL;
+ xmlXPathObjectPtr obj= NULL;
+ xmlXPathContextPtr ctxt = NULL;
+ xmlNodePtr cur = NULL;
+ xmlNodePtr ret = NULL;
+ int i = 0;
+
+ xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt);
+ if (!xml) {
+ vshError(NULL, "%s", _("Failed to get disk information"));
+ goto cleanup;
+ }
+
+ obj = xmlXPathEval(BAD_CAST "/domain/devices/disk", ctxt);
+ if (obj == NULL ||
+ obj->type != XPATH_NODESET ||
+ obj->nodesetval == NULL ||
+ obj->nodesetval->nodeNr == 0) {
+ vshError(NULL, "%s", _("Failed to get disk information"));
+ goto cleanup;
+ }
+
+ /* search disk using @path */
+ for (; i < obj->nodesetval->nodeNr; i++) {
+ bool is_supported = true;
+
+ if (type == VSH_FIND_DISK_CHANGEABLE) {
+ xmlNodePtr n = obj->nodesetval->nodeTab[i];
+ is_supported = false;
+
+ /* Check if the disk is CDROM or floppy disk */
+ if (xmlStrEqual(n->name, BAD_CAST "disk")) {
+ char *device_value = virXMLPropString(n, "device");
+
+ if (STREQ(device_value, "cdrom") ||
+ STREQ(device_value, "floppy"))
+ is_supported = true;
+
+ VIR_FREE(device_value);
+ }
+
+ if (!is_supported)
+ continue;
+ }
+
+ cur = obj->nodesetval->nodeTab[i]->children;
+ while (cur != NULL) {
+ if (cur->type == XML_ELEMENT_NODE) {
+ char *tmp = NULL;
+
+ if (xmlStrEqual(cur->name, BAD_CAST "source")) {
+ if ((tmp = virXMLPropString(cur, "file")) ||
+ (tmp = virXMLPropString(cur, "dev")) ||
+ (tmp = virXMLPropString(cur, "dir")) ||
+ (tmp = virXMLPropString(cur, "name"))) {
+ }
+ } else if (xmlStrEqual(cur->name, BAD_CAST "target")) {
+ tmp = virXMLPropString(cur, "dev");
+ }
+
+ if (STREQ_NULLABLE(tmp, path)) {
+ ret = xmlCopyNode(obj->nodesetval->nodeTab[i], 1);
+ VIR_FREE(tmp);
+ goto cleanup;
+ }
+ VIR_FREE(tmp);
+ }
+ cur = cur->next;
+ }
+ }
+
+ vshError(NULL, _("No found disk whose source path or target is %s"), path);
+
+cleanup:
+ xmlXPathFreeObject(obj);
+ xmlXPathFreeContext(ctxt);
+ xmlFreeDoc(xml);
+ return ret;
+}
+
+typedef enum {
+ VSH_PREPARE_DISK_XML_NONE = 0,
+ VSH_PREPARE_DISK_XML_EJECT,
+ VSH_PREPARE_DISK_XML_INSERT,
+ VSH_PREPARE_DISK_XML_UPDATE,
+} vshPrepareDiskXMLType;
+
+/* Helper function to prepare disk XML. Could be used for disk
+ * detaching, media changing(ejecting, inserting, updating)
+ * for changeable disk. Returns the processed XML as string on
+ * success, or NULL on failure. Caller must free the result.
+ */
+static char *
+vshPrepareDiskXML(xmlNodePtr disk_node,
+ const char *source,
+ const char *path,
+ int type)
+{
+ xmlNodePtr cur = NULL;
+ xmlBufferPtr xml_buf = NULL;
+ const char *disk_type = NULL;
+ const char *device_type = NULL;
+ xmlNodePtr new_node = NULL;
+ char *ret = NULL;
+
+ if (!disk_node)
+ return NULL;
+
+ xml_buf = xmlBufferCreate();
+ if (!xml_buf) {
+ vshError(NULL, "%s", _("Failed to allocate memory"));
+ return NULL;
+ }
+
+ device_type = virXMLPropString(disk_node, "device");
+
+ if (STREQ_NULLABLE(device_type, "cdrom") ||
+ STREQ_NULLABLE(device_type, "floppy")) {
+ bool has_source = false;
+ disk_type = virXMLPropString(disk_node, "type");
+
+ cur = disk_node->children;
+ while (cur != NULL) {
+ if (cur->type == XML_ELEMENT_NODE &&
+ xmlStrEqual(cur->name, BAD_CAST "source")) {
+ has_source = true;
+ break;
+ }
+ cur = cur->next;
+ }
+
+ if (!has_source) {
+ if (type == VSH_PREPARE_DISK_XML_EJECT) {
+ vshError(NULL, _("The disk device '%s' doesn't have media"),
+ path);
+ goto error;
+ }
+
+ if (source) {
+ new_node = xmlNewNode(NULL, BAD_CAST "source");
+ xmlNewProp(new_node, (const xmlChar *)disk_type,
+ (const xmlChar *)source);
+ xmlAddChild(disk_node, new_node);
+ } else if (type == VSH_PREPARE_DISK_XML_INSERT) {
+ vshError(NULL, _("No source is specified for inserting media"));
+ goto error;
+ } else if (type == VSH_PREPARE_DISK_XML_UPDATE) {
+ vshError(NULL, _("No source is specified for updating media"));
+ goto error;
+ }
+ }
+
+ if (has_source) {
+ if (type == VSH_PREPARE_DISK_XML_INSERT) {
+ vshError(NULL, _("The disk device '%s' already has media"),
+ path);
+ goto error;
+ }
+
+ /* Remove the source if it tends to eject/update media. */
+ xmlUnlinkNode(cur);
+ xmlFreeNode(cur);
+
+ if (source && (type == VSH_PREPARE_DISK_XML_UPDATE)) {
+ new_node = xmlNewNode(NULL, BAD_CAST "source");
+ xmlNewProp(new_node, (const xmlChar *)disk_type,
+ (const xmlChar *)source);
+ xmlAddChild(disk_node, new_node);
+ }
+ }
+ }
+
+ if (xmlNodeDump(xml_buf, NULL, disk_node, 0, 0) < 0) {
+ vshError(NULL, "%s", _("Failed to create XML"));
+ goto error;
+ }
+
+ goto cleanup;
+
+cleanup:
+ VIR_FREE(device_type);
+ VIR_FREE(disk_type);
+ if (xml_buf) {
+ int len = xmlBufferLength(xml_buf);
+ if (VIR_ALLOC_N(ret, len + 1) < 0) {
+ virReportOOMError();
+ return NULL;
+ }
+ memcpy(ret, (char *)xmlBufferContent(xml_buf), len);
+ ret[len] = '\0';
+ xmlBufferFree(xml_buf);
+ }
+ return ret;
+
+error:
+ xmlBufferFree(xml_buf);
+ xml_buf = NULL;
+ goto cleanup;
+}
+
+
+/*
+ * "detach-disk" command
+ */
+static const vshCmdInfo info_detach_disk[] = {
+ {"help", N_("detach disk device")},
+ {"desc", N_("Detach disk device.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_detach_disk[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"target", VSH_OT_DATA, VSH_OFLAG_REQ, N_("target of disk device")},
+ {"persistent", VSH_OT_ALIAS, 0, "config"},
+ {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDetachDisk(vshControl *ctl, const vshCmd *cmd)
+{
+ char *disk_xml = NULL;
+ virDomainPtr dom = NULL;
+ const char *target = NULL;
+ char *doc = NULL;
+ int ret;
+ bool functionReturn = false;
+ unsigned int flags;
+ xmlNodePtr disk_node = NULL;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ goto cleanup;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ goto cleanup;
+
+ if (vshCommandOptString(cmd, "target", &target) <= 0)
+ goto cleanup;
+
+ doc = virDomainGetXMLDesc(dom, 0);
+ if (!doc)
+ goto cleanup;
+
+ if (!(disk_node = vshFindDisk(doc, target, VSH_FIND_DISK_NORMAL)))
+ goto cleanup;
+
+ if (!(disk_xml = vshPrepareDiskXML(disk_node, NULL, NULL,
+ VSH_PREPARE_DISK_XML_NONE)))
+ goto cleanup;
+
+ if (vshCommandOptBool(cmd, "config")) {
+ flags = VIR_DOMAIN_AFFECT_CONFIG;
+ if (virDomainIsActive(dom) == 1)
+ flags |= VIR_DOMAIN_AFFECT_LIVE;
+ ret = virDomainDetachDeviceFlags(dom,
+ disk_xml,
+ flags);
+ } else {
+ ret = virDomainDetachDevice(dom, disk_xml);
+ }
+
+ if (ret != 0) {
+ vshError(ctl, "%s", _("Failed to detach disk"));
+ } else {
+ vshPrint(ctl, "%s", _("Disk detached successfully\n"));
+ functionReturn = true;
+ }
+
+ cleanup:
+ xmlFreeNode(disk_node);
+ VIR_FREE(disk_xml);
+ VIR_FREE(doc);
+ if (dom)
+ virDomainFree(dom);
+ return functionReturn;
+}
+
+/*
+ * "edit" command
+ */
+static const vshCmdInfo info_edit[] = {
+ {"help", N_("edit XML configuration for a domain")},
+ {"desc", N_("Edit the XML configuration for a domain.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_edit[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdEdit(vshControl *ctl, const vshCmd *cmd)
+{
+ bool ret = false;
+ virDomainPtr dom = NULL;
+ virDomainPtr dom_edited = NULL;
+ unsigned int flags = VIR_DOMAIN_XML_SECURE | VIR_DOMAIN_XML_INACTIVE;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ goto cleanup;
+
+ dom = vshCommandOptDomain(ctl, cmd, NULL);
+ if (dom == NULL)
+ goto cleanup;
+
+#define EDIT_GET_XML virDomainGetXMLDesc(dom, flags)
+#define EDIT_NOT_CHANGED \
+ vshPrint(ctl, _("Domain %s XML configuration not changed.\n"), \
+ virDomainGetName(dom)); \
+ ret = true; goto edit_cleanup;
+#define EDIT_DEFINE \
+ (dom_edited = virDomainDefineXML(ctl->conn, doc_edited))
+#define EDIT_FREE \
+ if (dom_edited) \
+ virDomainFree(dom_edited);
+#include "virsh-edit.c"
+
+ vshPrint(ctl, _("Domain %s XML configuration edited.\n"),
+ virDomainGetName(dom_edited));
+
+ ret = true;
+
+ cleanup:
+ if (dom)
+ virDomainFree(dom);
+ if (dom_edited)
+ virDomainFree(dom_edited);
+
+ return ret;
+}
+
+/*
+ * "change-media" command
+ */
+static const vshCmdInfo info_change_media[] = {
+ {"help", N_("Change media of CD or floppy drive")},
+ {"desc", N_("Change media of CD or floppy drive.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_change_media[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"path", VSH_OT_DATA, VSH_OFLAG_REQ, N_("Fully-qualified path or "
+ "target of disk device")},
+ {"source", VSH_OT_DATA, 0, N_("source of the media")},
+ {"eject", VSH_OT_BOOL, 0, N_("Eject the media")},
+ {"insert", VSH_OT_BOOL, 0, N_("Insert the media")},
+ {"update", VSH_OT_BOOL, 0, N_("Update the media")},
+ {"current", VSH_OT_BOOL, 0, N_("can be either or both of --live and --config, "
+ "depends on implementation of hypervisor driver")},
+ {"live", VSH_OT_BOOL, 0, N_("alter live configuration of running domain")},
+ {"config", VSH_OT_BOOL, 0, N_("alter persistent configuration, effect observed on next boot")},
+ {"force", VSH_OT_BOOL, 0, N_("force media insertion")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdChangeMedia(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom = NULL;
+ const char *source = NULL;
+ const char *path = NULL;
+ const char *doc = NULL;
+ xmlNodePtr disk_node = NULL;
+ const char *disk_xml = NULL;
+ int flags = 0;
+ bool config, live, current, force = false;
+ bool eject, insert, update = false;
+ bool ret = false;
+ int prepare_type = 0;
+ const char *action = NULL;
+
+ config = vshCommandOptBool(cmd, "config");
+ live = vshCommandOptBool(cmd, "live");
+ current = vshCommandOptBool(cmd, "current");
+ force = vshCommandOptBool(cmd, "force");
+ eject = vshCommandOptBool(cmd, "eject");
+ insert = vshCommandOptBool(cmd, "insert");
+ update = vshCommandOptBool(cmd, "update");
+
+ if (eject + insert + update > 1) {
+ vshError(ctl, "%s", _("--eject, --insert, and --update must be specified "
+ "exclusively."));
+ return false;
+ }
+
+ if (eject) {
+ prepare_type = VSH_PREPARE_DISK_XML_EJECT;
+ action = "eject";
+ }
+
+ if (insert) {
+ prepare_type = VSH_PREPARE_DISK_XML_INSERT;
+ action = "insert";
+ }
+
+ if (update || (!eject && !insert)) {
+ prepare_type = VSH_PREPARE_DISK_XML_UPDATE;
+ action = "update";
+ }
+
+ if (current) {
+ if (live || config) {
+ vshError(ctl, "%s", _("--current must be specified exclusively"));
+ return false;
+ }
+ flags = VIR_DOMAIN_AFFECT_CURRENT;
+ } else {
+ if (config)
+ flags |= VIR_DOMAIN_AFFECT_CONFIG;
+ if (live)
+ flags |= VIR_DOMAIN_AFFECT_LIVE;
+ }
+
+ if (force)
+ flags |= VIR_DOMAIN_DEVICE_MODIFY_FORCE;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ goto cleanup;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ goto cleanup;
+
+ if (vshCommandOptString(cmd, "path", &path) <= 0)
+ goto cleanup;
+
+ if (vshCommandOptString(cmd, "source", &source) < 0)
+ goto cleanup;
+
+ if (insert && !source) {
+ vshError(ctl, "%s", _("No disk source specified for inserting"));
+ goto cleanup;
+ }
+
+ if (flags & VIR_DOMAIN_AFFECT_CONFIG)
+ doc = virDomainGetXMLDesc(dom, VIR_DOMAIN_XML_INACTIVE);
+ else
+ doc = virDomainGetXMLDesc(dom, 0);
+ if (!doc)
+ goto cleanup;
+
+ if (!(disk_node = vshFindDisk(doc, path, VSH_FIND_DISK_CHANGEABLE)))
+ goto cleanup;
+
+ if (!(disk_xml = vshPrepareDiskXML(disk_node, source, path, prepare_type)))
+ goto cleanup;
+
+ if (virDomainUpdateDeviceFlags(dom, disk_xml, flags) != 0) {
+ vshError(ctl, _("Failed to complete action %s on media"), action);
+ goto cleanup;
+ }
+
+ vshPrint(ctl, _("succeeded to complete action %s on media\n"), action);
+ ret = true;
+
+cleanup:
+ VIR_FREE(doc);
+ xmlFreeNode(disk_node);
+ VIR_FREE(disk_xml);
+ if (dom)
+ virDomainFree(dom);
+ return ret;
+}
diff --git a/tools/virsh.c b/tools/virsh.c
index a7a8a6b3a998c6be6977be3e476eae28c9062c58..af6bce00a408cac61162d6d2ca76f12cf53acc3f 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -724,6 +724,14 @@ vshAskReedit(vshControl *ctl, const char *msg ATTRIBUTE_UNUSED)
}
#endif /* WIN32 */
+static int vshStreamSink(virStreamPtr st ATTRIBUTE_UNUSED,
+ const char *bytes, size_t nbytes, void *opaque)
+{
+ int *fd = opaque;
+
+ return safewrite(*fd, bytes, nbytes);
+}
+
/* ---------------
* Commands
* ---------------
@@ -783,55 +791,6 @@ cmdHelp(vshControl *ctl, const vshCmd *cmd)
}
}
-/*
- * "autostart" command
- */
-static const vshCmdInfo info_autostart[] = {
- {"help", N_("autostart a domain")},
- {"desc",
- N_("Configure a domain to be automatically started at boot.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_autostart[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {"disable", VSH_OT_BOOL, 0, N_("disable autostarting")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdAutostart(vshControl *ctl, const vshCmd *cmd)
-{
- virDomainPtr dom;
- const char *name;
- int autostart;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!(dom = vshCommandOptDomain(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);
- virDomainFree(dom);
- return false;
- }
-
- if (autostart)
- vshPrint(ctl, _("Domain %s marked as autostarted\n"), name);
- else
- vshPrint(ctl, _("Domain %s unmarked as autostarted\n"), name);
-
- virDomainFree(dom);
- return true;
-}
-
/*
* "connect" command
*/
@@ -884,2078 +843,1304 @@ cmdConnect(vshControl *ctl, const vshCmd *cmd)
return !!ctl->conn;
}
-#ifndef WIN32
-
/*
- * "console" command
+ * "freecell" command
*/
-static const vshCmdInfo info_console[] = {
- {"help", N_("connect to the guest console")},
- {"desc",
- N_("Connect the virtual serial console for the guest")},
+static const vshCmdInfo info_freecell[] = {
+ {"help", N_("NUMA free memory")},
+ {"desc", N_("display available free memory for the NUMA cell.")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_console[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {"devname", VSH_OT_STRING, 0, N_("character device name")},
- {"force", VSH_OT_BOOL, 0,
- N_("force console connection (disconnect already connected sessions)")},
- {"safe", VSH_OT_BOOL, 0,
- N_("only connect if safe console handling is supported")},
+static const vshCmdOptDef opts_freecell[] = {
+ {"cellno", VSH_OT_INT, 0, N_("NUMA cell number")},
+ {"all", VSH_OT_BOOL, 0, N_("show free memory for all NUMA cells")},
{NULL, 0, 0, NULL}
};
static bool
-cmdRunConsole(vshControl *ctl, virDomainPtr dom,
- const char *name,
- unsigned int flags)
+cmdFreecell(vshControl *ctl, const vshCmd *cmd)
{
- bool ret = false;
- int state;
+ bool func_ret = false;
+ int ret;
+ int cell = -1, cell_given;
+ unsigned long long memory;
+ xmlNodePtr *nodes = NULL;
+ unsigned long nodes_cnt;
+ unsigned long *nodes_id = NULL;
+ unsigned long long *nodes_free = NULL;
+ int all_given;
+ int i;
+ char *cap_xml = NULL;
+ xmlDocPtr xml = NULL;
+ xmlXPathContextPtr ctxt = NULL;
- if ((state = vshDomainState(ctl, dom, NULL)) < 0) {
- vshError(ctl, "%s", _("Unable to get domain status"));
- goto cleanup;
- }
- if (state == VIR_DOMAIN_SHUTOFF) {
- vshError(ctl, "%s", _("The domain is not running"));
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if ( (cell_given = vshCommandOptInt(cmd, "cellno", &cell)) < 0) {
+ vshError(ctl, "%s", _("cell number has to be a number"));
goto cleanup;
}
+ all_given = vshCommandOptBool(cmd, "all");
- if (!isatty(STDIN_FILENO)) {
- vshError(ctl, "%s", _("Cannot run interactive console without a controlling TTY"));
+ if (all_given && cell_given) {
+ vshError(ctl, "%s", _("--cellno and --all are mutually exclusive. "
+ "Please choose only one."));
goto cleanup;
}
- vshPrintExtra(ctl, _("Connected to domain %s\n"), virDomainGetName(dom));
- vshPrintExtra(ctl, _("Escape character is %s\n"), ctl->escapeChar);
- fflush(stdout);
- if (vshRunConsole(dom, name, ctl->escapeChar, flags) == 0)
- ret = true;
+ if (all_given) {
+ cap_xml = virConnectGetCapabilities(ctl->conn);
+ if (!cap_xml) {
+ vshError(ctl, "%s", _("unable to get node capabilities"));
+ goto cleanup;
+ }
- cleanup:
+ xml = virXMLParseStringCtxt(cap_xml, _("(capabilities)"), &ctxt);
+ if (!xml) {
+ vshError(ctl, "%s", _("unable to get node capabilities"));
+ goto cleanup;
+ }
+ nodes_cnt = virXPathNodeSet("/capabilities/host/topology/cells/cell",
+ ctxt, &nodes);
- return ret;
+ if (nodes_cnt == -1) {
+ vshError(ctl, "%s", _("could not get information about "
+ "NUMA topology"));
+ goto cleanup;
+ }
+
+ nodes_free = vshCalloc(ctl, nodes_cnt, sizeof(*nodes_free));
+ nodes_id = vshCalloc(ctl, nodes_cnt, sizeof(*nodes_id));
+
+ for (i = 0; i < nodes_cnt; i++) {
+ unsigned long id;
+ char *val = virXMLPropString(nodes[i], "id");
+ if (virStrToLong_ul(val, NULL, 10, &id)) {
+ vshError(ctl, "%s", _("conversion from string failed"));
+ VIR_FREE(val);
+ goto cleanup;
+ }
+ VIR_FREE(val);
+ nodes_id[i]=id;
+ ret = virNodeGetCellsFreeMemory(ctl->conn, &(nodes_free[i]), id, 1);
+ if (ret != 1) {
+ vshError(ctl, _("failed to get free memory for NUMA node "
+ "number: %lu"), id);
+ goto cleanup;
+ }
+ }
+
+ memory = 0;
+ for (cell = 0; cell < nodes_cnt; cell++) {
+ vshPrint(ctl, "%5lu: %10llu KiB\n", nodes_id[cell],
+ (nodes_free[cell]/1024));
+ memory += nodes_free[cell];
+ }
+
+ vshPrintExtra(ctl, "--------------------\n");
+ vshPrintExtra(ctl, "%5s: %10llu KiB\n", _("Total"), memory/1024);
+ } else {
+ if (!cell_given) {
+ memory = virNodeGetFreeMemory(ctl->conn);
+ if (memory == 0)
+ goto cleanup;
+ } else {
+ ret = virNodeGetCellsFreeMemory(ctl->conn, &memory, cell, 1);
+ if (ret != 1)
+ goto cleanup;
+ }
+
+ if (cell == -1)
+ vshPrint(ctl, "%s: %llu KiB\n", _("Total"), (memory/1024));
+ else
+ vshPrint(ctl, "%d: %llu KiB\n", cell, (memory/1024));
+ }
+
+ func_ret = true;
+
+cleanup:
+ xmlXPathFreeContext(ctxt);
+ xmlFreeDoc(xml);
+ VIR_FREE(nodes);
+ VIR_FREE(nodes_free);
+ VIR_FREE(nodes_id);
+ VIR_FREE(cap_xml);
+ return func_ret;
}
+/*
+ * "nodeinfo" command
+ */
+static const vshCmdInfo info_nodeinfo[] = {
+ {"help", N_("node information")},
+ {"desc", N_("Returns basic information about the node.")},
+ {NULL, NULL}
+};
+
static bool
-cmdConsole(vshControl *ctl, const vshCmd *cmd)
+cmdNodeinfo(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
- virDomainPtr dom;
- bool ret = false;
- bool force = vshCommandOptBool(cmd, "force");
- bool safe = vshCommandOptBool(cmd, "safe");
- unsigned int flags = 0;
- const char *name = NULL;
+ virNodeInfo info;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ if (virNodeGetInfo(ctl->conn, &info) < 0) {
+ vshError(ctl, "%s", _("failed to get node information"));
return false;
-
- if (vshCommandOptString(cmd, "devname", &name) < 0) {
- vshError(ctl, "%s", _("Invalid devname"));
- goto cleanup;
}
+ vshPrint(ctl, "%-20s %s\n", _("CPU model:"), info.model);
+ vshPrint(ctl, "%-20s %d\n", _("CPU(s):"), info.cpus);
+ vshPrint(ctl, "%-20s %d MHz\n", _("CPU frequency:"), info.mhz);
+ vshPrint(ctl, "%-20s %d\n", _("CPU socket(s):"), info.sockets);
+ vshPrint(ctl, "%-20s %d\n", _("Core(s) per socket:"), info.cores);
+ vshPrint(ctl, "%-20s %d\n", _("Thread(s) per core:"), info.threads);
+ vshPrint(ctl, "%-20s %d\n", _("NUMA cell(s):"), info.nodes);
+ vshPrint(ctl, "%-20s %lu KiB\n", _("Memory size:"), info.memory);
- if (force)
- flags |= VIR_DOMAIN_CONSOLE_FORCE;
- if (safe)
- flags |= VIR_DOMAIN_CONSOLE_SAFE;
-
- ret = cmdRunConsole(ctl, dom, name, flags);
-
-cleanup:
- virDomainFree(dom);
- return ret;
+ return true;
}
-#endif /* WIN32 */
-
/*
- * "desc" command for managing domain description and title
+ * "nodecpustats" command
*/
-static const vshCmdInfo info_desc[] = {
- {"help", N_("show or set domain's description or title")},
- {"desc", N_("Allows to show or modify description or title of a domain.")},
+static const vshCmdInfo info_nodecpustats[] = {
+ {"help", N_("Prints cpu stats of the node.")},
+ {"desc", N_("Returns cpu stats of the node, in nanoseconds.")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_desc[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {"live", VSH_OT_BOOL, 0, N_("modify/get running state")},
- {"config", VSH_OT_BOOL, 0, N_("modify/get persistent configuration")},
- {"current", VSH_OT_BOOL, 0, N_("modify/get current state configuration")},
- {"title", VSH_OT_BOOL, 0, N_("modify/get the title instead of description")},
- {"edit", VSH_OT_BOOL, 0, N_("open an editor to modify the description")},
- {"new-desc", VSH_OT_ARGV, 0, N_("message")},
+static const vshCmdOptDef opts_node_cpustats[] = {
+ {"cpu", VSH_OT_INT, 0, N_("prints specified cpu statistics only.")},
+ {"percent", VSH_OT_BOOL, 0, N_("prints by percentage during 1 second.")},
{NULL, 0, 0, NULL}
};
static bool
-cmdDesc(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
+cmdNodeCpuStats(vshControl *ctl, const vshCmd *cmd)
{
- virDomainPtr dom;
- bool config = vshCommandOptBool(cmd, "config");
- bool live = vshCommandOptBool(cmd, "live");
- bool current = vshCommandOptBool(cmd, "current");
-
- bool title = vshCommandOptBool(cmd, "title");
- bool edit = vshCommandOptBool(cmd, "edit");
-
- int state;
- int type;
- char *desc = NULL;
- char *desc_edited = NULL;
- char *tmp = NULL;
- char *tmpstr;
- const vshCmdOpt *opt = NULL;
- virBuffer buf = VIR_BUFFER_INITIALIZER;
- bool pad = false;
+ int i, j;
+ bool flag_utilization = false;
+ bool flag_percent = vshCommandOptBool(cmd, "percent");
+ int cpuNum = VIR_NODE_CPU_STATS_ALL_CPUS;
+ virNodeCPUStatsPtr params;
+ int nparams = 0;
bool ret = false;
- unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
-
- if (current) {
- if (live || config) {
- vshError(ctl, "%s", _("--current must be specified exclusively"));
- return false;
- }
- flags = VIR_DOMAIN_AFFECT_CURRENT;
- } else {
- if (config)
- flags |= VIR_DOMAIN_AFFECT_CONFIG;
- if (live)
- flags |= VIR_DOMAIN_AFFECT_LIVE;
- }
+ struct cpu_stats {
+ unsigned long long user;
+ unsigned long long sys;
+ unsigned long long idle;
+ unsigned long long iowait;
+ unsigned long long util;
+ } cpu_stats[2];
+ double user_time, sys_time, idle_time, iowait_time, total_time;
+ double usage;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ if (vshCommandOptInt(cmd, "cpu", &cpuNum) < 0) {
+ vshError(ctl, "%s", _("Invalid value of cpuNum"));
return false;
-
- if ((state = vshDomainState(ctl, dom, NULL)) < 0)
- goto cleanup;
-
- while ((opt = vshCommandOptArgv(cmd, opt))) {
- if (pad)
- virBufferAddChar(&buf, ' ');
- pad = true;
- virBufferAdd(&buf, opt->data, -1);
}
- if (title)
- type = VIR_DOMAIN_METADATA_TITLE;
- else
- type = VIR_DOMAIN_METADATA_DESCRIPTION;
-
- if (virBufferError(&buf)) {
- vshPrint(ctl, "%s", _("Failed to collect new description/title"));
- goto cleanup;
+ if (virNodeGetCPUStats(ctl->conn, cpuNum, NULL, &nparams, 0) != 0) {
+ vshError(ctl, "%s",
+ _("Unable to get number of cpu stats"));
+ return false;
+ }
+ if (nparams == 0) {
+ /* nothing to output */
+ return true;
}
- desc = virBufferContentAndReset(&buf);
- if (edit || desc) {
- if (!desc) {
- desc = vshGetDomainDescription(ctl, dom, title,
- config?VIR_DOMAIN_XML_INACTIVE:0);
- if (!desc)
- goto cleanup;
- }
+ memset(cpu_stats, 0, sizeof(cpu_stats));
+ params = vshCalloc(ctl, nparams, sizeof(*params));
- if (edit) {
- /* Create and open the temporary file. */
- if (!(tmp = editWriteToTempFile(ctl, desc)))
- goto cleanup;
+ for (i = 0; i < 2; i++) {
+ if (i > 0)
+ sleep(1);
- /* Start the editor. */
- if (editFile(ctl, tmp) == -1)
- goto cleanup;
+ if (virNodeGetCPUStats(ctl->conn, cpuNum, params, &nparams, 0) != 0) {
+ vshError(ctl, "%s", _("Unable to get node cpu stats"));
+ goto cleanup;
+ }
- /* Read back the edited file. */
- if (!(desc_edited = editReadBackFile(ctl, tmp)))
- goto cleanup;
+ for (j = 0; j < nparams; j++) {
+ unsigned long long value = params[j].value;
- /* strip a possible newline at the end of file; some
- * editors enforce a newline, this makes editing the title
- * more convenient */
- if (title &&
- (tmpstr = strrchr(desc_edited, '\n')) &&
- *(tmpstr+1) == '\0')
- *tmpstr = '\0';
-
- /* Compare original XML with edited. Has it changed at all? */
- if (STREQ(desc, desc_edited)) {
- vshPrint(ctl, _("Domain description not changed.\n"));
- ret = true;
- goto cleanup;
+ if (STREQ(params[j].field, VIR_NODE_CPU_STATS_KERNEL)) {
+ cpu_stats[i].sys = value;
+ } else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_USER)) {
+ cpu_stats[i].user = value;
+ } else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_IDLE)) {
+ cpu_stats[i].idle = value;
+ } else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_IOWAIT)) {
+ cpu_stats[i].iowait = value;
+ } else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_UTILIZATION)) {
+ cpu_stats[i].util = value;
+ flag_utilization = true;
}
-
- VIR_FREE(desc);
- desc = desc_edited;
- desc_edited = NULL;
}
- if (virDomainSetMetadata(dom, type, desc, NULL, NULL, flags) < 0) {
- vshError(ctl, "%s",
- _("Failed to set new domain description"));
- goto cleanup;
+ if (flag_utilization || !flag_percent)
+ break;
+ }
+
+ if (!flag_percent) {
+ if (!flag_utilization) {
+ vshPrint(ctl, "%-15s %20llu\n", _("user:"), cpu_stats[0].user);
+ vshPrint(ctl, "%-15s %20llu\n", _("system:"), cpu_stats[0].sys);
+ vshPrint(ctl, "%-15s %20llu\n", _("idle:"), cpu_stats[0].idle);
+ vshPrint(ctl, "%-15s %20llu\n", _("iowait:"), cpu_stats[0].iowait);
}
- vshPrint(ctl, "%s", _("Domain description updated successfully"));
} else {
- desc = vshGetDomainDescription(ctl, dom, title,
- config?VIR_DOMAIN_XML_INACTIVE:0);
- if (!desc)
- goto cleanup;
+ if (flag_utilization) {
+ usage = cpu_stats[0].util;
- if (strlen(desc) > 0)
- vshPrint(ctl, "%s", desc);
- else
- vshPrint(ctl, _("No description for domain: %s"),
- virDomainGetName(dom));
+ vshPrint(ctl, "%-15s %5.1lf%%\n", _("usage:"), usage);
+ vshPrint(ctl, "%-15s %5.1lf%%\n", _("idle:"), 100 - usage);
+ } else {
+ user_time = cpu_stats[1].user - cpu_stats[0].user;
+ sys_time = cpu_stats[1].sys - cpu_stats[0].sys;
+ idle_time = cpu_stats[1].idle - cpu_stats[0].idle;
+ iowait_time = cpu_stats[1].iowait - cpu_stats[0].iowait;
+ total_time = user_time + sys_time + idle_time + iowait_time;
+
+ usage = (user_time + sys_time) / total_time * 100;
+
+ vshPrint(ctl, "%-15s %5.1lf%%\n",
+ _("usage:"), usage);
+ vshPrint(ctl, "%-15s %5.1lf%%\n",
+ _("user:"), user_time / total_time * 100);
+ vshPrint(ctl, "%-15s %5.1lf%%\n",
+ _("system:"), sys_time / total_time * 100);
+ vshPrint(ctl, "%-15s %5.1lf%%\n",
+ _("idle:"), idle_time / total_time * 100);
+ vshPrint(ctl, "%-15s %5.1lf%%\n",
+ _("iowait:"), iowait_time / total_time * 100);
+ }
}
ret = true;
-cleanup:
- VIR_FREE(desc_edited);
- VIR_FREE(desc);
- if (tmp) {
- unlink(tmp);
- VIR_FREE(tmp);
- }
- if (dom)
- virDomainFree(dom);
+
+ cleanup:
+ VIR_FREE(params);
return ret;
}
-/* "domif-setlink" command
+/*
+ * "nodememstats" command
*/
-static const vshCmdInfo info_domif_setlink[] = {
- {"help", N_("set link state of a virtual interface")},
- {"desc", N_("Set link state of a domain's virtual interface. This command wraps usage of update-device command.")},
- {NULL,NULL}
+static const vshCmdInfo info_nodememstats[] = {
+ {"help", N_("Prints memory stats of the node.")},
+ {"desc", N_("Returns memory stats of the node, in kilobytes.")},
+ {NULL, NULL}
};
-static const vshCmdOptDef opts_domif_setlink[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface device (MAC Address)")},
- {"state", VSH_OT_DATA, VSH_OFLAG_REQ, N_("new state of the device")},
- {"persistent", VSH_OT_ALIAS, 0, "config"},
- {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
+static const vshCmdOptDef opts_node_memstats[] = {
+ {"cell", VSH_OT_INT, 0, N_("prints specified cell statistics only.")},
{NULL, 0, 0, NULL}
};
static bool
-cmdDomIfSetLink(vshControl *ctl, const vshCmd *cmd)
+cmdNodeMemStats(vshControl *ctl, const vshCmd *cmd)
{
- virDomainPtr dom;
- const char *iface;
- const char *state;
- const char *value;
- const char *desc;
- virMacAddr macaddr;
- const char *element;
- const char *attr;
- bool config;
+ int nparams = 0;
+ unsigned int i = 0;
+ int cellNum = VIR_NODE_MEMORY_STATS_ALL_CELLS;
+ virNodeMemoryStatsPtr params = NULL;
bool ret = false;
- unsigned int flags = 0;
- int i;
- xmlDocPtr xml = NULL;
- xmlXPathContextPtr ctxt = NULL;
- xmlXPathObjectPtr obj = NULL;
- xmlNodePtr cur = NULL;
- xmlBufferPtr xml_buf = NULL;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ if (vshCommandOptInt(cmd, "cell", &cellNum) < 0) {
+ vshError(ctl, "%s", _("Invalid value of cellNum"));
return false;
-
- if (vshCommandOptString(cmd, "interface", &iface) <= 0)
- goto cleanup;
-
- if (vshCommandOptString(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;
}
- /* get persistent or live description of network device */
- desc = virDomainGetXMLDesc(dom, config ? VIR_DOMAIN_XML_INACTIVE : 0);
- if (desc == NULL) {
- vshError(ctl, _("Failed to get domain description xml"));
+ /* get the number of memory parameters */
+ if (virNodeGetMemoryStats(ctl->conn, cellNum, NULL, &nparams, 0) != 0) {
+ vshError(ctl, "%s",
+ _("Unable to get number of memory stats"));
goto cleanup;
}
- if (config)
- flags = VIR_DOMAIN_AFFECT_CONFIG;
- else
- flags = VIR_DOMAIN_AFFECT_LIVE;
-
- if (virDomainIsActive(dom) == 0)
- flags = VIR_DOMAIN_AFFECT_CONFIG;
-
- /* extract current network device description */
- xml = virXMLParseStringCtxt(desc, _("(domain_definition)"), &ctxt);
- VIR_FREE(desc);
- if (!xml) {
- vshError(ctl, _("Failed to parse domain description xml"));
+ if (nparams == 0) {
+ /* nothing to output */
+ ret = true;
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"));
+ /* now go get all the memory parameters */
+ params = vshCalloc(ctl, nparams, sizeof(*params));
+ if (virNodeGetMemoryStats(ctl->conn, cellNum, params, &nparams, 0) != 0) {
+ vshError(ctl, "%s", _("Unable to get memory stats"));
goto cleanup;
}
- if (virMacAddrParse(iface, &macaddr) == 0) {
- element = "mac";
- attr = "address";
- } else {
- element = "target";
- attr = "dev";
- }
+ for (i = 0; i < nparams; i++)
+ vshPrint(ctl, "%-7s: %20llu KiB\n", params[i].field, params[i].value);
- /* find interface with matching mac addr */
- for (i = 0; i < obj->nodesetval->nodeNr; i++) {
- cur = obj->nodesetval->nodeTab[i]->children;
+ ret = true;
- while (cur) {
- if (cur->type == XML_ELEMENT_NODE &&
- xmlStrEqual(cur->name, BAD_CAST element)) {
- value = virXMLPropString(cur, attr);
+ cleanup:
+ VIR_FREE(params);
+ return ret;
+}
- if (STRCASEEQ(value, iface)) {
- VIR_FREE(value);
- goto hit;
- }
- VIR_FREE(value);
- }
- cur = cur->next;
- }
- }
+/*
+ * "nodesuspend" command
+ */
+static const vshCmdInfo info_nodesuspend[] = {
+ {"help", N_("suspend the host node for a given time duration")},
+ {"desc", N_("Suspend the host node for a given time duration "
+ "and attempt to resume thereafter.")},
+ {NULL, NULL}
+};
- vshError(ctl, _("interface (%s: %s) not found"), element, iface);
- goto cleanup;
+static const vshCmdOptDef opts_node_suspend[] = {
+ {"target", VSH_OT_DATA, VSH_OFLAG_REQ, N_("mem(Suspend-to-RAM), "
+ "disk(Suspend-to-Disk), hybrid(Hybrid-Suspend)")},
+ {"duration", VSH_OT_INT, VSH_OFLAG_REQ, N_("Suspend duration in seconds")},
+ {"flags", VSH_OT_INT, VSH_OFLAG_NONE, N_("Suspend flags, 0 for default")},
+ {NULL, 0, 0, NULL}
+};
-hit:
- /* find and modify/add link state node */
- /* try to find element */
- cur = obj->nodesetval->nodeTab[i]->children;
+static bool
+cmdNodeSuspend(vshControl *ctl, const vshCmd *cmd)
+{
+ const char *target = NULL;
+ unsigned int suspendTarget;
+ long long duration;
+ unsigned int flags = 0;
- while (cur) {
- if (cur->type == XML_ELEMENT_NODE &&
- xmlStrEqual(cur->name, BAD_CAST "link")) {
- /* found, just modify the property */
- xmlSetProp(cur, BAD_CAST "state", BAD_CAST state);
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
- break;
- }
- cur = cur->next;
+ if (vshCommandOptString(cmd, "target", &target) < 0) {
+ vshError(ctl, _("Invalid target argument"));
+ return false;
}
- if (!cur) {
- /* element not found, add one */
- cur = xmlNewChild(obj->nodesetval->nodeTab[i],
- NULL,
- BAD_CAST "link",
- NULL);
- if (!cur)
- goto cleanup;
+ if (vshCommandOptLongLong(cmd, "duration", &duration) < 0) {
+ vshError(ctl, _("Invalid duration argument"));
+ return false;
+ }
- if (xmlNewProp(cur, BAD_CAST "state", BAD_CAST state) == NULL)
- goto cleanup;
+ if (vshCommandOptUInt(cmd, "flags", &flags) < 0) {
+ vshError(ctl, _("Invalid flags argument"));
+ return false;
}
- xml_buf = xmlBufferCreate();
- if (!xml_buf) {
- vshError(ctl, _("Failed to allocate memory"));
- goto cleanup;
+ if (STREQ(target, "mem"))
+ suspendTarget = VIR_NODE_SUSPEND_TARGET_MEM;
+ else if (STREQ(target, "disk"))
+ suspendTarget = VIR_NODE_SUSPEND_TARGET_DISK;
+ else if (STREQ(target, "hybrid"))
+ suspendTarget = VIR_NODE_SUSPEND_TARGET_HYBRID;
+ else {
+ vshError(ctl, "%s", _("Invalid target"));
+ return false;
}
- if (xmlNodeDump(xml_buf, xml, obj->nodesetval->nodeTab[i], 0, 0) < 0 ) {
- vshError(ctl, _("Failed to create XML"));
- goto cleanup;
+ if (duration <= 0) {
+ vshError(ctl, "%s", _("Invalid duration"));
+ return false;
}
- if (virDomainUpdateDeviceFlags(dom, (char *)xmlBufferContent(xml_buf), flags) < 0) {
- vshError(ctl, _("Failed to update interface link state"));
- goto cleanup;
- } else {
- vshPrint(ctl, "%s", _("Device updated successfully\n"));
- ret = true;
+ if (virNodeSuspendForDuration(ctl->conn, suspendTarget, duration,
+ flags) < 0) {
+ vshError(ctl, "%s", _("The host was not suspended"));
+ return false;
}
+ return true;
+}
-cleanup:
- xmlXPathFreeObject(obj);
- xmlXPathFreeContext(ctxt);
- xmlFreeDoc(xml);
- xmlBufferFree(xml_buf);
- if (dom)
- virDomainFree(dom);
+/*
+ * "capabilities" command
+ */
+static const vshCmdInfo info_capabilities[] = {
+ {"help", N_("capabilities")},
+ {"desc", N_("Returns capabilities of hypervisor/driver.")},
+ {NULL, NULL}
+};
- return ret;
+static bool
+cmdCapabilities(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
+{
+ char *caps;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if ((caps = virConnectGetCapabilities(ctl->conn)) == NULL) {
+ vshError(ctl, "%s", _("failed to get capabilities"));
+ return false;
+ }
+ vshPrint(ctl, "%s\n", caps);
+ VIR_FREE(caps);
+
+ return true;
}
-/* "domiftune" command
+/*
+ * "net-autostart" command
*/
-static const vshCmdInfo info_domiftune[] = {
- {"help", N_("get/set parameters of a virtual interface")},
- {"desc", N_("Get/set parameters of a domain's virtual interface.")},
- {NULL,NULL}
+static const vshCmdInfo info_network_autostart[] = {
+ {"help", N_("autostart a network")},
+ {"desc",
+ N_("Configure a network to be automatically started at boot.")},
+ {NULL, NULL}
};
-static const vshCmdOptDef opts_domiftune[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface device (MAC Address)")},
- {"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")},
- {"config", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("affect next boot")},
- {"live", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("affect running domain")},
- {"current", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("affect current domain")},
+static const vshCmdOptDef opts_network_autostart[] = {
+ {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
+ {"disable", VSH_OT_BOOL, 0, N_("disable autostarting")},
{NULL, 0, 0, NULL}
};
static bool
-cmdDomIftune(vshControl *ctl, const vshCmd *cmd)
+cmdNetworkAutostart(vshControl *ctl, const vshCmd *cmd)
{
- virDomainPtr dom;
- const char *name = NULL, *device = NULL,
- *inboundStr = NULL, *outboundStr = NULL;
- unsigned int flags = 0;
- int nparams = 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;
- int i;
-
- if (current) {
- if (live || config) {
- vshError(ctl, "%s", _("--current must be specified exclusively"));
- return false;
- }
- flags = VIR_DOMAIN_AFFECT_CURRENT;
- } else {
- if (config)
- flags |= VIR_DOMAIN_AFFECT_CONFIG;
- if (live)
- flags |= VIR_DOMAIN_AFFECT_LIVE;
- }
+ virNetworkPtr network;
+ const char *name;
+ int autostart;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+ if (!(network = vshCommandOptNetwork(ctl, cmd, &name)))
return false;
- if (vshCommandOptString(cmd, "interface", &device) <= 0) {
- virDomainFree(dom);
- return false;
- }
-
- if (vshCommandOptString(cmd, "inbound", &inboundStr) < 0 ||
- vshCommandOptString(cmd, "outbound", &outboundStr) < 0) {
- vshError(ctl, "missing argument");
- goto cleanup;
- }
-
- memset(&inbound, 0, sizeof(inbound));
- memset(&outbound, 0, sizeof(outbound));
-
- if (inboundStr) {
- 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;
- }
- nparams++; /* average */
- if (inbound.peak) nparams++;
- if (inbound.burst) nparams++;
- }
- if (outboundStr) {
- 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;
- }
- nparams++; /* average */
- if (outbound.peak) nparams++;
- if (outbound.burst) nparams++;
- }
-
- 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 (!params) {
- virReportOOMError();
- goto cleanup;
- }
- 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 {
- /* set the interface parameters */
- params = vshCalloc(ctl, nparams, sizeof(*params));
- if (!params) {
- virReportOOMError();
- goto cleanup;
- }
-
- for (i = 0; i < nparams; i++)
- params[i].type = VIR_TYPED_PARAM_UINT;
-
- i = 0;
- if (inbound.average && i < nparams) {
- if (!virStrcpy(params[i].field, VIR_DOMAIN_BANDWIDTH_IN_AVERAGE,
- sizeof(params[i].field)))
- goto cleanup;
- params[i].value.ui = inbound.average;
- i++;
- }
- if (inbound.peak && i < nparams) {
- if (!virStrcpy(params[i].field, VIR_DOMAIN_BANDWIDTH_IN_PEAK,
- sizeof(params[i].field)))
- goto cleanup;
- params[i].value.ui = inbound.peak;
- i++;
- }
- if (inbound.burst && i < nparams) {
- if (!virStrcpy(params[i].field, VIR_DOMAIN_BANDWIDTH_IN_BURST,
- sizeof(params[i].field)))
- goto cleanup;
- params[i].value.ui = inbound.burst;
- i++;
- }
- if (outbound.average && i < nparams) {
- if (!virStrcpy(params[i].field, VIR_DOMAIN_BANDWIDTH_OUT_AVERAGE,
- sizeof(params[i].field)))
- goto cleanup;
- params[i].value.ui = outbound.average;
- i++;
- }
- if (outbound.peak && i < nparams) {
- if (!virStrcpy(params[i].field, VIR_DOMAIN_BANDWIDTH_OUT_PEAK,
- sizeof(params[i].field)))
- goto cleanup;
- params[i].value.ui = outbound.peak;
- i++;
- }
- if (outbound.burst && i < nparams) {
- if (!virStrcpy(params[i].field, VIR_DOMAIN_BANDWIDTH_OUT_BURST,
- sizeof(params[i].field)))
- goto cleanup;
- params[i].value.ui = outbound.burst;
- i++;
- }
+ autostart = !vshCommandOptBool(cmd, "disable");
- if (virDomainSetInterfaceParameters(dom, device, params, nparams, flags) != 0) {
- vshError(ctl, "%s", _("Unable to set interface parameters"));
- goto cleanup;
- }
+ if (virNetworkSetAutostart(network, autostart) < 0) {
+ if (autostart)
+ vshError(ctl, _("failed to mark network %s as autostarted"), name);
+ else
+ vshError(ctl, _("failed to unmark network %s as autostarted"), name);
+ virNetworkFree(network);
+ return false;
}
- ret = true;
+ if (autostart)
+ vshPrint(ctl, _("Network %s marked as autostarted\n"), name);
+ else
+ vshPrint(ctl, _("Network %s unmarked as autostarted\n"), name);
-cleanup:
- virTypedParameterArrayClear(params, nparams);
- VIR_FREE(params);
- virDomainFree(dom);
- return ret;
+ virNetworkFree(network);
+ return true;
}
/*
- * "suspend" command
+ * "net-create" command
*/
-static const vshCmdInfo info_suspend[] = {
- {"help", N_("suspend a domain")},
- {"desc", N_("Suspend a running domain.")},
+static const vshCmdInfo info_network_create[] = {
+ {"help", N_("create a network from an XML file")},
+ {"desc", N_("Create a network.")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_suspend[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+static const vshCmdOptDef opts_network_create[] = {
+ {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML network description")},
{NULL, 0, 0, NULL}
};
static bool
-cmdSuspend(vshControl *ctl, const vshCmd *cmd)
+cmdNetworkCreate(vshControl *ctl, const vshCmd *cmd)
{
- virDomainPtr dom;
- const char *name;
+ virNetworkPtr network;
+ const char *from = NULL;
bool ret = true;
+ char *buffer;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+ if (vshCommandOptString(cmd, "file", &from) <= 0)
+ return false;
+
+ if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
return false;
- if (virDomainSuspend(dom) == 0) {
- vshPrint(ctl, _("Domain %s suspended\n"), name);
+ network = virNetworkCreateXML(ctl->conn, buffer);
+ VIR_FREE(buffer);
+
+ if (network != NULL) {
+ vshPrint(ctl, _("Network %s created from %s\n"),
+ virNetworkGetName(network), from);
+ virNetworkFree(network);
} else {
- vshError(ctl, _("Failed to suspend domain %s"), name);
+ vshError(ctl, _("Failed to create network from %s"), from);
ret = false;
}
-
- virDomainFree(dom);
return ret;
}
+
/*
- * "dompmsuspend" command
+ * "net-define" command
*/
-static const vshCmdInfo info_dom_pm_suspend[] = {
- {"help", N_("suspend a domain gracefully using power management "
- "functions")},
- {"desc", N_("Suspends a running domain using guest OS's power management. "
- "(Note: This requires a guest agent configured and running in "
- "the guest OS).")},
+static const vshCmdInfo info_network_define[] = {
+ {"help", N_("define (but don't start) a network from an XML file")},
+ {"desc", N_("Define a network.")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_dom_pm_suspend[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {"duration", VSH_OT_INT, VSH_OFLAG_REQ_OPT, N_("duration in seconds")},
- {"target", VSH_OT_STRING, VSH_OFLAG_REQ, N_("mem(Suspend-to-RAM), "
- "disk(Suspend-to-Disk), "
- "hybrid(Hybrid-Suspend)")},
+static const vshCmdOptDef opts_network_define[] = {
+ {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML network description")},
{NULL, 0, 0, NULL}
};
static bool
-cmdDomPMSuspend(vshControl *ctl, const vshCmd *cmd)
+cmdNetworkDefine(vshControl *ctl, const vshCmd *cmd)
{
- virDomainPtr dom;
- const char *name;
- bool ret = false;
- const char *target = NULL;
- unsigned int suspendTarget;
- unsigned long long duration = 0;
+ virNetworkPtr network;
+ const char *from = NULL;
+ bool ret = true;
+ char *buffer;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+ if (vshCommandOptString(cmd, "file", &from) <= 0)
return false;
- if (vshCommandOptULongLong(cmd, "duration", &duration) < 0) {
- vshError(ctl, _("Invalid duration argument"));
- goto cleanup;
- }
-
- if (vshCommandOptString(cmd, "target", &target) < 0) {
- vshError(ctl, _("Invalid target argument"));
- goto cleanup;
- }
+ if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
+ return false;
- if (STREQ(target, "mem"))
- suspendTarget = VIR_NODE_SUSPEND_TARGET_MEM;
- else if (STREQ(target, "disk"))
- suspendTarget = VIR_NODE_SUSPEND_TARGET_DISK;
- else if (STREQ(target, "hybrid"))
- suspendTarget = VIR_NODE_SUSPEND_TARGET_HYBRID;
- else {
- vshError(ctl, "%s", _("Invalid target"));
- goto cleanup;
- }
+ network = virNetworkDefineXML(ctl->conn, buffer);
+ VIR_FREE(buffer);
- if (virDomainPMSuspendForDuration(dom, suspendTarget, duration, 0) < 0) {
- vshError(ctl, _("Domain %s could not be suspended"),
- virDomainGetName(dom));
- goto cleanup;
+ if (network != NULL) {
+ vshPrint(ctl, _("Network %s defined from %s\n"),
+ virNetworkGetName(network), from);
+ virNetworkFree(network);
+ } else {
+ vshError(ctl, _("Failed to define network from %s"), from);
+ ret = false;
}
-
- vshPrint(ctl, _("Domain %s successfully suspended"),
- virDomainGetName(dom));
-
- ret = true;
-
-cleanup:
- virDomainFree(dom);
return ret;
}
+
/*
- * "dompmwakeup" command
+ * "net-destroy" command
*/
-
-static const vshCmdInfo info_dom_pm_wakeup[] = {
- {"help", N_("wakeup a domain suspended by dompmsuspend command")},
- {"desc", N_("Wakeup a domain previously suspended "
- "by dompmsuspend command.")},
+static const vshCmdInfo info_network_destroy[] = {
+ {"help", N_("destroy (stop) a network")},
+ {"desc", N_("Forcefully stop a given network.")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_dom_pm_wakeup[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+static const vshCmdOptDef opts_network_destroy[] = {
+ {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
{NULL, 0, 0, NULL}
};
static bool
-cmdDomPMWakeup(vshControl *ctl, const vshCmd *cmd)
+cmdNetworkDestroy(vshControl *ctl, const vshCmd *cmd)
{
- virDomainPtr dom;
+ virNetworkPtr network;
+ bool ret = true;
const char *name;
- bool ret = false;
- unsigned int flags = 0;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+ if (!(network = vshCommandOptNetwork(ctl, cmd, &name)))
return false;
- if (virDomainPMWakeup(dom, flags) < 0) {
- vshError(ctl, _("Domain %s could not be woken up"),
- virDomainGetName(dom));
- goto cleanup;
+ if (virNetworkDestroy(network) == 0) {
+ vshPrint(ctl, _("Network %s destroyed\n"), name);
+ } else {
+ vshError(ctl, _("Failed to destroy network %s"), name);
+ ret = false;
}
- vshPrint(ctl, _("Domain %s successfully woken up"),
- virDomainGetName(dom));
-
- ret = true;
-
-cleanup:
- virDomainFree(dom);
+ virNetworkFree(network);
return ret;
}
+
/*
- * "create" command
+ * "net-dumpxml" command
*/
-static const vshCmdInfo info_create[] = {
- {"help", N_("create a domain from an XML file")},
- {"desc", N_("Create a domain.")},
+static const vshCmdInfo info_network_dumpxml[] = {
+ {"help", N_("network information in XML")},
+ {"desc", N_("Output the network information as an XML dump to stdout.")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_create[] = {
- {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML domain description")},
-#ifndef WIN32
- {"console", VSH_OT_BOOL, 0, N_("attach to console after creation")},
-#endif
- {"paused", VSH_OT_BOOL, 0, N_("leave the guest paused after creation")},
- {"autodestroy", VSH_OT_BOOL, 0, N_("automatically destroy the guest when virsh disconnects")},
+static const vshCmdOptDef opts_network_dumpxml[] = {
+ {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
+ {"inactive", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("network information of an inactive domain")},
{NULL, 0, 0, NULL}
};
static bool
-cmdCreate(vshControl *ctl, const vshCmd *cmd)
+cmdNetworkDumpXML(vshControl *ctl, const vshCmd *cmd)
{
- virDomainPtr dom;
- const char *from = NULL;
+ virNetworkPtr network;
bool ret = true;
- char *buffer;
-#ifndef WIN32
- bool console = vshCommandOptBool(cmd, "console");
-#endif
- unsigned int flags = VIR_DOMAIN_NONE;
+ char *dump;
+ unsigned int flags = 0;
+ int inactive;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (vshCommandOptString(cmd, "file", &from) <= 0)
- return false;
-
- if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
+ if (!(network = vshCommandOptNetwork(ctl, cmd, NULL)))
return false;
- if (vshCommandOptBool(cmd, "paused"))
- flags |= VIR_DOMAIN_START_PAUSED;
- if (vshCommandOptBool(cmd, "autodestroy"))
- flags |= VIR_DOMAIN_START_AUTODESTROY;
+ inactive = vshCommandOptBool(cmd, "inactive");
+ if (inactive)
+ flags |= VIR_NETWORK_XML_INACTIVE;
- dom = virDomainCreateXML(ctl->conn, buffer, flags);
- VIR_FREE(buffer);
+ dump = virNetworkGetXMLDesc(network, flags);
- if (dom != NULL) {
- vshPrint(ctl, _("Domain %s created from %s\n"),
- virDomainGetName(dom), from);
-#ifndef WIN32
- if (console)
- cmdRunConsole(ctl, dom, NULL, 0);
-#endif
- virDomainFree(dom);
+ if (dump != NULL) {
+ vshPrint(ctl, "%s", dump);
+ VIR_FREE(dump);
} else {
- vshError(ctl, _("Failed to create domain from %s"), from);
ret = false;
}
+
+ virNetworkFree(network);
return ret;
}
/*
- * "define" command
+ * "net-info" command
*/
-static const vshCmdInfo info_define[] = {
- {"help", N_("define (but don't start) a domain from an XML file")},
- {"desc", N_("Define a domain.")},
+static const vshCmdInfo info_network_info[] = {
+ {"help", N_("network information")},
+ {"desc", N_("Returns basic information about the network")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_define[] = {
- {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML domain description")},
+static const vshCmdOptDef opts_network_info[] = {
+ {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
{NULL, 0, 0, NULL}
};
static bool
-cmdDefine(vshControl *ctl, const vshCmd *cmd)
+cmdNetworkInfo(vshControl *ctl, const vshCmd *cmd)
{
- virDomainPtr dom;
- const char *from = NULL;
- bool ret = true;
- char *buffer;
+ virNetworkPtr network;
+ char uuid[VIR_UUID_STRING_BUFLEN];
+ int autostart;
+ int persistent = -1;
+ int active = -1;
+ char *bridge = NULL;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (vshCommandOptString(cmd, "file", &from) <= 0)
+ if (!(network = vshCommandOptNetwork(ctl, cmd, NULL)))
return false;
- if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
- return false;
+ vshPrint(ctl, "%-15s %s\n", _("Name"), virNetworkGetName(network));
- dom = virDomainDefineXML(ctl->conn, buffer);
- VIR_FREE(buffer);
+ if (virNetworkGetUUIDString(network, uuid) == 0)
+ vshPrint(ctl, "%-15s %s\n", _("UUID"), uuid);
- if (dom != NULL) {
- vshPrint(ctl, _("Domain %s defined from %s\n"),
- virDomainGetName(dom), from);
- virDomainFree(dom);
- } else {
- vshError(ctl, _("Failed to define domain from %s"), from);
- ret = false;
- }
- return ret;
-}
+ active = virNetworkIsActive(network);
+ if (active >= 0)
+ vshPrint(ctl, "%-15s %s\n", _("Active:"), active? _("yes") : _("no"));
-/*
- * "undefine" command
- */
-static const vshCmdInfo info_undefine[] = {
- {"help", N_("undefine a domain")},
- {"desc",
- N_("Undefine an inactive domain, or convert persistent to transient.")},
+ persistent = virNetworkIsPersistent(network);
+ if (persistent < 0)
+ vshPrint(ctl, "%-15s %s\n", _("Persistent:"), _("unknown"));
+ else
+ vshPrint(ctl, "%-15s %s\n", _("Persistent:"), persistent ? _("yes") : _("no"));
+
+ if (virNetworkGetAutostart(network, &autostart) < 0)
+ vshPrint(ctl, "%-15s %s\n", _("Autostart:"), _("no autostart"));
+ else
+ vshPrint(ctl, "%-15s %s\n", _("Autostart:"), autostart ? _("yes") : _("no"));
+
+ bridge = virNetworkGetBridgeName(network);
+ if (bridge)
+ vshPrint(ctl, "%-15s %s\n", _("Bridge:"), bridge);
+
+ VIR_FREE(bridge);
+ virNetworkFree(network);
+ return true;
+}
+
+/*
+ * "iface-edit" command
+ */
+static const vshCmdInfo info_interface_edit[] = {
+ {"help", N_("edit XML configuration for a physical host interface")},
+ {"desc", N_("Edit the XML configuration for a physical host interface.")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_undefine[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name or uuid")},
- {"managed-save", VSH_OT_BOOL, 0, N_("remove domain managed state file")},
- {"storage", VSH_OT_DATA, VSH_OFLAG_NONE,
- N_("remove associated storage volumes (comma separated list of targets "
- "or source paths) (see domblklist)")},
- {"remove-all-storage", VSH_OT_BOOL, 0,
- N_("remove all associated storage volumes (use with caution)")},
- {"wipe-storage", VSH_OT_BOOL, VSH_OFLAG_NONE,
- N_("wipe data on the removed volumes")},
- {"snapshots-metadata", VSH_OT_BOOL, 0,
- N_("remove all domain snapshot metadata, if inactive")},
+static const vshCmdOptDef opts_interface_edit[] = {
+ {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name or MAC address")},
{NULL, 0, 0, NULL}
};
static bool
-cmdUndefine(vshControl *ctl, const vshCmd *cmd)
+cmdInterfaceEdit(vshControl *ctl, const vshCmd *cmd)
{
- virDomainPtr dom;
bool ret = false;
- const char *name = NULL;
- /* Flags to attempt. */
- unsigned int flags = 0;
- /* User-requested actions. */
- bool managed_save = vshCommandOptBool(cmd, "managed-save");
- bool snapshots_metadata = vshCommandOptBool(cmd, "snapshots-metadata");
- bool wipe_storage = vshCommandOptBool(cmd, "wipe-storage");
- bool remove_storage = false;
- bool remove_all_storage = vshCommandOptBool(cmd, "remove-all-storage");
- /* Positive if these items exist. */
- int has_managed_save = 0;
- int has_snapshots_metadata = 0;
- int has_snapshots = 0;
- /* True if undefine will not strand data, even on older servers. */
- bool managed_save_safe = false;
- bool snapshots_safe = false;
- int rc = -1;
- int running;
- /* list of volumes to remove along with this domain */
- const char *volumes_arg = NULL;
- char *volumes = NULL;
- char **volume_tokens = NULL;
- char *volume_tok = NULL;
- int nvolume_tokens = 0;
- char *def = NULL;
- char *source = NULL;
- char *target = NULL;
- int vol_i;
- int tok_i;
- xmlDocPtr doc = NULL;
- xmlXPathContextPtr ctxt = NULL;
- xmlNodePtr *vol_nodes = NULL;
- int nvolumes = 0;
- virStorageVolPtr vol = NULL;
- bool vol_del_failed = false;
-
- if (managed_save) {
- flags |= VIR_DOMAIN_UNDEFINE_MANAGED_SAVE;
- managed_save_safe = true;
- }
- if (snapshots_metadata) {
- flags |= VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA;
- snapshots_safe = true;
- }
+ virInterfacePtr iface = NULL;
+ virInterfacePtr iface_edited = NULL;
+ unsigned int flags = VIR_INTERFACE_XML_INACTIVE;
if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
- return false;
-
- /* check if a string that should contain list of volumes to remove is present */
- if (vshCommandOptString(cmd, "storage", &volumes_arg) > 0) {
- volumes = vshStrdup(ctl, volumes_arg);
-
- if (remove_all_storage) {
- vshError(ctl, _("Specified both --storage and --remove-all-storage"));
- goto cleanup;
- }
- remove_storage = true;
- }
-
- /* Do some flag manipulation. The goal here is to disable bits
- * from flags to reduce the likelihood of a server rejecting
- * unknown flag bits, as well as to track conditions which are
- * safe by default for the given hypervisor and server version. */
- running = virDomainIsActive(dom);
- if (running < 0) {
- virshReportError(ctl);
- goto cleanup;
- }
- if (!running) {
- /* Undefine with snapshots only fails for inactive domains,
- * and managed save only exists on inactive domains; if
- * running, then we don't want to remove anything. */
- has_managed_save = virDomainHasManagedSaveImage(dom, 0);
- if (has_managed_save < 0) {
- if (last_error->code != VIR_ERR_NO_SUPPORT) {
- virshReportError(ctl);
- goto cleanup;
- }
- virFreeError(last_error);
- last_error = NULL;
- has_managed_save = 0;
- }
-
- has_snapshots = virDomainSnapshotNum(dom, 0);
- if (has_snapshots < 0) {
- if (last_error->code != VIR_ERR_NO_SUPPORT) {
- virshReportError(ctl);
- goto cleanup;
- }
- virFreeError(last_error);
- last_error = NULL;
- has_snapshots = 0;
- }
- if (has_snapshots) {
- has_snapshots_metadata
- = virDomainSnapshotNum(dom, VIR_DOMAIN_SNAPSHOT_LIST_METADATA);
- if (has_snapshots_metadata < 0) {
- /* The server did not know the new flag, assume that all
- snapshots have metadata. */
- virFreeError(last_error);
- last_error = NULL;
- has_snapshots_metadata = has_snapshots;
- } else {
- /* The server knew the new flag, all aspects of
- * undefineFlags are safe. */
- managed_save_safe = snapshots_safe = true;
- }
- }
- }
- if (!has_managed_save) {
- flags &= ~VIR_DOMAIN_UNDEFINE_MANAGED_SAVE;
- managed_save_safe = true;
- }
- if (has_snapshots == 0) {
- snapshots_safe = true;
- }
- if (has_snapshots_metadata == 0) {
- flags &= ~VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA;
- snapshots_safe = true;
- }
-
- /* Stash domain description for later use */
- if (remove_storage || remove_all_storage) {
- if (running) {
- vshError(ctl, _("Storage volume deletion is supported only on stopped domains"));
- goto cleanup;
- }
-
- if (!(def = virDomainGetXMLDesc(dom, 0))) {
- vshError(ctl, _("Could not retrieve domain XML description"));
- goto cleanup;
- }
- }
-
- /* Generally we want to try the new API first. However, while
- * virDomainUndefineFlags was introduced at the same time as
- * VIR_DOMAIN_UNDEFINE_MANAGED_SAVE in 0.9.4, the
- * VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA flag was not present
- * until 0.9.5; skip to piecewise emulation if we couldn't prove
- * above that the new API is safe. */
- if (managed_save_safe && snapshots_safe) {
- rc = virDomainUndefineFlags(dom, flags);
- if (rc == 0 || (last_error->code != VIR_ERR_NO_SUPPORT &&
- last_error->code != VIR_ERR_INVALID_ARG))
- goto out;
- virFreeError(last_error);
- last_error = NULL;
- }
-
- /* The new API is unsupported or unsafe; fall back to doing things
- * piecewise. */
- if (has_managed_save) {
- if (!managed_save) {
- vshError(ctl, "%s",
- _("Refusing to undefine while domain managed save "
- "image exists"));
- goto cleanup;
- }
- if (virDomainManagedSaveRemove(dom, 0) < 0) {
- virshReportError(ctl);
- goto cleanup;
- }
- }
-
- /* No way to emulate deletion of just snapshot metadata
- * without support for the newer flags. Oh well. */
- if (has_snapshots_metadata) {
- vshError(ctl,
- snapshots_metadata ?
- _("Unable to remove metadata of %d snapshots") :
- _("Refusing to undefine while %d snapshots exist"),
- has_snapshots_metadata);
goto cleanup;
- }
- rc = virDomainUndefine(dom);
-
-out:
- if (rc == 0) {
- vshPrint(ctl, _("Domain %s has been undefined\n"), name);
- ret = true;
- } else {
- vshError(ctl, _("Failed to undefine domain %s"), name);
+ iface = vshCommandOptInterface(ctl, cmd, NULL);
+ if (iface == NULL)
goto cleanup;
- }
-
- /* try to undefine storage volumes associated with this domain, if it's requested */
- if (remove_storage || remove_all_storage) {
- ret = false;
-
- /* tokenize the string from user and save it's parts into an array */
- if (volumes) {
- /* count the delimiters */
- volume_tok = volumes;
- nvolume_tokens = 1; /* we need at least one member */
- while (*volume_tok) {
- if (*volume_tok == ',')
- nvolume_tokens++;
- volume_tok++;
- }
-
- volume_tokens = vshCalloc(ctl, nvolume_tokens, sizeof(char *));
-
- /* tokenize the input string */
- nvolume_tokens = 0;
- volume_tok = volumes;
- do {
- volume_tokens[nvolume_tokens] = strsep(&volume_tok, ",");
- nvolume_tokens++;
- } while (volume_tok);
- }
-
- doc = virXMLParseStringCtxt(def, _("(domain_definition)"), &ctxt);
- if (!doc)
- goto cleanup;
-
- nvolumes = virXPathNodeSet("./devices/disk", ctxt, &vol_nodes);
-
- if (nvolumes < 0)
- goto cleanup;
-
- for (vol_i = 0; vol_i < nvolumes; vol_i++) {
- ctxt->node = vol_nodes[vol_i];
- VIR_FREE(target);
- VIR_FREE(source);
- if (vol) {
- virStorageVolFree(vol);
- vol = NULL;
- }
-
- /* get volume source and target paths */
- if (!(target = virXPathString("string(./target/@dev)", ctxt))) {
- vshError(ctl, _("Failed to enumerate devices"));
- goto cleanup;
- }
-
- if (!(source = virXPathString("string("
- "./source/@file|"
- "./source/@dir|"
- "./source/@name|"
- "./source/@dev)", ctxt)) &&
- virGetLastError())
- goto cleanup;
-
- /* lookup if volume was selected by user */
- if (volumes) {
- volume_tok = NULL;
- for (tok_i = 0; tok_i < nvolume_tokens; tok_i++) {
- if (volume_tokens[tok_i] &&
- (STREQ_NULLABLE(volume_tokens[tok_i], target) ||
- STREQ_NULLABLE(volume_tokens[tok_i], source))) {
- volume_tok = volume_tokens[tok_i];
- volume_tokens[tok_i] = NULL;
- break;
- }
- }
- if (!volume_tok)
- continue;
- }
-
- if (!source)
- continue;
-
- if (!(vol = virStorageVolLookupByPath(ctl->conn, source))) {
- vshPrint(ctl,
- _("Storage volume '%s'(%s) is not managed by libvirt. "
- "Remove it manually.\n"), target, source);
- virResetLastError();
- continue;
- }
-
- if (wipe_storage) {
- vshPrint(ctl, _("Wiping volume '%s'(%s) ... "), target, source);
- fflush(stdout);
- if (virStorageVolWipe(vol, 0) < 0) {
- vshError(ctl, _("Failed! Volume not removed."));
- vol_del_failed = true;
- continue;
- } else {
- vshPrint(ctl, _("Done.\n"));
- }
- }
- /* delete the volume */
- if (virStorageVolDelete(vol, 0) < 0) {
- vshError(ctl, _("Failed to remove storage volume '%s'(%s)"),
- target, source);
- vol_del_failed = true;
- }
- vshPrint(ctl, _("Volume '%s' removed.\n"), volume_tok?volume_tok:source);
- }
+#define EDIT_GET_XML virInterfaceGetXMLDesc(iface, flags)
+#define EDIT_NOT_CHANGED \
+ vshPrint(ctl, _("Interface %s XML configuration not changed.\n"), \
+ virInterfaceGetName(iface)); \
+ ret = true; goto edit_cleanup;
+#define EDIT_DEFINE \
+ (iface_edited = virInterfaceDefineXML(ctl->conn, doc_edited, 0))
+#define EDIT_FREE \
+ if (iface_edited) \
+ virInterfaceFree(iface_edited);
+#include "virsh-edit.c"
- /* print volumes specified by user that were not found in domain definition */
- if (volumes) {
- for (tok_i = 0; tok_i < nvolume_tokens; tok_i++) {
- if (volume_tokens[tok_i])
- vshPrint(ctl, _("Volume '%s' was not found in domain's "
- "definition.\n"),
- volume_tokens[tok_i]);
- }
- }
+ vshPrint(ctl, _("Interface %s XML configuration edited.\n"),
+ virInterfaceGetName(iface_edited));
- if (!vol_del_failed)
- ret = true;
- }
+ ret = true;
cleanup:
- VIR_FREE(source);
- VIR_FREE(target);
- VIR_FREE(volumes);
- VIR_FREE(volume_tokens);
- VIR_FREE(def);
- VIR_FREE(vol_nodes);
- if (vol)
- virStorageVolFree(vol);
- xmlFreeDoc(doc);
- xmlXPathFreeContext(ctxt);
- virDomainFree(dom);
+ if (iface)
+ virInterfaceFree(iface);
+ if (iface_edited)
+ virInterfaceFree(iface_edited);
+
return ret;
}
-
/*
- * "start" command
+ * "net-list" command
*/
-static const vshCmdInfo info_start[] = {
- {"help", N_("start a (previously defined) inactive domain")},
- {"desc", N_("Start a domain, either from the last managedsave\n"
- " state, or via a fresh boot if no managedsave state\n"
- " is present.")},
+static const vshCmdInfo info_network_list[] = {
+ {"help", N_("list networks")},
+ {"desc", N_("Returns list of networks.")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_start[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("name of the inactive domain")},
-#ifndef WIN32
- {"console", VSH_OT_BOOL, 0, N_("attach to console after creation")},
-#endif
- {"paused", VSH_OT_BOOL, 0, N_("leave the guest paused after creation")},
- {"autodestroy", VSH_OT_BOOL, 0,
- N_("automatically destroy the guest when virsh disconnects")},
- {"bypass-cache", VSH_OT_BOOL, 0,
- N_("avoid file system cache when loading")},
- {"force-boot", VSH_OT_BOOL, 0,
- N_("force fresh boot by discarding any managed save")},
+static const vshCmdOptDef opts_network_list[] = {
+ {"inactive", VSH_OT_BOOL, 0, N_("list inactive networks")},
+ {"all", VSH_OT_BOOL, 0, N_("list inactive & active networks")},
{NULL, 0, 0, NULL}
};
static bool
-cmdStart(vshControl *ctl, const vshCmd *cmd)
+cmdNetworkList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
- virDomainPtr dom;
- bool ret = false;
-#ifndef WIN32
- bool console = vshCommandOptBool(cmd, "console");
-#endif
- unsigned int flags = VIR_DOMAIN_NONE;
- int rc;
+ bool inactive = vshCommandOptBool(cmd, "inactive");
+ bool all = vshCommandOptBool(cmd, "all");
+ bool active = !inactive || all;
+ int maxactive = 0, maxinactive = 0, i;
+ char **activeNames = NULL, **inactiveNames = NULL;
+ inactive |= all;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (!(dom = vshCommandOptDomainBy(ctl, cmd, NULL,
- VSH_BYNAME | VSH_BYUUID)))
- return false;
+ if (active) {
+ maxactive = virConnectNumOfNetworks(ctl->conn);
+ if (maxactive < 0) {
+ vshError(ctl, "%s", _("Failed to list active networks"));
+ return false;
+ }
+ if (maxactive) {
+ activeNames = vshMalloc(ctl, sizeof(char *) * maxactive);
- if (virDomainGetID(dom) != (unsigned int)-1) {
- vshError(ctl, "%s", _("Domain is already active"));
- virDomainFree(dom);
- return false;
- }
+ if ((maxactive = virConnectListNetworks(ctl->conn, activeNames,
+ maxactive)) < 0) {
+ vshError(ctl, "%s", _("Failed to list active networks"));
+ VIR_FREE(activeNames);
+ return false;
+ }
- if (vshCommandOptBool(cmd, "paused"))
- flags |= VIR_DOMAIN_START_PAUSED;
- if (vshCommandOptBool(cmd, "autodestroy"))
- flags |= VIR_DOMAIN_START_AUTODESTROY;
- if (vshCommandOptBool(cmd, "bypass-cache"))
- flags |= VIR_DOMAIN_START_BYPASS_CACHE;
- if (vshCommandOptBool(cmd, "force-boot"))
- flags |= VIR_DOMAIN_START_FORCE_BOOT;
-
- /* We can emulate force boot, even for older servers that reject it. */
- if (flags & VIR_DOMAIN_START_FORCE_BOOT) {
- if (virDomainCreateWithFlags(dom, flags) == 0)
- goto started;
- if (last_error->code != VIR_ERR_NO_SUPPORT &&
- last_error->code != VIR_ERR_INVALID_ARG) {
- virshReportError(ctl);
- goto cleanup;
+ qsort(&activeNames[0], maxactive, sizeof(char *), vshNameSorter);
}
- virFreeError(last_error);
- last_error = NULL;
- rc = virDomainHasManagedSaveImage(dom, 0);
- if (rc < 0) {
- /* No managed save image to remove */
- virFreeError(last_error);
- last_error = NULL;
- } else if (rc > 0) {
- if (virDomainManagedSaveRemove(dom, 0) < 0) {
- virshReportError(ctl);
- goto cleanup;
+ }
+ if (inactive) {
+ maxinactive = virConnectNumOfDefinedNetworks(ctl->conn);
+ if (maxinactive < 0) {
+ vshError(ctl, "%s", _("Failed to list inactive networks"));
+ VIR_FREE(activeNames);
+ return false;
+ }
+ if (maxinactive) {
+ inactiveNames = vshMalloc(ctl, sizeof(char *) * maxinactive);
+
+ if ((maxinactive =
+ virConnectListDefinedNetworks(ctl->conn, inactiveNames,
+ maxinactive)) < 0) {
+ vshError(ctl, "%s", _("Failed to list inactive networks"));
+ VIR_FREE(activeNames);
+ VIR_FREE(inactiveNames);
+ return false;
}
+
+ qsort(&inactiveNames[0], maxinactive, sizeof(char*), vshNameSorter);
}
- flags &= ~VIR_DOMAIN_START_FORCE_BOOT;
}
+ vshPrintExtra(ctl, "%-20s %-10s %s\n", _("Name"), _("State"),
+ _("Autostart"));
+ vshPrintExtra(ctl, "-----------------------------------------\n");
- /* Prefer older API unless we have to pass a flag. */
- if ((flags ? virDomainCreateWithFlags(dom, flags)
- : virDomainCreate(dom)) < 0) {
- vshError(ctl, _("Failed to start domain %s"), virDomainGetName(dom));
- goto cleanup;
- }
+ for (i = 0; i < maxactive; i++) {
+ virNetworkPtr network =
+ virNetworkLookupByName(ctl->conn, activeNames[i]);
+ const char *autostartStr;
+ int autostart = 0;
-started:
- vshPrint(ctl, _("Domain %s started\n"),
- virDomainGetName(dom));
-#ifndef WIN32
- if (console && !cmdRunConsole(ctl, dom, NULL, 0))
- goto cleanup;
-#endif
+ /* this kind of work with networks is not atomic operation */
+ if (!network) {
+ VIR_FREE(activeNames[i]);
+ continue;
+ }
- ret = true;
+ if (virNetworkGetAutostart(network, &autostart) < 0)
+ autostartStr = _("no autostart");
+ else
+ autostartStr = autostart ? _("yes") : _("no");
-cleanup:
- virDomainFree(dom);
- return ret;
+ vshPrint(ctl, "%-20s %-10s %-10s\n",
+ virNetworkGetName(network),
+ _("active"),
+ autostartStr);
+ virNetworkFree(network);
+ VIR_FREE(activeNames[i]);
+ }
+ for (i = 0; i < maxinactive; i++) {
+ virNetworkPtr network = virNetworkLookupByName(ctl->conn, inactiveNames[i]);
+ const char *autostartStr;
+ int autostart = 0;
+
+ /* this kind of work with networks is not atomic operation */
+ if (!network) {
+ VIR_FREE(inactiveNames[i]);
+ continue;
+ }
+
+ if (virNetworkGetAutostart(network, &autostart) < 0)
+ autostartStr = _("no autostart");
+ else
+ autostartStr = autostart ? _("yes") : _("no");
+
+ vshPrint(ctl, "%-20s %-10s %-10s\n",
+ inactiveNames[i],
+ _("inactive"),
+ autostartStr);
+
+ virNetworkFree(network);
+ VIR_FREE(inactiveNames[i]);
+ }
+ VIR_FREE(activeNames);
+ VIR_FREE(inactiveNames);
+ return true;
}
+
/*
- * "save" command
+ * "net-name" command
*/
-static const vshCmdInfo info_save[] = {
- {"help", N_("save a domain state to a file")},
- {"desc", N_("Save the RAM state of a running domain.")},
+static const vshCmdInfo info_network_name[] = {
+ {"help", N_("convert a network UUID to network name")},
+ {"desc", ""},
{NULL, NULL}
};
-static const vshCmdOptDef opts_save[] = {
- {"bypass-cache", VSH_OT_BOOL, 0, N_("avoid file system cache when saving")},
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("where to save the data")},
- {"xml", VSH_OT_STRING, 0,
- N_("filename containing updated XML for the target")},
- {"running", VSH_OT_BOOL, 0, N_("set domain to be running on restore")},
- {"paused", VSH_OT_BOOL, 0, N_("set domain to be paused on restore")},
- {"verbose", VSH_OT_BOOL, 0, N_("display the progress of save")},
+static const vshCmdOptDef opts_network_name[] = {
+ {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network uuid")},
{NULL, 0, 0, NULL}
};
-static void
-doSave(void *opaque)
-{
- vshCtrlData *data = opaque;
- vshControl *ctl = data->ctl;
- const vshCmd *cmd = data->cmd;
- char ret = '1';
- virDomainPtr dom = NULL;
- const char *name = NULL;
- const char *to = NULL;
- unsigned int flags = 0;
- const char *xmlfile = NULL;
- char *xml = NULL;
- sigset_t sigmask, oldsigmask;
-
- sigemptyset(&sigmask);
- sigaddset(&sigmask, SIGINT);
- if (pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask) < 0)
- goto out_sig;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- goto out;
-
- if (vshCommandOptString(cmd, "file", &to) <= 0)
- goto out;
-
- if (vshCommandOptBool(cmd, "bypass-cache"))
- flags |= VIR_DOMAIN_SAVE_BYPASS_CACHE;
- if (vshCommandOptBool(cmd, "running"))
- flags |= VIR_DOMAIN_SAVE_RUNNING;
- if (vshCommandOptBool(cmd, "paused"))
- flags |= VIR_DOMAIN_SAVE_PAUSED;
-
- if (vshCommandOptString(cmd, "xml", &xmlfile) < 0) {
- vshError(ctl, "%s", _("malformed xml argument"));
- goto out;
- }
-
- if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
- goto out;
-
- if (xmlfile &&
- virFileReadAll(xmlfile, 8192, &xml) < 0)
- goto out;
-
- if (((flags || xml)
- ? virDomainSaveFlags(dom, to, xml, flags)
- : virDomainSave(dom, to)) < 0) {
- vshError(ctl, _("Failed to save domain %s to %s"), name, to);
- goto out;
- }
-
- ret = '0';
-
-out:
- pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
-out_sig:
- if (dom) virDomainFree(dom);
- VIR_FREE(xml);
- ignore_value(safewrite(data->writefd, &ret, sizeof(ret)));
-}
-
static bool
-cmdSave(vshControl *ctl, const vshCmd *cmd)
+cmdNetworkName(vshControl *ctl, const vshCmd *cmd)
{
- bool ret = false;
- virDomainPtr dom = NULL;
- int p[2] = {-1. -1};
- virThread workerThread;
- bool verbose = false;
- vshCtrlData data;
- const char *to = NULL;
- const char *name = NULL;
+ virNetworkPtr network;
- if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+ if (!(network = vshCommandOptNetworkBy(ctl, cmd, NULL,
+ VSH_BYUUID)))
return false;
- if (vshCommandOptString(cmd, "file", &to) <= 0)
- goto cleanup;
-
- if (vshCommandOptBool(cmd, "verbose"))
- verbose = true;
-
- if (pipe(p) < 0)
- goto cleanup;
-
- data.ctl = ctl;
- data.cmd = cmd;
- data.writefd = p[1];
-
- if (virThreadCreate(&workerThread,
- true,
- doSave,
- &data) < 0)
- goto cleanup;
-
- ret = vshWatchJob(ctl, dom, verbose, p[0], 0, NULL, NULL, _("Save"));
-
- virThreadJoin(&workerThread);
-
- if (ret)
- vshPrint(ctl, _("\nDomain %s saved to %s\n"), name, to);
-
-cleanup:
- if (dom)
- virDomainFree(dom);
- return ret;
+ vshPrint(ctl, "%s\n", virNetworkGetName(network));
+ virNetworkFree(network);
+ return true;
}
+
/*
- * "save-image-dumpxml" command
+ * "net-start" command
*/
-static const vshCmdInfo info_save_image_dumpxml[] = {
- {"help", N_("saved state domain information in XML")},
- {"desc", N_("Output the domain information for a saved state file,\n"
- "as an XML dump to stdout.")},
+static const vshCmdInfo info_network_start[] = {
+ {"help", N_("start a (previously defined) inactive network")},
+ {"desc", N_("Start a network.")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_save_image_dumpxml[] = {
- {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("saved state file to read")},
- {"security-info", VSH_OT_BOOL, 0, N_("include security sensitive information in XML dump")},
+static const vshCmdOptDef opts_network_start[] = {
+ {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
{NULL, 0, 0, NULL}
};
static bool
-cmdSaveImageDumpxml(vshControl *ctl, const vshCmd *cmd)
+cmdNetworkStart(vshControl *ctl, const vshCmd *cmd)
{
- const char *file = NULL;
- bool ret = false;
- unsigned int flags = 0;
- char *xml = NULL;
-
- if (vshCommandOptBool(cmd, "security-info"))
- flags |= VIR_DOMAIN_XML_SECURE;
+ virNetworkPtr network;
+ bool ret = true;
+ const char *name = NULL;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (vshCommandOptString(cmd, "file", &file) <= 0)
- return false;
-
- xml = virDomainSaveImageGetXMLDesc(ctl->conn, file, flags);
- if (!xml)
- goto cleanup;
-
- vshPrint(ctl, "%s", xml);
- ret = true;
+ if (!(network = vshCommandOptNetwork(ctl, cmd, &name)))
+ return false;
-cleanup:
- VIR_FREE(xml);
+ if (virNetworkCreate(network) == 0) {
+ vshPrint(ctl, _("Network %s started\n"), name);
+ } else {
+ vshError(ctl, _("Failed to start network %s"), name);
+ ret = false;
+ }
+ virNetworkFree(network);
return ret;
}
+
/*
- * "save-image-define" command
+ * "net-undefine" command
*/
-static const vshCmdInfo info_save_image_define[] = {
- {"help", N_("redefine the XML for a domain's saved state file")},
- {"desc", N_("Replace the domain XML associated with a saved state file")},
+static const vshCmdInfo info_network_undefine[] = {
+ {"help", N_("undefine an inactive network")},
+ {"desc", N_("Undefine the configuration for an inactive network.")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_save_image_define[] = {
- {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("saved state file to modify")},
- {"xml", VSH_OT_STRING, VSH_OFLAG_REQ,
- N_("filename containing updated XML for the target")},
- {"running", VSH_OT_BOOL, 0, N_("set domain to be running on restore")},
- {"paused", VSH_OT_BOOL, 0, N_("set domain to be paused on restore")},
+static const vshCmdOptDef opts_network_undefine[] = {
+ {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
{NULL, 0, 0, NULL}
};
static bool
-cmdSaveImageDefine(vshControl *ctl, const vshCmd *cmd)
+cmdNetworkUndefine(vshControl *ctl, const vshCmd *cmd)
{
- const char *file = NULL;
- bool ret = false;
- const char *xmlfile = NULL;
- char *xml = NULL;
- unsigned int flags = 0;
-
- if (vshCommandOptBool(cmd, "running"))
- flags |= VIR_DOMAIN_SAVE_RUNNING;
- if (vshCommandOptBool(cmd, "paused"))
- flags |= VIR_DOMAIN_SAVE_PAUSED;
+ virNetworkPtr network;
+ bool ret = true;
+ const char *name;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (vshCommandOptString(cmd, "file", &file) <= 0)
- return false;
-
- if (vshCommandOptString(cmd, "xml", &xmlfile) <= 0) {
- vshError(ctl, "%s", _("malformed or missing xml argument"));
+ if (!(network = vshCommandOptNetwork(ctl, cmd, &name)))
return false;
- }
- if (virFileReadAll(xmlfile, 8192, &xml) < 0)
- goto cleanup;
-
- if (virDomainSaveImageDefineXML(ctl->conn, file, xml, flags) < 0) {
- vshError(ctl, _("Failed to update %s"), file);
- goto cleanup;
+ if (virNetworkUndefine(network) == 0) {
+ vshPrint(ctl, _("Network %s has been undefined\n"), name);
+ } else {
+ vshError(ctl, _("Failed to undefine network %s"), name);
+ ret = false;
}
- vshPrint(ctl, _("State file %s updated.\n"), file);
- ret = true;
-
-cleanup:
- VIR_FREE(xml);
+ virNetworkFree(network);
return ret;
}
+
/*
- * "save-image-edit" command
+ * "net-uuid" command
*/
-static const vshCmdInfo info_save_image_edit[] = {
- {"help", N_("edit XML for a domain's saved state file")},
- {"desc", N_("Edit the domain XML associated with a saved state file")},
+static const vshCmdInfo info_network_uuid[] = {
+ {"help", N_("convert a network name to network UUID")},
+ {"desc", ""},
{NULL, NULL}
};
-static const vshCmdOptDef opts_save_image_edit[] = {
- {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("saved state file to edit")},
- {"running", VSH_OT_BOOL, 0, N_("set domain to be running on restore")},
- {"paused", VSH_OT_BOOL, 0, N_("set domain to be paused on restore")},
+static const vshCmdOptDef opts_network_uuid[] = {
+ {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name")},
{NULL, 0, 0, NULL}
};
static bool
-cmdSaveImageEdit(vshControl *ctl, const vshCmd *cmd)
+cmdNetworkUuid(vshControl *ctl, const vshCmd *cmd)
{
- const char *file = NULL;
- bool ret = false;
- unsigned int getxml_flags = VIR_DOMAIN_XML_SECURE;
- unsigned int define_flags = 0;
-
- if (vshCommandOptBool(cmd, "running"))
- define_flags |= VIR_DOMAIN_SAVE_RUNNING;
- if (vshCommandOptBool(cmd, "paused"))
- define_flags |= VIR_DOMAIN_SAVE_PAUSED;
-
- /* Normally, we let the API reject mutually exclusive flags.
- * However, in the edit cycle, we let the user retry if the define
- * step fails, but the define step will always fail on invalid
- * flags, so we reject it up front to avoid looping. */
- if (define_flags == (VIR_DOMAIN_SAVE_RUNNING | VIR_DOMAIN_SAVE_PAUSED)) {
- vshError(ctl, "%s", _("--running and --saved are mutually exclusive"));
- return false;
- }
+ virNetworkPtr network;
+ char uuid[VIR_UUID_STRING_BUFLEN];
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (vshCommandOptString(cmd, "file", &file) <= 0)
+ if (!(network = vshCommandOptNetworkBy(ctl, cmd, NULL,
+ VSH_BYNAME)))
return false;
-#define EDIT_GET_XML \
- virDomainSaveImageGetXMLDesc(ctl->conn, file, getxml_flags)
-#define EDIT_NOT_CHANGED \
- vshPrint(ctl, _("Saved image %s XML configuration " \
- "not changed.\n"), file); \
- ret = true; goto edit_cleanup;
-#define EDIT_DEFINE \
- virDomainSaveImageDefineXML(ctl->conn, file, doc_edited, define_flags)
-#define EDIT_FREE /* */
-#include "virsh-edit.c"
-
- vshPrint(ctl, _("State file %s edited.\n"), file);
- ret = true;
+ if (virNetworkGetUUIDString(network, uuid) != -1)
+ vshPrint(ctl, "%s\n", uuid);
+ else
+ vshError(ctl, "%s", _("failed to get network UUID"));
-cleanup:
- return ret;
+ virNetworkFree(network);
+ return true;
}
+
+/**************************************************************************/
/*
- * "managedsave" command
+ * "iface-list" command
*/
-static const vshCmdInfo info_managedsave[] = {
- {"help", N_("managed save of a domain state")},
- {"desc", N_("Save and destroy a running domain, so it can be restarted from\n"
- " the same state at a later time. When the virsh 'start'\n"
- " command is next run for the domain, it will automatically\n"
- " be started from this saved state.")},
+static const vshCmdInfo info_interface_list[] = {
+ {"help", N_("list physical host interfaces")},
+ {"desc", N_("Returns list of physical host interfaces.")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_managedsave[] = {
- {"bypass-cache", VSH_OT_BOOL, 0, N_("avoid file system cache when saving")},
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {"running", VSH_OT_BOOL, 0, N_("set domain to be running on next start")},
- {"paused", VSH_OT_BOOL, 0, N_("set domain to be paused on next start")},
- {"verbose", VSH_OT_BOOL, 0, N_("display the progress of save")},
+static const vshCmdOptDef opts_interface_list[] = {
+ {"inactive", VSH_OT_BOOL, 0, N_("list inactive interfaces")},
+ {"all", VSH_OT_BOOL, 0, N_("list inactive & active interfaces")},
{NULL, 0, 0, NULL}
};
-
-static void
-doManagedsave(void *opaque)
+static bool
+cmdInterfaceList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
- char ret = '1';
- vshCtrlData *data = opaque;
- vshControl *ctl = data->ctl;
- const vshCmd *cmd = data->cmd;
- virDomainPtr dom = NULL;
- const char *name;
- unsigned int flags = 0;
- sigset_t sigmask, oldsigmask;
-
- sigemptyset(&sigmask);
- sigaddset(&sigmask, SIGINT);
- if (pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask) < 0)
- goto out_sig;
+ bool inactive = vshCommandOptBool(cmd, "inactive");
+ bool all = vshCommandOptBool(cmd, "all");
+ bool active = !inactive || all;
+ int maxactive = 0, maxinactive = 0, i;
+ char **activeNames = NULL, **inactiveNames = NULL;
+ inactive |= all;
if (!vshConnectionUsability(ctl, ctl->conn))
- goto out;
+ return false;
- if (vshCommandOptBool(cmd, "bypass-cache"))
- flags |= VIR_DOMAIN_SAVE_BYPASS_CACHE;
- if (vshCommandOptBool(cmd, "running"))
- flags |= VIR_DOMAIN_SAVE_RUNNING;
- if (vshCommandOptBool(cmd, "paused"))
- flags |= VIR_DOMAIN_SAVE_PAUSED;
+ if (active) {
+ maxactive = virConnectNumOfInterfaces(ctl->conn);
+ if (maxactive < 0) {
+ vshError(ctl, "%s", _("Failed to list active interfaces"));
+ return false;
+ }
+ if (maxactive) {
+ activeNames = vshMalloc(ctl, sizeof(char *) * maxactive);
- if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
- goto out;
+ if ((maxactive = virConnectListInterfaces(ctl->conn, activeNames,
+ maxactive)) < 0) {
+ vshError(ctl, "%s", _("Failed to list active interfaces"));
+ VIR_FREE(activeNames);
+ return false;
+ }
- if (virDomainManagedSave(dom, flags) < 0) {
- vshError(ctl, _("Failed to save domain %s state"), name);
- goto out;
+ qsort(&activeNames[0], maxactive, sizeof(char *), vshNameSorter);
+ }
}
+ if (inactive) {
+ maxinactive = virConnectNumOfDefinedInterfaces(ctl->conn);
+ if (maxinactive < 0) {
+ vshError(ctl, "%s", _("Failed to list inactive interfaces"));
+ VIR_FREE(activeNames);
+ return false;
+ }
+ if (maxinactive) {
+ inactiveNames = vshMalloc(ctl, sizeof(char *) * maxinactive);
- ret = '0';
-out:
- pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
-out_sig:
- if (dom)
- virDomainFree(dom);
- ignore_value(safewrite(data->writefd, &ret, sizeof(ret)));
-}
-
-static bool
-cmdManagedSave(vshControl *ctl, const vshCmd *cmd)
-{
- virDomainPtr dom;
- int p[2] = { -1, -1};
- bool ret = false;
- bool verbose = false;
- const char *name = NULL;
- vshCtrlData data;
- virThread workerThread;
-
- if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
- return false;
-
- if (vshCommandOptBool(cmd, "verbose"))
- verbose = true;
+ if ((maxinactive =
+ virConnectListDefinedInterfaces(ctl->conn, inactiveNames,
+ maxinactive)) < 0) {
+ vshError(ctl, "%s", _("Failed to list inactive interfaces"));
+ VIR_FREE(activeNames);
+ VIR_FREE(inactiveNames);
+ return false;
+ }
- if (pipe(p) < 0)
- goto cleanup;
+ qsort(&inactiveNames[0], maxinactive, sizeof(char*), vshNameSorter);
+ }
+ }
+ vshPrintExtra(ctl, "%-20s %-10s %s\n", _("Name"), _("State"),
+ _("MAC Address"));
+ vshPrintExtra(ctl, "--------------------------------------------\n");
- data.ctl = ctl;
- data.cmd = cmd;
- data.writefd = p[1];
+ for (i = 0; i < maxactive; i++) {
+ virInterfacePtr iface =
+ virInterfaceLookupByName(ctl->conn, activeNames[i]);
- if (virThreadCreate(&workerThread,
- true,
- doManagedsave,
- &data) < 0)
- goto cleanup;
+ /* this kind of work with interfaces is not atomic */
+ if (!iface) {
+ VIR_FREE(activeNames[i]);
+ continue;
+ }
- ret = vshWatchJob(ctl, dom, verbose, p[0], 0,
- NULL, NULL, _("Managedsave"));
+ vshPrint(ctl, "%-20s %-10s %s\n",
+ virInterfaceGetName(iface),
+ _("active"),
+ virInterfaceGetMACString(iface));
+ virInterfaceFree(iface);
+ VIR_FREE(activeNames[i]);
+ }
+ for (i = 0; i < maxinactive; i++) {
+ virInterfacePtr iface =
+ virInterfaceLookupByName(ctl->conn, inactiveNames[i]);
- virThreadJoin(&workerThread);
+ /* this kind of work with interfaces is not atomic */
+ if (!iface) {
+ VIR_FREE(inactiveNames[i]);
+ continue;
+ }
- if (ret)
- vshPrint(ctl, _("\nDomain %s state saved by libvirt\n"), name);
+ vshPrint(ctl, "%-20s %-10s %s\n",
+ virInterfaceGetName(iface),
+ _("inactive"),
+ virInterfaceGetMACString(iface));
+ virInterfaceFree(iface);
+ VIR_FREE(inactiveNames[i]);
+ }
+ VIR_FREE(activeNames);
+ VIR_FREE(inactiveNames);
+ return true;
-cleanup:
- virDomainFree(dom);
- VIR_FORCE_CLOSE(p[0]);
- VIR_FORCE_CLOSE(p[1]);
- return ret;
}
/*
- * "managedsave-remove" command
+ * "iface-name" command
*/
-static const vshCmdInfo info_managedsaveremove[] = {
- {"help", N_("Remove managed save of a domain")},
- {"desc", N_("Remove an existing managed save state file from a domain")},
+static const vshCmdInfo info_interface_name[] = {
+ {"help", N_("convert an interface MAC address to interface name")},
+ {"desc", ""},
{NULL, NULL}
};
-static const vshCmdOptDef opts_managedsaveremove[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+static const vshCmdOptDef opts_interface_name[] = {
+ {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface mac")},
{NULL, 0, 0, NULL}
};
static bool
-cmdManagedSaveRemove(vshControl *ctl, const vshCmd *cmd)
+cmdInterfaceName(vshControl *ctl, const vshCmd *cmd)
{
- virDomainPtr dom;
- const char *name;
- bool ret = false;
- int hassave;
+ virInterfacePtr iface;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
-
- if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+ if (!(iface = vshCommandOptInterfaceBy(ctl, cmd, NULL, NULL,
+ VSH_BYMAC)))
return false;
- hassave = virDomainHasManagedSaveImage(dom, 0);
- if (hassave < 0) {
- vshError(ctl, "%s", _("Failed to check for domain managed save image"));
- goto cleanup;
- }
-
- if (hassave) {
- if (virDomainManagedSaveRemove(dom, 0) < 0) {
- vshError(ctl, _("Failed to remove managed save image for domain %s"),
- name);
- goto cleanup;
- }
- else
- vshPrint(ctl, _("Removed managedsave image for domain %s"), name);
- }
- else
- vshPrint(ctl, _("Domain %s has no manage save image; removal skipped"),
- name);
-
- ret = true;
-
-cleanup:
- virDomainFree(dom);
- return ret;
+ vshPrint(ctl, "%s\n", virInterfaceGetName(iface));
+ virInterfaceFree(iface);
+ return true;
}
/*
- * "schedinfo" command
+ * "iface-mac" command
*/
-static const vshCmdInfo info_schedinfo[] = {
- {"help", N_("show/set scheduler parameters")},
- {"desc", N_("Show/Set scheduler parameters.")},
+static const vshCmdInfo info_interface_mac[] = {
+ {"help", N_("convert an interface name to interface MAC address")},
+ {"desc", ""},
{NULL, NULL}
};
-static const vshCmdOptDef opts_schedinfo[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {"set", VSH_OT_STRING, VSH_OFLAG_NONE, N_("parameter=value")},
- {"weight", VSH_OT_INT, VSH_OFLAG_NONE, N_("weight for XEN_CREDIT")},
- {"cap", VSH_OT_INT, VSH_OFLAG_NONE, N_("cap for XEN_CREDIT")},
- {"current", VSH_OT_BOOL, 0, N_("get/set current scheduler info")},
- {"config", VSH_OT_BOOL, 0, N_("get/set value to be used on next boot")},
- {"live", VSH_OT_BOOL, 0, N_("get/set value from running domain")},
+static const vshCmdOptDef opts_interface_mac[] = {
+ {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name")},
{NULL, 0, 0, NULL}
};
-static int
-cmdSchedInfoUpdate(vshControl *ctl, const vshCmd *cmd,
- virTypedParameterPtr param)
+static bool
+cmdInterfaceMAC(vshControl *ctl, const vshCmd *cmd)
{
- const char *data = NULL;
-
- /* Legacy 'weight' parameter */
- if (STREQ(param->field, "weight") &&
- param->type == VIR_TYPED_PARAM_UINT &&
- vshCommandOptBool(cmd, "weight")) {
- int val;
- if (vshCommandOptInt(cmd, "weight", &val) <= 0) {
- vshError(ctl, "%s", _("Invalid value of weight"));
- return -1;
- } else {
- param->value.ui = val;
- }
- return 1;
- }
-
- /* Legacy 'cap' parameter */
- if (STREQ(param->field, "cap") &&
- param->type == VIR_TYPED_PARAM_UINT &&
- vshCommandOptBool(cmd, "cap")) {
- int val;
- if (vshCommandOptInt(cmd, "cap", &val) <= 0) {
- vshError(ctl, "%s", _("Invalid value of cap"));
- return -1;
- } else {
- param->value.ui = val;
- }
- return 1;
- }
-
- if (vshCommandOptString(cmd, "set", &data) > 0) {
- char *val = strchr(data, '=');
- int match = 0;
- if (!val) {
- vshError(ctl, "%s", _("Invalid syntax for --set, expecting name=value"));
- return -1;
- }
- *val = '\0';
- match = STREQ(data, param->field);
- *val = '=';
- val++;
-
- if (!match)
- return 0;
+ virInterfacePtr iface;
- switch (param->type) {
- case VIR_TYPED_PARAM_INT:
- if (virStrToLong_i(val, NULL, 10, ¶m->value.i) < 0) {
- vshError(ctl, "%s",
- _("Invalid value for parameter, expecting an int"));
- return -1;
- }
- break;
- case VIR_TYPED_PARAM_UINT:
- if (virStrToLong_ui(val, NULL, 10, ¶m->value.ui) < 0) {
- vshError(ctl, "%s",
- _("Invalid value for parameter, expecting an unsigned int"));
- return -1;
- }
- break;
- case VIR_TYPED_PARAM_LLONG:
- if (virStrToLong_ll(val, NULL, 10, ¶m->value.l) < 0) {
- vshError(ctl, "%s",
- _("Invalid value for parameter, expecting a long long"));
- return -1;
- }
- break;
- case VIR_TYPED_PARAM_ULLONG:
- if (virStrToLong_ull(val, NULL, 10, ¶m->value.ul) < 0) {
- vshError(ctl, "%s",
- _("Invalid value for parameter, expecting an unsigned long long"));
- return -1;
- }
- break;
- case VIR_TYPED_PARAM_DOUBLE:
- if (virStrToDouble(val, NULL, ¶m->value.d) < 0) {
- vshError(ctl, "%s", _("Invalid value for parameter, expecting a double"));
- return -1;
- }
- break;
- case VIR_TYPED_PARAM_BOOLEAN:
- param->value.b = STREQ(val, "0") ? 0 : 1;
- }
- return 1;
- }
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+ if (!(iface = vshCommandOptInterfaceBy(ctl, cmd, NULL, NULL,
+ VSH_BYNAME)))
+ return false;
- return 0;
+ vshPrint(ctl, "%s\n", virInterfaceGetMACString(iface));
+ virInterfaceFree(iface);
+ return true;
}
+/*
+ * "iface-dumpxml" command
+ */
+static const vshCmdInfo info_interface_dumpxml[] = {
+ {"help", N_("interface information in XML")},
+ {"desc", N_("Output the physical host interface information as an XML dump to stdout.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_interface_dumpxml[] = {
+ {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name or MAC address")},
+ {"inactive", VSH_OT_BOOL, 0, N_("show inactive defined XML")},
+ {NULL, 0, 0, NULL}
+};
static bool
-cmdSchedinfo(vshControl *ctl, const vshCmd *cmd)
+cmdInterfaceDumpXML(vshControl *ctl, const vshCmd *cmd)
{
- char *schedulertype;
- virDomainPtr dom;
- virTypedParameterPtr params = NULL;
- int nparams = 0;
- int update = 0;
- int i, ret;
- bool ret_val = false;
+ virInterfacePtr iface;
+ bool ret = true;
+ char *dump;
unsigned int flags = 0;
- bool current = vshCommandOptBool(cmd, "current");
- bool config = vshCommandOptBool(cmd, "config");
- bool live = vshCommandOptBool(cmd, "live");
+ bool inactive = vshCommandOptBool(cmd, "inactive");
- if (current) {
- if (live || config) {
- vshError(ctl, "%s", _("--current must be specified exclusively"));
- return false;
- }
- flags = VIR_DOMAIN_AFFECT_CURRENT;
- } else {
- if (config)
- flags |= VIR_DOMAIN_AFFECT_CONFIG;
- if (live)
- flags |= VIR_DOMAIN_AFFECT_LIVE;
- }
+ if (inactive)
+ flags |= VIR_INTERFACE_XML_INACTIVE;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ if (!(iface = vshCommandOptInterface(ctl, cmd, NULL)))
return false;
- /* Print SchedulerType */
- schedulertype = virDomainGetSchedulerType(dom, &nparams);
- if (schedulertype != NULL) {
- vshPrint(ctl, "%-15s: %s\n", _("Scheduler"),
- schedulertype);
- VIR_FREE(schedulertype);
+ dump = virInterfaceGetXMLDesc(iface, flags);
+ if (dump != NULL) {
+ vshPrint(ctl, "%s", dump);
+ VIR_FREE(dump);
} else {
- vshPrint(ctl, "%-15s: %s\n", _("Scheduler"), _("Unknown"));
- goto cleanup;
- }
-
- if (nparams) {
- params = vshMalloc(ctl, sizeof(*params) * nparams);
-
- memset(params, 0, sizeof(*params) * nparams);
- if (flags || current) {
- /* We cannot query both live and config at once, so settle
- on current in that case. If we are setting, then the
- two values should match when we re-query; otherwise, we
- report the error later. */
- ret = virDomainGetSchedulerParametersFlags(dom, params, &nparams,
- ((live && config) ? 0
- : flags));
- } else {
- ret = virDomainGetSchedulerParameters(dom, params, &nparams);
- }
- if (ret == -1)
- goto cleanup;
-
- /* See if any params are being set */
- for (i = 0; i < nparams; i++) {
- ret = cmdSchedInfoUpdate(ctl, cmd, &(params[i]));
- if (ret == -1)
- goto cleanup;
-
- if (ret == 1)
- update = 1;
- }
-
- /* Update parameters & refresh data */
- if (update) {
- if (flags || current)
- ret = virDomainSetSchedulerParametersFlags(dom, params,
- nparams, flags);
- else
- ret = virDomainSetSchedulerParameters(dom, params, nparams);
- if (ret == -1)
- goto cleanup;
-
- if (flags || current)
- ret = virDomainGetSchedulerParametersFlags(dom, params,
- &nparams,
- ((live && config) ? 0
- : flags));
- else
- ret = virDomainGetSchedulerParameters(dom, params, &nparams);
- if (ret == -1)
- goto cleanup;
- } else {
- /* See if we've tried to --set var=val. If so, the fact that
- we reach this point (with update == 0) means that "var" did
- not match any of the settable parameters. Report the error. */
- const char *var_value_pair = NULL;
- if (vshCommandOptString(cmd, "set", &var_value_pair) > 0) {
- vshError(ctl, _("invalid scheduler option: %s"),
- var_value_pair);
- goto cleanup;
- }
- /* When not doing --set, --live and --config do not mix. */
- if (live && config) {
- vshError(ctl, "%s",
- _("cannot query both live and config at once"));
- goto cleanup;
- }
- }
-
- ret_val = true;
- for (i = 0; i < nparams; i++) {
- char *str = vshGetTypedParamValue(ctl, ¶ms[i]);
- vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
- VIR_FREE(str);
- }
+ ret = false;
}
- cleanup:
- VIR_FREE(params);
- virDomainFree(dom);
- return ret_val;
+ virInterfaceFree(iface);
+ return ret;
}
/*
- * "restore" command
+ * "iface-define" command
*/
-static const vshCmdInfo info_restore[] = {
- {"help", N_("restore a domain from a saved state in a file")},
- {"desc", N_("Restore a domain.")},
+static const vshCmdInfo info_interface_define[] = {
+ {"help", N_("define (but don't start) a physical host interface from an XML file")},
+ {"desc", N_("Define a physical host interface.")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_restore[] = {
- {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("the state to restore")},
- {"bypass-cache", VSH_OT_BOOL, 0,
- N_("avoid file system cache when restoring")},
- {"xml", VSH_OT_STRING, 0,
- N_("filename containing updated XML for the target")},
- {"running", VSH_OT_BOOL, 0, N_("restore domain into running state")},
- {"paused", VSH_OT_BOOL, 0, N_("restore domain into paused state")},
+static const vshCmdOptDef opts_interface_define[] = {
+ {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML interface description")},
{NULL, 0, 0, NULL}
};
static bool
-cmdRestore(vshControl *ctl, const vshCmd *cmd)
+cmdInterfaceDefine(vshControl *ctl, const vshCmd *cmd)
{
+ virInterfacePtr iface;
const char *from = NULL;
- bool ret = false;
- unsigned int flags = 0;
- const char *xmlfile = NULL;
- char *xml = NULL;
+ bool ret = true;
+ char *buffer;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
@@ -2963,8395 +2148,1765 @@ cmdRestore(vshControl *ctl, const vshCmd *cmd)
if (vshCommandOptString(cmd, "file", &from) <= 0)
return false;
- if (vshCommandOptBool(cmd, "bypass-cache"))
- flags |= VIR_DOMAIN_SAVE_BYPASS_CACHE;
- if (vshCommandOptBool(cmd, "running"))
- flags |= VIR_DOMAIN_SAVE_RUNNING;
- if (vshCommandOptBool(cmd, "paused"))
- flags |= VIR_DOMAIN_SAVE_PAUSED;
-
- if (vshCommandOptString(cmd, "xml", &xmlfile) < 0) {
- vshError(ctl, "%s", _("malformed xml argument"));
+ if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
return false;
- }
- if (xmlfile &&
- virFileReadAll(xmlfile, 8192, &xml) < 0)
- goto cleanup;
+ iface = virInterfaceDefineXML(ctl->conn, buffer, 0);
+ VIR_FREE(buffer);
- if (((flags || xml)
- ? virDomainRestoreFlags(ctl->conn, from, xml, flags)
- : virDomainRestore(ctl->conn, from)) < 0) {
- vshError(ctl, _("Failed to restore domain from %s"), from);
- goto cleanup;
+ if (iface != NULL) {
+ vshPrint(ctl, _("Interface %s defined from %s\n"),
+ virInterfaceGetName(iface), from);
+ virInterfaceFree(iface);
+ } else {
+ vshError(ctl, _("Failed to define interface from %s"), from);
+ ret = false;
}
-
- vshPrint(ctl, _("Domain restored from %s\n"), from);
- ret = true;
-
-cleanup:
- VIR_FREE(xml);
- return ret;
-}
+ return ret;
+}
/*
- * "dump" command
+ * "iface-undefine" command
*/
-static const vshCmdInfo info_dump[] = {
- {"help", N_("dump the core of a domain to a file for analysis")},
- {"desc", N_("Core dump a domain.")},
+static const vshCmdInfo info_interface_undefine[] = {
+ {"help", N_("undefine a physical host interface (remove it from configuration)")},
+ {"desc", N_("undefine an interface.")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_dump[] = {
- {"live", VSH_OT_BOOL, 0, N_("perform a live core dump if supported")},
- {"crash", VSH_OT_BOOL, 0, N_("crash the domain after core dump")},
- {"bypass-cache", VSH_OT_BOOL, 0,
- N_("avoid file system cache when saving")},
- {"reset", VSH_OT_BOOL, 0, N_("reset the domain after core dump")},
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("where to dump the core")},
- {"verbose", VSH_OT_BOOL, 0, N_("display the progress of dump")},
- {"memory-only", VSH_OT_BOOL, 0, N_("dump domain's memory only")},
+static const vshCmdOptDef opts_interface_undefine[] = {
+ {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name or MAC address")},
{NULL, 0, 0, NULL}
};
-static void
-doDump(void *opaque)
-{
- char ret = '1';
- vshCtrlData *data = opaque;
- vshControl *ctl = data->ctl;
- const vshCmd *cmd = data->cmd;
- virDomainPtr dom = NULL;
- sigset_t sigmask, oldsigmask;
- const char *name = NULL;
- const char *to = NULL;
- unsigned int flags = 0;
-
- sigemptyset(&sigmask);
- sigaddset(&sigmask, SIGINT);
- if (pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask) < 0)
- goto out_sig;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- goto out;
-
- if (vshCommandOptString(cmd, "file", &to) <= 0)
- goto out;
-
- if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
- goto out;
-
- if (vshCommandOptBool(cmd, "live"))
- flags |= VIR_DUMP_LIVE;
- if (vshCommandOptBool(cmd, "crash"))
- flags |= VIR_DUMP_CRASH;
- if (vshCommandOptBool(cmd, "bypass-cache"))
- flags |= VIR_DUMP_BYPASS_CACHE;
- if (vshCommandOptBool(cmd, "reset"))
- flags |= VIR_DUMP_RESET;
- if (vshCommandOptBool(cmd, "memory-only"))
- flags |= VIR_DUMP_MEMORY_ONLY;
-
- if (virDomainCoreDump(dom, to, flags) < 0) {
- vshError(ctl, _("Failed to core dump domain %s to %s"), name, to);
- goto out;
- }
-
- ret = '0';
-out:
- pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
-out_sig:
- if (dom)
- virDomainFree(dom);
- ignore_value(safewrite(data->writefd, &ret, sizeof(ret)));
-}
-
static bool
-cmdDump(vshControl *ctl, const vshCmd *cmd)
+cmdInterfaceUndefine(vshControl *ctl, const vshCmd *cmd)
{
- virDomainPtr dom;
- int p[2] = { -1, -1};
- bool ret = false;
- bool verbose = false;
- const char *name = NULL;
- const char *to = NULL;
- vshCtrlData data;
- virThread workerThread;
+ virInterfacePtr iface;
+ bool ret = true;
+ const char *name;
- if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+ if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (vshCommandOptString(cmd, "file", &to) <= 0)
+ if (!(iface = vshCommandOptInterface(ctl, cmd, &name)))
return false;
- if (vshCommandOptBool(cmd, "verbose"))
- verbose = true;
-
- if (pipe(p) < 0)
- goto cleanup;
-
- data.ctl = ctl;
- data.cmd = cmd;
- data.writefd = p[1];
-
- if (virThreadCreate(&workerThread,
- true,
- doDump,
- &data) < 0)
- goto cleanup;
-
- ret = vshWatchJob(ctl, dom, verbose, p[0], 0, NULL, NULL, _("Dump"));
-
- virThreadJoin(&workerThread);
-
- if (ret)
- vshPrint(ctl, _("\nDomain %s dumped to %s\n"), name, to);
+ if (virInterfaceUndefine(iface) == 0) {
+ vshPrint(ctl, _("Interface %s undefined\n"), name);
+ } else {
+ vshError(ctl, _("Failed to undefine interface %s"), name);
+ ret = false;
+ }
-cleanup:
- virDomainFree(dom);
- VIR_FORCE_CLOSE(p[0]);
- VIR_FORCE_CLOSE(p[1]);
+ virInterfaceFree(iface);
return ret;
}
-static const vshCmdInfo info_screenshot[] = {
- {"help", N_("take a screenshot of a current domain console and store it "
- "into a file")},
- {"desc", N_("screenshot of a current domain console")},
+/*
+ * "iface-start" command
+ */
+static const vshCmdInfo info_interface_start[] = {
+ {"help", N_("start a physical host interface (enable it / \"if-up\")")},
+ {"desc", N_("start a physical host interface.")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_screenshot[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {"file", VSH_OT_DATA, VSH_OFLAG_NONE, N_("where to store the screenshot")},
- {"screen", VSH_OT_INT, VSH_OFLAG_NONE, N_("ID of a screen to take screenshot of")},
+static const vshCmdOptDef opts_interface_start[] = {
+ {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name or MAC address")},
{NULL, 0, 0, NULL}
};
-static int vshStreamSink(virStreamPtr st ATTRIBUTE_UNUSED,
- const char *bytes, size_t nbytes, void *opaque)
-{
- int *fd = opaque;
-
- return safewrite(*fd, bytes, nbytes);
-}
-
-/**
- * Generate string: '-[]'
- */
-static char *
-vshGenFileName(vshControl *ctl, virDomainPtr dom, const char *mime)
-{
- char timestr[100];
- struct timeval cur_time;
- struct tm time_info;
- const char *ext = NULL;
- char *ret = NULL;
-
- /* We should be already connected, but doesn't
- * hurt to check */
- if (!vshConnectionUsability(ctl, ctl->conn))
- return NULL;
-
- if (!dom) {
- vshError(ctl, "%s", _("Invalid domain supplied"));
- return NULL;
- }
-
- if (STREQ(mime, "image/x-portable-pixmap"))
- ext = ".ppm";
- else if (STREQ(mime, "image/png"))
- ext = ".png";
- /* add mime type here */
-
- gettimeofday(&cur_time, NULL);
- localtime_r(&cur_time.tv_sec, &time_info);
- strftime(timestr, sizeof(timestr), "%Y-%m-%d-%H:%M:%S", &time_info);
-
- if (virAsprintf(&ret, "%s-%s%s", virDomainGetName(dom),
- timestr, ext ? ext : "") < 0) {
- vshError(ctl, "%s", _("Out of memory"));
- return NULL;
- }
-
- return ret;
-}
-
static bool
-cmdScreenshot(vshControl *ctl, const vshCmd *cmd)
+cmdInterfaceStart(vshControl *ctl, const vshCmd *cmd)
{
- virDomainPtr dom;
- const char *name = NULL;
- char *file = NULL;
- int fd = -1;
- virStreamPtr st = NULL;
- unsigned int screen = 0;
- unsigned int flags = 0; /* currently unused */
- int ret = false;
- bool created = false;
- bool generated = false;
- char *mime = NULL;
+ virInterfacePtr iface;
+ bool ret = true;
+ const char *name;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (vshCommandOptString(cmd, "file", (const char **) &file) < 0) {
- vshError(ctl, "%s", _("file must not be empty"));
- return false;
- }
-
- if (vshCommandOptUInt(cmd, "screen", &screen) < 0) {
- vshError(ctl, "%s", _("invalid screen ID"));
- return false;
- }
-
- if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+ if (!(iface = vshCommandOptInterface(ctl, cmd, &name)))
return false;
- st = virStreamNew(ctl->conn, 0);
-
- mime = virDomainScreenshot(dom, st, screen, flags);
- if (!mime) {
- vshError(ctl, _("could not take a screenshot of %s"), name);
- goto cleanup;
- }
-
- if (!file) {
- if (!(file=vshGenFileName(ctl, dom, mime)))
- return false;
- generated = true;
- }
-
- if ((fd = open(file, O_WRONLY|O_CREAT|O_EXCL, 0666)) < 0) {
- if (errno != EEXIST ||
- (fd = open(file, O_WRONLY|O_TRUNC, 0666)) < 0) {
- vshError(ctl, _("cannot create file %s"), file);
- goto cleanup;
- }
+ if (virInterfaceCreate(iface, 0) == 0) {
+ vshPrint(ctl, _("Interface %s started\n"), name);
} else {
- created = true;
- }
-
- if (virStreamRecvAll(st, vshStreamSink, &fd) < 0) {
- vshError(ctl, _("could not receive data from domain %s"), name);
- goto cleanup;
- }
-
- if (VIR_CLOSE(fd) < 0) {
- vshError(ctl, _("cannot close file %s"), file);
- goto cleanup;
- }
-
- if (virStreamFinish(st) < 0) {
- vshError(ctl, _("cannot close stream on domain %s"), name);
- goto cleanup;
+ vshError(ctl, _("Failed to start interface %s"), name);
+ ret = false;
}
- vshPrint(ctl, _("Screenshot saved to %s, with type of %s"), file, mime);
- ret = true;
-
-cleanup:
- if (!ret && created)
- unlink(file);
- if (generated)
- VIR_FREE(file);
- virDomainFree(dom);
- if (st)
- virStreamFree(st);
- VIR_FORCE_CLOSE(fd);
- VIR_FREE(mime);
+ virInterfaceFree(iface);
return ret;
}
/*
- * "resume" command
+ * "iface-destroy" command
*/
-static const vshCmdInfo info_resume[] = {
- {"help", N_("resume a domain")},
- {"desc", N_("Resume a previously suspended domain.")},
+static const vshCmdInfo info_interface_destroy[] = {
+ {"help", N_("destroy a physical host interface (disable it / \"if-down\")")},
+ {"desc", N_("forcefully stop a physical host interface.")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_resume[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+static const vshCmdOptDef opts_interface_destroy[] = {
+ {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name or MAC address")},
{NULL, 0, 0, NULL}
};
static bool
-cmdResume(vshControl *ctl, const vshCmd *cmd)
+cmdInterfaceDestroy(vshControl *ctl, const vshCmd *cmd)
{
- virDomainPtr dom;
+ virInterfacePtr iface;
bool ret = true;
const char *name;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+ if (!(iface = vshCommandOptInterface(ctl, cmd, &name)))
return false;
- if (virDomainResume(dom) == 0) {
- vshPrint(ctl, _("Domain %s resumed\n"), name);
+ if (virInterfaceDestroy(iface, 0) == 0) {
+ vshPrint(ctl, _("Interface %s destroyed\n"), name);
} else {
- vshError(ctl, _("Failed to resume domain %s"), name);
+ vshError(ctl, _("Failed to destroy interface %s"), name);
ret = false;
}
- virDomainFree(dom);
+ virInterfaceFree(iface);
return ret;
}
/*
- * "shutdown" command
+ * "iface-begin" command
*/
-static const vshCmdInfo info_shutdown[] = {
- {"help", N_("gracefully shutdown a domain")},
- {"desc", N_("Run shutdown in the target domain.")},
+static const vshCmdInfo info_interface_begin[] = {
+ {"help", N_("create a snapshot of current interfaces settings, "
+ "which can be later committed (iface-commit) or "
+ "restored (iface-rollback)")},
+ {"desc", N_("Create a restore point for interfaces settings")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_shutdown[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {"mode", VSH_OT_STRING, VSH_OFLAG_NONE, N_("shutdown mode: acpi|agent")},
+static const vshCmdOptDef opts_interface_begin[] = {
{NULL, 0, 0, NULL}
};
static bool
-cmdShutdown(vshControl *ctl, const vshCmd *cmd)
+cmdInterfaceBegin(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
- virDomainPtr dom;
- bool ret = true;
- const char *name;
- const char *mode = NULL;
- int flags = 0;
- int rv;
-
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (vshCommandOptString(cmd, "mode", &mode) < 0) {
- vshError(ctl, "%s", _("Invalid type"));
- return false;
- }
-
- if (mode) {
- if (STREQ(mode, "acpi")) {
- flags |= VIR_DOMAIN_SHUTDOWN_ACPI_POWER_BTN;
- } else if (STREQ(mode, "agent")) {
- flags |= VIR_DOMAIN_SHUTDOWN_GUEST_AGENT;
- } else {
- vshError(ctl, _("Unknown mode %s value, expecting 'acpi' or 'agent'"), mode);
- return false;
- }
- }
-
- if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+ if (virInterfaceChangeBegin(ctl->conn, 0) < 0) {
+ vshError(ctl, "%s", _("Failed to begin network config change transaction"));
return false;
-
- if (flags)
- rv = virDomainShutdownFlags(dom, flags);
- else
- rv = virDomainShutdown(dom);
- if (rv == 0) {
- vshPrint(ctl, _("Domain %s is being shutdown\n"), name);
- } else {
- vshError(ctl, _("Failed to shutdown domain %s"), name);
- ret = false;
}
- virDomainFree(dom);
- return ret;
+ vshPrint(ctl, "%s", _("Network config change transaction started\n"));
+ return true;
}
/*
- * "reboot" command
+ * "iface-commit" command
*/
-static const vshCmdInfo info_reboot[] = {
- {"help", N_("reboot a domain")},
- {"desc", N_("Run a reboot command in the target domain.")},
+static const vshCmdInfo info_interface_commit[] = {
+ {"help", N_("commit changes made since iface-begin and free restore point")},
+ {"desc", N_("commit changes and free restore point")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_reboot[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {"mode", VSH_OT_STRING, VSH_OFLAG_NONE, N_("shutdown mode: acpi|agent")},
+static const vshCmdOptDef opts_interface_commit[] = {
{NULL, 0, 0, NULL}
};
static bool
-cmdReboot(vshControl *ctl, const vshCmd *cmd)
+cmdInterfaceCommit(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
- virDomainPtr dom;
- bool ret = true;
- const char *name;
- const char *mode = NULL;
- int flags = 0;
-
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (vshCommandOptString(cmd, "mode", &mode) < 0) {
- vshError(ctl, "%s", _("Invalid type"));
- return false;
- }
-
- if (mode) {
- if (STREQ(mode, "acpi")) {
- flags |= VIR_DOMAIN_SHUTDOWN_ACPI_POWER_BTN;
- } else if (STREQ(mode, "agent")) {
- flags |= VIR_DOMAIN_SHUTDOWN_GUEST_AGENT;
- } else {
- vshError(ctl, _("Unknown mode %s value, expecting 'acpi' or 'agent'"), mode);
- return false;
- }
- }
-
- if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+ if (virInterfaceChangeCommit(ctl->conn, 0) < 0) {
+ vshError(ctl, "%s", _("Failed to commit network config change transaction"));
return false;
-
- if (virDomainReboot(dom, flags) == 0) {
- vshPrint(ctl, _("Domain %s is being rebooted\n"), name);
- } else {
- vshError(ctl, _("Failed to reboot domain %s"), name);
- ret = false;
}
- virDomainFree(dom);
- return ret;
+ vshPrint(ctl, "%s", _("Network config change transaction committed\n"));
+ return true;
}
/*
- * "reset" command
+ * "iface-rollback" command
*/
-static const vshCmdInfo info_reset[] = {
- {"help", N_("reset a domain")},
- {"desc", N_("Reset the target domain as if by power button")},
+static const vshCmdInfo info_interface_rollback[] = {
+ {"help", N_("rollback to previous saved configuration created via iface-begin")},
+ {"desc", N_("rollback to previous restore point")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_reset[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+static const vshCmdOptDef opts_interface_rollback[] = {
{NULL, 0, 0, NULL}
};
static bool
-cmdReset(vshControl *ctl, const vshCmd *cmd)
+cmdInterfaceRollback(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
- virDomainPtr dom;
- bool ret = true;
- const char *name;
-
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
+ if (virInterfaceChangeRollback(ctl->conn, 0) < 0) {
+ vshError(ctl, "%s", _("Failed to rollback network config change transaction"));
return false;
-
- if (virDomainReset(dom, 0) == 0) {
- vshPrint(ctl, _("Domain %s was reset\n"), name);
- } else {
- vshError(ctl, _("Failed to reset domain %s"), name);
- ret = false;
}
- virDomainFree(dom);
- return ret;
+ vshPrint(ctl, "%s", _("Network config change transaction rolled back\n"));
+ return true;
}
/*
- * "destroy" command
+ * "iface-bridge" command
*/
-static const vshCmdInfo info_destroy[] = {
- {"help", N_("destroy (stop) a domain")},
- {"desc",
- N_("Forcefully stop a given domain, but leave its resources intact.")},
- {NULL, NULL}
+static const vshCmdInfo info_interface_bridge[] = {
+ {"help", N_("create a bridge device and attach an existing network device to it")},
+ {"desc", N_("bridge an existing network device")},
+ {NULL, NULL}
};
-static const vshCmdOptDef opts_destroy[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {"graceful", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("terminate gracefully")},
+static const vshCmdOptDef opts_interface_bridge[] = {
+ {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("existing interface name")},
+ {"bridge", VSH_OT_DATA, VSH_OFLAG_REQ, N_("new bridge device name")},
+ {"no-stp", VSH_OT_BOOL, 0, N_("do not enable STP for this bridge")},
+ {"delay", VSH_OT_INT, 0,
+ N_("number of seconds to squelch traffic on newly connected ports")},
+ {"no-start", VSH_OT_BOOL, 0, N_("don't start the bridge immediately")},
{NULL, 0, 0, NULL}
};
static bool
-cmdDestroy(vshControl *ctl, const vshCmd *cmd)
+cmdInterfaceBridge(vshControl *ctl, const vshCmd *cmd)
{
- virDomainPtr dom;
- bool ret = true;
- const char *name;
- unsigned int flags = 0;
- int result;
+ bool ret = false;
+ virInterfacePtr if_handle = NULL, br_handle = NULL;
+ const char *if_name, *br_name;
+ char *if_type = NULL, *if2_name = NULL, *delay_str = NULL;
+ bool stp = false, nostart = false;
+ unsigned int delay = 0;
+ char *if_xml = NULL;
+ xmlChar *br_xml = NULL;
+ int br_xml_size;
+ xmlDocPtr xml_doc = NULL;
+ xmlXPathContextPtr ctxt = NULL;
+ xmlNodePtr top_node, br_node, if_node, cur;
if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
+ goto cleanup;
- if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
- return false;
+ /* Get a handle to the original device */
+ if (!(if_handle = vshCommandOptInterfaceBy(ctl, cmd, "interface",
+ &if_name, VSH_BYNAME))) {
+ goto cleanup;
+ }
- if (vshCommandOptBool(cmd, "graceful"))
- flags |= VIR_DOMAIN_DESTROY_GRACEFUL;
+ /* Name for new bridge device */
+ if (vshCommandOptString(cmd, "bridge", &br_name) <= 0) {
+ vshError(ctl, "%s", _("Missing bridge device name in command"));
+ goto cleanup;
+ }
- if (flags)
- result = virDomainDestroyFlags(dom, VIR_DOMAIN_DESTROY_GRACEFUL);
- else
- result = virDomainDestroy(dom);
+ /* make sure "new" device doesn't already exist */
+ if ((br_handle = virInterfaceLookupByName(ctl->conn, br_name))) {
+ vshError(ctl, _("Network device %s already exists"), br_name);
+ goto cleanup;
+ }
- if (result == 0) {
- vshPrint(ctl, _("Domain %s destroyed\n"), name);
- } else {
- vshError(ctl, _("Failed to destroy domain %s"), name);
- ret = false;
+ /* use "no-stp" because we want "stp" to default true */
+ stp = !vshCommandOptBool(cmd, "no-stp");
+
+ if (vshCommandOptUInt(cmd, "delay", &delay) < 0) {
+ vshError(ctl, "%s", _("Unable to parse delay parameter"));
+ goto cleanup;
}
- virDomainFree(dom);
- return ret;
-}
+ nostart = vshCommandOptBool(cmd, "no-start");
-/*
- * "domjobinfo" command
- */
-static const vshCmdInfo info_domjobinfo[] = {
- {"help", N_("domain job information")},
- {"desc", N_("Returns information about jobs running on a domain.")},
- {NULL, NULL}
-};
+ /* Get the original interface into an xmlDoc */
+ if (!(if_xml = virInterfaceGetXMLDesc(if_handle, VIR_INTERFACE_XML_INACTIVE)))
+ goto cleanup;
+ if (!(xml_doc = virXMLParseStringCtxt(if_xml,
+ _("(interface definition)"), &ctxt))) {
+ vshError(ctl, _("Failed to parse configuration of %s"), if_name);
+ goto cleanup;
+ }
+ top_node = ctxt->node;
-static const vshCmdOptDef opts_domjobinfo[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {NULL, 0, 0, NULL}
-};
+ /* Verify that the original device isn't already a bridge. */
+ if (!(if_type = virXMLPropString(top_node, "type"))) {
+ vshError(ctl, _("Existing device %s has no type"), if_name);
+ goto cleanup;
+ }
+ if (STREQ(if_type, "bridge")) {
+ vshError(ctl, _("Existing device %s is already a bridge"), if_name);
+ goto cleanup;
+ }
-static bool
-cmdDomjobinfo(vshControl *ctl, const vshCmd *cmd)
-{
- virDomainJobInfo info;
- virDomainPtr dom;
- bool ret = true;
+ /* verify the name in the XML matches the device name */
+ if (!(if2_name = virXMLPropString(top_node, "name")) ||
+ STRNEQ(if2_name, if_name)) {
+ vshError(ctl, _("Interface name from config %s doesn't match given supplied name %s"),
+ if2_name, if_name);
+ goto cleanup;
+ }
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
+ /* Create a node under . */
+ if (!(br_node = xmlNewChild(top_node, NULL, BAD_CAST "bridge", NULL))) {
+ vshError(ctl, "%s", _("Failed to create bridge node in xml document"));
+ goto cleanup;
+ }
- if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
- return false;
+ /* Set stp and delay attributes in according to the
+ * commandline options.
+ */
+ if (!xmlSetProp(br_node, BAD_CAST "stp", BAD_CAST (stp ? "on" : "off"))) {
+ vshError(ctl, "%s", _("Failed to set stp attribute in xml document"));
+ goto cleanup;
+ }
- if (virDomainGetJobInfo(dom, &info) == 0) {
- const char *unit;
- double val;
+ if ((delay || stp) &&
+ ((virAsprintf(&delay_str, "%d", delay) < 0) ||
+ !xmlSetProp(br_node, BAD_CAST "delay", BAD_CAST delay_str))) {
+ vshError(ctl, _("Failed to set bridge delay %d in xml document"), delay);
+ goto cleanup;
+ }
- vshPrint(ctl, "%-17s ", _("Job type:"));
- switch (info.type) {
- case VIR_DOMAIN_JOB_BOUNDED:
- vshPrint(ctl, "%-12s\n", _("Bounded"));
- break;
+ /* Change the type of the outer/master interface to "bridge" and the
+ * name to the provided bridge name.
+ */
+ if (!xmlSetProp(top_node, BAD_CAST "type", BAD_CAST "bridge")) {
+ vshError(ctl, "%s", _("Failed to set bridge interface type to 'bridge' in xml document"));
+ goto cleanup;
+ }
- case VIR_DOMAIN_JOB_UNBOUNDED:
- vshPrint(ctl, "%-12s\n", _("Unbounded"));
- break;
+ if (!xmlSetProp(top_node, BAD_CAST "name", BAD_CAST br_name)) {
+ vshError(ctl, _("Failed to set master bridge interface name to '%s' in xml document"),
+ br_name);
+ goto cleanup;
+ }
- case VIR_DOMAIN_JOB_NONE:
- default:
- vshPrint(ctl, "%-12s\n", _("None"));
- goto cleanup;
- }
+ /* Create an node under that uses the
+ * original interface's type and name.
+ */
+ if (!(if_node = xmlNewChild(br_node, NULL, BAD_CAST "interface", NULL))) {
+ vshError(ctl, "%s", _("Failed to create interface node under bridge node in xml document"));
+ goto cleanup;
+ }
- vshPrint(ctl, "%-17s %-12llu ms\n", _("Time elapsed:"), info.timeElapsed);
- if (info.type == VIR_DOMAIN_JOB_BOUNDED)
- vshPrint(ctl, "%-17s %-12llu ms\n", _("Time remaining:"), info.timeRemaining);
- if (info.dataTotal || info.dataRemaining || info.dataProcessed) {
- val = prettyCapacity(info.dataProcessed, &unit);
- vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data processed:"), val, unit);
- val = prettyCapacity(info.dataRemaining, &unit);
- vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data remaining:"), val, unit);
- val = prettyCapacity(info.dataTotal, &unit);
- vshPrint(ctl, "%-17s %-.3lf %s\n", _("Data total:"), val, unit);
- }
- if (info.memTotal || info.memRemaining || info.memProcessed) {
- val = prettyCapacity(info.memProcessed, &unit);
- vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory processed:"), val, unit);
- val = prettyCapacity(info.memRemaining, &unit);
- vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory remaining:"), val, unit);
- val = prettyCapacity(info.memTotal, &unit);
- vshPrint(ctl, "%-17s %-.3lf %s\n", _("Memory total:"), val, unit);
- }
- if (info.fileTotal || info.fileRemaining || info.fileProcessed) {
- val = prettyCapacity(info.fileProcessed, &unit);
- vshPrint(ctl, "%-17s %-.3lf %s\n", _("File processed:"), val, unit);
- val = prettyCapacity(info.fileRemaining, &unit);
- vshPrint(ctl, "%-17s %-.3lf %s\n", _("File remaining:"), val, unit);
- val = prettyCapacity(info.fileTotal, &unit);
- vshPrint(ctl, "%-17s %-.3lf %s\n", _("File total:"), val, unit);
+ /* set the type of the inner/slave interface to the original
+ * if_type, and the name to the original if_name.
+ */
+ if (!xmlSetProp(if_node, BAD_CAST "type", BAD_CAST if_type)) {
+ vshError(ctl, _("Failed to set new slave interface type to '%s' in xml document"),
+ if_name);
+ goto cleanup;
+ }
+
+ if (!xmlSetProp(if_node, BAD_CAST "name", BAD_CAST if_name)) {
+ vshError(ctl, _("Failed to set new slave interface name to '%s' in xml document"),
+ br_name);
+ goto cleanup;
+ }
+
+ /* Cycle through all the nodes under the original ,
+ * moving all , and nodes down into the new
+ * lower level .
+ */
+ cur = top_node->children;
+ while (cur) {
+ xmlNodePtr old = cur;
+
+ cur = cur->next;
+ if ((old->type == XML_ELEMENT_NODE) &&
+ (xmlStrEqual(old->name, BAD_CAST "mac") || /* ethernet stuff to move down */
+ xmlStrEqual(old->name, BAD_CAST "bond") || /* bond stuff to move down */
+ xmlStrEqual(old->name, BAD_CAST "vlan"))) { /* vlan stuff to move down */
+ xmlUnlinkNode(old);
+ if (!xmlAddChild(if_node, old)) {
+ vshError(ctl, _("Failed to move '%s' element in xml document"), old->name);
+ xmlFreeNode(old);
+ goto cleanup;
+ }
}
- } else {
- ret = false;
}
-cleanup:
- virDomainFree(dom);
- return ret;
-}
-/*
- * "domjobabort" command
- */
-static const vshCmdInfo info_domjobabort[] = {
- {"help", N_("abort active domain job")},
- {"desc", N_("Aborts the currently running domain job")},
- {NULL, NULL}
-};
+ /* The document should now be fully converted; write it out to a string. */
+ xmlDocDumpMemory(xml_doc, &br_xml, &br_xml_size);
-static const vshCmdOptDef opts_domjobabort[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {NULL, 0, 0, NULL}
-};
+ if (!br_xml || br_xml_size <= 0) {
+ vshError(ctl, _("Failed to format new xml document for bridge %s"), br_name);
+ goto cleanup;
+ }
-static bool
-cmdDomjobabort(vshControl *ctl, const vshCmd *cmd)
-{
- virDomainPtr dom;
- bool ret = true;
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
+ /* br_xml is the new interface to define. It will automatically undefine the
+ * independent original interface.
+ */
+ if (!(br_handle = virInterfaceDefineXML(ctl->conn, (char *) br_xml, 0))) {
+ vshError(ctl, _("Failed to define new bridge interface %s"),
+ br_name);
+ goto cleanup;
+ }
- if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
- return false;
+ vshPrint(ctl, _("Created bridge %s with attached device %s\n"),
+ br_name, if_name);
- if (virDomainAbortJob(dom) < 0)
- ret = false;
+ /* start it up unless requested not to */
+ if (!nostart) {
+ if (virInterfaceCreate(br_handle, 0) < 0) {
+ vshError(ctl, _("Failed to start bridge interface %s"), br_name);
+ goto cleanup;
+ }
+ vshPrint(ctl, _("Bridge interface %s started\n"), br_name);
+ }
- virDomainFree(dom);
+ ret = true;
+ cleanup:
+ if (if_handle)
+ virInterfaceFree(if_handle);
+ if (br_handle)
+ virInterfaceFree(br_handle);
+ VIR_FREE(if_xml);
+ VIR_FREE(br_xml);
+ VIR_FREE(if_type);
+ VIR_FREE(if2_name);
+ VIR_FREE(delay_str);
+ xmlXPathFreeContext(ctxt);
+ xmlFreeDoc(xml_doc);
return ret;
}
/*
- * "freecell" command
+ * "iface-unbridge" command
*/
-static const vshCmdInfo info_freecell[] = {
- {"help", N_("NUMA free memory")},
- {"desc", N_("display available free memory for the NUMA cell.")},
+static const vshCmdInfo info_interface_unbridge[] = {
+ {"help", N_("undefine a bridge device after detaching its slave device")},
+ {"desc", N_("unbridge a network device")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_freecell[] = {
- {"cellno", VSH_OT_INT, 0, N_("NUMA cell number")},
- {"all", VSH_OT_BOOL, 0, N_("show free memory for all NUMA cells")},
+static const vshCmdOptDef opts_interface_unbridge[] = {
+ {"bridge", VSH_OT_DATA, VSH_OFLAG_REQ, N_("current bridge device name")},
+ {"no-start", VSH_OT_BOOL, 0,
+ N_("don't start the un-slaved interface immediately (not recommended)")},
{NULL, 0, 0, NULL}
};
static bool
-cmdFreecell(vshControl *ctl, const vshCmd *cmd)
+cmdInterfaceUnbridge(vshControl *ctl, const vshCmd *cmd)
{
- bool func_ret = false;
- int ret;
- int cell = -1, cell_given;
- unsigned long long memory;
- xmlNodePtr *nodes = NULL;
- unsigned long nodes_cnt;
- unsigned long *nodes_id = NULL;
- unsigned long long *nodes_free = NULL;
- int all_given;
- int i;
- char *cap_xml = NULL;
- xmlDocPtr xml = NULL;
+ bool ret = false;
+ virInterfacePtr if_handle = NULL, br_handle = NULL;
+ const char *br_name;
+ char *if_type = NULL, *if_name = NULL;
+ bool nostart = false;
+ char *br_xml = NULL;
+ xmlChar *if_xml = NULL;
+ int if_xml_size;
+ xmlDocPtr xml_doc = NULL;
xmlXPathContextPtr ctxt = NULL;
-
+ xmlNodePtr top_node, br_node, if_node, cur;
if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
+ goto cleanup;
- if ( (cell_given = vshCommandOptInt(cmd, "cellno", &cell)) < 0) {
- vshError(ctl, "%s", _("cell number has to be a number"));
+ /* Get a handle to the original device */
+ if (!(br_handle = vshCommandOptInterfaceBy(ctl, cmd, "bridge",
+ &br_name, VSH_BYNAME))) {
goto cleanup;
}
- all_given = vshCommandOptBool(cmd, "all");
- if (all_given && cell_given) {
- vshError(ctl, "%s", _("--cellno and --all are mutually exclusive. "
- "Please choose only one."));
+ nostart = vshCommandOptBool(cmd, "no-start");
+
+ /* Get the bridge xml into an xmlDoc */
+ if (!(br_xml = virInterfaceGetXMLDesc(br_handle, VIR_INTERFACE_XML_INACTIVE)))
+ goto cleanup;
+ if (!(xml_doc = virXMLParseStringCtxt(br_xml,
+ _("(bridge interface definition)"),
+ &ctxt))) {
+ vshError(ctl, _("Failed to parse configuration of %s"), br_name);
goto cleanup;
}
+ top_node = ctxt->node;
- if (all_given) {
- cap_xml = virConnectGetCapabilities(ctl->conn);
- if (!cap_xml) {
- vshError(ctl, "%s", _("unable to get node capabilities"));
- goto cleanup;
- }
+ /* Verify that the device really is a bridge. */
+ if (!(if_type = virXMLPropString(top_node, "type"))) {
+ vshError(ctl, _("Existing device %s has no type"), br_name);
+ goto cleanup;
+ }
- xml = virXMLParseStringCtxt(cap_xml, _("(capabilities)"), &ctxt);
- if (!xml) {
- vshError(ctl, "%s", _("unable to get node capabilities"));
- goto cleanup;
- }
- nodes_cnt = virXPathNodeSet("/capabilities/host/topology/cells/cell",
- ctxt, &nodes);
+ if (STRNEQ(if_type, "bridge")) {
+ vshError(ctl, _("Device %s is not a bridge"), br_name);
+ goto cleanup;
+ }
+ VIR_FREE(if_type);
- if (nodes_cnt == -1) {
- vshError(ctl, "%s", _("could not get information about "
- "NUMA topology"));
- goto cleanup;
- }
+ /* verify the name in the XML matches the device name */
+ if (!(if_name = virXMLPropString(top_node, "name")) ||
+ STRNEQ(if_name, br_name)) {
+ vshError(ctl, _("Interface name from config %s doesn't match given supplied name %s"),
+ if_name, br_name);
+ goto cleanup;
+ }
+ VIR_FREE(if_name);
- nodes_free = vshCalloc(ctl, nodes_cnt, sizeof(*nodes_free));
- nodes_id = vshCalloc(ctl, nodes_cnt, sizeof(*nodes_id));
+ /* Find the node under . */
+ if (!(br_node = virXPathNode("./bridge", ctxt))) {
+ vshError(ctl, "%s", _("No bridge node in xml document"));
+ goto cleanup;
+ }
- for (i = 0; i < nodes_cnt; i++) {
- unsigned long id;
- char *val = virXMLPropString(nodes[i], "id");
- if (virStrToLong_ul(val, NULL, 10, &id)) {
- vshError(ctl, "%s", _("conversion from string failed"));
- VIR_FREE(val);
- goto cleanup;
- }
- VIR_FREE(val);
- nodes_id[i]=id;
- ret = virNodeGetCellsFreeMemory(ctl->conn, &(nodes_free[i]), id, 1);
- if (ret != 1) {
- vshError(ctl, _("failed to get free memory for NUMA node "
- "number: %lu"), id);
+ if ((if_node = virXPathNode("./bridge/interface[2]", ctxt))) {
+ vshError(ctl, "%s", _("Multiple interfaces attached to bridge"));
+ goto cleanup;
+ }
+
+ if (!(if_node = virXPathNode("./bridge/interface", ctxt))) {
+ vshError(ctl, "%s", _("No interface attached to bridge"));
+ goto cleanup;
+ }
+
+ /* Change the type and name of the outer/master interface to
+ * the type/name of the attached slave interface.
+ */
+ if (!(if_name = virXMLPropString(if_node, "name"))) {
+ vshError(ctl, _("Device attached to bridge %s has no name"), br_name);
+ goto cleanup;
+ }
+
+ if (!(if_type = virXMLPropString(if_node, "type"))) {
+ vshError(ctl, _("Attached device %s has no type"), if_name);
+ goto cleanup;
+ }
+
+ if (!xmlSetProp(top_node, BAD_CAST "type", BAD_CAST if_type)) {
+ vshError(ctl, _("Failed to set interface type to '%s' in xml document"),
+ if_type);
+ goto cleanup;
+ }
+
+ if (!xmlSetProp(top_node, BAD_CAST "name", BAD_CAST if_name)) {
+ vshError(ctl, _("Failed to set interface name to '%s' in xml document"),
+ if_name);
+ goto cleanup;
+ }
+
+ /* Cycle through all the nodes under the attached ,
+ * moving all , and nodes up into the toplevel
+ * .
+ */
+ cur = if_node->children;
+ while (cur) {
+ xmlNodePtr old = cur;
+
+ cur = cur->next;
+ if ((old->type == XML_ELEMENT_NODE) &&
+ (xmlStrEqual(old->name, BAD_CAST "mac") || /* ethernet stuff to move down */
+ xmlStrEqual(old->name, BAD_CAST "bond") || /* bond stuff to move down */
+ xmlStrEqual(old->name, BAD_CAST "vlan"))) { /* vlan stuff to move down */
+ xmlUnlinkNode(old);
+ if (!xmlAddChild(top_node, old)) {
+ vshError(ctl, _("Failed to move '%s' element in xml document"), old->name);
+ xmlFreeNode(old);
goto cleanup;
}
}
+ }
- memory = 0;
- for (cell = 0; cell < nodes_cnt; cell++) {
- vshPrint(ctl, "%5lu: %10llu KiB\n", nodes_id[cell],
- (nodes_free[cell]/1024));
- memory += nodes_free[cell];
- }
+ /* The document should now be fully converted; write it out to a string. */
+ xmlDocDumpMemory(xml_doc, &if_xml, &if_xml_size);
- vshPrintExtra(ctl, "--------------------\n");
- vshPrintExtra(ctl, "%5s: %10llu KiB\n", _("Total"), memory/1024);
- } else {
- if (!cell_given) {
- memory = virNodeGetFreeMemory(ctl->conn);
- if (memory == 0)
- goto cleanup;
- } else {
- ret = virNodeGetCellsFreeMemory(ctl->conn, &memory, cell, 1);
- if (ret != 1)
- goto cleanup;
- }
+ if (!if_xml || if_xml_size <= 0) {
+ vshError(ctl, _("Failed to format new xml document for un-enslaved interface %s"),
+ if_name);
+ goto cleanup;
+ }
- if (cell == -1)
- vshPrint(ctl, "%s: %llu KiB\n", _("Total"), (memory/1024));
- else
- vshPrint(ctl, "%d: %llu KiB\n", cell, (memory/1024));
+ /* Destroy and Undefine the bridge device, since we otherwise
+ * can't safely define the unattached device.
+ */
+ if (virInterfaceDestroy(br_handle, 0) < 0) {
+ vshError(ctl, _("Failed to destroy bridge interface %s"), br_name);
+ goto cleanup;
+ }
+ if (virInterfaceUndefine(br_handle) < 0) {
+ vshError(ctl, _("Failed to undefine bridge interface %s"), br_name);
+ goto cleanup;
}
- func_ret = true;
+ /* if_xml is the new interface to define.
+ */
+ if (!(if_handle = virInterfaceDefineXML(ctl->conn, (char *) if_xml, 0))) {
+ vshError(ctl, _("Failed to define new interface %s"), if_name);
+ goto cleanup;
+ }
-cleanup:
+ vshPrint(ctl, _("Device %s un-attached from bridge %s\n"),
+ if_name, br_name);
+
+ /* unless requested otherwise, undefine the bridge device */
+ if (!nostart) {
+ if (virInterfaceCreate(if_handle, 0) < 0) {
+ vshError(ctl, _("Failed to start interface %s"), if_name);
+ goto cleanup;
+ }
+ vshPrint(ctl, _("Interface %s started\n"), if_name);
+ }
+
+ ret = true;
+ cleanup:
+ if (if_handle)
+ virInterfaceFree(if_handle);
+ if (br_handle)
+ virInterfaceFree(br_handle);
+ VIR_FREE(if_xml);
+ VIR_FREE(br_xml);
+ VIR_FREE(if_type);
+ VIR_FREE(if_name);
xmlXPathFreeContext(ctxt);
- xmlFreeDoc(xml);
- VIR_FREE(nodes);
- VIR_FREE(nodes_free);
- VIR_FREE(nodes_id);
- VIR_FREE(cap_xml);
- return func_ret;
+ xmlFreeDoc(xml_doc);
+ return ret;
}
/*
- * "maxvcpus" command
+ * "nwfilter-define" command
*/
-static const vshCmdInfo info_maxvcpus[] = {
- {"help", N_("connection vcpu maximum")},
- {"desc", N_("Show maximum number of virtual CPUs for guests on this connection.")},
+static const vshCmdInfo info_nwfilter_define[] = {
+ {"help", N_("define or update a network filter from an XML file")},
+ {"desc", N_("Define a new network filter or update an existing one.")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_maxvcpus[] = {
- {"type", VSH_OT_STRING, 0, N_("domain type")},
+static const vshCmdOptDef opts_nwfilter_define[] = {
+ {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML network filter description")},
{NULL, 0, 0, NULL}
};
static bool
-cmdMaxvcpus(vshControl *ctl, const vshCmd *cmd)
+cmdNWFilterDefine(vshControl *ctl, const vshCmd *cmd)
{
- const char *type = NULL;
- int vcpus;
-
- if (vshCommandOptString(cmd, "type", &type) < 0) {
- vshError(ctl, "%s", _("Invalid type"));
- return false;
- }
+ virNWFilterPtr nwfilter;
+ const char *from = NULL;
+ bool ret = true;
+ char *buffer;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- vcpus = virConnectGetMaxVcpus(ctl->conn, type);
- if (vcpus < 0)
+ if (vshCommandOptString(cmd, "file", &from) <= 0)
return false;
- vshPrint(ctl, "%d\n", vcpus);
- return true;
-}
+ if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
+ return false;
-/*
- * "vcpucount" command
- */
-static const vshCmdInfo info_vcpucount[] = {
- {"help", N_("domain vcpu counts")},
- {"desc", N_("Returns the number of virtual CPUs used by the domain.")},
- {NULL, NULL}
-};
+ nwfilter = virNWFilterDefineXML(ctl->conn, buffer);
+ VIR_FREE(buffer);
-static const vshCmdOptDef opts_vcpucount[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {"maximum", VSH_OT_BOOL, 0, N_("get maximum cap on vcpus")},
- {"active", VSH_OT_BOOL, 0, N_("get number of currently active vcpus")},
- {"live", VSH_OT_BOOL, 0, N_("get value from running domain")},
- {"config", VSH_OT_BOOL, 0, N_("get value to be used on next boot")},
- {"current", VSH_OT_BOOL, 0,
- N_("get value according to current domain state")},
+ if (nwfilter != NULL) {
+ vshPrint(ctl, _("Network filter %s defined from %s\n"),
+ virNWFilterGetName(nwfilter), from);
+ virNWFilterFree(nwfilter);
+ } else {
+ vshError(ctl, _("Failed to define network filter from %s"), from);
+ ret = false;
+ }
+ return ret;
+}
+
+
+/*
+ * "nwfilter-undefine" command
+ */
+static const vshCmdInfo info_nwfilter_undefine[] = {
+ {"help", N_("undefine a network filter")},
+ {"desc", N_("Undefine a given network filter.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_nwfilter_undefine[] = {
+ {"nwfilter", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network filter name or uuid")},
{NULL, 0, 0, NULL}
};
static bool
-cmdVcpucount(vshControl *ctl, const vshCmd *cmd)
+cmdNWFilterUndefine(vshControl *ctl, const vshCmd *cmd)
{
- virDomainPtr dom;
+ virNWFilterPtr nwfilter;
bool ret = true;
- bool maximum = vshCommandOptBool(cmd, "maximum");
- bool active = vshCommandOptBool(cmd, "active");
- bool config = vshCommandOptBool(cmd, "config");
- bool live = vshCommandOptBool(cmd, "live");
- bool current = vshCommandOptBool(cmd, "current");
- bool all = maximum + active + current + config + live == 0;
- int count;
-
- /* We want one of each pair of mutually exclusive options; that
- * is, use of flags requires exactly two options. We reject the
- * use of more than 2 flags later on. */
- if (maximum + active + current + config + live == 1) {
- if (maximum || active) {
- vshError(ctl,
- _("when using --%s, one of --config, --live, or --current "
- "must be specified"),
- maximum ? "maximum" : "active");
- } else {
- vshError(ctl,
- _("when using --%s, either --maximum or --active must be "
- "specified"),
- (current ? "current" : config ? "config" : "live"));
- }
- return false;
- }
-
- /* Backwards compatibility: prior to 0.9.4,
- * VIR_DOMAIN_AFFECT_CURRENT was unsupported, and --current meant
- * the opposite of --maximum. Translate the old '--current
- * --live' into the new '--active --live', while treating the new
- * '--maximum --current' correctly rather than rejecting it as
- * '--maximum --active'. */
- if (!maximum && !active && current) {
- current = false;
- active = true;
- }
-
- if (maximum && active) {
- vshError(ctl, "%s",
- _("--maximum and --active cannot both be specified"));
- return false;
- }
- if (current + config + live > 1) {
- vshError(ctl, "%s",
- _("--config, --live, and --current are mutually exclusive"));
- return false;
- }
+ const char *name;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ if (!(nwfilter = vshCommandOptNWFilter(ctl, cmd, &name)))
return false;
- /* In all cases, try the new API first; if it fails because we are
- * talking to an older client, generally we try a fallback API
- * before giving up. --current requires the new API, since we
- * don't know whether the domain is running or inactive. */
- if (current) {
- count = virDomainGetVcpusFlags(dom,
- maximum ? VIR_DOMAIN_VCPU_MAXIMUM : 0);
- if (count < 0) {
- virshReportError(ctl);
- ret = false;
- } else {
- vshPrint(ctl, "%d\n", count);
- }
- }
-
- if (all || (maximum && config)) {
- count = virDomainGetVcpusFlags(dom, (VIR_DOMAIN_VCPU_MAXIMUM |
- VIR_DOMAIN_AFFECT_CONFIG));
- if (count < 0 && (last_error->code == VIR_ERR_NO_SUPPORT
- || last_error->code == VIR_ERR_INVALID_ARG)) {
- char *tmp;
- char *xml = virDomainGetXMLDesc(dom, VIR_DOMAIN_XML_INACTIVE);
- if (xml && (tmp = strstr(xml, "');
- if (!tmp || virStrToLong_i(tmp + 1, &tmp, 10, &count) < 0)
- count = -1;
- }
- virFreeError(last_error);
- last_error = NULL;
- VIR_FREE(xml);
- }
-
- if (count < 0) {
- virshReportError(ctl);
- ret = false;
- } else if (all) {
- vshPrint(ctl, "%-12s %-12s %3d\n", _("maximum"), _("config"),
- count);
- } else {
- vshPrint(ctl, "%d\n", count);
- }
- virFreeError(last_error);
- last_error = NULL;
- }
-
- if (all || (maximum && live)) {
- count = virDomainGetVcpusFlags(dom, (VIR_DOMAIN_VCPU_MAXIMUM |
- VIR_DOMAIN_AFFECT_LIVE));
- if (count < 0 && (last_error->code == VIR_ERR_NO_SUPPORT
- || last_error->code == VIR_ERR_INVALID_ARG)) {
- count = virDomainGetMaxVcpus(dom);
- }
-
- if (count < 0) {
- virshReportError(ctl);
- ret = false;
- } else if (all) {
- vshPrint(ctl, "%-12s %-12s %3d\n", _("maximum"), _("live"),
- count);
- } else {
- vshPrint(ctl, "%d\n", count);
- }
- virFreeError(last_error);
- last_error = NULL;
- }
-
- if (all || (active && config)) {
- count = virDomainGetVcpusFlags(dom, VIR_DOMAIN_AFFECT_CONFIG);
- if (count < 0 && (last_error->code == VIR_ERR_NO_SUPPORT
- || last_error->code == VIR_ERR_INVALID_ARG)) {
- char *tmp, *end;
- char *xml = virDomainGetXMLDesc(dom, VIR_DOMAIN_XML_INACTIVE);
- if (xml && (tmp = strstr(xml, "');
- if (end) {
- *end = '\0';
- tmp = strstr(tmp, "current=");
- if (!tmp)
- tmp = end + 1;
- else {
- tmp += strlen("current=");
- tmp += *tmp == '\'' || *tmp == '"';
- }
- }
- if (!tmp || virStrToLong_i(tmp, &tmp, 10, &count) < 0)
- count = -1;
- }
- VIR_FREE(xml);
- }
-
- if (count < 0) {
- virshReportError(ctl);
- ret = false;
- } else if (all) {
- vshPrint(ctl, "%-12s %-12s %3d\n", _("current"), _("config"),
- count);
- } else {
- vshPrint(ctl, "%d\n", count);
- }
- virFreeError(last_error);
- last_error = NULL;
- }
-
- if (all || (active && live)) {
- count = virDomainGetVcpusFlags(dom, VIR_DOMAIN_AFFECT_LIVE);
- if (count < 0 && (last_error->code == VIR_ERR_NO_SUPPORT
- || last_error->code == VIR_ERR_INVALID_ARG)) {
- virDomainInfo info;
- if (virDomainGetInfo(dom, &info) == 0)
- count = info.nrVirtCpu;
- }
-
- if (count < 0) {
- virshReportError(ctl);
- ret = false;
- } else if (all) {
- vshPrint(ctl, "%-12s %-12s %3d\n", _("current"), _("live"),
- count);
- } else {
- vshPrint(ctl, "%d\n", count);
- }
- virFreeError(last_error);
- last_error = NULL;
+ if (virNWFilterUndefine(nwfilter) == 0) {
+ vshPrint(ctl, _("Network filter %s undefined\n"), name);
+ } else {
+ vshError(ctl, _("Failed to undefine network filter %s"), name);
+ ret = false;
}
- virDomainFree(dom);
+ virNWFilterFree(nwfilter);
return ret;
}
+
/*
- * "vcpuinfo" command
+ * "nwfilter-dumpxml" command
*/
-static const vshCmdInfo info_vcpuinfo[] = {
- {"help", N_("detailed domain vcpu information")},
- {"desc", N_("Returns basic information about the domain virtual CPUs.")},
+static const vshCmdInfo info_nwfilter_dumpxml[] = {
+ {"help", N_("network filter information in XML")},
+ {"desc", N_("Output the network filter information as an XML dump to stdout.")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_vcpuinfo[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+static const vshCmdOptDef opts_nwfilter_dumpxml[] = {
+ {"nwfilter", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network filter name or uuid")},
{NULL, 0, 0, NULL}
};
static bool
-cmdVcpuinfo(vshControl *ctl, const vshCmd *cmd)
+cmdNWFilterDumpXML(vshControl *ctl, const vshCmd *cmd)
{
- virDomainInfo info;
- virDomainPtr dom;
- virNodeInfo nodeinfo;
- virVcpuInfoPtr cpuinfo;
- unsigned char *cpumaps;
- int ncpus, maxcpu;
- size_t cpumaplen;
+ virNWFilterPtr nwfilter;
bool ret = true;
- int n, m;
+ char *dump;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
- return false;
-
- if (virNodeGetInfo(ctl->conn, &nodeinfo) != 0) {
- virDomainFree(dom);
- return false;
- }
-
- if (virDomainGetInfo(dom, &info) != 0) {
- virDomainFree(dom);
+ if (!(nwfilter = vshCommandOptNWFilter(ctl, cmd, NULL)))
return false;
- }
-
- cpuinfo = vshMalloc(ctl, sizeof(virVcpuInfo)*info.nrVirtCpu);
- maxcpu = VIR_NODEINFO_MAXCPUS(nodeinfo);
- cpumaplen = VIR_CPU_MAPLEN(maxcpu);
- cpumaps = vshMalloc(ctl, info.nrVirtCpu * cpumaplen);
-
- if ((ncpus = virDomainGetVcpus(dom,
- cpuinfo, info.nrVirtCpu,
- cpumaps, cpumaplen)) >= 0) {
- for (n = 0 ; n < ncpus ; n++) {
- vshPrint(ctl, "%-15s %d\n", _("VCPU:"), n);
- vshPrint(ctl, "%-15s %d\n", _("CPU:"), cpuinfo[n].cpu);
- vshPrint(ctl, "%-15s %s\n", _("State:"),
- _(vshDomainVcpuStateToString(cpuinfo[n].state)));
- if (cpuinfo[n].cpuTime != 0) {
- double cpuUsed = cpuinfo[n].cpuTime;
-
- cpuUsed /= 1000000000.0;
- vshPrint(ctl, "%-15s %.1lfs\n", _("CPU time:"), cpuUsed);
- }
- vshPrint(ctl, "%-15s ", _("CPU Affinity:"));
- for (m = 0; m < maxcpu; m++) {
- vshPrint(ctl, "%c", VIR_CPU_USABLE(cpumaps, cpumaplen, n, m) ? 'y' : '-');
- }
- vshPrint(ctl, "\n");
- if (n < (ncpus - 1)) {
- vshPrint(ctl, "\n");
- }
- }
+ dump = virNWFilterGetXMLDesc(nwfilter, 0);
+ if (dump != NULL) {
+ vshPrint(ctl, "%s", dump);
+ VIR_FREE(dump);
} else {
- if (info.state == VIR_DOMAIN_SHUTOFF &&
- (ncpus = virDomainGetVcpuPinInfo(dom, info.nrVirtCpu,
- cpumaps, cpumaplen,
- VIR_DOMAIN_AFFECT_CONFIG)) >= 0) {
-
- /* fallback plan to use virDomainGetVcpuPinInfo */
-
- for (n = 0; n < ncpus; n++) {
- vshPrint(ctl, "%-15s %d\n", _("VCPU:"), n);
- vshPrint(ctl, "%-15s %s\n", _("CPU:"), _("N/A"));
- vshPrint(ctl, "%-15s %s\n", _("State:"), _("N/A"));
- vshPrint(ctl, "%-15s %s\n", _("CPU time"), _("N/A"));
- vshPrint(ctl, "%-15s ", _("CPU Affinity:"));
- for (m = 0; m < maxcpu; m++) {
- vshPrint(ctl, "%c",
- VIR_CPU_USABLE(cpumaps, cpumaplen, n, m) ? 'y' : '-');
- }
- vshPrint(ctl, "\n");
- if (n < (ncpus - 1)) {
- vshPrint(ctl, "\n");
- }
- }
- } else {
- ret = false;
- }
+ ret = false;
}
- VIR_FREE(cpumaps);
- VIR_FREE(cpuinfo);
- virDomainFree(dom);
+ virNWFilterFree(nwfilter);
return ret;
}
/*
- * "vcpupin" command
+ * "nwfilter-list" command
*/
-static const vshCmdInfo info_vcpupin[] = {
- {"help", N_("control or query domain vcpu affinity")},
- {"desc", N_("Pin domain VCPUs to host physical CPUs.")},
+static const vshCmdInfo info_nwfilter_list[] = {
+ {"help", N_("list network filters")},
+ {"desc", N_("Returns list of network filters.")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_vcpupin[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {"vcpu", VSH_OT_INT, 0, N_("vcpu number")},
- {"cpulist", VSH_OT_DATA, VSH_OFLAG_EMPTY_OK,
- N_("host cpu number(s) to set, or omit option to query")},
- {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
- {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
- {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
+static const vshCmdOptDef opts_nwfilter_list[] = {
{NULL, 0, 0, NULL}
};
static bool
-cmdVcpuPin(vshControl *ctl, const vshCmd *cmd)
+cmdNWFilterList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
- virDomainInfo info;
- virDomainPtr dom;
- virNodeInfo nodeinfo;
- int vcpu = -1;
- const char *cpulist = NULL;
- bool ret = true;
- unsigned char *cpumap = NULL;
- unsigned char *cpumaps = NULL;
- size_t cpumaplen;
- bool bit, lastbit, isInvert;
- int i, cpu, lastcpu, maxcpu, ncpus;
- bool unuse = false;
- const char *cur;
- bool config = vshCommandOptBool(cmd, "config");
- bool live = vshCommandOptBool(cmd, "live");
- bool current = vshCommandOptBool(cmd, "current");
- bool query = false; /* Query mode if no cpulist */
- unsigned int flags = 0;
-
- if (current) {
- if (live || config) {
- vshError(ctl, "%s", _("--current must be specified exclusively"));
- return false;
- }
- flags = VIR_DOMAIN_AFFECT_CURRENT;
- } else {
- if (config)
- flags |= VIR_DOMAIN_AFFECT_CONFIG;
- if (live)
- flags |= VIR_DOMAIN_AFFECT_LIVE;
- /* neither option is specified */
- if (!live && !config)
- flags = -1;
- }
+ int numfilters, i;
+ char **names;
+ char uuid[VIR_UUID_STRING_BUFLEN];
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
- return false;
-
- if (vshCommandOptString(cmd, "cpulist", &cpulist) < 0) {
- vshError(ctl, "%s", _("vcpupin: Missing cpulist."));
- virDomainFree(dom);
- return false;
- }
- query = !cpulist;
-
- /* In query mode, "vcpu" is optional */
- if (vshCommandOptInt(cmd, "vcpu", &vcpu) < !query) {
- vshError(ctl, "%s",
- _("vcpupin: Invalid or missing vCPU number."));
- virDomainFree(dom);
- return false;
- }
-
- if (virNodeGetInfo(ctl->conn, &nodeinfo) != 0) {
- virDomainFree(dom);
+ numfilters = virConnectNumOfNWFilters(ctl->conn);
+ if (numfilters < 0) {
+ vshError(ctl, "%s", _("Failed to list network filters"));
return false;
}
- if (virDomainGetInfo(dom, &info) != 0) {
- vshError(ctl, "%s", _("vcpupin: failed to get domain information."));
- virDomainFree(dom);
- return false;
- }
+ names = vshMalloc(ctl, sizeof(char *) * numfilters);
- if (vcpu >= info.nrVirtCpu) {
- vshError(ctl, "%s", _("vcpupin: Invalid vCPU number."));
- virDomainFree(dom);
+ if ((numfilters = virConnectListNWFilters(ctl->conn, names,
+ numfilters)) < 0) {
+ vshError(ctl, "%s", _("Failed to list network filters"));
+ VIR_FREE(names);
return false;
}
- maxcpu = VIR_NODEINFO_MAXCPUS(nodeinfo);
- cpumaplen = VIR_CPU_MAPLEN(maxcpu);
-
- /* Query mode: show CPU affinity information then exit.*/
- if (query) {
- /* When query mode and neither "live", "config" nor "current"
- * is specified, set VIR_DOMAIN_AFFECT_CURRENT as flags */
- if (flags == -1)
- flags = VIR_DOMAIN_AFFECT_CURRENT;
-
- cpumaps = vshMalloc(ctl, info.nrVirtCpu * cpumaplen);
- if ((ncpus = virDomainGetVcpuPinInfo(dom, info.nrVirtCpu,
- cpumaps, cpumaplen, flags)) >= 0) {
-
- vshPrint(ctl, "%s %s\n", _("VCPU:"), _("CPU Affinity"));
- vshPrint(ctl, "----------------------------------\n");
- for (i = 0; i < ncpus; i++) {
-
- if (vcpu != -1 && i != vcpu)
- continue;
-
- bit = lastbit = isInvert = false;
- lastcpu = -1;
-
- vshPrint(ctl, "%4d: ", i);
- for (cpu = 0; cpu < maxcpu; cpu++) {
-
- bit = VIR_CPU_USABLE(cpumaps, cpumaplen, i, cpu);
-
- isInvert = (bit ^ lastbit);
- if (bit && isInvert) {
- if (lastcpu == -1)
- vshPrint(ctl, "%d", cpu);
- else
- vshPrint(ctl, ",%d", cpu);
- lastcpu = cpu;
- }
- if (!bit && isInvert && lastcpu != cpu - 1)
- vshPrint(ctl, "-%d", cpu - 1);
- lastbit = bit;
- }
- if (bit && !isInvert) {
- vshPrint(ctl, "-%d", maxcpu - 1);
- }
- vshPrint(ctl, "\n");
- }
-
- } else {
- ret = false;
- }
- VIR_FREE(cpumaps);
- goto cleanup;
- }
-
- /* Pin mode: pinning specified vcpu to specified physical cpus*/
-
- cpumap = vshCalloc(ctl, cpumaplen, sizeof(cpumap));
- /* Parse cpulist */
- cur = cpulist;
- if (*cur == 0) {
- goto parse_error;
- } else if (*cur == 'r') {
- for (cpu = 0; cpu < maxcpu; cpu++)
- VIR_USE_CPU(cpumap, cpu);
- cur = "";
- }
-
- while (*cur != 0) {
-
- /* the char '^' denotes exclusive */
- if (*cur == '^') {
- cur++;
- unuse = true;
- }
+ qsort(&names[0], numfilters, sizeof(char *), vshNameSorter);
- /* parse physical CPU number */
- if (!c_isdigit(*cur))
- goto parse_error;
- cpu = virParseNumber(&cur);
- if (cpu < 0) {
- goto parse_error;
- }
- if (cpu >= maxcpu) {
- vshError(ctl, _("Physical CPU %d doesn't exist."), cpu);
- goto parse_error;
- }
- virSkipSpaces(&cur);
+ vshPrintExtra(ctl, "%-36s %-20s \n", _("UUID"), _("Name"));
+ vshPrintExtra(ctl,
+ "----------------------------------------------------------------\n");
- if (*cur == ',' || *cur == 0) {
- if (unuse) {
- VIR_UNUSE_CPU(cpumap, cpu);
- } else {
- VIR_USE_CPU(cpumap, cpu);
- }
- } else if (*cur == '-') {
- /* the char '-' denotes range */
- if (unuse) {
- goto parse_error;
- }
- cur++;
- virSkipSpaces(&cur);
- /* parse the end of range */
- lastcpu = virParseNumber(&cur);
- if (lastcpu < cpu) {
- goto parse_error;
- }
- if (lastcpu >= maxcpu) {
- vshError(ctl, _("Physical CPU %d doesn't exist."), maxcpu);
- goto parse_error;
- }
- for (i = cpu; i <= lastcpu; i++) {
- VIR_USE_CPU(cpumap, i);
- }
- virSkipSpaces(&cur);
- }
+ for (i = 0; i < numfilters; i++) {
+ virNWFilterPtr nwfilter =
+ virNWFilterLookupByName(ctl->conn, names[i]);
- if (*cur == ',') {
- cur++;
- virSkipSpaces(&cur);
- unuse = false;
- } else if (*cur == 0) {
- break;
- } else {
- goto parse_error;
+ /* this kind of work with networks is not atomic operation */
+ if (!nwfilter) {
+ VIR_FREE(names[i]);
+ continue;
}
- }
- if (flags == -1) {
- if (virDomainPinVcpu(dom, vcpu, cpumap, cpumaplen) != 0) {
- ret = false;
- }
- } else {
- if (virDomainPinVcpuFlags(dom, vcpu, cpumap, cpumaplen, flags) != 0) {
- ret = false;
- }
+ virNWFilterGetUUIDString(nwfilter, uuid);
+ vshPrint(ctl, "%-36s %-20s\n",
+ uuid,
+ virNWFilterGetName(nwfilter));
+ virNWFilterFree(nwfilter);
+ VIR_FREE(names[i]);
}
-cleanup:
- VIR_FREE(cpumap);
- virDomainFree(dom);
- return ret;
-
-parse_error:
- vshError(ctl, "%s", _("cpulist: Invalid format."));
- ret = false;
- goto cleanup;
+ VIR_FREE(names);
+ return true;
}
+
/*
- * "setvcpus" command
+ * "nwfilter-edit" command
*/
-static const vshCmdInfo info_setvcpus[] = {
- {"help", N_("change number of virtual CPUs")},
- {"desc", N_("Change the number of virtual CPUs in the guest domain.")},
+static const vshCmdInfo info_nwfilter_edit[] = {
+ {"help", N_("edit XML configuration for a network filter")},
+ {"desc", N_("Edit the XML configuration for a network filter.")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_setvcpus[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {"count", VSH_OT_INT, VSH_OFLAG_REQ, N_("number of virtual CPUs")},
- {"maximum", VSH_OT_BOOL, 0, N_("set maximum limit on next boot")},
- {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
- {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
- {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
+static const vshCmdOptDef opts_nwfilter_edit[] = {
+ {"nwfilter", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network filter name or uuid")},
{NULL, 0, 0, NULL}
};
static bool
-cmdSetvcpus(vshControl *ctl, const vshCmd *cmd)
+cmdNWFilterEdit(vshControl *ctl, const vshCmd *cmd)
{
- virDomainPtr dom;
- int count = 0;
- bool ret = true;
- bool maximum = vshCommandOptBool(cmd, "maximum");
- bool config = vshCommandOptBool(cmd, "config");
- bool live = vshCommandOptBool(cmd, "live");
- bool current = vshCommandOptBool(cmd, "current");
- unsigned int flags = 0;
-
- if (current) {
- if (live || config) {
- vshError(ctl, "%s", _("--current must be specified exclusively"));
- return false;
- }
- flags = VIR_DOMAIN_AFFECT_CURRENT;
- } else {
- if (config)
- flags |= VIR_DOMAIN_AFFECT_CONFIG;
- if (live)
- flags |= VIR_DOMAIN_AFFECT_LIVE;
- /* neither option is specified */
- if (!live && !config && !maximum)
- flags = -1;
- }
+ bool ret = false;
+ virNWFilterPtr nwfilter = NULL;
+ virNWFilterPtr nwfilter_edited = NULL;
if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
- return false;
-
- if (vshCommandOptInt(cmd, "count", &count) < 0 || count <= 0) {
- vshError(ctl, "%s", _("Invalid number of virtual CPUs"));
goto cleanup;
- }
- if (flags == -1) {
- if (virDomainSetVcpus(dom, count) != 0) {
- ret = false;
- }
- } else {
- /* If the --maximum flag was given, we need to ensure only the
- --config flag is in effect as well */
- if (maximum) {
- vshDebug(ctl, VSH_ERR_DEBUG, "--maximum flag was given\n");
+ nwfilter = vshCommandOptNWFilter(ctl, cmd, NULL);
+ if (nwfilter == NULL)
+ goto cleanup;
- flags |= VIR_DOMAIN_VCPU_MAXIMUM;
+#define EDIT_GET_XML virNWFilterGetXMLDesc(nwfilter, 0)
+#define EDIT_NOT_CHANGED \
+ vshPrint(ctl, _("Network filter %s XML " \
+ "configuration not changed.\n"), \
+ virNWFilterGetName(nwfilter)); \
+ ret = true; goto edit_cleanup;
+#define EDIT_DEFINE \
+ (nwfilter_edited = virNWFilterDefineXML(ctl->conn, doc_edited))
+#define EDIT_FREE \
+ if (nwfilter_edited) \
+ virNWFilterFree(nwfilter);
+#include "virsh-edit.c"
- /* If neither the --config nor --live flags were given, OR
- if just the --live flag was given, we need to error out
- warning the user that the --maximum flag can only be used
- with the --config flag */
- if (live || !config) {
+ vshPrint(ctl, _("Network filter %s XML configuration edited.\n"),
+ virNWFilterGetName(nwfilter_edited));
- /* Warn the user about the invalid flag combination */
- vshError(ctl, _("--maximum must be used with --config only"));
- ret = false;
- goto cleanup;
- }
- }
+ ret = true;
- /* Apply the virtual cpu changes */
- if (virDomainSetVcpusFlags(dom, count, flags) < 0) {
- ret = false;
- }
- }
+cleanup:
+ if (nwfilter)
+ virNWFilterFree(nwfilter);
+ if (nwfilter_edited)
+ virNWFilterFree(nwfilter_edited);
- cleanup:
- virDomainFree(dom);
return ret;
}
+
+/**************************************************************************/
/*
- * "cpu-stats" command
+ * "pool-autostart" command
*/
-static const vshCmdInfo info_cpu_stats[] = {
- {"help", N_("show domain cpu statistics")},
+static const vshCmdInfo info_pool_autostart[] = {
+ {"help", N_("autostart a pool")},
{"desc",
- N_("Display per-CPU and total statistics about the domain's CPUs")},
- {NULL, NULL},
+ N_("Configure a pool to be automatically started at boot.")},
+ {NULL, NULL}
};
-static const vshCmdOptDef opts_cpu_stats[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {"total", VSH_OT_BOOL, 0, N_("Show total statistics only")},
- {"start", VSH_OT_INT, 0, N_("Show statistics from this CPU")},
- {"count", VSH_OT_INT, 0, N_("Number of shown CPUs at most")},
- {NULL, 0, 0, NULL},
+static const vshCmdOptDef opts_pool_autostart[] = {
+ {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
+ {"disable", VSH_OT_BOOL, 0, N_("disable autostarting")},
+ {NULL, 0, 0, NULL}
};
static bool
-cmdCPUStats(vshControl *ctl, const vshCmd *cmd)
+cmdPoolAutostart(vshControl *ctl, const vshCmd *cmd)
{
- virDomainPtr dom;
- virTypedParameterPtr params = NULL;
- int i, j, pos, max_id, cpu = -1, show_count = -1, nparams;
- bool show_total = false, show_per_cpu = false;
- unsigned int flags = 0;
+ virStoragePoolPtr pool;
+ const char *name;
+ int autostart;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
return false;
- show_total = vshCommandOptBool(cmd, "total");
- if (vshCommandOptInt(cmd, "start", &cpu) > 0)
- show_per_cpu = true;
- if (vshCommandOptInt(cmd, "count", &show_count) > 0)
- show_per_cpu = true;
-
- /* default show per_cpu and total */
- if (!show_total && !show_per_cpu) {
- show_total = true;
- show_per_cpu = true;
- }
-
- if (!show_per_cpu) /* show total stats only */
- goto do_show_total;
-
- /* check cpu, show_count, and ignore wrong argument */
- if (cpu < 0)
- cpu = 0;
-
- /* get number of cpus on the node */
- if ((max_id = virDomainGetCPUStats(dom, NULL, 0, 0, 0, flags)) < 0)
- goto failed_stats;
- if (show_count < 0 || show_count > max_id)
- show_count = max_id;
-
- /* get percpu information */
- if ((nparams = virDomainGetCPUStats(dom, NULL, 0, 0, 1, flags)) < 0)
- goto failed_stats;
-
- if (!nparams) {
- vshPrint(ctl, "%s", _("No per-CPU stats available"));
- goto do_show_total;
- }
-
- if (VIR_ALLOC_N(params, nparams * MIN(show_count, 128)) < 0)
- goto failed_params;
-
- while (show_count) {
- int ncpus = MIN(show_count, 128);
-
- if (virDomainGetCPUStats(dom, params, nparams, cpu, ncpus, flags) < 0)
- goto failed_stats;
-
- for (i = 0; i < ncpus; i++) {
- if (params[i * nparams].type == 0) /* this cpu is not in the map */
- continue;
- vshPrint(ctl, "CPU%d:\n", cpu + i);
-
- for (j = 0; j < nparams; j++) {
- pos = i * nparams + j;
- vshPrint(ctl, "\t%-12s ", params[pos].field);
- if ((STREQ(params[pos].field, VIR_DOMAIN_CPU_STATS_CPUTIME) ||
- STREQ(params[pos].field, VIR_DOMAIN_CPU_STATS_VCPUTIME)) &&
- params[j].type == VIR_TYPED_PARAM_ULLONG) {
- vshPrint(ctl, "%9lld.%09lld seconds\n",
- params[pos].value.ul / 1000000000,
- params[pos].value.ul % 1000000000);
- } else {
- const char *s = vshGetTypedParamValue(ctl, ¶ms[pos]);
- vshPrint(ctl, _("%s\n"), s);
- VIR_FREE(s);
- }
- }
- }
- cpu += ncpus;
- show_count -= ncpus;
- virTypedParameterArrayClear(params, nparams * ncpus);
- }
- VIR_FREE(params);
-
-do_show_total:
- if (!show_total)
- goto cleanup;
-
- /* get supported num of parameter for total statistics */
- if ((nparams = virDomainGetCPUStats(dom, NULL, 0, -1, 1, flags)) < 0)
- goto failed_stats;
+ autostart = !vshCommandOptBool(cmd, "disable");
- if (!nparams) {
- vshPrint(ctl, "%s", _("No total stats available"));
- goto cleanup;
+ if (virStoragePoolSetAutostart(pool, autostart) < 0) {
+ if (autostart)
+ vshError(ctl, _("failed to mark pool %s as autostarted"), name);
+ else
+ vshError(ctl, _("failed to unmark pool %s as autostarted"), name);
+ virStoragePoolFree(pool);
+ return false;
}
- if (VIR_ALLOC_N(params, nparams))
- goto failed_params;
-
- /* passing start_cpu == -1 gives us domain's total status */
- if ((nparams = virDomainGetCPUStats(dom, params, nparams, -1, 1, flags)) < 0)
- goto failed_stats;
-
- vshPrint(ctl, _("Total:\n"));
- for (i = 0; i < nparams; i++) {
- vshPrint(ctl, "\t%-12s ", params[i].field);
- if ((STREQ(params[i].field, VIR_DOMAIN_CPU_STATS_CPUTIME) ||
- STREQ(params[i].field, VIR_DOMAIN_CPU_STATS_USERTIME) ||
- STREQ(params[i].field, VIR_DOMAIN_CPU_STATS_SYSTEMTIME)) &&
- params[i].type == VIR_TYPED_PARAM_ULLONG) {
- vshPrint(ctl, "%9lld.%09lld seconds\n",
- params[i].value.ul / 1000000000,
- params[i].value.ul % 1000000000);
- } else {
- char *s = vshGetTypedParamValue(ctl, ¶ms[i]);
- vshPrint(ctl, "%s\n", s);
- VIR_FREE(s);
- }
- }
- virTypedParameterArrayClear(params, nparams);
- VIR_FREE(params);
+ if (autostart)
+ vshPrint(ctl, _("Pool %s marked as autostarted\n"), name);
+ else
+ vshPrint(ctl, _("Pool %s unmarked as autostarted\n"), name);
-cleanup:
- virDomainFree(dom);
+ virStoragePoolFree(pool);
return true;
-
-failed_params:
- virReportOOMError();
- virDomainFree(dom);
- return false;
-
-failed_stats:
- vshError(ctl, _("Failed to virDomainGetCPUStats()\n"));
- VIR_FREE(params);
- virDomainFree(dom);
- return false;
}
/*
- * "inject-nmi" command
+ * "pool-create" command
*/
-static const vshCmdInfo info_inject_nmi[] = {
- {"help", N_("Inject NMI to the guest")},
- {"desc", N_("Inject NMI to the guest domain.")},
+static const vshCmdInfo info_pool_create[] = {
+ {"help", N_("create a pool from an XML file")},
+ {"desc", N_("Create a pool.")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_inject_nmi[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+static const vshCmdOptDef opts_pool_create[] = {
+ {"file", VSH_OT_DATA, VSH_OFLAG_REQ,
+ N_("file containing an XML pool description")},
{NULL, 0, 0, NULL}
};
-
static bool
-cmdInjectNMI(vshControl *ctl, const vshCmd *cmd)
+cmdPoolCreate(vshControl *ctl, const vshCmd *cmd)
{
- virDomainPtr dom;
- int ret = true;
+ virStoragePoolPtr pool;
+ const char *from = NULL;
+ bool ret = true;
+ char *buffer;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ if (vshCommandOptString(cmd, "file", &from) <= 0)
+ return false;
+
+ if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
return false;
- if (virDomainInjectNMI(dom, 0) < 0)
- ret = false;
+ pool = virStoragePoolCreateXML(ctl->conn, buffer, 0);
+ VIR_FREE(buffer);
- virDomainFree(dom);
+ if (pool != NULL) {
+ vshPrint(ctl, _("Pool %s created from %s\n"),
+ virStoragePoolGetName(pool), from);
+ virStoragePoolFree(pool);
+ } else {
+ vshError(ctl, _("Failed to create pool from %s"), from);
+ ret = false;
+ }
return ret;
}
+
/*
- * "send-key" command
+ * "nodedev-create" command
*/
-static const vshCmdInfo info_send_key[] = {
- {"help", N_("Send keycodes to the guest")},
- {"desc", N_("Send keycodes (integers or symbolic names) to the guest")},
+static const vshCmdInfo info_node_device_create[] = {
+ {"help", N_("create a device defined "
+ "by an XML file on the node")},
+ {"desc", N_("Create a device on the node. Note that this "
+ "command creates devices on the physical host "
+ "that can then be assigned to a virtual machine.")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_send_key[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {"codeset", VSH_OT_STRING, VSH_OFLAG_REQ_OPT,
- N_("the codeset of keycodes, default:linux")},
- {"holdtime", VSH_OT_INT, VSH_OFLAG_REQ_OPT,
- N_("the time (in milliseconds) how long the keys will be held")},
- {"keycode", VSH_OT_ARGV, VSH_OFLAG_REQ, N_("the key code")},
+static const vshCmdOptDef opts_node_device_create[] = {
+ {"file", VSH_OT_DATA, VSH_OFLAG_REQ,
+ N_("file containing an XML description of the device")},
{NULL, 0, 0, NULL}
};
-static int
-get_integer_keycode(const char *key_name)
-{
- unsigned int val;
-
- if (virStrToLong_ui(key_name, NULL, 0, &val) < 0 || val > 0xffff || !val)
- return -1;
- return val;
-}
-
static bool
-cmdSendKey(vshControl *ctl, const vshCmd *cmd)
+cmdNodeDeviceCreate(vshControl *ctl, const vshCmd *cmd)
{
- virDomainPtr dom;
- int ret = false;
- const char *codeset_option;
- int codeset;
- int holdtime;
- int count = 0;
- const vshCmdOpt *opt = NULL;
- int keycode;
- unsigned int keycodes[VIR_DOMAIN_SEND_KEY_MAX_KEYS];
+ virNodeDevicePtr dev = NULL;
+ const char *from = NULL;
+ bool ret = true;
+ char *buffer;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ if (vshCommandOptString(cmd, "file", &from) <= 0)
return false;
- if (vshCommandOptString(cmd, "codeset", &codeset_option) <= 0)
- codeset_option = "linux";
-
- if (vshCommandOptInt(cmd, "holdtime", &holdtime) <= 0)
- holdtime = 0;
-
- codeset = virKeycodeSetTypeFromString(codeset_option);
- if ((int)codeset < 0) {
- vshError(ctl, _("unknown codeset: '%s'"), codeset_option);
- goto cleanup;
- }
-
- while ((opt = vshCommandOptArgv(cmd, opt))) {
- if (count == VIR_DOMAIN_SEND_KEY_MAX_KEYS) {
- vshError(ctl, _("too many keycodes"));
- goto cleanup;
- }
+ if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
+ return false;
- if ((keycode = get_integer_keycode(opt->data)) <= 0) {
- if ((keycode = virKeycodeValueFromString(codeset, opt->data)) <= 0) {
- vshError(ctl, _("invalid keycode: '%s'"), opt->data);
- goto cleanup;
- }
- }
+ dev = virNodeDeviceCreateXML(ctl->conn, buffer, 0);
+ VIR_FREE(buffer);
- keycodes[count] = keycode;
- count++;
+ if (dev != NULL) {
+ vshPrint(ctl, _("Node device %s created from %s\n"),
+ virNodeDeviceGetName(dev), from);
+ virNodeDeviceFree(dev);
+ } else {
+ vshError(ctl, _("Failed to create node device from %s"), from);
+ ret = false;
}
- if (!(virDomainSendKey(dom, codeset, holdtime, keycodes, count, 0) < 0))
- ret = true;
-
-cleanup:
- virDomainFree(dom);
return ret;
}
+
/*
- * "setmem" command
+ * "nodedev-destroy" command
*/
-static const vshCmdInfo info_setmem[] = {
- {"help", N_("change memory allocation")},
- {"desc", N_("Change the current memory allocation in the guest domain.")},
+static const vshCmdInfo info_node_device_destroy[] = {
+ {"help", N_("destroy (stop) a device on the node")},
+ {"desc", N_("Destroy a device on the node. Note that this "
+ "command destroys devices on the physical host")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_setmem[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {"kilobytes", VSH_OT_ALIAS, 0, "size"},
- {"size", VSH_OT_INT, VSH_OFLAG_REQ,
- N_("new memory size, as scaled integer (default KiB)")},
- {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
- {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
- {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
+static const vshCmdOptDef opts_node_device_destroy[] = {
+ {"name", VSH_OT_DATA, VSH_OFLAG_REQ,
+ N_("name of the device to be destroyed")},
{NULL, 0, 0, NULL}
};
static bool
-cmdSetmem(vshControl *ctl, const vshCmd *cmd)
+cmdNodeDeviceDestroy(vshControl *ctl, const vshCmd *cmd)
{
- virDomainPtr dom;
- unsigned long long bytes = 0;
- unsigned long long max;
- unsigned long kibibytes = 0;
+ virNodeDevicePtr dev = NULL;
bool ret = true;
- bool config = vshCommandOptBool(cmd, "config");
- bool live = vshCommandOptBool(cmd, "live");
- bool current = vshCommandOptBool(cmd, "current");
- unsigned int flags = 0;
-
- if (current) {
- if (live || config) {
- vshError(ctl, "%s", _("--current must be specified exclusively"));
- return false;
- }
- flags = VIR_DOMAIN_AFFECT_CURRENT;
- } else {
- if (config)
- flags |= VIR_DOMAIN_AFFECT_CONFIG;
- if (live)
- flags |= VIR_DOMAIN_AFFECT_LIVE;
- /* neither option is specified */
- if (!live && !config)
- flags = -1;
- }
+ const char *name = NULL;
- if (!vshConnectionUsability(ctl, ctl->conn))
+ if (!vshConnectionUsability(ctl, ctl->conn)) {
return false;
+ }
- if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ if (vshCommandOptString(cmd, "name", &name) <= 0)
return false;
- /* The API expects 'unsigned long' KiB, so depending on whether we
- * are 32-bit or 64-bit determines the maximum we can use. */
- if (sizeof(kibibytes) < sizeof(max))
- max = 1024ull * ULONG_MAX;
- else
- max = ULONG_MAX;
- if (vshCommandOptScaledInt(cmd, "size", &bytes, 1024, max) < 0) {
- vshError(ctl, "%s", _("memory size has to be a number"));
- virDomainFree(dom);
- return false;
- }
- kibibytes = VIR_DIV_UP(bytes, 1024);
+ dev = virNodeDeviceLookupByName(ctl->conn, name);
- if (flags == -1) {
- if (virDomainSetMemory(dom, kibibytes) != 0) {
- ret = false;
- }
+ if (virNodeDeviceDestroy(dev) == 0) {
+ vshPrint(ctl, _("Destroyed node device '%s'\n"), name);
} else {
- if (virDomainSetMemoryFlags(dom, kibibytes, flags) < 0) {
- ret = false;
- }
+ vshError(ctl, _("Failed to destroy node device '%s'"), name);
+ ret = false;
}
- virDomainFree(dom);
+ virNodeDeviceFree(dev);
return ret;
}
+
/*
- * "setmaxmem" command
+ * XML Building helper for pool-define-as and pool-create-as
*/
-static const vshCmdInfo info_setmaxmem[] = {
- {"help", N_("change maximum memory limit")},
- {"desc", N_("Change the maximum memory allocation limit in the guest domain.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_setmaxmem[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {"kilobytes", VSH_OT_ALIAS, 0, "size"},
- {"size", VSH_OT_INT, VSH_OFLAG_REQ,
- N_("new maximum memory size, as scaled integer (default KiB)")},
- {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
- {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
- {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
+static const vshCmdOptDef opts_pool_X_as[] = {
+ {"name", VSH_OT_DATA, VSH_OFLAG_REQ, N_("name of the pool")},
+ {"print-xml", VSH_OT_BOOL, 0, N_("print XML document, but don't define/create")},
+ {"type", VSH_OT_DATA, VSH_OFLAG_REQ, N_("type of the pool")},
+ {"source-host", VSH_OT_DATA, 0, N_("source-host for underlying storage")},
+ {"source-path", VSH_OT_DATA, 0, N_("source path for underlying storage")},
+ {"source-dev", VSH_OT_DATA, 0, N_("source device for underlying storage")},
+ {"source-name", VSH_OT_DATA, 0, N_("source name for underlying storage")},
+ {"target", VSH_OT_DATA, 0, N_("target for underlying storage")},
+ {"source-format", VSH_OT_STRING, 0, N_("format for underlying storage")},
{NULL, 0, 0, NULL}
};
-static bool
-cmdSetmaxmem(vshControl *ctl, const vshCmd *cmd)
-{
- virDomainPtr dom;
- unsigned long long bytes = 0;
- unsigned long long max;
- unsigned long kibibytes = 0;
- bool ret = true;
- bool config = vshCommandOptBool(cmd, "config");
- bool live = vshCommandOptBool(cmd, "live");
- bool current = vshCommandOptBool(cmd, "current");
- unsigned int flags = VIR_DOMAIN_MEM_MAXIMUM;
+static int buildPoolXML(const vshCmd *cmd, const char **retname, char **xml) {
- if (current) {
- if (live || config) {
- vshError(ctl, "%s", _("--current must be specified exclusively"));
- return false;
- }
- } else {
- if (config)
- flags |= VIR_DOMAIN_AFFECT_CONFIG;
- if (live)
- flags |= VIR_DOMAIN_AFFECT_LIVE;
- /* neither option is specified */
- if (!live && !config)
- flags = -1;
+ const char *name = NULL, *type = NULL, *srcHost = NULL, *srcPath = NULL,
+ *srcDev = NULL, *srcName = NULL, *srcFormat = NULL, *target = NULL;
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+
+ if (vshCommandOptString(cmd, "name", &name) <= 0)
+ goto cleanup;
+ if (vshCommandOptString(cmd, "type", &type) <= 0)
+ goto cleanup;
+
+ if (vshCommandOptString(cmd, "source-host", &srcHost) < 0 ||
+ vshCommandOptString(cmd, "source-path", &srcPath) < 0 ||
+ vshCommandOptString(cmd, "source-dev", &srcDev) < 0 ||
+ vshCommandOptString(cmd, "source-name", &srcName) < 0 ||
+ vshCommandOptString(cmd, "source-format", &srcFormat) < 0 ||
+ vshCommandOptString(cmd, "target", &target) < 0) {
+ vshError(NULL, "%s", _("missing argument"));
+ goto cleanup;
}
- if (!vshConnectionUsability(ctl, ctl->conn))
+ virBufferAsprintf(&buf, "\n", type);
+ virBufferAsprintf(&buf, " %s\n", name);
+ if (srcHost || srcPath || srcDev || srcFormat || srcName) {
+ virBufferAddLit(&buf, " \n");
+ }
+ if (target) {
+ virBufferAddLit(&buf, " \n");
+ virBufferAsprintf(&buf, " %s\n", target);
+ virBufferAddLit(&buf, " \n");
+ }
+ virBufferAddLit(&buf, "\n");
+
+ if (virBufferError(&buf)) {
+ vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
return false;
+ }
+
+ *xml = virBufferContentAndReset(&buf);
+ *retname = name;
+ return true;
+
+cleanup:
+ virBufferFreeAndReset(&buf);
+ return false;
+}
+
+/*
+ * "pool-create-as" command
+ */
+static const vshCmdInfo info_pool_create_as[] = {
+ {"help", N_("create a pool from a set of args")},
+ {"desc", N_("Create a pool.")},
+ {NULL, NULL}
+};
+
+static bool
+cmdPoolCreateAs(vshControl *ctl, const vshCmd *cmd)
+{
+ virStoragePoolPtr pool;
+ const char *name;
+ char *xml;
+ bool printXML = vshCommandOptBool(cmd, "print-xml");
- if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- /* The API expects 'unsigned long' KiB, so depending on whether we
- * are 32-bit or 64-bit determines the maximum we can use. */
- if (sizeof(kibibytes) < sizeof(max))
- max = 1024ull * ULONG_MAX;
- else
- max = ULONG_MAX;
- if (vshCommandOptScaledInt(cmd, "size", &bytes, 1024, max) < 0) {
- vshError(ctl, "%s", _("memory size has to be a number"));
- virDomainFree(dom);
+ if (!buildPoolXML(cmd, &name, &xml))
return false;
- }
- kibibytes = VIR_DIV_UP(bytes, 1024);
- if (flags == -1) {
- if (virDomainSetMaxMemory(dom, kibibytes) != 0) {
- vshError(ctl, "%s", _("Unable to change MaxMemorySize"));
- ret = false;
- }
+ if (printXML) {
+ vshPrint(ctl, "%s", xml);
+ VIR_FREE(xml);
} else {
- if (virDomainSetMemoryFlags(dom, kibibytes, flags) < 0) {
- vshError(ctl, "%s", _("Unable to change MaxMemorySize"));
- ret = false;
+ pool = virStoragePoolCreateXML(ctl->conn, xml, 0);
+ VIR_FREE(xml);
+
+ if (pool != NULL) {
+ vshPrint(ctl, _("Pool %s created\n"), name);
+ virStoragePoolFree(pool);
+ } else {
+ vshError(ctl, _("Failed to create pool %s"), name);
+ return false;
}
}
-
- virDomainFree(dom);
- return ret;
+ return true;
}
+
/*
- * "blkiotune" command
+ * "pool-define" command
*/
-static const vshCmdInfo info_blkiotune[] = {
- {"help", N_("Get or set blkio parameters")},
- {"desc", 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 ")},
+static const vshCmdInfo info_pool_define[] = {
+ {"help", N_("define (but don't start) a pool from an XML file")},
+ {"desc", N_("Define a pool.")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_blkiotune[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {"weight", VSH_OT_INT, VSH_OFLAG_NONE,
- N_("IO Weight in range [100, 1000]")},
- {"device-weights", VSH_OT_STRING, VSH_OFLAG_NONE,
- N_("per-device IO Weights, in the form of /path/to/device,weight,...")},
- {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
- {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
- {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
+static const vshCmdOptDef opts_pool_define[] = {
+ {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML pool description")},
{NULL, 0, 0, NULL}
};
static bool
-cmdBlkiotune(vshControl * ctl, const vshCmd * cmd)
+cmdPoolDefine(vshControl *ctl, const vshCmd *cmd)
{
- virDomainPtr dom;
- const char *device_weight = NULL;
- int weight = 0;
- int nparams = 0;
- int rv = 0;
- unsigned int i = 0;
- virTypedParameterPtr params = NULL, temp = NULL;
- bool ret = false;
- unsigned int flags = 0;
- bool current = vshCommandOptBool(cmd, "current");
- bool config = vshCommandOptBool(cmd, "config");
- bool live = vshCommandOptBool(cmd, "live");
-
- if (current) {
- if (live || config) {
- vshError(ctl, "%s", _("--current must be specified exclusively"));
- return false;
- }
- flags = VIR_DOMAIN_AFFECT_CURRENT;
- } else {
- if (config)
- flags |= VIR_DOMAIN_AFFECT_CONFIG;
- if (live)
- flags |= VIR_DOMAIN_AFFECT_LIVE;
- }
+ virStoragePoolPtr pool;
+ const char *from = NULL;
+ bool ret = true;
+ char *buffer;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ if (vshCommandOptString(cmd, "file", &from) <= 0)
return false;
- if ((rv = vshCommandOptInt(cmd, "weight", &weight)) < 0) {
- vshError(ctl, "%s",
- _("Unable to parse integer parameter"));
- goto cleanup;
- }
+ if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
+ return false;
- if (rv > 0) {
- nparams++;
- if (weight <= 0) {
- vshError(ctl, _("Invalid value of %d for I/O weight"), weight);
- goto cleanup;
- }
- }
+ pool = virStoragePoolDefineXML(ctl->conn, buffer, 0);
+ VIR_FREE(buffer);
- rv = vshCommandOptString(cmd, "device-weights", &device_weight);
- if (rv < 0) {
- vshError(ctl, "%s",
- _("Unable to parse string parameter"));
- goto cleanup;
- }
- if (rv > 0) {
- nparams++;
+ if (pool != NULL) {
+ vshPrint(ctl, _("Pool %s defined from %s\n"),
+ virStoragePoolGetName(pool), from);
+ virStoragePoolFree(pool);
+ } else {
+ vshError(ctl, _("Failed to define pool from %s"), from);
+ ret = false;
}
+ return ret;
+}
- 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;
- }
+/*
+ * "pool-define-as" command
+ */
+static const vshCmdInfo info_pool_define_as[] = {
+ {"help", N_("define a pool from a set of args")},
+ {"desc", N_("Define a pool.")},
+ {NULL, NULL}
+};
- /* 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;
- }
+static bool
+cmdPoolDefineAs(vshControl *ctl, const vshCmd *cmd)
+{
+ virStoragePoolPtr pool;
+ const char *name;
+ char *xml;
+ bool printXML = vshCommandOptBool(cmd, "print-xml");
- 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 */
- params = vshCalloc(ctl, nparams, sizeof(*params));
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
- for (i = 0; i < nparams; i++) {
- temp = ¶ms[i];
- temp->type = VIR_TYPED_PARAM_UINT;
+ if (!buildPoolXML(cmd, &name, &xml))
+ return false;
- if (weight) {
- temp->value.ui = weight;
- if (!virStrcpy(temp->field, VIR_DOMAIN_BLKIO_WEIGHT,
- sizeof(temp->field)))
- goto cleanup;
- weight = 0;
- } else if (device_weight) {
- temp->value.s = vshStrdup(ctl, device_weight);
- temp->type = VIR_TYPED_PARAM_STRING;
- if (!virStrcpy(temp->field, VIR_DOMAIN_BLKIO_DEVICE_WEIGHT,
- sizeof(temp->field)))
- goto cleanup;
- device_weight = NULL;
- }
- }
+ if (printXML) {
+ vshPrint(ctl, "%s", xml);
+ VIR_FREE(xml);
+ } else {
+ pool = virStoragePoolDefineXML(ctl->conn, xml, 0);
+ VIR_FREE(xml);
- if (virDomainSetBlkioParameters(dom, params, nparams, flags) < 0) {
- vshError(ctl, "%s", _("Unable to change blkio parameters"));
- goto cleanup;
+ if (pool != NULL) {
+ vshPrint(ctl, _("Pool %s defined\n"), name);
+ virStoragePoolFree(pool);
+ } else {
+ vshError(ctl, _("Failed to define pool %s"), name);
+ return false;
}
}
-
- ret = true;
-
- cleanup:
- virTypedParameterArrayClear(params, nparams);
- VIR_FREE(params);
- virDomainFree(dom);
- return ret;
+ return true;
}
+
/*
- * "memtune" command
+ * "pool-build" command
*/
-static const vshCmdInfo info_memtune[] = {
- {"help", N_("Get or set memory parameters")},
- {"desc", N_("Get or set the current memory parameters for a guest"
- " domain.\n"
- " To get the memory parameters use following command: \n\n"
- " virsh # memtune ")},
+static const vshCmdInfo info_pool_build[] = {
+ {"help", N_("build a pool")},
+ {"desc", N_("Build a given pool.")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_memtune[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {"hard-limit", VSH_OT_INT, VSH_OFLAG_NONE,
- N_("Max memory, as scaled integer (default KiB)")},
- {"soft-limit", VSH_OT_INT, VSH_OFLAG_NONE,
- N_("Memory during contention, as scaled integer (default KiB)")},
- {"swap-hard-limit", VSH_OT_INT, VSH_OFLAG_NONE,
- N_("Max memory plus swap, as scaled integer (default KiB)")},
- {"min-guarantee", VSH_OT_INT, VSH_OFLAG_NONE,
- N_("Min guaranteed memory, as scaled integer (default KiB)")},
- {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
- {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
- {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
+static const vshCmdOptDef opts_pool_build[] = {
+ {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
+ {"no-overwrite", VSH_OT_BOOL, 0, N_("do not overwrite an existing pool of this type")},
+ {"overwrite", VSH_OT_BOOL, 0, N_("overwrite any existing data")},
{NULL, 0, 0, NULL}
};
-static int
-vshMemtuneGetSize(const vshCmd *cmd, const char *name, long long *value)
-{
- int ret;
- unsigned long long tmp;
- const char *str;
- char *end;
-
- ret = vshCommandOptString(cmd, name, &str);
- if (ret <= 0)
- return ret;
- if (virStrToLong_ll(str, &end, 10, value) < 0)
- return -1;
- if (*value < 0) {
- *value = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
- return 1;
- }
- tmp = *value;
- if (virScaleInteger(&tmp, end, 1024, LLONG_MAX) < 0)
- return -1;
- *value = VIR_DIV_UP(tmp, 1024);
- return 0;
-}
-
static bool
-cmdMemtune(vshControl *ctl, const vshCmd *cmd)
+cmdPoolBuild(vshControl *ctl, const vshCmd *cmd)
{
- virDomainPtr dom;
- long long hard_limit = 0, soft_limit = 0, swap_hard_limit = 0;
- long long min_guarantee = 0;
- int nparams = 0;
- unsigned int i = 0;
- virTypedParameterPtr params = NULL, temp = NULL;
- bool ret = false;
+ virStoragePoolPtr pool;
+ bool ret = true;
+ const char *name;
unsigned int flags = 0;
- bool current = vshCommandOptBool(cmd, "current");
- bool config = vshCommandOptBool(cmd, "config");
- bool live = vshCommandOptBool(cmd, "live");
-
- if (current) {
- if (live || config) {
- vshError(ctl, "%s", _("--current must be specified exclusively"));
- return false;
- }
- flags = VIR_DOMAIN_AFFECT_CURRENT;
- } else {
- if (config)
- flags |= VIR_DOMAIN_AFFECT_CONFIG;
- if (live)
- flags |= VIR_DOMAIN_AFFECT_LIVE;
- }
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
return false;
- if (vshMemtuneGetSize(cmd, "hard-limit", &hard_limit) < 0 ||
- vshMemtuneGetSize(cmd, "soft-limit", &soft_limit) < 0 ||
- vshMemtuneGetSize(cmd, "swap-hard-limit", &swap_hard_limit) < 0 ||
- vshMemtuneGetSize(cmd, "min-guarantee", &min_guarantee) < 0) {
- vshError(ctl, "%s",
- _("Unable to parse integer parameter"));
- goto cleanup;
+ if (vshCommandOptBool(cmd, "no-overwrite")) {
+ flags |= VIR_STORAGE_POOL_BUILD_NO_OVERWRITE;
}
- if (hard_limit)
- nparams++;
+ if (vshCommandOptBool(cmd, "overwrite")) {
+ flags |= VIR_STORAGE_POOL_BUILD_OVERWRITE;
+ }
- if (soft_limit)
- nparams++;
+ if (virStoragePoolBuild(pool, flags) == 0) {
+ vshPrint(ctl, _("Pool %s built\n"), name);
+ } else {
+ vshError(ctl, _("Failed to build pool %s"), name);
+ ret = false;
+ }
- if (swap_hard_limit)
- nparams++;
+ virStoragePoolFree(pool);
- if (min_guarantee)
- nparams++;
+ return ret;
+}
- if (nparams == 0) {
- /* get the number of memory parameters */
- if (virDomainGetMemoryParameters(dom, NULL, &nparams, flags) != 0) {
- vshError(ctl, "%s",
- _("Unable to get number of memory parameters"));
- goto cleanup;
- }
-
- if (nparams == 0) {
- /* nothing to output */
- ret = true;
- goto cleanup;
- }
-
- /* now go get all the memory parameters */
- params = vshCalloc(ctl, nparams, sizeof(*params));
- if (virDomainGetMemoryParameters(dom, params, &nparams, flags) != 0) {
- vshError(ctl, "%s", _("Unable to get memory parameters"));
- goto cleanup;
- }
+/*
+ * "pool-destroy" command
+ */
+static const vshCmdInfo info_pool_destroy[] = {
+ {"help", N_("destroy (stop) a pool")},
+ {"desc",
+ N_("Forcefully stop a given pool. Raw data in the pool is untouched")},
+ {NULL, NULL}
+};
- for (i = 0; i < nparams; i++) {
- if (params[i].type == VIR_TYPED_PARAM_ULLONG &&
- params[i].value.ul == VIR_DOMAIN_MEMORY_PARAM_UNLIMITED) {
- vshPrint(ctl, "%-15s: %s\n", params[i].field, _("unlimited"));
- } else {
- char *str = vshGetTypedParamValue(ctl, ¶ms[i]);
- vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
- VIR_FREE(str);
- }
- }
+static const vshCmdOptDef opts_pool_destroy[] = {
+ {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
+ {NULL, 0, 0, NULL}
+};
- ret = true;
- } else {
- /* set the memory parameters */
- params = vshCalloc(ctl, nparams, sizeof(*params));
+static bool
+cmdPoolDestroy(vshControl *ctl, const vshCmd *cmd)
+{
+ virStoragePoolPtr pool;
+ bool ret = true;
+ const char *name;
- for (i = 0; i < nparams; i++) {
- temp = ¶ms[i];
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
- /*
- * Some magic here, this is used to fill the params structure with
- * the valid arguments passed, after filling the particular
- * argument we purposely make them 0, so on the next pass it goes
- * to the next valid argument and so on.
- */
- if (soft_limit) {
- if (virTypedParameterAssign(temp,
- VIR_DOMAIN_MEMORY_SOFT_LIMIT,
- VIR_TYPED_PARAM_ULLONG,
- soft_limit) < 0)
- goto error;
- soft_limit = 0;
- } else if (hard_limit) {
- if (virTypedParameterAssign(temp,
- VIR_DOMAIN_MEMORY_HARD_LIMIT,
- VIR_TYPED_PARAM_ULLONG,
- hard_limit) < 0)
- goto error;
- hard_limit = 0;
- } else if (swap_hard_limit) {
- if (virTypedParameterAssign(temp,
- VIR_DOMAIN_MEMORY_SWAP_HARD_LIMIT,
- VIR_TYPED_PARAM_ULLONG,
- swap_hard_limit) < 0)
- goto error;
- swap_hard_limit = 0;
- } else if (min_guarantee) {
- if (virTypedParameterAssign(temp,
- VIR_DOMAIN_MEMORY_MIN_GUARANTEE,
- VIR_TYPED_PARAM_ULLONG,
- min_guarantee) < 0)
- goto error;
- min_guarantee = 0;
- }
+ if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
+ return false;
- /* If the user has passed -1, we interpret it as unlimited */
- if (temp->value.ul == -1)
- temp->value.ul = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
- }
- if (virDomainSetMemoryParameters(dom, params, nparams, flags) != 0)
- goto error;
- else
- ret = true;
+ if (virStoragePoolDestroy(pool) == 0) {
+ vshPrint(ctl, _("Pool %s destroyed\n"), name);
+ } else {
+ vshError(ctl, _("Failed to destroy pool %s"), name);
+ ret = false;
}
-cleanup:
- VIR_FREE(params);
- virDomainFree(dom);
+ virStoragePoolFree(pool);
return ret;
-
-error:
- vshError(ctl, "%s", _("Unable to change memory parameters"));
- goto cleanup;
}
+
/*
- * "numatune" command
+ * "pool-delete" command
*/
-static const vshCmdInfo info_numatune[] = {
- {"help", N_("Get or set numa parameters")},
- {"desc", N_("Get or set the current numa parameters for a guest"
- " domain.\n"
- " To get the numa parameters use following command: \n\n"
- " virsh # numatune ")},
+static const vshCmdInfo info_pool_delete[] = {
+ {"help", N_("delete a pool")},
+ {"desc", N_("Delete a given pool.")},
{NULL, NULL}
-
};
-static const vshCmdOptDef opts_numatune[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {"mode", VSH_OT_DATA, VSH_OFLAG_NONE,
- N_("NUMA mode, one of strict, preferred and interleave")},
- {"nodeset", VSH_OT_DATA, VSH_OFLAG_NONE,
- N_("NUMA node selections to set")},
- {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
- {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
- {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
+static const vshCmdOptDef opts_pool_delete[] = {
+ {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
{NULL, 0, 0, NULL}
};
static bool
-cmdNumatune(vshControl * ctl, const vshCmd * cmd)
+cmdPoolDelete(vshControl *ctl, const vshCmd *cmd)
{
- virDomainPtr dom;
- int nparams = 0;
- unsigned int i = 0;
- virTypedParameterPtr params = NULL, temp = NULL;
- const char *nodeset = NULL;
- bool ret = false;
- unsigned int flags = 0;
- bool current = vshCommandOptBool(cmd, "current");
- bool config = vshCommandOptBool(cmd, "config");
- bool live = vshCommandOptBool(cmd, "live");
- const char *mode = NULL;
-
- if (current) {
- if (live || config) {
- vshError(ctl, "%s", _("--current must be specified exclusively"));
- return false;
- }
- flags = VIR_DOMAIN_AFFECT_CURRENT;
- } else {
- if (config)
- flags |= VIR_DOMAIN_AFFECT_CONFIG;
- if (live)
- flags |= VIR_DOMAIN_AFFECT_LIVE;
- }
+ virStoragePoolPtr pool;
+ bool ret = true;
+ const char *name;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
- return false;
-
- if (vshCommandOptString(cmd, "nodeset", &nodeset) < 0) {
- vshError(ctl, "%s", _("Unable to parse nodeset."));
- virDomainFree(dom);
- return false;
- }
- if (nodeset)
- nparams++;
- if (vshCommandOptString(cmd, "mode", &mode) < 0) {
- vshError(ctl, "%s", _("Unable to parse mode."));
- virDomainFree(dom);
+ if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
return false;
- }
- if (mode)
- nparams++;
-
- if (nparams == 0) {
- /* get the number of numa parameters */
- if (virDomainGetNumaParameters(dom, NULL, &nparams, flags) != 0) {
- vshError(ctl, "%s",
- _("Unable to get number of memory parameters"));
- goto cleanup;
- }
-
- if (nparams == 0) {
- /* nothing to output */
- ret = true;
- goto cleanup;
- }
-
- /* now go get all the numa parameters */
- params = vshCalloc(ctl, nparams, sizeof(*params));
- if (virDomainGetNumaParameters(dom, params, &nparams, flags) != 0) {
- vshError(ctl, "%s", _("Unable to get numa parameters"));
- goto cleanup;
- }
- for (i = 0; i < nparams; i++) {
- if (params[i].type == VIR_TYPED_PARAM_INT &&
- STREQ(params[i].field, VIR_DOMAIN_NUMA_MODE)) {
- vshPrint(ctl, "%-15s: %s\n", params[i].field,
- virDomainNumatuneMemModeTypeToString(params[i].value.i));
- } else {
- char *str = vshGetTypedParamValue(ctl, ¶ms[i]);
- vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
- VIR_FREE(str);
- }
- }
-
- ret = true;
+ if (virStoragePoolDelete(pool, 0) == 0) {
+ vshPrint(ctl, _("Pool %s deleted\n"), name);
} else {
- /* set the numa parameters */
- params = vshCalloc(ctl, nparams, sizeof(*params));
-
- for (i = 0; i < nparams; i++) {
- temp = ¶ms[i];
-
- /*
- * Some magic here, this is used to fill the params structure with
- * the valid arguments passed, after filling the particular
- * argument we purposely make them 0, so on the next pass it goes
- * to the next valid argument and so on.
- */
- if (mode) {
- /* Accept string or integer, in case server
- * understands newer integer than what strings we were
- * compiled with */
- if ((temp->value.i =
- virDomainNumatuneMemModeTypeFromString(mode)) < 0 &&
- virStrToLong_i(mode, NULL, 0, &temp->value.i) < 0) {
- vshError(ctl, _("Invalid mode: %s"), mode);
- goto cleanup;
- }
- if (!virStrcpy(temp->field, VIR_DOMAIN_NUMA_MODE,
- sizeof(temp->field)))
- goto cleanup;
- temp->type = VIR_TYPED_PARAM_INT;
- mode = NULL;
- } else if (nodeset) {
- temp->value.s = vshStrdup(ctl, nodeset);
- temp->type = VIR_TYPED_PARAM_STRING;
- if (!virStrcpy(temp->field, VIR_DOMAIN_NUMA_NODESET,
- sizeof(temp->field)))
- goto cleanup;
- nodeset = NULL;
- }
- }
- if (virDomainSetNumaParameters(dom, params, nparams, flags) != 0)
- vshError(ctl, "%s", _("Unable to change numa parameters"));
- else
- ret = true;
+ vshError(ctl, _("Failed to delete pool %s"), name);
+ ret = false;
}
- cleanup:
- virTypedParameterArrayClear(params, nparams);
- VIR_FREE(params);
- virDomainFree(dom);
+ virStoragePoolFree(pool);
return ret;
}
+
/*
- * "nodeinfo" command
+ * "pool-refresh" command
*/
-static const vshCmdInfo info_nodeinfo[] = {
- {"help", N_("node information")},
- {"desc", N_("Returns basic information about the node.")},
+static const vshCmdInfo info_pool_refresh[] = {
+ {"help", N_("refresh a pool")},
+ {"desc", N_("Refresh a given pool.")},
{NULL, NULL}
};
+static const vshCmdOptDef opts_pool_refresh[] = {
+ {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
+ {NULL, 0, 0, NULL}
+};
+
static bool
-cmdNodeinfo(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
+cmdPoolRefresh(vshControl *ctl, const vshCmd *cmd)
{
- virNodeInfo info;
+ virStoragePoolPtr pool;
+ bool ret = true;
+ const char *name;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (virNodeGetInfo(ctl->conn, &info) < 0) {
- vshError(ctl, "%s", _("failed to get node information"));
+ if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
return false;
+
+ if (virStoragePoolRefresh(pool, 0) == 0) {
+ vshPrint(ctl, _("Pool %s refreshed\n"), name);
+ } else {
+ vshError(ctl, _("Failed to refresh pool %s"), name);
+ ret = false;
}
- vshPrint(ctl, "%-20s %s\n", _("CPU model:"), info.model);
- vshPrint(ctl, "%-20s %d\n", _("CPU(s):"), info.cpus);
- vshPrint(ctl, "%-20s %d MHz\n", _("CPU frequency:"), info.mhz);
- vshPrint(ctl, "%-20s %d\n", _("CPU socket(s):"), info.sockets);
- vshPrint(ctl, "%-20s %d\n", _("Core(s) per socket:"), info.cores);
- vshPrint(ctl, "%-20s %d\n", _("Thread(s) per core:"), info.threads);
- vshPrint(ctl, "%-20s %d\n", _("NUMA cell(s):"), info.nodes);
- vshPrint(ctl, "%-20s %lu KiB\n", _("Memory size:"), info.memory);
+ virStoragePoolFree(pool);
- return true;
+ return ret;
}
+
/*
- * "nodecpustats" command
+ * "pool-dumpxml" command
*/
-static const vshCmdInfo info_nodecpustats[] = {
- {"help", N_("Prints cpu stats of the node.")},
- {"desc", N_("Returns cpu stats of the node, in nanoseconds.")},
+static const vshCmdInfo info_pool_dumpxml[] = {
+ {"help", N_("pool information in XML")},
+ {"desc", N_("Output the pool information as an XML dump to stdout.")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_node_cpustats[] = {
- {"cpu", VSH_OT_INT, 0, N_("prints specified cpu statistics only.")},
- {"percent", VSH_OT_BOOL, 0, N_("prints by percentage during 1 second.")},
+static const vshCmdOptDef opts_pool_dumpxml[] = {
+ {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
+ {"inactive", VSH_OT_BOOL, 0, N_("show inactive defined XML")},
{NULL, 0, 0, NULL}
};
static bool
-cmdNodeCpuStats(vshControl *ctl, const vshCmd *cmd)
+cmdPoolDumpXML(vshControl *ctl, const vshCmd *cmd)
{
- int i, j;
- bool flag_utilization = false;
- bool flag_percent = vshCommandOptBool(cmd, "percent");
- int cpuNum = VIR_NODE_CPU_STATS_ALL_CPUS;
- virNodeCPUStatsPtr params;
- int nparams = 0;
- bool ret = false;
- struct cpu_stats {
- unsigned long long user;
- unsigned long long sys;
- unsigned long long idle;
- unsigned long long iowait;
- unsigned long long util;
- } cpu_stats[2];
- double user_time, sys_time, idle_time, iowait_time, total_time;
- double usage;
+ virStoragePoolPtr pool;
+ bool ret = true;
+ bool inactive = vshCommandOptBool(cmd, "inactive");
+ unsigned int flags = 0;
+ char *dump;
+
+ if (inactive)
+ flags |= VIR_STORAGE_XML_INACTIVE;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (vshCommandOptInt(cmd, "cpu", &cpuNum) < 0) {
- vshError(ctl, "%s", _("Invalid value of cpuNum"));
+ if (!(pool = vshCommandOptPool(ctl, cmd, "pool", NULL)))
return false;
- }
- if (virNodeGetCPUStats(ctl->conn, cpuNum, NULL, &nparams, 0) != 0) {
- vshError(ctl, "%s",
- _("Unable to get number of cpu stats"));
- return false;
- }
- if (nparams == 0) {
- /* nothing to output */
- return true;
+ dump = virStoragePoolGetXMLDesc(pool, flags);
+ if (dump != NULL) {
+ vshPrint(ctl, "%s", dump);
+ VIR_FREE(dump);
+ } else {
+ ret = false;
}
- memset(cpu_stats, 0, sizeof(cpu_stats));
- params = vshCalloc(ctl, nparams, sizeof(*params));
+ virStoragePoolFree(pool);
+ return ret;
+}
- for (i = 0; i < 2; i++) {
- if (i > 0)
- sleep(1);
- if (virNodeGetCPUStats(ctl->conn, cpuNum, params, &nparams, 0) != 0) {
- vshError(ctl, "%s", _("Unable to get node cpu stats"));
- goto cleanup;
- }
+/*
+ * "pool-list" command
+ */
+static const vshCmdInfo info_pool_list[] = {
+ {"help", N_("list pools")},
+ {"desc", N_("Returns list of pools.")},
+ {NULL, NULL}
+};
- for (j = 0; j < nparams; j++) {
- unsigned long long value = params[j].value;
-
- if (STREQ(params[j].field, VIR_NODE_CPU_STATS_KERNEL)) {
- cpu_stats[i].sys = value;
- } else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_USER)) {
- cpu_stats[i].user = value;
- } else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_IDLE)) {
- cpu_stats[i].idle = value;
- } else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_IOWAIT)) {
- cpu_stats[i].iowait = value;
- } else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_UTILIZATION)) {
- cpu_stats[i].util = value;
- flag_utilization = true;
- }
- }
-
- if (flag_utilization || !flag_percent)
- break;
- }
-
- if (!flag_percent) {
- if (!flag_utilization) {
- vshPrint(ctl, "%-15s %20llu\n", _("user:"), cpu_stats[0].user);
- vshPrint(ctl, "%-15s %20llu\n", _("system:"), cpu_stats[0].sys);
- vshPrint(ctl, "%-15s %20llu\n", _("idle:"), cpu_stats[0].idle);
- vshPrint(ctl, "%-15s %20llu\n", _("iowait:"), cpu_stats[0].iowait);
- }
- } else {
- if (flag_utilization) {
- usage = cpu_stats[0].util;
-
- vshPrint(ctl, "%-15s %5.1lf%%\n", _("usage:"), usage);
- vshPrint(ctl, "%-15s %5.1lf%%\n", _("idle:"), 100 - usage);
- } else {
- user_time = cpu_stats[1].user - cpu_stats[0].user;
- sys_time = cpu_stats[1].sys - cpu_stats[0].sys;
- idle_time = cpu_stats[1].idle - cpu_stats[0].idle;
- iowait_time = cpu_stats[1].iowait - cpu_stats[0].iowait;
- total_time = user_time + sys_time + idle_time + iowait_time;
-
- usage = (user_time + sys_time) / total_time * 100;
-
- vshPrint(ctl, "%-15s %5.1lf%%\n",
- _("usage:"), usage);
- vshPrint(ctl, "%-15s %5.1lf%%\n",
- _("user:"), user_time / total_time * 100);
- vshPrint(ctl, "%-15s %5.1lf%%\n",
- _("system:"), sys_time / total_time * 100);
- vshPrint(ctl, "%-15s %5.1lf%%\n",
- _("idle:"), idle_time / total_time * 100);
- vshPrint(ctl, "%-15s %5.1lf%%\n",
- _("iowait:"), iowait_time / total_time * 100);
- }
- }
-
- ret = true;
-
- cleanup:
- VIR_FREE(params);
- return ret;
-}
-
-/*
- * "nodememstats" command
- */
-static const vshCmdInfo info_nodememstats[] = {
- {"help", N_("Prints memory stats of the node.")},
- {"desc", N_("Returns memory stats of the node, in kilobytes.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_node_memstats[] = {
- {"cell", VSH_OT_INT, 0, N_("prints specified cell statistics only.")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNodeMemStats(vshControl *ctl, const vshCmd *cmd)
-{
- int nparams = 0;
- unsigned int i = 0;
- int cellNum = VIR_NODE_MEMORY_STATS_ALL_CELLS;
- virNodeMemoryStatsPtr params = NULL;
- bool ret = false;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (vshCommandOptInt(cmd, "cell", &cellNum) < 0) {
- vshError(ctl, "%s", _("Invalid value of cellNum"));
- return false;
- }
-
- /* get the number of memory parameters */
- if (virNodeGetMemoryStats(ctl->conn, cellNum, NULL, &nparams, 0) != 0) {
- vshError(ctl, "%s",
- _("Unable to get number of memory stats"));
- goto cleanup;
- }
-
- if (nparams == 0) {
- /* nothing to output */
- ret = true;
- goto cleanup;
- }
-
- /* now go get all the memory parameters */
- params = vshCalloc(ctl, nparams, sizeof(*params));
- if (virNodeGetMemoryStats(ctl->conn, cellNum, params, &nparams, 0) != 0) {
- vshError(ctl, "%s", _("Unable to get memory stats"));
- goto cleanup;
- }
-
- for (i = 0; i < nparams; i++)
- vshPrint(ctl, "%-7s: %20llu KiB\n", params[i].field, params[i].value);
-
- ret = true;
-
- cleanup:
- VIR_FREE(params);
- return ret;
-}
-
-/*
- * "nodesuspend" command
- */
-static const vshCmdInfo info_nodesuspend[] = {
- {"help", N_("suspend the host node for a given time duration")},
- {"desc", N_("Suspend the host node for a given time duration "
- "and attempt to resume thereafter.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_node_suspend[] = {
- {"target", VSH_OT_DATA, VSH_OFLAG_REQ, N_("mem(Suspend-to-RAM), "
- "disk(Suspend-to-Disk), hybrid(Hybrid-Suspend)")},
- {"duration", VSH_OT_INT, VSH_OFLAG_REQ, N_("Suspend duration in seconds")},
- {"flags", VSH_OT_INT, VSH_OFLAG_NONE, N_("Suspend flags, 0 for default")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNodeSuspend(vshControl *ctl, const vshCmd *cmd)
-{
- const char *target = NULL;
- unsigned int suspendTarget;
- long long duration;
- unsigned int flags = 0;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (vshCommandOptString(cmd, "target", &target) < 0) {
- vshError(ctl, _("Invalid target argument"));
- return false;
- }
-
- if (vshCommandOptLongLong(cmd, "duration", &duration) < 0) {
- vshError(ctl, _("Invalid duration argument"));
- return false;
- }
-
- if (vshCommandOptUInt(cmd, "flags", &flags) < 0) {
- vshError(ctl, _("Invalid flags argument"));
- return false;
- }
-
- if (STREQ(target, "mem"))
- suspendTarget = VIR_NODE_SUSPEND_TARGET_MEM;
- else if (STREQ(target, "disk"))
- suspendTarget = VIR_NODE_SUSPEND_TARGET_DISK;
- else if (STREQ(target, "hybrid"))
- suspendTarget = VIR_NODE_SUSPEND_TARGET_HYBRID;
- else {
- vshError(ctl, "%s", _("Invalid target"));
- return false;
- }
-
- if (duration <= 0) {
- vshError(ctl, "%s", _("Invalid duration"));
- return false;
- }
-
- if (virNodeSuspendForDuration(ctl->conn, suspendTarget, duration,
- flags) < 0) {
- vshError(ctl, "%s", _("The host was not suspended"));
- return false;
- }
- return true;
-}
-
-
-/*
- * "capabilities" command
- */
-static const vshCmdInfo info_capabilities[] = {
- {"help", N_("capabilities")},
- {"desc", N_("Returns capabilities of hypervisor/driver.")},
- {NULL, NULL}
-};
-
-static bool
-cmdCapabilities(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
-{
- char *caps;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if ((caps = virConnectGetCapabilities(ctl->conn)) == NULL) {
- vshError(ctl, "%s", _("failed to get capabilities"));
- return false;
- }
- vshPrint(ctl, "%s\n", caps);
- VIR_FREE(caps);
-
- return true;
-}
-
-/*
- * "dumpxml" command
- */
-static const vshCmdInfo info_dumpxml[] = {
- {"help", N_("domain information in XML")},
- {"desc", N_("Output the domain information as an XML dump to stdout.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_dumpxml[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {"inactive", VSH_OT_BOOL, 0, N_("show inactive defined XML")},
- {"security-info", VSH_OT_BOOL, 0, N_("include security sensitive information in XML dump")},
- {"update-cpu", VSH_OT_BOOL, 0, N_("update guest CPU according to host CPU")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdDumpXML(vshControl *ctl, const vshCmd *cmd)
-{
- virDomainPtr dom;
- bool ret = true;
- char *dump;
- unsigned int flags = 0;
- bool inactive = vshCommandOptBool(cmd, "inactive");
- bool secure = vshCommandOptBool(cmd, "security-info");
- bool update = vshCommandOptBool(cmd, "update-cpu");
-
- if (inactive)
- flags |= VIR_DOMAIN_XML_INACTIVE;
- if (secure)
- flags |= VIR_DOMAIN_XML_SECURE;
- if (update)
- flags |= VIR_DOMAIN_XML_UPDATE_CPU;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
- return false;
-
- dump = virDomainGetXMLDesc(dom, flags);
- if (dump != NULL) {
- vshPrint(ctl, "%s", dump);
- VIR_FREE(dump);
- } else {
- ret = false;
- }
-
- virDomainFree(dom);
- return ret;
-}
-
-/*
- * "domxml-from-native" command
- */
-static const vshCmdInfo info_domxmlfromnative[] = {
- {"help", N_("Convert native config to domain XML")},
- {"desc", N_("Convert native guest configuration format to domain XML format.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_domxmlfromnative[] = {
- {"format", VSH_OT_DATA, VSH_OFLAG_REQ, N_("source config data format")},
- {"config", VSH_OT_DATA, VSH_OFLAG_REQ, N_("config data file to import from")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdDomXMLFromNative(vshControl *ctl, const vshCmd *cmd)
-{
- bool ret = true;
- const char *format = NULL;
- const char *configFile = NULL;
- char *configData;
- char *xmlData;
- unsigned int flags = 0;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (vshCommandOptString(cmd, "format", &format) < 0 ||
- vshCommandOptString(cmd, "config", &configFile) < 0)
- return false;
-
- if (virFileReadAll(configFile, 1024*1024, &configData) < 0)
- return false;
-
- xmlData = virConnectDomainXMLFromNative(ctl->conn, format, configData, flags);
- if (xmlData != NULL) {
- vshPrint(ctl, "%s", xmlData);
- VIR_FREE(xmlData);
- } else {
- ret = false;
- }
-
- VIR_FREE(configData);
- return ret;
-}
-
-/*
- * "domxml-to-native" command
- */
-static const vshCmdInfo info_domxmltonative[] = {
- {"help", N_("Convert domain XML to native config")},
- {"desc", N_("Convert domain XML config to a native guest configuration format.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_domxmltonative[] = {
- {"format", VSH_OT_DATA, VSH_OFLAG_REQ, N_("target config data type format")},
- {"xml", VSH_OT_DATA, VSH_OFLAG_REQ, N_("xml data file to export from")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdDomXMLToNative(vshControl *ctl, const vshCmd *cmd)
-{
- bool ret = true;
- const char *format = NULL;
- const char *xmlFile = NULL;
- char *configData;
- char *xmlData;
- unsigned int flags = 0;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (vshCommandOptString(cmd, "format", &format) < 0
- || vshCommandOptString(cmd, "xml", &xmlFile) < 0)
- return false;
-
- if (virFileReadAll(xmlFile, 1024*1024, &xmlData) < 0)
- return false;
-
- configData = virConnectDomainXMLToNative(ctl->conn, format, xmlData, flags);
- if (configData != NULL) {
- vshPrint(ctl, "%s", configData);
- VIR_FREE(configData);
- } else {
- ret = false;
- }
-
- VIR_FREE(xmlData);
- return ret;
-}
-
-/*
- * "domname" command
- */
-static const vshCmdInfo info_domname[] = {
- {"help", N_("convert a domain id or UUID to domain name")},
- {"desc", ""},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_domname[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain id or uuid")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdDomname(vshControl *ctl, const vshCmd *cmd)
-{
- virDomainPtr dom;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
- if (!(dom = vshCommandOptDomainBy(ctl, cmd, NULL,
- VSH_BYID|VSH_BYUUID)))
- return false;
-
- vshPrint(ctl, "%s\n", virDomainGetName(dom));
- virDomainFree(dom);
- return true;
-}
-
-/*
- * "domid" command
- */
-static const vshCmdInfo info_domid[] = {
- {"help", N_("convert a domain name or UUID to domain id")},
- {"desc", ""},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_domid[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name or uuid")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdDomid(vshControl *ctl, const vshCmd *cmd)
-{
- virDomainPtr dom;
- unsigned int id;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
- if (!(dom = vshCommandOptDomainBy(ctl, cmd, NULL,
- VSH_BYNAME|VSH_BYUUID)))
- return false;
-
- id = virDomainGetID(dom);
- if (id == ((unsigned int)-1))
- vshPrint(ctl, "%s\n", "-");
- else
- vshPrint(ctl, "%d\n", id);
- virDomainFree(dom);
- return true;
-}
-
-/*
- * "domuuid" command
- */
-static const vshCmdInfo info_domuuid[] = {
- {"help", N_("convert a domain name or id to domain UUID")},
- {"desc", ""},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_domuuid[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain id or name")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdDomuuid(vshControl *ctl, const vshCmd *cmd)
-{
- virDomainPtr dom;
- char uuid[VIR_UUID_STRING_BUFLEN];
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
- if (!(dom = vshCommandOptDomainBy(ctl, cmd, NULL,
- VSH_BYNAME|VSH_BYID)))
- return false;
-
- if (virDomainGetUUIDString(dom, uuid) != -1)
- vshPrint(ctl, "%s\n", uuid);
- else
- vshError(ctl, "%s", _("failed to get domain UUID"));
-
- virDomainFree(dom);
- return true;
-}
-
-/*
- * "migrate" command
- */
-static const vshCmdInfo info_migrate[] = {
- {"help", N_("migrate domain to another host")},
- {"desc", N_("Migrate domain to another host. Add --live for live migration.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_migrate[] = {
- {"live", VSH_OT_BOOL, 0, N_("live migration")},
- {"p2p", VSH_OT_BOOL, 0, N_("peer-2-peer migration")},
- {"direct", VSH_OT_BOOL, 0, N_("direct migration")},
- {"tunneled", VSH_OT_ALIAS, 0, "tunnelled"},
- {"tunnelled", VSH_OT_BOOL, 0, N_("tunnelled migration")},
- {"persistent", VSH_OT_BOOL, 0, N_("persist VM on destination")},
- {"undefinesource", VSH_OT_BOOL, 0, N_("undefine VM on source")},
- {"suspend", VSH_OT_BOOL, 0, N_("do not restart the domain on the destination host")},
- {"copy-storage-all", VSH_OT_BOOL, 0, N_("migration with non-shared storage with full disk copy")},
- {"copy-storage-inc", VSH_OT_BOOL, 0, N_("migration with non-shared storage with incremental copy (same base image shared between source and destination)")},
- {"change-protection", VSH_OT_BOOL, 0,
- N_("prevent any configuration changes to domain until migration ends)")},
- {"unsafe", VSH_OT_BOOL, 0, N_("force migration even if it may be unsafe")},
- {"verbose", VSH_OT_BOOL, 0, N_("display the progress of migration")},
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {"desturi", VSH_OT_DATA, VSH_OFLAG_REQ, N_("connection URI of the destination host as seen from the client(normal migration) or source(p2p migration)")},
- {"migrateuri", VSH_OT_DATA, 0, N_("migration URI, usually can be omitted")},
- {"dname", VSH_OT_DATA, 0, N_("rename to new name during migration (if supported)")},
- {"timeout", VSH_OT_INT, 0, N_("force guest to suspend if live migration exceeds timeout (in seconds)")},
- {"xml", VSH_OT_STRING, 0, N_("filename containing updated XML for the target")},
- {NULL, 0, 0, NULL}
-};
-
-static void
-doMigrate(void *opaque)
-{
- char ret = '1';
- virDomainPtr dom = NULL;
- const char *desturi = NULL;
- const char *migrateuri = NULL;
- const char *dname = NULL;
- unsigned int flags = 0;
- vshCtrlData *data = opaque;
- vshControl *ctl = data->ctl;
- const vshCmd *cmd = data->cmd;
- const char *xmlfile = NULL;
- char *xml = NULL;
- sigset_t sigmask, oldsigmask;
-
- sigemptyset(&sigmask);
- sigaddset(&sigmask, SIGINT);
- if (pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask) < 0)
- goto out_sig;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- goto out;
-
- if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
- goto out;
-
- if (vshCommandOptString(cmd, "desturi", &desturi) <= 0 ||
- vshCommandOptString(cmd, "migrateuri", &migrateuri) < 0 ||
- vshCommandOptString(cmd, "dname", &dname) < 0) {
- vshError(ctl, "%s", _("missing argument"));
- goto out;
- }
-
- if (vshCommandOptString(cmd, "xml", &xmlfile) < 0) {
- vshError(ctl, "%s", _("malformed xml argument"));
- goto out;
- }
-
- if (vshCommandOptBool(cmd, "live"))
- flags |= VIR_MIGRATE_LIVE;
- if (vshCommandOptBool(cmd, "p2p"))
- flags |= VIR_MIGRATE_PEER2PEER;
- if (vshCommandOptBool(cmd, "tunnelled"))
- flags |= VIR_MIGRATE_TUNNELLED;
-
- if (vshCommandOptBool(cmd, "persistent"))
- flags |= VIR_MIGRATE_PERSIST_DEST;
- if (vshCommandOptBool(cmd, "undefinesource"))
- flags |= VIR_MIGRATE_UNDEFINE_SOURCE;
-
- if (vshCommandOptBool(cmd, "suspend"))
- flags |= VIR_MIGRATE_PAUSED;
-
- if (vshCommandOptBool(cmd, "copy-storage-all"))
- flags |= VIR_MIGRATE_NON_SHARED_DISK;
-
- if (vshCommandOptBool(cmd, "copy-storage-inc"))
- flags |= VIR_MIGRATE_NON_SHARED_INC;
-
- if (vshCommandOptBool(cmd, "change-protection"))
- flags |= VIR_MIGRATE_CHANGE_PROTECTION;
-
- if (vshCommandOptBool(cmd, "unsafe"))
- flags |= VIR_MIGRATE_UNSAFE;
-
- if (xmlfile &&
- virFileReadAll(xmlfile, 8192, &xml) < 0) {
- vshError(ctl, _("file '%s' doesn't exist"), xmlfile);
- goto out;
- }
-
- if ((flags & VIR_MIGRATE_PEER2PEER) ||
- vshCommandOptBool(cmd, "direct")) {
- /* For peer2peer migration or direct migration we only expect one URI
- * a libvirt URI, or a hypervisor specific URI. */
-
- if (migrateuri != NULL) {
- vshError(ctl, "%s", _("migrate: Unexpected migrateuri for peer2peer/direct migration"));
- goto out;
- }
-
- if (virDomainMigrateToURI2(dom, desturi, NULL, xml, flags, dname, 0) == 0)
- ret = '0';
- } else {
- /* For traditional live migration, connect to the destination host directly. */
- virConnectPtr dconn = NULL;
- virDomainPtr ddom = NULL;
-
- dconn = virConnectOpenAuth(desturi, virConnectAuthPtrDefault, 0);
- if (!dconn) goto out;
-
- ddom = virDomainMigrate2(dom, dconn, xml, flags, dname, migrateuri, 0);
- if (ddom) {
- virDomainFree(ddom);
- ret = '0';
- }
- virConnectClose(dconn);
- }
-
-out:
- pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
-out_sig:
- if (dom) virDomainFree(dom);
- VIR_FREE(xml);
- ignore_value(safewrite(data->writefd, &ret, sizeof(ret)));
-}
-
-static void
-print_job_progress(const char *label, unsigned long long remaining,
- unsigned long long total)
-{
- int progress;
-
- if (total == 0)
- /* migration has not been started */
- return;
-
- 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 void
-vshMigrationTimeout(vshControl *ctl,
- virDomainPtr dom,
- void *opaque ATTRIBUTE_UNUSED)
-{
- vshDebug(ctl, VSH_ERR_DEBUG, "suspending the domain, "
- "since migration timed out\n");
- virDomainSuspend(dom);
-}
-
-static bool
-vshWatchJob(vshControl *ctl,
- virDomainPtr dom,
- bool verbose,
- int pipe_fd,
- int timeout,
- jobWatchTimeoutFunc timeout_func,
- void *opaque,
- const char *label)
-{
- struct sigaction sig_action;
- struct sigaction old_sig_action;
- struct pollfd pollfd;
- struct timeval start, curr;
- virDomainJobInfo jobinfo;
- int ret = -1;
- char retchar;
- bool functionReturn = false;
- sigset_t sigmask, oldsigmask;
-
- sigemptyset(&sigmask);
- sigaddset(&sigmask, SIGINT);
-
- intCaught = 0;
- sig_action.sa_sigaction = vshCatchInt;
- sig_action.sa_flags = SA_SIGINFO;
- sigemptyset(&sig_action.sa_mask);
- sigaction(SIGINT, &sig_action, &old_sig_action);
-
- pollfd.fd = pipe_fd;
- pollfd.events = POLLIN;
- pollfd.revents = 0;
-
- GETTIMEOFDAY(&start);
- while (1) {
-repoll:
- ret = poll(&pollfd, 1, 500);
- if (ret > 0) {
- if (pollfd.revents & POLLIN &&
- saferead(pipe_fd, &retchar, sizeof(retchar)) > 0 &&
- retchar == '0') {
- if (verbose) {
- /* print [100 %] */
- print_job_progress(label, 0, 1);
- }
- break;
- }
- goto cleanup;
- }
-
- if (ret < 0) {
- if (errno == EINTR) {
- if (intCaught) {
- virDomainAbortJob(dom);
- intCaught = 0;
- } else {
- goto repoll;
- }
- }
- goto cleanup;
- }
-
- GETTIMEOFDAY(&curr);
- if (timeout && (((int)(curr.tv_sec - start.tv_sec) * 1000 +
- (int)(curr.tv_usec - start.tv_usec) / 1000) >
- timeout * 1000)) {
- /* suspend the domain when migration timeouts. */
- vshDebug(ctl, VSH_ERR_DEBUG, "%s timeout", label);
- if (timeout_func)
- (timeout_func)(ctl, dom, opaque);
- timeout = 0;
- }
-
- if (verbose) {
- pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask);
- ret = virDomainGetJobInfo(dom, &jobinfo);
- pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
- if (ret == 0)
- print_job_progress(label, jobinfo.dataRemaining,
- jobinfo.dataTotal);
- }
- }
-
- functionReturn = true;
-
-cleanup:
- sigaction(SIGINT, &old_sig_action, NULL);
- return functionReturn;
-}
-
-static bool
-cmdMigrate(vshControl *ctl, const vshCmd *cmd)
-{
- virDomainPtr dom = NULL;
- int p[2] = {-1, -1};
- virThread workerThread;
- bool verbose = false;
- bool functionReturn = false;
- int timeout = 0;
- bool live_flag = false;
- vshCtrlData data;
-
- if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
- return false;
-
- if (vshCommandOptBool(cmd, "verbose"))
- verbose = true;
-
- if (vshCommandOptBool(cmd, "live"))
- live_flag = true;
- if (vshCommandOptInt(cmd, "timeout", &timeout) > 0) {
- if (! live_flag) {
- vshError(ctl, "%s",
- _("migrate: Unexpected timeout for offline migration"));
- goto cleanup;
- }
-
- if (timeout < 1) {
- vshError(ctl, "%s", _("migrate: Invalid timeout"));
- goto cleanup;
- }
-
- /* Ensure that we can multiply by 1000 without overflowing. */
- if (timeout > INT_MAX / 1000) {
- vshError(ctl, "%s", _("migrate: Timeout is too big"));
- goto cleanup;
- }
- }
-
- if (pipe(p) < 0)
- goto cleanup;
-
- data.ctl = ctl;
- data.cmd = cmd;
- data.writefd = p[1];
-
- if (virThreadCreate(&workerThread,
- true,
- doMigrate,
- &data) < 0)
- goto cleanup;
- functionReturn = vshWatchJob(ctl, dom, verbose, p[0], timeout,
- vshMigrationTimeout, NULL, _("Migration"));
-
- virThreadJoin(&workerThread);
-
-cleanup:
- virDomainFree(dom);
- VIR_FORCE_CLOSE(p[0]);
- VIR_FORCE_CLOSE(p[1]);
- return functionReturn;
-}
-
-/*
- * "migrate-setmaxdowntime" command
- */
-static const vshCmdInfo info_migrate_setmaxdowntime[] = {
- {"help", N_("set maximum tolerable downtime")},
- {"desc", N_("Set maximum tolerable downtime of a domain which is being live-migrated to another host.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_migrate_setmaxdowntime[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {"downtime", VSH_OT_INT, VSH_OFLAG_REQ, N_("maximum tolerable downtime (in milliseconds) for migration")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdMigrateSetMaxDowntime(vshControl *ctl, const vshCmd *cmd)
-{
- virDomainPtr dom = NULL;
- long long downtime = 0;
- bool ret = false;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
- return false;
-
- if (vshCommandOptLongLong(cmd, "downtime", &downtime) < 0 ||
- downtime < 1) {
- vshError(ctl, "%s", _("migrate: Invalid downtime"));
- goto done;
- }
-
- if (virDomainMigrateSetMaxDowntime(dom, downtime, 0))
- goto done;
-
- ret = true;
-
-done:
- virDomainFree(dom);
- return ret;
-}
-
-/*
- * "migrate-setspeed" command
- */
-static const vshCmdInfo info_migrate_setspeed[] = {
- {"help", N_("Set the maximum migration bandwidth")},
- {"desc", N_("Set the maximum migration bandwidth (in MiB/s) for a domain "
- "which is being migrated to another host.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_migrate_setspeed[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {"bandwidth", VSH_OT_INT, VSH_OFLAG_REQ,
- N_("migration bandwidth limit in MiB/s")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdMigrateSetMaxSpeed(vshControl *ctl, const vshCmd *cmd)
-{
- virDomainPtr dom = NULL;
- unsigned long bandwidth = 0;
- bool ret = false;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
- return false;
-
- if (vshCommandOptUL(cmd, "bandwidth", &bandwidth) < 0) {
- vshError(ctl, "%s", _("migrate: Invalid bandwidth"));
- goto done;
- }
-
- if (virDomainMigrateSetMaxSpeed(dom, bandwidth, 0) < 0)
- goto done;
-
- ret = true;
-
-done:
- virDomainFree(dom);
- return ret;
-}
-
-/*
- * "migrate-getspeed" command
- */
-static const vshCmdInfo info_migrate_getspeed[] = {
- {"help", N_("Get the maximum migration bandwidth")},
- {"desc", N_("Get the maximum migration bandwidth (in MiB/s) for a domain.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_migrate_getspeed[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdMigrateGetMaxSpeed(vshControl *ctl, const vshCmd *cmd)
-{
- virDomainPtr dom = NULL;
- unsigned long bandwidth;
- bool ret = false;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
- return false;
-
- if (virDomainMigrateGetMaxSpeed(dom, &bandwidth, 0) < 0)
- goto done;
-
- vshPrint(ctl, "%lu\n", bandwidth);
-
- ret = true;
-
-done:
- virDomainFree(dom);
- return ret;
-}
-
-typedef enum {
- VSH_CMD_BLOCK_JOB_ABORT = 0,
- VSH_CMD_BLOCK_JOB_INFO = 1,
- VSH_CMD_BLOCK_JOB_SPEED = 2,
- VSH_CMD_BLOCK_JOB_PULL = 3,
- VSH_CMD_BLOCK_JOB_COPY = 4,
-} vshCmdBlockJobMode;
-
-static int
-blockJobImpl(vshControl *ctl, const vshCmd *cmd,
- virDomainBlockJobInfoPtr info, int mode,
- virDomainPtr *pdom)
-{
- virDomainPtr dom = NULL;
- const char *name, *path;
- unsigned long bandwidth = 0;
- int ret = -1;
- const char *base = NULL;
- unsigned int flags = 0;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- goto cleanup;
-
- if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
- goto cleanup;
-
- if (vshCommandOptString(cmd, "path", &path) < 0)
- goto cleanup;
-
- if (vshCommandOptUL(cmd, "bandwidth", &bandwidth) < 0) {
- vshError(ctl, "%s", _("bandwidth must be a number"));
- goto cleanup;
- }
-
- switch ((vshCmdBlockJobMode) mode) {
- case VSH_CMD_BLOCK_JOB_ABORT:
- if (vshCommandOptBool(cmd, "async"))
- flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC;
- if (vshCommandOptBool(cmd, "pivot"))
- flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT;
- ret = virDomainBlockJobAbort(dom, path, flags);
- break;
- case VSH_CMD_BLOCK_JOB_INFO:
- ret = virDomainGetBlockJobInfo(dom, path, info, 0);
- break;
- case VSH_CMD_BLOCK_JOB_SPEED:
- ret = virDomainBlockJobSetSpeed(dom, path, bandwidth, 0);
- break;
- case VSH_CMD_BLOCK_JOB_PULL:
- if (vshCommandOptString(cmd, "base", &base) < 0)
- goto cleanup;
- if (base)
- ret = virDomainBlockRebase(dom, path, base, bandwidth, 0);
- else
- ret = virDomainBlockPull(dom, path, bandwidth, 0);
- break;
- case VSH_CMD_BLOCK_JOB_COPY:
- flags |= VIR_DOMAIN_BLOCK_REBASE_COPY;
- if (vshCommandOptBool(cmd, "shallow"))
- flags |= VIR_DOMAIN_BLOCK_REBASE_SHALLOW;
- if (vshCommandOptBool(cmd, "reuse-external"))
- flags |= VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT;
- if (vshCommandOptBool(cmd, "raw"))
- flags |= VIR_DOMAIN_BLOCK_REBASE_COPY_RAW;
- if (vshCommandOptString(cmd, "dest", &base) < 0)
- goto cleanup;
- ret = virDomainBlockRebase(dom, path, base, bandwidth, flags);
- }
-
-cleanup:
- if (pdom && ret == 0)
- *pdom = dom;
- else if (dom)
- virDomainFree(dom);
- return ret;
-}
-
-/*
- * "blockcopy" command
- */
-static const vshCmdInfo info_block_copy[] = {
- {"help", N_("Start a block copy operation.")},
- {"desc", N_("Populate a disk from its backing image.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_block_copy[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {"path", VSH_OT_DATA, VSH_OFLAG_REQ, N_("fully-qualified path of disk")},
- {"dest", VSH_OT_DATA, VSH_OFLAG_REQ, N_("path of the copy to create")},
- {"bandwidth", VSH_OT_DATA, VSH_OFLAG_NONE, N_("bandwidth limit in MiB/s")},
- {"shallow", VSH_OT_BOOL, 0, N_("make the copy share a backing chain")},
- {"reuse-external", VSH_OT_BOOL, 0, N_("reuse existing destination")},
- {"raw", VSH_OT_BOOL, 0, N_("use raw destination file")},
- {"wait", VSH_OT_BOOL, 0, N_("wait for job to reach mirroring phase")},
- {"verbose", VSH_OT_BOOL, 0, N_("with --wait, display the progress")},
- {"timeout", VSH_OT_INT, VSH_OFLAG_NONE,
- N_("with --wait, abort if copy exceeds timeout (in seconds)")},
- {"pivot", VSH_OT_BOOL, 0, N_("with --wait, pivot when mirroring starts")},
- {"finish", VSH_OT_BOOL, 0, N_("with --wait, quit when mirroring starts")},
- {"async", VSH_OT_BOOL, 0,
- N_("with --wait, don't wait for cancel to finish")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdBlockCopy(vshControl *ctl, const vshCmd *cmd)
-{
- virDomainPtr dom = NULL;
- bool ret = false;
- bool blocking = vshCommandOptBool(cmd, "wait");
- bool verbose = vshCommandOptBool(cmd, "verbose");
- bool pivot = vshCommandOptBool(cmd, "pivot");
- bool finish = vshCommandOptBool(cmd, "finish");
- int timeout = 0;
- struct sigaction sig_action;
- struct sigaction old_sig_action;
- sigset_t sigmask;
- struct timeval start;
- struct timeval curr;
- const char *path = NULL;
- bool quit = false;
- int abort_flags = 0;
-
- if (blocking) {
- if (pivot && finish) {
- vshError(ctl, "%s", _("cannot mix --pivot and --finish"));
- return false;
- }
- if (vshCommandOptInt(cmd, "timeout", &timeout) > 0) {
- if (timeout < 1) {
- vshError(ctl, "%s", _("migrate: Invalid timeout"));
- return false;
- }
-
- /* Ensure that we can multiply by 1000 without overflowing. */
- if (timeout > INT_MAX / 1000) {
- vshError(ctl, "%s", _("migrate: Timeout is too big"));
- return false;
- }
- }
- if (vshCommandOptString(cmd, "path", &path) < 0)
- return false;
- if (vshCommandOptBool(cmd, "async"))
- abort_flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC;
-
- sigemptyset(&sigmask);
- sigaddset(&sigmask, SIGINT);
-
- intCaught = 0;
- sig_action.sa_sigaction = vshCatchInt;
- sig_action.sa_flags = SA_SIGINFO;
- sigemptyset(&sig_action.sa_mask);
- sigaction(SIGINT, &sig_action, &old_sig_action);
-
- GETTIMEOFDAY(&start);
- } else if (verbose || vshCommandOptBool(cmd, "timeout") ||
- vshCommandOptBool(cmd, "async") || pivot || finish) {
- vshError(ctl, "%s", _("blocking control options require --wait"));
- return false;
- }
-
- if (blockJobImpl(ctl, cmd, NULL, VSH_CMD_BLOCK_JOB_COPY, &dom) < 0)
- goto cleanup;
-
- if (!blocking) {
- vshPrint(ctl, "%s", _("Block Copy started"));
- ret = true;
- goto cleanup;
- }
-
- while (blocking) {
- virDomainBlockJobInfo info;
- int result = virDomainGetBlockJobInfo(dom, path, &info, 0);
-
- if (result <= 0) {
- vshError(ctl, _("failed to query job for disk %s"), path);
- goto cleanup;
- }
- if (verbose)
- print_job_progress(_("Block Copy"), info.end - info.cur, info.end);
- if (info.cur == info.end)
- break;
-
- GETTIMEOFDAY(&curr);
- if (intCaught || (timeout &&
- (((int)(curr.tv_sec - start.tv_sec) * 1000 +
- (int)(curr.tv_usec - start.tv_usec) / 1000) >
- timeout * 1000))) {
- vshDebug(ctl, VSH_ERR_DEBUG,
- intCaught ? "interrupted" : "timeout");
- intCaught = 0;
- timeout = 0;
- quit = true;
- if (virDomainBlockJobAbort(dom, path, abort_flags) < 0) {
- vshError(ctl, _("failed to abort job for disk %s"), path);
- goto cleanup;
- }
- if (abort_flags & VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC)
- break;
- } else {
- usleep(500 * 1000);
- }
- }
-
- 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;
- }
- } else if (finish && virDomainBlockJobAbort(dom, path, abort_flags) < 0) {
- vshError(ctl, _("failed to finish job for disk %s"), path);
- goto cleanup;
- }
- vshPrint(ctl, "\n%s",
- quit ? _("Copy aborted") :
- pivot ? _("Successfully pivoted") :
- finish ? _("Successfully copied") :
- _("Now in mirroring phase"));
-
- ret = true;
-cleanup:
- if (dom)
- virDomainFree(dom);
- if (blocking)
- sigaction(SIGINT, &old_sig_action, NULL);
- return ret;
-}
-
-/*
- * "blockpull" command
- */
-static const vshCmdInfo info_block_pull[] = {
- {"help", N_("Populate a disk from its backing image.")},
- {"desc", N_("Populate a disk from its backing image.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_block_pull[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {"path", VSH_OT_DATA, VSH_OFLAG_REQ, N_("fully-qualified path of disk")},
- {"bandwidth", VSH_OT_DATA, VSH_OFLAG_NONE, N_("bandwidth limit in MiB/s")},
- {"base", VSH_OT_DATA, VSH_OFLAG_NONE,
- N_("path of backing file in chain for a partial pull")},
- {"wait", VSH_OT_BOOL, 0, N_("wait for job to finish")},
- {"verbose", VSH_OT_BOOL, 0, N_("with --wait, display the progress")},
- {"timeout", VSH_OT_INT, VSH_OFLAG_NONE,
- N_("with --wait, abort if pull exceeds timeout (in seconds)")},
- {"async", VSH_OT_BOOL, 0,
- N_("with --wait, don't wait for cancel to finish")},
- {NULL, 0, 0, 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");
- int timeout = 0;
- struct sigaction sig_action;
- struct sigaction old_sig_action;
- sigset_t sigmask;
- struct timeval start;
- struct timeval curr;
- const char *path = NULL;
- bool quit = false;
- int abort_flags = 0;
-
- if (blocking) {
- if (vshCommandOptInt(cmd, "timeout", &timeout) > 0) {
- if (timeout < 1) {
- vshError(ctl, "%s", _("invalid timeout"));
- return false;
- }
-
- /* Ensure that we can multiply by 1000 without overflowing. */
- if (timeout > INT_MAX / 1000) {
- vshError(ctl, "%s", _("timeout is too big"));
- return false;
- }
- }
- if (vshCommandOptString(cmd, "path", &path) < 0)
- return false;
- if (vshCommandOptBool(cmd, "async"))
- abort_flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC;
-
- sigemptyset(&sigmask);
- sigaddset(&sigmask, SIGINT);
-
- intCaught = 0;
- sig_action.sa_sigaction = vshCatchInt;
- sig_action.sa_flags = SA_SIGINFO;
- sigemptyset(&sig_action.sa_mask);
- sigaction(SIGINT, &sig_action, &old_sig_action);
-
- GETTIMEOFDAY(&start);
- } else if (verbose || vshCommandOptBool(cmd, "timeout") ||
- vshCommandOptBool(cmd, "async")) {
- vshError(ctl, "%s", _("blocking control options require --wait"));
- return false;
- }
-
- if (blockJobImpl(ctl, cmd, NULL, VSH_CMD_BLOCK_JOB_PULL, &dom) < 0)
- goto cleanup;
-
- if (!blocking) {
- vshPrint(ctl, "%s", _("Block Pull started"));
- ret = true;
- goto cleanup;
- }
-
- while (blocking) {
- virDomainBlockJobInfo info;
- int result = virDomainGetBlockJobInfo(dom, path, &info, 0);
-
- if (result < 0) {
- vshError(ctl, _("failed to query job for disk %s"), path);
- goto cleanup;
- }
- if (result == 0)
- break;
-
- if (verbose)
- print_job_progress(_("Block Pull"), info.end - info.cur, info.end);
-
- GETTIMEOFDAY(&curr);
- if (intCaught || (timeout &&
- (((int)(curr.tv_sec - start.tv_sec) * 1000 +
- (int)(curr.tv_usec - start.tv_usec) / 1000) >
- timeout * 1000))) {
- vshDebug(ctl, VSH_ERR_DEBUG,
- intCaught ? "interrupted" : "timeout");
- intCaught = 0;
- timeout = 0;
- quit = true;
- if (virDomainBlockJobAbort(dom, path, abort_flags) < 0) {
- vshError(ctl, _("failed to abort job for disk %s"), path);
- goto cleanup;
- }
- if (abort_flags & VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC)
- break;
- } else {
- usleep(500 * 1000);
- }
- }
-
- if (verbose && !quit) {
- /* printf [100 %] */
- print_job_progress(_("Block Pull"), 0, 1);
- }
- vshPrint(ctl, "\n%s", quit ? _("Pull aborted") : _("Pull complete"));
-
- ret = true;
-cleanup:
- if (dom)
- virDomainFree(dom);
- if (blocking)
- sigaction(SIGINT, &old_sig_action, NULL);
- return ret;
-}
-
-/*
- * "blockjob" command
- */
-static const vshCmdInfo info_block_job[] = {
- {"help", N_("Manage active block operations")},
- {"desc", N_("Query, adjust speed, or cancel active block operations.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_block_job[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {"path", VSH_OT_DATA, VSH_OFLAG_REQ, N_("fully-qualified path of disk")},
- {"abort", VSH_OT_BOOL, VSH_OFLAG_NONE,
- N_("abort the active job on the specified disk")},
- {"async", VSH_OT_BOOL, VSH_OFLAG_NONE,
- N_("don't wait for --abort to complete")},
- {"pivot", VSH_OT_BOOL, VSH_OFLAG_NONE,
- N_("conclude and pivot a copy job")},
- {"info", VSH_OT_BOOL, VSH_OFLAG_NONE,
- N_("get active job information for the specified disk")},
- {"bandwidth", VSH_OT_DATA, VSH_OFLAG_NONE,
- N_("set the Bandwidth limit in MiB/s")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdBlockJob(vshControl *ctl, const vshCmd *cmd)
-{
- int mode;
- virDomainBlockJobInfo info;
- const char *type;
- int ret;
- bool abortMode = (vshCommandOptBool(cmd, "abort") ||
- vshCommandOptBool(cmd, "async") ||
- vshCommandOptBool(cmd, "pivot"));
- bool infoMode = vshCommandOptBool(cmd, "info");
- bool bandwidth = vshCommandOptBool(cmd, "bandwidth");
-
- if (abortMode + infoMode + bandwidth > 1) {
- vshError(ctl, "%s",
- _("conflict between --abort, --info, and --bandwidth modes"));
- return false;
- }
-
- if (abortMode)
- mode = VSH_CMD_BLOCK_JOB_ABORT;
- else if (bandwidth)
- mode = VSH_CMD_BLOCK_JOB_SPEED;
- else
- mode = VSH_CMD_BLOCK_JOB_INFO;
-
- ret = blockJobImpl(ctl, cmd, &info, mode, NULL);
- if (ret < 0)
- return false;
-
- if (ret == 0 || mode != VSH_CMD_BLOCK_JOB_INFO)
- return true;
-
- switch (info.type) {
- case VIR_DOMAIN_BLOCK_JOB_TYPE_PULL:
- type = _("Block Pull");
- break;
- case VIR_DOMAIN_BLOCK_JOB_TYPE_COPY:
- type = _("Block Copy");
- break;
- default:
- type = _("Unknown job");
- break;
- }
-
- print_job_progress(type, info.end - info.cur, info.end);
- if (info.bandwidth != 0)
- vshPrint(ctl, _(" Bandwidth limit: %lu MiB/s\n"), info.bandwidth);
- return true;
-}
-
-/*
- * "blockresize" command
- */
-static const vshCmdInfo info_block_resize[] = {
- {"help", N_("Resize block device of domain.")},
- {"desc", N_("Resize block device of domain.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_block_resize[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {"path", VSH_OT_DATA, VSH_OFLAG_REQ,
- N_("Fully-qualified path of block device")},
- {"size", VSH_OT_INT, VSH_OFLAG_REQ,
- N_("New size of the block device, as scaled integer (default KiB)")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdBlockResize(vshControl *ctl, const vshCmd *cmd)
-{
- virDomainPtr dom;
- const char *path = NULL;
- unsigned long long size = 0;
- unsigned int flags = 0;
- int ret = false;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (vshCommandOptString(cmd, "path", (const char **) &path) < 0) {
- vshError(ctl, "%s", _("Path must not be empty"));
- return false;
- }
-
- if (vshCommandOptScaledInt(cmd, "size", &size, 1024, ULLONG_MAX) < 0) {
- vshError(ctl, "%s", _("Unable to parse integer"));
- return false;
- }
-
- /* Prefer the older interface of KiB. */
- if (size % 1024 == 0)
- size /= 1024;
- else
- flags |= VIR_DOMAIN_BLOCK_RESIZE_BYTES;
-
- if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
- return false;
-
- if (virDomainBlockResize(dom, path, size, flags) < 0) {
- vshError(ctl, _("Failed to resize block device '%s'"), path);
- } else {
- vshPrint(ctl, _("Block device '%s' is resized"), path);
- ret = true;
- }
-
- virDomainFree(dom);
- return ret;
-}
-
-/*
- * "net-autostart" command
- */
-static const vshCmdInfo info_network_autostart[] = {
- {"help", N_("autostart a network")},
- {"desc",
- N_("Configure a network to be automatically started at boot.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_network_autostart[] = {
- {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
- {"disable", VSH_OT_BOOL, 0, N_("disable autostarting")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNetworkAutostart(vshControl *ctl, const vshCmd *cmd)
-{
- virNetworkPtr network;
- const char *name;
- int autostart;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!(network = vshCommandOptNetwork(ctl, cmd, &name)))
- return false;
-
- autostart = !vshCommandOptBool(cmd, "disable");
-
- if (virNetworkSetAutostart(network, autostart) < 0) {
- if (autostart)
- vshError(ctl, _("failed to mark network %s as autostarted"), name);
- else
- vshError(ctl, _("failed to unmark network %s as autostarted"), name);
- virNetworkFree(network);
- return false;
- }
-
- if (autostart)
- vshPrint(ctl, _("Network %s marked as autostarted\n"), name);
- else
- vshPrint(ctl, _("Network %s unmarked as autostarted\n"), name);
-
- virNetworkFree(network);
- return true;
-}
-
-/*
- * "blkdeviotune" command
- */
-static const vshCmdInfo info_blkdeviotune[] = {
- {"help", N_("Set or query a block device I/O tuning parameters.")},
- {"desc", N_("Set or query disk I/O parameters such as block throttling.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_blkdeviotune[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("block device")},
- {"total_bytes_sec", VSH_OT_ALIAS, 0, "total-bytes-sec"},
- {"total-bytes-sec", VSH_OT_INT, VSH_OFLAG_NONE,
- N_("total throughput limit in bytes per second")},
- {"read_bytes_sec", VSH_OT_ALIAS, 0, "read-bytes-sec"},
- {"read-bytes-sec", VSH_OT_INT, VSH_OFLAG_NONE,
- N_("read throughput limit in bytes per second")},
- {"write_bytes_sec", VSH_OT_ALIAS, 0, "write-bytes-sec"},
- {"write-bytes-sec", VSH_OT_INT, VSH_OFLAG_NONE,
- N_("write throughput limit in bytes per second")},
- {"total_iops_sec", VSH_OT_ALIAS, 0, "total-iops-sec"},
- {"total-iops-sec", VSH_OT_INT, VSH_OFLAG_NONE,
- N_("total I/O operations limit per second")},
- {"read_iops_sec", VSH_OT_ALIAS, 0, "read-iops-sec"},
- {"read-iops-sec", VSH_OT_INT, VSH_OFLAG_NONE,
- N_("read I/O operations limit per second")},
- {"write_iops_sec", VSH_OT_ALIAS, 0, "write-iops-sec"},
- {"write-iops-sec", VSH_OT_INT, VSH_OFLAG_NONE,
- N_("write I/O operations limit per second")},
- {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
- {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
- {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdBlkdeviotune(vshControl *ctl, const vshCmd *cmd)
-{
- virDomainPtr dom = NULL;
- const char *name, *disk;
- unsigned long long total_bytes_sec = 0, read_bytes_sec = 0, write_bytes_sec = 0;
- unsigned long long total_iops_sec = 0, read_iops_sec = 0, write_iops_sec = 0;
- int nparams = 0;
- virTypedParameterPtr params = NULL;
- unsigned int flags = 0, i = 0;
- int rv = 0;
- bool current = vshCommandOptBool(cmd, "current");
- bool config = vshCommandOptBool(cmd, "config");
- bool live = vshCommandOptBool(cmd, "live");
- bool ret = false;
-
- if (current) {
- if (live || config) {
- vshError(ctl, "%s", _("--current must be specified exclusively"));
- return false;
- }
- flags = VIR_DOMAIN_AFFECT_CURRENT;
- } else {
- if (config)
- flags |= VIR_DOMAIN_AFFECT_CONFIG;
- if (live)
- flags |= VIR_DOMAIN_AFFECT_LIVE;
- }
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- goto cleanup;
-
- if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
- goto cleanup;
-
- if (vshCommandOptString(cmd, "device", &disk) < 0)
- goto cleanup;
-
- if ((rv = vshCommandOptULongLong(cmd, "total-bytes-sec",
- &total_bytes_sec)) < 0) {
- vshError(ctl, "%s",
- _("Unable to parse integer parameter"));
- goto cleanup;
- } else if (rv > 0) {
- nparams++;
- }
-
- if ((rv = vshCommandOptULongLong(cmd, "read-bytes-sec",
- &read_bytes_sec)) < 0) {
- vshError(ctl, "%s",
- _("Unable to parse integer parameter"));
- goto cleanup;
- } else if (rv > 0) {
- nparams++;
- }
-
- if ((rv = vshCommandOptULongLong(cmd, "write-bytes-sec",
- &write_bytes_sec)) < 0) {
- vshError(ctl, "%s",
- _("Unable to parse integer parameter"));
- goto cleanup;
- } else if (rv > 0) {
- nparams++;
- }
-
- if ((rv = vshCommandOptULongLong(cmd, "total-iops-sec",
- &total_iops_sec)) < 0) {
- vshError(ctl, "%s",
- _("Unable to parse integer parameter"));
- goto cleanup;
- } else if (rv > 0) {
- nparams++;
- }
-
- if ((rv = vshCommandOptULongLong(cmd, "read-iops-sec",
- &read_iops_sec)) < 0) {
- vshError(ctl, "%s",
- _("Unable to parse integer parameter"));
- goto cleanup;
- } else if (rv > 0) {
- nparams++;
- }
-
- if ((rv = vshCommandOptULongLong(cmd, "write-iops-sec",
- &write_iops_sec)) < 0) {
- vshError(ctl, "%s",
- _("Unable to parse integer parameter"));
- goto cleanup;
- } else if (rv > 0) {
- nparams++;
- }
-
- 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);
- }
-
- ret = true;
- goto cleanup;
- } else {
- /* Set the block I/O throttle, match by opt since parameters can be 0 */
- params = vshCalloc(ctl, nparams, sizeof(*params));
- i = 0;
-
- if (i < nparams && vshCommandOptBool(cmd, "total-bytes-sec") &&
- virTypedParameterAssign(¶ms[i++],
- VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_BYTES_SEC,
- VIR_TYPED_PARAM_ULLONG,
- total_bytes_sec) < 0)
- goto error;
-
- if (i < nparams && vshCommandOptBool(cmd, "read-bytes-sec") &&
- virTypedParameterAssign(¶ms[i++],
- VIR_DOMAIN_BLOCK_IOTUNE_READ_BYTES_SEC,
- VIR_TYPED_PARAM_ULLONG,
- read_bytes_sec) < 0)
- goto error;
-
- if (i < nparams && vshCommandOptBool(cmd, "write-bytes-sec") &&
- virTypedParameterAssign(¶ms[i++],
- VIR_DOMAIN_BLOCK_IOTUNE_WRITE_BYTES_SEC,
- VIR_TYPED_PARAM_ULLONG,
- write_bytes_sec) < 0)
- goto error;
-
- if (i < nparams && vshCommandOptBool(cmd, "total-iops-sec") &&
- virTypedParameterAssign(¶ms[i++],
- VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_IOPS_SEC,
- VIR_TYPED_PARAM_ULLONG,
- total_iops_sec) < 0)
- goto error;
-
- if (i < nparams && vshCommandOptBool(cmd, "read-iops-sec") &&
- virTypedParameterAssign(¶ms[i++],
- VIR_DOMAIN_BLOCK_IOTUNE_READ_IOPS_SEC,
- VIR_TYPED_PARAM_ULLONG,
- read_iops_sec) < 0)
- goto error;
-
- if (i < nparams && vshCommandOptBool(cmd, "write-iops-sec") &&
- virTypedParameterAssign(¶ms[i++],
- VIR_DOMAIN_BLOCK_IOTUNE_WRITE_IOPS_SEC,
- VIR_TYPED_PARAM_ULLONG,
- write_iops_sec) < 0)
- goto error;
-
- if (virDomainSetBlockIoTune(dom, disk, params, nparams, flags) < 0)
- goto error;
- }
-
- ret = true;
-
-cleanup:
- VIR_FREE(params);
- virDomainFree(dom);
- return ret;
-
-error:
- vshError(ctl, "%s", _("Unable to change block I/O throttle"));
- goto cleanup;
-}
-
-/*
- * "net-create" command
- */
-static const vshCmdInfo info_network_create[] = {
- {"help", N_("create a network from an XML file")},
- {"desc", N_("Create a network.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_network_create[] = {
- {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML network description")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNetworkCreate(vshControl *ctl, const vshCmd *cmd)
-{
- virNetworkPtr network;
- const char *from = NULL;
- bool ret = true;
- char *buffer;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (vshCommandOptString(cmd, "file", &from) <= 0)
- return false;
-
- if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
- return false;
-
- network = virNetworkCreateXML(ctl->conn, buffer);
- VIR_FREE(buffer);
-
- if (network != NULL) {
- vshPrint(ctl, _("Network %s created from %s\n"),
- virNetworkGetName(network), from);
- virNetworkFree(network);
- } else {
- vshError(ctl, _("Failed to create network from %s"), from);
- ret = false;
- }
- return ret;
-}
-
-
-/*
- * "net-define" command
- */
-static const vshCmdInfo info_network_define[] = {
- {"help", N_("define (but don't start) a network from an XML file")},
- {"desc", N_("Define a network.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_network_define[] = {
- {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML network description")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNetworkDefine(vshControl *ctl, const vshCmd *cmd)
-{
- virNetworkPtr network;
- const char *from = NULL;
- bool ret = true;
- char *buffer;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (vshCommandOptString(cmd, "file", &from) <= 0)
- return false;
-
- if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
- return false;
-
- network = virNetworkDefineXML(ctl->conn, buffer);
- VIR_FREE(buffer);
-
- if (network != NULL) {
- vshPrint(ctl, _("Network %s defined from %s\n"),
- virNetworkGetName(network), from);
- virNetworkFree(network);
- } else {
- vshError(ctl, _("Failed to define network from %s"), from);
- ret = false;
- }
- return ret;
-}
-
-
-/*
- * "net-destroy" command
- */
-static const vshCmdInfo info_network_destroy[] = {
- {"help", N_("destroy (stop) a network")},
- {"desc", N_("Forcefully stop a given network.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_network_destroy[] = {
- {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNetworkDestroy(vshControl *ctl, const vshCmd *cmd)
-{
- virNetworkPtr network;
- bool ret = true;
- const char *name;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!(network = vshCommandOptNetwork(ctl, cmd, &name)))
- return false;
-
- if (virNetworkDestroy(network) == 0) {
- vshPrint(ctl, _("Network %s destroyed\n"), name);
- } else {
- vshError(ctl, _("Failed to destroy network %s"), name);
- ret = false;
- }
-
- virNetworkFree(network);
- return ret;
-}
-
-
-/*
- * "net-dumpxml" command
- */
-static const vshCmdInfo info_network_dumpxml[] = {
- {"help", N_("network information in XML")},
- {"desc", N_("Output the network information as an XML dump to stdout.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_network_dumpxml[] = {
- {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
- {"inactive", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("network information of an inactive domain")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNetworkDumpXML(vshControl *ctl, const vshCmd *cmd)
-{
- virNetworkPtr network;
- bool ret = true;
- char *dump;
- unsigned int flags = 0;
- int inactive;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!(network = vshCommandOptNetwork(ctl, cmd, NULL)))
- return false;
-
- inactive = vshCommandOptBool(cmd, "inactive");
- if (inactive)
- flags |= VIR_NETWORK_XML_INACTIVE;
-
- dump = virNetworkGetXMLDesc(network, flags);
-
- if (dump != NULL) {
- vshPrint(ctl, "%s", dump);
- VIR_FREE(dump);
- } else {
- ret = false;
- }
-
- virNetworkFree(network);
- return ret;
-}
-
-/*
- * "net-info" command
- */
-static const vshCmdInfo info_network_info[] = {
- {"help", N_("network information")},
- {"desc", N_("Returns basic information about the network")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_network_info[] = {
- {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNetworkInfo(vshControl *ctl, const vshCmd *cmd)
-{
- virNetworkPtr network;
- char uuid[VIR_UUID_STRING_BUFLEN];
- int autostart;
- int persistent = -1;
- int active = -1;
- char *bridge = NULL;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!(network = vshCommandOptNetwork(ctl, cmd, NULL)))
- return false;
-
- vshPrint(ctl, "%-15s %s\n", _("Name"), virNetworkGetName(network));
-
- if (virNetworkGetUUIDString(network, uuid) == 0)
- vshPrint(ctl, "%-15s %s\n", _("UUID"), uuid);
-
- active = virNetworkIsActive(network);
- if (active >= 0)
- vshPrint(ctl, "%-15s %s\n", _("Active:"), active? _("yes") : _("no"));
-
- persistent = virNetworkIsPersistent(network);
- if (persistent < 0)
- vshPrint(ctl, "%-15s %s\n", _("Persistent:"), _("unknown"));
- else
- vshPrint(ctl, "%-15s %s\n", _("Persistent:"), persistent ? _("yes") : _("no"));
-
- if (virNetworkGetAutostart(network, &autostart) < 0)
- vshPrint(ctl, "%-15s %s\n", _("Autostart:"), _("no autostart"));
- else
- vshPrint(ctl, "%-15s %s\n", _("Autostart:"), autostart ? _("yes") : _("no"));
-
- bridge = virNetworkGetBridgeName(network);
- if (bridge)
- vshPrint(ctl, "%-15s %s\n", _("Bridge:"), bridge);
-
- VIR_FREE(bridge);
- virNetworkFree(network);
- return true;
-}
-
-/*
- * "iface-edit" command
- */
-static const vshCmdInfo info_interface_edit[] = {
- {"help", N_("edit XML configuration for a physical host interface")},
- {"desc", N_("Edit the XML configuration for a physical host interface.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_interface_edit[] = {
- {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name or MAC address")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdInterfaceEdit(vshControl *ctl, const vshCmd *cmd)
-{
- bool ret = false;
- virInterfacePtr iface = NULL;
- virInterfacePtr iface_edited = NULL;
- unsigned int flags = VIR_INTERFACE_XML_INACTIVE;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- goto cleanup;
-
- iface = vshCommandOptInterface(ctl, cmd, NULL);
- if (iface == NULL)
- goto cleanup;
-
-#define EDIT_GET_XML virInterfaceGetXMLDesc(iface, flags)
-#define EDIT_NOT_CHANGED \
- vshPrint(ctl, _("Interface %s XML configuration not changed.\n"), \
- virInterfaceGetName(iface)); \
- ret = true; goto edit_cleanup;
-#define EDIT_DEFINE \
- (iface_edited = virInterfaceDefineXML(ctl->conn, doc_edited, 0))
-#define EDIT_FREE \
- if (iface_edited) \
- virInterfaceFree(iface_edited);
-#include "virsh-edit.c"
-
- vshPrint(ctl, _("Interface %s XML configuration edited.\n"),
- virInterfaceGetName(iface_edited));
-
- ret = true;
-
-cleanup:
- if (iface)
- virInterfaceFree(iface);
- if (iface_edited)
- virInterfaceFree(iface_edited);
-
- return ret;
-}
-
-/*
- * "net-list" command
- */
-static const vshCmdInfo info_network_list[] = {
- {"help", N_("list networks")},
- {"desc", N_("Returns list of networks.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_network_list[] = {
- {"inactive", VSH_OT_BOOL, 0, N_("list inactive networks")},
- {"all", VSH_OT_BOOL, 0, N_("list inactive & active networks")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNetworkList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
-{
- bool inactive = vshCommandOptBool(cmd, "inactive");
- bool all = vshCommandOptBool(cmd, "all");
- bool active = !inactive || all;
- int maxactive = 0, maxinactive = 0, i;
- char **activeNames = NULL, **inactiveNames = NULL;
- inactive |= all;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (active) {
- maxactive = virConnectNumOfNetworks(ctl->conn);
- if (maxactive < 0) {
- vshError(ctl, "%s", _("Failed to list active networks"));
- return false;
- }
- if (maxactive) {
- activeNames = vshMalloc(ctl, sizeof(char *) * maxactive);
-
- if ((maxactive = virConnectListNetworks(ctl->conn, activeNames,
- maxactive)) < 0) {
- vshError(ctl, "%s", _("Failed to list active networks"));
- VIR_FREE(activeNames);
- return false;
- }
-
- qsort(&activeNames[0], maxactive, sizeof(char *), vshNameSorter);
- }
- }
- if (inactive) {
- maxinactive = virConnectNumOfDefinedNetworks(ctl->conn);
- if (maxinactive < 0) {
- vshError(ctl, "%s", _("Failed to list inactive networks"));
- VIR_FREE(activeNames);
- return false;
- }
- if (maxinactive) {
- inactiveNames = vshMalloc(ctl, sizeof(char *) * maxinactive);
-
- if ((maxinactive =
- virConnectListDefinedNetworks(ctl->conn, inactiveNames,
- maxinactive)) < 0) {
- vshError(ctl, "%s", _("Failed to list inactive networks"));
- VIR_FREE(activeNames);
- VIR_FREE(inactiveNames);
- return false;
- }
-
- qsort(&inactiveNames[0], maxinactive, sizeof(char*), vshNameSorter);
- }
- }
- vshPrintExtra(ctl, "%-20s %-10s %s\n", _("Name"), _("State"),
- _("Autostart"));
- vshPrintExtra(ctl, "-----------------------------------------\n");
-
- for (i = 0; i < maxactive; i++) {
- virNetworkPtr network =
- virNetworkLookupByName(ctl->conn, activeNames[i]);
- const char *autostartStr;
- int autostart = 0;
-
- /* this kind of work with networks is not atomic operation */
- if (!network) {
- VIR_FREE(activeNames[i]);
- continue;
- }
-
- if (virNetworkGetAutostart(network, &autostart) < 0)
- autostartStr = _("no autostart");
- else
- autostartStr = autostart ? _("yes") : _("no");
-
- vshPrint(ctl, "%-20s %-10s %-10s\n",
- virNetworkGetName(network),
- _("active"),
- autostartStr);
- virNetworkFree(network);
- VIR_FREE(activeNames[i]);
- }
- for (i = 0; i < maxinactive; i++) {
- virNetworkPtr network = virNetworkLookupByName(ctl->conn, inactiveNames[i]);
- const char *autostartStr;
- int autostart = 0;
-
- /* this kind of work with networks is not atomic operation */
- if (!network) {
- VIR_FREE(inactiveNames[i]);
- continue;
- }
-
- if (virNetworkGetAutostart(network, &autostart) < 0)
- autostartStr = _("no autostart");
- else
- autostartStr = autostart ? _("yes") : _("no");
-
- vshPrint(ctl, "%-20s %-10s %-10s\n",
- inactiveNames[i],
- _("inactive"),
- autostartStr);
-
- virNetworkFree(network);
- VIR_FREE(inactiveNames[i]);
- }
- VIR_FREE(activeNames);
- VIR_FREE(inactiveNames);
- return true;
-}
-
-
-/*
- * "net-name" command
- */
-static const vshCmdInfo info_network_name[] = {
- {"help", N_("convert a network UUID to network name")},
- {"desc", ""},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_network_name[] = {
- {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network uuid")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNetworkName(vshControl *ctl, const vshCmd *cmd)
-{
- virNetworkPtr network;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
- if (!(network = vshCommandOptNetworkBy(ctl, cmd, NULL,
- VSH_BYUUID)))
- return false;
-
- vshPrint(ctl, "%s\n", virNetworkGetName(network));
- virNetworkFree(network);
- return true;
-}
-
-
-/*
- * "net-start" command
- */
-static const vshCmdInfo info_network_start[] = {
- {"help", N_("start a (previously defined) inactive network")},
- {"desc", N_("Start a network.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_network_start[] = {
- {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNetworkStart(vshControl *ctl, const vshCmd *cmd)
-{
- virNetworkPtr network;
- bool ret = true;
- const char *name = NULL;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!(network = vshCommandOptNetwork(ctl, cmd, &name)))
- return false;
-
- if (virNetworkCreate(network) == 0) {
- vshPrint(ctl, _("Network %s started\n"), name);
- } else {
- vshError(ctl, _("Failed to start network %s"), name);
- ret = false;
- }
- virNetworkFree(network);
- return ret;
-}
-
-
-/*
- * "net-undefine" command
- */
-static const vshCmdInfo info_network_undefine[] = {
- {"help", N_("undefine an inactive network")},
- {"desc", N_("Undefine the configuration for an inactive network.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_network_undefine[] = {
- {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNetworkUndefine(vshControl *ctl, const vshCmd *cmd)
-{
- virNetworkPtr network;
- bool ret = true;
- const char *name;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!(network = vshCommandOptNetwork(ctl, cmd, &name)))
- return false;
-
- if (virNetworkUndefine(network) == 0) {
- vshPrint(ctl, _("Network %s has been undefined\n"), name);
- } else {
- vshError(ctl, _("Failed to undefine network %s"), name);
- ret = false;
- }
-
- virNetworkFree(network);
- return ret;
-}
-
-
-/*
- * "net-uuid" command
- */
-static const vshCmdInfo info_network_uuid[] = {
- {"help", N_("convert a network name to network UUID")},
- {"desc", ""},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_network_uuid[] = {
- {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNetworkUuid(vshControl *ctl, const vshCmd *cmd)
-{
- virNetworkPtr network;
- char uuid[VIR_UUID_STRING_BUFLEN];
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!(network = vshCommandOptNetworkBy(ctl, cmd, NULL,
- VSH_BYNAME)))
- return false;
-
- if (virNetworkGetUUIDString(network, uuid) != -1)
- vshPrint(ctl, "%s\n", uuid);
- else
- vshError(ctl, "%s", _("failed to get network UUID"));
-
- virNetworkFree(network);
- return true;
-}
-
-
-/**************************************************************************/
-/*
- * "iface-list" command
- */
-static const vshCmdInfo info_interface_list[] = {
- {"help", N_("list physical host interfaces")},
- {"desc", N_("Returns list of physical host interfaces.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_interface_list[] = {
- {"inactive", VSH_OT_BOOL, 0, N_("list inactive interfaces")},
- {"all", VSH_OT_BOOL, 0, N_("list inactive & active interfaces")},
- {NULL, 0, 0, NULL}
-};
-static bool
-cmdInterfaceList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
-{
- bool inactive = vshCommandOptBool(cmd, "inactive");
- bool all = vshCommandOptBool(cmd, "all");
- bool active = !inactive || all;
- int maxactive = 0, maxinactive = 0, i;
- char **activeNames = NULL, **inactiveNames = NULL;
- inactive |= all;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (active) {
- maxactive = virConnectNumOfInterfaces(ctl->conn);
- if (maxactive < 0) {
- vshError(ctl, "%s", _("Failed to list active interfaces"));
- return false;
- }
- if (maxactive) {
- activeNames = vshMalloc(ctl, sizeof(char *) * maxactive);
-
- if ((maxactive = virConnectListInterfaces(ctl->conn, activeNames,
- maxactive)) < 0) {
- vshError(ctl, "%s", _("Failed to list active interfaces"));
- VIR_FREE(activeNames);
- return false;
- }
-
- qsort(&activeNames[0], maxactive, sizeof(char *), vshNameSorter);
- }
- }
- if (inactive) {
- maxinactive = virConnectNumOfDefinedInterfaces(ctl->conn);
- if (maxinactive < 0) {
- vshError(ctl, "%s", _("Failed to list inactive interfaces"));
- VIR_FREE(activeNames);
- return false;
- }
- if (maxinactive) {
- inactiveNames = vshMalloc(ctl, sizeof(char *) * maxinactive);
-
- if ((maxinactive =
- virConnectListDefinedInterfaces(ctl->conn, inactiveNames,
- maxinactive)) < 0) {
- vshError(ctl, "%s", _("Failed to list inactive interfaces"));
- VIR_FREE(activeNames);
- VIR_FREE(inactiveNames);
- return false;
- }
-
- qsort(&inactiveNames[0], maxinactive, sizeof(char*), vshNameSorter);
- }
- }
- vshPrintExtra(ctl, "%-20s %-10s %s\n", _("Name"), _("State"),
- _("MAC Address"));
- vshPrintExtra(ctl, "--------------------------------------------\n");
-
- for (i = 0; i < maxactive; i++) {
- virInterfacePtr iface =
- virInterfaceLookupByName(ctl->conn, activeNames[i]);
-
- /* this kind of work with interfaces is not atomic */
- if (!iface) {
- VIR_FREE(activeNames[i]);
- continue;
- }
-
- vshPrint(ctl, "%-20s %-10s %s\n",
- virInterfaceGetName(iface),
- _("active"),
- virInterfaceGetMACString(iface));
- virInterfaceFree(iface);
- VIR_FREE(activeNames[i]);
- }
- for (i = 0; i < maxinactive; i++) {
- virInterfacePtr iface =
- virInterfaceLookupByName(ctl->conn, inactiveNames[i]);
-
- /* this kind of work with interfaces is not atomic */
- if (!iface) {
- VIR_FREE(inactiveNames[i]);
- continue;
- }
-
- vshPrint(ctl, "%-20s %-10s %s\n",
- virInterfaceGetName(iface),
- _("inactive"),
- virInterfaceGetMACString(iface));
- virInterfaceFree(iface);
- VIR_FREE(inactiveNames[i]);
- }
- VIR_FREE(activeNames);
- VIR_FREE(inactiveNames);
- return true;
-
-}
-
-/*
- * "iface-name" command
- */
-static const vshCmdInfo info_interface_name[] = {
- {"help", N_("convert an interface MAC address to interface name")},
- {"desc", ""},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_interface_name[] = {
- {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface mac")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdInterfaceName(vshControl *ctl, const vshCmd *cmd)
-{
- virInterfacePtr iface;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
- if (!(iface = vshCommandOptInterfaceBy(ctl, cmd, NULL, NULL,
- VSH_BYMAC)))
- return false;
-
- vshPrint(ctl, "%s\n", virInterfaceGetName(iface));
- virInterfaceFree(iface);
- return true;
-}
-
-/*
- * "iface-mac" command
- */
-static const vshCmdInfo info_interface_mac[] = {
- {"help", N_("convert an interface name to interface MAC address")},
- {"desc", ""},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_interface_mac[] = {
- {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdInterfaceMAC(vshControl *ctl, const vshCmd *cmd)
-{
- virInterfacePtr iface;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
- if (!(iface = vshCommandOptInterfaceBy(ctl, cmd, NULL, NULL,
- VSH_BYNAME)))
- return false;
-
- vshPrint(ctl, "%s\n", virInterfaceGetMACString(iface));
- virInterfaceFree(iface);
- return true;
-}
-
-/*
- * "iface-dumpxml" command
- */
-static const vshCmdInfo info_interface_dumpxml[] = {
- {"help", N_("interface information in XML")},
- {"desc", N_("Output the physical host interface information as an XML dump to stdout.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_interface_dumpxml[] = {
- {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name or MAC address")},
- {"inactive", VSH_OT_BOOL, 0, N_("show inactive defined XML")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdInterfaceDumpXML(vshControl *ctl, const vshCmd *cmd)
-{
- virInterfacePtr iface;
- bool ret = true;
- char *dump;
- unsigned int flags = 0;
- bool inactive = vshCommandOptBool(cmd, "inactive");
-
- if (inactive)
- flags |= VIR_INTERFACE_XML_INACTIVE;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!(iface = vshCommandOptInterface(ctl, cmd, NULL)))
- return false;
-
- dump = virInterfaceGetXMLDesc(iface, flags);
- if (dump != NULL) {
- vshPrint(ctl, "%s", dump);
- VIR_FREE(dump);
- } else {
- ret = false;
- }
-
- virInterfaceFree(iface);
- return ret;
-}
-
-/*
- * "iface-define" command
- */
-static const vshCmdInfo info_interface_define[] = {
- {"help", N_("define (but don't start) a physical host interface from an XML file")},
- {"desc", N_("Define a physical host interface.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_interface_define[] = {
- {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML interface description")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdInterfaceDefine(vshControl *ctl, const vshCmd *cmd)
-{
- virInterfacePtr iface;
- const char *from = NULL;
- bool ret = true;
- char *buffer;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (vshCommandOptString(cmd, "file", &from) <= 0)
- return false;
-
- if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
- return false;
-
- iface = virInterfaceDefineXML(ctl->conn, buffer, 0);
- VIR_FREE(buffer);
-
- if (iface != NULL) {
- vshPrint(ctl, _("Interface %s defined from %s\n"),
- virInterfaceGetName(iface), from);
- virInterfaceFree(iface);
- } else {
- vshError(ctl, _("Failed to define interface from %s"), from);
- ret = false;
- }
- return ret;
-}
-
-/*
- * "iface-undefine" command
- */
-static const vshCmdInfo info_interface_undefine[] = {
- {"help", N_("undefine a physical host interface (remove it from configuration)")},
- {"desc", N_("undefine an interface.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_interface_undefine[] = {
- {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name or MAC address")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdInterfaceUndefine(vshControl *ctl, const vshCmd *cmd)
-{
- virInterfacePtr iface;
- bool ret = true;
- const char *name;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!(iface = vshCommandOptInterface(ctl, cmd, &name)))
- return false;
-
- if (virInterfaceUndefine(iface) == 0) {
- vshPrint(ctl, _("Interface %s undefined\n"), name);
- } else {
- vshError(ctl, _("Failed to undefine interface %s"), name);
- ret = false;
- }
-
- virInterfaceFree(iface);
- return ret;
-}
-
-/*
- * "iface-start" command
- */
-static const vshCmdInfo info_interface_start[] = {
- {"help", N_("start a physical host interface (enable it / \"if-up\")")},
- {"desc", N_("start a physical host interface.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_interface_start[] = {
- {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name or MAC address")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdInterfaceStart(vshControl *ctl, const vshCmd *cmd)
-{
- virInterfacePtr iface;
- bool ret = true;
- const char *name;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!(iface = vshCommandOptInterface(ctl, cmd, &name)))
- return false;
-
- if (virInterfaceCreate(iface, 0) == 0) {
- vshPrint(ctl, _("Interface %s started\n"), name);
- } else {
- vshError(ctl, _("Failed to start interface %s"), name);
- ret = false;
- }
-
- virInterfaceFree(iface);
- return ret;
-}
-
-/*
- * "iface-destroy" command
- */
-static const vshCmdInfo info_interface_destroy[] = {
- {"help", N_("destroy a physical host interface (disable it / \"if-down\")")},
- {"desc", N_("forcefully stop a physical host interface.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_interface_destroy[] = {
- {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name or MAC address")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdInterfaceDestroy(vshControl *ctl, const vshCmd *cmd)
-{
- virInterfacePtr iface;
- bool ret = true;
- const char *name;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!(iface = vshCommandOptInterface(ctl, cmd, &name)))
- return false;
-
- if (virInterfaceDestroy(iface, 0) == 0) {
- vshPrint(ctl, _("Interface %s destroyed\n"), name);
- } else {
- vshError(ctl, _("Failed to destroy interface %s"), name);
- ret = false;
- }
-
- virInterfaceFree(iface);
- return ret;
-}
-
-/*
- * "iface-begin" command
- */
-static const vshCmdInfo info_interface_begin[] = {
- {"help", N_("create a snapshot of current interfaces settings, "
- "which can be later committed (iface-commit) or "
- "restored (iface-rollback)")},
- {"desc", N_("Create a restore point for interfaces settings")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_interface_begin[] = {
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdInterfaceBegin(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
-{
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (virInterfaceChangeBegin(ctl->conn, 0) < 0) {
- vshError(ctl, "%s", _("Failed to begin network config change transaction"));
- return false;
- }
-
- vshPrint(ctl, "%s", _("Network config change transaction started\n"));
- return true;
-}
-
-/*
- * "iface-commit" command
- */
-static const vshCmdInfo info_interface_commit[] = {
- {"help", N_("commit changes made since iface-begin and free restore point")},
- {"desc", N_("commit changes and free restore point")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_interface_commit[] = {
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdInterfaceCommit(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
-{
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (virInterfaceChangeCommit(ctl->conn, 0) < 0) {
- vshError(ctl, "%s", _("Failed to commit network config change transaction"));
- return false;
- }
-
- vshPrint(ctl, "%s", _("Network config change transaction committed\n"));
- return true;
-}
-
-/*
- * "iface-rollback" command
- */
-static const vshCmdInfo info_interface_rollback[] = {
- {"help", N_("rollback to previous saved configuration created via iface-begin")},
- {"desc", N_("rollback to previous restore point")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_interface_rollback[] = {
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdInterfaceRollback(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
-{
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (virInterfaceChangeRollback(ctl->conn, 0) < 0) {
- vshError(ctl, "%s", _("Failed to rollback network config change transaction"));
- return false;
- }
-
- vshPrint(ctl, "%s", _("Network config change transaction rolled back\n"));
- return true;
-}
-
-/*
- * "iface-bridge" command
- */
-static const vshCmdInfo info_interface_bridge[] = {
- {"help", N_("create a bridge device and attach an existing network device to it")},
- {"desc", N_("bridge an existing network device")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_interface_bridge[] = {
- {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("existing interface name")},
- {"bridge", VSH_OT_DATA, VSH_OFLAG_REQ, N_("new bridge device name")},
- {"no-stp", VSH_OT_BOOL, 0, N_("do not enable STP for this bridge")},
- {"delay", VSH_OT_INT, 0,
- N_("number of seconds to squelch traffic on newly connected ports")},
- {"no-start", VSH_OT_BOOL, 0, N_("don't start the bridge immediately")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdInterfaceBridge(vshControl *ctl, const vshCmd *cmd)
-{
- bool ret = false;
- virInterfacePtr if_handle = NULL, br_handle = NULL;
- const char *if_name, *br_name;
- char *if_type = NULL, *if2_name = NULL, *delay_str = NULL;
- bool stp = false, nostart = false;
- unsigned int delay = 0;
- char *if_xml = NULL;
- xmlChar *br_xml = NULL;
- int br_xml_size;
- xmlDocPtr xml_doc = NULL;
- xmlXPathContextPtr ctxt = NULL;
- xmlNodePtr top_node, br_node, if_node, cur;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- goto cleanup;
-
- /* Get a handle to the original device */
- if (!(if_handle = vshCommandOptInterfaceBy(ctl, cmd, "interface",
- &if_name, VSH_BYNAME))) {
- goto cleanup;
- }
-
- /* Name for new bridge device */
- if (vshCommandOptString(cmd, "bridge", &br_name) <= 0) {
- vshError(ctl, "%s", _("Missing bridge device name in command"));
- goto cleanup;
- }
-
- /* make sure "new" device doesn't already exist */
- if ((br_handle = virInterfaceLookupByName(ctl->conn, br_name))) {
- vshError(ctl, _("Network device %s already exists"), br_name);
- goto cleanup;
- }
-
- /* use "no-stp" because we want "stp" to default true */
- stp = !vshCommandOptBool(cmd, "no-stp");
-
- if (vshCommandOptUInt(cmd, "delay", &delay) < 0) {
- vshError(ctl, "%s", _("Unable to parse delay parameter"));
- goto cleanup;
- }
-
- nostart = vshCommandOptBool(cmd, "no-start");
-
- /* Get the original interface into an xmlDoc */
- if (!(if_xml = virInterfaceGetXMLDesc(if_handle, VIR_INTERFACE_XML_INACTIVE)))
- goto cleanup;
- if (!(xml_doc = virXMLParseStringCtxt(if_xml,
- _("(interface definition)"), &ctxt))) {
- vshError(ctl, _("Failed to parse configuration of %s"), if_name);
- goto cleanup;
- }
- top_node = ctxt->node;
-
- /* Verify that the original device isn't already a bridge. */
- if (!(if_type = virXMLPropString(top_node, "type"))) {
- vshError(ctl, _("Existing device %s has no type"), if_name);
- goto cleanup;
- }
-
- if (STREQ(if_type, "bridge")) {
- vshError(ctl, _("Existing device %s is already a bridge"), if_name);
- goto cleanup;
- }
-
- /* verify the name in the XML matches the device name */
- if (!(if2_name = virXMLPropString(top_node, "name")) ||
- STRNEQ(if2_name, if_name)) {
- vshError(ctl, _("Interface name from config %s doesn't match given supplied name %s"),
- if2_name, if_name);
- goto cleanup;
- }
-
- /* Create a node under . */
- if (!(br_node = xmlNewChild(top_node, NULL, BAD_CAST "bridge", NULL))) {
- vshError(ctl, "%s", _("Failed to create bridge node in xml document"));
- goto cleanup;
- }
-
- /* Set stp and delay attributes in according to the
- * commandline options.
- */
- if (!xmlSetProp(br_node, BAD_CAST "stp", BAD_CAST (stp ? "on" : "off"))) {
- vshError(ctl, "%s", _("Failed to set stp attribute in xml document"));
- goto cleanup;
- }
-
- if ((delay || stp) &&
- ((virAsprintf(&delay_str, "%d", delay) < 0) ||
- !xmlSetProp(br_node, BAD_CAST "delay", BAD_CAST delay_str))) {
- vshError(ctl, _("Failed to set bridge delay %d in xml document"), delay);
- goto cleanup;
- }
-
- /* Change the type of the outer/master interface to "bridge" and the
- * name to the provided bridge name.
- */
- if (!xmlSetProp(top_node, BAD_CAST "type", BAD_CAST "bridge")) {
- vshError(ctl, "%s", _("Failed to set bridge interface type to 'bridge' in xml document"));
- goto cleanup;
- }
-
- if (!xmlSetProp(top_node, BAD_CAST "name", BAD_CAST br_name)) {
- vshError(ctl, _("Failed to set master bridge interface name to '%s' in xml document"),
- br_name);
- goto cleanup;
- }
-
- /* Create an node under that uses the
- * original interface's type and name.
- */
- if (!(if_node = xmlNewChild(br_node, NULL, BAD_CAST "interface", NULL))) {
- vshError(ctl, "%s", _("Failed to create interface node under bridge node in xml document"));
- goto cleanup;
- }
-
- /* set the type of the inner/slave interface to the original
- * if_type, and the name to the original if_name.
- */
- if (!xmlSetProp(if_node, BAD_CAST "type", BAD_CAST if_type)) {
- vshError(ctl, _("Failed to set new slave interface type to '%s' in xml document"),
- if_name);
- goto cleanup;
- }
-
- if (!xmlSetProp(if_node, BAD_CAST "name", BAD_CAST if_name)) {
- vshError(ctl, _("Failed to set new slave interface name to '%s' in xml document"),
- br_name);
- goto cleanup;
- }
-
- /* Cycle through all the nodes under the original ,
- * moving all , and nodes down into the new
- * lower level .
- */
- cur = top_node->children;
- while (cur) {
- xmlNodePtr old = cur;
-
- cur = cur->next;
- if ((old->type == XML_ELEMENT_NODE) &&
- (xmlStrEqual(old->name, BAD_CAST "mac") || /* ethernet stuff to move down */
- xmlStrEqual(old->name, BAD_CAST "bond") || /* bond stuff to move down */
- xmlStrEqual(old->name, BAD_CAST "vlan"))) { /* vlan stuff to move down */
- xmlUnlinkNode(old);
- if (!xmlAddChild(if_node, old)) {
- vshError(ctl, _("Failed to move '%s' element in xml document"), old->name);
- xmlFreeNode(old);
- goto cleanup;
- }
- }
- }
-
- /* The document should now be fully converted; write it out to a string. */
- xmlDocDumpMemory(xml_doc, &br_xml, &br_xml_size);
-
- if (!br_xml || br_xml_size <= 0) {
- vshError(ctl, _("Failed to format new xml document for bridge %s"), br_name);
- goto cleanup;
- }
-
-
- /* br_xml is the new interface to define. It will automatically undefine the
- * independent original interface.
- */
- if (!(br_handle = virInterfaceDefineXML(ctl->conn, (char *) br_xml, 0))) {
- vshError(ctl, _("Failed to define new bridge interface %s"),
- br_name);
- goto cleanup;
- }
-
- vshPrint(ctl, _("Created bridge %s with attached device %s\n"),
- br_name, if_name);
-
- /* start it up unless requested not to */
- if (!nostart) {
- if (virInterfaceCreate(br_handle, 0) < 0) {
- vshError(ctl, _("Failed to start bridge interface %s"), br_name);
- goto cleanup;
- }
- vshPrint(ctl, _("Bridge interface %s started\n"), br_name);
- }
-
- ret = true;
- cleanup:
- if (if_handle)
- virInterfaceFree(if_handle);
- if (br_handle)
- virInterfaceFree(br_handle);
- VIR_FREE(if_xml);
- VIR_FREE(br_xml);
- VIR_FREE(if_type);
- VIR_FREE(if2_name);
- VIR_FREE(delay_str);
- xmlXPathFreeContext(ctxt);
- xmlFreeDoc(xml_doc);
- return ret;
-}
-
-/*
- * "iface-unbridge" command
- */
-static const vshCmdInfo info_interface_unbridge[] = {
- {"help", N_("undefine a bridge device after detaching its slave device")},
- {"desc", N_("unbridge a network device")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_interface_unbridge[] = {
- {"bridge", VSH_OT_DATA, VSH_OFLAG_REQ, N_("current bridge device name")},
- {"no-start", VSH_OT_BOOL, 0,
- N_("don't start the un-slaved interface immediately (not recommended)")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdInterfaceUnbridge(vshControl *ctl, const vshCmd *cmd)
-{
- bool ret = false;
- virInterfacePtr if_handle = NULL, br_handle = NULL;
- const char *br_name;
- char *if_type = NULL, *if_name = NULL;
- bool nostart = false;
- char *br_xml = NULL;
- xmlChar *if_xml = NULL;
- int if_xml_size;
- xmlDocPtr xml_doc = NULL;
- xmlXPathContextPtr ctxt = NULL;
- xmlNodePtr top_node, br_node, if_node, cur;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- goto cleanup;
-
- /* Get a handle to the original device */
- if (!(br_handle = vshCommandOptInterfaceBy(ctl, cmd, "bridge",
- &br_name, VSH_BYNAME))) {
- goto cleanup;
- }
-
- nostart = vshCommandOptBool(cmd, "no-start");
-
- /* Get the bridge xml into an xmlDoc */
- if (!(br_xml = virInterfaceGetXMLDesc(br_handle, VIR_INTERFACE_XML_INACTIVE)))
- goto cleanup;
- if (!(xml_doc = virXMLParseStringCtxt(br_xml,
- _("(bridge interface definition)"),
- &ctxt))) {
- vshError(ctl, _("Failed to parse configuration of %s"), br_name);
- goto cleanup;
- }
- top_node = ctxt->node;
-
- /* Verify that the device really is a bridge. */
- if (!(if_type = virXMLPropString(top_node, "type"))) {
- vshError(ctl, _("Existing device %s has no type"), br_name);
- goto cleanup;
- }
-
- if (STRNEQ(if_type, "bridge")) {
- vshError(ctl, _("Device %s is not a bridge"), br_name);
- goto cleanup;
- }
- VIR_FREE(if_type);
-
- /* verify the name in the XML matches the device name */
- if (!(if_name = virXMLPropString(top_node, "name")) ||
- STRNEQ(if_name, br_name)) {
- vshError(ctl, _("Interface name from config %s doesn't match given supplied name %s"),
- if_name, br_name);
- goto cleanup;
- }
- VIR_FREE(if_name);
-
- /* Find the node under . */
- if (!(br_node = virXPathNode("./bridge", ctxt))) {
- vshError(ctl, "%s", _("No bridge node in xml document"));
- goto cleanup;
- }
-
- if ((if_node = virXPathNode("./bridge/interface[2]", ctxt))) {
- vshError(ctl, "%s", _("Multiple interfaces attached to bridge"));
- goto cleanup;
- }
-
- if (!(if_node = virXPathNode("./bridge/interface", ctxt))) {
- vshError(ctl, "%s", _("No interface attached to bridge"));
- goto cleanup;
- }
-
- /* Change the type and name of the outer/master interface to
- * the type/name of the attached slave interface.
- */
- if (!(if_name = virXMLPropString(if_node, "name"))) {
- vshError(ctl, _("Device attached to bridge %s has no name"), br_name);
- goto cleanup;
- }
-
- if (!(if_type = virXMLPropString(if_node, "type"))) {
- vshError(ctl, _("Attached device %s has no type"), if_name);
- goto cleanup;
- }
-
- if (!xmlSetProp(top_node, BAD_CAST "type", BAD_CAST if_type)) {
- vshError(ctl, _("Failed to set interface type to '%s' in xml document"),
- if_type);
- goto cleanup;
- }
-
- if (!xmlSetProp(top_node, BAD_CAST "name", BAD_CAST if_name)) {
- vshError(ctl, _("Failed to set interface name to '%s' in xml document"),
- if_name);
- goto cleanup;
- }
-
- /* Cycle through all the nodes under the attached ,
- * moving all , and nodes up into the toplevel
- * .
- */
- cur = if_node->children;
- while (cur) {
- xmlNodePtr old = cur;
-
- cur = cur->next;
- if ((old->type == XML_ELEMENT_NODE) &&
- (xmlStrEqual(old->name, BAD_CAST "mac") || /* ethernet stuff to move down */
- xmlStrEqual(old->name, BAD_CAST "bond") || /* bond stuff to move down */
- xmlStrEqual(old->name, BAD_CAST "vlan"))) { /* vlan stuff to move down */
- xmlUnlinkNode(old);
- if (!xmlAddChild(top_node, old)) {
- vshError(ctl, _("Failed to move '%s' element in xml document"), old->name);
- xmlFreeNode(old);
- goto cleanup;
- }
- }
- }
-
- /* The document should now be fully converted; write it out to a string. */
- xmlDocDumpMemory(xml_doc, &if_xml, &if_xml_size);
-
- if (!if_xml || if_xml_size <= 0) {
- vshError(ctl, _("Failed to format new xml document for un-enslaved interface %s"),
- if_name);
- goto cleanup;
- }
-
- /* Destroy and Undefine the bridge device, since we otherwise
- * can't safely define the unattached device.
- */
- if (virInterfaceDestroy(br_handle, 0) < 0) {
- vshError(ctl, _("Failed to destroy bridge interface %s"), br_name);
- goto cleanup;
- }
- if (virInterfaceUndefine(br_handle) < 0) {
- vshError(ctl, _("Failed to undefine bridge interface %s"), br_name);
- goto cleanup;
- }
-
- /* if_xml is the new interface to define.
- */
- if (!(if_handle = virInterfaceDefineXML(ctl->conn, (char *) if_xml, 0))) {
- vshError(ctl, _("Failed to define new interface %s"), if_name);
- goto cleanup;
- }
-
- vshPrint(ctl, _("Device %s un-attached from bridge %s\n"),
- if_name, br_name);
-
- /* unless requested otherwise, undefine the bridge device */
- if (!nostart) {
- if (virInterfaceCreate(if_handle, 0) < 0) {
- vshError(ctl, _("Failed to start interface %s"), if_name);
- goto cleanup;
- }
- vshPrint(ctl, _("Interface %s started\n"), if_name);
- }
-
- ret = true;
- cleanup:
- if (if_handle)
- virInterfaceFree(if_handle);
- if (br_handle)
- virInterfaceFree(br_handle);
- VIR_FREE(if_xml);
- VIR_FREE(br_xml);
- VIR_FREE(if_type);
- VIR_FREE(if_name);
- xmlXPathFreeContext(ctxt);
- xmlFreeDoc(xml_doc);
- return ret;
-}
-
-/*
- * "nwfilter-define" command
- */
-static const vshCmdInfo info_nwfilter_define[] = {
- {"help", N_("define or update a network filter from an XML file")},
- {"desc", N_("Define a new network filter or update an existing one.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_nwfilter_define[] = {
- {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML network filter description")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNWFilterDefine(vshControl *ctl, const vshCmd *cmd)
-{
- virNWFilterPtr nwfilter;
- const char *from = NULL;
- bool ret = true;
- char *buffer;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (vshCommandOptString(cmd, "file", &from) <= 0)
- return false;
-
- if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
- return false;
-
- nwfilter = virNWFilterDefineXML(ctl->conn, buffer);
- VIR_FREE(buffer);
-
- if (nwfilter != NULL) {
- vshPrint(ctl, _("Network filter %s defined from %s\n"),
- virNWFilterGetName(nwfilter), from);
- virNWFilterFree(nwfilter);
- } else {
- vshError(ctl, _("Failed to define network filter from %s"), from);
- ret = false;
- }
- return ret;
-}
-
-
-/*
- * "nwfilter-undefine" command
- */
-static const vshCmdInfo info_nwfilter_undefine[] = {
- {"help", N_("undefine a network filter")},
- {"desc", N_("Undefine a given network filter.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_nwfilter_undefine[] = {
- {"nwfilter", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network filter name or uuid")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNWFilterUndefine(vshControl *ctl, const vshCmd *cmd)
-{
- virNWFilterPtr nwfilter;
- bool ret = true;
- const char *name;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!(nwfilter = vshCommandOptNWFilter(ctl, cmd, &name)))
- return false;
-
- if (virNWFilterUndefine(nwfilter) == 0) {
- vshPrint(ctl, _("Network filter %s undefined\n"), name);
- } else {
- vshError(ctl, _("Failed to undefine network filter %s"), name);
- ret = false;
- }
-
- virNWFilterFree(nwfilter);
- return ret;
-}
-
-
-/*
- * "nwfilter-dumpxml" command
- */
-static const vshCmdInfo info_nwfilter_dumpxml[] = {
- {"help", N_("network filter information in XML")},
- {"desc", N_("Output the network filter information as an XML dump to stdout.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_nwfilter_dumpxml[] = {
- {"nwfilter", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network filter name or uuid")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNWFilterDumpXML(vshControl *ctl, const vshCmd *cmd)
-{
- virNWFilterPtr nwfilter;
- bool ret = true;
- char *dump;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!(nwfilter = vshCommandOptNWFilter(ctl, cmd, NULL)))
- return false;
-
- dump = virNWFilterGetXMLDesc(nwfilter, 0);
- if (dump != NULL) {
- vshPrint(ctl, "%s", dump);
- VIR_FREE(dump);
- } else {
- ret = false;
- }
-
- virNWFilterFree(nwfilter);
- return ret;
-}
-
-/*
- * "nwfilter-list" command
- */
-static const vshCmdInfo info_nwfilter_list[] = {
- {"help", N_("list network filters")},
- {"desc", N_("Returns list of network filters.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_nwfilter_list[] = {
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNWFilterList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
-{
- int numfilters, i;
- char **names;
- char uuid[VIR_UUID_STRING_BUFLEN];
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- numfilters = virConnectNumOfNWFilters(ctl->conn);
- if (numfilters < 0) {
- vshError(ctl, "%s", _("Failed to list network filters"));
- return false;
- }
-
- names = vshMalloc(ctl, sizeof(char *) * numfilters);
-
- if ((numfilters = virConnectListNWFilters(ctl->conn, names,
- numfilters)) < 0) {
- vshError(ctl, "%s", _("Failed to list network filters"));
- VIR_FREE(names);
- return false;
- }
-
- qsort(&names[0], numfilters, sizeof(char *), vshNameSorter);
-
- vshPrintExtra(ctl, "%-36s %-20s \n", _("UUID"), _("Name"));
- vshPrintExtra(ctl,
- "----------------------------------------------------------------\n");
-
- for (i = 0; i < numfilters; i++) {
- virNWFilterPtr nwfilter =
- virNWFilterLookupByName(ctl->conn, names[i]);
-
- /* this kind of work with networks is not atomic operation */
- if (!nwfilter) {
- VIR_FREE(names[i]);
- continue;
- }
-
- virNWFilterGetUUIDString(nwfilter, uuid);
- vshPrint(ctl, "%-36s %-20s\n",
- uuid,
- virNWFilterGetName(nwfilter));
- virNWFilterFree(nwfilter);
- VIR_FREE(names[i]);
- }
-
- VIR_FREE(names);
- return true;
-}
-
-
-/*
- * "nwfilter-edit" command
- */
-static const vshCmdInfo info_nwfilter_edit[] = {
- {"help", N_("edit XML configuration for a network filter")},
- {"desc", N_("Edit the XML configuration for a network filter.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_nwfilter_edit[] = {
- {"nwfilter", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network filter name or uuid")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNWFilterEdit(vshControl *ctl, const vshCmd *cmd)
-{
- bool ret = false;
- virNWFilterPtr nwfilter = NULL;
- virNWFilterPtr nwfilter_edited = NULL;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- goto cleanup;
-
- nwfilter = vshCommandOptNWFilter(ctl, cmd, NULL);
- if (nwfilter == NULL)
- goto cleanup;
-
-#define EDIT_GET_XML virNWFilterGetXMLDesc(nwfilter, 0)
-#define EDIT_NOT_CHANGED \
- vshPrint(ctl, _("Network filter %s XML " \
- "configuration not changed.\n"), \
- virNWFilterGetName(nwfilter)); \
- ret = true; goto edit_cleanup;
-#define EDIT_DEFINE \
- (nwfilter_edited = virNWFilterDefineXML(ctl->conn, doc_edited))
-#define EDIT_FREE \
- if (nwfilter_edited) \
- virNWFilterFree(nwfilter);
-#include "virsh-edit.c"
-
- vshPrint(ctl, _("Network filter %s XML configuration edited.\n"),
- virNWFilterGetName(nwfilter_edited));
-
- ret = true;
-
-cleanup:
- if (nwfilter)
- virNWFilterFree(nwfilter);
- if (nwfilter_edited)
- virNWFilterFree(nwfilter_edited);
-
- return ret;
-}
-
-
-/**************************************************************************/
-/*
- * "pool-autostart" command
- */
-static const vshCmdInfo info_pool_autostart[] = {
- {"help", N_("autostart a pool")},
- {"desc",
- N_("Configure a pool to be automatically started at boot.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_pool_autostart[] = {
- {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
- {"disable", VSH_OT_BOOL, 0, N_("disable autostarting")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdPoolAutostart(vshControl *ctl, const vshCmd *cmd)
-{
- virStoragePoolPtr pool;
- const char *name;
- int autostart;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
- return false;
-
- autostart = !vshCommandOptBool(cmd, "disable");
-
- if (virStoragePoolSetAutostart(pool, autostart) < 0) {
- if (autostart)
- vshError(ctl, _("failed to mark pool %s as autostarted"), name);
- else
- vshError(ctl, _("failed to unmark pool %s as autostarted"), name);
- virStoragePoolFree(pool);
- return false;
- }
-
- if (autostart)
- vshPrint(ctl, _("Pool %s marked as autostarted\n"), name);
- else
- vshPrint(ctl, _("Pool %s unmarked as autostarted\n"), name);
-
- virStoragePoolFree(pool);
- return true;
-}
-
-/*
- * "pool-create" command
- */
-static const vshCmdInfo info_pool_create[] = {
- {"help", N_("create a pool from an XML file")},
- {"desc", N_("Create a pool.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_pool_create[] = {
- {"file", VSH_OT_DATA, VSH_OFLAG_REQ,
- N_("file containing an XML pool description")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdPoolCreate(vshControl *ctl, const vshCmd *cmd)
-{
- virStoragePoolPtr pool;
- const char *from = NULL;
- bool ret = true;
- char *buffer;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (vshCommandOptString(cmd, "file", &from) <= 0)
- return false;
-
- if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
- return false;
-
- pool = virStoragePoolCreateXML(ctl->conn, buffer, 0);
- VIR_FREE(buffer);
-
- if (pool != NULL) {
- vshPrint(ctl, _("Pool %s created from %s\n"),
- virStoragePoolGetName(pool), from);
- virStoragePoolFree(pool);
- } else {
- vshError(ctl, _("Failed to create pool from %s"), from);
- ret = false;
- }
- return ret;
-}
-
-
-/*
- * "nodedev-create" command
- */
-static const vshCmdInfo info_node_device_create[] = {
- {"help", N_("create a device defined "
- "by an XML file on the node")},
- {"desc", N_("Create a device on the node. Note that this "
- "command creates devices on the physical host "
- "that can then be assigned to a virtual machine.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_node_device_create[] = {
- {"file", VSH_OT_DATA, VSH_OFLAG_REQ,
- N_("file containing an XML description of the device")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNodeDeviceCreate(vshControl *ctl, const vshCmd *cmd)
-{
- virNodeDevicePtr dev = NULL;
- const char *from = NULL;
- bool ret = true;
- char *buffer;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (vshCommandOptString(cmd, "file", &from) <= 0)
- return false;
-
- if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
- return false;
-
- dev = virNodeDeviceCreateXML(ctl->conn, buffer, 0);
- VIR_FREE(buffer);
-
- if (dev != NULL) {
- vshPrint(ctl, _("Node device %s created from %s\n"),
- virNodeDeviceGetName(dev), from);
- virNodeDeviceFree(dev);
- } else {
- vshError(ctl, _("Failed to create node device from %s"), from);
- ret = false;
- }
-
- return ret;
-}
-
-
-/*
- * "nodedev-destroy" command
- */
-static const vshCmdInfo info_node_device_destroy[] = {
- {"help", N_("destroy (stop) a device on the node")},
- {"desc", N_("Destroy a device on the node. Note that this "
- "command destroys devices on the physical host")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_node_device_destroy[] = {
- {"name", VSH_OT_DATA, VSH_OFLAG_REQ,
- N_("name of the device to be destroyed")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdNodeDeviceDestroy(vshControl *ctl, const vshCmd *cmd)
-{
- virNodeDevicePtr dev = NULL;
- bool ret = true;
- const char *name = NULL;
-
- if (!vshConnectionUsability(ctl, ctl->conn)) {
- return false;
- }
-
- if (vshCommandOptString(cmd, "name", &name) <= 0)
- return false;
-
- dev = virNodeDeviceLookupByName(ctl->conn, name);
-
- if (virNodeDeviceDestroy(dev) == 0) {
- vshPrint(ctl, _("Destroyed node device '%s'\n"), name);
- } else {
- vshError(ctl, _("Failed to destroy node device '%s'"), name);
- ret = false;
- }
-
- virNodeDeviceFree(dev);
- return ret;
-}
-
-
-/*
- * XML Building helper for pool-define-as and pool-create-as
- */
-static const vshCmdOptDef opts_pool_X_as[] = {
- {"name", VSH_OT_DATA, VSH_OFLAG_REQ, N_("name of the pool")},
- {"print-xml", VSH_OT_BOOL, 0, N_("print XML document, but don't define/create")},
- {"type", VSH_OT_DATA, VSH_OFLAG_REQ, N_("type of the pool")},
- {"source-host", VSH_OT_DATA, 0, N_("source-host for underlying storage")},
- {"source-path", VSH_OT_DATA, 0, N_("source path for underlying storage")},
- {"source-dev", VSH_OT_DATA, 0, N_("source device for underlying storage")},
- {"source-name", VSH_OT_DATA, 0, N_("source name for underlying storage")},
- {"target", VSH_OT_DATA, 0, N_("target for underlying storage")},
- {"source-format", VSH_OT_STRING, 0, N_("format for underlying storage")},
- {NULL, 0, 0, NULL}
-};
-
-static int buildPoolXML(const vshCmd *cmd, const char **retname, char **xml) {
-
- const char *name = NULL, *type = NULL, *srcHost = NULL, *srcPath = NULL,
- *srcDev = NULL, *srcName = NULL, *srcFormat = NULL, *target = NULL;
- virBuffer buf = VIR_BUFFER_INITIALIZER;
-
- if (vshCommandOptString(cmd, "name", &name) <= 0)
- goto cleanup;
- if (vshCommandOptString(cmd, "type", &type) <= 0)
- goto cleanup;
-
- if (vshCommandOptString(cmd, "source-host", &srcHost) < 0 ||
- vshCommandOptString(cmd, "source-path", &srcPath) < 0 ||
- vshCommandOptString(cmd, "source-dev", &srcDev) < 0 ||
- vshCommandOptString(cmd, "source-name", &srcName) < 0 ||
- vshCommandOptString(cmd, "source-format", &srcFormat) < 0 ||
- vshCommandOptString(cmd, "target", &target) < 0) {
- vshError(NULL, "%s", _("missing argument"));
- goto cleanup;
- }
-
- virBufferAsprintf(&buf, "\n", type);
- virBufferAsprintf(&buf, " %s\n", name);
- if (srcHost || srcPath || srcDev || srcFormat || srcName) {
- virBufferAddLit(&buf, " \n");
- }
- if (target) {
- virBufferAddLit(&buf, " \n");
- virBufferAsprintf(&buf, " %s\n", target);
- virBufferAddLit(&buf, " \n");
- }
- virBufferAddLit(&buf, "\n");
-
- if (virBufferError(&buf)) {
- vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
- return false;
- }
-
- *xml = virBufferContentAndReset(&buf);
- *retname = name;
- return true;
-
-cleanup:
- virBufferFreeAndReset(&buf);
- return false;
-}
-
-/*
- * "pool-create-as" command
- */
-static const vshCmdInfo info_pool_create_as[] = {
- {"help", N_("create a pool from a set of args")},
- {"desc", N_("Create a pool.")},
- {NULL, NULL}
-};
-
-static bool
-cmdPoolCreateAs(vshControl *ctl, const vshCmd *cmd)
-{
- virStoragePoolPtr pool;
- const char *name;
- char *xml;
- bool printXML = vshCommandOptBool(cmd, "print-xml");
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!buildPoolXML(cmd, &name, &xml))
- return false;
-
- if (printXML) {
- vshPrint(ctl, "%s", xml);
- VIR_FREE(xml);
- } else {
- pool = virStoragePoolCreateXML(ctl->conn, xml, 0);
- VIR_FREE(xml);
-
- if (pool != NULL) {
- vshPrint(ctl, _("Pool %s created\n"), name);
- virStoragePoolFree(pool);
- } else {
- vshError(ctl, _("Failed to create pool %s"), name);
- return false;
- }
- }
- return true;
-}
-
-
-/*
- * "pool-define" command
- */
-static const vshCmdInfo info_pool_define[] = {
- {"help", N_("define (but don't start) a pool from an XML file")},
- {"desc", N_("Define a pool.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_pool_define[] = {
- {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML pool description")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdPoolDefine(vshControl *ctl, const vshCmd *cmd)
-{
- virStoragePoolPtr pool;
- const char *from = NULL;
- bool ret = true;
- char *buffer;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (vshCommandOptString(cmd, "file", &from) <= 0)
- return false;
-
- if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
- return false;
-
- pool = virStoragePoolDefineXML(ctl->conn, buffer, 0);
- VIR_FREE(buffer);
-
- if (pool != NULL) {
- vshPrint(ctl, _("Pool %s defined from %s\n"),
- virStoragePoolGetName(pool), from);
- virStoragePoolFree(pool);
- } else {
- vshError(ctl, _("Failed to define pool from %s"), from);
- ret = false;
- }
- return ret;
-}
-
-
-/*
- * "pool-define-as" command
- */
-static const vshCmdInfo info_pool_define_as[] = {
- {"help", N_("define a pool from a set of args")},
- {"desc", N_("Define a pool.")},
- {NULL, NULL}
-};
-
-static bool
-cmdPoolDefineAs(vshControl *ctl, const vshCmd *cmd)
-{
- virStoragePoolPtr pool;
- const char *name;
- char *xml;
- bool printXML = vshCommandOptBool(cmd, "print-xml");
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!buildPoolXML(cmd, &name, &xml))
- return false;
-
- if (printXML) {
- vshPrint(ctl, "%s", xml);
- VIR_FREE(xml);
- } else {
- pool = virStoragePoolDefineXML(ctl->conn, xml, 0);
- VIR_FREE(xml);
-
- if (pool != NULL) {
- vshPrint(ctl, _("Pool %s defined\n"), name);
- virStoragePoolFree(pool);
- } else {
- vshError(ctl, _("Failed to define pool %s"), name);
- return false;
- }
- }
- return true;
-}
-
-
-/*
- * "pool-build" command
- */
-static const vshCmdInfo info_pool_build[] = {
- {"help", N_("build a pool")},
- {"desc", N_("Build a given pool.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_pool_build[] = {
- {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
- {"no-overwrite", VSH_OT_BOOL, 0, N_("do not overwrite an existing pool of this type")},
- {"overwrite", VSH_OT_BOOL, 0, N_("overwrite any existing data")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdPoolBuild(vshControl *ctl, const vshCmd *cmd)
-{
- virStoragePoolPtr pool;
- bool ret = true;
- const char *name;
- unsigned int flags = 0;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
- return false;
-
- if (vshCommandOptBool(cmd, "no-overwrite")) {
- flags |= VIR_STORAGE_POOL_BUILD_NO_OVERWRITE;
- }
-
- if (vshCommandOptBool(cmd, "overwrite")) {
- flags |= VIR_STORAGE_POOL_BUILD_OVERWRITE;
- }
-
- if (virStoragePoolBuild(pool, flags) == 0) {
- vshPrint(ctl, _("Pool %s built\n"), name);
- } else {
- vshError(ctl, _("Failed to build pool %s"), name);
- ret = false;
- }
-
- virStoragePoolFree(pool);
-
- return ret;
-}
-
-/*
- * "pool-destroy" command
- */
-static const vshCmdInfo info_pool_destroy[] = {
- {"help", N_("destroy (stop) a pool")},
- {"desc",
- N_("Forcefully stop a given pool. Raw data in the pool is untouched")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_pool_destroy[] = {
- {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdPoolDestroy(vshControl *ctl, const vshCmd *cmd)
-{
- virStoragePoolPtr pool;
- bool ret = true;
- const char *name;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
- return false;
-
- if (virStoragePoolDestroy(pool) == 0) {
- vshPrint(ctl, _("Pool %s destroyed\n"), name);
- } else {
- vshError(ctl, _("Failed to destroy pool %s"), name);
- ret = false;
- }
-
- virStoragePoolFree(pool);
- return ret;
-}
-
-
-/*
- * "pool-delete" command
- */
-static const vshCmdInfo info_pool_delete[] = {
- {"help", N_("delete a pool")},
- {"desc", N_("Delete a given pool.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_pool_delete[] = {
- {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdPoolDelete(vshControl *ctl, const vshCmd *cmd)
-{
- virStoragePoolPtr pool;
- bool ret = true;
- const char *name;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
- return false;
-
- if (virStoragePoolDelete(pool, 0) == 0) {
- vshPrint(ctl, _("Pool %s deleted\n"), name);
- } else {
- vshError(ctl, _("Failed to delete pool %s"), name);
- ret = false;
- }
-
- virStoragePoolFree(pool);
- return ret;
-}
-
-
-/*
- * "pool-refresh" command
- */
-static const vshCmdInfo info_pool_refresh[] = {
- {"help", N_("refresh a pool")},
- {"desc", N_("Refresh a given pool.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_pool_refresh[] = {
- {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdPoolRefresh(vshControl *ctl, const vshCmd *cmd)
-{
- virStoragePoolPtr pool;
- bool ret = true;
- const char *name;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
- return false;
-
- if (virStoragePoolRefresh(pool, 0) == 0) {
- vshPrint(ctl, _("Pool %s refreshed\n"), name);
- } else {
- vshError(ctl, _("Failed to refresh pool %s"), name);
- ret = false;
- }
- virStoragePoolFree(pool);
-
- return ret;
-}
-
-
-/*
- * "pool-dumpxml" command
- */
-static const vshCmdInfo info_pool_dumpxml[] = {
- {"help", N_("pool information in XML")},
- {"desc", N_("Output the pool information as an XML dump to stdout.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_pool_dumpxml[] = {
- {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
- {"inactive", VSH_OT_BOOL, 0, N_("show inactive defined XML")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdPoolDumpXML(vshControl *ctl, const vshCmd *cmd)
-{
- virStoragePoolPtr pool;
- bool ret = true;
- bool inactive = vshCommandOptBool(cmd, "inactive");
- unsigned int flags = 0;
- char *dump;
-
- if (inactive)
- flags |= VIR_STORAGE_XML_INACTIVE;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!(pool = vshCommandOptPool(ctl, cmd, "pool", NULL)))
- return false;
-
- dump = virStoragePoolGetXMLDesc(pool, flags);
- if (dump != NULL) {
- vshPrint(ctl, "%s", dump);
- VIR_FREE(dump);
- } else {
- ret = false;
- }
-
- virStoragePoolFree(pool);
- return ret;
-}
-
-
-/*
- * "pool-list" command
- */
-static const vshCmdInfo info_pool_list[] = {
- {"help", N_("list pools")},
- {"desc", N_("Returns list of pools.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_pool_list[] = {
- {"inactive", VSH_OT_BOOL, 0, N_("list inactive pools")},
- {"all", VSH_OT_BOOL, 0, N_("list inactive & active pools")},
- {"details", VSH_OT_BOOL, 0, N_("display extended details for pools")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdPoolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
-{
- virStoragePoolInfo info;
- char **poolNames = NULL;
- int i, ret;
- bool functionReturn;
- int numActivePools = 0, numInactivePools = 0, numAllPools = 0;
- size_t stringLength = 0, nameStrLength = 0;
- size_t autostartStrLength = 0, persistStrLength = 0;
- size_t stateStrLength = 0, capStrLength = 0;
- size_t allocStrLength = 0, availStrLength = 0;
- struct poolInfoText {
- char *state;
- char *autostart;
- char *persistent;
- char *capacity;
- char *allocation;
- char *available;
- };
- struct poolInfoText *poolInfoTexts = NULL;
-
- /* Determine the options passed by the user */
- bool all = vshCommandOptBool(cmd, "all");
- bool details = vshCommandOptBool(cmd, "details");
- bool inactive = vshCommandOptBool(cmd, "inactive");
- bool active = !inactive || all;
- inactive |= all;
-
- /* Check the connection to libvirtd daemon is still working */
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- /* Retrieve the number of active storage pools */
- if (active) {
- numActivePools = virConnectNumOfStoragePools(ctl->conn);
- if (numActivePools < 0) {
- vshError(ctl, "%s", _("Failed to list active pools"));
- return false;
- }
- }
-
- /* Retrieve the number of inactive storage pools */
- if (inactive) {
- numInactivePools = virConnectNumOfDefinedStoragePools(ctl->conn);
- if (numInactivePools < 0) {
- vshError(ctl, "%s", _("Failed to list inactive pools"));
- return false;
- }
- }
-
- /* Determine the total number of pools to list */
- numAllPools = numActivePools + numInactivePools;
-
- /* Allocate memory for arrays of storage pool names and info */
- poolNames = vshCalloc(ctl, numAllPools, sizeof(*poolNames));
- poolInfoTexts =
- vshCalloc(ctl, numAllPools, sizeof(*poolInfoTexts));
-
- /* Retrieve a list of active storage pool names */
- if (active) {
- if (virConnectListStoragePools(ctl->conn,
- poolNames, numActivePools) < 0) {
- vshError(ctl, "%s", _("Failed to list active pools"));
- VIR_FREE(poolInfoTexts);
- VIR_FREE(poolNames);
- return false;
- }
- }
-
- /* Add the inactive storage pools to the end of the name list */
- if (inactive) {
- if (virConnectListDefinedStoragePools(ctl->conn,
- &poolNames[numActivePools],
- numInactivePools) < 0) {
- vshError(ctl, "%s", _("Failed to list inactive pools"));
- VIR_FREE(poolInfoTexts);
- VIR_FREE(poolNames);
- return false;
- }
- }
-
- /* Sort the storage pool names */
- qsort(poolNames, numAllPools, sizeof(*poolNames), vshNameSorter);
-
- /* Collect the storage pool information for display */
- for (i = 0; i < numAllPools; i++) {
- int autostart = 0, persistent = 0;
-
- /* Retrieve a pool object, looking it up by name */
- virStoragePoolPtr pool = virStoragePoolLookupByName(ctl->conn,
- poolNames[i]);
- if (!pool) {
- VIR_FREE(poolNames[i]);
- continue;
- }
-
- /* Retrieve the autostart status of the pool */
- if (virStoragePoolGetAutostart(pool, &autostart) < 0)
- poolInfoTexts[i].autostart = vshStrdup(ctl, _("no autostart"));
- else
- poolInfoTexts[i].autostart = vshStrdup(ctl, autostart ?
- _("yes") : _("no"));
-
- /* Retrieve the persistence status of the pool */
- if (details) {
- persistent = virStoragePoolIsPersistent(pool);
- vshDebug(ctl, VSH_ERR_DEBUG, "Persistent flag value: %d\n",
- persistent);
- if (persistent < 0)
- poolInfoTexts[i].persistent = vshStrdup(ctl, _("unknown"));
- else
- poolInfoTexts[i].persistent = vshStrdup(ctl, persistent ?
- _("yes") : _("no"));
-
- /* Keep the length of persistent string if longest so far */
- stringLength = strlen(poolInfoTexts[i].persistent);
- if (stringLength > persistStrLength)
- persistStrLength = stringLength;
- }
-
- /* Collect further extended information about the pool */
- if (virStoragePoolGetInfo(pool, &info) != 0) {
- /* Something went wrong retrieving pool info, cope with it */
- vshError(ctl, "%s", _("Could not retrieve pool information"));
- poolInfoTexts[i].state = vshStrdup(ctl, _("unknown"));
- if (details) {
- poolInfoTexts[i].capacity = vshStrdup(ctl, _("unknown"));
- poolInfoTexts[i].allocation = vshStrdup(ctl, _("unknown"));
- poolInfoTexts[i].available = vshStrdup(ctl, _("unknown"));
- }
- } else {
- /* Decide which state string to display */
- if (details) {
- /* --details option was specified, we're using detailed state
- * strings */
- switch (info.state) {
- case VIR_STORAGE_POOL_INACTIVE:
- poolInfoTexts[i].state = vshStrdup(ctl, _("inactive"));
- break;
- case VIR_STORAGE_POOL_BUILDING:
- poolInfoTexts[i].state = vshStrdup(ctl, _("building"));
- break;
- case VIR_STORAGE_POOL_RUNNING:
- poolInfoTexts[i].state = vshStrdup(ctl, _("running"));
- break;
- case VIR_STORAGE_POOL_DEGRADED:
- poolInfoTexts[i].state = vshStrdup(ctl, _("degraded"));
- break;
- case VIR_STORAGE_POOL_INACCESSIBLE:
- poolInfoTexts[i].state = vshStrdup(ctl, _("inaccessible"));
- break;
- }
-
- /* Create the pool size related strings */
- if (info.state == VIR_STORAGE_POOL_RUNNING ||
- info.state == VIR_STORAGE_POOL_DEGRADED) {
- double val;
- const char *unit;
-
- /* Create the capacity output string */
- val = prettyCapacity(info.capacity, &unit);
- ret = virAsprintf(&poolInfoTexts[i].capacity,
- "%.2lf %s", val, unit);
- if (ret < 0) {
- /* An error occurred creating the string, return */
- goto asprintf_failure;
- }
-
- /* Create the allocation output string */
- val = prettyCapacity(info.allocation, &unit);
- ret = virAsprintf(&poolInfoTexts[i].allocation,
- "%.2lf %s", val, unit);
- if (ret < 0) {
- /* An error occurred creating the string, return */
- goto asprintf_failure;
- }
-
- /* Create the available space output string */
- val = prettyCapacity(info.available, &unit);
- ret = virAsprintf(&poolInfoTexts[i].available,
- "%.2lf %s", val, unit);
- if (ret < 0) {
- /* An error occurred creating the string, return */
- goto asprintf_failure;
- }
- } else {
- /* Capacity related information isn't available */
- poolInfoTexts[i].capacity = vshStrdup(ctl, _("-"));
- poolInfoTexts[i].allocation = vshStrdup(ctl, _("-"));
- poolInfoTexts[i].available = vshStrdup(ctl, _("-"));
- }
-
- /* Keep the length of capacity string if longest so far */
- stringLength = strlen(poolInfoTexts[i].capacity);
- if (stringLength > capStrLength)
- capStrLength = stringLength;
-
- /* Keep the length of allocation string if longest so far */
- stringLength = strlen(poolInfoTexts[i].allocation);
- if (stringLength > allocStrLength)
- allocStrLength = stringLength;
-
- /* Keep the length of available string if longest so far */
- stringLength = strlen(poolInfoTexts[i].available);
- if (stringLength > availStrLength)
- availStrLength = stringLength;
- } else {
- /* --details option was not specified, only active/inactive
- * state strings are used */
- if (info.state == VIR_STORAGE_POOL_INACTIVE)
- poolInfoTexts[i].state = vshStrdup(ctl, _("inactive"));
- else
- poolInfoTexts[i].state = vshStrdup(ctl, _("active"));
- }
- }
-
- /* Keep the length of name string if longest so far */
- stringLength = strlen(poolNames[i]);
- if (stringLength > nameStrLength)
- nameStrLength = stringLength;
-
- /* Keep the length of state string if longest so far */
- stringLength = strlen(poolInfoTexts[i].state);
- if (stringLength > stateStrLength)
- stateStrLength = stringLength;
-
- /* Keep the length of autostart string if longest so far */
- stringLength = strlen(poolInfoTexts[i].autostart);
- if (stringLength > autostartStrLength)
- autostartStrLength = stringLength;
-
- /* Free the pool object */
- virStoragePoolFree(pool);
- }
-
- /* If the --details option wasn't selected, we output the pool
- * info using the fixed string format from previous versions to
- * maintain backward compatibility.
- */
-
- /* Output basic info then return if --details option not selected */
- if (!details) {
- /* Output old style header */
- vshPrintExtra(ctl, "%-20s %-10s %-10s\n", _("Name"), _("State"),
- _("Autostart"));
- vshPrintExtra(ctl, "-----------------------------------------\n");
-
- /* Output old style pool info */
- for (i = 0; i < numAllPools; i++) {
- vshPrint(ctl, "%-20s %-10s %-10s\n",
- poolNames[i],
- poolInfoTexts[i].state,
- poolInfoTexts[i].autostart);
- }
-
- /* Cleanup and return */
- functionReturn = true;
- goto cleanup;
- }
-
- /* We only get here if the --details option was selected. */
-
- /* Use the length of name header string if it's longest */
- stringLength = strlen(_("Name"));
- if (stringLength > nameStrLength)
- nameStrLength = stringLength;
-
- /* Use the length of state header string if it's longest */
- stringLength = strlen(_("State"));
- if (stringLength > stateStrLength)
- stateStrLength = stringLength;
-
- /* Use the length of autostart header string if it's longest */
- stringLength = strlen(_("Autostart"));
- if (stringLength > autostartStrLength)
- autostartStrLength = stringLength;
-
- /* Use the length of persistent header string if it's longest */
- stringLength = strlen(_("Persistent"));
- if (stringLength > persistStrLength)
- persistStrLength = stringLength;
-
- /* Use the length of capacity header string if it's longest */
- stringLength = strlen(_("Capacity"));
- if (stringLength > capStrLength)
- capStrLength = stringLength;
-
- /* Use the length of allocation header string if it's longest */
- stringLength = strlen(_("Allocation"));
- if (stringLength > allocStrLength)
- allocStrLength = stringLength;
-
- /* Use the length of available header string if it's longest */
- stringLength = strlen(_("Available"));
- if (stringLength > availStrLength)
- availStrLength = stringLength;
-
- /* Display the string lengths for debugging. */
- vshDebug(ctl, VSH_ERR_DEBUG, "Longest name string = %lu chars\n",
- (unsigned long) nameStrLength);
- vshDebug(ctl, VSH_ERR_DEBUG, "Longest state string = %lu chars\n",
- (unsigned long) stateStrLength);
- vshDebug(ctl, VSH_ERR_DEBUG, "Longest autostart string = %lu chars\n",
- (unsigned long) autostartStrLength);
- vshDebug(ctl, VSH_ERR_DEBUG, "Longest persistent string = %lu chars\n",
- (unsigned long) persistStrLength);
- vshDebug(ctl, VSH_ERR_DEBUG, "Longest capacity string = %lu chars\n",
- (unsigned long) capStrLength);
- vshDebug(ctl, VSH_ERR_DEBUG, "Longest allocation string = %lu chars\n",
- (unsigned long) allocStrLength);
- vshDebug(ctl, VSH_ERR_DEBUG, "Longest available string = %lu chars\n",
- (unsigned long) availStrLength);
-
- /* Create the output template. Each column is sized according to
- * the longest string.
- */
- char *outputStr;
- ret = virAsprintf(&outputStr,
- "%%-%lus %%-%lus %%-%lus %%-%lus %%%lus %%%lus %%%lus\n",
- (unsigned long) nameStrLength,
- (unsigned long) stateStrLength,
- (unsigned long) autostartStrLength,
- (unsigned long) persistStrLength,
- (unsigned long) capStrLength,
- (unsigned long) allocStrLength,
- (unsigned long) availStrLength);
- if (ret < 0) {
- /* An error occurred creating the string, return */
- goto asprintf_failure;
- }
-
- /* Display the header */
- vshPrint(ctl, outputStr, _("Name"), _("State"), _("Autostart"),
- _("Persistent"), _("Capacity"), _("Allocation"), _("Available"));
- for (i = nameStrLength + stateStrLength + autostartStrLength
- + persistStrLength + capStrLength
- + allocStrLength + availStrLength
- + 12; i > 0; i--)
- vshPrintExtra(ctl, "-");
- vshPrintExtra(ctl, "\n");
-
- /* Display the pool info rows */
- for (i = 0; i < numAllPools; i++) {
- vshPrint(ctl, outputStr,
- poolNames[i],
- poolInfoTexts[i].state,
- poolInfoTexts[i].autostart,
- poolInfoTexts[i].persistent,
- poolInfoTexts[i].capacity,
- poolInfoTexts[i].allocation,
- poolInfoTexts[i].available);
- }
-
- /* Cleanup and return */
- functionReturn = true;
- goto cleanup;
-
-asprintf_failure:
-
- /* Display an appropriate error message then cleanup and return */
- switch (errno) {
- case ENOMEM:
- /* Couldn't allocate memory */
- vshError(ctl, "%s", _("Out of memory"));
- break;
- default:
- /* Some other error */
- vshError(ctl, _("virAsprintf failed (errno %d)"), errno);
- }
- functionReturn = false;
-
-cleanup:
-
- /* Safely free the memory allocated in this function */
- for (i = 0; i < numAllPools; i++) {
- /* Cleanup the memory for one pool info structure */
- VIR_FREE(poolInfoTexts[i].state);
- VIR_FREE(poolInfoTexts[i].autostart);
- VIR_FREE(poolInfoTexts[i].persistent);
- VIR_FREE(poolInfoTexts[i].capacity);
- VIR_FREE(poolInfoTexts[i].allocation);
- VIR_FREE(poolInfoTexts[i].available);
- VIR_FREE(poolNames[i]);
- }
-
- /* Cleanup the memory for the initial arrays*/
- VIR_FREE(poolInfoTexts);
- VIR_FREE(poolNames);
-
- /* Return the desired value */
- return functionReturn;
-}
-
-/*
- * "find-storage-pool-sources-as" command
- */
-static const vshCmdInfo info_find_storage_pool_sources_as[] = {
- {"help", N_("find potential storage pool sources")},
- {"desc", N_("Returns XML document.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_find_storage_pool_sources_as[] = {
- {"type", VSH_OT_DATA, VSH_OFLAG_REQ,
- N_("type of storage pool sources to find")},
- {"host", VSH_OT_DATA, VSH_OFLAG_NONE, N_("optional host to query")},
- {"port", VSH_OT_DATA, VSH_OFLAG_NONE, N_("optional port to query")},
- {"initiator", VSH_OT_DATA, VSH_OFLAG_NONE, N_("optional initiator IQN to use for query")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdPoolDiscoverSourcesAs(vshControl * ctl, const vshCmd * cmd ATTRIBUTE_UNUSED)
-{
- const char *type = NULL, *host = NULL;
- char *srcSpec = NULL;
- char *srcList;
- const char *initiator = NULL;
-
- if (vshCommandOptString(cmd, "type", &type) <= 0 ||
- vshCommandOptString(cmd, "host", &host) < 0 ||
- vshCommandOptString(cmd, "initiator", &initiator) < 0) {
- vshError(ctl,"%s", _("missing argument"));
- return false;
- }
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (host) {
- const char *port = NULL;
- virBuffer buf = VIR_BUFFER_INITIALIZER;
-
- if (vshCommandOptString(cmd, "port", &port) < 0) {
- vshError(ctl, "%s", _("missing argument"));
- virBufferFreeAndReset(&buf);
- return false;
- }
- virBufferAddLit(&buf, "\n");
- if (virBufferError(&buf)) {
- vshError(ctl, "%s", _("Out of memory"));
- return false;
- }
- srcSpec = virBufferContentAndReset(&buf);
- }
-
- srcList = virConnectFindStoragePoolSources(ctl->conn, type, srcSpec, 0);
- VIR_FREE(srcSpec);
- if (srcList == NULL) {
- vshError(ctl, _("Failed to find any %s pool sources"), type);
- return false;
- }
- vshPrint(ctl, "%s", srcList);
- VIR_FREE(srcList);
-
- return true;
-}
-
-
-/*
- * "find-storage-pool-sources" command
- */
-static const vshCmdInfo info_find_storage_pool_sources[] = {
- {"help", N_("discover potential storage pool sources")},
- {"desc", N_("Returns XML document.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_find_storage_pool_sources[] = {
- {"type", VSH_OT_DATA, VSH_OFLAG_REQ,
- N_("type of storage pool sources to discover")},
- {"srcSpec", VSH_OT_DATA, VSH_OFLAG_NONE,
- N_("optional file of source xml to query for pools")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdPoolDiscoverSources(vshControl * ctl, const vshCmd * cmd ATTRIBUTE_UNUSED)
-{
- const char *type = NULL, *srcSpecFile = NULL;
- char *srcSpec = NULL, *srcList;
-
- if (vshCommandOptString(cmd, "type", &type) <= 0)
- return false;
-
- if (vshCommandOptString(cmd, "srcSpec", &srcSpecFile) < 0) {
- vshError(ctl, "%s", _("missing option"));
- return false;
- }
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (srcSpecFile && virFileReadAll(srcSpecFile, VIRSH_MAX_XML_FILE, &srcSpec) < 0)
- return false;
-
- srcList = virConnectFindStoragePoolSources(ctl->conn, type, srcSpec, 0);
- VIR_FREE(srcSpec);
- if (srcList == NULL) {
- vshError(ctl, _("Failed to find any %s pool sources"), type);
- return false;
- }
- vshPrint(ctl, "%s", srcList);
- VIR_FREE(srcList);
-
- return true;
-}
-
-
-/*
- * "pool-info" command
- */
-static const vshCmdInfo info_pool_info[] = {
- {"help", N_("storage pool information")},
- {"desc", N_("Returns basic information about the storage pool.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_pool_info[] = {
- {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdPoolInfo(vshControl *ctl, const vshCmd *cmd)
-{
- virStoragePoolInfo info;
- virStoragePoolPtr pool;
- int autostart = 0;
- int persistent = 0;
- bool ret = true;
- char uuid[VIR_UUID_STRING_BUFLEN];
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!(pool = vshCommandOptPool(ctl, cmd, "pool", NULL)))
- return false;
-
- vshPrint(ctl, "%-15s %s\n", _("Name:"), virStoragePoolGetName(pool));
-
- if (virStoragePoolGetUUIDString(pool, &uuid[0])==0)
- vshPrint(ctl, "%-15s %s\n", _("UUID:"), uuid);
-
- if (virStoragePoolGetInfo(pool, &info) == 0) {
- double val;
- const char *unit;
- switch (info.state) {
- case VIR_STORAGE_POOL_INACTIVE:
- vshPrint(ctl, "%-15s %s\n", _("State:"),
- _("inactive"));
- break;
- case VIR_STORAGE_POOL_BUILDING:
- vshPrint(ctl, "%-15s %s\n", _("State:"),
- _("building"));
- break;
- case VIR_STORAGE_POOL_RUNNING:
- vshPrint(ctl, "%-15s %s\n", _("State:"),
- _("running"));
- break;
- case VIR_STORAGE_POOL_DEGRADED:
- vshPrint(ctl, "%-15s %s\n", _("State:"),
- _("degraded"));
- break;
- case VIR_STORAGE_POOL_INACCESSIBLE:
- vshPrint(ctl, "%-15s %s\n", _("State:"),
- _("inaccessible"));
- break;
- }
-
- /* Check and display whether the pool is persistent or not */
- persistent = virStoragePoolIsPersistent(pool);
- vshDebug(ctl, VSH_ERR_DEBUG, "Pool persistent flag value: %d\n",
- persistent);
- if (persistent < 0)
- vshPrint(ctl, "%-15s %s\n", _("Persistent:"), _("unknown"));
- else
- vshPrint(ctl, "%-15s %s\n", _("Persistent:"), persistent ? _("yes") : _("no"));
-
- /* Check and display whether the pool is autostarted or not */
- virStoragePoolGetAutostart(pool, &autostart);
- vshDebug(ctl, VSH_ERR_DEBUG, "Pool autostart flag value: %d\n",
- autostart);
- if (autostart < 0)
- vshPrint(ctl, "%-15s %s\n", _("Autostart:"), _("no autostart"));
- else
- vshPrint(ctl, "%-15s %s\n", _("Autostart:"), autostart ? _("yes") : _("no"));
-
- if (info.state == VIR_STORAGE_POOL_RUNNING ||
- info.state == VIR_STORAGE_POOL_DEGRADED) {
- val = prettyCapacity(info.capacity, &unit);
- vshPrint(ctl, "%-15s %2.2lf %s\n", _("Capacity:"), val, unit);
-
- val = prettyCapacity(info.allocation, &unit);
- vshPrint(ctl, "%-15s %2.2lf %s\n", _("Allocation:"), val, unit);
-
- val = prettyCapacity(info.available, &unit);
- vshPrint(ctl, "%-15s %2.2lf %s\n", _("Available:"), val, unit);
- }
- } else {
- ret = false;
- }
-
- virStoragePoolFree(pool);
- return ret;
-}
-
-
-/*
- * "pool-name" command
- */
-static const vshCmdInfo info_pool_name[] = {
- {"help", N_("convert a pool UUID to pool name")},
- {"desc", ""},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_pool_name[] = {
- {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool uuid")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdPoolName(vshControl *ctl, const vshCmd *cmd)
-{
- virStoragePoolPtr pool;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
- if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL,
- VSH_BYUUID)))
- return false;
-
- vshPrint(ctl, "%s\n", virStoragePoolGetName(pool));
- virStoragePoolFree(pool);
- return true;
-}
-
-
-/*
- * "pool-start" command
- */
-static const vshCmdInfo info_pool_start[] = {
- {"help", N_("start a (previously defined) inactive pool")},
- {"desc", N_("Start a pool.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_pool_start[] = {
- {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("name or uuid of the inactive pool")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdPoolStart(vshControl *ctl, const vshCmd *cmd)
-{
- virStoragePoolPtr pool;
- bool ret = true;
- const char *name = NULL;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
- return false;
-
- if (virStoragePoolCreate(pool, 0) == 0) {
- vshPrint(ctl, _("Pool %s started\n"), name);
- } else {
- vshError(ctl, _("Failed to start pool %s"), name);
- ret = false;
- }
-
- virStoragePoolFree(pool);
- return ret;
-}
-
-
-/*
- * "vol-create-as" command
- */
-static const vshCmdInfo info_vol_create_as[] = {
- {"help", N_("create a volume from a set of args")},
- {"desc", N_("Create a vol.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_vol_create_as[] = {
- {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name")},
- {"name", VSH_OT_DATA, VSH_OFLAG_REQ, N_("name of the volume")},
- {"capacity", VSH_OT_DATA, VSH_OFLAG_REQ,
- N_("size of the vol, as scaled integer (default bytes)")},
- {"allocation", VSH_OT_STRING, 0,
- N_("initial allocation size, as scaled integer (default bytes)")},
- {"format", VSH_OT_STRING, 0,
- N_("file format type raw,bochs,qcow,qcow2,qed,vmdk")},
- {"backing-vol", VSH_OT_STRING, 0,
- N_("the backing volume if taking a snapshot")},
- {"backing-vol-format", VSH_OT_STRING, 0,
- N_("format of backing volume if taking a snapshot")},
- {NULL, 0, 0, NULL}
-};
-
-static int
-vshVolSize(const char *data, unsigned long long *val)
-{
- char *end;
- if (virStrToLong_ull(data, &end, 10, val) < 0)
- return -1;
- return virScaleInteger(val, end, 1, ULLONG_MAX);
-}
-
-static bool
-cmdVolCreateAs(vshControl *ctl, const vshCmd *cmd)
-{
- virStoragePoolPtr pool;
- virStorageVolPtr vol;
- char *xml;
- const char *name, *capacityStr = NULL, *allocationStr = NULL, *format = NULL;
- const char *snapshotStrVol = NULL, *snapshotStrFormat = NULL;
- unsigned long long capacity, allocation = 0;
- virBuffer buf = VIR_BUFFER_INITIALIZER;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL,
- VSH_BYNAME)))
- return false;
-
- if (vshCommandOptString(cmd, "name", &name) <= 0)
- goto cleanup;
-
- if (vshCommandOptString(cmd, "capacity", &capacityStr) <= 0)
- goto cleanup;
-
- if (vshVolSize(capacityStr, &capacity) < 0) {
- vshError(ctl, _("Malformed size %s"), capacityStr);
- goto cleanup;
- }
-
- if (vshCommandOptString(cmd, "allocation", &allocationStr) > 0 &&
- vshVolSize(allocationStr, &allocation) < 0) {
- vshError(ctl, _("Malformed size %s"), allocationStr);
- goto cleanup;
- }
-
- if (vshCommandOptString(cmd, "format", &format) < 0 ||
- vshCommandOptString(cmd, "backing-vol", &snapshotStrVol) < 0 ||
- vshCommandOptString(cmd, "backing-vol-format",
- &snapshotStrFormat) < 0) {
- vshError(ctl, "%s", _("missing argument"));
- goto cleanup;
- }
-
-
- virBufferAddLit(&buf, "\n");
- virBufferAsprintf(&buf, " %s\n", name);
- virBufferAsprintf(&buf, " %llu\n", capacity);
- if (allocationStr)
- virBufferAsprintf(&buf, " %llu\n", allocation);
-
- if (format) {
- virBufferAddLit(&buf, " \n");
- virBufferAsprintf(&buf, " \n",format);
- virBufferAddLit(&buf, " \n");
- }
-
- /* Convert the snapshot parameters into backingStore XML */
- if (snapshotStrVol) {
- /* Lookup snapshot backing volume. Try the backing-vol
- * parameter as a name */
- vshDebug(ctl, VSH_ERR_DEBUG,
- "%s: Look up backing store volume '%s' as name\n",
- cmd->def->name, snapshotStrVol);
- virStorageVolPtr snapVol = virStorageVolLookupByName(pool, snapshotStrVol);
- if (snapVol)
- vshDebug(ctl, VSH_ERR_DEBUG,
- "%s: Backing store volume found using '%s' as name\n",
- cmd->def->name, snapshotStrVol);
-
- if (snapVol == NULL) {
- /* Snapshot backing volume not found by name. Try the
- * backing-vol parameter as a key */
- vshDebug(ctl, VSH_ERR_DEBUG,
- "%s: Look up backing store volume '%s' as key\n",
- cmd->def->name, snapshotStrVol);
- snapVol = virStorageVolLookupByKey(ctl->conn, snapshotStrVol);
- if (snapVol)
- vshDebug(ctl, VSH_ERR_DEBUG,
- "%s: Backing store volume found using '%s' as key\n",
- cmd->def->name, snapshotStrVol);
- }
- if (snapVol == NULL) {
- /* Snapshot backing volume not found by key. Try the
- * backing-vol parameter as a path */
- vshDebug(ctl, VSH_ERR_DEBUG,
- "%s: Look up backing store volume '%s' as path\n",
- cmd->def->name, snapshotStrVol);
- snapVol = virStorageVolLookupByPath(ctl->conn, snapshotStrVol);
- if (snapVol)
- vshDebug(ctl, VSH_ERR_DEBUG,
- "%s: Backing store volume found using '%s' as path\n",
- cmd->def->name, snapshotStrVol);
- }
- if (snapVol == NULL) {
- vshError(ctl, _("failed to get vol '%s'"), snapshotStrVol);
- goto cleanup;
- }
-
- char *snapshotStrVolPath;
- if ((snapshotStrVolPath = virStorageVolGetPath(snapVol)) == NULL) {
- virStorageVolFree(snapVol);
- goto cleanup;
- }
-
- /* Create XML for the backing store */
- virBufferAddLit(&buf, " \n");
- virBufferAsprintf(&buf, " %s\n",snapshotStrVolPath);
- if (snapshotStrFormat)
- virBufferAsprintf(&buf, " \n",snapshotStrFormat);
- virBufferAddLit(&buf, " \n");
-
- /* Cleanup snapshot allocations */
- VIR_FREE(snapshotStrVolPath);
- virStorageVolFree(snapVol);
- }
-
- virBufferAddLit(&buf, "\n");
-
- if (virBufferError(&buf)) {
- vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
- goto cleanup;
- }
- xml = virBufferContentAndReset(&buf);
- vol = virStorageVolCreateXML(pool, xml, 0);
- VIR_FREE(xml);
- virStoragePoolFree(pool);
-
- if (vol != NULL) {
- vshPrint(ctl, _("Vol %s created\n"), name);
- virStorageVolFree(vol);
- return true;
- } else {
- vshError(ctl, _("Failed to create vol %s"), name);
- return false;
- }
-
- cleanup:
- virBufferFreeAndReset(&buf);
- virStoragePoolFree(pool);
- return false;
-}
-
-
-/*
- * "pool-undefine" command
- */
-static const vshCmdInfo info_pool_undefine[] = {
- {"help", N_("undefine an inactive pool")},
- {"desc", N_("Undefine the configuration for an inactive pool.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_pool_undefine[] = {
- {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdPoolUndefine(vshControl *ctl, const vshCmd *cmd)
-{
- virStoragePoolPtr pool;
- bool ret = true;
- const char *name;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
- return false;
-
- if (virStoragePoolUndefine(pool) == 0) {
- vshPrint(ctl, _("Pool %s has been undefined\n"), name);
- } else {
- vshError(ctl, _("Failed to undefine pool %s"), name);
- ret = false;
- }
-
- virStoragePoolFree(pool);
- return ret;
-}
-
-
-/*
- * "pool-uuid" command
- */
-static const vshCmdInfo info_pool_uuid[] = {
- {"help", N_("convert a pool name to pool UUID")},
- {"desc", ""},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_pool_uuid[] = {
- {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdPoolUuid(vshControl *ctl, const vshCmd *cmd)
-{
- virStoragePoolPtr pool;
- char uuid[VIR_UUID_STRING_BUFLEN];
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL,
- VSH_BYNAME)))
- return false;
-
- if (virStoragePoolGetUUIDString(pool, uuid) != -1)
- vshPrint(ctl, "%s\n", uuid);
- else
- vshError(ctl, "%s", _("failed to get pool UUID"));
-
- virStoragePoolFree(pool);
- return true;
-}
-
-
-/*
- * "vol-create" command
- */
-static const vshCmdInfo info_vol_create[] = {
- {"help", N_("create a vol from an XML file")},
- {"desc", N_("Create a vol.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_vol_create[] = {
- {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name")},
- {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML vol description")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdVolCreate(vshControl *ctl, const vshCmd *cmd)
-{
- virStoragePoolPtr pool;
- virStorageVolPtr vol;
- const char *from = NULL;
- bool ret = true;
- char *buffer;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL,
- VSH_BYNAME)))
- return false;
-
- if (vshCommandOptString(cmd, "file", &from) <= 0) {
- virStoragePoolFree(pool);
- return false;
- }
-
- if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
- virshReportError(ctl);
- virStoragePoolFree(pool);
- return false;
- }
-
- vol = virStorageVolCreateXML(pool, buffer, 0);
- VIR_FREE(buffer);
- virStoragePoolFree(pool);
-
- if (vol != NULL) {
- vshPrint(ctl, _("Vol %s created from %s\n"),
- virStorageVolGetName(vol), from);
- virStorageVolFree(vol);
- } else {
- vshError(ctl, _("Failed to create vol from %s"), from);
- ret = false;
- }
- return ret;
-}
-
-/*
- * "vol-create-from" command
- */
-static const vshCmdInfo info_vol_create_from[] = {
- {"help", N_("create a vol, using another volume as input")},
- {"desc", N_("Create a vol from an existing volume.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_vol_create_from[] = {
- {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
- {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML vol description")},
- {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("input vol name or key")},
- {"inputpool", VSH_OT_STRING, 0, N_("pool name or uuid of the input volume's pool")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdVolCreateFrom(vshControl *ctl, const vshCmd *cmd)
-{
- virStoragePoolPtr pool = NULL;
- virStorageVolPtr newvol = NULL, inputvol = NULL;
- const char *from = NULL;
- bool ret = false;
- char *buffer = NULL;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- goto cleanup;
-
- if (!(pool = vshCommandOptPool(ctl, cmd, "pool", NULL)))
- goto cleanup;
-
- if (vshCommandOptString(cmd, "file", &from) <= 0) {
- goto cleanup;
- }
-
- if (!(inputvol = vshCommandOptVol(ctl, cmd, "vol", "inputpool", NULL)))
- goto cleanup;
-
- if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
- virshReportError(ctl);
- goto cleanup;
- }
-
- newvol = virStorageVolCreateXMLFrom(pool, buffer, inputvol, 0);
-
- if (newvol != NULL) {
- vshPrint(ctl, _("Vol %s created from input vol %s\n"),
- virStorageVolGetName(newvol), virStorageVolGetName(inputvol));
- } else {
- vshError(ctl, _("Failed to create vol from %s"), from);
- goto cleanup;
- }
-
- ret = true;
-cleanup:
- VIR_FREE(buffer);
- if (pool)
- virStoragePoolFree(pool);
- if (inputvol)
- virStorageVolFree(inputvol);
- if (newvol)
- virStorageVolFree(newvol);
- return ret;
-}
-
-static xmlChar *
-makeCloneXML(const char *origxml, const char *newname)
-{
-
- xmlDocPtr doc = NULL;
- xmlXPathContextPtr ctxt = NULL;
- xmlXPathObjectPtr obj = NULL;
- xmlChar *newxml = NULL;
- int size;
-
- doc = virXMLParseStringCtxt(origxml, _("(volume_definition)"), &ctxt);
- if (!doc)
- goto cleanup;
-
- obj = xmlXPathEval(BAD_CAST "/volume/name", ctxt);
- if (obj == NULL || obj->nodesetval == NULL ||
- obj->nodesetval->nodeTab == NULL)
- goto cleanup;
-
- xmlNodeSetContent(obj->nodesetval->nodeTab[0], (const xmlChar *)newname);
- xmlDocDumpMemory(doc, &newxml, &size);
-
-cleanup:
- xmlXPathFreeObject(obj);
- xmlXPathFreeContext(ctxt);
- xmlFreeDoc(doc);
- return newxml;
-}
-
-/*
- * "vol-clone" command
- */
-static const vshCmdInfo info_vol_clone[] = {
- {"help", N_("clone a volume.")},
- {"desc", N_("Clone an existing volume.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_vol_clone[] = {
- {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("orig vol name or key")},
- {"newname", VSH_OT_DATA, VSH_OFLAG_REQ, N_("clone name")},
- {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdVolClone(vshControl *ctl, const vshCmd *cmd)
-{
- virStoragePoolPtr origpool = NULL;
- virStorageVolPtr origvol = NULL, newvol = NULL;
- const char *name = NULL;
- char *origxml = NULL;
- xmlChar *newxml = NULL;
- bool ret = false;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- goto cleanup;
-
- if (!(origvol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL)))
- goto cleanup;
-
- origpool = virStoragePoolLookupByVolume(origvol);
- if (!origpool) {
- vshError(ctl, "%s", _("failed to get parent pool"));
- goto cleanup;
- }
-
- if (vshCommandOptString(cmd, "newname", &name) <= 0)
- goto cleanup;
-
- origxml = virStorageVolGetXMLDesc(origvol, 0);
- if (!origxml)
- goto cleanup;
-
- newxml = makeCloneXML(origxml, name);
- if (!newxml) {
- vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
- goto cleanup;
- }
-
- newvol = virStorageVolCreateXMLFrom(origpool, (char *) newxml, origvol, 0);
-
- if (newvol != NULL) {
- vshPrint(ctl, _("Vol %s cloned from %s\n"),
- virStorageVolGetName(newvol), virStorageVolGetName(origvol));
- } else {
- vshError(ctl, _("Failed to clone vol from %s"),
- virStorageVolGetName(origvol));
- goto cleanup;
- }
-
- ret = true;
-
-cleanup:
- VIR_FREE(origxml);
- xmlFree(newxml);
- if (origvol)
- virStorageVolFree(origvol);
- if (newvol)
- virStorageVolFree(newvol);
- if (origpool)
- virStoragePoolFree(origpool);
- return ret;
-}
-
-
-/*
- * "vol-upload" command
- */
-static const vshCmdInfo info_vol_upload[] = {
- {"help", N_("upload a file into a volume")},
- {"desc", N_("Upload a file into a volume")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_vol_upload[] = {
- {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
- {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file")},
- {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
- {"offset", VSH_OT_INT, 0, N_("volume offset to upload to") },
- {"length", VSH_OT_INT, 0, N_("amount of data to upload") },
- {NULL, 0, 0, NULL}
-};
-
-static int
-cmdVolUploadSource(virStreamPtr st ATTRIBUTE_UNUSED,
- char *bytes, size_t nbytes, void *opaque)
-{
- int *fd = opaque;
-
- return saferead(*fd, bytes, nbytes);
-}
-
-static bool
-cmdVolUpload(vshControl *ctl, const vshCmd *cmd)
-{
- const char *file = NULL;
- virStorageVolPtr vol = NULL;
- bool ret = false;
- int fd = -1;
- virStreamPtr st = NULL;
- const char *name = NULL;
- unsigned long long offset = 0, length = 0;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- goto cleanup;
-
- if (vshCommandOptULongLong(cmd, "offset", &offset) < 0) {
- vshError(ctl, _("Unable to parse integer"));
- return false;
- }
-
- if (vshCommandOptULongLong(cmd, "length", &length) < 0) {
- vshError(ctl, _("Unable to parse integer"));
- return false;
- }
-
- if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", &name))) {
- return false;
- }
-
- if (vshCommandOptString(cmd, "file", &file) < 0) {
- vshError(ctl, _("file must not be empty"));
- goto cleanup;
- }
-
- if ((fd = open(file, O_RDONLY)) < 0) {
- vshError(ctl, _("cannot read %s"), file);
- goto cleanup;
- }
-
- st = virStreamNew(ctl->conn, 0);
- if (virStorageVolUpload(vol, st, offset, length, 0) < 0) {
- vshError(ctl, _("cannot upload to volume %s"), name);
- goto cleanup;
- }
-
- if (virStreamSendAll(st, cmdVolUploadSource, &fd) < 0) {
- vshError(ctl, _("cannot send data to volume %s"), name);
- goto cleanup;
- }
-
- if (VIR_CLOSE(fd) < 0) {
- vshError(ctl, _("cannot close file %s"), file);
- virStreamAbort(st);
- goto cleanup;
- }
-
- if (virStreamFinish(st) < 0) {
- vshError(ctl, _("cannot close volume %s"), name);
- goto cleanup;
- }
-
- ret = true;
-
-cleanup:
- if (vol)
- virStorageVolFree(vol);
- if (st)
- virStreamFree(st);
- VIR_FORCE_CLOSE(fd);
- return ret;
-}
-
-
-
-/*
- * "vol-download" command
- */
-static const vshCmdInfo info_vol_download[] = {
- {"help", N_("Download a volume to a file")},
- {"desc", N_("Download a volume to a file")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_vol_download[] = {
- {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
- {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file")},
- {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
- {"offset", VSH_OT_INT, 0, N_("volume offset to download from") },
- {"length", VSH_OT_INT, 0, N_("amount of data to download") },
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdVolDownload(vshControl *ctl, const vshCmd *cmd)
-{
- const char *file = NULL;
- virStorageVolPtr vol = NULL;
- bool ret = false;
- int fd = -1;
- virStreamPtr st = NULL;
- const char *name = NULL;
- unsigned long long offset = 0, length = 0;
- bool created = false;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (vshCommandOptULongLong(cmd, "offset", &offset) < 0) {
- vshError(ctl, _("Unable to parse integer"));
- return false;
- }
-
- if (vshCommandOptULongLong(cmd, "length", &length) < 0) {
- vshError(ctl, _("Unable to parse integer"));
- return false;
- }
-
- if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", &name)))
- return false;
-
- if (vshCommandOptString(cmd, "file", &file) < 0) {
- vshError(ctl, _("file must not be empty"));
- goto cleanup;
- }
-
- if ((fd = open(file, O_WRONLY|O_CREAT|O_EXCL, 0666)) < 0) {
- if (errno != EEXIST ||
- (fd = open(file, O_WRONLY|O_TRUNC, 0666)) < 0) {
- vshError(ctl, _("cannot create %s"), file);
- goto cleanup;
- }
- } else {
- created = true;
- }
-
- st = virStreamNew(ctl->conn, 0);
- if (virStorageVolDownload(vol, st, offset, length, 0) < 0) {
- vshError(ctl, _("cannot download from volume %s"), name);
- goto cleanup;
- }
-
- if (virStreamRecvAll(st, vshStreamSink, &fd) < 0) {
- vshError(ctl, _("cannot receive data from volume %s"), name);
- goto cleanup;
- }
-
- if (VIR_CLOSE(fd) < 0) {
- vshError(ctl, _("cannot close file %s"), file);
- virStreamAbort(st);
- goto cleanup;
- }
-
- if (virStreamFinish(st) < 0) {
- vshError(ctl, _("cannot close volume %s"), name);
- goto cleanup;
- }
-
- ret = true;
-
-cleanup:
- VIR_FORCE_CLOSE(fd);
- if (!ret && created)
- unlink(file);
- if (vol)
- virStorageVolFree(vol);
- if (st)
- virStreamFree(st);
- return ret;
-}
-
-
-/*
- * "vol-delete" command
- */
-static const vshCmdInfo info_vol_delete[] = {
- {"help", N_("delete a vol")},
- {"desc", N_("Delete a given vol.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_vol_delete[] = {
- {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
- {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdVolDelete(vshControl *ctl, const vshCmd *cmd)
-{
- virStorageVolPtr vol;
- bool ret = true;
- const char *name;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", &name))) {
- return false;
- }
-
- if (virStorageVolDelete(vol, 0) == 0) {
- vshPrint(ctl, _("Vol %s deleted\n"), name);
- } else {
- vshError(ctl, _("Failed to delete vol %s"), name);
- ret = false;
- }
-
- virStorageVolFree(vol);
- return ret;
-}
-
-
-/*
- * "vol-wipe" command
- */
-static const vshCmdInfo info_vol_wipe[] = {
- {"help", N_("wipe a vol")},
- {"desc", N_("Ensure data previously on a volume is not accessible to future reads")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_vol_wipe[] = {
- {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
- {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
- {"algorithm", VSH_OT_STRING, 0, N_("perform selected wiping algorithm")},
- {NULL, 0, 0, NULL}
-};
-
-VIR_ENUM_DECL(virStorageVolWipeAlgorithm)
-VIR_ENUM_IMPL(virStorageVolWipeAlgorithm, VIR_STORAGE_VOL_WIPE_ALG_LAST,
- "zero", "nnsa", "dod", "bsi", "gutmann", "schneier",
- "pfitzner7", "pfitzner33", "random");
-
-static bool
-cmdVolWipe(vshControl *ctl, const vshCmd *cmd)
-{
- virStorageVolPtr vol;
- bool ret = false;
- const char *name;
- const char *algorithm_str = NULL;
- int algorithm = VIR_STORAGE_VOL_WIPE_ALG_ZERO;
- int funcRet;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", &name))) {
- return false;
- }
-
- if (vshCommandOptString(cmd, "algorithm", &algorithm_str) < 0) {
- vshError(ctl, "%s", _("missing argument"));
- goto out;
- }
-
- if (algorithm_str &&
- (algorithm = virStorageVolWipeAlgorithmTypeFromString(algorithm_str)) < 0) {
- vshError(ctl, _("Unsupported algorithm '%s'"), algorithm_str);
- goto out;
- }
-
- if ((funcRet = virStorageVolWipePattern(vol, algorithm, 0)) < 0) {
- if (last_error->code == VIR_ERR_NO_SUPPORT &&
- algorithm == VIR_STORAGE_VOL_WIPE_ALG_ZERO)
- funcRet = virStorageVolWipe(vol, 0);
- }
-
- if (funcRet < 0) {
- vshError(ctl, _("Failed to wipe vol %s"), name);
- goto out;
- }
-
- vshPrint(ctl, _("Vol %s wiped\n"), name);
- ret = true;
-out:
- virStorageVolFree(vol);
- return ret;
-}
-
-
-/*
- * "vol-info" command
- */
-static const vshCmdInfo info_vol_info[] = {
- {"help", N_("storage vol information")},
- {"desc", N_("Returns basic information about the storage vol.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_vol_info[] = {
- {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
- {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdVolInfo(vshControl *ctl, const vshCmd *cmd)
-{
- virStorageVolInfo info;
- virStorageVolPtr vol;
- bool ret = true;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL)))
- return false;
-
- vshPrint(ctl, "%-15s %s\n", _("Name:"), virStorageVolGetName(vol));
-
- if (virStorageVolGetInfo(vol, &info) == 0) {
- double val;
- const char *unit;
- switch(info.type) {
- case VIR_STORAGE_VOL_FILE:
- vshPrint(ctl, "%-15s %s\n", _("Type:"), _("file"));
- break;
-
- case VIR_STORAGE_VOL_BLOCK:
- vshPrint(ctl, "%-15s %s\n", _("Type:"), _("block"));
- break;
-
- case VIR_STORAGE_VOL_DIR:
- vshPrint(ctl, "%-15s %s\n", _("Type:"), _("dir"));
- break;
-
- case VIR_STORAGE_VOL_NETWORK:
- vshPrint(ctl, "%-15s %s\n", _("Type:"), _("network"));
- break;
-
- default:
- vshPrint(ctl, "%-15s %s\n", _("Type:"), _("unknown"));
- }
-
- val = prettyCapacity(info.capacity, &unit);
- vshPrint(ctl, "%-15s %2.2lf %s\n", _("Capacity:"), val, unit);
-
- val = prettyCapacity(info.allocation, &unit);
- vshPrint(ctl, "%-15s %2.2lf %s\n", _("Allocation:"), val, unit);
- } else {
- ret = false;
- }
-
- virStorageVolFree(vol);
- return ret;
-}
-
-/*
- * "vol-resize" command
- */
-static const vshCmdInfo info_vol_resize[] = {
- {"help", N_("resize a vol")},
- {"desc", N_("Resizes a storage volume.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_vol_resize[] = {
- {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
- {"capacity", VSH_OT_DATA, VSH_OFLAG_REQ,
- N_("new capacity for the vol, as scaled integer (default bytes)")},
- {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
- {"allocate", VSH_OT_BOOL, 0,
- N_("allocate the new capacity, rather than leaving it sparse")},
- {"delta", VSH_OT_BOOL, 0,
- N_("use capacity as a delta to current size, rather than the new size")},
- {"shrink", VSH_OT_BOOL, 0, N_("allow the resize to shrink the volume")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdVolResize(vshControl *ctl, const vshCmd *cmd)
-{
- virStorageVolPtr vol;
- const char *capacityStr = NULL;
- unsigned long long capacity = 0;
- unsigned int flags = 0;
- bool ret = false;
- bool delta = false;
-
- if (vshCommandOptBool(cmd, "allocate"))
- flags |= VIR_STORAGE_VOL_RESIZE_ALLOCATE;
- if (vshCommandOptBool(cmd, "delta")) {
- delta = true;
- flags |= VIR_STORAGE_VOL_RESIZE_DELTA;
- }
- if (vshCommandOptBool(cmd, "shrink"))
- flags |= VIR_STORAGE_VOL_RESIZE_SHRINK;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL)))
- return false;
-
- if (vshCommandOptString(cmd, "capacity", &capacityStr) <= 0)
- goto cleanup;
- virSkipSpaces(&capacityStr);
- if (*capacityStr == '-') {
- /* The API always requires a positive value; but we allow a
- * negative value for convenience. */
- if (delta && vshCommandOptBool(cmd, "shrink")){
- capacityStr++;
- } else {
- vshError(ctl, "%s",
- _("negative size requires --delta and --shrink"));
- goto cleanup;
- }
- }
- if (vshVolSize(capacityStr, &capacity) < 0) {
- vshError(ctl, _("Malformed size %s"), capacityStr);
- goto cleanup;
- }
-
- if (virStorageVolResize(vol, capacity, flags) == 0) {
- vshPrint(ctl,
- delta ? _("Size of volume '%s' successfully changed by %s\n")
- : _("Size of volume '%s' successfully changed to %s\n"),
- virStorageVolGetName(vol), capacityStr);
- ret = true;
- } else {
- vshError(ctl,
- delta ? _("Failed to change size of volume '%s' by %s\n")
- : _("Failed to change size of volume '%s' to %s\n"),
- virStorageVolGetName(vol), capacityStr);
- ret = false;
- }
-
-cleanup:
- virStorageVolFree(vol);
- return ret;
-}
-
-
-/*
- * "vol-dumpxml" command
- */
-static const vshCmdInfo info_vol_dumpxml[] = {
- {"help", N_("vol information in XML")},
- {"desc", N_("Output the vol information as an XML dump to stdout.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_vol_dumpxml[] = {
- {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
- {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdVolDumpXML(vshControl *ctl, const vshCmd *cmd)
-{
- virStorageVolPtr vol;
- bool ret = true;
- char *dump;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL)))
- return false;
-
- dump = virStorageVolGetXMLDesc(vol, 0);
- if (dump != NULL) {
- vshPrint(ctl, "%s", dump);
- VIR_FREE(dump);
- } else {
- ret = false;
- }
-
- virStorageVolFree(vol);
- return ret;
-}
-
-
-/*
- * "vol-list" command
- */
-static const vshCmdInfo info_vol_list[] = {
- {"help", N_("list vols")},
- {"desc", N_("Returns list of vols by pool.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_vol_list[] = {
- {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
- {"details", VSH_OT_BOOL, 0, N_("display extended details for volumes")},
- {NULL, 0, 0, NULL}
-};
+static const vshCmdOptDef opts_pool_list[] = {
+ {"inactive", VSH_OT_BOOL, 0, N_("list inactive pools")},
+ {"all", VSH_OT_BOOL, 0, N_("list inactive & active pools")},
+ {"details", VSH_OT_BOOL, 0, N_("display extended details for pools")},
+ {NULL, 0, 0, NULL}
+};
static bool
-cmdVolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
+cmdPoolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
- virStorageVolInfo volumeInfo;
- virStoragePoolPtr pool;
- char **activeNames = NULL;
- char *outputStr = NULL;
- const char *unit;
- double val;
- bool details = vshCommandOptBool(cmd, "details");
- int numVolumes = 0, i;
- int ret;
+ virStoragePoolInfo info;
+ char **poolNames = NULL;
+ int i, ret;
bool functionReturn;
- int stringLength = 0;
- size_t allocStrLength = 0, capStrLength = 0;
- size_t nameStrLength = 0, pathStrLength = 0;
- size_t typeStrLength = 0;
- struct volInfoText {
- char *allocation;
+ int numActivePools = 0, numInactivePools = 0, numAllPools = 0;
+ size_t stringLength = 0, nameStrLength = 0;
+ size_t autostartStrLength = 0, persistStrLength = 0;
+ size_t stateStrLength = 0, capStrLength = 0;
+ size_t allocStrLength = 0, availStrLength = 0;
+ struct poolInfoText {
+ char *state;
+ char *autostart;
+ char *persistent;
char *capacity;
- char *path;
- char *type;
+ char *allocation;
+ char *available;
};
- struct volInfoText *volInfoTexts = NULL;
+ struct poolInfoText *poolInfoTexts = NULL;
+
+ /* Determine the options passed by the user */
+ bool all = vshCommandOptBool(cmd, "all");
+ bool details = vshCommandOptBool(cmd, "details");
+ bool inactive = vshCommandOptBool(cmd, "inactive");
+ bool active = !inactive || all;
+ inactive |= all;
/* Check the connection to libvirtd daemon is still working */
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- /* Look up the pool information given to us by the user */
- if (!(pool = vshCommandOptPool(ctl, cmd, "pool", NULL)))
- return false;
-
- /* Determine the number of volumes in the pool */
- numVolumes = virStoragePoolNumOfVolumes(pool);
-
- if (numVolumes < 0) {
- vshError(ctl, "%s", _("Failed to list storage volumes"));
- virStoragePoolFree(pool);
- return false;
+ /* Retrieve the number of active storage pools */
+ if (active) {
+ numActivePools = virConnectNumOfStoragePools(ctl->conn);
+ if (numActivePools < 0) {
+ vshError(ctl, "%s", _("Failed to list active pools"));
+ return false;
+ }
}
- /* Retrieve the list of volume names in the pool */
- if (numVolumes > 0) {
- activeNames = vshCalloc(ctl, numVolumes, sizeof(*activeNames));
- if ((numVolumes = virStoragePoolListVolumes(pool, activeNames,
- numVolumes)) < 0) {
- vshError(ctl, "%s", _("Failed to list active vols"));
- VIR_FREE(activeNames);
- virStoragePoolFree(pool);
+ /* Retrieve the number of inactive storage pools */
+ if (inactive) {
+ numInactivePools = virConnectNumOfDefinedStoragePools(ctl->conn);
+ if (numInactivePools < 0) {
+ vshError(ctl, "%s", _("Failed to list inactive pools"));
return false;
}
+ }
- /* Sort the volume names */
- qsort(&activeNames[0], numVolumes, sizeof(*activeNames), vshNameSorter);
+ /* Determine the total number of pools to list */
+ numAllPools = numActivePools + numInactivePools;
- /* Set aside memory for volume information pointers */
- volInfoTexts = vshCalloc(ctl, numVolumes, sizeof(*volInfoTexts));
+ /* Allocate memory for arrays of storage pool names and info */
+ poolNames = vshCalloc(ctl, numAllPools, sizeof(*poolNames));
+ poolInfoTexts =
+ vshCalloc(ctl, numAllPools, sizeof(*poolInfoTexts));
+
+ /* Retrieve a list of active storage pool names */
+ if (active) {
+ if (virConnectListStoragePools(ctl->conn,
+ poolNames, numActivePools) < 0) {
+ vshError(ctl, "%s", _("Failed to list active pools"));
+ VIR_FREE(poolInfoTexts);
+ VIR_FREE(poolNames);
+ return false;
+ }
}
- /* Collect the rest of the volume information for display */
- for (i = 0; i < numVolumes; i++) {
- /* Retrieve volume info */
- virStorageVolPtr vol = virStorageVolLookupByName(pool,
- activeNames[i]);
+ /* Add the inactive storage pools to the end of the name list */
+ if (inactive) {
+ if (virConnectListDefinedStoragePools(ctl->conn,
+ &poolNames[numActivePools],
+ numInactivePools) < 0) {
+ vshError(ctl, "%s", _("Failed to list inactive pools"));
+ VIR_FREE(poolInfoTexts);
+ VIR_FREE(poolNames);
+ return false;
+ }
+ }
- /* Retrieve the volume path */
- if ((volInfoTexts[i].path = virStorageVolGetPath(vol)) == NULL) {
- /* Something went wrong retrieving a volume path, cope with it */
- volInfoTexts[i].path = vshStrdup(ctl, _("unknown"));
+ /* Sort the storage pool names */
+ qsort(poolNames, numAllPools, sizeof(*poolNames), vshNameSorter);
+
+ /* Collect the storage pool information for display */
+ for (i = 0; i < numAllPools; i++) {
+ int autostart = 0, persistent = 0;
+
+ /* Retrieve a pool object, looking it up by name */
+ virStoragePoolPtr pool = virStoragePoolLookupByName(ctl->conn,
+ poolNames[i]);
+ if (!pool) {
+ VIR_FREE(poolNames[i]);
+ continue;
}
- /* If requested, retrieve volume type and sizing information */
+ /* Retrieve the autostart status of the pool */
+ if (virStoragePoolGetAutostart(pool, &autostart) < 0)
+ poolInfoTexts[i].autostart = vshStrdup(ctl, _("no autostart"));
+ else
+ poolInfoTexts[i].autostart = vshStrdup(ctl, autostart ?
+ _("yes") : _("no"));
+
+ /* Retrieve the persistence status of the pool */
if (details) {
- if (virStorageVolGetInfo(vol, &volumeInfo) != 0) {
- /* Something went wrong retrieving volume info, cope with it */
- volInfoTexts[i].allocation = vshStrdup(ctl, _("unknown"));
- volInfoTexts[i].capacity = vshStrdup(ctl, _("unknown"));
- volInfoTexts[i].type = vshStrdup(ctl, _("unknown"));
- } else {
- /* Convert the returned volume info into output strings */
+ persistent = virStoragePoolIsPersistent(pool);
+ vshDebug(ctl, VSH_ERR_DEBUG, "Persistent flag value: %d\n",
+ persistent);
+ if (persistent < 0)
+ poolInfoTexts[i].persistent = vshStrdup(ctl, _("unknown"));
+ else
+ poolInfoTexts[i].persistent = vshStrdup(ctl, persistent ?
+ _("yes") : _("no"));
- /* Volume type */
- switch (volumeInfo.type) {
- case VIR_STORAGE_VOL_FILE:
- volInfoTexts[i].type = vshStrdup(ctl, _("file"));
- break;
- case VIR_STORAGE_VOL_BLOCK:
- volInfoTexts[i].type = vshStrdup(ctl, _("block"));
- break;
- case VIR_STORAGE_VOL_DIR:
- volInfoTexts[i].type = vshStrdup(ctl, _("dir"));
- break;
- default:
- volInfoTexts[i].type = vshStrdup(ctl, _("unknown"));
- }
+ /* Keep the length of persistent string if longest so far */
+ stringLength = strlen(poolInfoTexts[i].persistent);
+ if (stringLength > persistStrLength)
+ persistStrLength = stringLength;
+ }
- /* Create the capacity output string */
- val = prettyCapacity(volumeInfo.capacity, &unit);
- ret = virAsprintf(&volInfoTexts[i].capacity,
- "%.2lf %s", val, unit);
- if (ret < 0) {
- /* An error occurred creating the string, return */
- goto asprintf_failure;
+ /* Collect further extended information about the pool */
+ if (virStoragePoolGetInfo(pool, &info) != 0) {
+ /* Something went wrong retrieving pool info, cope with it */
+ vshError(ctl, "%s", _("Could not retrieve pool information"));
+ poolInfoTexts[i].state = vshStrdup(ctl, _("unknown"));
+ if (details) {
+ poolInfoTexts[i].capacity = vshStrdup(ctl, _("unknown"));
+ poolInfoTexts[i].allocation = vshStrdup(ctl, _("unknown"));
+ poolInfoTexts[i].available = vshStrdup(ctl, _("unknown"));
+ }
+ } else {
+ /* Decide which state string to display */
+ if (details) {
+ /* --details option was specified, we're using detailed state
+ * strings */
+ switch (info.state) {
+ case VIR_STORAGE_POOL_INACTIVE:
+ poolInfoTexts[i].state = vshStrdup(ctl, _("inactive"));
+ break;
+ case VIR_STORAGE_POOL_BUILDING:
+ poolInfoTexts[i].state = vshStrdup(ctl, _("building"));
+ break;
+ case VIR_STORAGE_POOL_RUNNING:
+ poolInfoTexts[i].state = vshStrdup(ctl, _("running"));
+ break;
+ case VIR_STORAGE_POOL_DEGRADED:
+ poolInfoTexts[i].state = vshStrdup(ctl, _("degraded"));
+ break;
+ case VIR_STORAGE_POOL_INACCESSIBLE:
+ poolInfoTexts[i].state = vshStrdup(ctl, _("inaccessible"));
+ break;
}
- /* Create the allocation output string */
- val = prettyCapacity(volumeInfo.allocation, &unit);
- ret = virAsprintf(&volInfoTexts[i].allocation,
- "%.2lf %s", val, unit);
- if (ret < 0) {
- /* An error occurred creating the string, return */
- goto asprintf_failure;
- }
- }
+ /* Create the pool size related strings */
+ if (info.state == VIR_STORAGE_POOL_RUNNING ||
+ info.state == VIR_STORAGE_POOL_DEGRADED) {
+ double val;
+ const char *unit;
- /* Remember the largest length for each output string.
- * This lets us displaying header and volume information rows
- * using a single, properly sized, printf style output string.
- */
+ /* Create the capacity output string */
+ val = prettyCapacity(info.capacity, &unit);
+ ret = virAsprintf(&poolInfoTexts[i].capacity,
+ "%.2lf %s", val, unit);
+ if (ret < 0) {
+ /* An error occurred creating the string, return */
+ goto asprintf_failure;
+ }
- /* Keep the length of name string if longest so far */
- stringLength = strlen(activeNames[i]);
- if (stringLength > nameStrLength)
- nameStrLength = stringLength;
+ /* Create the allocation output string */
+ val = prettyCapacity(info.allocation, &unit);
+ ret = virAsprintf(&poolInfoTexts[i].allocation,
+ "%.2lf %s", val, unit);
+ if (ret < 0) {
+ /* An error occurred creating the string, return */
+ goto asprintf_failure;
+ }
- /* Keep the length of path string if longest so far */
- stringLength = strlen(volInfoTexts[i].path);
- if (stringLength > pathStrLength)
- pathStrLength = stringLength;
+ /* Create the available space output string */
+ val = prettyCapacity(info.available, &unit);
+ ret = virAsprintf(&poolInfoTexts[i].available,
+ "%.2lf %s", val, unit);
+ if (ret < 0) {
+ /* An error occurred creating the string, return */
+ goto asprintf_failure;
+ }
+ } else {
+ /* Capacity related information isn't available */
+ poolInfoTexts[i].capacity = vshStrdup(ctl, _("-"));
+ poolInfoTexts[i].allocation = vshStrdup(ctl, _("-"));
+ poolInfoTexts[i].available = vshStrdup(ctl, _("-"));
+ }
- /* Keep the length of type string if longest so far */
- stringLength = strlen(volInfoTexts[i].type);
- if (stringLength > typeStrLength)
- typeStrLength = stringLength;
+ /* Keep the length of capacity string if longest so far */
+ stringLength = strlen(poolInfoTexts[i].capacity);
+ if (stringLength > capStrLength)
+ capStrLength = stringLength;
+
+ /* Keep the length of allocation string if longest so far */
+ stringLength = strlen(poolInfoTexts[i].allocation);
+ if (stringLength > allocStrLength)
+ allocStrLength = stringLength;
+
+ /* Keep the length of available string if longest so far */
+ stringLength = strlen(poolInfoTexts[i].available);
+ if (stringLength > availStrLength)
+ availStrLength = stringLength;
+ } else {
+ /* --details option was not specified, only active/inactive
+ * state strings are used */
+ if (info.state == VIR_STORAGE_POOL_INACTIVE)
+ poolInfoTexts[i].state = vshStrdup(ctl, _("inactive"));
+ else
+ poolInfoTexts[i].state = vshStrdup(ctl, _("active"));
+ }
+ }
- /* Keep the length of capacity string if longest so far */
- stringLength = strlen(volInfoTexts[i].capacity);
- if (stringLength > capStrLength)
- capStrLength = stringLength;
+ /* Keep the length of name string if longest so far */
+ stringLength = strlen(poolNames[i]);
+ if (stringLength > nameStrLength)
+ nameStrLength = stringLength;
- /* Keep the length of allocation string if longest so far */
- stringLength = strlen(volInfoTexts[i].allocation);
- if (stringLength > allocStrLength)
- allocStrLength = stringLength;
- }
+ /* Keep the length of state string if longest so far */
+ stringLength = strlen(poolInfoTexts[i].state);
+ if (stringLength > stateStrLength)
+ stateStrLength = stringLength;
- /* Cleanup memory allocation */
- virStorageVolFree(vol);
+ /* Keep the length of autostart string if longest so far */
+ stringLength = strlen(poolInfoTexts[i].autostart);
+ if (stringLength > autostartStrLength)
+ autostartStrLength = stringLength;
+
+ /* Free the pool object */
+ virStoragePoolFree(pool);
}
- /* If the --details option wasn't selected, we output the volume
+ /* If the --details option wasn't selected, we output the pool
* info using the fixed string format from previous versions to
* maintain backward compatibility.
*/
/* Output basic info then return if --details option not selected */
if (!details) {
- /* The old output format */
- vshPrintExtra(ctl, "%-20s %-40s\n", _("Name"), _("Path"));
+ /* Output old style header */
+ vshPrintExtra(ctl, "%-20s %-10s %-10s\n", _("Name"), _("State"),
+ _("Autostart"));
vshPrintExtra(ctl, "-----------------------------------------\n");
- for (i = 0; i < numVolumes; i++) {
- vshPrint(ctl, "%-20s %-40s\n", activeNames[i],
- volInfoTexts[i].path);
+
+ /* Output old style pool info */
+ for (i = 0; i < numAllPools; i++) {
+ vshPrint(ctl, "%-20s %-10s %-10s\n",
+ poolNames[i],
+ poolInfoTexts[i].state,
+ poolInfoTexts[i].autostart);
}
/* Cleanup and return */
@@ -11366,15 +3921,20 @@ cmdVolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
if (stringLength > nameStrLength)
nameStrLength = stringLength;
- /* Use the length of path header string if it's longest */
- stringLength = strlen(_("Path"));
- if (stringLength > pathStrLength)
- pathStrLength = stringLength;
+ /* Use the length of state header string if it's longest */
+ stringLength = strlen(_("State"));
+ if (stringLength > stateStrLength)
+ stateStrLength = stringLength;
- /* Use the length of type header string if it's longest */
- stringLength = strlen(_("Type"));
- if (stringLength > typeStrLength)
- typeStrLength = stringLength;
+ /* Use the length of autostart header string if it's longest */
+ stringLength = strlen(_("Autostart"));
+ if (stringLength > autostartStrLength)
+ autostartStrLength = stringLength;
+
+ /* Use the length of persistent header string if it's longest */
+ stringLength = strlen(_("Persistent"));
+ if (stringLength > persistStrLength)
+ persistStrLength = stringLength;
/* Use the length of capacity header string if it's longest */
stringLength = strlen(_("Capacity"));
@@ -11386,48 +3946,65 @@ cmdVolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
if (stringLength > allocStrLength)
allocStrLength = stringLength;
- /* Display the string lengths for debugging */
- vshDebug(ctl, VSH_ERR_DEBUG,
- "Longest name string = %zu chars\n", nameStrLength);
- vshDebug(ctl, VSH_ERR_DEBUG,
- "Longest path string = %zu chars\n", pathStrLength);
- vshDebug(ctl, VSH_ERR_DEBUG,
- "Longest type string = %zu chars\n", typeStrLength);
- vshDebug(ctl, VSH_ERR_DEBUG,
- "Longest capacity string = %zu chars\n", capStrLength);
- vshDebug(ctl, VSH_ERR_DEBUG,
- "Longest allocation string = %zu chars\n", allocStrLength);
+ /* Use the length of available header string if it's longest */
+ stringLength = strlen(_("Available"));
+ if (stringLength > availStrLength)
+ availStrLength = stringLength;
- /* Create the output template */
+ /* Display the string lengths for debugging. */
+ vshDebug(ctl, VSH_ERR_DEBUG, "Longest name string = %lu chars\n",
+ (unsigned long) nameStrLength);
+ vshDebug(ctl, VSH_ERR_DEBUG, "Longest state string = %lu chars\n",
+ (unsigned long) stateStrLength);
+ vshDebug(ctl, VSH_ERR_DEBUG, "Longest autostart string = %lu chars\n",
+ (unsigned long) autostartStrLength);
+ vshDebug(ctl, VSH_ERR_DEBUG, "Longest persistent string = %lu chars\n",
+ (unsigned long) persistStrLength);
+ vshDebug(ctl, VSH_ERR_DEBUG, "Longest capacity string = %lu chars\n",
+ (unsigned long) capStrLength);
+ vshDebug(ctl, VSH_ERR_DEBUG, "Longest allocation string = %lu chars\n",
+ (unsigned long) allocStrLength);
+ vshDebug(ctl, VSH_ERR_DEBUG, "Longest available string = %lu chars\n",
+ (unsigned long) availStrLength);
+
+ /* Create the output template. Each column is sized according to
+ * the longest string.
+ */
+ char *outputStr;
ret = virAsprintf(&outputStr,
- "%%-%lus %%-%lus %%-%lus %%%lus %%%lus\n",
- (unsigned long) nameStrLength,
- (unsigned long) pathStrLength,
- (unsigned long) typeStrLength,
- (unsigned long) capStrLength,
- (unsigned long) allocStrLength);
+ "%%-%lus %%-%lus %%-%lus %%-%lus %%%lus %%%lus %%%lus\n",
+ (unsigned long) nameStrLength,
+ (unsigned long) stateStrLength,
+ (unsigned long) autostartStrLength,
+ (unsigned long) persistStrLength,
+ (unsigned long) capStrLength,
+ (unsigned long) allocStrLength,
+ (unsigned long) availStrLength);
if (ret < 0) {
/* An error occurred creating the string, return */
goto asprintf_failure;
}
/* Display the header */
- vshPrint(ctl, outputStr, _("Name"), _("Path"), _("Type"),
- ("Capacity"), _("Allocation"));
- for (i = nameStrLength + pathStrLength + typeStrLength
- + capStrLength + allocStrLength
- + 8; i > 0; i--)
+ vshPrint(ctl, outputStr, _("Name"), _("State"), _("Autostart"),
+ _("Persistent"), _("Capacity"), _("Allocation"), _("Available"));
+ for (i = nameStrLength + stateStrLength + autostartStrLength
+ + persistStrLength + capStrLength
+ + allocStrLength + availStrLength
+ + 12; i > 0; i--)
vshPrintExtra(ctl, "-");
vshPrintExtra(ctl, "\n");
- /* Display the volume info rows */
- for (i = 0; i < numVolumes; i++) {
+ /* Display the pool info rows */
+ for (i = 0; i < numAllPools; i++) {
vshPrint(ctl, outputStr,
- activeNames[i],
- volInfoTexts[i].path,
- volInfoTexts[i].type,
- volInfoTexts[i].capacity,
- volInfoTexts[i].allocation);
+ poolNames[i],
+ poolInfoTexts[i].state,
+ poolInfoTexts[i].autostart,
+ poolInfoTexts[i].persistent,
+ poolInfoTexts[i].capacity,
+ poolInfoTexts[i].allocation,
+ poolInfoTexts[i].available);
}
/* Cleanup and return */
@@ -11451,3146 +4028,2607 @@ asprintf_failure:
cleanup:
/* Safely free the memory allocated in this function */
- for (i = 0; i < numVolumes; i++) {
- /* Cleanup the memory for one volume info structure per loop */
- VIR_FREE(volInfoTexts[i].path);
- VIR_FREE(volInfoTexts[i].type);
- VIR_FREE(volInfoTexts[i].capacity);
- VIR_FREE(volInfoTexts[i].allocation);
- VIR_FREE(activeNames[i]);
+ for (i = 0; i < numAllPools; i++) {
+ /* Cleanup the memory for one pool info structure */
+ VIR_FREE(poolInfoTexts[i].state);
+ VIR_FREE(poolInfoTexts[i].autostart);
+ VIR_FREE(poolInfoTexts[i].persistent);
+ VIR_FREE(poolInfoTexts[i].capacity);
+ VIR_FREE(poolInfoTexts[i].allocation);
+ VIR_FREE(poolInfoTexts[i].available);
+ VIR_FREE(poolNames[i]);
}
- /* Cleanup remaining memory */
- VIR_FREE(outputStr);
- VIR_FREE(volInfoTexts);
- VIR_FREE(activeNames);
- virStoragePoolFree(pool);
+ /* Cleanup the memory for the initial arrays*/
+ VIR_FREE(poolInfoTexts);
+ VIR_FREE(poolNames);
/* Return the desired value */
return functionReturn;
}
-
/*
- * "vol-name" command
+ * "find-storage-pool-sources-as" command
*/
-static const vshCmdInfo info_vol_name[] = {
- {"help", N_("returns the volume name for a given volume key or path")},
- {"desc", ""},
+static const vshCmdInfo info_find_storage_pool_sources_as[] = {
+ {"help", N_("find potential storage pool sources")},
+ {"desc", N_("Returns XML document.")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_vol_name[] = {
- {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("volume key or path")},
+static const vshCmdOptDef opts_find_storage_pool_sources_as[] = {
+ {"type", VSH_OT_DATA, VSH_OFLAG_REQ,
+ N_("type of storage pool sources to find")},
+ {"host", VSH_OT_DATA, VSH_OFLAG_NONE, N_("optional host to query")},
+ {"port", VSH_OT_DATA, VSH_OFLAG_NONE, N_("optional port to query")},
+ {"initiator", VSH_OT_DATA, VSH_OFLAG_NONE, N_("optional initiator IQN to use for query")},
{NULL, 0, 0, NULL}
};
static bool
-cmdVolName(vshControl *ctl, const vshCmd *cmd)
+cmdPoolDiscoverSourcesAs(vshControl * ctl, const vshCmd * cmd ATTRIBUTE_UNUSED)
{
- virStorageVolPtr vol;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
+ const char *type = NULL, *host = NULL;
+ char *srcSpec = NULL;
+ char *srcList;
+ const char *initiator = NULL;
- if (!(vol = vshCommandOptVolBy(ctl, cmd, "vol", NULL, NULL,
- VSH_BYUUID)))
+ if (vshCommandOptString(cmd, "type", &type) <= 0 ||
+ vshCommandOptString(cmd, "host", &host) < 0 ||
+ vshCommandOptString(cmd, "initiator", &initiator) < 0) {
+ vshError(ctl,"%s", _("missing argument"));
return false;
+ }
- vshPrint(ctl, "%s\n", virStorageVolGetName(vol));
- virStorageVolFree(vol);
- return true;
-}
-
-
-/*
- * "vol-pool" command
- */
-static const vshCmdInfo info_vol_pool[] = {
- {"help", N_("returns the storage pool for a given volume key or path")},
- {"desc", ""},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_vol_pool[] = {
- {"uuid", VSH_OT_BOOL, 0, N_("return the pool uuid rather than pool name")},
- {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("volume key or path")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdVolPool(vshControl *ctl, const vshCmd *cmd)
-{
- virStoragePoolPtr pool;
- virStorageVolPtr vol;
- char uuid[VIR_UUID_STRING_BUFLEN];
-
- /* Check the connection to libvirtd daemon is still working */
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- /* Use the supplied string to locate the volume */
- if (!(vol = vshCommandOptVolBy(ctl, cmd, "vol", NULL, NULL,
- VSH_BYUUID))) {
- return false;
+ if (host) {
+ const char *port = NULL;
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+
+ if (vshCommandOptString(cmd, "port", &port) < 0) {
+ vshError(ctl, "%s", _("missing argument"));
+ virBufferFreeAndReset(&buf);
+ return false;
+ }
+ virBufferAddLit(&buf, "\n");
+ if (virBufferError(&buf)) {
+ vshError(ctl, "%s", _("Out of memory"));
+ return false;
+ }
+ srcSpec = virBufferContentAndReset(&buf);
}
- /* Look up the parent storage pool for the volume */
- pool = virStoragePoolLookupByVolume(vol);
- if (pool == NULL) {
- vshError(ctl, "%s", _("failed to get parent pool"));
- virStorageVolFree(vol);
+ srcList = virConnectFindStoragePoolSources(ctl->conn, type, srcSpec, 0);
+ VIR_FREE(srcSpec);
+ if (srcList == NULL) {
+ vshError(ctl, _("Failed to find any %s pool sources"), type);
return false;
}
+ vshPrint(ctl, "%s", srcList);
+ VIR_FREE(srcList);
- /* Return the requested details of the parent storage pool */
- if (vshCommandOptBool(cmd, "uuid")) {
- /* Retrieve and return pool UUID string */
- if (virStoragePoolGetUUIDString(pool, &uuid[0]) == 0)
- vshPrint(ctl, "%s\n", uuid);
- } else {
- /* Return the storage pool name */
- vshPrint(ctl, "%s\n", virStoragePoolGetName(pool));
- }
-
- /* Cleanup */
- virStorageVolFree(vol);
- virStoragePoolFree(pool);
return true;
}
/*
- * "vol-key" command
+ * "find-storage-pool-sources" command
*/
-static const vshCmdInfo info_vol_key[] = {
- {"help", N_("returns the volume key for a given volume name or path")},
- {"desc", ""},
+static const vshCmdInfo info_find_storage_pool_sources[] = {
+ {"help", N_("discover potential storage pool sources")},
+ {"desc", N_("Returns XML document.")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_vol_key[] = {
- {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("volume name or path")},
- {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
+static const vshCmdOptDef opts_find_storage_pool_sources[] = {
+ {"type", VSH_OT_DATA, VSH_OFLAG_REQ,
+ N_("type of storage pool sources to discover")},
+ {"srcSpec", VSH_OT_DATA, VSH_OFLAG_NONE,
+ N_("optional file of source xml to query for pools")},
{NULL, 0, 0, NULL}
};
static bool
-cmdVolKey(vshControl *ctl, const vshCmd *cmd)
+cmdPoolDiscoverSources(vshControl * ctl, const vshCmd * cmd ATTRIBUTE_UNUSED)
{
- virStorageVolPtr vol;
+ const char *type = NULL, *srcSpecFile = NULL;
+ char *srcSpec = NULL, *srcList;
+
+ if (vshCommandOptString(cmd, "type", &type) <= 0)
+ return false;
+
+ if (vshCommandOptString(cmd, "srcSpec", &srcSpecFile) < 0) {
+ vshError(ctl, "%s", _("missing option"));
+ return false;
+ }
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL)))
+ if (srcSpecFile && virFileReadAll(srcSpecFile, VIRSH_MAX_XML_FILE, &srcSpec) < 0)
return false;
- vshPrint(ctl, "%s\n", virStorageVolGetKey(vol));
- virStorageVolFree(vol);
+ srcList = virConnectFindStoragePoolSources(ctl->conn, type, srcSpec, 0);
+ VIR_FREE(srcSpec);
+ if (srcList == NULL) {
+ vshError(ctl, _("Failed to find any %s pool sources"), type);
+ return false;
+ }
+ vshPrint(ctl, "%s", srcList);
+ VIR_FREE(srcList);
+
return true;
}
-
/*
- * "vol-path" command
+ * "pool-info" command
*/
-static const vshCmdInfo info_vol_path[] = {
- {"help", N_("returns the volume path for a given volume name or key")},
- {"desc", ""},
+static const vshCmdInfo info_pool_info[] = {
+ {"help", N_("storage pool information")},
+ {"desc", N_("Returns basic information about the storage pool.")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_vol_path[] = {
- {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("volume name or key")},
- {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
+static const vshCmdOptDef opts_pool_info[] = {
+ {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
{NULL, 0, 0, NULL}
};
static bool
-cmdVolPath(vshControl *ctl, const vshCmd *cmd)
+cmdPoolInfo(vshControl *ctl, const vshCmd *cmd)
{
- virStorageVolPtr vol;
- char * StorageVolPath;
+ virStoragePoolInfo info;
+ virStoragePoolPtr pool;
+ int autostart = 0;
+ int persistent = 0;
+ bool ret = true;
+ char uuid[VIR_UUID_STRING_BUFLEN];
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL))) {
+ if (!(pool = vshCommandOptPool(ctl, cmd, "pool", NULL)))
return false;
- }
- if ((StorageVolPath = virStorageVolGetPath(vol)) == NULL) {
- virStorageVolFree(vol);
- return false;
+ vshPrint(ctl, "%-15s %s\n", _("Name:"), virStoragePoolGetName(pool));
+
+ if (virStoragePoolGetUUIDString(pool, &uuid[0])==0)
+ vshPrint(ctl, "%-15s %s\n", _("UUID:"), uuid);
+
+ if (virStoragePoolGetInfo(pool, &info) == 0) {
+ double val;
+ const char *unit;
+ switch (info.state) {
+ case VIR_STORAGE_POOL_INACTIVE:
+ vshPrint(ctl, "%-15s %s\n", _("State:"),
+ _("inactive"));
+ break;
+ case VIR_STORAGE_POOL_BUILDING:
+ vshPrint(ctl, "%-15s %s\n", _("State:"),
+ _("building"));
+ break;
+ case VIR_STORAGE_POOL_RUNNING:
+ vshPrint(ctl, "%-15s %s\n", _("State:"),
+ _("running"));
+ break;
+ case VIR_STORAGE_POOL_DEGRADED:
+ vshPrint(ctl, "%-15s %s\n", _("State:"),
+ _("degraded"));
+ break;
+ case VIR_STORAGE_POOL_INACCESSIBLE:
+ vshPrint(ctl, "%-15s %s\n", _("State:"),
+ _("inaccessible"));
+ break;
+ }
+
+ /* Check and display whether the pool is persistent or not */
+ persistent = virStoragePoolIsPersistent(pool);
+ vshDebug(ctl, VSH_ERR_DEBUG, "Pool persistent flag value: %d\n",
+ persistent);
+ if (persistent < 0)
+ vshPrint(ctl, "%-15s %s\n", _("Persistent:"), _("unknown"));
+ else
+ vshPrint(ctl, "%-15s %s\n", _("Persistent:"), persistent ? _("yes") : _("no"));
+
+ /* Check and display whether the pool is autostarted or not */
+ virStoragePoolGetAutostart(pool, &autostart);
+ vshDebug(ctl, VSH_ERR_DEBUG, "Pool autostart flag value: %d\n",
+ autostart);
+ if (autostart < 0)
+ vshPrint(ctl, "%-15s %s\n", _("Autostart:"), _("no autostart"));
+ else
+ vshPrint(ctl, "%-15s %s\n", _("Autostart:"), autostart ? _("yes") : _("no"));
+
+ if (info.state == VIR_STORAGE_POOL_RUNNING ||
+ info.state == VIR_STORAGE_POOL_DEGRADED) {
+ val = prettyCapacity(info.capacity, &unit);
+ vshPrint(ctl, "%-15s %2.2lf %s\n", _("Capacity:"), val, unit);
+
+ val = prettyCapacity(info.allocation, &unit);
+ vshPrint(ctl, "%-15s %2.2lf %s\n", _("Allocation:"), val, unit);
+
+ val = prettyCapacity(info.available, &unit);
+ vshPrint(ctl, "%-15s %2.2lf %s\n", _("Available:"), val, unit);
+ }
+ } else {
+ ret = false;
}
- vshPrint(ctl, "%s\n", StorageVolPath);
- VIR_FREE(StorageVolPath);
- virStorageVolFree(vol);
- return true;
+ virStoragePoolFree(pool);
+ return ret;
}
/*
- * "secret-define" command
+ * "pool-name" command
*/
-static const vshCmdInfo info_secret_define[] = {
- {"help", N_("define or modify a secret from an XML file")},
- {"desc", N_("Define or modify a secret.")},
+static const vshCmdInfo info_pool_name[] = {
+ {"help", N_("convert a pool UUID to pool name")},
+ {"desc", ""},
{NULL, NULL}
};
-static const vshCmdOptDef opts_secret_define[] = {
- {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing secret attributes in XML")},
+static const vshCmdOptDef opts_pool_name[] = {
+ {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool uuid")},
{NULL, 0, 0, NULL}
};
static bool
-cmdSecretDefine(vshControl *ctl, const vshCmd *cmd)
+cmdPoolName(vshControl *ctl, const vshCmd *cmd)
{
- const char *from = NULL;
- char *buffer;
- virSecretPtr res;
- char uuid[VIR_UUID_STRING_BUFLEN];
+ virStoragePoolPtr pool;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
-
- if (vshCommandOptString(cmd, "file", &from) <= 0)
- return false;
-
- if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
+ if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL,
+ VSH_BYUUID)))
return false;
- res = virSecretDefineXML(ctl->conn, buffer, 0);
- VIR_FREE(buffer);
-
- if (res == NULL) {
- vshError(ctl, _("Failed to set attributes from %s"), from);
- return false;
- }
- if (virSecretGetUUIDString(res, &(uuid[0])) < 0) {
- vshError(ctl, "%s", _("Failed to get UUID of created secret"));
- virSecretFree(res);
- return false;
- }
- vshPrint(ctl, _("Secret %s created\n"), uuid);
- virSecretFree(res);
+ vshPrint(ctl, "%s\n", virStoragePoolGetName(pool));
+ virStoragePoolFree(pool);
return true;
}
+
/*
- * "secret-dumpxml" command
+ * "pool-start" command
*/
-static const vshCmdInfo info_secret_dumpxml[] = {
- {"help", N_("secret attributes in XML")},
- {"desc", N_("Output attributes of a secret as an XML dump to stdout.")},
+static const vshCmdInfo info_pool_start[] = {
+ {"help", N_("start a (previously defined) inactive pool")},
+ {"desc", N_("Start a pool.")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_secret_dumpxml[] = {
- {"secret", VSH_OT_DATA, VSH_OFLAG_REQ, N_("secret UUID")},
+static const vshCmdOptDef opts_pool_start[] = {
+ {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("name or uuid of the inactive pool")},
{NULL, 0, 0, NULL}
};
static bool
-cmdSecretDumpXML(vshControl *ctl, const vshCmd *cmd)
+cmdPoolStart(vshControl *ctl, const vshCmd *cmd)
{
- virSecretPtr secret;
- bool ret = false;
- char *xml;
+ virStoragePoolPtr pool;
+ bool ret = true;
+ const char *name = NULL;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- secret = vshCommandOptSecret(ctl, cmd, NULL);
- if (secret == NULL)
- return false;
+ if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
+ return false;
- xml = virSecretGetXMLDesc(secret, 0);
- if (xml == NULL)
- goto cleanup;
- vshPrint(ctl, "%s", xml);
- VIR_FREE(xml);
- ret = true;
+ if (virStoragePoolCreate(pool, 0) == 0) {
+ vshPrint(ctl, _("Pool %s started\n"), name);
+ } else {
+ vshError(ctl, _("Failed to start pool %s"), name);
+ ret = false;
+ }
-cleanup:
- virSecretFree(secret);
+ virStoragePoolFree(pool);
return ret;
}
+
/*
- * "secret-set-value" command
+ * "vol-create-as" command
*/
-static const vshCmdInfo info_secret_set_value[] = {
- {"help", N_("set a secret value")},
- {"desc", N_("Set a secret value.")},
+static const vshCmdInfo info_vol_create_as[] = {
+ {"help", N_("create a volume from a set of args")},
+ {"desc", N_("Create a vol.")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_secret_set_value[] = {
- {"secret", VSH_OT_DATA, VSH_OFLAG_REQ, N_("secret UUID")},
- {"base64", VSH_OT_DATA, VSH_OFLAG_REQ, N_("base64-encoded secret value")},
+static const vshCmdOptDef opts_vol_create_as[] = {
+ {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name")},
+ {"name", VSH_OT_DATA, VSH_OFLAG_REQ, N_("name of the volume")},
+ {"capacity", VSH_OT_DATA, VSH_OFLAG_REQ,
+ N_("size of the vol, as scaled integer (default bytes)")},
+ {"allocation", VSH_OT_STRING, 0,
+ N_("initial allocation size, as scaled integer (default bytes)")},
+ {"format", VSH_OT_STRING, 0,
+ N_("file format type raw,bochs,qcow,qcow2,qed,vmdk")},
+ {"backing-vol", VSH_OT_STRING, 0,
+ N_("the backing volume if taking a snapshot")},
+ {"backing-vol-format", VSH_OT_STRING, 0,
+ N_("format of backing volume if taking a snapshot")},
{NULL, 0, 0, NULL}
};
+static int
+vshVolSize(const char *data, unsigned long long *val)
+{
+ char *end;
+ if (virStrToLong_ull(data, &end, 10, val) < 0)
+ return -1;
+ return virScaleInteger(val, end, 1, ULLONG_MAX);
+}
+
static bool
-cmdSecretSetValue(vshControl *ctl, const vshCmd *cmd)
+cmdVolCreateAs(vshControl *ctl, const vshCmd *cmd)
{
- virSecretPtr secret;
- size_t value_size;
- const char *base64 = NULL;
- char *value;
- int res;
- bool ret = false;
+ virStoragePoolPtr pool;
+ virStorageVolPtr vol;
+ char *xml;
+ const char *name, *capacityStr = NULL, *allocationStr = NULL, *format = NULL;
+ const char *snapshotStrVol = NULL, *snapshotStrFormat = NULL;
+ unsigned long long capacity, allocation = 0;
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- secret = vshCommandOptSecret(ctl, cmd, NULL);
- if (secret == NULL)
+ if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL,
+ VSH_BYNAME)))
return false;
- if (vshCommandOptString(cmd, "base64", &base64) <= 0)
+ if (vshCommandOptString(cmd, "name", &name) <= 0)
goto cleanup;
- if (!base64_decode_alloc(base64, strlen(base64), &value, &value_size)) {
- vshError(ctl, "%s", _("Invalid base64 data"));
+ if (vshCommandOptString(cmd, "capacity", &capacityStr) <= 0)
+ goto cleanup;
+
+ if (vshVolSize(capacityStr, &capacity) < 0) {
+ vshError(ctl, _("Malformed size %s"), capacityStr);
goto cleanup;
}
- if (value == NULL) {
- vshError(ctl, "%s", _("Failed to allocate memory"));
- return false;
+
+ if (vshCommandOptString(cmd, "allocation", &allocationStr) > 0 &&
+ vshVolSize(allocationStr, &allocation) < 0) {
+ vshError(ctl, _("Malformed size %s"), allocationStr);
+ goto cleanup;
}
- res = virSecretSetValue(secret, (unsigned char *)value, value_size, 0);
- memset(value, 0, value_size);
- VIR_FREE(value);
+ if (vshCommandOptString(cmd, "format", &format) < 0 ||
+ vshCommandOptString(cmd, "backing-vol", &snapshotStrVol) < 0 ||
+ vshCommandOptString(cmd, "backing-vol-format",
+ &snapshotStrFormat) < 0) {
+ vshError(ctl, "%s", _("missing argument"));
+ goto cleanup;
+ }
- if (res != 0) {
- vshError(ctl, "%s", _("Failed to set secret value"));
+
+ virBufferAddLit(&buf, "\n");
+ virBufferAsprintf(&buf, " %s\n", name);
+ virBufferAsprintf(&buf, " %llu\n", capacity);
+ if (allocationStr)
+ virBufferAsprintf(&buf, " %llu\n", allocation);
+
+ if (format) {
+ virBufferAddLit(&buf, " \n");
+ virBufferAsprintf(&buf, " \n",format);
+ virBufferAddLit(&buf, " \n");
+ }
+
+ /* Convert the snapshot parameters into backingStore XML */
+ if (snapshotStrVol) {
+ /* Lookup snapshot backing volume. Try the backing-vol
+ * parameter as a name */
+ vshDebug(ctl, VSH_ERR_DEBUG,
+ "%s: Look up backing store volume '%s' as name\n",
+ cmd->def->name, snapshotStrVol);
+ virStorageVolPtr snapVol = virStorageVolLookupByName(pool, snapshotStrVol);
+ if (snapVol)
+ vshDebug(ctl, VSH_ERR_DEBUG,
+ "%s: Backing store volume found using '%s' as name\n",
+ cmd->def->name, snapshotStrVol);
+
+ if (snapVol == NULL) {
+ /* Snapshot backing volume not found by name. Try the
+ * backing-vol parameter as a key */
+ vshDebug(ctl, VSH_ERR_DEBUG,
+ "%s: Look up backing store volume '%s' as key\n",
+ cmd->def->name, snapshotStrVol);
+ snapVol = virStorageVolLookupByKey(ctl->conn, snapshotStrVol);
+ if (snapVol)
+ vshDebug(ctl, VSH_ERR_DEBUG,
+ "%s: Backing store volume found using '%s' as key\n",
+ cmd->def->name, snapshotStrVol);
+ }
+ if (snapVol == NULL) {
+ /* Snapshot backing volume not found by key. Try the
+ * backing-vol parameter as a path */
+ vshDebug(ctl, VSH_ERR_DEBUG,
+ "%s: Look up backing store volume '%s' as path\n",
+ cmd->def->name, snapshotStrVol);
+ snapVol = virStorageVolLookupByPath(ctl->conn, snapshotStrVol);
+ if (snapVol)
+ vshDebug(ctl, VSH_ERR_DEBUG,
+ "%s: Backing store volume found using '%s' as path\n",
+ cmd->def->name, snapshotStrVol);
+ }
+ if (snapVol == NULL) {
+ vshError(ctl, _("failed to get vol '%s'"), snapshotStrVol);
+ goto cleanup;
+ }
+
+ char *snapshotStrVolPath;
+ if ((snapshotStrVolPath = virStorageVolGetPath(snapVol)) == NULL) {
+ virStorageVolFree(snapVol);
+ goto cleanup;
+ }
+
+ /* Create XML for the backing store */
+ virBufferAddLit(&buf, " \n");
+ virBufferAsprintf(&buf, " %s\n",snapshotStrVolPath);
+ if (snapshotStrFormat)
+ virBufferAsprintf(&buf, " \n",snapshotStrFormat);
+ virBufferAddLit(&buf, " \n");
+
+ /* Cleanup snapshot allocations */
+ VIR_FREE(snapshotStrVolPath);
+ virStorageVolFree(snapVol);
+ }
+
+ virBufferAddLit(&buf, "\n");
+
+ if (virBufferError(&buf)) {
+ vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
goto cleanup;
}
- vshPrint(ctl, "%s", _("Secret value set\n"));
- ret = true;
+ xml = virBufferContentAndReset(&buf);
+ vol = virStorageVolCreateXML(pool, xml, 0);
+ VIR_FREE(xml);
+ virStoragePoolFree(pool);
-cleanup:
- virSecretFree(secret);
- return ret;
+ if (vol != NULL) {
+ vshPrint(ctl, _("Vol %s created\n"), name);
+ virStorageVolFree(vol);
+ return true;
+ } else {
+ vshError(ctl, _("Failed to create vol %s"), name);
+ return false;
+ }
+
+ cleanup:
+ virBufferFreeAndReset(&buf);
+ virStoragePoolFree(pool);
+ return false;
}
+
/*
- * "secret-get-value" command
+ * "pool-undefine" command
*/
-static const vshCmdInfo info_secret_get_value[] = {
- {"help", N_("Output a secret value")},
- {"desc", N_("Output a secret value to stdout.")},
+static const vshCmdInfo info_pool_undefine[] = {
+ {"help", N_("undefine an inactive pool")},
+ {"desc", N_("Undefine the configuration for an inactive pool.")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_secret_get_value[] = {
- {"secret", VSH_OT_DATA, VSH_OFLAG_REQ, N_("secret UUID")},
+static const vshCmdOptDef opts_pool_undefine[] = {
+ {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
{NULL, 0, 0, NULL}
};
static bool
-cmdSecretGetValue(vshControl *ctl, const vshCmd *cmd)
+cmdPoolUndefine(vshControl *ctl, const vshCmd *cmd)
{
- virSecretPtr secret;
- char *base64;
- unsigned char *value;
- size_t value_size;
- bool ret = false;
+ virStoragePoolPtr pool;
+ bool ret = true;
+ const char *name;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- secret = vshCommandOptSecret(ctl, cmd, NULL);
- if (secret == NULL)
+ if (!(pool = vshCommandOptPool(ctl, cmd, "pool", &name)))
return false;
- value = virSecretGetValue(secret, &value_size, 0);
- if (value == NULL)
- goto cleanup;
-
- base64_encode_alloc((char *)value, value_size, &base64);
- memset(value, 0, value_size);
- VIR_FREE(value);
-
- if (base64 == NULL) {
- vshError(ctl, "%s", _("Failed to allocate memory"));
- goto cleanup;
+ if (virStoragePoolUndefine(pool) == 0) {
+ vshPrint(ctl, _("Pool %s has been undefined\n"), name);
+ } else {
+ vshError(ctl, _("Failed to undefine pool %s"), name);
+ ret = false;
}
- vshPrint(ctl, "%s", base64);
- memset(base64, 0, strlen(base64));
- VIR_FREE(base64);
- ret = true;
-cleanup:
- virSecretFree(secret);
+ virStoragePoolFree(pool);
return ret;
}
+
/*
- * "secret-undefine" command
+ * "pool-uuid" command
*/
-static const vshCmdInfo info_secret_undefine[] = {
- {"help", N_("undefine a secret")},
- {"desc", N_("Undefine a secret.")},
+static const vshCmdInfo info_pool_uuid[] = {
+ {"help", N_("convert a pool name to pool UUID")},
+ {"desc", ""},
{NULL, NULL}
};
-static const vshCmdOptDef opts_secret_undefine[] = {
- {"secret", VSH_OT_DATA, VSH_OFLAG_REQ, N_("secret UUID")},
+static const vshCmdOptDef opts_pool_uuid[] = {
+ {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name")},
{NULL, 0, 0, NULL}
};
static bool
-cmdSecretUndefine(vshControl *ctl, const vshCmd *cmd)
+cmdPoolUuid(vshControl *ctl, const vshCmd *cmd)
{
- virSecretPtr secret;
- bool ret = false;
- const char *uuid;
+ virStoragePoolPtr pool;
+ char uuid[VIR_UUID_STRING_BUFLEN];
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- secret = vshCommandOptSecret(ctl, cmd, &uuid);
- if (secret == NULL)
+ if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL,
+ VSH_BYNAME)))
return false;
- if (virSecretUndefine(secret) < 0) {
- vshError(ctl, _("Failed to delete secret %s"), uuid);
- goto cleanup;
- }
- vshPrint(ctl, _("Secret %s deleted\n"), uuid);
- ret = true;
+ if (virStoragePoolGetUUIDString(pool, uuid) != -1)
+ vshPrint(ctl, "%s\n", uuid);
+ else
+ vshError(ctl, "%s", _("failed to get pool UUID"));
-cleanup:
- virSecretFree(secret);
- return ret;
+ virStoragePoolFree(pool);
+ return true;
}
+
/*
- * "secret-list" command
+ * "vol-create" command
*/
-static const vshCmdInfo info_secret_list[] = {
- {"help", N_("list secrets")},
- {"desc", N_("Returns a list of secrets")},
+static const vshCmdInfo info_vol_create[] = {
+ {"help", N_("create a vol from an XML file")},
+ {"desc", N_("Create a vol.")},
{NULL, NULL}
};
+static const vshCmdOptDef opts_vol_create[] = {
+ {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name")},
+ {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML vol description")},
+ {NULL, 0, 0, NULL}
+};
+
static bool
-cmdSecretList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
+cmdVolCreate(vshControl *ctl, const vshCmd *cmd)
{
- int maxuuids = 0, i;
- char **uuids = NULL;
+ virStoragePoolPtr pool;
+ virStorageVolPtr vol;
+ const char *from = NULL;
+ bool ret = true;
+ char *buffer;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- maxuuids = virConnectNumOfSecrets(ctl->conn);
- if (maxuuids < 0) {
- vshError(ctl, "%s", _("Failed to list secrets"));
+ if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL,
+ VSH_BYNAME)))
return false;
- }
- uuids = vshMalloc(ctl, sizeof(*uuids) * maxuuids);
- maxuuids = virConnectListSecrets(ctl->conn, uuids, maxuuids);
- if (maxuuids < 0) {
- vshError(ctl, "%s", _("Failed to list secrets"));
- VIR_FREE(uuids);
+ if (vshCommandOptString(cmd, "file", &from) <= 0) {
+ virStoragePoolFree(pool);
return false;
}
- qsort(uuids, maxuuids, sizeof(char *), vshNameSorter);
-
- vshPrintExtra(ctl, "%-36s %s\n", _("UUID"), _("Usage"));
- vshPrintExtra(ctl, "-----------------------------------------------------------\n");
-
- for (i = 0; i < maxuuids; i++) {
- virSecretPtr sec = virSecretLookupByUUIDString(ctl->conn, uuids[i]);
- const char *usageType = NULL;
-
- if (!sec) {
- VIR_FREE(uuids[i]);
- continue;
- }
+ if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
+ virshReportError(ctl);
+ virStoragePoolFree(pool);
+ return false;
+ }
- switch (virSecretGetUsageType(sec)) {
- case VIR_SECRET_USAGE_TYPE_VOLUME:
- usageType = _("Volume");
- break;
- }
+ vol = virStorageVolCreateXML(pool, buffer, 0);
+ VIR_FREE(buffer);
+ virStoragePoolFree(pool);
- if (usageType) {
- vshPrint(ctl, "%-36s %s %s\n",
- uuids[i], usageType,
- virSecretGetUsageID(sec));
- } else {
- vshPrint(ctl, "%-36s %s\n",
- uuids[i], _("Unused"));
- }
- virSecretFree(sec);
- VIR_FREE(uuids[i]);
+ if (vol != NULL) {
+ vshPrint(ctl, _("Vol %s created from %s\n"),
+ virStorageVolGetName(vol), from);
+ virStorageVolFree(vol);
+ } else {
+ vshError(ctl, _("Failed to create vol from %s"), from);
+ ret = false;
}
- VIR_FREE(uuids);
- return true;
+ return ret;
}
-
/*
- * "version" command
+ * "vol-create-from" command
*/
-static const vshCmdInfo info_version[] = {
- {"help", N_("show version")},
- {"desc", N_("Display the system version information.")},
+static const vshCmdInfo info_vol_create_from[] = {
+ {"help", N_("create a vol, using another volume as input")},
+ {"desc", N_("Create a vol from an existing volume.")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_version[] = {
- {"daemon", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("report daemon version too")},
+static const vshCmdOptDef opts_vol_create_from[] = {
+ {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
+ {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML vol description")},
+ {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("input vol name or key")},
+ {"inputpool", VSH_OT_STRING, 0, N_("pool name or uuid of the input volume's pool")},
{NULL, 0, 0, NULL}
};
static bool
-cmdVersion(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
+cmdVolCreateFrom(vshControl *ctl, const vshCmd *cmd)
{
- unsigned long hvVersion;
- const char *hvType;
- unsigned long libVersion;
- unsigned long includeVersion;
- unsigned long apiVersion;
- unsigned long daemonVersion;
- int ret;
- unsigned int major;
- unsigned int minor;
- unsigned int rel;
+ virStoragePoolPtr pool = NULL;
+ virStorageVolPtr newvol = NULL, inputvol = NULL;
+ const char *from = NULL;
+ bool ret = false;
+ char *buffer = NULL;
if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- hvType = virConnectGetType(ctl->conn);
- if (hvType == NULL) {
- vshError(ctl, "%s", _("failed to get hypervisor type"));
- return false;
- }
+ goto cleanup;
- includeVersion = LIBVIR_VERSION_NUMBER;
- major = includeVersion / 1000000;
- includeVersion %= 1000000;
- minor = includeVersion / 1000;
- rel = includeVersion % 1000;
- vshPrint(ctl, _("Compiled against library: libvir %d.%d.%d\n"),
- major, minor, rel);
+ if (!(pool = vshCommandOptPool(ctl, cmd, "pool", NULL)))
+ goto cleanup;
- ret = virGetVersion(&libVersion, hvType, &apiVersion);
- if (ret < 0) {
- vshError(ctl, "%s", _("failed to get the library version"));
- return false;
+ if (vshCommandOptString(cmd, "file", &from) <= 0) {
+ goto cleanup;
}
- major = libVersion / 1000000;
- libVersion %= 1000000;
- minor = libVersion / 1000;
- rel = libVersion % 1000;
- vshPrint(ctl, _("Using library: libvir %d.%d.%d\n"),
- major, minor, rel);
- major = apiVersion / 1000000;
- apiVersion %= 1000000;
- minor = apiVersion / 1000;
- rel = apiVersion % 1000;
- vshPrint(ctl, _("Using API: %s %d.%d.%d\n"), hvType,
- major, minor, rel);
+ if (!(inputvol = vshCommandOptVol(ctl, cmd, "vol", "inputpool", NULL)))
+ goto cleanup;
- ret = virConnectGetVersion(ctl->conn, &hvVersion);
- if (ret < 0) {
- vshError(ctl, "%s", _("failed to get the hypervisor version"));
- return false;
+ if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
+ virshReportError(ctl);
+ goto cleanup;
}
- if (hvVersion == 0) {
- vshPrint(ctl,
- _("Cannot extract running %s hypervisor version\n"), hvType);
- } else {
- major = hvVersion / 1000000;
- hvVersion %= 1000000;
- minor = hvVersion / 1000;
- rel = hvVersion % 1000;
- vshPrint(ctl, _("Running hypervisor: %s %d.%d.%d\n"),
- hvType, major, minor, rel);
- }
+ newvol = virStorageVolCreateXMLFrom(pool, buffer, inputvol, 0);
- if (vshCommandOptBool(cmd, "daemon")) {
- ret = virConnectGetLibVersion(ctl->conn, &daemonVersion);
- if (ret < 0) {
- vshError(ctl, "%s", _("failed to get the daemon version"));
- } else {
- major = daemonVersion / 1000000;
- daemonVersion %= 1000000;
- minor = daemonVersion / 1000;
- rel = daemonVersion % 1000;
- vshPrint(ctl, _("Running against daemon: %d.%d.%d\n"),
- major, minor, rel);
- }
+ if (newvol != NULL) {
+ vshPrint(ctl, _("Vol %s created from input vol %s\n"),
+ virStorageVolGetName(newvol), virStorageVolGetName(inputvol));
+ } else {
+ vshError(ctl, _("Failed to create vol from %s"), from);
+ goto cleanup;
}
- return true;
+ ret = true;
+cleanup:
+ VIR_FREE(buffer);
+ if (pool)
+ virStoragePoolFree(pool);
+ if (inputvol)
+ virStorageVolFree(inputvol);
+ if (newvol)
+ virStorageVolFree(newvol);
+ return ret;
}
-/* Tree listing helpers. */
+static xmlChar *
+makeCloneXML(const char *origxml, const char *newname)
+{
-/* Given an index, return either the name of that device (non-NULL) or
- * of its parent (NULL if a root). */
-typedef const char * (*vshTreeLookup)(int devid, bool parent, void *opaque);
+ xmlDocPtr doc = NULL;
+ xmlXPathContextPtr ctxt = NULL;
+ xmlXPathObjectPtr obj = NULL;
+ xmlChar *newxml = NULL;
+ int size;
-static int
-vshTreePrintInternal(vshControl *ctl,
- vshTreeLookup lookup,
- void *opaque,
- int num_devices,
- int devid,
- int lastdev,
- bool root,
- virBufferPtr indent)
+ doc = virXMLParseStringCtxt(origxml, _("(volume_definition)"), &ctxt);
+ if (!doc)
+ goto cleanup;
+
+ obj = xmlXPathEval(BAD_CAST "/volume/name", ctxt);
+ if (obj == NULL || obj->nodesetval == NULL ||
+ obj->nodesetval->nodeTab == NULL)
+ goto cleanup;
+
+ xmlNodeSetContent(obj->nodesetval->nodeTab[0], (const xmlChar *)newname);
+ xmlDocDumpMemory(doc, &newxml, &size);
+
+cleanup:
+ xmlXPathFreeObject(obj);
+ xmlXPathFreeContext(ctxt);
+ xmlFreeDoc(doc);
+ return newxml;
+}
+
+/*
+ * "vol-clone" command
+ */
+static const vshCmdInfo info_vol_clone[] = {
+ {"help", N_("clone a volume.")},
+ {"desc", N_("Clone an existing volume.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_vol_clone[] = {
+ {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("orig vol name or key")},
+ {"newname", VSH_OT_DATA, VSH_OFLAG_REQ, N_("clone name")},
+ {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdVolClone(vshControl *ctl, const vshCmd *cmd)
{
- int i;
- int nextlastdev = -1;
- int ret = -1;
- const char *dev = (lookup)(devid, false, opaque);
+ virStoragePoolPtr origpool = NULL;
+ virStorageVolPtr origvol = NULL, newvol = NULL;
+ const char *name = NULL;
+ char *origxml = NULL;
+ xmlChar *newxml = NULL;
+ bool ret = false;
- if (virBufferError(indent))
+ if (!vshConnectionUsability(ctl, ctl->conn))
goto cleanup;
- /* Print this device, with indent if not at root */
- vshPrint(ctl, "%s%s%s\n", virBufferCurrentContent(indent),
- root ? "" : "+- ", dev);
+ if (!(origvol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL)))
+ goto cleanup;
- /* Update indent to show '|' or ' ' for child devices */
- if (!root) {
- virBufferAddChar(indent, devid == lastdev ? ' ' : '|');
- virBufferAddChar(indent, ' ');
- if (virBufferError(indent))
- goto cleanup;
+ origpool = virStoragePoolLookupByVolume(origvol);
+ if (!origpool) {
+ vshError(ctl, "%s", _("failed to get parent pool"));
+ goto cleanup;
}
- /* Determine the index of the last child device */
- for (i = 0 ; i < num_devices ; i++) {
- const char *parent = (lookup)(i, true, opaque);
+ if (vshCommandOptString(cmd, "newname", &name) <= 0)
+ goto cleanup;
- if (parent && STREQ(parent, dev))
- nextlastdev = i;
- }
+ origxml = virStorageVolGetXMLDesc(origvol, 0);
+ if (!origxml)
+ goto cleanup;
- /* If there is a child device, then print another blank line */
- if (nextlastdev != -1)
- vshPrint(ctl, "%s |\n", virBufferCurrentContent(indent));
+ newxml = makeCloneXML(origxml, name);
+ if (!newxml) {
+ vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
+ goto cleanup;
+ }
- /* Finally print all children */
- virBufferAddLit(indent, " ");
- for (i = 0 ; i < num_devices ; i++) {
- const char *parent = (lookup)(i, true, opaque);
+ newvol = virStorageVolCreateXMLFrom(origpool, (char *) newxml, origvol, 0);
- if (parent && STREQ(parent, dev) &&
- vshTreePrintInternal(ctl, lookup, opaque,
- num_devices, i, nextlastdev,
- false, indent) < 0)
- goto cleanup;
+ if (newvol != NULL) {
+ vshPrint(ctl, _("Vol %s cloned from %s\n"),
+ virStorageVolGetName(newvol), virStorageVolGetName(origvol));
+ } else {
+ vshError(ctl, _("Failed to clone vol from %s"),
+ virStorageVolGetName(origvol));
+ goto cleanup;
}
- virBufferTrim(indent, " ", -1);
- /* If there was no child device, and we're the last in
- * a list of devices, then print another blank line */
- if (nextlastdev == -1 && devid == lastdev)
- vshPrint(ctl, "%s\n", virBufferCurrentContent(indent));
+ ret = true;
- if (!root)
- virBufferTrim(indent, NULL, 2);
- ret = 0;
cleanup:
+ VIR_FREE(origxml);
+ xmlFree(newxml);
+ if (origvol)
+ virStorageVolFree(origvol);
+ if (newvol)
+ virStorageVolFree(newvol);
+ if (origpool)
+ virStoragePoolFree(origpool);
return ret;
}
-static int
-vshTreePrint(vshControl *ctl, vshTreeLookup lookup, void *opaque,
- int num_devices, int devid)
-{
- int ret;
- virBuffer indent = VIR_BUFFER_INITIALIZER;
-
- ret = vshTreePrintInternal(ctl, lookup, opaque, num_devices,
- devid, devid, true, &indent);
- if (ret < 0)
- vshError(ctl, "%s", _("Failed to complete tree listing"));
- virBufferFreeAndReset(&indent);
- return ret;
-}
-
-struct vshNodeList {
- char **names;
- char **parents;
-};
-
-static const char *
-vshNodeListLookup(int devid, bool parent, void *opaque)
-{
- struct vshNodeList *arrays = opaque;
- if (parent)
- return arrays->parents[devid];
- return arrays->names[devid];
-}
/*
- * "nodedev-list" command
+ * "vol-upload" command
*/
-static const vshCmdInfo info_node_list_devices[] = {
- {"help", N_("enumerate devices on this host")},
- {"desc", ""},
+static const vshCmdInfo info_vol_upload[] = {
+ {"help", N_("upload a file into a volume")},
+ {"desc", N_("Upload a file into a volume")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_node_list_devices[] = {
- {"tree", VSH_OT_BOOL, 0, N_("list devices in a tree")},
- {"cap", VSH_OT_STRING, VSH_OFLAG_NONE, N_("capability name")},
+static const vshCmdOptDef opts_vol_upload[] = {
+ {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
+ {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file")},
+ {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
+ {"offset", VSH_OT_INT, 0, N_("volume offset to upload to") },
+ {"length", VSH_OT_INT, 0, N_("amount of data to upload") },
{NULL, 0, 0, NULL}
};
+static int
+cmdVolUploadSource(virStreamPtr st ATTRIBUTE_UNUSED,
+ char *bytes, size_t nbytes, void *opaque)
+{
+ int *fd = opaque;
+
+ return saferead(*fd, bytes, nbytes);
+}
+
static bool
-cmdNodeListDevices(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
+cmdVolUpload(vshControl *ctl, const vshCmd *cmd)
{
- const char *cap = NULL;
- char **devices;
- int num_devices, i;
- bool tree = vshCommandOptBool(cmd, "tree");
- bool ret = true;
+ const char *file = NULL;
+ virStorageVolPtr vol = NULL;
+ bool ret = false;
+ int fd = -1;
+ virStreamPtr st = NULL;
+ const char *name = NULL;
+ unsigned long long offset = 0, length = 0;
if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
+ goto cleanup;
- if (vshCommandOptString(cmd, "cap", &cap) <= 0)
- cap = NULL;
+ if (vshCommandOptULongLong(cmd, "offset", &offset) < 0) {
+ vshError(ctl, _("Unable to parse integer"));
+ return false;
+ }
- num_devices = virNodeNumOfDevices(ctl->conn, cap, 0);
- if (num_devices < 0) {
- vshError(ctl, "%s", _("Failed to count node devices"));
+ if (vshCommandOptULongLong(cmd, "length", &length) < 0) {
+ vshError(ctl, _("Unable to parse integer"));
return false;
- } else if (num_devices == 0) {
- return true;
}
- devices = vshMalloc(ctl, sizeof(char *) * num_devices);
- num_devices =
- virNodeListDevices(ctl->conn, cap, devices, num_devices, 0);
- if (num_devices < 0) {
- vshError(ctl, "%s", _("Failed to list node devices"));
- VIR_FREE(devices);
+ if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", &name))) {
return false;
}
- qsort(&devices[0], num_devices, sizeof(char*), vshNameSorter);
- if (tree) {
- char **parents = vshMalloc(ctl, sizeof(char *) * num_devices);
- struct vshNodeList arrays = { devices, parents };
- for (i = 0; i < num_devices; i++) {
- virNodeDevicePtr dev = virNodeDeviceLookupByName(ctl->conn, devices[i]);
- if (dev && STRNEQ(devices[i], "computer")) {
- const char *parent = virNodeDeviceGetParent(dev);
- parents[i] = parent ? vshStrdup(ctl, parent) : NULL;
- } else {
- parents[i] = NULL;
- }
- virNodeDeviceFree(dev);
- }
- for (i = 0 ; i < num_devices ; i++) {
- if (parents[i] == NULL &&
- vshTreePrint(ctl, vshNodeListLookup, &arrays, num_devices,
- i) < 0)
- ret = false;
- }
- for (i = 0 ; i < num_devices ; i++) {
- VIR_FREE(devices[i]);
- VIR_FREE(parents[i]);
- }
- VIR_FREE(parents);
- } else {
- for (i = 0; i < num_devices; i++) {
- vshPrint(ctl, "%s\n", devices[i]);
- VIR_FREE(devices[i]);
- }
+ if (vshCommandOptString(cmd, "file", &file) < 0) {
+ vshError(ctl, _("file must not be empty"));
+ goto cleanup;
}
- VIR_FREE(devices);
+
+ if ((fd = open(file, O_RDONLY)) < 0) {
+ vshError(ctl, _("cannot read %s"), file);
+ goto cleanup;
+ }
+
+ st = virStreamNew(ctl->conn, 0);
+ if (virStorageVolUpload(vol, st, offset, length, 0) < 0) {
+ vshError(ctl, _("cannot upload to volume %s"), name);
+ goto cleanup;
+ }
+
+ if (virStreamSendAll(st, cmdVolUploadSource, &fd) < 0) {
+ vshError(ctl, _("cannot send data to volume %s"), name);
+ goto cleanup;
+ }
+
+ if (VIR_CLOSE(fd) < 0) {
+ vshError(ctl, _("cannot close file %s"), file);
+ virStreamAbort(st);
+ goto cleanup;
+ }
+
+ if (virStreamFinish(st) < 0) {
+ vshError(ctl, _("cannot close volume %s"), name);
+ goto cleanup;
+ }
+
+ ret = true;
+
+cleanup:
+ if (vol)
+ virStorageVolFree(vol);
+ if (st)
+ virStreamFree(st);
+ VIR_FORCE_CLOSE(fd);
return ret;
}
+
+
/*
- * "nodedev-dumpxml" command
- */
-static const vshCmdInfo info_node_device_dumpxml[] = {
- {"help", N_("node device details in XML")},
- {"desc", N_("Output the node device details as an XML dump to stdout.")},
+ * "vol-download" command
+ */
+static const vshCmdInfo info_vol_download[] = {
+ {"help", N_("Download a volume to a file")},
+ {"desc", N_("Download a volume to a file")},
{NULL, NULL}
};
-
-static const vshCmdOptDef opts_node_device_dumpxml[] = {
- {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("device key")},
+static const vshCmdOptDef opts_vol_download[] = {
+ {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
+ {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file")},
+ {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
+ {"offset", VSH_OT_INT, 0, N_("volume offset to download from") },
+ {"length", VSH_OT_INT, 0, N_("amount of data to download") },
{NULL, 0, 0, NULL}
};
static bool
-cmdNodeDeviceDumpXML(vshControl *ctl, const vshCmd *cmd)
+cmdVolDownload(vshControl *ctl, const vshCmd *cmd)
{
+ const char *file = NULL;
+ virStorageVolPtr vol = NULL;
+ bool ret = false;
+ int fd = -1;
+ virStreamPtr st = NULL;
const char *name = NULL;
- virNodeDevicePtr device;
- char *xml;
+ unsigned long long offset = 0, length = 0;
+ bool created = false;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (vshCommandOptString(cmd, "device", &name) <= 0)
+
+ if (vshCommandOptULongLong(cmd, "offset", &offset) < 0) {
+ vshError(ctl, _("Unable to parse integer"));
return false;
- if (!(device = virNodeDeviceLookupByName(ctl->conn, name))) {
- vshError(ctl, "%s '%s'", _("Could not find matching device"), name);
+ }
+
+ if (vshCommandOptULongLong(cmd, "length", &length) < 0) {
+ vshError(ctl, _("Unable to parse integer"));
return false;
}
- xml = virNodeDeviceGetXMLDesc(device, 0);
- if (!xml) {
- virNodeDeviceFree(device);
+ if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", &name)))
return false;
+
+ if (vshCommandOptString(cmd, "file", &file) < 0) {
+ vshError(ctl, _("file must not be empty"));
+ goto cleanup;
}
- vshPrint(ctl, "%s\n", xml);
- VIR_FREE(xml);
- virNodeDeviceFree(device);
- return true;
+ if ((fd = open(file, O_WRONLY|O_CREAT|O_EXCL, 0666)) < 0) {
+ if (errno != EEXIST ||
+ (fd = open(file, O_WRONLY|O_TRUNC, 0666)) < 0) {
+ vshError(ctl, _("cannot create %s"), file);
+ goto cleanup;
+ }
+ } else {
+ created = true;
+ }
+
+ st = virStreamNew(ctl->conn, 0);
+ if (virStorageVolDownload(vol, st, offset, length, 0) < 0) {
+ vshError(ctl, _("cannot download from volume %s"), name);
+ goto cleanup;
+ }
+
+ if (virStreamRecvAll(st, vshStreamSink, &fd) < 0) {
+ vshError(ctl, _("cannot receive data from volume %s"), name);
+ goto cleanup;
+ }
+
+ if (VIR_CLOSE(fd) < 0) {
+ vshError(ctl, _("cannot close file %s"), file);
+ virStreamAbort(st);
+ goto cleanup;
+ }
+
+ if (virStreamFinish(st) < 0) {
+ vshError(ctl, _("cannot close volume %s"), name);
+ goto cleanup;
+ }
+
+ ret = true;
+
+cleanup:
+ VIR_FORCE_CLOSE(fd);
+ if (!ret && created)
+ unlink(file);
+ if (vol)
+ virStorageVolFree(vol);
+ if (st)
+ virStreamFree(st);
+ return ret;
}
+
/*
- * "nodedev-detach" command
+ * "vol-delete" command
*/
-static const vshCmdInfo info_node_device_detach[] = {
- {"help", N_("detach node device from its device driver")},
- {"desc", N_("Detach node device from its device driver before assigning to a domain.")},
+static const vshCmdInfo info_vol_delete[] = {
+ {"help", N_("delete a vol")},
+ {"desc", N_("Delete a given vol.")},
{NULL, NULL}
};
-
-static const vshCmdOptDef opts_node_device_detach[] = {
- {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("device key")},
+static const vshCmdOptDef opts_vol_delete[] = {
+ {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
+ {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
{NULL, 0, 0, NULL}
};
static bool
-cmdNodeDeviceDetach(vshControl *ctl, const vshCmd *cmd)
+cmdVolDelete(vshControl *ctl, const vshCmd *cmd)
{
- const char *name = NULL;
- virNodeDevicePtr device;
+ virStorageVolPtr vol;
bool ret = true;
+ const char *name;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (vshCommandOptString(cmd, "device", &name) <= 0)
- return false;
- if (!(device = virNodeDeviceLookupByName(ctl->conn, name))) {
- vshError(ctl, "%s '%s'", _("Could not find matching device"), name);
+
+ if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", &name))) {
return false;
}
- /* Yes, our public API is misspelled. At least virsh can accept
- * either spelling. */
- if (virNodeDeviceDettach(device) == 0) {
- vshPrint(ctl, _("Device %s detached\n"), name);
+ if (virStorageVolDelete(vol, 0) == 0) {
+ vshPrint(ctl, _("Vol %s deleted\n"), name);
} else {
- vshError(ctl, _("Failed to detach device %s"), name);
+ vshError(ctl, _("Failed to delete vol %s"), name);
ret = false;
}
- virNodeDeviceFree(device);
+
+ virStorageVolFree(vol);
return ret;
}
+
/*
- * "nodedev-reattach" command
+ * "vol-wipe" command
*/
-static const vshCmdInfo info_node_device_reattach[] = {
- {"help", N_("reattach node device to its device driver")},
- {"desc", N_("Reattach node device to its device driver once released by the domain.")},
+static const vshCmdInfo info_vol_wipe[] = {
+ {"help", N_("wipe a vol")},
+ {"desc", N_("Ensure data previously on a volume is not accessible to future reads")},
{NULL, NULL}
};
-
-static const vshCmdOptDef opts_node_device_reattach[] = {
- {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("device key")},
+static const vshCmdOptDef opts_vol_wipe[] = {
+ {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
+ {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
+ {"algorithm", VSH_OT_STRING, 0, N_("perform selected wiping algorithm")},
{NULL, 0, 0, NULL}
};
+VIR_ENUM_DECL(virStorageVolWipeAlgorithm)
+VIR_ENUM_IMPL(virStorageVolWipeAlgorithm, VIR_STORAGE_VOL_WIPE_ALG_LAST,
+ "zero", "nnsa", "dod", "bsi", "gutmann", "schneier",
+ "pfitzner7", "pfitzner33", "random");
+
static bool
-cmdNodeDeviceReAttach(vshControl *ctl, const vshCmd *cmd)
+cmdVolWipe(vshControl *ctl, const vshCmd *cmd)
{
- const char *name = NULL;
- virNodeDevicePtr device;
- bool ret = true;
+ virStorageVolPtr vol;
+ bool ret = false;
+ const char *name;
+ const char *algorithm_str = NULL;
+ int algorithm = VIR_STORAGE_VOL_WIPE_ALG_ZERO;
+ int funcRet;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (vshCommandOptString(cmd, "device", &name) <= 0)
- return false;
- if (!(device = virNodeDeviceLookupByName(ctl->conn, name))) {
- vshError(ctl, "%s '%s'", _("Could not find matching device"), name);
+
+ if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", &name))) {
return false;
}
- if (virNodeDeviceReAttach(device) == 0) {
- vshPrint(ctl, _("Device %s re-attached\n"), name);
- } else {
- vshError(ctl, _("Failed to re-attach device %s"), name);
- ret = false;
+ if (vshCommandOptString(cmd, "algorithm", &algorithm_str) < 0) {
+ vshError(ctl, "%s", _("missing argument"));
+ goto out;
}
- virNodeDeviceFree(device);
+
+ if (algorithm_str &&
+ (algorithm = virStorageVolWipeAlgorithmTypeFromString(algorithm_str)) < 0) {
+ vshError(ctl, _("Unsupported algorithm '%s'"), algorithm_str);
+ goto out;
+ }
+
+ if ((funcRet = virStorageVolWipePattern(vol, algorithm, 0)) < 0) {
+ if (last_error->code == VIR_ERR_NO_SUPPORT &&
+ algorithm == VIR_STORAGE_VOL_WIPE_ALG_ZERO)
+ funcRet = virStorageVolWipe(vol, 0);
+ }
+
+ if (funcRet < 0) {
+ vshError(ctl, _("Failed to wipe vol %s"), name);
+ goto out;
+ }
+
+ vshPrint(ctl, _("Vol %s wiped\n"), name);
+ ret = true;
+out:
+ virStorageVolFree(vol);
return ret;
}
+
/*
- * "nodedev-reset" command
+ * "vol-info" command
*/
-static const vshCmdInfo info_node_device_reset[] = {
- {"help", N_("reset node device")},
- {"desc", N_("Reset node device before or after assigning to a domain.")},
+static const vshCmdInfo info_vol_info[] = {
+ {"help", N_("storage vol information")},
+ {"desc", N_("Returns basic information about the storage vol.")},
{NULL, NULL}
};
-
-static const vshCmdOptDef opts_node_device_reset[] = {
- {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("device key")},
+static const vshCmdOptDef opts_vol_info[] = {
+ {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
+ {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
{NULL, 0, 0, NULL}
};
static bool
-cmdNodeDeviceReset(vshControl *ctl, const vshCmd *cmd)
+cmdVolInfo(vshControl *ctl, const vshCmd *cmd)
{
- const char *name = NULL;
- virNodeDevicePtr device;
+ virStorageVolInfo info;
+ virStorageVolPtr vol;
bool ret = true;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (vshCommandOptString(cmd, "device", &name) <= 0)
- return false;
- if (!(device = virNodeDeviceLookupByName(ctl->conn, name))) {
- vshError(ctl, "%s '%s'", _("Could not find matching device"), name);
+
+ if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL)))
return false;
- }
- if (virNodeDeviceReset(device) == 0) {
- vshPrint(ctl, _("Device %s reset\n"), name);
+ vshPrint(ctl, "%-15s %s\n", _("Name:"), virStorageVolGetName(vol));
+
+ if (virStorageVolGetInfo(vol, &info) == 0) {
+ double val;
+ const char *unit;
+ switch(info.type) {
+ case VIR_STORAGE_VOL_FILE:
+ vshPrint(ctl, "%-15s %s\n", _("Type:"), _("file"));
+ break;
+
+ case VIR_STORAGE_VOL_BLOCK:
+ vshPrint(ctl, "%-15s %s\n", _("Type:"), _("block"));
+ break;
+
+ case VIR_STORAGE_VOL_DIR:
+ vshPrint(ctl, "%-15s %s\n", _("Type:"), _("dir"));
+ break;
+
+ case VIR_STORAGE_VOL_NETWORK:
+ vshPrint(ctl, "%-15s %s\n", _("Type:"), _("network"));
+ break;
+
+ default:
+ vshPrint(ctl, "%-15s %s\n", _("Type:"), _("unknown"));
+ }
+
+ val = prettyCapacity(info.capacity, &unit);
+ vshPrint(ctl, "%-15s %2.2lf %s\n", _("Capacity:"), val, unit);
+
+ val = prettyCapacity(info.allocation, &unit);
+ vshPrint(ctl, "%-15s %2.2lf %s\n", _("Allocation:"), val, unit);
} else {
- vshError(ctl, _("Failed to reset device %s"), name);
ret = false;
}
- virNodeDeviceFree(device);
+
+ virStorageVolFree(vol);
return ret;
}
/*
- * "hostname" command
+ * "vol-resize" command
*/
-static const vshCmdInfo info_hostname[] = {
- {"help", N_("print the hypervisor hostname")},
- {"desc", ""},
+static const vshCmdInfo info_vol_resize[] = {
+ {"help", N_("resize a vol")},
+ {"desc", N_("Resizes a storage volume.")},
{NULL, NULL}
};
-static bool
-cmdHostname(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
-{
- char *hostname;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
-
- hostname = virConnectGetHostname(ctl->conn);
- if (hostname == NULL) {
- vshError(ctl, "%s", _("failed to get hostname"));
- return false;
- }
-
- vshPrint (ctl, "%s\n", hostname);
- VIR_FREE(hostname);
-
- return true;
-}
-
-/*
- * "uri" command
- */
-static const vshCmdInfo info_uri[] = {
- {"help", N_("print the hypervisor canonical URI")},
- {"desc", ""},
- {NULL, NULL}
+static const vshCmdOptDef opts_vol_resize[] = {
+ {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
+ {"capacity", VSH_OT_DATA, VSH_OFLAG_REQ,
+ N_("new capacity for the vol, as scaled integer (default bytes)")},
+ {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
+ {"allocate", VSH_OT_BOOL, 0,
+ N_("allocate the new capacity, rather than leaving it sparse")},
+ {"delta", VSH_OT_BOOL, 0,
+ N_("use capacity as a delta to current size, rather than the new size")},
+ {"shrink", VSH_OT_BOOL, 0, N_("allow the resize to shrink the volume")},
+ {NULL, 0, 0, NULL}
};
static bool
-cmdURI(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
+cmdVolResize(vshControl *ctl, const vshCmd *cmd)
{
- char *uri;
+ virStorageVolPtr vol;
+ const char *capacityStr = NULL;
+ unsigned long long capacity = 0;
+ unsigned int flags = 0;
+ bool ret = false;
+ bool delta = false;
+
+ if (vshCommandOptBool(cmd, "allocate"))
+ flags |= VIR_STORAGE_VOL_RESIZE_ALLOCATE;
+ if (vshCommandOptBool(cmd, "delta")) {
+ delta = true;
+ flags |= VIR_STORAGE_VOL_RESIZE_DELTA;
+ }
+ if (vshCommandOptBool(cmd, "shrink"))
+ flags |= VIR_STORAGE_VOL_RESIZE_SHRINK;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- uri = virConnectGetURI(ctl->conn);
- if (uri == NULL) {
- vshError(ctl, "%s", _("failed to get URI"));
+ if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL)))
return false;
+
+ if (vshCommandOptString(cmd, "capacity", &capacityStr) <= 0)
+ goto cleanup;
+ virSkipSpaces(&capacityStr);
+ if (*capacityStr == '-') {
+ /* The API always requires a positive value; but we allow a
+ * negative value for convenience. */
+ if (delta && vshCommandOptBool(cmd, "shrink")){
+ capacityStr++;
+ } else {
+ vshError(ctl, "%s",
+ _("negative size requires --delta and --shrink"));
+ goto cleanup;
+ }
+ }
+ if (vshVolSize(capacityStr, &capacity) < 0) {
+ vshError(ctl, _("Malformed size %s"), capacityStr);
+ goto cleanup;
}
- vshPrint(ctl, "%s\n", uri);
- VIR_FREE(uri);
+ if (virStorageVolResize(vol, capacity, flags) == 0) {
+ vshPrint(ctl,
+ delta ? _("Size of volume '%s' successfully changed by %s\n")
+ : _("Size of volume '%s' successfully changed to %s\n"),
+ virStorageVolGetName(vol), capacityStr);
+ ret = true;
+ } else {
+ vshError(ctl,
+ delta ? _("Failed to change size of volume '%s' by %s\n")
+ : _("Failed to change size of volume '%s' to %s\n"),
+ virStorageVolGetName(vol), capacityStr);
+ ret = false;
+ }
- return true;
+cleanup:
+ virStorageVolFree(vol);
+ return ret;
}
+
/*
- * "sysinfo" command
+ * "vol-dumpxml" command
*/
-static const vshCmdInfo info_sysinfo[] = {
- {"help", N_("print the hypervisor sysinfo")},
- {"desc",
- N_("output an XML string for the hypervisor sysinfo, if available")},
+static const vshCmdInfo info_vol_dumpxml[] = {
+ {"help", N_("vol information in XML")},
+ {"desc", N_("Output the vol information as an XML dump to stdout.")},
{NULL, NULL}
};
+static const vshCmdOptDef opts_vol_dumpxml[] = {
+ {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")},
+ {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
+ {NULL, 0, 0, NULL}
+};
+
static bool
-cmdSysinfo(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
+cmdVolDumpXML(vshControl *ctl, const vshCmd *cmd)
{
- char *sysinfo;
+ virStorageVolPtr vol;
+ bool ret = true;
+ char *dump;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- sysinfo = virConnectGetSysinfo(ctl->conn, 0);
- if (sysinfo == NULL) {
- vshError(ctl, "%s", _("failed to get sysinfo"));
+ if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL)))
return false;
- }
- vshPrint(ctl, "%s", sysinfo);
- VIR_FREE(sysinfo);
+ dump = virStorageVolGetXMLDesc(vol, 0);
+ if (dump != NULL) {
+ vshPrint(ctl, "%s", dump);
+ VIR_FREE(dump);
+ } else {
+ ret = false;
+ }
- return true;
+ virStorageVolFree(vol);
+ return ret;
}
+
/*
- * "domdisplay" command
+ * "vol-list" command
*/
-static const vshCmdInfo info_domdisplay[] = {
- {"help", N_("domain display connection URI")},
- {"desc", N_("Output the IP address and port number for the graphical display.")},
+static const vshCmdInfo info_vol_list[] = {
+ {"help", N_("list vols")},
+ {"desc", N_("Returns list of vols by pool.")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_domdisplay[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {"include-password", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("includes the password into the connection URI if available")},
+static const vshCmdOptDef opts_vol_list[] = {
+ {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")},
+ {"details", VSH_OT_BOOL, 0, N_("display extended details for volumes")},
{NULL, 0, 0, NULL}
};
static bool
-cmdDomDisplay(vshControl *ctl, const vshCmd *cmd)
+cmdVolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
- xmlDocPtr xml = NULL;
- xmlXPathContextPtr ctxt = NULL;
- virDomainPtr dom;
- virBuffer buf = VIR_BUFFER_INITIALIZER;
- bool ret = false;
- char *doc;
- char *xpath;
- char *listen_addr;
- int port, tls_port = 0;
- char *passwd = NULL;
- char *output = NULL;
- const char *scheme[] = { "vnc", "spice", "rdp", NULL };
- int iter = 0;
- int tmp;
- int flags = 0;
+ virStorageVolInfo volumeInfo;
+ virStoragePoolPtr pool;
+ char **activeNames = NULL;
+ char *outputStr = NULL;
+ const char *unit;
+ double val;
+ bool details = vshCommandOptBool(cmd, "details");
+ int numVolumes = 0, i;
+ int ret;
+ bool functionReturn;
+ int stringLength = 0;
+ size_t allocStrLength = 0, capStrLength = 0;
+ size_t nameStrLength = 0, pathStrLength = 0;
+ size_t typeStrLength = 0;
+ struct volInfoText {
+ char *allocation;
+ char *capacity;
+ char *path;
+ char *type;
+ };
+ struct volInfoText *volInfoTexts = NULL;
+ /* Check the connection to libvirtd daemon is still working */
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ /* Look up the pool information given to us by the user */
+ if (!(pool = vshCommandOptPool(ctl, cmd, "pool", NULL)))
return false;
- if (!virDomainIsActive(dom)) {
- vshError(ctl, _("Domain is not running"));
- goto cleanup;
+ /* Determine the number of volumes in the pool */
+ numVolumes = virStoragePoolNumOfVolumes(pool);
+
+ if (numVolumes < 0) {
+ vshError(ctl, "%s", _("Failed to list storage volumes"));
+ virStoragePoolFree(pool);
+ return false;
}
- if (vshCommandOptBool(cmd, "include-password"))
- flags |= VIR_DOMAIN_XML_SECURE;
+ /* Retrieve the list of volume names in the pool */
+ if (numVolumes > 0) {
+ activeNames = vshCalloc(ctl, numVolumes, sizeof(*activeNames));
+ if ((numVolumes = virStoragePoolListVolumes(pool, activeNames,
+ numVolumes)) < 0) {
+ vshError(ctl, "%s", _("Failed to list active vols"));
+ VIR_FREE(activeNames);
+ virStoragePoolFree(pool);
+ return false;
+ }
- doc = virDomainGetXMLDesc(dom, flags);
+ /* Sort the volume names */
+ qsort(&activeNames[0], numVolumes, sizeof(*activeNames), vshNameSorter);
- if (!doc)
- goto cleanup;
+ /* Set aside memory for volume information pointers */
+ volInfoTexts = vshCalloc(ctl, numVolumes, sizeof(*volInfoTexts));
+ }
- xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt);
- VIR_FREE(doc);
- if (!xml)
- goto cleanup;
+ /* Collect the rest of the volume information for display */
+ for (i = 0; i < numVolumes; i++) {
+ /* Retrieve volume info */
+ virStorageVolPtr vol = virStorageVolLookupByName(pool,
+ activeNames[i]);
- /* Attempt to grab our display info */
- for (iter = 0; scheme[iter] != NULL; iter++) {
- /* Create our XPATH lookup for the current display's port */
- virAsprintf(&xpath, "string(/domain/devices/graphics[@type='%s']"
- "/@port)", scheme[iter]);
- if (!xpath) {
- virReportOOMError();
- goto cleanup;
+ /* Retrieve the volume path */
+ if ((volInfoTexts[i].path = virStorageVolGetPath(vol)) == NULL) {
+ /* Something went wrong retrieving a volume path, cope with it */
+ volInfoTexts[i].path = vshStrdup(ctl, _("unknown"));
}
- /* Attempt to get the port number for the current graphics scheme */
- tmp = virXPathInt(xpath, ctxt, &port);
- VIR_FREE(xpath);
-
- /* If there is no port number for this type, then jump to the next
- * scheme
- */
- if (tmp)
- continue;
-
- /* Create our XPATH lookup for the current display's address */
- virAsprintf(&xpath, "string(/domain/devices/graphics[@type='%s']"
- "/@listen)", scheme[iter]);
- if (!xpath) {
- virReportOOMError();
- goto cleanup;
- }
+ /* If requested, retrieve volume type and sizing information */
+ if (details) {
+ if (virStorageVolGetInfo(vol, &volumeInfo) != 0) {
+ /* Something went wrong retrieving volume info, cope with it */
+ volInfoTexts[i].allocation = vshStrdup(ctl, _("unknown"));
+ volInfoTexts[i].capacity = vshStrdup(ctl, _("unknown"));
+ volInfoTexts[i].type = vshStrdup(ctl, _("unknown"));
+ } else {
+ /* Convert the returned volume info into output strings */
- /* Attempt to get the listening addr if set for the current
- * graphics scheme
- */
- listen_addr = virXPathString(xpath, ctxt);
- VIR_FREE(xpath);
-
- /* Per scheme data mangling */
- if (STREQ(scheme[iter], "vnc")) {
- /* VNC protocol handlers take their port number as 'port' - 5900 */
- port -= 5900;
- } else if (STREQ(scheme[iter], "spice")) {
- /* Create our XPATH lookup for the SPICE TLS Port */
- virAsprintf(&xpath, "string(/domain/devices/graphics[@type='%s']"
- "/@tlsPort)", scheme[iter]);
- if (!xpath) {
- virReportOOMError();
- goto cleanup;
- }
+ /* Volume type */
+ switch (volumeInfo.type) {
+ case VIR_STORAGE_VOL_FILE:
+ volInfoTexts[i].type = vshStrdup(ctl, _("file"));
+ break;
+ case VIR_STORAGE_VOL_BLOCK:
+ volInfoTexts[i].type = vshStrdup(ctl, _("block"));
+ break;
+ case VIR_STORAGE_VOL_DIR:
+ volInfoTexts[i].type = vshStrdup(ctl, _("dir"));
+ break;
+ default:
+ volInfoTexts[i].type = vshStrdup(ctl, _("unknown"));
+ }
- /* Attempt to get the TLS port number for SPICE */
- tmp = virXPathInt(xpath, ctxt, &tls_port);
- VIR_FREE(xpath);
- if (tmp)
- tls_port = 0;
-
- if (vshCommandOptBool(cmd, "include-password")) {
- /* Create our XPATH lookup for the SPICE password */
- virAsprintf(&xpath, "string(/domain/devices/graphics"
- "[@type='%s']/@passwd)", scheme[iter]);
- if (!xpath) {
- virReportOOMError();
- goto cleanup;
+ /* Create the capacity output string */
+ val = prettyCapacity(volumeInfo.capacity, &unit);
+ ret = virAsprintf(&volInfoTexts[i].capacity,
+ "%.2lf %s", val, unit);
+ if (ret < 0) {
+ /* An error occurred creating the string, return */
+ goto asprintf_failure;
}
- /* Attempt to get the SPICE password */
- passwd = virXPathString(xpath, ctxt);
- VIR_FREE(xpath);
+ /* Create the allocation output string */
+ val = prettyCapacity(volumeInfo.allocation, &unit);
+ ret = virAsprintf(&volInfoTexts[i].allocation,
+ "%.2lf %s", val, unit);
+ if (ret < 0) {
+ /* An error occurred creating the string, return */
+ goto asprintf_failure;
+ }
}
- }
-
- /* Build up the full URI, starting with the scheme */
- virBufferAsprintf(&buf, "%s://", scheme[iter]);
- /* Then host name or IP */
- if (!listen_addr || STREQ((const char *)listen_addr, "0.0.0.0"))
- virBufferAddLit(&buf, "localhost");
- else
- virBufferAsprintf(&buf, "%s", listen_addr);
+ /* Remember the largest length for each output string.
+ * This lets us displaying header and volume information rows
+ * using a single, properly sized, printf style output string.
+ */
- VIR_FREE(listen_addr);
+ /* Keep the length of name string if longest so far */
+ stringLength = strlen(activeNames[i]);
+ if (stringLength > nameStrLength)
+ nameStrLength = stringLength;
- /* Add the port */
- if (STREQ(scheme[iter], "spice"))
- virBufferAsprintf(&buf, "?port=%d", port);
- else
- virBufferAsprintf(&buf, ":%d", port);
+ /* Keep the length of path string if longest so far */
+ stringLength = strlen(volInfoTexts[i].path);
+ if (stringLength > pathStrLength)
+ pathStrLength = stringLength;
- /* TLS Port */
- if (tls_port)
- virBufferAsprintf(&buf, "&tls-port=%d", tls_port);
+ /* Keep the length of type string if longest so far */
+ stringLength = strlen(volInfoTexts[i].type);
+ if (stringLength > typeStrLength)
+ typeStrLength = stringLength;
- /* Password */
- if (passwd) {
- virBufferAsprintf(&buf, "&password=%s", passwd);
- VIR_FREE(passwd);
- }
+ /* Keep the length of capacity string if longest so far */
+ stringLength = strlen(volInfoTexts[i].capacity);
+ if (stringLength > capStrLength)
+ capStrLength = stringLength;
- /* Ensure we can print our URI */
- if (virBufferError(&buf)) {
- vshPrint(ctl, "%s", _("Failed to create display URI"));
- goto cleanup;
+ /* Keep the length of allocation string if longest so far */
+ stringLength = strlen(volInfoTexts[i].allocation);
+ if (stringLength > allocStrLength)
+ allocStrLength = stringLength;
}
- /* Print out our full URI */
- output = virBufferContentAndReset(&buf);
- vshPrint(ctl, "%s", output);
- VIR_FREE(output);
-
- /* We got what we came for so return successfully */
- ret = true;
- break;
+ /* Cleanup memory allocation */
+ virStorageVolFree(vol);
}
-cleanup:
- xmlXPathFreeContext(ctxt);
- xmlFreeDoc(xml);
- virDomainFree(dom);
- return ret;
-}
-
-/*
- * "vncdisplay" command
- */
-static const vshCmdInfo info_vncdisplay[] = {
- {"help", N_("vnc display")},
- {"desc", N_("Output the IP address and port number for the VNC display.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_vncdisplay[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdVNCDisplay(vshControl *ctl, const vshCmd *cmd)
-{
- xmlDocPtr xml = NULL;
- xmlXPathContextPtr ctxt = NULL;
- virDomainPtr dom;
- bool ret = false;
- int port = 0;
- char *doc = NULL;
- char *listen_addr = NULL;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
+ /* If the --details option wasn't selected, we output the volume
+ * info using the fixed string format from previous versions to
+ * maintain backward compatibility.
+ */
- if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
- return false;
+ /* Output basic info then return if --details option not selected */
+ if (!details) {
+ /* The old output format */
+ vshPrintExtra(ctl, "%-20s %-40s\n", _("Name"), _("Path"));
+ vshPrintExtra(ctl, "-----------------------------------------\n");
+ for (i = 0; i < numVolumes; i++) {
+ vshPrint(ctl, "%-20s %-40s\n", activeNames[i],
+ volInfoTexts[i].path);
+ }
- /* Check if the domain is active and don't rely on -1 for this */
- if (!virDomainIsActive(dom)) {
- vshError(ctl, _("Domain is not running"));
+ /* Cleanup and return */
+ functionReturn = true;
goto cleanup;
}
- if (!(doc = virDomainGetXMLDesc(dom, 0)))
- goto cleanup;
+ /* We only get here if the --details option was selected. */
- if (!(xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt)))
- goto cleanup;
+ /* Use the length of name header string if it's longest */
+ stringLength = strlen(_("Name"));
+ if (stringLength > nameStrLength)
+ nameStrLength = stringLength;
- /* Get the VNC port */
- if (virXPathInt("string(/domain/devices/graphics[@type='vnc']/@port)",
- ctxt, &port)) {
- vshError(ctl, _("Failed to get VNC port. Is this domain using VNC?"));
- goto cleanup;
- }
+ /* Use the length of path header string if it's longest */
+ stringLength = strlen(_("Path"));
+ if (stringLength > pathStrLength)
+ pathStrLength = stringLength;
- listen_addr = virXPathString("string(/domain/devices/graphics"
- "[@type='vnc']/@listen)", ctxt);
- if (listen_addr == NULL || STREQ(listen_addr, "0.0.0.0"))
- vshPrint(ctl, ":%d\n", port-5900);
- else
- vshPrint(ctl, "%s:%d\n", listen_addr, port-5900);
+ /* Use the length of type header string if it's longest */
+ stringLength = strlen(_("Type"));
+ if (stringLength > typeStrLength)
+ typeStrLength = stringLength;
- ret = true;
+ /* Use the length of capacity header string if it's longest */
+ stringLength = strlen(_("Capacity"));
+ if (stringLength > capStrLength)
+ capStrLength = stringLength;
- cleanup:
- VIR_FREE(doc);
- VIR_FREE(listen_addr);
- xmlXPathFreeContext(ctxt);
- xmlFreeDoc(xml);
- virDomainFree(dom);
- return ret;
-}
+ /* Use the length of allocation header string if it's longest */
+ stringLength = strlen(_("Allocation"));
+ if (stringLength > allocStrLength)
+ allocStrLength = stringLength;
-/*
- * "ttyconsole" command
- */
-static const vshCmdInfo info_ttyconsole[] = {
- {"help", N_("tty console")},
- {"desc", N_("Output the device for the TTY console.")},
- {NULL, NULL}
-};
+ /* Display the string lengths for debugging */
+ vshDebug(ctl, VSH_ERR_DEBUG,
+ "Longest name string = %zu chars\n", nameStrLength);
+ vshDebug(ctl, VSH_ERR_DEBUG,
+ "Longest path string = %zu chars\n", pathStrLength);
+ vshDebug(ctl, VSH_ERR_DEBUG,
+ "Longest type string = %zu chars\n", typeStrLength);
+ vshDebug(ctl, VSH_ERR_DEBUG,
+ "Longest capacity string = %zu chars\n", capStrLength);
+ vshDebug(ctl, VSH_ERR_DEBUG,
+ "Longest allocation string = %zu chars\n", allocStrLength);
-static const vshCmdOptDef opts_ttyconsole[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {NULL, 0, 0, NULL}
-};
+ /* Create the output template */
+ ret = virAsprintf(&outputStr,
+ "%%-%lus %%-%lus %%-%lus %%%lus %%%lus\n",
+ (unsigned long) nameStrLength,
+ (unsigned long) pathStrLength,
+ (unsigned long) typeStrLength,
+ (unsigned long) capStrLength,
+ (unsigned long) allocStrLength);
+ if (ret < 0) {
+ /* An error occurred creating the string, return */
+ goto asprintf_failure;
+ }
-static bool
-cmdTTYConsole(vshControl *ctl, const vshCmd *cmd)
-{
- xmlDocPtr xml = NULL;
- xmlXPathObjectPtr obj = NULL;
- xmlXPathContextPtr ctxt = NULL;
- virDomainPtr dom;
- bool ret = false;
- char *doc;
+ /* Display the header */
+ vshPrint(ctl, outputStr, _("Name"), _("Path"), _("Type"),
+ ("Capacity"), _("Allocation"));
+ for (i = nameStrLength + pathStrLength + typeStrLength
+ + capStrLength + allocStrLength
+ + 8; i > 0; i--)
+ vshPrintExtra(ctl, "-");
+ vshPrintExtra(ctl, "\n");
- if (!vshConnectionUsability(ctl, ctl->conn))
- return false;
+ /* Display the volume info rows */
+ for (i = 0; i < numVolumes; i++) {
+ vshPrint(ctl, outputStr,
+ activeNames[i],
+ volInfoTexts[i].path,
+ volInfoTexts[i].type,
+ volInfoTexts[i].capacity,
+ volInfoTexts[i].allocation);
+ }
- if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
- return false;
+ /* Cleanup and return */
+ functionReturn = true;
+ goto cleanup;
- doc = virDomainGetXMLDesc(dom, 0);
- if (!doc)
- goto cleanup;
+asprintf_failure:
- xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt);
- VIR_FREE(doc);
- if (!xml)
- goto cleanup;
+ /* Display an appropriate error message then cleanup and return */
+ switch (errno) {
+ case ENOMEM:
+ /* Couldn't allocate memory */
+ vshError(ctl, "%s", _("Out of memory"));
+ break;
+ default:
+ /* Some other error */
+ vshError(ctl, _("virAsprintf failed (errno %d)"), errno);
+ }
+ functionReturn = false;
- obj = xmlXPathEval(BAD_CAST "string(/domain/devices/console/@tty)", ctxt);
- if (obj == NULL || obj->type != XPATH_STRING ||
- obj->stringval == NULL || obj->stringval[0] == 0) {
- goto cleanup;
+cleanup:
+
+ /* Safely free the memory allocated in this function */
+ for (i = 0; i < numVolumes; i++) {
+ /* Cleanup the memory for one volume info structure per loop */
+ VIR_FREE(volInfoTexts[i].path);
+ VIR_FREE(volInfoTexts[i].type);
+ VIR_FREE(volInfoTexts[i].capacity);
+ VIR_FREE(volInfoTexts[i].allocation);
+ VIR_FREE(activeNames[i]);
}
- vshPrint(ctl, "%s\n", (const char *)obj->stringval);
- ret = true;
- cleanup:
- xmlXPathFreeObject(obj);
- xmlXPathFreeContext(ctxt);
- xmlFreeDoc(xml);
- virDomainFree(dom);
- return ret;
+ /* Cleanup remaining memory */
+ VIR_FREE(outputStr);
+ VIR_FREE(volInfoTexts);
+ VIR_FREE(activeNames);
+ virStoragePoolFree(pool);
+
+ /* Return the desired value */
+ return functionReturn;
}
+
/*
- * "domhostname" command
+ * "vol-name" command
*/
-static const vshCmdInfo info_domhostname[] = {
- {"help", N_("print the domain's hostname")},
+static const vshCmdInfo info_vol_name[] = {
+ {"help", N_("returns the volume name for a given volume key or path")},
{"desc", ""},
{NULL, NULL}
};
-static const vshCmdOptDef opts_domhostname[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+static const vshCmdOptDef opts_vol_name[] = {
+ {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("volume key or path")},
{NULL, 0, 0, NULL}
};
static bool
-cmdDomHostname(vshControl *ctl, const vshCmd *cmd)
+cmdVolName(vshControl *ctl, const vshCmd *cmd)
{
- char *hostname;
- virDomainPtr dom;
- bool ret = false;
+ virStorageVolPtr vol;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
- return false;
-
- hostname = virDomainGetHostname(dom, 0);
- if (hostname == NULL) {
- vshError(ctl, "%s", _("failed to get hostname"));
- goto error;
- }
-
- vshPrint(ctl, "%s\n", hostname);
- ret = true;
+ if (!(vol = vshCommandOptVolBy(ctl, cmd, "vol", NULL, NULL,
+ VSH_BYUUID)))
+ return false;
-error:
- VIR_FREE(hostname);
- virDomainFree(dom);
- return ret;
+ vshPrint(ctl, "%s\n", virStorageVolGetName(vol));
+ virStorageVolFree(vol);
+ return true;
}
/*
- * "attach-device" command
+ * "vol-pool" command
*/
-static const vshCmdInfo info_attach_device[] = {
- {"help", N_("attach device from an XML file")},
- {"desc", N_("Attach device from an XML .")},
+static const vshCmdInfo info_vol_pool[] = {
+ {"help", N_("returns the storage pool for a given volume key or path")},
+ {"desc", ""},
{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")},
+static const vshCmdOptDef opts_vol_pool[] = {
+ {"uuid", VSH_OT_BOOL, 0, N_("return the pool uuid rather than pool name")},
+ {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("volume key or path")},
{NULL, 0, 0, NULL}
};
static bool
-cmdAttachDevice(vshControl *ctl, const vshCmd *cmd)
+cmdVolPool(vshControl *ctl, const vshCmd *cmd)
{
- virDomainPtr dom;
- const char *from = NULL;
- char *buffer;
- int ret;
- unsigned int flags;
+ virStoragePoolPtr pool;
+ virStorageVolPtr vol;
+ char uuid[VIR_UUID_STRING_BUFLEN];
+ /* Check the connection to libvirtd daemon is still working */
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
- return false;
-
- if (vshCommandOptString(cmd, "file", &from) <= 0) {
- virDomainFree(dom);
+ /* Use the supplied string to locate the volume */
+ if (!(vol = vshCommandOptVolBy(ctl, cmd, "vol", NULL, NULL,
+ VSH_BYUUID))) {
return false;
}
- if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
- virshReportError(ctl);
- virDomainFree(dom);
+ /* Look up the parent storage pool for the volume */
+ pool = virStoragePoolLookupByVolume(vol);
+ if (pool == NULL) {
+ vshError(ctl, "%s", _("failed to get parent pool"));
+ virStorageVolFree(vol);
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;
+ /* Return the requested details of the parent storage pool */
+ if (vshCommandOptBool(cmd, "uuid")) {
+ /* Retrieve and return pool UUID string */
+ if (virStoragePoolGetUUIDString(pool, &uuid[0]) == 0)
+ vshPrint(ctl, "%s\n", uuid);
} else {
- vshPrint(ctl, "%s", _("Device attached successfully\n"));
+ /* Return the storage pool name */
+ vshPrint(ctl, "%s\n", virStoragePoolGetName(pool));
}
- virDomainFree(dom);
+ /* Cleanup */
+ virStorageVolFree(vol);
+ virStoragePoolFree(pool);
return true;
}
-/**
- * Check if n1 is superset of n2, meaning n1 contains all elements and
- * attributes as n2 at least. Including children.
- * @n1 first node
- * @n2 second node
- * returns true in case n1 covers n2, false otherwise.
- */
-ATTRIBUTE_UNUSED
-static bool
-vshNodeIsSuperset(xmlNodePtr n1, xmlNodePtr n2)
-{
- xmlNodePtr child1, child2;
- xmlAttrPtr attr;
- char *prop1, *prop2;
- bool found;
- bool visited;
- bool ret = false;
- long n1_child_size, n2_child_size, n1_iter;
- virBitmapPtr bitmap;
-
- if (!n1 && !n2)
- return true;
- if (!n1 || !n2)
- return false;
+/*
+ * "vol-key" command
+ */
+static const vshCmdInfo info_vol_key[] = {
+ {"help", N_("returns the volume key for a given volume name or path")},
+ {"desc", ""},
+ {NULL, NULL}
+};
- if (!xmlStrEqual(n1->name, n2->name))
- return false;
+static const vshCmdOptDef opts_vol_key[] = {
+ {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("volume name or path")},
+ {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
+ {NULL, 0, 0, NULL}
+};
- /* Iterate over n2 attributes and check if n1 contains them*/
- attr = n2->properties;
- while (attr) {
- if (attr->type == XML_ATTRIBUTE_NODE) {
- prop1 = virXMLPropString(n1, (const char *) attr->name);
- prop2 = virXMLPropString(n2, (const char *) attr->name);
- if (STRNEQ_NULLABLE(prop1, prop2)) {
- xmlFree(prop1);
- xmlFree(prop2);
- return false;
- }
- xmlFree(prop1);
- xmlFree(prop2);
- }
- attr = attr->next;
- }
+static bool
+cmdVolKey(vshControl *ctl, const vshCmd *cmd)
+{
+ virStorageVolPtr vol;
- n1_child_size = virXMLChildElementCount(n1);
- n2_child_size = virXMLChildElementCount(n2);
- if (n1_child_size < 0 || n2_child_size < 0 ||
- n1_child_size < n2_child_size)
+ if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (n1_child_size == 0 && n2_child_size == 0)
- return true;
-
- if (!(bitmap = virBitmapAlloc(n1_child_size))) {
- virReportOOMError();
+ if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL)))
return false;
- }
- child2 = n2->children;
- while (child2) {
- if (child2->type != XML_ELEMENT_NODE) {
- child2 = child2->next;
- continue;
- }
-
- child1 = n1->children;
- n1_iter = 0;
- found = false;
- while (child1) {
- if (child1->type != XML_ELEMENT_NODE) {
- child1 = child1->next;
- continue;
- }
-
- if (virBitmapGetBit(bitmap, n1_iter, &visited) < 0) {
- vshError(NULL, "%s", _("Bad child elements counting."));
- goto cleanup;
- }
-
- if (visited) {
- child1 = child1->next;
- n1_iter++;
- continue;
- }
-
- if (xmlStrEqual(child1->name, child2->name)) {
- found = true;
- if (virBitmapSetBit(bitmap, n1_iter) < 0) {
- vshError(NULL, "%s", _("Bad child elements counting."));
- goto cleanup;
- }
-
- if (!vshNodeIsSuperset(child1, child2))
- goto cleanup;
-
- break;
- }
-
- child1 = child1->next;
- n1_iter++;
- }
-
- if (!found)
- goto cleanup;
-
- child2 = child2->next;
- }
-
- ret = true;
-
-cleanup:
- virBitmapFree(bitmap);
- return ret;
+ vshPrint(ctl, "%s\n", virStorageVolGetKey(vol));
+ virStorageVolFree(vol);
+ return true;
}
-/**
- * vshCompleteXMLFromDomain:
- * @ctl vshControl for error messages printing
- * @dom domain
- * @oldXML device XML before
- * @newXML and after completion
- *
- * For given domain and (probably incomplete) device XML specification try to
- * find such device in domain and complete missing parts. This is however
- * possible only when given device XML is sufficiently precise so it addresses
- * only one device.
- *
- * Returns -2 when no such device exists in domain, -3 when given XML selects many
- * (is too ambiguous), 0 in case of success. Otherwise returns -1. @newXML
- * is touched only in case of success.
- */
-ATTRIBUTE_UNUSED
-static int
-vshCompleteXMLFromDomain(vshControl *ctl, virDomainPtr dom, char *oldXML,
- char **newXML)
-{
- int funcRet = -1;
- char *domXML = NULL;
- xmlDocPtr domDoc = NULL, devDoc = NULL;
- xmlNodePtr node = NULL;
- xmlXPathContextPtr domCtxt = NULL, devCtxt = NULL;
- xmlNodePtr *devices = NULL;
- xmlSaveCtxtPtr sctxt = NULL;
- int devices_size;
- char *xpath = NULL;
- xmlBufferPtr buf = NULL;
- int i = 0;
- int indx = -1;
-
- if (!(domXML = virDomainGetXMLDesc(dom, 0))) {
- vshError(ctl, _("couldn't get XML description of domain %s"),
- virDomainGetName(dom));
- goto cleanup;
- }
-
- domDoc = virXMLParseStringCtxt(domXML, _("(domain_definition)"), &domCtxt);
- if (!domDoc) {
- vshError(ctl, _("Failed to parse domain definition xml"));
- goto cleanup;
- }
-
- devDoc = virXMLParseStringCtxt(oldXML, _("(device_definition)"), &devCtxt);
- if (!devDoc) {
- vshError(ctl, _("Failed to parse device definition xml"));
- goto cleanup;
- }
-
- node = xmlDocGetRootElement(devDoc);
-
- buf = xmlBufferCreate();
- if (!buf) {
- vshError(ctl, "%s", _("out of memory"));
- goto cleanup;
- }
-
- /* Get all possible devices */
- virAsprintf(&xpath, "/domain/devices/%s", node->name);
- if (!xpath) {
- virReportOOMError();
- goto cleanup;
- }
- devices_size = virXPathNodeSet(xpath, domCtxt, &devices);
-
- if (devices_size < 0) {
- /* error */
- vshError(ctl, "%s", _("error when selecting nodes"));
- goto cleanup;
- } else if (devices_size == 0) {
- /* no such device */
- funcRet = -2;
- goto cleanup;
- }
-
- /* and refine */
- for (i = 0; i < devices_size; i++) {
- if (vshNodeIsSuperset(devices[i], node)) {
- if (indx >= 0) {
- funcRet = -3; /* ambiguous */
- goto cleanup;
- }
- indx = i;
- }
- }
-
- if (indx < 0) {
- funcRet = -2; /* no such device */
- goto cleanup;
- }
-
- vshDebug(ctl, VSH_ERR_DEBUG, "Found device at pos %d\n", indx);
-
- if (newXML) {
- sctxt = xmlSaveToBuffer(buf, NULL, 0);
- if (!sctxt) {
- vshError(ctl, "%s", _("failed to create document saving context"));
- goto cleanup;
- }
-
- xmlSaveTree(sctxt, devices[indx]);
- xmlSaveClose(sctxt);
- *newXML = (char *) xmlBufferContent(buf);
- if (!*newXML) {
- virReportOOMError();
- goto cleanup;
- }
- buf->content = NULL;
- }
-
- vshDebug(ctl, VSH_ERR_DEBUG, "Old xml:\n%s\nNew xml:\n%s\n", oldXML,
- newXML ? NULLSTR(*newXML) : "(null)");
- funcRet = 0;
-
-cleanup:
- xmlBufferFree(buf);
- VIR_FREE(devices);
- xmlXPathFreeContext(devCtxt);
- xmlXPathFreeContext(domCtxt);
- xmlFreeDoc(devDoc);
- xmlFreeDoc(domDoc);
- VIR_FREE(domXML);
- VIR_FREE(xpath);
- return funcRet;
-}
/*
- * "detach-device" command
+ * "vol-path" command
*/
-static const vshCmdInfo info_detach_device[] = {
- {"help", N_("detach device from an XML file")},
- {"desc", N_("Detach device from an XML ")},
+static const vshCmdInfo info_vol_path[] = {
+ {"help", N_("returns the volume path for a given volume name or key")},
+ {"desc", ""},
{NULL, NULL}
};
-static const vshCmdOptDef opts_detach_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")},
+static const vshCmdOptDef opts_vol_path[] = {
+ {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("volume name or key")},
+ {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")},
{NULL, 0, 0, NULL}
};
static bool
-cmdDetachDevice(vshControl *ctl, const vshCmd *cmd)
+cmdVolPath(vshControl *ctl, const vshCmd *cmd)
{
- virDomainPtr dom = NULL;
- const char *from = NULL;
- char *buffer = NULL;
- int ret;
- bool funcRet = false;
- unsigned int flags;
+ virStorageVolPtr vol;
+ char * StorageVolPath;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL))) {
return false;
-
- if (vshCommandOptString(cmd, "file", &from) <= 0)
- goto cleanup;
-
- if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
- virshReportError(ctl);
- goto cleanup;
}
- if (vshCommandOptBool(cmd, "config")) {
- flags = VIR_DOMAIN_AFFECT_CONFIG;
- if (virDomainIsActive(dom) == 1)
- flags |= VIR_DOMAIN_AFFECT_LIVE;
- ret = virDomainDetachDeviceFlags(dom, buffer, flags);
- } else {
- ret = virDomainDetachDevice(dom, buffer);
- }
-
- if (ret < 0) {
- vshError(ctl, _("Failed to detach device from %s"), from);
- goto cleanup;
+ if ((StorageVolPath = virStorageVolGetPath(vol)) == NULL) {
+ virStorageVolFree(vol);
+ return false;
}
- vshPrint(ctl, "%s", _("Device detached successfully\n"));
- funcRet = true;
-
-cleanup:
- VIR_FREE(buffer);
- virDomainFree(dom);
- return funcRet;
+ vshPrint(ctl, "%s\n", StorageVolPath);
+ VIR_FREE(StorageVolPath);
+ virStorageVolFree(vol);
+ return true;
}
/*
- * "update-device" command
+ * "secret-define" command
*/
-static const vshCmdInfo info_update_device[] = {
- {"help", N_("update device from an XML file")},
- {"desc", N_("Update device from an XML .")},
+static const vshCmdInfo info_secret_define[] = {
+ {"help", N_("define or modify a secret from an XML file")},
+ {"desc", N_("Define or modify a secret.")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_update_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")},
- {"force", VSH_OT_BOOL, 0, N_("force device update")},
+static const vshCmdOptDef opts_secret_define[] = {
+ {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing secret attributes in XML")},
{NULL, 0, 0, NULL}
};
static bool
-cmdUpdateDevice(vshControl *ctl, const vshCmd *cmd)
+cmdSecretDefine(vshControl *ctl, const vshCmd *cmd)
{
- virDomainPtr dom;
const char *from = NULL;
char *buffer;
- int ret;
- unsigned int flags;
+ virSecretPtr res;
+ char uuid[VIR_UUID_STRING_BUFLEN];
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
- return false;
-
- if (vshCommandOptString(cmd, "file", &from) <= 0) {
- virDomainFree(dom);
+ if (vshCommandOptString(cmd, "file", &from) <= 0)
return false;
- }
- if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
- virshReportError(ctl);
- virDomainFree(dom);
+ if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
return false;
- }
-
- if (vshCommandOptBool(cmd, "config")) {
- flags = VIR_DOMAIN_AFFECT_CONFIG;
- if (virDomainIsActive(dom) == 1)
- flags |= VIR_DOMAIN_AFFECT_LIVE;
- } else {
- flags = VIR_DOMAIN_AFFECT_LIVE;
- }
-
- if (vshCommandOptBool(cmd, "force"))
- flags |= VIR_DOMAIN_DEVICE_MODIFY_FORCE;
- ret = virDomainUpdateDeviceFlags(dom, buffer, flags);
+ res = virSecretDefineXML(ctl->conn, buffer, 0);
VIR_FREE(buffer);
- if (ret < 0) {
- vshError(ctl, _("Failed to update device from %s"), from);
- virDomainFree(dom);
+ if (res == NULL) {
+ vshError(ctl, _("Failed to set attributes from %s"), from);
+ return false;
+ }
+ if (virSecretGetUUIDString(res, &(uuid[0])) < 0) {
+ vshError(ctl, "%s", _("Failed to get UUID of created secret"));
+ virSecretFree(res);
return false;
- } else {
- vshPrint(ctl, "%s", _("Device updated successfully\n"));
}
-
- virDomainFree(dom);
+ vshPrint(ctl, _("Secret %s created\n"), uuid);
+ virSecretFree(res);
return true;
}
-
/*
- * "attach-interface" command
+ * "secret-dumpxml" command
*/
-static const vshCmdInfo info_attach_interface[] = {
- {"help", N_("attach network interface")},
- {"desc", N_("Attach new network interface.")},
+static const vshCmdInfo info_secret_dumpxml[] = {
+ {"help", N_("secret attributes in XML")},
+ {"desc", N_("Output attributes of a secret as an XML dump to stdout.")},
{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")},
+static const vshCmdOptDef opts_secret_dumpxml[] = {
+ {"secret", VSH_OT_DATA, VSH_OFLAG_REQ, N_("secret UUID")},
{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)
+cmdSecretDumpXML(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;
+ virSecretPtr secret;
+ bool ret = false;
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, " \n", script);
- if (model != NULL)
- virBufferAsprintf(&buf, " \n", model);
-
- if (inboundStr || outboundStr) {
- virBufferAsprintf(&buf, " \n");
- if (inboundStr && inbound.average > 0) {
- virBufferAsprintf(&buf, " 0)
- virBufferAsprintf(&buf, " peak='%llu'", inbound.peak);
- if (inbound.burst > 0)
- virBufferAsprintf(&buf, " burst='%llu'", inbound.burst);
- virBufferAsprintf(&buf, "/>\n");
- }
- if (outboundStr && outbound.average > 0) {
- virBufferAsprintf(&buf, " 0)
- virBufferAsprintf(&buf, " peak='%llu'", outbound.peak);
- if (outbound.burst > 0)
- virBufferAsprintf(&buf, " burst='%llu'", outbound.burst);
- virBufferAsprintf(&buf, "/>\n");
- }
- virBufferAsprintf(&buf, " \n");
- }
+ return false;
- virBufferAddLit(&buf, "\n");
+ secret = vshCommandOptSecret(ctl, cmd, NULL);
+ if (secret == NULL)
+ return false;
- if (virBufferError(&buf)) {
- vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
+ xml = virSecretGetXMLDesc(secret, 0);
+ if (xml == NULL)
goto cleanup;
- }
-
- 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);
- }
-
+ vshPrint(ctl, "%s", xml);
VIR_FREE(xml);
+ ret = true;
- if (ret != 0) {
- vshError(ctl, "%s", _("Failed to attach interface"));
- } else {
- vshPrint(ctl, "%s", _("Interface attached successfully\n"));
- functionReturn = true;
- }
-
- cleanup:
- if (dom)
- virDomainFree(dom);
- virBufferFreeAndReset(&buf);
- return functionReturn;
+cleanup:
+ virSecretFree(secret);
+ return ret;
}
/*
- * "detach-interface" command
+ * "secret-set-value" command
*/
-static const vshCmdInfo info_detach_interface[] = {
- {"help", N_("detach network interface")},
- {"desc", N_("Detach network interface.")},
+static const vshCmdInfo info_secret_set_value[] = {
+ {"help", N_("set a secret value")},
+ {"desc", N_("Set a secret value.")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_detach_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")},
- {"mac", VSH_OT_STRING, 0, N_("MAC address")},
- {"persistent", VSH_OT_ALIAS, 0, "config"},
- {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
+static const vshCmdOptDef opts_secret_set_value[] = {
+ {"secret", VSH_OT_DATA, VSH_OFLAG_REQ, N_("secret UUID")},
+ {"base64", VSH_OT_DATA, VSH_OFLAG_REQ, N_("base64-encoded secret value")},
{NULL, 0, 0, NULL}
};
static bool
-cmdDetachInterface(vshControl *ctl, const vshCmd *cmd)
+cmdSecretSetValue(vshControl *ctl, const vshCmd *cmd)
{
- virDomainPtr dom = NULL;
- xmlDocPtr xml = NULL;
- xmlXPathObjectPtr obj=NULL;
- xmlXPathContextPtr ctxt = NULL;
- xmlNodePtr cur = NULL;
- xmlBufferPtr xml_buf = NULL;
- const char *mac =NULL, *type = NULL;
- char *doc;
- char buf[64];
- int i = 0, diff_mac;
- int ret;
- int functionReturn = false;
- unsigned int flags;
+ virSecretPtr secret;
+ size_t value_size;
+ const char *base64 = NULL;
+ char *value;
+ int res;
+ bool ret = false;
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, "mac", &mac) < 0) {
- vshError(ctl, "%s", _("missing option"));
- goto cleanup;
- }
-
- doc = virDomainGetXMLDesc(dom, 0);
- if (!doc)
- goto cleanup;
+ return false;
- xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt);
- VIR_FREE(doc);
- if (!xml) {
- vshError(ctl, "%s", _("Failed to get interface information"));
- goto cleanup;
- }
+ secret = vshCommandOptSecret(ctl, cmd, NULL);
+ if (secret == NULL)
+ return false;
- snprintf(buf, sizeof(buf), "/domain/devices/interface[@type='%s']", type);
- obj = xmlXPathEval(BAD_CAST buf, ctxt);
- if (obj == NULL || obj->type != XPATH_NODESET ||
- obj->nodesetval == NULL || obj->nodesetval->nodeNr == 0) {
- vshError(ctl, _("No found interface whose type is %s"), type);
+ if (vshCommandOptString(cmd, "base64", &base64) <= 0)
goto cleanup;
- }
- if (!mac && obj->nodesetval->nodeNr > 1) {
- vshError(ctl, _("Domain has %d interfaces. Please specify which one "
- "to detach using --mac"), obj->nodesetval->nodeNr);
+ if (!base64_decode_alloc(base64, strlen(base64), &value, &value_size)) {
+ vshError(ctl, "%s", _("Invalid base64 data"));
goto cleanup;
}
-
- if (!mac)
- goto hit;
-
- /* search mac */
- for (; i < obj->nodesetval->nodeNr; i++) {
- cur = obj->nodesetval->nodeTab[i]->children;
- while (cur != NULL) {
- if (cur->type == XML_ELEMENT_NODE &&
- xmlStrEqual(cur->name, BAD_CAST "mac")) {
- char *tmp_mac = virXMLPropString(cur, "address");
- diff_mac = virMacAddrCompare(tmp_mac, mac);
- VIR_FREE(tmp_mac);
- if (!diff_mac) {
- goto hit;
- }
- }
- cur = cur->next;
- }
- }
- vshError(ctl, _("No found interface whose MAC address is %s"), mac);
- goto cleanup;
-
- hit:
- xml_buf = xmlBufferCreate();
- if (!xml_buf) {
+ if (value == NULL) {
vshError(ctl, "%s", _("Failed to allocate memory"));
- goto cleanup;
- }
-
- if (xmlNodeDump(xml_buf, xml, obj->nodesetval->nodeTab[i], 0, 0) < 0) {
- vshError(ctl, "%s", _("Failed to create XML"));
- goto cleanup;
+ return false;
}
- if (vshCommandOptBool(cmd, "config")) {
- flags = VIR_DOMAIN_AFFECT_CONFIG;
- if (virDomainIsActive(dom) == 1)
- flags |= VIR_DOMAIN_AFFECT_LIVE;
- ret = virDomainDetachDeviceFlags(dom,
- (char *)xmlBufferContent(xml_buf),
- flags);
- } else {
- ret = virDomainDetachDevice(dom, (char *)xmlBufferContent(xml_buf));
- }
+ res = virSecretSetValue(secret, (unsigned char *)value, value_size, 0);
+ memset(value, 0, value_size);
+ VIR_FREE(value);
- if (ret != 0) {
- vshError(ctl, "%s", _("Failed to detach interface"));
- } else {
- vshPrint(ctl, "%s", _("Interface detached successfully\n"));
- functionReturn = true;
+ if (res != 0) {
+ vshError(ctl, "%s", _("Failed to set secret value"));
+ goto cleanup;
}
+ vshPrint(ctl, "%s", _("Secret value set\n"));
+ ret = true;
- cleanup:
- if (dom)
- virDomainFree(dom);
- xmlXPathFreeObject(obj);
- xmlXPathFreeContext(ctxt);
- xmlFreeDoc(xml);
- xmlBufferFree(xml_buf);
- return functionReturn;
+cleanup:
+ virSecretFree(secret);
+ return ret;
}
/*
- * "attach-disk" command
+ * "secret-get-value" command
*/
-static const vshCmdInfo info_attach_disk[] = {
- {"help", N_("attach disk device")},
- {"desc", N_("Attach new disk device.")},
+static const vshCmdInfo info_secret_get_value[] = {
+ {"help", N_("Output a secret value")},
+ {"desc", N_("Output a secret value to stdout.")},
{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")},
+static const vshCmdOptDef opts_secret_get_value[] = {
+ {"secret", VSH_OT_DATA, VSH_OFLAG_REQ, N_("secret UUID")},
{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)
+static bool
+cmdSecretGetValue(vshControl *ctl, const vshCmd *cmd)
{
- char *controller, *bus, *unit;
+ virSecretPtr secret;
+ char *base64;
+ unsigned char *value;
+ size_t value_size;
+ bool ret = false;
- if (!ideAddr)
- return -1;
- if (!str)
- return -1;
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
- controller = (char *)str;
+ secret = vshCommandOptSecret(ctl, cmd, NULL);
+ if (secret == NULL)
+ return false;
- if (virStrToLong_ui(controller, &bus, 0, &ideAddr->controller) != 0)
- return -1;
+ value = virSecretGetValue(secret, &value_size, 0);
+ if (value == NULL)
+ goto cleanup;
- bus++;
- if (virStrToLong_ui(bus, &unit, 0, &ideAddr->bus) != 0)
- return -1;
+ base64_encode_alloc((char *)value, value_size, &base64);
+ memset(value, 0, value_size);
+ VIR_FREE(value);
- unit++;
- if (virStrToLong_ui(unit, NULL, 0, &ideAddr->unit) != 0)
- return -1;
+ if (base64 == NULL) {
+ vshError(ctl, "%s", _("Failed to allocate memory"));
+ goto cleanup;
+ }
+ vshPrint(ctl, "%s", base64);
+ memset(base64, 0, strlen(base64));
+ VIR_FREE(base64);
+ ret = true;
- return 0;
+cleanup:
+ virSecretFree(secret);
+ return ret;
}
-/* 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)
+/*
+ * "secret-undefine" command
*/
+static const vshCmdInfo info_secret_undefine[] = {
+ {"help", N_("undefine a secret")},
+ {"desc", N_("Undefine a secret.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_secret_undefine[] = {
+ {"secret", VSH_OT_DATA, VSH_OFLAG_REQ, N_("secret UUID")},
+ {NULL, 0, 0, NULL}
+};
-static int str2DiskAddress(const char *str, struct DiskAddress *diskAddr)
+static bool
+cmdSecretUndefine(vshControl *ctl, const vshCmd *cmd)
{
- char *type, *addr;
+ virSecretPtr secret;
+ bool ret = false;
+ const char *uuid;
- if (!diskAddr)
- return -1;
- if (!str)
- return -1;
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
- type = (char *)str;
- addr = strchr(type, ':');
- if (!addr)
- return -1;
+ secret = vshCommandOptSecret(ctl, cmd, &uuid);
+ if (secret == NULL)
+ return false;
- 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);
+ if (virSecretUndefine(secret) < 0) {
+ vshError(ctl, _("Failed to delete secret %s"), uuid);
+ goto cleanup;
}
+ vshPrint(ctl, _("Secret %s deleted\n"), uuid);
+ ret = true;
- return -1;
+cleanup:
+ virSecretFree(secret);
+ return ret;
}
+/*
+ * "secret-list" command
+ */
+static const vshCmdInfo info_secret_list[] = {
+ {"help", N_("list secrets")},
+ {"desc", N_("Returns a list of secrets")},
+ {NULL, NULL}
+};
+
static bool
-cmdAttachDisk(vshControl *ctl, const vshCmd *cmd)
+cmdSecretList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
- 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;
+ int maxuuids = 0, i;
+ char **uuids = NULL;
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;
+ return false;
- 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;
+ maxuuids = virConnectNumOfSecrets(ctl->conn);
+ if (maxuuids < 0) {
+ vshError(ctl, "%s", _("Failed to list secrets"));
+ return false;
}
+ uuids = vshMalloc(ctl, sizeof(*uuids) * maxuuids);
- 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;
+ maxuuids = virConnectListSecrets(ctl->conn, uuids, maxuuids);
+ if (maxuuids < 0) {
+ vshError(ctl, "%s", _("Failed to list secrets"));
+ VIR_FREE(uuids);
+ return false;
}
- if (mode) {
- if (STRNEQ(mode, "readonly") && STRNEQ(mode, "shareable")) {
- vshError(ctl, _("No support for %s in command 'attach-disk'"),
- mode);
- goto cleanup;
- }
- }
+ qsort(uuids, maxuuids, sizeof(char *), vshNameSorter);
- /* Make XML of disk */
- virBufferAsprintf(&buf, "\n");
+ vshPrintExtra(ctl, "%-36s %s\n", _("UUID"), _("Usage"));
+ vshPrintExtra(ctl, "-----------------------------------------------------------\n");
- if (driver || subdriver || cache) {
- virBufferAsprintf(&buf, " conn, uuids[i]);
+ const char *usageType = NULL;
- if (driver)
- virBufferAsprintf(&buf, " name='%s'", driver);
- if (subdriver)
- virBufferAsprintf(&buf, " type='%s'", subdriver);
- if (cache)
- virBufferAsprintf(&buf, " cache='%s'", cache);
+ if (!sec) {
+ VIR_FREE(uuids[i]);
+ continue;
+ }
- virBufferAddLit(&buf, "/>\n");
+ switch (virSecretGetUsageType(sec)) {
+ case VIR_SECRET_USAGE_TYPE_VOLUME:
+ usageType = _("Volume");
+ break;
+ }
+
+ if (usageType) {
+ vshPrint(ctl, "%-36s %s %s\n",
+ uuids[i], usageType,
+ virSecretGetUsageID(sec));
+ } else {
+ vshPrint(ctl, "%-36s %s\n",
+ uuids[i], _("Unused"));
+ }
+ virSecretFree(sec);
+ VIR_FREE(uuids[i]);
}
+ VIR_FREE(uuids);
+ return true;
+}
- 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);
+/*
+ * "version" command
+ */
+static const vshCmdInfo info_version[] = {
+ {"help", N_("show version")},
+ {"desc", N_("Display the system version information.")},
+ {NULL, NULL}
+};
- if (vshCommandOptBool(cmd, "shareable"))
- virBufferAsprintf(&buf, " \n");
+static const vshCmdOptDef opts_version[] = {
+ {"daemon", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("report daemon version too")},
+ {NULL, 0, 0, NULL}
+};
- if (straddr) {
- if (str2DiskAddress(straddr, &diskAddr) != 0) {
- vshError(ctl, _("Invalid address."));
- goto cleanup;
- }
+static bool
+cmdVersion(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
+{
+ unsigned long hvVersion;
+ const char *hvType;
+ unsigned long libVersion;
+ unsigned long includeVersion;
+ unsigned long apiVersion;
+ unsigned long daemonVersion;
+ int ret;
+ unsigned int major;
+ unsigned int minor;
+ unsigned int rel;
- 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;
- }
- }
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ hvType = virConnectGetType(ctl->conn);
+ if (hvType == NULL) {
+ vshError(ctl, "%s", _("failed to get hypervisor type"));
+ return false;
}
- virBufferAddLit(&buf, "\n");
+ includeVersion = LIBVIR_VERSION_NUMBER;
+ major = includeVersion / 1000000;
+ includeVersion %= 1000000;
+ minor = includeVersion / 1000;
+ rel = includeVersion % 1000;
+ vshPrint(ctl, _("Compiled against library: libvir %d.%d.%d\n"),
+ major, minor, rel);
- if (virBufferError(&buf)) {
- vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
+ ret = virGetVersion(&libVersion, hvType, &apiVersion);
+ if (ret < 0) {
+ vshError(ctl, "%s", _("failed to get the library version"));
return false;
}
+ major = libVersion / 1000000;
+ libVersion %= 1000000;
+ minor = libVersion / 1000;
+ rel = libVersion % 1000;
+ vshPrint(ctl, _("Using library: libvir %d.%d.%d\n"),
+ major, minor, rel);
- xml = virBufferContentAndReset(&buf);
+ major = apiVersion / 1000000;
+ apiVersion %= 1000000;
+ minor = apiVersion / 1000;
+ rel = apiVersion % 1000;
+ vshPrint(ctl, _("Using API: %s %d.%d.%d\n"), hvType,
+ major, minor, rel);
- 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);
+ ret = virConnectGetVersion(ctl->conn, &hvVersion);
+ if (ret < 0) {
+ vshError(ctl, "%s", _("failed to get the hypervisor version"));
+ return false;
}
+ if (hvVersion == 0) {
+ vshPrint(ctl,
+ _("Cannot extract running %s hypervisor version\n"), hvType);
+ } else {
+ major = hvVersion / 1000000;
+ hvVersion %= 1000000;
+ minor = hvVersion / 1000;
+ rel = hvVersion % 1000;
- VIR_FREE(xml);
+ vshPrint(ctl, _("Running hypervisor: %s %d.%d.%d\n"),
+ hvType, major, minor, rel);
+ }
- if (ret != 0) {
- vshError(ctl, "%s", _("Failed to attach disk"));
- } else {
- vshPrint(ctl, "%s", _("Disk attached successfully\n"));
- functionReturn = true;
+ if (vshCommandOptBool(cmd, "daemon")) {
+ ret = virConnectGetLibVersion(ctl->conn, &daemonVersion);
+ if (ret < 0) {
+ vshError(ctl, "%s", _("failed to get the daemon version"));
+ } else {
+ major = daemonVersion / 1000000;
+ daemonVersion %= 1000000;
+ minor = daemonVersion / 1000;
+ rel = daemonVersion % 1000;
+ vshPrint(ctl, _("Running against daemon: %d.%d.%d\n"),
+ major, minor, rel);
+ }
}
- cleanup:
- if (dom)
- virDomainFree(dom);
- virBufferFreeAndReset(&buf);
- return functionReturn;
+ return true;
}
-typedef enum {
- VSH_FIND_DISK_NORMAL,
- VSH_FIND_DISK_CHANGEABLE,
-} vshFindDiskType;
-
-/* Helper function to find disk device in XML doc. Returns the disk
- * node on success, or NULL on failure. Caller must free the result
- * @path: Fully-qualified path or target of disk device.
- * @type: Either VSH_FIND_DISK_NORMAL or VSH_FIND_DISK_CHANGEABLE.
- */
-static xmlNodePtr
-vshFindDisk(const char *doc,
- const char *path,
- int type)
+/* Tree listing helpers. */
+
+/* Given an index, return either the name of that device (non-NULL) or
+ * of its parent (NULL if a root). */
+typedef const char * (*vshTreeLookup)(int devid, bool parent, void *opaque);
+
+static int
+vshTreePrintInternal(vshControl *ctl,
+ vshTreeLookup lookup,
+ void *opaque,
+ int num_devices,
+ int devid,
+ int lastdev,
+ bool root,
+ virBufferPtr indent)
{
- xmlDocPtr xml = NULL;
- xmlXPathObjectPtr obj= NULL;
- xmlXPathContextPtr ctxt = NULL;
- xmlNodePtr cur = NULL;
- xmlNodePtr ret = NULL;
- int i = 0;
+ int i;
+ int nextlastdev = -1;
+ int ret = -1;
+ const char *dev = (lookup)(devid, false, opaque);
- xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt);
- if (!xml) {
- vshError(NULL, "%s", _("Failed to get disk information"));
+ if (virBufferError(indent))
goto cleanup;
- }
- obj = xmlXPathEval(BAD_CAST "/domain/devices/disk", ctxt);
- if (obj == NULL ||
- obj->type != XPATH_NODESET ||
- obj->nodesetval == NULL ||
- obj->nodesetval->nodeNr == 0) {
- vshError(NULL, "%s", _("Failed to get disk information"));
- goto cleanup;
+ /* Print this device, with indent if not at root */
+ vshPrint(ctl, "%s%s%s\n", virBufferCurrentContent(indent),
+ root ? "" : "+- ", dev);
+
+ /* Update indent to show '|' or ' ' for child devices */
+ if (!root) {
+ virBufferAddChar(indent, devid == lastdev ? ' ' : '|');
+ virBufferAddChar(indent, ' ');
+ if (virBufferError(indent))
+ goto cleanup;
}
- /* search disk using @path */
- for (; i < obj->nodesetval->nodeNr; i++) {
- bool is_supported = true;
-
- if (type == VSH_FIND_DISK_CHANGEABLE) {
- xmlNodePtr n = obj->nodesetval->nodeTab[i];
- is_supported = false;
-
- /* Check if the disk is CDROM or floppy disk */
- if (xmlStrEqual(n->name, BAD_CAST "disk")) {
- char *device_value = virXMLPropString(n, "device");
-
- if (STREQ(device_value, "cdrom") ||
- STREQ(device_value, "floppy"))
- is_supported = true;
-
- VIR_FREE(device_value);
- }
+ /* Determine the index of the last child device */
+ for (i = 0 ; i < num_devices ; i++) {
+ const char *parent = (lookup)(i, true, opaque);
- if (!is_supported)
- continue;
- }
+ if (parent && STREQ(parent, dev))
+ nextlastdev = i;
+ }
- cur = obj->nodesetval->nodeTab[i]->children;
- while (cur != NULL) {
- if (cur->type == XML_ELEMENT_NODE) {
- char *tmp = NULL;
+ /* If there is a child device, then print another blank line */
+ if (nextlastdev != -1)
+ vshPrint(ctl, "%s |\n", virBufferCurrentContent(indent));
- if (xmlStrEqual(cur->name, BAD_CAST "source")) {
- if ((tmp = virXMLPropString(cur, "file")) ||
- (tmp = virXMLPropString(cur, "dev")) ||
- (tmp = virXMLPropString(cur, "dir")) ||
- (tmp = virXMLPropString(cur, "name"))) {
- }
- } else if (xmlStrEqual(cur->name, BAD_CAST "target")) {
- tmp = virXMLPropString(cur, "dev");
- }
+ /* Finally print all children */
+ virBufferAddLit(indent, " ");
+ for (i = 0 ; i < num_devices ; i++) {
+ const char *parent = (lookup)(i, true, opaque);
- if (STREQ_NULLABLE(tmp, path)) {
- ret = xmlCopyNode(obj->nodesetval->nodeTab[i], 1);
- VIR_FREE(tmp);
- goto cleanup;
- }
- VIR_FREE(tmp);
- }
- cur = cur->next;
- }
+ if (parent && STREQ(parent, dev) &&
+ vshTreePrintInternal(ctl, lookup, opaque,
+ num_devices, i, nextlastdev,
+ false, indent) < 0)
+ goto cleanup;
}
+ virBufferTrim(indent, " ", -1);
- vshError(NULL, _("No found disk whose source path or target is %s"), path);
+ /* If there was no child device, and we're the last in
+ * a list of devices, then print another blank line */
+ if (nextlastdev == -1 && devid == lastdev)
+ vshPrint(ctl, "%s\n", virBufferCurrentContent(indent));
+ if (!root)
+ virBufferTrim(indent, NULL, 2);
+ ret = 0;
cleanup:
- xmlXPathFreeObject(obj);
- xmlXPathFreeContext(ctxt);
- xmlFreeDoc(xml);
return ret;
}
-typedef enum {
- VSH_PREPARE_DISK_XML_NONE = 0,
- VSH_PREPARE_DISK_XML_EJECT,
- VSH_PREPARE_DISK_XML_INSERT,
- VSH_PREPARE_DISK_XML_UPDATE,
-} vshPrepareDiskXMLType;
-
-/* Helper function to prepare disk XML. Could be used for disk
- * detaching, media changing(ejecting, inserting, updating)
- * for changeable disk. Returns the processed XML as string on
- * success, or NULL on failure. Caller must free the result.
- */
-static char *
-vshPrepareDiskXML(xmlNodePtr disk_node,
- const char *source,
- const char *path,
- int type)
+static int
+vshTreePrint(vshControl *ctl, vshTreeLookup lookup, void *opaque,
+ int num_devices, int devid)
{
- xmlNodePtr cur = NULL;
- xmlBufferPtr xml_buf = NULL;
- const char *disk_type = NULL;
- const char *device_type = NULL;
- xmlNodePtr new_node = NULL;
- char *ret = NULL;
-
- if (!disk_node)
- return NULL;
-
- xml_buf = xmlBufferCreate();
- if (!xml_buf) {
- vshError(NULL, "%s", _("Failed to allocate memory"));
- return NULL;
- }
-
- device_type = virXMLPropString(disk_node, "device");
-
- if (STREQ_NULLABLE(device_type, "cdrom") ||
- STREQ_NULLABLE(device_type, "floppy")) {
- bool has_source = false;
- disk_type = virXMLPropString(disk_node, "type");
-
- cur = disk_node->children;
- while (cur != NULL) {
- if (cur->type == XML_ELEMENT_NODE &&
- xmlStrEqual(cur->name, BAD_CAST "source")) {
- has_source = true;
- break;
- }
- cur = cur->next;
- }
-
- if (!has_source) {
- if (type == VSH_PREPARE_DISK_XML_EJECT) {
- vshError(NULL, _("The disk device '%s' doesn't have media"),
- path);
- goto error;
- }
-
- if (source) {
- new_node = xmlNewNode(NULL, BAD_CAST "source");
- xmlNewProp(new_node, (const xmlChar *)disk_type,
- (const xmlChar *)source);
- xmlAddChild(disk_node, new_node);
- } else if (type == VSH_PREPARE_DISK_XML_INSERT) {
- vshError(NULL, _("No source is specified for inserting media"));
- goto error;
- } else if (type == VSH_PREPARE_DISK_XML_UPDATE) {
- vshError(NULL, _("No source is specified for updating media"));
- goto error;
- }
- }
-
- if (has_source) {
- if (type == VSH_PREPARE_DISK_XML_INSERT) {
- vshError(NULL, _("The disk device '%s' already has media"),
- path);
- goto error;
- }
-
- /* Remove the source if it tends to eject/update media. */
- xmlUnlinkNode(cur);
- xmlFreeNode(cur);
-
- if (source && (type == VSH_PREPARE_DISK_XML_UPDATE)) {
- new_node = xmlNewNode(NULL, BAD_CAST "source");
- xmlNewProp(new_node, (const xmlChar *)disk_type,
- (const xmlChar *)source);
- xmlAddChild(disk_node, new_node);
- }
- }
- }
-
- if (xmlNodeDump(xml_buf, NULL, disk_node, 0, 0) < 0) {
- vshError(NULL, "%s", _("Failed to create XML"));
- goto error;
- }
-
- goto cleanup;
+ int ret;
+ virBuffer indent = VIR_BUFFER_INITIALIZER;
-cleanup:
- VIR_FREE(device_type);
- VIR_FREE(disk_type);
- if (xml_buf) {
- int len = xmlBufferLength(xml_buf);
- if (VIR_ALLOC_N(ret, len + 1) < 0) {
- virReportOOMError();
- return NULL;
- }
- memcpy(ret, (char *)xmlBufferContent(xml_buf), len);
- ret[len] = '\0';
- xmlBufferFree(xml_buf);
- }
+ ret = vshTreePrintInternal(ctl, lookup, opaque, num_devices,
+ devid, devid, true, &indent);
+ if (ret < 0)
+ vshError(ctl, "%s", _("Failed to complete tree listing"));
+ virBufferFreeAndReset(&indent);
return ret;
+}
-error:
- xmlBufferFree(xml_buf);
- xml_buf = NULL;
- goto cleanup;
+struct vshNodeList {
+ char **names;
+ char **parents;
+};
+
+static const char *
+vshNodeListLookup(int devid, bool parent, void *opaque)
+{
+ struct vshNodeList *arrays = opaque;
+ if (parent)
+ return arrays->parents[devid];
+ return arrays->names[devid];
}
/*
- * "detach-disk" command
+ * "nodedev-list" command
*/
-static const vshCmdInfo info_detach_disk[] = {
- {"help", N_("detach disk device")},
- {"desc", N_("Detach disk device.")},
+static const vshCmdInfo info_node_list_devices[] = {
+ {"help", N_("enumerate devices on this host")},
+ {"desc", ""},
{NULL, NULL}
};
-static const vshCmdOptDef opts_detach_disk[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {"target", VSH_OT_DATA, VSH_OFLAG_REQ, N_("target of disk device")},
- {"persistent", VSH_OT_ALIAS, 0, "config"},
- {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
+static const vshCmdOptDef opts_node_list_devices[] = {
+ {"tree", VSH_OT_BOOL, 0, N_("list devices in a tree")},
+ {"cap", VSH_OT_STRING, VSH_OFLAG_NONE, N_("capability name")},
{NULL, 0, 0, NULL}
};
static bool
-cmdDetachDisk(vshControl *ctl, const vshCmd *cmd)
+cmdNodeListDevices(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
- char *disk_xml = NULL;
- virDomainPtr dom = NULL;
- const char *target = NULL;
- char *doc = NULL;
- int ret;
- bool functionReturn = false;
- unsigned int flags;
- xmlNodePtr disk_node = NULL;
+ const char *cap = NULL;
+ char **devices;
+ int num_devices, i;
+ bool tree = vshCommandOptBool(cmd, "tree");
+ bool ret = true;
if (!vshConnectionUsability(ctl, ctl->conn))
- goto cleanup;
-
- if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
- goto cleanup;
-
- if (vshCommandOptString(cmd, "target", &target) <= 0)
- goto cleanup;
-
- doc = virDomainGetXMLDesc(dom, 0);
- if (!doc)
- goto cleanup;
+ return false;
- if (!(disk_node = vshFindDisk(doc, target, VSH_FIND_DISK_NORMAL)))
- goto cleanup;
+ if (vshCommandOptString(cmd, "cap", &cap) <= 0)
+ cap = NULL;
- if (!(disk_xml = vshPrepareDiskXML(disk_node, NULL, NULL,
- VSH_PREPARE_DISK_XML_NONE)))
- goto cleanup;
+ num_devices = virNodeNumOfDevices(ctl->conn, cap, 0);
+ if (num_devices < 0) {
+ vshError(ctl, "%s", _("Failed to count node devices"));
+ return false;
+ } else if (num_devices == 0) {
+ return true;
+ }
- if (vshCommandOptBool(cmd, "config")) {
- flags = VIR_DOMAIN_AFFECT_CONFIG;
- if (virDomainIsActive(dom) == 1)
- flags |= VIR_DOMAIN_AFFECT_LIVE;
- ret = virDomainDetachDeviceFlags(dom,
- disk_xml,
- flags);
- } else {
- ret = virDomainDetachDevice(dom, disk_xml);
+ devices = vshMalloc(ctl, sizeof(char *) * num_devices);
+ num_devices =
+ virNodeListDevices(ctl->conn, cap, devices, num_devices, 0);
+ if (num_devices < 0) {
+ vshError(ctl, "%s", _("Failed to list node devices"));
+ VIR_FREE(devices);
+ return false;
}
+ qsort(&devices[0], num_devices, sizeof(char*), vshNameSorter);
+ if (tree) {
+ char **parents = vshMalloc(ctl, sizeof(char *) * num_devices);
+ struct vshNodeList arrays = { devices, parents };
- if (ret != 0) {
- vshError(ctl, "%s", _("Failed to detach disk"));
+ for (i = 0; i < num_devices; i++) {
+ virNodeDevicePtr dev = virNodeDeviceLookupByName(ctl->conn, devices[i]);
+ if (dev && STRNEQ(devices[i], "computer")) {
+ const char *parent = virNodeDeviceGetParent(dev);
+ parents[i] = parent ? vshStrdup(ctl, parent) : NULL;
+ } else {
+ parents[i] = NULL;
+ }
+ virNodeDeviceFree(dev);
+ }
+ for (i = 0 ; i < num_devices ; i++) {
+ if (parents[i] == NULL &&
+ vshTreePrint(ctl, vshNodeListLookup, &arrays, num_devices,
+ i) < 0)
+ ret = false;
+ }
+ for (i = 0 ; i < num_devices ; i++) {
+ VIR_FREE(devices[i]);
+ VIR_FREE(parents[i]);
+ }
+ VIR_FREE(parents);
} else {
- vshPrint(ctl, "%s", _("Disk detached successfully\n"));
- functionReturn = true;
+ for (i = 0; i < num_devices; i++) {
+ vshPrint(ctl, "%s\n", devices[i]);
+ VIR_FREE(devices[i]);
+ }
}
-
- cleanup:
- xmlFreeNode(disk_node);
- VIR_FREE(disk_xml);
- VIR_FREE(doc);
- if (dom)
- virDomainFree(dom);
- return functionReturn;
+ VIR_FREE(devices);
+ return ret;
}
/*
- * "change-media" command
+ * "nodedev-dumpxml" command
*/
-static const vshCmdInfo info_change_media[] = {
- {"help", N_("Change media of CD or floppy drive")},
- {"desc", N_("Change media of CD or floppy drive.")},
+static const vshCmdInfo info_node_device_dumpxml[] = {
+ {"help", N_("node device details in XML")},
+ {"desc", N_("Output the node device details as an XML dump to stdout.")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_change_media[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {"path", VSH_OT_DATA, VSH_OFLAG_REQ, N_("Fully-qualified path or "
- "target of disk device")},
- {"source", VSH_OT_DATA, 0, N_("source of the media")},
- {"eject", VSH_OT_BOOL, 0, N_("Eject the media")},
- {"insert", VSH_OT_BOOL, 0, N_("Insert the media")},
- {"update", VSH_OT_BOOL, 0, N_("Update the media")},
- {"current", VSH_OT_BOOL, 0, N_("can be either or both of --live and --config, "
- "depends on implementation of hypervisor driver")},
- {"live", VSH_OT_BOOL, 0, N_("alter live configuration of running domain")},
- {"config", VSH_OT_BOOL, 0, N_("alter persistent configuration, effect observed on next boot")},
- {"force", VSH_OT_BOOL, 0, N_("force media insertion")},
+
+static const vshCmdOptDef opts_node_device_dumpxml[] = {
+ {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("device key")},
{NULL, 0, 0, NULL}
};
static bool
-cmdChangeMedia(vshControl *ctl, const vshCmd *cmd)
+cmdNodeDeviceDumpXML(vshControl *ctl, const vshCmd *cmd)
{
- virDomainPtr dom = NULL;
- const char *source = NULL;
- const char *path = NULL;
- const char *doc = NULL;
- xmlNodePtr disk_node = NULL;
- const char *disk_xml = NULL;
- int flags = 0;
- bool config, live, current, force = false;
- bool eject, insert, update = false;
- bool ret = false;
- int prepare_type = 0;
- const char *action = NULL;
-
- config = vshCommandOptBool(cmd, "config");
- live = vshCommandOptBool(cmd, "live");
- current = vshCommandOptBool(cmd, "current");
- force = vshCommandOptBool(cmd, "force");
- eject = vshCommandOptBool(cmd, "eject");
- insert = vshCommandOptBool(cmd, "insert");
- update = vshCommandOptBool(cmd, "update");
-
- if (eject + insert + update > 1) {
- vshError(ctl, "%s", _("--eject, --insert, and --update must be specified "
- "exclusively."));
- return false;
- }
-
- if (eject) {
- prepare_type = VSH_PREPARE_DISK_XML_EJECT;
- action = "eject";
- }
-
- if (insert) {
- prepare_type = VSH_PREPARE_DISK_XML_INSERT;
- action = "insert";
- }
+ const char *name = NULL;
+ virNodeDevicePtr device;
+ char *xml;
- if (update || (!eject && !insert)) {
- prepare_type = VSH_PREPARE_DISK_XML_UPDATE;
- action = "update";
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+ if (vshCommandOptString(cmd, "device", &name) <= 0)
+ return false;
+ if (!(device = virNodeDeviceLookupByName(ctl->conn, name))) {
+ vshError(ctl, "%s '%s'", _("Could not find matching device"), name);
+ return false;
}
- if (current) {
- if (live || config) {
- vshError(ctl, "%s", _("--current must be specified exclusively"));
- return false;
- }
- flags = VIR_DOMAIN_AFFECT_CURRENT;
- } else {
- if (config)
- flags |= VIR_DOMAIN_AFFECT_CONFIG;
- if (live)
- flags |= VIR_DOMAIN_AFFECT_LIVE;
+ xml = virNodeDeviceGetXMLDesc(device, 0);
+ if (!xml) {
+ virNodeDeviceFree(device);
+ return false;
}
- if (force)
- flags |= VIR_DOMAIN_DEVICE_MODIFY_FORCE;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- goto cleanup;
-
- if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
- goto cleanup;
-
- if (vshCommandOptString(cmd, "path", &path) <= 0)
- goto cleanup;
-
- if (vshCommandOptString(cmd, "source", &source) < 0)
- goto cleanup;
+ vshPrint(ctl, "%s\n", xml);
+ VIR_FREE(xml);
+ virNodeDeviceFree(device);
+ return true;
+}
- if (insert && !source) {
- vshError(ctl, "%s", _("No disk source specified for inserting"));
- goto cleanup;
- }
+/*
+ * "nodedev-detach" command
+ */
+static const vshCmdInfo info_node_device_detach[] = {
+ {"help", N_("detach node device from its device driver")},
+ {"desc", N_("Detach node device from its device driver before assigning to a domain.")},
+ {NULL, NULL}
+};
- if (flags & VIR_DOMAIN_AFFECT_CONFIG)
- doc = virDomainGetXMLDesc(dom, VIR_DOMAIN_XML_INACTIVE);
- else
- doc = virDomainGetXMLDesc(dom, 0);
- if (!doc)
- goto cleanup;
- if (!(disk_node = vshFindDisk(doc, path, VSH_FIND_DISK_CHANGEABLE)))
- goto cleanup;
+static const vshCmdOptDef opts_node_device_detach[] = {
+ {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("device key")},
+ {NULL, 0, 0, NULL}
+};
- if (!(disk_xml = vshPrepareDiskXML(disk_node, source, path, prepare_type)))
- goto cleanup;
+static bool
+cmdNodeDeviceDetach(vshControl *ctl, const vshCmd *cmd)
+{
+ const char *name = NULL;
+ virNodeDevicePtr device;
+ bool ret = true;
- if (virDomainUpdateDeviceFlags(dom, disk_xml, flags) != 0) {
- vshError(ctl, _("Failed to complete action %s on media"), action);
- goto cleanup;
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+ if (vshCommandOptString(cmd, "device", &name) <= 0)
+ return false;
+ if (!(device = virNodeDeviceLookupByName(ctl->conn, name))) {
+ vshError(ctl, "%s '%s'", _("Could not find matching device"), name);
+ return false;
}
- vshPrint(ctl, _("succeeded to complete action %s on media\n"), action);
- ret = true;
-
-cleanup:
- VIR_FREE(doc);
- xmlFreeNode(disk_node);
- VIR_FREE(disk_xml);
- if (dom)
- virDomainFree(dom);
+ /* Yes, our public API is misspelled. At least virsh can accept
+ * either spelling. */
+ if (virNodeDeviceDettach(device) == 0) {
+ vshPrint(ctl, _("Device %s detached\n"), name);
+ } else {
+ vshError(ctl, _("Failed to detach device %s"), name);
+ ret = false;
+ }
+ virNodeDeviceFree(device);
return ret;
}
/*
- * "cpu-compare" command
+ * "nodedev-reattach" command
*/
-static const vshCmdInfo info_cpu_compare[] = {
- {"help", N_("compare host CPU with a CPU described by an XML file")},
- {"desc", N_("compare CPU with host CPU")},
+static const vshCmdInfo info_node_device_reattach[] = {
+ {"help", N_("reattach node device to its device driver")},
+ {"desc", N_("Reattach node device to its device driver once released by the domain.")},
{NULL, NULL}
};
-static const vshCmdOptDef opts_cpu_compare[] = {
- {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML CPU description")},
+
+static const vshCmdOptDef opts_node_device_reattach[] = {
+ {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("device key")},
{NULL, 0, 0, NULL}
};
static bool
-cmdCPUCompare(vshControl *ctl, const vshCmd *cmd)
+cmdNodeDeviceReAttach(vshControl *ctl, const vshCmd *cmd)
{
- const char *from = NULL;
- bool ret = false;
- char *buffer;
- int result;
- const char *snippet;
-
- xmlDocPtr xml = NULL;
- xmlXPathContextPtr ctxt = NULL;
- xmlBufferPtr xml_buf = NULL;
- xmlNodePtr node;
+ const char *name = NULL;
+ virNodeDevicePtr device;
+ bool ret = true;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
-
- if (vshCommandOptString(cmd, "file", &from) <= 0)
+ if (vshCommandOptString(cmd, "device", &name) <= 0)
return false;
-
- if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
- vshError(ctl, _("Failed to read file '%s' to compare"),
- from);
+ if (!(device = virNodeDeviceLookupByName(ctl->conn, name))) {
+ vshError(ctl, "%s '%s'", _("Could not find matching device"), name);
return false;
}
- /* try to extract the CPU element from as it would appear in a domain XML*/
- if (!(xml = virXMLParseStringCtxt(buffer, from, &ctxt)))
- goto cleanup;
-
- if ((node = virXPathNode("/cpu|"
- "/domain/cpu|"
- "/capabilities/host/cpu", ctxt))) {
- if (!(xml_buf = xmlBufferCreate())) {
- vshError(ctl, _("Can't create XML buffer to extract CPU element."));
- goto cleanup;
- }
-
- if (xmlNodeDump(xml_buf, xml, node, 0, 0) < 0) {
- vshError(ctl, _("Failed to extract CPU element snippet from domain XML."));
- goto cleanup;
- }
-
- snippet = (const char *) xmlBufferContent(xml_buf);
+ if (virNodeDeviceReAttach(device) == 0) {
+ vshPrint(ctl, _("Device %s re-attached\n"), name);
} else {
- vshError(ctl, _("File '%s' does not contain a element or is not "
- "a valid domain or capabilities XML"), from);
- goto cleanup;
+ vshError(ctl, _("Failed to re-attach device %s"), name);
+ ret = false;
}
+ virNodeDeviceFree(device);
+ return ret;
+}
- result = virConnectCompareCPU(ctl->conn, snippet, 0);
+/*
+ * "nodedev-reset" command
+ */
+static const vshCmdInfo info_node_device_reset[] = {
+ {"help", N_("reset node device")},
+ {"desc", N_("Reset node device before or after assigning to a domain.")},
+ {NULL, NULL}
+};
- switch (result) {
- case VIR_CPU_COMPARE_INCOMPATIBLE:
- vshPrint(ctl, _("CPU described in %s is incompatible with host CPU\n"),
- from);
- goto cleanup;
- break;
- case VIR_CPU_COMPARE_IDENTICAL:
- vshPrint(ctl, _("CPU described in %s is identical to host CPU\n"),
- from);
- break;
+static const vshCmdOptDef opts_node_device_reset[] = {
+ {"device", VSH_OT_DATA, VSH_OFLAG_REQ, N_("device key")},
+ {NULL, 0, 0, NULL}
+};
- case VIR_CPU_COMPARE_SUPERSET:
- vshPrint(ctl, _("Host CPU is a superset of CPU described in %s\n"),
- from);
- break;
+static bool
+cmdNodeDeviceReset(vshControl *ctl, const vshCmd *cmd)
+{
+ const char *name = NULL;
+ virNodeDevicePtr device;
+ bool ret = true;
- case VIR_CPU_COMPARE_ERROR:
- default:
- vshError(ctl, _("Failed to compare host CPU with %s"), from);
- goto cleanup;
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+ if (vshCommandOptString(cmd, "device", &name) <= 0)
+ return false;
+ if (!(device = virNodeDeviceLookupByName(ctl->conn, name))) {
+ vshError(ctl, "%s '%s'", _("Could not find matching device"), name);
+ return false;
}
- ret = true;
-
-cleanup:
- VIR_FREE(buffer);
- xmlBufferFree(xml_buf);
- xmlXPathFreeContext(ctxt);
- xmlFreeDoc(xml);
-
+ if (virNodeDeviceReset(device) == 0) {
+ vshPrint(ctl, _("Device %s reset\n"), name);
+ } else {
+ vshError(ctl, _("Failed to reset device %s"), name);
+ ret = false;
+ }
+ virNodeDeviceFree(device);
return ret;
}
/*
- * "cpu-baseline" command
+ * "hostname" command
*/
-static const vshCmdInfo info_cpu_baseline[] = {
- {"help", N_("compute baseline CPU")},
- {"desc", N_("Compute baseline CPU for a set of given CPUs.")},
+static const vshCmdInfo info_hostname[] = {
+ {"help", N_("print the hypervisor hostname")},
+ {"desc", ""},
{NULL, NULL}
};
-static const vshCmdOptDef opts_cpu_baseline[] = {
- {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing XML CPU descriptions")},
- {NULL, 0, 0, NULL}
-};
-
static bool
-cmdCPUBaseline(vshControl *ctl, const vshCmd *cmd)
+cmdHostname(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
{
- const char *from = NULL;
- bool ret = false;
- char *buffer;
- char *result = NULL;
- const char **list = NULL;
- int count = 0;
-
- xmlDocPtr xml = NULL;
- xmlNodePtr *node_list = NULL;
- xmlXPathContextPtr ctxt = NULL;
- xmlBufferPtr xml_buf = NULL;
- virBuffer buf = VIR_BUFFER_INITIALIZER;
- int i;
+ char *hostname;
if (!vshConnectionUsability(ctl, ctl->conn))
return false;
- if (vshCommandOptString(cmd, "file", &from) <= 0)
- return false;
-
- if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
+ hostname = virConnectGetHostname(ctl->conn);
+ if (hostname == NULL) {
+ vshError(ctl, "%s", _("failed to get hostname"));
return false;
+ }
- /* add a separate container around the xml */
- virBufferStrcat(&buf, "", buffer, "", NULL);
- if (virBufferError(&buf))
- goto no_memory;
+ vshPrint (ctl, "%s\n", hostname);
+ VIR_FREE(hostname);
- VIR_FREE(buffer);
- buffer = virBufferContentAndReset(&buf);
+ return true;
+}
+/*
+ * "uri" command
+ */
+static const vshCmdInfo info_uri[] = {
+ {"help", N_("print the hypervisor canonical URI")},
+ {"desc", ""},
+ {NULL, NULL}
+};
- if (!(xml = virXMLParseStringCtxt(buffer, from, &ctxt)))
- goto cleanup;
+static bool
+cmdURI(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
+{
+ char *uri;
- if ((count = virXPathNodeSet("//cpu[not(ancestor::cpus)]",
- ctxt, &node_list)) == -1)
- goto cleanup;
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
- if (count == 0) {
- vshError(ctl, _("No host CPU specified in '%s'"), from);
- goto cleanup;
+ uri = virConnectGetURI(ctl->conn);
+ if (uri == NULL) {
+ vshError(ctl, "%s", _("failed to get URI"));
+ return false;
}
- list = vshCalloc(ctl, count, sizeof(const char *));
-
- if (!(xml_buf = xmlBufferCreate()))
- goto no_memory;
-
- for (i = 0; i < count; i++) {
- xmlBufferEmpty(xml_buf);
+ vshPrint(ctl, "%s\n", uri);
+ VIR_FREE(uri);
- if (xmlNodeDump(xml_buf, xml, node_list[i], 0, 0) < 0) {
- vshError(ctl, _("Failed to extract element"));
- goto cleanup;
- }
+ return true;
+}
- list[i] = vshStrdup(ctl, (const char *)xmlBufferContent(xml_buf));
- }
+/*
+ * "sysinfo" command
+ */
+static const vshCmdInfo info_sysinfo[] = {
+ {"help", N_("print the hypervisor sysinfo")},
+ {"desc",
+ N_("output an XML string for the hypervisor sysinfo, if available")},
+ {NULL, NULL}
+};
- result = virConnectBaselineCPU(ctl->conn, list, count, 0);
+static bool
+cmdSysinfo(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
+{
+ char *sysinfo;
- if (result) {
- vshPrint(ctl, "%s", result);
- ret = true;
- }
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
-cleanup:
- xmlXPathFreeContext(ctxt);
- xmlFreeDoc(xml);
- xmlBufferFree(xml_buf);
- VIR_FREE(result);
- if (list != NULL && count > 0) {
- for (i = 0; i < count; i++)
- VIR_FREE(list[i]);
+ sysinfo = virConnectGetSysinfo(ctl->conn, 0);
+ if (sysinfo == NULL) {
+ vshError(ctl, "%s", _("failed to get sysinfo"));
+ return false;
}
- VIR_FREE(list);
- VIR_FREE(buffer);
- return ret;
+ vshPrint(ctl, "%s", sysinfo);
+ VIR_FREE(sysinfo);
-no_memory:
- vshError(ctl, "%s", _("Out of memory"));
- ret = false;
- goto cleanup;
+ return true;
}
/* Common code for the edit / net-edit / pool-edit functions which follow. */
@@ -14849,62 +6887,6 @@ cmdEcho(vshControl *ctl, const vshCmd *cmd)
return true;
}
-/*
- * "edit" command
- */
-static const vshCmdInfo info_edit[] = {
- {"help", N_("edit XML configuration for a domain")},
- {"desc", N_("Edit the XML configuration for a domain.")},
- {NULL, NULL}
-};
-
-static const vshCmdOptDef opts_edit[] = {
- {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
- {NULL, 0, 0, NULL}
-};
-
-static bool
-cmdEdit(vshControl *ctl, const vshCmd *cmd)
-{
- bool ret = false;
- virDomainPtr dom = NULL;
- virDomainPtr dom_edited = NULL;
- unsigned int flags = VIR_DOMAIN_XML_SECURE | VIR_DOMAIN_XML_INACTIVE;
-
- if (!vshConnectionUsability(ctl, ctl->conn))
- goto cleanup;
-
- dom = vshCommandOptDomain(ctl, cmd, NULL);
- if (dom == NULL)
- goto cleanup;
-
-#define EDIT_GET_XML virDomainGetXMLDesc(dom, flags)
-#define EDIT_NOT_CHANGED \
- vshPrint(ctl, _("Domain %s XML configuration not changed.\n"), \
- virDomainGetName(dom)); \
- ret = true; goto edit_cleanup;
-#define EDIT_DEFINE \
- (dom_edited = virDomainDefineXML(ctl->conn, doc_edited))
-#define EDIT_FREE \
- if (dom_edited) \
- virDomainFree(dom_edited);
-#include "virsh-edit.c"
-
- vshPrint(ctl, _("Domain %s XML configuration edited.\n"),
- virDomainGetName(dom_edited));
-
- ret = true;
-
- cleanup:
- if (dom)
- virDomainFree(dom);
- if (dom_edited)
- virDomainFree(dom_edited);
-
- return ret;
-}
-
-
/*
* "net-edit" command
*/
@@ -18174,22 +10156,6 @@ vshFindTypedParamByName(const char *name, virTypedParameterPtr list, int count)
return NULL;
}
-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");
-}
-
static bool
vshConnectionUsability(vshControl *ctl, virConnectPtr conn)
{
@@ -19062,6 +11028,8 @@ vshParseArgv(vshControl *ctl, int argc, char **argv)
return true;
}
+#include "virsh-domain.c"
+
static const vshCmdDef domManagementCmds[] = {
{"attach-device", cmdAttachDevice, opts_attach_device,
info_attach_device, 0},