diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index 7db2f403b1..7174472e01 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -1,4 +1,4 @@ -INCLUDES = -I $(top_srcdir) -I $(top_srcdir)/warmrestart +INCLUDES = -I $(top_srcdir) -I $(top_srcdir)/warmrestart -I flex_counter CFLAGS_SAI = -I /usr/include/sai @@ -55,6 +55,8 @@ orchagent_SOURCES = \ policerorch.cpp \ chassisorch.cpp +orchagent_SOURCES += flex_counter/flex_counter_manager.cpp flex_counter/flex_counter_stat_manager.cpp + orchagent_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) orchagent_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) orchagent_LDADD = -lnl-3 -lnl-route-3 -lpthread -lsairedis -lswsscommon -lsaimeta -lsaimetadata diff --git a/orchagent/flex_counter/flex_counter_manager.cpp b/orchagent/flex_counter/flex_counter_manager.cpp new file mode 100644 index 0000000000..0b0eddd8c4 --- /dev/null +++ b/orchagent/flex_counter/flex_counter_manager.cpp @@ -0,0 +1,225 @@ +#include "flex_counter_manager.h" + +#include + +#include "schema.h" +#include "rediscommand.h" +#include "logger.h" +#include "sai_serialize.h" + +using std::shared_ptr; +using std::string; +using std::unordered_map; +using std::unordered_set; +using std::vector; +using swss::DBConnector; +using swss::FieldValueTuple; +using swss::ProducerTable; + +const string FLEX_COUNTER_ENABLE("enable"); +const string FLEX_COUNTER_DISABLE("disable"); + +const unordered_map FlexCounterManager::stats_mode_lookup = +{ + { StatsMode::READ, STATS_MODE_READ }, +}; + +const unordered_map FlexCounterManager::status_lookup = +{ + { false, FLEX_COUNTER_DISABLE }, + { true, FLEX_COUNTER_ENABLE } +}; + +const unordered_map FlexCounterManager::counter_id_field_lookup = +{ + { CounterType::PORT_DEBUG, PORT_DEBUG_COUNTER_ID_LIST }, + { CounterType::SWITCH_DEBUG, SWITCH_DEBUG_COUNTER_ID_LIST }, +}; + +// This constructor will create a group that is disabled by default. +FlexCounterManager::FlexCounterManager( + const string& group_name, + const StatsMode stats_mode, + const uint polling_interval) : + group_name(group_name), + stats_mode(stats_mode), + polling_interval(polling_interval), + enabled(false), + flex_counter_db(new DBConnector(FLEX_COUNTER_DB, DBConnector::DEFAULT_UNIXSOCKET, 0)), + flex_counter_group_table(new ProducerTable(flex_counter_db.get(), FLEX_COUNTER_GROUP_TABLE)), + flex_counter_table(new ProducerTable(flex_counter_db.get(), FLEX_COUNTER_TABLE)) +{ + SWSS_LOG_ENTER(); + + applyGroupConfiguration(); + + SWSS_LOG_DEBUG("Initialized flex counter group '%s'.", group_name.c_str()); +} + +FlexCounterManager::~FlexCounterManager() +{ + SWSS_LOG_ENTER(); + + for (const auto& counter: installed_counters) + { + flex_counter_table->del(getFlexCounterTableKey(group_name, counter)); + } + + flex_counter_group_table->del(group_name); + + SWSS_LOG_DEBUG("Deleted flex counter group '%s'.", group_name.c_str()); +} + +void FlexCounterManager::applyGroupConfiguration() +{ + vector field_values = + { + FieldValueTuple(STATS_MODE_FIELD, stats_mode_lookup.at(stats_mode)), + FieldValueTuple(POLL_INTERVAL_FIELD, std::to_string(polling_interval)), + FieldValueTuple(FLEX_COUNTER_STATUS_FIELD, status_lookup.at(enabled)) + }; + + flex_counter_group_table->set(group_name, field_values); +} + +void FlexCounterManager::updateGroupPollingInterval( + const uint polling_interval) +{ + SWSS_LOG_ENTER(); + + vector field_values = + { + FieldValueTuple(POLL_INTERVAL_FIELD, std::to_string(polling_interval)) + }; + flex_counter_group_table->set(group_name, field_values); + + SWSS_LOG_DEBUG("Set polling interval for flex counter group '%s' to %d ms.", + group_name.c_str(), polling_interval); +} + +// enableFlexCounterGroup will do nothing if the flex counter group is already +// enabled. +void FlexCounterManager::enableFlexCounterGroup() +{ + SWSS_LOG_ENTER(); + + if (enabled) + { + return; + } + + vector field_values = + { + FieldValueTuple(FLEX_COUNTER_STATUS_FIELD, FLEX_COUNTER_ENABLE) + }; + flex_counter_group_table->set(group_name, field_values); + enabled = true; + + SWSS_LOG_DEBUG("Enabling flex counters for group '%s'.", + group_name.c_str()); +} + +// disableFlexCounterGroup will do nothing if the flex counter group has been +// disabled. +void FlexCounterManager::disableFlexCounterGroup() +{ + SWSS_LOG_ENTER(); + + if (!enabled) + { + return; + } + + vector field_values = + { + FieldValueTuple(FLEX_COUNTER_STATUS_FIELD, FLEX_COUNTER_DISABLE) + }; + flex_counter_group_table->set(group_name, field_values); + enabled = false; + + SWSS_LOG_DEBUG("Disabling flex counters for group '%s'.", + group_name.c_str()); +} + +// setCounterIdList configures a flex counter to poll the set of provided stats +// that are associated with the given object. +void FlexCounterManager::setCounterIdList( + const sai_object_id_t object_id, + const CounterType counter_type, + const unordered_set& counter_stats) +{ + SWSS_LOG_ENTER(); + + auto counter_type_it = counter_id_field_lookup.find(counter_type); + if (counter_type_it == counter_id_field_lookup.end()) + { + SWSS_LOG_ERROR("Could not update flex counter id list for group '%s': counter type not found.", + group_name.c_str()); + return; + } + + std::vector field_values = + { + FieldValueTuple(counter_type_it->second, serializeCounterStats(counter_stats)) + }; + flex_counter_table->set(getFlexCounterTableKey(group_name, object_id), field_values); + installed_counters.insert(object_id); + + SWSS_LOG_DEBUG("Updated flex counter id list for object '%lu' in group '%s'.", + object_id, + group_name.c_str()); +} + +// clearCounterIdList clears all stats that are currently being polled from +// the given object. +void FlexCounterManager::clearCounterIdList(const sai_object_id_t object_id) +{ + SWSS_LOG_ENTER(); + + auto counter_it = installed_counters.find(object_id); + if (counter_it == installed_counters.end()) + { + SWSS_LOG_WARN("No counters found on object '%lu' in group '%s'.", + object_id, + group_name.c_str()); + return; + } + + flex_counter_table->del(getFlexCounterTableKey(group_name, object_id)); + installed_counters.erase(counter_it); + + SWSS_LOG_DEBUG("Cleared flex counter id list for object '%lu' in group '%s'.", + object_id, + group_name.c_str()); +} + +string FlexCounterManager::getFlexCounterTableKey( + const string& group_name, + const sai_object_id_t object_id) const +{ + SWSS_LOG_ENTER(); + + return group_name + flex_counter_table->getTableNameSeparator() + sai_serialize_object_id(object_id); +} + +// serializeCounterStats turns a set of stats into a format suitable for FLEX_COUNTER_DB. +string FlexCounterManager::serializeCounterStats( + const unordered_set& counter_stats) const +{ + SWSS_LOG_ENTER(); + + string stats_string; + for (const auto& stat : counter_stats) + { + stats_string.append(stat); + stats_string.append(","); + } + + if (!stats_string.empty()) + { + // Fence post: remove the trailing comma + stats_string.pop_back(); + } + + return stats_string; +} \ No newline at end of file diff --git a/orchagent/flex_counter/flex_counter_manager.h b/orchagent/flex_counter/flex_counter_manager.h new file mode 100644 index 0000000000..0d444d57ed --- /dev/null +++ b/orchagent/flex_counter/flex_counter_manager.h @@ -0,0 +1,77 @@ +#ifndef ORCHAGENT_FLEX_COUNTER_MANAGER_H +#define ORCHAGENT_FLEX_COUNTER_MANAGER_H + +#include +#include +#include +#include "dbconnector.h" +#include "producertable.h" + +extern "C" { +#include "sai.h" +} + +enum class StatsMode +{ + READ +}; + +enum class CounterType +{ + PORT_DEBUG, + SWITCH_DEBUG +}; + +// FlexCounterManager allows users to manage a group of flex counters. +// +// TODO: FlexCounterManager doesn't currently support the full range of +// flex counter features. In particular, support for standard (i.e. non-debug) +// counters and support for plugins needs to be added. +class FlexCounterManager +{ + public: + FlexCounterManager( + const std::string& group_name, + const StatsMode stats_mode, + const uint polling_interval); + + FlexCounterManager(const FlexCounterManager&) = delete; + FlexCounterManager& operator=(const FlexCounterManager&) = delete; + virtual ~FlexCounterManager(); + + void updateGroupPollingInterval(const uint polling_interval); + void enableFlexCounterGroup(); + void disableFlexCounterGroup(); + + void setCounterIdList( + const sai_object_id_t object_id, + const CounterType counter_type, + const std::unordered_set& counter_stats); + void clearCounterIdList(const sai_object_id_t object_id); + + protected: + void applyGroupConfiguration(); + + private: + std::string getFlexCounterTableKey( + const std::string& group_name, + const sai_object_id_t object_id) const; + std::string serializeCounterStats( + const std::unordered_set& counter_stats) const; + + std::string group_name; + StatsMode stats_mode; + uint polling_interval; + bool enabled; + std::unordered_set installed_counters; + + std::shared_ptr flex_counter_db = nullptr; + std::shared_ptr flex_counter_group_table = nullptr; + std::shared_ptr flex_counter_table = nullptr; + + static const std::unordered_map stats_mode_lookup; + static const std::unordered_map status_lookup; + static const std::unordered_map counter_id_field_lookup; +}; + +#endif // ORCHAGENT_FLEX_COUNTER_MANAGER_H diff --git a/orchagent/flex_counter/flex_counter_stat_manager.cpp b/orchagent/flex_counter/flex_counter_stat_manager.cpp new file mode 100644 index 0000000000..4f6cab843e --- /dev/null +++ b/orchagent/flex_counter/flex_counter_stat_manager.cpp @@ -0,0 +1,93 @@ +#include "flex_counter_stat_manager.h" + +#include "schema.h" +#include "rediscommand.h" +#include "logger.h" +#include "sai_serialize.h" + +using std::string; +using std::unordered_map; +using std::unordered_set; +using swss::FieldValueTuple; + +FlexCounterStatManager::FlexCounterStatManager( + const string& group_name, + const StatsMode stats_mode, + const int polling_interval) : + FlexCounterManager(group_name, stats_mode, polling_interval) +{ + SWSS_LOG_ENTER(); +} + +FlexCounterStatManager::~FlexCounterStatManager() +{ + SWSS_LOG_ENTER(); +} + +// addFlexCounterStat will add a new stat for the given object to poll. +void FlexCounterStatManager::addFlexCounterStat( + const sai_object_id_t object_id, + const CounterType counter_type, + const string& counter_stat) +{ + SWSS_LOG_ENTER(); + + auto counter_stats = object_stats.find(object_id); + if (counter_stats == object_stats.end()) + { + unordered_set new_stats = { counter_stat }; + counter_stats = object_stats.emplace(object_id, new_stats).first; + } + else + { + counter_stats->second.emplace(counter_stat); + } + + // FIXME: Currently the state of the flex counter group is lost if all + // stats are removed from a flex counter group. This will be fixed once + // syncd flex counters are refactored. For now, we can workaround this + // by re-applying the group configuration when we set the counter id list. + FlexCounterManager::applyGroupConfiguration(); + + FlexCounterManager::setCounterIdList(object_id, counter_type, counter_stats->second); + + SWSS_LOG_DEBUG("Added flex stat '%s' to object '%s'", counter_stat.c_str(), sai_serialize_object_id(object_id).c_str()); +} + +// removeFlexCounterStat will remove a stat from the set of stats the given +// object are polling. +void FlexCounterStatManager::removeFlexCounterStat( + const sai_object_id_t object_id, + const CounterType counter_type, + const string& counter_stat) +{ + SWSS_LOG_ENTER(); + + auto counter_stats = object_stats.find(object_id); + if (counter_stats == object_stats.end()) + { + SWSS_LOG_WARN("Could not find flex stat '%s' on object '%s'", + counter_stat.c_str(), sai_serialize_object_id(object_id).c_str()); + return; + } + + counter_stats->second.erase(counter_stat); + + // If we don't have any stats left for this object, delete the flex + // counter entirely. + if (counter_stats->second.empty()) + { + object_stats.erase(counter_stats); + FlexCounterManager::clearCounterIdList(object_id); + + SWSS_LOG_DEBUG("Flex stat is empty, removing flex counter from object '%s'", + sai_serialize_object_id(object_id).c_str()); + return; + } + + FlexCounterManager::setCounterIdList(object_id, counter_type, counter_stats->second); + + SWSS_LOG_DEBUG("Removing flex stat '%s' from object '%s'", + counter_stat.c_str(), + sai_serialize_object_id(object_id).c_str()); +} diff --git a/orchagent/flex_counter/flex_counter_stat_manager.h b/orchagent/flex_counter/flex_counter_stat_manager.h new file mode 100644 index 0000000000..250adb724c --- /dev/null +++ b/orchagent/flex_counter/flex_counter_stat_manager.h @@ -0,0 +1,33 @@ +#ifndef ORCHAGENT_FLEX_COUNTER_STAT_MANAGER_H +#define ORCHAGENT_FLEX_COUNTER_STAT_MANAGER_H + +#include "flex_counter_manager.h" + +// FlexCounterStatManager allows users to manage a group of flex counters +// where the objects have highly variable sets of stats to track. +class FlexCounterStatManager : public FlexCounterManager +{ + public: + FlexCounterStatManager( + const std::string& group_name, + const StatsMode stats_mode, + const int polling_interval); + + FlexCounterStatManager(const FlexCounterStatManager&) = delete; + FlexCounterStatManager& operator=(const FlexCounterStatManager&) = delete; + ~FlexCounterStatManager(); + + void addFlexCounterStat( + const sai_object_id_t object_id, + const CounterType counter_type, + const std::string& counter_stat); + void removeFlexCounterStat( + const sai_object_id_t object_id, + const CounterType counter_type, + const std::string& counter_stat); + + private: + std::unordered_map> object_stats; +}; + +#endif // ORCHAGENT_FLEX_COUNTER_STAT_MANAGER_H