From: Christopher Faulet Date: Sat, 4 Apr 2020 08:27:09 +0000 (+0200) Subject: MEDIUM: checks: Implement SPOP check using tcp-check rules X-Git-Tag: v2.2-dev7~122 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=267b01b7611699a4a2b98c6f36c651f5cacd76a9;p=thirdparty%2Fhaproxy.git MEDIUM: checks: Implement SPOP check using tcp-check rules A share tcp-check ruleset is now created to support SPOP checks. This way no extra memory is used if several backends use a SPOP check. The following sequence is used : tcp-check send-binary SPOP_REQ tcp-check expect custom min-recv 4 The spop request is the result of the function spoe_prepare_healthcheck_request() and the expect rule relies on a custom function calling spoe_handle_healthcheck_response(). --- diff --git a/include/proto/checks.h b/include/proto/checks.h index ff432384a4..669c620801 100644 --- a/include/proto/checks.h +++ b/include/proto/checks.h @@ -81,6 +81,8 @@ int proxy_parse_mysql_check_opt(char **args, int cur_arg, struct proxy *curpx, s const char *file, int line); int proxy_parse_ldap_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx, const char *file, int line); +int proxy_parse_spop_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx, + const char *file, int line); #endif /* _PROTO_CHECKS_H */ diff --git a/include/types/checks.h b/include/types/checks.h index a5676c18ae..d3da5f29ad 100644 --- a/include/types/checks.h +++ b/include/types/checks.h @@ -318,6 +318,7 @@ struct tcpcheck_rule { #define TCPCHK_RULES_MYSQL_CHK 0x00000050 #define TCPCHK_RULES_LDAP_CHK 0x00000060 #define TCPCHK_RULES_SSL3_CHK 0x00000070 +#define TCPCHK_RULES_SPOP_CHK 0x00000090 /* A list of tcp-check vars, to be registered before executing a ruleset */ struct tcpcheck_var { diff --git a/include/types/proxy.h b/include/types/proxy.h index bf2a798b33..e13c63c055 100644 --- a/include/types/proxy.h +++ b/include/types/proxy.h @@ -175,8 +175,7 @@ enum PR_SRV_STATE_FILE { #define PR_O2_LB_AGENT_CHK 0x80000000 /* use a TCP connection to obtain a metric of server health */ #define PR_O2_TCPCHK_CHK 0x90000000 /* use TCPCHK check for server health */ #define PR_O2_EXT_CHK 0xA0000000 /* use external command for server health */ -#define PR_O2_SPOP_CHK 0xB0000000 /* use SPOP for server health */ -/* unused: 0xC0000000 to 0xF000000, reserved for health checks */ +/* unused: 0xB0000000 to 0xF000000, reserved for health checks */ #define PR_O2_CHK_ANY 0xF0000000 /* Mask to cover any check */ /* end of proxy->options2 */ diff --git a/src/cfgparse-listen.c b/src/cfgparse-listen.c index 4286742587..1111b743f4 100644 --- a/src/cfgparse-listen.c +++ b/src/cfgparse-listen.c @@ -2427,31 +2427,8 @@ stats_error_parsing: goto out; } else if (!strcmp(args[1], "spop-check")) { - if (curproxy == &defproxy) { - ha_alert("parsing [%s:%d] : '%s %s' not allowed in 'defaults' section.\n", - file, linenum, args[0], args[1]); - err_code |= ERR_ALERT | ERR_FATAL; - goto out; - } - if (curproxy->cap & PR_CAP_FE) { - ha_alert("parsing [%s:%d] : '%s %s' not allowed in 'frontend' and 'listen' sections.\n", - file, linenum, args[0], args[1]); - err_code |= ERR_ALERT | ERR_FATAL; - goto out; - } - - /* use SPOE request to check servers' health */ - free(curproxy->check_req); - curproxy->check_req = NULL; - curproxy->options2 &= ~PR_O2_CHK_ANY; - curproxy->options2 |= PR_O2_SPOP_CHK; - - if (spoe_prepare_healthcheck_request(&curproxy->check_req, &curproxy->check_len)) { - ha_alert("parsing [%s:%d] : failed to prepare SPOP healthcheck request.\n", file, linenum); - err_code |= ERR_ALERT | ERR_FATAL; - goto out; - } - if (alertif_too_many_args_idx(0, 1, file, linenum, args, &err_code)) + err_code |= proxy_parse_spop_check_opt(args, 0, curproxy, &defproxy, file, linenum); + if (err_code & ERR_FATAL) goto out; } else if (!strcmp(args[1], "tcp-check")) { diff --git a/src/checks.c b/src/checks.c index fa595ea659..dc09a19fd5 100644 --- a/src/checks.c +++ b/src/checks.c @@ -1155,26 +1155,6 @@ static void __event_srv_chk_r(struct conn_stream *cs) break; } - case PR_O2_SPOP_CHK: { - unsigned int framesz; - char err[HCHK_DESC_LEN]; - - if (!done && b_data(&check->bi) < 4) - goto wait_more_data; - - memcpy(&framesz, b_head(&check->bi), 4); - framesz = ntohl(framesz); - - if (!done && b_data(&check->bi) < (4+framesz)) - goto wait_more_data; - - if (!spoe_handle_healthcheck_response(b_head(&check->bi)+4, framesz, err, HCHK_DESC_LEN-1)) - set_server_check_status(check, HCHK_STATUS_L7OKD, "SPOA server is ok"); - else - set_server_check_status(check, HCHK_STATUS_L7STS, err); - break; - } - default: /* good connection is enough for pure TCP check */ if (!(conn->flags & CO_FL_WAIT_XPRT) && !check->type) { @@ -2782,6 +2762,48 @@ static enum tcpcheck_eval_ret tcpcheck_ldap_expect_bindrsp(struct check *check, goto out; } + +static enum tcpcheck_eval_ret tcpcheck_spop_expect_agenthello(struct check *check, struct tcpcheck_rule *rule, int last_read) +{ + enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE; + enum healthcheck_status status; + struct buffer *msg = NULL; + struct ist desc = ist(NULL); + unsigned int framesz; + + + memcpy(&framesz, b_head(&check->bi), 4); + framesz = ntohl(framesz); + + if (!last_read && b_data(&check->bi) < (4+framesz)) + goto wait_more_data; + + memset(b_orig(&trash), 0, b_size(&trash)); + if (spoe_handle_healthcheck_response(b_peek(&check->bi, 4), framesz, b_orig(&trash), HCHK_DESC_LEN) == -1) { + status = HCHK_STATUS_L7RSP; + desc = ist2(b_orig(&trash), strlen(b_orig(&trash))); + goto error; + } + + set_server_check_status(check, HCHK_STATUS_L7OKD, "SPOA server is ok"); + + out: + free_trash_chunk(msg); + return ret; + + error: + ret = TCPCHK_EVAL_STOP; + msg = alloc_trash_chunk(); + if (msg) + tcpcheck_onerror_message(msg, check, rule, 0, desc); + set_server_check_status(check, status, (msg ? b_head(msg) : NULL)); + goto out; + + wait_more_data: + ret = TCPCHK_EVAL_WAIT; + goto out; +} + /* Evaluate a TCPCHK_ACT_CONNECT rule. It returns 1 to evaluate the next rule, 0 * to wait and -1 to stop the check. */ static enum tcpcheck_eval_ret tcpcheck_eval_connect(struct check *check, struct tcpcheck_rule *rule) @@ -5862,6 +5884,91 @@ int proxy_parse_ldap_check_opt(char **args, int cur_arg, struct proxy *curpx, st goto out; } +int proxy_parse_spop_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx, + const char *file, int line) +{ + struct tcpcheck_ruleset *rs = NULL; + struct tcpcheck_rules *rules = &curpx->tcpcheck_rules; + struct tcpcheck_rule *chk; + char *spop_req = NULL; + char *errmsg = NULL; + int spop_len = 0, err_code = 0; + + if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL)) + err_code |= ERR_WARN; + + if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code)) + goto out; + + if (rules->list && !(rules->flags & TCPCHK_RULES_SHARED)) { + ha_alert("parsing [%s:%d] : A custom tcp-check ruleset is already configured.\n", + file, line); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + + curpx->options2 &= ~PR_O2_CHK_ANY; + curpx->options2 |= PR_O2_TCPCHK_CHK; + + free_tcpcheck_vars(&rules->preset_vars); + rules->list = NULL; + rules->flags = 0; + + + rs = tcpcheck_ruleset_lookup("*spop-check"); + if (rs) + goto ruleset_found; + + rs = tcpcheck_ruleset_create("*spop-check"); + if (rs == NULL) { + ha_alert("parsing [%s:%d] : out of memory.\n", file, line); + goto error; + } + + if (spoe_prepare_healthcheck_request(&spop_req, &spop_len) == -1) { + ha_alert("parsing [%s:%d] : out of memory.\n", file, line); + goto error; + } + chunk_reset(&trash); + dump_binary(&trash, spop_req, spop_len); + trash.area[trash.data] = '\0'; + + chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", b_head(&trash), ""}, + 1, curpx, &rs->rules, file, line, &errmsg); + if (!chk) { + ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg); + goto error; + } + chk->index = 0; + LIST_ADDQ(&rs->rules, &chk->list); + + chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", "min-recv", "4", ""}, + 1, curpx, &rs->rules, file, line, &errmsg); + if (!chk) { + ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg); + goto error; + } + chk->expect.custom = tcpcheck_spop_expect_agenthello; + chk->index = 1; + LIST_ADDQ(&rs->rules, &chk->list); + + LIST_ADDQ(&tcpchecks_list, &rs->list); + + ruleset_found: + rules->list = &rs->rules; + rules->flags |= (TCPCHK_RULES_SHARED|TCPCHK_RULES_SPOP_CHK); + + out: + free(spop_req); + free(errmsg); + return err_code; + + error: + tcpcheck_ruleset_release(rs); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; +} + static struct cfg_kw_list cfg_kws = {ILH, { { CFG_LISTEN, "tcp-check", proxy_parse_tcpcheck }, { 0, NULL, NULL },