diff --git a/orchagent/port.h b/orchagent/port.h index dc6de89aa8..225222a622 100644 --- a/orchagent/port.h +++ b/orchagent/port.h @@ -238,6 +238,7 @@ class Port sai_port_interface_type_t m_interface_type = SAI_PORT_INTERFACE_TYPE_NONE; std::set m_adv_interface_types; bool m_mpls = false; + std::string m_media_type = "unknown"; /* * Following bit vector is used to lock * the queue from being changed in BufferOrch. diff --git a/orchagent/port/portcnt.h b/orchagent/port/portcnt.h index 91af64fed6..3442af130f 100644 --- a/orchagent/port/portcnt.h +++ b/orchagent/port/portcnt.h @@ -98,6 +98,11 @@ class PortConfig final bool is_set = false; } link_training; // Port link training + struct { + std::string value; + bool is_set = false; + } media_type; // Port media type + struct { struct { diff --git a/orchagent/port/porthlpr.cpp b/orchagent/port/porthlpr.cpp index 15ccd3585d..aef5dbc3f1 100644 --- a/orchagent/port/porthlpr.cpp +++ b/orchagent/port/porthlpr.cpp @@ -969,6 +969,22 @@ bool PortHelper::parsePortPtTimestampTemplate(PortConfig &port, const std::strin return true; } +bool PortHelper::parsePortMediaType(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 string is prohibited", field.c_str()); + return false; + } + + port.media_type.value = value; + port.media_type.is_set = true; + + return true; +} + bool PortHelper::parsePortConfig(PortConfig &port) const { SWSS_LOG_ENTER(); @@ -1299,6 +1315,13 @@ bool PortHelper::parsePortConfig(PortConfig &port) const * Setting exists in sonic-port.yang with possible values: routed|access|trunk */ } + else if (field == PORT_MEDIA_TYPE) + { + if (!this->parsePortMediaType(port, field, value)) + { + return false; + } + } else { SWSS_LOG_WARN("Unknown field(%s): skipping ...", field.c_str()); diff --git a/orchagent/port/porthlpr.h b/orchagent/port/porthlpr.h index 153c784be7..23e6d51678 100644 --- a/orchagent/port/porthlpr.h +++ b/orchagent/port/porthlpr.h @@ -72,4 +72,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 parsePortMediaType(PortConfig &port, const std::string &field, const std::string &value) const; }; diff --git a/orchagent/port/portschema.h b/orchagent/port/portschema.h index b2667baa25..6f42f6957a 100644 --- a/orchagent/port/portschema.h +++ b/orchagent/port/portschema.h @@ -104,3 +104,4 @@ #define PORT_FLAP_PENALTY "flap_penalty" #define PORT_MODE "mode" #define PORT_UNRELIABLE_LOS "unreliable_los" +#define PORT_MEDIA_TYPE "media_type" diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index 66c0f78c0f..a8b679444f 100644 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -155,7 +155,8 @@ static map learn_mode_map = static map media_type_map = { { "fiber", SAI_PORT_MEDIA_TYPE_FIBER }, - { "copper", SAI_PORT_MEDIA_TYPE_COPPER } + { "copper", SAI_PORT_MEDIA_TYPE_COPPER }, + { "backplane", SAI_PORT_MEDIA_TYPE_BACKPLANE} }; static map loopback_mode_map = @@ -5194,6 +5195,21 @@ void PortsOrch::doPortTask(Consumer &consumer) } } } + if (pCfg.media_type.is_set) + { + if (setPortMediaType(p, pCfg.media_type.value)) + { + SWSS_LOG_NOTICE("Set port %s Media Type %s is successful", + p.m_alias.c_str(), pCfg.media_type.value.c_str()); + } + else + { + SWSS_LOG_ERROR("Failed to set port %s Media Type %s", + p.m_alias.c_str(), pCfg.media_type.value.c_str()); + it++; + continue; + } + } /* create host_tx_ready field in state-db */ initHostTxReadyState(p); @@ -9530,6 +9546,36 @@ bool PortsOrch::setPortSerdesAttribute(sai_object_id_t port_id, sai_object_id_t } } SWSS_LOG_NOTICE("Created port serdes object 0x%" PRIx64 " for port 0x%" PRIx64, port_serdes_id, port_id); + + return true; +} + +bool PortsOrch::setPortMediaType(Port& port, const string &media_type) +{ + sai_attribute_t attr; + sai_status_t status; + + if (media_type_map.find(media_type) == media_type_map.end()) + { + SWSS_LOG_NOTICE("Invalid media_type:%s for port %s", media_type.c_str(), port.m_alias.c_str()); + return false; + } + + attr.id = SAI_PORT_ATTR_MEDIA_TYPE; + attr.value.u32 = media_type_map[media_type]; + status = sai_port_api->set_port_attribute(port.m_port_id, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set Media Type %s 0x%x to port %s, rv:%d", + media_type.c_str(), attr.value.u32, port.m_alias.c_str(), status); + task_process_status handle_status = handleSaiSetStatus(SAI_API_PORT, status); + if (handle_status != task_success) + { + return parseHandleSaiStatusFailure(handle_status); + } + } + port.m_media_type = media_type; + SWSS_LOG_INFO("Set Media Type %s 0x%x to port pid:%" PRIx64, media_type.c_str(), attr.value.u32, port.m_port_id); return true; } diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h index 954c42ed6d..e7873eeb1e 100644 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -448,6 +448,7 @@ class PortsOrch : public Orch, public Subject bool setPortFecOverride(sai_object_id_t port_obj, bool override_fec); bool setPortPfcAsym(Port &port, sai_port_priority_flow_control_mode_t pfc_asym); bool getDestPortId(sai_object_id_t src_port_id, dest_port_type_t port_type, sai_object_id_t &des_port_id); + bool setPortMediaType(Port& port, const string &media_type); bool setBridgePortAdminStatus(sai_object_id_t id, bool up); diff --git a/tests/mock_tests/portsorch_ut.cpp b/tests/mock_tests/portsorch_ut.cpp index 63c3df5d94..a91b030788 100644 --- a/tests/mock_tests/portsorch_ut.cpp +++ b/tests/mock_tests/portsorch_ut.cpp @@ -1219,19 +1219,51 @@ namespace portsorch_test { "obnlev", "0x5f,0x61,0x60,0x62" }, { "regn_bfm1p", "0x1e,0x20,0x1f,0x21" }, { "regn_bfm1n", "0xaa,0xac,0xab,0xad" }, - { "custom_serdes_attrs", custom_serdes_attrs } + { "custom_serdes_attrs", custom_serdes_attrs }, + { "media_type", "backplane" } } }}; + std::deque kfvList1 = {{"Ethernet0", SET_COMMAND, {{ "media_type", "" }}}}; + std::deque kfvList2 = {{"Ethernet0", SET_COMMAND, {{ "media_type", "none" }}}}; + std::deque kfvListDel = {{"Ethernet0", "DEL" , {}}}; + + auto fvs = ports.begin()->second; - // Refill consumer auto consumer = dynamic_cast(gPortsOrch->getExecutor(APP_PORT_TABLE_NAME)); + std::deque kfvListAdd= {{"Ethernet0", SET_COMMAND , fvs}}; + + Port p; + //empty media_type should not be processed + consumer->addToSync(kfvList1); + static_cast(gPortsOrch)->doTask(); + ASSERT_TRUE(gPortsOrch->getPort("Ethernet0", p)); + ASSERT_EQ(p.m_media_type, "unknown"); + + consumer->addToSync(kfvListDel); + static_cast(gPortsOrch)->doTask(); + consumer->addToSync(kfvListAdd); + static_cast(gPortsOrch)->doTask(); + ASSERT_TRUE(gPortsOrch->getPort("Ethernet0", p)); + + consumer->addToSync(kfvList2); + static_cast(gPortsOrch)->doTask(); + ASSERT_TRUE(gPortsOrch->getPort("Ethernet0", p)); + ASSERT_EQ(p.m_media_type, "unknown"); + + consumer->addToSync(kfvListDel); + static_cast(gPortsOrch)->doTask(); + consumer->addToSync(kfvListAdd); + static_cast(gPortsOrch)->doTask(); + ASSERT_TRUE(gPortsOrch->getPort("Ethernet0", p)); + + // Refill consumer consumer->addToSync(kfvList); // Apply configuration static_cast(gPortsOrch)->doTask(); // Get port - Port p; + // Port p; ASSERT_TRUE(gPortsOrch->getPort("Ethernet0", p)); // Verify preemphasis @@ -1305,9 +1337,14 @@ namespace portsorch_test // Verify custom_serdes_attrs ASSERT_EQ(p.m_serdes_attrs.at(SAI_PORT_SERDES_ATTR_CUSTOM_COLLECTION), SerdesValue(custom_serdes_attrs)); + // Verify media_type + std::string media_type = "backplane"; + ASSERT_EQ(p.m_media_type, media_type); + // Verify unreliablelos ASSERT_EQ(p.m_unreliable_los, false); + // Dump pending tasks std::vector taskList; gPortsOrch->dumpPendingTasks(taskList); @@ -4071,5 +4108,4 @@ namespace portsorch_test ASSERT_FALSE(port.m_init); } - } diff --git a/tests/test_port.py b/tests/test_port.py index 129cab981c..2336e77d38 100644 --- a/tests/test_port.py +++ b/tests/test_port.py @@ -536,6 +536,63 @@ def test_PortAdminRestore(self, dvs, testlog): if fv[0] == "SAI_PORT_ATTR_ADMIN_STATE": assert fv[1] == "false" + def test_media_type(self, dvs, testlog): + + db = swsscommon.DBConnector(0, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + tbl = swsscommon.Table(db, "PORT_TABLE") + ptbl = swsscommon.ProducerStateTable(db, "PORT_TABLE") + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_PORT") + + media_name = 'media_type' + media_val = "" + + fvs = swsscommon.FieldValuePairs([(media_name, media_val)]) + ptbl.set("Ethernet0", fvs) + + time.sleep(1) + + # get media_type + (status, fvs) = atbl.get(dvs.asicdb.portnamemap["Ethernet0"]) + assert status == True + #empty media_type should be rejected + for fv in fvs: + assert fv[0] != "SAI_PORT_ATTR_MEDIA_TYPE" + + media_name = 'media_type' + media_val = "none" + fvs = swsscommon.FieldValuePairs([(media_name, media_val)]) + ptbl.set("Ethernet0", fvs) + + time.sleep(1) + + # get media_type + (status, fvs) = atbl.get(dvs.asicdb.portnamemap["Ethernet0"]) + assert status == True + #"none" media_type should be rejected + for fv in fvs: + assert fv[0] != "SAI_PORT_ATTR_MEDIA_TYPE" + + media_name = 'media_type' + media_val = "backplane" + fvs = swsscommon.FieldValuePairs([(media_name, media_val)]) + ptbl.set("Ethernet0", fvs) + + time.sleep(1) + + # get media_type + (status, fvs) = atbl.get(dvs.asicdb.portnamemap["Ethernet0"]) + assert status == True + + found = False + #check the media_type is added in ASIC_DB + for fv in fvs: + if fv[0] == "SAI_PORT_ATTR_MEDIA_TYPE": + assert fv[1] == "SAI_PORT_MEDIA_TYPE_BACKPLANE" + found = True + assert found == True + # Add Dummy always-pass test at end as workaroud # for issue when Flaky fail on final test it invokes module tear-down before retrying def test_nonflaky_dummy():