diff --git a/orchagent/sfloworch.cpp b/orchagent/sfloworch.cpp index 2ec367b412..295b8a60ce 100644 --- a/orchagent/sfloworch.cpp +++ b/orchagent/sfloworch.cpp @@ -5,11 +5,646 @@ using namespace std; using namespace swss; +extern sai_hostif_api_t* sai_hostif_api; +extern sai_policer_api_t* sai_policer_api; extern sai_samplepacket_api_t* sai_samplepacket_api; +extern sai_switch_api_t* sai_switch_api; +extern sai_tam_api_t* sai_tam_api; extern sai_port_api_t* sai_port_api; extern sai_object_id_t gSwitchId; extern PortsOrch* gPortsOrch; +// TODO: Add the value to copp_cfg.j2 +#define SFLOW_DROP_MONITOR_CPU_QUEUE 47 + +bool SflowDropMonitor::enableDropMonitor(int32_t limit_rate) +{ + if (isEnabled()) + { + if (getLimitRate() == limit_rate) + { + return true; + } + + // Reenable drop monitor when rate limit is changed + disableDropMonitor(); + + return enableDropMonitor(limit_rate); + } + + if (!initializeDropMonitor(limit_rate)) + { + SWSS_LOG_ERROR("Failed to initialize drop monitor."); + cleanupDropMonitor(); + return false; + } + + // Bind the TAM object to switch + { + sai_status_t status; + sai_attribute_t attr; + sai_object_id_t tam = m_tam; + + attr.id = SAI_SWITCH_ATTR_TAM_OBJECT_ID; + attr.value.objlist.count = 1; + attr.value.objlist.list = &tam; + + status = sai_switch_api->set_switch_attribute(gSwitchId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to enable drop monitor when binding the TAM object to switch, rv:%d", status); + cleanupDropMonitor(); + return false; + } + } + + m_limitRate = limit_rate; + m_enable = true; + return true; +} + +bool SflowDropMonitor::disableDropMonitor() +{ + sai_attribute_t attr; + sai_status_t status; + sai_object_id_t null_oid = SAI_NULL_OBJECT_ID; + + if (!isEnabled()) + { + return true; + } + + attr.id = SAI_SWITCH_ATTR_TAM_OBJECT_ID; + attr.value.objlist.count = 0; + attr.value.objlist.list = &null_oid; + status = sai_switch_api->set_switch_attribute(gSwitchId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to disable drop monitor when unbinding the TAM object from switch, rv:%d", status); + return false; + } + + cleanupDropMonitor(); + + m_limitRate = 0; + m_enable = false; + return true; +} + +bool SflowDropMonitor::createTamReport() +{ + sai_status_t status; + sai_attribute_t attr; + vector attributes; + sai_object_id_t tam_report = SAI_NULL_OBJECT_ID; + + if (m_tamReport != SAI_NULL_OBJECT_ID) + { + return false; + } + + attr.id = SAI_TAM_REPORT_ATTR_TYPE; + attr.value.s32 = SAI_TAM_REPORT_TYPE_GENETLINK; + attributes.push_back(attr); + + status = sai_tam_api->create_tam_report(&tam_report, gSwitchId, (uint32_t)attributes.size(), attributes.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create TAM report, rv:%d", status); + return false; + } + + m_tamReport = tam_report; + return true; +} + +bool SflowDropMonitor::removeTamReport() +{ + sai_status_t status; + + if (m_tamReport == SAI_NULL_OBJECT_ID) + { + return true; + } + + status = sai_tam_api->remove_tam_report(m_tamReport); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove TAM report, rv:%d", status); + return false; + } + + m_tamReport = SAI_NULL_OBJECT_ID; + return true; +} + +bool SflowDropMonitor::createTamEventAction() +{ + sai_status_t status; + sai_attribute_t attr; + vector attributes; + sai_object_id_t tam_event_action = SAI_NULL_OBJECT_ID; + + if (m_tamEventAction != SAI_NULL_OBJECT_ID) + { + SWSS_LOG_ERROR("The TAM event action has already been created."); + return false; + } + + if (m_tamReport == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_ERROR("The TAM report must be created before the TAM event action."); + return false; + } + + attr.id = SAI_TAM_EVENT_ACTION_ATTR_REPORT_TYPE; + attr.value.oid = m_tamReport; + attributes.push_back(attr); + status = sai_tam_api->create_tam_event_action(&tam_event_action, gSwitchId, (uint32_t)attributes.size(), attributes.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create TAM event action, rv:%d", status); + return false; + } + + m_tamEventAction = tam_event_action; + return true; +} + +bool SflowDropMonitor::removeTamEventAction() +{ + sai_status_t status; + + if (m_tamEventAction == SAI_NULL_OBJECT_ID) + { + return true; + } + + status = sai_tam_api->remove_tam_event_action(m_tamEventAction); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove TAM event action, rv:%d", status); + return false; + } + + m_tamEventAction = SAI_NULL_OBJECT_ID; + return true; +} + +bool SflowDropMonitor::createTamTransport() +{ + sai_status_t status; + sai_attribute_t attr; + vector attributes; + sai_object_id_t tam_transport = SAI_NULL_OBJECT_ID; + + if (m_tamTransport != SAI_NULL_OBJECT_ID) + { + SWSS_LOG_ERROR("The TAM transport has already been created."); + return false; + } + + attr.id = SAI_TAM_TRANSPORT_ATTR_TRANSPORT_TYPE; + attr.value.s32 = SAI_TAM_TRANSPORT_TYPE_NONE; + attributes.push_back(attr); + + status = sai_tam_api->create_tam_transport(&tam_transport, gSwitchId, (uint32_t)attributes.size(), attributes.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create TAM transport, rv:%d", status); + return false; + } + + m_tamTransport = tam_transport; + return true; +} + +bool SflowDropMonitor::removeTamTransport() +{ + sai_status_t status; + + if (m_tamTransport == SAI_NULL_OBJECT_ID) + { + return true; + } + + status = sai_tam_api->remove_tam_transport(m_tamTransport); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove TAM transport, rv:%d", status); + return false; + } + + m_tamTransport = SAI_NULL_OBJECT_ID; + return true; +} + +bool SflowDropMonitor::createTamEvent() +{ + sai_status_t status; + sai_attribute_t attr; + vector attributes; + sai_object_id_t tam_event = SAI_NULL_OBJECT_ID; + sai_object_id_t tam_collector = m_tamCollector; + sai_object_id_t tam_event_action = m_tamEventAction; + + if (m_tamEvent != SAI_NULL_OBJECT_ID) + { + SWSS_LOG_ERROR("The TAM event has already been created."); + return false; + } + + if (tam_collector == SAI_NULL_OBJECT_ID || tam_event_action == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_ERROR("The TAM collector and TAM event action must be created before the TAM event."); + return false; + } + + attr.id = SAI_TAM_EVENT_ATTR_TYPE; + attr.value.s32 = SAI_TAM_EVENT_TYPE_PACKET_DROP; + attributes.push_back(attr); + attr.id = SAI_TAM_EVENT_ATTR_COLLECTOR_LIST; + attr.value.objlist.count = 1; + attr.value.objlist.list = &tam_collector; + attributes.push_back(attr); + attr.id = SAI_TAM_EVENT_ATTR_ACTION_LIST; + attr.value.objlist.count = 1; + attr.value.objlist.list = &tam_event_action; + attributes.push_back(attr); + + status = sai_tam_api->create_tam_event(&tam_event, gSwitchId, (uint32_t)attributes.size(), attributes.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create TAM event, rv:%d", status); + return false; + } + + m_tamEvent = tam_event; + return true; +} + +bool SflowDropMonitor::removeTamEvent() +{ + sai_status_t status; + + if (m_tamEvent == SAI_NULL_OBJECT_ID) + { + return true; + } + + status = sai_tam_api->remove_tam_event(m_tamEvent); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove TAM event, rv:%d", status); + return false; + } + + m_tamEvent = SAI_NULL_OBJECT_ID; + return true; +} + +bool SflowDropMonitor::createTamCollector() +{ + sai_status_t status; + sai_attribute_t attr; + vector attributes; + sai_object_id_t tam_collector = SAI_NULL_OBJECT_ID; + sai_object_id_t tam_transport = m_tamTransport; + sai_object_id_t hostif_user_defined_trap = m_hostifUserDefinedTrap; + + if (m_tamCollector != SAI_NULL_OBJECT_ID) + { + SWSS_LOG_ERROR("The TAM collector has already been created."); + return false; + } + + if (tam_transport == SAI_NULL_OBJECT_ID || hostif_user_defined_trap == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_ERROR("The TAM transport and HOSTIF user defined trap must be created before the TAM collector."); + return false; + } + + // The SAI_TAM_COLLECTOR_ATTR_LOCALHOST is set as true, however, the + // SAI_TAM_COLLECTOR_ATTR_SRC_IP and SAI_TAM_COLLECTOR_ATTR_DST_IP + // values are still required on creation by SAI definition. + attr.id = SAI_TAM_COLLECTOR_ATTR_SRC_IP; + attr.value.ipaddr.addr_family = SAI_IP_ADDR_FAMILY_IPV4; + attr.value.ipaddr.addr.ip4 = 0x7f000001; // 127.0.0.1 + attributes.push_back(attr); + attr.id = SAI_TAM_COLLECTOR_ATTR_DST_IP; + attr.value.ipaddr.addr_family = SAI_IP_ADDR_FAMILY_IPV4; + attr.value.ipaddr.addr.ip4 = 0x7f000001; // 127.0.0.1 + attributes.push_back(attr); + attr.id = SAI_TAM_COLLECTOR_ATTR_DSCP_VALUE; + attr.value.u8 = 0; + attributes.push_back(attr); + attr.id = SAI_TAM_COLLECTOR_ATTR_TRANSPORT; + attr.value.oid = tam_transport; + attributes.push_back(attr); + attr.id = SAI_TAM_COLLECTOR_ATTR_LOCALHOST; + attr.value.booldata = true; + attributes.push_back(attr); + attr.id = SAI_TAM_COLLECTOR_ATTR_HOSTIF_TRAP; + attr.value.oid = hostif_user_defined_trap; + attributes.push_back(attr); + + status = sai_tam_api->create_tam_collector(&tam_collector, gSwitchId, (uint32_t)attributes.size(), attributes.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create TAM collector, rv:%d", status); + return false; + } + + m_tamCollector = tam_collector; + return true; +} + +bool SflowDropMonitor::removeTamCollector() +{ + sai_status_t status; + + if (m_tamCollector == SAI_NULL_OBJECT_ID) + { + return true; + } + + status = sai_tam_api->remove_tam_collector(m_tamCollector); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove TAM collector, rv:%d", status); + return false; + } + + m_tamCollector = SAI_NULL_OBJECT_ID; + return true; +} + +bool SflowDropMonitor::createTam() +{ + sai_status_t status; + sai_attribute_t attr; + vector attributes; + sai_object_id_t tam = SAI_NULL_OBJECT_ID; + sai_object_id_t tam_event = m_tamEvent; + int32_t bind_point = SAI_TAM_BIND_POINT_TYPE_SWITCH; + + if (m_tam != SAI_NULL_OBJECT_ID) + { + SWSS_LOG_ERROR("The TAM has already been created."); + return false; + } + + if (tam_event == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_ERROR("The TAM event must be created before the TAM."); + return false; + } + + attr.id = SAI_TAM_ATTR_EVENT_OBJECTS_LIST; + attr.value.objlist.count = 1; + attr.value.objlist.list = &tam_event; + attributes.push_back(attr); + attr.id = SAI_TAM_ATTR_TAM_BIND_POINT_TYPE_LIST; + attr.value.s32list.count = 1; + attr.value.s32list.list = &bind_point; + attributes.push_back(attr); + + status = sai_tam_api->create_tam(&tam, gSwitchId, (uint32_t)attributes.size(), attributes.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create TAM, rv:%d", status); + return false; + } + + m_tam = tam; + return true; +} + +bool SflowDropMonitor::removeTam() +{ + sai_status_t status; + + if (m_tam == SAI_NULL_OBJECT_ID) + { + return true; + } + + status = sai_tam_api->remove_tam(m_tam); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove TAM, rv:%d", status); + return false; + } + + m_tam = SAI_NULL_OBJECT_ID; + return true; +} + +bool SflowDropMonitor::createPolicer(int32_t limit_rate) +{ + sai_status_t status; + sai_attribute_t attr; + vector attributes; + sai_object_id_t policer = SAI_NULL_OBJECT_ID; + + if (m_policer != SAI_NULL_OBJECT_ID) + { + SWSS_LOG_ERROR("The policer for sFlow drop monitor has already been created."); + return false; + } + + attr.id = SAI_POLICER_ATTR_METER_TYPE; + attr.value.s32 = SAI_METER_TYPE_PACKETS; + attributes.push_back(attr); + attr.id = SAI_POLICER_ATTR_MODE; + attr.value.s32 = SAI_POLICER_MODE_SR_TCM; + attributes.push_back(attr); + attr.id = SAI_POLICER_ATTR_CIR; + attr.value.u64 = (sai_uint64_t)limit_rate; + attributes.push_back(attr); + attr.id = SAI_POLICER_ATTR_CBS; + attr.value.u64 = (sai_uint64_t)limit_rate; + attributes.push_back(attr); + attr.id = SAI_POLICER_ATTR_GREEN_PACKET_ACTION; + attr.value.s32 = SAI_PACKET_ACTION_FORWARD; + attributes.push_back(attr); + attr.id = SAI_POLICER_ATTR_YELLOW_PACKET_ACTION; + attr.value.s32 = SAI_PACKET_ACTION_DROP; + attributes.push_back(attr); + attr.id = SAI_POLICER_ATTR_RED_PACKET_ACTION; + attr.value.s32 = SAI_PACKET_ACTION_DROP; + attributes.push_back(attr); + + status = sai_policer_api->create_policer(&policer, gSwitchId, (uint32_t)attributes.size(), attributes.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create policer, rv:%d", status); + return false; + } + + m_policer = policer; + return true; +} + +bool SflowDropMonitor::removePolicer() +{ + sai_status_t status; + + if (m_policer == SAI_NULL_OBJECT_ID) + { + return true; + } + + status = sai_policer_api->remove_policer(m_policer); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove policer, rv:%d", status); + return false; + } + + m_policer = SAI_NULL_OBJECT_ID; + return true; +} + +bool SflowDropMonitor::createHostifTrapGroup() +{ + sai_status_t status; + sai_attribute_t attr; + vector attributes; + sai_object_id_t hostif_trap_group = SAI_NULL_OBJECT_ID; + + if (m_hostifTrapGroup != SAI_NULL_OBJECT_ID) + { + SWSS_LOG_ERROR("The HOSTIF trap group has already been created."); + return false; + } + + attr.id = SAI_HOSTIF_TRAP_GROUP_ATTR_QUEUE; + attr.value.u32 = SFLOW_DROP_MONITOR_CPU_QUEUE; + attributes.push_back(attr); + if (m_policer != SAI_NULL_OBJECT_ID) + { + attr.id = SAI_HOSTIF_TRAP_GROUP_ATTR_POLICER; + attr.value.oid = m_policer; + attributes.push_back(attr); + } + status = sai_hostif_api->create_hostif_trap_group(&hostif_trap_group, gSwitchId, (uint32_t)attributes.size(), attributes.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create HOSTIF trap group, rv:%d", status); + return false; + } + + m_hostifTrapGroup = hostif_trap_group; + return true; +} + +bool SflowDropMonitor::removeHostifTrapGroup() +{ + sai_status_t status; + + if (m_hostifTrapGroup == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_ERROR("The HOSTIF trap group does not exist or has already been removed."); + return true; + } + + status = sai_hostif_api->remove_hostif_trap_group(m_hostifTrapGroup); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove HOSTIF trap group, rv:%d", status); + return false; + } + + m_hostifTrapGroup = SAI_NULL_OBJECT_ID; + return true; +} + +bool SflowDropMonitor::createHostifUserDefinedTrap() +{ + sai_status_t status; + sai_attribute_t attr; + vector attributes; + sai_object_id_t hostif_user_defined_trap = SAI_NULL_OBJECT_ID; + sai_object_id_t hostif_trap_group = m_hostifTrapGroup; + + if (m_hostifUserDefinedTrap != SAI_NULL_OBJECT_ID) + { + SWSS_LOG_ERROR("The HOSTIF user defined trap has already been created."); + return false; + } + + if (hostif_trap_group == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_ERROR("The HOSTIF trap group must be created before the HOSTIF user defined trap."); + return false; + } + + attr.id = SAI_HOSTIF_USER_DEFINED_TRAP_ATTR_TRAP_GROUP; + attr.value.oid = hostif_trap_group; + attributes.push_back(attr); + attr.id = SAI_HOSTIF_USER_DEFINED_TRAP_ATTR_TYPE; + attr.value.s32 = SAI_HOSTIF_USER_DEFINED_TRAP_TYPE_TAM; + attributes.push_back(attr); + + status = sai_hostif_api->create_hostif_user_defined_trap(&hostif_user_defined_trap, gSwitchId, (uint32_t)attributes.size(), attributes.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create HOSTIF user defined trap, rv:%d", status); + return false; + } + + m_hostifUserDefinedTrap = hostif_user_defined_trap; + return true; +} + +bool SflowDropMonitor::removeHostifUserDefinedTrap() +{ + sai_status_t status; + + if (m_hostifUserDefinedTrap == SAI_NULL_OBJECT_ID) + { + return true; + } + + status = sai_hostif_api->remove_hostif_user_defined_trap(m_hostifUserDefinedTrap); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove HOSTIF user defined trap, rv:%d", status); + return false; + } + + m_hostifUserDefinedTrap = SAI_NULL_OBJECT_ID; + return true; +} + +bool SflowDropMonitor::initializeDropMonitor(int32_t limit_rate) +{ + return (createTamReport() && + createTamEventAction() && + createTamTransport() && + createPolicer(limit_rate) && + createHostifTrapGroup() && + createHostifUserDefinedTrap() && + createTamCollector() && + createTamEvent() && + createTam()); +} + +void SflowDropMonitor::cleanupDropMonitor() +{ + // Remove all created SAI objects + removeTam(); + removeTamEvent(); + removeTamCollector(); + removeHostifUserDefinedTrap(); + removeHostifTrapGroup(); + removePolicer(); + removeTamTransport(); + removeTamEventAction(); + removeTamReport(); +} + SflowOrch::SflowOrch(DBConnector* db, vector &tableNames) : Orch(db, tableNames) { @@ -291,8 +926,63 @@ void SflowOrch::sflowExtractInfo(vector &fvs, bool &admin, uint } } +void SflowOrch::sflowExtractGlobalInfo(vector &fvs, bool &admin, uint32_t &rate, string &dir, int32_t &drop_monitor_limit) +{ + for (auto i : fvs) + { + if (fvField(i) == "admin_state") + { + if (fvValue(i) == "up") + { + admin = true; + } + else if (fvValue(i) == "down") + { + admin = false; + } + } + else if (fvField(i) == "sample_rate") + { + if (fvValue(i) != "error") + { + rate = (uint32_t)stoul(fvValue(i)); + } + else + { + rate = 0; + } + } + else if (fvField(i) == "sample_direction") + { + if (fvValue(i) != "error") + { + dir = fvValue(i); + } + } + else if (fvField(i) == "drop_monitor_limit") + { + try + { + drop_monitor_limit = stoi(fvValue(i)); + } + catch (...) + { + stringstream err_msg; + err_msg << "Incorrect drop_monitor_limit:" << fvValue(i) << "."; + SWSS_LOG_ERROR("%s", err_msg.str().c_str()); + + drop_monitor_limit = 0; + continue; + } + } + } +} + void SflowOrch::sflowStatusSet(Consumer &consumer) { + bool sflow_status = false; + int32_t sflow_drop_monitor_limit = 0; + auto it = consumer.m_toSync.begin(); while (it != consumer.m_toSync.end()) @@ -304,14 +994,32 @@ void SflowOrch::sflowStatusSet(Consumer &consumer) if (op == SET_COMMAND) { - sflowExtractInfo(kfvFieldsValues(tuple), m_sflowStatus, rate, dir); + sflowExtractGlobalInfo(kfvFieldsValues(tuple), sflow_status, rate, dir, sflow_drop_monitor_limit); } else if (op == DEL_COMMAND) { - m_sflowStatus = false; + sflow_status = false; + sflow_drop_monitor_limit = 0; } it = consumer.m_toSync.erase(it); } + + // Enable, disable or change drop monitor limit when configuration changes + if (m_sflowStatus != sflow_status || + m_sflowDropMonitor.getLimitRate() != sflow_drop_monitor_limit) + { + // Drop monitor only enabled when sFlow is enabled + if (sflow_status && sflow_drop_monitor_limit > 0) + { + m_sflowDropMonitor.enableDropMonitor(sflow_drop_monitor_limit); + } + else + { + m_sflowDropMonitor.disableDropMonitor(); + } + } + + m_sflowStatus = sflow_status; } uint32_t SflowOrch::sflowSessionGetRate(sai_object_id_t m_sample_id) diff --git a/orchagent/sfloworch.h b/orchagent/sfloworch.h index 508b22c0aa..812365bd36 100644 --- a/orchagent/sfloworch.h +++ b/orchagent/sfloworch.h @@ -20,6 +20,79 @@ struct SflowSession uint32_t ref_count; }; +class SflowDropMonitor +{ +public: + SflowDropMonitor() + : m_enable(false) + , m_limitRate(0) + , m_tamReport(SAI_NULL_OBJECT_ID) + , m_tamEventAction(SAI_NULL_OBJECT_ID) + , m_tamTransport(SAI_NULL_OBJECT_ID) + , m_tamEvent(SAI_NULL_OBJECT_ID) + , m_tamCollector(SAI_NULL_OBJECT_ID) + , m_tam(SAI_NULL_OBJECT_ID) + , m_policer(SAI_NULL_OBJECT_ID) + , m_hostifTrapGroup(SAI_NULL_OBJECT_ID) + , m_hostifUserDefinedTrap(SAI_NULL_OBJECT_ID) + { } + + inline bool isEnabled() { + return m_enable; + } + + inline int32_t getLimitRate() { + return m_limitRate; + } + + bool enableDropMonitor(int32_t limit_rate); + bool disableDropMonitor(); + +private: + bool m_enable; + int32_t m_limitRate; // packet per second + + sai_object_id_t m_tamReport; + sai_object_id_t m_tamEventAction; + sai_object_id_t m_tamTransport; + sai_object_id_t m_tamEvent; + sai_object_id_t m_tamCollector; + sai_object_id_t m_tam; + sai_object_id_t m_policer; + sai_object_id_t m_hostifTrapGroup; + sai_object_id_t m_hostifUserDefinedTrap; + + bool createTamReport(); + bool removeTamReport(); + + bool createTamEventAction(); + bool removeTamEventAction(); + + bool createTamTransport(); + bool removeTamTransport(); + + bool createTamEvent(); + bool removeTamEvent(); + + bool createTamCollector(); + bool removeTamCollector(); + + bool createTam(); + bool removeTam(); + + bool createPolicer(int32_t rate); + bool removePolicer(); + + bool createHostifTrapGroup(); + bool removeHostifTrapGroup(); + + bool createHostifUserDefinedTrap(); + bool removeHostifUserDefinedTrap(); + + bool initializeDropMonitor(int32_t limit_rate); + void cleanupDropMonitor(); +}; + /* SAI Port to Sflow Port Info Map */ typedef std::map SflowPortInfoMap; @@ -35,6 +108,8 @@ class SflowOrch : public Orch SflowPortInfoMap m_sflowPortInfoMap; SflowRateSampleMap m_sflowRateSampleMap; bool m_sflowStatus; + SflowDropMonitor m_sflowDropMonitor; + virtual void doTask(Consumer& consumer); bool sflowCreateSession(uint32_t rate, SflowSession &session); @@ -46,5 +121,7 @@ class SflowOrch : public Orch 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, string &dir); + void sflowExtractInfo(vector &fvs, bool &admin, uint32_t &rate, string &dir); + void sflowExtractGlobalInfo(vector &fvs, bool &admin, uint32_t &rate, string &dir, int32_t &drop_monitor_limit); + }; diff --git a/orchagent/switchorch.cpp b/orchagent/switchorch.cpp index aa09f3df80..15320fbfcd 100644 --- a/orchagent/switchorch.cpp +++ b/orchagent/switchorch.cpp @@ -164,6 +164,7 @@ SwitchOrch::SwitchOrch(DBConnector *db, vector& connectors, Tabl initSensorsTable(); querySwitchTpidCapability(); querySwitchPortEgressSampleCapability(); + querySwitchMirrorOnDropCapability(); querySwitchHashDefaults(); setSwitchIcmpOffloadCapability(); @@ -1859,6 +1860,182 @@ void SwitchOrch::querySwitchPortEgressSampleCapability() set_switch_capability(fvVector); } +void SwitchOrch::querySwitchMirrorOnDropCapability() +{ + vector fvVector; + sai_status_t status = SAI_STATUS_SUCCESS; + sai_attr_capability_t capability; + + // Check enum value of SAI_TAM_EVENT_ATTR_TYPE + const auto* meta = sai_metadata_get_attr_metadata(SAI_OBJECT_TYPE_TAM_EVENT, + SAI_TAM_EVENT_ATTR_TYPE); + if (meta == nullptr) + { + SWSS_LOG_WARN("Could not get metadata for SAI_TAM_EVENT_ATTR_TYPE"); + fvVector.emplace_back(SWITCH_CAPABILITY_TABLE_MIRROR_ON_DROP_CAPABLE, "false"); + set_switch_capability(fvVector); + return; + } + + vector values_list(meta->enummetadata->valuescount); + sai_s32_list_t values; + values.count = static_cast(values_list.size()); + values.list = values_list.data(); + + status = sai_query_attribute_enum_values_capability(gSwitchId, + SAI_OBJECT_TYPE_TAM_EVENT, + SAI_TAM_EVENT_ATTR_TYPE, + &values); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_WARN("Could not query TAM Event type capability %d", status); + fvVector.emplace_back(SWITCH_CAPABILITY_TABLE_MIRROR_ON_DROP_CAPABLE, "false"); + set_switch_capability(fvVector); + return; + } + + // check value.list include SAI_TAM_EVENT_TYPE_PACKET_DROP + bool tam_event_type_packet_drop_supported = false; + for (uint32_t i = 0; i < values.count; i++) + { + if (values.list[i] == SAI_TAM_EVENT_TYPE_PACKET_DROP) + { + tam_event_type_packet_drop_supported = true; + break; + } + } + + if (!tam_event_type_packet_drop_supported) + { + SWSS_LOG_WARN("TAM Event type SAI_TAM_EVENT_TYPE_PACKET_DROP not supported"); + fvVector.emplace_back(SWITCH_CAPABILITY_TABLE_MIRROR_ON_DROP_CAPABLE, "false"); + set_switch_capability(fvVector); + return; + } + + // Check enum value of SAI_HOSTIF_USER_DEFINED_TRAP_ATTR_TYPE + meta = sai_metadata_get_attr_metadata(SAI_OBJECT_TYPE_HOSTIF_USER_DEFINED_TRAP, + SAI_HOSTIF_USER_DEFINED_TRAP_ATTR_TYPE); + if (meta == nullptr) + { + SWSS_LOG_WARN("Could not get metadata for SAI_HOSTIF_USER_DEFINED_TRAP_ATTR_TYPE"); + fvVector.emplace_back(SWITCH_CAPABILITY_TABLE_MIRROR_ON_DROP_CAPABLE, "false"); + set_switch_capability(fvVector); + return; + } + + values_list.resize(meta->enummetadata->valuescount); + values.count = static_cast(values_list.size()); + values.list = values_list.data(); + + status = sai_query_attribute_enum_values_capability(gSwitchId, + SAI_OBJECT_TYPE_HOSTIF_USER_DEFINED_TRAP, + SAI_HOSTIF_USER_DEFINED_TRAP_ATTR_TYPE, + &values); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_WARN("Could not query TAM Hostif User Defined Trap type capability %d", status); + fvVector.emplace_back(SWITCH_CAPABILITY_TABLE_MIRROR_ON_DROP_CAPABLE, "false"); + set_switch_capability(fvVector); + return; + } + + // check value.list include SAI_HOSTIF_USER_DEFINED_TRAP_TYPE_TAM + bool hostif_user_defined_trap_type_tam_supported = false; + for (uint32_t i = 0; i < values.count; i++) + { + if (values.list[i] == SAI_HOSTIF_USER_DEFINED_TRAP_TYPE_TAM) + { + hostif_user_defined_trap_type_tam_supported = true; + break; + } + } + + if (!hostif_user_defined_trap_type_tam_supported) + { + SWSS_LOG_WARN("TAM Hostif User Defined Trap type SAI_HOSTIF_USER_DEFINED_TRAP_TYPE_TAM not supported"); + fvVector.emplace_back(SWITCH_CAPABILITY_TABLE_MIRROR_ON_DROP_CAPABLE, "false"); + set_switch_capability(fvVector); + return; + } + + // Check capability for SAI_SWITCH_ATTR_TAM_OBJECT_ID + status = sai_query_attribute_capability(gSwitchId, SAI_OBJECT_TYPE_SWITCH, + SAI_SWITCH_ATTR_TAM_OBJECT_ID, &capability); + if (status != SAI_STATUS_SUCCESS || capability.set_implemented != true) + { + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_WARN("Could not query switch TAM capability %d", status); + } + else + { + SWSS_LOG_WARN("Switch TAM capability not implemented"); + } + fvVector.emplace_back(SWITCH_CAPABILITY_TABLE_MIRROR_ON_DROP_CAPABLE, "false"); + set_switch_capability(fvVector); + return; + } + + // Check capability for SAI_TAM_COLLECTOR_ATTR_HOSTIF_TRAP + status = sai_query_attribute_capability(gSwitchId, SAI_OBJECT_TYPE_TAM_COLLECTOR, + SAI_TAM_COLLECTOR_ATTR_HOSTIF_TRAP, &capability); + if (status != SAI_STATUS_SUCCESS || capability.set_implemented != true) + { + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_WARN("Could not query TAM Collector capability %d", status); + } + else + { + SWSS_LOG_WARN("TAM Collector capability not implemented"); + } + fvVector.emplace_back(SWITCH_CAPABILITY_TABLE_MIRROR_ON_DROP_CAPABLE, "false"); + set_switch_capability(fvVector); + return; + } + + // Check capability for SAI_TAM_ATTR_EVENT_OBJECTS_LIST + status = sai_query_attribute_capability(gSwitchId, SAI_OBJECT_TYPE_TAM_EVENT, + SAI_TAM_ATTR_EVENT_OBJECTS_LIST, &capability); + if (status != SAI_STATUS_SUCCESS || capability.set_implemented != true) + { + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_WARN("Could not query TAM Event capability %d", status); + } + else + { + SWSS_LOG_WARN("TAM Event capability not implemented"); + } + fvVector.emplace_back(SWITCH_CAPABILITY_TABLE_MIRROR_ON_DROP_CAPABLE, "false"); + set_switch_capability(fvVector); + return; + } + + // Check capability for SAI_HOSTIF_USER_DEFINED_TRAP_ATTR_TRAP_GROUP + status = sai_query_attribute_capability(gSwitchId, SAI_OBJECT_TYPE_HOSTIF, + SAI_HOSTIF_USER_DEFINED_TRAP_ATTR_TRAP_GROUP, &capability); + if (status != SAI_STATUS_SUCCESS || capability.set_implemented != true) + { + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_WARN("Could not query TAM Event capability %d", status); + } + else + { + SWSS_LOG_WARN("TAM Event capability not implemented"); + } + fvVector.emplace_back(SWITCH_CAPABILITY_TABLE_MIRROR_ON_DROP_CAPABLE, "false"); + set_switch_capability(fvVector); + return; + } + + fvVector.emplace_back(SWITCH_CAPABILITY_TABLE_MIRROR_ON_DROP_CAPABLE, "true"); + SWSS_LOG_NOTICE("Mirror on drop capability is supported"); + set_switch_capability(fvVector); +} + void SwitchOrch::querySwitchTpidCapability() { SWSS_LOG_ENTER(); diff --git a/orchagent/switchorch.h b/orchagent/switchorch.h index 67a93625c0..93aab5d7dc 100644 --- a/orchagent/switchorch.h +++ b/orchagent/switchorch.h @@ -89,6 +89,7 @@ class SwitchOrch : public Orch void initSensorsTable(); void querySwitchTpidCapability(); void querySwitchPortEgressSampleCapability(); + void querySwitchMirrorOnDropCapability(); // Statistics void generateSwitchCounterNameMap() const; diff --git a/tests/mock_tests/portal.h b/tests/mock_tests/portal.h index cfc78ed45d..2e24e2b1c1 100644 --- a/tests/mock_tests/portal.h +++ b/tests/mock_tests/portal.h @@ -110,6 +110,16 @@ struct Portal { return obj.m_sflowPortInfoMap; } + + static bool getSflowDropMonitorStatusEnable(SflowOrch &obj) + { + return obj.m_sflowDropMonitor.m_enable; + } + + static int32_t getSflowDropMonitorLimitRate(SflowOrch &obj) + { + return obj.m_sflowDropMonitor.m_limitRate; + } }; struct TwampOrchInternal diff --git a/tests/mock_tests/sfloworh_ut.cpp b/tests/mock_tests/sfloworh_ut.cpp index d3d4d0defa..94042af3ef 100644 --- a/tests/mock_tests/sfloworh_ut.cpp +++ b/tests/mock_tests/sfloworh_ut.cpp @@ -367,6 +367,108 @@ namespace sflow_test ASSERT_FALSE(Portal::SflowOrchInternal::getSflowStatusEnable(mock_orch.get())); } } + + /* Test enabling/disabling SFLOW drop monitor */ + TEST_F(SflowOrchTest, SflowDropMonitorEnableDisable) + { + // SFLOW drop monitor only enable when SFLOW is enabled + MockSflowOrch mock_orch; + { + auto table1 = deque( + { + { + "global", + SET_COMMAND, + { + {"admin_state", "down"}, + {"drop_monitor_limit", "100"} + } + } + }); + mock_orch.doSflowTableTask(table1); + + ASSERT_FALSE(Portal::SflowOrchInternal::getSflowStatusEnable(mock_orch.get())); + ASSERT_FALSE(Portal::SflowOrchInternal::getSflowDropMonitorStatusEnable(mock_orch.get())); + } + { + auto table2 = deque( + { + { + "global", + SET_COMMAND, + { + {"admin_state", "up"}, + {"drop_monitor_limit", "100"} + } + } + }); + mock_orch.doSflowTableTask(table2); + + ASSERT_TRUE(Portal::SflowOrchInternal::getSflowStatusEnable(mock_orch.get())); + ASSERT_TRUE(Portal::SflowOrchInternal::getSflowDropMonitorStatusEnable(mock_orch.get())); + } + // Disable SFLOW drop monitor by setting rate limit to 0 + { + auto table3 = deque( + { + { + "global", + SET_COMMAND, + { + {"admin_state", "up"}, + {"drop_monitor_limit", "0"} + } + } + }); + mock_orch.doSflowTableTask(table3); + + ASSERT_TRUE(Portal::SflowOrchInternal::getSflowStatusEnable(mock_orch.get())); + ASSERT_FALSE(Portal::SflowOrchInternal::getSflowDropMonitorStatusEnable(mock_orch.get())); + } + } + + /* Test changing SFLOW drop monitor limit rate */ + TEST_F(SflowOrchTest, SflowDropMonitorChangeLimitRate) + { + MockSflowOrch mock_orch; + { + auto table1 = deque( + { + { + "global", + SET_COMMAND, + { + {"admin_state", "up"}, + {"drop_monitor_limit", "100"} + } + } + }); + mock_orch.doSflowTableTask(table1); + + ASSERT_TRUE(Portal::SflowOrchInternal::getSflowStatusEnable(mock_orch.get())); + ASSERT_TRUE(Portal::SflowOrchInternal::getSflowDropMonitorStatusEnable(mock_orch.get())); + ASSERT_EQ(Portal::SflowOrchInternal::getSflowDropMonitorLimitRate(mock_orch.get()), 100); + } + { + auto table2 = deque( + { + { + "global", + SET_COMMAND, + { + {"admin_state", "up"}, + {"drop_monitor_limit", "200"} + } + } + }); + mock_orch.doSflowTableTask(table2); + + ASSERT_TRUE(Portal::SflowOrchInternal::getSflowStatusEnable(mock_orch.get())); + ASSERT_TRUE(Portal::SflowOrchInternal::getSflowDropMonitorStatusEnable(mock_orch.get())); + ASSERT_EQ(Portal::SflowOrchInternal::getSflowDropMonitorLimitRate(mock_orch.get()), 200); + } + } + }