diff --git a/TODO b/TODO index ae120a1e8..74b3ccefd 100644 --- a/TODO +++ b/TODO @@ -174,9 +174,6 @@ TODO for 1.3 filters and backend, on which every entity could rely. - implement 'on uri ', 'on host ' - remove the first now useless hop in hdr_idx - - implement "track XXX.YYY" for each server as an alternative to - health checks. This will automatically set the server state to - the same as server YYY of proxy XXX. - balance on URI hash (specify length or depth) - balance on any header hash (eg: host) - balance with redirections to real servers diff --git a/doc/configuration.txt b/doc/configuration.txt index 900b9a719..504f8a946 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -3893,6 +3893,13 @@ source [:] [usesrc { [:] | client | clientip } ] as the backend "source" keyword, except that it only applies to the server referencing it. Please consult the "source" keyword for details. +track [/] + This option enables ability to set the current state of the server by + tracking another one. Only a server with checks enabled can be tracked + so it is not possible for example to track a server that tracks another + one. If is omitted the current one is used. If disable-on-404 is + used, it has to be enabled on both proxies. + weight The "weight" parameter is used to adjust the server's weight relative to other servers. All servers will receive a load proportional to their weight diff --git a/include/proto/proxy.h b/include/proto/proxy.h index 55f9aab49..7268bfaad 100644 --- a/include/proto/proxy.h +++ b/include/proto/proxy.h @@ -36,6 +36,7 @@ void listen_proxies(void); const char *proxy_cap_str(int cap); const char *proxy_mode_str(int mode); struct proxy *findproxy(const char *name, int mode, int cap); +struct server *findserver(const struct proxy *px, const char *name); int proxy_parse_timeout(const char **args, struct proxy *proxy, struct proxy *defpx, char *err, int errlen); diff --git a/include/types/server.h b/include/types/server.h index c93236cb4..0e2183ef5 100644 --- a/include/types/server.h +++ b/include/types/server.h @@ -91,6 +91,8 @@ struct server { struct sockaddr_in tproxy_addr; /* non-local address we want to bind to for connect() */ #endif + struct server *tracknext, *tracked; /* next server in a tracking list, tracked server */ + char *trackit; /* temporary variable to make assignment deferrable */ struct sockaddr_in check_addr; /* the address to check, if different from */ short check_port; /* the port to use for the health checks */ int health; /* 0->rise-1 = bad; rise->rise+fall-1 = good */ diff --git a/src/cfgparse.c b/src/cfgparse.c index 13b0e3693..25d954a4e 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -1625,6 +1625,18 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int inv) newsrv->slowstart = (val + 999) / 1000; cur_arg += 2; } + else if (!strcmp(args[cur_arg], "track")) { + + if (!*args[cur_arg + 1]) { + Alert("parsing [%s:%d]: 'track' expects [/] as argument.\n", + file, linenum); + return -1; + } + + newsrv->trackit = strdup(args[cur_arg + 1]); + + cur_arg += 2; + } else if (!strcmp(args[cur_arg], "check")) { global.maxsock++; do_check = 1; @@ -1684,13 +1696,19 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int inv) return -1; } else { - Alert("parsing [%s:%d] : server %s only supports options 'backup', 'cookie', 'redir', 'check', 'inter', 'fastinter', 'downinter', 'rise', 'fall', 'addr', 'port', 'source', 'minconn', 'maxconn', 'maxqueue', 'slowstart' and 'weight'.\n", + Alert("parsing [%s:%d] : server %s only supports options 'backup', 'cookie', 'redir', 'check', 'track', 'inter', 'fastinter', 'downinter', 'rise', 'fall', 'addr', 'port', 'source', 'minconn', 'maxconn', 'maxqueue', 'slowstart' and 'weight'.\n", file, linenum, newsrv->id); return -1; } } if (do_check) { + if (newsrv->trackit) { + Alert("parsing [%s:%d]: unable to enable checks and tracking at the same time!\n", + file, linenum); + return -1; + } + if (!newsrv->check_port && newsrv->check_addr.sin_port) newsrv->check_port = newsrv->check_addr.sin_port; @@ -2913,6 +2931,7 @@ int readcfgfile(const char *file) /* * If this server supports a maxconn parameter, it needs a dedicated * tasks to fill the emptied slots when a connection leaves. + * Also, resolve deferred tracking dependency if needed. */ newsrv = curproxy->srv; while (newsrv != NULL) { @@ -2950,6 +2969,65 @@ int readcfgfile(const char *file) tv_eternity(&t->expire); task_queue(t); } + + if (newsrv->trackit) { + struct proxy *px; + struct server *srv; + char *pname, *sname; + + pname = newsrv->trackit; + sname = strrchr(pname, '/'); + + if (sname) + *sname++ = '\0'; + else { + sname = pname; + pname = NULL; + } + + if (pname) { + px = findproxy(pname, curproxy->mode, PR_CAP_BE); + if (!px) { + Alert("parsing %s, %s '%s', server '%s': unable to find required proxy '%s' for tracking.\n", + file, proxy_type_str(curproxy), curproxy->id, + newsrv->id, pname); + return -1; + } + } else + px = curproxy; + + srv = findserver(px, sname); + if (!srv) { + Alert("parsing %s, %s '%s', server '%s': unable to find required server '%s' for tracking.\n", + file, proxy_type_str(curproxy), curproxy->id, + newsrv->id, sname); + return -1; + } + + if (!(srv->state & SRV_CHECKED)) { + Alert("parsing %s, %s '%s', server '%s': unable to use %s/%s for " + "tracing as it does not have checks enabled.\n", + file, proxy_type_str(curproxy), curproxy->id, + newsrv->id, px->id, srv->id); + return -1; + } + + if (curproxy != px && + (curproxy->options & PR_O_DISABLE404) != (px->options & PR_O_DISABLE404)) { + Alert("parsing %s, %s '%s', server '%s': unable to use %s/%s for" + "tracing: disable-on-404 option inconsistency.\n", + file, proxy_type_str(curproxy), curproxy->id, + newsrv->id, px->id, srv->id); + return -1; + } + + newsrv->tracked = srv; + newsrv->tracknext = srv->tracknext; + srv->tracknext = newsrv; + + free(newsrv->trackit); + } + newsrv = newsrv->next; } diff --git a/src/checks.c b/src/checks.c index 1a5bf7160..53b7e5488 100644 --- a/src/checks.c +++ b/src/checks.c @@ -2,6 +2,7 @@ * Health-checks functions. * * Copyright 2000-2008 Willy Tarreau + * Copyright 2007-2008 Krzysztof Piotr Oledzki * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -34,6 +35,7 @@ #include #include +#include #include #include #include @@ -120,9 +122,11 @@ static int check_for_pending(struct server *s) */ static void set_server_down(struct server *s) { + struct server *srv; + struct chunk msg; int xferred; - if (s->health == s->rise) { + if (s->health == s->rise || s->tracked) { int srv_was_paused = s->state & SRV_GOINGDOWN; s->last_change = now.tv_sec; @@ -134,10 +138,21 @@ static void set_server_down(struct server *s) * to another server or to the proxy itself. */ xferred = redistribute_pending(s); - sprintf(trash, "%sServer %s/%s is DOWN. %d active and %d backup servers left.%s" + + msg.len = 0; + msg.str = trash; + + chunk_printf(&msg, sizeof(trash), + "%sServer %s/%s is DOWN", s->state & SRV_BACKUP ? "Backup " : "", + s->proxy->id, s->id); + + if (s->tracked) + chunk_printf(&msg, sizeof(trash), " via %s/%s", + s->tracked->proxy->id, s->tracked->id); + + chunk_printf(&msg, sizeof(trash), ". %d active and %d backup servers left.%s" " %d sessions active, %d requeued, %d remaining in queue.\n", - s->state & SRV_BACKUP ? "Backup " : "", - s->proxy->id, s->id, s->proxy->srv_act, s->proxy->srv_bck, + s->proxy->srv_act, s->proxy->srv_bck, (s->proxy->srv_bck && !s->proxy->srv_act) ? " Running on backup." : "", s->cur_sess, xferred, s->nbpend); @@ -153,10 +168,167 @@ static void set_server_down(struct server *s) set_backend_down(s->proxy); s->down_trans++; + + if (s->state && SRV_CHECKED) + for(srv = s->tracknext; srv; srv = srv->tracknext) + set_server_down(srv); } + s->health = 0; /* failure */ } +static void set_server_up(struct server *s) { + + struct server *srv; + struct chunk msg; + int xferred; + + if (s->health == s->rise || s->tracked) { + if (s->proxy->srv_bck == 0 && s->proxy->srv_act == 0) { + if (s->proxy->last_change < now.tv_sec) // ignore negative times + s->proxy->down_time += now.tv_sec - s->proxy->last_change; + s->proxy->last_change = now.tv_sec; + } + + if (s->last_change < now.tv_sec) // ignore negative times + s->down_time += now.tv_sec - s->last_change; + + s->last_change = now.tv_sec; + s->state |= SRV_RUNNING; + + if (s->slowstart > 0) { + s->state |= SRV_WARMINGUP; + if (s->proxy->lbprm.algo & BE_LB_PROP_DYN) { + /* For dynamic algorithms, start at the first step of the weight, + * without multiplying by BE_WEIGHT_SCALE. + */ + s->eweight = s->uweight; + if (s->proxy->lbprm.update_server_eweight) + s->proxy->lbprm.update_server_eweight(s); + } + } + s->proxy->lbprm.set_server_status_up(s); + + /* check if we can handle some connections queued at the proxy. We + * will take as many as we can handle. + */ + xferred = check_for_pending(s); + + msg.len = 0; + msg.str = trash; + + chunk_printf(&msg, sizeof(trash), + "%sServer %s/%s is UP", s->state & SRV_BACKUP ? "Backup " : "", + s->proxy->id, s->id); + + if (s->tracked) + chunk_printf(&msg, sizeof(trash), " via %s/%s", + s->tracked->proxy->id, s->tracked->id); + + chunk_printf(&msg, sizeof(trash), ". %d active and %d backup servers online.%s" + " %d sessions requeued, %d total in queue.\n", + s->proxy->srv_act, s->proxy->srv_bck, + (s->proxy->srv_bck && !s->proxy->srv_act) ? " Running on backup." : "", + s->cur_sess, xferred, s->nbpend); + + Warning("%s", trash); + send_log(s->proxy, LOG_NOTICE, "%s", trash); + + if (s->state && SRV_CHECKED) + for(srv = s->tracknext; srv; srv = srv->tracknext) + set_server_up(srv); + } + + if (s->health >= s->rise) + s->health = s->rise + s->fall - 1; /* OK now */ + +} + +static void set_server_disabled(struct server *s) { + + struct server *srv; + struct chunk msg; + int xferred; + + s->state |= SRV_GOINGDOWN; + s->proxy->lbprm.set_server_status_down(s); + + /* we might have sessions queued on this server and waiting for + * a connection. Those which are redispatchable will be queued + * to another server or to the proxy itself. + */ + xferred = redistribute_pending(s); + + msg.len = 0; + msg.str = trash; + + chunk_printf(&msg, sizeof(trash), + "Load-balancing on %sServer %s/%s is disabled", + s->state & SRV_BACKUP ? "Backup " : "", + s->proxy->id, s->id); + + if (s->tracked) + chunk_printf(&msg, sizeof(trash), " via %s/%s", + s->tracked->proxy->id, s->tracked->id); + + + chunk_printf(&msg, sizeof(trash),". %d active and %d backup servers online.%s" + " %d sessions requeued, %d total in queue.\n", + s->proxy->srv_act, s->proxy->srv_bck, + (s->proxy->srv_bck && !s->proxy->srv_act) ? " Running on backup." : "", + xferred, s->nbpend); + + Warning("%s", trash); + + send_log(s->proxy, LOG_NOTICE, "%s", trash); + + if (!s->proxy->srv_bck && !s->proxy->srv_act) + set_backend_down(s->proxy); + + if (s->state && SRV_CHECKED) + for(srv = s->tracknext; srv; srv = srv->tracknext) + set_server_disabled(srv); +} + +static void set_server_enabled(struct server *s) { + + struct server *srv; + struct chunk msg; + int xferred; + + s->state &= ~SRV_GOINGDOWN; + s->proxy->lbprm.set_server_status_up(s); + + /* check if we can handle some connections queued at the proxy. We + * will take as many as we can handle. + */ + xferred = check_for_pending(s); + + msg.len = 0; + msg.str = trash; + + chunk_printf(&msg, sizeof(trash), + "Load-balancing on %sServer %s/%s is enabled again", + s->state & SRV_BACKUP ? "Backup " : "", + s->proxy->id, s->id); + + if (s->tracked) + chunk_printf(&msg, sizeof(trash), " via %s/%s", + s->tracked->proxy->id, s->tracked->id); + + chunk_printf(&msg, sizeof(trash), ". %d active and %d backup servers online.%s" + " %d sessions requeued, %d total in queue.\n", + s->proxy->srv_act, s->proxy->srv_bck, + (s->proxy->srv_bck && !s->proxy->srv_act) ? " Running on backup." : "", + xferred, s->nbpend); + + Warning("%s", trash); + send_log(s->proxy, LOG_NOTICE, "%s", trash); + + if (s->state && SRV_CHECKED) + for(srv = s->tracknext; srv; srv = srv->tracknext) + set_server_enabled(srv); +} /* * This function is used only for server health-checks. It handles @@ -364,7 +536,6 @@ void process_chk(struct task *t, struct timeval *next) __label__ new_chk, out; struct server *s = t->context; struct sockaddr_in sa; - int xferred; int fd; int rv; @@ -575,103 +746,18 @@ void process_chk(struct task *t, struct timeval *next) /* we may have to add/remove this server from the LB group */ if ((s->state & SRV_RUNNING) && (s->proxy->options & PR_O_DISABLE404)) { if ((s->state & SRV_GOINGDOWN) && - ((s->result & (SRV_CHK_RUNNING|SRV_CHK_DISABLE)) == SRV_CHK_RUNNING)) { - /* server enabled again */ - s->state &= ~SRV_GOINGDOWN; - s->proxy->lbprm.set_server_status_up(s); - - /* check if we can handle some connections queued at the proxy. We - * will take as many as we can handle. - */ - xferred = check_for_pending(s); - - sprintf(trash, - "Load-balancing on %sServer %s/%s is enabled again. %d active and %d backup servers online.%s" - " %d sessions requeued, %d total in queue.\n", - s->state & SRV_BACKUP ? "Backup " : "", - s->proxy->id, s->id, s->proxy->srv_act, s->proxy->srv_bck, - (s->proxy->srv_bck && !s->proxy->srv_act) ? " Running on backup." : "", - xferred, s->nbpend); - - Warning("%s", trash); - send_log(s->proxy, LOG_NOTICE, "%s", trash); - } + ((s->result & (SRV_CHK_RUNNING|SRV_CHK_DISABLE)) == SRV_CHK_RUNNING)) + set_server_enabled(s); else if (!(s->state & SRV_GOINGDOWN) && ((s->result & (SRV_CHK_RUNNING | SRV_CHK_DISABLE)) == - (SRV_CHK_RUNNING | SRV_CHK_DISABLE))) { - /* server disabled */ - s->state |= SRV_GOINGDOWN; - s->proxy->lbprm.set_server_status_down(s); - - /* we might have sessions queued on this server and waiting for - * a connection. Those which are redispatchable will be queued - * to another server or to the proxy itself. - */ - xferred = redistribute_pending(s); - - sprintf(trash, - "Load-balancing on %sServer %s/%s is disabled. %d active and %d backup servers online.%s" - " %d sessions requeued, %d total in queue.\n", - s->state & SRV_BACKUP ? "Backup " : "", - s->proxy->id, s->id, s->proxy->srv_act, s->proxy->srv_bck, - (s->proxy->srv_bck && !s->proxy->srv_act) ? " Running on backup." : "", - xferred, s->nbpend); - - Warning("%s", trash); - - send_log(s->proxy, LOG_NOTICE, "%s", trash); - if (!s->proxy->srv_bck && !s->proxy->srv_act) - set_backend_down(s->proxy); - } + (SRV_CHK_RUNNING | SRV_CHK_DISABLE))) + set_server_disabled(s); } if (s->health < s->rise + s->fall - 1) { s->health++; /* was bad, stays for a while */ - if (s->health == s->rise) { - if (s->proxy->srv_bck == 0 && s->proxy->srv_act == 0) { - if (s->proxy->last_change < now.tv_sec) // ignore negative times - s->proxy->down_time += now.tv_sec - s->proxy->last_change; - s->proxy->last_change = now.tv_sec; - } - - if (s->last_change < now.tv_sec) // ignore negative times - s->down_time += now.tv_sec - s->last_change; - - s->last_change = now.tv_sec; - s->state |= SRV_RUNNING; - if (s->slowstart > 0) { - s->state |= SRV_WARMINGUP; - if (s->proxy->lbprm.algo & BE_LB_PROP_DYN) { - /* For dynamic algorithms, start at the first step of the weight, - * without multiplying by BE_WEIGHT_SCALE. - */ - s->eweight = s->uweight; - if (s->proxy->lbprm.update_server_eweight) - s->proxy->lbprm.update_server_eweight(s); - } - } - s->proxy->lbprm.set_server_status_up(s); - - /* check if we can handle some connections queued at the proxy. We - * will take as many as we can handle. - */ - xferred = check_for_pending(s); - - sprintf(trash, - "%sServer %s/%s is UP. %d active and %d backup servers online.%s" - " %d sessions requeued, %d total in queue.\n", - s->state & SRV_BACKUP ? "Backup " : "", - s->proxy->id, s->id, s->proxy->srv_act, s->proxy->srv_bck, - (s->proxy->srv_bck && !s->proxy->srv_act) ? " Running on backup." : "", - xferred, s->nbpend); - - Warning("%s", trash); - send_log(s->proxy, LOG_NOTICE, "%s", trash); - } - - if (s->health >= s->rise) - s->health = s->rise + s->fall - 1; /* OK now */ + set_server_up(s); } s->curfd = -1; /* no check running anymore */ fd_delete(fd); diff --git a/src/dumpstats.c b/src/dumpstats.c index 59ee93b6e..71e382afd 100644 --- a/src/dumpstats.c +++ b/src/dumpstats.c @@ -171,7 +171,7 @@ int print_csv_header(struct chunk *msg, int size) "wretr,wredis," "status,weight,act,bck," "chkfail,chkdown,lastchg,downtime,qlimit," - "pid,iid,sid,throttle,lbtot," + "pid,iid,sid,throttle,lbtot,tracked," "\n"); } @@ -587,7 +587,7 @@ int stats_dump_http(struct session *s, struct uri_auth *uri, int flags) int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri, int flags) { struct buffer *rep = s->rep; - struct server *sv; + struct server *sv, *svs; /* server and server-state, server-state=server or server->tracked */ struct chunk msg; msg.len = 0; @@ -706,8 +706,8 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri, "%s," /* rest of server: nothing */ ",,,,,,,," - /* pid, iid, sid, throttle, lbtot, */ - "%d,%d,0,,," + /* pid, iid, sid, throttle, lbtot, tracked*/ + "%d,%d,0,,,," "\n", px->id, px->feconn, px->feconn_max, px->maxconn, px->cum_feconn, @@ -734,20 +734,25 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri, sv = s->data_ctx.stats.sv; + if (sv->tracked) + svs = sv->tracked; + else + svs = sv; + /* FIXME: produce some small strings for "UP/DOWN x/y &#xxxx;" */ - if (!(sv->state & SRV_CHECKED)) + if (!(svs->state & SRV_CHECKED)) sv_state = 6; - else if (sv->state & SRV_RUNNING) { - if (sv->health == sv->rise + sv->fall - 1) + else if (svs->state & SRV_RUNNING) { + if (svs->health == svs->rise + svs->fall - 1) sv_state = 3; /* UP */ else sv_state = 2; /* going down */ - if (sv->state & SRV_GOINGDOWN) + if (svs->state & SRV_GOINGDOWN) sv_state += 2; } else - if (sv->health) + if (svs->health) sv_state = 1; /* going up */ else sv_state = 0; /* DOWN */ @@ -800,8 +805,8 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri, chunk_printf(&msg, sizeof(trash), srv_hlt_st[sv_state], - (sv->state & SRV_RUNNING) ? (sv->health - sv->rise + 1) : (sv->health), - (sv->state & SRV_RUNNING) ? (sv->fall) : (sv->rise)); + (svs->state & SRV_RUNNING) ? (svs->health - svs->rise + 1) : (svs->health), + (svs->state & SRV_RUNNING) ? (svs->fall) : (svs->rise)); chunk_printf(&msg, sizeof(trash), /* weight */ @@ -819,8 +824,11 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri, "%d%d" "%s" "", - sv->failed_checks, sv->down_trans, + svs->failed_checks, svs->down_trans, human_time(srv_downtime(sv), 1)); + else if (sv != svs) + chunk_printf(&msg, sizeof(trash), + "via %s/%s", svs->proxy->id, svs->id ); else chunk_printf(&msg, sizeof(trash), ""); @@ -908,6 +916,14 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri, /* sessions: lbtot */ chunk_printf(&msg, sizeof(trash), ",%d", sv->cum_lbconn); + + /* tracked */ + if (sv->tracked) + chunk_printf(&msg, sizeof(trash), ",%s/%s", + sv->tracked->proxy->id, sv->tracked->id); + else + chunk_printf(&msg, sizeof(trash), ","); + /* ',' then EOL */ chunk_printf(&msg, sizeof(trash), ",\n"); } @@ -991,8 +1007,8 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri, "%d,%d,%d," /* rest of backend: nothing, down transitions, last change, total downtime */ ",%d,%d,%d,," - /* pid, iid, sid, throttle, lbtot, */ - "%d,%d,0,,%d," + /* pid, iid, sid, throttle, lbtot, tracked,*/ + "%d,%d,0,,%d,," "\n", px->id, px->nbpend /* or px->totpend ? */, px->nbpend_max, diff --git a/src/proxy.c b/src/proxy.c index 281ee8e06..402fc88cf 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -176,7 +176,7 @@ int proxy_parse_timeout(const char **args, struct proxy *proxy, struct proxy *findproxy(const char *name, int mode, int cap) { - struct proxy *curproxy, *target=NULL; + struct proxy *curproxy, *target = NULL; for (curproxy = proxy; curproxy; curproxy = curproxy->next) { if ((curproxy->cap & cap)!=cap || strcmp(curproxy->id, name)) @@ -203,6 +203,37 @@ struct proxy *findproxy(const char *name, int mode, int cap) { return target; } +/* + * This function finds a server with matching name within selected proxy. + * It also checks if there are more matching servers with + * requested name as this often leads into unexpected situations. + */ + +struct server *findserver(const struct proxy *px, const char *name) { + + struct server *cursrv, *target = NULL; + + if (!px) + return NULL; + + for (cursrv = px->srv; cursrv; cursrv = cursrv->next) { + if (strcmp(cursrv->id, name)) + continue; + + if (!target) { + target = cursrv; + continue; + } + + Alert("Refusing to use duplicated server '%s' fould in proxy: %s!\n", + name, px->id); + + return NULL; + } + + return target; +} + /* * This function creates all proxy sockets. It should be done very early, * typically before privileges are dropped. The sockets will be registered