From b810c51c9df942abdb59a044c54440f234d094cc Mon Sep 17 00:00:00 2001 From: Roy Yi Date: Thu, 28 Mar 2024 23:19:38 +0000 Subject: [PATCH] Add support for configuring loopback mode Added SWSS support for configuring loopback modes through CONFIG DB. --- orchagent/port.h | 1 + orchagent/port/portcnt.h | 5 ++ orchagent/port/porthlpr.cpp | 43 +++++++++++++++ orchagent/port/porthlpr.h | 2 + orchagent/port/portschema.h | 1 + orchagent/portsorch.cpp | 45 ++++++++++++++++ orchagent/portsorch.h | 1 + tests/test_port.py | 102 ++++++++++++++++++++++++++++++++++++ 8 files changed, 200 insertions(+) diff --git a/orchagent/port.h b/orchagent/port.h index 0ae9b97b67..7d29282045 100644 --- a/orchagent/port.h +++ b/orchagent/port.h @@ -171,6 +171,7 @@ class Port uint32_t m_maximum_headroom = 0; std::set m_adv_speeds; sai_port_interface_type_t m_interface_type = SAI_PORT_INTERFACE_TYPE_NONE; + sai_port_loopback_mode_t m_loopback_mode = SAI_PORT_LOOPBACK_MODE_NONE; std::set m_adv_interface_types; bool m_mpls = false; /* diff --git a/orchagent/port/portcnt.h b/orchagent/port/portcnt.h index 33d52231cc..e76bdc19ae 100644 --- a/orchagent/port/portcnt.h +++ b/orchagent/port/portcnt.h @@ -217,6 +217,11 @@ class PortConfig final bool is_set = false; } pt_timestamp_template; // Port timestamp template for Path Tracing + struct { + sai_port_loopback_mode_t value; + bool is_set = false; + } loopback_mode; // Port loopback mode + struct { sai_redis_link_event_damping_algorithm_t value; bool is_set = false; diff --git a/orchagent/port/porthlpr.cpp b/orchagent/port/porthlpr.cpp index 181fef9f69..a3702002f9 100644 --- a/orchagent/port/porthlpr.cpp +++ b/orchagent/port/porthlpr.cpp @@ -127,6 +127,14 @@ static const std::unordered_map portLoopbackModeMap = { + {"none", SAI_PORT_LOOPBACK_MODE_NONE}, + {"phy_local", SAI_PORT_LOOPBACK_MODE_PHY}, + {"mac_local", SAI_PORT_LOOPBACK_MODE_MAC}, + {"phy_remote", SAI_PORT_LOOPBACK_MODE_PHY_REMOTE}, + {"mac_remote", SAI_PORT_LOOPBACK_MODE_MAC_REMOTE}, +}; + static const std::unordered_map g_linkEventDampingAlgorithmMap = { { "disabled", SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_DISABLED }, @@ -253,6 +261,11 @@ std::string PortHelper::getPtTimestampTemplateStr(const PortConfig &port) const return this->getFieldValueStr(port, PORT_PT_TIMESTAMP_TEMPLATE); } +std::string PortHelper::getLoopbackModeStr(const PortConfig &port) const +{ + return this->getFieldValueStr(port, PORT_LOOPBACK_MODE); +} + std::string PortHelper::getDampingAlgorithm(const PortConfig &port) const { return this->getFieldValueStr(port, PORT_DAMPING_ALGO); @@ -919,6 +932,29 @@ bool PortHelper::parsePortPtTimestampTemplate(PortConfig &port, const std::strin return true; } +bool PortHelper::parsePortLoopbackMode(PortConfig &port, const std::string &field, const std::string &value) const +{ + SWSS_LOG_ENTER(); + + if (value.empty()) + { + SWSS_LOG_ERROR("Failed to parse field(%s): empty value is prohibited", field.c_str()); + return false; + } + + const auto &cit = portLoopbackModeMap.find(value); + if (cit == portLoopbackModeMap.cend()) + { + SWSS_LOG_ERROR("Failed to parse field(%s): invalid value(%s)", field.c_str(), value.c_str()); + return false; + } + + port.loopback_mode.value = cit->second; + port.loopback_mode.is_set = true; + + return true; +} + bool PortHelper::parsePortConfig(PortConfig &port) const { SWSS_LOG_ENTER(); @@ -1187,6 +1223,13 @@ bool PortHelper::parsePortConfig(PortConfig &port) const return false; } } + else if (field == PORT_LOOPBACK_MODE) + { + if (!this->parsePortLoopbackMode(port, field, value)) + { + return false; + } + } else if (field == PORT_DAMPING_ALGO) { if (!this->parsePortLinkEventDampingAlgorithm(port, field, value)) diff --git a/orchagent/port/porthlpr.h b/orchagent/port/porthlpr.h index 45a4893a39..35839c7dbe 100644 --- a/orchagent/port/porthlpr.h +++ b/orchagent/port/porthlpr.h @@ -27,6 +27,7 @@ class PortHelper final std::string getLinkTrainingStr(const PortConfig &port) const; std::string getAdminStatusStr(const PortConfig &port) const; std::string getPtTimestampTemplateStr(const PortConfig &port) const; + std::string getLoopbackModeStr(const PortConfig &port) const; std::string getDampingAlgorithm(const PortConfig &port) const; bool parsePortConfig(PortConfig &port) const; @@ -62,4 +63,5 @@ class PortHelper final bool parsePortSubport(PortConfig &port, const std::string &field, const std::string &value) const; bool parsePortPtIntfId(PortConfig &port, const std::string &field, const std::string &value) const; bool parsePortPtTimestampTemplate(PortConfig &port, const std::string &field, const std::string &value) const; + bool parsePortLoopbackMode(PortConfig &port, const std::string &field, const std::string &value) const; }; diff --git a/orchagent/port/portschema.h b/orchagent/port/portschema.h index 8dd7f79200..4d9dbfbd76 100644 --- a/orchagent/port/portschema.h +++ b/orchagent/port/portschema.h @@ -95,6 +95,7 @@ #define PORT_SUBPORT "subport" #define PORT_PT_INTF_ID "pt_interface_id" #define PORT_PT_TIMESTAMP_TEMPLATE "pt_timestamp_template" +#define PORT_LOOPBACK_MODE "loopback_mode" #define PORT_DAMPING_ALGO "link_event_damping_algorithm" #define PORT_MAX_SUPPRESS_TIME "max_suppress_time" #define PORT_DECAY_HALF_LIFE "decay_half_life" diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index ebce79093c..e4d5308546 100644 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -4595,6 +4595,30 @@ void PortsOrch::doPortTask(Consumer &consumer) } } + if (pCfg.loopback_mode.is_set) + { + if (p.m_loopback_mode != pCfg.loopback_mode.value) + { + auto status = setPortLoopbackMode(p.m_port_id, pCfg.loopback_mode.value); + if (status.ok()) + { + SWSS_LOG_NOTICE("Set port %s loopback mode to %s", + p.m_alias.c_str(), + m_portHlpr.getLoopbackModeStr(pCfg).c_str()); + p.m_loopback_mode = pCfg.loopback_mode.value; + m_portList[p.m_alias] = p; + } + else + { + SWSS_LOG_ERROR( + "Failed to set port %s loopback mode to %s", + p.m_alias.c_str(), m_portHlpr.getLoopbackModeStr(pCfg).c_str()); + it = taskMap.erase(it); + continue; + } + } + } + /* create host_tx_ready field in state-db */ initHostTxReadyState(p); @@ -8528,6 +8552,27 @@ void PortsOrch::getPortSerdesVal(const std::string& val_str, } } +ReturnCode PortsOrch::setPortLoopbackMode(sai_object_id_t id, sai_port_loopback_mode_t loopback_mode) +{ + SWSS_LOG_ENTER(); + + sai_attribute_t attr; + attr.id = SAI_PORT_ATTR_LOOPBACK_MODE; + attr.value.u32 = loopback_mode; + + sai_status_t status = sai_port_api->set_port_attribute(id, &attr); + if (status != SAI_STATUS_SUCCESS) + { + LOG_ERROR_AND_RETURN(ReturnCode(status) + << "Failed to set loopback mode " << attr.value.u32 + << " to port pid 0x" << std::hex << id + << ", rv: " << sai_serialize_status(status)); + } + + SWSS_LOG_INFO("Set loopback mode %u on port pid: %" PRIx64, attr.value.u32, id); + return ReturnCode(); +} + /* Bring up/down Vlan interface associated with L3 VNI*/ bool PortsOrch::updateL3VniStatus(uint16_t vlan_id, bool isUp) { diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h index 3ae283fb80..a13c96311e 100644 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -498,6 +498,7 @@ class PortsOrch : public Orch, public Subject ReturnCode addSendToIngressHostIf(const std::string &send_to_ingress_name); ReturnCode removeSendToIngressHostIf(); + ReturnCode setPortLoopbackMode(sai_object_id_t id, sai_port_loopback_mode_t loopback_mode); void initGearbox(); bool initGearboxPort(Port &port); bool getPortOperFec(const Port& port, sai_port_fec_mode_t &fec_mode) const; diff --git a/tests/test_port.py b/tests/test_port.py index feccb6917a..7baa730dde 100644 --- a/tests/test_port.py +++ b/tests/test_port.py @@ -432,6 +432,108 @@ def test_PortPathTracing(self, dvs, testlog): for key, queue in buffer_queues.items(): dvs.get_config_db().update_entry("BUFFER_QUEUE", key, queue) + def test_InvalidPortLoopbackMode(self, dvs, testlog): + pdb = swsscommon.DBConnector(0, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + table_name = "PORT_TABLE" + port_name = "Ethernet0" + attribute_name = "loopback_mode" + + for attribute_value in ["", "invalid"]: + # Set invalid loopback mode. + tbl = swsscommon.ProducerStateTable(pdb, table_name) + fvs = swsscommon.FieldValuePairs([(attribute_name, attribute_value)]) + tbl.set(port_name, fvs) + time.sleep(1) + + # Check application database. + tbl = swsscommon.Table(pdb, table_name) + (status, fvs) = tbl.get(port_name) + assert status == True + attribute_found = False + for fv in fvs: + if fv[0] == attribute_name: + attribute_found = True + assert fv[1] == attribute_value + assert attribute_found == True + + # Check attribute not present in asic database. + tbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_PORT") + (status, fvs) = tbl.get(dvs.asicdb.portnamemap[port_name]) + assert status == True + attribute_found = False + for fv in fvs: + if fv[0] == "SAI_PORT_ATTR_LOOPBACK_MODE": + attribute_found = True + assert attribute_found == False + + def test_PortLoopbackMode(self, dvs, testlog): + pdb = swsscommon.DBConnector(0, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + table_name = "PORT_TABLE" + port_name = "Ethernet0" + attribute_name = "loopback_mode" + attribute_value = "mac_local" + + # Enable loopback mode. + tbl = swsscommon.ProducerStateTable(pdb, table_name) + fvs = swsscommon.FieldValuePairs([(attribute_name, attribute_value)]) + tbl.set(port_name, fvs) + time.sleep(1) + + # Check application database. + tbl = swsscommon.Table(pdb, table_name) + (status, fvs) = tbl.get(port_name) + assert status == True + attribute_found = False + for fv in fvs: + if fv[0] == attribute_name: + attribute_found = True + assert fv[1] == attribute_value + assert attribute_found == True + + # Check asic database. + tbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_PORT") + (status, fvs) = tbl.get(dvs.asicdb.portnamemap[port_name]) + assert status == True + attribute_found = False + for fv in fvs: + if fv[0] == "SAI_PORT_ATTR_LOOPBACK_MODE": + attribute_found = True + assert fv[1] == "SAI_PORT_LOOPBACK_MODE_MAC" + assert attribute_found == True + + # Disable loopback mode. + attribute_value = "none" + tbl = swsscommon.ProducerStateTable(pdb, table_name) + fvs = swsscommon.FieldValuePairs([(attribute_name, attribute_value)]) + tbl.set(port_name, fvs) + time.sleep(1) + + # Check application database. + tbl = swsscommon.Table(pdb, table_name) + (status, fvs) = tbl.get(port_name) + assert status == True + attribute_found = False + for fv in fvs: + if fv[0] == attribute_name: + attribute_found = True + assert fv[1] == attribute_value + assert attribute_found == True + + # Check asic database. + tbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_PORT") + (status, fvs) = tbl.get(dvs.asicdb.portnamemap[port_name]) + assert status == True + attribute_found = False + for fv in fvs: + if fv[0] == "SAI_PORT_ATTR_LOOPBACK_MODE": + attribute_found = True + assert fv[1] == "SAI_PORT_LOOPBACK_MODE_NONE" + assert attribute_found == True + def test_PortLinkEventDamping(self, dvs, testlog): cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) pdb = swsscommon.DBConnector(0, dvs.redis_sock, 0)