]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: server: implement 'add server' cli command
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Mon, 8 Mar 2021 16:13:32 +0000 (17:13 +0100)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Thu, 18 Mar 2021 14:52:07 +0000 (15:52 +0100)
Add a new cli command 'add server'. This command is used to create a new
server at runtime attached on an existing backend. The syntax is the
following one :

$ add server <be_name>/<sv_name> [<kws>...]

This command is only available through experimental mode for the moment.

Currently, no server keywords are supported. They will be activated
individually when deemed properly functional and safe.

Another limitation is put on the backend load-balancing algorithm. The
algorithm must use consistent hashing to guarantee a minimal
reallocation of existing connections on the new server insertion.

doc/management.txt
src/server.c

index 5f88a5d71ccd59c8c982bf9c8388020a0d72bdac..bc7071def41e3dcbf01a2f50cace703832af9091 100644 (file)
@@ -1423,6 +1423,18 @@ add map <map> <payload>
 
     >
 
+add server <backend>/<server> [args]*
+  Instantiate a new server attached to the backend <backend>. Only supported on
+  a CLI connection running in experimental mode (see "experimental-mode on").
+  This method is still in development and may change in the future.
+
+  The <server> name must not be already used in the backend. A special
+  restriction is put on the backend which must used a compatible load-balancing
+  algorithm with consistent hashing method. A subset of keywords from the
+  server config file statement can be used to configure the server behavior.
+  They can also be specified via an existing 'default-server' statement in the
+  backend. For the moment, no keywords are supported.
+
 add ssl crt-list <crtlist> <certificate>
 add ssl crt-list <crtlist> <payload>
   Add an certificate in a crt-list. It can also be used for directories since
index ec9e6e69112ab5febfff1c0702124bda39834c1a..e77d77fb5675ae23f6606f3d36358b50470de489 100644 (file)
@@ -38,7 +38,7 @@
 #include <haproxy/sample.h>
 #include <haproxy/server.h>
 #include <haproxy/ssl_sock.h>
-#include <haproxy/stats-t.h>
+#include <haproxy/stats.h>
 #include <haproxy/stream.h>
 #include <haproxy/stream_interface.h>
 #include <haproxy/task.h>
@@ -4278,6 +4278,168 @@ static int cli_parse_enable_server(char **args, char *payload, struct appctx *ap
        return 1;
 }
 
+/* Allocates data structure related to load balancing for the server <sv>. It
+ * is only required for dynamic servers.
+ *
+ * At the moment, the server lock is not used as this function is only called
+ * for a dynamic server not yet registered.
+ *
+ * Returns 1 on success, 0 on allocation failure.
+ */
+static int srv_alloc_lb(struct server *sv, struct proxy *be)
+{
+       int node;
+
+       sv->lb_tree = (sv->flags & SRV_F_BACKUP) ?
+                     &be->lbprm.chash.bck : &be->lbprm.chash.act;
+       sv->lb_nodes_tot = sv->uweight * BE_WEIGHT_SCALE;
+       sv->lb_nodes_now = 0;
+
+       if ((be->lbprm.algo & BE_LB_PARM) == BE_LB_RR_RANDOM) {
+               sv->lb_nodes = calloc(sv->lb_nodes_tot, sizeof(*sv->lb_nodes));
+
+               if (!sv->lb_nodes)
+                       return 0;
+
+               for (node = 0; node < sv->lb_nodes_tot; node++) {
+                       sv->lb_nodes[node].server = sv;
+                       sv->lb_nodes[node].node.key = full_hash(sv->puid * SRV_EWGHT_RANGE + node);
+               }
+       }
+
+       return 1;
+}
+
+/* Parse a "add server" command
+ * Returns 0 if the server has been successfully initialized, 1 on failure.
+ */
+static int cli_parse_add_server(char **args, char *payload, struct appctx *appctx, void *private)
+{
+       struct proxy *be;
+       struct server *srv;
+       char *be_name, *sv_name;
+       char *errmsg = NULL;
+       int errcode, argc;
+       int i;
+       const int parse_flags = SRV_PARSE_DYNAMIC|SRV_PARSE_PARSE_ADDR;
+
+       if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
+               return 1;
+
+       ++args;
+
+       sv_name = be_name = args[1];
+       /* split backend/server arg */
+       while (*sv_name && *(++sv_name)) {
+               if (*sv_name == '/') {
+                       *sv_name = '\0';
+                       ++sv_name;
+                       break;
+               }
+       }
+
+       if (!*sv_name)
+               return cli_err(appctx, "Require 'backend/server'.");
+
+       get_backend_server(be_name, sv_name, &be, &srv);
+       if (!be)
+               return cli_err(appctx, "No such backend.");
+
+       if (!(be->lbprm.algo & BE_LB_PROP_DYN)) {
+               cli_err(appctx, "Backend must use a consistent hashing method for load balancing to support dynamic servers.");
+               return 1;
+       }
+
+       if (srv)
+               return cli_err(appctx, "Already exists a server with the same name in backend.");
+
+       args[1] = sv_name;
+       errcode = _srv_parse_init(&srv, args, &argc, be, parse_flags, &errmsg);
+       if (errcode) {
+               if (errmsg)
+                       cli_dynerr(appctx, errmsg);
+               goto out;
+       }
+
+       while (*args[argc]) {
+               errcode = _srv_parse_kw(srv, args, &argc, be, parse_flags, &errmsg);
+
+               if (errcode) {
+                       if (errmsg)
+                               cli_dynerr(appctx, errmsg);
+                       goto out;
+               }
+       }
+
+       _srv_parse_finalize(args, argc, srv, be, parse_flags, &errmsg);
+       if (errmsg) {
+               cli_dynerr(appctx, errmsg);
+               goto out;
+       }
+
+       srv->per_thr = calloc(global.nbthread, sizeof(*srv->per_thr));
+       if (!srv->per_thr) {
+               cli_err(appctx, "failed to allocate per-thread lists for server.");
+               goto out;
+       }
+
+       for (i = 0; i < global.nbthread; i++) {
+               srv->per_thr[i].idle_conns = EB_ROOT;
+               srv->per_thr[i].safe_conns = EB_ROOT;
+               srv->per_thr[i].avail_conns = EB_ROOT;
+               MT_LIST_INIT(&srv->per_thr[i].streams);
+       }
+
+       if (srv->max_idle_conns != 0) {
+               srv->curr_idle_thr = calloc(global.nbthread, sizeof(*srv->curr_idle_thr));
+               if (!srv->curr_idle_thr) {
+                       cli_err(appctx, "failed to allocate counters for server.");
+                       goto out;
+               }
+       }
+
+       if (!srv_alloc_lb(srv, be)) {
+               cli_err(appctx, "Failed to initialize load-balancing data.");
+               goto out;
+       }
+
+       if (!stats_allocate_proxy_counters_internal(&srv->extra_counters,
+                                                   COUNTERS_SV,
+                                                   STATS_PX_CAP_SRV)) {
+               cli_err(appctx, "failed to allocate extra counters for server.");
+               goto out;
+       }
+
+       /* attach the server to the end of proxy linked list
+        *
+        * The proxy servers list is currently not protected by a lock, so this
+        * requires thread_isolate/release.
+        */
+       thread_isolate();
+       /* TODO use a double-linked list for px->srv */
+       if (be->srv) {
+               struct server *next;
+               for (next = be->srv; next->next; next = next->next)
+                       ;
+
+               next->next = srv;
+       }
+       else {
+               srv->next = be->srv;
+               be->srv = srv;
+       }
+       thread_release();
+
+       cli_msg(appctx, LOG_INFO, "New server registered.");
+
+       return 0;
+
+out:
+       if (srv)
+               free_server(srv);
+       return 1;
+}
+
 /* register cli keywords */
 static struct cli_kw_list cli_kws = {{ },{
        { { "disable", "agent",  NULL }, "disable agent  : disable agent checks (use 'set server' instead)", cli_parse_disable_agent, NULL },
@@ -4290,6 +4452,7 @@ static struct cli_kw_list cli_kws = {{ },{
        { { "set", "server", NULL }, "set server     : change a server's state, weight, address or ssl",  cli_parse_set_server },
        { { "get", "weight", NULL }, "get weight     : report a server's current weight",  cli_parse_get_weight },
        { { "set", "weight", NULL }, "set weight     : change a server's weight (deprecated)",  cli_parse_set_weight },
+       { { "add", "server", NULL }, "add server     : create a new server (EXPERIMENTAL)", cli_parse_add_server, NULL, NULL, NULL, ACCESS_EXPERIMENTAL },
 
        {{},}
 }};