mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2024-12-28 15:42:30 +00:00
MINOR: cli: add a new "wait" command to wait for a certain delay
This allows to insert delays between commands, i.e. to collect a same set of metrics at a fixed interval. E.g: $ socat -t20 /path/to/socket <<< "show activity; wait 10s; show activity" The goal will be to extend the feature to optionally support waiting on certain conditions. For this reason the struct definitions and enums were placed into cli-t.h.
This commit is contained in:
parent
02b31fa003
commit
1d2255a78a
@ -3997,6 +3997,15 @@ update ssl ocsp-response <certfile>
|
||||
local tree, its contents will be displayed on the standard output. The format
|
||||
is the same as the one described in "show ssl ocsp-response".
|
||||
|
||||
wait <delay>
|
||||
This simply waits for the requested delay before continuing. This can be used
|
||||
to collect metrics around a specific interval. The default unit for the delay
|
||||
is milliseconds, though other units are accepted if suffixed with the usual
|
||||
timer units (s, m, h, d). When used with the 'socat' utility, do not forget
|
||||
to extend socat's close timeout to cover the wait time.
|
||||
Example:
|
||||
$ socat -t20 /path/to/socket <<< "show activity; wait 10s; show activity"
|
||||
|
||||
|
||||
9.4. Master CLI
|
||||
---------------
|
||||
|
@ -81,6 +81,27 @@ struct cli_print_ctx {
|
||||
int severity; /* severity of the message to be returned according to (syslog) rfc5424 */
|
||||
};
|
||||
|
||||
/* context for the "wait" command that's used to wait for some time on a
|
||||
* condition. We store the start date and the expiration date. The error
|
||||
* value is set by the I/O handler to be printed by the release handler at
|
||||
* the end.
|
||||
*/
|
||||
enum cli_wait_err {
|
||||
CLI_WAIT_ERR_DONE, // condition satisfied
|
||||
CLI_WAIT_ERR_INTR, // interrupted
|
||||
CLI_WAIT_ERR_EXP, // finished on wait expiration
|
||||
};
|
||||
|
||||
enum cli_wait_cond {
|
||||
CLI_WAIT_COND_NONE, // no condition to wait on
|
||||
};
|
||||
|
||||
struct cli_wait_ctx {
|
||||
uint start, deadline; // both are in ticks.
|
||||
enum cli_wait_cond cond; // CLI_WAIT_COND_*
|
||||
enum cli_wait_err error; // CLI_WAIT_ERR_*
|
||||
};
|
||||
|
||||
struct cli_kw {
|
||||
const char *str_kw[CLI_PREFIX_KW_NB]; /* keywords ended by NULL, limited to CLI_PREFIX_KW_NB
|
||||
separated keywords combination */
|
||||
|
100
src/cli.c
100
src/cli.c
@ -2010,6 +2010,105 @@ static int cli_parse_set_ratelimit(char **args, char *payload, struct appctx *ap
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Parse a "wait <time>" command.
|
||||
* It uses a "cli_wait_ctx" struct for its context.
|
||||
* Returns 0 if the server deletion has been successfully scheduled, 1 on failure.
|
||||
*/
|
||||
static int cli_parse_wait(char **args, char *payload, struct appctx *appctx, void *private)
|
||||
{
|
||||
struct cli_wait_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
|
||||
uint wait_ms;
|
||||
const char *err;
|
||||
|
||||
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||
return 1;
|
||||
|
||||
if (!*args[1])
|
||||
return cli_err(appctx, "Expects a duration in milliseconds.\n");
|
||||
|
||||
err = parse_time_err(args[1], &wait_ms, TIME_UNIT_MS);
|
||||
if (err || wait_ms < 1)
|
||||
return cli_err(appctx, "Invalid duration.\n");
|
||||
|
||||
ctx->start = now_ms;
|
||||
ctx->deadline = tick_add(now_ms, wait_ms);
|
||||
|
||||
/* proceed with the I/O handler */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Execute a "wait" condition. The delay is exponentially incremented between
|
||||
* now_ms and ctx->deadline in powers of 1.5 and with a bound set to 10% of the
|
||||
* programmed wait time, so that in a few wakeups we can later check a condition
|
||||
* with reasonable accuracy. Shutdowns and other errors are handled as well and
|
||||
* terminate the operation, but not new inputs so that it remains possible to
|
||||
* chain other commands after it. Returns 0 if not finished, 1 if finished.
|
||||
*/
|
||||
static int cli_io_handler_wait(struct appctx *appctx)
|
||||
{
|
||||
struct cli_wait_ctx *ctx = appctx->svcctx;
|
||||
struct stconn *sc = appctx_sc(appctx);
|
||||
uint total, elapsed, left, wait;
|
||||
|
||||
/* note: upon first invocation, the timeout is not set */
|
||||
if (tick_isset(appctx->t->expire) &&
|
||||
!tick_is_expired(appctx->t->expire, now_ms))
|
||||
goto wait;
|
||||
|
||||
/* here we should evaluate our waiting conditions, if any */
|
||||
|
||||
/* and here we recalculate the new wait time or abort */
|
||||
left = tick_remain(now_ms, ctx->deadline);
|
||||
if (!left) {
|
||||
/* let the release handler know we've expired. When there is no
|
||||
* wait condition, it's a simple sleep so we declare we're done.
|
||||
*/
|
||||
if (ctx->cond == CLI_WAIT_COND_NONE)
|
||||
ctx->error = CLI_WAIT_ERR_DONE;
|
||||
else
|
||||
ctx->error = CLI_WAIT_ERR_EXP;
|
||||
return 1;
|
||||
}
|
||||
|
||||
total = tick_remain(ctx->start, ctx->deadline);
|
||||
elapsed = total - left;
|
||||
wait = elapsed / 2 + 1;
|
||||
if (wait > left)
|
||||
wait = left;
|
||||
else if (wait > total / 10)
|
||||
wait = total / 10;
|
||||
|
||||
appctx->t->expire = tick_add(now_ms, wait);
|
||||
|
||||
wait:
|
||||
/* Stop waiting upon close/abort/error */
|
||||
if (unlikely((sc->flags & SC_FL_SHUT_DONE) ||
|
||||
se_fl_test(appctx->sedesc, (SE_FL_EOS|SE_FL_ERROR|SE_FL_SHR|SE_FL_SHW)))) {
|
||||
co_skip(sc_oc(sc), sc_oc(sc)->output);
|
||||
ctx->error = CLI_WAIT_ERR_INTR;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* release structs allocated by "delete server" */
|
||||
static void cli_release_wait(struct appctx *appctx)
|
||||
{
|
||||
struct cli_wait_ctx *ctx = appctx->svcctx;
|
||||
const char *msg;
|
||||
|
||||
switch (ctx->error) {
|
||||
case CLI_WAIT_ERR_EXP: msg = "Wait delay expired.\n"; break;
|
||||
case CLI_WAIT_ERR_INTR: msg = "Interrupted.\n"; break;
|
||||
default: msg = "Done.\n"; break;
|
||||
}
|
||||
|
||||
if (ctx->error == CLI_WAIT_ERR_DONE)
|
||||
cli_msg(appctx, LOG_INFO, msg);
|
||||
else
|
||||
cli_err(appctx, msg);
|
||||
}
|
||||
|
||||
/* parse the "expose-fd" argument on the bind lines */
|
||||
static int bind_parse_expose_fd(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
|
||||
{
|
||||
@ -3406,6 +3505,7 @@ static struct cli_kw_list cli_kws = {{ },{
|
||||
{ { "show", "version", NULL }, "show version : show version of the current process", cli_parse_show_version, NULL, NULL, NULL, ACCESS_MASTER },
|
||||
{ { "operator", NULL }, "operator : lower the level of the current CLI session to operator", cli_parse_set_lvl, NULL, NULL, NULL, ACCESS_MASTER},
|
||||
{ { "user", NULL }, "user : lower the level of the current CLI session to user", cli_parse_set_lvl, NULL, NULL, NULL, ACCESS_MASTER},
|
||||
{ { "wait", NULL }, "wait <ms> : wait the specified delay", cli_parse_wait, cli_io_handler_wait, cli_release_wait, NULL },
|
||||
{{},}
|
||||
}};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user