From 80f52079319df45a650cb54502fa4b70adf4e5ff Mon Sep 17 00:00:00 2001 From: Roy Yi <126022672+royyi8@users.noreply.github.com> Date: Tue, 4 Jun 2024 14:36:03 -0700 Subject: [PATCH] Add SWSS support for link event damping feature (#2933) What I did Added support for link event damping in SWSS. Required Syncd PR: sonic-net/sonic-sairedis#1297 CLI PR: sonic-net/sonic-utilities#3001 HLD: https://github.com/sonic-net/SONiC/blob/master/doc/link_event_damping/Link-event-damping-HLD.md Why I did it How I verified it Use the config interface damping CLI to set the port attributes on the switch and observe that Syncd processes link event damping parameters. --- orchagent/port.h | 9 + orchagent/port/portcnt.h | 34 +++ orchagent/port/porthlpr.cpp | 108 ++++++++++ orchagent/port/porthlpr.h | 5 + orchagent/port/portschema.h | 6 + orchagent/portsorch.cpp | 123 +++++++++++ orchagent/portsorch.h | 5 + tests/mock_tests/portsorch_ut.cpp | 334 ++++++++++++++++++++++++++++++ tests/test_port.py | 48 +++++ 9 files changed, 672 insertions(+) diff --git a/orchagent/port.h b/orchagent/port.h index d153b20318..0ae9b97b67 100644 --- a/orchagent/port.h +++ b/orchagent/port.h @@ -13,6 +13,7 @@ extern "C" { #include #include +#include #define DEFAULT_PORT_VLAN_ID 1 /* @@ -212,6 +213,14 @@ class Port /* Path Tracing */ uint16_t m_pt_intf_id = 0; sai_port_path_tracing_timestamp_type_t m_pt_timestamp_template = SAI_PORT_PATH_TRACING_TIMESTAMP_TYPE_16_23; + + /* link event damping */ + sai_redis_link_event_damping_algorithm_t m_link_event_damping_algorithm = SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_DISABLED; + uint32_t m_max_suppress_time = 0; + uint32_t m_decay_half_life = 0; + uint32_t m_suppress_threshold = 0; + uint32_t m_reuse_threshold = 0; + uint32_t m_flap_penalty = 0; }; } diff --git a/orchagent/port/portcnt.h b/orchagent/port/portcnt.h index 9e3e63f9b7..33d52231cc 100644 --- a/orchagent/port/portcnt.h +++ b/orchagent/port/portcnt.h @@ -217,6 +217,40 @@ class PortConfig final bool is_set = false; } pt_timestamp_template; // Port timestamp template for Path Tracing + struct { + sai_redis_link_event_damping_algorithm_t value; + bool is_set = false; + } link_event_damping_algorithm; // Port link event damping algorithm + + struct { + + struct { + uint32_t value; + bool is_set = false; + } max_suppress_time; // Max suppress time + + struct { + uint32_t value; + bool is_set = false; + } decay_half_life; // Decay half life + + struct { + uint32_t value; + bool is_set = false; + } suppress_threshold; // Suppress threshold + + struct { + uint32_t value; + bool is_set = false; + } reuse_threshold; // Reuse threshold + + struct { + uint32_t value; + bool is_set = false; + } flap_penalty; // Flap penalty + + } link_event_damping_config; // Port link event damping config + std::string key; std::string op; diff --git a/orchagent/port/porthlpr.cpp b/orchagent/port/porthlpr.cpp index 7ac9c15c52..181fef9f69 100644 --- a/orchagent/port/porthlpr.cpp +++ b/orchagent/port/porthlpr.cpp @@ -21,6 +21,7 @@ using namespace swss; // types -------------------------------------------------------------------------------------------------------------- typedef decltype(PortConfig::serdes) PortSerdes_t; +typedef decltype(PortConfig::link_event_damping_config) PortDampingConfig_t; // constants ---------------------------------------------------------------------------------------------------------- @@ -126,6 +127,12 @@ static const std::unordered_map g_linkEventDampingAlgorithmMap = +{ + { "disabled", SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_DISABLED }, + { "aied", SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED } +}; + // functions ---------------------------------------------------------------------------------------------------------- template @@ -246,6 +253,11 @@ std::string PortHelper::getPtTimestampTemplateStr(const PortConfig &port) const return this->getFieldValueStr(port, PORT_PT_TIMESTAMP_TEMPLATE); } +std::string PortHelper::getDampingAlgorithm(const PortConfig &port) const +{ + return this->getFieldValueStr(port, PORT_DAMPING_ALGO); +} + bool PortHelper::parsePortAlias(PortConfig &port, const std::string &field, const std::string &value) const { SWSS_LOG_ENTER(); @@ -786,6 +798,60 @@ bool PortHelper::parsePortSubport(PortConfig &port, const std::string &field, co return true; } +bool PortHelper::parsePortLinkEventDampingAlgorithm(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 = g_linkEventDampingAlgorithmMap.find(value); + if (cit == g_linkEventDampingAlgorithmMap.cend()) + { + SWSS_LOG_ERROR("Failed to parse field(%s): invalid value(%s)", field.c_str(), value.c_str()); + return false; + } + + port.link_event_damping_algorithm.value = cit->second; + port.link_event_damping_algorithm.is_set = true; + + return true; +} + +template +bool PortHelper::parsePortLinkEventDampingConfig(T &damping_config_attr, 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; + } + + try + { + damping_config_attr.value = to_uint(value); + damping_config_attr.is_set = true; + } + catch (const std::exception &e) + { + SWSS_LOG_ERROR("Failed to parse field(%s): %s", field.c_str(), e.what()); + return false; + } + + return true; +} + +template bool PortHelper::parsePortLinkEventDampingConfig(decltype(PortDampingConfig_t::max_suppress_time) &damping_config_attr, const std::string &field, const std::string &value) const; +template bool PortHelper::parsePortLinkEventDampingConfig(decltype(PortDampingConfig_t::decay_half_life) &damping_config_attr, const std::string &field, const std::string &value) const; +template bool PortHelper::parsePortLinkEventDampingConfig(decltype(PortDampingConfig_t::suppress_threshold) &damping_config_attr, const std::string &field, const std::string &value) const; +template bool PortHelper::parsePortLinkEventDampingConfig(decltype(PortDampingConfig_t::reuse_threshold) &damping_config_attr, const std::string &field, const std::string &value) const; +template bool PortHelper::parsePortLinkEventDampingConfig(decltype(PortDampingConfig_t::flap_penalty) &damping_config_attr, const std::string &field, const std::string &value) const; + bool PortHelper::parsePortPtIntfId(PortConfig &port, const std::string &field, const std::string &value) const { SWSS_LOG_ENTER(); @@ -1121,6 +1187,48 @@ bool PortHelper::parsePortConfig(PortConfig &port) const return false; } } + else if (field == PORT_DAMPING_ALGO) + { + if (!this->parsePortLinkEventDampingAlgorithm(port, field, value)) + { + return false; + } + } + else if (field == PORT_MAX_SUPPRESS_TIME) + { + if (!this->parsePortLinkEventDampingConfig(port.link_event_damping_config.max_suppress_time, field, value)) + { + return false; + } + } + else if (field == PORT_DECAY_HALF_LIFE) + { + if (!this->parsePortLinkEventDampingConfig(port.link_event_damping_config.decay_half_life, field, value)) + { + return false; + } + } + else if (field == PORT_SUPPRESS_THRESHOLD) + { + if (!this->parsePortLinkEventDampingConfig(port.link_event_damping_config.suppress_threshold, field, value)) + { + return false; + } + } + else if (field == PORT_REUSE_THRESHOLD) + { + if (!this->parsePortLinkEventDampingConfig(port.link_event_damping_config.reuse_threshold, field, value)) + { + return false; + } + } + else if (field == PORT_FLAP_PENALTY) + { + if (!this->parsePortLinkEventDampingConfig(port.link_event_damping_config.flap_penalty, 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 3852759975..45a4893a39 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 getDampingAlgorithm(const PortConfig &port) const; bool parsePortConfig(PortConfig &port) const; bool validatePortConfig(PortConfig &port) const; @@ -37,6 +38,10 @@ class PortHelper final template bool parsePortSerdes(T &serdes, const std::string &field, const std::string &value) const; + bool parsePortLinkEventDampingAlgorithm(PortConfig &port, const std::string &field, const std::string &value) const; + template + bool parsePortLinkEventDampingConfig(T &damping_config_attr, const std::string &field, const std::string &value) const; + bool parsePortAlias(PortConfig &port, const std::string &field, const std::string &value) const; bool parsePortIndex(PortConfig &port, const std::string &field, const std::string &value) const; bool parsePortLanes(PortConfig &port, const std::string &field, const std::string &value) const; diff --git a/orchagent/port/portschema.h b/orchagent/port/portschema.h index c9a3274913..8dd7f79200 100644 --- a/orchagent/port/portschema.h +++ b/orchagent/port/portschema.h @@ -95,3 +95,9 @@ #define PORT_SUBPORT "subport" #define PORT_PT_INTF_ID "pt_interface_id" #define PORT_PT_TIMESTAMP_TEMPLATE "pt_timestamp_template" +#define PORT_DAMPING_ALGO "link_event_damping_algorithm" +#define PORT_MAX_SUPPRESS_TIME "max_suppress_time" +#define PORT_DECAY_HALF_LIFE "decay_half_life" +#define PORT_SUPPRESS_THRESHOLD "suppress_threshold" +#define PORT_REUSE_THRESHOLD "reuse_threshold" +#define PORT_FLAP_PENALTY "flap_penalty" diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index f370d09607..ebce79093c 100644 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -3171,6 +3171,49 @@ task_process_status PortsOrch::setPortLinkTraining(const Port &port, bool state) return task_success; } +ReturnCode PortsOrch::setPortLinkEventDampingAlgorithm(Port &port, + sai_redis_link_event_damping_algorithm_t &link_event_damping_algorithm) +{ + SWSS_LOG_ENTER(); + sai_attribute_t attr; + attr.id = SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM; + attr.value.s32 = link_event_damping_algorithm; + + CHECK_ERROR_AND_LOG_AND_RETURN( + sai_port_api->set_port_attribute(port.m_port_id, &attr), + "Failed to set link event damping algorithm (" << link_event_damping_algorithm << ") for port " + << port.m_alias); + + SWSS_LOG_INFO("Set link event damping algorithm %u for port %s", link_event_damping_algorithm, port.m_alias.c_str()); + return ReturnCode(); +} + +ReturnCode PortsOrch::setPortLinkEventDampingAiedConfig(Port &port, + sai_redis_link_event_damping_algo_aied_config_t &config) { + + SWSS_LOG_ENTER(); + sai_attribute_t attr; + attr.id = SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG; + attr.value.ptr = (void *) &config; + + std::stringstream msg; + msg << "link event damping algorithm aied config for port " << port.m_alias << " - "; + msg << "max_suppress_time: " << config.max_suppress_time << ", "; + msg << "decay_half_life: " << config.decay_half_life << ", "; + msg << "suppress_threshold: " << config.suppress_threshold << ", "; + msg << "reuse_threshold: " << config.reuse_threshold << ", "; + msg << "flap_penalty: " << config.flap_penalty; + + std::string msg_str = msg.str(); + + CHECK_ERROR_AND_LOG_AND_RETURN( + sai_port_api->set_port_attribute(port.m_port_id, &attr), "Failed to set " + msg_str); + + SWSS_LOG_INFO("Set %s", msg_str.c_str()); + + return ReturnCode(); +} + bool PortsOrch::setHostIntfsOperStatus(const Port& port, bool isUp) const { SWSS_LOG_ENTER(); @@ -4033,6 +4076,86 @@ void PortsOrch::doPortTask(Consumer &consumer) } } + if (pCfg.link_event_damping_algorithm.is_set) + { + if (p.m_link_event_damping_algorithm != pCfg.link_event_damping_algorithm.value) + { + auto status = setPortLinkEventDampingAlgorithm(p, pCfg.link_event_damping_algorithm.value); + if (!status.ok()) + { + SWSS_LOG_ERROR( + "Failed to set port %s link event damping algorithm to %s", + p.m_alias.c_str(), m_portHlpr.getDampingAlgorithm(pCfg).c_str() + ); + it = taskMap.erase(it); + continue; + } + + p.m_link_event_damping_algorithm = pCfg.link_event_damping_algorithm.value; + m_portList[p.m_alias] = p; + + SWSS_LOG_NOTICE( + "Set port %s link event damping algorithm to %s", + p.m_alias.c_str(), m_portHlpr.getDampingAlgorithm(pCfg).c_str() + ); + } + } + + sai_redis_link_event_damping_algo_aied_config_t aied_config = { + p.m_max_suppress_time, + p.m_suppress_threshold, + p.m_reuse_threshold, + p.m_decay_half_life, + p.m_flap_penalty, + }; + + if (pCfg.link_event_damping_config.max_suppress_time.is_set) + { + aied_config.max_suppress_time = pCfg.link_event_damping_config.max_suppress_time.value; + } + if (pCfg.link_event_damping_config.decay_half_life.is_set) + { + aied_config.decay_half_life = pCfg.link_event_damping_config.decay_half_life.value; + } + if (pCfg.link_event_damping_config.suppress_threshold.is_set) + { + aied_config.suppress_threshold = pCfg.link_event_damping_config.suppress_threshold.value; + } + if (pCfg.link_event_damping_config.reuse_threshold.is_set) + { + aied_config.reuse_threshold = pCfg.link_event_damping_config.reuse_threshold.value; + } + if (pCfg.link_event_damping_config.flap_penalty.is_set) + { + aied_config.flap_penalty = pCfg.link_event_damping_config.flap_penalty.value; + } + + bool config_changed = !(aied_config.max_suppress_time == p.m_max_suppress_time && + aied_config.decay_half_life == p.m_decay_half_life && + aied_config.suppress_threshold == p.m_suppress_threshold && + aied_config.reuse_threshold == p.m_reuse_threshold && + aied_config.flap_penalty == p.m_flap_penalty); + + if (config_changed) + { + auto status = setPortLinkEventDampingAiedConfig(p, aied_config); + if (!status.ok()) + { + SWSS_LOG_ERROR("Failed to set port %s link event damping config", p.m_alias.c_str()); + it = taskMap.erase(it); + continue; + } + + p.m_max_suppress_time = aied_config.max_suppress_time; + p.m_decay_half_life = aied_config.decay_half_life; + p.m_suppress_threshold = aied_config.suppress_threshold; + p.m_reuse_threshold = aied_config.reuse_threshold; + p.m_flap_penalty = aied_config.flap_penalty; + m_portList[p.m_alias] = p; + + SWSS_LOG_NOTICE("Set port %s link event damping config successfully", p.m_alias.c_str()); + } + if (pCfg.speed.is_set) { if (p.m_speed != pCfg.speed.value) diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h index 70f6248fda..3ae283fb80 100644 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -462,6 +462,11 @@ class PortsOrch : public Orch, public Subject task_process_status setPortAdvInterfaceTypes(Port &port, std::set &interface_types); task_process_status setPortLinkTraining(const Port& port, bool state); + ReturnCode setPortLinkEventDampingAlgorithm(Port &port, + sai_redis_link_event_damping_algorithm_t &link_event_damping_algorithm); + ReturnCode setPortLinkEventDampingAiedConfig(Port &port, + sai_redis_link_event_damping_algo_aied_config_t &config); + void updatePortOperStatus(Port &port, sai_port_oper_status_t status); bool getPortOperSpeed(const Port& port, sai_uint32_t& speed) const; diff --git a/tests/mock_tests/portsorch_ut.cpp b/tests/mock_tests/portsorch_ut.cpp index afa26dc439..22f8632af1 100644 --- a/tests/mock_tests/portsorch_ut.cpp +++ b/tests/mock_tests/portsorch_ut.cpp @@ -92,6 +92,12 @@ namespace portsorch_test uint32_t set_pt_interface_id_failures; uint32_t set_pt_timestamp_template_failures; uint32_t set_port_tam_failures; + bool set_link_event_damping_success = true; + uint32_t _sai_set_link_event_damping_algorithm_count; + uint32_t _sai_set_link_event_damping_config_count; + int32_t _sai_link_event_damping_algorithm = 0; + sai_redis_link_event_damping_algo_aied_config_t _sai_link_event_damping_config = {0, 0, 0, 0, 0}; + sai_status_t _ut_stub_sai_set_port_attribute( _In_ sai_object_id_t port_id, _In_ const sai_attribute_t *attr) @@ -148,6 +154,26 @@ namespace portsorch_test return SAI_STATUS_INVALID_ATTR_VALUE_0; } } + else if (attr[0].id == SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGORITHM) + { + _sai_set_link_event_damping_algorithm_count++; + + if (set_link_event_damping_success) { + _sai_link_event_damping_algorithm = attr[0].value.s32; + return SAI_STATUS_SUCCESS; + } + return SAI_STATUS_FAILURE; + } + else if (attr[0].id == SAI_REDIS_PORT_ATTR_LINK_EVENT_DAMPING_ALGO_AIED_CONFIG) + { + _sai_set_link_event_damping_config_count++; + + if (set_link_event_damping_success) { + _sai_link_event_damping_config = *(reinterpret_cast(attr[0].value.ptr)); + return SAI_STATUS_SUCCESS; + } + return SAI_STATUS_FAILURE; + } return pold_sai_port_api->set_port_attribute(port_id, attr); } @@ -1671,6 +1697,314 @@ namespace portsorch_test _unhook_sai_bridge_api(); } + TEST_F(PortsOrchTest, SupportedLinkEventDampingAlgorithmSuccess) + { + _hook_sai_port_api(); + Table portTable = Table(m_app_db.get(), APP_PORT_TABLE_NAME); + std::deque entries; + + // Get SAI default ports to populate DB + auto ports = ut_helper::getInitialSaiPorts(); + + for (const auto &it : ports) + { + portTable.set(it.first, it.second); + } + + // Set PortConfigDone + portTable.set("PortConfigDone", { { "count", to_string(ports.size()) } }); + + // refill consumer + gPortsOrch->addExistingData(&portTable); + + // Apply configuration : + // create ports + static_cast(gPortsOrch)->doTask(); + + uint32_t current_sai_api_call_count = _sai_set_link_event_damping_algorithm_count; + + entries.push_back({"Ethernet0", "SET", + { + {"link_event_damping_algorithm", "aied"} + }}); + auto consumer = dynamic_cast(gPortsOrch->getExecutor(APP_PORT_TABLE_NAME)); + consumer->addToSync(entries); + static_cast(gPortsOrch)->doTask(); + entries.clear(); + + // verify SAI call was made and set algorithm successfully + ASSERT_EQ(_sai_set_link_event_damping_algorithm_count, ++current_sai_api_call_count); + ASSERT_EQ(_sai_link_event_damping_algorithm, SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_AIED); + + vector ts; + + gPortsOrch->dumpPendingTasks(ts); + ASSERT_TRUE(ts.empty()); + + _unhook_sai_port_api(); + } + + TEST_F(PortsOrchTest, SupportedLinkEventDampingAlgorithmFailure) + { + _hook_sai_port_api(); + Table portTable = Table(m_app_db.get(), APP_PORT_TABLE_NAME); + std::deque entries; + + set_link_event_damping_success = false; + _sai_link_event_damping_algorithm = SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_DISABLED; + + // Get SAI default ports to populate DB + auto ports = ut_helper::getInitialSaiPorts(); + + for (const auto &it : ports) + { + portTable.set(it.first, it.second); + } + + // Set PortConfigDone + portTable.set("PortConfigDone", { { "count", to_string(ports.size()) } }); + + // refill consumer + gPortsOrch->addExistingData(&portTable); + + // Apply configuration : + // create ports + static_cast(gPortsOrch)->doTask(); + + uint32_t current_sai_api_call_count = _sai_set_link_event_damping_algorithm_count; + + + entries.push_back({"Ethernet0", "SET", + { + {"link_event_damping_algorithm", "aied"} + }}); + auto consumer = dynamic_cast(gPortsOrch->getExecutor(APP_PORT_TABLE_NAME)); + consumer->addToSync(entries); + static_cast(gPortsOrch)->doTask(); + entries.clear(); + + // Verify that SAI call was made, algorithm not set + ASSERT_EQ(_sai_set_link_event_damping_algorithm_count, ++current_sai_api_call_count); + ASSERT_EQ(_sai_link_event_damping_algorithm, SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_DISABLED); + + vector ts; + + gPortsOrch->dumpPendingTasks(ts); + ASSERT_TRUE(ts.empty()); + + _unhook_sai_port_api(); + } + + TEST_F(PortsOrchTest, NotSupportedLinkEventDampingAlgorithm) + { + _hook_sai_port_api(); + Table portTable = Table(m_app_db.get(), APP_PORT_TABLE_NAME); + std::deque entries; + + // Get SAI default ports to populate DB + auto ports = ut_helper::getInitialSaiPorts(); + + for (const auto &it : ports) + { + portTable.set(it.first, it.second); + } + + // Set PortConfigDone + portTable.set("PortConfigDone", { { "count", to_string(ports.size()) } }); + + // refill consumer + gPortsOrch->addExistingData(&portTable); + + // Apply configuration : + // create ports + static_cast(gPortsOrch)->doTask(); + + uint32_t current_sai_api_call_count = _sai_set_link_event_damping_algorithm_count; + + entries.push_back({"Ethernet0", "SET", + { + {"link_event_damping_algorithm", "test_algo"} + }}); + auto consumer = dynamic_cast(gPortsOrch->getExecutor(APP_PORT_TABLE_NAME)); + consumer->addToSync(entries); + static_cast(gPortsOrch)->doTask(); + entries.clear(); + + // Verify that no SAI call was made + ASSERT_EQ(_sai_set_link_event_damping_algorithm_count, current_sai_api_call_count); + ASSERT_EQ(_sai_link_event_damping_algorithm, SAI_REDIS_LINK_EVENT_DAMPING_ALGORITHM_DISABLED); + + vector ts; + + gPortsOrch->dumpPendingTasks(ts); + ASSERT_TRUE(ts.empty()); + + _unhook_sai_port_api(); + } + + TEST_F(PortsOrchTest, SetLinkEventDampingFullConfigSuccess) { + _hook_sai_port_api(); + Table portTable = Table(m_app_db.get(), APP_PORT_TABLE_NAME); + std::deque entries; + + set_link_event_damping_success = true; + // Get SAI default ports to populate DB + auto ports = ut_helper::getInitialSaiPorts(); + + for (const auto &it : ports) + { + portTable.set(it.first, it.second); + } + + // Set PortConfigDone + portTable.set("PortConfigDone", { { "count", to_string(ports.size()) } }); + + // refill consumer + gPortsOrch->addExistingData(&portTable); + + // Apply configuration : + // create ports + static_cast(gPortsOrch)->doTask(); + + uint32_t current_sai_api_call_count = _sai_set_link_event_damping_config_count; + + entries.push_back({"Ethernet0", "SET", + { + {"max_suppress_time", "64000"}, + {"decay_half_life", "45000"}, + {"suppress_threshold", "1650"}, + {"reuse_threshold", "1500"}, + {"flap_penalty", "1000"}, + }}); + auto consumer = dynamic_cast(gPortsOrch->getExecutor(APP_PORT_TABLE_NAME)); + consumer->addToSync(entries); + static_cast(gPortsOrch)->doTask(); + entries.clear(); + + ASSERT_EQ(_sai_set_link_event_damping_config_count, ++current_sai_api_call_count); + ASSERT_EQ(_sai_link_event_damping_config.max_suppress_time, 64000); + ASSERT_EQ(_sai_link_event_damping_config.decay_half_life, 45000); + ASSERT_EQ(_sai_link_event_damping_config.suppress_threshold, 1650); + ASSERT_EQ(_sai_link_event_damping_config.reuse_threshold, 1500); + ASSERT_EQ(_sai_link_event_damping_config.flap_penalty, 1000); + + vector ts; + + gPortsOrch->dumpPendingTasks(ts); + ASSERT_TRUE(ts.empty()); + + _unhook_sai_port_api(); + } + + TEST_F(PortsOrchTest, SetLinkEventDampingPartialConfigSuccess) { + _hook_sai_port_api(); + Table portTable = Table(m_app_db.get(), APP_PORT_TABLE_NAME); + std::deque entries; + + _sai_link_event_damping_config = {0, 0, 0, 0, 0}; + + // Get SAI default ports to populate DB + auto ports = ut_helper::getInitialSaiPorts(); + + for (const auto &it : ports) + { + portTable.set(it.first, it.second); + } + + // Set PortConfigDone + portTable.set("PortConfigDone", { { "count", to_string(ports.size()) } }); + + // refill consumer + gPortsOrch->addExistingData(&portTable); + + // Apply configuration : + // create ports + static_cast(gPortsOrch)->doTask(); + + uint32_t current_sai_api_call_count = _sai_set_link_event_damping_config_count; + + entries.push_back({"Ethernet0", "SET", + { + {"decay_half_life", "30000"}, + {"reuse_threshold", "1200"}, + }}); + auto consumer = dynamic_cast(gPortsOrch->getExecutor(APP_PORT_TABLE_NAME)); + consumer->addToSync(entries); + static_cast(gPortsOrch)->doTask(); + entries.clear(); + + ASSERT_EQ(_sai_set_link_event_damping_config_count, ++current_sai_api_call_count); + ASSERT_EQ(_sai_link_event_damping_config.max_suppress_time, 0); + ASSERT_EQ(_sai_link_event_damping_config.decay_half_life, 30000); + ASSERT_EQ(_sai_link_event_damping_config.suppress_threshold, 0); + ASSERT_EQ(_sai_link_event_damping_config.reuse_threshold, 1200); + ASSERT_EQ(_sai_link_event_damping_config.flap_penalty, 0); + + vector ts; + + gPortsOrch->dumpPendingTasks(ts); + ASSERT_TRUE(ts.empty()); + + _unhook_sai_port_api(); + } + + TEST_F(PortsOrchTest, SetLinkEventDampingConfigFailure) { + _hook_sai_port_api(); + Table portTable = Table(m_app_db.get(), APP_PORT_TABLE_NAME); + std::deque entries; + + set_link_event_damping_success = false; + _sai_link_event_damping_config = {0, 0, 0, 0, 0}; + + // Get SAI default ports to populate DB + auto ports = ut_helper::getInitialSaiPorts(); + + for (const auto &it : ports) + { + portTable.set(it.first, it.second); + } + + // Set PortConfigDone + portTable.set("PortConfigDone", { { "count", to_string(ports.size()) } }); + + // refill consumer + gPortsOrch->addExistingData(&portTable); + + // Apply configuration : + // create ports + static_cast(gPortsOrch)->doTask(); + + uint32_t current_sai_api_call_count = _sai_set_link_event_damping_config_count; + + entries.push_back({"Ethernet0", "SET", + { + {"max_suppress_time", "64000"}, + {"decay_half_life", "45000"}, + {"suppress_threshold", "1650"}, + {"reuse_threshold", "1500"}, + {"flap_penalty", "1000"}, + }}); + auto consumer = dynamic_cast(gPortsOrch->getExecutor(APP_PORT_TABLE_NAME)); + consumer->addToSync(entries); + static_cast(gPortsOrch)->doTask(); + entries.clear(); + + // Verify that config is not set + ASSERT_EQ(_sai_set_link_event_damping_config_count, ++current_sai_api_call_count); + ASSERT_EQ(_sai_link_event_damping_config.max_suppress_time, 0); + ASSERT_EQ(_sai_link_event_damping_config.decay_half_life, 0); + ASSERT_EQ(_sai_link_event_damping_config.suppress_threshold, 0); + ASSERT_EQ(_sai_link_event_damping_config.reuse_threshold, 0); + ASSERT_EQ(_sai_link_event_damping_config.flap_penalty, 0); + + vector ts; + + gPortsOrch->dumpPendingTasks(ts); + ASSERT_TRUE(ts.empty()); + + _unhook_sai_port_api(); + } + TEST_F(PortsOrchTest, PortSupportedFecModes) { _hook_sai_port_api(); diff --git a/tests/test_port.py b/tests/test_port.py index d7bf62d2d7..feccb6917a 100644 --- a/tests/test_port.py +++ b/tests/test_port.py @@ -432,6 +432,54 @@ 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_PortLinkEventDamping(self, dvs, testlog): + cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) + pdb = swsscommon.DBConnector(0, dvs.redis_sock, 0) + + cfg_tbl = swsscommon.Table(cdb, "PORT") + app_tbl = swsscommon.Table(pdb, "PORT_TABLE") + port_name = "Ethernet0" + + # Set link event damping. + fvs = swsscommon.FieldValuePairs([("link_event_damping_algorithm", "aied"), + ("max_suppress_time", "54000"), + ("decay_half_life", "45000"), + ("suppress_threshold", "1650"), + ("reuse_threshold", "1500"), + ("flap_penalty", "1000") + ]) + cfg_tbl.set(port_name, fvs) + time.sleep(1) + + # Check application database. + (status, fvs) = app_tbl.get(port_name) + assert status == True + for fv in fvs: + if fv[0] == "link_event_damping_algorithm": + assert fv[1] == "aied" + elif fv[0] == "max_suppress_time": + assert fv[1] == "54000" + elif fv[0] == "decay_half_life": + assert fv[1] == "45000" + elif fv[0] == "suppress_threshold": + assert fv[1] == "1650" + elif fv[0] == "reuse_threshold": + assert fv[1] == "1500" + elif fv[0] == "flap_penalty": + assert fv[1] == "1000" + + # Disable link event damping. + fvs = swsscommon.FieldValuePairs([("link_event_damping_algorithm", "disabled")]) + cfg_tbl.set(port_name, fvs) + time.sleep(1) + + # Check application database. + (status, fvs) = app_tbl.get(port_name) + assert status == True + for fv in fvs: + if fv[0] == "link_event_damping_algorithm": + assert fv[1] == "disabled" + # Add Dummy always-pass test at end as workaroud # for issue when Flaky fail on final test it invokes module tear-down before retrying