diff --git a/.azure-pipelines/build-template.yml b/.azure-pipelines/build-template.yml index 33bd75cfd15..c859e4b6393 100644 --- a/.azure-pipelines/build-template.yml +++ b/.azure-pipelines/build-template.yml @@ -104,11 +104,12 @@ jobs: clean: true submodules: true - script: | + set -xe sudo apt-get update sudo apt-get install -y \ libhiredis-dev \ libzmq3-dev \ - swig4.0 \ + swig \ libdbus-1-dev \ libteam-dev sudo pip3 install lcov_cobertura diff --git a/teamsyncd/Makefile.am b/teamsyncd/Makefile.am index c72498d9e30..594ea1ba944 100644 --- a/teamsyncd/Makefile.am +++ b/teamsyncd/Makefile.am @@ -12,7 +12,7 @@ teamsyncd_SOURCES = teamsyncd.cpp teamsync.cpp teamsyncd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_ASAN) teamsyncd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_ASAN) -teamsyncd_LDADD = $(LDFLAGS_ASAN) -lnl-3 -lnl-route-3 -lhiredis -lswsscommon -lteam +teamsyncd_LDADD = $(LDFLAGS_ASAN) -lnl-3 -lnl-route-3 -lhiredis -lswsscommon -lteam -lteamdctl if GCOV_ENABLED teamsyncd_SOURCES += ../gcovpreload/gcovpreload.cpp diff --git a/teamsyncd/teamsync.cpp b/teamsyncd/teamsync.cpp index e8dcb1fc55a..93c48547ef7 100644 --- a/teamsyncd/teamsync.cpp +++ b/teamsyncd/teamsync.cpp @@ -12,6 +12,8 @@ #include "warm_restart.h" #include "teamsync.h" +#include +#include #include using namespace std; @@ -279,19 +281,51 @@ TeamSync::TeamPortSync::TeamPortSync(const string &lagName, int ifindex, "Unable to register port change event"); } + struct teamdctl *m_teamdctl = teamdctl_alloc(); + if (!m_teamdctl) + { + team_free(m_team); + m_team = NULL; + throw system_error(make_error_code(errc::address_not_available), + "Unable to allocate teamdctl socket"); + } + + err = teamdctl_connect(m_teamdctl, lagName.c_str(), nullptr, "usock"); + if (err) + { + team_free(m_team); + m_team = NULL; + teamdctl_free(m_teamdctl); + throw system_error(make_error_code(errc::connection_refused), + "Unable to connect to teamd"); + } + + char *response; + err = teamdctl_config_get_raw_direct(m_teamdctl, &response); + if (err) + { + team_free(m_team); + m_team = NULL; + teamdctl_disconnect(m_teamdctl); + teamdctl_free(m_teamdctl); + throw system_error(make_error_code(errc::io_error), + "Unable to get config from teamd (to prove that it is running and alive)"); + } + + teamdctl_disconnect(m_teamdctl); + teamdctl_free(m_teamdctl); + break; } catch (const system_error& e) { + SWSS_LOG_WARN("Failed to initialize team handler. LAG=%s error=%d:%s, attempt=%d", + lagName.c_str(), e.code().value(), e.what(), count); + if (++count == max_retries) { throw; } - else - { - SWSS_LOG_WARN("Failed to initialize team handler. LAG=%s error=%d:%s, attempt=%d", - lagName.c_str(), e.code().value(), e.what(), count); - } sleep(1); } diff --git a/tests/mock_tests/Makefile.am b/tests/mock_tests/Makefile.am index b5b5c353c29..18de34cd97f 100644 --- a/tests/mock_tests/Makefile.am +++ b/tests/mock_tests/Makefile.am @@ -9,9 +9,9 @@ CXXFLAGS = -g -O0 CFLAGS_SAI = -I /usr/include/sai -TESTS = tests tests_intfmgrd tests_teammgrd tests_portsyncd tests_fpmsyncd tests_response_publisher +TESTS = tests tests_intfmgrd tests_teammgrd tests_portsyncd tests_fpmsyncd tests_response_publisher tests_teamsyncd -noinst_PROGRAMS = tests tests_intfmgrd tests_teammgrd tests_portsyncd tests_fpmsyncd tests_response_publisher +noinst_PROGRAMS = tests tests_intfmgrd tests_teammgrd tests_portsyncd tests_fpmsyncd tests_response_publisher tests_teamsyncd LDADD_SAI = -lsaimeta -lsaimetadata -lsaivs -lsairedis @@ -312,3 +312,12 @@ tests_response_publisher_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CF tests_response_publisher_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_GTEST) $(CFLAGS_SAI) $(tests_response_publisher_INCLUDES) tests_response_publisher_LDADD = $(LDADD_GTEST) $(LDADD_SAI) -lnl-genl-3 -lhiredis -lhiredis \ -lswsscommon -lswsscommon -lgtest -lgtest_main -lzmq -lnl-3 -lnl-route-3 -lpthread + +tests_teamsyncd_SOURCES = teamsync_ut.cpp \ + $(top_srcdir)/teamsyncd/teamsync.cpp + +tests_teamsyncd_INCLUDES = $(tests_INCLUDES) -I$(top_srcdir)/teamsyncd +tests_teamsyncd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_GTEST) $(CFLAGS_SAI) +tests_teamsyncd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_GTEST) $(CFLAGS_SAI) $(tests_teamsyncd_INCLUDES) +tests_teamsyncd_LDADD = $(LDADD_GTEST) $(LDADD_SAI) -lnl-genl-3 \ + -lswsscommon -lgtest -lgtest_main -lpthread -lteam -lteamdctl diff --git a/tests/mock_tests/teamsync_ut.cpp b/tests/mock_tests/teamsync_ut.cpp new file mode 100644 index 00000000000..471f89f5e86 --- /dev/null +++ b/tests/mock_tests/teamsync_ut.cpp @@ -0,0 +1,185 @@ +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include +#include +#include +#include +#include "teamsync.h" + +static unsigned int (*callback_sleep)(unsigned int seconds) = NULL; +static int (*callback_team_init)(struct team_handle *th, uint32_t ifindex) = NULL; +static int (*callback_team_change_handler)(struct team_handle *th, struct team_change_handler *handler, void *priv) = NULL; +static int (*callback_teamdctl_connect)(struct teamdctl *tdc, const char *team_name, const char *addr, const char *cli_type) = NULL; +static int (*callback_teamdctl_config_get_raw_direct)(struct teamdctl *tdc, char **response) = NULL; +static void (*callback_teamdctl_disconnect)(struct teamdctl *tdc) = NULL; + +static unsigned int cb_sleep(unsigned int seconds) +{ + return 0; +} + +unsigned int sleep(unsigned int seconds) +{ + if (callback_sleep) + { + return callback_sleep(seconds); + } + unsigned int (*realfunc)(unsigned int) = + (unsigned int (*)(unsigned int))(dlsym (RTLD_NEXT, "sleep")); + return realfunc(seconds); +} + + +static int cb_team_init(struct team_handle *th, uint32_t ifindex) +{ + return 0; +} + +int team_init(struct team_handle *th, uint32_t ifindex) +{ + if (callback_team_init) + { + return callback_team_init(th, ifindex); + } + int (*realfunc)(struct team_handle *, uint32_t) = + (int (*)(struct team_handle *, uint32_t))(dlsym (RTLD_NEXT, "team_init")); + return realfunc(th, ifindex); +} + +static int cb_team_change_handler(struct team_handle *th, struct team_change_handler *handler, void *priv) +{ + return 0; +} + +int team_change_handler(struct team_handle *th, struct team_change_handler *handler, void *priv) +{ + if (callback_team_change_handler) + { + return callback_team_change_handler(th, handler, priv); + } + int (*realfunc)(struct team_handle *, struct team_change_handler*, void*) = + (int (*)(struct team_handle *, struct team_change_handler*, void*))(dlsym (RTLD_NEXT, "team_change_handler")); + return realfunc(th, handler, priv); +} + +static int cb_teamdctl_connect(struct teamdctl *tdc, const char *team_name, const char *addr, const char *cli_type) +{ + return 0; +} + +int teamdctl_connect(struct teamdctl *tdc, const char *team_name, const char *addr, const char *cli_type) +{ + if (callback_teamdctl_connect) + { + return callback_teamdctl_connect(tdc, team_name, addr, cli_type); + } + int (*realfunc)(struct teamdctl *, const char *, const char *, const char *) = + (int (*)(struct teamdctl *, const char *, const char *, const char *))(dlsym (RTLD_NEXT, "teamdctl_connect")); + return realfunc(tdc, team_name, addr, cli_type); +} + +static int cb_teamdctl_config_get_raw_direct_force_error(struct teamdctl *tdc, char **response) +{ + // Forced error + return 1; +} + +static int cb_teamdctl_config_get_raw_direct_success(struct teamdctl *tdc, char **response) +{ + return 0; +} + +int teamdctl_config_get_raw_direct(struct teamdctl *tdc, char **response) +{ + if (callback_teamdctl_config_get_raw_direct) + { + return callback_teamdctl_config_get_raw_direct(tdc, response); + } + int (*realfunc)(struct teamdctl *, char **) = + (int (*)(struct teamdctl *, char **))(dlsym (RTLD_NEXT, "teamdctl_config_get_raw_direct")); + return realfunc(tdc, response); +} + +static void cb_teamdctl_disconnect(struct teamdctl *tdc) +{ +} + +void teamdctl_disconnect(struct teamdctl *tdc) +{ + if (callback_teamdctl_disconnect) + { + callback_teamdctl_disconnect(tdc); + return; + } + int (*realfunc)(struct teamdctl *) = + (int (*)(struct teamdctl *))(dlsym (RTLD_NEXT, "teamdctl_disconnect")); + realfunc(tdc); +} + +namespace teamportsync_test +{ + struct TeamPortSyncTest : public ::testing::Test + { + virtual void SetUp() override + { + callback_sleep = cb_sleep; + callback_team_init = NULL; + callback_team_change_handler = NULL; + callback_teamdctl_connect = NULL; + callback_teamdctl_config_get_raw_direct = cb_teamdctl_config_get_raw_direct_force_error; + callback_teamdctl_disconnect = cb_teamdctl_disconnect; + } + + virtual void TearDown() override + { + callback_sleep = NULL; + callback_team_init = NULL; + callback_team_change_handler = NULL; + callback_teamdctl_connect = NULL; + callback_teamdctl_config_get_raw_direct = NULL; + callback_teamdctl_disconnect = NULL; + } + }; + + TEST_F(TeamPortSyncTest, TestInvalidIfIndex) + { + try { + swss::TeamSync::TeamPortSync("testLag", 0, NULL); + FAIL(); + } catch (std::runtime_error &exception) { + EXPECT_THAT(exception.what(), testing::HasSubstr("Unable to initialize team socket")); + } + } + + TEST_F(TeamPortSyncTest, NoLagPresent) + { + try { + swss::TeamSync::TeamPortSync("testLag", 4, NULL); + FAIL(); + } catch (std::runtime_error &exception) { + EXPECT_THAT(exception.what(), testing::HasSubstr("Unable to initialize team socket")); + } + } + + TEST_F(TeamPortSyncTest, TeamdctlNoConfig) + { + callback_team_init = cb_team_init; + callback_team_change_handler = cb_team_change_handler; + callback_teamdctl_connect = cb_teamdctl_connect; + try { + swss::TeamSync::TeamPortSync("testLag", 4, NULL); + FAIL(); + } catch (std::runtime_error &exception) { + EXPECT_THAT(exception.what(), testing::HasSubstr("Unable to get config from teamd")); + } + } + + TEST_F(TeamPortSyncTest, AllSuccess) + { + callback_team_init = cb_team_init; + callback_team_change_handler = cb_team_change_handler; + callback_teamdctl_connect = cb_teamdctl_connect; + callback_teamdctl_config_get_raw_direct = cb_teamdctl_config_get_raw_direct_success; + swss::TeamSync::TeamPortSync("testLag", 4, NULL); + } +}