From b2c03d199ada03d6021ee8b3315199c7b10e9a5b Mon Sep 17 00:00:00 2001 From: Rajkumar-Marvell <54936542+rajkumar38@users.noreply.github.com> Date: Tue, 30 May 2023 21:42:35 +0530 Subject: [PATCH] Egress Sflow Support (#2731) * [sflow] Added Egress Sflow support. --- cfgmgr/sflowmgr.cpp | 145 ++++++++++++++++++++++++++------ cfgmgr/sflowmgr.h | 8 +- orchagent/sfloworch.cpp | 177 ++++++++++++++++++++++++++++++++++----- orchagent/sfloworch.h | 8 +- orchagent/switchorch.cpp | 30 +++++++ orchagent/switchorch.h | 2 + tests/test_sflow.py | 87 +++++++++++++++++++ 7 files changed, 405 insertions(+), 52 deletions(-) diff --git a/cfgmgr/sflowmgr.cpp b/cfgmgr/sflowmgr.cpp index bb732e83d53e..a063708c1481 100644 --- a/cfgmgr/sflowmgr.cpp +++ b/cfgmgr/sflowmgr.cpp @@ -31,6 +31,8 @@ SflowMgr::SflowMgr(DBConnector *cfgDb, DBConnector *appDb, const vector { m_intfAllConf = true; m_gEnable = false; + m_gDirection = "rx"; + m_intfAllDir = "rx"; } void SflowMgr::sflowHandleService(bool enable) @@ -85,9 +87,11 @@ void SflowMgr::sflowUpdatePortInfo(Consumer &consumer) new_port = true; port_info.local_rate_cfg = false; port_info.local_admin_cfg = false; + port_info.local_dir_cfg = false; port_info.speed = SFLOW_ERROR_SPEED_STR; port_info.rate = ""; port_info.admin = ""; + port_info.dir = ""; m_sflowPortConfMap[key] = port_info; } @@ -106,13 +110,19 @@ void SflowMgr::sflowUpdatePortInfo(Consumer &consumer) speed_change = true; } + string def_dir = "rx"; + if (m_sflowPortConfMap[key].dir != def_dir && !m_sflowPortConfMap[key].local_dir_cfg) + { + m_sflowPortConfMap[key].dir = def_dir; + } + if (m_gEnable && m_intfAllConf) { // If the Local rate Conf is already present, dont't override it even though the speed is changed if (new_port || (speed_change && !m_sflowPortConfMap[key].local_rate_cfg)) { vector fvs; - sflowGetGlobalInfo(fvs, m_sflowPortConfMap[key].speed); + sflowGetGlobalInfo(fvs, m_sflowPortConfMap[key].speed, m_sflowPortConfMap[key].dir); m_appSflowSessionTable.set(key, fvs); } } @@ -123,7 +133,8 @@ void SflowMgr::sflowUpdatePortInfo(Consumer &consumer) if (sflowPortConf != m_sflowPortConfMap.end()) { bool local_cfg = m_sflowPortConfMap[key].local_rate_cfg || - m_sflowPortConfMap[key].local_admin_cfg; + m_sflowPortConfMap[key].local_admin_cfg || + m_sflowPortConfMap[key].local_dir_cfg; m_sflowPortConfMap.erase(key); if ((m_intfAllConf && m_gEnable) || local_cfg) @@ -136,14 +147,14 @@ void SflowMgr::sflowUpdatePortInfo(Consumer &consumer) } } -void SflowMgr::sflowHandleSessionAll(bool enable) +void SflowMgr::sflowHandleSessionAll(bool enable, string direction) { for (auto it: m_sflowPortConfMap) { if (enable) { vector fvs; - if (it.second.local_rate_cfg || it.second.local_admin_cfg) + if (it.second.local_rate_cfg || it.second.local_admin_cfg || it.second.local_dir_cfg) { sflowGetPortInfo(fvs, it.second); /* Use global admin state if there is not a local one */ @@ -151,10 +162,16 @@ void SflowMgr::sflowHandleSessionAll(bool enable) FieldValueTuple fv1("admin_state", "up"); fvs.push_back(fv1); } + + /* Use global sample direction state if there is not a local one */ + if (!it.second.local_dir_cfg) { + FieldValueTuple fv2("sample_direction", direction); + fvs.push_back(fv2); + } } else { - sflowGetGlobalInfo(fvs, it.second.speed); + sflowGetGlobalInfo(fvs, it.second.speed, direction); } m_appSflowSessionTable.set(it.first, fvs); } @@ -169,7 +186,7 @@ void SflowMgr::sflowHandleSessionLocal(bool enable) { for (auto it: m_sflowPortConfMap) { - if (it.second.local_admin_cfg || it.second.local_rate_cfg) + if (it.second.local_admin_cfg || it.second.local_rate_cfg || it.second.local_dir_cfg) { vector fvs; sflowGetPortInfo(fvs, it.second); @@ -185,7 +202,7 @@ void SflowMgr::sflowHandleSessionLocal(bool enable) } } -void SflowMgr::sflowGetGlobalInfo(vector &fvs, string speed) +void SflowMgr::sflowGetGlobalInfo(vector &fvs, string speed, string dir) { string rate; FieldValueTuple fv1("admin_state", "up"); @@ -201,6 +218,9 @@ void SflowMgr::sflowGetGlobalInfo(vector &fvs, string speed) } FieldValueTuple fv2("sample_rate",rate); fvs.push_back(fv2); + + FieldValueTuple fv3("sample_direction",dir); + fvs.push_back(fv3); } void SflowMgr::sflowGetPortInfo(vector &fvs, SflowPortInfo &local_info) @@ -213,6 +233,12 @@ void SflowMgr::sflowGetPortInfo(vector &fvs, SflowPortInfo &loc FieldValueTuple fv2("sample_rate", local_info.rate); fvs.push_back(fv2); + + if (local_info.local_dir_cfg) + { + FieldValueTuple fv3("sample_direction", local_info.dir); + fvs.push_back(fv3); + } } void SflowMgr::sflowCheckAndFillValues(string alias, vector &values, @@ -221,6 +247,7 @@ void SflowMgr::sflowCheckAndFillValues(string alias, vector &va string rate; bool admin_present = false; bool rate_present = false; + bool dir_present = false; for (auto i : values) { @@ -240,6 +267,14 @@ void SflowMgr::sflowCheckAndFillValues(string alias, vector &va FieldValueTuple fv(fvField(i), fvValue(i)); fvs.push_back(fv); } + if (fvField(i) == "sample_direction") + { + dir_present = true; + m_sflowPortConfMap[alias].dir = fvValue(i); + m_sflowPortConfMap[alias].local_dir_cfg = true; + FieldValueTuple fv(fvField(i), fvValue(i)); + fvs.push_back(fv); + } if (fvField(i) == "NULL") { continue; @@ -282,6 +317,18 @@ void SflowMgr::sflowCheckAndFillValues(string alias, vector &va FieldValueTuple fv("admin_state", m_sflowPortConfMap[alias].admin); fvs.push_back(fv); } + + if (!dir_present) + { + if (m_sflowPortConfMap[alias].dir == "") + { + /* By default direction is set to global, if not set explicitly */ + m_sflowPortConfMap[alias].dir = m_gDirection; + } + m_sflowPortConfMap[alias].local_dir_cfg = false; + FieldValueTuple fv("sample_direction", m_sflowPortConfMap[alias].dir); + fvs.push_back(fv); + } } void SflowMgr::doTask(Consumer &consumer) @@ -309,51 +356,92 @@ void SflowMgr::doTask(Consumer &consumer) { if (table == CFG_SFLOW_TABLE_NAME) { + SWSS_LOG_DEBUG("Current Cfg admin %d dir %s ", (unsigned int)m_gEnable, m_gDirection.c_str()); + bool enable = false; + string direction = "rx"; for (auto i : values) { if (fvField(i) == "admin_state") { - bool enable = false; if (fvValue(i) == "up") { enable = true; } - if (enable == m_gEnable) - { - break; - } - m_gEnable = enable; - sflowHandleService(enable); - if (m_intfAllConf) - { - sflowHandleSessionAll(enable); - } - sflowHandleSessionLocal(enable); + } + else if (fvField(i) == "sample_direction") + { + direction = fvValue(i); } } + + if (direction != m_gDirection) + { + m_gDirection = direction; + } + + if (m_gEnable != enable) + { + m_gEnable = enable; + sflowHandleService(enable); + } + + if (m_intfAllConf) + { + sflowHandleSessionAll(m_gEnable, m_gDirection); + } + + sflowHandleSessionLocal(m_gEnable); m_appSflowTable.set(key, values); + + SWSS_LOG_DEBUG("New config admin %d dir %s ", (unsigned int)m_gEnable, m_gDirection.c_str()); } else if (table == CFG_SFLOW_SESSION_TABLE_NAME) { if (key == "all") { + SWSS_LOG_DEBUG("current config gAdmin %d dir %s intfAllEna %d intfAllDir %s", + (unsigned int)m_gEnable, m_gDirection.c_str(), + (unsigned int)m_intfAllConf, m_intfAllDir.c_str()); + + string direction = m_intfAllDir; + bool enable = m_intfAllConf; for (auto i : values) { if (fvField(i) == "admin_state") { - bool enable = false; - if (fvValue(i) == "up") { enable = true; } - if ((enable != m_intfAllConf) && (m_gEnable)) + else if (fvValue(i) == "down") { - sflowHandleSessionAll(enable); + enable = false; } - m_intfAllConf = enable; } + else if (fvField(i) == "sample_direction") + { + direction = fvValue(i); + } + } + + if (m_intfAllDir != direction) + { + m_intfAllDir = direction; + } + + if (enable != m_intfAllConf) + { + m_intfAllConf = enable; + } + + if (m_gEnable) + { + sflowHandleSessionAll(m_intfAllConf, m_intfAllDir); } + + SWSS_LOG_DEBUG("New config gAdmin %d dir %s intfAllEna %d intfAllDir %s", + (unsigned int)m_gEnable, m_gDirection.c_str(), + (unsigned int)m_intfAllConf, m_intfAllDir.c_str()); } else { @@ -380,10 +468,11 @@ void SflowMgr::doTask(Consumer &consumer) if (m_gEnable) { sflowHandleService(false); - sflowHandleSessionAll(false); + sflowHandleSessionAll(false, ""); sflowHandleSessionLocal(false); } m_gEnable = false; + m_gDirection = "rx"; m_appSflowTable.del(key); } else if (table == CFG_SFLOW_SESSION_TABLE_NAME) @@ -394,7 +483,7 @@ void SflowMgr::doTask(Consumer &consumer) { if (m_gEnable) { - sflowHandleSessionAll(true); + sflowHandleSessionAll(true, m_gDirection); } } m_intfAllConf = true; @@ -404,14 +493,16 @@ void SflowMgr::doTask(Consumer &consumer) m_appSflowSessionTable.del(key); m_sflowPortConfMap[key].local_rate_cfg = false; m_sflowPortConfMap[key].local_admin_cfg = false; + m_sflowPortConfMap[key].local_dir_cfg = false; m_sflowPortConfMap[key].rate = ""; m_sflowPortConfMap[key].admin = ""; + m_sflowPortConfMap[key].dir = ""; /* If Global configured, set global session on port after local config is deleted */ if (m_intfAllConf) { vector fvs; - sflowGetGlobalInfo(fvs, m_sflowPortConfMap[key].speed); + sflowGetGlobalInfo(fvs, m_sflowPortConfMap[key].speed, m_intfAllDir); m_appSflowSessionTable.set(key,fvs); } } diff --git a/cfgmgr/sflowmgr.h b/cfgmgr/sflowmgr.h index eb35ec212595..8f13266f6f0d 100644 --- a/cfgmgr/sflowmgr.h +++ b/cfgmgr/sflowmgr.h @@ -34,9 +34,11 @@ struct SflowPortInfo { bool local_rate_cfg; bool local_admin_cfg; + bool local_dir_cfg; std::string speed; std::string rate; std::string admin; + std::string dir; }; /* Port to Local config map */ @@ -56,15 +58,17 @@ class SflowMgr : public Orch SflowPortConfMap m_sflowPortConfMap; bool m_intfAllConf; bool m_gEnable; + std::string m_intfAllDir; + std::string m_gDirection; void doTask(Consumer &consumer); void sflowHandleService(bool enable); void sflowUpdatePortInfo(Consumer &consumer); - void sflowHandleSessionAll(bool enable); + void sflowHandleSessionAll(bool enable, std::string direction); void sflowHandleSessionLocal(bool enable); void sflowCheckAndFillValues(std::string alias, std::vector &values, std::vector &fvs); void sflowGetPortInfo(std::vector &fvs, SflowPortInfo &local_info); - void sflowGetGlobalInfo(std::vector &fvs, std::string speed); + void sflowGetGlobalInfo(std::vector &fvs, std::string speed, std::string direction); }; } diff --git a/orchagent/sfloworch.cpp b/orchagent/sfloworch.cpp index ac76d2300426..2ec367b41264 100644 --- a/orchagent/sfloworch.cpp +++ b/orchagent/sfloworch.cpp @@ -83,7 +83,7 @@ bool SflowOrch::sflowUpdateRate(sai_object_id_t port_id, uint32_t rate) if (port_info->second.admin_state) { - if (!sflowAddPort(new_session.m_sample_id, port_id)) + if (!sflowAddPort(new_session.m_sample_id, port_id, port_info->second.m_sample_dir)) { return false; } @@ -107,49 +107,155 @@ bool SflowOrch::sflowUpdateRate(sai_object_id_t port_id, uint32_t rate) return true; } -bool SflowOrch::sflowAddPort(sai_object_id_t sample_id, sai_object_id_t port_id) +bool SflowOrch::sflowAddPort(sai_object_id_t sample_id, sai_object_id_t port_id, string direction) { sai_attribute_t attr; sai_status_t sai_rc; - attr.id = SAI_PORT_ATTR_INGRESS_SAMPLEPACKET_ENABLE; - attr.value.oid = sample_id; - sai_rc = sai_port_api->set_port_attribute(port_id, &attr); + SWSS_LOG_DEBUG("sflowAddPort %" PRIx64 " portOid %" PRIx64 " dir %s", + sample_id, port_id, direction.c_str()); - if (sai_rc != SAI_STATUS_SUCCESS) + if (direction == "both" || direction == "rx") { - SWSS_LOG_ERROR("Failed to set session %" PRIx64 " on port %" PRIx64 , sample_id, port_id); - task_process_status handle_status = handleSaiSetStatus(SAI_API_PORT, sai_rc); - if (handle_status != task_success) + attr.id = SAI_PORT_ATTR_INGRESS_SAMPLEPACKET_ENABLE; + attr.value.oid = sample_id; + sai_rc = sai_port_api->set_port_attribute(port_id, &attr); + + if (sai_rc != SAI_STATUS_SUCCESS) { - return parseHandleSaiStatusFailure(handle_status); + SWSS_LOG_ERROR("Failed to set session %" PRIx64 " on port %" PRIx64, sample_id, port_id); + task_process_status handle_status = handleSaiSetStatus(SAI_API_PORT, sai_rc); + if (handle_status != task_success) + { + return parseHandleSaiStatusFailure(handle_status); + } + } + } + + if (direction == "both" || direction == "tx") + { + attr.id = SAI_PORT_ATTR_EGRESS_SAMPLEPACKET_ENABLE; + attr.value.oid = sample_id; + sai_rc = sai_port_api->set_port_attribute(port_id, &attr); + + if (sai_rc != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set session %" PRIx64 " on port %" PRIx64, sample_id, port_id); + task_process_status handle_status = handleSaiSetStatus(SAI_API_PORT, sai_rc); + if (handle_status != task_success) + { + return parseHandleSaiStatusFailure(handle_status); + } + } + } + return true; +} + +bool SflowOrch::sflowDelPort(sai_object_id_t port_id, string direction) +{ + sai_attribute_t attr; + sai_status_t sai_rc; + + SWSS_LOG_DEBUG("sflowDelPort portOid %" PRIx64 " dir %s", + port_id, direction.c_str()); + + if (direction == "both" || direction == "rx") + { + attr.id = SAI_PORT_ATTR_INGRESS_SAMPLEPACKET_ENABLE; + attr.value.oid = SAI_NULL_OBJECT_ID; + sai_rc = sai_port_api->set_port_attribute(port_id, &attr); + + if (sai_rc != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to delete session on port %" PRIx64, port_id); + task_process_status handle_status = handleSaiSetStatus(SAI_API_PORT, sai_rc); + if (handle_status != task_success) + { + return parseHandleSaiStatusFailure(handle_status); + } + } + } + + if (direction == "both" || direction == "tx") + { + attr.id = SAI_PORT_ATTR_EGRESS_SAMPLEPACKET_ENABLE; + attr.value.oid = SAI_NULL_OBJECT_ID; + sai_rc = sai_port_api->set_port_attribute(port_id, &attr); + + if (sai_rc != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to delete session on port %" PRIx64, port_id); + task_process_status handle_status = handleSaiSetStatus(SAI_API_PORT, sai_rc); + if (handle_status != task_success) + { + return parseHandleSaiStatusFailure(handle_status); + } } } return true; } -bool SflowOrch::sflowDelPort(sai_object_id_t port_id) +bool SflowOrch::sflowUpdateSampleDirection(sai_object_id_t port_id, string old_dir, string new_dir) { + sai_object_id_t ing_sample_oid = SAI_NULL_OBJECT_ID; + sai_object_id_t egr_sample_oid = SAI_NULL_OBJECT_ID; sai_attribute_t attr; sai_status_t sai_rc; + auto port_info = m_sflowPortInfoMap.find(port_id); + + SWSS_LOG_DEBUG("sflowUpdateSampleDirection portOid %" PRIx64 " old dir %s new dir %s", + port_id, old_dir.c_str(), new_dir.c_str()); + + if ((new_dir == "tx") && (old_dir == "rx" || old_dir == "both")) + { + ing_sample_oid = SAI_NULL_OBJECT_ID; + egr_sample_oid = port_info->second.m_sample_id; + } + + if ((new_dir == "rx") && (old_dir == "tx" || old_dir == "both")) + { + ing_sample_oid = port_info->second.m_sample_id; + egr_sample_oid = SAI_NULL_OBJECT_ID; + } + + if ((new_dir == "both") && (old_dir == "tx" || old_dir == "rx")) + { + ing_sample_oid = port_info->second.m_sample_id; + egr_sample_oid = port_info->second.m_sample_id; + } attr.id = SAI_PORT_ATTR_INGRESS_SAMPLEPACKET_ENABLE; - attr.value.oid = SAI_NULL_OBJECT_ID; + attr.value.oid = ing_sample_oid; sai_rc = sai_port_api->set_port_attribute(port_id, &attr); if (sai_rc != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to delete session on port %" PRIx64 , port_id); + SWSS_LOG_ERROR("Failed to Ingress session on port %" PRIx64, port_id); task_process_status handle_status = handleSaiSetStatus(SAI_API_PORT, sai_rc); if (handle_status != task_success) { return parseHandleSaiStatusFailure(handle_status); } } + + attr.id = SAI_PORT_ATTR_EGRESS_SAMPLEPACKET_ENABLE; + attr.value.oid = egr_sample_oid; + sai_rc = sai_port_api->set_port_attribute(port_id, &attr); + + if (sai_rc != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to Update Egress session on port %" PRIx64, port_id); + task_process_status handle_status = handleSaiSetStatus(SAI_API_PORT, sai_rc); + if (handle_status != task_success) + { + return parseHandleSaiStatusFailure(handle_status); + } + } + return true; } -void SflowOrch::sflowExtractInfo(vector &fvs, bool &admin, uint32_t &rate) +void SflowOrch::sflowExtractInfo(vector &fvs, bool &admin, uint32_t &rate, string &dir) { for (auto i : fvs) { @@ -175,6 +281,13 @@ void SflowOrch::sflowExtractInfo(vector &fvs, bool &admin, uint rate = 0; } } + else if (fvField(i) == "sample_direction") + { + if (fvValue(i) != "error") + { + dir = fvValue(i); + } + } } } @@ -187,10 +300,11 @@ void SflowOrch::sflowStatusSet(Consumer &consumer) auto tuple = it->second; string op = kfvOp(tuple); uint32_t rate = 0; + string dir = ""; if (op == SET_COMMAND) { - sflowExtractInfo(kfvFieldsValues(tuple), m_sflowStatus, rate); + sflowExtractInfo(kfvFieldsValues(tuple), m_sflowStatus, rate, dir); } else if (op == DEL_COMMAND) { @@ -221,7 +335,7 @@ bool SflowOrch::handleSflowSessionDel(sai_object_id_t port_id) uint32_t rate = sflowSessionGetRate(sflowInfo->second.m_sample_id); if (sflowInfo->second.admin_state) { - if (!sflowDelPort(port_id)) + if (!sflowDelPort(port_id, sflowInfo->second.m_sample_dir)) { return false; } @@ -270,6 +384,7 @@ void SflowOrch::doTask(Consumer &consumer) { bool admin_state = m_sflowStatus; uint32_t rate = 0; + string dir = "rx"; if (!m_sflowStatus) { @@ -282,7 +397,15 @@ void SflowOrch::doTask(Consumer &consumer) admin_state = sflowInfo->second.admin_state; } - sflowExtractInfo(kfvFieldsValues(tuple), admin_state, rate); + SWSS_LOG_DEBUG(" Existing Cfg portOid %" PRIx64 " admin %d rate %d dir %s", + port.m_port_id, (unsigned int)admin_state, rate, + sflowInfo->second.m_sample_dir.c_str()); + + sflowExtractInfo(kfvFieldsValues(tuple), admin_state, rate, dir); + + SWSS_LOG_DEBUG("New Cfg portOid %" PRIx64 " admin %d rate %d dir %s", + port.m_port_id, (unsigned int)admin_state, rate, dir.c_str()); + if (sflowInfo == m_sflowPortInfoMap.end()) { if (rate == 0) @@ -308,9 +431,11 @@ void SflowOrch::doTask(Consumer &consumer) m_sflowRateSampleMap[rate] = session; port_info.m_sample_id = session.m_sample_id; } + port_info.m_sample_dir = dir; + if (admin_state) { - if (!sflowAddPort(port_info.m_sample_id, port.m_port_id)) + if (!sflowAddPort(port_info.m_sample_id, port.m_port_id, port_info.m_sample_dir)) { it++; continue; @@ -335,11 +460,12 @@ void SflowOrch::doTask(Consumer &consumer) bool ret = false; if (admin_state) { - ret = sflowAddPort(sflowInfo->second.m_sample_id, port.m_port_id); + ret = sflowAddPort(sflowInfo->second.m_sample_id, port.m_port_id, + sflowInfo->second.m_sample_dir); } else { - ret = sflowDelPort(port.m_port_id); + ret = sflowDelPort(port.m_port_id, sflowInfo->second.m_sample_dir); } if (!ret) { @@ -348,6 +474,17 @@ void SflowOrch::doTask(Consumer &consumer) } sflowInfo->second.admin_state = admin_state; } + + if (dir != sflowInfo->second.m_sample_dir) + { + string old_dir = sflowInfo->second.m_sample_dir; + if (!sflowUpdateSampleDirection(port.m_port_id, old_dir, dir)) + { + it++; + continue; + } + sflowInfo->second.m_sample_dir = dir; + } } } else if (op == DEL_COMMAND) diff --git a/orchagent/sfloworch.h b/orchagent/sfloworch.h index 04a5c9d65002..508b22c0aa34 100644 --- a/orchagent/sfloworch.h +++ b/orchagent/sfloworch.h @@ -10,6 +10,7 @@ struct SflowPortInfo { bool admin_state; + string m_sample_dir; sai_object_id_t m_sample_id; }; @@ -38,11 +39,12 @@ class SflowOrch : public Orch virtual void doTask(Consumer& consumer); bool sflowCreateSession(uint32_t rate, SflowSession &session); bool sflowDestroySession(SflowSession &session); - bool sflowAddPort(sai_object_id_t sample_id, sai_object_id_t port_id); - bool sflowDelPort(sai_object_id_t port_id); + bool sflowAddPort(sai_object_id_t sample_id, sai_object_id_t port_id, string direction); + bool sflowDelPort(sai_object_id_t port_id, string direction); void sflowStatusSet(Consumer &consumer); bool sflowUpdateRate(sai_object_id_t port_id, uint32_t rate); + bool sflowUpdateSampleDirection(sai_object_id_t port_id, string old_dir, string new_dir); uint32_t sflowSessionGetRate(sai_object_id_t sample_id); bool handleSflowSessionDel(sai_object_id_t port_id); - void sflowExtractInfo(std::vector &fvs, bool &admin, uint32_t &rate); + void sflowExtractInfo(std::vector &fvs, bool &admin, uint32_t &rate, string &dir); }; diff --git a/orchagent/switchorch.cpp b/orchagent/switchorch.cpp index b5aed63adc90..52ef7a24b320 100644 --- a/orchagent/switchorch.cpp +++ b/orchagent/switchorch.cpp @@ -85,6 +85,7 @@ SwitchOrch::SwitchOrch(DBConnector *db, vector& connectors, Tabl set_switch_pfc_dlr_init_capability(); initSensorsTable(); querySwitchTpidCapability(); + querySwitchPortEgressSampleCapability(); auto executorT = new ExecutableTimer(m_sensorsPollerTimer, this, "ASIC_SENSORS_POLL_TIMER"); Orch::addExecutor(executorT); } @@ -731,6 +732,35 @@ void SwitchOrch::set_switch_capability(const std::vector& value m_switchTable.set("switch", values); } +void SwitchOrch::querySwitchPortEgressSampleCapability() +{ + vector fvVector; + sai_status_t status = SAI_STATUS_SUCCESS; + sai_attr_capability_t capability; + + // Check if SAI is capable of handling Port egress sample. + status = sai_query_attribute_capability(gSwitchId, SAI_OBJECT_TYPE_PORT, + SAI_PORT_ATTR_EGRESS_SAMPLEPACKET_ENABLE, &capability); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_WARN("Could not query port egress Sample capability %d", status); + fvVector.emplace_back(SWITCH_CAPABILITY_TABLE_PORT_EGRESS_SAMPLE_CAPABLE, "false"); + } + else + { + if (capability.set_implemented) + { + fvVector.emplace_back(SWITCH_CAPABILITY_TABLE_PORT_EGRESS_SAMPLE_CAPABLE, "true"); + } + else + { + fvVector.emplace_back(SWITCH_CAPABILITY_TABLE_PORT_EGRESS_SAMPLE_CAPABLE, "false"); + } + SWSS_LOG_NOTICE("port egress Sample capability %d", capability.set_implemented); + } + set_switch_capability(fvVector); +} + void SwitchOrch::querySwitchTpidCapability() { SWSS_LOG_ENTER(); diff --git a/orchagent/switchorch.h b/orchagent/switchorch.h index 87e6b1a309cc..cabdb9359d2e 100644 --- a/orchagent/switchorch.h +++ b/orchagent/switchorch.h @@ -12,6 +12,7 @@ #define SWITCH_CAPABILITY_TABLE_LAG_TPID_CAPABLE "LAG_TPID_CAPABLE" #define SWITCH_CAPABILITY_TABLE_ORDERED_ECMP_CAPABLE "ORDERED_ECMP_CAPABLE" #define SWITCH_CAPABILITY_TABLE_PFC_DLR_INIT_CAPABLE "PFC_DLR_INIT_CAPABLE" +#define SWITCH_CAPABILITY_TABLE_PORT_EGRESS_SAMPLE_CAPABLE "PORT_EGRESS_SAMPLE_CAPABLE" struct WarmRestartCheck { @@ -50,6 +51,7 @@ class SwitchOrch : public Orch void doAppSwitchTableTask(Consumer &consumer); void initSensorsTable(); void querySwitchTpidCapability(); + void querySwitchPortEgressSampleCapability(); sai_status_t setSwitchTunnelVxlanParams(swss::FieldValueTuple &val); void setSwitchNonSaiAttributes(swss::FieldValueTuple &val); diff --git a/tests/test_sflow.py b/tests/test_sflow.py index f6ab6a3c1344..25e3a8eaf972 100644 --- a/tests/test_sflow.py +++ b/tests/test_sflow.py @@ -253,6 +253,93 @@ def test_Teardown(self, dvs, testlog): self.cdb.delete_entry("SFLOW", "global") self.adb.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_SAMPLEPACKET", 0) + def test_globalSetSampleDir(self, dvs, testlog): + self.setup_sflow(dvs) + + # Verify that the session is up first + port_oid = self.adb.port_name_map["Ethernet0"] + expected_fields = {"SAI_PORT_ATTR_INGRESS_SAMPLEPACKET_ENABLE": "oid:0x0"} + expected_fields_egr = {"SAI_PORT_ATTR_EGRESS_SAMPLEPACKET_ENABLE": "oid:0x0"} + + self.adb.wait_for_field_negative_match("ASIC_STATE:SAI_OBJECT_TYPE_PORT", port_oid, expected_fields) + + self.cdb.update_entry("SFLOW", "global", {"sample_direction": "both"}) + self.adb.wait_for_field_negative_match("ASIC_STATE:SAI_OBJECT_TYPE_PORT", port_oid, expected_fields) + self.adb.wait_for_field_negative_match("ASIC_STATE:SAI_OBJECT_TYPE_PORT", port_oid, expected_fields_egr) + + self.cdb.update_entry("SFLOW", "global", {"sample_direction": "tx"}) + self.adb.wait_for_field_match("ASIC_STATE:SAI_OBJECT_TYPE_PORT", port_oid, expected_fields) + self.adb.wait_for_field_negative_match("ASIC_STATE:SAI_OBJECT_TYPE_PORT", port_oid, expected_fields_egr) + + self.cdb.delete_entry("SFLOW", "global") + self.adb.wait_for_field_match("ASIC_STATE:SAI_OBJECT_TYPE_PORT", port_oid, expected_fields) + self.adb.wait_for_field_match("ASIC_STATE:SAI_OBJECT_TYPE_PORT", port_oid, expected_fields_egr) + + def test_globalAllSetDir(self, dvs, testlog): + self.setup_sflow(dvs) + # Verify that the session is up first + port_oid = self.adb.port_name_map["Ethernet0"] + self.cdb.update_entry("SFLOW_SESSION", "all", {"sample_direction": "both"}) + expected_fields = {"SAI_PORT_ATTR_INGRESS_SAMPLEPACKET_ENABLE": "oid:0x0"} + expected_fields_egr = {"SAI_PORT_ATTR_EGRESS_SAMPLEPACKET_ENABLE": "oid:0x0"} + self.adb.wait_for_field_negative_match("ASIC_STATE:SAI_OBJECT_TYPE_PORT", port_oid, expected_fields) + self.adb.wait_for_field_negative_match("ASIC_STATE:SAI_OBJECT_TYPE_PORT", port_oid, expected_fields_egr) + + self.cdb.update_entry("SFLOW_SESSION", "all", {"sample_direction": "tx"}) + self.adb.wait_for_field_match("ASIC_STATE:SAI_OBJECT_TYPE_PORT", port_oid, expected_fields) + self.adb.wait_for_field_negative_match("ASIC_STATE:SAI_OBJECT_TYPE_PORT", port_oid, expected_fields_egr) + + self.cdb.delete_entry("SFLOW", "global") + self.adb.wait_for_field_match("ASIC_STATE:SAI_OBJECT_TYPE_PORT", port_oid, expected_fields) + self.adb.wait_for_field_match("ASIC_STATE:SAI_OBJECT_TYPE_PORT", port_oid, expected_fields_egr) + + def test_InterfaceSetDir(self, dvs, testlog): + self.setup_sflow(dvs) + + # Get the global session info as a baseline + port_oid = self.adb.port_name_map["Ethernet0"] + expected_fields = ["SAI_PORT_ATTR_INGRESS_SAMPLEPACKET_ENABLE"] + fvs = self.adb.wait_for_fields("ASIC_STATE:SAI_OBJECT_TYPE_PORT", port_oid, expected_fields) + global_session = fvs["SAI_PORT_ATTR_INGRESS_SAMPLEPACKET_ENABLE"] + + # Then create the interface session + session_params = {"admin_state": "up", "sample_rate": "1000", "sample_direction": "both"} + self.cdb.create_entry("SFLOW_SESSION", "Ethernet0", session_params) + + # Verify that the new interface session has been created and is different from the global one + port_oid = self.adb.port_name_map["Ethernet0"] + expected_fields = {"SAI_PORT_ATTR_INGRESS_SAMPLEPACKET_ENABLE": global_session} + fvs = self.adb.wait_for_field_negative_match("ASIC_STATE:SAI_OBJECT_TYPE_PORT", port_oid, expected_fields) + + expected_fields_egr = {"SAI_PORT_ATTR_EGRESS_SAMPLEPACKET_ENABLE": global_session} + fvs = self.adb.wait_for_field_negative_match("ASIC_STATE:SAI_OBJECT_TYPE_PORT", port_oid, expected_fields_egr) + + local_ing_session = fvs["SAI_PORT_ATTR_INGRESS_SAMPLEPACKET_ENABLE"] + local_egr_session = fvs["SAI_PORT_ATTR_EGRESS_SAMPLEPACKET_ENABLE"] + + self.cdb.update_entry("SFLOW_SESSION", "Ethernet0", {"sample_direction": "tx"}) + + expected_fields = {"SAI_PORT_ATTR_INGRESS_SAMPLEPACKET_ENABLE": "oid:0x0"} + expected_fields_egr = {"SAI_PORT_ATTR_EGRESS_SAMPLEPACKET_ENABLE": local_egr_session} + fvs = self.adb.wait_for_field_match("ASIC_STATE:SAI_OBJECT_TYPE_PORT", port_oid, expected_fields) + fvs = self.adb.wait_for_field_match("ASIC_STATE:SAI_OBJECT_TYPE_PORT", port_oid, expected_fields_egr) + + self.cdb.update_entry("SFLOW_SESSION", "Ethernet0", {"sample_direction": "rx"}) + + expected_fields = {"SAI_PORT_ATTR_INGRESS_SAMPLEPACKET_ENABLE": local_ing_session} + expected_fields_egr = {"SAI_PORT_ATTR_EGRESS_SAMPLEPACKET_ENABLE": "oid:0x0"} + fvs = self.adb.wait_for_field_match("ASIC_STATE:SAI_OBJECT_TYPE_PORT", port_oid, expected_fields) + fvs = self.adb.wait_for_field_match("ASIC_STATE:SAI_OBJECT_TYPE_PORT", port_oid, expected_fields_egr) + + # interface config higher precedence then global/all. Changing all sample-dir should not affect existing interface config + self.cdb.create_entry("SFLOW_SESSION", "all", {"admin_state": "up", "sample_direction": "both"}) + fvs = self.adb.wait_for_field_match("ASIC_STATE:SAI_OBJECT_TYPE_PORT", port_oid, expected_fields) + fvs = self.adb.wait_for_field_match("ASIC_STATE:SAI_OBJECT_TYPE_PORT", port_oid, expected_fields_egr) + + # interface delete will set fallback to all (sample-direction) if enabled. + self.cdb.delete_entry("SFLOW_SESSION", "Ethernet0") + fvs = self.adb.wait_for_field_match("ASIC_STATE:SAI_OBJECT_TYPE_PORT", port_oid, {"SAI_PORT_ATTR_INGRESS_SAMPLEPACKET_ENABLE": local_ing_session}) + fvs = self.adb.wait_for_field_match("ASIC_STATE:SAI_OBJECT_TYPE_PORT", port_oid, {"SAI_PORT_ATTR_INGRESS_SAMPLEPACKET_ENABLE": local_egr_session}) # Add Dummy always-pass test at end as workaroud # for issue when Flaky fail on final test it invokes module tear-down before retrying