This patch factors out the vCPU count retrieval including fallback means into vshCPUCountCollect() and removes the duplicated code to retrieve individual counts. The --current flag (this flag is assumed by default) now works also with --maximum or --active without the need to explicitly specify the state of the domain that is requested. This patch also fixes the output of "virsh vcpucount domain" on inactive domains: Before: $ virsh vcpucount domain maximum config 4 error: Requested operation is not valid: domain is not running current config 4 error: Requested operation is not valid: domain is not running After: $virsh vcpucount domain maximum config 4 current config 4 .. and for transient domains too: Before: $ virsh vcpucount transient-domain error: Requested operation is not valid: cannot change persistent config of a transient domain maximum live 3 error: Requested operation is not valid: cannot change persistent config of a transient domain current live 1 After: $ virsh vcpucount transient-domain maximum live 3 current live 1 --- Notes: Version 2: - think of transient guests too - fix ordering of compatibility code to actually work - print error on specific requests that can't be fulfilled tools/virsh-domain.c | 267 +++++++++++++++++++++++---------------------------- 1 file changed, 121 insertions(+), 146 deletions(-) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 4d0cc8f..5726744 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -5066,7 +5066,7 @@ static const vshCmdOptDef opts_vcpucount[] = { }, {.name = "maximum", .type = VSH_OT_BOOL, - .help = N_("get maximum cap on vcpus") + .help = N_("get maximum count of vcpus") }, {.name = "active", .type = VSH_OT_BOOL, @@ -5087,36 +5087,102 @@ static const vshCmdOptDef opts_vcpucount[] = { {.name = NULL} }; +/** + * Collect the number of vCPUs for a guest possibly with fallback means. + * + * Returns the count of vCPUs for a domain and certain flags. Returns -2 in case + * of error. If @checkState is true, in case live stats can't be collected when + * the domain is inactive or persistent stats can't be collected if domain is + * transient -1 is returned and no error is reported. + */ + +static int +vshCPUCountCollect(vshControl *ctl, + virDomainPtr dom, + unsigned int flags, + bool checkState) +{ + int ret = -2; + virDomainInfo info; + int count; + char *def = NULL; + xmlDocPtr xml = NULL; + xmlXPathContextPtr ctxt = NULL; + + if (checkState && + ((flags & VIR_DOMAIN_AFFECT_LIVE && virDomainIsActive(dom) < 1) || + (flags & VIR_DOMAIN_AFFECT_CONFIG && virDomainIsPersistent(dom) < 1))) + return -1; + + /* In all cases, try the new API first; if it fails because we are talking + * to an older daemon, 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 ((count = virDomainGetVcpusFlags(dom, flags)) >= 0) + return count; + + /* fallback code */ + if (!(last_error->code == VIR_ERR_NO_SUPPORT || + last_error->code == VIR_ERR_INVALID_ARG)) + goto cleanup; + + if (!(flags & (VIR_DOMAIN_AFFECT_LIVE | VIR_DOMAIN_AFFECT_CONFIG)) && + virDomainIsActive(dom) == 1) + flags |= VIR_DOMAIN_AFFECT_LIVE; + + vshResetLibvirtError(); + + if (flags & VIR_DOMAIN_AFFECT_LIVE) { + if (flags & VIR_DOMAIN_VCPU_MAXIMUM) { + count = virDomainGetMaxVcpus(dom); + } else { + if (virDomainGetInfo(dom, &info) < 0) + goto cleanup; + + count = info.nrVirtCpu; + } + } else { + if (!(def = virDomainGetXMLDesc(dom, VIR_DOMAIN_XML_INACTIVE))) + goto cleanup; + + if (!(xml = virXMLParseStringCtxt(def, _("(domain_definition)"), &ctxt))) + goto cleanup; + + if (flags & VIR_DOMAIN_VCPU_MAXIMUM) { + if (virXPathInt("string(/domain/vcpus)", ctxt, &count) < 0) { + vshError(ctl, "%s", _("Failed to retrieve maximum vcpu count")); + goto cleanup; + } + } else { + if (virXPathInt("string(/domain/vcpus/@current)", + ctxt, &count) < 0) { + vshError(ctl, "%s", _("Failed to retrieve current vcpu count")); + goto cleanup; + } + } + } + + ret = count; +cleanup: + VIR_FREE(def); + xmlXPathFreeContext(ctxt); + xmlFreeDoc(xml); + + return ret; +} + static bool cmdVcpucount(vshControl *ctl, const vshCmd *cmd) { virDomainPtr dom; - bool ret = true; + bool ret = false; 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; - } + unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT; /* Backwards compatibility: prior to 0.9.4, * VIR_DOMAIN_AFFECT_CURRENT was unsupported, and --current meant @@ -5124,145 +5190,54 @@ cmdVcpucount(vshControl *ctl, const vshCmd *cmd) * --live' into the new '--active --live', while treating the new * '--maximum --current' correctly rather than rejecting it as * '--maximum --active'. */ - if (!maximum && !active && current) { + 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; - } + VSH_EXCLUSIVE_OPTIONS_VAR(current, live); + VSH_EXCLUSIVE_OPTIONS_VAR(current, config); + VSH_EXCLUSIVE_OPTIONS_VAR(active, maximum); + + if (live) + flags |= VIR_DOMAIN_AFFECT_LIVE; + if (config) + flags |= VIR_DOMAIN_AFFECT_CONFIG; + if (maximum) + flags |= VIR_DOMAIN_VCPU_MAXIMUM; 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) { - vshReportError(ctl); - ret = false; - } else { - vshPrint(ctl, "%d\n", count); - } - } + if (all) { + int conf_max = vshCPUCountCollect(ctl, dom, VIR_DOMAIN_AFFECT_CONFIG | + VIR_DOMAIN_VCPU_MAXIMUM, true); + int conf_cur = vshCPUCountCollect(ctl, dom, VIR_DOMAIN_AFFECT_CONFIG, true); + int live_max = vshCPUCountCollect(ctl, dom, VIR_DOMAIN_AFFECT_LIVE | + VIR_DOMAIN_VCPU_MAXIMUM, true); + int live_cur = vshCPUCountCollect(ctl, dom, VIR_DOMAIN_AFFECT_LIVE, true); - 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, "<vcpu"))) { - tmp = strchr(tmp, '>'); - if (!tmp || virStrToLong_i(tmp + 1, &tmp, 10, &count) < 0) - count = -1; - } - vshResetLibvirtError(); - VIR_FREE(xml); - } - - if (count < 0) { - vshReportError(ctl); - ret = false; - } else if (all) { - vshPrint(ctl, "%-12s %-12s %3d\n", _("maximum"), _("config"), - count); - } else { - vshPrint(ctl, "%d\n", count); - } - vshResetLibvirtError(); - } + if (conf_max == -2 || conf_cur == -2 || live_max == -2 || live_cur == -2) + goto cleanup; - 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); - } +#define PRINT_COUNT(VAR, MAX, STATE) if (VAR > 0) \ + vshPrint(ctl, "%-12s %-12s %3d\n", _(MAX), _(STATE), VAR) + PRINT_COUNT(conf_max, "maximum", "config"); + PRINT_COUNT(live_max, "maximum", "live"); + PRINT_COUNT(conf_cur, "current", "config"); + PRINT_COUNT(live_cur, "current", "live"); +#undef PRINT_COUNT - if (count < 0) { - vshReportError(ctl); - ret = false; - } else if (all) { - vshPrint(ctl, "%-12s %-12s %3d\n", _("maximum"), _("live"), - count); - } else { - vshPrint(ctl, "%d\n", count); - } - vshResetLibvirtError(); - } + } else { + int count = vshCPUCountCollect(ctl, dom, flags, false); - 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, "<vcpu"))) { - end = strchr(tmp, '>'); - 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) + goto cleanup; - if (count < 0) { - vshReportError(ctl); - ret = false; - } else if (all) { - vshPrint(ctl, "%-12s %-12s %3d\n", _("current"), _("config"), - count); - } else { - vshPrint(ctl, "%d\n", count); - } - vshResetLibvirtError(); + vshPrint(ctl, "%d\n", count); } - 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) { - vshReportError(ctl); - ret = false; - } else if (all) { - vshPrint(ctl, "%-12s %-12s %3d\n", _("current"), _("live"), - count); - } else { - vshPrint(ctl, "%d\n", count); - } - vshResetLibvirtError(); - } + ret = true; +cleanup: virDomainFree(dom); return ret; } -- 1.8.1.5 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list