summaryrefslogtreecommitdiff
path: root/tacplus-daemon/test/serverConnectTester.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tacplus-daemon/test/serverConnectTester.cpp')
-rw-r--r--tacplus-daemon/test/serverConnectTester.cpp1527
1 files changed, 1527 insertions, 0 deletions
diff --git a/tacplus-daemon/test/serverConnectTester.cpp b/tacplus-daemon/test/serverConnectTester.cpp
new file mode 100644
index 0000000..98654ba
--- /dev/null
+++ b/tacplus-daemon/test/serverConnectTester.cpp
@@ -0,0 +1,1527 @@
+/*
+ Copyright (c) 2018-2019 AT&T Intellectual Property.
+ Copyright (c) 2015 Brocade Communications Systems, Inc.
+
+ SPDX-License-Identifier: GPL-2.0-only
+*/
+
+#include "CppUTest/TestHarness.h"
+extern "C" {
+ #include "tacplus_srv_conn.h"
+ #include "global.h"
+ #include "parser.h"
+ #include "statistics.h"
+}
+#include "ut_utils.h"
+
+TEST_GROUP(ServerConnection) {
+
+#define SET_OPTS_SERVER(O,I,A,P) \
+ { \
+ CHECK(I < O->n_servers); \
+ O->server[I].id = I; \
+ O->server[I].addrs = tacplus_addrinfo(A, P); \
+ SET_OPTS_SERVER_SRC_ADDR(O, I, NULL); \
+ O->server[I].timeout = (I+1)*5; \
+ O->server[I].secret = strdup(#I); \
+ }
+
+#define SET_OPTS_SERVER_SRC_ADDR(O,I,S) \
+ { \
+ O->server[I].src_addrs = S ? tacplus_addrinfo(S, "0") : NULL; \
+ }
+
+ static const int num_servers = 3;
+
+ struct tacplus_options *opts;
+
+ void setup()
+ {
+ CHECK_EQUAL(0, create_statistics(num_servers));
+
+ connControl->opts = opts = tacplus_options_alloc(num_servers);
+ CHECK(opts);
+
+ SET_OPTS_SERVER(opts, 0, "1.1.1.1", "1");
+ SET_OPTS_SERVER(opts, 1, "2.2.2.2", "2");
+ SET_OPTS_SERVER(opts, 2, "3:3::3:3", "3");
+ }
+
+ void teardown()
+ {
+ cleanup_tacplus_options(&opts);
+ POINTERS_EQUAL(NULL, opts);
+
+ free_statistics();
+
+ ut_reset_tac_connect_wrapper();
+ }
+
+};
+
+#define PORT_STR_LEN (strlen("65535") + 1)
+
+static struct tacplus_options *copy_tacplus_options(struct tacplus_options *src)
+{
+ struct tacplus_options *new_opts = tacplus_options_alloc(src->n_servers);
+ CHECK(new_opts);
+
+ memcpy(new_opts, src, sizeof(*src));
+
+ for (unsigned i = 0; i < src->n_servers; i++) {
+ struct tacplus_options::tacplus_options_server *server = &(src->server[i]);
+
+ /* Quick and dirty copy for simple params */
+ memcpy(&new_opts->server[server->id], server, sizeof(*server));
+
+ /* Now need to properly copy params which point to allocated memory */
+ char port_str[PORT_STR_LEN];
+ int ret = snprintf(port_str, sizeof(port_str),
+ "%u", get_addrinfo_port(server->addrs));
+ CHECK(ret > 0 && ret < (int) sizeof(port_str));
+
+ SET_OPTS_SERVER(new_opts, server->id,
+ addrinfo_to_string(server->addrs), port_str);
+ if (server->src_addrs)
+ SET_OPTS_SERVER_SRC_ADDR(new_opts, server->id,
+ addrinfo_to_string(server->src_addrs));
+ }
+
+ return new_opts;
+}
+
+TEST(ServerConnection, initOptions)
+{
+ CHECK_EQUAL(num_servers, opts->n_servers);
+ CHECK_EQUAL(HIGHEST_PRIO_SERVER_ID, opts->curr_server);
+
+ for (unsigned i = 0; i < opts->n_servers; i++) {
+ struct tacplus_options::tacplus_options_server *serv = \
+ (struct tacplus_options::tacplus_options_server *) tacplus_server(opts, i);
+#ifndef HAVE_LIBTAC_EVENT
+ CHECK_EQUAL(-1, serv->fd);
+#endif
+ CHECK_TIMESPEC_VALS(serv->state.lastTrouble, -1, -1);
+ }
+}
+
+TEST(ServerConnection, lookupServer)
+{
+ struct tacplus_options::tacplus_options_server *serv;
+
+ for (int i = 0; i < num_servers; i++) {
+ serv = (struct tacplus_options::tacplus_options_server *) tacplus_server(opts, i);
+ CHECK_EQUAL(i, serv->id);
+ }
+
+ /* Check out of bounds request */
+ POINTERS_EQUAL(NULL, tacplus_server(opts, num_servers));
+ POINTERS_EQUAL(NULL, tacplus_server(opts, num_servers+9999));
+ POINTERS_EQUAL(NULL, tacplus_server(opts, -1));
+}
+
+#ifndef HAVE_LIBTAC_EVENT
+TEST(ServerConnection, lookupCurrentServer)
+{
+ struct tacplus_options::tacplus_options_server *serv;
+
+ CHECK_EQUAL(0, opts->curr_server);
+ serv = (struct tacplus_options::tacplus_options_server *) tacplus_current_server(opts);
+ CHECK_EQUAL(0, serv->id);
+
+ opts->curr_server = 2;
+ serv = (struct tacplus_options::tacplus_options_server *) tacplus_current_server(opts);
+ CHECK_EQUAL(2, serv->id);
+
+ opts->curr_server = INVALID_SERVER_ID;
+ POINTERS_EQUAL(NULL, tacplus_current_server(opts));
+}
+
+TEST(ServerConnection, lookupCurrentSessionExtra)
+{
+ struct tac_session_extra extra, *ret;
+
+ CHECK_EQUAL(0, opts->curr_server);
+ ret = tacplus_current_session_extra(opts, &extra);
+
+ POINTERS_EQUAL(&extra, ret);
+ CHECK_EQUAL(0, extra.server_id);
+ POINTERS_EQUAL(&opts->server[0], extra.server);
+
+ opts->curr_server = 2;
+ ret = tacplus_current_session_extra(opts, &extra);
+
+ POINTERS_EQUAL(&extra, ret);
+ CHECK_EQUAL(2, extra.server_id);
+ POINTERS_EQUAL(&opts->server[2], extra.server);
+
+ opts->curr_server = INVALID_SERVER_ID;
+ ret = tacplus_current_session_extra(opts, &extra);
+
+ POINTERS_EQUAL(&extra, ret);
+ CHECK_EQUAL(INVALID_SERVER_ID, extra.server_id);
+ POINTERS_EQUAL(NULL, extra.server);
+}
+
+TEST(ServerConnection, connectSingleServerNoHoldDown)
+{
+ opts->n_servers = 1;
+ SET_OPTS_SERVER(opts, 0, "127.0.0.1", "100");
+ SET_OPTS_SERVER_SRC_ADDR(opts, 0, "10.10.10.10");
+
+ int fds[] = { 1, -1, 2 };
+ ut_set_tac_connect_fds(fds, ARRAY_SIZE(fds));
+
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(1, opts->server[0].fd);
+ CHECK_EQUAL(0, opts->curr_server);
+ CHECK_EQUAL(INVALID_SERVER_ID, opts->next_server);
+
+ CHECK_FALSE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(-1, opts->server[0].fd);
+ CHECK_EQUAL(0, opts->curr_server);
+ CHECK_EQUAL(0, opts->next_server);
+
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(2, opts->server[0].fd);
+ CHECK_EQUAL(0, opts->curr_server);
+ CHECK_EQUAL(INVALID_SERVER_ID, opts->next_server);
+
+ struct tac_connect_call exp_calls[] = {
+ {
+ .server_addr = *(opts->server[0].addrs->ai_addr),
+ .source_addr = *(opts->server[0].src_addrs->ai_addr),
+ .key = "0",
+ .timeout = 5,
+ },
+ {
+ .server_addr = *(opts->server[0].addrs->ai_addr),
+ .source_addr = *(opts->server[0].src_addrs->ai_addr),
+ .key = "0",
+ .timeout = 5,
+ },
+ {
+ .server_addr = *(opts->server[0].addrs->ai_addr),
+ .source_addr = *(opts->server[0].src_addrs->ai_addr),
+ .key = "0",
+ .timeout = 5,
+ },
+ };
+
+ struct tac_connect_call *calls;
+ int calls_len = ut_get_tac_connect_calls(&calls);
+ LONGS_EQUAL(ARRAY_SIZE(exp_calls), calls_len);
+
+ for (int i = 0; i < calls_len; i++)
+ CHECK_TRUE(ut_tac_connect_call_eq(&exp_calls[i], &calls[i]));
+}
+
+TEST(ServerConnection, connectSingleServerHoldDown)
+{
+ opts->n_servers = 1;
+ SET_OPTS_SERVER(opts, 0, "127.0.0.1", "100");
+ opts->server[0].hold_down = 10;
+
+ int fds[] = { 1, -1, 2 };
+ ut_set_tac_connect_fds(fds, ARRAY_SIZE(fds));
+
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(1, opts->server[0].fd);
+ CHECK_EQUAL(0, opts->curr_server);
+ CHECK_EQUAL(INVALID_SERVER_ID, opts->next_server);
+
+ /* Check connect calls without a secret */
+ free((void *) opts->server[0].secret);
+ opts->server[0].secret = NULL;
+
+ CHECK_FALSE(tacplus_connect());
+ CHECK_FALSE(tacplusd_online());
+ CHECK_EQUAL(-1, opts->server[0].fd);
+ CHECK_EQUAL(INVALID_SERVER_ID, opts->curr_server);
+ CHECK_EQUAL(0, opts->next_server);
+
+ /* Server is held down so there should be no attempt to connect */
+ CHECK_FALSE(tacplus_connect());
+ CHECK_FALSE(tacplusd_online());
+ CHECK_EQUAL(-1, opts->server[0].fd);
+ CHECK_EQUAL(INVALID_SERVER_ID, opts->curr_server);
+ CHECK_EQUAL(0, opts->next_server);
+
+ /* Expire hold down timer */
+ ut_inc_cur_mono_time(20, 0);
+
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(2, opts->server[0].fd);
+ CHECK_EQUAL(0, opts->curr_server);
+ CHECK_EQUAL(INVALID_SERVER_ID, opts->next_server);
+
+ struct tac_connect_call exp_calls[] = {
+ {
+ .server_addr = *(opts->server[0].addrs->ai_addr),
+ .source_addr = {0},
+ .key = "0",
+ .timeout = 5,
+ },
+ {
+ .server_addr = *(opts->server[0].addrs->ai_addr),
+ .source_addr = {0},
+ .key = NULL,
+ .timeout = 5,
+ },
+ {
+ .server_addr = *(opts->server[0].addrs->ai_addr),
+ .source_addr = {0},
+ .key = NULL,
+ .timeout = 5,
+ },
+ };
+
+ struct tac_connect_call *calls;
+ int calls_len = ut_get_tac_connect_calls(&calls);
+ LONGS_EQUAL(ARRAY_SIZE(exp_calls), calls_len);
+
+ for (int i = 0; i < calls_len; i++)
+ CHECK_TRUE(ut_tac_connect_call_eq(&exp_calls[i], &calls[i]));
+}
+
+TEST(ServerConnection, connectMultiServerNoHoldDown)
+{
+ opts->n_servers = 2;
+ SET_OPTS_SERVER(opts, 0, "127.0.0.1", "100");
+ SET_OPTS_SERVER(opts, 1, "127.0.0.2", "100");
+ SET_OPTS_SERVER_SRC_ADDR(opts, 0, "10.10.20.20");
+ SET_OPTS_SERVER_SRC_ADDR(opts, 1, "20.20.10.10");
+
+ int fds[] = { 1, -1, 2, -1, -1, 3 };
+ ut_set_tac_connect_fds(fds, ARRAY_SIZE(fds));
+
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(1, opts->server[0].fd);
+ CHECK_EQUAL(0, opts->curr_server);
+ CHECK_EQUAL(INVALID_SERVER_ID, opts->next_server);
+
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(-1, opts->server[0].fd);
+ CHECK_EQUAL(2, opts->server[1].fd)
+ CHECK_EQUAL(1, opts->curr_server);
+ CHECK_EQUAL(0, opts->next_server);
+
+ /* Both servers fail to connect */
+ CHECK_FALSE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(-1, opts->server[0].fd);
+ CHECK_EQUAL(-1, opts->server[1].fd);
+ CHECK_EQUAL(0, opts->curr_server);
+ CHECK_EQUAL(0, opts->next_server);
+
+ /* Back to server 0 */
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(3, opts->server[0].fd);
+ CHECK_EQUAL(0, opts->curr_server);
+ CHECK_EQUAL(INVALID_SERVER_ID, opts->next_server);
+
+ struct tac_connect_call exp_calls[] = {
+ {
+ .server_addr = *(opts->server[0].addrs->ai_addr),
+ .source_addr = *(opts->server[0].src_addrs->ai_addr),
+ .key = "0",
+ .timeout = 5,
+ },
+ {
+ .server_addr = *(opts->server[0].addrs->ai_addr),
+ .source_addr = *(opts->server[0].src_addrs->ai_addr),
+ .key = "0",
+ .timeout = 5,
+ },
+ {
+ .server_addr = *(opts->server[1].addrs->ai_addr),
+ .source_addr = *(opts->server[1].src_addrs->ai_addr),
+ .key = "1",
+ .timeout = 10,
+ },
+ {
+ .server_addr = *(opts->server[0].addrs->ai_addr),
+ .source_addr = *(opts->server[0].src_addrs->ai_addr),
+ .key = "0",
+ .timeout = 5,
+ },
+ {
+ .server_addr = *(opts->server[1].addrs->ai_addr),
+ .source_addr = *(opts->server[1].src_addrs->ai_addr),
+ .key = "1",
+ .timeout = 10,
+ },
+ {
+ .server_addr = *(opts->server[0].addrs->ai_addr),
+ .source_addr = *(opts->server[0].src_addrs->ai_addr),
+ .key = "0",
+ .timeout = 5,
+ },
+ };
+
+ struct tac_connect_call *calls;
+ int calls_len = ut_get_tac_connect_calls(&calls);
+ LONGS_EQUAL(ARRAY_SIZE(exp_calls), calls_len);
+
+ for (int i = 0; i < calls_len; i++)
+ CHECK_TRUE(ut_tac_connect_call_eq(&exp_calls[i], &calls[i]));
+}
+
+TEST(ServerConnection, connectMultiServerHoldDown)
+{
+ SET_OPTS_SERVER(opts, 0, "127.0.0.1", "100");
+ SET_OPTS_SERVER(opts, 1, "127.0.0.2", "100");
+ SET_OPTS_SERVER(opts, 2, "127.0.0.3", "100");
+ opts->server[0].hold_down = 10;
+ opts->server[1].hold_down = 10;
+ opts->server[2].hold_down = 10;
+ SET_OPTS_SERVER_SRC_ADDR(opts, 0, "10.10.10.10");
+ SET_OPTS_SERVER_SRC_ADDR(opts, 2, "11.11.11.11");
+
+ int fds[] = { 1, -1, 2, -1, 3, -1, 4, 5 };
+ ut_set_tac_connect_fds(fds, ARRAY_SIZE(fds));
+
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(1, opts->server[0].fd);
+ CHECK_EQUAL(0, opts->curr_server);
+ CHECK_EQUAL(INVALID_SERVER_ID, opts->next_server);
+
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(-1, opts->server[0].fd);
+ CHECK_EQUAL(2, opts->server[1].fd)
+ CHECK_EQUAL(1, opts->curr_server);
+ CHECK_EQUAL(0, opts->next_server);
+
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(-1, opts->server[0].fd);
+ CHECK_EQUAL(-1, opts->server[1].fd);
+ CHECK_EQUAL(3, opts->server[2].fd);
+ CHECK_EQUAL(2, opts->curr_server);
+ CHECK_EQUAL(0, opts->next_server);
+
+ CHECK_FALSE(tacplus_connect());
+ CHECK_FALSE(tacplusd_online());
+ CHECK_EQUAL(-1, opts->server[0].fd);
+ CHECK_EQUAL(-1, opts->server[1].fd);
+ CHECK_EQUAL(-1, opts->server[2].fd);
+ CHECK_EQUAL(INVALID_SERVER_ID, opts->curr_server);
+ CHECK_EQUAL(0, opts->next_server);
+
+ /* All servers are held down so there should be no attempt to connect to any */
+ CHECK_FALSE(tacplus_connect());
+ CHECK_FALSE(tacplusd_online());
+ CHECK_EQUAL(-1, opts->server[0].fd);
+ CHECK_EQUAL(-1, opts->server[1].fd);
+ CHECK_EQUAL(-1, opts->server[2].fd);
+ CHECK_EQUAL(INVALID_SERVER_ID, opts->curr_server);
+ CHECK_EQUAL(0, opts->next_server);
+
+ /* Expire hold down timers */
+ ut_inc_cur_mono_time(20, 0);
+
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(4, opts->server[0].fd);
+ CHECK_EQUAL(0, opts->curr_server);
+ CHECK_EQUAL(INVALID_SERVER_ID, opts->next_server);
+
+ /* Should stick with server 0 on a subsequent connect */
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(5, opts->server[0].fd);
+ CHECK_EQUAL(0, opts->curr_server);
+ CHECK_EQUAL(INVALID_SERVER_ID, opts->next_server);
+
+ struct tac_connect_call exp_calls[] = {
+ {
+ .server_addr = *(opts->server[0].addrs->ai_addr),
+ .source_addr = *(opts->server[0].src_addrs->ai_addr),
+ .key = "0",
+ .timeout = 5,
+ },
+ {
+ .server_addr = *(opts->server[0].addrs->ai_addr),
+ .source_addr = *(opts->server[0].src_addrs->ai_addr),
+ .key = "0",
+ .timeout = 5,
+ },
+ {
+ .server_addr = *(opts->server[1].addrs->ai_addr),
+ .source_addr = {0},
+ .key = "1",
+ .timeout = 10,
+ },
+ {
+ .server_addr = *(opts->server[1].addrs->ai_addr),
+ .source_addr = {0},
+ .key = "1",
+ .timeout = 10,
+ },
+ {
+ .server_addr = *(opts->server[2].addrs->ai_addr),
+ .source_addr = *(opts->server[2].src_addrs->ai_addr),
+ .key = "2",
+ .timeout = 15,
+ },
+ {
+ .server_addr = *(opts->server[2].addrs->ai_addr),
+ .source_addr = *(opts->server[2].src_addrs->ai_addr),
+ .key = "2",
+ .timeout = 15,
+ },
+ {
+ .server_addr = *(opts->server[0].addrs->ai_addr),
+ .source_addr = *(opts->server[0].src_addrs->ai_addr),
+ .key = "0",
+ .timeout = 5,
+ },
+ {
+ .server_addr = *(opts->server[0].addrs->ai_addr),
+ .source_addr = *(opts->server[0].src_addrs->ai_addr),
+ .key = "0",
+ .timeout = 5,
+ },
+ };
+
+ struct tac_connect_call *calls;
+ int calls_len = ut_get_tac_connect_calls(&calls);
+ LONGS_EQUAL(ARRAY_SIZE(exp_calls), calls_len);
+
+ for (int i = 0; i < calls_len; i++)
+ CHECK_TRUE(ut_tac_connect_call_eq(&exp_calls[i], &calls[i]));
+}
+
+TEST(ServerConnection, connectMultiServerDiffHoldDown)
+{
+ SET_OPTS_SERVER(opts, 0, "127.0.0.1", "100");
+ SET_OPTS_SERVER(opts, 1, "127.0.0.2", "100");
+ SET_OPTS_SERVER(opts, 2, "127.0.0.3", "100");
+ opts->server[0].hold_down = 10;
+ opts->server[1].hold_down = 5;
+ opts->server[2].hold_down = 0;
+
+ int fds[] = { 1, -1, 2, -1, 3, -1, 4, 5, 6, 7 };
+ ut_set_tac_connect_fds(fds, ARRAY_SIZE(fds));
+
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(1, opts->server[0].fd);
+ CHECK_EQUAL(0, opts->curr_server);
+ CHECK_EQUAL(INVALID_SERVER_ID, opts->next_server);
+
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(-1, opts->server[0].fd);
+ CHECK_EQUAL(2, opts->server[1].fd)
+ CHECK_EQUAL(1, opts->curr_server);
+ CHECK_EQUAL(0, opts->next_server);
+
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(-1, opts->server[0].fd);
+ CHECK_EQUAL(-1, opts->server[1].fd);
+ CHECK_EQUAL(3, opts->server[2].fd);
+ CHECK_EQUAL(2, opts->curr_server);
+ CHECK_EQUAL(1, opts->next_server);
+
+ CHECK_FALSE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(-1, opts->server[0].fd);
+ CHECK_EQUAL(-1, opts->server[1].fd);
+ CHECK_EQUAL(-1, opts->server[2].fd);
+ CHECK_EQUAL(2, opts->curr_server);
+ CHECK_EQUAL(2, opts->next_server);
+
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(4, opts->server[2].fd);
+ CHECK_EQUAL(2, opts->curr_server);
+ CHECK_EQUAL(1, opts->next_server);
+
+ /* Expire hold down timer for server 1 */
+ ut_inc_cur_mono_time(6, 0);
+
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(5, opts->server[1].fd);
+ CHECK_EQUAL(1, opts->curr_server);
+ CHECK_EQUAL(0, opts->next_server);
+
+ /* Expire hold down timer for server 0 */
+ ut_inc_cur_mono_time(20, 0);
+
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(6, opts->server[0].fd);
+ CHECK_EQUAL(0, opts->curr_server);
+ CHECK_EQUAL(INVALID_SERVER_ID, opts->next_server);
+
+ /* Should stick with server 0 on a subsequent connect */
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(7, opts->server[0].fd);
+ CHECK_EQUAL(0, opts->curr_server);
+ CHECK_EQUAL(INVALID_SERVER_ID, opts->next_server);
+
+ struct tac_connect_call exp_calls[] = {
+ {
+ .server_addr = *(opts->server[0].addrs->ai_addr),
+ .source_addr = {0},
+ .key = "0",
+ .timeout = 5,
+ },
+ {
+ .server_addr = *(opts->server[0].addrs->ai_addr),
+ .source_addr = {0},
+ .key = "0",
+ .timeout = 5,
+ },
+ {
+ .server_addr = *(opts->server[1].addrs->ai_addr),
+ .source_addr = {0},
+ .key = "1",
+ .timeout = 10,
+ },
+ {
+ .server_addr = *(opts->server[1].addrs->ai_addr),
+ .source_addr = {0},
+ .key = "1",
+ .timeout = 10,
+ },
+ {
+ .server_addr = *(opts->server[2].addrs->ai_addr),
+ .source_addr = {0},
+ .key = "2",
+ .timeout = 15,
+ },
+ {
+ .server_addr = *(opts->server[2].addrs->ai_addr),
+ .source_addr = {0},
+ .key = "2",
+ .timeout = 15,
+ },
+ {
+ .server_addr = *(opts->server[2].addrs->ai_addr),
+ .source_addr = {0},
+ .key = "2",
+ .timeout = 15,
+ },
+ {
+ .server_addr = *(opts->server[1].addrs->ai_addr),
+ .source_addr = {0},
+ .key = "1",
+ .timeout = 10,
+ },
+ {
+ .server_addr = *(opts->server[0].addrs->ai_addr),
+ .source_addr = {0},
+ .key = "0",
+ .timeout = 5,
+ },
+ {
+ .server_addr = *(opts->server[0].addrs->ai_addr),
+ .source_addr = {0},
+ .key = "0",
+ .timeout = 5,
+ },
+ };
+
+ struct tac_connect_call *calls;
+ int calls_len = ut_get_tac_connect_calls(&calls);
+ LONGS_EQUAL(ARRAY_SIZE(exp_calls), calls_len);
+
+ for (int i = 0; i < calls_len; i++)
+ CHECK_TRUE(ut_tac_connect_call_eq(&exp_calls[i], &calls[i]));
+}
+#endif
+
+/*
+ * Hold down timer tests
+ */
+
+TEST(ServerConnection, activateHoldDown)
+{
+ struct tacplus_options::tacplus_options_server *serv = &opts->server[0];
+
+ CHECK_TIMESPEC_VALS(serv->state.lastTrouble, -1, -1);
+ serv->hold_down = 10;
+
+ ut_set_cur_mono_time(1245, 19828);
+ tacplus_server_activate_hold_down((struct tacplus_options_server *)serv);
+ CHECK_TIMESPEC_VALS(serv->state.lastTrouble, 1245, 19828);
+ CHECK(tacplus_server_is_held_down((const struct tacplus_options_server *)serv));
+
+ ut_set_cur_mono_time(97876757, 87687);
+ tacplus_server_activate_hold_down((struct tacplus_options_server *)serv);
+ CHECK_TIMESPEC_VALS(serv->state.lastTrouble, 97876757, 87687);
+ CHECK(tacplus_server_is_held_down((const struct tacplus_options_server *)serv));
+}
+
+TEST(ServerConnection, resetHoldDown)
+{
+ struct tacplus_options::tacplus_options_server *serv = &opts->server[0];
+
+ ut_set_cur_mono_time(110, 0);
+ serv->hold_down = 10;
+
+ SET_TIMESPEC_VALS(serv->state.lastTrouble, 105, 20221);
+ CHECK_TIMESPEC_VALS(serv->state.lastTrouble, 105, 20221);
+
+ CHECK(tacplus_server_is_held_down((const struct tacplus_options_server *)serv));
+
+ tacplus_server_reset_hold_down((struct tacplus_options_server *)serv);
+ CHECK_TIMESPEC_VALS(serv->state.lastTrouble, -1, -1);
+
+ CHECK(! tacplus_server_is_held_down((const struct tacplus_options_server *)serv));
+}
+
+TEST(ServerConnection, remainingHoldDown)
+{
+ struct tacplus_options::tacplus_options_server serv = {};
+ struct timespec remaining;
+ bool held_down;
+
+ ut_set_cur_mono_time(20, 500);
+
+ /* No hold down configured, and no connection issues detected */
+ UNSIGNED_LONGS_EQUAL(0, serv.hold_down);
+ CHECK_TIMESPEC_VALS(serv.state.lastTrouble, 0, 0);
+
+ held_down = tacplus_server_remaining_hold_down((const struct tacplus_options_server *)
+ &serv, &remaining);
+ CHECK_FALSE(held_down);
+ CHECK_TIMESPEC_VALS(remaining, 0, 0);
+
+ /* Configure a hold down, still no connection issues detected */
+ serv.hold_down = 10;
+ held_down = tacplus_server_remaining_hold_down((const struct tacplus_options_server *)
+ &serv, &remaining);
+ CHECK_FALSE(held_down);
+ CHECK_TIMESPEC_VALS(remaining, 0, 0);
+
+ /* Experience some connection trouble */
+ SET_TIMESPEC_VALS(serv.state.lastTrouble, 18, 99769);
+
+ held_down = tacplus_server_remaining_hold_down((const struct tacplus_options_server *)
+ &serv, &remaining);
+ CHECK(held_down);
+ CHECK_TIMESPEC_VALS(remaining, 8, 99269);
+
+ /* Some time passes */
+ ut_set_cur_mono_time(24, 921978);
+ held_down = tacplus_server_remaining_hold_down((const struct tacplus_options_server *)
+ &serv, &remaining);
+ CHECK(held_down);
+ CHECK_TIMESPEC_VALS(remaining, 3, 999177791);
+
+ /* Not quite enough time passes */
+ ut_set_cur_mono_time(28, 99768);
+ held_down = tacplus_server_remaining_hold_down((const struct tacplus_options_server *)
+ &serv, &remaining);
+ CHECK(held_down);
+ CHECK_TIMESPEC_VALS(remaining, 0, 1);
+
+ /* Exactly enough time passes */
+ ut_set_cur_mono_time(28, 99769);
+ held_down = tacplus_server_remaining_hold_down((const struct tacplus_options_server *)
+ &serv, &remaining);
+ CHECK_FALSE(held_down);
+ CHECK_TIMESPEC_VALS(remaining, 0, 0);
+
+ /* Some more time passes */
+ ut_set_cur_mono_time(30, 10029);
+ held_down = tacplus_server_remaining_hold_down((const struct tacplus_options_server *)
+ &serv, &remaining);
+ CHECK_FALSE(held_down);
+ CHECK_TIMESPEC_VALS(remaining, 0, 0);
+
+ /* Experience some more connection trouble */
+ SET_TIMESPEC_VALS(serv.state.lastTrouble, 30, 10029);
+ held_down = tacplus_server_remaining_hold_down((const struct tacplus_options_server *)
+ &serv, &remaining);
+ CHECK(held_down);
+ CHECK_TIMESPEC_VALS(remaining, 10, 0);
+
+ /* Some time passes */
+ ut_set_cur_mono_time(32, 100);
+ held_down = tacplus_server_remaining_hold_down((const struct tacplus_options_server *)
+ &serv, &remaining);
+ CHECK(held_down);
+ CHECK_TIMESPEC_VALS(remaining, 8, 9929);
+
+ /* Enough time passes to expire timer again */
+ ut_set_cur_mono_time(50, 89817);
+ held_down = tacplus_server_remaining_hold_down((const struct tacplus_options_server *)
+ &serv, &remaining);
+ CHECK_FALSE(held_down);
+ CHECK_TIMESPEC_VALS(remaining, 0, 0);
+}
+
+TEST(ServerConnection, remainingHoldDownConfChange)
+{
+ struct tacplus_options::tacplus_options_server serv = {};
+ struct timespec remaining;
+ bool held_down;
+
+ /* Hold down timer is active */
+ ut_set_cur_mono_time(20, 500);
+ serv.hold_down = 15;
+ SET_TIMESPEC_VALS(serv.state.lastTrouble, 14, 21984);
+
+ held_down = tacplus_server_remaining_hold_down((const struct tacplus_options_server *)
+ &serv, &remaining);
+ CHECK(held_down);
+ CHECK_TIMESPEC_VALS(remaining, 9, 21484);
+
+ /* Hold down timer increases */
+ serv.hold_down += 30;
+ ut_set_cur_mono_time(24, 98897);
+
+ held_down = tacplus_server_remaining_hold_down((const struct tacplus_options_server *)
+ &serv, &remaining);
+ CHECK(held_down);
+ CHECK_TIMESPEC_VALS(remaining, 34, 999923087);
+
+ /* Hold down timer decreases */
+ serv.hold_down -= 21;
+ ut_set_cur_mono_time(26, 71298);
+
+ held_down = tacplus_server_remaining_hold_down((const struct tacplus_options_server *)
+ &serv, &remaining);
+ CHECK(held_down);
+ CHECK_TIMESPEC_VALS(remaining, 11, 999950686);
+
+ /* Hold down timer disabled */
+ serv.hold_down = 0;
+
+ held_down = tacplus_server_remaining_hold_down((const struct tacplus_options_server *)
+ &serv, &remaining);
+ CHECK_FALSE(held_down);
+ CHECK_TIMESPEC_VALS(remaining, 0, 0);
+}
+
+TEST(ServerConnection, isHeldDown)
+{
+ struct tacplus_options::tacplus_options_server serv = {};
+ bool held_down;
+
+ held_down = tacplus_server_is_held_down((const struct tacplus_options_server *) &serv);
+ CHECK_FALSE(held_down);
+
+ /* Hold down timer is active */
+ ut_set_cur_mono_time(30, 2480);
+ serv.hold_down = 25;
+ SET_TIMESPEC_VALS(serv.state.lastTrouble, 24, 21984);
+
+ held_down = tacplus_server_is_held_down((const struct tacplus_options_server *) &serv);
+ CHECK(held_down);
+
+ /* Hold down timer increases */
+ serv.hold_down += 30;
+ ut_set_cur_mono_time(34, 98897);
+
+ held_down = tacplus_server_is_held_down((const struct tacplus_options_server *) &serv);
+ CHECK(held_down);
+
+ /* Hold down timer decreases */
+ serv.hold_down -= 21;
+ ut_set_cur_mono_time(36, 71298);
+
+ held_down = tacplus_server_is_held_down((const struct tacplus_options_server *) &serv);
+ CHECK(held_down);
+
+ /* Hold down timer disabled */
+ serv.hold_down = 0;
+
+ held_down = tacplus_server_is_held_down((const struct tacplus_options_server *) &serv);
+ CHECK_FALSE(held_down);
+}
+
+TEST(ServerConnection, remainingHoldDownSecs)
+{
+ struct tacplus_options::tacplus_options_server serv = {};
+ time_t remaining;
+
+ /* Hold down timer is active */
+ ut_set_cur_mono_time(20, 500);
+ serv.hold_down = 15;
+ SET_TIMESPEC_VALS(serv.state.lastTrouble, 14, 21984);
+
+ remaining = tacplus_server_remaining_hold_down_secs(
+ (const struct tacplus_options_server *) &serv);
+ LONGS_EQUAL(9, remaining);
+
+ /* Hold down timer increases */
+ serv.hold_down += 30;
+ ut_set_cur_mono_time(24, 98897);
+
+ remaining = tacplus_server_remaining_hold_down_secs(
+ (const struct tacplus_options_server *) &serv);
+ LONGS_EQUAL(35, remaining);
+
+ /* Hold down timer decreases */
+ serv.hold_down -= 21;
+ ut_set_cur_mono_time(26, 71298);
+
+ remaining = tacplus_server_remaining_hold_down_secs(
+ (const struct tacplus_options_server *) &serv);
+ LONGS_EQUAL(12, remaining);
+
+ /* Hold down timer disabled */
+ serv.hold_down = 0;
+
+ remaining = tacplus_server_remaining_hold_down_secs(
+ (const struct tacplus_options_server *) &serv);
+ LONGS_EQUAL(0, remaining);
+}
+
+TEST(ServerConnection, copyServerState)
+{
+ struct tacplus_options *new_opts;
+
+ /* Set state on the current opts servers */
+ SET_TIMESPEC_VALS(opts->server[0].state.lastTrouble, 10, 5);
+ SET_TIMESPEC_VALS(opts->server[1].state.lastTrouble, -1, -1);
+ SET_TIMESPEC_VALS(opts->server[2].state.lastTrouble, 767, 9867);
+
+ /* Set some config - this should not be transferred */
+ opts->server[0].hold_down = 10;
+ opts->server[1].hold_down = 15;
+ opts->server[2].hold_down = 20;
+
+ /* Exactly the same servers remain in the new opts */
+ new_opts = tacplus_options_alloc(num_servers);
+ CHECK(new_opts);
+
+ SET_OPTS_SERVER(new_opts, 0, "1.1.1.1", "1");
+ SET_OPTS_SERVER(new_opts, 1, "2.2.2.2", "2");
+ SET_OPTS_SERVER(new_opts, 2, "3:3::3:3", "3");
+
+ CHECK_TIMESPEC_VALS(new_opts->server[0].state.lastTrouble, -1, -1);
+ CHECK_TIMESPEC_VALS(new_opts->server[1].state.lastTrouble, -1, -1);
+ CHECK_TIMESPEC_VALS(new_opts->server[2].state.lastTrouble, -1, -1);
+
+ tacplus_copy_server_state(opts, new_opts);
+
+ CHECK_TIMESPEC_VALS(new_opts->server[0].state.lastTrouble, 10, 5);
+ CHECK_TIMESPEC_VALS(new_opts->server[1].state.lastTrouble, -1, -1);
+ CHECK_TIMESPEC_VALS(new_opts->server[2].state.lastTrouble, 767, 9867);
+ CHECK_TIMESPEC_VALS(opts->server[0].state.lastTrouble, 10, 5);
+ CHECK_TIMESPEC_VALS(opts->server[1].state.lastTrouble, -1, -1);
+ CHECK_TIMESPEC_VALS(opts->server[2].state.lastTrouble, 767, 9867);
+
+ CHECK_EQUAL(new_opts->server[0].hold_down, 0);
+ CHECK_EQUAL(new_opts->server[1].hold_down, 0);
+ CHECK_EQUAL(new_opts->server[2].hold_down, 0);
+}
+
+TEST(ServerConnection, copyServerStateSomeMatch)
+{
+ struct tacplus_options *new_opts;
+
+ /* Set state on the current opts servers */
+ SET_TIMESPEC_VALS(opts->server[0].state.lastTrouble, 10, 5);
+ SET_TIMESPEC_VALS(opts->server[1].state.lastTrouble, -1, -1);
+ SET_TIMESPEC_VALS(opts->server[2].state.lastTrouble, 767, 9867);
+
+ /* Set some config - this should not be transferred */
+ opts->server[0].hold_down = 10;
+ opts->server[1].hold_down = 15;
+ opts->server[2].hold_down = 20;
+
+ /* Only server ID 0 remains the same in the new opts */
+ new_opts = tacplus_options_alloc(2);
+ CHECK(new_opts);
+
+ SET_OPTS_SERVER(new_opts, 0, "1.1.1.1", "1");
+ SET_OPTS_SERVER(new_opts, 1, "3.3.3.3", "3");
+
+ CHECK_TIMESPEC_VALS(new_opts->server[0].state.lastTrouble, -1, -1);
+ CHECK_TIMESPEC_VALS(new_opts->server[1].state.lastTrouble, -1, -1);
+
+ tacplus_copy_server_state(opts, new_opts);
+
+ CHECK_TIMESPEC_VALS(new_opts->server[0].state.lastTrouble, 10, 5);
+ CHECK_TIMESPEC_VALS(new_opts->server[1].state.lastTrouble, -1, -1);
+ CHECK_TIMESPEC_VALS(opts->server[0].state.lastTrouble, 10, 5);
+ CHECK_TIMESPEC_VALS(opts->server[1].state.lastTrouble, -1, -1);
+ CHECK_TIMESPEC_VALS(opts->server[2].state.lastTrouble, 767, 9867);
+
+ CHECK_EQUAL(new_opts->server[0].hold_down, 0);
+ CHECK_EQUAL(new_opts->server[1].hold_down, 0);
+}
+
+TEST(ServerConnection, copyServerStateAdded)
+{
+ struct tacplus_options *new_opts;
+
+ /* Set state on the current opts servers */
+ SET_TIMESPEC_VALS(opts->server[0].state.lastTrouble, 10, 5);
+ SET_TIMESPEC_VALS(opts->server[1].state.lastTrouble, 0, 2);
+ SET_TIMESPEC_VALS(opts->server[2].state.lastTrouble, 767, 9867);
+
+ /* Set some config - this should not be transferred */
+ opts->server[0].hold_down = 0;
+ opts->server[1].hold_down = 15;
+ opts->server[2].hold_down = 20;
+
+ /* Add an additional server to new opts */
+ new_opts = tacplus_options_alloc(4);
+ CHECK(new_opts);
+
+ SET_OPTS_SERVER(new_opts, 0, "1.1.1.1", "1");
+ SET_OPTS_SERVER(new_opts, 1, "2.2.2.2", "200"); // Port does not affect match
+ SET_OPTS_SERVER(new_opts, 2, "3:3::3:3", "33");
+ SET_OPTS_SERVER(new_opts, 3, "4.4.4.4", "4");
+
+ CHECK_TIMESPEC_VALS(new_opts->server[0].state.lastTrouble, -1, -1);
+ CHECK_TIMESPEC_VALS(new_opts->server[1].state.lastTrouble, -1, -1);
+ CHECK_TIMESPEC_VALS(new_opts->server[2].state.lastTrouble, -1, -1);
+ CHECK_TIMESPEC_VALS(new_opts->server[3].state.lastTrouble, -1, -1);
+
+ tacplus_copy_server_state(opts, new_opts);
+
+ /* Hold down not was not previously configured - so last trouble should not be transferred */
+ CHECK_TIMESPEC_VALS(new_opts->server[0].state.lastTrouble, -1, -1);
+
+ CHECK_TIMESPEC_VALS(new_opts->server[1].state.lastTrouble, 0, 2);
+ CHECK_TIMESPEC_VALS(new_opts->server[2].state.lastTrouble, 767, 9867);
+ CHECK_TIMESPEC_VALS(new_opts->server[3].state.lastTrouble, -1, -1);
+ CHECK_TIMESPEC_VALS(opts->server[0].state.lastTrouble, 10, 5);
+ CHECK_TIMESPEC_VALS(opts->server[1].state.lastTrouble, 0, 2);
+ CHECK_TIMESPEC_VALS(opts->server[2].state.lastTrouble, 767, 9867);
+
+ CHECK_EQUAL(new_opts->server[0].hold_down, 0);
+ CHECK_EQUAL(new_opts->server[1].hold_down, 0);
+ CHECK_EQUAL(new_opts->server[2].hold_down, 0);
+ CHECK_EQUAL(new_opts->server[3].hold_down, 0);
+}
+
+TEST(ServerConnection, tacplusReloadOptionsNull)
+{
+ struct tacplus_options *empty = NULL;
+
+ POINTERS_EQUAL(NULL, tacplus_reload_options(&opts, NULL));
+ POINTERS_EQUAL(NULL, opts);
+
+ POINTERS_EQUAL(NULL, tacplus_reload_options(&empty, NULL));
+ POINTERS_EQUAL(NULL, empty);
+}
+
+TEST(ServerConnection, tacplusReloadOptionsHoldDownConfigWithTrouble)
+{
+ struct tacplus_options *opts_one, *opts_two, *opts_three;
+
+ opts_one = tacplus_options_alloc(1);
+ CHECK(opts_one);
+
+ SET_OPTS_SERVER(opts_one, 0, "1.1.1.1", "1");
+
+ /* Current server experienced trouble */
+ tacplus_server_activate_hold_down(
+ (struct tacplus_options_server *)&opts->server[0]);
+ LONGS_EQUAL(0, opts->server[0].hold_down);
+
+ /* Configure a hold down timer and reload */
+ opts_one->server[0].hold_down = 10;
+ ut_inc_cur_mono_time(3, 0);
+ POINTERS_EQUAL(opts_one, tacplus_reload_options(&opts, opts_one));
+ POINTERS_EQUAL(NULL, opts);
+
+ /* Hold down timer should not be activated */
+ CHECK(! tacplus_server_is_held_down(
+ (struct tacplus_options_server *)&opts_one->server[0]));
+
+ /* Unless we experience some more trouble on the server */
+ ut_inc_cur_mono_time(2, 0);
+ tacplus_server_activate_hold_down(
+ (struct tacplus_options_server *)&opts_one->server[0]);
+
+ CHECK(tacplus_server_is_held_down(
+ (struct tacplus_options_server *)&opts_one->server[0]));
+ LONGS_EQUAL(10, tacplus_server_remaining_hold_down_secs(
+ (struct tacplus_options_server *)&opts_one->server[0]));
+
+ /* Reconfigure with disabled hold down timers */
+ opts_two = tacplus_options_alloc(1);
+ CHECK(opts_two);
+
+ SET_OPTS_SERVER(opts_two, 0, "1.1.1.1", "1");
+
+ LONGS_EQUAL(0, opts_two->server[0].hold_down);
+
+ POINTERS_EQUAL(opts_two, tacplus_reload_options(&opts_one, opts_two));
+ POINTERS_EQUAL(NULL, opts_one);
+
+ /* Hold down timer should not be activated */
+ CHECK(! tacplus_server_is_held_down(
+ (struct tacplus_options_server *)&opts_two->server[0]));
+
+ /* Reconfigure with hold down timer once again */
+ opts_three = tacplus_options_alloc(1);
+
+ SET_OPTS_SERVER(opts_three, 0, "1.1.1.1", "1");
+ opts_three->server[0].hold_down = 3000;
+
+ POINTERS_EQUAL(opts_three, tacplus_reload_options(&opts_two, opts_three));
+ POINTERS_EQUAL(NULL, opts_two);
+
+ /* Hold down timer should not be activated */
+ CHECK(! tacplus_server_is_held_down(
+ (struct tacplus_options_server *)&opts_three->server[0]));
+
+ cleanup_tacplus_options(&opts_three);
+ POINTERS_EQUAL(NULL, opts_three);
+}
+
+TEST(ServerConnection, tacplusReloadOptionsHoldDownConfigWithoutTrouble)
+{
+ struct tacplus_options *new_opts;
+
+ new_opts = tacplus_options_alloc(2);
+ CHECK(new_opts);
+
+ SET_OPTS_SERVER(new_opts, 0, "1.1.1.1", "1");
+ SET_OPTS_SERVER(new_opts, 1, "3:3::3:3", "3");
+
+ LONGS_EQUAL(0, opts->server[0].hold_down);
+
+ /* Configure a hold down timer and reload */
+ new_opts->server[0].hold_down = 10;
+ ut_inc_cur_mono_time(3, 0);
+ POINTERS_EQUAL(new_opts, tacplus_reload_options(&opts, new_opts));
+
+ /* Hold down timer should not be activated */
+ CHECK(! tacplus_server_is_held_down(
+ (struct tacplus_options_server *)&new_opts->server[0]));
+
+ cleanup_tacplus_options(&new_opts);
+ POINTERS_EQUAL(NULL, new_opts);
+}
+
+TEST(ServerConnection, tacplusReloadOptionsHoldDownConfigDisable)
+{
+ struct tacplus_options *new_opts;
+
+ new_opts = tacplus_options_alloc(2);
+ CHECK(new_opts);
+
+ SET_OPTS_SERVER(new_opts, 0, "1.1.1.1", "1");
+ SET_OPTS_SERVER(new_opts, 1, "3:3::3:3", "3");
+
+ LONGS_EQUAL(0, new_opts->server[0].hold_down);
+ LONGS_EQUAL(0, new_opts->server[1].hold_down);
+
+ /* Add hold down to current config */
+ opts->server[0].hold_down = 15;
+ opts->server[1].hold_down = 20;
+
+ tacplus_server_activate_hold_down(
+ (struct tacplus_options_server *)&opts->server[0]);
+
+ CHECK(tacplus_server_is_held_down(
+ (struct tacplus_options_server *)&opts->server[0]));
+
+ ut_inc_cur_mono_time(10, 0);
+ POINTERS_EQUAL(new_opts, tacplus_reload_options(&opts, new_opts));
+
+ /* Hold down timers should not be activated */
+ CHECK(! tacplus_server_is_held_down(
+ (struct tacplus_options_server *)&new_opts->server[0]));
+ CHECK(! tacplus_server_is_held_down(
+ (struct tacplus_options_server *)&new_opts->server[1]));
+
+ cleanup_tacplus_options(&new_opts);
+ POINTERS_EQUAL(NULL, new_opts);
+}
+
+TEST(ServerConnection, tacplusReloadOptionsHoldDownConfigIncrement)
+{
+ struct tacplus_options *new_opts;
+
+ new_opts = tacplus_options_alloc(2);
+ CHECK(new_opts);
+
+ SET_OPTS_SERVER(new_opts, 0, "1.1.1.1", "1");
+ SET_OPTS_SERVER(new_opts, 1, "3:3::3:3", "3");
+
+ /* Add hold down to current config */
+ opts->server[0].hold_down = 15;
+ opts->server[1].hold_down = 20;
+
+ /* Increase hold down of new config */
+ new_opts->server[0].hold_down = 20;
+ new_opts->server[1].hold_down = 25;
+
+ tacplus_server_activate_hold_down(
+ (struct tacplus_options_server *)&opts->server[0]);
+
+ LONGS_EQUAL(15, tacplus_server_remaining_hold_down_secs(
+ (struct tacplus_options_server *)&opts->server[0]));
+
+ ut_inc_cur_mono_time(10, 0);
+ POINTERS_EQUAL(new_opts, tacplus_reload_options(&opts, new_opts));
+
+ LONGS_EQUAL(10, tacplus_server_remaining_hold_down_secs(
+ (struct tacplus_options_server *)&new_opts->server[0]));
+
+ cleanup_tacplus_options(&new_opts);
+ POINTERS_EQUAL(NULL, new_opts);
+}
+
+TEST(ServerConnection, tacplusReloadOptionsHoldDownConfigDecrement)
+{
+ struct tacplus_options *new_opts;
+
+ new_opts = tacplus_options_alloc(2);
+ CHECK(new_opts);
+
+ SET_OPTS_SERVER(new_opts, 0, "1.1.1.1", "1");
+ SET_OPTS_SERVER(new_opts, 1, "3:3::3:3", "3");
+
+ /* Add hold down to current config */
+ opts->server[0].hold_down = 15;
+ opts->server[1].hold_down = 20;
+
+ /* Decrease hold down of new config */
+ new_opts->server[0].hold_down = 10;
+ new_opts->server[1].hold_down = 15;
+
+ tacplus_server_activate_hold_down(
+ (struct tacplus_options_server *)&opts->server[0]);
+
+ LONGS_EQUAL(15, tacplus_server_remaining_hold_down_secs(
+ (struct tacplus_options_server *)&opts->server[0]));
+
+ ut_inc_cur_mono_time(5, 0);
+ POINTERS_EQUAL(new_opts, tacplus_reload_options(&opts, new_opts));
+
+ LONGS_EQUAL(5, tacplus_server_remaining_hold_down_secs(
+ (struct tacplus_options_server *)&new_opts->server[0]));
+
+ cleanup_tacplus_options(&new_opts);
+ POINTERS_EQUAL(NULL, new_opts);
+}
+
+TEST(ServerConnection, tacplusReloadOptionsHoldDownConfigDecrementBelowRemaining)
+{
+ struct tacplus_options *new_opts;
+
+ new_opts = tacplus_options_alloc(2);
+ CHECK(new_opts);
+
+ SET_OPTS_SERVER(new_opts, 0, "1.1.1.1", "1");
+ SET_OPTS_SERVER(new_opts, 1, "3:3::3:3", "3");
+
+ /* Add hold down to current config */
+ opts->server[0].hold_down = 15;
+ opts->server[1].hold_down = 20;
+
+ /* Decrease hold down of new config */
+ new_opts->server[0].hold_down = 10;
+ new_opts->server[1].hold_down = 15;
+
+ tacplus_server_activate_hold_down(
+ (struct tacplus_options_server *)&opts->server[0]);
+
+ CHECK(tacplus_server_is_held_down(
+ (struct tacplus_options_server *)&opts->server[0]));
+ CHECK(! tacplus_server_is_held_down(
+ (struct tacplus_options_server *)&opts->server[1]));
+
+ ut_inc_cur_mono_time(12, 0);
+ POINTERS_EQUAL(new_opts, tacplus_reload_options(&opts, new_opts));
+
+ CHECK(! tacplus_server_is_held_down(
+ (struct tacplus_options_server *)&new_opts->server[0]));
+ CHECK(! tacplus_server_is_held_down(
+ (struct tacplus_options_server *)&new_opts->server[1]));
+
+ cleanup_tacplus_options(&new_opts);
+ POINTERS_EQUAL(NULL, new_opts);
+}
+
+TEST(ServerConnection, tacplusReloadOptionsNoHoldDownConfigOnlineCheck)
+{
+ struct tacplus_options *new_opts = copy_tacplus_options(opts);
+
+ /* Verify no hold down on current and new config */
+ for (unsigned i = 0; i < opts->n_servers; i++) {
+ LONGS_EQUAL(0, opts->server[i].hold_down);
+ LONGS_EQUAL(0, new_opts->server[i].hold_down);
+ }
+
+ /* No hold down - we should be online */
+ CHECK_TRUE(tacplusd_online());
+
+ /* Pass some time and reload */
+ ut_inc_cur_mono_time(10, 0);
+ POINTERS_EQUAL(new_opts, tacplus_reload_options(&opts, new_opts));
+
+ /* Still No hold down - we should still be online */
+ CHECK_TRUE(tacplusd_online());
+
+ cleanup_tacplus_options(&new_opts);
+ POINTERS_EQUAL(NULL, new_opts);
+}
+
+TEST(ServerConnection, tacplusReloadOptionsHoldDownConfigOnlineCheck)
+{
+ struct tacplus_options *new_opts = copy_tacplus_options(opts);
+
+ /* Verify no hold down on current config */
+ for (unsigned i = 0; i < opts->n_servers; i++)
+ LONGS_EQUAL(0, opts->server[i].hold_down);
+
+ /* Add hold down to new config */
+ new_opts->server[0].hold_down = 10;
+ new_opts->server[1].hold_down = 15;
+ new_opts->server[2].hold_down = 20;
+
+ /* No hold down - we should be online */
+ CHECK_TRUE(tacplusd_online());
+
+ /* Pass some time and reload */
+ ut_inc_cur_mono_time(5, 0);
+ POINTERS_EQUAL(new_opts, tacplus_reload_options(&opts, new_opts));
+
+ /* Now have hold down but no trouble - we should still be online */
+ CHECK_TRUE(tacplusd_online());
+
+ cleanup_tacplus_options(&new_opts);
+ POINTERS_EQUAL(NULL, new_opts);
+}
+
+static void ut_do_failing_tacplus_connect(
+ struct tacplus_options *opts, bool enforce_go_offline)
+{
+ int *fds = (int *) calloc(opts->n_servers, sizeof(int));
+ CHECK(fds);
+
+ unsigned num_servers_with_hold_down = 0;
+ for (unsigned i = 0; i < opts->n_servers; i++) {
+ if (opts->server[i].hold_down > 0)
+ num_servers_with_hold_down++;
+ fds[i] = -1;
+ }
+ ut_set_tac_connect_fds(fds, opts->n_servers);
+
+ if (enforce_go_offline) {
+ LONGS_EQUAL_TEXT(opts->n_servers, num_servers_with_hold_down,
+ "Test requires a hold down on all servers");
+ }
+ else {
+ CHECK_TEXT(num_servers_with_hold_down < opts->n_servers,
+ "Test requires no hold down on at least one server");
+ }
+
+ CHECK_FALSE(tacplus_connect());
+ CHECK_TEXT(tacplusd_online() == !enforce_go_offline,
+ "Unexpected online state");
+
+ ut_reset_tac_connect_wrapper();
+ free(fds);
+}
+
+TEST(ServerConnection, tacplusReloadOptionsHoldDownConfigWithTroubleOnlineCheck)
+{
+ struct tacplus_options *new_opts = copy_tacplus_options(opts);
+
+ /* Verify no hold down on current config */
+ for (unsigned i = 0; i < opts->n_servers; i++)
+ LONGS_EQUAL(0, opts->server[i].hold_down);
+
+ /* Add hold down to new config */
+ new_opts->server[0].hold_down = 10;
+ new_opts->server[1].hold_down = 15;
+ new_opts->server[2].hold_down = 20;
+
+ /* Experience trouble on all servers */
+ ut_do_failing_tacplus_connect(opts, false);
+
+ /* No hold down - we should be online despite trouble */
+ CHECK_TRUE(tacplusd_online());
+
+ /* Pass some time and reload */
+ ut_inc_cur_mono_time(5, 0);
+ POINTERS_EQUAL(new_opts, tacplus_reload_options(&opts, new_opts));
+
+ /*
+ * We should still be online - previous trouble is ignored on a
+ * newly configured hold down timer.
+ */
+ CHECK_TRUE(tacplusd_online());
+
+ cleanup_tacplus_options(&new_opts);
+ POINTERS_EQUAL(NULL, new_opts);
+}
+
+TEST(ServerConnection, tacplusReloadOptionsHoldDownConfigDisableOnlineCheck)
+{
+ struct tacplus_options *new_opts = copy_tacplus_options(opts);
+
+ /* Add hold down to current config */
+ opts->server[0].hold_down = 10;
+ opts->server[1].hold_down = 15;
+ opts->server[2].hold_down = 20;
+
+ /* Verify no hold down on new config */
+ for (unsigned i = 0; i < new_opts->n_servers; i++)
+ LONGS_EQUAL(0, new_opts->server[i].hold_down);
+
+ /* Go offline */
+ ut_do_failing_tacplus_connect(opts, true);
+
+ /* Pass some time (but still within offline period) and reload */
+ ut_inc_cur_mono_time(5, 0);
+ POINTERS_EQUAL(new_opts, tacplus_reload_options(&opts, new_opts));
+
+ /* Hold down was removed - we should be online again */
+ CHECK_TRUE(tacplusd_online());
+
+ cleanup_tacplus_options(&new_opts);
+ POINTERS_EQUAL(NULL, new_opts);
+}
+
+TEST(ServerConnection, tacplusReloadOptionsHoldDownConfigDisableOneOnlineCheck)
+{
+ struct tacplus_options *new_opts;
+
+ /* Add hold down to current config */
+ opts->server[0].hold_down = 20;
+ opts->server[1].hold_down = 25;
+ opts->server[2].hold_down = 30;
+
+ new_opts = copy_tacplus_options(opts);
+
+ /* Remove hold down from second server in new config */
+ new_opts->server[1].hold_down = 0;
+ LONGS_EQUAL(20, new_opts->server[0].hold_down);
+ LONGS_EQUAL(30, new_opts->server[2].hold_down);
+
+ /* Go offline */
+ ut_do_failing_tacplus_connect(opts, true);
+
+ /* Pass some time (but still within offline period) and reload */
+ ut_inc_cur_mono_time(15, 0);
+ POINTERS_EQUAL(new_opts, tacplus_reload_options(&opts, new_opts));
+
+ /* We should be online again since server 2 is now available */
+ CHECK_TRUE(tacplusd_online());
+
+ cleanup_tacplus_options(&new_opts);
+ POINTERS_EQUAL(NULL, new_opts);
+}
+
+TEST(ServerConnection, tacplusReloadOptionsHoldDownConfigIncrementOnlineCheck)
+{
+ struct tacplus_options *new_opts = copy_tacplus_options(opts);
+
+ /* Add hold down to current config */
+ opts->server[0].hold_down = 10;
+ opts->server[1].hold_down = 15;
+ opts->server[2].hold_down = 20;
+
+ /* Increase hold down on new config */
+ new_opts->server[0].hold_down = 20;
+ new_opts->server[1].hold_down = 25;
+ new_opts->server[2].hold_down = 30;
+
+ /* Go offline */
+ ut_do_failing_tacplus_connect(opts, true);
+
+ /* Pass some time (but still within offline period) and reload */
+ ut_inc_cur_mono_time(5, 0);
+ POINTERS_EQUAL(new_opts, tacplus_reload_options(&opts, new_opts));
+
+ /* We should still be offline */
+ CHECK_FALSE(tacplusd_online());
+
+ /* Pass enough time that the original hold down would have expired */
+ ut_inc_cur_mono_time(6, 0);
+
+ /* We should still be offline */
+ CHECK_FALSE(tacplusd_online());
+
+ /* Pass enough time that the first server hold down expires */
+ ut_inc_cur_mono_time(10, 0);
+
+ /* We should be online again */
+ CHECK_TRUE(tacplusd_online());
+
+ cleanup_tacplus_options(&new_opts);
+ POINTERS_EQUAL(NULL, new_opts);
+}
+
+TEST(ServerConnection, tacplusReloadOptionsHoldDownConfigDecrementOnlineCheck)
+{
+ struct tacplus_options *new_opts = copy_tacplus_options(opts);
+
+ /* Add hold down to current config */
+ opts->server[0].hold_down = 20;
+ opts->server[1].hold_down = 25;
+ opts->server[2].hold_down = 30;
+
+ /* Decrease hold down on new config */
+ new_opts->server[0].hold_down = 10;
+ new_opts->server[1].hold_down = 15;
+ new_opts->server[2].hold_down = 20;
+
+ /* Go offline */
+ ut_do_failing_tacplus_connect(opts, true);
+
+ /* Pass some time (but still within offline period) and reload */
+ ut_inc_cur_mono_time(5, 0);
+ POINTERS_EQUAL(new_opts, tacplus_reload_options(&opts, new_opts));
+
+ /* We should still be offline */
+ CHECK_FALSE(tacplusd_online());
+
+ /* Pass enough time that the new hold down expires */
+ ut_inc_cur_mono_time(6, 0);
+
+ /* We should be online again */
+ CHECK_TRUE(tacplusd_online());
+
+ cleanup_tacplus_options(&new_opts);
+ POINTERS_EQUAL(NULL, new_opts);
+}
+
+TEST(ServerConnection, tacplusReloadOptionsHoldDownConfigDecrementBelowRemainingOnlineCheck)
+{
+ struct tacplus_options *new_opts = copy_tacplus_options(opts);
+
+ /* Add hold down to current config */
+ opts->server[0].hold_down = 20;
+ opts->server[1].hold_down = 25;
+ opts->server[2].hold_down = 30;
+
+ /* Decrease hold down on new config */
+ new_opts->server[0].hold_down = 10;
+ new_opts->server[1].hold_down = 15;
+ new_opts->server[2].hold_down = 20;
+
+ /* Go offline */
+ ut_do_failing_tacplus_connect(opts, true);
+
+ /* Pass enough time that the new hold down expires */
+ ut_inc_cur_mono_time(15, 0);
+ POINTERS_EQUAL(new_opts, tacplus_reload_options(&opts, new_opts));
+
+ /* We should be online again */
+ CHECK_TRUE(tacplusd_online());
+
+ cleanup_tacplus_options(&new_opts);
+ POINTERS_EQUAL(NULL, new_opts);
+}