]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: server: Add 'server-template' new keyword supported in backend sections.
authorFrédéric Lécaille <flecaille@haproxy.com>
Thu, 13 Apr 2017 16:24:23 +0000 (18:24 +0200)
committerWilly Tarreau <w@1wt.eu>
Fri, 21 Apr 2017 13:42:10 +0000 (15:42 +0200)
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 ...

include/common/standard.h
include/types/server.h
src/cfgparse.c
src/server.c
src/standard.c

index be719f71438b9d4b801dca7f6c88e60e811a9372..682711105ee8953c1525196d3fdc4ffbabf6b48c 100644 (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
index bfaa941159becd18f37b9394a34f8681cbe45638..8d68dcb56270277b171f865cf7b29f85797fbc49 100644 (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
index 348b9e886667e80157b6b6a2a8f6dcec6ef7f0e4..d44949a7df71b0804b41ffef1805e85f6afabce3 100644 (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;
index f25a7a0008aa8daf025b1d5f17a4baa4ac44e175..9f0778874f376d64c28990d7f49b7412c72c02df 100644 (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,21 +2025,49 @@ 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]);
-                       err_code |= ERR_ALERT | ERR_FATAL;
-                       goto out;
+               /* 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]);
                }
 
-               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]);
+               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;
                }
 
+               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) {
                        struct sockaddr_storage *sk;
                        int port1, port2, port;
@@ -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;
index 99f7066bb867b2049458e89e425bddc8fc2be7c2..6abedb4a2b6f26043165388f90d2658cffd53865 100644 (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