Skip to content

Commit

Permalink
Implement telemetry daemon for teamd (sonic-net#1317)
Browse files Browse the repository at this point in the history
* Implement telemetry daemon for teamd

Every second all presented teamd daemons is going to be requested
for the state dump in json format.
After that the json dump will be converted to the memory structure
represented the desired format. The database is going to be updated
only for entries which were changed since the last poll.

The daemon will track a case when a LAG has been added or removed.

* Fix typos

* Fix order in the class declaration

* Make the code more c++

* Add more changes to .gitignore

Co-authored-by: PS <ps@cs.com>
  • Loading branch information
pavel-shirshov and PS authored Jun 19, 2020
1 parent 7cc71d1 commit cbfe521
Show file tree
Hide file tree
Showing 9 changed files with 781 additions and 1 deletion.
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,12 @@ libtool
Makefile.in
stamp-h1
**/Makefile
autom4te.cache

# Dependency Folder #
#####################
deps/
**/.deps

# Executables #
###############
Expand All @@ -47,6 +49,9 @@ cfgmgr/teammgrd
cfgmgr/vlanmgrd
cfgmgr/vrfmgrd
cfgmgr/vxlanmgrd
cfgmgr/natmgrd
cfgmgr/sflowmgrd
portsyncd/portsyncd
fpmsyncd/fpmsyncd
mclagsyncd/mclagsyncd
natsyncd/natsyncd
Expand All @@ -57,11 +62,16 @@ orchagent/routeresync
portsyncd/portsyncd
swssconfig/swssconfig
swssconfig/swssplayer
tlm_teamd/tlm_teamd
teamsyncd/teamsyncd
tests/tests

# Test Files #
##############
tests/log
tests/mock_tests/test-suite.log
tests/mock_tests/tests.log
tests/mock_tests/tests.trs
tests/test-suite.log
tests/tests.log
tests/tests.trs
2 changes: 1 addition & 1 deletion Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
SUBDIRS = fpmsyncd neighsyncd portsyncd mclagsyncd natsyncd orchagent swssconfig cfgmgr tests

if HAVE_LIBTEAM
SUBDIRS += teamsyncd
SUBDIRS += teamsyncd tlm_teamd
endif
3 changes: 3 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ AC_CHECK_LIB([team], [team_alloc],
[AC_MSG_WARN([libteam is not installed.])
AM_CONDITIONAL(HAVE_LIBTEAM, false)])

PKG_CHECK_MODULES([JANSSON], [jansson])

AC_CHECK_LIB([sai], [sai_object_type_query],
AM_CONDITIONAL(HAVE_SAI, true),
[AC_MSG_WARN([libsai is not installed.])
Expand Down Expand Up @@ -92,6 +94,7 @@ AC_CONFIG_FILES([
natsyncd/Makefile
portsyncd/Makefile
teamsyncd/Makefile
tlm_teamd/Makefile
mclagsyncd/Makefile
swssconfig/Makefile
cfgmgr/Makefile
Expand Down
15 changes: 15 additions & 0 deletions tlm_teamd/Makefile.am
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
INCLUDES = -I $(top_srcdir)

bin_PROGRAMS = tlm_teamd

if DEBUG
DBGFLAGS = -ggdb -DDEBUG
else
DBGFLAGS = -g
endif

tlm_teamd_SOURCES = main.cpp teamdctl_mgr.cpp values_store.cpp

tlm_teamd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON)
tlm_teamd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(JANSSON_CFLAGS)
tlm_teamd_LDADD = -lhiredis -lswsscommon -lteamdctl $(JANSSON_LIBS)
128 changes: 128 additions & 0 deletions tlm_teamd/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
#include <csignal>
#include <iostream>
#include <deque>

#include <logger.h>
#include <select.h>
#include <dbconnector.h>
#include <subscriberstatetable.h>

#include "teamdctl_mgr.h"
#include "values_store.h"


bool g_run = true;


/// This function extract all available updates from the table
/// and add or remove LAG interfaces from the TeamdCtlMgr
///
/// @param table reference to the SubscriberStateTable
/// @param mgr reference to the TeamdCtlMgr
///
void update_interfaces(swss::SubscriberStateTable & table, TeamdCtlMgr & mgr)
{
std::deque<swss::KeyOpFieldsValuesTuple> entries;

table.pops(entries);
for (const auto & entry: entries)
{
const auto & lag_name = kfvKey(entry);
const auto & op = kfvOp(entry);

if (op == "SET")
{
mgr.add_lag(lag_name);
}
else if (op == "DEL")
{
mgr.remove_lag(lag_name);
}
else
{
SWSS_LOG_WARN("Got invalid operation: '%s' with key '%s'", op.c_str(), lag_name.c_str());
}
}
}

///
/// Signal handler
///
void sig_handler(int signo)
{
(void)signo;
g_run = false;
}

///
/// main function
///
int main()
{
const int ms_select_timeout = 1000;

sighandler_t sig_res;

sig_res = signal(SIGTERM, sig_handler);
if (sig_res == SIG_ERR)
{
std::cerr << "Can't set signal handler for SIGTERM\n";
return -1;
}

sig_res = signal(SIGINT, sig_handler);
if (sig_res == SIG_ERR)
{
std::cerr << "Can't set signal handler for SIGINT\n";
return -1;
}

int rc = 0;
try
{
swss::Logger::linkToDbNative("tlm_teamd");
SWSS_LOG_NOTICE("Starting");
swss::DBConnector db("STATE_DB", 0);

ValuesStore values_store(&db);
TeamdCtlMgr teamdctl_mgr;

swss::Select s;
swss::Selectable * event;
swss::SubscriberStateTable sst_lag(&db, STATE_LAG_TABLE_NAME);
s.addSelectable(&sst_lag);

while (g_run && rc == 0)
{
int res = s.select(&event, ms_select_timeout);
if (res == swss::Select::OBJECT)
{
update_interfaces(sst_lag, teamdctl_mgr);
values_store.update(teamdctl_mgr.get_dumps());
}
else if (res == swss::Select::ERROR)
{
SWSS_LOG_ERROR("Select returned ERROR");
rc = -2;
}
else if (res == swss::Select::TIMEOUT)
{
values_store.update(teamdctl_mgr.get_dumps());
}
else
{
SWSS_LOG_ERROR("Select returned unknown value");
rc = -3;
}
}
SWSS_LOG_NOTICE("Exiting");
}
catch (const std::exception & e)
{
std::cerr << "Exception \"" << e.what() << "\" had been thrown" << std::endl;
SWSS_LOG_ERROR("Exception '%s' had been thrown", e.what());
rc = -1;
}

return rc;
}
146 changes: 146 additions & 0 deletions tlm_teamd/teamdctl_mgr.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
#include <cstring>

#include <logger.h>

#include "teamdctl_mgr.h"

///
/// The destructor clean up handlers to teamds
///
TeamdCtlMgr::~TeamdCtlMgr()
{
for (const auto & p: m_handlers)
{
const auto & lag_name = p.first;
const auto & tdc = m_handlers[lag_name];
teamdctl_disconnect(tdc);
teamdctl_free(tdc);
SWSS_LOG_NOTICE("Exiting. Disconnecting from teamd. LAG '%s'", lag_name.c_str());
}
}

///
/// Returns true, if we have LAG with name lag_name
/// in the manager.
/// @param lag_name a name for LAG interface
/// @return true if has key, false if doesn't
///
bool TeamdCtlMgr::has_key(const std::string & lag_name) const
{
return m_handlers.find(lag_name) != m_handlers.end();
}

///
/// Adds a LAG interface with lag_name to the manager
/// This method allocates structures to connect to teamd
/// @param lag_name a name for LAG interface
/// @return true if the lag was added successfully, false otherwise
///
bool TeamdCtlMgr::add_lag(const std::string & lag_name)
{
if (has_key(lag_name))
{
SWSS_LOG_DEBUG("The LAG '%s' was already added. Skip adding it.", lag_name.c_str());
return true;
}
else
{
auto tdc = teamdctl_alloc();
if (!tdc)
{
SWSS_LOG_ERROR("Can't allocate memory for teamdctl handler. LAG='%s'", lag_name.c_str());
return false;
}

int err = teamdctl_connect(tdc, lag_name.c_str(), nullptr, nullptr);
if (err)
{
SWSS_LOG_ERROR("Can't connect to teamd LAG='%s', error='%s'", lag_name.c_str(), strerror(-err));
teamdctl_free(tdc);
return false;
}
m_handlers.emplace(lag_name, tdc);
SWSS_LOG_NOTICE("The LAG '%s' has been added.", lag_name.c_str());
}

return true;
}

///
/// Removes a LAG interface with lag_name from the manager
/// This method deallocates teamd structures
/// @param lag_name a name for LAG interface
/// @return true if the lag was removed successfully, false otherwise
///
bool TeamdCtlMgr::remove_lag(const std::string & lag_name)
{
if (has_key(lag_name))
{
auto tdc = m_handlers[lag_name];
teamdctl_disconnect(tdc);
teamdctl_free(tdc);
m_handlers.erase(lag_name);
SWSS_LOG_NOTICE("The LAG '%s' has been removed.", lag_name.c_str());
}
else
{
SWSS_LOG_WARN("The LAG '%s' hasn't been added. Can't remove it", lag_name.c_str());
}
return true;
}

///
/// Get json dump from teamd for LAG interface with name lag_name
/// @param lag_name a name for LAG interface
/// @return a pair. First element of the pair is true, if the method is successful
/// false otherwise. If the first element is true, the second element has a dump
/// otherwise the second element is an empty string
///
TeamdCtlDump TeamdCtlMgr::get_dump(const std::string & lag_name)
{
TeamdCtlDump res = { false, "" };
if (has_key(lag_name))
{
auto tdc = m_handlers[lag_name];
char * dump;
int r = teamdctl_state_get_raw_direct(tdc, &dump);
if (r == 0)
{
res = { true, std::string(dump) };
}
else
{
SWSS_LOG_ERROR("Can't get dump for LAG '%s'. Skipping", lag_name.c_str());
}
}
else
{
SWSS_LOG_ERROR("Can't update state. LAG not found. LAG='%s'", lag_name.c_str());
}

return res;
}

///
/// Get dumps for all registered LAG interfaces
/// @return vector of pairs. Each pair first value is a name of LAG, second value is a dump
///
TeamdCtlDumps TeamdCtlMgr::get_dumps()
{
TeamdCtlDumps res;

for (const auto & p: m_handlers)
{
const auto & lag_name = p.first;
const auto & result = get_dump(lag_name);
const auto & status = result.first;
const auto & dump = result.second;
if (status)
{
res.push_back({ lag_name, dump });
}
}

return res;
}

27 changes: 27 additions & 0 deletions tlm_teamd/teamdctl_mgr.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#pragma once

#include <string>
#include <vector>
#include <unordered_map>

#include <teamdctl.h>

using TeamdCtlDump = std::pair<bool, std::string>;
using TeamdCtlDumpsEntry = std::pair<std::string, std::string>;
using TeamdCtlDumps = std::vector<TeamdCtlDumpsEntry>;

class TeamdCtlMgr
{
public:
TeamdCtlMgr() = default;
~TeamdCtlMgr();
bool add_lag(const std::string & kag_name);
bool remove_lag(const std::string & kag_name);
TeamdCtlDump get_dump(const std::string & lag_name);
TeamdCtlDumps get_dumps();

private:
bool has_key(const std::string & lag_name) const;

std::unordered_map<std::string, struct teamdctl*> m_handlers;
};
Loading

0 comments on commit cbfe521

Please sign in to comment.