[MEDIUM] config: automatically find unused IDs for proxies, servers and listeners

Until now it was required that every custom ID was above 1000 in order to
avoid conflicts. Now we have the list of all assigned IDs and can automatically
pick the first unused one. This means that it is perfectly possible to interleave
automatic IDs with persistent IDs and the parser will automatically allocate
unused values starting with 1.
This commit is contained in:
Willy Tarreau 2009-10-04 23:04:08 +02:00
parent 482b00d1b4
commit 53fb4ae261
6 changed files with 102 additions and 46 deletions

View File

@ -724,6 +724,7 @@ errorloc303 X X X X
fullconn X - X X fullconn X - X X
grace - X X X grace - X X X
http-check disable-on-404 X - X X http-check disable-on-404 X - X X
id - X X X
log X X X X log X X X X
maxconn X X X - maxconn X X X -
mode X X X X mode X X X X
@ -1140,10 +1141,10 @@ bind [<address>]:<port> [, ...] name <name>
work on other operating systems. The commonly advertised work on other operating systems. The commonly advertised
value on Ethernet networks is 1460 = 1500(MTU) - 40(IP+TCP). value on Ethernet networks is 1460 = 1500(MTU) - 40(IP+TCP).
<id> is a persistent value for socket ID. Must be unique and <id> is a persistent value for socket ID. Must be positive and
larger than 1000, as smaller values are reserved for unique in the proxy. An unused value will automatically be
auto-assigned ids. Can only be used when defining only assigned if unset. Can only be used when defining only a
a single socket. single socket.
<name> is an optional name provided for stats <name> is an optional name provided for stats
@ -1748,8 +1749,14 @@ http-check disable-on-404
id <value> id <value>
Set a persistent value for proxy ID. Must be unique and larger than 1000, as Set a persistent ID to a proxy.
smaller values are reserved for auto-assigned ids. May be used in sections : defaults | frontend | listen | backend
no | yes | yes | yes
Arguments : none
Set a persistent ID for the proxy. This ID must be unique and positive.
An unused ID will automatically be assigned if unset. The first assigned
value will be 1. This ID is currently only returned in statistics.
log global log global
@ -4583,8 +4590,9 @@ fall <count>
unspecified. See also the "check", "inter" and "rise" parameters. unspecified. See also the "check", "inter" and "rise" parameters.
id <value> id <value>
Set a persistent value for server ID. Must be unique and larger than 1000, as Set a persistent ID for the server. This ID must be positive and unique for
smaller values are reserved for auto-assigned ids. the proxy. An unused ID will automatically be assigned if unset. The first
assigned value will be 1. This ID is currently only returned in statistics.
inter <delay> inter <delay>
fastinter <delay> fastinter <delay>

View File

@ -28,6 +28,7 @@
#include <sys/un.h> #include <sys/un.h>
#include <common/config.h> #include <common/config.h>
#include <common/eb32tree.h>
#include <common/mini-clist.h> #include <common/mini-clist.h>
#include <types/counters.h> #include <types/counters.h>
@ -108,6 +109,7 @@ struct listener {
struct { struct {
const char *file; /* file where the section appears */ const char *file; /* file where the section appears */
int line; /* line where the section appears */ int line; /* line where the section appears */
struct eb32_node id; /* place in the tree of used IDs */
} conf; /* config information */ } conf; /* config information */
}; };

View File

@ -29,6 +29,7 @@
#include <common/appsession.h> #include <common/appsession.h>
#include <common/config.h> #include <common/config.h>
#include <common/eb32tree.h>
#include <common/mini-clist.h> #include <common/mini-clist.h>
#include <common/regex.h> #include <common/regex.h>
#include <common/sessionhash.h> #include <common/sessionhash.h>
@ -248,7 +249,6 @@ struct proxy {
int check_len; /* Length of the HTTP or SSL3 request */ int check_len; /* Length of the HTTP or SSL3 request */
struct chunk errmsg[HTTP_ERR_SIZE]; /* default or customized error messages for known errors */ struct chunk errmsg[HTTP_ERR_SIZE]; /* default or customized error messages for known errors */
int uuid; /* universally unique proxy ID, used for SNMP */ int uuid; /* universally unique proxy ID, used for SNMP */
int next_svid, next_lid; /* next server-id and listener-id, used for SNMP */
unsigned int backlog; /* force the frontend's listen backlog */ unsigned int backlog; /* force the frontend's listen backlog */
unsigned int bind_proc; /* bitmask of processes using this proxy. 0 = all. */ unsigned int bind_proc; /* bitmask of processes using this proxy. 0 = all. */
struct error_snapshot invalid_req, invalid_rep; /* captures of last errors */ struct error_snapshot invalid_req, invalid_rep; /* captures of last errors */
@ -262,6 +262,9 @@ struct proxy {
struct { struct {
const char *file; /* file where the section appears */ const char *file; /* file where the section appears */
int line; /* line where the section appears */ int line; /* line where the section appears */
struct eb32_node id; /* place in the tree of used IDs */
struct eb_root used_listener_id;/* list of listener IDs in use */
struct eb_root used_server_id; /* list of server IDs in use */
} conf; /* config information */ } conf; /* config information */
}; };
@ -287,7 +290,7 @@ struct redirect_rule {
}; };
extern struct proxy *proxy; extern struct proxy *proxy;
extern int next_pxid; extern struct eb_root used_proxy_id; /* list of proxy IDs in use */
#endif /* _TYPES_PROXY_H */ #endif /* _TYPES_PROXY_H */

View File

@ -135,6 +135,7 @@ struct server {
struct { struct {
const char *file; /* file where the section appears */ const char *file; /* file where the section appears */
int line; /* line where the section appears */ int line; /* line where the section appears */
struct eb32_node id; /* place in the tree of used IDs */
} conf; /* config information */ } conf; /* config information */
}; };

View File

@ -258,7 +258,6 @@ static int str2listener(char *str, struct proxy *curproxy)
tcpv4_add_listener(l); tcpv4_add_listener(l);
} }
l->luid = curproxy->next_lid++;
listeners++; listeners++;
} /* end for(port) */ } /* end for(port) */
} /* end while(next) */ } /* end while(next) */
@ -1013,9 +1012,8 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
curproxy->loglev2 = defproxy.loglev2; curproxy->loglev2 = defproxy.loglev2;
curproxy->minlvl2 = defproxy.minlvl2; curproxy->minlvl2 = defproxy.minlvl2;
curproxy->grace = defproxy.grace; curproxy->grace = defproxy.grace;
curproxy->uuid = next_pxid++; /* generate a uuid for this proxy */ curproxy->conf.used_listener_id = EB_ROOT;
curproxy->next_svid = 1; /* server id 0 is reserved */ curproxy->conf.used_server_id = EB_ROOT;
curproxy->next_lid = 1; /* listener id 0 is reserved */
goto out; goto out;
} }
@ -1172,6 +1170,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
} }
if (!strcmp(args[cur_arg], "id")) { if (!strcmp(args[cur_arg], "id")) {
struct eb32_node *node;
struct listener *l; struct listener *l;
if (curproxy->listen->next != last_listen) { if (curproxy->listen->next != last_listen) {
@ -1189,21 +1188,25 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
} }
curproxy->listen->luid = atol(args[cur_arg + 1]); curproxy->listen->luid = atol(args[cur_arg + 1]);
curproxy->listen->conf.id.key = curproxy->listen->luid;
if (curproxy->listen->luid < 1001) { if (curproxy->listen->luid <= 0) {
Alert("parsing [%s:%d]: custom id has to be > 1000\n", Alert("parsing [%s:%d]: custom id has to be > 0\n",
file, linenum); file, linenum);
err_code |= ERR_ALERT | ERR_FATAL; err_code |= ERR_ALERT | ERR_FATAL;
goto out; goto out;
} }
for (l = curproxy->listen; l; l = l->next) node = eb32_lookup(&curproxy->conf.used_listener_id, curproxy->listen->luid);
if (curproxy->listen != l && l->luid == curproxy->listen->luid) { if (node) {
l = container_of(node, struct listener, conf.id);
Alert("parsing [%s:%d]: custom id %d for socket '%s' already used at %s:%d.\n", Alert("parsing [%s:%d]: custom id %d for socket '%s' already used at %s:%d.\n",
file, linenum, l->luid, args[1], l->conf.file, l->conf.line); file, linenum, l->luid, args[1], l->conf.file, l->conf.line);
err_code |= ERR_ALERT | ERR_FATAL; err_code |= ERR_ALERT | ERR_FATAL;
goto out; goto out;
} }
eb32_insert(&curproxy->conf.used_listener_id, &curproxy->listen->conf.id);
cur_arg += 2; cur_arg += 2;
continue; continue;
} }
@ -1260,7 +1263,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
} }
} }
else if (!strcmp(args[0], "id")) { else if (!strcmp(args[0], "id")) {
struct proxy *target; struct eb32_node *node;
if (curproxy == &defproxy) { if (curproxy == &defproxy) {
Alert("parsing [%s:%d]: '%s' not allowed in 'defaults' section.\n", Alert("parsing [%s:%d]: '%s' not allowed in 'defaults' section.\n",
@ -1277,22 +1280,25 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
} }
curproxy->uuid = atol(args[1]); curproxy->uuid = atol(args[1]);
curproxy->conf.id.key = curproxy->uuid;
if (curproxy->uuid < 1001) { if (curproxy->uuid <= 0) {
Alert("parsing [%s:%d]: custom id has to be > 1000.\n", Alert("parsing [%s:%d]: custom id has to be > 0.\n",
file, linenum); file, linenum);
err_code |= ERR_ALERT | ERR_FATAL; err_code |= ERR_ALERT | ERR_FATAL;
goto out; goto out;
} }
for (target = proxy; target; target = target->next) node = eb32_lookup(&used_proxy_id, curproxy->uuid);
if (curproxy != target && curproxy->uuid == target->uuid) { if (node) {
struct proxy *target = container_of(node, struct proxy, conf.id);
Alert("parsing [%s:%d]: %s %s reuses same custom id as %s %s (declared at %s:%d).\n", Alert("parsing [%s:%d]: %s %s reuses same custom id as %s %s (declared at %s:%d).\n",
file, linenum, proxy_type_str(curproxy), curproxy->id, file, linenum, proxy_type_str(curproxy), curproxy->id,
proxy_type_str(target), target->id, target->conf.file, target->conf.line); proxy_type_str(target), target->id, target->conf.file, target->conf.line);
err_code |= ERR_ALERT | ERR_FATAL; err_code |= ERR_ALERT | ERR_FATAL;
goto out; goto out;
} }
eb32_insert(&used_proxy_id, &curproxy->conf.id);
} }
else if (!strcmp(args[0], "description")) { else if (!strcmp(args[0], "description")) {
int i, len=0; int i, len=0;
@ -2474,7 +2480,6 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
newsrv->next = curproxy->srv; newsrv->next = curproxy->srv;
curproxy->srv = newsrv; curproxy->srv = newsrv;
newsrv->proxy = curproxy; newsrv->proxy = curproxy;
newsrv->puid = curproxy->next_svid++;
newsrv->conf.file = file; newsrv->conf.file = file;
newsrv->conf.line = linenum; newsrv->conf.line = linenum;
@ -2521,7 +2526,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
cur_arg = 3; cur_arg = 3;
while (*args[cur_arg]) { while (*args[cur_arg]) {
if (!strcmp(args[cur_arg], "id")) { if (!strcmp(args[cur_arg], "id")) {
struct server *target; struct eb32_node *node;
if (!*args[cur_arg + 1]) { if (!*args[cur_arg + 1]) {
Alert("parsing [%s:%d]: '%s' expects an integer argument.\n", Alert("parsing [%s:%d]: '%s' expects an integer argument.\n",
@ -2531,21 +2536,24 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
} }
newsrv->puid = atol(args[cur_arg + 1]); newsrv->puid = atol(args[cur_arg + 1]);
newsrv->conf.id.key = newsrv->puid;
if (newsrv->puid< 1001) { if (newsrv->puid <= 0) {
Alert("parsing [%s:%d]: custom id has to be > 1000.\n", Alert("parsing [%s:%d]: custom id has to be > 0.\n",
file, linenum); file, linenum);
err_code |= ERR_ALERT | ERR_FATAL; err_code |= ERR_ALERT | ERR_FATAL;
goto out; goto out;
} }
for (target = proxy->srv; target; target = target->next) node = eb32_lookup(&curproxy->conf.used_server_id, newsrv->puid);
if (newsrv != target && newsrv->puid == target->puid) { if (node) {
struct server *target = container_of(node, struct server, conf.id);
Alert("parsing [%s:%d]: server %s reuses same custom id as server %s (declared at %s:%d).\n", Alert("parsing [%s:%d]: server %s reuses same custom id as server %s (declared at %s:%d).\n",
file, linenum, newsrv->id, target->id, target->conf.file, target->conf.line); file, linenum, newsrv->id, target->id, target->conf.file, target->conf.line);
err_code |= ERR_ALERT | ERR_FATAL; err_code |= ERR_ALERT | ERR_FATAL;
goto out; goto out;
} }
eb32_insert(&curproxy->conf.used_server_id, &newsrv->conf.id);
cur_arg += 2; cur_arg += 2;
} }
else if (!strcmp(args[cur_arg], "cookie")) { else if (!strcmp(args[cur_arg], "cookie")) {
@ -3986,6 +3994,7 @@ int check_config_validity()
struct proxy *curproxy = NULL; struct proxy *curproxy = NULL;
struct server *newsrv = NULL; struct server *newsrv = NULL;
int err_code = 0; int err_code = 0;
unsigned int next_pxid = 1;
/* /*
* Now, check for the integrity of all that we have collected. * Now, check for the integrity of all that we have collected.
@ -4016,6 +4025,17 @@ int check_config_validity()
while (curproxy != NULL) { while (curproxy != NULL) {
struct switching_rule *rule; struct switching_rule *rule;
struct listener *listener; struct listener *listener;
unsigned int next_id;
if (!curproxy->uuid) {
/* proxy ID not set, use automatic numbering with first
* spare entry starting with next_pxid.
*/
next_pxid = get_next_id(&used_proxy_id, next_pxid);
curproxy->conf.id.key = curproxy->uuid = next_pxid;
eb32_insert(&used_proxy_id, &curproxy->conf.id);
next_pxid++;
}
if (curproxy->state == PR_STSTOPPED) { if (curproxy->state == PR_STSTOPPED) {
/* ensure we don't keep listeners uselessly bound */ /* ensure we don't keep listeners uselessly bound */
@ -4301,8 +4321,19 @@ int check_config_validity()
/* /*
* ensure that we're not cross-dressing a TCP server into HTTP. * ensure that we're not cross-dressing a TCP server into HTTP.
*/ */
next_id = 1;
newsrv = curproxy->srv; newsrv = curproxy->srv;
while (newsrv != NULL) { while (newsrv != NULL) {
if (!newsrv->puid) {
/* server ID not set, use automatic numbering with first
* spare entry starting with next_svid.
*/
next_id = get_next_id(&curproxy->conf.used_server_id, next_id);
newsrv->conf.id.key = newsrv->puid = next_id;
eb32_insert(&curproxy->conf.used_server_id, &newsrv->conf.id);
next_id++;
}
if ((curproxy->mode != PR_MODE_HTTP) && (newsrv->rdr_len || newsrv->cklen)) { if ((curproxy->mode != PR_MODE_HTTP) && (newsrv->rdr_len || newsrv->cklen)) {
Alert("config : %s '%s' : server cannot have cookie or redirect prefix in non-HTTP mode.\n", Alert("config : %s '%s' : server cannot have cookie or redirect prefix in non-HTTP mode.\n",
proxy_type_str(curproxy), curproxy->id); proxy_type_str(curproxy), curproxy->id);
@ -4440,8 +4471,19 @@ int check_config_validity()
} }
/* adjust this proxy's listeners */ /* adjust this proxy's listeners */
next_id = 1;
listener = curproxy->listen; listener = curproxy->listen;
while (listener) { while (listener) {
if (!listener->luid) {
/* listener ID not set, use automatic numbering with first
* spare entry starting with next_luid.
*/
next_id = get_next_id(&curproxy->conf.used_listener_id, next_id);
listener->conf.id.key = listener->luid = next_id;
eb32_insert(&curproxy->conf.used_listener_id, &listener->conf.id);
next_id++;
}
/* enable separate counters */ /* enable separate counters */
if (curproxy->options2 & PR_O2_SOCKSTAT) { if (curproxy->options2 & PR_O2_SOCKSTAT) {
listener->counters = (struct licounters *)calloc(1, sizeof(struct licounters)); listener->counters = (struct licounters *)calloc(1, sizeof(struct licounters));

View File

@ -39,7 +39,7 @@
int listeners; /* # of proxy listeners, set by cfgparse, unset by maintain_proxies */ int listeners; /* # of proxy listeners, set by cfgparse, unset by maintain_proxies */
struct proxy *proxy = NULL; /* list of all existing proxies */ struct proxy *proxy = NULL; /* list of all existing proxies */
int next_pxid = 1; /* UUID assigned to next new proxy, 0 reserved */ struct eb_root used_proxy_id = EB_ROOT; /* list of proxy IDs in use */
/* /*
* This function returns a string containing a name describing capabilities to * This function returns a string containing a name describing capabilities to