]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: server: cli: Add server FQDNs to server-state file and stats socket.
authorFrédéric Lécaille <flecaille@haproxy.com>
Wed, 26 Apr 2017 09:24:02 +0000 (11:24 +0200)
committerWilly Tarreau <w@1wt.eu>
Wed, 3 May 2017 04:58:53 +0000 (06:58 +0200)
This patch adds a new stats socket command to modify server
FQDNs at run time.
Its syntax:
  set server <backend>/<server> fqdn <FQDN>
This patch also adds FQDNs to server state file at the end
of each line for backward compatibility ("-" if not present).

doc/management.txt
include/types/server.h
src/cfgparse.c
src/proxy.c
src/server.c

index 33682779ed3150ed4c049d0211283b7df3c196ae..d1ea1f64af384a518b1b022cd47b222e0c091238 100644 (file)
@@ -1650,6 +1650,9 @@ set server <backend>/<server> weight <weight>[%]
   Change a server's weight to the value passed in argument. This is the exact
   equivalent of the "set weight" command below.
 
+set server <backend>/<server> fqdn <FQDN>
+  Change a server's FQDN to the value passed in argument.
+
 set ssl ocsp-response <response>
   This command is used to update an OCSP Response for a certificate (see "crt"
   on "bind" lines). Same controls are performed as during the initial loading of
@@ -1962,6 +1965,9 @@ show servers state [<backend>]
                                     0x20 = SRV_ADMF_RMAINT
                                       The server is in maintenance because of an
                                       IP address resolution failure.
+                                    0x40 = SRV_ADMF_HMAINT
+                                      The server FQDN was set from stats socket.
+
      srv_uweight:                 User visible server's weight.
      srv_iweight:                 Server's initial weight.
      srv_time_since_last_change:  Time since last operational change.
@@ -2003,6 +2009,7 @@ show servers state [<backend>]
                                   configuration.
      srv_f_forced_id:             Flag to know if the server's ID is forced by
                                   configuration.
+     srv_fqdn:                    Server FQDN.
 
 show sess
   Dump all known sessions. Avoid doing this on slow connections as this can
index 8d68dcb56270277b171f865cf7b29f85797fbc49..83b1e80c6f9d1472fd792f339e6ff551baf31275 100644 (file)
@@ -82,6 +82,7 @@ enum srv_admin {
        SRV_ADMF_IDRAIN    = 0x10,        /* the server has inherited the drain status from a tracked server */
        SRV_ADMF_DRAIN     = 0x18,        /* mask to check if any drain flag is present */
        SRV_ADMF_RMAINT    = 0x20,        /* the server is down because of an IP address resolution failure */
+       SRV_ADMF_HMAINT    = 0x40,        /* the server FQDN has been set from socket stats */
 };
 
 /* options for servers' "init-addr" parameter
@@ -103,7 +104,26 @@ enum srv_initaddr {
 #define SRV_STATE_FILE_VERSION 1
 #define SRV_STATE_FILE_VERSION_MIN 1
 #define SRV_STATE_FILE_VERSION_MAX 1
-#define SRV_STATE_FILE_FIELD_NAMES "be_id be_name srv_id srv_name srv_addr srv_op_state srv_admin_state srv_uweight srv_iweight srv_time_since_last_change srv_check_status srv_check_result srv_check_health srv_check_state srv_agent_state bk_f_forced_id srv_f_forced_id"
+#define SRV_STATE_FILE_FIELD_NAMES \
+    "be_id "                      \
+    "be_name "                    \
+    "srv_id "                     \
+    "srv_name "                   \
+    "srv_addr "                   \
+    "srv_op_state "               \
+    "srv_admin_state "            \
+    "srv_uweight "                \
+    "srv_iweight "                \
+    "srv_time_since_last_change " \
+    "srv_check_status "           \
+    "srv_check_result "           \
+    "srv_check_health "           \
+    "srv_check_state "            \
+    "srv_agent_state "            \
+    "bk_f_forced_id "             \
+    "srv_f_forced_id "            \
+    "srv_fqdn"
+
 #define SRV_STATE_FILE_MAX_FIELDS 18
 #define SRV_STATE_FILE_NB_FIELDS_VERSION_1 18
 #define SRV_STATE_LINE_MAXLEN 512
index d44949a7df71b0804b41ffef1805e85f6afabce3..f16732191335a821bf3afe0ce4a3abff3804752d 100644 (file)
@@ -8492,8 +8492,6 @@ out_uri_auth_compat:
                                        newsrv->id, newsrv->resolvers_id);
                                        cfgerr++;
                                } else {
-                                       free(newsrv->resolvers_id);
-                                       newsrv->resolvers_id = NULL;
                                        if (newsrv->resolution)
                                                newsrv->resolution->resolvers = curr_resolvers;
                                }
index dc702139adde1da7f429e0d6c5266cf6801293c8..f6a32146dc1fb787080c6b105cbb3e10abd66cbf 100644 (file)
@@ -1458,13 +1458,13 @@ static int dump_servers_state(struct stream_interface *si, struct chunk *buf)
                                "%d %s %s "
                                "%d %d %d %d %ld "
                                "%d %d %d %d %d "
-                               "%d %d"
+                               "%d %d %s"
                                "\n",
                                px->uuid, px->id,
                                srv->puid, srv->id, srv_addr,
                                srv->state, srv->admin, srv->uweight, srv->iweight, (long int)srv_time_since_last_change,
                                srv->check.status, srv->check.result, srv->check.health, srv->check.state, srv->agent.state,
-                               bk_f_forced_id, srv_f_forced_id);
+                               bk_f_forced_id, srv_f_forced_id, srv->hostname ? srv->hostname : "-");
                if (bi_putchk(si_ic(si), &trash) == -1) {
                        si_applet_cant_put(si);
                        return 0;
index 69c1ec30ed7f47bc78a2fa952127cac2ec37fad8..85da083517c47ed8337febdc11f9280f863a3183 100644 (file)
@@ -46,6 +46,7 @@
 
 static void srv_update_state(struct server *srv, int version, char **params);
 static int srv_apply_lastaddr(struct server *srv, int *err_code);
+static int srv_set_fqdn(struct server *srv, const char *fqdn);
 
 /* List head of all known server keywords */
 static struct srv_kw_list srv_keywords = {
@@ -1652,16 +1653,18 @@ static void srv_ssl_settings_cpy(struct server *srv, struct server *src)
 /*
  * Allocate <srv> server dns resolution.
  * May be safely called with a default server as <src> argument (without hostname).
+ * Returns -1 in case of any allocation failure, 0 if not.
  */
-static void srv_alloc_dns_resolution(struct server *srv, const char *hostname)
+static int srv_alloc_dns_resolution(struct server *srv, const char *hostname)
 {
        char *hostname_dn;
        int hostname_dn_len;
        struct dns_resolution *dst_dns_rslt;
 
        if (!hostname)
-               return;
+               return 0;
 
+       free(srv->hostname);
        srv->hostname = strdup(hostname);
        dst_dns_rslt = calloc(1, sizeof *dst_dns_rslt);
        hostname_dn_len = dns_str_to_dn_label_len(hostname);
@@ -1687,13 +1690,41 @@ static void srv_alloc_dns_resolution(struct server *srv, const char *hostname)
        /* a first resolution has been done by the configuration parser */
        srv->resolution->last_resolution = 0;
 
-       return;
+       if (srv->resolvers_id) {
+               struct dns_resolvers *curr_resolvers;
+               int found = 0;
+
+               list_for_each_entry(curr_resolvers, &dns_resolvers, list) {
+                       if (!strcmp(curr_resolvers->id, srv->resolvers_id)) {
+                               found = 1;
+                               break;
+                       }
+               }
+               if (!found)
+                       goto err;
+               srv->resolution->resolvers = curr_resolvers;
+       }
+       else
+               goto err;
+
+       return 0;
 
  err:
        free(srv->hostname);
        srv->hostname = NULL;
        free(hostname_dn);
        free(dst_dns_rslt);
+       return -1;
+}
+
+static void srv_free_dns_resolution(struct server *srv)
+{
+       if (!srv->resolution)
+               return;
+
+       free(srv->resolution->hostname_dn);
+       free(srv->resolution);
+       srv->resolution = NULL;
 }
 
 /*
@@ -2888,7 +2919,10 @@ static void srv_update_state(struct server *srv, int version, char **params)
        int srv_check_state, srv_agent_state;
        int bk_f_forced_id;
        int srv_f_forced_id;
+       int fqdn_set_by_cli;
+       const char *fqdn;
 
+       fqdn = NULL;
        msg = get_trash_chunk();
        switch (version) {
                case 1:
@@ -2907,6 +2941,7 @@ static void srv_update_state(struct server *srv, int version, char **params)
                         * srv_agent_state:      params[10]
                         * bk_f_forced_id:       params[11]
                         * srv_f_forced_id:      params[12]
+                        * srv_fqdn:             params[13]
                         */
 
                        /* validating srv_op_state */
@@ -2925,9 +2960,12 @@ static void srv_update_state(struct server *srv, int version, char **params)
                        p = NULL;
                        errno = 0;
                        srv_admin_state = strtol(params[2], &p, 10);
+                       fqdn_set_by_cli = !!(srv_admin_state & SRV_ADMF_HMAINT);
 
-                       /* inherited statuses will be recomputed later */
-                       srv_admin_state &= ~SRV_ADMF_IDRAIN & ~SRV_ADMF_IMAINT;
+                       /* inherited statuses will be recomputed later.
+                        * Also disable SRV_ADMF_HMAINT flag (set from stats socket fqdn).
+                        */
+                       srv_admin_state &= ~SRV_ADMF_IDRAIN & ~SRV_ADMF_IMAINT & ~SRV_ADMF_HMAINT;
 
                        if ((p == params[2]) || errno == EINVAL || errno == ERANGE ||
                            (srv_admin_state != 0 &&
@@ -3018,6 +3056,14 @@ static void srv_update_state(struct server *srv, int version, char **params)
                        if (p == params[12] || errno == EINVAL || errno == ERANGE || !((srv_f_forced_id == 0) || (srv_f_forced_id == 1)))
                                chunk_appendf(msg, ", invalid srv_f_forced_id value '%s'", params[12]);
 
+                       /* validating srv_fqdn */
+                       fqdn = params[13];
+                       if (fqdn && *fqdn == '-')
+                               fqdn = NULL;
+                       if (fqdn && (strlen(fqdn) > DNS_MAX_NAME_SIZE || invalid_domainchar(fqdn))) {
+                               chunk_appendf(msg, ", invalid srv_fqdn value '%s'", params[13]);
+                               fqdn = NULL;
+                       }
 
                        /* don't apply anything if one error has been detected */
                        if (msg->len)
@@ -3122,6 +3168,34 @@ static void srv_update_state(struct server *srv, int version, char **params)
 
                        /* load server IP address */
                        srv->lastaddr = strdup(params[0]);
+
+                       if (fqdn && srv->hostname) {
+                               if (!strcmp(srv->hostname, fqdn)) {
+                                       /* Here we reset the 'set from stats socket FQDN' flag
+                                        * to support such transitions:
+                                        * Let's say initial FQDN value is foo1 (in configuration file).
+                                        * - FQDN changed from stats socket, from foo1 to foo2 value,
+                                        * - FQDN changed again from file configuration (with the same previous value
+                                            set from stats socket, from foo1 to foo2 value),
+                                        * - reload for any other reason than a FQDN modification,
+                                        * the configuration file FQDN matches the fqdn server state file value.
+                                        * So we must reset the 'set from stats socket FQDN' flag to be consistent with
+                                        * any futher FQDN modification.
+                                        */
+                                       srv->admin &= ~SRV_ADMF_HMAINT;
+                               }
+                               else {
+                                       /* If the FDQN has been changed from stats socket,
+                                        * apply fqdn state file value (which is the value set
+                                        * from stats socket).
+                                        */
+                                       if (fqdn_set_by_cli) {
+                                               srv_set_fqdn(srv, fqdn);
+                                               srv->admin |= SRV_ADMF_HMAINT;
+                                       }
+                               }
+                       }
+
                        break;
                default:
                        chunk_appendf(msg, ", version '%d' not supported", version);
@@ -3152,8 +3226,8 @@ void apply_server_state(void)
        char *cur, *end;
        char mybuf[SRV_STATE_LINE_MAXLEN];
        int mybuflen;
-       char *params[SRV_STATE_FILE_MAX_FIELDS];
-       char *srv_params[SRV_STATE_FILE_MAX_FIELDS];
+       char *params[SRV_STATE_FILE_MAX_FIELDS] = {0};
+       char *srv_params[SRV_STATE_FILE_MAX_FIELDS] = {0};
        int arg, srv_arg, version, diff;
        FILE *f;
        char *filepath;
@@ -3331,12 +3405,18 @@ void apply_server_state(void)
                        while (isspace(*cur))
                                ++cur;
 
-                       if (cur == end)
+                       /* Ignore empty or commented lines */
+                       if (cur == end || *cur == '#')
                                continue;
 
-                       /* ignore comment line */
-                       if (*cur == '#')
+                       /* truncated lines */
+                       if (mybuf[mybuflen - 1] != '\n') {
+                               Warning("server-state file '%s': truncated line\n", filepath);
                                continue;
+                       }
+
+                       /* Removes trailing '\n' */
+                       mybuf[mybuflen - 1] = '\0';
 
                        /* we're now ready to move the line into *srv_params[] */
                        params[0] = cur;
@@ -3364,6 +3444,7 @@ void apply_server_state(void)
                                                         * srv_agent_state:      params[14] => srv_params[10]
                                                         * bk_f_forced_id:       params[15] => srv_params[11]
                                                         * srv_f_forced_id:      params[16] => srv_params[12]
+                                                        * srv_fqdn:             params[17] => srv_params[13]
                                                         */
                                                        if (arg >= 4) {
                                                                srv_params[srv_arg] = cur;
@@ -4016,6 +4097,16 @@ int srv_set_addr_via_libc(struct server *srv, int *err_code)
        return 0;
 }
 
+/* Set the server's FDQN (->hostname) from <hostname>.
+ * Returns -1 if failed, 0 if not.
+ */
+int srv_set_fqdn(struct server *srv, const char *hostname)
+{
+       srv_free_dns_resolution(srv);
+
+       return srv_alloc_dns_resolution(srv, hostname);
+}
+
 /* Sets the server's address (srv->addr) from srv->lastaddr which was filled
  * from the state file. This is suited for initial address configuration.
  * Returns 0 on success otherwise a non-zero error code. In case of error,
@@ -4139,6 +4230,46 @@ int srv_init_addr(void)
        return return_code;
 }
 
+const char *update_server_fqdn(struct server *server, const char *fqdn, const char *updater)
+{
+
+       struct chunk *msg;
+
+       msg = get_trash_chunk();
+       chunk_reset(msg);
+
+       if (!strcmp(fqdn, server->hostname)) {
+               chunk_appendf(msg, "no need to change the FDQN");
+               goto out;
+       }
+
+       if (strlen(fqdn) > DNS_MAX_NAME_SIZE || invalid_domainchar(fqdn)) {
+               chunk_appendf(msg, "invalid fqdn '%s'", fqdn);
+               goto out;
+       }
+
+       chunk_appendf(msg, "%s/%s changed its FQDN from %s to %s",
+                     server->proxy->id, server->id, server->hostname, fqdn);
+
+       if (srv_set_fqdn(server, fqdn) < 0) {
+               chunk_reset(msg);
+               chunk_appendf(msg, "could not update %s/%s FQDN",
+                             server->proxy->id, server->id);
+               goto out;
+       }
+
+       /* Flag as FQDN set from stats socket. */
+       server->admin |= SRV_ADMF_HMAINT;
+
+ out:
+       if (updater)
+               chunk_appendf(msg, " by '%s'", updater);
+       chunk_appendf(msg, "\n");
+
+       return msg->str;
+}
+
+
 /* Expects to find a backend and a server in <arg> under the form <backend>/<server>,
  * and returns the pointer to the server. Otherwise, display adequate error messages
  * on the CLI, sets the CLI's state to CLI_ST_PRINT and returns NULL. This is only
@@ -4319,8 +4450,20 @@ static int cli_parse_set_server(char **args, struct appctx *appctx, void *privat
                }
                srv_clr_admin_flag(sv, SRV_ADMF_RMAINT);
        }
+       else if (strcmp(args[3], "fqdn") == 0) {
+               if (!*args[4]) {
+                       appctx->ctx.cli.msg = "set server <b>/<s> fqdn requires a FQDN.\n";
+                       appctx->st0 = CLI_ST_PRINT;
+                       return 1;
+               }
+               warning = update_server_fqdn(sv, args[4], "stats socket command");
+               if (warning) {
+                       appctx->ctx.cli.msg = warning;
+                       appctx->st0 = CLI_ST_PRINT;
+               }
+       }
        else {
-               appctx->ctx.cli.msg = "'set server <srv>' only supports 'agent', 'health', 'state', 'weight', 'addr' and 'check-port'.\n";
+               appctx->ctx.cli.msg = "'set server <srv>' only supports 'agent', 'health', 'state', 'weight', 'addr', 'fqdn' and 'check-port'.\n";
                appctx->st0 = CLI_ST_PRINT;
        }
        return 1;