MINOR: server: prepare parsing for dynamic servers

Prepare the server parsing API to support dynamic servers.
- define a new parsing flag to be used for dynamic servers
- each keyword contains a new field dynamic_ok to indicate if it can be
  used for a dynamic server. For now, no keyword are supported.
- do not copy settings from the default server for a new dynamic server.
- a dynamic server is created in a maintenance mode and requires an
  explicit 'enable server' command.
- a new server flag named SRV_F_DYNAMIC is created. This flag is set for
  all servers created at runtime. It might be useful later, for example
  to know if a server can be purged.
diff --git a/src/server.c b/src/server.c
index 3c26243..ec9e6e6 100644
--- a/src/server.c
+++ b/src/server.c
@@ -1626,48 +1626,48 @@
  * Note: -1 as ->skip value means that the number of arguments are variable.
  */
 static struct srv_kw_list srv_kws = { "ALL", { }, {
-	{ "backup",              srv_parse_backup,              0,  1 }, /* Flag as backup server */
-	{ "cookie",              srv_parse_cookie,              1,  1 }, /* Assign a cookie to the server */
-	{ "disabled",            srv_parse_disabled,            0,  1 }, /* Start the server in 'disabled' state */
-	{ "enabled",             srv_parse_enabled,             0,  1 }, /* Start the server in 'enabled' state */
-	{ "error-limit",         srv_parse_error_limit,         1,  1 }, /* Configure the consecutive count of check failures to consider a server on error */
-	{ "id",                  srv_parse_id,                  1,  0 }, /* set id# of server */
-	{ "init-addr",           srv_parse_init_addr,           1,  1 }, /* */
-	{ "log-proto",           srv_parse_log_proto,           1,  1 }, /* Set the protocol for event messages, only relevant in a ring section */
-	{ "maxconn",             srv_parse_maxconn,             1,  1 }, /* Set the max number of concurrent connection */
-	{ "maxqueue",            srv_parse_maxqueue,            1,  1 }, /* Set the max number of connection to put in queue */
-	{ "max-reuse",           srv_parse_max_reuse,           1,  1 }, /* Set the max number of requests on a connection, -1 means unlimited */
-	{ "minconn",             srv_parse_minconn,             1,  1 }, /* Enable a dynamic maxconn limit */
-	{ "namespace",           srv_parse_namespace,           1,  1 }, /* Namespace the server socket belongs to (if supported) */
-	{ "no-backup",           srv_parse_no_backup,           0,  1 }, /* Flag as non-backup server */
-	{ "no-send-proxy",       srv_parse_no_send_proxy,       0,  1 }, /* Disable use of PROXY V1 protocol */
-	{ "no-send-proxy-v2",    srv_parse_no_send_proxy_v2,    0,  1 }, /* Disable use of PROXY V2 protocol */
-	{ "no-tfo",              srv_parse_no_tfo,              0,  1 }, /* Disable use of TCP Fast Open */
-	{ "non-stick",           srv_parse_non_stick,           0,  1 }, /* Disable stick-table persistence */
-	{ "observe",             srv_parse_observe,             1,  1 }, /* Enables health adjusting based on observing communication with the server */
-	{ "on-error",            srv_parse_on_error,            1,  1 }, /* Configure the action on check failure */
-	{ "on-marked-down",      srv_parse_on_marked_down,      1,  1 }, /* Configure the action when a server is marked down */
-	{ "on-marked-up",        srv_parse_on_marked_up,        1,  1 }, /* Configure the action when a server is marked up */
-	{ "pool-low-conn",       srv_parse_pool_low_conn,       1,  1 }, /* Set the min number of orphan idle connecbefore being allowed to pick from other threads */
-	{ "pool-max-conn",       srv_parse_pool_max_conn,       1,  1 }, /* Set the max number of orphan idle connections, -1 means unlimited */
-	{ "pool-purge-delay",    srv_parse_pool_purge_delay,    1,  1 }, /* Set the time before we destroy orphan idle connections, defaults to 1s */
-	{ "proto",               srv_parse_proto,               1,  1 }, /* Set the proto to use for all outgoing connections */
-	{ "proxy-v2-options",    srv_parse_proxy_v2_options,    1,  1 }, /* options for send-proxy-v2 */
-	{ "redir",               srv_parse_redir,               1,  1 }, /* Enable redirection mode */
-	{ "resolve-net",         srv_parse_resolve_net,         1,  1 }, /* Set the prefered network range for name resolution */
-	{ "resolve-opts",        srv_parse_resolve_opts,        1,  1 }, /* Set options for name resolution */
-	{ "resolve-prefer",      srv_parse_resolve_prefer,      1,  1 }, /* Set the prefered family for name resolution */
-	{ "resolvers",           srv_parse_resolvers,           1,  1 }, /* Configure the resolver to use for name resolution */
-	{ "send-proxy",          srv_parse_send_proxy,          0,  1 }, /* Enforce use of PROXY V1 protocol */
-	{ "send-proxy-v2",       srv_parse_send_proxy_v2,       0,  1 }, /* Enforce use of PROXY V2 protocol */
-	{ "slowstart",           srv_parse_slowstart,           1,  1 }, /* Set the warm-up timer for a previously failed server */
-	{ "source",              srv_parse_source,             -1,  1 }, /* Set the source address to be used to connect to the server */
-	{ "stick",               srv_parse_stick,               0,  1 }, /* Enable stick-table persistence */
-	{ "tfo",                 srv_parse_tfo,                 0,  1 }, /* enable TCP Fast Open of server */
-	{ "track",               srv_parse_track,               1,  1 }, /* Set the current state of the server, tracking another one */
-	{ "socks4",              srv_parse_socks4,              1,  1 }, /* Set the socks4 proxy of the server*/
-	{ "usesrc",              srv_parse_usesrc,              0,  1 }, /* safe-guard against usesrc without preceding <source> keyword */
-	{ "weight",              srv_parse_weight,              1,  1 }, /* Set the load-balancing weight */
+	{ "backup",              srv_parse_backup,              0,  1,  0 }, /* Flag as backup server */
+	{ "cookie",              srv_parse_cookie,              1,  1,  0 }, /* Assign a cookie to the server */
+	{ "disabled",            srv_parse_disabled,            0,  1,  0 }, /* Start the server in 'disabled' state */
+	{ "enabled",             srv_parse_enabled,             0,  1,  0 }, /* Start the server in 'enabled' state */
+	{ "error-limit",         srv_parse_error_limit,         1,  1,  0 }, /* Configure the consecutive count of check failures to consider a server on error */
+	{ "id",                  srv_parse_id,                  1,  0,  0 }, /* set id# of server */
+	{ "init-addr",           srv_parse_init_addr,           1,  1,  0 }, /* */
+	{ "log-proto",           srv_parse_log_proto,           1,  1,  0 }, /* Set the protocol for event messages, only relevant in a ring section */
+	{ "maxconn",             srv_parse_maxconn,             1,  1,  0 }, /* Set the max number of concurrent connection */
+	{ "maxqueue",            srv_parse_maxqueue,            1,  1,  0 }, /* Set the max number of connection to put in queue */
+	{ "max-reuse",           srv_parse_max_reuse,           1,  1,  0 }, /* Set the max number of requests on a connection, -1 means unlimited */
+	{ "minconn",             srv_parse_minconn,             1,  1,  0 }, /* Enable a dynamic maxconn limit */
+	{ "namespace",           srv_parse_namespace,           1,  1,  0 }, /* Namespace the server socket belongs to (if supported) */
+	{ "no-backup",           srv_parse_no_backup,           0,  1,  0 }, /* Flag as non-backup server */
+	{ "no-send-proxy",       srv_parse_no_send_proxy,       0,  1,  0 }, /* Disable use of PROXY V1 protocol */
+	{ "no-send-proxy-v2",    srv_parse_no_send_proxy_v2,    0,  1,  0 }, /* Disable use of PROXY V2 protocol */
+	{ "no-tfo",              srv_parse_no_tfo,              0,  1,  0 }, /* Disable use of TCP Fast Open */
+	{ "non-stick",           srv_parse_non_stick,           0,  1,  0 }, /* Disable stick-table persistence */
+	{ "observe",             srv_parse_observe,             1,  1,  0 }, /* Enables health adjusting based on observing communication with the server */
+	{ "on-error",            srv_parse_on_error,            1,  1,  0 }, /* Configure the action on check failure */
+	{ "on-marked-down",      srv_parse_on_marked_down,      1,  1,  0 }, /* Configure the action when a server is marked down */
+	{ "on-marked-up",        srv_parse_on_marked_up,        1,  1,  0 }, /* Configure the action when a server is marked up */
+	{ "pool-low-conn",       srv_parse_pool_low_conn,       1,  1,  0 }, /* Set the min number of orphan idle connecbefore being allowed to pick from other threads */
+	{ "pool-max-conn",       srv_parse_pool_max_conn,       1,  1,  0 }, /* Set the max number of orphan idle connections, -1 means unlimited */
+	{ "pool-purge-delay",    srv_parse_pool_purge_delay,    1,  1,  0 }, /* Set the time before we destroy orphan idle connections, defaults to 1s */
+	{ "proto",               srv_parse_proto,               1,  1,  0 }, /* Set the proto to use for all outgoing connections */
+	{ "proxy-v2-options",    srv_parse_proxy_v2_options,    1,  1,  0 }, /* options for send-proxy-v2 */
+	{ "redir",               srv_parse_redir,               1,  1,  0 }, /* Enable redirection mode */
+	{ "resolve-net",         srv_parse_resolve_net,         1,  1,  0 }, /* Set the prefered network range for name resolution */
+	{ "resolve-opts",        srv_parse_resolve_opts,        1,  1,  0 }, /* Set options for name resolution */
+	{ "resolve-prefer",      srv_parse_resolve_prefer,      1,  1,  0 }, /* Set the prefered family for name resolution */
+	{ "resolvers",           srv_parse_resolvers,           1,  1,  0 }, /* Configure the resolver to use for name resolution */
+	{ "send-proxy",          srv_parse_send_proxy,          0,  1,  0 }, /* Enforce use of PROXY V1 protocol */
+	{ "send-proxy-v2",       srv_parse_send_proxy_v2,       0,  1,  0 }, /* Enforce use of PROXY V2 protocol */
+	{ "slowstart",           srv_parse_slowstart,           1,  1,  0 }, /* Set the warm-up timer for a previously failed server */
+	{ "source",              srv_parse_source,             -1,  1,  0 }, /* Set the source address to be used to connect to the server */
+	{ "stick",               srv_parse_stick,               0,  1,  0 }, /* Enable stick-table persistence */
+	{ "tfo",                 srv_parse_tfo,                 0,  1,  0 }, /* enable TCP Fast Open of server */
+	{ "track",               srv_parse_track,               1,  1,  0 }, /* Set the current state of the server, tracking another one */
+	{ "socks4",              srv_parse_socks4,              1,  1,  0 }, /* Set the socks4 proxy of the server*/
+	{ "usesrc",              srv_parse_usesrc,              0,  1,  0 }, /* safe-guard against usesrc without preceding <source> keyword */
+	{ "weight",              srv_parse_weight,              1,  1,  0 }, /* Set the load-balancing weight */
 	{ NULL, NULL, 0 },
 }};
 
@@ -2212,8 +2212,12 @@
 	srv = NULL;
 }
 
-/* This function is first intented to be used through parse_server to
- * initialize a new server on startup.
+/*
+ * 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_parse_tmpl_range(struct server *srv, const char *arg,
                                  int *nb_low, int *nb_high)
@@ -2399,6 +2403,9 @@
 			newsrv->tmpl_info.nb_high = tmpl_range_high;
 		}
 
+		if (parse_flags & SRV_PARSE_DYNAMIC)
+			newsrv->flags |= SRV_F_DYNAMIC;
+
 		/* 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.
@@ -2466,8 +2473,18 @@
 
 		(*cur_arg)++;
  skip_addr:
-		/* Copy default server settings to new server settings. */
-		srv_settings_cpy(newsrv, &curproxy->defsrv, 0);
+		if (!(parse_flags & SRV_PARSE_DYNAMIC)) {
+			/* Copy default server settings to new server */
+			srv_settings_cpy(newsrv, &curproxy->defsrv, 0);
+		} else {
+			/* Initialize dynamic server weight to 1 */
+			newsrv->uweight = newsrv->iweight = 1;
+
+			/* A dynamic server is disabled on startup */
+			newsrv->next_admin = SRV_ADMF_FMAINT;
+			newsrv->next_state = SRV_ST_STOPPED;
+			server_recalc_eweight(newsrv, 0);
+		}
 		HA_SPIN_INIT(&newsrv->lock);
 	}
 	else {
@@ -2529,6 +2546,12 @@
 		err_code = ERR_ALERT;
 		goto out;
 	}
+	else if ((parse_flags & SRV_PARSE_DYNAMIC) && !kw->dynamic_ok) {
+		memprintf(errmsg, "'%s' option is not accepted for dynamic server",
+		          args[*cur_arg]);
+		err_code |= ERR_ALERT;
+		goto out;
+	}
 
 	err_code = kw->parse(args, cur_arg, curproxy, srv, errmsg);
 
@@ -2569,7 +2592,7 @@
  */
 static int _srv_parse_finalize(char **args, int cur_arg,
                                struct server *srv, struct proxy *px,
-                               char **errmsg)
+                               int parse_flags, char **errmsg)
 {
 #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
 	int ret;
@@ -2591,10 +2614,16 @@
 		return ret;
 #endif
 
-	if (srv->flags & SRV_F_BACKUP)
-		px->srv_bck++;
-	else
-		px->srv_act++;
+	/* A dynamic server is disabled on startup. It must not be counted as
+	 * an active backend entry.
+	 */
+	if (!(parse_flags & SRV_PARSE_DYNAMIC)) {
+		if (srv->flags & SRV_F_BACKUP)
+			px->srv_bck++;
+		else
+			px->srv_act++;
+	}
+
 	srv_lb_commit_status(srv);
 
 	return 0;
@@ -2660,7 +2689,7 @@
 	}
 
 	if (!(parse_flags & SRV_PARSE_DEFAULT_SERVER)) {
-		err_code |= _srv_parse_finalize(args, cur_arg, newsrv, curproxy, &errmsg);
+		err_code |= _srv_parse_finalize(args, cur_arg, newsrv, curproxy, parse_flags, &errmsg);
 		if (err_code) {
 			display_parser_err(file, linenum, args, cur_arg, err_code, &errmsg);
 			free(errmsg);