From f68c781f328b6b866fae5b2a7e3b63f046db9fd8 Mon Sep 17 00:00:00 2001 From: oplklum Date: Wed, 19 Nov 2025 11:47:58 -0800 Subject: [PATCH 1/2] Support OTN 1. Support attenuator, oa, ocm, osc OTN objects. 2. Improve OTN flex counter feature. 3. Support OTN config manager. Signed-off-by: oplklum --- cfgmgr/otnmgr.cpp | 96 ++++ cfgmgr/otnmgr.h | 30 ++ cfgmgr/otnmgrd.cpp | 83 ++++ orchagent/Makefile.am | 3 +- orchagent/main.cpp | 32 +- orchagent/otn/attenuatororch.cpp | 39 ++ orchagent/otn/attenuatororch.h | 9 + orchagent/otn/oaorch.cpp | 39 ++ orchagent/otn/oaorch.h | 9 + orchagent/otn/objectorch.cpp | 775 +++++++++++++++++++++++++++++++ orchagent/otn/objectorch.h | 166 +++++++ orchagent/otn/ocmorch.cpp | 72 +++ orchagent/otn/ocmorch.h | 16 + orchagent/otn/oscorch.cpp | 39 ++ orchagent/otn/oscorch.h | 9 + orchagent/otn/otnhelper.cpp | 63 +++ orchagent/otn/otnhelper.h | 5 + orchagent/otn/otnorchdaemon.cpp | 63 +++ orchagent/otn/otnorchdaemon.h | 13 + orchagent/saihelper.cpp | 2 +- 20 files changed, 1560 insertions(+), 3 deletions(-) create mode 100644 cfgmgr/otnmgr.cpp create mode 100644 cfgmgr/otnmgr.h create mode 100644 cfgmgr/otnmgrd.cpp create mode 100644 orchagent/otn/attenuatororch.cpp create mode 100644 orchagent/otn/attenuatororch.h create mode 100644 orchagent/otn/oaorch.cpp create mode 100644 orchagent/otn/oaorch.h create mode 100644 orchagent/otn/objectorch.cpp create mode 100644 orchagent/otn/objectorch.h create mode 100644 orchagent/otn/ocmorch.cpp create mode 100644 orchagent/otn/ocmorch.h create mode 100644 orchagent/otn/oscorch.cpp create mode 100644 orchagent/otn/oscorch.h create mode 100644 orchagent/otn/otnhelper.cpp create mode 100644 orchagent/otn/otnhelper.h create mode 100644 orchagent/otn/otnorchdaemon.cpp create mode 100644 orchagent/otn/otnorchdaemon.h diff --git a/cfgmgr/otnmgr.cpp b/cfgmgr/otnmgr.cpp new file mode 100644 index 00000000000..053f722eede --- /dev/null +++ b/cfgmgr/otnmgr.cpp @@ -0,0 +1,96 @@ +#include "logger.h" +#include "dbconnector.h" +#include "tokenize.h" +#include "ipprefix.h" +#include "otnmgr.h" +#include "exec.h" +#include "shellcmd.h" +#include + +using namespace std; +using namespace swss; + +OtnMgr::OtnMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, const std::vector &tableNames, const std::map &tableMaps) : + Orch(cfgDb, tableNames), + m_appl_db(appDb), + m_state_db(stateDb), + m_tableMaps(tableMaps) +{ +} + +void OtnMgr::doTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + string cfgName = consumer.getTableName(); + + /* get app table by name */ + auto itApp = m_tableMaps.find(cfgName); + if (itApp == m_tableMaps.end()) + { + SWSS_LOG_ERROR("OtnMgr|%s is invalid", cfgName.c_str()); + return; + } + const string &appName = itApp->second; + shared_ptr appTable; + auto itTable = m_appTables.find(appName); + if (itTable == m_appTables.end()) + { + appTable = make_shared(m_appl_db, appName); + m_appTables[appName] = appTable; + } + else + { + appTable = itTable->second; + } + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + string alias = kfvKey(t); + string op = kfvOp(t); + + SWSS_LOG_NOTICE("OtnMgr doTask, cfg=%s, app=%s, key=%s, op=%s", cfgName.c_str(), appName.c_str(), alias.c_str(), op.c_str()); + + if (op == SET_COMMAND) + { + auto values = kfvFieldsValues(t); + for (auto value : values) + { + SWSS_LOG_NOTICE("OtnMgr doTask, key=%s, value=%s", value.first.c_str(), value.second.c_str()); + } + if (values.size()) + { + writeConfigToAppDb(appTable, alias, values); + } + } + else if (op == DEL_COMMAND) + { + SWSS_LOG_NOTICE("Delete component: %s", alias.c_str()); + appTable->del(alias); + } + + it = consumer.m_toSync.erase(it); + } +} + +bool OtnMgr::writeConfigToAppDb(std::shared_ptr &table, const std::string &alias, const std::string &field, const std::string &value) +{ + SWSS_LOG_ENTER(); + + vector fvs; + FieldValueTuple fv(field, value); + fvs.push_back(fv); + table->set(alias, fvs); + + return true; +} + +bool OtnMgr::writeConfigToAppDb(std::shared_ptr &table, const std::string &alias, std::vector &field_values) +{ + SWSS_LOG_ENTER(); + + table->set(alias, field_values); + return true; +} diff --git a/cfgmgr/otnmgr.h b/cfgmgr/otnmgr.h new file mode 100644 index 00000000000..a15f4b13cca --- /dev/null +++ b/cfgmgr/otnmgr.h @@ -0,0 +1,30 @@ +#pragma once + +#include "dbconnector.h" +#include "orch.h" +#include "producerstatetable.h" + +#include +#include +#include + +namespace swss { + +class OtnMgr : public Orch +{ +public: + OtnMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, const std::vector &tableNames, const std::map &tableMaps); + + using Orch::doTask; +private: + DBConnector *m_appl_db; + DBConnector *m_state_db; + const std::map &m_tableMaps; + std::map> m_appTables; + + void doTask(Consumer &consumer); + bool writeConfigToAppDb(std::shared_ptr &table, const std::string &alias, const std::string &field, const std::string &value); + bool writeConfigToAppDb(std::shared_ptr &table, const std::string &alias, std::vector &field_values); +}; + +} diff --git a/cfgmgr/otnmgrd.cpp b/cfgmgr/otnmgrd.cpp new file mode 100644 index 00000000000..150885b0455 --- /dev/null +++ b/cfgmgr/otnmgrd.cpp @@ -0,0 +1,83 @@ +#include +#include +#include +#include +#include + +#include "exec.h" +#include "otnmgr.h" +#include "schema.h" +#include "select.h" + +using namespace std; +using namespace swss; + +/* select() function timeout retry time, in millisecond */ +#define SELECT_TIMEOUT 1000 + +int main(int argc, char **argv) +{ + Logger::linkToDbNative("OtnMgrd"); + SWSS_LOG_ENTER(); + + SWSS_LOG_NOTICE("--- Starting OtnMgrd ---"); + + try + { + map cfg_maps = + { + { CFG_OTN_ATTENUATOR_TABLE_NAME, APP_OTN_ATTENUATOR_TABLE_NAME }, + { CFG_OTN_OA_TABLE_NAME, APP_OTN_OA_TABLE_NAME }, + { CFG_OTN_OCM_TABLE_NAME, APP_OTN_OCM_TABLE_NAME }, + { CFG_OTN_OCM_CHANNEL_TABLE_NAME, APP_OTN_OCM_CHANNEL_TABLE_NAME }, + { CFG_OTN_OSC_TABLE_NAME, APP_OTN_OSC_TABLE_NAME } + }; + + vector cfg_tables; + for (auto const &it : cfg_maps) + { + cfg_tables.push_back(it.first); + } + + DBConnector cfgDb("CONFIG_DB", 0); + DBConnector appDb("APPL_DB", 0); + DBConnector stateDb("STATE_DB", 0); + + OtnMgr otnMgr(&cfgDb, &appDb, &stateDb, cfg_tables, cfg_maps); + + // TODO: add tables in stateDB which interface depends on to monitor list + vector cfgOrchList = { &otnMgr }; + + swss::Select s; + for (Orch *o : cfgOrchList) + { + s.addSelectables(o->getSelectables()); + } + + while (true) + { + Selectable *sel; + int ret; + + ret = s.select(&sel, SELECT_TIMEOUT); + if (ret == Select::ERROR) + { + SWSS_LOG_NOTICE("Error: %s!", strerror(errno)); + continue; + } + if (ret == Select::TIMEOUT) + { + otnMgr.doTask(); + continue; + } + + auto *c = (Executor *)sel; + c->execute(); + } + } + catch (const exception &e) + { + SWSS_LOG_ERROR("Runtime error: %s", e.what()); + } + return -1; +} diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index ba45c7ddeff..37e98acbbab 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -6,7 +6,8 @@ INCLUDES = -I $(top_srcdir)/lib \ -I debug_counter \ -I port \ -I pbh \ - -I nhg + -I nhg \ + -I otn SUBDIRS = p4orch/tests diff --git a/orchagent/main.cpp b/orchagent/main.cpp index 7f05479ff4f..058455a1e85 100644 --- a/orchagent/main.cpp +++ b/orchagent/main.cpp @@ -34,6 +34,9 @@ extern "C" { #include "gearboxutils.h" #include "macsecpost.h" +#include "otnhelper.h" +#include "otnorchdaemon.h" + using namespace std; using namespace swss; @@ -201,7 +204,7 @@ void getCfgSwitchType(DBConnector *cfgDb, string &switch_type, string &switch_su switch_type = "switch"; } - if (switch_type != "voq" && switch_type != "fabric" && switch_type != "chassis-packet" && switch_type != "switch" && switch_type != "dpu") + if (switch_type != "voq" && switch_type != "fabric" && switch_type != "chassis-packet" && switch_type != "switch" && switch_type != "dpu" && switch_type != SWITCH_TYPE_OTN) { SWSS_LOG_ERROR("Invalid switch type %s configured", switch_type.c_str()); //If configured switch type is none of the supported, assume regular switch @@ -584,6 +587,33 @@ int main(int argc, char **argv) // Get switch_type getCfgSwitchType(&config_db, gMySwitchType, gMySwitchSubType); + /* Initialize sairedis */ + if (gMySwitchType == SWITCH_TYPE_OTN) { + SWSS_LOG_NOTICE("OTN platform detected, initializing OTN API"); + initOtnApi(); + } else { + initSaiApi(); + } + + initSaiRedis(); + initFlexCounterTables(); + + /* Initialize remaining recorder parameters */ + Recorder::Instance().swss.setRecord( + (record_type & SWSS_RECORD_ENABLE) == SWSS_RECORD_ENABLE + ); + Recorder::Instance().swss.setLocation(record_location); + Recorder::Instance().swss.setFileName(swss_rec_filename); + Recorder::Instance().swss.startRec(true); + + Recorder::Instance().respub.setRecord( + (record_type & RESPONSE_PUBLISHER_RECORD_ENABLE) == + RESPONSE_PUBLISHER_RECORD_ENABLE + ); + Recorder::Instance().respub.setLocation(record_location); + Recorder::Instance().respub.setFileName(responsepublisher_rec_filename); + Recorder::Instance().respub.startRec(false); + sai_attribute_t attr; vector attrs; diff --git a/orchagent/otn/attenuatororch.cpp b/orchagent/otn/attenuatororch.cpp new file mode 100644 index 00000000000..ca0ac1dedc2 --- /dev/null +++ b/orchagent/otn/attenuatororch.cpp @@ -0,0 +1,39 @@ +#include "attenuatororch.h" +#include "schema.h" + + +extern sai_otn_attenuator_api_t *sai_otn_attenuator_api; + +#define OTN_ATTENUATOR_NOTIFICATION "OTN_ATTENUATOR_NOTIFICATION" +#define OTN_ATTENUATOR_REPLY "OTN_ATTENUATOR_REPLY" +#define OTN_ATTENUATOR_FLEX_COUNTER_GROUP "OTN_ATTENUATOR_FLEX_COUNTER" +#define OTN_ATTENUATOR_PLUGIN_DEFAULT_POLLING_INTERVAL_MS 1000 // ms +#define OTN_ATTENUATOR_PLUGIN_DEFAULT_ENABLED_STATE true + +AttenuatorOrch::AttenuatorOrch(DBConnector *db, const std::vector &table_names) : + ObjectOrch(db, table_names, (sai_object_type_t)SAI_OBJECT_TYPE_OTN_ATTENUATOR, CounterType::OTN_ATTENUATOR_ATTR) +{ + SWSS_LOG_ENTER(); + + std::string scriptPath = "otn_attenuator_pluggin.lua"; + createFlexCounter(scriptPath, + OTN_ATTENUATOR_PLUGIN_FIELD, + OTN_ATTENUATOR_FLEX_COUNTER_GROUP, + StatsMode::READ, + OTN_ATTENUATOR_PLUGIN_DEFAULT_POLLING_INTERVAL_MS, + OTN_ATTENUATOR_PLUGIN_DEFAULT_ENABLED_STATE); + + m_stateTable = std::unique_ptr(new Table(m_stateDb.get(), STATE_OTN_ATTENUATOR_TABLE_NAME)); + m_nameMapTable = std::unique_ptr
(new Table(m_countersDb.get(), COUNTERS_OTN_ATTENUATOR_NAME_MAP)); + + m_notificationConsumer = new NotificationConsumer(db, OTN_ATTENUATOR_NOTIFICATION); + auto notifier = new Notifier(m_notificationConsumer, this, OTN_ATTENUATOR_NOTIFICATION); + Orch::addExecutor(notifier); + m_notificationProducer = new NotificationProducer(db, OTN_ATTENUATOR_REPLY); + + m_createFunc = sai_otn_attenuator_api->create_otn_attenuator; + m_removeFunc = sai_otn_attenuator_api->remove_otn_attenuator; + m_setFunc = sai_otn_attenuator_api->set_otn_attenuator_attribute; + m_getFunc = sai_otn_attenuator_api->get_otn_attenuator_attribute; + +} diff --git a/orchagent/otn/attenuatororch.h b/orchagent/otn/attenuatororch.h new file mode 100644 index 00000000000..498168e99ba --- /dev/null +++ b/orchagent/otn/attenuatororch.h @@ -0,0 +1,9 @@ +#pragma once + +#include "objectorch.h" + +class AttenuatorOrch: public ObjectOrch +{ +public: + AttenuatorOrch(DBConnector *db, const std::vector &table_names); +}; diff --git a/orchagent/otn/oaorch.cpp b/orchagent/otn/oaorch.cpp new file mode 100644 index 00000000000..4a94a4076b1 --- /dev/null +++ b/orchagent/otn/oaorch.cpp @@ -0,0 +1,39 @@ +#include "oaorch.h" +#include "schema.h" + + +extern sai_otn_oa_api_t *sai_otn_oa_api; + +#define OTN_OA_NOTIFICATION "OTN_OA_NOTIFICATION" +#define OTN_OA_REPLY "OTN_OA_REPLY" +#define OTN_OA_FLEX_COUNTER_GROUP "OTN_OA_FLEX_COUNTER" +#define OTN_OA_DEFAULT_POLLING_INTERVAL_MS 1000 // ms +#define OTN_OA_DEFAULT_ENABLED_STATE true + +OaOrch::OaOrch(DBConnector *db, const std::vector &table_names) : + ObjectOrch(db, table_names, (sai_object_type_t)SAI_OBJECT_TYPE_OTN_OA, CounterType::OTN_OA_ATTR) +{ + SWSS_LOG_ENTER(); + + std::string scriptPath = "otn_oa_pluggin.lua"; + createFlexCounter(scriptPath, + OTN_OA_PLUGIN_FIELD, + OTN_OA_FLEX_COUNTER_GROUP, + StatsMode::READ, + OTN_OA_DEFAULT_POLLING_INTERVAL_MS, + OTN_OA_DEFAULT_ENABLED_STATE); + + m_stateTable = std::unique_ptr
(new Table(m_stateDb.get(), STATE_OTN_OA_TABLE_NAME)); + m_nameMapTable = std::unique_ptr
(new Table(m_countersDb.get(), COUNTERS_OTN_OA_NAME_MAP)); + + m_notificationConsumer = new NotificationConsumer(db, OTN_OA_NOTIFICATION); + auto notifier = new Notifier(m_notificationConsumer, this, OTN_OA_NOTIFICATION); + Orch::addExecutor(notifier); + m_notificationProducer = new NotificationProducer(db, OTN_OA_REPLY); + + m_createFunc = sai_otn_oa_api->create_otn_oa; + m_removeFunc = sai_otn_oa_api->remove_otn_oa; + m_setFunc = sai_otn_oa_api->set_otn_oa_attribute; + m_getFunc = sai_otn_oa_api->get_otn_oa_attribute; + +} diff --git a/orchagent/otn/oaorch.h b/orchagent/otn/oaorch.h new file mode 100644 index 00000000000..7e21b8fcb82 --- /dev/null +++ b/orchagent/otn/oaorch.h @@ -0,0 +1,9 @@ +#pragma once + +#include "objectorch.h" + +class OaOrch: public ObjectOrch +{ +public: + OaOrch(DBConnector *db, const std::vector &table_names); +}; diff --git a/orchagent/otn/objectorch.cpp b/orchagent/otn/objectorch.cpp new file mode 100644 index 00000000000..df7d01534a9 --- /dev/null +++ b/orchagent/otn/objectorch.cpp @@ -0,0 +1,775 @@ +#include +#include +#include +#include +#include +#include +#include +#include "timestamp.h" +#include "objectorch.h" +#include "sai_serialize.h" +#include "flexcounterorch.h" +#include "converter.h" +#include "subscriberstatetable.h" +#include "tokenize.h" +#include "logger.h" +#include "consumerstatetable.h" +#include "redisapi.h" + + +extern sai_object_id_t gSwitchId; +extern FlexManagerDirectory g_FlexManagerDirectory; + +void ObjectOrch::localDataInit(DBConnector *db) +{ + SWSS_LOG_ENTER(); + + const char *objectName = sai_metadata_get_object_type_name(m_objectType); + if (objectName == NULL) + { + SWSS_LOG_ERROR("Invalid object type %u", m_objectType); + return; + } + + m_objectName = objectName; + m_stateDb = std::shared_ptr(new DBConnector("STATE_DB", 0)); + m_countersDb = std::shared_ptr(new DBConnector("COUNTERS_DB", 0)); + m_vid2NameTable = std::unique_ptr
(new Table(m_countersDb.get(), "VID2NAME")); + + SWSS_LOG_NOTICE("ObjectOrch init, object type=%u, object name=%s", m_objectType, objectName); + + /* Initialize local data from meta data, save the short names to match openconfig keys */ + const sai_object_type_info_t *oi = sai_metadata_get_object_type_info(m_objectType); + if (oi == NULL) { + SWSS_LOG_ERROR("Invalid object type %u, object name=%s", m_objectType, objectName); + return; + } + + for (size_t index = 0; index < oi->enummetadata->valuescount; index++) + { + /** + * Record the attribute short name and id. + * The default name format from sai meta data is underline. + */ + std::string name(oi->enummetadata->valuesshortnames[index]); + std::transform(name.begin(), name.end(), name.begin(), ::tolower); + + /** + * To compatible with both hyphen and underline naming. + * e.g. + * 1. leaf-name + * 2. leaf_name + */ + std::string hyphen_name(name); + std::replace(hyphen_name.begin(), hyphen_name.end(), '_', '-'); + sai_attr_id_t id = oi->enummetadata->values[index]; + const sai_attr_metadata_t *const attr = oi->attrmetadata[index]; + + SWSS_LOG_DEBUG("localDataInit, enum index=%ld, attr valueprecision: %ld", index, attr->valueprecision); + // Save precision value for each attribuite if precision is valid. + if (attr->valueprecision > 0) { + m_attrPrecisions[name] = attr->valueprecision; + m_attrPrecisions[hyphen_name] = attr->valueprecision; + } + + // Save attribute id for each different type attribute. + auto addAttrToMap = [&](std::map& attr_map) { + attr_map[name] = id; + attr_map[hyphen_name] = id; + return; + }; + + if (attr->ismandatoryoncreate) + { + addAttrToMap(m_mandatoryAttrs); + } + + if (attr->iscreateonly) + { + addAttrToMap(m_createonlyAttrs); + } + else if (attr->iscreateandset) + { + addAttrToMap(m_createandsetAttrs); + } + else if (attr->isreadonly) + { + addAttrToMap(m_readonlyAttrs); + + /* add original name for flex counter */ + m_readonlyOrgAttrs[oi->enummetadata->valuesnames[index]] = id; + } + + if (attr->isenum) + { + for (size_t i = 0; i < attr->enummetadata->valuescount; i++) + { + /* enum original name */ + std::string enum_name(attr->enummetadata->valuesshortnames[i]); + m_enumValues[enum_name] = attr->enummetadata->valuesnames[i]; + + /* support enum name with lower case */ + std::transform(enum_name.begin(), enum_name.end(), enum_name.begin(), ::tolower); + m_enumValues[enum_name] = attr->enummetadata->valuesnames[i]; + } + } + } + + /* Set create and set, create only attributes to state cache list */ + for (auto it : m_createandsetAttrs) + { + m_needToCache.insert(it.first); + } + + for (auto it : m_createonlyAttrs) + { + m_needToCache.insert(it.first); + } + + SWSS_LOG_DEBUG("localDataInit, exit"); +} + +ObjectOrch::ObjectOrch(DBConnector *db, + const std::vector& table_names, + sai_object_type_t obj_type, + CounterType flex_counter_type) : + Orch(db, table_names), + m_objectType(obj_type), + m_flex_counter_type(flex_counter_type), + m_notificationConsumer(nullptr), + m_notificationProducer(nullptr), + m_flex_stat_manager(nullptr) +{ + SWSS_LOG_ENTER(); + + localDataInit(db); +} + +ObjectOrch::ObjectOrch(DBConnector *db, + std::vector &connectors, + sai_object_type_t obj_type, + CounterType flex_counter_type) : + Orch(connectors), + m_objectType(obj_type), + m_flex_counter_type(flex_counter_type), + m_notificationConsumer(nullptr), + m_notificationProducer(nullptr), + m_flex_stat_manager(nullptr) +{ + SWSS_LOG_ENTER(); + + localDataInit(db); +} + +void ObjectOrch::doTask(NotificationConsumer& consumer) +{ + SWSS_LOG_ENTER(); + + std::string op; + std::string data; + sai_status_t status; + std::vector values; + sai_object_id_t oid = SAI_NULL_OBJECT_ID; + + if (&consumer != m_notificationConsumer) + { + return; + } + + consumer.pop(op, data, values); + + if (m_key2oid.find(data) == m_key2oid.end()) + { + SWSS_LOG_ERROR("Failed to get oid, key=%s|%s", m_objectName.c_str(), data.c_str()); + goto error; + } + + oid = m_key2oid[data]; + + if (op == "set") + { + for (unsigned i = 0; i < values.size(); i++) + { + std::string &value = fvValue(values[i]); + std::string &field = fvField(values[i]); + + status = setObjectAttr(oid, field, value); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set attr, field=%s, value=%s, status=%d", + field.c_str(), value.c_str(), status); + goto error; + } + } + op = "SUCCESS"; + m_notificationProducer->send(op, data, values); + + return; + } + else if (op == "get") + { + for (unsigned i = 0; i < values.size(); i++) + { + std::string &value = fvValue(values[i]); + std::string &field = fvField(values[i]); + + status = getObjectAttr(oid, field, value); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to get attr, field=%s, status=%d", + field.c_str(), status); + goto error; + } + } + op = "SUCCESS"; + m_notificationProducer->send(op, data, values); + + return; + } + +error: + op = "FAILED"; + m_notificationProducer->send(op, data, values); + + return; +} + +bool ObjectOrch::createObject(const std::string &key) +{ + SWSS_LOG_ENTER(); + + std::vector attrs; + std::map &createonly_attrs = m_key2createonlyAttrs[key]; + for (auto fv: createonly_attrs) + { + sai_attribute_t attr; + if (translateObjectAttr(fv.first, fv.second, attr) == false) + { + SWSS_LOG_ERROR("Failed to translate attr, %s|%s", + m_objectName.c_str(), fv.first.c_str()); + continue; + } + attrs.push_back(attr); + } + + addExtraAttrsOnCreate(key, attrs); + + sai_object_id_t oid; + sai_status_t status = m_createFunc(&oid, gSwitchId, static_cast(attrs.size()), attrs.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create %s|%s, rv=%d", m_objectName.c_str(), key.c_str(), status); + return false; + } + + /* Copy config to state */ + copyConfigToState(key, createonly_attrs); + + SWSS_LOG_NOTICE("Create %s|%s oid:%" PRIx64, m_objectName.c_str(), key.c_str(), oid); + + m_key2oid[key] = oid; + + if (!setObjectAttrs(key, m_key2createandsetAttrs[key])) + { + SWSS_LOG_ERROR("Failed to set fields, %s", key.c_str()); + } + + FieldValueTuple tuple(sai_serialize_object_id(oid), key); + std::vector fields; + fields.push_back(tuple); + m_nameMapTable->set("", fields); + + m_vid2NameTable->set("", fields); + + setFlexCounter(oid); + + SWSS_LOG_NOTICE("Initialized %s", key.c_str()); + + return true; +} + +bool ObjectOrch::removeObject(const std::string &key) +{ + SWSS_LOG_ENTER(); + + sai_status_t status; + sai_object_id_t oid; + + if (m_key2oid.find(key) == m_key2oid.end()) + { + SWSS_LOG_ERROR("Failed to get oid, key=%s|%s", m_objectName.c_str(), key.c_str()); + return false; + } + + oid = m_key2oid[key]; + + /* clean flex counter first then remove object */ + clearFlexCounter(oid); + + status = m_removeFunc(oid); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove %s|%s, rv=%d", m_objectName.c_str(), key.c_str(), status); + return false; + } + + SWSS_LOG_NOTICE("Remove %s|%s oid:%" PRIx64, m_objectName.c_str(), key.c_str(), oid); + + m_keys.erase(key); + m_key2oid.erase(key); + m_key2createonlyAttrs.erase(key); + m_key2createandsetAttrs.erase(key); + + /* delete redis backed tables: + m_vid2NameTable + m_nameMapTable + */ + std::string oid_str = sai_serialize_object_id(oid); + m_vid2NameTable->hdel("", oid_str); + m_nameMapTable->hdel("", oid_str); + + return true; +} + +void ObjectOrch::publishOperationResult(std::string channel, sai_status_t status_code, std::string message) +{ + swss::NotificationProducer notifications(m_stateDb.get(), channel); + std::vector entry; + auto sent_clients = notifications.send(std::to_string(status_code), message, entry); + SWSS_LOG_NOTICE("publishresult %d, %s to %ld client on channel %s", + status_code, message.c_str(), sent_clients, channel.c_str()); +} + +bool ObjectOrch::setObjectAttrs(const std::string& key, std::map& field_values, std::string operation_id) +{ + SWSS_LOG_ENTER(); + + bool rv = true; + + if (m_key2oid.find(key) == m_key2oid.end()) + { + SWSS_LOG_ERROR("Failed to get oid, key=%s|%s", m_objectName.c_str(), key.c_str()); + return false; + } + + std::string error_msg; + sai_status_t status = SAI_STATUS_SUCCESS; + + for (auto fv : field_values) + { + std::string channel = fv.first + "-" + operation_id; + + SWSS_LOG_NOTICE("set field=%s value=%s", fv.first.c_str(), fv.second.c_str()); + + status = setObjectAttr(m_key2oid[key], fv.first, fv.second); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set %s|%s %s to %s, status=%d", + m_objectName.c_str(), + key.c_str(), + fv.first.c_str(), + fv.second.c_str(), + status); + + rv = false; + error_msg = "Failed to set " + key + " " + fv.first + " to " + fv.second; + } + else + { + SWSS_LOG_NOTICE("Set %s|%s %s to %s", + m_objectName.c_str(), + key.c_str(), + fv.first.c_str(), + fv.second.c_str()); + + copyConfigToState(key, fv); + + error_msg = "Set " + key + " " + fv.first + " to " + fv.second; + } + + publishOperationResult(channel, status, error_msg); + } + + return rv; +} + +bool ObjectOrch::translateObjectAttr( + _In_ const std::string &field, + _In_ const std::string &value, + _Out_ sai_attribute_t &attr) +{ + if (m_createandsetAttrs.find(field) != m_createandsetAttrs.end()) + { + attr.id = m_createandsetAttrs[field]; + } + else if (m_createonlyAttrs.find(field) != m_createonlyAttrs.end()) + { + attr.id = m_createonlyAttrs[field]; + } + else + { + SWSS_LOG_ERROR("Unrecognized attr, %s|%s", m_objectName.c_str(), field.c_str()); + return false; + } + + auto meta = sai_metadata_get_attr_metadata(m_objectType, attr.id); + if (meta == nullptr) + { + SWSS_LOG_THROW("Unable to get %s metadata, attr=%d", m_objectName.c_str(), attr.id); + } + + /* Value translate */ + std::string newValue(value); + if (m_enumValues.find(value) != m_enumValues.end()) + { + newValue = m_enumValues[value]; + } + else if (m_attrPrecisions.find(field) != m_attrPrecisions.end()) + { + /* Convert float string to int string according to the precision */ + try + { + double float_value = std::stod(value); + size_t precision = m_attrPrecisions[field]; + int64_t int_value = static_cast(float_value * (std::pow(10, precision))); + newValue = std::to_string(int_value); + } + catch (const std::invalid_argument &e) { + SWSS_LOG_ERROR("Invalid float value, %s|%s|%s", + m_objectName.c_str(), field.c_str(), value.c_str()); + return false; + } + catch (const std::out_of_range &e) { + SWSS_LOG_ERROR("Out of range float value, %s|%s|%s", + m_objectName.c_str(), field.c_str(), value.c_str()); + return false; + } + } + + SWSS_LOG_NOTICE("translateObjectAttr, field = %s, value = %s", field.c_str(), newValue.c_str()); + + try + { + sai_deserialize_attr_value(newValue, *meta, attr); + } + catch (...) + { + SWSS_LOG_ERROR("Unrecongnized attr value, %s|%s|%s", + m_objectName.c_str(), field.c_str(), newValue.c_str()); + return false; + } + + return true; +} + +sai_status_t ObjectOrch::setObjectAttr( + sai_object_id_t oid, + const std::string &field, + const std::string &value) +{ + SWSS_LOG_ENTER(); + + sai_attribute_t attr; + if (translateObjectAttr(field, value, attr) == false) + { + SWSS_LOG_ERROR("Failed to translate attr, %s|%s", + m_objectName.c_str(), field.c_str()); + return SAI_STATUS_FAILURE; + } + + sai_status_t status = m_setFunc(oid, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set %s attr, field=%s, value=%s, status=%d", + m_objectName.c_str(), field.c_str(), value.c_str(), status); + return status; + } + + SWSS_LOG_NOTICE("Set %s attr, pid:%" PRIx64 " field=%s, value=%s", + m_objectName.c_str(), oid, field.c_str(), value.c_str()); + + return SAI_STATUS_SUCCESS; +} + +sai_status_t ObjectOrch::getObjectAttr(sai_object_id_t oid, const std::string &field, std::string &value) +{ + SWSS_LOG_ENTER(); + + sai_attribute_t attr; + if (m_readonlyAttrs.find(field) == m_readonlyAttrs.end()) + { + SWSS_LOG_ERROR("Unsupported attr, %s|%s", m_objectName.c_str(), field.c_str()); + return SAI_STATUS_FAILURE; + } + attr.id = m_readonlyAttrs[field]; + + sai_status_t status = m_getFunc(oid, 1, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to get %s attr, field=%s, status=%d", + m_objectName.c_str(), field.c_str(), status); + return status; + } + auto meta = sai_metadata_get_attr_metadata(m_objectType, attr.id); + if (meta == NULL) + { + SWSS_LOG_ERROR("Unable to get %s metadata, attr=%d", m_objectName.c_str(), attr.id); + return SAI_STATUS_FAILURE; + } + + try + { + value = sai_serialize_attr_value(*meta, attr, false); + } + catch (...) + { + SWSS_LOG_ERROR("Failed to serialize attr value, %s|%s|%s", + m_objectName.c_str(), field.c_str(), value.c_str()); + return SAI_STATUS_FAILURE; + } + SWSS_LOG_NOTICE("Get %s attr successed, pid:%" PRIx64 " field=%s, value=%s", + m_objectName.c_str(), oid, field.c_str(), value.c_str()); + + return SAI_STATUS_SUCCESS; +} + +void ObjectOrch::doTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + if (consumer.getDbName() == "STATE_DB") + { + doStateTask(consumer); + return; + } + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + auto &t = it->second; + + std::string key = kfvKey(t); + std::string op = kfvOp(t); + + SWSS_LOG_NOTICE("doTask: Table = %s, key = %s, op = %s", m_objectName.c_str(), key.c_str(), op.c_str()); + + if (op == SET_COMMAND) + { + std::string operation_id = key; + + std::map createonly_attrs; + std::map createandset_attrs; + + for (auto i : kfvFieldsValues(t)) + { + auto name = fvField(i); + if (m_createonlyAttrs.find(name) != m_createonlyAttrs.end()) + { + createonly_attrs[name] = fvValue(i); + } + else if (m_createandsetAttrs.find(name) != m_createandsetAttrs.end()) + { + createandset_attrs[name] = fvValue(i); + SWSS_LOG_NOTICE("ObjectOrch::doTask, key=%s, value=%s", name.c_str(), fvValue(i).c_str()); + } + } + + // Add attribute name + createonly_attrs["name"] = key; + + if (m_keys.find(key) == m_keys.end()) + { + //Add create only attribute. + m_keys.insert(key); + m_key2createandsetAttrs[key] = createandset_attrs; + m_key2createonlyAttrs[key] = createonly_attrs; + } + + it = consumer.m_toSync.erase(it); + + /* Create object if needed */ + if (m_key2oid.find(key) == m_key2oid.end()) + { + if (!createObject(key)) + { + SWSS_LOG_THROW("Failed to create object"); + } + + continue; + } + + if (!setObjectAttrs(key, createandset_attrs, operation_id)) + { + SWSS_LOG_ERROR("Failed to set attributes, %s", key.c_str()); + } + } + else if (op == DEL_COMMAND) + { + SWSS_LOG_NOTICE("Deleting %s", key.c_str()); + if (!removeObject(key)) + { + SWSS_LOG_ERROR("Failed to remove object, %s", key.c_str()); + } + + it = consumer.m_toSync.erase(it); + } + else + { + SWSS_LOG_ERROR("Unknown operation type %s", op.c_str()); + it = consumer.m_toSync.erase(it); + } + } +} + +void ObjectOrch::doStateTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + // TODO, need to solve present state. + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + auto &t = it->second; + + std::string key = kfvKey(t); + std::string op = kfvOp(t); + + bool has_present_field = false; + std::string present_value; + + SWSS_LOG_DEBUG("%s, key = %s, op = %s", m_objectName.c_str(), key.c_str(), op.c_str()); + + if (m_key2oid.find(key) == m_key2oid.end()) + { + it = consumer.m_toSync.erase(it); + continue; + } + + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == "present") + { + has_present_field = true; + present_value = fvValue(i); + break; + } + } + if (has_present_field == false) + { + it = consumer.m_toSync.erase(it); + continue; + } + + sai_object_id_t id = m_key2oid[key]; + + std::string present; + + if (m_key2present.find(key) != m_key2present.end()) + { + present = m_key2present[key]; + } + + if (present_value != present) + { + if (present_value == "PRESENT") + { + SWSS_LOG_NOTICE("setCounterIdList 0x%lx, key = %s", id, key.c_str()); + setFlexCounter(id); + } + else if (present_value == "NOT_PRESENT") + { + SWSS_LOG_NOTICE("clearCounterIdList 0x%lx, key = %s", id, key.c_str()); + clearFlexCounter(id); + } + + doSubobjectStateTask(key, present_value); + m_key2present[key] = present_value; + } + + it = consumer.m_toSync.erase(it); + } +} + +bool ObjectOrch::createFlexCounter( + _In_ const std::string& script_path, + _In_ const std::string& plugin_field, + _In_ const std::string& group_name, + _In_ const StatsMode stats_mode, + _In_ const uint polling_interval, + _In_ const bool enabled) +{ + SWSS_LOG_ENTER(); + + FieldValueTuple fv_stat = std::make_pair("",""); + + if (!script_path.empty()) + { + try + { + std::string path("/usr/share/sonic/platform/"); + path += script_path; + std::string att_script = swss::readTextFile(path); + std::string att_sha = swss::loadRedisScript(m_countersDb.get(), att_script); + fv_stat = FieldValueTuple(plugin_field, att_sha); + } + catch (const std::runtime_error &e) + { + SWSS_LOG_WARN("%s group plugins was not set successfully: %s", group_name.c_str(), e.what()); + fv_stat = std::make_pair("",""); + } + } + + m_flex_stat_manager = g_FlexManagerDirectory.createFlexCounterManager( + group_name, stats_mode, polling_interval, enabled, fv_stat); + + return m_flex_stat_manager != nullptr; +} + +void ObjectOrch::setFlexCounter(sai_object_id_t id) +{ + SWSS_LOG_ENTER(); + + if (m_flex_stat_manager == nullptr) + { + SWSS_LOG_WARN("Flex counter manager not initialized for %" PRIx64 "", id); + return; + } + + std::unordered_set counter_attrs; + for (const auto& it : m_readonlyOrgAttrs) { + counter_attrs.emplace(it.first); + } + m_flex_stat_manager->setCounterIdList(id, m_flex_counter_type, counter_attrs); +} + +void ObjectOrch::clearFlexCounter(sai_object_id_t id) { + SWSS_LOG_ENTER(); + + if (m_flex_stat_manager == nullptr) + { + SWSS_LOG_WARN("Flex counter manager not initialized"); + return; + } + + SWSS_LOG_NOTICE("Clear flex counter, id: %" PRIx64 "", id); + m_flex_stat_manager->clearCounterIdList(id); +} + +void ObjectOrch::copyConfigToState(const std::string &key, const FieldValueTuple &fv) +{ + if (m_needToCache.find(fv.first) != m_needToCache.end()) + { + std::vector fvs; + fvs.push_back(fv); + m_stateTable->set(key, fvs); + } +} + +void ObjectOrch::copyConfigToState(const std::string &key, std::map &fvs) +{ + for (const auto &fv : fvs) + { + copyConfigToState(key, fv); + } +} diff --git a/orchagent/otn/objectorch.h b/orchagent/otn/objectorch.h new file mode 100644 index 00000000000..9ad22e7de11 --- /dev/null +++ b/orchagent/otn/objectorch.h @@ -0,0 +1,166 @@ +#pragma once + +#include +#include +#include +#include +#include "orch.h" +#include "saihelper.h" +#include "notifier.h" +#include "notificationproducer.h" +#include "notifications.h" +#include "timer.h" +#include "flex_counter_manager.h" + +using namespace swss; + +typedef sai_status_t (*CreateObjectFunc)( + sai_object_id_t *oid, + sai_object_id_t linecard_id, + uint32_t attr_count, + const sai_attribute_t *attr_list); + +typedef sai_status_t (*RemoveObjectFunc)( + sai_object_id_t oid); + +typedef sai_status_t (*SetObjectAttrFunc)( + sai_object_id_t oid, + const sai_attribute_t *attr); + +typedef sai_status_t (*GetObjectAttrFunc)( + sai_object_id_t oid, + uint32_t attr_count, + sai_attribute_t *attr_list); + +typedef enum _ConfigState_E +{ + CONFIG_MISSING = 0, + CONFIG_RECEIVED, + CONFIG_CREATED, + CONFIG_DONE, +} ConfigState_E; + +class ObjectOrch: public Orch +{ +public: + ObjectOrch(DBConnector *db, const std::vector &table_names): Orch(db, table_names) {} + + ObjectOrch(DBConnector *db, + const std::vector &table_names, + sai_object_type_t obj_type, + CounterType flex_counter_type); + + ObjectOrch(DBConnector *db, + std::vector &connectors, + sai_object_type_t obj_type, + CounterType flex_counter_type); + + void localDataInit(DBConnector *db); + + void doTask(Consumer &consumer); + + virtual void doTask(NotificationConsumer &consumer); + + void doStateTask(Consumer &consumer); + + bool createObject(const std::string &key); + bool removeObject(const std::string &key); + + virtual void addExtraAttrsOnCreate(const std::string &key, std::vector &attrs) {}; + + bool setObjectAttrs(const std::string &key, + std::map &field_values, + std::string operation_id=""); + + sai_status_t setObjectAttr(sai_object_id_t oid, const std::string &field, const std::string &value); + + sai_status_t getObjectAttr(sai_object_id_t oid, const std::string &field, std::string &value); + + virtual void setFlexCounter(sai_object_id_t id); + + virtual void clearFlexCounter(sai_object_id_t id); + + virtual void doSubobjectStateTask(const std::string &key, const std::string &present){}; + + void publishOperationResult(std::string operation_id, int status_code, std::string message); + + bool translateObjectAttr(_In_ const std::string &field, + _In_ const std::string &value, + _Out_ sai_attribute_t &attr); + + bool createFlexCounter(_In_ const std::string &script_path, + _In_ const std::string &plugin_field, + _In_ const std::string &group_name, + _In_ const StatsMode stats_mode, + _In_ const uint polling_interval, + _In_ const bool enabled); + + void copyConfigToState(const std::string &key, const FieldValueTuple &fv); + void copyConfigToState(const std::string &key, std::map &fvs); + +protected: + + std::shared_ptr m_stateDb; + + std::unique_ptr
m_stateTable; + + std::shared_ptr m_countersDb; + + std::unique_ptr
m_nameMapTable; + + std::unique_ptr
m_vid2NameTable; + + CreateObjectFunc m_createFunc; + + RemoveObjectFunc m_removeFunc; + + SetObjectAttrFunc m_setFunc; + + GetObjectAttrFunc m_getFunc; + + uint32_t m_count; + + sai_object_type_t m_objectType; + + std::string m_objectName; + + CounterType m_flex_counter_type; + + /* Attributes that can be modified at anytime. */ + std::map m_createandsetAttrs; + + /* Attributes that can only be set during creation. */ + std::map m_createonlyAttrs; + + std::map m_mandatoryAttrs; + + std::map m_readonlyAttrs; + + std::map m_enumValues; + + /* record original name instead of short name above */ + std::map m_readonlyOrgAttrs; + + /* record precision */ + std::map m_attrPrecisions; + + ConfigState_E m_configState = CONFIG_MISSING; + + std::set m_keys; + + std::map m_key2oid; + + std::map> m_key2createonlyAttrs; + + std::map> m_key2createandsetAttrs; + + std::map m_key2present; + + std::set m_needToCache; + + NotificationConsumer *m_notificationConsumer; + + NotificationProducer *m_notificationProducer; + + FlexCounterManager *m_flex_stat_manager; +}; diff --git a/orchagent/otn/ocmorch.cpp b/orchagent/otn/ocmorch.cpp new file mode 100644 index 00000000000..efa3cd35ae1 --- /dev/null +++ b/orchagent/otn/ocmorch.cpp @@ -0,0 +1,72 @@ +#include "ocmorch.h" +#include "schema.h" + + +extern sai_otn_ocm_api_t *sai_otn_ocm_api; + +#define OTN_OCM_NOTIFICATION "OTN_OCM_NOTIFICATION" +#define OTN_OCM_REPLY "OTN_OCM_REPLY" + +#define OTN_OCM_CHANNEL_NOTIFICATION "OTN_OCM_CHANNEL_NOTIFICATION" +#define OTN_OCM_CHANNEL_REPLY "OTN_OCM_CHANNEL_REPLY" +#define OTN_OCM_CHANNEL_FLEX_COUNTER_GROUP "OTN_OCM_CHANNEL_FLEX_COUNTER" +#define OTN_OCM_CHANNEL_DEFAULT_POLLING_INTERVAL_MS 1000 // ms +#define OTN_OCM_CHANNEL_DEFAULT_ENABLED_STATE true + +OcmOrch::OcmOrch(DBConnector *db, const std::vector &table_names) : + ObjectOrch(db, table_names, (sai_object_type_t)SAI_OBJECT_TYPE_OTN_OCM, CounterType::OTN_OCM_ATTR) +{ + SWSS_LOG_ENTER(); + + // For OCM channel pluggin + std::string scriptPath = "otn_ocm_pluggin.lua"; + createFlexCounter(scriptPath, + OTN_OCM_CHANNEL_PLUGIN_FIELD, + OTN_OCM_CHANNEL_FLEX_COUNTER_GROUP, + StatsMode::READ, + OTN_OCM_CHANNEL_DEFAULT_POLLING_INTERVAL_MS, + OTN_OCM_CHANNEL_DEFAULT_ENABLED_STATE); + + m_stateTable = std::unique_ptr
(new Table(m_stateDb.get(), STATE_OTN_OCM_TABLE_NAME)); + m_nameMapTable = std::unique_ptr
(new Table(m_countersDb.get(), COUNTERS_OTN_OCM_NAME_MAP)); + + m_notificationConsumer = new NotificationConsumer(db, OTN_OCM_NOTIFICATION); + auto notifier = new Notifier(m_notificationConsumer, this, OTN_OCM_NOTIFICATION); + Orch::addExecutor(notifier); + m_notificationProducer = new NotificationProducer(db, OTN_OCM_REPLY); + + m_createFunc = sai_otn_ocm_api->create_otn_ocm; + m_removeFunc = sai_otn_ocm_api->remove_otn_ocm; + m_setFunc = sai_otn_ocm_api->set_otn_ocm_attribute; + m_getFunc = sai_otn_ocm_api->get_otn_ocm_attribute; + +} + +// OCM channel +OcmChannelOrch::OcmChannelOrch(DBConnector *db, const std::vector &table_names) : + ObjectOrch(db, table_names, (sai_object_type_t)SAI_OBJECT_TYPE_OTN_OCM_CHANNEL, CounterType::OTN_OCM_CHANNEL_ATTR) +{ + SWSS_LOG_ENTER(); + + // For OCM channel flex counter + createFlexCounter("", + "", + OTN_OCM_CHANNEL_FLEX_COUNTER_GROUP, + StatsMode::READ, + OTN_OCM_CHANNEL_DEFAULT_POLLING_INTERVAL_MS, + OTN_OCM_CHANNEL_DEFAULT_ENABLED_STATE); + + m_stateTable = std::unique_ptr
(new Table(m_stateDb.get(), STATE_OTN_OCM_CHANNEL_TABLE_NAME)); + m_nameMapTable = std::unique_ptr
(new Table(m_countersDb.get(), COUNTERS_OTN_OCM_CHANNEL_NAME_MAP)); + + m_notificationConsumer = new NotificationConsumer(db, OTN_OCM_CHANNEL_NOTIFICATION); + auto notifier = new Notifier(m_notificationConsumer, this, OTN_OCM_CHANNEL_NOTIFICATION); + Orch::addExecutor(notifier); + m_notificationProducer = new NotificationProducer(db, OTN_OCM_CHANNEL_REPLY); + + m_createFunc = sai_otn_ocm_api->create_otn_ocm_channel; + m_removeFunc = sai_otn_ocm_api->remove_otn_ocm_channel; + m_setFunc = sai_otn_ocm_api->set_otn_ocm_channel_attribute; + m_getFunc = sai_otn_ocm_api->get_otn_ocm_channel_attribute; + +} diff --git a/orchagent/otn/ocmorch.h b/orchagent/otn/ocmorch.h new file mode 100644 index 00000000000..28c9dffece7 --- /dev/null +++ b/orchagent/otn/ocmorch.h @@ -0,0 +1,16 @@ +#pragma once + +#include "objectorch.h" + +class OcmOrch: public ObjectOrch +{ +public: + OcmOrch(DBConnector *db, const std::vector &table_names); +}; + + +class OcmChannelOrch: public ObjectOrch +{ +public: + OcmChannelOrch(DBConnector *db, const std::vector &table_names); +}; diff --git a/orchagent/otn/oscorch.cpp b/orchagent/otn/oscorch.cpp new file mode 100644 index 00000000000..9c8b6eb1bb0 --- /dev/null +++ b/orchagent/otn/oscorch.cpp @@ -0,0 +1,39 @@ +#include "oscorch.h" +#include "schema.h" + + +extern sai_otn_osc_api_t *sai_otn_osc_api; + +#define OTN_OSC_NOTIFICATION "OTN_OSC_NOTIFICATION" +#define OTN_OSC_REPLY "OTN_OSC_REPLY" +#define OTN_OSC_FLEX_COUNTER_GROUP "OTN_OSC_FLEX_COUNTER" +#define OTN_OSC_DEFAULT_POLLING_INTERVAL_MS 1000 // ms +#define OTN_OSC_DEFAULT_ENABLED_STATE true + +OscOrch::OscOrch(DBConnector *db, const std::vector &table_names) : + ObjectOrch(db, table_names, (sai_object_type_t)SAI_OBJECT_TYPE_OTN_OSC, CounterType::OTN_OSC_ATTR) +{ + SWSS_LOG_ENTER(); + + std::string scriptPath = "otn_osc_pluggin.lua"; + createFlexCounter(scriptPath, + OTN_OSC_PLUGIN_FIELD, + OTN_OSC_FLEX_COUNTER_GROUP, + StatsMode::READ, + OTN_OSC_DEFAULT_POLLING_INTERVAL_MS, + OTN_OSC_DEFAULT_ENABLED_STATE); + + m_stateTable = std::unique_ptr
(new Table(m_stateDb.get(), STATE_OTN_OSC_TABLE_NAME)); + m_nameMapTable = std::unique_ptr
(new Table(m_countersDb.get(), COUNTERS_OTN_OSC_NAME_MAP)); + + m_notificationConsumer = new NotificationConsumer(db, OTN_OSC_NOTIFICATION); + auto notifier = new Notifier(m_notificationConsumer, this, OTN_OSC_NOTIFICATION); + Orch::addExecutor(notifier); + m_notificationProducer = new NotificationProducer(db, OTN_OSC_REPLY); + + m_createFunc = sai_otn_osc_api->create_otn_osc; + m_removeFunc = sai_otn_osc_api->remove_otn_osc; + m_setFunc = sai_otn_osc_api->set_otn_osc_attribute; + m_getFunc = sai_otn_osc_api->get_otn_osc_attribute; + +} diff --git a/orchagent/otn/oscorch.h b/orchagent/otn/oscorch.h new file mode 100644 index 00000000000..d1d0fc3b992 --- /dev/null +++ b/orchagent/otn/oscorch.h @@ -0,0 +1,9 @@ +#pragma once + +#include "objectorch.h" + +class OscOrch: public ObjectOrch +{ +public: + OscOrch(DBConnector *db, const std::vector &table_names); +}; diff --git a/orchagent/otn/otnhelper.cpp b/orchagent/otn/otnhelper.cpp new file mode 100644 index 00000000000..6ba50042a72 --- /dev/null +++ b/orchagent/otn/otnhelper.cpp @@ -0,0 +1,63 @@ +extern "C" { + +#include "sai.h" +#include "saistatus.h" +#include "saiextensions.h" +} + +#include + +#include +#include +#include + +#include "otnhelper.h" + +using namespace swss; + + +/* Initialize all otai api pointers */ +extern sai_switch_api_t *sai_switch_api; +extern sai_router_interface_api_t *sai_router_intfs_api; +sai_otn_attenuator_api_t *sai_otn_attenuator_api; +sai_otn_oa_api_t *sai_otn_oa_api; +sai_otn_ocm_api_t *sai_otn_ocm_api; +sai_otn_osc_api_t *sai_otn_osc_api; + + +extern const char *test_profile_get_value ( + _In_ sai_switch_profile_id_t profile_id, + _In_ const char *variable); + +extern int test_profile_get_next_value ( + _In_ sai_switch_profile_id_t profile_id, + _Out_ const char **variable, + _Out_ const char **value); + + +void initOtnApi() +{ + SWSS_LOG_ENTER(); + SWSS_LOG_NOTICE("Initializing OTN API"); + + sai_service_method_table_t services = { + test_profile_get_value, + test_profile_get_next_value + }; + + sai_api_initialize(0, (const sai_service_method_table_t *)&services); + + sai_api_query(SAI_API_SWITCH, (void **)&sai_switch_api); + sai_api_query(SAI_API_ROUTER_INTERFACE, (void **)&sai_router_intfs_api); + sai_api_query((sai_api_t)SAI_API_OTN_ATTENUATOR, (void **)&sai_otn_attenuator_api); + sai_api_query((sai_api_t)SAI_API_OTN_OA, (void **)&sai_otn_oa_api); + sai_api_query((sai_api_t)SAI_API_OTN_OCM, (void **)&sai_otn_ocm_api); + sai_api_query((sai_api_t)SAI_API_OTN_OSC, (void **)&sai_otn_osc_api); + + sai_log_set(SAI_API_SWITCH, SAI_LOG_LEVEL_NOTICE); + sai_log_set(SAI_API_ROUTER_INTERFACE, SAI_LOG_LEVEL_NOTICE); + sai_log_set((sai_api_t)SAI_API_OTN_ATTENUATOR, SAI_LOG_LEVEL_NOTICE); + sai_log_set((sai_api_t)SAI_API_OTN_OA, SAI_LOG_LEVEL_NOTICE); + sai_log_set((sai_api_t)SAI_API_OTN_OCM, SAI_LOG_LEVEL_NOTICE); + sai_log_set((sai_api_t)SAI_API_OTN_OSC, SAI_LOG_LEVEL_NOTICE); +} diff --git a/orchagent/otn/otnhelper.h b/orchagent/otn/otnhelper.h new file mode 100644 index 00000000000..9a2b5e695ba --- /dev/null +++ b/orchagent/otn/otnhelper.h @@ -0,0 +1,5 @@ +#pragma once + +#define SWITCH_TYPE_OTN "otn" + +void initOtnApi(); diff --git a/orchagent/otn/otnorchdaemon.cpp b/orchagent/otn/otnorchdaemon.cpp new file mode 100644 index 00000000000..899d5bbd7c4 --- /dev/null +++ b/orchagent/otn/otnorchdaemon.cpp @@ -0,0 +1,63 @@ +#include "otnorchdaemon.h" +#include "attenuatororch.h" +#include "oaorch.h" +#include "ocmorch.h" +#include "oscorch.h" + +OtnOrchDaemon::OtnOrchDaemon(DBConnector *applDb, DBConnector *configDb, DBConnector *stateDb, DBConnector *chassisAppDb, ZmqServer *zmqServer) : + OrchDaemon(applDb, configDb, stateDb, chassisAppDb, zmqServer), + m_applDb(applDb), + m_configDb(configDb) +{ + SWSS_LOG_ENTER(); + SWSS_LOG_NOTICE("OtnOrchDaemon starting..."); +} + +bool OtnOrchDaemon::init() +{ + SWSS_LOG_ENTER(); + SWSS_LOG_NOTICE("OtnOrchDaemon init"); + + /* attenuator */ + const std::vector attenuator_tables = { + APP_OTN_ATTENUATOR_TABLE_NAME + }; + AttenuatorOrch *attenuatorOrch = new AttenuatorOrch(m_applDb, attenuator_tables); + addOrchList(attenuatorOrch); + + /* OA */ + const std::vector oa_tables = { + APP_OTN_OA_TABLE_NAME + }; + OaOrch *oaOrch = new OaOrch(m_applDb, oa_tables); + addOrchList(oaOrch); + + /* OCM */ + const std::vector ocm_tables = { + APP_OTN_OCM_TABLE_NAME + }; + OcmOrch *ocmOrch = new OcmOrch(m_applDb, ocm_tables); + addOrchList(ocmOrch); + + /* OCM Channel */ + const std::vector ocm_channel_tables = { + APP_OTN_OCM_CHANNEL_TABLE_NAME + }; + OcmChannelOrch *ocmChannelOrch = new OcmChannelOrch(m_applDb, ocm_channel_tables); + addOrchList(ocmChannelOrch); + + /* OSC */ + const std::vector osc_tables = { + APP_OTN_OSC_TABLE_NAME + }; + OscOrch *oscOrch = new OscOrch(m_applDb, osc_tables); + addOrchList(oscOrch); + + /* Flex counter */ + std::vector flex_counter_tables = { + CFG_FLEX_COUNTER_TABLE_NAME + }; + addOrchList(new FlexCounterOrch(m_configDb, flex_counter_tables)); + + return true; +} diff --git a/orchagent/otn/otnorchdaemon.h b/orchagent/otn/otnorchdaemon.h new file mode 100644 index 00000000000..1c021eabb42 --- /dev/null +++ b/orchagent/otn/otnorchdaemon.h @@ -0,0 +1,13 @@ +#pragma once +#include "orchdaemon.h" + +class OtnOrchDaemon : public OrchDaemon +{ +public: + OtnOrchDaemon(DBConnector *applDb, DBConnector *configDb, DBConnector *stateDb, DBConnector *chassisAppDb, ZmqServer *zmqServer); + bool init() override; + +private: + DBConnector *m_applDb; + DBConnector *m_configDb; +}; diff --git a/orchagent/saihelper.cpp b/orchagent/saihelper.cpp index 453a056bee7..3a7c88791ef 100644 --- a/orchagent/saihelper.cpp +++ b/orchagent/saihelper.cpp @@ -448,7 +448,7 @@ void initSaiRedis() } SWSS_LOG_NOTICE("Notify syncd INIT_VIEW"); - if (platform && (strstr(platform, MLNX_PLATFORM_SUBSTRING) || strstr(platform, XS_PLATFORM_SUBSTRING))) + if (platform && (strstr(platform, MLNX_PLATFORM_SUBSTRING) || strstr(platform, XS_PLATFORM_SUBSTRING) || strstr(platform, OTN_PLATFORM_SUBSTRING))) { /* Set timeout back to the default value */ attr.id = SAI_REDIS_SWITCH_ATTR_SYNC_OPERATION_RESPONSE_TIMEOUT; From d5b9bdc0bd6a4f23e2eaad41500903c4e4b2efbe Mon Sep 17 00:00:00 2001 From: Jimmy Jin Date: Fri, 27 Feb 2026 16:58:55 -0800 Subject: [PATCH 2/2] Add OTN alarm and notification support --- cfgmgr/otnmgr.cpp | 7 +-- cfgmgr/otnmgr.h | 4 +- cfgmgr/otnmgrd.cpp | 1 + orchagent/Makefile.am | 10 +++- orchagent/otn/objectorch.cpp | 12 +++++ orchagent/otn/objectorch.h | 4 ++ orchagent/otn/otndeviceorch.cpp | 93 +++++++++++++++++++++++++++++++++ orchagent/otn/otndeviceorch.h | 16 ++++++ orchagent/otn/otnhelper.cpp | 3 ++ orchagent/otn/otnorchdaemon.cpp | 8 +++ 10 files changed, 150 insertions(+), 8 deletions(-) create mode 100644 orchagent/otn/otndeviceorch.cpp create mode 100644 orchagent/otn/otndeviceorch.h diff --git a/cfgmgr/otnmgr.cpp b/cfgmgr/otnmgr.cpp index 053f722eede..0efa3255831 100644 --- a/cfgmgr/otnmgr.cpp +++ b/cfgmgr/otnmgr.cpp @@ -75,7 +75,7 @@ void OtnMgr::doTask(Consumer &consumer) } } -bool OtnMgr::writeConfigToAppDb(std::shared_ptr &table, const std::string &alias, const std::string &field, const std::string &value) +void OtnMgr::writeConfigToAppDb(std::shared_ptr &table, const std::string &alias, const std::string &field, const std::string &value) { SWSS_LOG_ENTER(); @@ -83,14 +83,11 @@ bool OtnMgr::writeConfigToAppDb(std::shared_ptr &table, cons FieldValueTuple fv(field, value); fvs.push_back(fv); table->set(alias, fvs); - - return true; } -bool OtnMgr::writeConfigToAppDb(std::shared_ptr &table, const std::string &alias, std::vector &field_values) +void OtnMgr::writeConfigToAppDb(std::shared_ptr &table, const std::string &alias, std::vector &field_values) { SWSS_LOG_ENTER(); table->set(alias, field_values); - return true; } diff --git a/cfgmgr/otnmgr.h b/cfgmgr/otnmgr.h index a15f4b13cca..a4a85f4c6eb 100644 --- a/cfgmgr/otnmgr.h +++ b/cfgmgr/otnmgr.h @@ -23,8 +23,8 @@ class OtnMgr : public Orch std::map> m_appTables; void doTask(Consumer &consumer); - bool writeConfigToAppDb(std::shared_ptr &table, const std::string &alias, const std::string &field, const std::string &value); - bool writeConfigToAppDb(std::shared_ptr &table, const std::string &alias, std::vector &field_values); + void writeConfigToAppDb(std::shared_ptr &table, const std::string &alias, const std::string &field, const std::string &value); + void writeConfigToAppDb(std::shared_ptr &table, const std::string &alias, std::vector &field_values); }; } diff --git a/cfgmgr/otnmgrd.cpp b/cfgmgr/otnmgrd.cpp index 150885b0455..033b8525170 100644 --- a/cfgmgr/otnmgrd.cpp +++ b/cfgmgr/otnmgrd.cpp @@ -26,6 +26,7 @@ int main(int argc, char **argv) { map cfg_maps = { + { CFG_OTN_DEVICE_TABLE_NAME, APP_OTN_DEVICE_TABLE_NAME }, { CFG_OTN_ATTENUATOR_TABLE_NAME, APP_OTN_ATTENUATOR_TABLE_NAME }, { CFG_OTN_OA_TABLE_NAME, APP_OTN_OA_TABLE_NAME }, { CFG_OTN_OCM_TABLE_NAME, APP_OTN_OCM_TABLE_NAME }, diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index 37e98acbbab..3e1fa800e5e 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -139,7 +139,15 @@ orchagent_SOURCES = \ high_frequency_telemetry/hftelprofile.cpp \ high_frequency_telemetry/counternameupdater.cpp \ high_frequency_telemetry/hftelutils.cpp \ - high_frequency_telemetry/hftelgroup.cpp + high_frequency_telemetry/hftelgroup.cpp \ + otn/otnhelper.cpp \ + otn/objectorch.cpp \ + otn/attenuatororch.cpp \ + otn/oaorch.cpp \ + otn/ocmorch.cpp \ + otn/oscorch.cpp \ + otn/otndeviceorch.cpp \ + otn/otnorchdaemon.cpp orchagent_SOURCES += flex_counter/flex_counter_manager.cpp flex_counter/flex_counter_stat_manager.cpp flex_counter/flow_counter_handler.cpp flex_counter/flowcounterrouteorch.cpp orchagent_SOURCES += debug_counter/debug_counter.cpp debug_counter/drop_counter.cpp diff --git a/orchagent/otn/objectorch.cpp b/orchagent/otn/objectorch.cpp index df7d01534a9..3f83471858a 100644 --- a/orchagent/otn/objectorch.cpp +++ b/orchagent/otn/objectorch.cpp @@ -129,6 +129,18 @@ void ObjectOrch::localDataInit(DBConnector *db) SWSS_LOG_DEBUG("localDataInit, exit"); } +ObjectOrch::ObjectOrch(DBConnector *db, const std::vector &table_names, sai_object_type_t obj_type) : + Orch(db, table_names), + m_objectType(obj_type), + m_notificationConsumer(nullptr), + m_notificationProducer(nullptr), + m_flex_stat_manager(nullptr) +{ + SWSS_LOG_ENTER(); + + localDataInit(db); +} + ObjectOrch::ObjectOrch(DBConnector *db, const std::vector& table_names, sai_object_type_t obj_type, diff --git a/orchagent/otn/objectorch.h b/orchagent/otn/objectorch.h index 9ad22e7de11..393df9d7b93 100644 --- a/orchagent/otn/objectorch.h +++ b/orchagent/otn/objectorch.h @@ -45,6 +45,10 @@ class ObjectOrch: public Orch public: ObjectOrch(DBConnector *db, const std::vector &table_names): Orch(db, table_names) {} + ObjectOrch(DBConnector *db, + const std::vector &table_names, + sai_object_type_t obj_type); + ObjectOrch(DBConnector *db, const std::vector &table_names, sai_object_type_t obj_type, diff --git a/orchagent/otn/otndeviceorch.cpp b/orchagent/otn/otndeviceorch.cpp new file mode 100644 index 00000000000..e3c5190c86f --- /dev/null +++ b/orchagent/otn/otndeviceorch.cpp @@ -0,0 +1,93 @@ +#include "otndeviceorch.h" +#include "schema.h" +#include "sai_serialize_otn.h" + + +extern sai_otn_device_api_t* sai_otn_device_api; +extern sai_object_id_t gSwitchId; +extern sai_switch_api_t* sai_switch_api; + + +#define OTN_DEVICE_NOTIFICATION "OTN_DEVICE_NOTIFICATION" +#define OTN_DEVICE_REPLY "OTN_DEVICE_REPLY" + + +OtnDeviceOrch::OtnDeviceOrch(DBConnector *db, const std::vector &table_names) + : ObjectOrch(db, table_names, (sai_object_type_t)SAI_OBJECT_TYPE_OTN_DEVICE) +{ + SWSS_LOG_ENTER(); + + m_stateTable = std::unique_ptr
(new Table(m_stateDb.get(), STATE_OTN_DEVICE_TABLE_NAME)); + m_nameMapTable = std::unique_ptr
(new Table(m_countersDb.get(), COUNTERS_OTN_DEVICE_NAME_MAP)); + + m_notificationConsumer = new NotificationConsumer(db, OTN_DEVICE_NOTIFICATION); + auto notifier = new Notifier(m_notificationConsumer, this, OTN_DEVICE_NOTIFICATION); + Orch::addExecutor(notifier); + m_notificationProducer = new NotificationProducer(db, OTN_DEVICE_REPLY); + + m_createFunc = sai_otn_device_api->create_otn_device; + m_removeFunc = sai_otn_device_api->remove_otn_device; + m_setFunc = sai_otn_device_api->set_otn_device_attribute; + m_getFunc = sai_otn_device_api->get_otn_device_attribute; + + RegisterNotifications(); +} + +bool OtnDeviceOrch::RegisterNotifications() +{ + SWSS_LOG_ENTER(); + + sai_attribute_t attr = {SAI_SWITCH_ATTR_OTN_ALARM_EVENT_NOTIFY, {0}}; + sai_status_t status = SAI_STATUS_SUCCESS; + sai_attr_capability_t capability = {}; + + status = sai_query_attribute_capability(gSwitchId, SAI_OBJECT_TYPE_SWITCH, + SAI_SWITCH_ATTR_OTN_ALARM_EVENT_NOTIFY, + &capability); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Unable to query the Otn Device event notification capability"); + return false; + } + + if (!capability.set_implemented) + { + SWSS_LOG_INFO("Otn Device event notification not supported"); + return false; + } + + /* Query the alarm event notification value before setting it */ + status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Unable to query the Otn Device event notification value"); + return false; + } + + if (attr.value.ptr != nullptr) + { + SWSS_LOG_INFO("Otn Device event notification already set"); + return true; + } + + attr.value.ptr = (void *)OnOtnDeviceAlarmNotification; + + status = sai_switch_api->set_switch_attribute(gSwitchId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to register Otn Device event notification"); + return false; + } + + SWSS_LOG_NOTICE("Otn Device event notification registered"); + + return true; +} + +void OtnDeviceOrch::OnOtnDeviceAlarmNotification(uint32_t count, const sai_otn_alarm_event_data_t *data) +{ + SWSS_LOG_ENTER(); + + // TODO: handle the alarm notification + SWSS_LOG_NOTICE("Otn Device alarm notification received: %s", sai_serialize_otn_alarm_event_ntf(count, data).c_str()); +} diff --git a/orchagent/otn/otndeviceorch.h b/orchagent/otn/otndeviceorch.h new file mode 100644 index 00000000000..4e5e700d77f --- /dev/null +++ b/orchagent/otn/otndeviceorch.h @@ -0,0 +1,16 @@ +#pragma once + +#include "objectorch.h" + + +class OtnDeviceOrch : public ObjectOrch +{ +public: + OtnDeviceOrch(DBConnector *db, const std::vector &table_names); + +protected: + bool RegisterNotifications(); + +private: + static void OnOtnDeviceAlarmNotification(uint32_t count, const sai_otn_alarm_event_data_t *data); +}; diff --git a/orchagent/otn/otnhelper.cpp b/orchagent/otn/otnhelper.cpp index 6ba50042a72..ee15df1dc97 100644 --- a/orchagent/otn/otnhelper.cpp +++ b/orchagent/otn/otnhelper.cpp @@ -19,6 +19,7 @@ using namespace swss; /* Initialize all otai api pointers */ extern sai_switch_api_t *sai_switch_api; extern sai_router_interface_api_t *sai_router_intfs_api; +sai_otn_device_api_t sai_otn_device_api; sai_otn_attenuator_api_t *sai_otn_attenuator_api; sai_otn_oa_api_t *sai_otn_oa_api; sai_otn_ocm_api_t *sai_otn_ocm_api; @@ -49,6 +50,7 @@ void initOtnApi() sai_api_query(SAI_API_SWITCH, (void **)&sai_switch_api); sai_api_query(SAI_API_ROUTER_INTERFACE, (void **)&sai_router_intfs_api); + sai_api_query((sai_api_t)SAI_API_OTN_DEVICE, (void **)&sai_otn_device_api); sai_api_query((sai_api_t)SAI_API_OTN_ATTENUATOR, (void **)&sai_otn_attenuator_api); sai_api_query((sai_api_t)SAI_API_OTN_OA, (void **)&sai_otn_oa_api); sai_api_query((sai_api_t)SAI_API_OTN_OCM, (void **)&sai_otn_ocm_api); @@ -56,6 +58,7 @@ void initOtnApi() sai_log_set(SAI_API_SWITCH, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_ROUTER_INTERFACE, SAI_LOG_LEVEL_NOTICE); + sai_log_set((sai_api_t)SAI_API_OTN_DEVICE, SAI_LOG_LEVEL_NOTICE); sai_log_set((sai_api_t)SAI_API_OTN_ATTENUATOR, SAI_LOG_LEVEL_NOTICE); sai_log_set((sai_api_t)SAI_API_OTN_OA, SAI_LOG_LEVEL_NOTICE); sai_log_set((sai_api_t)SAI_API_OTN_OCM, SAI_LOG_LEVEL_NOTICE); diff --git a/orchagent/otn/otnorchdaemon.cpp b/orchagent/otn/otnorchdaemon.cpp index 899d5bbd7c4..365712a2036 100644 --- a/orchagent/otn/otnorchdaemon.cpp +++ b/orchagent/otn/otnorchdaemon.cpp @@ -1,4 +1,5 @@ #include "otnorchdaemon.h" +#include "otndeviceorch.h" #include "attenuatororch.h" #include "oaorch.h" #include "ocmorch.h" @@ -18,6 +19,13 @@ bool OtnOrchDaemon::init() SWSS_LOG_ENTER(); SWSS_LOG_NOTICE("OtnOrchDaemon init"); + /* Otn Device */ + const std::vector otn_device_tables = { + APP_OTN_DEVICE_TABLE_NAME + }; + OtnDeviceOrch *otnDeviceOrch = new OtnDeviceOrch(m_applDb, otn_device_tables); + addOrchList(otnDeviceOrch); + /* attenuator */ const std::vector attenuator_tables = { APP_OTN_ATTENUATOR_TABLE_NAME