mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-23 06:05:27 +00:00
fcd1c865e1
Since commit v4.3.0-336-gc84726fbdd all {hypervisor-,}cpu-{baseline,compare} commands use a generic vshExtractCPUDefXMLs helper for extracting individual CPU definitions from the provided input file. The helper wraps the input file in a <container> element so that several independent elements can be easily parsed from the file. This works fine except when the file starts with XML declaration (<?xml version="1.0" ... ?>) because the XML declaration cannot be put inside any element. In fact it has to be at the very beginning of the XML document without any preceding white space characters. We can just simply skip the XML declaration. https://bugzilla.redhat.com/show_bug.cgi?id=1592737 Signed-off-by: Jiri Denemark <jdenemar@redhat.com> Reviewed-by: Ján Tomko <jtomko@redhat.com>
1936 lines
52 KiB
C
1936 lines
52 KiB
C
/*
|
|
* virsh-host.c: Commands in "Host and Hypervisor" group.
|
|
*
|
|
* Copyright (C) 2005, 2007-2014 Red Hat, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see
|
|
* <http://www.gnu.org/licenses/>.
|
|
*
|
|
* Daniel Veillard <veillard@redhat.com>
|
|
* Karel Zak <kzak@redhat.com>
|
|
* Daniel P. Berrange <berrange@redhat.com>
|
|
*
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include "virsh-host.h"
|
|
|
|
#include <libxml/parser.h>
|
|
#include <libxml/tree.h>
|
|
#include <libxml/xpath.h>
|
|
#include <libxml/xmlsave.h>
|
|
|
|
#include "internal.h"
|
|
#include "virbitmap.h"
|
|
#include "virbuffer.h"
|
|
#include "viralloc.h"
|
|
#include "virxml.h"
|
|
#include "virtypedparam.h"
|
|
#include "virstring.h"
|
|
#include "virfile.h"
|
|
|
|
/*
|
|
* "capabilities" command
|
|
*/
|
|
static const vshCmdInfo info_capabilities[] = {
|
|
{.name = "help",
|
|
.data = N_("capabilities")
|
|
},
|
|
{.name = "desc",
|
|
.data = N_("Returns capabilities of hypervisor/driver.")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static bool
|
|
cmdCapabilities(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
|
|
{
|
|
char *caps;
|
|
virshControlPtr priv = ctl->privData;
|
|
|
|
if ((caps = virConnectGetCapabilities(priv->conn)) == NULL) {
|
|
vshError(ctl, "%s", _("failed to get capabilities"));
|
|
return false;
|
|
}
|
|
vshPrint(ctl, "%s\n", caps);
|
|
VIR_FREE(caps);
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* "domcapabilities" command
|
|
*/
|
|
static const vshCmdInfo info_domcapabilities[] = {
|
|
{.name = "help",
|
|
.data = N_("domain capabilities")
|
|
},
|
|
{.name = "desc",
|
|
.data = N_("Returns capabilities of emulator with respect to host and libvirt.")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static const vshCmdOptDef opts_domcapabilities[] = {
|
|
{.name = "virttype",
|
|
.type = VSH_OT_STRING,
|
|
.help = N_("virtualization type (/domain/@type)"),
|
|
},
|
|
{.name = "emulatorbin",
|
|
.type = VSH_OT_STRING,
|
|
.help = N_("path to emulator binary (/domain/devices/emulator)"),
|
|
},
|
|
{.name = "arch",
|
|
.type = VSH_OT_STRING,
|
|
.help = N_("domain architecture (/domain/os/type/@arch)"),
|
|
},
|
|
{.name = "machine",
|
|
.type = VSH_OT_STRING,
|
|
.help = N_("machine type (/domain/os/type/@machine)"),
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static bool
|
|
cmdDomCapabilities(vshControl *ctl, const vshCmd *cmd)
|
|
{
|
|
bool ret = false;
|
|
char *caps = NULL;
|
|
const char *virttype = NULL;
|
|
const char *emulatorbin = NULL;
|
|
const char *arch = NULL;
|
|
const char *machine = NULL;
|
|
const unsigned int flags = 0; /* No flags so far */
|
|
virshControlPtr priv = ctl->privData;
|
|
|
|
if (vshCommandOptStringReq(ctl, cmd, "virttype", &virttype) < 0 ||
|
|
vshCommandOptStringReq(ctl, cmd, "emulatorbin", &emulatorbin) < 0 ||
|
|
vshCommandOptStringReq(ctl, cmd, "arch", &arch) < 0 ||
|
|
vshCommandOptStringReq(ctl, cmd, "machine", &machine) < 0)
|
|
return ret;
|
|
|
|
caps = virConnectGetDomainCapabilities(priv->conn, emulatorbin,
|
|
arch, machine, virttype, flags);
|
|
if (!caps) {
|
|
vshError(ctl, "%s", _("failed to get emulator capabilities"));
|
|
goto cleanup;
|
|
}
|
|
|
|
vshPrint(ctl, "%s\n", caps);
|
|
ret = true;
|
|
cleanup:
|
|
VIR_FREE(caps);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* "freecell" command
|
|
*/
|
|
static const vshCmdInfo info_freecell[] = {
|
|
{.name = "help",
|
|
.data = N_("NUMA free memory")
|
|
},
|
|
{.name = "desc",
|
|
.data = N_("display available free memory for the NUMA cell.")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static const vshCmdOptDef opts_freecell[] = {
|
|
{.name = "cellno",
|
|
.type = VSH_OT_INT,
|
|
.completer = virshCellnoCompleter,
|
|
.help = N_("NUMA cell number")
|
|
},
|
|
{.name = "all",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("show free memory for all NUMA cells")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static bool
|
|
cmdFreecell(vshControl *ctl, const vshCmd *cmd)
|
|
{
|
|
bool ret = false;
|
|
int cell = -1;
|
|
unsigned long long memory = 0;
|
|
xmlNodePtr *nodes = NULL;
|
|
unsigned long nodes_cnt;
|
|
unsigned long *nodes_id = NULL;
|
|
unsigned long long *nodes_free = NULL;
|
|
bool all = vshCommandOptBool(cmd, "all");
|
|
bool cellno = vshCommandOptBool(cmd, "cellno");
|
|
size_t i;
|
|
char *cap_xml = NULL;
|
|
xmlDocPtr xml = NULL;
|
|
xmlXPathContextPtr ctxt = NULL;
|
|
virshControlPtr priv = ctl->privData;
|
|
|
|
VSH_EXCLUSIVE_OPTIONS_VAR(all, cellno);
|
|
|
|
if (cellno && vshCommandOptInt(ctl, cmd, "cellno", &cell) < 0)
|
|
return false;
|
|
|
|
if (all) {
|
|
if (!(cap_xml = virConnectGetCapabilities(priv->conn))) {
|
|
vshError(ctl, "%s", _("unable to get node capabilities"));
|
|
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 (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_ulp(val, NULL, 10, &id)) {
|
|
vshError(ctl, "%s", _("conversion from string failed"));
|
|
VIR_FREE(val);
|
|
goto cleanup;
|
|
}
|
|
VIR_FREE(val);
|
|
nodes_id[i] = id;
|
|
if (virNodeGetCellsFreeMemory(priv->conn, &(nodes_free[i]),
|
|
id, 1) != 1) {
|
|
vshError(ctl, _("failed to get free memory for NUMA node "
|
|
"number: %lu"), id);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
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 (cellno) {
|
|
if (virNodeGetCellsFreeMemory(priv->conn, &memory, cell, 1) != 1)
|
|
goto cleanup;
|
|
|
|
vshPrint(ctl, "%d: %llu KiB\n", cell, (memory/1024));
|
|
} else {
|
|
if ((memory = virNodeGetFreeMemory(priv->conn)) == 0)
|
|
goto cleanup;
|
|
|
|
vshPrint(ctl, "%s: %llu KiB\n", _("Total"), (memory/1024));
|
|
}
|
|
}
|
|
|
|
ret = true;
|
|
|
|
cleanup:
|
|
xmlXPathFreeContext(ctxt);
|
|
xmlFreeDoc(xml);
|
|
VIR_FREE(nodes);
|
|
VIR_FREE(nodes_free);
|
|
VIR_FREE(nodes_id);
|
|
VIR_FREE(cap_xml);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* "freepages" command
|
|
*/
|
|
static const vshCmdInfo info_freepages[] = {
|
|
{.name = "help",
|
|
.data = N_("NUMA free pages")
|
|
},
|
|
{.name = "desc",
|
|
.data = N_("display available free pages for the NUMA cell.")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static const vshCmdOptDef opts_freepages[] = {
|
|
{.name = "cellno",
|
|
.type = VSH_OT_INT,
|
|
.completer = virshCellnoCompleter,
|
|
.help = N_("NUMA cell number")
|
|
},
|
|
{.name = "pagesize",
|
|
.type = VSH_OT_INT,
|
|
.completer = virshAllocpagesPagesizeCompleter,
|
|
.help = N_("page size (in kibibytes)")
|
|
},
|
|
{.name = "all",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("show free pages for all NUMA cells")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static int
|
|
vshPageSizeSorter(const void *a, const void *b)
|
|
{
|
|
unsigned int pa = *(unsigned int *)a;
|
|
unsigned int pb = *(unsigned int *)b;
|
|
|
|
return pa - pb;
|
|
}
|
|
|
|
static bool
|
|
cmdFreepages(vshControl *ctl, const vshCmd *cmd)
|
|
{
|
|
bool ret = false;
|
|
unsigned int npages;
|
|
unsigned int *pagesize = NULL;
|
|
unsigned long long bytes = 0;
|
|
unsigned int kibibytes = 0;
|
|
int cell;
|
|
unsigned long long *counts = NULL;
|
|
size_t i, j;
|
|
xmlNodePtr *nodes = NULL;
|
|
int nodes_cnt;
|
|
char *cap_xml = NULL;
|
|
xmlDocPtr doc = NULL;
|
|
xmlXPathContextPtr ctxt = NULL;
|
|
bool all = vshCommandOptBool(cmd, "all");
|
|
bool cellno = vshCommandOptBool(cmd, "cellno");
|
|
bool pagesz = vshCommandOptBool(cmd, "pagesize");
|
|
virshControlPtr priv = ctl->privData;
|
|
|
|
VSH_EXCLUSIVE_OPTIONS_VAR(all, cellno);
|
|
|
|
if (vshCommandOptScaledInt(ctl, cmd, "pagesize", &bytes, 1024, UINT_MAX) < 0)
|
|
goto cleanup;
|
|
kibibytes = VIR_DIV_UP(bytes, 1024);
|
|
|
|
if (all) {
|
|
if (!(cap_xml = virConnectGetCapabilities(priv->conn))) {
|
|
vshError(ctl, "%s", _("unable to get node capabilities"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(doc = virXMLParseStringCtxt(cap_xml, _("capabilities"), &ctxt))) {
|
|
vshError(ctl, "%s", _("unable to parse node capabilities"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!pagesz) {
|
|
nodes_cnt = virXPathNodeSet("/capabilities/host/cpu/pages", ctxt, &nodes);
|
|
|
|
if (nodes_cnt <= 0) {
|
|
/* Some drivers don't export page sizes under the
|
|
* XPath above. Do another trick to get them. */
|
|
nodes_cnt = virXPathNodeSet("/capabilities/host/topology/cells/cell/pages",
|
|
ctxt, &nodes);
|
|
if (nodes_cnt <= 0) {
|
|
vshError(ctl, "%s", _("could not get information about "
|
|
"supported page sizes"));
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
pagesize = vshCalloc(ctl, nodes_cnt, sizeof(*pagesize));
|
|
|
|
for (i = 0; i < nodes_cnt; i++) {
|
|
char *val = virXMLPropString(nodes[i], "size");
|
|
|
|
if (virStrToLong_uip(val, NULL, 10, &pagesize[i]) < 0) {
|
|
vshError(ctl, _("unable to parse page size: %s"), val);
|
|
VIR_FREE(val);
|
|
goto cleanup;
|
|
}
|
|
|
|
VIR_FREE(val);
|
|
}
|
|
|
|
/* Here, if we've done the trick few lines above,
|
|
* @pagesize array will contain duplicates. We should
|
|
* remove them otherwise not very nice output will be
|
|
* produced. */
|
|
qsort(pagesize, nodes_cnt, sizeof(*pagesize), vshPageSizeSorter);
|
|
|
|
for (i = 0; i < nodes_cnt - 1;) {
|
|
if (pagesize[i] == pagesize[i + 1]) {
|
|
memmove(pagesize + i, pagesize + i + 1,
|
|
(nodes_cnt - i + 1) * sizeof(*pagesize));
|
|
nodes_cnt--;
|
|
} else {
|
|
i++;
|
|
}
|
|
}
|
|
|
|
npages = nodes_cnt;
|
|
VIR_FREE(nodes);
|
|
} else {
|
|
pagesize = vshMalloc(ctl, sizeof(*pagesize));
|
|
pagesize[0] = kibibytes;
|
|
npages = 1;
|
|
}
|
|
|
|
counts = vshCalloc(ctl, npages, sizeof(*counts));
|
|
|
|
nodes_cnt = virXPathNodeSet("/capabilities/host/topology/cells/cell",
|
|
ctxt, &nodes);
|
|
for (i = 0; i < nodes_cnt; i++) {
|
|
char *val = virXMLPropString(nodes[i], "id");
|
|
|
|
if (virStrToLong_i(val, NULL, 10, &cell) < 0) {
|
|
vshError(ctl, _("unable to parse numa node id: %s"), val);
|
|
VIR_FREE(val);
|
|
goto cleanup;
|
|
}
|
|
VIR_FREE(val);
|
|
|
|
if (virNodeGetFreePages(priv->conn, npages, pagesize,
|
|
cell, 1, counts, 0) < 0)
|
|
goto cleanup;
|
|
|
|
vshPrint(ctl, _("Node %d:\n"), cell);
|
|
for (j = 0; j < npages; j++)
|
|
vshPrint(ctl, "%uKiB: %lld\n", pagesize[j], counts[j]);
|
|
vshPrint(ctl, "%c", '\n');
|
|
}
|
|
|
|
} else {
|
|
if (!cellno) {
|
|
vshError(ctl, "%s", _("missing cellno argument"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (vshCommandOptInt(ctl, cmd, "cellno", &cell) < 0)
|
|
goto cleanup;
|
|
|
|
if (cell < -1) {
|
|
vshError(ctl, "%s",
|
|
_("cell number must be non-negative integer or -1"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!pagesz) {
|
|
vshError(ctl, "%s", _("missing pagesize argument"));
|
|
goto cleanup;
|
|
}
|
|
|
|
/* page size is expected in kibibytes */
|
|
pagesize = vshMalloc(ctl, sizeof(*pagesize));
|
|
pagesize[0] = kibibytes;
|
|
|
|
counts = vshMalloc(ctl, sizeof(*counts));
|
|
|
|
if (virNodeGetFreePages(priv->conn, 1, pagesize,
|
|
cell, 1, counts, 0) < 0)
|
|
goto cleanup;
|
|
|
|
vshPrint(ctl, "%uKiB: %lld\n", *pagesize, counts[0]);
|
|
}
|
|
|
|
ret = true;
|
|
cleanup:
|
|
xmlXPathFreeContext(ctxt);
|
|
xmlFreeDoc(doc);
|
|
VIR_FREE(cap_xml);
|
|
VIR_FREE(nodes);
|
|
VIR_FREE(counts);
|
|
VIR_FREE(pagesize);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* "allocpages" command
|
|
*/
|
|
static const vshCmdInfo info_allocpages[] = {
|
|
{.name = "help",
|
|
.data = N_("Manipulate pages pool size")
|
|
},
|
|
{.name = "desc",
|
|
.data = N_("Allocate or free some pages in the pool for NUMA cell.")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
static const vshCmdOptDef opts_allocpages[] = {
|
|
{.name = "pagesize",
|
|
.type = VSH_OT_INT,
|
|
.flags = VSH_OFLAG_REQ,
|
|
.completer = virshAllocpagesPagesizeCompleter,
|
|
.help = N_("page size (in kibibytes)")
|
|
},
|
|
{.name = "pagecount",
|
|
.type = VSH_OT_INT,
|
|
.flags = VSH_OFLAG_REQ,
|
|
.help = N_("page count")
|
|
},
|
|
{.name = "cellno",
|
|
.type = VSH_OT_INT,
|
|
.completer = virshCellnoCompleter,
|
|
.help = N_("NUMA cell number")
|
|
},
|
|
{.name = "add",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("instead of setting new pool size add pages to it")
|
|
},
|
|
{.name = "all",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("set on all NUMA cells")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static bool
|
|
cmdAllocpages(vshControl *ctl, const vshCmd *cmd)
|
|
{
|
|
bool ret = false;
|
|
bool add = vshCommandOptBool(cmd, "add");
|
|
bool all = vshCommandOptBool(cmd, "all");
|
|
bool cellno = vshCommandOptBool(cmd, "cellno");
|
|
int startCell = -1;
|
|
int cellCount = 1;
|
|
unsigned int pageSizes[1];
|
|
unsigned long long pageCounts[1], tmp;
|
|
unsigned int flags = 0;
|
|
char *cap_xml = NULL;
|
|
xmlDocPtr xml = NULL;
|
|
xmlXPathContextPtr ctxt = NULL;
|
|
xmlNodePtr *nodes = NULL;
|
|
virshControlPtr priv = ctl->privData;
|
|
|
|
VSH_EXCLUSIVE_OPTIONS_VAR(all, cellno);
|
|
|
|
if (cellno && vshCommandOptInt(ctl, cmd, "cellno", &startCell) < 0)
|
|
return false;
|
|
|
|
if (vshCommandOptScaledInt(ctl, cmd, "pagesize", &tmp, 1024, UINT_MAX) < 0)
|
|
return false;
|
|
pageSizes[0] = VIR_DIV_UP(tmp, 1024);
|
|
|
|
if (vshCommandOptULongLong(ctl, cmd, "pagecount", &pageCounts[0]) < 0)
|
|
return false;
|
|
|
|
flags |= add ? VIR_NODE_ALLOC_PAGES_ADD : VIR_NODE_ALLOC_PAGES_SET;
|
|
|
|
if (all) {
|
|
unsigned long nodes_cnt;
|
|
size_t i;
|
|
|
|
if (!(cap_xml = virConnectGetCapabilities(priv->conn))) {
|
|
vshError(ctl, "%s", _("unable to get node capabilities"));
|
|
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 (nodes_cnt == -1) {
|
|
vshError(ctl, "%s", _("could not get information about "
|
|
"NUMA topology"));
|
|
goto cleanup;
|
|
}
|
|
|
|
for (i = 0; i < nodes_cnt; i++) {
|
|
unsigned long id;
|
|
char *val = virXMLPropString(nodes[i], "id");
|
|
if (virStrToLong_ulp(val, NULL, 10, &id)) {
|
|
vshError(ctl, "%s", _("conversion from string failed"));
|
|
VIR_FREE(val);
|
|
goto cleanup;
|
|
}
|
|
VIR_FREE(val);
|
|
|
|
if (virNodeAllocPages(priv->conn, 1, pageSizes,
|
|
pageCounts, id, 1, flags) < 0)
|
|
goto cleanup;
|
|
}
|
|
} else {
|
|
if (virNodeAllocPages(priv->conn, 1, pageSizes, pageCounts,
|
|
startCell, cellCount, flags) < 0)
|
|
goto cleanup;
|
|
}
|
|
|
|
ret = true;
|
|
cleanup:
|
|
xmlXPathFreeContext(ctxt);
|
|
xmlFreeDoc(xml);
|
|
VIR_FREE(nodes);
|
|
VIR_FREE(cap_xml);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* "maxvcpus" command
|
|
*/
|
|
static const vshCmdInfo info_maxvcpus[] = {
|
|
{.name = "help",
|
|
.data = N_("connection vcpu maximum")
|
|
},
|
|
{.name = "desc",
|
|
.data = N_("Show maximum number of virtual CPUs for guests on this connection.")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static const vshCmdOptDef opts_maxvcpus[] = {
|
|
{.name = "type",
|
|
.type = VSH_OT_STRING,
|
|
.help = N_("domain type")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static bool
|
|
cmdMaxvcpus(vshControl *ctl, const vshCmd *cmd)
|
|
{
|
|
const char *type = NULL;
|
|
int vcpus = -1;
|
|
char *caps = NULL;
|
|
xmlDocPtr xml = NULL;
|
|
xmlXPathContextPtr ctxt = NULL;
|
|
virshControlPtr priv = ctl->privData;
|
|
bool ret = false;
|
|
|
|
if (vshCommandOptStringReq(ctl, cmd, "type", &type) < 0)
|
|
return false;
|
|
|
|
if ((caps = virConnectGetDomainCapabilities(priv->conn, NULL, NULL, NULL,
|
|
type, 0))) {
|
|
if (!(xml = virXMLParseStringCtxt(caps, _("(domainCapabilities)"), &ctxt)))
|
|
goto cleanup;
|
|
|
|
ignore_value(virXPathInt("string(./vcpu[1]/@max)", ctxt, &vcpus));
|
|
} else {
|
|
vshResetLibvirtError();
|
|
}
|
|
|
|
if (vcpus < 0 && (vcpus = virConnectGetMaxVcpus(priv->conn, type)) < 0)
|
|
goto cleanup;
|
|
|
|
vshPrint(ctl, "%d\n", vcpus);
|
|
ret = true;
|
|
|
|
cleanup:
|
|
xmlXPathFreeContext(ctxt);
|
|
xmlFreeDoc(xml);
|
|
VIR_FREE(caps);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* "nodeinfo" command
|
|
*/
|
|
static const vshCmdInfo info_nodeinfo[] = {
|
|
{.name = "help",
|
|
.data = N_("node information")
|
|
},
|
|
{.name = "desc",
|
|
.data = N_("Returns basic information about the node.")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static bool
|
|
cmdNodeinfo(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
|
|
{
|
|
virNodeInfo info;
|
|
virshControlPtr priv = ctl->privData;
|
|
|
|
if (virNodeGetInfo(priv->conn, &info) < 0) {
|
|
vshError(ctl, "%s", _("failed to get node information"));
|
|
return false;
|
|
}
|
|
vshPrint(ctl, "%-20s %s\n", _("CPU model:"), info.model);
|
|
vshPrint(ctl, "%-20s %d\n", _("CPU(s):"), info.cpus);
|
|
if (info.mhz)
|
|
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);
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* "nodecpumap" command
|
|
*/
|
|
static const vshCmdInfo info_node_cpumap[] = {
|
|
{.name = "help",
|
|
.data = N_("node cpu map")
|
|
},
|
|
{.name = "desc",
|
|
.data = N_("Displays the node's total number of CPUs, the number of"
|
|
" online CPUs and the list of online CPUs.")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static const vshCmdOptDef opts_node_cpumap[] = {
|
|
{.name = "pretty",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("return human readable output")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static bool
|
|
cmdNodeCpuMap(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
|
|
{
|
|
int cpu, cpunum;
|
|
unsigned char *cpumap = NULL;
|
|
unsigned int online;
|
|
bool pretty = vshCommandOptBool(cmd, "pretty");
|
|
bool ret = false;
|
|
virshControlPtr priv = ctl->privData;
|
|
|
|
cpunum = virNodeGetCPUMap(priv->conn, &cpumap, &online, 0);
|
|
if (cpunum < 0) {
|
|
vshError(ctl, "%s", _("Unable to get cpu map"));
|
|
goto cleanup;
|
|
}
|
|
|
|
vshPrint(ctl, "%-15s %d\n", _("CPUs present:"), cpunum);
|
|
vshPrint(ctl, "%-15s %d\n", _("CPUs online:"), online);
|
|
|
|
vshPrint(ctl, "%-15s ", _("CPU map:"));
|
|
if (pretty) {
|
|
char *str = virBitmapDataFormat(cpumap, VIR_CPU_MAPLEN(cpunum));
|
|
|
|
if (!str)
|
|
goto cleanup;
|
|
vshPrint(ctl, "%s", str);
|
|
VIR_FREE(str);
|
|
} else {
|
|
for (cpu = 0; cpu < cpunum; cpu++)
|
|
vshPrint(ctl, "%c", VIR_CPU_USED(cpumap, cpu) ? 'y' : '-');
|
|
}
|
|
vshPrint(ctl, "\n");
|
|
|
|
ret = true;
|
|
|
|
cleanup:
|
|
VIR_FREE(cpumap);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* "nodecpustats" command
|
|
*/
|
|
static const vshCmdInfo info_nodecpustats[] = {
|
|
{.name = "help",
|
|
.data = N_("Prints cpu stats of the node.")
|
|
},
|
|
{.name = "desc",
|
|
.data = N_("Returns cpu stats of the node, in nanoseconds.")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static const vshCmdOptDef opts_node_cpustats[] = {
|
|
{.name = "cpu",
|
|
.type = VSH_OT_INT,
|
|
.help = N_("prints specified cpu statistics only.")
|
|
},
|
|
{.name = "percent",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("prints by percentage during 1 second.")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
typedef enum {
|
|
VIRSH_CPU_USER,
|
|
VIRSH_CPU_SYSTEM,
|
|
VIRSH_CPU_IDLE,
|
|
VIRSH_CPU_IOWAIT,
|
|
VIRSH_CPU_INTR,
|
|
VIRSH_CPU_USAGE,
|
|
VIRSH_CPU_LAST
|
|
} virshCPUStats;
|
|
|
|
VIR_ENUM_DECL(virshCPUStats);
|
|
VIR_ENUM_IMPL(virshCPUStats, VIRSH_CPU_LAST,
|
|
VIR_NODE_CPU_STATS_USER,
|
|
VIR_NODE_CPU_STATS_KERNEL,
|
|
VIR_NODE_CPU_STATS_IDLE,
|
|
VIR_NODE_CPU_STATS_IOWAIT,
|
|
VIR_NODE_CPU_STATS_INTR,
|
|
VIR_NODE_CPU_STATS_UTILIZATION);
|
|
|
|
const char *virshCPUOutput[] = {
|
|
N_("user:"),
|
|
N_("system:"),
|
|
N_("idle:"),
|
|
N_("iowait:"),
|
|
N_("intr:"),
|
|
N_("usage:")
|
|
};
|
|
|
|
static bool
|
|
cmdNodeCpuStats(vshControl *ctl, const vshCmd *cmd)
|
|
{
|
|
size_t i, j;
|
|
bool flag_percent = vshCommandOptBool(cmd, "percent");
|
|
int cpuNum = VIR_NODE_CPU_STATS_ALL_CPUS;
|
|
virNodeCPUStatsPtr params;
|
|
int nparams = 0;
|
|
bool ret = false;
|
|
unsigned long long cpu_stats[VIRSH_CPU_LAST] = { 0 };
|
|
bool present[VIRSH_CPU_LAST] = { false };
|
|
virshControlPtr priv = ctl->privData;
|
|
|
|
if (vshCommandOptInt(ctl, cmd, "cpu", &cpuNum) < 0)
|
|
return false;
|
|
|
|
if (virNodeGetCPUStats(priv->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;
|
|
}
|
|
|
|
memset(cpu_stats, 0, sizeof(cpu_stats));
|
|
params = vshCalloc(ctl, nparams, sizeof(*params));
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
if (virNodeGetCPUStats(priv->conn, cpuNum, params, &nparams, 0) != 0) {
|
|
vshError(ctl, "%s", _("Unable to get node cpu stats"));
|
|
goto cleanup;
|
|
}
|
|
|
|
for (j = 0; j < nparams; j++) {
|
|
int field = virshCPUStatsTypeFromString(params[j].field);
|
|
|
|
if (field < 0)
|
|
continue;
|
|
|
|
if (i == 0) {
|
|
cpu_stats[field] = params[j].value;
|
|
present[field] = true;
|
|
} else if (present[field]) {
|
|
cpu_stats[field] = params[j].value - cpu_stats[field];
|
|
}
|
|
}
|
|
|
|
if (present[VIRSH_CPU_USAGE] || !flag_percent)
|
|
break;
|
|
|
|
sleep(1);
|
|
}
|
|
|
|
if (!flag_percent) {
|
|
for (i = 0; i < VIRSH_CPU_USAGE; i++) {
|
|
if (present[i]) {
|
|
vshPrint(ctl, "%-15s %20llu\n", _(virshCPUOutput[i]),
|
|
cpu_stats[i]);
|
|
}
|
|
}
|
|
} else {
|
|
if (present[VIRSH_CPU_USAGE]) {
|
|
vshPrint(ctl, "%-15s %5.1llu%%\n",
|
|
_("usage:"), cpu_stats[VIRSH_CPU_USAGE]);
|
|
vshPrint(ctl, "%-15s %5.1llu%%\n",
|
|
_("idle:"), 100 - cpu_stats[VIRSH_CPU_USAGE]);
|
|
} else {
|
|
double usage, total_time = 0;
|
|
for (i = 0; i < VIRSH_CPU_USAGE; i++)
|
|
total_time += cpu_stats[i];
|
|
|
|
usage = (cpu_stats[VIRSH_CPU_USER] + cpu_stats[VIRSH_CPU_SYSTEM])
|
|
/ total_time * 100;
|
|
|
|
vshPrint(ctl, "%-15s %5.1lf%%\n", _("usage:"), usage);
|
|
for (i = 0; i < VIRSH_CPU_USAGE; i++) {
|
|
if (present[i]) {
|
|
vshPrint(ctl, "%-15s %5.1lf%%\n", _(virshCPUOutput[i]),
|
|
cpu_stats[i] / total_time * 100);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ret = true;
|
|
|
|
cleanup:
|
|
VIR_FREE(params);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* "nodememstats" command
|
|
*/
|
|
static const vshCmdInfo info_nodememstats[] = {
|
|
{.name = "help",
|
|
.data = N_("Prints memory stats of the node.")
|
|
},
|
|
{.name = "desc",
|
|
.data = N_("Returns memory stats of the node, in kilobytes.")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static const vshCmdOptDef opts_node_memstats[] = {
|
|
{.name = "cell",
|
|
.type = VSH_OT_INT,
|
|
.help = N_("prints specified cell statistics only.")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static bool
|
|
cmdNodeMemStats(vshControl *ctl, const vshCmd *cmd)
|
|
{
|
|
int nparams = 0;
|
|
size_t i;
|
|
int cellNum = VIR_NODE_MEMORY_STATS_ALL_CELLS;
|
|
virNodeMemoryStatsPtr params = NULL;
|
|
bool ret = false;
|
|
virshControlPtr priv = ctl->privData;
|
|
|
|
if (vshCommandOptInt(ctl, cmd, "cell", &cellNum) < 0)
|
|
return false;
|
|
|
|
/* get the number of memory parameters */
|
|
if (virNodeGetMemoryStats(priv->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(priv->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[] = {
|
|
{.name = "help",
|
|
.data = N_("suspend the host node for a given time duration")
|
|
},
|
|
{.name = "desc",
|
|
.data = N_("Suspend the host node for a given time duration "
|
|
"and attempt to resume thereafter.")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static const vshCmdOptDef opts_node_suspend[] = {
|
|
{.name = "target",
|
|
.type = VSH_OT_DATA,
|
|
.flags = VSH_OFLAG_REQ,
|
|
.help = N_("mem(Suspend-to-RAM), disk(Suspend-to-Disk), "
|
|
"hybrid(Hybrid-Suspend)")
|
|
},
|
|
{.name = "duration",
|
|
.type = VSH_OT_INT,
|
|
.flags = VSH_OFLAG_REQ,
|
|
.help = N_("Suspend duration in seconds, at least 60")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static bool
|
|
cmdNodeSuspend(vshControl *ctl, const vshCmd *cmd)
|
|
{
|
|
const char *target = NULL;
|
|
unsigned int suspendTarget;
|
|
long long duration;
|
|
virshControlPtr priv = ctl->privData;
|
|
|
|
if (vshCommandOptStringReq(ctl, cmd, "target", &target) < 0)
|
|
return false;
|
|
|
|
if (vshCommandOptLongLong(ctl, cmd, "duration", &duration) < 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"));
|
|
return false;
|
|
}
|
|
|
|
if (duration < 0) {
|
|
vshError(ctl, "%s", _("Invalid duration"));
|
|
return false;
|
|
}
|
|
|
|
if (virNodeSuspendForDuration(priv->conn, suspendTarget, duration, 0) < 0) {
|
|
vshError(ctl, "%s", _("The host was not suspended"));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* "sysinfo" command
|
|
*/
|
|
static const vshCmdInfo info_sysinfo[] = {
|
|
{.name = "help",
|
|
.data = N_("print the hypervisor sysinfo")
|
|
},
|
|
{.name = "desc",
|
|
.data = N_("output an XML string for the hypervisor sysinfo, if available")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static bool
|
|
cmdSysinfo(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
|
|
{
|
|
char *sysinfo;
|
|
virshControlPtr priv = ctl->privData;
|
|
|
|
sysinfo = virConnectGetSysinfo(priv->conn, 0);
|
|
if (sysinfo == NULL) {
|
|
vshError(ctl, "%s", _("failed to get sysinfo"));
|
|
return false;
|
|
}
|
|
|
|
vshPrint(ctl, "%s", sysinfo);
|
|
VIR_FREE(sysinfo);
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* "hostname" command
|
|
*/
|
|
static const vshCmdInfo info_hostname[] = {
|
|
{.name = "help",
|
|
.data = N_("print the hypervisor hostname")
|
|
},
|
|
{.name = "desc",
|
|
.data = ""
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static bool
|
|
cmdHostname(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
|
|
{
|
|
char *hostname;
|
|
virshControlPtr priv = ctl->privData;
|
|
|
|
hostname = virConnectGetHostname(priv->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[] = {
|
|
{.name = "help",
|
|
.data = N_("print the hypervisor canonical URI")
|
|
},
|
|
{.name = "desc",
|
|
.data = ""
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static bool
|
|
cmdURI(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
|
|
{
|
|
char *uri;
|
|
virshControlPtr priv = ctl->privData;
|
|
|
|
uri = virConnectGetURI(priv->conn);
|
|
if (uri == NULL) {
|
|
vshError(ctl, "%s", _("failed to get URI"));
|
|
return false;
|
|
}
|
|
|
|
vshPrint(ctl, "%s\n", uri);
|
|
VIR_FREE(uri);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/* Extracts the CPU definition XML strings from a file which may contain either
|
|
* - just the CPU definitions,
|
|
* - domain XMLs,
|
|
* - capabilities XMLs, or
|
|
* - domain capabilities XMLs.
|
|
*
|
|
* Returns NULL terminated string list.
|
|
*/
|
|
static char **
|
|
vshExtractCPUDefXMLs(vshControl *ctl,
|
|
const char *xmlFile)
|
|
{
|
|
char **cpus = NULL;
|
|
char *buffer = NULL;
|
|
char *xmlStr = NULL;
|
|
xmlDocPtr xml = NULL;
|
|
xmlXPathContextPtr ctxt = NULL;
|
|
xmlNodePtr *nodes = NULL;
|
|
char *doc;
|
|
size_t i;
|
|
int n;
|
|
|
|
if (virFileReadAll(xmlFile, VSH_MAX_XML_FILE, &buffer) < 0)
|
|
goto error;
|
|
|
|
/* Strip possible XML declaration */
|
|
if (STRPREFIX(buffer, "<?xml") && (doc = strstr(buffer, "?>")))
|
|
doc += 2;
|
|
else
|
|
doc = buffer;
|
|
|
|
if (virAsprintf(&xmlStr, "<container>%s</container>", doc) < 0)
|
|
goto error;
|
|
|
|
if (!(xml = virXMLParseStringCtxt(xmlStr, xmlFile, &ctxt)))
|
|
goto error;
|
|
|
|
n = virXPathNodeSet("/container/cpu|"
|
|
"/container/domain/cpu|"
|
|
"/container/capabilities/host/cpu|"
|
|
"/container/domainCapabilities/cpu/"
|
|
"mode[@name='host-model' and @supported='yes']",
|
|
ctxt, &nodes);
|
|
if (n < 0)
|
|
goto error;
|
|
|
|
if (n == 0) {
|
|
vshError(ctl, _("File '%s' does not contain any <cpu> element or "
|
|
"valid domain XML, host capabilities XML, or "
|
|
"domain capabilities XML"), xmlFile);
|
|
goto error;
|
|
}
|
|
|
|
cpus = vshCalloc(ctl, n + 1, sizeof(const char *));
|
|
|
|
for (i = 0; i < n; i++) {
|
|
/* If the user provided domain capabilities XML, we need to replace
|
|
* <mode ...> element with <cpu>. */
|
|
if (xmlStrEqual(nodes[i]->name, BAD_CAST "mode")) {
|
|
xmlNodeSetName(nodes[i], (const xmlChar *)"cpu");
|
|
while (nodes[i]->properties) {
|
|
if (xmlRemoveProp(nodes[i]->properties) < 0) {
|
|
vshError(ctl,
|
|
_("Cannot extract CPU definition from domain "
|
|
"capabilities XML"));
|
|
goto error;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!(cpus[i] = virXMLNodeToString(xml, nodes[i]))) {
|
|
vshSaveLibvirtError();
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
VIR_FREE(buffer);
|
|
VIR_FREE(xmlStr);
|
|
xmlFreeDoc(xml);
|
|
xmlXPathFreeContext(ctxt);
|
|
VIR_FREE(nodes);
|
|
return cpus;
|
|
|
|
error:
|
|
virStringListFree(cpus);
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
/*
|
|
* "cpu-compare" command
|
|
*/
|
|
static const vshCmdInfo info_cpu_compare[] = {
|
|
{.name = "help",
|
|
.data = N_("compare host CPU with a CPU described by an XML file")
|
|
},
|
|
{.name = "desc",
|
|
.data = N_("compare CPU with host CPU")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static const vshCmdOptDef opts_cpu_compare[] = {
|
|
VIRSH_COMMON_OPT_FILE(N_("file containing an XML CPU description")),
|
|
{.name = "error",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("report error if CPUs are incompatible")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static bool
|
|
cmdCPUCompare(vshControl *ctl, const vshCmd *cmd)
|
|
{
|
|
const char *from = NULL;
|
|
bool ret = false;
|
|
int result;
|
|
char **cpus = NULL;
|
|
unsigned int flags = 0;
|
|
virshControlPtr priv = ctl->privData;
|
|
|
|
if (vshCommandOptBool(cmd, "error"))
|
|
flags |= VIR_CONNECT_COMPARE_CPU_FAIL_INCOMPATIBLE;
|
|
|
|
if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0)
|
|
return false;
|
|
|
|
if (!(cpus = vshExtractCPUDefXMLs(ctl, from)))
|
|
return false;
|
|
|
|
result = virConnectCompareCPU(priv->conn, cpus[0], flags);
|
|
|
|
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:
|
|
virStringListFree(cpus);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* "cpu-baseline" command
|
|
*/
|
|
static const vshCmdInfo info_cpu_baseline[] = {
|
|
{.name = "help",
|
|
.data = N_("compute baseline CPU")
|
|
},
|
|
{.name = "desc",
|
|
.data = N_("Compute baseline CPU for a set of given CPUs.")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static const vshCmdOptDef opts_cpu_baseline[] = {
|
|
VIRSH_COMMON_OPT_FILE(N_("file containing XML CPU descriptions")),
|
|
{.name = "features",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("Show features that are part of the CPU model type")
|
|
},
|
|
{.name = "migratable",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("Do not include features that block migration")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static bool
|
|
cmdCPUBaseline(vshControl *ctl, const vshCmd *cmd)
|
|
{
|
|
const char *from = NULL;
|
|
bool ret = false;
|
|
char *result = NULL;
|
|
char **list = NULL;
|
|
unsigned int flags = 0;
|
|
virshControlPtr priv = ctl->privData;
|
|
|
|
if (vshCommandOptBool(cmd, "features"))
|
|
flags |= VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES;
|
|
if (vshCommandOptBool(cmd, "migratable"))
|
|
flags |= VIR_CONNECT_BASELINE_CPU_MIGRATABLE;
|
|
|
|
if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0)
|
|
return false;
|
|
|
|
if (!(list = vshExtractCPUDefXMLs(ctl, from)))
|
|
return false;
|
|
|
|
result = virConnectBaselineCPU(priv->conn, (const char **)list,
|
|
virStringListLength((const char **)list),
|
|
flags);
|
|
|
|
if (result) {
|
|
vshPrint(ctl, "%s", result);
|
|
ret = true;
|
|
}
|
|
|
|
VIR_FREE(result);
|
|
virStringListFree(list);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* "cpu-models" command
|
|
*/
|
|
static const vshCmdInfo info_cpu_models[] = {
|
|
{.name = "help",
|
|
.data = N_("CPU models")
|
|
},
|
|
{.name = "desc",
|
|
.data = N_("Get the CPU models for an arch.")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static const vshCmdOptDef opts_cpu_models[] = {
|
|
{.name = "arch",
|
|
.type = VSH_OT_DATA,
|
|
.flags = VSH_OFLAG_REQ,
|
|
.help = N_("architecture")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static bool
|
|
cmdCPUModelNames(vshControl *ctl, const vshCmd *cmd)
|
|
{
|
|
char **models;
|
|
size_t i;
|
|
int nmodels;
|
|
const char *arch = NULL;
|
|
virshControlPtr priv = ctl->privData;
|
|
|
|
if (vshCommandOptStringReq(ctl, cmd, "arch", &arch) < 0)
|
|
return false;
|
|
|
|
nmodels = virConnectGetCPUModelNames(priv->conn, arch, &models, 0);
|
|
if (nmodels < 0) {
|
|
vshError(ctl, "%s", _("failed to get CPU model names"));
|
|
return false;
|
|
}
|
|
|
|
if (nmodels == 0) {
|
|
vshPrintExtra(ctl, "%s\n", _("all CPU models are accepted"));
|
|
} else {
|
|
for (i = 0; i < nmodels; i++) {
|
|
vshPrint(ctl, "%s\n", models[i]);
|
|
VIR_FREE(models[i]);
|
|
}
|
|
}
|
|
VIR_FREE(models);
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* "version" command
|
|
*/
|
|
static const vshCmdInfo info_version[] = {
|
|
{.name = "help",
|
|
.data = N_("show version")
|
|
},
|
|
{.name = "desc",
|
|
.data = N_("Display the system version information.")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static const vshCmdOptDef opts_version[] = {
|
|
{.name = "daemon",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("report daemon version too")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
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;
|
|
virshControlPtr priv = ctl->privData;
|
|
|
|
hvType = virConnectGetType(priv->conn);
|
|
if (hvType == NULL) {
|
|
vshError(ctl, "%s", _("failed to get hypervisor type"));
|
|
return false;
|
|
}
|
|
|
|
includeVersion = LIBVIR_VERSION_NUMBER;
|
|
major = includeVersion / 1000000;
|
|
includeVersion %= 1000000;
|
|
minor = includeVersion / 1000;
|
|
rel = includeVersion % 1000;
|
|
vshPrint(ctl, _("Compiled against library: libvirt %d.%d.%d\n"),
|
|
major, minor, rel);
|
|
|
|
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: libvirt %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);
|
|
|
|
ret = virConnectGetVersion(priv->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;
|
|
|
|
vshPrint(ctl, _("Running hypervisor: %s %d.%d.%d\n"),
|
|
hvType, major, minor, rel);
|
|
}
|
|
|
|
if (vshCommandOptBool(cmd, "daemon")) {
|
|
ret = virConnectGetLibVersion(priv->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);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static const vshCmdInfo info_node_memory_tune[] = {
|
|
{"help", N_("Get or set node memory parameters")},
|
|
{"desc", N_("Get or set node memory parameters\n"
|
|
" To get the memory parameters, use following command: \n\n"
|
|
" virsh # node-memory-tune")},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
static const vshCmdOptDef opts_node_memory_tune[] = {
|
|
{.name = "shm-pages-to-scan",
|
|
.type = VSH_OT_INT,
|
|
.help = N_("number of pages to scan before the shared memory service "
|
|
"goes to sleep")
|
|
},
|
|
{.name = "shm-sleep-millisecs",
|
|
.type = VSH_OT_INT,
|
|
.help = N_("number of millisecs the shared memory service should "
|
|
"sleep before next scan")
|
|
},
|
|
{.name = "shm-merge-across-nodes",
|
|
.type = VSH_OT_INT,
|
|
.help = N_("Specifies if pages from different numa nodes can be merged")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static bool
|
|
cmdNodeMemoryTune(vshControl *ctl, const vshCmd *cmd)
|
|
{
|
|
virTypedParameterPtr params = NULL;
|
|
int nparams = 0;
|
|
int maxparams = 0;
|
|
unsigned int flags = 0;
|
|
unsigned int value;
|
|
bool ret = false;
|
|
int rc = -1;
|
|
size_t i;
|
|
virshControlPtr priv = ctl->privData;
|
|
|
|
if ((rc = vshCommandOptUInt(ctl, cmd, "shm-pages-to-scan", &value)) < 0) {
|
|
goto cleanup;
|
|
} else if (rc > 0) {
|
|
if (virTypedParamsAddUInt(¶ms, &nparams, &maxparams,
|
|
VIR_NODE_MEMORY_SHARED_PAGES_TO_SCAN,
|
|
value) < 0)
|
|
goto save_error;
|
|
}
|
|
|
|
if ((rc = vshCommandOptUInt(ctl, cmd, "shm-sleep-millisecs", &value)) < 0) {
|
|
goto cleanup;
|
|
} else if (rc > 0) {
|
|
if (virTypedParamsAddUInt(¶ms, &nparams, &maxparams,
|
|
VIR_NODE_MEMORY_SHARED_SLEEP_MILLISECS,
|
|
value) < 0)
|
|
goto save_error;
|
|
}
|
|
|
|
if ((rc = vshCommandOptUInt(ctl, cmd, "shm-merge-across-nodes", &value)) < 0) {
|
|
goto cleanup;
|
|
} else if (rc > 0) {
|
|
if (virTypedParamsAddUInt(¶ms, &nparams, &maxparams,
|
|
VIR_NODE_MEMORY_SHARED_MERGE_ACROSS_NODES,
|
|
value) < 0)
|
|
goto save_error;
|
|
}
|
|
|
|
if (nparams == 0) {
|
|
/* Get the number of memory parameters */
|
|
if (virNodeGetMemoryParameters(priv->conn, NULL, &nparams, flags) != 0) {
|
|
vshError(ctl, "%s",
|
|
_("Unable to get number of memory parameters"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (nparams == 0) {
|
|
ret = true;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Now go get all the memory parameters */
|
|
params = vshCalloc(ctl, nparams, sizeof(*params));
|
|
if (virNodeGetMemoryParameters(priv->conn, params, &nparams, flags) != 0) {
|
|
vshError(ctl, "%s", _("Unable to get memory parameters"));
|
|
goto cleanup;
|
|
}
|
|
|
|
/* XXX: Need to sort the returned params once new parameter
|
|
* fields not of shared memory are added.
|
|
*/
|
|
vshPrint(ctl, _("Shared memory:\n"));
|
|
for (i = 0; i < nparams; i++) {
|
|
char *str = vshGetTypedParamValue(ctl, ¶ms[i]);
|
|
vshPrint(ctl, "\t%-15s %s\n", params[i].field, str);
|
|
VIR_FREE(str);
|
|
}
|
|
} else {
|
|
if (virNodeSetMemoryParameters(priv->conn, params, nparams, flags) != 0)
|
|
goto error;
|
|
}
|
|
|
|
ret = true;
|
|
|
|
cleanup:
|
|
virTypedParamsFree(params, nparams);
|
|
return ret;
|
|
|
|
save_error:
|
|
vshSaveLibvirtError();
|
|
error:
|
|
vshError(ctl, "%s", _("Unable to change memory parameters"));
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
/*
|
|
* "hypervisor-cpu-compare" command
|
|
*/
|
|
static const vshCmdInfo info_hypervisor_cpu_compare[] = {
|
|
{.name = "help",
|
|
.data = N_("compare a CPU with the CPU created by a hypervisor on the host")
|
|
},
|
|
{.name = "desc",
|
|
.data = N_("compare CPU with hypervisor CPU")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static const vshCmdOptDef opts_hypervisor_cpu_compare[] = {
|
|
VIRSH_COMMON_OPT_FILE(N_("file containing an XML CPU description")),
|
|
{.name = "virttype",
|
|
.type = VSH_OT_STRING,
|
|
.help = N_("virtualization type (/domain/@type)"),
|
|
},
|
|
{.name = "emulator",
|
|
.type = VSH_OT_STRING,
|
|
.help = N_("path to emulator binary (/domain/devices/emulator)"),
|
|
},
|
|
{.name = "arch",
|
|
.type = VSH_OT_STRING,
|
|
.help = N_("CPU architecture (/domain/os/type/@arch)"),
|
|
},
|
|
{.name = "machine",
|
|
.type = VSH_OT_STRING,
|
|
.help = N_("machine type (/domain/os/type/@machine)"),
|
|
},
|
|
{.name = "error",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("report error if CPUs are incompatible")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static bool
|
|
cmdHypervisorCPUCompare(vshControl *ctl,
|
|
const vshCmd *cmd)
|
|
{
|
|
const char *from = NULL;
|
|
const char *virttype = NULL;
|
|
const char *emulator = NULL;
|
|
const char *arch = NULL;
|
|
const char *machine = NULL;
|
|
bool ret = false;
|
|
int result;
|
|
char **cpus = NULL;
|
|
unsigned int flags = 0;
|
|
virshControlPtr priv = ctl->privData;
|
|
|
|
if (vshCommandOptBool(cmd, "error"))
|
|
flags |= VIR_CONNECT_COMPARE_CPU_FAIL_INCOMPATIBLE;
|
|
|
|
if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0 ||
|
|
vshCommandOptStringReq(ctl, cmd, "virttype", &virttype) < 0 ||
|
|
vshCommandOptStringReq(ctl, cmd, "emulator", &emulator) < 0 ||
|
|
vshCommandOptStringReq(ctl, cmd, "arch", &arch) < 0 ||
|
|
vshCommandOptStringReq(ctl, cmd, "machine", &machine) < 0)
|
|
return false;
|
|
|
|
if (!(cpus = vshExtractCPUDefXMLs(ctl, from)))
|
|
return false;
|
|
|
|
result = virConnectCompareHypervisorCPU(priv->conn, emulator, arch,
|
|
machine, virttype, cpus[0], flags);
|
|
|
|
switch (result) {
|
|
case VIR_CPU_COMPARE_INCOMPATIBLE:
|
|
vshPrint(ctl,
|
|
_("CPU described in %s is incompatible with the CPU provided "
|
|
"by hypervisor on the host\n"),
|
|
from);
|
|
goto cleanup;
|
|
break;
|
|
|
|
case VIR_CPU_COMPARE_IDENTICAL:
|
|
vshPrint(ctl,
|
|
_("CPU described in %s is identical to the CPU provided by "
|
|
"hypervisor on the host\n"),
|
|
from);
|
|
break;
|
|
|
|
case VIR_CPU_COMPARE_SUPERSET:
|
|
vshPrint(ctl,
|
|
_("The CPU provided by hypervisor on the host is a superset "
|
|
"of CPU described in %s\n"),
|
|
from);
|
|
break;
|
|
|
|
case VIR_CPU_COMPARE_ERROR:
|
|
default:
|
|
vshError(ctl, _("Failed to compare hypervisor CPU with %s"), from);
|
|
goto cleanup;
|
|
}
|
|
|
|
ret = true;
|
|
|
|
cleanup:
|
|
virStringListFree(cpus);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* "hypervisor-cpu-baseline" command
|
|
*/
|
|
static const vshCmdInfo info_hypervisor_cpu_baseline[] = {
|
|
{.name = "help",
|
|
.data = N_("compute baseline CPU usable by a specific hypervisor")
|
|
},
|
|
{.name = "desc",
|
|
.data = N_("Compute baseline CPU for a set of given CPUs. The result "
|
|
"will be tailored to the specified hypervisor.")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static const vshCmdOptDef opts_hypervisor_cpu_baseline[] = {
|
|
VIRSH_COMMON_OPT_FILE(N_("file containing XML CPU descriptions")),
|
|
{.name = "virttype",
|
|
.type = VSH_OT_STRING,
|
|
.help = N_("virtualization type (/domain/@type)"),
|
|
},
|
|
{.name = "emulator",
|
|
.type = VSH_OT_STRING,
|
|
.help = N_("path to emulator binary (/domain/devices/emulator)"),
|
|
},
|
|
{.name = "arch",
|
|
.type = VSH_OT_STRING,
|
|
.help = N_("CPU architecture (/domain/os/type/@arch)"),
|
|
},
|
|
{.name = "machine",
|
|
.type = VSH_OT_STRING,
|
|
.help = N_("machine type (/domain/os/type/@machine)"),
|
|
},
|
|
{.name = "features",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("Show features that are part of the CPU model type")
|
|
},
|
|
{.name = "migratable",
|
|
.type = VSH_OT_BOOL,
|
|
.help = N_("Do not include features that block migration")
|
|
},
|
|
{.name = NULL}
|
|
};
|
|
|
|
static bool
|
|
cmdHypervisorCPUBaseline(vshControl *ctl,
|
|
const vshCmd *cmd)
|
|
{
|
|
const char *from = NULL;
|
|
const char *virttype = NULL;
|
|
const char *emulator = NULL;
|
|
const char *arch = NULL;
|
|
const char *machine = NULL;
|
|
bool ret = false;
|
|
char *result = NULL;
|
|
char **list = NULL;
|
|
unsigned int flags = 0;
|
|
virshControlPtr priv = ctl->privData;
|
|
|
|
if (vshCommandOptBool(cmd, "features"))
|
|
flags |= VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES;
|
|
if (vshCommandOptBool(cmd, "migratable"))
|
|
flags |= VIR_CONNECT_BASELINE_CPU_MIGRATABLE;
|
|
|
|
if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0 ||
|
|
vshCommandOptStringReq(ctl, cmd, "virttype", &virttype) < 0 ||
|
|
vshCommandOptStringReq(ctl, cmd, "emulator", &emulator) < 0 ||
|
|
vshCommandOptStringReq(ctl, cmd, "arch", &arch) < 0 ||
|
|
vshCommandOptStringReq(ctl, cmd, "machine", &machine) < 0)
|
|
return false;
|
|
|
|
if (!(list = vshExtractCPUDefXMLs(ctl, from)))
|
|
return false;
|
|
|
|
result = virConnectBaselineHypervisorCPU(priv->conn, emulator, arch,
|
|
machine, virttype,
|
|
(const char **)list,
|
|
virStringListLength((const char **)list),
|
|
flags);
|
|
|
|
if (result) {
|
|
vshPrint(ctl, "%s", result);
|
|
ret = true;
|
|
}
|
|
|
|
VIR_FREE(result);
|
|
virStringListFree(list);
|
|
return ret;
|
|
}
|
|
|
|
|
|
const vshCmdDef hostAndHypervisorCmds[] = {
|
|
{.name = "allocpages",
|
|
.handler = cmdAllocpages,
|
|
.opts = opts_allocpages,
|
|
.info = info_allocpages,
|
|
.flags = 0
|
|
},
|
|
{.name = "capabilities",
|
|
.handler = cmdCapabilities,
|
|
.opts = NULL,
|
|
.info = info_capabilities,
|
|
.flags = 0
|
|
},
|
|
{.name = "cpu-baseline",
|
|
.handler = cmdCPUBaseline,
|
|
.opts = opts_cpu_baseline,
|
|
.info = info_cpu_baseline,
|
|
.flags = 0
|
|
},
|
|
{.name = "cpu-compare",
|
|
.handler = cmdCPUCompare,
|
|
.opts = opts_cpu_compare,
|
|
.info = info_cpu_compare,
|
|
.flags = 0
|
|
},
|
|
{.name = "cpu-models",
|
|
.handler = cmdCPUModelNames,
|
|
.opts = opts_cpu_models,
|
|
.info = info_cpu_models,
|
|
.flags = 0
|
|
},
|
|
{.name = "domcapabilities",
|
|
.handler = cmdDomCapabilities,
|
|
.opts = opts_domcapabilities,
|
|
.info = info_domcapabilities,
|
|
.flags = 0
|
|
},
|
|
{.name = "freecell",
|
|
.handler = cmdFreecell,
|
|
.opts = opts_freecell,
|
|
.info = info_freecell,
|
|
.flags = 0
|
|
},
|
|
{.name = "freepages",
|
|
.handler = cmdFreepages,
|
|
.opts = opts_freepages,
|
|
.info = info_freepages,
|
|
.flags = 0
|
|
},
|
|
{.name = "hostname",
|
|
.handler = cmdHostname,
|
|
.opts = NULL,
|
|
.info = info_hostname,
|
|
.flags = 0
|
|
},
|
|
{.name = "hypervisor-cpu-baseline",
|
|
.handler = cmdHypervisorCPUBaseline,
|
|
.opts = opts_hypervisor_cpu_baseline,
|
|
.info = info_hypervisor_cpu_baseline,
|
|
.flags = 0
|
|
},
|
|
{.name = "hypervisor-cpu-compare",
|
|
.handler = cmdHypervisorCPUCompare,
|
|
.opts = opts_hypervisor_cpu_compare,
|
|
.info = info_hypervisor_cpu_compare,
|
|
.flags = 0
|
|
},
|
|
{.name = "maxvcpus",
|
|
.handler = cmdMaxvcpus,
|
|
.opts = opts_maxvcpus,
|
|
.info = info_maxvcpus,
|
|
.flags = 0
|
|
},
|
|
{.name = "node-memory-tune",
|
|
.handler = cmdNodeMemoryTune,
|
|
.opts = opts_node_memory_tune,
|
|
.info = info_node_memory_tune,
|
|
.flags = 0
|
|
},
|
|
{.name = "nodecpumap",
|
|
.handler = cmdNodeCpuMap,
|
|
.opts = opts_node_cpumap,
|
|
.info = info_node_cpumap,
|
|
.flags = 0
|
|
},
|
|
{.name = "nodecpustats",
|
|
.handler = cmdNodeCpuStats,
|
|
.opts = opts_node_cpustats,
|
|
.info = info_nodecpustats,
|
|
.flags = 0
|
|
},
|
|
{.name = "nodeinfo",
|
|
.handler = cmdNodeinfo,
|
|
.opts = NULL,
|
|
.info = info_nodeinfo,
|
|
.flags = 0
|
|
},
|
|
{.name = "nodememstats",
|
|
.handler = cmdNodeMemStats,
|
|
.opts = opts_node_memstats,
|
|
.info = info_nodememstats,
|
|
.flags = 0
|
|
},
|
|
{.name = "nodesuspend",
|
|
.handler = cmdNodeSuspend,
|
|
.opts = opts_node_suspend,
|
|
.info = info_nodesuspend,
|
|
.flags = 0
|
|
},
|
|
{.name = "sysinfo",
|
|
.handler = cmdSysinfo,
|
|
.opts = NULL,
|
|
.info = info_sysinfo,
|
|
.flags = 0
|
|
},
|
|
{.name = "uri",
|
|
.handler = cmdURI,
|
|
.opts = NULL,
|
|
.info = info_uri,
|
|
.flags = 0
|
|
},
|
|
{.name = "version",
|
|
.handler = cmdVersion,
|
|
.opts = opts_version,
|
|
.info = info_version,
|
|
.flags = 0
|
|
},
|
|
{.name = NULL}
|
|
};
|