diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index 79cfe99f6d5..f60a0130dfd 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -55,7 +55,8 @@ orchagent_SOURCES = \ policerorch.cpp \ sfloworch.cpp \ chassisorch.cpp \ - debugcounterorch.cpp + debugcounterorch.cpp \ + errororch.cpp orchagent_SOURCES += flex_counter/flex_counter_manager.cpp flex_counter/flex_counter_stat_manager.cpp orchagent_SOURCES += debug_counter/debug_counter.cpp debug_counter/drop_counter.cpp diff --git a/orchagent/errororch.cpp b/orchagent/errororch.cpp new file mode 100644 index 00000000000..cb954876e21 --- /dev/null +++ b/orchagent/errororch.cpp @@ -0,0 +1,531 @@ +/* + * Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or + * its subsidiaries. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "logger.h" +#include "tokenize.h" +#include "errororch.h" +#include "errormap.h" +#include "notifier.h" +#include "sai_serialize.h" +#include "swss/json.hpp" +using json = nlohmann::json; + +#define EHF_OPERATION_SAIAPI_STATUS "saiapi_status" + +/* Error handling framework currently handling only the following + * tables and can be extended to other tables */ +static std::map m_ObjTableMap = { + {"SAI_OBJECT_TYPE_ROUTE_ENTRY", APP_ROUTE_TABLE_NAME}, + {"SAI_OBJECT_TYPE_NEIGHBOR_ENTRY", APP_NEIGH_TABLE_NAME}, +}; + +ErrorOrch::ErrorOrch(swss::DBConnector *asicDb, swss::DBConnector *errorDb, std::vector &tableNames) : + Orch(asicDb, tableNames) +{ + SWSS_LOG_ENTER(); + + m_errorDb = std::shared_ptr(errorDb); + /* Create Table objects for the requested tables */ + for(auto it : tableNames) + { + createTableObject(it); + } + + /* Clear ERROR DB entries request from user interface */ + m_errorFlushNotificationConsumer = std::unique_ptr(new swss::NotificationConsumer(asicDb, "FLUSH_ERROR_DB")); + auto errorNotifier = new Notifier(m_errorFlushNotificationConsumer.get(), this, "FLUSH_ERROR_DB"); + Orch::addExecutor(errorNotifier); + + /* Add SAI API status error notifications support from Syncd */ + m_errorNotificationConsumer = std::unique_ptr(new swss::NotificationConsumer(asicDb, "ERROR_NOTIFICATIONS")); + errorNotifier = new Notifier(m_errorNotificationConsumer.get(), this, "SYNCD_ERROR_NOTIFICATIONS"); + Orch::addExecutor(errorNotifier); + + /* Create notification channels through which errors are sent to + * the applications via error listerner */ + std::shared_ptr errorNotifications; + for(auto objTable : m_ObjTableMap) + { + std::string strChannel = getErrorListenerChannelName(objTable.second); + errorNotifications = std::make_shared(errorDb, strChannel); + m_TableChannel[objTable.second] = errorNotifications; + SWSS_LOG_INFO("Notification channel %s is created for %s", + strChannel.c_str(), objTable.second.c_str()); + } + SWSS_LOG_INFO("Ready to receive status notifications"); +} + +void ErrorOrch::doTask(swss::NotificationConsumer& consumer) +{ + SWSS_LOG_ENTER(); + + std::string op; + std::string data; + std::vector values; + + consumer.pop(op, data, values); + + SWSS_LOG_DEBUG("Received operation: %s data : %s", op.c_str(), data.c_str()); + if(&consumer == m_errorNotificationConsumer.get() && op == EHF_OPERATION_SAIAPI_STATUS) + { + json j = json::parse(data); + + std::string asicKey = j["key"]; + /* Extract SAI object type */ + const std::string &str_object_type = asicKey.substr(0, asicKey.find(":")); + + sai_object_type_t object_type; + sai_deserialize_object_type(str_object_type, object_type); + + /* Find out the table object based on the object type */ + if(m_ObjTableMap.find(str_object_type) == m_ObjTableMap.end()) + { + SWSS_LOG_INFO("Unsupported SAI object type %s", + str_object_type.c_str()); + return; + } + std::string tableName = m_ObjTableMap[str_object_type]; + + if(m_TableOrchMap.find(tableName) == m_TableOrchMap.end()) + { + SWSS_LOG_INFO("No registrants for %s mapping", tableName.c_str()); + return; + } + + Orch *orch; + orch = m_TableOrchMap[tableName]; + if(orch == NULL) + { + SWSS_LOG_INFO("Invalid Orch agent mapper object for %s", tableName.c_str()); + return; + } + + std::vector asicValues; + std::vector appValues; + for(json::iterator it = j.begin(); it != j.end(); ++it) + { + asicValues.emplace_back(it.key(), it.value()); + } + + if(orch->mapToErrorDbFormat(object_type, asicValues, appValues) == false) + { + SWSS_LOG_ERROR("Mapping for object type %s is failed", str_object_type.c_str()); + return; + } + + SWSS_LOG_DEBUG("Field values for %s after mapping: ", str_object_type.c_str()); + for(size_t i = 0; i < appValues.size(); i++) + { + SWSS_LOG_DEBUG("%s -> %s", fvField(appValues[i]).c_str(), fvValue(appValues[i]).c_str()); + } + + /* Convert SAI error code to SWSS error code */ + std::string swssRCStr = swss::ErrorMap::getSwssRCStr(j["rc"]); + appValues.emplace_back("rc", swssRCStr); + + /* SAI operation could be create/remove/set */ + appValues.emplace_back("operation", j["operation"]); + + /* Update error database if the notification is about failure */ + std::string errKeyVal; + auto dbValues = appValues; + extractEntry(dbValues, "key", errKeyVal); + updateErrorDb(tableName, errKeyVal, dbValues); + + /* Send the notification to registered applications */ + if(applNotificationEnabled(object_type)) + { + json js; + for(const auto &v: appValues) + { + js[fvField(v)] = fvValue(v); + } + std::string s = js.dump(); + std::string strOp = getOperation(tableName); + sendNotification(tableName, strOp, s); + } + } + else if(&consumer == m_errorFlushNotificationConsumer.get()) + { + if(op == "ALL" || op == "TABLE") + { + flushErrorDb(op, data); + } + else + { + SWSS_LOG_ERROR("Received unknown flush ERROR DB request"); + return; + } + } +} + +int ErrorOrch::flushErrorDb(const std::string &op, const std::string &data) +{ + SWSS_LOG_ENTER(); + std::shared_ptr table; + std::vector keys; + SWSS_LOG_DEBUG("ERROR DB flush request received, op %s, data %s", op.c_str(), data.c_str()); + + std::string errTableName = data; + for(auto iter : m_TableNameObjMap) + { + if((op != "ALL") && (errTableName != iter.first)) + { + SWSS_LOG_INFO("Skipping flushing of entries for %s", errTableName.c_str()); + continue; + } + table = iter.second; + if(table == NULL) + { + SWSS_LOG_INFO("Invalid Table object found for %s", iter.first.c_str()); + continue; + } + + table->getKeys(keys); + for(auto& key : keys) + { + table->del(key); + } + SWSS_LOG_DEBUG("Flushed ERROR DB entries for %s", iter.first.c_str()); + } + return 0; +} + +/* To filter out error notifications to the application */ +bool ErrorOrch::applNotificationEnabled(_In_ sai_object_type_t object_type) +{ + /* This is used to filter out notifications to the applications based on the object type. + * The complete list of supported objects are mentioned in m_ObjTableMap */ + return true; +} +void ErrorOrch::sendNotification( + _In_ std::string& tableName, + _In_ std::string& op, + _In_ std::string& data, + _In_ std::vector &entry) +{ + SWSS_LOG_ENTER(); + int64_t rv = 0; + + SWSS_LOG_INFO("%s %s", op.c_str(), data.c_str()); + + auto tableChannel = m_TableChannel.find(tableName); + if(tableChannel == m_TableChannel.end()) + { + SWSS_LOG_ERROR("Failed to find the notification channel for %s", tableName.c_str()); + return; + } + if(tableChannel->second == NULL) + { + SWSS_LOG_ERROR("Invalid notification channel found for %s", tableName.c_str()); + return; + } + + std::shared_ptr notifications = tableChannel->second; + + rv = notifications->send(op, data, entry); + + SWSS_LOG_DEBUG("notification send successful, subscribers count %ld", rv); +} + +void ErrorOrch::sendNotification( + _In_ std::string& tableName, + _In_ std::string& op, + _In_ std::string& data) +{ + SWSS_LOG_ENTER(); + + std::vector entry; + + sendNotification(tableName, op, data, entry); +} + +void ErrorOrch::doTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); +} + +std::string ErrorOrch::getOperation(std::string &tableName) +{ + std::string strOperation = "oper_"; + strOperation += tableName; + + return strOperation; +} + +std::string ErrorOrch::getErrorListenerChannelName(std::string &appDbTableName) +{ + std::string errorChannel = "ERROR_"; + errorChannel += appDbTableName + "_CHANNEL"; + + return errorChannel; +} + +std::string ErrorOrch::getErrorTableName(std::string &appDbTableName) +{ + std::string errorTableName = "ERROR_"; + errorTableName += appDbTableName; + + return errorTableName; +} + +std::string ErrorOrch::getAppTableName(std::string &errDbTableName) +{ + std::string appTableName = errDbTableName; + appTableName.erase(0, strlen("ERROR_")); + + return appTableName; +} + +bool ErrorOrch::mappingHandlerRegister(std::string tableName, Orch* orch) +{ + SWSS_LOG_ENTER(); + if(m_TableOrchMap.find(tableName) != m_TableOrchMap.end()) + { + SWSS_LOG_ERROR("Mapper for %s already exists", tableName.c_str()); + return false; + } + m_TableOrchMap[tableName] = orch; + + SWSS_LOG_INFO("Mapper for %s is registered", tableName.c_str()); + return true; +} + +bool ErrorOrch::mappingHandlerDeRegister(std::string tableName) +{ + SWSS_LOG_ENTER(); + + if(m_TableOrchMap.find(tableName) == m_TableOrchMap.end()) + { + SWSS_LOG_ERROR("De-registration with Error Handling Framework failed for %s", + tableName.c_str()); + return false; + } + + m_TableOrchMap.erase(m_ObjTableMap[tableName]); + SWSS_LOG_INFO("De-registration with Error Handling Framework for %s is successful", + tableName.c_str()); + + return true; +} + +bool ErrorOrch::createTableObject(std::string &errTableName) +{ + SWSS_LOG_ENTER(); + if(m_TableNameObjMap.find(errTableName) != m_TableNameObjMap.end()) + { + SWSS_LOG_ERROR("Table object for %s already exists", errTableName.c_str()); + return false; + } + m_TableNameObjMap[errTableName] = std::shared_ptr(new swss::Table(m_errorDb.get(), errTableName)); + + SWSS_LOG_DEBUG("Created Table object for %s", errTableName.c_str()); + return true; +} + +bool ErrorOrch::deleteTableObject(std::string &errTableName) +{ + SWSS_LOG_ENTER(); + + if(m_TableNameObjMap.find(errTableName) == m_TableNameObjMap.end()) + { + SWSS_LOG_ERROR("Failed to remove Table object for %s", errTableName.c_str()); + return false; + } + + m_TableNameObjMap.erase(errTableName); + SWSS_LOG_DEBUG("Removed Table object for %s", errTableName.c_str()); + + return true; +} + +/* Extract the requested entry and erase it from the input list */ +void ErrorOrch::extractEntry(std::vector &values, + const std::string &field, std::string &value) +{ + auto iu = values.begin(); + while(iu != values.end()) + { + if(fvField(*iu) == field) + { + value = fvValue(*iu); + iu = values.erase(iu); + break; + } + else + { + iu++; + } + } + return; +} + +void ErrorOrch::updateErrorDb(std::string &tableName, const std::string &key, + std::vector &values) +{ + SWSS_LOG_ENTER(); + std::string strOp, strRc; + bool entryFound = false; + bool removeEntry = false; + bool updateEntry = false; + + /* Extract current return code and operation */ + for(size_t i = 0; i < values.size(); i++) + { + if(fvField(values[i]) == "operation") + { + strOp = fvValue(values[i]); + } + else if(fvField(values[i]) == "rc") + { + strRc = fvValue(values[i]); + } + } + + /* Get ERROR DB table object */ + std::shared_ptr table; + std::string errTableName = getErrorTableName(tableName); + if(m_TableNameObjMap.find(errTableName) == m_TableNameObjMap.end()) + { + SWSS_LOG_INFO("Failed to find Table object for %s", errTableName.c_str()); + return; + } + + table = m_TableNameObjMap[errTableName]; + if(table == NULL) + { + SWSS_LOG_INFO("Invalid Table object found for %s", errTableName.c_str()); + return; + } + + /* Check if the entry with the key is present in ERROR DB */ + std::vector ovalues; + if(table->get(key, ovalues)) + { + entryFound = true; + } + + if(strRc == "SWSS_RC_SUCCESS") + { + /* Remove the entry if present in error database */ + if(entryFound == true) + { + removeEntry = true; + } + } + else + { + if(strOp == "create") + { + /* Add new entry into error database */ + updateEntry = true; + } + else if(strOp == "remove") + { + if(entryFound == true) + { + /* Remove operation has failed due to the previous + * create/update operation failure */ + removeEntry = true; + } + else + { + /* Add new entry into error database */ + updateEntry = true; + } + } + else if(strOp == "set") + { + /* Add/Update entry in the error database */ + updateEntry = true; + } + } + SWSS_LOG_DEBUG("key %s operation %s RC %s Entry found %d update %d remove %d ", + key.c_str(), strOp.c_str(), strRc.c_str(), + entryFound, updateEntry, removeEntry); + if(updateEntry == true) + { + /* Create/Update the database entry */ + table->set(key, values); + SWSS_LOG_INFO("Publish %s(ok) to error db", key.c_str()); + } + else if(removeEntry == true) + { + /* Remove the entry from database */ + table->del(key); + SWSS_LOG_INFO("Removed %s(ok) from error db", key.c_str()); + } + + return; +} + +/* This is used by other Orch Agents to add the entry into error database + * and optionally send a notification to the applications */ +void ErrorOrch::addErrorEntry(sai_object_type_t object_type, + std::vector &appValues, uint32_t flags) +{ + SWSS_LOG_ENTER(); + + std::shared_ptr table; + + /* Extract SAI object type */ + std::string str_object_type = sai_serialize_object_type(object_type); + SWSS_LOG_DEBUG("Field values received for %s: ", str_object_type.c_str()); + for(size_t i = 0; i < appValues.size(); i++) + { + SWSS_LOG_DEBUG("%s -> %s", fvField(appValues[i]).c_str(), + fvValue(appValues[i]).c_str()); + } + + /* Find out the table object based on the object type */ + if(m_ObjTableMap.find(str_object_type) == m_ObjTableMap.end()) + { + SWSS_LOG_INFO("Unsupported SAI object type %s", + str_object_type.c_str()); + return; + } + + std::string tableName = m_ObjTableMap[str_object_type]; + if(m_TableOrchMap.find(tableName) == m_TableOrchMap.end()) + { + SWSS_LOG_INFO("Error handling is not supported for %s", + tableName.c_str()); + return; + } + + /* Update error database if the notification is about failure */ + std::string errKeyVal; + auto dbValues = appValues; + extractEntry(dbValues, "key", errKeyVal); + updateErrorDb(tableName, errKeyVal, dbValues); + + /* Send the notification to registered applications */ + if((flags & ERRORORCH_FLAGS_NOTIF_SEND) && (applNotificationEnabled(object_type))) + { + json js; + for(const auto &v: appValues) + { + js[fvField(v)] = fvValue(v); + } + std::string s = js.dump(); + std::string strOp = getOperation(tableName); + sendNotification(tableName, strOp, s); + } +} diff --git a/orchagent/errororch.h b/orchagent/errororch.h new file mode 100644 index 00000000000..4ae8eee3df3 --- /dev/null +++ b/orchagent/errororch.h @@ -0,0 +1,70 @@ +/* + * Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or + * its subsidiaries. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SWSS_ERRORORCH_H +#define SWSS_ERRORORCH_H + +#include "orch.h" +#include "notificationproducer.h" +#include "notificationconsumer.h" +#include + +typedef enum { + ERRORORCH_FLAGS_NOTIF_SEND = 0x1, + ERRORORCH_FLAGS_LAST +} errororch_flags_t; + +class ErrorOrch: public Orch +{ + public: + ErrorOrch(swss::DBConnector *asicDb, swss::DBConnector *errorDb, std::vector &tableNames); + std::string getOperation(std::string &tableName); + std::string getErrorListenerChannelName(std::string &appDbTableName); + std::string getErrorTableName(std::string &appDbTableName); + std::string getAppTableName(std::string &errDbTableName); + bool mappingHandlerRegister(std::string tableName, Orch* orch); + bool mappingHandlerDeRegister(std::string tableName); + bool createTableObject(std::string &errTableName); + bool deleteTableObject(std::string &errTableName); + bool applNotificationEnabled(_In_ sai_object_type_t object_type); + void sendNotification(_In_ std::string& tableName, _In_ std::string& op, + _In_ std::string& data, _In_ std::vector &entry); + void sendNotification(_In_ std::string& tableName, + _In_ std::string& op, _In_ std::string& data); + void addErrorEntry(sai_object_type_t object_type, + std::vector &appValues, uint32_t flags); + + private: + std::shared_ptr m_errorDb; + std::unique_ptr m_errorNotificationConsumer; + std::unique_ptr m_errorFlushNotificationConsumer; + /* Table ID to Orchestration agent object map */ + std::map m_TableOrchMap; + std::map> m_TableNameObjMap; + std::unordered_map> m_TableChannel; + + void doTask(Consumer &consumer); + void doTask(swss::NotificationConsumer& consumer); + void extractEntry(std::vector &values, + const std::string &field, std::string &value); + int flushErrorDb(const std::string &op, const std::string &tableName); + void updateErrorDb(std::string &tableName, const std::string &key, + std::vector &values); + +}; + +#endif /* SWSS_ERRORORCH_H */ diff --git a/orchagent/main.cpp b/orchagent/main.cpp index a5591b9d004..d2dcdece060 100644 --- a/orchagent/main.cpp +++ b/orchagent/main.cpp @@ -291,11 +291,15 @@ int main(int argc, char **argv) SWSS_LOG_NOTICE("Created underlay router interface ID %" PRIx64, gUnderlayIfId); /* Initialize orchestration components */ - DBConnector appl_db("APPL_DB", 0); - DBConnector config_db("CONFIG_DB", 0); - DBConnector state_db("STATE_DB", 0); + DBConnector appl_db(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector config_db(CONFIG_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector state_db(STATE_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector counters_db(COUNTERS_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector asic_db(ASIC_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector error_db(ERROR_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); - auto orchDaemon = make_shared(&appl_db, &config_db, &state_db); + + auto orchDaemon = make_shared(&appl_db, &config_db, &state_db, &counters_db, &asic_db, &error_db); if (!orchDaemon->init()) { diff --git a/orchagent/neighorch.cpp b/orchagent/neighorch.cpp index ae0db0751c2..4f9fbccbc01 100644 --- a/orchagent/neighorch.cpp +++ b/orchagent/neighorch.cpp @@ -4,6 +4,13 @@ #include "swssnet.h" #include "crmorch.h" #include "routeorch.h" +#include "errororch.h" +#include "tokenize.h" +#include "sai_serialize.h" +#include "redisclient.h" + +#include "swss/json.hpp" +using json = nlohmann::json; extern sai_neighbor_api_t* sai_neighbor_api; extern sai_next_hop_api_t* sai_next_hop_api; @@ -12,6 +19,9 @@ extern PortsOrch *gPortsOrch; extern sai_object_id_t gSwitchId; extern CrmOrch *gCrmOrch; extern RouteOrch *gRouteOrch; +extern ErrorOrch *gErrorOrch; +extern std::shared_ptr g_redisClientAsicDb; +extern std::shared_ptr g_redisClientCountersDb; const int neighorch_pri = 30; @@ -19,6 +29,12 @@ NeighOrch::NeighOrch(DBConnector *db, string tableName, IntfsOrch *intfsOrch) : Orch(db, tableName, neighorch_pri), m_intfsOrch(intfsOrch) { SWSS_LOG_ENTER(); + + if(gErrorOrch->mappingHandlerRegister(APP_NEIGH_TABLE_NAME, this) == false) + { + SWSS_LOG_ERROR("Failed to register with Error Handling Framework for %s", + APP_NEIGH_TABLE_NAME); + } } bool NeighOrch::hasNextHop(const NextHopKey &nexthop) @@ -581,3 +597,129 @@ bool NeighOrch::removeNeighbor(const NeighborEntry &neighborEntry) return true; } + +bool NeighOrch::mapToErrorDbFormat(sai_object_type_t& object_type, std::vector &asicValues, + std::vector &appValues) +{ + SWSS_LOG_ENTER(); + + if(object_type != SAI_OBJECT_TYPE_NEIGHBOR_ENTRY) + { + return false; + } + + /* + 127.0.0.1:6379> hgetall "NEIGH_TABLE:Ethernet0:2.2.2.2" + 1) "neigh" + 2) "00:00:3a:3e:9e:a7" + 3) "family" + 4) "IPv4" + + 127.0.0.1:6379[1]> hgetall "ASIC_STATE:SAI_OBJECT_TYPE_NEIGHBOR_ENTRY:{\"ip\":\"2.2.2.2\",\"rif\":\"oid:0x60000000006f3\",\"switch_id\":\"oid:0x21000000000000\"}" + 1) "SAI_NEIGHBOR_ENTRY_ATTR_DST_MAC_ADDRESS" + 2) "00:00:3A:3E:9E:A7" + 127.0.0.1:6379[1]> + + 127.0.0.1:6379[2]> hgetall "COUNTERS_PORT_NAME_MAP" + 25) "Ethernet0" + 26) "oid:0x100000000000e" + */ + + const auto& values = asicValues; + std::string asicKV, strNbrIP, strRifOid, strMac; + std::string strIntfName, strRtrIntfType; + for(size_t i = 0; i < values.size(); i++) + { + if(fvField(values[i]) == "key") + { + /* Extract Neighbor IP and Router Interface ID from the "key" field */ + asicKV = fvValue(values[i]); + auto tokens = tokenize(asicKV, ':', 1); + json j = json::parse(tokens[1]); + strNbrIP = j["ip"]; + strRifOid = j["rif"]; + SWSS_LOG_DEBUG("Neighbor IP is %s, router interface ID is %s", + strNbrIP.c_str(), strRifOid.c_str()); + + /* Extract Port OID from Router Interface OID */ + std::string strRtrIfKey = "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE:" + strRifOid; + std::string strIntfOid; + /* OID in neighbor entry points to ROUTER_INTERFACE + * Port based routing interface + * ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE:oid:0x60000000006d9 + * SAI_ROUTER_INTERFACE_ATTR_TYPE = SAI_ROUTER_INTERFACE_TYPE_PORT + * SAI_ROUTER_INTERFACE_ATTR_PORT_ID = oid:0x100000000000e + * Check above oid in COUNTERS_PORT_NAME_MAP in COUNTERS_DB to get the physical interface name + * + * VLAN based routing interface + * ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE:oid:0x60000000006f6 + * SAI_ROUTER_INTERFACE_ATTR_TYPE -> SAI_ROUTER_INTERFACE_TYPE_VLAN + * SAI_ROUTER_INTERFACE_ATTR_VLAN_ID -> oid:0x260000000006f3 + * Check above oid in "ASIC_STATE:SAI_OBJECT_TYPE_VLAN:oid:0x260000000006f3" + */ + + auto hashRif = g_redisClientAsicDb->hgetall(strRtrIfKey); + for(auto &kv: hashRif) + { + const std::string &skey = kv.first; + const std::string &svalue = kv.second; + + if(skey == "SAI_ROUTER_INTERFACE_ATTR_TYPE") + { + strRtrIntfType = svalue; + } + if(skey == "SAI_ROUTER_INTERFACE_ATTR_PORT_ID" || skey == "SAI_ROUTER_INTERFACE_ATTR_VLAN_ID") + { + strIntfOid = svalue; + } + } + SWSS_LOG_DEBUG("Router interface type is %s, interface ID is %s", + strRtrIntfType.c_str(), strIntfOid.c_str()); + + /* Extract Port name from the Port OID */ + if(strRtrIntfType == "SAI_ROUTER_INTERFACE_TYPE_PORT") + { + auto hashCntr = g_redisClientCountersDb->hgetall("COUNTERS_PORT_NAME_MAP"); + for(auto &kv: hashCntr) + { + const std::string &skey = kv.first; + const std::string &svalue = kv.second; + if(svalue == strIntfOid) + { + strIntfName = skey; + break; + } + } + } + else if(strRtrIntfType == "SAI_ROUTER_INTERFACE_TYPE_VLAN") + { + std::string strVlanKey = "ASIC_STATE:SAI_OBJECT_TYPE_VLAN:" + strIntfOid; + auto hashVlan = g_redisClientAsicDb->hgetall(strVlanKey); + for(auto &kv: hashVlan) + { + const std::string &skey = kv.first; + const std::string &svalue = kv.second; + if(skey == "SAI_VLAN_ATTR_VLAN_ID") + { + strIntfName = "Vlan" + svalue; + break; + } + } + } + SWSS_LOG_DEBUG("Interface name is %s", strIntfName.c_str()); + } /* End of if(fvField(values[i]) == "key") */ + + /* Extract MAC address */ + if(fvField(values[i]) == "SAI_NEIGHBOR_ENTRY_ATTR_DST_MAC_ADDRESS") + { + strMac = fvValue(values[i]); + } + } /* End of for(size_t i = 0; i < values.size(); i++) */ + + std::string appKey = strIntfName + ":" + strNbrIP; + appValues.emplace_back("key", appKey); + appValues.emplace_back("neigh", strMac); + + return true; +} + diff --git a/orchagent/neighorch.h b/orchagent/neighorch.h index 60c40f9053d..d007a7cd6c5 100644 --- a/orchagent/neighorch.h +++ b/orchagent/neighorch.h @@ -67,6 +67,8 @@ class NeighOrch : public Orch, public Subject bool clearNextHopFlag(const NextHopKey &, const uint32_t); void doTask(Consumer &consumer); + bool mapToErrorDbFormat(sai_object_type_t& object_type, std::vector &asicValues, + std::vector &appValues); }; #endif /* SWSS_NEIGHORCH_H */ diff --git a/orchagent/orch.cpp b/orchagent/orch.cpp index a6dce7efbb0..b7a3288b9f4 100644 --- a/orchagent/orch.cpp +++ b/orchagent/orch.cpp @@ -432,6 +432,12 @@ string Orch::dumpTuple(Consumer &consumer, KeyOpFieldsValuesTuple &tuple) return s; } +bool Orch::mapToErrorDbFormat(sai_object_type_t& object_type, std::vector &asicValues, + std::vector &appValues) +{ + return false; +} + ref_resolve_status Orch::resolveFieldRefArray( type_map &type_maps, const string &field_name, diff --git a/orchagent/orch.h b/orchagent/orch.h index 1ea75b19d35..bbd23d5d6dc 100644 --- a/orchagent/orch.h +++ b/orchagent/orch.h @@ -196,6 +196,8 @@ class Orch static void recordTuple(Consumer &consumer, swss::KeyOpFieldsValuesTuple &tuple); void dumpPendingTasks(std::vector &ts); + virtual bool mapToErrorDbFormat(sai_object_type_t& object_type, std::vector &asicValues, + std::vector &appValues); protected: ConsumerMap m_consumerMap; diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index 756b8a35eb3..5114d1a4f2f 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -33,14 +33,26 @@ AclOrch *gAclOrch; CrmOrch *gCrmOrch; BufferOrch *gBufferOrch; SwitchOrch *gSwitchOrch; +ErrorOrch *gErrorOrch; Directory gDirectory; -OrchDaemon::OrchDaemon(DBConnector *applDb, DBConnector *configDb, DBConnector *stateDb) : +std::shared_ptr g_redisClientAppDb; +std::shared_ptr g_redisClientAsicDb; +std::shared_ptr g_redisClientCountersDb; + +OrchDaemon::OrchDaemon(DBConnector *applDb, DBConnector *configDb, DBConnector *stateDb, + DBConnector *countersDb, DBConnector *asicDb, DBConnector *errorDb) : m_applDb(applDb), m_configDb(configDb), - m_stateDb(stateDb) + m_stateDb(stateDb), + m_countersDb(countersDb), + m_asicDb(asicDb), + m_errorDb(errorDb) { SWSS_LOG_ENTER(); + g_redisClientAppDb = std::make_shared(applDb); + g_redisClientAsicDb = std::make_shared(asicDb); + g_redisClientCountersDb = std::make_shared(countersDb); } OrchDaemon::~OrchDaemon() @@ -68,6 +80,11 @@ bool OrchDaemon::init() string platform = getenv("platform") ? getenv("platform") : ""; + vector error_tables = { + { ERROR_ROUTE_TABLE_NAME}, + { ERROR_NEIGH_TABLE_NAME} + }; + gErrorOrch = new ErrorOrch(m_asicDb, m_errorDb, error_tables); gSwitchOrch = new SwitchOrch(m_applDb, APP_SWITCH_TABLE_NAME); const int portsorch_base_pri = 40; @@ -215,7 +232,7 @@ bool OrchDaemon::init() * when iterating ConsumerMap. * That is ensured implicitly by the order of map key, "LAG_TABLE" is smaller than "VLAN_TABLE" in lexicographic order. */ - m_orchList = { gSwitchOrch, gCrmOrch, gBufferOrch, gPortsOrch, gIntfsOrch, gNeighOrch, gRouteOrch, copp_orch, tunnel_decap_orch, qos_orch, wm_orch, policer_orch, sflow_orch, debug_counter_orch}; + m_orchList = { gErrorOrch, gSwitchOrch, gCrmOrch, gBufferOrch, gPortsOrch, gIntfsOrch, gNeighOrch, gRouteOrch, copp_orch, tunnel_decap_orch, qos_orch, wm_orch, policer_orch, sflow_orch, debug_counter_orch}; bool initialize_dtel = false; if (platform == BFN_PLATFORM_SUBSTRING || platform == VS_PLATFORM_SUBSTRING) diff --git a/orchagent/orchdaemon.h b/orchagent/orchdaemon.h index 5e9c9b914af..1a2f1cb571a 100644 --- a/orchagent/orchdaemon.h +++ b/orchagent/orchdaemon.h @@ -23,6 +23,7 @@ #include "vrforch.h" #include "vxlanorch.h" #include "vnetorch.h" +#include "errororch.h" #include "countercheckorch.h" #include "flexcounterorch.h" #include "watermarkorch.h" @@ -36,7 +37,8 @@ using namespace swss; class OrchDaemon { public: - OrchDaemon(DBConnector *, DBConnector *, DBConnector *); + OrchDaemon(DBConnector *applDb, DBConnector *configDb, DBConnector *stateDb, + DBConnector *countersDb, DBConnector *asicDb, DBConnector *errorDb); ~OrchDaemon(); bool init(); @@ -50,6 +52,9 @@ class OrchDaemon DBConnector *m_applDb; DBConnector *m_configDb; DBConnector *m_stateDb; + DBConnector *m_countersDb; + DBConnector *m_asicDb; + DBConnector *m_errorDb; std::vector m_orchList; Select *m_select; diff --git a/orchagent/routeorch.cpp b/orchagent/routeorch.cpp index 8aabef4a9e5..4009d53eadb 100644 --- a/orchagent/routeorch.cpp +++ b/orchagent/routeorch.cpp @@ -4,6 +4,16 @@ #include "logger.h" #include "swssnet.h" #include "crmorch.h" +#include "tokenize.h" +#include "errororch.h" +#include "sai_serialize.h" +#include "redisclient.h" +#include "vrforch.h" + +#include "swss/json.hpp" +using json = nlohmann::json; +using namespace swss; +#define VRF_PREFIX "Vrf" extern sai_object_id_t gVirtualRouterId; extern sai_object_id_t gSwitchId; @@ -14,6 +24,12 @@ extern sai_switch_api_t* sai_switch_api; extern PortsOrch *gPortsOrch; extern CrmOrch *gCrmOrch; +extern ErrorOrch *gErrorOrch; +extern std::shared_ptr g_redisClientAppDb; +extern std::shared_ptr g_redisClientAsicDb; +extern std::shared_ptr g_redisClientCountersDb; + +const char route_table_key_delimiter = ':'; /* Default maximum number of next hop groups */ #define DEFAULT_NUMBER_OF_ECMP_GROUPS 128 @@ -31,6 +47,12 @@ RouteOrch::RouteOrch(DBConnector *db, string tableName, NeighOrch *neighOrch, In { SWSS_LOG_ENTER(); + if(gErrorOrch->mappingHandlerRegister(APP_ROUTE_TABLE_NAME, this) == false) + { + SWSS_LOG_ERROR("Failed to register with Error Handling Framework for %s", + APP_ROUTE_TABLE_NAME); + } + sai_attribute_t attr; attr.id = SAI_SWITCH_ATTR_NUMBER_OF_ECMP_GROUPS; @@ -1187,3 +1209,92 @@ bool RouteOrch::removeRoute(sai_object_id_t vrf_id, const IpPrefix &ipPrefix) return true; } + +bool RouteOrch::mapToErrorDbFormat(sai_object_type_t& object_type, std::vector &asicValues, + std::vector &appValues) +{ + SWSS_LOG_ENTER(); + + if(object_type != SAI_OBJECT_TYPE_ROUTE_ENTRY) + { + return false; + } + + /* + 127.0.0.1:6379> hgetall "ROUTE_TABLE:1.1.1.0/24" + 1) "nexthop" + 2) "2.2.2.2" + 3) "ifname" + 4) "Ethernet0" + + 127.0.0.1:6379[1]> hgetall "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY:{\"dest\":\"1.1.1.0/24\",\"switch_id\":\"oid:0x21000000000000\",\"vr\":\"oid:0x3000000000028\"}" + 1) "SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID" + 2) "oid:0x40000000006fd" + 127.0.0.1:6379[1]> + + */ + + const auto& values = asicValues; + std::string asicKV; + std::string strPrefix; + std::string vrfName = ""; + sai_object_id_t vrf_object_id; + for(size_t i = 0; i < values.size(); i++) + { + /* Extract IP prefix from the key */ + if(fvField(values[i]) == "key") + { + asicKV = fvValue(values[i]); + auto tokens = tokenize(asicKV, route_table_key_delimiter, 1); + json j = json::parse(tokens[1]); + + /* Extract VRF Name */ + for(json::iterator it = j.begin(); it != j.end(); ++it) + { + string field = it.key(); + string value = it.value(); + SWSS_LOG_DEBUG("Field values %s : %s", field.c_str(), value.c_str()); + + if(field == "dest") + { + strPrefix = value; + } + if(field == "vr") + { + sai_deserialize_object_id(value, vrf_object_id); + vrfName = m_vrfOrch->getVRFname(vrf_object_id); + break; + } + } + + SWSS_LOG_DEBUG("VRF is \"%s\", Prefix is %s", vrfName.c_str(), strPrefix.c_str()); + break; + } + } /* End of for(i < values.size(); i++) */ + + if(strPrefix.empty()) + { + return false; + } + + std::string strRouteTable = APP_ROUTE_TABLE_NAME; + std::string strAppKey = ""; + if(!vrfName.empty()) + { + strAppKey += vrfName; + strAppKey += route_table_key_delimiter; + } + strAppKey += strPrefix; + appValues.emplace_back("key", strAppKey); + /* Get the entries from APP DB Table */ + auto hashApp = g_redisClientAppDb->hgetall(strRouteTable + ":" + strAppKey); + for(auto &kv: hashApp) + { + const std::string &skey = kv.first; + const std::string &svalue = kv.second; + appValues.emplace_back(skey, svalue); + } + + return true; +} + diff --git a/orchagent/routeorch.h b/orchagent/routeorch.h index 84550d75896..2b382cd91e6 100644 --- a/orchagent/routeorch.h +++ b/orchagent/routeorch.h @@ -98,6 +98,8 @@ class RouteOrch : public Orch, public Subject void addLinkLocalRouteToMe(sai_object_id_t vrf_id, IpPrefix linklocal_prefix); void doTask(Consumer& consumer); + bool mapToErrorDbFormat(sai_object_type_t& object_type, std::vector &asicValues, + std::vector &appValues); }; #endif /* SWSS_ROUTEORCH_H */