diff --git a/doc/management.txt b/doc/management.txt index dfd8eee2f..eb534d051 100644 --- a/doc/management.txt +++ b/doc/management.txt @@ -3997,15 +3997,36 @@ update ssl ocsp-response 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 { -h | } - 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 (us, ms, s, m, h, d). When used with the 'socat' utility, do not - forget to extend socat's close timeout to cover the wait time. Passing "-h" - as the first or second argument provides the command's usage. +wait { -h | } [ [...]] + In its simplest form without any condition, this simply waits for the + requested delay before continuing. This can be used to collect metrics around + a specific interval. + + With a condition and optional arguments, the command will wait for the + specified condition to be satisfied, to unrecoverably fail, or to remain + unsatisfied for the whole duration. The supported conditions are: + + - srv-unused / : this will wait for the specified server to be + removable, i.e. be in maintenance and no longer have any connection on it. + Some conditions will never be accepted (e.g. not in maintenance) and will + cause the report of a specific error message indicating what condition is + not met. The server might even have been removed in parallel and no longer + exit. If everything is OK before the delay, a success is returned and the + operation is terminated. + + The default unit for the delay is milliseconds, though other units are + accepted if suffixed with the usual timer units (us, ms, s, m, h, d). When + used with the 'socat' utility, do not forget to extend socat's close timeout + to cover the wait time. Passing "-h" as the first or second argument provides + the command's usage. Example: - $ socat -t20 /path/to/socket <<< "show activity; wait 10s; show activity" + $ socat -t20 /path/to/socket - <<< "show activity; wait 10s; show activity" + + $ socat -t5 /path/to/socket - <<< " + disable server px/srv1 + shutdown sessions server px/srv1 + wait 2s srv-unused px/srv1 + del server px/srv1" 9.4. Master CLI diff --git a/include/haproxy/cli-t.h b/include/haproxy/cli-t.h index 4a11893ed..9a33a9445 100644 --- a/include/haproxy/cli-t.h +++ b/include/haproxy/cli-t.h @@ -95,6 +95,7 @@ enum cli_wait_err { enum cli_wait_cond { CLI_WAIT_COND_NONE, // no condition to wait on + CLI_WAIT_COND_SRV_UNUSED,// wait for server to become unused }; struct cli_wait_ctx { diff --git a/src/cli.c b/src/cli.c index 786f2a24e..f3b015ab0 100644 --- a/src/cli.c +++ b/src/cli.c @@ -2035,14 +2035,40 @@ static int cli_parse_wait(char **args, char *payload, struct appctx *appctx, voi return cli_err(appctx, "Invalid duration.\n"); } - if (*args[2]) { + if (strcmp(args[2], "srv-unused") == 0) { + struct ist be_name, sv_name; + + if (!*args[3]) + return cli_err(appctx, "Missing server name (/).\n"); + + sv_name = ist(args[3]); + be_name = istsplit(&sv_name, '/'); + if (!istlen(sv_name)) + return cli_err(appctx, "Require 'backend/server'.\n"); + + be_name = istdup(be_name); + sv_name = istdup(sv_name); + if (!isttest(be_name) || !isttest(sv_name)) { + free(istptr(be_name)); + free(istptr(sv_name)); + return cli_err(appctx, "Out of memory trying to clone the server name.\n"); + } + + ctx->args[0] = ist0(be_name); + ctx->args[1] = ist0(sv_name); + ctx->cond = CLI_WAIT_COND_SRV_UNUSED; + } + else if (*args[2]) { /* show the command's help either upon request (-h) or error */ err = "Usage: wait {-h|} [condition [args...]]\n" " - '-h' displays this help\n" " - is the maximum wait time, optionally suffixed by the unit among\n" " 'us', 'ms', 's', 'm', 'h', and 'd'. ; the default unit is milliseconds.\n" - " - indicates what to wait for. By default, no events aborts the\n" - " operation, which makes it reliably pause for the specified duration.\n"; + " - indicates what to wait for, no longer than the specified\n" + " duration. Supported conditions are:\n" + " - : by default, just sleep for the specified duration.\n" + " - srv-unused / : wait for this server to become unused.\n" + ""; if (strcmp(args[2], "-h") == 0) return cli_msg(appctx, LOG_INFO, err); @@ -2069,6 +2095,7 @@ 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; + int ret; /* note: upon first invocation, the timeout is not set */ if (tick_isset(appctx->t->expire) && @@ -2077,6 +2104,24 @@ static int cli_io_handler_wait(struct appctx *appctx) /* here we should evaluate our waiting conditions, if any */ + if (ctx->cond == CLI_WAIT_COND_SRV_UNUSED) { + /* check if the server in args[0]/args[1] can be released now */ + thread_isolate(); + ret = srv_check_for_deletion(ctx->args[0], ctx->args[1], NULL, NULL, NULL); + thread_release(); + + if (ret < 0) { + /* unrecoverable failure */ + ctx->error = CLI_WAIT_ERR_FAIL; + return 1; + } else if (ret > 0) { + /* immediate success */ + ctx->error = CLI_WAIT_ERR_DONE; + return 1; + } + /* let's check the timer */ + } + /* and here we recalculate the new wait time or abort */ left = tick_remain(now_ms, ctx->deadline); if (!left) { @@ -3530,7 +3575,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 {-h|} : wait the specified delay (-h to see usage)", cli_parse_wait, cli_io_handler_wait, cli_release_wait, NULL }, + { { "wait", NULL }, "wait {-h|} cond [args...] : wait the specified delay or condition (-h to see list)", cli_parse_wait, cli_io_handler_wait, cli_release_wait, NULL }, {{},} }};