1
0
mirror of https://gitlab.com/libvirt/libvirt.git synced 2025-03-30 18:50:18 +03:00

virsh: Add support for throttle group operations

Implement new throttle cmds

* Add new virsh cmds: domthrottlegroupset, domthrottlegrouplist,
  domthrottlegroupinfo, domthrottlegroupdel
* Add doc for new cmds at docs/manpages/virsh.rst
* Add cmd helper "virshDomainThrottleGroupCompleter", which is used by
  domthrottlegroupset, domthrottlegroupinfo, domthrottlegroupdel

Signed-off-by: Chun Feng Wu <danielwuwy@163.com>

* Update of code documentation comments.
* Reimplement Get throttle group from XML.

Signed-off-by: Harikumar Rajkumar <harirajkumar230@gmail.com>a

* Fixed memleaks
* Rewrote getter to avoid extra copies
* Simplified name extractor

Reviewed-by: Peter Krempa <pkrempa@redhat.com>
Signed-off-by: Peter Krempa <pkrempa@redhat.com>
Reviewed-by: Ján Tomko <jtomko@redhat.com>
This commit is contained in:
Chun Feng Wu 2025-02-19 22:27:21 +05:30 committed by Peter Krempa
parent f839ff5887
commit 180d41934c
4 changed files with 555 additions and 1 deletions

View File

@ -1164,6 +1164,140 @@ given, but *--current* is exclusive. For querying only one of *--live*,
is different depending on hypervisor.
domthrottlegroupset
-------------------
**Syntax:**
::
domthrottlegroupset domain group-name [[--config] [--live] | [--current]]
[[total-bytes-sec] | [read-bytes-sec] [write-bytes-sec]]
[[total-iops-sec] | [read-iops-sec] [write-iops-sec]]
[[total-bytes-sec-max] | [read-bytes-sec-max] [write-bytes-sec-max]]
[[total-iops-sec-max] | [read-iops-sec-max] [write-iops-sec-max]]
[[total-bytes-sec-max-length] |
[read-bytes-sec-max-length] [write-bytes-sec-max-length]]
[[total-iops-sec-max-length] |
[read-iops-sec-max-length] [write-iops-sec-max-length]]
[size-iops-sec]
Add or update a throttle group against specific *domain*.
*group-name* specifies a unique throttle group name, which defines limit, and
will be referenced by drives.
If no limit is specified, default them as all zeros, which will fail,
Otherwise, set limits with these flags:
*--total-bytes-sec* specifies total throughput limit as a scaled integer, the
default being bytes per second if no suffix is specified.
*--read-bytes-sec* specifies read throughput limit as a scaled integer, the
default being bytes per second if no suffix is specified.
*--write-bytes-sec* specifies write throughput limit as a scaled integer, the
default being bytes per second if no suffix is specified.
*--total-iops-sec* specifies total I/O operations limit per second.
*--read-iops-sec* specifies read I/O operations limit per second.
*--write-iops-sec* specifies write I/O operations limit per second.
*--total-bytes-sec-max* specifies maximum total throughput limit as a scaled
integer, the default being bytes per second if no suffix is specified
*--read-bytes-sec-max* specifies maximum read throughput limit as a scaled
integer, the default being bytes per second if no suffix is specified.
*--write-bytes-sec-max* specifies maximum write throughput limit as a scaled
integer, the default being bytes per second if no suffix is specified.
*--total-iops-sec-max* specifies maximum total I/O operations limit per second.
*--read-iops-sec-max* specifies maximum read I/O operations limit per second.
*--write-iops-sec-max* specifies maximum write I/O operations limit per second.
*--total-bytes-sec-max-length* specifies duration in seconds to allow maximum
total throughput limit.
*--read-bytes-sec-max-length* specifies duration in seconds to allow maximum
read throughput limit.
*--write-bytes-sec-max-length* specifies duration in seconds to allow maximum
write throughput limit.
*--total-iops-sec-max-length* specifies duration in seconds to allow maximum
total I/O operations limit.
*--read-iops-sec-max-length* specifies duration in seconds to allow maximum
read I/O operations limit.
*--write-iops-sec-max-length* specifies duration in seconds to allow maximum
write I/O operations limit.
*--size-iops-sec* specifies size I/O operations limit per second.
Bytes and iops values are independent, but setting only one value (such
as --read-bytes-sec) resets the other two in that category to unlimited.
An explicit 0 also clears any limit. A non-zero value for a given total
cannot be mixed with non-zero values for read or write.
It is up to the hypervisor to determine how to handle the length values.
For the QEMU hypervisor, if an I/O limit value or maximum value is set,
then the default value of 1 second will be displayed. Supplying a 0 will
reset the value back to the default.
If *--live* is specified, affect a running guest.
If *--config* is specified, affect the next start of a persistent guest.
If *--current* is specified, it is equivalent to either *--live* or
*--config*, depending on the current state of the guest.
When setting the disk io parameters both *--live* and *--config*
are specified, both live configuration and config are updated while setting
the description, but *--current* is exclusive. If no flag is specified, behavior
is different depending on hypervisor.
domthrottlegroupdel
-------------------
**Syntax:**
::
domthrottlegroupdel domain group-name [[--config] [--live] | [--current]]
Delete a Throttlegroup from the domain using the specified *group-name*.
If an Throttlegroup is currently referenced by a disk resource, then the attempt
to remove the Throttlegroup will fail.
If the *group-name* does not exist an error will occur.
If *--live* is specified, affect a running guest. If the guest is not
running an error is returned.
If *--config* is specified, affect the next start of a persistent guest.
If *--current* is specified, it is equivalent to either *--live* or
*--config*, depending on the current state of the guest.
domthrottlegroupinfo
--------------------
**Syntax:**
::
domthrottlegroupinfo domain group-name [[--config] [--live] | [--current]]
Display domain Throttlegroup information including I/O limits setting.
If *--live* is specified, get the Throttlegroup data from the running guest. If
the guest is not running, an error is returned.
If *--config* is specified, get the Throttlegroup data from the next start of
a persistent guest.
If *--current* is specified or *--live* and *--config* are not specified,
then get the Throttlegroup data based on the current guest state, which can
either be live or offline.
If both *--live* and *--config* are specified, the *--config* option takes
precedence on getting the current description.
domthrottlegrouplist
--------------------
**Syntax:**
::
domthrottlegrouplist domain [--inactive]
Print a table showing names of all throttle groups
associated with *domain*. If *--inactive* is specified, query the
Throttlegroup data that will be used on the next boot, rather than those
currently in use by a running domain.
blkiotune
---------

View File

@ -248,6 +248,51 @@ virshDomainMigrateDisksCompleter(vshControl *ctl,
}
char **
virshGetThrottleGroupNames(xmlXPathContext *ctxt)
{
g_auto(GStrv) groupNames = NULL;
g_autofree xmlNodePtr *groups = NULL;
int ngroups;
size_t i;
if ((ngroups = virXPathNodeSet("./throttlegroups/throttlegroup", ctxt, &groups)) < 0)
return NULL;
groupNames = g_new0(char *, ngroups + 1);
for (i = 0; i < ngroups; i++) {
ctxt->node = groups[i];
if (!(groupNames[i] = virXPathString("string(./group_name)", ctxt)))
return NULL;
}
return g_steal_pointer(&groupNames);
}
char **
virshDomainThrottleGroupCompleter(vshControl *ctl,
const vshCmd *cmd,
unsigned int flags)
{
virshControl *priv = ctl->privData;
g_autoptr(xmlDoc) xmldoc = NULL;
g_autoptr(xmlXPathContext) ctxt = NULL;
virCheckFlags(0, NULL);
if (!priv->conn || virConnectIsAlive(priv->conn) <= 0)
return NULL;
if (virshDomainGetXML(ctl, cmd, 0, &xmldoc, &ctxt) < 0)
return NULL;
return virshGetThrottleGroupNames(ctxt);
}
char **
virshDomainUndefineStorageDisksCompleter(vshControl *ctl,
const vshCmd *cmd,

View File

@ -21,6 +21,7 @@
#pragma once
#include "vsh.h"
#include <libxml/xpath.h>
char **
virshDomainNameCompleter(vshControl *ctl,
@ -41,6 +42,14 @@ virshDomainDiskTargetCompleter(vshControl *ctl,
const vshCmd *cmd,
unsigned int flags);
char **
virshGetThrottleGroupNames(xmlXPathContext *ctxt);
char **
virshDomainThrottleGroupCompleter(vshControl *ctl,
const vshCmd *cmd,
unsigned int flags);
char **
virshDomainInterfaceStateCompleter(vshControl *ctl,
const vshCmd *cmd,

View File

@ -1395,7 +1395,7 @@ static const vshCmdOptDef opts_blkdeviotune[] = {
VIRSH_COMMON_OPT_DOMAIN_CURRENT,
{.name = NULL}
};
#undef VSH_OPTS_IOTUNE
static bool
cmdBlkdeviotune(vshControl *ctl, const vshCmd *cmd)
@ -1533,6 +1533,348 @@ cmdBlkdeviotune(vshControl *ctl, const vshCmd *cmd)
goto cleanup;
}
/*
* "domthrottlegrouplist" command
*/
static const vshCmdInfo info_domthrottlegrouplist = {
.help = N_("list all domain throttlegroups."),
.desc = N_("Get the summary of throttle groups for a domain."),
};
static const vshCmdOptDef opts_domthrottlegrouplist[] = {
VIRSH_COMMON_OPT_DOMAIN_FULL(0),
{.name = "inactive",
.type = VSH_OT_BOOL,
.help = N_("get inactive rather than running configuration")
},
{.name = NULL}
};
static bool
cmdThrottleGroupList(vshControl *ctl,
const vshCmd *cmd)
{
unsigned int flags = 0;
g_autoptr(xmlDoc) xml = NULL;
g_autoptr(xmlXPathContext) ctxt = NULL;
g_auto(GStrv) groupNames = NULL;
char **n;
g_autoptr(vshTable) table = NULL;
if (vshCommandOptBool(cmd, "inactive"))
flags |= VIR_DOMAIN_XML_INACTIVE;
if (virshDomainGetXML(ctl, cmd, flags, &xml, &ctxt) < 0)
return false;
if (!(table = vshTableNew(_("Name"), NULL)))
return false;
if (!(groupNames = virshGetThrottleGroupNames(ctxt)))
return false;
for (n = groupNames; *n; n++) {
if (vshTableRowAppend(table, *n, NULL) < 0)
return false;
}
vshTablePrintToStdout(table, ctl);
return true;
}
/*
* "domthrottlegroupset" command
*/
static const vshCmdInfo info_domthrottlegroupset = {
.help = N_("Add or update a throttling group."),
.desc = N_("Add or updte a throttling group."),
};
static const vshCmdOptDef opts_domthrottlegroupset[] = {
VIRSH_COMMON_OPT_DOMAIN_FULL(0),
{.name = "group-name",
.type = VSH_OT_STRING,
.positional = true,
.required = true,
.completer = virshDomainThrottleGroupCompleter,
.help = N_("throttle group name")
},
VSH_OPTS_IOTUNE,
VIRSH_COMMON_OPT_DOMAIN_CONFIG,
VIRSH_COMMON_OPT_DOMAIN_LIVE,
VIRSH_COMMON_OPT_DOMAIN_CURRENT,
{.name = NULL}
};
#undef VSH_OPTS_IOTUNE
static bool
cmdThrottleGroupSet(vshControl *ctl,
const vshCmd *cmd)
{
g_autoptr(virshDomain) dom = NULL;
const char *group_name = NULL;
unsigned long long value;
int nparams = 0;
int maxparams = 0;
virTypedParameterPtr params = NULL;
unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
int rv = 0;
bool current = vshCommandOptBool(cmd, "current");
bool config = vshCommandOptBool(cmd, "config");
bool live = vshCommandOptBool(cmd, "live");
bool ret = false;
VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
if (config)
flags |= VIR_DOMAIN_AFFECT_CONFIG;
if (live)
flags |= VIR_DOMAIN_AFFECT_LIVE;
if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
goto cleanup;
#define VSH_SET_THROTTLE_GROUP_SCALED(PARAM, CONST) \
if ((rv = vshCommandOptScaledInt(ctl, cmd, #PARAM, &value, \
1, ULLONG_MAX)) < 0) { \
goto interror; \
} else if (rv > 0) { \
if (virTypedParamsAddULLong(&params, &nparams, &maxparams, \
VIR_DOMAIN_BLOCK_IOTUNE_##CONST, \
value) < 0) \
goto save_error; \
}
VSH_SET_THROTTLE_GROUP_SCALED(total-bytes-sec, TOTAL_BYTES_SEC);
VSH_SET_THROTTLE_GROUP_SCALED(read-bytes-sec, READ_BYTES_SEC);
VSH_SET_THROTTLE_GROUP_SCALED(write-bytes-sec, WRITE_BYTES_SEC);
VSH_SET_THROTTLE_GROUP_SCALED(total-bytes-sec-max, TOTAL_BYTES_SEC_MAX);
VSH_SET_THROTTLE_GROUP_SCALED(read-bytes-sec-max, READ_BYTES_SEC_MAX);
VSH_SET_THROTTLE_GROUP_SCALED(write-bytes-sec-max, WRITE_BYTES_SEC_MAX);
#undef VSH_SET_THROTTLE_GROUP_SCALED
#define VSH_SET_THROTTLE_GROUP(PARAM, CONST) \
if ((rv = vshCommandOptULongLong(ctl, cmd, #PARAM, &value)) < 0) { \
goto interror; \
} else if (rv > 0) { \
if (virTypedParamsAddULLong(&params, &nparams, &maxparams, \
VIR_DOMAIN_BLOCK_IOTUNE_##CONST, \
value) < 0) \
goto save_error; \
}
VSH_SET_THROTTLE_GROUP(total-iops-sec, TOTAL_IOPS_SEC);
VSH_SET_THROTTLE_GROUP(read-iops-sec, READ_IOPS_SEC);
VSH_SET_THROTTLE_GROUP(write-iops-sec, WRITE_IOPS_SEC);
VSH_SET_THROTTLE_GROUP(total-iops-sec-max, TOTAL_IOPS_SEC_MAX);
VSH_SET_THROTTLE_GROUP(read-iops-sec-max, READ_IOPS_SEC_MAX);
VSH_SET_THROTTLE_GROUP(write-iops-sec-max, WRITE_IOPS_SEC_MAX);
VSH_SET_THROTTLE_GROUP(size-iops-sec, SIZE_IOPS_SEC);
VSH_SET_THROTTLE_GROUP(total-bytes-sec-max-length, TOTAL_BYTES_SEC_MAX_LENGTH);
VSH_SET_THROTTLE_GROUP(read-bytes-sec-max-length, READ_BYTES_SEC_MAX_LENGTH);
VSH_SET_THROTTLE_GROUP(write-bytes-sec-max-length, WRITE_BYTES_SEC_MAX_LENGTH);
VSH_SET_THROTTLE_GROUP(total-iops-sec-max-length, TOTAL_IOPS_SEC_MAX_LENGTH);
VSH_SET_THROTTLE_GROUP(read-iops-sec-max-length, READ_IOPS_SEC_MAX_LENGTH);
VSH_SET_THROTTLE_GROUP(write-iops-sec-max-length, WRITE_IOPS_SEC_MAX_LENGTH);
#undef VSH_SET_THROTTLE_GROUP
if (vshCommandOptString(ctl, cmd, "group-name", &group_name) < 0) {
goto cleanup;
}
if (group_name) {
if (virTypedParamsAddString(&params, &nparams, &maxparams,
VIR_DOMAIN_BLOCK_IOTUNE_GROUP_NAME,
group_name) < 0)
goto save_error;
}
if (virDomainSetThrottleGroup(dom, group_name, params, nparams, flags) < 0)
goto error;
vshPrintExtra(ctl, "%s", _("Throttle group set successfully\n"));
ret = true;
cleanup:
virTypedParamsFree(params, nparams);
return ret;
save_error:
vshSaveLibvirtError();
error:
vshError(ctl, "%s", _("Unable to set throttle group"));
goto cleanup;
interror:
vshError(ctl, "%s", _("Unable to parse integer parameter"));
goto cleanup;
}
/*
* "domthrottlegroupdel" command
*/
static const vshCmdInfo info_domthrottlegroupdel = {
.help = N_("Delete a throttling group."),
.desc = N_("Delete a throttling group."),
};
static const vshCmdOptDef opts_domthrottlegroupdel[] = {
VIRSH_COMMON_OPT_DOMAIN_FULL(0),
{.name = "group-name",
.type = VSH_OT_STRING,
.positional = true,
.required = true,
.completer = virshDomainThrottleGroupCompleter,
.help = N_("throttle group name")
},
VIRSH_COMMON_OPT_DOMAIN_CONFIG,
VIRSH_COMMON_OPT_DOMAIN_LIVE,
VIRSH_COMMON_OPT_DOMAIN_CURRENT,
{.name = NULL}
};
static bool
cmdThrottleGroupDel(vshControl *ctl,
const vshCmd *cmd)
{
g_autoptr(virshDomain) dom = NULL;
const char *group_name = NULL;
bool config = vshCommandOptBool(cmd, "config");
bool live = vshCommandOptBool(cmd, "live");
bool current = vshCommandOptBool(cmd, "current");
unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
if (config)
flags |= VIR_DOMAIN_AFFECT_CONFIG;
if (live)
flags |= VIR_DOMAIN_AFFECT_LIVE;
if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
return false;
if (vshCommandOptString(ctl, cmd, "group-name", &group_name) < 0) {
return false;
}
if (virDomainDelThrottleGroup(dom, group_name, flags) < 0)
return false;
vshPrintExtra(ctl, "%s", _("Throttle group deleted successfully\n"));
return true;
}
/*
* "domthrottlegroupinfo" command
*/
static const vshCmdInfo info_domthrottlegroupinfo = {
.help = N_("Get a throttling group."),
.desc = N_("Get a throttling group."),
};
static const vshCmdOptDef opts_domthrottlegroupinfo[] = {
VIRSH_COMMON_OPT_DOMAIN_FULL(0),
{.name = "group-name",
.type = VSH_OT_STRING,
.positional = true,
.required = true,
.completer = virshDomainThrottleGroupCompleter,
.help = N_("throttle group name")
},
{.name = "inactive",
.type = VSH_OT_BOOL,
.help = N_("get inactive rather than running configuration")
},
{.name = NULL}
};
#define PARSE_THROTTLE_GROUP(val) \
do { \
g_autofree char *str = virXPathString("string(./" #val ")", ctxt); \
if (str) \
vshPrint(ctl, "%-15s: %s\n", #val, str); \
} while (false)
static bool
cmdThrottleGroupInfo(vshControl *ctl,
const vshCmd *cmd)
{
const char *group_name = NULL;
unsigned int flags = 0;
g_autoptr(xmlDoc) xml = NULL;
g_autoptr(xmlXPathContext) ctxt = NULL;
g_autofree xmlNodePtr *node = NULL;
int n = 0;
size_t i;
if (vshCommandOptBool(cmd, "inactive"))
flags |= VIR_DOMAIN_XML_INACTIVE;
if (vshCommandOptString(ctl, cmd, "group-name", &group_name) < 0)
return false;
if (virshDomainGetXML(ctl, cmd, flags, &xml, &ctxt) < 0)
return false;
if ((n = virXPathNodeSet("/domain/throttlegroups/throttlegroup", ctxt, &node)) < 0)
return false;
for (i = 0; i < n; i++) {
g_autofree char *name = NULL;
VIR_XPATH_NODE_AUTORESTORE(ctxt);
ctxt->node = node[i];
name = virXPathString("string(./group_name)", ctxt);
if (STRNEQ_NULLABLE(group_name, name))
continue;
PARSE_THROTTLE_GROUP(total_bytes_sec);
PARSE_THROTTLE_GROUP(read_bytes_sec);
PARSE_THROTTLE_GROUP(write_bytes_sec);
PARSE_THROTTLE_GROUP(total_iops_sec);
PARSE_THROTTLE_GROUP(read_iops_sec);
PARSE_THROTTLE_GROUP(write_iops_sec);
PARSE_THROTTLE_GROUP(total_bytes_sec_max);
PARSE_THROTTLE_GROUP(read_bytes_sec_max);
PARSE_THROTTLE_GROUP(write_bytes_sec_max);
PARSE_THROTTLE_GROUP(total_iops_sec_max);
PARSE_THROTTLE_GROUP(read_iops_sec_max);
PARSE_THROTTLE_GROUP(write_iops_sec_max);
PARSE_THROTTLE_GROUP(size_iops_sec);
PARSE_THROTTLE_GROUP(total_bytes_sec_max_length);
PARSE_THROTTLE_GROUP(read_bytes_sec_max_length);
PARSE_THROTTLE_GROUP(write_bytes_sec_max_length);
PARSE_THROTTLE_GROUP(total_iops_sec_max_length);
PARSE_THROTTLE_GROUP(read_iops_sec_max_length);
PARSE_THROTTLE_GROUP(write_iops_sec_max_length);
}
return true;
}
#undef PARSE_THROTTLE_GROUP
/*
* "blkiotune" command
*/
@ -13463,6 +13805,30 @@ const vshCmdDef domManagementCmds[] = {
.info = &info_blkdeviotune,
.flags = 0
},
{.name = "domthrottlegroupset",
.handler = cmdThrottleGroupSet,
.opts = opts_domthrottlegroupset,
.info = &info_domthrottlegroupset,
.flags = 0
},
{.name = "domthrottlegroupdel",
.handler = cmdThrottleGroupDel,
.opts = opts_domthrottlegroupdel,
.info = &info_domthrottlegroupdel,
.flags = 0
},
{.name = "domthrottlegroupinfo",
.handler = cmdThrottleGroupInfo,
.opts = opts_domthrottlegroupinfo,
.info = &info_domthrottlegroupinfo,
.flags = 0
},
{.name = "domthrottlegrouplist",
.handler = cmdThrottleGroupList,
.opts = opts_domthrottlegrouplist,
.info = &info_domthrottlegrouplist,
.flags = 0
},
{.name = "blkiotune",
.handler = cmdBlkiotune,
.opts = opts_blkiotune,