diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index 79cfe99f6d5..f322cf50432 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..5e183c82b4b --- /dev/null +++ b/orchagent/errororch.cpp @@ -0,0 +1,558 @@ +/* + * Copyright 2019 Broadcom Inc. + * + * 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 +#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; + +/* Error handling framework currently handling only the following + * tables and can be extended to other tables */ +static map m_ObjTableMap = { + {"SAI_OBJECT_TYPE_ROUTE_ENTRY", APP_ROUTE_TABLE_NAME}, + {"SAI_OBJECT_TYPE_NEIGHBOR_ENTRY", APP_NEIGH_TABLE_NAME}, +}; + +ErrorOrch::ErrorOrch(DBConnector *asicDb, DBConnector *errorDb, vector &tableNames) : + Orch(asicDb, tableNames) +{ + SWSS_LOG_ENTER(); + + m_errorDb = 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 = new swss::NotificationConsumer(asicDb, "FLUSH_ERROR_DB"); + auto errorNotifier = new Notifier(m_errorFlushNotificationConsumer, this, "FLUSH_ERROR_DB"); + Orch::addExecutor(errorNotifier); + + /* Add SAI API status error notifications support from Syncd */ + m_errorNotificationConsumer = new swss::NotificationConsumer(asicDb, "ERROR_NOTIFICATIONS"); + errorNotifier = new Notifier(m_errorNotificationConsumer, 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.begin(); objTable!= m_ObjTableMap.end(); objTable++) + { + 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("EHF is ready to receive status reports from Syncd"); +} + +ErrorOrch::~ErrorOrch() +{ + SWSS_LOG_ENTER(); + delete m_errorNotificationConsumer; + delete m_errorFlushNotificationConsumer; +} + +void ErrorOrch::doTask(NotificationConsumer& consumer) +{ + SWSS_LOG_ENTER(); + + string op; + string data; + std::vector values; + Orch *orch; + std::shared_ptr table; + + consumer.pop(op, data, values); + + SWSS_LOG_DEBUG("EHF received operation: %s data : %s", op.c_str(), data.c_str()); + if (&consumer == m_errorNotificationConsumer && op == "saiapi_status") + { + /* + * The following steps are performed: + * Extract object type from the received data + * Map the object type to registrant + * Invoke mapper function to convert the data to application friendly format + * Update DB if the notification is about failure + * Send notification to error listener class + */ + + json j = json::parse(data); + + string asicKey = j["key"]; + const string &str_object_type = asicKey.substr(0, asicKey.find(":")); + + sai_object_type_t object_type; + sai_deserialize_object_type(str_object_type, object_type); + + if(m_ObjTableMap.find(str_object_type) == m_ObjTableMap.end()) + { + SWSS_LOG_INFO("EHF does not support SAI object type %s", + str_object_type.c_str()); + return; + } + 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 = 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 */ + string swssRCStr = ErrorMap::getSwssRCStr(j["rc"]); + appValues.emplace_back("rc", swssRCStr); + + /* SAI operation could be create/remove/set */ + appValues.emplace_back("operation", j["operation"]); + + string errKey; + auto dbValues = appValues; + + /* Remove key from FV as it's passed explicitly while updating DB */ + auto iu = dbValues.begin(); + while (iu != dbValues.end()) + { + if(fvField(*iu) == "key") + { + errKey = fvValue(*iu); + iu = dbValues.erase(iu); + break; + } + else + { + iu++; + } + } + + /* Update Error Database */ + updateErrorDb(tableName, errKey, dbValues); + + if (applNotificationEnabled(object_type)) + { + /* Write the entry into Notification channel */ + json js; + for (const auto &v: appValues) + { + js[fvField(v)] = fvValue(v); + } + string s = js.dump(); + string strOp = "oper_" + tableName; + sendNotification(tableName, strOp, s); + } + } + else if (&consumer == m_errorFlushNotificationConsumer) + { + if (op == "ALL" || op == "TABLE") + { + flushErrorDb(op, data); + } + else + { + SWSS_LOG_ERROR("Received unknown flush ERROR DB request"); + return; + } + } +} + +int ErrorOrch::flushErrorDb(const string &op, const string &data) +{ + std::shared_ptr
table; + vector keys; + SWSS_LOG_DEBUG("ERROR DB flush request received, op %s, data %s", op.c_str(), data.c_str()); + + string errTableName = data; + for(auto iter = m_TableNameObjMap.begin(); iter!= m_TableNameObjMap.end(); iter++) + { + 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 EHF 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; +} + +bool ErrorOrch::applNotificationEnabled(_In_ sai_object_type_t object_type) +{ + if(object_type == SAI_OBJECT_TYPE_ROUTE_ENTRY + || object_type == SAI_OBJECT_TYPE_NEIGHBOR_ENTRY) + { + return true; + } + return false; +} +void ErrorOrch::sendNotification( + _In_ string& tableName, + _In_ string& op, + _In_ string& data, + _In_ std::vector &entry) +{ + int64_t rv = 0; + SWSS_LOG_ENTER(); + + 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_ string& tableName, + _In_ string& op, + _In_ string& data) +{ + SWSS_LOG_ENTER(); + + std::vector entry; + + sendNotification(tableName, op, data, entry); +} + +void ErrorOrch::doTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); +} + +string ErrorOrch::getErrorListenerChannelName(string &appDbTableName) +{ + string errorChnl = "ERROR_"; + errorChnl += appDbTableName + "_CHANNEL"; + + return errorChnl; +} + +string ErrorOrch::getErrorTableName(string &appDbTableName) +{ + string errorTableName = "ERROR_"; + errorTableName += appDbTableName; + + return errorTableName; +} + +string ErrorOrch::getAppTableName(string &errDbTableName) +{ + string appTableName = errDbTableName; + appTableName.erase(0, strlen("ERROR_")); + + return appTableName; +} + +bool ErrorOrch::mappingHandlerRegister(string tableName, Orch* orch) +{ + SWSS_LOG_ENTER(); + if(m_TableOrchMap.find(tableName) != m_TableOrchMap.end()) + { + SWSS_LOG_ERROR("Mapper for %s already exists in EHF", tableName.c_str()); + return false; + } + m_TableOrchMap[tableName] = orch; + + SWSS_LOG_INFO("Mapper for %s is registered with EHF", tableName.c_str()); + return true; +} + +bool ErrorOrch::mappingHandlerDeRegister(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(string &tableName) +{ + SWSS_LOG_ENTER(); + if(m_TableNameObjMap.find(tableName) != m_TableNameObjMap.end()) + { + SWSS_LOG_ERROR("EHF Table object for %s already exists", tableName.c_str()); + return false; + } + m_TableNameObjMap[tableName] = shared_ptr
(new Table(m_errorDb.get(), tableName)); + + SWSS_LOG_DEBUG("Created EHF Table object for %s", tableName.c_str()); + return true; +} + +bool ErrorOrch::deleteTableObject(string &tableName) +{ + SWSS_LOG_ENTER(); + + string appTableName = getAppTableName(tableName); + if(m_TableNameObjMap.find(appTableName) == m_TableNameObjMap.end()) + { + SWSS_LOG_ERROR("Failed to remove EHF Table object for %s", tableName.c_str()); + return false; + } + + m_TableNameObjMap.erase(tableName); + SWSS_LOG_DEBUG("Removed EHF Table object for %s", tableName.c_str()); + + return true; +} + +void ErrorOrch::updateErrorDb(string &tableName, const string &key, + std::vector &values) + { + SWSS_LOG_ENTER(); + string strOp, strRc; + bool entryFound = false; + bool addEntry = 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; + string errTableName = getErrorTableName(tableName); + if(m_TableNameObjMap.find(errTableName) == m_TableNameObjMap.end()) + { + SWSS_LOG_INFO("Failed to find EHF Table object for %s", errTableName.c_str()); + return; + } + + table = m_TableNameObjMap[errTableName]; + if(table == NULL) + { + SWSS_LOG_INFO("Invalid EHF 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") + { + addEntry = true; + } + else if(strOp == "remove") + { + if(entryFound == true) + { + removeEntry = true; + } + else + { + addEntry = true; + } + } + else if(strOp == "set") + { + if(entryFound == true) + { + updateEntry = true; + } + else + { + addEntry = true; + } + } + } + SWSS_LOG_DEBUG("key %s operation %s RC %s Entry in DB %d add:remove:update %d:%d:%d ", + key.c_str(), strOp.c_str(), strRc.c_str(), + entryFound, addEntry, removeEntry, updateEntry); + if(addEntry == true || 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; + } + +/* The following function 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; + + /* + * The following steps are performed: + * Find out the table object based on the object type + * Update DB if the notification is about failure + * Send notification to error listener class if the flag is set + */ + + 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()); + } + + if(m_ObjTableMap.find(str_object_type) == m_ObjTableMap.end()) + { + SWSS_LOG_INFO("EHF does not support SAI object type %s", + str_object_type.c_str()); + return; + } + + string tableName = m_ObjTableMap[str_object_type]; + if(m_TableOrchMap.find(tableName) == m_TableOrchMap.end()) + { + SWSS_LOG_INFO("EHF does not support error handling for %s", + tableName.c_str()); + return; + } + + string errKey; + auto dbValues = appValues; + + /* Remove key from FV as it's passed explicitly while updating DB */ + auto iu = dbValues.begin(); + while (iu != dbValues.end()) + { + if(fvField(*iu) == "key") + { + errKey = fvValue(*iu); + iu = dbValues.erase(iu); + break; + } + else + { + iu++; + } + } + + /* Update Error Database */ + updateErrorDb(tableName, errKey, dbValues); + + /* Write the entry into Notificaition channel */ + if ((flags & ERRORORCH_FLAGS_NOTIF_SEND) && (applNotificationEnabled(object_type))) + { + json js; + for (const auto &v: appValues) + { + js[fvField(v)] = fvValue(v); + } + string s = js.dump(); + string strOp = "oper_" + tableName; + sendNotification(tableName, strOp, s); + } +} diff --git a/orchagent/errororch.h b/orchagent/errororch.h new file mode 100644 index 00000000000..d872e1a2a97 --- /dev/null +++ b/orchagent/errororch.h @@ -0,0 +1,67 @@ +/* + * Copyright 2019 Broadcom Inc. + * + * 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(DBConnector *asicDb, DBConnector *errorDb, vector &tableNames); + ~ErrorOrch(); + string getErrorListenerChannelName(string &appDbTableName); + string getErrorTableName(string &appDbTableName); + string getAppTableName(string &errDbTableName); + bool mappingHandlerRegister(string tableName, Orch* orch); + bool mappingHandlerDeRegister(string tableName); + bool createTableObject(string &tableName); + bool deleteTableObject(string &tableName); + bool applNotificationEnabled(_In_ sai_object_type_t object_type); + void sendNotification(_In_ string& tableName, _In_ string& op, + _In_ string& data, _In_ std::vector &entry); + void sendNotification(_In_ string& tableName, + _In_ string& op, _In_ string& data); + void addErrorEntry(sai_object_type_t object_type, + std::vector &appValues, uint32_t flags); + + private: + shared_ptr m_errorDb; + NotificationConsumer* m_errorNotificationConsumer; + NotificationConsumer* m_errorFlushNotificationConsumer; + /* Table ID to Orchestration agent object map */ + map m_TableOrchMap; + map> m_TableNameObjMap; + std::unordered_map> m_TableChannel; + + void doTask(Consumer &consumer); + void doTask(NotificationConsumer& consumer); + int flushErrorDb(const string &op, const string &tableName); + void updateErrorDb(string &tableName, const string &key, + std::vector &values); + +}; + +#endif /* SWSS_ERRORORCH_H */ diff --git a/orchagent/main.cpp b/orchagent/main.cpp index a5591b9d004..7ec78fe1668 100644 --- a/orchagent/main.cpp +++ b/orchagent/main.cpp @@ -294,8 +294,11 @@ int main(int argc, char **argv) DBConnector appl_db("APPL_DB", 0); DBConnector config_db("CONFIG_DB", 0); DBConnector state_db("STATE_DB", 0); + DBConnector counters_db("COUNTERS_DB", 0); + DBConnector asic_db("ASIC_DB", 0); + DBConnector error_db("ERROR_DB", 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..06df77dd920 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..94febfd6c1b 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..366c5ba77b9 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -33,14 +33,25 @@ 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 +79,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 +231,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..06062299236 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,7 @@ using namespace swss; class OrchDaemon { public: - OrchDaemon(DBConnector *, DBConnector *, DBConnector *); + OrchDaemon(DBConnector *, DBConnector *, DBConnector *, DBConnector *, DBConnector *, DBConnector *); ~OrchDaemon(); bool init(); @@ -50,6 +51,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..430de0b8b21 100644 --- a/orchagent/routeorch.cpp +++ b/orchagent/routeorch.cpp @@ -4,6 +4,14 @@ #include "logger.h" #include "swssnet.h" #include "crmorch.h" +#include "tokenize.h" +#include "errororch.h" +#include "sai_serialize.h" +#include "redisclient.h" + +#include "swss/json.hpp" +using json = nlohmann::json; +using namespace swss; extern sai_object_id_t gVirtualRouterId; extern sai_object_id_t gSwitchId; @@ -14,6 +22,10 @@ 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; /* Default maximum number of next hop groups */ #define DEFAULT_NUMBER_OF_ECMP_GROUPS 128 @@ -31,6 +43,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 +1205,59 @@ 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, strNhopKey, strNextHopIP; + std::string strRifOid, strPrefix, strIntfName, strRtrIntfType; + 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, ':', 1); + json j = json::parse(tokens[1]); + strPrefix = j["dest"]; + SWSS_LOG_DEBUG("Prefix is %s", strPrefix.c_str()); + break; + } + } /* End of for (i < values.size(); i++) */ + + appValues.emplace_back("key", strPrefix); + string strRouteTable = APP_ROUTE_TABLE_NAME; + string strAppKey = strRouteTable + ":" + strPrefix; + auto hashApp = g_redisClientAppDb->hgetall(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 */