From f3377d8ae69c74b0673797b1c33bc467ef2e2a7b Mon Sep 17 00:00:00 2001 From: Serhiy Boiko Date: Mon, 8 Jul 2024 10:09:54 +0300 Subject: [PATCH] [swss] Add support for PoE Add poesyncd daemon Parse PoE-related configs *What I did* Add support for PoE based on sonic-sairedis support of multiple switches. *How I verified it* Builds and tests using VS builds. Signed-off-by: Serhiy Boiko --- .gitignore | 1 + Makefile.am | 4 +- cfgmgr/Makefile.am | 9 +- cfgmgr/poemgr.cpp | 50 ++ cfgmgr/poemgr.h | 28 + cfgmgr/poemgrd.cpp | 70 +++ configure.ac | 1 + lib/poecfg.cpp | 248 +++++++++ lib/poecfg.h | 74 +++ orchagent/Makefile.am | 4 +- orchagent/main.cpp | 31 ++ orchagent/orchdaemon.cpp | 6 + orchagent/poeorch.cpp | 655 +++++++++++++++++++++++ orchagent/poeorch.h | 38 ++ orchagent/saihelper.cpp | 32 ++ orchagent/saihelper.h | 2 + poesyncd/Makefile.am | 24 + poesyncd/poeparser.cpp | 163 ++++++ poesyncd/poeparser.h | 47 ++ poesyncd/poesyncd.cpp | 83 +++ tests/mock_tests/Makefile.am | 4 +- tests/mock_tests/mock_orchagent_main.cpp | 1 + tests/mock_tests/mock_orchagent_main.h | 1 + 23 files changed, 1571 insertions(+), 5 deletions(-) create mode 100644 cfgmgr/poemgr.cpp create mode 100644 cfgmgr/poemgr.h create mode 100644 cfgmgr/poemgrd.cpp create mode 100644 lib/poecfg.cpp create mode 100644 lib/poecfg.h create mode 100644 orchagent/poeorch.cpp create mode 100644 orchagent/poeorch.h create mode 100644 poesyncd/Makefile.am create mode 100644 poesyncd/poeparser.cpp create mode 100644 poesyncd/poeparser.h create mode 100644 poesyncd/poesyncd.cpp diff --git a/.gitignore b/.gitignore index 001db00e4b..3835921e83 100644 --- a/.gitignore +++ b/.gitignore @@ -52,6 +52,7 @@ cfgmgr/portmgrd cfgmgr/sflowmgrd cfgmgr/teammgrd cfgmgr/vlanmgrd +cfgmgr/poemgrd cfgmgr/vrfmgrd cfgmgr/vxlanmgrd cfgmgr/natmgrd diff --git a/Makefile.am b/Makefile.am index 757db0d8d6..ea7708f280 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,7 +1,7 @@ if GCOV_ENABLED -SUBDIRS = gcovpreload fpmsyncd neighsyncd portsyncd mclagsyncd natsyncd fdbsyncd orchagent swssconfig cfgmgr tests gearsyncd +SUBDIRS = gcovpreload fpmsyncd neighsyncd portsyncd mclagsyncd natsyncd fdbsyncd orchagent swssconfig cfgmgr tests gearsyncd poesyncd else -SUBDIRS = fpmsyncd neighsyncd portsyncd mclagsyncd natsyncd fdbsyncd orchagent swssconfig cfgmgr tests gearsyncd +SUBDIRS = fpmsyncd neighsyncd portsyncd mclagsyncd natsyncd fdbsyncd orchagent swssconfig cfgmgr tests gearsyncd poesyncd endif diff --git a/cfgmgr/Makefile.am b/cfgmgr/Makefile.am index 45afff7e9b..5a7e05e348 100644 --- a/cfgmgr/Makefile.am +++ b/cfgmgr/Makefile.am @@ -5,7 +5,7 @@ LIBNL_LIBS = -lnl-genl-3 -lnl-route-3 -lnl-3 SAIMETA_LIBS = -lsaimeta -lsaimetadata -lzmq COMMON_LIBS = -lswsscommon -lpthread -bin_PROGRAMS = vlanmgrd teammgrd portmgrd intfmgrd buffermgrd vrfmgrd nbrmgrd vxlanmgrd sflowmgrd natmgrd coppmgrd tunnelmgrd macsecmgrd fabricmgrd +bin_PROGRAMS = vlanmgrd teammgrd portmgrd intfmgrd buffermgrd vrfmgrd nbrmgrd vxlanmgrd sflowmgrd natmgrd coppmgrd tunnelmgrd macsecmgrd fabricmgrd poemgrd cfgmgrdir = $(datadir)/swss @@ -101,6 +101,11 @@ macsecmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CF macsecmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CFLAGS_ASAN) macsecmgrd_LDADD = $(LDFLAGS_ASAN) $(COMMON_LIBS) $(SAIMETA_LIBS) +poemgrd_SOURCES = poemgrd.cpp poemgr.cpp $(COMMON_ORCH_SOURCE) shellcmd.h +poemgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CFLAGS_ASAN) +poemgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CFLAGS_ASAN) +poemgrd_LDADD = $(LDFLAGS_ASAN) $(COMMON_LIBS) $(SAIMETA_LIBS) + if GCOV_ENABLED vlanmgrd_SOURCES += ../gcovpreload/gcovpreload.cpp teammgrd_SOURCES += ../gcovpreload/gcovpreload.cpp @@ -116,6 +121,7 @@ natmgrd_SOURCES += ../gcovpreload/gcovpreload.cpp coppmgrd_SOURCES += ../gcovpreload/gcovpreload.cpp tunnelmgrd_SOURCES += ../gcovpreload/gcovpreload.cpp macsecmgrd_SOURCES += ../gcovpreload/gcovpreload.cpp +poemgrd_SOURCES += ../gcovpreload/gcovpreload.cpp endif if ASAN_ENABLED @@ -133,5 +139,6 @@ coppmgrd_SOURCES += $(top_srcdir)/lib/asan.cpp tunnelmgrd_SOURCES += $(top_srcdir)/lib/asan.cpp macsecmgrd_SOURCES += $(top_srcdir)/lib/asan.cpp fabricmgrd_SOURCES += $(top_srcdir)/lib/asan.cpp +poemgrd_SOURCES += $(top_srcdir)/lib/asan.cpp endif diff --git a/cfgmgr/poemgr.cpp b/cfgmgr/poemgr.cpp new file mode 100644 index 0000000000..31f61ed227 --- /dev/null +++ b/cfgmgr/poemgr.cpp @@ -0,0 +1,50 @@ +#include "poemgr.h" +#include "logger.h" +#include "tokenize.h" +#include "warm_restart.h" +#include "converter.h" + +using namespace swss; + +PoeMgr::PoeMgr(DBConnector *appDb, DBConnector *cfgDb, const std::vector &poeTables) : + Orch(cfgDb, poeTables), + m_appPoeTable(appDb, APP_POE_TABLE_NAME) +{ + SWSS_LOG_ENTER(); +} + +void PoeMgr::doTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + std::string table_name = consumer.getTableName(); + if (table_name != CFG_POE_TABLE_NAME) + { + SWSS_LOG_ERROR("Unknown config table %s ", table_name.c_str()); + throw std::runtime_error("PoeMgr doTask failure."); + } + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + std::string alias = kfvKey(t); + std::string op = kfvOp(t); + + SWSS_LOG_NOTICE("TABLE key: %s : %s", alias.c_str(), op.c_str()); + if (op == SET_COMMAND) + { + SWSS_LOG_NOTICE("Add PoE port: %s", alias.c_str()); + m_appPoeTable.set(alias, kfvFieldsValues(t)); + } + else if (op == DEL_COMMAND) + { + SWSS_LOG_NOTICE("Removing PoE port: %s", alias.c_str()); + m_appPoeTable.del(alias); + } + else + { + SWSS_LOG_ERROR("Unknown operation type %s", op.c_str()); + } + it = consumer.m_toSync.erase(it); + } +} diff --git a/cfgmgr/poemgr.h b/cfgmgr/poemgr.h new file mode 100644 index 0000000000..bc657696cc --- /dev/null +++ b/cfgmgr/poemgr.h @@ -0,0 +1,28 @@ +#ifndef __POEMGR__ +#define __POEMGR__ + +#include "dbconnector.h" +#include "producerstatetable.h" +#include "orch.h" + +#include +#include + +namespace swss { + +class PoeMgr : public Orch +{ +public: + PoeMgr(DBConnector *appDb, DBConnector *cfgDb, const std::vector &poeTables); + using Orch::doTask; + +private: + ProducerStateTable m_appPoeTable; + + void doTask(Consumer &consumer); + +}; + +} + +#endif diff --git a/cfgmgr/poemgrd.cpp b/cfgmgr/poemgrd.cpp new file mode 100644 index 0000000000..868de05e5a --- /dev/null +++ b/cfgmgr/poemgrd.cpp @@ -0,0 +1,70 @@ +#include + +#include "poemgr.h" +#include "dbconnector.h" +#include "select.h" +#include "warm_restart.h" + +using namespace swss; + +/* select() function timeout retry time, in millisecond */ +#define SELECT_TIMEOUT 1000 + + +int main(int argc, char **argv) +{ + Logger::linkToDbNative("poemgrd"); + SWSS_LOG_ENTER(); + + SWSS_LOG_NOTICE("--- Starting poemgrd ---"); + + try + { + std::vector cfg_tables = { + CFG_POE_TABLE_NAME, + }; + + DBConnector cfgDb("CONFIG_DB", 0); + DBConnector appDb("APPL_DB", 0); + + WarmStart::initialize("poemgrd", "swss"); + WarmStart::checkWarmStart("poemgrd", "swss"); + + PoeMgr manager(&appDb, &cfgDb, cfg_tables); + + std::vector cfgOrchList = {&manager}; + + Select s; + for (Orch *o : cfgOrchList) + { + s.addSelectables(o->getSelectables()); + } + + SWSS_LOG_NOTICE("starting main loop"); + 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) + { + manager.doTask(); + continue; + } + + auto *c = (Executor *)sel; + c->execute(); + } + } + catch(const std::exception &e) + { + SWSS_LOG_ERROR("Runtime error: %s", e.what()); + } + return -1; +} diff --git a/configure.ac b/configure.ac index 231f1e1c58..5f112d8172 100644 --- a/configure.ac +++ b/configure.ac @@ -150,6 +150,7 @@ AC_CONFIG_FILES([ fpmsyncd/Makefile neighsyncd/Makefile gearsyncd/Makefile + poesyncd/Makefile fdbsyncd/Makefile natsyncd/Makefile portsyncd/Makefile diff --git a/lib/poecfg.cpp b/lib/poecfg.cpp new file mode 100644 index 0000000000..580bc1bee8 --- /dev/null +++ b/lib/poecfg.cpp @@ -0,0 +1,248 @@ +/* + * Copyright 2024 + * + * 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 "poecfg.h" +#include "tokenize.h" + +namespace swss { + +std::string PoeConfig::parseKey(const std::string &key_str) +{ + std::vector token = tokenize(key_str, ':'); + std::string type, idx; + + if (token.size() == 1) + { + if (token[0].rfind("Ethernet", 0) != 0) + { + return ""; + } + type = "port"; + idx = token.at(0); + } + else if (token.size() == 2) + { + type = token.at(0); + idx = token.at(1); + } + + SWSS_LOG_NOTICE("Parsed key:%s, Return:(%s)(%s)", key_str.c_str(), type.c_str(), idx.c_str()); + + return type; +} + +PoeConfig::PoeConfig(Table *poe_table) +{ + poeTable = poe_table; +} + +PoeConfig::PoeConfig(Table &poe_table) +{ + poeTable = &poe_table; +} + +bool PoeConfig::isConfigDone() +{ + std::vector tuples; + + if (!poeTable->get("PoeConfigDone", tuples)) + { + return false; + } + + for (auto& el : tuples) + { + if (el.first == "success" && el.second == "true") + { + return true; + } + } + return false; +} + +bool PoeConfig::platformHasPoe() +{ + bool ret = false; + + if (access("/usr/share/sonic/hwsku/poe_config.json", F_OK) != -1) + { + ret = true; + } + return ret; +} + +bool PoeConfig::isPoeEnabled() +{ + int count_s = 10; + + SWSS_LOG_ENTER(); + if (!platformHasPoe()) + { + return false; + } + + while (count_s > 0) /* wait for 10s */ + { + if (isConfigDone()) + { + return true; + } + sleep(1); + count_s--; + continue; + } + + return false; +} + +bool PoeConfig::loadDeviceConfig(const std::vector &ovalues) +{ + poe_device_t dev = {}; + + for (auto &val : ovalues) + { + if (val.first == "oid") + { + dev.oid = val.second; + } + else if (val.first == "id") + { + dev.id = (uint32_t)std::stoul(val.second); + } + else if (val.first == "hw_info") + { + dev.hwInfo = val.second; + } + else if (val.first == "power_limit_mode") + { + dev.powerLimitMode = val.second; + } + } + poeDeviceMap[dev.id] = dev; + return true; +} + +bool PoeConfig::loadPseConfig(const std::vector &ovalues) +{ + poe_pse_t pse = {}; + + for (auto &val : ovalues) + { + if (val.first == "oid") + { + pse.oid = val.second; + } + else if (val.first == "device_id") + { + pse.deviceId = (uint32_t)std::stoul(val.second); + } + else if (val.first == "pse_index") + { + pse.pseIndex = (uint32_t)std::stoul(val.second); + } + } + poePseMap[pse.pseIndex] = pse; + return true; +} + +bool PoeConfig::loadPortConfig(const std::vector &ovalues) +{ + poe_port_t port = {}; + + for (auto &val : ovalues) + { + if (val.first == "oid") + { + port.oid = val.second; + } + else if (val.first == "device_id") + { + port.deviceId = (uint32_t)std::stoul(val.second); + } + else if (val.first == "front_panel_index") + { + port.frontPanelIndex = (uint32_t)std::stoul(val.second); + } + else if (val.first == "interface") + { + port.interface = val.second; + } + else if (val.first == "power_limit") + { + port.powerLimit = (uint32_t)std::stoul(val.second); + } + else if (val.first == "power_priority") + { + port.powerPriority = val.second; + } + } + poePortMap[port.frontPanelIndex] = port; + return true; +} + +bool PoeConfig::loadConfig() +{ + std::tuple tokens; + std::vector ovalues; + std::vector keys; + + SWSS_LOG_ENTER(); + + poeTable->getKeys(keys); + + if (keys.empty()) + { + SWSS_LOG_ERROR("No PoE records in ApplDB!"); + return false; + } + + for (auto &k : keys) + { + std::string type = parseKey(k); + poeTable->get(k, ovalues); + + if (type == "dev") + { + if (!loadDeviceConfig(ovalues)) + { + SWSS_LOG_ERROR("Failed to parse PoE device config"); + return false; + } + } + else if (type == "pse") + { + if (!loadPseConfig(ovalues)) + { + SWSS_LOG_ERROR("Failed to parse PoE pse config"); + return false; + } + } + else if (type == "port") + { + if (!loadPortConfig(ovalues)) + { + SWSS_LOG_ERROR("Failed to parse PoE port config"); + return false; + } + } + } + SWSS_LOG_NOTICE("poeDeviceMap size: %lu", poeDeviceMap.size()); + SWSS_LOG_NOTICE("poePseMap size: %lu", poePseMap.size()); + SWSS_LOG_NOTICE("poePortMap size: %lu", poePortMap.size()); + return true; +} + +} diff --git a/lib/poecfg.h b/lib/poecfg.h new file mode 100644 index 0000000000..a9a6bcccb6 --- /dev/null +++ b/lib/poecfg.h @@ -0,0 +1,74 @@ +/* + * Copyright 2024 + * + * 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_COMMON_POE_CONFIG_H +#define SWSS_COMMON_POE_CONFIG_H + +#include +#include + +#include "table.h" + +namespace swss { + +typedef struct { + std::string oid; + uint32_t id; + std::string hwInfo; + std::string powerLimitMode; +} poe_device_t; + +typedef struct { + std::string oid; + uint32_t deviceId; + uint32_t pseIndex; +} poe_pse_t; + +typedef struct { + std::string oid; + uint32_t deviceId; + uint32_t frontPanelIndex; + std::string interface; + uint32_t powerLimit; + std::string powerPriority; +} poe_port_t; + +class PoeConfig +{ + private: + std::map poeDeviceMap; + std::map poePseMap; + std::map poePortMap; + Table *poeTable; + bool loadDeviceConfig(const std::vector &ovalues); + bool loadPseConfig(const std::vector &ovalues); + bool loadPortConfig(const std::vector &ovalues); + std::string parseKey(const std::string &key_str); + public: + PoeConfig(Table *table); + PoeConfig(Table &table); + bool platformHasPoe(); + bool isPoeEnabled(); + bool isConfigDone(); + bool loadConfig(); + std::map getDeviceMap() { return poeDeviceMap; }; + std::map getPseMap() { return poePseMap; }; + std::map getPortMap() { return poePortMap; }; +}; + +} + +#endif /* SWSS_COMMON_POE_CONFIG_H */ diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index 44714f62a1..307ccb5246 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -48,6 +48,7 @@ endif orchagent_SOURCES = \ main.cpp \ $(top_srcdir)/lib/gearboxutils.cpp \ + $(top_srcdir)/lib/poecfg.cpp \ $(top_srcdir)/lib/subintf.cpp \ $(top_srcdir)/lib/recorder.cpp \ orchdaemon.cpp \ @@ -116,7 +117,8 @@ orchagent_SOURCES = \ dash/dashaclgroupmgr.cpp \ dash/dashtagmgr.cpp \ dash/pbutils.cpp \ - twamporch.cpp + twamporch.cpp \ + poeorch.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/main.cpp b/orchagent/main.cpp index 556bb70892..e872e9f7f5 100644 --- a/orchagent/main.cpp +++ b/orchagent/main.cpp @@ -29,6 +29,7 @@ extern "C" { #include #include "warm_restart.h" #include "gearboxutils.h" +#include "poecfg.h" using namespace std; using namespace swss; @@ -44,6 +45,7 @@ extern sai_router_interface_api_t *sai_router_intfs_api; sai_object_id_t gVirtualRouterId; sai_object_id_t gUnderlayIfId; sai_object_id_t gSwitchId = SAI_NULL_OBJECT_ID; +sai_object_id_t gPoeSwitchId = SAI_NULL_OBJECT_ID; MacAddress gMacAddress; MacAddress gVxlanMacAddress; @@ -159,6 +161,34 @@ void init_gearbox_phys(DBConnector *applDb) delete tmpGearboxTable; } +void init_poe(DBConnector *applDb) +{ + Table tmpPoeTable(applDb, "_POE_TABLE"); + PoeConfig poe(tmpPoeTable); + + if (!poe.platformHasPoe()) + { + // platform doesn't support PoE, no need to do anything + return; + } + + SWSS_LOG_NOTICE("POE: Initialize"); + if (!poe.isPoeEnabled()) + { + SWSS_LOG_WARN("POE: not enabled"); + return; + } + + if (initSaiPoeApi(gPoeSwitchId) == SAI_STATUS_SUCCESS) + { + SWSS_LOG_NOTICE("POE: Created Switch %s", sai_serialize_object_id(gPoeSwitchId).c_str()); + } + else + { + SWSS_LOG_ERROR("POE: Failed to initialize switch"); + } +} + void getCfgSwitchType(DBConnector *cfgDb, string &switch_type) { Table cfgDeviceMetaDataTable(cfgDb, CFG_DEVICE_METADATA_TABLE_NAME); @@ -774,6 +804,7 @@ int main(int argc, char **argv) /* Initialize orchestration components */ init_gearbox_phys(&appl_db); + init_poe(&appl_db); } shared_ptr orchDaemon; diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index 0d2ab1c200..62e98ac1a6 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -7,6 +7,7 @@ #include #include "warm_restart.h" #include +#include "poeorch.h" #define SAI_SWITCH_ATTR_CUSTOM_RANGE_BASE SAI_SWITCH_ATTR_CUSTOM_RANGE_START #include "sairedis.h" @@ -53,6 +54,7 @@ SwitchOrch *gSwitchOrch; Directory gDirectory; NatOrch *gNatOrch; PolicerOrch *gPolicerOrch; +PoeOrch *gPoeOrch; MlagOrch *gMlagOrch; IsoGrpOrch *gIsoGrpOrch; MACsecOrch *gMacsecOrch; @@ -768,6 +770,10 @@ bool OrchDaemon::init() TwampOrch *twamp_orch = new TwampOrch(confDbTwampTable, stateDbTwampTable, gSwitchOrch, gPortsOrch, vrf_orch); m_orchList.push_back(twamp_orch); + vector poe_tables = {APP_POE_TABLE_NAME}; + gPoeOrch = new PoeOrch(m_applDb, m_configDb, m_stateDb, poe_tables); + m_orchList.push_back(gPoeOrch); + if (WarmStart::isWarmStart()) { bool suc = warmRestoreAndSyncUp(); diff --git a/orchagent/poeorch.cpp b/orchagent/poeorch.cpp new file mode 100644 index 0000000000..1307135e88 --- /dev/null +++ b/orchagent/poeorch.cpp @@ -0,0 +1,655 @@ +#include "poeorch.h" +#include "table.h" +#include "converter.h" +#include "schema.h" +#include "timer.h" + +#include + +using namespace swss; + +extern sai_object_id_t gPoeSwitchId; +extern sai_poe_api_t* sai_poe_api; + +#define POE_PORT_UPD_INTERVAL 2 + +static std::map poe_device_limit_mode = { + {"port", SAI_POE_DEVICE_LIMIT_MODE_PORT}, + {"class", SAI_POE_DEVICE_LIMIT_MODE_CLASS}, +}; + +static std::map poe_device_limit_mode_to_str = { + {SAI_POE_DEVICE_LIMIT_MODE_PORT, "port"}, + {SAI_POE_DEVICE_LIMIT_MODE_CLASS, "class"}, +}; + +static std::map poe_device_attr_to_field = { + {SAI_POE_DEVICE_ATTR_HARDWARE_INFO, "hw_info"}, + {SAI_POE_DEVICE_ATTR_POE_PSE_LIST, "total_pse"}, + {SAI_POE_DEVICE_ATTR_POE_PORT_LIST, "total_ports"}, + {SAI_POE_DEVICE_ATTR_TOTAL_POWER, "total_pwr"}, + {SAI_POE_DEVICE_ATTR_POWER_CONSUMPTION, "pwr_consump"}, + {SAI_POE_DEVICE_ATTR_VERSION, "version"}, + {SAI_POE_DEVICE_ATTR_POWER_LIMIT_MODE, "pwr_limit_mode"}, +}; + +static std::map poe_pse_status_to_str = { + {SAI_POE_PSE_STATUS_TYPE_ACTIVE, "active"}, + {SAI_POE_PSE_STATUS_TYPE_FAIL, "fail"}, + {SAI_POE_PSE_STATUS_TYPE_NOT_PRESENT, "not present"}, +}; + +static std::map poe_pse_attr_to_field = { + {SAI_POE_PSE_ATTR_SOFTWARE_VERSION, "sw_ver"}, + {SAI_POE_PSE_ATTR_HARDWARE_VERSION, "hw_ver"}, + {SAI_POE_PSE_ATTR_TEMPERATURE, "temperature"}, + {SAI_POE_PSE_ATTR_STATUS, "status"}, +}; + +static std::map poe_port_power_priority = { + {"low", SAI_POE_PORT_POWER_PRIORITY_TYPE_LOW}, + {"high", SAI_POE_PORT_POWER_PRIORITY_TYPE_HIGH}, + {"crit", SAI_POE_PORT_POWER_PRIORITY_TYPE_CRITICAL}, +}; + +static std::map power_priority_to_str = { + {SAI_POE_PORT_POWER_PRIORITY_TYPE_LOW, "low"}, + {SAI_POE_PORT_POWER_PRIORITY_TYPE_HIGH, "high"}, + {SAI_POE_PORT_POWER_PRIORITY_TYPE_CRITICAL, "crit"}, +}; + +static std::map port_admin_state = { + {"disable", false}, + {"enable", true}, +}; + +static std::map port_admin_state_to_str = { + {false, "disable"}, + {true, "enable"}, +}; + +static std::map poe_standard_to_str = { + {SAI_POE_PORT_STANDARD_TYPE_AF, "802.3af"}, + {SAI_POE_PORT_STANDARD_TYPE_AT, "802.3at"}, + {SAI_POE_PORT_STANDARD_TYPE_60W, "60w"}, + {SAI_POE_PORT_STANDARD_TYPE_BT_TYPE3, "802.3bt Type 3"}, + {SAI_POE_PORT_STANDARD_TYPE_BT_TYPE4, "802.3bt Type 4"}, +}; + +static std::map poe_port_status_to_str = { + {SAI_POE_PORT_STATUS_TYPE_OFF, "off"}, + {SAI_POE_PORT_STATUS_TYPE_SEARCHING, "searching"}, + {SAI_POE_PORT_STATUS_TYPE_DELIVERING_POWER, "delivering"}, + {SAI_POE_PORT_STATUS_TYPE_FAULT, "fail"}, +}; + +static std::map poe_port_attr_to_field = { + {SAI_POE_PORT_ATTR_FRONT_PANEL_ID, "fp_port"}, + {SAI_POE_PORT_ATTR_STANDARD, "protocol"}, + {SAI_POE_PORT_ATTR_ADMIN_ENABLED_STATE, "enabled"}, + {SAI_POE_PORT_ATTR_POWER_LIMIT, "pwr_limit"}, + {SAI_POE_PORT_ATTR_POWER_PRIORITY, "priority"}, + {SAI_POE_PORT_ATTR_CONSUMPTION, "pwr_consump"}, + {SAI_POE_PORT_ATTR_STATUS, "status"}, +}; + +PoeOrch::PoeOrch(DBConnector *applDb, DBConnector *cfgDb, DBConnector *stateDb, const std::vector &poeTables) : + Orch(applDb, poeTables), + m_cfgPoeTable(cfgDb, CFG_POE_TABLE_NAME), + m_appPoeTable(applDb, "_POE_TABLE"), + m_deviceStateTable(stateDb, STATE_POE_DEVICE_TABLE_NAME), + m_pseStateTable(stateDb, STATE_POE_PSE_TABLE_NAME), + m_portStateTable(stateDb, STATE_POE_PORT_TABLE_NAME) +{ + SWSS_LOG_ENTER(); + + if (!initPoe()) + { + SWSS_LOG_NOTICE("Failed to init PoE"); + return; + } + + auto timer = new SelectableTimer(timespec {.tv_sec = POE_PORT_UPD_INTERVAL, .tv_nsec = 0}); + Orch::addExecutor(new ExecutableTimer(timer, this, "POE_PORT_POLL")); + timer->start(); + initDone = true; +} + +bool PoeOrch::initPoe() +{ + PoeConfig poe(m_appPoeTable); + if (!poe.isPoeEnabled()) + { + SWSS_LOG_WARN("poe not enabled"); + return false; + } + + poe.loadConfig(); + m_poeDeviceMap = poe.getDeviceMap(); + m_poePortMap = poe.getPortMap(); + m_poePseMap = poe.getPseMap(); + + for (auto &device : m_poeDeviceMap) + { + if (!initPoeDevice(gPoeSwitchId, device.second)) + { + SWSS_LOG_ERROR("Failed to create poe device"); + return false; + } + } + for (auto &pse : m_poePseMap) + { + sai_object_id_t devOid; + sai_deserialize_object_id(m_poeDeviceMap[pse.second.deviceId].oid, devOid); + if (!initPoePse(gPoeSwitchId, devOid, pse.second)) + { + SWSS_LOG_ERROR("Failed to create poe pse"); + return false; + } + } + for (auto &port : m_poePortMap) + { + sai_object_id_t devOid; + sai_deserialize_object_id(m_poeDeviceMap[port.second.deviceId].oid, devOid); + if (!initPoePort(gPoeSwitchId, devOid, port.second)) + { + SWSS_LOG_ERROR("Failed to create poe port"); + return false; + } + } + // Set state values that are defined at init and won't change later + initStateTables(); + return true; +} + +bool PoeOrch::initPoeDevice(const sai_object_id_t &switchOid, poe_device_t &dev) +{ + std::vector attrs; + sai_attribute_t attr = {}; + sai_object_id_t devOid; + sai_status_t status; + + SWSS_LOG_ENTER(); + + attr.id = SAI_POE_DEVICE_ATTR_HARDWARE_INFO; + strncpy(attr.value.chardata, dev.hwInfo.c_str(), sizeof(attr.value.chardata) - 1); + attrs.push_back(attr); + + if (!dev.powerLimitMode.empty()) + { + attr.id = SAI_POE_DEVICE_ATTR_POWER_LIMIT_MODE; + attr.value.u32 = poe_device_limit_mode.at(dev.powerLimitMode); + attrs.push_back(attr); + } + + status = sai_poe_api->create_poe_device(&devOid, switchOid, (uint32_t)attrs.size(), attrs.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("poe: Failed to create device %u", dev.id); + return false; + } + + dev.oid = sai_serialize_object_id(devOid); + return true; +} + +bool PoeOrch::initPoePse(const sai_object_id_t &switchOid, const sai_object_id_t &devOid, poe_pse_t &pse) +{ + std::vector attrs; + sai_object_id_t pseOid; + sai_attribute_t attr; + sai_status_t status; + + SWSS_LOG_ENTER(); + + attr.id = SAI_POE_PSE_ATTR_ID; + attr.value.u32 = pse.pseIndex; + attrs.push_back(attr); + + attr.id = SAI_POE_PSE_ATTR_DEVICE_ID; + attr.value.oid = devOid; + attrs.push_back(attr); + + status = sai_poe_api->create_poe_pse(&pseOid, switchOid, (uint32_t)attrs.size(), attrs.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("poe: Failed to create pse %u", pse.pseIndex); + return false; + } + + pse.oid = sai_serialize_object_id(pseOid); + return true; +} + +bool PoeOrch::initPoePort(const sai_object_id_t &switchOid, const sai_object_id_t &devOid, poe_port_t &port) +{ + std::vector attrs; + sai_object_id_t portOid; + sai_attribute_t attr; + sai_status_t status; + + SWSS_LOG_ENTER(); + + attr.id = SAI_POE_PORT_ATTR_FRONT_PANEL_ID; + attr.value.u32 = port.frontPanelIndex; + attrs.push_back(attr); + + attr.id = SAI_POE_PORT_ATTR_DEVICE_ID; + attr.value.oid = devOid; + attrs.push_back(attr); + + if (port.powerLimit) + { + attr.id = SAI_POE_PORT_ATTR_POWER_LIMIT; + attr.value.u32 = port.powerLimit; + attrs.push_back(attr); + } + + if (!port.powerPriority.empty()) + { + attr.id = SAI_POE_PORT_ATTR_POWER_PRIORITY; + attr.value.u32 = poe_port_power_priority.at(port.powerPriority); + attrs.push_back(attr); + } + + status = sai_poe_api->create_poe_port(&portOid, switchOid, (uint32_t)attrs.size(), attrs.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("poe: Failed to create port %u", port.frontPanelIndex); + return false; + } + + port.oid = sai_serialize_object_id(portOid); + return true; +} + +void PoeOrch::initStateTables() +{ + sai_attribute_t attr; + sai_object_id_t oid; + sai_status_t status; + + for (auto &dev : m_poeDeviceMap) + { + std::vector port_list(48); /* typical port count */ + + attr.id = SAI_POE_DEVICE_ATTR_POE_PORT_LIST; + attr.value.objlist.count = (uint32_t)port_list.size(); + attr.value.objlist.list = port_list.data(); + sai_deserialize_object_id(dev.second.oid, oid); + status = sai_poe_api->get_poe_device_attribute(oid, 1, &attr); + if (SAI_STATUS_BUFFER_OVERFLOW == status) + { + port_list.resize(attr.value.objlist.count); + attr.value.objlist.count = (uint32_t)port_list.size(); + attr.value.objlist.list = port_list.data(); + status = sai_poe_api->get_poe_device_attribute(oid, 1, &attr); + } + if (SAI_STATUS_SUCCESS != status) + { + SWSS_LOG_ERROR("Failed to get PoE device port count"); + } + else + { + m_deviceStateTable.hset(std::to_string(dev.second.id), poe_device_attr_to_field[attr.id], + std::to_string(attr.value.objlist.count)); + } + + attr.id = SAI_POE_DEVICE_ATTR_VERSION; + attr.value = {}; + status = sai_poe_api->get_poe_device_attribute(oid, 1, &attr); + if (SAI_STATUS_SUCCESS != status) + { + SWSS_LOG_ERROR("Failed to get PoE device version"); + } + else + { + m_deviceStateTable.hset(std::to_string(dev.second.id), poe_device_attr_to_field[attr.id], + std::string(attr.value.chardata)); + } + + attr.id = SAI_POE_DEVICE_ATTR_HARDWARE_INFO; + attr.value = {}; + status = sai_poe_api->get_poe_device_attribute(oid, 1, &attr); + if (SAI_STATUS_SUCCESS != status) + { + SWSS_LOG_ERROR("Failed to get PoE device hardware info"); + } + else + { + m_deviceStateTable.hset(std::to_string(dev.second.id), poe_device_attr_to_field[attr.id], + std::string(attr.value.chardata)); + } + } + for (auto &pse : m_poePseMap) + { + attr.id = SAI_POE_PSE_ATTR_SOFTWARE_VERSION; + attr.value = {}; + sai_deserialize_object_id(pse.second.oid, oid); + status = sai_poe_api->get_poe_pse_attribute(oid, 1, &attr); + if (SAI_STATUS_SUCCESS != status) + { + SWSS_LOG_ERROR("Failed to get PoE pse version"); + } + else + { + m_pseStateTable.hset(std::to_string(pse.second.pseIndex), poe_pse_attr_to_field[attr.id], + std::string(attr.value.chardata)); + } + + attr.id = SAI_POE_PSE_ATTR_HARDWARE_VERSION; + attr.value = {}; + status = sai_poe_api->get_poe_pse_attribute(oid, 1, &attr); + if (SAI_STATUS_SUCCESS != status) + { + SWSS_LOG_ERROR("Failed to get PoE pse version"); + } + else + { + m_pseStateTable.hset(std::to_string(pse.second.pseIndex), poe_pse_attr_to_field[attr.id], + std::string(attr.value.chardata)); + } + } + for (auto &port : m_poePortMap) + { + attr.id = SAI_POE_PORT_ATTR_STANDARD; + attr.value = {}; + sai_deserialize_object_id(port.second.oid, oid); + status = sai_poe_api->get_poe_port_attribute(oid, 1, &attr); + if (SAI_STATUS_SUCCESS != status) + { + SWSS_LOG_ERROR("Failed to get PoE port standard"); + } + else + { + std::string val = poe_standard_to_str[static_cast(attr.value.u32)]; + m_portStateTable.hset(port.second.interface, poe_port_attr_to_field[attr.id], val); + } + m_portStateTable.hset(port.second.interface, poe_port_attr_to_field[SAI_POE_PORT_ATTR_FRONT_PANEL_ID], + std::to_string(port.second.frontPanelIndex)); + + /* write current configuration to cfgdb */ + attr.id = SAI_POE_PORT_ATTR_ADMIN_ENABLED_STATE; + attr.value = {}; + status = sai_poe_api->get_poe_port_attribute(oid, 1, &attr); + if (SAI_STATUS_SUCCESS != status) + { + SWSS_LOG_ERROR("Failed to get PoE port state"); + } + else + { + std::string val = port_admin_state_to_str[attr.value.booldata]; + m_cfgPoeTable.hset(port.second.interface, poe_port_attr_to_field[attr.id], val); + } + + attr.id = SAI_POE_PORT_ATTR_POWER_LIMIT; + attr.value = {}; + status = sai_poe_api->get_poe_port_attribute(oid, 1, &attr); + if (SAI_STATUS_SUCCESS != status) + { + SWSS_LOG_ERROR("Failed to get PoE port power limit"); + } + else + { + std::string val = std::to_string(attr.value.u32); + m_cfgPoeTable.hset(port.second.interface, poe_port_attr_to_field[attr.id], val); + } + + attr.id = SAI_POE_PORT_ATTR_POWER_PRIORITY; + attr.value = {}; + status = sai_poe_api->get_poe_port_attribute(oid, 1, &attr); + if (SAI_STATUS_SUCCESS != status) + { + SWSS_LOG_ERROR("Failed to get PoE port power priority"); + } + else + { + std::string val = power_priority_to_str[static_cast(attr.value.u32)]; + m_cfgPoeTable.hset(port.second.interface, poe_port_attr_to_field[attr.id], val); + } + } +} + +void PoeOrch::doTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + if (!initDone) + { + SWSS_LOG_NOTICE("Consumer waiting for init to finish"); + return; + } + + std::string table_name = consumer.getTableName(); + + if (table_name != APP_POE_TABLE_NAME) + { + SWSS_LOG_ERROR("Invalid table %s", table_name.c_str()); + return; + } + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + std::string key = kfvKey(t); + std::string op = kfvOp(t); + + if (key.rfind("Ethernet", 0) != 0) + { + it = consumer.m_toSync.erase(it); + SWSS_LOG_DEBUG("not ethernet OP: %s, key: %s", op.c_str(), key.c_str()); + continue; + } + SWSS_LOG_DEBUG("OP: %s, key: %s", op.c_str(), key.c_str()); + bool found = false; + sai_object_id_t portOid; + for (auto &port : m_poePortMap) + { + if (port.second.interface != key) + continue; + sai_deserialize_object_id(port.second.oid, portOid); + found = true; + } + if (!found) + { + SWSS_LOG_ERROR("unknown interface %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + if (op == SET_COMMAND) + { + sai_attribute_t attr; + // Scan all attributes + for (auto itp : kfvFieldsValues(t)) + { + std::string attr_name = fvField(itp); + std::string attr_value = fvValue(itp); + + SWSS_LOG_DEBUG("TABLE ATTRIBUTE: %s : %s", attr_name.c_str(), attr_value.c_str()); + if (attr_name == poe_port_attr_to_field[SAI_POE_PORT_ATTR_ADMIN_ENABLED_STATE]) + { + attr.id = SAI_POE_PORT_ATTR_ADMIN_ENABLED_STATE; + attr.value.booldata = port_admin_state[attr_value]; + } + else if (attr_name == poe_port_attr_to_field[SAI_POE_PORT_ATTR_POWER_LIMIT]) + { + attr.id = SAI_POE_PORT_ATTR_POWER_LIMIT; + attr.value.u32 = std::stoi(attr_value); + } + else if (attr_name == poe_port_attr_to_field[SAI_POE_PORT_ATTR_POWER_PRIORITY]) + { + attr.id = SAI_POE_PORT_ATTR_POWER_PRIORITY; + attr.value.u32 = poe_port_power_priority[attr_value]; + } + else + { + SWSS_LOG_ERROR("unknown field %s", attr_name.c_str()); + continue; + } + sai_status_t status = sai_poe_api->set_poe_port_attribute(portOid, &attr); + if (SAI_STATUS_SUCCESS != status) + { + SWSS_LOG_ERROR("Failed to set PoE port attribute"); + } + } + } + else + { + SWSS_LOG_ERROR("Unknown operation type %s", op.c_str()); + } + it = consumer.m_toSync.erase(it); + } +} + +static std::string milli_to_str(uint32_t val) +{ + std::string s(16, '\0'); + snprintf(&s[0], s.size(), "%.3f", ((double)val) / 1000); + return s; +} + +void PoeOrch::doTask(SelectableTimer &timer) +{ + SWSS_LOG_ENTER(); + if (!initDone) + { + SWSS_LOG_NOTICE("SelectableTimer waiting for init to finish"); + return; + } + + for (auto &device : m_poeDeviceMap) + { + // fetch all attrs for device + std::vector attrs = { + {.id=SAI_POE_DEVICE_ATTR_TOTAL_POWER, .value={}}, + {.id=SAI_POE_DEVICE_ATTR_POWER_CONSUMPTION, .value={}}, + {.id=SAI_POE_DEVICE_ATTR_POWER_LIMIT_MODE, .value={}}, + }; + uint32_t idx = device.second.id; + sai_object_id_t deviceOid; + sai_deserialize_object_id(device.second.oid, deviceOid); + sai_status_t status = sai_poe_api->get_poe_device_attribute(deviceOid, static_cast(attrs.size()), attrs.data()); + if (SAI_STATUS_SUCCESS != status) + { + SWSS_LOG_ERROR("Failed to get PoE device status [%u]", idx); + continue; + } + // convert results to strings + for (auto &attr : attrs) + { + std::string val; + switch (attr.id) + { + case SAI_POE_DEVICE_ATTR_TOTAL_POWER: + val = std::to_string(attr.value.u16); + break; + case SAI_POE_DEVICE_ATTR_POWER_CONSUMPTION: + val = milli_to_str(attr.value.u16); + break; + case SAI_POE_DEVICE_ATTR_POWER_LIMIT_MODE: + val = poe_device_limit_mode_to_str[static_cast(attr.value.u32)]; + break; + default: + SWSS_LOG_ERROR("Unknown attr id %u", attr.id); + continue; // go to next attr + } + // update state table + m_deviceStateTable.hset(std::to_string(idx), poe_device_attr_to_field[attr.id], val); + } + } + + for (auto &pse : m_poePseMap) + { + // fetch all attrs for pse + std::vector attrs = { + {.id=SAI_POE_PSE_ATTR_TEMPERATURE, .value={}}, + {.id=SAI_POE_PSE_ATTR_STATUS, .value={}}, + }; + uint32_t idx = pse.second.pseIndex; + sai_object_id_t pseOid; + sai_deserialize_object_id(pse.second.oid, pseOid); + sai_status_t status = sai_poe_api->get_poe_pse_attribute(pseOid, static_cast(attrs.size()), attrs.data()); + if (SAI_STATUS_SUCCESS != status) + { + SWSS_LOG_ERROR("Failed to get PoE PSE status [%u]", idx); + continue; + } + // convert results to strings + for (auto &attr : attrs) + { + std::string val; + switch (attr.id) + { + case SAI_POE_PSE_ATTR_TEMPERATURE: + val = std::to_string(attr.value.u16); + break; + case SAI_POE_PSE_ATTR_STATUS: + val = poe_pse_status_to_str[static_cast(attr.value.u32)]; + break; + default: + SWSS_LOG_ERROR("Unknown attr id %u", attr.id); + continue; // go to next attr + } + // update state table + m_pseStateTable.hset(std::to_string(idx), poe_pse_attr_to_field[attr.id], val); + } + } + + for (auto &port : m_poePortMap) + { + // fetch all attrs for port + std::vector attrs = { + {.id=SAI_POE_PORT_ATTR_ADMIN_ENABLED_STATE, .value={}}, + {.id=SAI_POE_PORT_ATTR_POWER_LIMIT, .value={}}, + {.id=SAI_POE_PORT_ATTR_POWER_PRIORITY, .value={}}, + {.id=SAI_POE_PORT_ATTR_CONSUMPTION, .value={}}, + {.id=SAI_POE_PORT_ATTR_STATUS, .value={}}, + }; + sai_object_id_t portOid; + sai_deserialize_object_id(port.second.oid, portOid); + sai_status_t status = sai_poe_api->get_poe_port_attribute(portOid, static_cast(attrs.size()), attrs.data()); + if (SAI_STATUS_SUCCESS != status) + { + SWSS_LOG_ERROR("Failed to get PoE port status [%s]", port.second.interface.c_str()); + continue; + } + // convert results to strings + for (auto &attr : attrs) + { + std::string val; + switch (attr.id) + { + case SAI_POE_PORT_ATTR_POWER_PRIORITY: + val = power_priority_to_str[static_cast(attr.value.u32)]; + break; + case SAI_POE_PORT_ATTR_STATUS: + val = poe_port_status_to_str[static_cast(attr.value.u32)]; + break; + case SAI_POE_PORT_ATTR_ADMIN_ENABLED_STATE: + val = port_admin_state_to_str[attr.value.booldata]; + break; + case SAI_POE_PORT_ATTR_POWER_LIMIT: + val = std::to_string(attr.value.u32); + break; + case SAI_POE_PORT_ATTR_CONSUMPTION: + val = std::to_string(attr.value.portpowerconsumption.assigned_class_a); + m_portStateTable.hset(port.second.interface, "class_a", val); + val = std::to_string(attr.value.portpowerconsumption.assigned_class_b); + m_portStateTable.hset(port.second.interface, "class_b", val); + val = milli_to_str(attr.value.portpowerconsumption.consumption); + m_portStateTable.hset(port.second.interface, "pwr_consump", val); + val = milli_to_str(attr.value.portpowerconsumption.voltage); + m_portStateTable.hset(port.second.interface, "voltage", val); + val = milli_to_str(attr.value.portpowerconsumption.current); + m_portStateTable.hset(port.second.interface, "current", val); + continue; // go to next attr + default: + SWSS_LOG_ERROR("Unknown attr id %u", attr.id); + continue; // go to next attr + } + // update state table + m_portStateTable.hset(port.second.interface, poe_port_attr_to_field[attr.id], val); + } + } +} diff --git a/orchagent/poeorch.h b/orchagent/poeorch.h new file mode 100644 index 0000000000..ac1ebec822 --- /dev/null +++ b/orchagent/poeorch.h @@ -0,0 +1,38 @@ +#pragma once + +#include "orch.h" +#include "observer.h" +#include "poecfg.h" +#include "selectabletimer.h" + +#include +#include + +using namespace swss; + +class PoeOrch : public Orch +{ +public: + PoeOrch(DBConnector *applDb, DBConnector *cfgDb, DBConnector *stateDb, const std::vector &poeTables); + +private: + bool initPoe(); + bool initPoeDevice(const sai_object_id_t &switchOid, poe_device_t &dev); + bool initPoePse(const sai_object_id_t &switchOid, const sai_object_id_t &devOid, poe_pse_t &pse); + bool initPoePort(const sai_object_id_t &switchOid, const sai_object_id_t &devOid, poe_port_t &port); + void initStateTables(); + bool initDone = false; + + Table m_cfgPoeTable; + Table m_appPoeTable; + Table m_deviceStateTable; + Table m_pseStateTable; + Table m_portStateTable; + + std::map m_poeDeviceMap; + std::map m_poePseMap; + std::map m_poePortMap; + + void doTask(Consumer &consumer); + void doTask(SelectableTimer &timer); +}; diff --git a/orchagent/saihelper.cpp b/orchagent/saihelper.cpp index 1265364d97..240340eae7 100644 --- a/orchagent/saihelper.cpp +++ b/orchagent/saihelper.cpp @@ -85,6 +85,7 @@ sai_dash_vip_api_t* sai_dash_vip_api; sai_dash_direction_lookup_api_t* sai_dash_direction_lookup_api; sai_twamp_api_t* sai_twamp_api; sai_tam_api_t* sai_tam_api; +sai_poe_api_t* sai_poe_api; extern sai_object_id_t gSwitchId; extern bool gTraditionalFlexCounter; @@ -232,6 +233,7 @@ void initSaiApi() sai_api_query((sai_api_t)SAI_API_DASH_DIRECTION_LOOKUP, (void**)&sai_dash_direction_lookup_api); sai_api_query(SAI_API_TWAMP, (void **)&sai_twamp_api); sai_api_query(SAI_API_TAM, (void **)&sai_tam_api); + sai_api_query(SAI_API_POE, (void **)&sai_poe_api); sai_log_set(SAI_API_SWITCH, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_BRIDGE, SAI_LOG_LEVEL_NOTICE); @@ -273,6 +275,7 @@ void initSaiApi() sai_log_set(SAI_API_GENERIC_PROGRAMMABLE, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_TWAMP, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_TAM, SAI_LOG_LEVEL_NOTICE); + sai_log_set(SAI_API_POE, SAI_LOG_LEVEL_NOTICE); } void initFlexCounterTables() @@ -514,6 +517,35 @@ sai_status_t initSaiPhyApi(swss::gearbox_phy_t *phy) return status; } +sai_status_t initSaiPoeApi(sai_object_id_t &switchOid) +{ + sai_attribute_t attr; + vector attrs; + sai_status_t status = SAI_STATUS_SUCCESS; + + SWSS_LOG_ENTER(); + + attr.id = SAI_SWITCH_ATTR_INIT_SWITCH; + attr.value.booldata = true; + attrs.push_back(attr); + + attr.id = SAI_SWITCH_ATTR_TYPE; + attr.value.u32 = SAI_SWITCH_TYPE_POE; + attrs.push_back(attr); + + /* Must be last Attribute */ + attr.id = SAI_REDIS_SWITCH_ATTR_CONTEXT; + attr.value.u64 = 1; + attrs.push_back(attr); + + status = sai_switch_api->create_switch(&switchOid, (uint32_t)attrs.size(), attrs.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("poe: Failed to create switch"); + } + return status; +} + task_process_status handleSaiCreateStatus(sai_api_t api, sai_status_t status, void *context) { /* diff --git a/orchagent/saihelper.h b/orchagent/saihelper.h index 693fffd742..28e439317c 100644 --- a/orchagent/saihelper.h +++ b/orchagent/saihelper.h @@ -1,6 +1,7 @@ #pragma once #include "gearboxutils.h" +#include "poecfg.h" #include #include "orch.h" @@ -13,6 +14,7 @@ void initFlexCounterTables(); void initSaiApi(); void initSaiRedis(); sai_status_t initSaiPhyApi(swss::gearbox_phy_t *phy); +sai_status_t initSaiPoeApi(sai_object_id_t &switchOid); /* Handling SAI status*/ task_process_status handleSaiCreateStatus(sai_api_t api, sai_status_t status, void *context = nullptr); diff --git a/poesyncd/Makefile.am b/poesyncd/Makefile.am new file mode 100644 index 0000000000..ad9ac1f6ac --- /dev/null +++ b/poesyncd/Makefile.am @@ -0,0 +1,24 @@ +INCLUDES = -I $(top_srcdir)/lib -I $(top_srcdir) -I $(top_srcdir)/warmrestart -I $(top_srcdir)/cfgmgr + +bin_PROGRAMS = poesyncd + +if DEBUG +DBGFLAGS = -ggdb -DDEBUG +else +DBGFLAGS = -g +endif + +poesyncd_SOURCES = $(top_srcdir)/lib/poecfg.cpp poesyncd.cpp poeparser.cpp + +poesyncd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(COV_CFLAGS) $(CFLAGS_ASAN) + +poesyncd_LDADD = $(LDFLAGS_ASAN) -lnl-3 -lnl-route-3 -lswsscommon $(COV_LDFLAGS) + +if GCOV_ENABLED +poesyncd_LDADD += -lgcovpreload +endif + +if ASAN_ENABLED +poesyncd_SOURCES += $(top_srcdir)/lib/asan.cpp +endif + diff --git a/poesyncd/poeparser.cpp b/poesyncd/poeparser.cpp new file mode 100644 index 0000000000..8fecfd87a4 --- /dev/null +++ b/poesyncd/poeparser.cpp @@ -0,0 +1,163 @@ +/* + * Copyright 2024 + * + * 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 "poeparser.h" +#include "schema.h" +#include + + +PoeParser::PoeParser(const std::string &configPath) : + m_cfgPath(configPath), + m_applDb(swss::DBConnector(APPL_DB, swss::DBConnector::DEFAULT_UNIXSOCKET, 0)), + m_cfgDb(swss::DBConnector(CONFIG_DB, swss::DBConnector::DEFAULT_UNIXSOCKET, 0)), + m_poeTable(swss::ProducerStateTable(&m_applDb, APP_POE_TABLE_NAME)) +{ +} + +void PoeParser::notifyConfigDone(bool success) +{ + swss::FieldValueTuple notice("success", success ? "true" : "false"); + std::vector attrs = { notice }; + writeToDb(m_poeTable, "PoeConfigDone", attrs); +} + +bool PoeParser::writeToDb(swss::ProducerStateTable &table, + const std::string &key, + const std::vector &vals) +{ + table.set(key.c_str(), vals); + return true; +} + +bool PoeParser::loadConfig() +{ + std::ifstream file(m_cfgPath); + + if (!file.is_open()) + { + return false; + } + std::string contents; + + file.seekg(0, std::ios::end); + contents.resize(file.tellg()); + file.seekg(0, std::ios::beg); + file.read(&contents[0], contents.size()); + file.close(); + + m_cfg = json::parse(contents.c_str()); + return true; +} + +bool PoeParser::storeConfigToDb() +{ + std::string sep = m_poeTable.getTableNameSeparator(); + std::vector attrs; + json devs, dev, ports, port, val; + + if (!m_cfg.is_array()) + { + SWSS_LOG_ERROR("Expected PoE config root to be an array"); + return false; + } + + for (uint32_t devId = 0; devId < m_cfg.size(); devId++) + { + swss::FieldValueTuple attr; + attrs.clear(); + + dev = m_cfg[devId]; + + attr = std::make_pair("id", std::to_string(devId)); + attrs.push_back(attr); + + if (dev.find("hw_info") == dev.end() || dev["hw_info"].empty()) + { + SWSS_LOG_ERROR("Missing mandatory field 'hw_info'"); + return false; + } + attr = std::make_pair("hw_info", dev["hw_info"]); + attrs.push_back(attr); + + if (dev.find("power_limit_mode") != dev.end()) + { + attr = std::make_pair("power_limit_mode", dev["power_limit_mode"]); + attrs.push_back(attr); + } + + writeToDb(m_poeTable, "dev" + sep + std::to_string(devId), attrs); + + for (auto& pse : dev["pse_list"]) + { + attrs.clear(); + + attr = std::make_pair("device_id", std::to_string(devId)); + attrs.push_back(attr); + + if (pse.find("pse_index") == pse.end()) + { + SWSS_LOG_ERROR("Missing mandatory field 'pse_index'"); + return false; + } + auto pseIndex = pse["pse_index"].get(); + attr = std::make_pair("pse_index", std::to_string(pseIndex)); + attrs.push_back(attr); + + writeToDb(m_poeTable, "pse" + sep + std::to_string(pseIndex), attrs); + } + + for (auto& port : dev["port_mapping_list"]) + { + attrs.clear(); + + attr = std::make_pair("device_id", std::to_string(devId)); + attrs.push_back(attr); + + if (port.find("front_panel_index") == port.end()) + { + SWSS_LOG_ERROR("Missing mandatory field 'front_panel_index'"); + return false; + } + auto frontPanelIndex = port["front_panel_index"].get(); + attr = std::make_pair("front_panel_index", std::to_string(frontPanelIndex)); + attrs.push_back(attr); + + if (port.find("interface") == port.end()) + { + SWSS_LOG_ERROR("Missing mandatory field 'interface'"); + return false; + } + auto ifname = port["interface"].get(); + attr = std::make_pair("interface", ifname); + attrs.push_back(attr); + + if (port.find("power_limit") != port.end()) + { + attr = std::make_pair("power_limit", std::to_string(port["power_limit"].get())); + attrs.push_back(attr); + } + + if (port.find("power_priority") != port.end()) + { + attr = std::make_pair("power_priority", port["power_priority"]); + attrs.push_back(attr); + } + + writeToDb(m_poeTable, ifname, attrs); + } + } + return true; +} diff --git a/poesyncd/poeparser.h b/poesyncd/poeparser.h new file mode 100644 index 0000000000..38ebf05f85 --- /dev/null +++ b/poesyncd/poeparser.h @@ -0,0 +1,47 @@ +/* + * Copyright 2024 + * + * 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. + */ + +#pragma once + +#include +#include +#include "dbconnector.h" +#include "producerstatetable.h" + +// #pragma GCC diagnostic push +// #pragma GCC diagnostic ignored "-Wshadow" +#include +// #pragma GCC diagnostic pop + +using json = nlohmann::json; + +class PoeParser +{ +public: + PoeParser(const std::string &configPath); + bool loadConfig(); + bool storeConfigToDb(); + bool writeToDb(swss::ProducerStateTable &table, + const std::string &key, + const std::vector &vals); + void notifyConfigDone(bool success); +private: + swss::DBConnector m_applDb; + swss::DBConnector m_cfgDb; + swss::ProducerStateTable m_poeTable; + std::string m_cfgPath; + json m_cfg; +}; diff --git a/poesyncd/poesyncd.cpp b/poesyncd/poesyncd.cpp new file mode 100644 index 0000000000..7bf1920d12 --- /dev/null +++ b/poesyncd/poesyncd.cpp @@ -0,0 +1,83 @@ +/* + * Copyright 2024 + * + * 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 "warm_restart.h" +#include "poeparser.h" + + +static void usage() +{ + std::cout << "Usage: poesyncd [-p poe_config.json]" << std::endl; + std::cout << " -p poe_config.json: import poe config" << std::endl; +} + +int main(int argc, char **argv) +{ + swss::Logger::linkToDbNative("poesyncd"); + SWSS_LOG_ENTER(); + std::string config_file; + int opt; + + while ((opt = getopt(argc, argv, "p:h")) != -1 ) + { + switch (opt) + { + case 'p': + config_file.assign(optarg); + break; + case 'h': + usage(); + return EXIT_FAILURE; + default: /* '?' */ + usage(); + return EXIT_FAILURE; + } + } + + swss::WarmStart::initialize("poesyncd", "swss"); + swss::WarmStart::checkWarmStart("poesyncd", "swss"); + + if (config_file.empty()) + { + SWSS_LOG_ERROR("Missing PoE config"); + return EXIT_FAILURE; + } + + try + { + PoeParser parser(config_file); + parser.notifyConfigDone(false); + if (parser.loadConfig() && parser.storeConfigToDb()) + { + parser.notifyConfigDone(true); + } + else + { + SWSS_LOG_ERROR("Failed to parse PoE config"); + } + } + catch (const std::exception& e) + { + SWSS_LOG_ERROR("Exception \"%s\" had been thrown in poesyncd daemon", e.what()); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} diff --git a/tests/mock_tests/Makefile.am b/tests/mock_tests/Makefile.am index 0f5afa4486..d99385cf77 100644 --- a/tests/mock_tests/Makefile.am +++ b/tests/mock_tests/Makefile.am @@ -67,6 +67,7 @@ tests_SOURCES = aclorch_ut.cpp \ mock_orch_test.cpp \ $(top_srcdir)/warmrestart/warmRestartHelper.cpp \ $(top_srcdir)/lib/gearboxutils.cpp \ + $(top_srcdir)/lib/poecfg.cpp \ $(top_srcdir)/lib/subintf.cpp \ $(top_srcdir)/lib/recorder.cpp \ $(top_srcdir)/orchagent/orchdaemon.cpp \ @@ -139,7 +140,8 @@ tests_SOURCES = aclorch_ut.cpp \ $(top_srcdir)/warmrestart/warmRestartAssist.cpp \ $(top_srcdir)/orchagent/dash/pbutils.cpp \ $(top_srcdir)/cfgmgr/coppmgr.cpp \ - $(top_srcdir)/orchagent/twamporch.cpp + $(top_srcdir)/orchagent/twamporch.cpp \ + $(top_srcdir)/orchagent/poeorch.cpp tests_SOURCES += $(FLEX_CTR_DIR)/flex_counter_manager.cpp $(FLEX_CTR_DIR)/flex_counter_stat_manager.cpp $(FLEX_CTR_DIR)/flow_counter_handler.cpp $(FLEX_CTR_DIR)/flowcounterrouteorch.cpp tests_SOURCES += $(DEBUG_CTR_DIR)/debug_counter.cpp $(DEBUG_CTR_DIR)/drop_counter.cpp diff --git a/tests/mock_tests/mock_orchagent_main.cpp b/tests/mock_tests/mock_orchagent_main.cpp index 96d86018fa..984fa37e07 100644 --- a/tests/mock_tests/mock_orchagent_main.cpp +++ b/tests/mock_tests/mock_orchagent_main.cpp @@ -9,6 +9,7 @@ extern "C" { sai_object_id_t gVirtualRouterId; sai_object_id_t gUnderlayIfId; sai_object_id_t gSwitchId = SAI_NULL_OBJECT_ID; +sai_object_id_t gPoeSwitchId = SAI_NULL_OBJECT_ID; MacAddress gMacAddress; MacAddress gVxlanMacAddress; diff --git a/tests/mock_tests/mock_orchagent_main.h b/tests/mock_tests/mock_orchagent_main.h index f2469a09ef..3c814984ce 100644 --- a/tests/mock_tests/mock_orchagent_main.h +++ b/tests/mock_tests/mock_orchagent_main.h @@ -37,6 +37,7 @@ extern MacAddress gMacAddress; extern MacAddress gVxlanMacAddress; extern sai_object_id_t gSwitchId; +extern sai_object_id_t gPoeSwitchId; extern sai_object_id_t gVirtualRouterId; extern sai_object_id_t gUnderlayIfId;