diff options
Diffstat (limited to 'tacplus-daemon/test/serverConnectTester.cpp')
-rw-r--r-- | tacplus-daemon/test/serverConnectTester.cpp | 1527 |
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); +} |