MINOR: server: Add 'server-template' new keyword supported in backend sections.

This patch makes backend sections support 'server-template' new keyword.
Such 'server-template' objects are parsed similarly to a 'server' object
by parse_server() function, but its first arguments are as follows:
    server-template <ID prefix> <nb | range> <ip | fqdn>:<port> ...

The remaining arguments are the same as for 'server' lines.

With such server template declarations, servers may be allocated with IDs
built from <ID prefix> and <nb | range> arguments.

For instance declaring:
    server-template foo 1-5 google.com:80 ...
or
    server-template foo 5 google.com:80 ...

would be equivalent to declare:
    server foo1 google.com:80 ...
    server foo2 google.com:80 ...
    server foo3 google.com:80 ...
    server foo4 google.com:80 ...
    server foo5 google.com:80 ...
This commit is contained in:
Frdric Lcaille 2017-04-13 18:24:23 +02:00 committed by Willy Tarreau
parent 759ea98db2
commit b82f742b78
5 changed files with 133 additions and 21 deletions

View File

@ -259,12 +259,19 @@ unsigned int round_2dig(unsigned int i);
extern const char *invalid_char(const char *name);
/*
* Checks <domainname> for invalid characters. Valid chars are [A-Za-z0-9_.-].
* Checks <name> for invalid characters. Valid chars are [A-Za-z0-9_.-].
* If an invalid character is found, a pointer to it is returned.
* If everything is fine, NULL is returned.
*/
extern const char *invalid_domainchar(const char *name);
/*
* Checks <name> for invalid characters. Valid chars are [A-Za-z_.-].
* If an invalid character is found, a pointer to it is returned.
* If everything is fine, NULL is returned.
*/
extern const char *invalid_prefix_char(const char *name);
/*
* converts <str> to a locally allocated struct sockaddr_storage *, and a
* port range consisting in two integers. The low and high end are always set

View File

@ -274,6 +274,15 @@ struct server {
int line; /* line where the section appears */
struct eb32_node id; /* place in the tree of used IDs */
} conf; /* config information */
/* Template information used only for server objects which
* serve as template filled at parsing time and used during
* server allocations from server templates.
*/
struct {
char *prefix;
int nb_low;
int nb_high;
} tmpl_info;
};
/* Descriptor for a "server" keyword. The ->parse() function returns 0 in case of

View File

@ -2859,7 +2859,9 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
curproxy->conf.args.line = linenum;
/* Now let's parse the proxy-specific keywords */
if (!strcmp(args[0], "server") || !strcmp(args[0], "default-server")) {
if (!strcmp(args[0], "server") ||
!strcmp(args[0], "default-server") ||
!strcmp(args[0], "server-template")) {
err_code |= parse_server(file, linenum, args, curproxy, &defproxy);
if (err_code & ERR_FATAL)
goto out;

View File

@ -1969,18 +1969,53 @@ static int server_finalize_init(const char *file, int linenum, char **args, int
return 0;
}
/*
* Parse as much as possible such a range string argument: low[-high]
* Set <nb_low> and <nb_high> values so that they may be reused by this loop
* for(int i = nb_low; i <= nb_high; i++)... with nb_low >= 1.
* Fails if 'low' < 0 or 'high' is present and not higher than 'low'.
* Returns 0 if succeeded, -1 if not.
*/
static int srv_tmpl_parse_range(struct server *srv, const char *arg, int *nb_low, int *nb_high)
{
char *nb_high_arg;
*nb_high = 0;
chunk_printf(&trash, "%s", arg);
*nb_low = atoi(trash.str);
if ((nb_high_arg = strchr(trash.str, '-'))) {
*nb_high_arg++ = '\0';
*nb_high = atoi(nb_high_arg);
}
else {
*nb_high += *nb_low;
*nb_low = 1;
}
if (*nb_low < 0 || *nb_high < *nb_low)
return -1;
return 0;
}
int parse_server(const char *file, int linenum, char **args, struct proxy *curproxy, struct proxy *defproxy)
{
struct server *newsrv = NULL;
const char *err;
const char *err = NULL;
char *errmsg = NULL;
int err_code = 0;
unsigned val;
char *fqdn = NULL;
if (!strcmp(args[0], "server") || !strcmp(args[0], "default-server")) { /* server address */
if (!strcmp(args[0], "server") ||
!strcmp(args[0], "default-server") ||
!strcmp(args[0], "server-template")) {
int cur_arg;
int defsrv = (*args[0] == 'd');
int srv = !defsrv && !strcmp(args[0], "server");
int srv_tmpl = !defsrv && !srv;
int tmpl_range_low = 0, tmpl_range_high = 0;
if (!defsrv && curproxy == defproxy) {
Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
@ -1990,19 +2025,47 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr
else if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL))
err_code |= ERR_ALERT | ERR_FATAL;
if (!defsrv && !*args[2]) {
Alert("parsing [%s:%d] : '%s' expects <name> and <addr>[:<port>] as arguments.\n",
file, linenum, args[0]);
/* There is no mandatory first arguments for default server. */
if (srv) {
if (!*args[2]) {
/* 'server' line number of argument check. */
Alert("parsing [%s:%d] : '%s' expects <name> and <addr>[:<port>] as arguments.\n",
file, linenum, args[0]);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
err = invalid_char(args[1]);
}
else if (srv_tmpl) {
if (!*args[3]) {
/* 'server-template' line number of argument check. */
Alert("parsing [%s:%d] : '%s' expects <prefix> <nb | range> <addr>[:<port>] as arguments.\n",
file, linenum, args[0]);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
err = invalid_prefix_char(args[1]);
}
if (err) {
Alert("parsing [%s:%d] : character '%c' is not permitted in %s %s '%s'.\n",
file, linenum, *err, args[0], srv ? "name" : "prefix", args[1]);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
err = invalid_char(args[1]);
if (err && !defsrv) {
Alert("parsing [%s:%d] : character '%c' is not permitted in server name '%s'.\n",
file, linenum, *err, args[1]);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
cur_arg = 2;
if (srv_tmpl) {
/* Parse server-template <nb | range> arg. */
if (srv_tmpl_parse_range(newsrv, args[cur_arg], &tmpl_range_low, &tmpl_range_high) < 0) {
Alert("parsing [%s:%d] : Wrong %s number or range arg '%s'.\n",
file, linenum, args[0], args[cur_arg]);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
cur_arg++;
}
if (!defsrv) {
@ -2018,12 +2081,24 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr
goto out;
}
if (srv_tmpl) {
newsrv->tmpl_info.nb_low = tmpl_range_low;
newsrv->tmpl_info.nb_high = tmpl_range_high;
}
/* the servers are linked backwards first */
newsrv->next = curproxy->srv;
curproxy->srv = newsrv;
newsrv->conf.file = strdup(file);
newsrv->conf.line = linenum;
newsrv->id = strdup(args[1]);
/* Note: for a server template, its id is its prefix.
* This is a temporary id which will be used for server allocations to come
* after parsing.
*/
if (srv)
newsrv->id = strdup(args[1]);
else
newsrv->tmpl_info.prefix = strdup(args[1]);
/* several ways to check the port component :
* - IP => port=+0, relative (IPv4 only)
@ -2032,7 +2107,7 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr
* - IP:+N => port=+N, relative
* - IP:-N => port=-N, relative
*/
sk = str2sa_range(args[2], &port, &port1, &port2, &errmsg, NULL, &fqdn, 0);
sk = str2sa_range(args[cur_arg], &port, &port1, &port2, &errmsg, NULL, &fqdn, 0);
if (!sk) {
Alert("parsing [%s:%d] : '%s %s' : %s\n", file, linenum, args[0], args[1], errmsg);
err_code |= ERR_ALERT | ERR_FATAL;
@ -2073,7 +2148,7 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr
goto skip_name_resolution;
if ((dns_str_to_dn_label(newsrv->hostname, curr_resolution->hostname_dn, curr_resolution->hostname_dn_len + 1)) == NULL) {
Alert("parsing [%s:%d] : Invalid hostname '%s'\n",
file, linenum, args[2]);
file, linenum, args[cur_arg]);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
@ -2093,14 +2168,14 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr
if (!newsrv->hostname && !protocol_by_family(newsrv->addr.ss_family)) {
Alert("parsing [%s:%d] : Unknown protocol family %d '%s'\n",
file, linenum, newsrv->addr.ss_family, args[2]);
file, linenum, newsrv->addr.ss_family, args[cur_arg]);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
/* Copy default server settings to new server settings. */
srv_settings_cpy(newsrv, &curproxy->defsrv);
cur_arg = 3;
cur_arg++;
} else {
newsrv = &curproxy->defsrv;
cur_arg = 1;

View File

@ -592,17 +592,18 @@ const char *invalid_char(const char *name)
}
/*
* Checks <domainname> for invalid characters. Valid chars are [A-Za-z0-9_.-].
* Checks <name> for invalid characters. Valid chars are [_.-] and those
* accepted by <f> function.
* If an invalid character is found, a pointer to it is returned.
* If everything is fine, NULL is returned.
*/
const char *invalid_domainchar(const char *name) {
static inline const char *__invalid_char(const char *name, int (*f)(int)) {
if (!*name)
return name;
while (*name) {
if (!isalnum((int)(unsigned char)*name) && *name != '.' &&
if (!f((int)(unsigned char)*name) && *name != '.' &&
*name != '_' && *name != '-')
return name;
@ -612,6 +613,24 @@ const char *invalid_domainchar(const char *name) {
return NULL;
}
/*
* Checks <name> for invalid characters. Valid chars are [A-Za-z0-9_.-].
* If an invalid character is found, a pointer to it is returned.
* If everything is fine, NULL is returned.
*/
const char *invalid_domainchar(const char *name) {
return __invalid_char(name, isalnum);
}
/*
* Checks <name> for invalid characters. Valid chars are [A-Za-z_.-].
* If an invalid character is found, a pointer to it is returned.
* If everything is fine, NULL is returned.
*/
const char *invalid_prefix_char(const char *name) {
return __invalid_char(name, isalpha);
}
/*
* converts <str> to a struct sockaddr_storage* provided by the caller. The
* caller must have zeroed <sa> first, and may have set sa->ss_family to force