diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index b0cbceb241..f535971c54 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -106,6 +106,7 @@ orchagent_SOURCES = \ watermarkorch.cpp \ notificationconsumerstatsorch.cpp \ policerorch.cpp \ + namelabelmapper.cpp \ sfloworch.cpp \ chassisorch.cpp \ debugcounterorch.cpp \ diff --git a/orchagent/namelabelmapper.cpp b/orchagent/namelabelmapper.cpp new file mode 100644 index 0000000000..1c4603173a --- /dev/null +++ b/orchagent/namelabelmapper.cpp @@ -0,0 +1,222 @@ +#include "namelabelmapper.h" + +#include +#include +#include + +#include +#include "logger.h" +#include "sai_serialize.h" + +extern "C" +{ +#include "sai.h" +} + +using ::nlohmann::json; + +namespace +{ + +std::string convertToDBField(_In_ const sai_object_type_t object_type, _In_ const std::string &key) +{ + return sai_serialize_object_type(object_type) + "|" + key; +} + +} // namespace + +NameLabelMapper::NameLabelMapper() : m_db("STATE_DB", 0), m_table(&m_db, "SAI_KEY_LABEL_MAP") +{ +} + +bool NameLabelMapper::setLabel(_In_ sai_object_type_t object_type, _In_ const std::string &key, _In_ std::string &label) +{ + SWSS_LOG_ENTER(); + + if (existsLabel(object_type, key)) + { + SWSS_LOG_ERROR("Key %s with SAI object type %d already exists in label mapper", key.c_str(), object_type); + return false; + } + + m_labelTables[object_type][key] = label; + SWSS_LOG_INFO("Created new label %s for Key %s with SAI object type %d", label.c_str(), key.c_str(), object_type); + return true; +} + +bool NameLabelMapper::getLabel(_In_ sai_object_type_t object_type, _In_ const std::string &key, + _Out_ std::string &label) +{ + SWSS_LOG_ENTER(); + + if (!existsLabel(object_type, key)) + { + SWSS_LOG_INFO("Key %s with SAI object type %d does not exist in label mapper", key.c_str(), object_type); + return false; + } + + label = m_labelTables[object_type][key]; + return true; +} + +bool NameLabelMapper::eraseLabel(_In_ sai_object_type_t object_type, _In_ const std::string &key) +{ + SWSS_LOG_ENTER(); + + if (!existsLabel(object_type, key)) + { + SWSS_LOG_ERROR("Key %s with SAI object type %d does not exist in " + "label mapper when erasing", + key.c_str(), object_type); + return false; + } + + m_labelTables[object_type].erase(key); + return true; +} + +void NameLabelMapper::deleteMapperInDb() +{ + SWSS_LOG_ENTER(); + m_table.del(""); +} + +size_t NameLabelMapper::getNumEntries(_In_ sai_object_type_t object_type) const +{ + SWSS_LOG_ENTER(); + + return (m_labelTables[object_type].size()); +} + +bool NameLabelMapper::existsLabel(_In_ sai_object_type_t object_type, _In_ const std::string &key) const +{ + SWSS_LOG_ENTER(); + + return m_labelTables[object_type].find(key) != m_labelTables[object_type].end(); +} + +std::string NameLabelMapper::generateKeyFromTableAndObjectName(std::string table_name, std::string object_name) +{ + return table_name + ":" + object_name; +} + +std::string NameLabelMapper::generateUniqueLabel() +{ + char label_buf[UNIQUE_LABEL_SIZE]; + uint64_t msec = + std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()) + .count(); + snprintf(label_buf, UNIQUE_LABEL_SIZE, "%" PRIu64 "", msec); + return std::string(reinterpret_cast(label_buf)); +} + +bool NameLabelMapper::allocateLabel(_In_ sai_object_type_t object_type, _In_ const std::string &key, + _Out_ std::string &label) +{ + if (!getLabel(object_type, key, label)) + { + label = generateUniqueLabel(); + return false; + } + return true; +} + +bool NameLabelMapper::addLabelToAttr(sai_object_type_t object_type, const std::string &table_name, + const std::string &key, sai_attribute_t &attr, sai_attr_id_t attr_id, + std::string &mapper_key, std::string &label) +{ + mapper_key = generateKeyFromTableAndObjectName(table_name, key); + bool label_present = allocateLabel(object_type, mapper_key, label); + attr.id = attr_id; + auto size = sizeof(attr.value.chardata); + snprintf(attr.value.chardata, size, "%s", label.c_str()); + SWSS_LOG_NOTICE("Add ATTR_LABEL %s for sai object %s for %s", label.c_str(), + sai_serialize_object_type(object_type).c_str(), mapper_key.c_str()); + return label_present; +} + +std::string NameLabelMapper::dumpStateCache() +{ + json cache = json({}); + for (int i = 0; i < SAI_OBJECT_TYPE_MAX; i++) + { + if (m_labelTables[i].empty()) + { + continue; + } + + json label_mapper_j = json({}); + for (const auto &kv_pair : m_labelTables[i]) + { + label_mapper_j[kv_pair.first] = kv_pair.second; + } + std::string sai_object_type = sai_serialize_object_type(static_cast(i)); + cache[sai_object_type] = label_mapper_j; + } + return cache.dump(4); +} + +void NameLabelMapper::saveMapperToDb() +{ + for (int i = 0; i < SAI_OBJECT_TYPE_MAX; i++) + { + if (m_labelTables[i].empty()) + { + continue; + } + for (const auto &kv_pair : m_labelTables[i]) + { + auto key = kv_pair.first; + auto label = kv_pair.second; + m_table.hset("", convertToDBField(static_cast(i), key), label); + SWSS_LOG_INFO("label %s for Key %s with SAI object type %d save into state_db", label.c_str(), key.c_str(), + i); + } + } +} + +void NameLabelMapper::readMapperFromDb() +{ + std::vector tuples; + m_table.get("", tuples); + SWSS_LOG_INFO("m_table->get size %zd", tuples.size()); + for (auto &fv : tuples) + { + std::string combo = fvField(fv); + std::string label = fvValue(fv); + SWSS_LOG_INFO("Got field %s label %s from db", combo.c_str(), label.c_str()); + + size_t pos = 0; + std::string obj_type_str, key; + if ((pos = combo.find("|")) != std::string::npos) + { + obj_type_str = combo.substr(0, pos); + key = combo.substr(pos + 1, combo.size() - pos - 1); + + sai_object_type_t sai_object_type; + sai_deserialize_object_type(obj_type_str, sai_object_type); + setLabel(sai_object_type, key, label); + } + } +} + +std::string NameLabelMapper::verifyLabelMapping( + _In_ sai_object_type_t object_type, _In_ const std::string& key, + _In_ std::string label) { + SWSS_LOG_ENTER(); + + std::string mapper_label; + if (!getLabel(object_type, key, mapper_label)) { + std::stringstream msg; + msg << "Label not found in mapper for key " << key; + return msg.str(); + } + if (mapper_label != label) { + std::stringstream msg; + msg << "Label mismatched in mapper for key " << key << ": " << label + << " vs " << mapper_label; + return msg.str(); + } + + return ""; +} diff --git a/orchagent/namelabelmapper.h b/orchagent/namelabelmapper.h new file mode 100644 index 0000000000..bdffbca021 --- /dev/null +++ b/orchagent/namelabelmapper.h @@ -0,0 +1,92 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include + +#include "dbconnector.h" +#include "table.h" + +extern "C" +{ +#include "sai.h" +} + +#define UNIQUE_LABEL_SIZE 32 + +// Interface for mapping object name/key to unique Label. +// This class is not thread safe. +class NameLabelMapper +{ + public: + NameLabelMapper(); + ~NameLabelMapper() = default; + + // Sets label for the given key for the specific object_type. Returns false if + // the key already exists. + bool setLabel(_In_ sai_object_type_t object_type, _In_ const std::string &key, _In_ std::string &label); + + // Return true if label present in the mapper, and copy the label in the 3rd + // argument; or false, and a new unique label is allocated and saved in mapper + bool allocateLabel(_In_ sai_object_type_t object_type, _In_ const std::string &key, _Out_ std::string &label); + + // Gets label from mapper for the given key for the SAI object_type. + // Returns true on success. + bool getLabel(_In_ sai_object_type_t object_type, _In_ const std::string &key, _Out_ std::string &label); + + // Erases label for the given key for the SAI object_type. + // Returns true on success. + bool eraseLabel(_In_ sai_object_type_t object_type, _In_ const std::string &key); + + // Delete mapper table in db + void deleteMapperInDb(); + + // Gets the number of labels for the SAI object_type. + size_t getNumEntries(_In_ sai_object_type_t object_type) const; + + // SAI object subtype and name to label mapper name + // Returns concat of (subtype + object_name) + // For example, for POLICER, there are 4 subtypes: + // COPP trap group, ACL policer, storm policer, regular policer + // One way is use APPL_DB table name in subtype field, that is + // subtype = APPL_DB table name + std::string generateKeyFromTableAndObjectName(std::string table_name, std::string object_name); + + // Add the unique label to an SAI attribute + // return true if the label was already present; + // return false if a new label is generated. + bool addLabelToAttr(sai_object_type_t object_type, const std::string &table_name, const std::string &key, + sai_attribute_t &attr, sai_attr_id_t attr_id, std::string &mapper_key, std::string &label); + + // Save the all entries to state db + void saveMapperToDb(); + + // Read the all entries from state db + void readMapperFromDb(); + + // Returns a json string that contains each non-empty label mapper. + std::string dumpStateCache(); + + // Checks whether label mapping exists for the given key for the specific + // object type. + bool existsLabel(_In_ sai_object_type_t object_type, _In_ const std::string &key) const; + + // Verify the given label in the label mapper + std::string verifyLabelMapping(_In_ sai_object_type_t object_type, + _In_ const std::string& key, + _In_ std::string label); + + private: + // Generate and return a unique label + std::string generateUniqueLabel(); + + // Buckets of map tables, one for every SAI object type. + std::unordered_map m_labelTables[SAI_OBJECT_TYPE_MAX]; + + swss::DBConnector m_db; + swss::Table m_table; +}; diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index 90637c7e03..715d80ba00 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -7,6 +7,7 @@ #include "orchdaemon.h" #include "logger.h" #include +#include "namelabelmapper.h" #include "warm_restart.h" #include #include "orch_zmq_config.h" @@ -78,6 +79,7 @@ ShlOrch *gShlOrch; EvpnMhOrch *gEvpnMhOrch; L2NhgOrch *gL2NhgOrch; +NameLabelMapper *gLabelMapper; bool gIsNatSupported = false; event_handle_t g_events_handle; @@ -189,6 +191,7 @@ void OrchDaemon::disableRingBuffer() { bool OrchDaemon::init() { SWSS_LOG_ENTER(); + gLabelMapper = new NameLabelMapper(); string platform = getenv("platform") ? getenv("platform") : ""; @@ -1138,7 +1141,8 @@ bool OrchDaemon::warmRestoreAndSyncUp() SWSS_LOG_ENTER(); WarmStart::setWarmStartState("orchagent", WarmStart::INITIALIZED); - + gLabelMapper->readMapperFromDb(); + for (Orch *o : m_orchList) { o->bake(); @@ -1209,6 +1213,7 @@ bool OrchDaemon::warmRestoreAndSyncUp() * The "RECONCILED" state of orchagent doesn't mean the state related to neighbor is up to date. */ WarmStart::setWarmStartState("orchagent", WarmStart::RECONCILED); + gLabelMapper->deleteMapperInDb(); return true; } diff --git a/orchagent/p4orch/p4orch.cpp b/orchagent/p4orch/p4orch.cpp index 2aa7f8f3d8..1e9efcaa53 100644 --- a/orchagent/p4orch/p4orch.cpp +++ b/orchagent/p4orch/p4orch.cpp @@ -8,6 +8,7 @@ #include "copporch.h" #include "logger.h" +#include "namelabelmapper.h" #include "orch.h" #include "p4orch/acl_rule_manager.h" #include "p4orch/acl_table_manager.h" @@ -28,8 +29,9 @@ #include "sai_serialize.h" #include "timer.h" #include "timestamp.h" - +extern NameLabelMapper *gLabelMapper; extern PortsOrch *gPortsOrch; + #define P4_ACL_COUNTERS_STATS_POLL_TIMER_NAME "P4_ACL_COUNTERS_STATS_POLL_TIMER" #define P4_EXT_COUNTERS_STATS_POLL_TIMER_NAME "P4_EXT_COUNTERS_STATS_POLL_TIMER" #define APP_P4RT_EXT_TABLES_MANAGER "EXT_TABLES_MANAGER" diff --git a/orchagent/p4orch/tests/Makefile.am b/orchagent/p4orch/tests/Makefile.am index 416536dc8f..f7613fc24e 100644 --- a/orchagent/p4orch/tests/Makefile.am +++ b/orchagent/p4orch/tests/Makefile.am @@ -29,6 +29,7 @@ p4orch_tests_SOURCES = $(ORCHAGENT_DIR)/orch.cpp \ $(ORCHAGENT_DIR)/request_parser.cpp \ $(top_srcdir)/lib/recorder.cpp \ $(ORCHAGENT_DIR)/zmqorch.cpp \ + $(ORCHAGENT_DIR)/namelabelmapper.cpp \ $(ORCHAGENT_DIR)/flex_counter/flex_counter_manager.cpp \ $(ORCHAGENT_DIR)/flex_counter/flow_counter_handler.cpp \ $(ORCHAGENT_DIR)/port/port_capabilities.cpp \ @@ -68,7 +69,9 @@ p4orch_tests_SOURCES = $(ORCHAGENT_DIR)/orch.cpp \ fake_table.cpp \ fake_aclorch.cpp \ fake_zmqserver.cpp \ + fake_namelabelmapper.cpp \ p4oidmapper_test.cpp \ + namelabelmapper_test.cpp \ p4orch_test.cpp \ p4orch_util_test.cpp \ return_code_test.cpp \ diff --git a/orchagent/p4orch/tests/fake_namelabelmapper.cpp b/orchagent/p4orch/tests/fake_namelabelmapper.cpp new file mode 100644 index 0000000000..b1a2580c24 --- /dev/null +++ b/orchagent/p4orch/tests/fake_namelabelmapper.cpp @@ -0,0 +1,2 @@ +#include "namelabelmapper.h" +NameLabelMapper *gLabelMapper = new NameLabelMapper(); diff --git a/orchagent/p4orch/tests/namelabelmapper_test.cpp b/orchagent/p4orch/tests/namelabelmapper_test.cpp new file mode 100644 index 0000000000..c59604ab4d --- /dev/null +++ b/orchagent/p4orch/tests/namelabelmapper_test.cpp @@ -0,0 +1,113 @@ +#include "namelabelmapper.h" + +#include + +#include + +#include "sai_serialize.h" + +extern "C" +{ +#include "sai.h" +} + +namespace +{ + +constexpr char *kNextHopObject1 = "NextHop1"; +constexpr char *kNextHopObject2 = "NextHop2"; +constexpr char *kRouteObject1 = "Route1"; +constexpr char *kRouteObject2 = "Route2"; + +std::string cache_dump = + R"({ + "SAI_OBJECT_TYPE_NEXT_HOP": { + "NextHop1": "1111", + "NextHop2": "2222" + }, + "SAI_OBJECT_TYPE_ROUTE_ENTRY": { + "Route1": "3333", + "Route2": "4444" + } +})"; + +TEST(NameLabelMapperTest, MapperTest) +{ + NameLabelMapper mapper; + std::string label1, label2, label3, label4; + EXPECT_FALSE(mapper.allocateLabel(SAI_OBJECT_TYPE_NEXT_HOP, kNextHopObject1, label1)); + EXPECT_TRUE(mapper.setLabel(SAI_OBJECT_TYPE_NEXT_HOP, kNextHopObject1, label1)); + EXPECT_FALSE(mapper.allocateLabel(SAI_OBJECT_TYPE_NEXT_HOP, kNextHopObject2, label2)); + EXPECT_TRUE(mapper.setLabel(SAI_OBJECT_TYPE_NEXT_HOP, kNextHopObject2, label2)); + + EXPECT_FALSE(mapper.allocateLabel(SAI_OBJECT_TYPE_ROUTE_ENTRY, kRouteObject1, label3)); + EXPECT_TRUE(mapper.setLabel(SAI_OBJECT_TYPE_ROUTE_ENTRY, kRouteObject1, label3)); + EXPECT_FALSE(mapper.allocateLabel(SAI_OBJECT_TYPE_ROUTE_ENTRY, kRouteObject2, label4)); + EXPECT_TRUE(mapper.setLabel(SAI_OBJECT_TYPE_ROUTE_ENTRY, kRouteObject2, label4)); + + EXPECT_EQ(2, mapper.getNumEntries(SAI_OBJECT_TYPE_NEXT_HOP)); + EXPECT_EQ(2, mapper.getNumEntries(SAI_OBJECT_TYPE_ROUTE_ENTRY)); + + EXPECT_TRUE(mapper.existsLabel(SAI_OBJECT_TYPE_NEXT_HOP, kNextHopObject1)); + EXPECT_TRUE(mapper.existsLabel(SAI_OBJECT_TYPE_NEXT_HOP, kNextHopObject2)); + EXPECT_TRUE(mapper.existsLabel(SAI_OBJECT_TYPE_ROUTE_ENTRY, kRouteObject1)); + EXPECT_TRUE(mapper.existsLabel(SAI_OBJECT_TYPE_ROUTE_ENTRY, kRouteObject2)); + + std::string label; + EXPECT_TRUE(mapper.getLabel(SAI_OBJECT_TYPE_NEXT_HOP, kNextHopObject1, label)); + EXPECT_EQ(label1, label); + EXPECT_TRUE(mapper.getLabel(SAI_OBJECT_TYPE_NEXT_HOP, kNextHopObject2, label)); + EXPECT_EQ(label2, label); + + mapper.eraseLabel(SAI_OBJECT_TYPE_ROUTE_ENTRY, kRouteObject1); + mapper.eraseLabel(SAI_OBJECT_TYPE_ROUTE_ENTRY, kRouteObject2); + mapper.eraseLabel(SAI_OBJECT_TYPE_NEXT_HOP, kNextHopObject1); + + EXPECT_EQ(1, mapper.getNumEntries(SAI_OBJECT_TYPE_NEXT_HOP)); + EXPECT_EQ(0, mapper.getNumEntries(SAI_OBJECT_TYPE_ROUTE_ENTRY)); + EXPECT_FALSE(mapper.existsLabel(SAI_OBJECT_TYPE_NEXT_HOP, kNextHopObject1)); + EXPECT_TRUE(mapper.existsLabel(SAI_OBJECT_TYPE_NEXT_HOP, kNextHopObject2)); + EXPECT_FALSE(mapper.existsLabel(SAI_OBJECT_TYPE_ROUTE_ENTRY, kRouteObject1)); + EXPECT_FALSE(mapper.existsLabel(SAI_OBJECT_TYPE_ROUTE_ENTRY, kRouteObject2)); +} + +TEST(NameLabelMapperTest, ErrorTest) +{ + NameLabelMapper mapper; + std::string label1; + EXPECT_FALSE(mapper.allocateLabel(SAI_OBJECT_TYPE_NEXT_HOP, kNextHopObject1, label1)); + EXPECT_TRUE(mapper.setLabel(SAI_OBJECT_TYPE_NEXT_HOP, kNextHopObject1, label1)); + + // Set existing Label should fail. + std::string label2 = "abcdefg"; + EXPECT_FALSE(mapper.setLabel(SAI_OBJECT_TYPE_NEXT_HOP, kNextHopObject1, label2)); + + // Get non-existing Label should fail. + std::string label3; + EXPECT_FALSE(mapper.getLabel(SAI_OBJECT_TYPE_NEXT_HOP, kNextHopObject2, label3)); + + // Erase non-existing Label should fail. + EXPECT_FALSE(mapper.eraseLabel(SAI_OBJECT_TYPE_NEXT_HOP, kNextHopObject2)); +} + +TEST(NameLabelMapperTest, DumpEmptyStateCacheTest) +{ + NameLabelMapper mapper; + std::string msg = mapper.dumpStateCache(); + EXPECT_EQ(msg, "{}"); +} + +TEST(NameLabelMapperTest, DumpStateCacheTest) +{ + NameLabelMapper mapper; + std::string label1 = "1111", label2 = "2222", label3 = "3333", label4 = "4444"; + EXPECT_TRUE(mapper.setLabel(SAI_OBJECT_TYPE_NEXT_HOP, kNextHopObject1, label1)); + EXPECT_TRUE(mapper.setLabel(SAI_OBJECT_TYPE_NEXT_HOP, kNextHopObject2, label2)); + EXPECT_TRUE(mapper.setLabel(SAI_OBJECT_TYPE_ROUTE_ENTRY, kRouteObject1, label3)); + EXPECT_TRUE(mapper.setLabel(SAI_OBJECT_TYPE_ROUTE_ENTRY, kRouteObject2, label4)); + + std::string msg = mapper.dumpStateCache(); + EXPECT_EQ(msg, cache_dump); +} + +} // namespace diff --git a/tests/mock_tests/Makefile.am b/tests/mock_tests/Makefile.am index 5872007459..fa055b0834 100644 --- a/tests/mock_tests/Makefile.am +++ b/tests/mock_tests/Makefile.am @@ -179,6 +179,7 @@ tests_SOURCES = aclorch_ut.cpp \ $(top_srcdir)/cfgmgr/portmgr.cpp \ $(top_srcdir)/cfgmgr/sflowmgr.cpp \ $(top_srcdir)/orchagent/zmqorch.cpp \ + $(top_srcdir)/orchagent/namelabelmapper.cpp \ $(top_srcdir)/orchagent/dash/dashenifwdorch.cpp \ $(top_srcdir)/orchagent/dash/dashenifwdinfo.cpp \ $(top_srcdir)/orchagent/dash/dashaclorch.cpp \ diff --git a/tests/mock_tests/mock_orchagent_main.h b/tests/mock_tests/mock_orchagent_main.h index 1e810a9aa7..c3cc6c2275 100644 --- a/tests/mock_tests/mock_orchagent_main.h +++ b/tests/mock_tests/mock_orchagent_main.h @@ -3,6 +3,7 @@ #include "orch.h" #include "switchorch.h" #include "crmorch.h" +#include "namelabelmapper.h" #include "portsorch.h" #include "debugcounterorch.h" #include "routeorch.h"