diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index d39e73d737..5e3cd741cc 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -105,6 +105,7 @@ orchagent_SOURCES += debug_counter/debug_counter.cpp debug_counter/drop_counter. orchagent_SOURCES += p4orch/p4orch.cpp \ p4orch/p4orch_util.cpp \ p4orch/p4oidmapper.cpp \ + p4orch/tables_definition_manager.cpp \ p4orch/router_interface_manager.cpp \ p4orch/gre_tunnel_manager.cpp \ p4orch/neighbor_manager.cpp \ @@ -115,7 +116,8 @@ orchagent_SOURCES += p4orch/p4orch.cpp \ p4orch/acl_rule_manager.cpp \ p4orch/wcmp_manager.cpp \ p4orch/mirror_session_manager.cpp \ - p4orch/l3_admit_manager.cpp + p4orch/l3_admit_manager.cpp \ + p4orch/ext_tables_manager.cpp orchagent_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CFLAGS_ASAN) orchagent_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CFLAGS_ASAN) diff --git a/orchagent/p4orch/acl_rule_manager.cpp b/orchagent/p4orch/acl_rule_manager.cpp index fb73cb0128..985ea4b1c7 100644 --- a/orchagent/p4orch/acl_rule_manager.cpp +++ b/orchagent/p4orch/acl_rule_manager.cpp @@ -165,7 +165,12 @@ std::vector getMeterSaiAttrs(const P4AclMeter &p4_acl_meter) } // namespace -void AclRuleManager::enqueue(const swss::KeyOpFieldsValuesTuple &entry) +ReturnCode AclRuleManager::getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) +{ + return StatusCode::SWSS_RC_UNIMPLEMENTED; +} + +void AclRuleManager::enqueue(const std::string &table_name, const swss::KeyOpFieldsValuesTuple &entry) { m_entries.push_back(entry); } diff --git a/orchagent/p4orch/acl_rule_manager.h b/orchagent/p4orch/acl_rule_manager.h index 34cb8361c0..230f226f98 100644 --- a/orchagent/p4orch/acl_rule_manager.h +++ b/orchagent/p4orch/acl_rule_manager.h @@ -41,9 +41,10 @@ class AclRuleManager : public ObjectManagerInterface } virtual ~AclRuleManager() = default; - void enqueue(const swss::KeyOpFieldsValuesTuple &entry) override; + void enqueue(const std::string &table_name, const swss::KeyOpFieldsValuesTuple &entry) override; void drain() override; std::string verifyState(const std::string &key, const std::vector &tuple) override; + ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) override; // Update counters stats for every rule in each ACL table in COUNTERS_DB, if // counters are enabled in rules. diff --git a/orchagent/p4orch/acl_table_manager.cpp b/orchagent/p4orch/acl_table_manager.cpp index 6412803c9f..c1ad1c1c05 100644 --- a/orchagent/p4orch/acl_table_manager.cpp +++ b/orchagent/p4orch/acl_table_manager.cpp @@ -205,7 +205,12 @@ ReturnCodeOr> AclTableManager::getUdfSaiAttrs(const return udf_attrs; } -void AclTableManager::enqueue(const swss::KeyOpFieldsValuesTuple &entry) +ReturnCode AclTableManager::getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) +{ + return StatusCode::SWSS_RC_UNIMPLEMENTED; +} + +void AclTableManager::enqueue(const std::string &table_name, const swss::KeyOpFieldsValuesTuple &entry) { m_entries.push_back(entry); } diff --git a/orchagent/p4orch/acl_table_manager.h b/orchagent/p4orch/acl_table_manager.h index f48d34c309..5ebaf459e9 100644 --- a/orchagent/p4orch/acl_table_manager.h +++ b/orchagent/p4orch/acl_table_manager.h @@ -31,9 +31,10 @@ class AclTableManager : public ObjectManagerInterface explicit AclTableManager(P4OidMapper *p4oidMapper, ResponsePublisherInterface *publisher); virtual ~AclTableManager(); - void enqueue(const swss::KeyOpFieldsValuesTuple &entry) override; + void enqueue(const std::string &table_name, const swss::KeyOpFieldsValuesTuple &entry) override; void drain() override; std::string verifyState(const std::string &key, const std::vector &tuple) override; + ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) override; // Get ACL table definition by table name in cache. Return nullptr if not // found. diff --git a/orchagent/p4orch/ext_tables_manager.cpp b/orchagent/p4orch/ext_tables_manager.cpp new file mode 100644 index 0000000000..f2bf20a4e0 --- /dev/null +++ b/orchagent/p4orch/ext_tables_manager.cpp @@ -0,0 +1,873 @@ +#include "p4orch/ext_tables_manager.h" + +#include +#include +#include +#include +#include +#include + +#include "directory.h" +#include "json.hpp" +#include "logger.h" +#include "tokenize.h" +#include "orch.h" +#include "p4orch/p4orch.h" +#include "p4orch/p4orch_util.h" + +extern sai_counter_api_t* sai_counter_api; +extern sai_generic_programmable_api_t *sai_generic_programmable_api; + +extern Directory gDirectory; +extern sai_object_id_t gSwitchId; +extern P4Orch *gP4Orch; + +P4ExtTableEntry *ExtTablesManager::getP4ExtTableEntry(const std::string &table_name, const std::string &key) +{ + SWSS_LOG_ENTER(); + + auto it = m_extTables.find(table_name); + if (it == m_extTables.end()) + return nullptr; + + if (it->second.find(key) == it->second.end()) + return nullptr; + + return &it->second[key]; +} + +std::string getCrossRefTableName(const std::string table_name) +{ + auto it = FixedTablesMap.find(table_name); + if (it != FixedTablesMap.end()) + { + return(it->second); + } + + return(table_name); +} + +ReturnCode ExtTablesManager::validateActionParamsCrossRef(P4ExtTableAppDbEntry &app_db_entry, ActionInfo *action) +{ + const std::string action_name = action->name; + std::unordered_map cross_ref_key_j; + ReturnCode status; + + for (auto param_defn_it = action->params.begin(); + param_defn_it != action->params.end(); param_defn_it++) + { + ActionParamInfo action_param_defn = param_defn_it->second; + if (action_param_defn.table_reference_map.empty()) + { + continue; + } + + std::string param_name = param_defn_it->first; + + auto app_db_param_it = app_db_entry.action_params[action_name].find(param_name); + if (app_db_param_it == app_db_entry.action_params[action_name].end()) + { + SWSS_LOG_ERROR("Required param not specified for action %s\n", action_name.c_str()); + return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) + << "Required param not specified for action %s " << action_name.c_str(); + } + + for (auto cross_ref_it = action_param_defn.table_reference_map.begin(); + cross_ref_it != action_param_defn.table_reference_map.end(); cross_ref_it++) + { + cross_ref_key_j[cross_ref_it->first].push_back(nlohmann::json::object_t::value_type(prependMatchField(cross_ref_it->second), app_db_param_it->second)); + } + } + + + for (auto it = cross_ref_key_j.begin(); it != cross_ref_key_j.end(); it++) + { + const std::string table_name = getCrossRefTableName(it->first); + const std::string table_key = it->second.dump(); + std::string key; + sai_object_type_t object_type; + sai_object_id_t oid; + DepObject dep_object = {}; + + if (gP4Orch->m_p4TableToManagerMap.find(table_name) != gP4Orch->m_p4TableToManagerMap.end()) + { + status = gP4Orch->m_p4TableToManagerMap[table_name]->getSaiObject(table_key, object_type, key); + if (!status.ok()) + { + SWSS_LOG_ERROR("Cross-table reference validation failed from fixed-table %s", table_name.c_str()); + return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) + << "Cross-table reference valdiation failed from fixed-table"; + } + } + else + { + if (getTableInfo(table_name)) + { + auto ext_table_key = KeyGenerator::generateExtTableKey(table_name, table_key); + status = getSaiObject(ext_table_key, object_type, key); + if (!status.ok()) + { + SWSS_LOG_ERROR("Cross-table reference validation failed from extension-table %s", table_name.c_str()); + return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) + << "Cross-table reference valdiation failed from extension table"; + } + } + else + { + SWSS_LOG_ERROR("Cross-table reference validation failed due to non-existent table %s", table_name.c_str()); + return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) + << "Cross-table reference valdiation failed due to non-existent table"; + } + } + + if (!m_p4OidMapper->getOID(object_type, key, &oid)) + { + SWSS_LOG_ERROR("Cross-table reference validation failed, no OID found from table %s", table_name.c_str()); + return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) + << "Cross-table reference valdiation failed, no OID found"; + } + + if (oid == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_ERROR("Cross-table reference validation failed, null OID expected from table %s", table_name.c_str()); + return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) + << "Cross-table reference valdiation failed, null OID"; + } + + dep_object.sai_object = object_type; + dep_object.key = key; + dep_object.oid = oid; + app_db_entry.action_dep_objects[action_name] = dep_object; + } + + return ReturnCode(); +} + +ReturnCode ExtTablesManager::validateP4ExtTableAppDbEntry(P4ExtTableAppDbEntry &app_db_entry) +{ + // Perform generic APP DB entry validations. Operation specific validations + // will be done by the respective request process methods. + ReturnCode status; + + TableInfo *table; + table = getTableInfo(app_db_entry.table_name); + if (table == nullptr) + { + SWSS_LOG_ERROR("Not a valid extension table %s", app_db_entry.table_name.c_str()); + return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) + << "Not a valid extension table " << app_db_entry.table_name.c_str(); + } + + if (table->action_ref_tables.empty()) + { + return ReturnCode(); + } + + ActionInfo *action; + for (auto app_db_action_it = app_db_entry.action_params.begin(); + app_db_action_it != app_db_entry.action_params.end(); app_db_action_it++) + { + auto action_name = app_db_action_it->first; + action = getTableActionInfo(table, action_name); + if (action == nullptr) + { + return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) + << "Not a valid action " << action_name.c_str() + << " in extension table " << app_db_entry.table_name.c_str(); + } + + if (!action->refers_to) + { + continue; + } + + status = validateActionParamsCrossRef(app_db_entry, action); + if (!status.ok()) + { + return status; + } + } + + return ReturnCode(); +} + + +ReturnCodeOr ExtTablesManager::deserializeP4ExtTableEntry( + const std::string &table_name, + const std::string &key, const std::vector &attributes) +{ + std::string action_name; + + SWSS_LOG_ENTER(); + + P4ExtTableAppDbEntry app_db_entry_or = {}; + app_db_entry_or.table_name = table_name; + app_db_entry_or.table_key = key; + + action_name = ""; + for (const auto &it : attributes) + { + auto field = fvField(it); + auto value = fvValue(it); + + if (field == p4orch::kAction) + { + action_name = value; + continue; + } + + const auto &tokenized_fields = tokenize(field, p4orch::kFieldDelimiter); + if (tokenized_fields.size() <= 1) + { + SWSS_LOG_ERROR("Unknown extension entry field"); + return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) + << "Unknown extension entry field " << QuotedVar(field); + } + + const auto &prefix = tokenized_fields[0]; + if (prefix == p4orch::kActionParamPrefix) + { + const auto ¶m_name = tokenized_fields[1]; + app_db_entry_or.action_params[action_name][param_name] = value; + continue; + } + else + { + SWSS_LOG_ERROR("Unexpected extension entry field"); + return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) + << "Unexpected extension entry field " << QuotedVar(field); + } + } + + return app_db_entry_or; +} + + +ReturnCode ExtTablesManager::prepareP4SaiExtAPIParams(const P4ExtTableAppDbEntry &app_db_entry, + std::string &ext_table_entry_attr) +{ + nlohmann::json sai_j, sai_metadata_j, sai_array_j = {}, sai_entry_j; + + SWSS_LOG_ENTER(); + + try + { + TableInfo *table; + table = getTableInfo(app_db_entry.table_name); + if (!table) + { + SWSS_LOG_ERROR("extension entry for invalid table %s", app_db_entry.table_name.c_str()); + return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) + << "extension entry for invalid table " << app_db_entry.table_name.c_str(); + } + + nlohmann::json j = nlohmann::json::parse(app_db_entry.table_key); + for (auto it = j.begin(); it != j.end(); ++it) + { + std::string match, value, prefix; + std::size_t pos; + + match = it.key(); + value = it.value(); + + prefix = p4orch::kMatchPrefix; + pos = match.rfind(prefix); + if (pos != std::string::npos) + { + match.erase(0, prefix.length()); + } + else + { + SWSS_LOG_ERROR("Failed to encode match fields for sai call"); + return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) << "Failed to encode match fields for sai call"; + } + + prefix = p4orch::kFieldDelimiter; + pos = match.rfind(prefix); + if (pos != std::string::npos) + { + match.erase(0, prefix.length()); + } + else + { + SWSS_LOG_ERROR("Failed to encode match fields for sai call"); + return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) << "Failed to encode match fields for sai call"; + } + + auto match_defn_it = table->match_fields.find(match); + if (match_defn_it == table->match_fields.end()) + { + SWSS_LOG_ERROR("extension entry for invalid match field %s", match.c_str()); + return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) + << "extension entry for invalid match field " << match.c_str(); + } + + sai_metadata_j = nlohmann::json::object({}); + sai_metadata_j["sai_attr_value_type"] = match_defn_it->second.datatype; + + sai_j = nlohmann::json::object({}); + sai_j[match]["value"] = value; + sai_j[match]["sai_metadata"] = sai_metadata_j; + + sai_array_j.push_back(sai_j); + } + + for (auto app_db_action_it = app_db_entry.action_params.begin(); + app_db_action_it != app_db_entry.action_params.end(); app_db_action_it++) + { + sai_j = nlohmann::json::object({}); + auto action_dep_object_it = app_db_entry.action_dep_objects.find(app_db_action_it->first); + if (action_dep_object_it == app_db_entry.action_dep_objects.end()) + { + auto action_defn_it = table->action_fields.find(app_db_action_it->first); + for (auto app_db_param_it = app_db_action_it->second.begin(); + app_db_param_it != app_db_action_it->second.end(); app_db_param_it++) + { + nlohmann::json params_j = nlohmann::json::object({}); + if (action_defn_it != table->action_fields.end()) + { + auto param_defn_it = action_defn_it->second.params.find(app_db_param_it->first); + if (param_defn_it != action_defn_it->second.params.end()) + { + sai_metadata_j = nlohmann::json::object({}); + sai_metadata_j["sai_attr_value_type"] = param_defn_it->second.datatype; + + params_j[app_db_param_it->first]["sai_metadata"] = sai_metadata_j; + } + } + params_j[app_db_param_it->first]["value"] = app_db_param_it->second; + sai_j[app_db_action_it->first].push_back(params_j); + } + } + else + { + auto action_dep_object = action_dep_object_it->second; + + sai_metadata_j = nlohmann::json::object({}); + sai_metadata_j["sai_attr_value_type"] = "SAI_ATTR_VALUE_TYPE_OBJECT_ID"; + + sai_j[app_db_action_it->first]["sai_metadata"] = sai_metadata_j; + sai_j[app_db_action_it->first]["value"] = action_dep_object.oid; + } + + sai_array_j.push_back(sai_j); + } + } + catch (std::exception &ex) + { + SWSS_LOG_ERROR("Failed to encode table %s entry for sai call", app_db_entry.table_name.c_str()); + return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) << "Failed to encode table entry for sai call"; + } + + sai_entry_j = nlohmann::json::object({}); + sai_entry_j.push_back(nlohmann::json::object_t::value_type("attributes", sai_array_j)); + SWSS_LOG_ERROR("table: %s, sai entry: %s", app_db_entry.table_name.c_str(), sai_entry_j.dump().c_str()); + ext_table_entry_attr = sai_entry_j.dump(); + + return ReturnCode(); +} + +bool removeGenericCounter(sai_object_id_t counter_id) +{ + sai_status_t sai_status = sai_counter_api->remove_counter(counter_id); + if (sai_status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove generic counter: %" PRId64 "", counter_id); + return false; + } + + return true; +} + +bool createGenericCounter(sai_object_id_t &counter_id) +{ + sai_attribute_t counter_attr; + counter_attr.id = SAI_COUNTER_ATTR_TYPE; + counter_attr.value.s32 = SAI_COUNTER_TYPE_REGULAR; + sai_status_t sai_status = sai_counter_api->create_counter(&counter_id, gSwitchId, 1, &counter_attr); + if (sai_status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_WARN("Failed to create generic counter"); + return false; + } + + return true; +} + + +ReturnCode ExtTablesManager::createP4ExtTableEntry(const P4ExtTableAppDbEntry &app_db_entry, + P4ExtTableEntry &ext_table_entry) +{ + ReturnCode status; + sai_object_type_t object_type; + std::string key; + std::string ext_table_entry_attr; + sai_object_id_t counter_id; + + SWSS_LOG_ENTER(); + + status = prepareP4SaiExtAPIParams(app_db_entry, ext_table_entry_attr); + if (!status.ok()) + { + return status; + } + + // Prepare attributes for the SAI create call. + std::vector generic_programmable_attrs; + sai_attribute_t generic_programmable_attr; + + generic_programmable_attr.id = SAI_GENERIC_PROGRAMMABLE_ATTR_OBJECT_NAME; + generic_programmable_attr.value.s8list.count = (uint32_t)app_db_entry.table_name.size(); + generic_programmable_attr.value.s8list.list = (int8_t *)const_cast(app_db_entry.table_name.c_str()); + generic_programmable_attrs.push_back(generic_programmable_attr); + + generic_programmable_attr.id = SAI_GENERIC_PROGRAMMABLE_ATTR_ENTRY; + generic_programmable_attr.value.json.json.count = (uint32_t)ext_table_entry_attr.size(); + generic_programmable_attr.value.json.json.list = (int8_t *)const_cast(ext_table_entry_attr.c_str()); + generic_programmable_attrs.push_back(generic_programmable_attr); + + + auto *table = getTableInfo(app_db_entry.table_name); + if (!table) + { + SWSS_LOG_ERROR("extension entry for invalid table %s", app_db_entry.table_name.c_str()); + return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) + << "extension entry for invalid table " << app_db_entry.table_name.c_str(); + } + + if (table->counter_bytes_enabled || table->counter_packets_enabled) + { + if (!createGenericCounter(counter_id)) + { + SWSS_LOG_WARN("Failed to create counter for table %s, key %s\n", + app_db_entry.table_name.c_str(), + app_db_entry.table_key.c_str()); + } + else + { + ext_table_entry.sai_counter_oid = counter_id; + } + + generic_programmable_attr.id = SAI_GENERIC_PROGRAMMABLE_ATTR_COUNTER_ID; + generic_programmable_attr.value.oid = counter_id; + generic_programmable_attrs.push_back(generic_programmable_attr); + } + + sai_object_id_t sai_generic_programmable_oid = SAI_NULL_OBJECT_ID; + sai_status_t sai_status = sai_generic_programmable_api->create_generic_programmable( + &sai_generic_programmable_oid, gSwitchId, + (uint32_t)generic_programmable_attrs.size(), + generic_programmable_attrs.data()); + if (sai_status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("create sai api call failed for extension entry table %s, entry %s", + app_db_entry.table_name.c_str(), app_db_entry.table_key.c_str()); + return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) + << "create sai api call failed for extension entry table " + << app_db_entry.table_name.c_str() + << " , entry " << app_db_entry.table_key.c_str(); + } + + + ext_table_entry.sai_entry_oid = sai_generic_programmable_oid; + for (auto action_dep_object_it = app_db_entry.action_dep_objects.begin(); + action_dep_object_it != app_db_entry.action_dep_objects.end(); action_dep_object_it++) + { + auto action_dep_object = action_dep_object_it->second; + m_p4OidMapper->increaseRefCount(action_dep_object.sai_object, action_dep_object.key); + ext_table_entry.action_dep_objects[action_dep_object_it->first] = action_dep_object; + } + + + auto ext_table_key = KeyGenerator::generateExtTableKey(app_db_entry.table_name, app_db_entry.table_key); + status = getSaiObject(ext_table_key, object_type, key); + if (!status.ok()) + { + SWSS_LOG_ERROR("Invalid formation of a key %s", ext_table_key.c_str()); + return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) << "Invalid formation of a key"; + } + + m_p4OidMapper->setOID(object_type, key, ext_table_entry.sai_entry_oid); + m_extTables[app_db_entry.table_name][app_db_entry.table_key] = ext_table_entry; + return ReturnCode(); +} + + +ReturnCode ExtTablesManager::updateP4ExtTableEntry(const P4ExtTableAppDbEntry &app_db_entry, + P4ExtTableEntry *ext_table_entry) +{ + ReturnCode status; + std::string ext_table_entry_attr; + std::unordered_map old_action_dep_objects; + + SWSS_LOG_ENTER(); + + if (ext_table_entry->sai_entry_oid == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_ERROR("update sai api call for NULL extension entry table %s, entry %s", + app_db_entry.table_name.c_str(), ext_table_entry->table_key.c_str()); + return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) + << "update sai api call for NULL extension entry table " + << app_db_entry.table_name.c_str() + << " , entry " << ext_table_entry->table_key.c_str(); + } + + status = prepareP4SaiExtAPIParams(app_db_entry, ext_table_entry_attr); + if (!status.ok()) + { + return status; + } + + // Prepare attribute for the SAI update call. + sai_attribute_t generic_programmable_attr; + + generic_programmable_attr.id = SAI_GENERIC_PROGRAMMABLE_ATTR_ENTRY; + generic_programmable_attr.value.json.json.count = (uint32_t)ext_table_entry_attr.length(); + generic_programmable_attr.value.json.json.list = (int8_t *)const_cast(ext_table_entry_attr.c_str()); + + sai_status_t sai_status = sai_generic_programmable_api->set_generic_programmable_attribute( + ext_table_entry->sai_entry_oid, + &generic_programmable_attr); + if (sai_status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("update sai api call failed for extension entry table %s, entry %s", + app_db_entry.table_name.c_str(), ext_table_entry->table_key.c_str()); + return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) + << "update sai api call failed for extension entry table " + << app_db_entry.table_name.c_str() + << " , entry " << ext_table_entry->table_key.c_str(); + } + + + old_action_dep_objects = ext_table_entry->action_dep_objects; + ext_table_entry->action_dep_objects.clear(); + + for (auto action_dep_object_it = app_db_entry.action_dep_objects.begin(); + action_dep_object_it != app_db_entry.action_dep_objects.end(); action_dep_object_it++) + { + auto action_dep_object = action_dep_object_it->second; + m_p4OidMapper->increaseRefCount(action_dep_object.sai_object, action_dep_object.key); + ext_table_entry->action_dep_objects[action_dep_object_it->first] = action_dep_object; + } + + for (auto old_action_dep_object_it = old_action_dep_objects.begin(); + old_action_dep_object_it != old_action_dep_objects.end(); old_action_dep_object_it++) + { + auto old_action_dep_object = old_action_dep_object_it->second; + m_p4OidMapper->decreaseRefCount(old_action_dep_object.sai_object, old_action_dep_object.key); + } + + return ReturnCode(); +} + +ReturnCode ExtTablesManager::removeP4ExtTableEntry(const std::string &table_name, + const std::string &table_key) +{ + ReturnCode status; + sai_object_type_t object_type; + std::string key; + + SWSS_LOG_ENTER(); + + auto *ext_table_entry = getP4ExtTableEntry(table_name, table_key); + if (!ext_table_entry) + { + LOG_ERROR_AND_RETURN(ReturnCode(StatusCode::SWSS_RC_NOT_FOUND) + << "extension entry with key " << QuotedVar(table_key) + << " does not exist for table " << QuotedVar(table_name)); + } + + if (ext_table_entry->sai_entry_oid == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_ERROR("remove sai api call for NULL extension entry table %s, entry %s", + table_name.c_str(), table_key.c_str()); + return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) + << "remove sai api call for NULL extension entry table " + << table_name.c_str() << " , entry " << table_key.c_str(); + } + + SWSS_LOG_ERROR("table: %s, key: %s", ext_table_entry->table_name.c_str(), + ext_table_entry->table_key.c_str()); + sai_status_t sai_status = sai_generic_programmable_api->remove_generic_programmable( + ext_table_entry->sai_entry_oid); + if (sai_status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("remove sai api call failed for extension entry table %s, entry %s", + table_name.c_str(), table_key.c_str()); + return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) + << "remove sai api call failed for extension entry table " + << table_name.c_str() << " , entry " << table_key.c_str(); + } + + + auto ext_table_key = KeyGenerator::generateExtTableKey(table_name, table_key); + status = getSaiObject(ext_table_key, object_type, key); + if (!status.ok()) + { + SWSS_LOG_ERROR("Invalid formation of a key %s", ext_table_key.c_str()); + return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) << "Invalid formation of a key"; + } + + uint32_t ref_count; + if (!m_p4OidMapper->getRefCount(object_type, key, &ref_count)) + { + RETURN_INTERNAL_ERROR_AND_RAISE_CRITICAL("Failed to get reference count for " << QuotedVar(key)); + } + if (ref_count > 0) + { + LOG_ERROR_AND_RETURN(ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) + << "extension entry " << QuotedVar(key) + << " referenced by other objects (ref_count = " << ref_count); + } + m_p4OidMapper->eraseOID(object_type, key); + + for (auto action_dep_object_it = ext_table_entry->action_dep_objects.begin(); + action_dep_object_it != ext_table_entry->action_dep_objects.end(); action_dep_object_it++) + { + auto action_dep_object = action_dep_object_it->second; + m_p4OidMapper->decreaseRefCount(action_dep_object.sai_object, action_dep_object.key); + } + + if (ext_table_entry->sai_counter_oid != SAI_NULL_OBJECT_ID) + { + m_countersTable->del(ext_table_entry->db_key); + removeGenericCounter(ext_table_entry->sai_counter_oid); + } + + m_extTables[table_name].erase(table_key); + + return ReturnCode(); +} + + +ReturnCode ExtTablesManager::processAddRequest(const P4ExtTableAppDbEntry &app_db_entry) +{ + SWSS_LOG_ENTER(); + + P4ExtTableEntry ext_table_entry(app_db_entry.db_key, app_db_entry.table_name, app_db_entry.table_key); + auto status = createP4ExtTableEntry(app_db_entry, ext_table_entry); + if (!status.ok()) + { + return status; + } + return ReturnCode(); +} + +ReturnCode ExtTablesManager::processUpdateRequest(const P4ExtTableAppDbEntry &app_db_entry, + P4ExtTableEntry *ext_table_entry) +{ + SWSS_LOG_ENTER(); + + auto status = updateP4ExtTableEntry(app_db_entry, ext_table_entry); + if (!status.ok()) + { + SWSS_LOG_ERROR("Failed to update extension entry with key %s", + app_db_entry.table_key.c_str()); + } + return ReturnCode(); +} + +ReturnCode ExtTablesManager::processDeleteRequest(const P4ExtTableAppDbEntry &app_db_entry) +{ + SWSS_LOG_ENTER(); + + auto status = removeP4ExtTableEntry(app_db_entry.table_name, app_db_entry.table_key); + if (!status.ok()) + { + SWSS_LOG_ERROR("Failed to remove extension entry with key %s", + app_db_entry.table_key.c_str()); + } + return ReturnCode(); +} + + +ReturnCode ExtTablesManager::getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) +{ + object_type = SAI_OBJECT_TYPE_GENERIC_PROGRAMMABLE; + object_key = json_key; + + return ReturnCode(); +} + +void ExtTablesManager::enqueue(const std::string &table_name, const swss::KeyOpFieldsValuesTuple &entry) +{ + m_entriesTables[table_name].push_back(entry); +} + +void ExtTablesManager::drain() +{ + SWSS_LOG_ENTER(); + std::string table_prefix = "EXT_"; + + if (gP4Orch->tablesinfo) { + for (auto table_it = gP4Orch->tablesinfo->m_tablePrecedenceMap.begin(); + table_it != gP4Orch->tablesinfo->m_tablePrecedenceMap.end(); ++table_it) + { + auto table_name = table_prefix + table_it->second; + boost::algorithm::to_upper(table_name); + auto it_m = m_entriesTables.find(table_name); + if (it_m == m_entriesTables.end()) + { + continue; + } + + for (const auto &key_op_fvs_tuple : it_m->second) + { + std::string table_name; + std::string table_key; + + parseP4RTKey(kfvKey(key_op_fvs_tuple), &table_name, &table_key); + const std::vector &attributes = kfvFieldsValues(key_op_fvs_tuple); + + if (table_name.rfind(table_prefix, 0) == std::string::npos) + { + SWSS_LOG_ERROR("Table %s is without prefix %s", table_name.c_str(), table_prefix.c_str()); + m_publisher->publish(APP_P4RT_TABLE_NAME, kfvKey(key_op_fvs_tuple), kfvFieldsValues(key_op_fvs_tuple), + StatusCode::SWSS_RC_INVALID_PARAM, /*replace=*/true); + continue; + } + table_name = table_name.substr(table_prefix.length()); + boost::algorithm::to_lower(table_name); + + ReturnCode status; + auto app_db_entry_or = deserializeP4ExtTableEntry(table_name, table_key, attributes); + if (!app_db_entry_or.ok()) + { + status = app_db_entry_or.status(); + SWSS_LOG_ERROR("Unable to deserialize APP DB entry with key %s: %s", + QuotedVar(kfvKey(key_op_fvs_tuple)).c_str(), status.message().c_str()); + m_publisher->publish(APP_P4RT_TABLE_NAME, kfvKey(key_op_fvs_tuple), kfvFieldsValues(key_op_fvs_tuple), + status, /*replace=*/true); + continue; + } + + auto &app_db_entry = *app_db_entry_or; + status = validateP4ExtTableAppDbEntry(app_db_entry); + if (!status.ok()) + { + SWSS_LOG_ERROR("Validation failed for extension APP DB entry with key %s: %s", + QuotedVar(kfvKey(key_op_fvs_tuple)).c_str(), status.message().c_str()); + m_publisher->publish(APP_P4RT_TABLE_NAME, kfvKey(key_op_fvs_tuple), kfvFieldsValues(key_op_fvs_tuple), + status, /*replace=*/true); + continue; + } + + const std::string &operation = kfvOp(key_op_fvs_tuple); + if (operation == SET_COMMAND) + { + auto *ext_table_entry = getP4ExtTableEntry(app_db_entry.table_name, app_db_entry.table_key); + if (ext_table_entry == nullptr) + { + // Create extension entry + app_db_entry.db_key = kfvKey(key_op_fvs_tuple); + status = processAddRequest(app_db_entry); + } + else + { + // Modify existing extension entry + status = processUpdateRequest(app_db_entry, ext_table_entry); + } + } + else if (operation == DEL_COMMAND) + { + // Delete extension entry + status = processDeleteRequest(app_db_entry); + } + else + { + status = ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) + << "Unknown operation type " << QuotedVar(operation); + SWSS_LOG_ERROR("%s", status.message().c_str()); + } + if (!status.ok()) + { + SWSS_LOG_ERROR("Processing failed for extension APP_DB entry with key %s: %s", + QuotedVar(kfvKey(key_op_fvs_tuple)).c_str(), status.message().c_str()); + } + m_publisher->publish(APP_P4RT_TABLE_NAME, kfvKey(key_op_fvs_tuple), kfvFieldsValues(key_op_fvs_tuple), + status, /*replace=*/true); + } + + it_m->second.clear(); + } + } + + // Now report error for all remaining un-processed entries + for (auto it_m = m_entriesTables.begin(); it_m != m_entriesTables.end(); it_m++) + { + for (const auto &key_op_fvs_tuple : it_m->second) + { + m_publisher->publish(APP_P4RT_TABLE_NAME, kfvKey(key_op_fvs_tuple), kfvFieldsValues(key_op_fvs_tuple), + StatusCode::SWSS_RC_INVALID_PARAM, /*replace=*/true); + } + + it_m->second.clear(); + } +} + + +void ExtTablesManager::doExtCounterStatsTask() +{ + SWSS_LOG_ENTER(); + + if (!gP4Orch->tablesinfo) + { + return; + } + + sai_stat_id_t stat_ids[] = { SAI_COUNTER_STAT_PACKETS, SAI_COUNTER_STAT_BYTES }; + uint64_t stats[2]; + std::vector counter_stats_values; + + for (auto table_it = gP4Orch->tablesinfo->m_tableInfoMap.begin(); + table_it != gP4Orch->tablesinfo->m_tableInfoMap.end(); ++table_it) + { + if (!table_it->second.counter_bytes_enabled && !table_it->second.counter_packets_enabled) + { + continue; + } + + auto table_name = table_it->second.name; + auto ext_table_it = m_extTables.find(table_name); + if (ext_table_it == m_extTables.end()) + { + continue; + } + + for (auto ext_table_entry_it = ext_table_it->second.begin(); + ext_table_entry_it != ext_table_it->second.end(); ++ext_table_entry_it) + { + auto *ext_table_entry = &ext_table_entry_it->second; + if (ext_table_entry->sai_counter_oid == SAI_NULL_OBJECT_ID) + { + continue; + } + + sai_status_t sai_status = + sai_counter_api->get_counter_stats(ext_table_entry->sai_counter_oid, 2, stat_ids, stats); + if (sai_status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_WARN("Failed to set counters stats for extension entry %s:%s in COUNTERS_DB: ", + table_name.c_str(), ext_table_entry->table_key.c_str()); + continue; + } + + counter_stats_values.push_back( + swss::FieldValueTuple{P4_COUNTER_STATS_PACKETS, std::to_string(stats[0])}); + counter_stats_values.push_back( + swss::FieldValueTuple{P4_COUNTER_STATS_BYTES, std::to_string(stats[1])}); + + // Set field value tuples for counters stats in COUNTERS_DB + m_countersTable->set(ext_table_entry->db_key, counter_stats_values); + } + } +} + +std::string ExtTablesManager::verifyState(const std::string &key, const std::vector &tuple) +{ + std::string result = ""; + SWSS_LOG_ENTER(); + + return result; +} + diff --git a/orchagent/p4orch/ext_tables_manager.h b/orchagent/p4orch/ext_tables_manager.h new file mode 100644 index 0000000000..d9ac44858f --- /dev/null +++ b/orchagent/p4orch/ext_tables_manager.h @@ -0,0 +1,96 @@ +#pragma once + +#include +#include +#include +#include + +#include "macaddress.h" +#include "json.hpp" +#include "orch.h" +#include "p4orch/object_manager_interface.h" +#include "p4orch/p4oidmapper.h" +#include "p4orch/p4orch_util.h" +#include "p4orch/tables_definition_manager.h" +#include "response_publisher_interface.h" +#include "return_code.h" +#include "vrforch.h" +extern "C" +{ +#include "sai.h" +} + +struct P4ExtTableEntry +{ + std::string db_key; + std::string table_name; + std::string table_key; + sai_object_id_t sai_entry_oid = SAI_NULL_OBJECT_ID; + sai_object_id_t sai_counter_oid = SAI_NULL_OBJECT_ID; + std::unordered_map action_dep_objects; + + P4ExtTableEntry() {}; + P4ExtTableEntry(const std::string &db_key, const std::string &table_name, const std::string &table_key) + : db_key(db_key), table_name(table_name), table_key(table_key) + { + } +}; + +typedef std::unordered_map P4ExtTableEntryMap; +typedef std::unordered_map P4ExtTableMap; +typedef std::unordered_map> m_entriesTableMap; + +class ExtTablesManager : public ObjectManagerInterface +{ + public: + ExtTablesManager(P4OidMapper *p4oidMapper, VRFOrch *vrfOrch, ResponsePublisherInterface *publisher) + : m_vrfOrch(vrfOrch), + m_countersDb(std::make_unique("COUNTERS_DB", 0)), + m_countersTable(std::make_unique( + m_countersDb.get(), std::string(COUNTERS_TABLE) + DEFAULT_KEY_SEPARATOR + APP_P4RT_TABLE_NAME)) + { + SWSS_LOG_ENTER(); + + assert(p4oidMapper != nullptr); + m_p4OidMapper = p4oidMapper; + assert(publisher != nullptr); + m_publisher = publisher; + } + virtual ~ExtTablesManager() = default; + + void enqueue(const std::string &table_name, const swss::KeyOpFieldsValuesTuple &entry) override; + void drain() override; + std::string verifyState(const std::string &key, const std::vector &tuple) override; + ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) override; + + // For every extension entry, update counters stats in COUNTERS_DB, if + // counters are enabled for those entries + void doExtCounterStatsTask(); + + private: + ReturnCodeOr deserializeP4ExtTableEntry( + const std::string &table_name, + const std::string &key, const std::vector &attributes); + ReturnCode validateActionParamsCrossRef(P4ExtTableAppDbEntry &app_db_entry, ActionInfo *action); + ReturnCode validateP4ExtTableAppDbEntry(P4ExtTableAppDbEntry &app_db_entry); + P4ExtTableEntry *getP4ExtTableEntry(const std::string &table_name, const std::string &table_key); + ReturnCode prepareP4SaiExtAPIParams(const P4ExtTableAppDbEntry &app_db_entry, + std::string &ext_table_entry_attr); + ReturnCode createP4ExtTableEntry(const P4ExtTableAppDbEntry &app_db_entry, P4ExtTableEntry &ext_table_entry); + ReturnCode updateP4ExtTableEntry(const P4ExtTableAppDbEntry &app_db_entry, P4ExtTableEntry *ext_table_entry); + ReturnCode removeP4ExtTableEntry(const std::string &table_name, const std::string &table_key); + ReturnCode processAddRequest(const P4ExtTableAppDbEntry &app_db_entry); + ReturnCode processUpdateRequest(const P4ExtTableAppDbEntry &app_db_entry, P4ExtTableEntry *ext_table_entry); + ReturnCode processDeleteRequest(const P4ExtTableAppDbEntry &app_db_entry); + + ReturnCode setExtTableCounterStats(P4ExtTableEntry *ext_table_entry); + + P4ExtTableMap m_extTables; + P4OidMapper *m_p4OidMapper; + VRFOrch *m_vrfOrch; + ResponsePublisherInterface *m_publisher; + m_entriesTableMap m_entriesTables; + + std::unique_ptr m_countersDb; + std::unique_ptr m_countersTable; +}; diff --git a/orchagent/p4orch/gre_tunnel_manager.cpp b/orchagent/p4orch/gre_tunnel_manager.cpp index f982499504..ddadf8ddcd 100644 --- a/orchagent/p4orch/gre_tunnel_manager.cpp +++ b/orchagent/p4orch/gre_tunnel_manager.cpp @@ -98,7 +98,12 @@ P4GreTunnelEntry::P4GreTunnelEntry(const std::string &tunnel_id, const std::stri tunnel_key = KeyGenerator::generateTunnelKey(tunnel_id); } -void GreTunnelManager::enqueue(const swss::KeyOpFieldsValuesTuple &entry) +ReturnCode GreTunnelManager::getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) +{ + return StatusCode::SWSS_RC_UNIMPLEMENTED; +} + +void GreTunnelManager::enqueue(const std::string &table_name, const swss::KeyOpFieldsValuesTuple &entry) { m_entries.push_back(entry); } diff --git a/orchagent/p4orch/gre_tunnel_manager.h b/orchagent/p4orch/gre_tunnel_manager.h index c1f17f1024..d5cb32e9bf 100644 --- a/orchagent/p4orch/gre_tunnel_manager.h +++ b/orchagent/p4orch/gre_tunnel_manager.h @@ -69,9 +69,10 @@ class GreTunnelManager : public ObjectManagerInterface virtual ~GreTunnelManager() = default; - void enqueue(const swss::KeyOpFieldsValuesTuple &entry) override; + void enqueue(const std::string &table_name, const swss::KeyOpFieldsValuesTuple &entry) override; void drain() override; std::string verifyState(const std::string &key, const std::vector &tuple) override; + ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) override; ReturnCodeOr getConstGreTunnelEntry(const std::string &gre_tunnel_key); diff --git a/orchagent/p4orch/l3_admit_manager.cpp b/orchagent/p4orch/l3_admit_manager.cpp index 75d4d6f7d2..8f17165df2 100644 --- a/orchagent/p4orch/l3_admit_manager.cpp +++ b/orchagent/p4orch/l3_admit_manager.cpp @@ -64,7 +64,12 @@ ReturnCodeOr> getSaiAttrs(const P4L3AdmitEntry &l3_ } // namespace -void L3AdmitManager::enqueue(const swss::KeyOpFieldsValuesTuple &entry) +ReturnCode L3AdmitManager::getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) +{ + return StatusCode::SWSS_RC_UNIMPLEMENTED; +} + +void L3AdmitManager::enqueue(const std::string &table_name, const swss::KeyOpFieldsValuesTuple &entry) { m_entries.push_back(entry); } diff --git a/orchagent/p4orch/l3_admit_manager.h b/orchagent/p4orch/l3_admit_manager.h index 933f5792c8..d378775c4f 100644 --- a/orchagent/p4orch/l3_admit_manager.h +++ b/orchagent/p4orch/l3_admit_manager.h @@ -60,9 +60,10 @@ class L3AdmitManager : public ObjectManagerInterface virtual ~L3AdmitManager() = default; - void enqueue(const swss::KeyOpFieldsValuesTuple &entry) override; + void enqueue(const std::string &table_name, const swss::KeyOpFieldsValuesTuple &entry) override; void drain() override; std::string verifyState(const std::string &key, const std::vector &tuple) override; + ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) override; private: // Gets the internal cached next hop entry by its key. @@ -95,4 +96,4 @@ class L3AdmitManager : public ObjectManagerInterface P4OidMapper *m_p4OidMapper; friend class L3AdmitManagerTest; -}; \ No newline at end of file +}; diff --git a/orchagent/p4orch/mirror_session_manager.cpp b/orchagent/p4orch/mirror_session_manager.cpp index dfecb74ad7..f61b3d4be5 100644 --- a/orchagent/p4orch/mirror_session_manager.cpp +++ b/orchagent/p4orch/mirror_session_manager.cpp @@ -21,7 +21,34 @@ extern sai_object_id_t gSwitchId; namespace p4orch { -void MirrorSessionManager::enqueue(const swss::KeyOpFieldsValuesTuple &entry) +ReturnCode MirrorSessionManager::getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) +{ + std::string value; + + try + { + nlohmann::json j = nlohmann::json::parse(json_key); + if (j.find(prependMatchField(p4orch::kMirrorSessionId)) != j.end()) + { + value = j.at(prependMatchField(p4orch::kMirrorSessionId)).get(); + object_key = KeyGenerator::generateMirrorSessionKey(value); + object_type = SAI_OBJECT_TYPE_MIRROR_SESSION; + return ReturnCode(); + } + else + { + SWSS_LOG_ERROR("%s match parameter absent: required for dependent object query", p4orch::kMirrorSessionId); + } + } + catch (std::exception &ex) + { + SWSS_LOG_ERROR("json_key parse error"); + } + + return StatusCode::SWSS_RC_INVALID_PARAM; +} + +void MirrorSessionManager::enqueue(const std::string &table_name, const swss::KeyOpFieldsValuesTuple &entry) { SWSS_LOG_ENTER(); m_entries.push_back(entry); diff --git a/orchagent/p4orch/mirror_session_manager.h b/orchagent/p4orch/mirror_session_manager.h index 3cbd46ee15..5f1c26e10a 100644 --- a/orchagent/p4orch/mirror_session_manager.h +++ b/orchagent/p4orch/mirror_session_manager.h @@ -81,12 +81,14 @@ class MirrorSessionManager : public ObjectManagerInterface m_publisher = publisher; } - void enqueue(const swss::KeyOpFieldsValuesTuple &entry) override; + void enqueue(const std::string &table_name, const swss::KeyOpFieldsValuesTuple &entry) override; void drain() override; std::string verifyState(const std::string &key, const std::vector &tuple) override; + ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) override; + private: ReturnCodeOr deserializeP4MirrorSessionAppDbEntry( const std::string &key, const std::vector &attributes); diff --git a/orchagent/p4orch/neighbor_manager.cpp b/orchagent/p4orch/neighbor_manager.cpp index 9a903baa6a..06c1ee9eb1 100644 --- a/orchagent/p4orch/neighbor_manager.cpp +++ b/orchagent/p4orch/neighbor_manager.cpp @@ -324,7 +324,44 @@ ReturnCode NeighborManager::processDeleteRequest(const std::string &neighbor_key return status; } -void NeighborManager::enqueue(const swss::KeyOpFieldsValuesTuple &entry) +ReturnCode NeighborManager::getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) +{ + std::string router_intf_id, neighbor_id; + swss::IpAddress neighbor; + + try + { + nlohmann::json j = nlohmann::json::parse(json_key); + if (j.find(prependMatchField(p4orch::kRouterInterfaceId)) != j.end()) + { + router_intf_id = j.at(prependMatchField(p4orch::kRouterInterfaceId)).get(); + if (j.find(prependMatchField(p4orch::kNeighborId)) != j.end()) + { + neighbor_id = j.at(prependMatchField(p4orch::kNeighborId)).get(); + neighbor = swss::IpAddress(neighbor_id); + object_key = KeyGenerator::generateNeighborKey(router_intf_id, neighbor); + object_type = SAI_OBJECT_TYPE_NEIGHBOR_ENTRY; + return ReturnCode(); + } + else + { + SWSS_LOG_ERROR("%s match parameter absent: required for dependent object query", p4orch::kNeighborId); + } + } + else + { + SWSS_LOG_ERROR("%s match parameter absent: required for dependent object query", p4orch::kRouterInterfaceId); + } + } + catch (std::exception &ex) + { + SWSS_LOG_ERROR("json_key parse error"); + } + + return StatusCode::SWSS_RC_INVALID_PARAM; +} + +void NeighborManager::enqueue(const std::string &table_name, const swss::KeyOpFieldsValuesTuple &entry) { m_entries.push_back(entry); } diff --git a/orchagent/p4orch/neighbor_manager.h b/orchagent/p4orch/neighbor_manager.h index 4165bb90ed..0022d3a8cc 100644 --- a/orchagent/p4orch/neighbor_manager.h +++ b/orchagent/p4orch/neighbor_manager.h @@ -49,9 +49,10 @@ class NeighborManager : public ObjectManagerInterface } virtual ~NeighborManager() = default; - void enqueue(const swss::KeyOpFieldsValuesTuple &entry) override; + void enqueue(const std::string &table_name, const swss::KeyOpFieldsValuesTuple &entry) override; void drain() override; std::string verifyState(const std::string &key, const std::vector &tuple) override; + ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) override; private: ReturnCodeOr deserializeNeighborEntry(const std::string &key, diff --git a/orchagent/p4orch/next_hop_manager.cpp b/orchagent/p4orch/next_hop_manager.cpp index 3ff7d3d3d6..5718fbb72b 100644 --- a/orchagent/p4orch/next_hop_manager.cpp +++ b/orchagent/p4orch/next_hop_manager.cpp @@ -147,7 +147,34 @@ ReturnCodeOr> NextHopManager::getSaiAttrs(const P4N return next_hop_attrs; } -void NextHopManager::enqueue(const swss::KeyOpFieldsValuesTuple &entry) +ReturnCode NextHopManager::getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) +{ + std::string value; + + try + { + nlohmann::json j = nlohmann::json::parse(json_key); + if (j.find(prependMatchField(p4orch::kNexthopId)) != j.end()) + { + value = j.at(prependMatchField(p4orch::kNexthopId)).get(); + object_key = KeyGenerator::generateNextHopKey(value); + object_type = SAI_OBJECT_TYPE_NEXT_HOP; + return ReturnCode(); + } + else + { + SWSS_LOG_ERROR("%s match parameter absent: required for dependent object query", p4orch::kNexthopId); + } + } + catch (std::exception &ex) + { + SWSS_LOG_ERROR("json_key parse error"); + } + + return StatusCode::SWSS_RC_INVALID_PARAM; +} + +void NextHopManager::enqueue(const std::string &table_name, const swss::KeyOpFieldsValuesTuple &entry) { m_entries.push_back(entry); } diff --git a/orchagent/p4orch/next_hop_manager.h b/orchagent/p4orch/next_hop_manager.h index eda1ac0001..7bacdad534 100644 --- a/orchagent/p4orch/next_hop_manager.h +++ b/orchagent/p4orch/next_hop_manager.h @@ -57,9 +57,10 @@ class NextHopManager : public ObjectManagerInterface virtual ~NextHopManager() = default; - void enqueue(const swss::KeyOpFieldsValuesTuple &entry) override; + void enqueue(const std::string &table_name, const swss::KeyOpFieldsValuesTuple &entry) override; void drain() override; std::string verifyState(const std::string &key, const std::vector &tuple) override; + ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) override; private: // Gets the internal cached next hop entry by its key. diff --git a/orchagent/p4orch/object_manager_interface.h b/orchagent/p4orch/object_manager_interface.h index 17b6e9ec84..966288a156 100644 --- a/orchagent/p4orch/object_manager_interface.h +++ b/orchagent/p4orch/object_manager_interface.h @@ -8,11 +8,15 @@ class ObjectManagerInterface virtual ~ObjectManagerInterface() = default; // Enqueues an entry into the manager - virtual void enqueue(const swss::KeyOpFieldsValuesTuple &entry) = 0; + virtual void enqueue(const std::string &table_name, const swss::KeyOpFieldsValuesTuple &entry) = 0; // Processes all entries in the queue virtual void drain() = 0; // StateVerification helper function for the manager virtual std::string verifyState(const std::string &key, const std::vector &tuple) = 0; + + // For sai extension objects depending on a sai object + // return sai object id for a given table with a given key + virtual ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) = 0; }; diff --git a/orchagent/p4orch/p4orch.cpp b/orchagent/p4orch/p4orch.cpp index 6a9beea830..eca0918171 100644 --- a/orchagent/p4orch/p4orch.cpp +++ b/orchagent/p4orch/p4orch.cpp @@ -8,6 +8,8 @@ #include "copporch.h" #include "logger.h" #include "orch.h" +#include "p4orch/p4orch_util.h" +#include "p4orch/tables_definition_manager.h" #include "p4orch/acl_rule_manager.h" #include "p4orch/acl_table_manager.h" #include "p4orch/gre_tunnel_manager.h" @@ -16,6 +18,7 @@ #include "p4orch/next_hop_manager.h" #include "p4orch/route_manager.h" #include "p4orch/router_interface_manager.h" +#include "p4orch/ext_tables_manager.h" #include "portsorch.h" #include "return_code.h" #include "sai_serialize.h" @@ -23,12 +26,15 @@ 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" P4Orch::P4Orch(swss::DBConnector *db, std::vector tableNames, VRFOrch *vrfOrch, CoppOrch *coppOrch) : Orch(db, tableNames) { SWSS_LOG_ENTER(); + m_tablesDefnManager = std::make_unique(&m_p4OidMapper, &m_publisher); m_routerIntfManager = std::make_unique(&m_p4OidMapper, &m_publisher); m_neighborManager = std::make_unique(&m_p4OidMapper, &m_publisher); m_greTunnelManager = std::make_unique(&m_p4OidMapper, &m_publisher); @@ -39,7 +45,9 @@ P4Orch::P4Orch(swss::DBConnector *db, std::vector tableNames, VRFOr m_aclRuleManager = std::make_unique(&m_p4OidMapper, vrfOrch, coppOrch, &m_publisher); m_wcmpManager = std::make_unique(&m_p4OidMapper, &m_publisher); m_l3AdmitManager = std::make_unique(&m_p4OidMapper, &m_publisher); + m_extTablesManager = std::make_unique(&m_p4OidMapper, vrfOrch, &m_publisher); + m_p4TableToManagerMap[APP_P4RT_TABLES_DEFINITION_TABLE_NAME] = m_tablesDefnManager.get(); m_p4TableToManagerMap[APP_P4RT_ROUTER_INTERFACE_TABLE_NAME] = m_routerIntfManager.get(); m_p4TableToManagerMap[APP_P4RT_NEIGHBOR_TABLE_NAME] = m_neighborManager.get(); m_p4TableToManagerMap[APP_P4RT_TUNNEL_TABLE_NAME] = m_greTunnelManager.get(); @@ -50,7 +58,9 @@ P4Orch::P4Orch(swss::DBConnector *db, std::vector tableNames, VRFOr m_p4TableToManagerMap[APP_P4RT_ACL_TABLE_DEFINITION_NAME] = m_aclTableManager.get(); m_p4TableToManagerMap[APP_P4RT_WCMP_GROUP_TABLE_NAME] = m_wcmpManager.get(); m_p4TableToManagerMap[APP_P4RT_L3_ADMIT_TABLE_NAME] = m_l3AdmitManager.get(); + m_p4TableToManagerMap[APP_P4RT_EXT_TABLES_MANAGER] = m_extTablesManager.get(); + m_p4ManagerPrecedence.push_back(m_tablesDefnManager.get()); m_p4ManagerPrecedence.push_back(m_routerIntfManager.get()); m_p4ManagerPrecedence.push_back(m_neighborManager.get()); m_p4ManagerPrecedence.push_back(m_greTunnelManager.get()); @@ -61,14 +71,23 @@ P4Orch::P4Orch(swss::DBConnector *db, std::vector tableNames, VRFOr m_p4ManagerPrecedence.push_back(m_aclTableManager.get()); m_p4ManagerPrecedence.push_back(m_aclRuleManager.get()); m_p4ManagerPrecedence.push_back(m_l3AdmitManager.get()); + m_p4ManagerPrecedence.push_back(m_extTablesManager.get()); + tablesinfo = nullptr; // Add timer executor to update ACL counters stats in COUNTERS_DB - auto interv = timespec{.tv_sec = P4_COUNTERS_READ_INTERVAL, .tv_nsec = 0}; - m_aclCounterStatsTimer = new swss::SelectableTimer(interv); - auto executor = new swss::ExecutableTimer(m_aclCounterStatsTimer, this, P4_ACL_COUNTERS_STATS_POLL_TIMER_NAME); - Orch::addExecutor(executor); + auto acl_interv = timespec{.tv_sec = P4_COUNTERS_READ_INTERVAL, .tv_nsec = 0}; + m_aclCounterStatsTimer = new swss::SelectableTimer(acl_interv); + auto acl_executor = new swss::ExecutableTimer(m_aclCounterStatsTimer, this, P4_ACL_COUNTERS_STATS_POLL_TIMER_NAME); + Orch::addExecutor(acl_executor); m_aclCounterStatsTimer->start(); + // Add timer executor to update EXT counters stats in COUNTERS_DB + auto ext_interv = timespec{.tv_sec = P4_COUNTERS_READ_INTERVAL, .tv_nsec = 0}; + m_extCounterStatsTimer = new swss::SelectableTimer(ext_interv); + auto ext_executor = new swss::ExecutableTimer(m_extCounterStatsTimer, this, P4_EXT_COUNTERS_STATS_POLL_TIMER_NAME); + Orch::addExecutor(ext_executor); + m_extCounterStatsTimer->start(); + // Add port state change notification handling support swss::DBConnector notificationsDb("ASIC_DB", 0); m_portStatusNotificationConsumer = new swss::NotificationConsumer(¬ificationsDb, "NOTIFICATIONS"); @@ -110,16 +129,25 @@ void P4Orch::doTask(Consumer &consumer) status); continue; } - if (m_p4TableToManagerMap.find(table_name) == m_p4TableToManagerMap.end()) + if (m_p4TableToManagerMap.find(table_name) != m_p4TableToManagerMap.end()) { - auto status = ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) - << "Failed to find P4Orch Manager for " << table_name << " P4RT DB table"; - SWSS_LOG_ERROR("%s", status.message().c_str()); - m_publisher.publish(APP_P4RT_TABLE_NAME, kfvKey(key_op_fvs_tuple), kfvFieldsValues(key_op_fvs_tuple), - status); - continue; + m_p4TableToManagerMap[table_name]->enqueue(table_name, key_op_fvs_tuple); + } + else + { + if (table_name.rfind(p4orch::kTablePrefixEXT, 0) != std::string::npos) + { + m_p4TableToManagerMap[APP_P4RT_EXT_TABLES_MANAGER]->enqueue(table_name, key_op_fvs_tuple); + } + else + { + auto status = ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) + << "Failed to find P4Orch Manager for " << table_name << " P4RT DB table"; + SWSS_LOG_ERROR("%s", status.message().c_str()); + m_publisher.publish(APP_P4RT_TABLE_NAME, kfvKey(key_op_fvs_tuple), kfvFieldsValues(key_op_fvs_tuple), + status); + } } - m_p4TableToManagerMap[table_name]->enqueue(key_op_fvs_tuple); } for (const auto &manager : m_p4ManagerPrecedence) @@ -141,6 +169,10 @@ void P4Orch::doTask(swss::SelectableTimer &timer) { m_aclRuleManager->doAclCounterStatsTask(); } + else if (&timer == m_extCounterStatsTimer) + { + m_extTablesManager->doExtCounterStatsTask(); + } else { SWSS_LOG_NOTICE("Unrecognized timer passed in P4Orch::doTask(swss::SelectableTimer& " diff --git a/orchagent/p4orch/p4orch.h b/orchagent/p4orch/p4orch.h index e39041802b..9385346d20 100644 --- a/orchagent/p4orch/p4orch.h +++ b/orchagent/p4orch/p4orch.h @@ -10,6 +10,7 @@ #include "notificationconsumer.h" #include "notifier.h" #include "orch.h" +#include "p4orch/tables_definition_manager.h" #include "p4orch/acl_rule_manager.h" #include "p4orch/acl_table_manager.h" #include "p4orch/gre_tunnel_manager.h" @@ -22,9 +23,22 @@ #include "p4orch/route_manager.h" #include "p4orch/router_interface_manager.h" #include "p4orch/wcmp_manager.h" +#include "p4orch/ext_tables_manager.h" #include "response_publisher.h" #include "vrforch.h" +static const std::map FixedTablesMap = { + {"router_interface_table", APP_P4RT_ROUTER_INTERFACE_TABLE_NAME }, + {"neighbor_table", APP_P4RT_NEIGHBOR_TABLE_NAME}, + {"nexthop_table", APP_P4RT_NEXTHOP_TABLE_NAME}, + {"wcmp_group_table", APP_P4RT_WCMP_GROUP_TABLE_NAME}, + {"ipv4_table", APP_P4RT_IPV4_TABLE_NAME}, + {"ipv6_table", APP_P4RT_IPV6_TABLE_NAME}, + {"mirror_session_table", APP_P4RT_MIRROR_SESSION_TABLE_NAME}, + {"l3_admit_table", APP_P4RT_L3_ADMIT_TABLE_NAME}, + {"tunnel_table", APP_P4RT_TUNNEL_TABLE_NAME} +}; + class P4Orch : public Orch { public: @@ -37,6 +51,11 @@ class P4Orch : public Orch p4orch::AclRuleManager *getAclRuleManager(); p4orch::WcmpManager *getWcmpManager(); GreTunnelManager *getGreTunnelManager(); + TablesInfo *tablesinfo = NULL; + + // m_p4TableToManagerMap: P4 APP DB table name, P4 Object Manager + std::unordered_map m_p4TableToManagerMap; + private: void doTask(Consumer &consumer); @@ -44,13 +63,13 @@ class P4Orch : public Orch void doTask(swss::NotificationConsumer &consumer); void handlePortStatusChangeNotification(const std::string &op, const std::string &data); - // m_p4TableToManagerMap: P4 APP DB table name, P4 Object Manager - std::unordered_map m_p4TableToManagerMap; // P4 object manager request processing order. std::vector m_p4ManagerPrecedence; swss::SelectableTimer *m_aclCounterStatsTimer; + swss::SelectableTimer *m_extCounterStatsTimer; P4OidMapper m_p4OidMapper; + std::unique_ptr m_tablesDefnManager; std::unique_ptr m_routerIntfManager; std::unique_ptr m_greTunnelManager; std::unique_ptr m_neighborManager; @@ -61,6 +80,7 @@ class P4Orch : public Orch std::unique_ptr m_aclRuleManager; std::unique_ptr m_wcmpManager; std::unique_ptr m_l3AdmitManager; + std::unique_ptr m_extTablesManager; // Notification consumer for port state change swss::NotificationConsumer *m_portStatusNotificationConsumer; diff --git a/orchagent/p4orch/p4orch_util.cpp b/orchagent/p4orch/p4orch_util.cpp index 5ff0c058d4..b2ea0a762b 100644 --- a/orchagent/p4orch/p4orch_util.cpp +++ b/orchagent/p4orch/p4orch_util.cpp @@ -1,8 +1,10 @@ +#include "p4orch/p4orch.h" #include "p4orch/p4orch_util.h" #include "schema.h" using ::p4orch::kTableKeyDelimiter; +extern P4Orch *gP4Orch; // Prepends "match/" to the input string str to construct a new string. std::string prependMatchField(const std::string &str) @@ -80,6 +82,46 @@ std::string verifyAttrs(const std::vector &targets, return ""; } +TableInfo *getTableInfo(const std::string &table_name) +{ + if (!gP4Orch->tablesinfo) + { + return nullptr; + } + + auto it = gP4Orch->tablesinfo->m_tableInfoMap.find(table_name); + if (it == gP4Orch->tablesinfo->m_tableInfoMap.end()) + { + return nullptr; + } + + return &it->second; +} + +ActionInfo *getTableActionInfo(TableInfo *table, const std::string &action_name) +{ + if (!table) + { + return nullptr; + } + + auto it = table->action_fields.find(action_name); + if (it == table->action_fields.end()) + { + return nullptr; + } + + return &it->second; +} + +std::string KeyGenerator::generateTablesInfoKey(const std::string &context) +{ + std::map fv_map = { + {"context", context} + }; + return generateKey(fv_map); +} + std::string KeyGenerator::generateRouteKey(const std::string &vrf_id, const swss::IpPrefix &ip_prefix) { std::map fv_map = { @@ -152,6 +194,17 @@ std::string KeyGenerator::generateTunnelKey(const std::string &tunnel_id) return generateKey(fv_map); } +std::string KeyGenerator::generateExtTableKey(const std::string &table_name, const std::string &table_key) +{ + std::string key; + + key.append(table_name); + key.append(":"); + key.append(table_key); + + return key; +} + std::string KeyGenerator::generateKey(const std::map &fv_map) { std::string key; diff --git a/orchagent/p4orch/p4orch_util.h b/orchagent/p4orch/p4orch_util.h index 198442ef81..f95a9fd8eb 100644 --- a/orchagent/p4orch/p4orch_util.h +++ b/orchagent/p4orch/p4orch_util.h @@ -6,16 +6,23 @@ #include #include #include +#include #include "ipaddress.h" #include "ipprefix.h" #include "macaddress.h" #include "table.h" +extern "C" +{ +#include "saitypes.h" +} + namespace p4orch { // Field names in P4RT APP DB entry. +constexpr char *kTablePrefixEXT = "EXT_"; constexpr char *kRouterInterfaceId = "router_interface_id"; constexpr char *kPort = "port"; constexpr char *kInPort = "in_port"; @@ -80,6 +87,19 @@ constexpr char *kTos = "tos"; constexpr char *kMirrorAsIpv4Erspan = "mirror_as_ipv4_erspan"; constexpr char *kL3AdmitAction = "admit_to_l3"; constexpr char *kTunnelAction = "mark_for_p2p_tunnel_encap"; + +// Field names in P4RT TABLE DEFINITION APP DB entry. +constexpr char *kTables = "tables"; +constexpr char *kId = "id"; +constexpr char *kName = "name"; +constexpr char *kAlias = "alias"; +constexpr char *kBitwidth = "bitwidth"; +constexpr char *kFormat = "format"; +constexpr char *kmatchFields = "matchFields"; +constexpr char *kActionParams = "params"; +constexpr char *kReferences = "references"; +constexpr char *kTableRef = "table"; +constexpr char *kMatchRef = "match"; } // namespace p4orch // Prepends "match/" to the input string str to construct a new string. @@ -88,6 +108,58 @@ std::string prependMatchField(const std::string &str); // Prepends "param/" to the input string str to construct a new string. std::string prependParamField(const std::string &str); +struct ActionParamInfo +{ + std::string name; + std::string fieldtype; + std::string datatype; + std::unordered_map table_reference_map; +}; + +struct ActionInfo +{ + std::string name; + std::unordered_map params; + bool refers_to; +}; + +struct TableMatchInfo +{ + std::string name; + std::string fieldtype; + std::string datatype; + std::unordered_map table_reference_map; +}; + +/** + * Dervied table definition + * This is a derived state out of table definition provided by P4RT-APP + */ +struct TableInfo +{ + std::string name; + int id; + int precedence; + std::unordered_map match_fields; + std::unordered_map action_fields; + bool counter_bytes_enabled; + bool counter_packets_enabled; + std::vector action_ref_tables; + // list of tables across all actions, of current table, refer to +}; + +/** + * table-name to table-definition map + */ +typedef std::unordered_map TableInfoMap; + +struct TablesInfoAppDbEntry +{ + std::string context; + std::string info; +}; + + struct P4RouterInterfaceAppDbEntry { std::string router_interface_id; @@ -221,6 +293,26 @@ struct P4AclRuleAppDbEntry P4AclMeterAppDb meter; }; +struct DepObject +{ + sai_object_type_t sai_object; + std::string key; + sai_object_id_t oid; +}; + +struct P4ExtTableAppDbEntry +{ + std::string db_key; + std::string table_name; + std::string table_key; + std::unordered_map> action_params; + std::unordered_map action_dep_objects; +}; + + +TableInfo *getTableInfo(const std::string &table_name); +ActionInfo *getTableActionInfo(TableInfo *table, const std::string &action_name); + // Get the table name and key content from the given P4RT key. // Outputs will be empty strings in case of error. // Example: FIXED_NEIGHBOR_TABLE:{content} @@ -246,6 +338,8 @@ std::string verifyAttrs(const std::vector &targets, class KeyGenerator { public: + static std::string generateTablesInfoKey(const std::string &context); + static std::string generateRouteKey(const std::string &vrf_id, const swss::IpPrefix &ip_prefix); static std::string generateRouterInterfaceKey(const std::string &router_intf_id); @@ -267,6 +361,8 @@ class KeyGenerator static std::string generateTunnelKey(const std::string &tunnel_id); + static std::string generateExtTableKey(const std::string &table_name, const std::string &table_key); + // Generates key used by object managers and centralized mapper. // Takes map of as input and returns a concatenated string // of the form id1=value1:id2=value2... @@ -283,4 +379,4 @@ template std::string QuotedVar(T name) } // Trim tailing and leading whitespace -std::string trim(const std::string &s); \ No newline at end of file +std::string trim(const std::string &s); diff --git a/orchagent/p4orch/route_manager.cpp b/orchagent/p4orch/route_manager.cpp index e029489fde..3b1886179f 100644 --- a/orchagent/p4orch/route_manager.cpp +++ b/orchagent/p4orch/route_manager.cpp @@ -837,7 +837,12 @@ std::vector RouteManager::deleteRouteEntries(const std::vector &tuple) override; + ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) override; private: // Applies route entry updates from src to dest. The merged result will be diff --git a/orchagent/p4orch/router_interface_manager.cpp b/orchagent/p4orch/router_interface_manager.cpp index 43dc652eb6..bc059217b4 100644 --- a/orchagent/p4orch/router_interface_manager.cpp +++ b/orchagent/p4orch/router_interface_manager.cpp @@ -337,7 +337,34 @@ ReturnCode RouterInterfaceManager::processDeleteRequest(const std::string &route return status; } -void RouterInterfaceManager::enqueue(const swss::KeyOpFieldsValuesTuple &entry) +ReturnCode RouterInterfaceManager::getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) +{ + std::string value; + + try + { + nlohmann::json j = nlohmann::json::parse(json_key); + if (j.find(prependMatchField(p4orch::kRouterInterfaceId)) != j.end()) + { + value = j.at(prependMatchField(p4orch::kRouterInterfaceId)).get(); + object_key = KeyGenerator::generateRouterInterfaceKey(value); + object_type = SAI_OBJECT_TYPE_ROUTER_INTERFACE; + return ReturnCode(); + } + else + { + SWSS_LOG_ERROR("%s match parameter absent: required for dependent object query", p4orch::kRouterInterfaceId); + } + } + catch (std::exception &ex) + { + SWSS_LOG_ERROR("json_key parse error"); + } + + return StatusCode::SWSS_RC_INVALID_PARAM; +} + +void RouterInterfaceManager::enqueue(const std::string &table_name, const swss::KeyOpFieldsValuesTuple &entry) { m_entries.push_back(entry); } diff --git a/orchagent/p4orch/router_interface_manager.h b/orchagent/p4orch/router_interface_manager.h index 73d994ff06..427400e9c0 100644 --- a/orchagent/p4orch/router_interface_manager.h +++ b/orchagent/p4orch/router_interface_manager.h @@ -49,9 +49,10 @@ class RouterInterfaceManager : public ObjectManagerInterface } virtual ~RouterInterfaceManager() = default; - void enqueue(const swss::KeyOpFieldsValuesTuple &entry) override; + void enqueue(const std::string &table_name, const swss::KeyOpFieldsValuesTuple &entry) override; void drain() override; std::string verifyState(const std::string &key, const std::vector &tuple) override; + ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) override; private: ReturnCodeOr deserializeRouterIntfEntry( diff --git a/orchagent/p4orch/tables_definition_manager.cpp b/orchagent/p4orch/tables_definition_manager.cpp new file mode 100644 index 0000000000..aa91fb40f4 --- /dev/null +++ b/orchagent/p4orch/tables_definition_manager.cpp @@ -0,0 +1,687 @@ +#include "p4orch/tables_definition_manager.h" + +#include +#include +#include +#include +#include + +#include "directory.h" +#include "json.hpp" +#include "logger.h" +#include "tokenize.h" +#include "orch.h" +#include "p4orch/p4orch.h" +#include "p4orch/p4orch_util.h" +extern "C" +{ +#include "saitypes.h" +} + + +extern Directory gDirectory; +extern P4Orch *gP4Orch; +const std::map format_datatype_map = +{ + {"MAC", "SAI_ATTR_VALUE_TYPE_MAC"}, + {"IPV4", "SAI_ATTR_VALUE_TYPE_IPV4"}, + {"IPV6", "SAI_ATTR_VALUE_TYPE_IPV6"} +}; + + +std::string +BitwidthToDatatype (int bitwidth) +{ + std::string datatype = "SAI_ATTR_VALUE_TYPE_CHARDATA"; + + if (bitwidth <= 0) + { + datatype = "SAI_ATTR_VALUE_TYPE_CHARDATA"; + } + else if (bitwidth <= 8) + { + datatype = "SAI_ATTR_VALUE_TYPE_UINT8"; + } + else if (bitwidth <= 16) + { + datatype = "SAI_ATTR_VALUE_TYPE_UINT16"; + } + else if (bitwidth <= 32) + { + datatype = "SAI_ATTR_VALUE_TYPE_UINT32"; + } + else if (bitwidth <= 64) + { + datatype = "SAI_ATTR_VALUE_TYPE_UINT64"; + } + + return datatype; +} + +std::string +parseBitwidthToDatatype (const nlohmann::json &json) +{ + int bitwidth; + std::string datatype = "SAI_ATTR_VALUE_TYPE_CHARDATA"; + + if (json.find(p4orch::kBitwidth) != json.end()) + { + bitwidth = json.at(p4orch::kBitwidth).get(); + datatype = BitwidthToDatatype(bitwidth); + } + + return datatype; +} + +std::string +parseFormatToDatatype (const nlohmann::json &json, std::string datatype) +{ + std::string format; + + if (json.find(p4orch::kFormat) != json.end()) + { + format = json.at(p4orch::kFormat).get(); + + auto it = format_datatype_map.find(format); + if (it != format_datatype_map.end()) + { + datatype = it->second; + } + } + + return datatype; +} + +ReturnCode +parseTableMatchReferences (const nlohmann::json &match_json, TableMatchInfo &match) +{ + std::string table, field; + + if (match_json.find(p4orch::kReferences) != match_json.end()) + { + for (const auto &ref_json : match_json[p4orch::kReferences]) + { + try + { + table = ref_json.at(p4orch::kTableRef).get(); + field = ref_json.at(p4orch::kMatchRef).get(); + match.table_reference_map[table] = field; + } + catch (std::exception &ex) + { + return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) + << "can not parse tables from app-db supplied table definition info"; + } + } + } + + return ReturnCode(); +} + +ReturnCode +parseActionParamReferences (const nlohmann::json ¶m_json, ActionParamInfo ¶m) +{ + std::string table, field; + + if (param_json.find(p4orch::kReferences) != param_json.end()) + { + for (const auto &ref_json : param_json[p4orch::kReferences]) + { + try + { + table = ref_json.at(p4orch::kTableRef).get(); + field = ref_json.at(p4orch::kMatchRef).get(); + param.table_reference_map[table] = field; + } + catch (std::exception &ex) + { + return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) + << "can not parse tables from app-db supplied table definition info"; + } + } + } + + return ReturnCode(); +} + +ReturnCode +parseTableActionParams (const nlohmann::json &action_json, ActionInfo &action) +{ + action.refers_to = false; + if (action_json.find(p4orch::kActionParams) != action_json.end()) + { + for (const auto ¶m_json : action_json[p4orch::kActionParams]) + { + try + { + ActionParamInfo param; + std::string param_name; + + param_name = param_json.at(p4orch::kName).get(); + param.name = param_name; + param.datatype = parseBitwidthToDatatype(param_json); + param.datatype = parseFormatToDatatype(param_json, param.datatype); + parseActionParamReferences(param_json, param); + action.params[param_name] = param; + + if (!param.table_reference_map.empty()) + { + /** + * Helps avoid walk of action parameters if this is set to false at action level + */ + action.refers_to = true; + } + } + catch (std::exception &ex) + { + return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) + << "can not parse tables from app-db supplied table definition info"; + } + } + } + + return ReturnCode(); +} + +ReturnCode +parseTableCounter (const nlohmann::json &table_json, TableInfo &table) +{ + if (table_json.find(p4orch::kCounterUnit) != table_json.end()) + { + auto unit = table_json.at(p4orch::kCounterUnit); + if (unit == "PACKETS") + { + table.counter_packets_enabled = true; + } + else if (unit == "BYTES") + { + table.counter_bytes_enabled = true; + } + else + { + table.counter_packets_enabled = true; + table.counter_bytes_enabled = true; + } + } + + return ReturnCode(); +} + +ReturnCode +parseTablesInfo (const nlohmann::json &info_json, TablesInfo &info_entry) +{ + ReturnCode status; + int table_id; + std::string table_name, field_name; + + if (info_json.find(p4orch::kTables) == info_json.end()) + { + return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) + << "no tables in app-db supplied table definition info"; + } + + for (const auto &table_json : info_json[p4orch::kTables]) + { + try + { + table_id = table_json.at(p4orch::kId).get(); + table_name = table_json.at(p4orch::kAlias).get(); + } + catch (std::exception &ex) + { + return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) + << "can not parse tables from app-db supplied table definition info"; + } + + + TableInfo table = {}; + table.name = table_name; + table.id = table_id; + try + { + for (const auto &match_json : table_json[p4orch::kmatchFields]) + { + TableMatchInfo match = {}; + std::string match_name; + + match_name = match_json.at(p4orch::kName).get(); + match.name = match_name; + match.datatype = parseBitwidthToDatatype(match_json); + match.datatype = parseFormatToDatatype(match_json, match.datatype); + parseTableMatchReferences(match_json, match); + table.match_fields[match_name] = match; + } + + for (const auto &action_json : table_json[p4orch::kActions]) + { + ActionInfo action = {}; + std::string action_name; + + action_name = action_json.at(p4orch::kAlias).get(); + action.name = action_name; + parseTableActionParams(action_json, action); + table.action_fields[action_name] = action; + + /** + * If any parameter of action refers to another table, add that one in the + * cross-reference list of current table + */ + for (auto param_it = action.params.begin(); + param_it != action.params.end(); param_it++) + { + ActionParamInfo action_param = param_it->second; + for (auto ref_it = action_param.table_reference_map.begin(); + ref_it != action_param.table_reference_map.end(); ref_it++) + { + if (std::find(table.action_ref_tables.begin(), + table.action_ref_tables.end(), + ref_it->first) == table.action_ref_tables.end()) + { + table.action_ref_tables.push_back(ref_it->first); + } + } + } + } + + parseTableCounter(table_json, table); + } + catch (std::exception &ex) + { + return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) + << "can not parse table " << QuotedVar(table_name.c_str()) << "match fields"; + } + + + info_entry.m_tableIdNameMap[std::to_string(table_id)] = table_name; + info_entry.m_tableInfoMap[table_name] = table; + } + + return ReturnCode(); +} + + +ReturnCodeOr TablesDefnManager::deserializeTablesInfoEntry( + const std::string &key, const std::vector &attributes) +{ + SWSS_LOG_ENTER(); + + TablesInfoAppDbEntry app_db_entry = {}; + try + { + nlohmann::json j = nlohmann::json::parse(key); + app_db_entry.context = j["context"]; + } + catch (std::exception &ex) + { + return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) << "Failed to deserialize tables info"; + } + + for (const auto &it : attributes) + { + const auto &field = fvField(it); + std::string value = fvValue(it); + if (field == "info") + { + app_db_entry.info = value; + } + else + { + return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) + << "Unexpected field " << QuotedVar(field) << " in table entry"; + } + } + + return app_db_entry; +} + +ReturnCode validateTablesInfoAppDbEntry(const TablesInfoAppDbEntry &app_db_entry) +{ + // Perform generic APP DB entry validations. Operation specific validations + // will be done by the respective request process methods. + + return ReturnCode(); +} + +TablesInfo *TablesDefnManager::getTablesInfoEntry(const std::string &context_key) +{ + SWSS_LOG_ENTER(); + + if (m_tablesinfoMap.find(context_key) == m_tablesinfoMap.end()) + return nullptr; + + return &m_tablesinfoMap[context_key]; +} + +ReturnCode TablesDefnManager::processAddRequest(const TablesInfoAppDbEntry &app_db_entry, + const std::string &context_key) +{ + nlohmann::json tablesinfo_json; + ReturnCode status; + + SWSS_LOG_ENTER(); + + if (!m_tablesinfoMap.empty()) + { + // For now p4rt can send only same table-definition, so ignore it silently + return ReturnCode(); + } + + try + { + tablesinfo_json = nlohmann::json::parse(app_db_entry.info); + } + catch (std::exception &ex) + { + return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) << "tables info from appdb can not be parsed\n"; + } + + TablesInfo tablesinfo_entry(app_db_entry.context, tablesinfo_json); + + status = parseTablesInfo(tablesinfo_json, tablesinfo_entry); + if (!status.ok()) + { + return status; + } + + m_tablesinfoMap[app_db_entry.context] = tablesinfo_entry; + gP4Orch->tablesinfo = &m_tablesinfoMap[app_db_entry.context]; + return ReturnCode(); +} + +ReturnCode TablesDefnManager::processUpdateRequest(const TablesInfoAppDbEntry &app_db_entry, + const std::string &context_key) +{ + SWSS_LOG_ENTER(); + + return ReturnCode(StatusCode::SWSS_RC_UNIMPLEMENTED) << "update of Tables Definition not supported"; +} + +ReturnCode TablesDefnManager::processDeleteRequest(const std::string &context_key) +{ + SWSS_LOG_ENTER(); + + auto *tablesinfo = getTablesInfoEntry(context_key); + + if (tablesinfo) + { + if (gP4Orch->tablesinfo == tablesinfo) + { + gP4Orch->tablesinfo = nullptr; + } + + tablesinfo->m_tableIdNameMap.clear(); + } + + m_tablesinfoMap.erase(context_key); + return ReturnCode(); +} + +ReturnCode TablesDefnManager::getSaiObject(const std::string &json_key, + sai_object_type_t &object_type, std::string &object_key) +{ + return StatusCode::SWSS_RC_INVALID_PARAM; +} + +std::unordered_map> +createGraph (std::vector> preReq) +{ + std::unordered_map> graph; + + for (auto pre : preReq) + { + auto it = graph.find(pre.second); + if (it != graph.end()) + { + it->second.insert(pre.first); + } + else + { + graph[pre.second].insert(pre.first); + } + } + + return graph; +} + +std::unordered_map +computeIndegree (std::unordered_map> &graph) +{ + std::unordered_map degrees; + + for (auto g_it = graph.begin(); g_it != graph.end(); g_it++) + { + for (int neigh : g_it->second) + { + auto n_it = degrees.find(neigh); + if (n_it != degrees.end()) + { + n_it->second++; + } + else + { + degrees.insert({neigh, 0}); + } + } + } + + return degrees; +} + + +std::vector +findTablePrecedence (int tables, std::vector> preReq, TablesInfo *tables_info) +{ + std::unordered_map> graph = createGraph(preReq); + std::unordered_map degrees = computeIndegree(graph); + std::vector visited; + std::vector toposort; + std::queue zeros; + + // initialize queue with tables having no dependencies + for (auto table_it = tables_info->m_tableInfoMap.begin(); + table_it != tables_info->m_tableInfoMap.end(); table_it++) + { + TableInfo table_info = table_it->second; + if (degrees.find(table_info.id) == degrees.end()) + { + zeros.push(table_info.id); + visited.push_back(table_info.id); + } + } + + for (int i = 0; i < tables; i++) + { + // Err input data like possible cyclic dependencies, could not build precedence order + if (zeros.empty()) + { + SWSS_LOG_ERROR("Filed to build table precedence order"); + return {}; + } + + // Run BFS + int zero = zeros.front(); + zeros.pop(); + toposort.push_back(zero); + auto g_it = graph.find(zero); + if (g_it != graph.end()) + { + for (int neigh : g_it->second) + { + auto n_it = degrees.find(neigh); + if (n_it != degrees.end()) + { + if (!n_it->second) + { + if (std::find(visited.begin(), visited.end(), neigh) == visited.end()) + { + zeros.push(neigh); + visited.push_back(neigh); + } + } + else + { + n_it->second--; + } + } + } + } + } + + return toposort; +} + + +void +buildTablePrecedence (TablesInfo *tables_info) +{ + std::vector> preReq; + std::vector orderedTables; + int tables = 0; + + if (!tables_info) { + return; + } + + // build dependencies + for (auto table_it = tables_info->m_tableInfoMap.begin(); + table_it != tables_info->m_tableInfoMap.end(); table_it++) + { + TableInfo table_info = table_it->second; + tables++; + + for (std::size_t i = 0; i < table_info.action_ref_tables.size(); i++) + { + /** + * For now processing precedence order is only amongst extension tables + * Skip fixed tables, include them in precedence calculations when fixed + * and extension tables processing precedence may be interleaved + */ + if (FixedTablesMap.find(table_info.action_ref_tables[i]) != FixedTablesMap.end()) + { + continue; + } + + TableInfo ref_table_info = tables_info->m_tableInfoMap[table_info.action_ref_tables[i]]; + if (std::find(preReq.begin(), preReq.end(), + std::make_pair(table_info.id, ref_table_info.id)) == preReq.end()) + { + preReq.push_back(std::make_pair(table_info.id, ref_table_info.id)); + } + } + } + + // find precedence of tables based on dependencies + orderedTables = findTablePrecedence(tables, preReq, tables_info); + + // update each table with calculated precedence value and build table precedence map + for (std::size_t i = 0; i < orderedTables.size(); i++) + { + auto table_id = orderedTables[i]; + auto id_it = tables_info->m_tableIdNameMap.find(std::to_string(table_id)); + if (id_it == tables_info->m_tableIdNameMap.end()) + { + continue; + } + + auto table_it = tables_info->m_tableInfoMap.find(id_it->second); + if (table_it == tables_info->m_tableInfoMap.end()) + { + continue; + } + + table_it->second.precedence = (int)i; + tables_info->m_tablePrecedenceMap[(int)i] = table_it->second.name; + } + + return; +} + + +void TablesDefnManager::enqueue(const std::string &table_name, const swss::KeyOpFieldsValuesTuple &entry) +{ + m_entries.push_back(entry); +} + +void TablesDefnManager::drain() +{ + SWSS_LOG_ENTER(); + + for (const auto &key_op_fvs_tuple : m_entries) + { + std::string table_name; + std::string key; + parseP4RTKey(kfvKey(key_op_fvs_tuple), &table_name, &key); + const std::vector &attributes = kfvFieldsValues(key_op_fvs_tuple); + + ReturnCode status; + auto app_db_entry_or = deserializeTablesInfoEntry(key, attributes); + if (!app_db_entry_or.ok()) + { + status = app_db_entry_or.status(); + SWSS_LOG_ERROR("Unable to deserialize APP DB entry with key %s: %s", + QuotedVar(table_name + ":" + key).c_str(), status.message().c_str()); + m_publisher->publish(APP_P4RT_TABLE_NAME, kfvKey(key_op_fvs_tuple), kfvFieldsValues(key_op_fvs_tuple), + status, /*replace=*/true); + continue; + } + auto &app_db_entry = *app_db_entry_or; + + status = validateTablesInfoAppDbEntry(app_db_entry); + if (!status.ok()) + { + SWSS_LOG_ERROR("Validation failed for tables definition APP DB entry with key %s: %s", + QuotedVar(table_name + ":" + key).c_str(), status.message().c_str()); + m_publisher->publish(APP_P4RT_TABLE_NAME, kfvKey(key_op_fvs_tuple), kfvFieldsValues(key_op_fvs_tuple), + status, /*replace=*/true); + continue; + } + + const std::string context_key = KeyGenerator::generateTablesInfoKey(app_db_entry.context); + + const std::string &operation = kfvOp(key_op_fvs_tuple); + if (operation == SET_COMMAND) + { + auto *tablesinfo = getTablesInfoEntry(context_key); + if (tablesinfo == nullptr) + { + // Create TablesInfo + status = processAddRequest(app_db_entry, context_key); + } + else + { + // Modify existing TablesInfo + status = processUpdateRequest(app_db_entry, context_key); + } + } + else if (operation == DEL_COMMAND) + { + // Delete TablesInfo + status = processDeleteRequest(context_key); + } + else + { + status = ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) << "Unknown operation type " << QuotedVar(operation); + SWSS_LOG_ERROR("%s", status.message().c_str()); + } + if (!status.ok()) + { + SWSS_LOG_ERROR("Processing failed for tables definition APP DB entry with key %s: %s", + QuotedVar(table_name + ":" + key).c_str(), status.message().c_str()); + } + else + { + buildTablePrecedence(gP4Orch->tablesinfo); + } + m_publisher->publish(APP_P4RT_TABLE_NAME, kfvKey(key_op_fvs_tuple), kfvFieldsValues(key_op_fvs_tuple), + status, /*replace=*/true); + } + m_entries.clear(); +} + +std::string TablesDefnManager::verifyState(const std::string &key, const std::vector &tuple) +{ + std::string result = ""; + SWSS_LOG_ENTER(); + + return result; +} diff --git a/orchagent/p4orch/tables_definition_manager.h b/orchagent/p4orch/tables_definition_manager.h new file mode 100644 index 0000000000..6d0fbe444a --- /dev/null +++ b/orchagent/p4orch/tables_definition_manager.h @@ -0,0 +1,78 @@ +#pragma once + +#include +#include +#include +#include + +#include "macaddress.h" +#include "json.hpp" +#include "orch.h" +#include "p4orch/object_manager_interface.h" +#include "p4orch/p4oidmapper.h" +#include "p4orch/p4orch_util.h" +#include "response_publisher_interface.h" +#include "return_code.h" +extern "C" +{ +#include "sai.h" +} + +/** + * A set of tables definition + */ +struct TablesInfo +{ + std::string context; + nlohmann::json info; + std::unordered_map m_tableIdNameMap; + std::unordered_map m_tableInfoMap; + std::map m_tablePrecedenceMap; + + TablesInfo() {}; + TablesInfo(const std::string &context_key, const nlohmann::json &info_value) + : context(context_key), info(info_value) + { + } +}; + +/** + * Datastructure is designed to hold multiple set of table definition. + * However, current support handles only one set of table definition. + */ +typedef std::unordered_map TablesInfoMap; + +class TablesDefnManager : public ObjectManagerInterface +{ + public: + TablesDefnManager(P4OidMapper *p4oidMapper, ResponsePublisherInterface *publisher) + { + SWSS_LOG_ENTER(); + + assert(p4oidMapper != nullptr); + m_p4OidMapper = p4oidMapper; + assert(publisher != nullptr); + m_publisher = publisher; + } + virtual ~TablesDefnManager() = default; + + void enqueue(const std::string &table_name, const swss::KeyOpFieldsValuesTuple &entry) override; + void drain() override; + std::string verifyState(const std::string &key, const std::vector &tuple) override; + ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) override; + + private: + ReturnCodeOr deserializeTablesInfoEntry( + const std::string &key, const std::vector &attributes); + TablesInfo *getTablesInfoEntry(const std::string &context_key); + ReturnCode createTablesInfo(const std::string &context_key, TablesInfo &tablesinfo_entry); + ReturnCode removeTablesInfo(const std::string &context_key); + ReturnCode processAddRequest(const TablesInfoAppDbEntry &app_db_entry, const std::string &context_key); + ReturnCode processUpdateRequest(const TablesInfoAppDbEntry &app_db_entry, const std::string &context_key); + ReturnCode processDeleteRequest(const std::string &context_key); + + TablesInfoMap m_tablesinfoMap; + P4OidMapper *m_p4OidMapper; + ResponsePublisherInterface *m_publisher; + std::deque m_entries; +}; diff --git a/orchagent/p4orch/tests/Makefile.am b/orchagent/p4orch/tests/Makefile.am index 2af4e8e613..d759033c68 100644 --- a/orchagent/p4orch/tests/Makefile.am +++ b/orchagent/p4orch/tests/Makefile.am @@ -33,6 +33,7 @@ p4orch_tests_SOURCES = $(ORCHAGENT_DIR)/orch.cpp \ $(P4ORCH_DIR)/p4oidmapper.cpp \ $(P4ORCH_DIR)/p4orch.cpp \ $(P4ORCH_DIR)/p4orch_util.cpp \ + $(P4ORCH_DIR)/tables_definition_manager.cpp \ $(P4ORCH_DIR)/router_interface_manager.cpp \ $(P4ORCH_DIR)/gre_tunnel_manager.cpp \ $(P4ORCH_DIR)/neighbor_manager.cpp \ @@ -44,6 +45,7 @@ p4orch_tests_SOURCES = $(ORCHAGENT_DIR)/orch.cpp \ $(P4ORCH_DIR)/wcmp_manager.cpp \ $(P4ORCH_DIR)/mirror_session_manager.cpp \ $(P4ORCH_DIR)/l3_admit_manager.cpp \ + $(P4ORCH_DIR)/ext_tables_manager.cpp \ $(top_srcdir)/tests/mock_tests/fake_response_publisher.cpp \ fake_portorch.cpp \ fake_crmorch.cpp \ diff --git a/orchagent/p4orch/tests/acl_manager_test.cpp b/orchagent/p4orch/tests/acl_manager_test.cpp index b18fdc4fcb..9b0154ab55 100644 --- a/orchagent/p4orch/tests/acl_manager_test.cpp +++ b/orchagent/p4orch/tests/acl_manager_test.cpp @@ -983,7 +983,7 @@ class AclManagerTest : public ::testing::Test } void EnqueueTableTuple(const swss::KeyOpFieldsValuesTuple &entry) { - acl_table_manager_->enqueue(entry); + acl_table_manager_->enqueue(APP_P4RT_ACL_TABLE_DEFINITION_NAME, entry); } std::string VerifyTableState(const std::string &key, const std::vector &tuple) { @@ -994,9 +994,9 @@ class AclManagerTest : public ::testing::Test { acl_rule_manager_->drain(); } - void EnqueueRuleTuple(const swss::KeyOpFieldsValuesTuple &entry) + void EnqueueRuleTuple(const std::string &table_name, const swss::KeyOpFieldsValuesTuple &entry) { - acl_rule_manager_->enqueue(entry); + acl_rule_manager_->enqueue(table_name, entry); } std::string VerifyRuleState(const std::string &key, const std::vector &tuple) { @@ -2320,10 +2320,12 @@ TEST_F(AclManagerTest, DrainRuleTuplesToProcessSetRequestSucceeds) "ipv6_dst\":\"fdf8:f53b:82e4::53 & " "fdf8:f53b:82e4::53\",\"priority\":15}"; const auto &rule_tuple_key = std::string(kAclIngressTableName) + kTableKeyDelimiter + acl_rule_json_key; - EnqueueRuleTuple(swss::KeyOpFieldsValuesTuple({rule_tuple_key, SET_COMMAND, getDefaultRuleFieldValueTuples()})); + EnqueueRuleTuple(std::string(kAclIngressTableName), + swss::KeyOpFieldsValuesTuple({rule_tuple_key, SET_COMMAND, getDefaultRuleFieldValueTuples()})); // Update request on exact rule without change will not need SAI call - EnqueueRuleTuple(swss::KeyOpFieldsValuesTuple({rule_tuple_key, SET_COMMAND, getDefaultRuleFieldValueTuples()})); + EnqueueRuleTuple(std::string(kAclIngressTableName), + swss::KeyOpFieldsValuesTuple({rule_tuple_key, SET_COMMAND, getDefaultRuleFieldValueTuples()})); // Drain rule tuples to process SET request EXPECT_CALL(mock_sai_acl_, create_acl_entry(_, _, _, _)) @@ -2348,7 +2350,8 @@ TEST_F(AclManagerTest, DrainRuleTuplesToProcessSetDelRequestSucceeds) "ipv6_dst\":\"fdf8:f53b:82e4::53 & " "fdf8:f53b:82e4::53\",\"priority\":15}"; const auto &rule_tuple_key = std::string(kAclIngressTableName) + kTableKeyDelimiter + acl_rule_json_key; - EnqueueRuleTuple(swss::KeyOpFieldsValuesTuple({rule_tuple_key, SET_COMMAND, attributes})); + EnqueueRuleTuple(std::string(kAclIngressTableName), + swss::KeyOpFieldsValuesTuple({rule_tuple_key, SET_COMMAND, attributes})); // Drain ACL rule tuple to process SET request EXPECT_CALL(mock_sai_acl_, create_acl_entry(_, _, _, _)) @@ -2385,7 +2388,8 @@ TEST_F(AclManagerTest, DrainRuleTuplesToProcessSetDelRequestSucceeds) // Drain ACL rule tuple to process DEL request attributes.clear(); - EnqueueRuleTuple(swss::KeyOpFieldsValuesTuple({rule_tuple_key, DEL_COMMAND, attributes})); + EnqueueRuleTuple(std::string(kAclIngressTableName), + swss::KeyOpFieldsValuesTuple({rule_tuple_key, DEL_COMMAND, attributes})); EXPECT_CALL(mock_sai_acl_, remove_acl_entry(Eq(kAclIngressRuleOid1))).WillOnce(Return(SAI_STATUS_SUCCESS)); EXPECT_CALL(mock_sai_acl_, remove_acl_counter(_)).WillOnce(Return(SAI_STATUS_SUCCESS)); @@ -2401,7 +2405,8 @@ TEST_F(AclManagerTest, DrainRuleTuplesToProcessSetRequestInvalidTableNameRuleKey "ipv6_dst\":\"fdf8:f53b:82e4::53 & " "fdf8:f53b:82e4::53\",\"priority\":15}"; auto rule_tuple_key = std::string("INVALID_TABLE_NAME") + kTableKeyDelimiter + acl_rule_json_key; - EnqueueRuleTuple(swss::KeyOpFieldsValuesTuple({rule_tuple_key, SET_COMMAND, attributes})); + EnqueueRuleTuple(std::string("INVALID_TABLE_NAME"), + swss::KeyOpFieldsValuesTuple({rule_tuple_key, SET_COMMAND, attributes})); // Drain rule tuple to process SET request with invalid ACL table name: // "INVALID_TABLE_NAME" DrainRuleTuples(); @@ -2417,7 +2422,8 @@ TEST_F(AclManagerTest, DrainRuleTuplesToProcessSetRequestInvalidTableNameRuleKey rule_tuple_key = std::string(kAclIngressTableName) + kTableKeyDelimiter + acl_rule_json_key; acl_rule_key = "match/ether_type=0x0800:match/ipv6_dst=fdf8:f53b:82e4::53 & " "fdf8:f53b:82e4::53"; - EnqueueRuleTuple(swss::KeyOpFieldsValuesTuple({rule_tuple_key, SET_COMMAND, attributes})); + EnqueueRuleTuple(std::string(kAclIngressTableName), + swss::KeyOpFieldsValuesTuple({rule_tuple_key, SET_COMMAND, attributes})); // Drain rule tuple to process SET request without priority field in rule // JSON key DrainRuleTuples(); @@ -2479,7 +2485,8 @@ TEST_F(AclManagerTest, DrainRuleTuplesWithInvalidCommand) "ipv6_dst\":\"fdf8:f53b:82e4::53 & " "fdf8:f53b:82e4::53\",\"priority\":15}"; const auto &rule_tuple_key = std::string(kAclIngressTableName) + kTableKeyDelimiter + acl_rule_json_key; - EnqueueRuleTuple(swss::KeyOpFieldsValuesTuple({rule_tuple_key, "INVALID_COMMAND", attributes})); + EnqueueRuleTuple(std::string(kAclIngressTableName), + swss::KeyOpFieldsValuesTuple({rule_tuple_key, "INVALID_COMMAND", attributes})); DrainRuleTuples(); const auto &acl_rule_key = "match/ether_type=0x0800:match/ipv6_dst=fdf8:f53b:82e4::53 & " "fdf8:f53b:82e4::53:priority=15"; @@ -4761,7 +4768,8 @@ TEST_F(AclManagerTest, AclRuleVerifyStateTest) "\"match/in_ports\": \"Ethernet1,Ethernet2\", \"match/out_ports\": " "\"Ethernet4,Ethernet5\", \"priority\":15}"; const auto &rule_tuple_key = std::string(kAclIngressTableName) + kTableKeyDelimiter + acl_rule_json_key; - EnqueueRuleTuple(swss::KeyOpFieldsValuesTuple({rule_tuple_key, SET_COMMAND, attributes})); + EnqueueRuleTuple(std::string(kAclIngressTableName), + swss::KeyOpFieldsValuesTuple({rule_tuple_key, SET_COMMAND, attributes})); EXPECT_CALL(mock_sai_acl_, create_acl_entry(_, _, _, _)) .WillOnce(DoAll(SetArgPointee<0>(kAclIngressRuleOid1), Return(SAI_STATUS_SUCCESS))); EXPECT_CALL(mock_sai_acl_, create_acl_counter(_, _, _, _)) @@ -5189,7 +5197,8 @@ TEST_F(AclManagerTest, AclRuleVerifyStateAsicDbTest) "ipv6_dst\":\"fdf8:f53b:82e4::53 & " "fdf8:f53b:82e4::53\",\"priority\":15}"; const auto &rule_tuple_key = std::string(kAclIngressTableName) + kTableKeyDelimiter + acl_rule_json_key; - EnqueueRuleTuple(swss::KeyOpFieldsValuesTuple({rule_tuple_key, SET_COMMAND, attributes})); + EnqueueRuleTuple(std::string(kAclIngressTableName), + swss::KeyOpFieldsValuesTuple({rule_tuple_key, SET_COMMAND, attributes})); EXPECT_CALL(mock_sai_acl_, create_acl_entry(_, _, _, _)) .WillOnce(DoAll(SetArgPointee<0>(kAclIngressRuleOid1), Return(SAI_STATUS_SUCCESS))); EXPECT_CALL(mock_sai_acl_, create_acl_counter(_, _, _, _)) diff --git a/orchagent/p4orch/tests/gre_tunnel_manager_test.cpp b/orchagent/p4orch/tests/gre_tunnel_manager_test.cpp index 0177eca80b..937c329fbe 100644 --- a/orchagent/p4orch/tests/gre_tunnel_manager_test.cpp +++ b/orchagent/p4orch/tests/gre_tunnel_manager_test.cpp @@ -215,7 +215,7 @@ class GreTunnelManagerTest : public ::testing::Test void Enqueue(const swss::KeyOpFieldsValuesTuple &entry) { - gre_tunnel_manager_.enqueue(entry); + gre_tunnel_manager_.enqueue(APP_P4RT_TUNNEL_TABLE_NAME, entry); } void Drain() @@ -906,4 +906,4 @@ TEST_F(GreTunnelManagerTest, VerifyStateAsicDbTest) p4_tunnel_entry->encap_src_ip = swss::IpAddress("1.2.3.4"); EXPECT_FALSE(VerifyState(db_key, attributes).empty()); p4_tunnel_entry->encap_src_ip = swss::IpAddress("2607:f8b0:8096:3110::1"); -} \ No newline at end of file +} diff --git a/orchagent/p4orch/tests/l3_admit_manager_test.cpp b/orchagent/p4orch/tests/l3_admit_manager_test.cpp index 6d0d67dd0e..f1d85cdded 100644 --- a/orchagent/p4orch/tests/l3_admit_manager_test.cpp +++ b/orchagent/p4orch/tests/l3_admit_manager_test.cpp @@ -178,7 +178,7 @@ class L3AdmitManagerTest : public ::testing::Test void Enqueue(const swss::KeyOpFieldsValuesTuple &entry) { - l3_admit_manager_.enqueue(entry); + l3_admit_manager_.enqueue(APP_P4RT_L3_ADMIT_TABLE_NAME, entry); } void Drain() @@ -650,4 +650,4 @@ TEST_F(L3AdmitManagerTest, VerifyStateAsicDbTest) swss::FieldValueTuple{"SAI_MY_MAC_ATTR_MAC_ADDRESS", kP4L3AdmitAppDbEntry1.mac_address_data.to_string()}, swss::FieldValueTuple{"SAI_MY_MAC_ATTR_MAC_ADDRESS_MASK", "FF:FF:FF:FF:00:00"}, swss::FieldValueTuple{"SAI_MY_MAC_ATTR_PRIORITY", "2030"}}); -} \ No newline at end of file +} diff --git a/orchagent/p4orch/tests/mirror_session_manager_test.cpp b/orchagent/p4orch/tests/mirror_session_manager_test.cpp index bc5563a078..19b1e33f1b 100644 --- a/orchagent/p4orch/tests/mirror_session_manager_test.cpp +++ b/orchagent/p4orch/tests/mirror_session_manager_test.cpp @@ -217,7 +217,7 @@ class MirrorSessionManagerTest : public ::testing::Test void Enqueue(const swss::KeyOpFieldsValuesTuple &entry) { - return mirror_session_manager_.enqueue(entry); + return mirror_session_manager_.enqueue(APP_P4RT_MIRROR_SESSION_TABLE_NAME, entry); } void Drain() diff --git a/orchagent/p4orch/tests/neighbor_manager_test.cpp b/orchagent/p4orch/tests/neighbor_manager_test.cpp index ae91f4f567..37cf0162be 100644 --- a/orchagent/p4orch/tests/neighbor_manager_test.cpp +++ b/orchagent/p4orch/tests/neighbor_manager_test.cpp @@ -130,7 +130,7 @@ class NeighborManagerTest : public ::testing::Test void Enqueue(const swss::KeyOpFieldsValuesTuple &entry) { - neighbor_manager_.enqueue(entry); + neighbor_manager_.enqueue(APP_P4RT_NEIGHBOR_TABLE_NAME, entry); } void Drain() diff --git a/orchagent/p4orch/tests/next_hop_manager_test.cpp b/orchagent/p4orch/tests/next_hop_manager_test.cpp index 91c59a9b69..d9e6073a06 100644 --- a/orchagent/p4orch/tests/next_hop_manager_test.cpp +++ b/orchagent/p4orch/tests/next_hop_manager_test.cpp @@ -267,7 +267,7 @@ class NextHopManagerTest : public ::testing::Test void Enqueue(const swss::KeyOpFieldsValuesTuple &entry) { - next_hop_manager_.enqueue(entry); + next_hop_manager_.enqueue(APP_P4RT_NEXTHOP_TABLE_NAME, entry); } void Drain() diff --git a/orchagent/p4orch/tests/route_manager_test.cpp b/orchagent/p4orch/tests/route_manager_test.cpp index 06095d5ebe..640cf18ccf 100644 --- a/orchagent/p4orch/tests/route_manager_test.cpp +++ b/orchagent/p4orch/tests/route_manager_test.cpp @@ -242,9 +242,9 @@ class RouteManagerTest : public ::testing::Test return route_manager_.deleteRouteEntries(route_entries); } - void Enqueue(const swss::KeyOpFieldsValuesTuple &entry) + void Enqueue(const std::string &table_name, const swss::KeyOpFieldsValuesTuple &entry) { - route_manager_.enqueue(entry); + route_manager_.enqueue(table_name, entry); } void Drain() @@ -2469,7 +2469,7 @@ TEST_F(RouteManagerTest, RouteCreateAndUpdateInDrainSucceeds) p4_oid_mapper_.setOID(SAI_OBJECT_TYPE_NEXT_HOP, KeyGenerator::generateNextHopKey(kNexthopId1), kNexthopOid1); auto key_op_fvs_1 = GenerateKeyOpFieldsValuesTuple(gVrfName, swss_ipv4_route_prefix, SET_COMMAND, p4orch::kSetNexthopId, kNexthopId1); - Enqueue(key_op_fvs_1); + Enqueue(APP_P4RT_IPV4_TABLE_NAME, key_op_fvs_1); std::vector exp_status{SAI_STATUS_SUCCESS}; EXPECT_CALL(mock_sai_route_, create_route_entries(_, _, _, _, _, _)) .WillOnce(DoAll(SetArrayArgument<5>(exp_status.begin(), exp_status.end()), Return(SAI_STATUS_SUCCESS))); @@ -2482,7 +2482,7 @@ TEST_F(RouteManagerTest, RouteCreateAndUpdateInDrainSucceeds) p4_oid_mapper_.setOID(SAI_OBJECT_TYPE_NEXT_HOP, KeyGenerator::generateNextHopKey(kNexthopId2), kNexthopOid2); auto key_op_fvs_2 = GenerateKeyOpFieldsValuesTuple(gVrfName, swss_ipv4_route_prefix, SET_COMMAND, p4orch::kSetNexthopId, kNexthopId2); - Enqueue(key_op_fvs_2); + Enqueue(APP_P4RT_IPV4_TABLE_NAME, key_op_fvs_2); EXPECT_CALL(mock_sai_route_, set_route_entries_attribute(_, _, _, _, _)) .WillOnce(DoAll(SetArrayArgument<4>(exp_status.begin(), exp_status.end()), Return(SAI_STATUS_SUCCESS))); EXPECT_CALL(publisher_, publish(Eq(APP_P4RT_TABLE_NAME), Eq(kfvKey(key_op_fvs_2)), @@ -2505,7 +2505,7 @@ TEST_F(RouteManagerTest, RouteCreateAndUpdateInDrainSucceeds) auto key_op_fvs_3 = GenerateKeyOpFieldsValuesTuple(gVrfName, swss_ipv4_route_prefix, SET_COMMAND, p4orch::kSetMetadataAndDrop, "", kMetadata1); - Enqueue(key_op_fvs_3); + Enqueue(APP_P4RT_IPV4_TABLE_NAME, key_op_fvs_3); EXPECT_CALL(mock_sai_route_, set_route_entries_attribute(_, _, _, _, _)) .WillRepeatedly(DoAll(SetArrayArgument<4>(exp_status.begin(), exp_status.end()), Return(SAI_STATUS_SUCCESS))); EXPECT_CALL(publisher_, publish(Eq(APP_P4RT_TABLE_NAME), Eq(kfvKey(key_op_fvs_3)), @@ -2530,7 +2530,7 @@ TEST_F(RouteManagerTest, RouteCreateAndDeleteInDrainSucceeds) p4_oid_mapper_.setOID(SAI_OBJECT_TYPE_NEXT_HOP, KeyGenerator::generateNextHopKey(kNexthopId1), kNexthopOid1); auto key_op_fvs_1 = GenerateKeyOpFieldsValuesTuple(gVrfName, swss_ipv4_route_prefix, SET_COMMAND, p4orch::kSetNexthopId, kNexthopId1); - Enqueue(key_op_fvs_1); + Enqueue(APP_P4RT_IPV4_TABLE_NAME, key_op_fvs_1); std::vector exp_status{SAI_STATUS_SUCCESS}; EXPECT_CALL(mock_sai_route_, create_route_entries(_, _, _, _, _, _)) .WillOnce(DoAll(SetArrayArgument<5>(exp_status.begin(), exp_status.end()), Return(SAI_STATUS_SUCCESS))); @@ -2541,7 +2541,7 @@ TEST_F(RouteManagerTest, RouteCreateAndDeleteInDrainSucceeds) Drain(); auto key_op_fvs_2 = GenerateKeyOpFieldsValuesTuple(gVrfName, swss_ipv4_route_prefix, DEL_COMMAND, "", ""); - Enqueue(key_op_fvs_2); + Enqueue(APP_P4RT_IPV4_TABLE_NAME, key_op_fvs_2); EXPECT_CALL(mock_sai_route_, remove_route_entries(_, _, _, _)) .WillOnce(DoAll(SetArrayArgument<3>(exp_status.begin(), exp_status.end()), Return(SAI_STATUS_SUCCESS))); EXPECT_CALL(publisher_, publish(Eq(APP_P4RT_TABLE_NAME), Eq(kfvKey(key_op_fvs_2)), @@ -2566,10 +2566,10 @@ TEST_F(RouteManagerTest, UpdateFailsWhenCreateAndUpdateTheSameRouteInDrain) p4_oid_mapper_.setOID(SAI_OBJECT_TYPE_NEXT_HOP, KeyGenerator::generateNextHopKey(kNexthopId2), kNexthopOid2); auto key_op_fvs_1 = GenerateKeyOpFieldsValuesTuple(gVrfName, swss::IpPrefix(kIpv4Prefix), SET_COMMAND, p4orch::kSetNexthopId, kNexthopId1); - Enqueue(key_op_fvs_1); + Enqueue(APP_P4RT_IPV4_TABLE_NAME, key_op_fvs_1); auto key_op_fvs_2 = GenerateKeyOpFieldsValuesTuple(gVrfName, swss::IpPrefix(kIpv4Prefix), SET_COMMAND, p4orch::kSetNexthopId, kNexthopId2); - Enqueue(key_op_fvs_2); + Enqueue(APP_P4RT_IPV4_TABLE_NAME, key_op_fvs_2); std::vector exp_status{SAI_STATUS_SUCCESS}; EXPECT_CALL(mock_sai_route_, create_route_entries(_, _, _, _, _, _)) @@ -2603,9 +2603,9 @@ TEST_F(RouteManagerTest, DeleteFailsWhenCreateAndDeleteTheSameRouteInDrain) p4_oid_mapper_.setOID(SAI_OBJECT_TYPE_NEXT_HOP, KeyGenerator::generateNextHopKey(kNexthopId1), kNexthopOid1); auto key_op_fvs_1 = GenerateKeyOpFieldsValuesTuple(gVrfName, swss::IpPrefix(kIpv4Prefix), SET_COMMAND, p4orch::kSetNexthopId, kNexthopId1); - Enqueue(key_op_fvs_1); + Enqueue(APP_P4RT_IPV4_TABLE_NAME, key_op_fvs_1); auto key_op_fvs_2 = GenerateKeyOpFieldsValuesTuple(gVrfName, swss::IpPrefix(kIpv4Prefix), DEL_COMMAND, "", ""); - Enqueue(key_op_fvs_2); + Enqueue(APP_P4RT_IPV4_TABLE_NAME, key_op_fvs_2); std::vector exp_status{SAI_STATUS_SUCCESS}; EXPECT_CALL(mock_sai_route_, create_route_entries(_, _, _, _, _, _)) @@ -2639,7 +2639,7 @@ TEST_F(RouteManagerTest, RouteCreateInDrainSucceedsWhenVrfIsEmpty) p4_oid_mapper_.setOID(SAI_OBJECT_TYPE_NEXT_HOP, KeyGenerator::generateNextHopKey(kNexthopId1), kNexthopOid1); auto key_op_fvs = GenerateKeyOpFieldsValuesTuple(kDefaultVrfName, swss::IpPrefix(kIpv4Prefix), SET_COMMAND, p4orch::kSetNexthopId, kNexthopId1); - Enqueue(key_op_fvs); + Enqueue(APP_P4RT_IPV4_TABLE_NAME, key_op_fvs); sai_route_entry_t exp_sai_route_entry; exp_sai_route_entry.switch_id = gSwitchId; @@ -2678,7 +2678,7 @@ TEST_F(RouteManagerTest, DeserializeRouteEntryInDrainFails) const std::string kKeyPrefix = std::string(APP_P4RT_IPV4_TABLE_NAME) + kTableKeyDelimiter; auto key_op_fvs = swss::KeyOpFieldsValuesTuple(kKeyPrefix + "{{{{{{{{{{{{", SET_COMMAND, std::vector{}); - Enqueue(key_op_fvs); + Enqueue(APP_P4RT_IPV4_TABLE_NAME, key_op_fvs); EXPECT_CALL(publisher_, publish(Eq(APP_P4RT_TABLE_NAME), Eq(kfvKey(key_op_fvs)), FieldValueTupleArrayEq(kfvFieldsValues(key_op_fvs)), Eq(StatusCode::SWSS_RC_INVALID_PARAM), Eq(true))) @@ -2691,7 +2691,7 @@ TEST_F(RouteManagerTest, ValidateRouteEntryInDrainFailsWhenVrfDoesNotExist) p4_oid_mapper_.setOID(SAI_OBJECT_TYPE_NEXT_HOP, KeyGenerator::generateNextHopKey(kNexthopId1), kNexthopOid1); auto key_op_fvs = GenerateKeyOpFieldsValuesTuple("Invalid-Vrf", swss::IpPrefix(kIpv4Prefix), SET_COMMAND, p4orch::kSetNexthopId, kNexthopId1); - Enqueue(key_op_fvs); + Enqueue(APP_P4RT_IPV4_TABLE_NAME, key_op_fvs); EXPECT_CALL(publisher_, publish(Eq(APP_P4RT_TABLE_NAME), Eq(kfvKey(key_op_fvs)), FieldValueTupleArrayEq(kfvFieldsValues(key_op_fvs)), Eq(StatusCode::SWSS_RC_NOT_FOUND), Eq(true))) @@ -2703,7 +2703,7 @@ TEST_F(RouteManagerTest, ValidateRouteEntryInDrainFailsWhenNexthopDoesNotExist) { auto key_op_fvs = GenerateKeyOpFieldsValuesTuple(gVrfName, swss::IpPrefix(kIpv4Prefix), SET_COMMAND, p4orch::kSetNexthopId, kNexthopId1); - Enqueue(key_op_fvs); + Enqueue(APP_P4RT_IPV4_TABLE_NAME, key_op_fvs); EXPECT_CALL(publisher_, publish(Eq(APP_P4RT_TABLE_NAME), Eq(kfvKey(key_op_fvs)), FieldValueTupleArrayEq(kfvFieldsValues(key_op_fvs)), Eq(StatusCode::SWSS_RC_NOT_FOUND), Eq(true))) @@ -2717,7 +2717,7 @@ TEST_F(RouteManagerTest, InvalidateSetRouteEntryInDrainFails) // No nexthop ID with kSetNexthopId action. auto key_op_fvs = GenerateKeyOpFieldsValuesTuple(gVrfName, swss::IpPrefix(kIpv4Prefix), SET_COMMAND, p4orch::kSetNexthopId, ""); - Enqueue(key_op_fvs); + Enqueue(APP_P4RT_IPV4_TABLE_NAME, key_op_fvs); EXPECT_CALL(publisher_, publish(Eq(APP_P4RT_TABLE_NAME), Eq(kfvKey(key_op_fvs)), FieldValueTupleArrayEq(kfvFieldsValues(key_op_fvs)), Eq(StatusCode::SWSS_RC_INVALID_PARAM), Eq(true))) @@ -2730,7 +2730,7 @@ TEST_F(RouteManagerTest, InvalidateDelRouteEntryInDrainFails) // Route does not exist. auto key_op_fvs = GenerateKeyOpFieldsValuesTuple(gVrfName, swss::IpPrefix(kIpv4Prefix), DEL_COMMAND, p4orch::kSetNexthopId, kNexthopId1); - Enqueue(key_op_fvs); + Enqueue(APP_P4RT_IPV4_TABLE_NAME, key_op_fvs); EXPECT_CALL(publisher_, publish(Eq(APP_P4RT_TABLE_NAME), Eq(kfvKey(key_op_fvs)), FieldValueTupleArrayEq(kfvFieldsValues(key_op_fvs)), Eq(StatusCode::SWSS_RC_NOT_FOUND), Eq(true))) @@ -2749,7 +2749,7 @@ TEST_F(RouteManagerTest, InvalidCommandInDrainFails) attributes.push_back(swss::FieldValueTuple{p4orch::kAction, p4orch::kSetNexthopId}); attributes.push_back(swss::FieldValueTuple{prependParamField(p4orch::kNexthopId), kNexthopId1}); auto key_op_fvs = swss::KeyOpFieldsValuesTuple(kKeyPrefix + j.dump(), "INVALID_COMMAND", attributes); - Enqueue(key_op_fvs); + Enqueue(APP_P4RT_IPV4_TABLE_NAME, key_op_fvs); EXPECT_CALL(publisher_, publish(Eq(APP_P4RT_TABLE_NAME), Eq(kfvKey(key_op_fvs)), FieldValueTupleArrayEq(kfvFieldsValues(key_op_fvs)), Eq(StatusCode::SWSS_RC_INVALID_PARAM), Eq(true))) diff --git a/orchagent/p4orch/tests/router_interface_manager_test.cpp b/orchagent/p4orch/tests/router_interface_manager_test.cpp index 9c81310e5f..d1c7330cc7 100644 --- a/orchagent/p4orch/tests/router_interface_manager_test.cpp +++ b/orchagent/p4orch/tests/router_interface_manager_test.cpp @@ -164,7 +164,7 @@ class RouterInterfaceManagerTest : public ::testing::Test void Enqueue(const swss::KeyOpFieldsValuesTuple &entry) { - router_intf_manager_.enqueue(entry); + router_intf_manager_.enqueue(APP_P4RT_ROUTER_INTERFACE_TABLE_NAME, entry); } void Drain() @@ -965,4 +965,4 @@ TEST_F(RouterInterfaceManagerTest, VerifyStateAsicDbTest) router_intf_entry_ptr->port_name = "Ethernet8"; EXPECT_FALSE(VerifyState(db_key, attributes).empty()); router_intf_entry_ptr->port_name = "Ethernet7"; -} \ No newline at end of file +} diff --git a/orchagent/p4orch/tests/test_main.cpp b/orchagent/p4orch/tests/test_main.cpp index e1edb3584c..91b4296a29 100644 --- a/orchagent/p4orch/tests/test_main.cpp +++ b/orchagent/p4orch/tests/test_main.cpp @@ -78,6 +78,7 @@ sai_udf_api_t *sai_udf_api; sai_tunnel_api_t *sai_tunnel_api; sai_my_mac_api_t *sai_my_mac_api; sai_counter_api_t *sai_counter_api; +sai_generic_programmable_api_t *sai_generic_programmable_api; namespace { @@ -170,6 +171,7 @@ int main(int argc, char *argv[]) sai_my_mac_api_t my_mac_api; sai_tunnel_api_t tunnel_api; sai_counter_api_t counter_api; + sai_generic_programmable_api_t generic_programmable_api; sai_router_intfs_api = &router_intfs_api; sai_neighbor_api = &neighbor_api; sai_next_hop_api = &next_hop_api; @@ -185,6 +187,7 @@ int main(int argc, char *argv[]) sai_my_mac_api = &my_mac_api; sai_tunnel_api = &tunnel_api; sai_counter_api = &counter_api; + sai_generic_programmable_api = &generic_programmable_api; swss::DBConnector appl_db("APPL_DB", 0); swss::DBConnector state_db("STATE_DB", 0); diff --git a/orchagent/p4orch/tests/wcmp_manager_test.cpp b/orchagent/p4orch/tests/wcmp_manager_test.cpp index cf8d22b684..95e8ecb70f 100644 --- a/orchagent/p4orch/tests/wcmp_manager_test.cpp +++ b/orchagent/p4orch/tests/wcmp_manager_test.cpp @@ -273,7 +273,7 @@ class WcmpManagerTest : public ::testing::Test void Enqueue(const swss::KeyOpFieldsValuesTuple &entry) { - wcmp_group_manager_->enqueue(entry); + wcmp_group_manager_->enqueue(APP_P4RT_WCMP_GROUP_TABLE_NAME, entry); } void Drain() diff --git a/orchagent/p4orch/wcmp_manager.cpp b/orchagent/p4orch/wcmp_manager.cpp index 446a58e021..257dd7d2a1 100644 --- a/orchagent/p4orch/wcmp_manager.cpp +++ b/orchagent/p4orch/wcmp_manager.cpp @@ -734,7 +734,34 @@ void WcmpManager::updatePortOperStatusMap(const std::string &port, const sai_por port_oper_status_map[port] = status; } -void WcmpManager::enqueue(const swss::KeyOpFieldsValuesTuple &entry) +ReturnCode WcmpManager::getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) +{ + std::string value; + + try + { + nlohmann::json j = nlohmann::json::parse(json_key); + if (j.find(prependMatchField(p4orch::kWcmpGroupId)) != j.end()) + { + value = j.at(prependMatchField(p4orch::kWcmpGroupId)).get(); + object_key = KeyGenerator::generateWcmpGroupKey(value); + object_type = SAI_OBJECT_TYPE_NEXT_HOP_GROUP; + return ReturnCode(); + } + else + { + SWSS_LOG_ERROR("%s match parameter absent: required for dependent object query", p4orch::kWcmpGroupId); + } + } + catch (std::exception &ex) + { + SWSS_LOG_ERROR("json_key parse error"); + } + + return StatusCode::SWSS_RC_INVALID_PARAM; +} + +void WcmpManager::enqueue(const std::string &table_name, const swss::KeyOpFieldsValuesTuple &entry) { m_entries.push_back(entry); } diff --git a/orchagent/p4orch/wcmp_manager.h b/orchagent/p4orch/wcmp_manager.h index abf5a091f1..64fd4283e4 100644 --- a/orchagent/p4orch/wcmp_manager.h +++ b/orchagent/p4orch/wcmp_manager.h @@ -69,9 +69,10 @@ class WcmpManager : public ObjectManagerInterface virtual ~WcmpManager() = default; - void enqueue(const swss::KeyOpFieldsValuesTuple &entry) override; + void enqueue(const std::string &table_name, const swss::KeyOpFieldsValuesTuple &entry) override; void drain() override; std::string verifyState(const std::string &key, const std::vector &tuple) override; + ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) override; // Prunes next hop members egressing through the given port. void pruneNextHops(const std::string &port); diff --git a/orchagent/saihelper.cpp b/orchagent/saihelper.cpp index b216e0dccd..2df9ebce22 100644 --- a/orchagent/saihelper.cpp +++ b/orchagent/saihelper.cpp @@ -72,6 +72,7 @@ sai_l2mc_group_api_t* sai_l2mc_group_api; sai_counter_api_t* sai_counter_api; sai_bfd_api_t* sai_bfd_api; sai_my_mac_api_t* sai_my_mac_api; +sai_generic_programmable_api_t* sai_generic_programmable_api; extern sai_object_id_t gSwitchId; extern bool gSairedisRecord; @@ -201,6 +202,7 @@ void initSaiApi() sai_api_query(SAI_API_COUNTER, (void **)&sai_counter_api); sai_api_query(SAI_API_BFD, (void **)&sai_bfd_api); sai_api_query(SAI_API_MY_MAC, (void **)&sai_my_mac_api); + sai_api_query(SAI_API_GENERIC_PROGRAMMABLE, (void **)&sai_generic_programmable_api); sai_log_set(SAI_API_SWITCH, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_BRIDGE, SAI_LOG_LEVEL_NOTICE); @@ -239,6 +241,7 @@ void initSaiApi() sai_log_set(SAI_API_COUNTER, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_BFD, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_MY_MAC, SAI_LOG_LEVEL_NOTICE); + sai_log_set(SAI_API_GENERIC_PROGRAMMABLE, SAI_LOG_LEVEL_NOTICE); } void initSaiRedis(const string &record_location, const std::string &record_filename) diff --git a/tests/mock_tests/Makefile.am b/tests/mock_tests/Makefile.am index 2a1650d700..16f5e429ff 100644 --- a/tests/mock_tests/Makefile.am +++ b/tests/mock_tests/Makefile.am @@ -111,6 +111,7 @@ tests_SOURCES += $(DEBUG_CTR_DIR)/debug_counter.cpp $(DEBUG_CTR_DIR)/drop_counte tests_SOURCES += $(P4_ORCH_DIR)/p4orch.cpp \ $(P4_ORCH_DIR)/p4orch_util.cpp \ $(P4_ORCH_DIR)/p4oidmapper.cpp \ + $(P4_ORCH_DIR)/tables_definition_manager.cpp \ $(P4_ORCH_DIR)/router_interface_manager.cpp \ $(P4_ORCH_DIR)/neighbor_manager.cpp \ $(P4_ORCH_DIR)/next_hop_manager.cpp \ @@ -122,6 +123,7 @@ tests_SOURCES += $(P4_ORCH_DIR)/p4orch.cpp \ $(P4_ORCH_DIR)/mirror_session_manager.cpp \ $(P4_ORCH_DIR)/gre_tunnel_manager.cpp \ $(P4_ORCH_DIR)/l3_admit_manager.cpp \ + $(P4_ORCH_DIR)/ext_tables_manager.cpp \ $(P4_ORCH_DIR)/tests/mock_sai_switch.cpp tests_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_GTEST) $(CFLAGS_SAI) diff --git a/tests/p4rt/tables_definition.py b/tests/p4rt/tables_definition.py new file mode 100644 index 0000000000..fe3a077def --- /dev/null +++ b/tests/p4rt/tables_definition.py @@ -0,0 +1,35 @@ +from swsscommon import swsscommon + +import util +import json + + +class P4RtTableDefinitionWrapper(util.DBInterface): + """Interface to interact with APP DB for P4RT tables definition.""" + + # database constants + APP_DB_TBL_NAME = swsscommon.APP_P4RT_TABLE_NAME + TBL_NAME = swsscommon.APP_P4RT_TABLES_DEFINITION_TABLE_NAME + + # attribute fields for tables definition object + INFO_FIELD = "info" + + # tables definition object's attribute values + INFO_VALUE = "{\"tables\":[{\"actions\":[{\"alias\":\"drop\",\"id\":16777222,\"name\":\"ingress.routing.drop\",\"params\":null},{\"alias\":\"set_nexthop_id\",\"id\":16777221,\"name\":\"ingress.routing.set_nexthop_id\",\"params\":[{\"bitwidth\":0,\"format\":\"STRING\",\"id\":1,\"name\":\"nexthop_id\",\"references\":[{\"match\":\"nexthop_id\",\"table\":\"nexthop_table\"}]}]},{\"alias\":\"set_wcmp_group_id\",\"id\":16777220,\"name\":\"ingress.routing.set_wcmp_group_id\",\"params\":[{\"bitwidth\":0,\"format\":\"STRING\",\"id\":1,\"name\":\"wcmp_group_id\",\"references\":[{\"match\":\"wcmp_group_id\",\"table\":\"wcmp_group_table\"}]}]}],\"alias\":\"vipv4_table\",\"counter/unit\":\"BOTH\",\"id\":33554500,\"matchFields\":[{\"bitwidth\":32,\"format\":\"IPV4\",\"id\":1,\"name\":\"ipv4_dst\",\"references\":null}],\"name\":\"ingress.routing.vipv4_table\"}]}" + + + def generate_app_db_key(self, context): + d = {} + d["context"] = context + key = json.dumps(d, separators=(",", ":")) + return self.TBL_NAME + ":" + key + + + # create tables definition set + def create_tables_definition(self, info=None): + info = info or self.INFO_VALUE + attr_list = [(self.INFO_FIELD, info)] + tables_definition_key = self.generate_app_db_key("0") + self.set_app_db_entry(tables_definition_key, attr_list) + return tables_definition_key, attr_list + diff --git a/tests/p4rt/test_viplb.py b/tests/p4rt/test_viplb.py new file mode 100644 index 0000000000..b2c8203405 --- /dev/null +++ b/tests/p4rt/test_viplb.py @@ -0,0 +1,247 @@ +from swsscommon import swsscommon + +import pytest +import json +import util +import l3 +import viplb +import tables_definition + + +class TestP4RTVIPLB(object): + + def _set_up(self, dvs): + self._p4rt_tables_definition_obj = tables_definition.P4RtTableDefinitionWrapper() + self._p4rt_router_intf_obj = l3.P4RtRouterInterfaceWrapper() + self._p4rt_neighbor_obj = l3.P4RtNeighborWrapper() + self._p4rt_nexthop_obj = l3.P4RtNextHopWrapper() + self._p4rt_viplb_obj = viplb.P4RtVIPLBWrapper() + + self._p4rt_tables_definition_obj.set_up_databases(dvs) + self._p4rt_router_intf_obj.set_up_databases(dvs) + self._p4rt_neighbor_obj.set_up_databases(dvs) + self._p4rt_nexthop_obj.set_up_databases(dvs) + self._p4rt_viplb_obj.set_up_databases(dvs) + self.response_consumer = swsscommon.NotificationConsumer( + self._p4rt_viplb_obj.appl_db, "APPL_DB_" + + swsscommon.APP_P4RT_TABLE_NAME + "_RESPONSE_CHANNEL" + ) + + def test_VIPv4LBWithGoodNexthopAddUpdateDeletePass(self, dvs, testlog): + # Initialize L3 objects and database connectors. + self._set_up(dvs) + + # Create tables definition AppDb entry + tables_definition_key, attr_list = ( + self._p4rt_tables_definition_obj.create_tables_definition() + ) + util.verify_response(self.response_consumer, tables_definition_key, + attr_list, "SWSS_RC_SUCCESS") + + # Set IP type for viplb object. + self._p4rt_viplb_obj.set_ip_type("IPV4") + + # Maintain list of original Application and ASIC DB entries before + # adding new entry. + db_list = ((self._p4rt_nexthop_obj.asic_db, + self._p4rt_nexthop_obj.ASIC_DB_TBL_NAME),) + self._p4rt_nexthop_obj.get_original_redis_entries(db_list) + db_list = ((self._p4rt_viplb_obj.appl_db, + "%s:%s" % (self._p4rt_viplb_obj.APP_DB_TBL_NAME, + self._p4rt_viplb_obj.TBL_NAME)), + (self._p4rt_viplb_obj.appl_state_db, + "%s:%s" % (self._p4rt_viplb_obj.APP_DB_TBL_NAME, + self._p4rt_viplb_obj.TBL_NAME)), + (self._p4rt_viplb_obj.asic_db, + self._p4rt_viplb_obj.ASIC_DB_TBL_NAME)) + self._p4rt_viplb_obj.get_original_redis_entries(db_list) + + # Fetch the original key to oid information from Redis DB. + key_to_oid_helper = util.KeyToOidDBHelper(dvs) + _, original_key_oid_info = key_to_oid_helper.get_db_info() + + # Create router interface. + router_interface_id, router_intf_key, attr_list = ( + self._p4rt_router_intf_obj.create_router_interface() + ) + util.verify_response(self.response_consumer, router_intf_key, + attr_list, "SWSS_RC_SUCCESS") + + # Verify that P4RT key to OID count incremented by 1 in Redis DB. + count = 1 + status, fvs = key_to_oid_helper.get_db_info() + assert status == True + assert len(fvs) == len(original_key_oid_info) + count + + # Create neighbor. + neighbor_id, neighbor_key, attr_list = ( + self._p4rt_neighbor_obj.create_neighbor() + ) + util.verify_response(self.response_consumer, neighbor_key, attr_list, + "SWSS_RC_SUCCESS") + + # Verify that P4RT key to OID count incremented by 1 in Redis DB. + count += 1 + status, fvs = key_to_oid_helper.get_db_info() + assert status == True + assert len(fvs) == len(original_key_oid_info) + count + + # Create nexthop. + first_nexthop_id, first_nexthop_key, attr_list = ( + self._p4rt_nexthop_obj.create_next_hop() + ) + util.verify_response(self.response_consumer, first_nexthop_key, attr_list, + "SWSS_RC_SUCCESS") + # get nexthop_oid of newly created nexthop + first_nexthop_oid = self._p4rt_nexthop_obj.get_newly_created_nexthop_oid() + assert first_nexthop_oid is not None + + # Verify that P4RT key to OID count incremented by 1 in Redis DB. + count += 1 + status, fvs = key_to_oid_helper.get_db_info() + assert status == True + assert len(fvs) == len(original_key_oid_info) + count + + # Create viplb. + viplb_key, attr_list = ( + self._p4rt_viplb_obj.create_viplb(first_nexthop_id) + ) + util.verify_response(self.response_consumer, viplb_key, attr_list, + "SWSS_RC_SUCCESS") + + # Verify that P4RT key to OID count incremented by 1 in Redis DB. + count += 1 + status, fvs = key_to_oid_helper.get_db_info() + assert status == True + assert len(fvs) == len(original_key_oid_info) + count + + # Query application database for viplb entries. + viplb_entries = util.get_keys( + self._p4rt_viplb_obj.appl_db, + self._p4rt_viplb_obj.APP_DB_TBL_NAME + ":" + self._p4rt_viplb_obj.TBL_NAME) + assert len(viplb_entries) == ( + self._p4rt_viplb_obj.get_original_appl_db_entries_count() + 1 + ) + + # Query application database for newly created viplb key. + (status, fvs) = util.get_key(self._p4rt_viplb_obj.appl_db, + self._p4rt_viplb_obj.APP_DB_TBL_NAME, + viplb_key) + assert status == True + util.verify_attr(fvs, attr_list) + + # Query application state database for viplb entries. + state_viplb_entries = util.get_keys( + self._p4rt_viplb_obj.appl_state_db, + self._p4rt_viplb_obj.APP_DB_TBL_NAME + ":" + self._p4rt_viplb_obj.TBL_NAME) + assert len(state_viplb_entries) == ( + self._p4rt_viplb_obj.get_original_appl_state_db_entries_count() + 1 + ) + + # Query application state database for newly created viplb key. + (status, fvs) = util.get_key(self._p4rt_viplb_obj.appl_state_db, + self._p4rt_viplb_obj.APP_DB_TBL_NAME, + viplb_key) + assert status == True + util.verify_attr(fvs, attr_list) + + + # get programmable_object_oid of newly created viplb + viplb_oid = self._p4rt_viplb_obj.get_newly_created_programmable_object_oid() + assert viplb_oid is not None + + + # Create another router interface. + router_interface_id, router_intf_key, attr_list = ( + self._p4rt_router_intf_obj.create_router_interface(router_interace_id="20") + ) + util.verify_response(self.response_consumer, router_intf_key, + attr_list, "SWSS_RC_SUCCESS") + + # Verify that P4RT key to OID count incremented by 1 in Redis DB. + count += 1 + status, fvs = key_to_oid_helper.get_db_info() + assert status == True + assert len(fvs) == len(original_key_oid_info) + count + + # Create another neighbor. + neighbor_id, neighbor_key, attr_list = ( + self._p4rt_neighbor_obj.create_neighbor(router_interface_id="20", neighbor_id="10.0.0.1") + ) + util.verify_response(self.response_consumer, neighbor_key, attr_list, + "SWSS_RC_SUCCESS") + + # Verify that P4RT key to OID count incremented by 1 in Redis DB. + count += 1 + status, fvs = key_to_oid_helper.get_db_info() + assert status == True + assert len(fvs) == len(original_key_oid_info) + count + + # Create another nexthop. + second_nexthop_id, second_nexthop_key, attr_list = ( + self._p4rt_nexthop_obj.create_next_hop(router_interface_id="20", neighbor_id="10.0.0.1", nexthop_id="16") + ) + util.verify_response(self.response_consumer, second_nexthop_key, attr_list, + "SWSS_RC_SUCCESS") + + # Verify that P4RT key to OID count incremented by 1 in Redis DB. + count += 1 + status, fvs = key_to_oid_helper.get_db_info() + assert status == True + assert len(fvs) == len(original_key_oid_info) + count + + # Update viplb. + viplb_key, attr_list = ( + self._p4rt_viplb_obj.create_viplb(second_nexthop_id) + ) + util.verify_response(self.response_consumer, viplb_key, attr_list, + "SWSS_RC_SUCCESS") + + + # Remove nexthop. + self._p4rt_nexthop_obj.remove_app_db_entry(first_nexthop_key) + util.verify_response(self.response_consumer, first_nexthop_key, [], + "SWSS_RC_SUCCESS") + + # Verify that P4RT key to OID count decremented by 1 in Redis DB. + count -= 1 + status, fvs = key_to_oid_helper.get_db_info() + assert status == True + assert len(fvs) == len(original_key_oid_info) + count + + + # Remove viplb entry. + self._p4rt_viplb_obj.remove_app_db_entry(viplb_key) + util.verify_response( + self.response_consumer, viplb_key, [], "SWSS_RC_SUCCESS") + + # Verify that P4RT key to OID count decremented by 1 in Redis DB. + count -= 1 + status, fvs = key_to_oid_helper.get_db_info() + assert status == True + assert len(fvs) == len(original_key_oid_info) + count + + + + def test_VIPv4LBWithBadNexthopAddUpdateDeletePass(self, dvs, testlog): + # Initialize L3 objects and database connectors. + self._set_up(dvs) + return + + # Create tables definition AppDb entry + tables_definition_key, attr_list = ( + self._p4rt_tables_definition_obj.create_tables_definition() + ) + util.verify_response(self.response_consumer, tables_definition_key, + attr_list, "SWSS_RC_SUCCESS") + + # Set IP type for viplb object. + self._p4rt_viplb_obj.set_ip_type("IPV4") + + # Create viplb. + viplb_key, attr_list = ( + self._p4rt_viplb_obj.create_viplb() + ) + util.verify_response(self.response_consumer, viplb_key, attr_list, + "SWSS_RC_INVALID_PARAM", "[OrchAgent] Cross-table reference valdiation failed, no OID found") + diff --git a/tests/p4rt/viplb.py b/tests/p4rt/viplb.py new file mode 100644 index 0000000000..06e61443fa --- /dev/null +++ b/tests/p4rt/viplb.py @@ -0,0 +1,74 @@ +from swsscommon import swsscommon + +import util +import json + + +class P4RtVIPLBWrapper(util.DBInterface): + """Interface to interact with APP DB and ASIC DB tables for P4RT viplb object.""" + + # database and SAI constants + APP_DB_TBL_NAME = swsscommon.APP_P4RT_TABLE_NAME + ASIC_DB_TBL_NAME = "ASIC_STATE:SAI_OBJECT_TYPE_GENERIC_PROGRAMMABLE" + SAI_ATTR_TYPE = "SAI_GENERIC_PROGRAMMABLE_ATTR_TYPE" + SAI_ATTR_OBJECT_NAME = "SAI_GENERIC_PROGRAMMABLE_ATTR_OBJECT_NAME" + SAI_ATTR_ENTRY = "SAI_GENERIC_PROGRAMMABLE_ATTR_ENTRY" + + # default viplb attribute values + DEFAULT_ACTION = "set_nexthop_id" + DEFAULT_NEXTHOP_ID = "18" + DEFAULT_DST = "10.11.12.0/24" + + # attribute fields for viplb object + NEXTHOP_ID_FIELD = "nexthop_id" + + def generate_app_db_key(self, dst): + assert self.ip_type is not None + d = {} + if self.ip_type == "IPV4": + d[util.prepend_match_field("ipv4_dst")] = dst + else: + d[util.prepend_match_field("ipv6_dst")] = dst + key = json.dumps(d, separators=(",", ":")) + return self.TBL_NAME + ":" + key + + def set_ip_type(self, ip_type): + assert ip_type in ("IPV4", "IPV6") + self.ip_type = ip_type + self.TBL_NAME = "EXT_V" + ip_type + "_TABLE" + + # Create entry + def create_viplb(self, nexthop_id=None, action=None, dst=None): + action = action or self.DEFAULT_ACTION + dst = dst or self.DEFAULT_DST + if action == "set_nexthop_id": + nexthop_id = nexthop_id or self.DEFAULT_NEXTHOP_ID + attr_list = [(self.ACTION_FIELD, action), + (util.prepend_param_field(self.NEXTHOP_ID_FIELD), + nexthop_id)] + else: + attr_list = [(self.ACTION_FIELD, action)] + viplb_key = self.generate_app_db_key(dst) + self.set_app_db_entry(viplb_key, attr_list) + return viplb_key, attr_list + + def get_newly_created_programmable_object_oid(self): + viplb_oid = None + viplb_entries = util.get_keys(self.asic_db, self.ASIC_DB_TBL_NAME) + for key in viplb_entries: + if key not in self._original_entries["{}:{}".format(self.asic_db, + self.ASIC_DB_TBL_NAME)]: + viplb_oid = key + break + return viplb_oid + + def get_original_appl_db_entries_count(self): + return len(self._original_entries["%s:%s" % (self.appl_db, + (self.APP_DB_TBL_NAME + ":" + + self.TBL_NAME))]) + + def get_original_appl_state_db_entries_count(self): + return len(self._original_entries["%s:%s" % (self.appl_state_db, + (self.APP_DB_TBL_NAME + ":" + + self.TBL_NAME))]) +