On Wed, Jun 12, 2024 at 03:02:23 -0700, [email protected] wrote:
> From: Chun Feng Wu <[email protected]>
>
> * Add new cmds: throttlegroupset, throttlegrouplist, throttlegroupinfo,
> throttlegroupdel
>
> Signed-off-by: Chun Feng Wu <[email protected]>
> ---
> tools/virsh-domain.c | 428 +++++++++++++++++++++++++++++++++++++++++++
This patch is missing addition to the manpage documenting the commands
in docs/manpages/virsh.rst.
> 1 file changed, 428 insertions(+)
>
> diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c
> index 50e80689a2..f84a65451a 100644
> --- a/tools/virsh-domain.c
> +++ b/tools/virsh-domain.c
> @@ -1509,6 +1509,410 @@ cmdBlkdeviotune(vshControl *ctl, const vshCmd *cmd)
> goto cleanup;
> }
>
> +
> +/*
> + * "throttlegrouplist" command
> + */
> +static const vshCmdInfo info_throttlegrouplist = {
> + .help = N_("list all domain throttlegroups."),
> + .desc = N_("Get the summary of throttle groups for a domain."),
> +};
> +
> +
> +static const vshCmdOptDef opts_throttlegrouplist[] = {
> + 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;
> + size_t i;
> + g_autoptr(xmlDoc) xml = NULL;
> + g_autoptr(xmlXPathContext) ctxt = NULL;
> + g_autofree xmlNodePtr *groups = NULL;
> + ssize_t ngroups;
> + g_autoptr(vshTable) table = NULL;
> +
> + if (vshCommandOptBool(cmd, "inactive"))
> + flags |= VIR_DOMAIN_XML_INACTIVE;
> +
> + if (virshDomainGetXML(ctl, cmd, flags, &xml, &ctxt) < 0)
> + return false;
> +
> + ngroups = virXPathNodeSet("./throttlegroups/throttlegroup", ctxt,
> &groups);
> + if (ngroups < 0)
You need to report an error here, but on the other hand I don't think
that having 0 groups is an error.
> + return false;
> +
> + table = vshTableNew(_("Name"), NULL);
> +
> + if (!table)
> + return false;
> +
> + for (i = 0; i < ngroups; i++) {
> + g_autofree char *name = NULL;
> + ctxt->node = groups[i];
> + name = virXPathString("string(./group_name)", ctxt);
Here you don't handle NULL
> + if (vshTableRowAppend(table, name, NULL) < 0)
... which would make this fail use NULLSTR_EMPTY
> + return false;
> + }
> +
> + vshTablePrintToStdout(table, ctl);
> +
> + return true;
> +}
> +
> +
> +/*
> + * "throttlegroupset" command
> + */
> +static const vshCmdInfo info_throttlegroupset = {
> + .help = N_("Add or update a throttling group."),
> + .desc = N_("Add or updte a throttling group."),
> +};
> +
> +
> +static const vshCmdOptDef opts_throttlegroupset[] = {
> + VIRSH_COMMON_OPT_DOMAIN_FULL(0),
> + {.name = "group-name",
> + .type = VSH_OT_STRING,
> + .positional = true,
> + .required = true,
> + .completer = virshCompleteEmpty,
It will be possible to auto-complete this argument so virshCompleteEmpty
is not appropriate. That annotation must be used exclusively for fields
which don't make sense tot be complted.
> + .help = N_("throttle group name")
> + },
> + {.name = "total-bytes-sec",
> + .type = VSH_OT_INT,
> + .help = N_("total throughput limit, as scaled integer (default bytes)")
> + },
> + {.name = "read-bytes-sec",
> + .type = VSH_OT_INT,
> + .help = N_("read throughput limit, as scaled integer (default bytes)")
> + },
> + {.name = "write-bytes-sec",
> + .type = VSH_OT_INT,
> + .help = N_("write throughput limit, as scaled integer (default bytes)")
> + },
> + {.name = "total-iops-sec",
> + .type = VSH_OT_INT,
> + .help = N_("total I/O operations limit per second")
> + },
> + {.name = "read-iops-sec",
> + .type = VSH_OT_INT,
> + .help = N_("read I/O operations limit per second")
> + },
> + {.name = "write-iops-sec",
> + .type = VSH_OT_INT,
> + .help = N_("write I/O operations limit per second")
> + },
> + {.name = "total-bytes-sec-max",
> + .type = VSH_OT_INT,
> + .help = N_("total max, as scaled integer (default bytes)")
> + },
> + {.name = "read-bytes-sec-max",
> + .type = VSH_OT_INT,
> + .help = N_("read max, as scaled integer (default bytes)")
> + },
> + {.name = "write-bytes-sec-max",
> + .type = VSH_OT_INT,
> + .help = N_("write max, as scaled integer (default bytes)")
> + },
> + {.name = "total-iops-sec-max",
> + .type = VSH_OT_INT,
> + .help = N_("total I/O operations max")
> + },
> + {.name = "read-iops-sec-max",
> + .type = VSH_OT_INT,
> + .help = N_("read I/O operations max")
> + },
> + {.name = "write-iops-sec-max",
> + .type = VSH_OT_INT,
> + .help = N_("write I/O operations max")
> + },
> + {.name = "size-iops-sec",
> + .type = VSH_OT_INT,
> + .help = N_("I/O size in bytes")
> + },
> + {.name = "total-bytes-sec-max-length",
> + .type = VSH_OT_INT,
> + .help = N_("duration in seconds to allow total max bytes")
> + },
> + {.name = "read-bytes-sec-max-length",
> + .type = VSH_OT_INT,
> + .help = N_("duration in seconds to allow read max bytes")
> + },
> + {.name = "write-bytes-sec-max-length",
> + .type = VSH_OT_INT,
> + .help = N_("duration in seconds to allow write max bytes")
> + },
> + {.name = "total-iops-sec-max-length",
> + .type = VSH_OT_INT,
> + .help = N_("duration in seconds to allow total I/O operations max")
> + },
> + {.name = "read-iops-sec-max-length",
> + .type = VSH_OT_INT,
> + .help = N_("duration in seconds to allow read I/O operations max")
> + },
> + {.name = "write-iops-sec-max-length",
> + .type = VSH_OT_INT,
> + .help = N_("duration in seconds to allow write I/O operations max")
> + },
I guess that once we have two of the commands using these it'd make
sense to have these as a macro to avoid having to update two places the
next time another tunable is added.
> + VIRSH_COMMON_OPT_DOMAIN_CONFIG,
> + VIRSH_COMMON_OPT_DOMAIN_LIVE,
> + VIRSH_COMMON_OPT_DOMAIN_CURRENT,
> + {.name = NULL}
> +};
> +
> +
> +static bool
> +cmdThrottleGroupSet(vshControl *ctl,
> + const vshCmd *cmd)
> +{
> + g_autoptr(virshDomain) dom = NULL;
> + const char *name;
> + 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, &name)))
> + goto cleanup;
This function doesn't use 'name' any more so there's no point in
fetching it.
> +
> +
> +#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(¶ms, &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(¶ms, &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) {
gropu name is guaranteed to exist ...
> + if (virTypedParamsAddString(¶ms, &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;
otherwis this'd fail anyways.
> + 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 change throttle group"));
> + goto cleanup;
> +
> + interror:
> + vshError(ctl, "%s", _("Unable to parse integer parameter"));
> + goto cleanup;
> +}
> +
> +
> +/*
> + * "throttlegroupdel" command
> + */
> +static const vshCmdInfo info_throttlegroupdel = {
> + .help = N_("Delete a throttling group."),
> + .desc = N_("Delete a throttling group."),
> +};
> +
> +
> +static const vshCmdOptDef opts_throttlegroupdel[] = {
> + VIRSH_COMMON_OPT_DOMAIN_FULL(0),
> + {.name = "group-name",
> + .type = VSH_OT_STRING,
> + .positional = true,
> + .required = true,
> + .completer = virshCompleteEmpty,
Same as above regarding the annotation.
> + .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;
> +}
> +
> +
> +/*
> + * "throttlegroupinfo" command
> + */
> +static const vshCmdInfo info_throttlegroupinfo = {
> + .help = N_("Get a throttling group."),
> + .desc = N_("Get a throttling group."),
> +};
> +
> +
> +static const vshCmdOptDef opts_throttlegroupinfo[] = {
> + VIRSH_COMMON_OPT_DOMAIN_FULL(0),
> + {.name = "group-name",
> + .type = VSH_OT_STRING,
> + .positional = true,
> + .required = true,
> + .completer = virshCompleteEmptya
Same as above.
,
> + .help = N_("throttle group name")
> + },
> + VIRSH_COMMON_OPT_DOMAIN_CONFIG,
> + VIRSH_COMMON_OPT_DOMAIN_LIVE,
> + VIRSH_COMMON_OPT_DOMAIN_CURRENT,
> + {.name = NULL}
> +};
> +
> +
> +static bool
> +cmdThrottleGroupInfo(vshControl *ctl,
> + const vshCmd *cmd)
> +{
> + g_autoptr(virshDomain) dom = NULL;
> + const char *name;
> + const char *group_name = NULL;
> + int nparams = 0;
> + virTypedParameterPtr params = NULL;
> + unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
> + size_t i;
> + bool current = vshCommandOptBool(cmd, "current");
> + bool config = vshCommandOptBool(cmd, "config");
> + bool live = vshCommandOptBool(cmd, "live");
> + bool ret = false;
> +
> + VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
> + VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
> +
> + if (config)
> + flags |= VIR_DOMAIN_AFFECT_CONFIG;
> + if (live)
> + flags |= VIR_DOMAIN_AFFECT_LIVE;
> +
> + if (!(dom = virshCommandOptDomain(ctl, cmd, &name)))
> + goto cleanup;
Name is not used.
> +
> + if (vshCommandOptString(ctl, cmd, "group-name", &group_name) < 0) {
> + goto cleanup;
> + }
> +
> + if (virDomainGetThrottleGroup(dom, group_name, ¶ms, &nparams, flags)
> != 0) {
> + vshError(ctl, "%s",
> + _("Unable to get throttle group parameters"));
Misaligned.
> + goto cleanup;
> + }
> +
> + for (i = 0; i < nparams; i++) {
> + g_autofree char *str = vshGetTypedParamValue(ctl, ¶ms[i]);
> + vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
> + }
> +
> + ret = true;
> +
> + cleanup:
> + virTypedParamsFree(params, nparams);
> + return ret;
> +}
> +
> +
> /*
> * "blkiotune" command
> */
> @@ -13378,6 +13782,30 @@ const vshCmdDef domManagementCmds[] = {
> .info = &info_blkdeviotune,
> .flags = 0
> },
> + {.name = "throttlegroupset",
> + .handler = cmdThrottleGroupSet,
> + .opts = opts_throttlegroupset,
> + .info = &info_throttlegroupset,
> + .flags = 0
> + },
> + {.name = "throttlegroupdel",
> + .handler = cmdThrottleGroupDel,
> + .opts = opts_throttlegroupdel,
> + .info = &info_throttlegroupdel,
> + .flags = 0
> + },
> + {.name = "throttlegroupinfo",
> + .handler = cmdThrottleGroupInfo,
> + .opts = opts_throttlegroupinfo,
> + .info = &info_throttlegroupinfo,
> + .flags = 0
> + },
> + {.name = "throttlegrouplist",
> + .handler = cmdThrottleGroupList,
> + .opts = opts_throttlegrouplist,
> + .info = &info_throttlegrouplist,
> + .flags = 0
> + },
I suggest using the 'dom' prefix as all of the objects are per-domain.
> {.name = "blkiotune",
> .handler = cmdBlkiotune,
> .opts = opts_blkiotune,
> --
> 2.34.1
>