diff --git a/configure.ac b/configure.ac index 8f14f83c7898..a33396cd3c94 100644 --- a/configure.ac +++ b/configure.ac @@ -39,6 +39,11 @@ esac],[gtest=false]) AM_CONDITIONAL(GTEST, test x$gtest = xtrue) CFLAGS_COMMON="-std=c++11 -Wall -fPIC -Wno-write-strings -I/usr/include/libnl3 -I/usr/include/swss" + +AM_CONDITIONAL(sonic_asic_platform_barefoot, test x$CONFIGURED_PLATFORM = xbarefoot) +AM_COND_IF([sonic_asic_platform_barefoot], + [CFLAGS_COMMON+=" -I/opt/bfn/install/include/switchsai"]) + CFLAGS_COMMON+=" -Werror" CFLAGS_COMMON+=" -Wno-reorder" CFLAGS_COMMON+=" -Wcast-align" diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index d65ff773b980..e2bd73ff4e99 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -7,6 +7,7 @@ swssdir = $(datadir)/swss dist_swss_DATA = \ pfc_detect_mellanox.lua \ pfc_detect_broadcom.lua \ + pfc_detect_barefoot.lua \ pfc_restore.lua bin_PROGRAMS = orchagent routeresync @@ -41,6 +42,7 @@ orchagent_SOURCES = \ request_parser.cpp \ vrforch.cpp \ countercheckorch.cpp \ + dtelorch.cpp \ flexcounterorch.cpp\ acltable.h \ aclorch.h \ @@ -67,6 +69,7 @@ orchagent_SOURCES = \ crmorch.h request_parser.h \ vrforch.h \ + dtelorch.h \ countercheckorch.h \ flexcounterorch.h diff --git a/orchagent/aclorch.cpp b/orchagent/aclorch.cpp index e82d67a32161..cc433a2c7962 100644 --- a/orchagent/aclorch.cpp +++ b/orchagent/aclorch.cpp @@ -46,13 +46,36 @@ acl_rule_attr_lookup_t aclMatchLookup = { MATCH_TC, SAI_ACL_ENTRY_ATTR_FIELD_TC }, { MATCH_L4_SRC_PORT_RANGE, (sai_acl_entry_attr_t)SAI_ACL_RANGE_TYPE_L4_SRC_PORT_RANGE }, { MATCH_L4_DST_PORT_RANGE, (sai_acl_entry_attr_t)SAI_ACL_RANGE_TYPE_L4_DST_PORT_RANGE }, + { MATCH_TUNNEL_VNI, SAI_ACL_ENTRY_ATTR_FIELD_TUNNEL_VNI }, + { MATCH_INNER_ETHER_TYPE, SAI_ACL_ENTRY_ATTR_FIELD_INNER_ETHER_TYPE }, + { MATCH_INNER_IP_PROTOCOL, SAI_ACL_ENTRY_ATTR_FIELD_INNER_IP_PROTOCOL }, + { MATCH_INNER_L4_SRC_PORT, SAI_ACL_ENTRY_ATTR_FIELD_INNER_L4_SRC_PORT }, + { MATCH_INNER_L4_DST_PORT, SAI_ACL_ENTRY_ATTR_FIELD_INNER_L4_DST_PORT } }; acl_rule_attr_lookup_t aclL3ActionLookup = { - { PACKET_ACTION_FORWARD, SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION }, - { PACKET_ACTION_DROP, SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION }, - { PACKET_ACTION_REDIRECT, SAI_ACL_ENTRY_ATTR_ACTION_REDIRECT } + { PACKET_ACTION_FORWARD, SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION }, + { PACKET_ACTION_DROP, SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION }, + { PACKET_ACTION_REDIRECT, SAI_ACL_ENTRY_ATTR_ACTION_REDIRECT } +}; + +acl_rule_attr_lookup_t aclDTelActionLookup = +{ + { ACTION_DTEL_FLOW_OP, SAI_ACL_ENTRY_ATTR_ACTION_ACL_DTEL_FLOW_OP }, + { ACTION_DTEL_INT_SESSION, SAI_ACL_ENTRY_ATTR_ACTION_DTEL_INT_SESSION }, + { ACTION_DTEL_DROP_REPORT_ENABLE, SAI_ACL_ENTRY_ATTR_ACTION_DTEL_DROP_REPORT_ENABLE }, + { ACTION_DTEL_TAIL_DROP_REPORT_ENABLE, SAI_ACL_ENTRY_ATTR_ACTION_DTEL_TAIL_DROP_REPORT_ENABLE }, + { ACTION_DTEL_FLOW_SAMPLE_PERCENT, SAI_ACL_ENTRY_ATTR_ACTION_DTEL_FLOW_SAMPLE_PERCENT }, + { ACTION_DTEL_REPORT_ALL_PACKETS, SAI_ACL_ENTRY_ATTR_ACTION_DTEL_REPORT_ALL_PACKETS } +}; + +acl_dtel_flow_op_type_lookup_t aclDTelFlowOpTypeLookup = +{ + { DTEL_FLOW_OP_NOP, SAI_ACL_DTEL_FLOW_OP_NOP }, + { DTEL_FLOW_OP_POSTCARD, SAI_ACL_DTEL_FLOW_OP_POSTCARD }, + { DTEL_FLOW_OP_INT, SAI_ACL_DTEL_FLOW_OP_INT }, + { DTEL_FLOW_OP_IOAM, SAI_ACL_DTEL_FLOW_OP_IOAM } }; static acl_table_type_lookup_t aclTableTypeLookUp = @@ -60,7 +83,9 @@ static acl_table_type_lookup_t aclTableTypeLookUp = { TABLE_TYPE_L3, ACL_TABLE_L3 }, { TABLE_TYPE_L3V6, ACL_TABLE_L3V6 }, { TABLE_TYPE_MIRROR, ACL_TABLE_MIRROR }, - { TABLE_TYPE_CTRLPLANE, ACL_TABLE_CTRLPLANE } + { TABLE_TYPE_CTRLPLANE, ACL_TABLE_CTRLPLANE }, + { TABLE_TYPE_DTEL_FLOW_WATCHLIST, ACL_TABLE_DTEL_FLOW_WATCHLIST }, + { TABLE_TYPE_DTEL_DROP_WATCHLIST, ACL_TABLE_DTEL_DROP_WATCHLIST } }; static acl_stage_type_lookup_t aclStageLookUp = @@ -266,7 +291,22 @@ bool AclRule::validateAddMatch(string attr_name, string attr_value) value.aclfield.data.u8 = to_uint(attr_value); value.aclfield.mask.u8 = 0xFF; } - + else if (attr_name == MATCH_TUNNEL_VNI) + { + value.aclfield.data.u32 = to_uint(attr_value); + value.aclfield.mask.u32 = 0xFFFFFFFF; + } + else if (attr_name == MATCH_INNER_ETHER_TYPE || attr_name == MATCH_INNER_L4_SRC_PORT || + attr_name == MATCH_INNER_L4_DST_PORT) + { + value.aclfield.data.u16 = to_uint(attr_value); + value.aclfield.mask.u16 = 0xFFFF; + } + else if (attr_name == MATCH_INNER_IP_PROTOCOL) + { + value.aclfield.data.u8 = to_uint(attr_value); + value.aclfield.mask.u8 = 0xFF; + } } catch (exception &e) { @@ -468,7 +508,7 @@ AclRuleCounters AclRule::getCounters() return AclRuleCounters(counter_attr[0].value.u64, counter_attr[1].value.u64); } -shared_ptr AclRule::makeShared(acl_table_type_t type, AclOrch *acl, MirrorOrch *mirror, const string& rule, const string& table, const KeyOpFieldsValuesTuple& data) +shared_ptr AclRule::makeShared(acl_table_type_t type, AclOrch *acl, MirrorOrch *mirror, DTelOrch *dtel, const string& rule, const string& table, const KeyOpFieldsValuesTuple& data) { string action; bool action_found = false; @@ -477,7 +517,12 @@ shared_ptr AclRule::makeShared(acl_table_type_t type, AclOrch *acl, Mir { string attr_name = toUpper(fvField(itr)); string attr_value = fvValue(itr); - if (attr_name == ACTION_PACKET_ACTION || attr_name == ACTION_MIRROR_ACTION) + if (attr_name == ACTION_PACKET_ACTION || attr_name == ACTION_MIRROR_ACTION || + attr_name == ACTION_DTEL_FLOW_OP || attr_name == ACTION_DTEL_INT_SESSION || + attr_name == ACTION_DTEL_DROP_REPORT_ENABLE || + attr_name == ACTION_DTEL_TAIL_DROP_REPORT_ENABLE || + attr_name == ACTION_DTEL_FLOW_SAMPLE_PERCENT || + attr_name == ACTION_DTEL_REPORT_ALL_PACKETS) { action_found = true; action = attr_name; @@ -490,7 +535,7 @@ shared_ptr AclRule::makeShared(acl_table_type_t type, AclOrch *acl, Mir throw runtime_error("ACL rule action is not found in rule " + rule); } - if (type != ACL_TABLE_L3 && type != ACL_TABLE_L3V6 && type != ACL_TABLE_MIRROR) + if (type != ACL_TABLE_L3 && type != ACL_TABLE_L3V6 && type != ACL_TABLE_MIRROR && type != ACL_TABLE_DTEL_FLOW_WATCHLIST && type != ACL_TABLE_DTEL_DROP_WATCHLIST) { throw runtime_error("Unknown table type."); } @@ -515,6 +560,24 @@ shared_ptr AclRule::makeShared(acl_table_type_t type, AclOrch *acl, Mir { return make_shared(acl, rule, table, type); } + else if (type == ACL_TABLE_DTEL_FLOW_WATCHLIST) + { + if (dtel) + { + return make_shared(acl, dtel, rule, table, type); + } else { + throw runtime_error("DTel feature is not enabled. Watchlists cannot be configured"); + } + } + else if (type == ACL_TABLE_DTEL_DROP_WATCHLIST) + { + if (dtel) + { + return make_shared(acl, dtel, rule, table, type); + } else { + throw runtime_error("DTel feature is not enabled. Watchlists cannot be configured"); + } + } throw runtime_error("Wrong combination of table type and action in rule " + rule); } @@ -1213,6 +1276,272 @@ AclRuleCounters AclRuleMirror::getCounters() return cnt; } +AclRuleDTelFlowWatchListEntry::AclRuleDTelFlowWatchListEntry(AclOrch *aclOrch, DTelOrch *dtel, string rule, string table, acl_table_type_t type) : + AclRule(aclOrch, rule, table, type), + m_pDTelOrch(dtel) +{ +} + +bool AclRuleDTelFlowWatchListEntry::validateAddAction(string attr_name, string attr_val) +{ + SWSS_LOG_ENTER(); + + sai_attribute_value_t value; + string attr_value = toUpper(attr_val); + sai_object_id_t session_oid; + + if (!m_pDTelOrch || + (attr_name != ACTION_DTEL_FLOW_OP && + attr_name != ACTION_DTEL_INT_SESSION && + attr_name != ACTION_DTEL_FLOW_SAMPLE_PERCENT && + attr_name != ACTION_DTEL_REPORT_ALL_PACKETS)) + { + return false; + } + + if (attr_name == ACTION_DTEL_FLOW_OP) + { + auto it = aclDTelFlowOpTypeLookup.find(attr_value); + + if (it == aclDTelFlowOpTypeLookup.end()) + { + return false; + } + + value.aclaction.parameter.s32 = it->second; + + if (attr_value == DTEL_FLOW_OP_INT) + { + INT_enabled = true; + } + else + { + INT_enabled = false; + } + } + + if (attr_name == ACTION_DTEL_INT_SESSION) + { + m_intSessionId = attr_value; + + bool ret = m_pDTelOrch->getINTSessionOid(attr_value, session_oid); + if (ret) + { + value.aclaction.parameter.oid = session_oid; + + // Increase session reference count regardless of state to deny + // attempt to remove INT session with attached ACL rules. + if (!m_pDTelOrch->increaseINTSessionRefCount(m_intSessionId)) + { + SWSS_LOG_ERROR("Failed to increase INT session %s reference count", m_intSessionId.c_str()); + return false; + } + + INT_session_valid = true; + } else { + SWSS_LOG_ERROR("Invalid INT session id %s used for ACL action", m_intSessionId.c_str()); + INT_session_valid = false; + } + } + + if (attr_name == ACTION_DTEL_FLOW_SAMPLE_PERCENT) + { + value.aclaction.parameter.u8 = to_uint(attr_value); + } + + value.aclaction.enable = true; + + if (attr_name == ACTION_DTEL_REPORT_ALL_PACKETS) + { + value.aclaction.parameter.booldata = (attr_value == DTEL_ENABLED) ? true : false; + value.aclaction.enable = (attr_value == DTEL_ENABLED) ? true : false; + } + + m_actions[aclDTelActionLookup[attr_name]] = value; + + return true; +} + +bool AclRuleDTelFlowWatchListEntry::validate() +{ + SWSS_LOG_ENTER(); + + if(!m_pDTelOrch) + { + return false; + } + + if (m_matches.size() == 0 || m_actions.size() == 0) + { + return false; + } + + return true; +} + +bool AclRuleDTelFlowWatchListEntry::create() +{ + SWSS_LOG_ENTER(); + + if (!m_pDTelOrch) + { + return false; + } + + if (INT_enabled && !INT_session_valid) + { + return true; + } + + if (!AclRule::create()) + { + return false; + } + + return true; +} + +bool AclRuleDTelFlowWatchListEntry::remove() +{ + if (!m_pDTelOrch) + { + return false; + } + + if (INT_enabled && !INT_session_valid) + { + return true; + } + + if (!AclRule::remove()) + { + return false; + } + + if (INT_enabled && INT_session_valid) + { + if (!m_pDTelOrch->decreaseINTSessionRefCount(m_intSessionId)) + { + SWSS_LOG_ERROR("Could not decrement INT session %s reference count", m_intSessionId.c_str()); + return false; + } + } + + return true; +} + +void AclRuleDTelFlowWatchListEntry::update(SubjectType type, void *cntx) +{ + sai_attribute_value_t value; + sai_object_id_t session_oid = SAI_NULL_OBJECT_ID; + + if (!m_pDTelOrch) + { + return; + } + + if (type != SUBJECT_TYPE_INT_SESSION_CHANGE || !INT_enabled) + { + return; + } + + DTelINTSessionUpdate *update = static_cast(cntx); + + if (m_intSessionId != update->session_id) + { + return; + } + + if (update->active) + { + SWSS_LOG_INFO("Activating INT watchlist %s for session %s", m_id.c_str(), m_intSessionId.c_str()); + + bool ret = m_pDTelOrch->getINTSessionOid(m_intSessionId, session_oid); + if (!ret) + { + SWSS_LOG_ERROR("Invalid INT session id used for ACL action"); + return; + } + + value.aclaction.enable = true; + value.aclaction.parameter.oid = session_oid; + + // Increase session reference count regardless of state to deny + // attempt to remove INT session with attached ACL rules. + if (!m_pDTelOrch->increaseINTSessionRefCount(m_intSessionId)) + { + throw runtime_error("Failed to increase INT session reference count"); + } + + m_actions[SAI_ACL_ENTRY_ATTR_ACTION_DTEL_INT_SESSION] = value; + + INT_session_valid = true; + + create(); + } + else + { + SWSS_LOG_INFO("Deactivating INT watchlist %s for session %s", m_id.c_str(), m_intSessionId.c_str()); + remove(); + INT_session_valid = false; + } +} + +AclRuleDTelDropWatchListEntry::AclRuleDTelDropWatchListEntry(AclOrch *aclOrch, DTelOrch *dtel, string rule, string table, acl_table_type_t type) : + AclRule(aclOrch, rule, table, type), + m_pDTelOrch(dtel) +{ +} + +bool AclRuleDTelDropWatchListEntry::validateAddAction(string attr_name, string attr_val) +{ + SWSS_LOG_ENTER(); + + if (!m_pDTelOrch) + { + return false; + } + + sai_attribute_value_t value; + string attr_value = toUpper(attr_val); + + if (attr_name != ACTION_DTEL_DROP_REPORT_ENABLE && + attr_name != ACTION_DTEL_TAIL_DROP_REPORT_ENABLE) + { + return false; + } + + + value.aclaction.parameter.booldata = (attr_value == DTEL_ENABLED) ? true : false; + value.aclaction.enable = (attr_value == DTEL_ENABLED) ? true : false; + + m_actions[aclDTelActionLookup[attr_name]] = value; + + return true; +} + +bool AclRuleDTelDropWatchListEntry::validate() +{ + SWSS_LOG_ENTER(); + + if (!m_pDTelOrch) + { + return false; + } + + if (m_matches.size() == 0 || m_actions.size() == 0) + { + return false; + } + + return true; +} + +void AclRuleDTelDropWatchListEntry::update(SubjectType, void *) +{ + // Do nothing +} + AclRange::AclRange(sai_acl_range_type_t type, sai_object_id_t oid, int min, int max): m_oid(oid), m_refCnt(0), m_min(min), m_max(max), m_type(type) { @@ -1338,11 +1667,7 @@ bool AclRange::remove() return true; } -AclOrch::AclOrch(vector& connectors, PortsOrch *portOrch, MirrorOrch *mirrorOrch, NeighOrch *neighOrch, RouteOrch *routeOrch) : - Orch(connectors), - m_mirrorOrch(mirrorOrch), - m_neighOrch(neighOrch), - m_routeOrch(routeOrch) +void AclOrch::init(vector& connectors, PortsOrch *portOrch, MirrorOrch *mirrorOrch, NeighOrch *neighOrch, RouteOrch *routeOrch) { SWSS_LOG_ENTER(); @@ -1373,19 +1698,57 @@ AclOrch::AclOrch(vector& connectors, PortsOrch *portOrch, Mirror timer->start(); } +AclOrch::AclOrch(vector& connectors, PortsOrch *portOrch, MirrorOrch *mirrorOrch, NeighOrch *neighOrch, RouteOrch *routeOrch) : + Orch(connectors), + m_mirrorOrch(mirrorOrch), + m_neighOrch(neighOrch), + m_routeOrch(routeOrch), + m_dTelOrch(NULL) +{ + SWSS_LOG_ENTER(); + + init(connectors, portOrch, mirrorOrch, neighOrch, routeOrch); +} + +AclOrch::AclOrch(vector& connectors, PortsOrch *portOrch, MirrorOrch *mirrorOrch, NeighOrch *neighOrch, RouteOrch *routeOrch, DTelOrch *dtelOrch) : + Orch(connectors), + m_mirrorOrch(mirrorOrch), + m_neighOrch(neighOrch), + m_routeOrch(routeOrch), + m_dTelOrch(dtelOrch) +{ + SWSS_LOG_ENTER(); + + init(connectors, portOrch, mirrorOrch, neighOrch, routeOrch); + + if (m_dTelOrch) + { + m_dTelOrch->attach(this); + } + + createDTelWatchListTables(); +} + AclOrch::~AclOrch() { m_mirrorOrch->detach(this); + if(m_dTelOrch) + { + m_dTelOrch->detach(this); + } + m_bCollectCounters = false; m_sleepGuard.notify_all(); + + deleteDTelWatchListTables(); } void AclOrch::update(SubjectType type, void *cntx) { SWSS_LOG_ENTER(); - if (type != SUBJECT_TYPE_MIRROR_SESSION_CHANGE) + if (type != SUBJECT_TYPE_MIRROR_SESSION_CHANGE && type != SUBJECT_TYPE_INT_SESSION_CHANGE) { return; } @@ -1649,7 +2012,7 @@ void AclOrch::doAclRuleTask(Consumer &consumer) continue; } - newRule = AclRule::makeShared(m_AclTables[table_oid].type, this, m_mirrorOrch, rule_id, table_id, t); + newRule = AclRule::makeShared(m_AclTables[table_oid].type, this, m_mirrorOrch, m_dTelOrch, rule_id, table_id, t); for (const auto& itr : kfvFieldsValues(t)) { @@ -1978,3 +2341,203 @@ sai_status_t AclOrch::bindAclTable(sai_object_id_t table_oid, AclTable &aclTable return status; } + +sai_status_t AclOrch::createDTelWatchListTables() +{ + SWSS_LOG_ENTER(); + + AclTable flowWLTable, dropWLTable; + sai_object_id_t table_oid; + + sai_status_t status; + sai_attribute_t attr; + vector table_attrs; + + /* Create Flow watchlist ACL table */ + + flowWLTable.id = TABLE_TYPE_DTEL_FLOW_WATCHLIST; + flowWLTable.type = ACL_TABLE_DTEL_FLOW_WATCHLIST; + flowWLTable.description = "Dataplane Telemetry Flow Watchlist table"; + + attr.id = SAI_ACL_TABLE_ATTR_ACL_STAGE; + attr.value.s32 = SAI_ACL_STAGE_INGRESS; + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_ACL_BIND_POINT_TYPE_LIST; + vector bpoint_list; + bpoint_list.push_back(SAI_ACL_BIND_POINT_TYPE_SWITCH); + attr.value.s32list.count = 1; + attr.value.s32list.list = bpoint_list.data(); + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_FIELD_ETHER_TYPE; + attr.value.booldata = true; + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_FIELD_SRC_IP; + attr.value.booldata = true; + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_FIELD_DST_IP; + attr.value.booldata = true; + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_FIELD_L4_SRC_PORT; + attr.value.booldata = true; + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_FIELD_L4_DST_PORT; + attr.value.booldata = true; + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_FIELD_IP_PROTOCOL; + attr.value.booldata = true; + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_FIELD_TUNNEL_VNI; + attr.value.booldata = true; + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_FIELD_INNER_ETHER_TYPE; + attr.value.booldata = true; + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_FIELD_INNER_SRC_IP; + attr.value.booldata = true; + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_FIELD_INNER_DST_IP; + attr.value.booldata = true; + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_ACL_ACTION_TYPE_LIST; + int32_t acl_action_list[4]; + acl_action_list[0] = SAI_ACL_ACTION_TYPE_ACL_DTEL_FLOW_OP; + acl_action_list[1] = SAI_ACL_ACTION_TYPE_DTEL_INT_SESSION; + acl_action_list[2] = SAI_ACL_ACTION_TYPE_DTEL_REPORT_ALL_PACKETS; + acl_action_list[3] = SAI_ACL_ACTION_TYPE_DTEL_FLOW_SAMPLE_PERCENT; + attr.value.s32list.count = 4; + attr.value.s32list.list = acl_action_list; + table_attrs.push_back(attr); + + status = sai_acl_api->create_acl_table(&table_oid, gSwitchId, (uint32_t)table_attrs.size(), table_attrs.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create table %s", flowWLTable.description.c_str()); + return status; + } + + m_AclTables[table_oid] = flowWLTable; + SWSS_LOG_INFO("Successfully created ACL table %s, oid: %lX", flowWLTable.description.c_str(), table_oid); + + /* Create Drop watchlist ACL table */ + + table_attrs.clear(); + + dropWLTable.id = TABLE_TYPE_DTEL_DROP_WATCHLIST; + dropWLTable.type = ACL_TABLE_DTEL_DROP_WATCHLIST; + dropWLTable.description = "Dataplane Telemetry Drop Watchlist table"; + + attr.id = SAI_ACL_TABLE_ATTR_ACL_STAGE; + attr.value.s32 = SAI_ACL_STAGE_INGRESS; + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_ACL_BIND_POINT_TYPE_LIST; + bpoint_list.clear(); + bpoint_list.push_back(SAI_ACL_BIND_POINT_TYPE_SWITCH); + attr.value.s32list.count = 1; + attr.value.s32list.list = bpoint_list.data(); + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_FIELD_ETHER_TYPE; + attr.value.booldata = true; + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_FIELD_SRC_IP; + attr.value.booldata = true; + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_FIELD_DST_IP; + attr.value.booldata = true; + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_FIELD_L4_SRC_PORT; + attr.value.booldata = true; + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_FIELD_L4_DST_PORT; + attr.value.booldata = true; + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_FIELD_IP_PROTOCOL; + attr.value.booldata = true; + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_ACL_ACTION_TYPE_LIST; + acl_action_list[0] = SAI_ACL_ACTION_TYPE_DTEL_DROP_REPORT_ENABLE; + acl_action_list[1] = SAI_ACL_ACTION_TYPE_DTEL_TAIL_DROP_REPORT_ENABLE; + attr.value.s32list.count = 2; + attr.value.s32list.list = acl_action_list; + table_attrs.push_back(attr); + + status = sai_acl_api->create_acl_table(&table_oid, gSwitchId, (uint32_t)table_attrs.size(), table_attrs.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create table %s", dropWLTable.description.c_str()); + return status; + } + + m_AclTables[table_oid] = dropWLTable; + SWSS_LOG_INFO("Successfully created ACL table %s, oid: %lX", dropWLTable.description.c_str(), table_oid); + + return status; +} + +sai_status_t AclOrch::deleteDTelWatchListTables() +{ + SWSS_LOG_ENTER(); + + AclTable flowWLTable, dropWLTable; + sai_object_id_t table_oid; + string table_id = TABLE_TYPE_DTEL_FLOW_WATCHLIST; + + sai_status_t status; + + table_oid = getTableById(table_id); + + if (table_oid == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_INFO("Failed to find ACL table %s", table_id.c_str()); + return SAI_STATUS_FAILURE; + } + + status = sai_acl_api->remove_acl_table(table_oid); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to delete table %s", table_id.c_str()); + return status; + } + + m_AclTables.erase(table_oid); + + table_id = TABLE_TYPE_DTEL_DROP_WATCHLIST; + + table_oid = getTableById(table_id); + + if (table_oid == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_INFO("Failed to find ACL table %s", table_id.c_str()); + return SAI_STATUS_FAILURE; + } + + status = sai_acl_api->remove_acl_table(table_oid); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to delete table %s", table_id.c_str()); + return status; + } + + m_AclTables.erase(table_oid); + + return SAI_STATUS_SUCCESS; +} diff --git a/orchagent/aclorch.h b/orchagent/aclorch.h index 31c8f3d356ce..990848d5626e 100644 --- a/orchagent/aclorch.h +++ b/orchagent/aclorch.h @@ -11,6 +11,7 @@ #include "orch.h" #include "portsorch.h" #include "mirrororch.h" +#include "dtelorch.h" #include "observer.h" // ACL counters update interval in the DB @@ -27,6 +28,8 @@ #define TABLE_TYPE_MIRROR "MIRROR" #define TABLE_TYPE_PFCWD "PFCWD" #define TABLE_TYPE_CTRLPLANE "CTRLPLANE" +#define TABLE_TYPE_DTEL_FLOW_WATCHLIST "DTEL_FLOW_WATCHLIST" +#define TABLE_TYPE_DTEL_DROP_WATCHLIST "DTEL_DROP_WATCHLIST" #define RULE_PRIORITY "PRIORITY" #define MATCH_SRC_IP "SRC_IP" @@ -43,14 +46,33 @@ #define MATCH_L4_SRC_PORT_RANGE "L4_SRC_PORT_RANGE" #define MATCH_L4_DST_PORT_RANGE "L4_DST_PORT_RANGE" #define MATCH_TC "TC" +#define MATCH_TUNNEL_VNI "TUNNEL_VNI" +#define MATCH_INNER_ETHER_TYPE "INNER_ETHER_TYPE" +#define MATCH_INNER_IP_PROTOCOL "INNER_IP_PROTOCOL" +#define MATCH_INNER_L4_SRC_PORT "INNER_L4_SRC_PORT" +#define MATCH_INNER_L4_DST_PORT "INNER_L4_DST_PORT" #define ACTION_PACKET_ACTION "PACKET_ACTION" #define ACTION_MIRROR_ACTION "MIRROR_ACTION" +#define ACTION_DTEL_FLOW_OP "FLOW_OP" +#define ACTION_DTEL_INT_SESSION "INT_SESSION" +#define ACTION_DTEL_DROP_REPORT_ENABLE "DROP_REPORT_ENABLE" +#define ACTION_DTEL_TAIL_DROP_REPORT_ENABLE "TAIL_DROP_REPORT_ENABLE" +#define ACTION_DTEL_FLOW_SAMPLE_PERCENT "FLOW_SAMPLE_PERCENT" +#define ACTION_DTEL_REPORT_ALL_PACKETS "REPORT_ALL_PACKETS" #define PACKET_ACTION_FORWARD "FORWARD" #define PACKET_ACTION_DROP "DROP" #define PACKET_ACTION_REDIRECT "REDIRECT" +#define DTEL_FLOW_OP_NOP "NOP" +#define DTEL_FLOW_OP_POSTCARD "POSTCARD" +#define DTEL_FLOW_OP_INT "INT" +#define DTEL_FLOW_OP_IOAM "IOAM" + +#define DTEL_ENABLED "TRUE" +#define DTEL_DISABLED "FALSE" + #define IP_TYPE_ANY "ANY" #define IP_TYPE_IP "IP" #define IP_TYPE_NON_IP "NON_IP" @@ -71,12 +93,15 @@ typedef enum ACL_TABLE_L3V6, ACL_TABLE_MIRROR, ACL_TABLE_PFCWD, - ACL_TABLE_CTRLPLANE + ACL_TABLE_CTRLPLANE, + ACL_TABLE_DTEL_FLOW_WATCHLIST, + ACL_TABLE_DTEL_DROP_WATCHLIST } acl_table_type_t; typedef map acl_table_type_lookup_t; typedef map acl_rule_attr_lookup_t; typedef map acl_ip_type_lookup_t; +typedef map acl_dtel_flow_op_type_lookup_t; typedef tuple acl_range_properties_t; class AclOrch; @@ -163,7 +188,7 @@ class AclRule return m_counterOid; } - static shared_ptr makeShared(acl_table_type_t type, AclOrch *acl, MirrorOrch *mirror, const string& rule, const string& table, const KeyOpFieldsValuesTuple&); + static shared_ptr makeShared(acl_table_type_t type, AclOrch *acl, MirrorOrch *mirror, DTelOrch *dtel, const string& rule, const string& table, const KeyOpFieldsValuesTuple&); virtual ~AclRule() {} protected: @@ -236,6 +261,35 @@ class AclRuleMirror: public AclRule MirrorOrch *m_pMirrorOrch; }; +class AclRuleDTelFlowWatchListEntry: public AclRule +{ +public: + AclRuleDTelFlowWatchListEntry(AclOrch *m_pAclOrch, DTelOrch *m_pDTelOrch, string rule, string table, acl_table_type_t type); + bool validateAddAction(string attr_name, string attr_value); + bool validate(); + bool create(); + bool remove(); + void update(SubjectType, void *); + +protected: + DTelOrch *m_pDTelOrch; + string m_intSessionId; + bool INT_enabled; + bool INT_session_valid; +}; + +class AclRuleDTelDropWatchListEntry: public AclRule +{ +public: + AclRuleDTelDropWatchListEntry(AclOrch *m_pAclOrch, DTelOrch *m_pDTelOrch, string rule, string table, acl_table_type_t type); + bool validateAddAction(string attr_name, string attr_value); + bool validate(); + void update(SubjectType, void *); + +protected: + DTelOrch *m_pDTelOrch; +}; + class AclTable { sai_object_id_t m_oid; public: @@ -299,6 +353,7 @@ class AclOrch : public Orch, public Observer { public: AclOrch(vector& connectors, PortsOrch *portOrch, MirrorOrch *mirrorOrch, NeighOrch *neighOrch, RouteOrch *routeOrch); + AclOrch(vector& connectors, PortsOrch *portOrch, MirrorOrch *mirrorOrch, NeighOrch *neighOrch, RouteOrch *routeOrch, DTelOrch *m_dTelOrch); ~AclOrch(); void update(SubjectType, void *); @@ -313,6 +368,7 @@ class AclOrch : public Orch, public Observer MirrorOrch *m_mirrorOrch; NeighOrch *m_neighOrch; RouteOrch *m_routeOrch; + DTelOrch *m_dTelOrch; bool addAclTable(AclTable &aclTable, string table_id); bool removeAclTable(string table_id); @@ -325,6 +381,7 @@ class AclOrch : public Orch, public Observer void doAclRuleTask(Consumer &consumer); void doAclTablePortUpdateTask(Consumer &consumer); void doTask(SelectableTimer &timer); + void init(vector& connectors, PortsOrch *portOrch, MirrorOrch *mirrorOrch, NeighOrch *neighOrch, RouteOrch *routeOrch); static void collectCountersThread(AclOrch *pAclOrch); @@ -337,6 +394,8 @@ class AclOrch : public Orch, public Observer bool processPorts(AclTable &aclTable, string portsList, std::function inserter); bool processPendingPort(AclTable &aclTable, string portAlias, std::function inserter); bool validateAclTable(AclTable &aclTable); + sai_status_t createDTelWatchListTables(); + sai_status_t deleteDTelWatchListTables(); //vector m_AclTables; map m_AclTables; diff --git a/orchagent/dtelorch.cpp b/orchagent/dtelorch.cpp new file mode 100644 index 000000000000..52b55cd841fd --- /dev/null +++ b/orchagent/dtelorch.cpp @@ -0,0 +1,1407 @@ +#include "dtelorch.h" + +#include "logger.h" +#include "schema.h" +#include "converter.h" +#include "ipprefix.h" +#include "swssnet.h" + +using namespace std; +using namespace swss; + +extern sai_switch_api_t* sai_switch_api; +extern sai_dtel_api_t* sai_dtel_api; +extern sai_object_id_t gVirtualRouterId; +extern sai_object_id_t gSwitchId; + +dtelEventLookup_t dTelEventLookup = +{ + { EVENT_TYPE_FLOW_STATE, SAI_DTEL_EVENT_TYPE_FLOW_STATE }, + { EVENT_TYPE_FLOW_REPORT_ALL_PACKETS, SAI_DTEL_EVENT_TYPE_FLOW_REPORT_ALL_PACKETS }, + { EVENT_TYPE_FLOW_TCPFLAG, SAI_DTEL_EVENT_TYPE_FLOW_TCPFLAG }, + { EVENT_TYPE_QUEUE_REPORT_THRESHOLD_BREACH, SAI_DTEL_EVENT_TYPE_QUEUE_REPORT_THRESHOLD_BREACH }, + { EVENT_TYPE_QUEUE_REPORT_TAIL_DROP, SAI_DTEL_EVENT_TYPE_QUEUE_REPORT_TAIL_DROP }, + { EVENT_TYPE_DROP_REPORT, SAI_DTEL_EVENT_TYPE_DROP_REPORT } +}; + +DTelOrch::DTelOrch(DBConnector *db, vector tableNames, PortsOrch *portOrch) : + Orch(db, tableNames), + m_portOrch(portOrch) +{ + SWSS_LOG_ENTER(); + sai_attribute_t attr; + + sai_status_t status = sai_dtel_api->create_dtel(&dtelId, gSwitchId, 0, {}); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Error creating DTel id"); + return; + } + + attr.id = SAI_DTEL_ATTR_INT_L4_DSCP; + + attr.value.aclfield.data.u8 = 0x11; + attr.value.aclfield.mask.u8 = 0x3f; + + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to set default INT L4 DSCP value/mask"); + return; + } +} + +DTelOrch::~DTelOrch() +{ + sai_status_t status = sai_dtel_api->remove_dtel(dtelId); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Error deleting DTel id"); + return; + } +} + +bool DTelOrch::intSessionExists(const string& name) +{ + SWSS_LOG_ENTER(); + + return m_dTelINTSessionTable.find(name) != m_dTelINTSessionTable.end(); +} + +bool DTelOrch::getINTSessionOid(const string& name, sai_object_id_t& oid) +{ + SWSS_LOG_ENTER(); + + if (!intSessionExists(name)) + { + return false; + } + + oid = m_dTelINTSessionTable[name].intSessionOid; + + return true; +} + +bool DTelOrch::increaseINTSessionRefCount(const string& name) +{ + SWSS_LOG_ENTER(); + + if (!intSessionExists(name)) + { + SWSS_LOG_ERROR("DTEL ERROR: INT session does not exist"); + return false; + } + + ++m_dTelINTSessionTable[name].refCount; + + return true; +} + +bool DTelOrch::decreaseINTSessionRefCount(const string& name) +{ + SWSS_LOG_ENTER(); + + if (!intSessionExists(name)) + { + SWSS_LOG_ERROR("DTEL ERROR: INT session does not exist"); + return false; + } + + if (m_dTelINTSessionTable[name].refCount < 0) + { + SWSS_LOG_ERROR("DTEL ERROR: Session reference counter could not be less than 0"); + return false; + } + + --m_dTelINTSessionTable[name].refCount; + + if(m_dTelINTSessionTable[name].refCount == 0) + { + deleteINTSession(name); + } + + return true; +} + +int64_t DTelOrch::getINTSessionRefCount(const string& name) +{ + if (!intSessionExists(name)) + { + SWSS_LOG_ERROR("DTEL ERROR: INT session does not exist"); + return -1; + } + + return m_dTelINTSessionTable[name].refCount; +} + +bool DTelOrch::reportSessionExists(const string& name) +{ + SWSS_LOG_ENTER(); + + return m_dTelReportSessionTable.find(name) != m_dTelReportSessionTable.end(); +} + +bool DTelOrch::getReportSessionOid(const string& name, sai_object_id_t& oid) +{ + SWSS_LOG_ENTER(); + + if (!reportSessionExists(name)) + { + SWSS_LOG_ERROR("DTEL ERROR: Report session does not exist"); + return false; + } + + oid = m_dTelReportSessionTable[name].reportSessionOid; + + return true; +} + +bool DTelOrch::increaseReportSessionRefCount(const string& name) +{ + SWSS_LOG_ENTER(); + + if (!reportSessionExists(name)) + { + SWSS_LOG_ERROR("DTEL ERROR: Report session does not exist"); + return false; + } + + ++m_dTelReportSessionTable[name].refCount; + + return true; +} + +bool DTelOrch::decreaseReportSessionRefCount(const string& name) +{ + SWSS_LOG_ENTER(); + + if (!reportSessionExists(name)) + { + SWSS_LOG_ERROR("DTEL ERROR: Report session does not exist"); + return false; + } + + if (m_dTelReportSessionTable[name].refCount < 0) + { + SWSS_LOG_ERROR("DTEL ERROR: Session reference counter could not be less than 0"); + return false; + } + + --m_dTelReportSessionTable[name].refCount; + + if (m_dTelReportSessionTable[name].refCount == 0) + { + deleteReportSession(name); + } + + return true; +} + +int64_t DTelOrch::getReportSessionRefCount(const string& name) +{ + if (!reportSessionExists(name)) + { + SWSS_LOG_ERROR("DTEL ERROR: Report session does not exist"); + return -1; + } + + return m_dTelReportSessionTable[name].refCount; +} + +bool DTelOrch::isQueueReportEnabled(const string& port, const string& queue) +{ + SWSS_LOG_ENTER(); + + auto port_entry_iter = m_dTelPortTable.find(port); + + if (port_entry_iter == m_dTelPortTable.end() || + (port_entry_iter->second.queueTable).find(queue) == (port_entry_iter->second.queueTable).end()) + { + return false; + } + + return true; +} + +bool DTelOrch::getQueueReportOid(const string& port, const string& queue, sai_object_id_t& oid) +{ + SWSS_LOG_ENTER(); + + if (!isQueueReportEnabled(port, queue)) + { + SWSS_LOG_ERROR("DTEL ERROR: Queue report not enabled on port %s, queue %s", port.c_str(), queue.c_str()); + return false; + } + + oid = m_dTelPortTable[port].queueTable[queue].queueReportOid; + + return true; +} + +bool DTelOrch::removePortQueue(const string& port, const string& queue) +{ + if (!isQueueReportEnabled(port, queue)) + { + SWSS_LOG_ERROR("DTEL ERROR: Queue report not enabled on port %s, queue %s", port.c_str(), queue.c_str()); + return false; + } + + m_dTelPortTable[port].queueTable.erase(queue); + + if (m_dTelPortTable[port].queueTable.empty()) + { + m_dTelPortTable.erase(port); + } + + return true; +} + +bool DTelOrch::addPortQueue(const string& port, const string& queue, DTelQueueReportEntry **qreport) +{ + if (isQueueReportEnabled(port, queue)) + { + SWSS_LOG_ERROR("DTEL ERROR: Queue report already enabled on port %s, queue %s", port.c_str(), queue.c_str()); + return false; + } + + m_dTelPortTable[port] = DTelPortEntry(); + m_dTelPortTable[port].queueTable[queue] = DTelQueueReportEntry(); + *qreport = &m_dTelPortTable[port].queueTable[queue]; + return true; +} + +bool DTelOrch::isEventConfigured(const string& event) +{ + SWSS_LOG_ENTER(); + + if (m_dtelEventTable.find(event) == m_dtelEventTable.end()) + { + return false; + } + + return true; +} + +bool DTelOrch::getEventOid(const string& event, sai_object_id_t& oid) +{ + SWSS_LOG_ENTER(); + + if (!isEventConfigured(event)) + { + SWSS_LOG_ERROR("DTEL ERROR: Event not configured %s", event.c_str()); + return false; + } + + oid = m_dtelEventTable[event].eventOid; + + return true; +} + +void DTelOrch::addEvent(const string& event, const sai_object_id_t& event_oid, const string& report_session_id) +{ + if (isEventConfigured(event)) + { + SWSS_LOG_ERROR("DTEL ERROR: Event is already configured %s", event.c_str()); + return; + } + + DTelEventEntry event_entry; + event_entry.eventOid = event_oid; + event_entry.reportSessionId = report_session_id; + m_dtelEventTable[event] = event_entry; + + increaseReportSessionRefCount(report_session_id); +} + +void DTelOrch::removeEvent(const string& event) +{ + if (!isEventConfigured(event)) + { + SWSS_LOG_ERROR("DTEL ERROR: Event not configured %s", event.c_str()); + return; + } + + string& report_session_id = m_dtelEventTable[event].reportSessionId; + + decreaseReportSessionRefCount(report_session_id); + + m_dtelEventTable.erase(event); +} + +sai_status_t DTelOrch::updateSinkPortList() +{ + sai_attribute_t attr; + attr.id = SAI_DTEL_ATTR_SINK_PORT_LIST; + sai_status_t status = SAI_STATUS_SUCCESS; + + vector port_list; + + for (auto it = sinkPortList.begin(); it != sinkPortList.end(); it++) + { + if (it->second != 0) + { + port_list.push_back(it->second); + } + } + + attr.value.objlist.count = (uint32_t)port_list.size(); + if (port_list.size() == 0) + { + attr.value.objlist.list = {}; + } else { + attr.value.objlist.list = port_list.data(); + } + + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to set INT sink port list"); + return status; + } + + return status; +} + +bool DTelOrch::addSinkPortToCache(const Port& port) +{ + if (sinkPortList.find(port.m_alias) == sinkPortList.end()) + { + return false; + } + + if (port.m_type != Port::PHY) + { + SWSS_LOG_ERROR("DTEL ERROR: Only physical ports supported as INT sink. %s is not a physical port", port.m_alias.c_str()); + return false; + } + + sinkPortList[port.m_alias] = port.m_port_id; + return true; +} + +bool DTelOrch::removeSinkPortFromCache(const string &port_alias) +{ + if (sinkPortList.find(port_alias) == sinkPortList.end()) + { + return false; + } + + sinkPortList[port_alias] = 0; + return true; +} + +void DTelOrch::update(SubjectType type, void *cntx) +{ + sai_status_t status; + + if (type != SUBJECT_TYPE_PORT_CHANGE) + { + return; + } + + PortUpdate *update = static_cast(cntx); + + /* Check if sink ports need to be updated */ + if (update->add) + { + if (addSinkPortToCache(update->port)) + { + status = updateSinkPortList(); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to update sink port list on port add"); + return; + } + } + } else { + if (removeSinkPortFromCache(update->port.m_alias)) + { + status = updateSinkPortList(); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to update sink port list on port remove"); + return; + } + } + } + + /* Check if queue reports need to be updated */ + auto port_entry_iter = m_dTelPortTable.find(update->port.m_alias); + + if (port_entry_iter == m_dTelPortTable.end()) + { + return; + } + + dTelPortQueueTable_t qTable = port_entry_iter->second.queueTable; + + for (auto it = qTable.begin(); it != qTable.end(); it++ ) + { + DTelQueueReportEntry qreport = it->second; + if (update->add) + { + if (qreport.queueReportOid != 0) + { + SWSS_LOG_ERROR("DTEL ERROR: Queue report already enabled for port %s, queue %d", update->port.m_alias.c_str(), qreport.q_ind); + return; + } + + if (update->port.m_type != Port::PHY) + { + SWSS_LOG_ERROR("DTEL ERROR: Queue reporting applies only to physical ports. %s is not a physical port", update->port.m_alias.c_str()); + return; + } + + qreport.queueOid = update->port.m_queue_ids[qreport.q_ind]; + + status = enableQueueReport(update->port.m_alias, qreport); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to update queue report for queue %d on port add %s", qreport.q_ind, update->port.m_alias.c_str()); + return; + } + } else { + if (qreport.queueReportOid == 0) + { + SWSS_LOG_ERROR("DTEL ERROR: Queue report already disabled for port %s, queue %d", update->port.m_alias.c_str(), qreport.q_ind); + return; + } + + status = disableQueueReport(update->port.m_alias, it->first); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to update queue report for queue %d on port remove %s", qreport.q_ind, update->port.m_alias.c_str()); + return; + } + + qreport.queueOid = 0; + } + } +} + +void DTelOrch::doDtelTableTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + sai_attribute_t attr; + sai_status_t status; + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + string table_attr = kfvKey(t); + string op = kfvOp(t); + + if (op == SET_COMMAND) + { + if (table_attr == INT_ENDPOINT) + { + FieldValueTuple e = kfvFieldsValues(t)[0]; + + attr.id = SAI_DTEL_ATTR_INT_ENDPOINT_ENABLE; + attr.value.booldata = (fvValue(e) == ENABLED) ? true : false; + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to enable INT endpoint mode"); + goto dtel_table_continue; + } + } + else if (table_attr == INT_TRANSIT) + { + FieldValueTuple e = kfvFieldsValues(t)[0]; + + attr.id = SAI_DTEL_ATTR_INT_TRANSIT_ENABLE; + attr.value.booldata = (fvValue(e) == ENABLED) ? true : false; + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to enable INT transit mode"); + goto dtel_table_continue; + } + } + else if (table_attr == POSTCARD) + { + FieldValueTuple e = kfvFieldsValues(t)[0]; + + attr.id = SAI_DTEL_ATTR_POSTCARD_ENABLE; + attr.value.booldata = (fvValue(e) == ENABLED) ? true : false; + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to enable DTel postcard"); + goto dtel_table_continue; + } + } + else if (table_attr == DROP_REPORT) + { + FieldValueTuple e = kfvFieldsValues(t)[0]; + + attr.id = SAI_DTEL_ATTR_DROP_REPORT_ENABLE; + attr.value.booldata = (fvValue(e) == ENABLED) ? true : false; + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to enable drop report"); + goto dtel_table_continue; + } + } + else if (table_attr == QUEUE_REPORT) + { + FieldValueTuple e = kfvFieldsValues(t)[0]; + + attr.id = SAI_DTEL_ATTR_QUEUE_REPORT_ENABLE; + attr.value.booldata = (fvValue(e) == ENABLED) ? true : false; + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to enable queue report"); + goto dtel_table_continue; + } + } + else if (table_attr == SWITCH_ID) + { + FieldValueTuple e = kfvFieldsValues(t)[0]; + + attr.id = SAI_DTEL_ATTR_SWITCH_ID; + attr.value.u32 = to_uint(fvValue(e)); + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to set switch id"); + goto dtel_table_continue; + } + } + else if (table_attr == FLOW_STATE_CLEAR_CYCLE) + { + FieldValueTuple e = kfvFieldsValues(t)[0]; + + attr.id = SAI_DTEL_ATTR_FLOW_STATE_CLEAR_CYCLE; + attr.value.u16 = to_uint(fvValue(e)); + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to set Dtel flow state clear cycle"); + goto dtel_table_continue; + } + } + else if (table_attr == LATENCY_SENSITIVITY) + { + FieldValueTuple e = kfvFieldsValues(t)[0]; + + attr.id = SAI_DTEL_ATTR_LATENCY_SENSITIVITY; + attr.value.u16 = to_uint(fvValue(e)); + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to set Dtel latency sensitivity"); + goto dtel_table_continue; + } + } + else if (table_attr == SINK_PORT_LIST) + { + Port port; + + for (auto i : kfvFieldsValues(t)) + { + if (m_portOrch->getPort(fvField(i), port)) + { + if (port.m_type != Port::PHY) + { + SWSS_LOG_ERROR("DTEL ERROR: Only physical ports supported as INT sink. %s is not a physical port", fvField(i).c_str()); + goto dtel_table_continue; + } + + sinkPortList[fvField(i)] = port.m_port_id; + } else { + SWSS_LOG_ERROR("DTEL ERROR: Port to be added a sink port %s doesn't exist", fvField(i).c_str()); + sinkPortList[fvField(i)] = 0; + } + } + + if (sinkPortList.size() == 0) + { + SWSS_LOG_ERROR("DTEL ERROR: Invalid INT sink port list"); + goto dtel_table_continue; + } + + status = updateSinkPortList(); + if (status != SAI_STATUS_SUCCESS) + { + goto dtel_table_continue; + } + } + else if (table_attr == INT_L4_DSCP) + { + attr.id = SAI_DTEL_ATTR_INT_L4_DSCP; + + if ((uint32_t)(kfvFieldsValues(t).size()) != 2) + { + SWSS_LOG_ERROR("DTEL ERROR: INT L4 DSCP attribute requires value and mask"); + goto dtel_table_continue; + } + + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == INT_L4_DSCP_VALUE) + { + attr.value.aclfield.data.u8 = to_uint(fvValue(i)); + } + else if (fvField(i) == INT_L4_DSCP_MASK) + { + attr.value.aclfield.mask.u8 = to_uint(fvValue(i)); + } + } + + if (attr.value.aclfield.data.u8 == 0 || + attr.value.aclfield.mask.u8 == 0) + { + SWSS_LOG_ERROR("DTEL ERROR: Invalid INT L4 DSCP value/mask"); + goto dtel_table_continue; + } + + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to set INT L4 DSCP value/mask"); + goto dtel_table_continue; + } + } + + } + else if (op == DEL_COMMAND) + { + if (table_attr == INT_ENDPOINT) + { + attr.id = SAI_DTEL_ATTR_INT_ENDPOINT_ENABLE; + attr.value.booldata = false; + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to disable INT endpoint mode"); + goto dtel_table_continue; + } + } + else if (table_attr == INT_TRANSIT) + { + attr.id = SAI_DTEL_ATTR_INT_TRANSIT_ENABLE; + attr.value.booldata = false; + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to disable INT transit mode"); + goto dtel_table_continue; + } + } + else if (table_attr == POSTCARD) + { + attr.id = SAI_DTEL_ATTR_POSTCARD_ENABLE; + attr.value.booldata = false; + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to disable postcard mode"); + goto dtel_table_continue; + } + } + else if (table_attr == DROP_REPORT) + { + attr.id = SAI_DTEL_ATTR_DROP_REPORT_ENABLE; + attr.value.booldata = false; + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to disable drop report"); + goto dtel_table_continue; + } + } + else if (table_attr == QUEUE_REPORT) + { + attr.id = SAI_DTEL_ATTR_QUEUE_REPORT_ENABLE; + attr.value.booldata = false; + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to disable queue report"); + goto dtel_table_continue; + } + } + else if (table_attr == SWITCH_ID) + { + auto i = kfvFieldsValues(t); + + attr.id = SAI_DTEL_ATTR_SWITCH_ID; + attr.value.u32 = 0; + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to reset switch id"); + goto dtel_table_continue; + } + } + else if (table_attr == FLOW_STATE_CLEAR_CYCLE) + { + auto i = kfvFieldsValues(t); + + attr.id = SAI_DTEL_ATTR_FLOW_STATE_CLEAR_CYCLE; + attr.value.u16 = 0; + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to reset flow state clear cycle"); + goto dtel_table_continue; + } + } + else if (table_attr == LATENCY_SENSITIVITY) + { + auto i = kfvFieldsValues(t); + + attr.id = SAI_DTEL_ATTR_LATENCY_SENSITIVITY; + attr.value.u16 = 0; + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to reset latency sensitivity"); + goto dtel_table_continue; + } + } + else if (table_attr == SINK_PORT_LIST) + { + sinkPortList.clear(); + status = updateSinkPortList(); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to reset sink port list"); + goto dtel_table_continue; + } + } + else if (table_attr == INT_L4_DSCP) + { + attr.id = SAI_DTEL_ATTR_INT_L4_DSCP; + attr.value.aclfield.data.u8 = 0; + attr.value.aclfield.mask.u8 = 0; + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to reset INT L4 DSCP value/mask"); + goto dtel_table_continue; + } + } + } + +dtel_table_continue: + it = consumer.m_toSync.erase(it); + } +} + +bool DTelOrch::deleteReportSession(const string &report_session_id) +{ + sai_object_id_t report_session_oid; + sai_status_t status; + + if (!reportSessionExists(report_session_id)) + { + SWSS_LOG_ERROR("DTEL ERROR: Report session %s does not exist", report_session_id.c_str()); + return false; + } + + if (!getReportSessionOid(report_session_id, report_session_oid)) + { + SWSS_LOG_ERROR("DTEL ERROR: Could not get report session oid for session %s", report_session_id.c_str()); + return false; + } + + status = sai_dtel_api->remove_dtel_report_session(report_session_oid); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to delete report session %s", report_session_id.c_str()); + return false; + } + + m_dTelReportSessionTable.erase(report_session_id); + return true; +} + +void DTelOrch::doDtelReportSessionTableTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + sai_status_t status; + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + sai_object_id_t report_session_oid; + + KeyOpFieldsValuesTuple t = it->second; + string report_session_id = kfvKey(t); + + string op = kfvOp(t); + + if (op == SET_COMMAND) + { + vector report_session_attr; + vector dst_ip_list; + sai_ip_address_t dst_ip; + sai_attribute_t rs_attr; + + /* If report session already exists, delete it first */ + if (reportSessionExists(report_session_id)) + { + if (!deleteReportSession(report_session_id)) + { + goto report_session_table_continue; + } + } + + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == SRC_IP) + { + IpAddress ip(fvValue(i)); + rs_attr.id = SAI_DTEL_REPORT_SESSION_ATTR_SRC_IP; + copy(rs_attr.value.ipaddr, ip); + report_session_attr.push_back(rs_attr); + } + else if (fvField(i) == DST_IP_LIST) + { + dst_ip.addr_family = SAI_IP_ADDR_FAMILY_IPV4; + size_t prev = 0; + size_t next = fvValue(i).find(';'); + while (next != std::string::npos) + { + IpAddress ip(fvValue(i).substr(prev, next - prev)); + copy(dst_ip, ip); + dst_ip_list.push_back(dst_ip); + prev = next + 1; + next = fvValue(i).find(';', prev); + } + + /* Add the last IP */ + IpAddress ip(fvValue(i).substr(prev)); + copy(dst_ip, ip); + dst_ip_list.push_back(dst_ip); + + rs_attr.id = SAI_DTEL_REPORT_SESSION_ATTR_DST_IP_LIST; + rs_attr.value.ipaddrlist.count = (uint32_t)dst_ip_list.size(); + rs_attr.value.ipaddrlist.list = dst_ip_list.data(); + report_session_attr.push_back(rs_attr); + } + else if (fvField(i) == VRF) + { + rs_attr.id = SAI_DTEL_REPORT_SESSION_ATTR_VIRTUAL_ROUTER_ID; + /* TODO: find a way to convert vrf to oid */ + rs_attr.value.oid = gVirtualRouterId; + report_session_attr.push_back(rs_attr); + } + else if (fvField(i) == TRUNCATE_SIZE) + { + rs_attr.id = SAI_DTEL_REPORT_SESSION_ATTR_TRUNCATE_SIZE; + rs_attr.value.u16 = to_uint(fvValue(i)); + report_session_attr.push_back(rs_attr); + } + else if (fvField(i) == UDP_DEST_PORT) + { + rs_attr.id = SAI_DTEL_REPORT_SESSION_ATTR_UDP_DST_PORT; + rs_attr.value.u16 = to_uint(fvValue(i)); + report_session_attr.push_back(rs_attr); + } + } + + status = sai_dtel_api->create_dtel_report_session(&report_session_oid, + gSwitchId, (uint32_t)report_session_attr.size(), report_session_attr.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to set INT EP report session %s", report_session_id.c_str()); + goto report_session_table_continue; + } + + DTelReportSessionEntry rs_entry = { }; + rs_entry.reportSessionOid = report_session_oid; + m_dTelReportSessionTable[report_session_id] = rs_entry; + } + else if (op == DEL_COMMAND) + { + if (!reportSessionExists(report_session_id)) + { + goto report_session_table_continue; + } + + if(getReportSessionRefCount(report_session_id) > 1) + { + decreaseReportSessionRefCount(report_session_id); + goto report_session_table_continue; + } + + if (!deleteReportSession(report_session_id)) + { + goto report_session_table_continue; + } + } + +report_session_table_continue: + it = consumer.m_toSync.erase(it); + } +} + +bool DTelOrch::deleteINTSession(const string &int_session_id) +{ + sai_object_id_t int_session_oid; + sai_status_t status; + + if (!intSessionExists(int_session_id)) + { + SWSS_LOG_ERROR("DTEL ERROR: INT session %s does not exist", int_session_id.c_str()); + return false; + } + + if (!getINTSessionOid(int_session_id, int_session_oid)) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to get INT session oid %s", int_session_id.c_str()); + return false; + } + + status = sai_dtel_api->remove_dtel_int_session(int_session_oid); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to delete INT session %s", int_session_id.c_str()); + return false; + } + + m_dTelINTSessionTable.erase(int_session_id); + + /* Notify all interested parties about INT session being deleted */ + DTelINTSessionUpdate update = {int_session_id, false}; + notify(SUBJECT_TYPE_INT_SESSION_CHANGE, static_cast(&update)); + return true; +} + +void DTelOrch::doDtelINTSessionTableTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + sai_status_t status; + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + sai_object_id_t int_session_oid; + + KeyOpFieldsValuesTuple t = it->second; + string int_session_id = kfvKey(t); + + string op = kfvOp(t); + + if (op == SET_COMMAND) + { + vector int_session_attr; + sai_attribute_t s_attr; + + /* If INT session already exists, delete it first */ + if (intSessionExists(int_session_id)) + { + if (!deleteINTSession(int_session_id)) + { + goto int_session_table_continue; + } + } + + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == COLLECT_SWITCH_ID) + { + s_attr.id = SAI_DTEL_INT_SESSION_ATTR_COLLECT_SWITCH_ID; + s_attr.value.booldata = (fvValue(i) == ENABLED) ? true : false; + int_session_attr.push_back(s_attr); + } + else if (fvField(i) == COLLECT_INGRESS_TIMESTAMP) + { + s_attr.id = SAI_DTEL_INT_SESSION_ATTR_COLLECT_INGRESS_TIMESTAMP; + s_attr.value.booldata = (fvValue(i) == ENABLED) ? true : false; + int_session_attr.push_back(s_attr); + } + else if (fvField(i) == COLLECT_EGRESS_TIMESTAMP) + { + s_attr.id = SAI_DTEL_INT_SESSION_ATTR_COLLECT_EGRESS_TIMESTAMP; + s_attr.value.booldata = (fvValue(i) == ENABLED) ? true : false; + int_session_attr.push_back(s_attr); + } + else if (fvField(i) == COLLECT_SWITCH_PORTS) + { + s_attr.id = SAI_DTEL_INT_SESSION_ATTR_COLLECT_SWITCH_PORTS; + s_attr.value.booldata = (fvValue(i) == ENABLED) ? true : false; + int_session_attr.push_back(s_attr); + } + else if (fvField(i) == COLLECT_QUEUE_INFO) + { + s_attr.id = SAI_DTEL_INT_SESSION_ATTR_COLLECT_QUEUE_INFO; + s_attr.value.booldata = (fvValue(i) == ENABLED) ? true : false; + int_session_attr.push_back(s_attr); + } + else if (fvField(i) == MAX_HOP_COUNT) + { + s_attr.id = SAI_DTEL_INT_SESSION_ATTR_MAX_HOP_COUNT; + s_attr.value.u16 = to_uint(fvValue(i)); + int_session_attr.push_back(s_attr); + } + } + + status = sai_dtel_api->create_dtel_int_session(&int_session_oid, + gSwitchId, (uint32_t)int_session_attr.size(), int_session_attr.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to set INT session %s", int_session_id.c_str()); + goto int_session_table_continue; + } + + DTelINTSessionEntry is_entry; + is_entry.intSessionOid = int_session_oid; + m_dTelINTSessionTable[int_session_id] = is_entry; + + /* Notify all interested parties about INT session being added */ + DTelINTSessionUpdate update = {int_session_id, true}; + notify(SUBJECT_TYPE_INT_SESSION_CHANGE, static_cast(&update)); + } + else if (op == DEL_COMMAND) + { + if (!intSessionExists(int_session_id)) + { + goto int_session_table_continue; + } + + if(getINTSessionRefCount(int_session_id) > 1) + { + decreaseINTSessionRefCount(int_session_id); + goto int_session_table_continue; + } + + if (!deleteINTSession(int_session_id)) + { + goto int_session_table_continue; + } + } + +int_session_table_continue: + it = consumer.m_toSync.erase(it); + } +} + +bool DTelOrch::disableQueueReport(const string &port, const string &queue) +{ + sai_object_id_t queue_report_oid; + sai_status_t status; + + if (!isQueueReportEnabled(port, queue)) + { + SWSS_LOG_ERROR("DTEL ERROR: queue report not enabled for port %s, queue %s", port.c_str(), queue.c_str()); + return false; + } + + if (!getQueueReportOid(port, queue, queue_report_oid)) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to get queue report oid for port %s, queue %s", port.c_str(), queue.c_str()); + return false; + } + + if (queue_report_oid == 0) + { + return true; + } + + status = sai_dtel_api->remove_dtel_queue_report(queue_report_oid); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to disable queue report for port %s, queue %s", port.c_str(), queue.c_str()); + return false; + } + + m_dTelPortTable[port].queueTable[queue].queueReportOid = 0; + + return true; +} + +sai_status_t DTelOrch::enableQueueReport(const string& port, DTelQueueReportEntry& qreport) +{ + sai_attribute_t qr_attr; + sai_status_t status = SAI_STATUS_SUCCESS; + + if (qreport.queueOid == 0) + { + qreport.queueReportOid = 0; + return status; + } + + qr_attr.id = SAI_DTEL_QUEUE_REPORT_ATTR_QUEUE_ID; + qr_attr.value.oid = qreport.queueOid; + qreport.queue_report_attr.push_back(qr_attr); + + status = sai_dtel_api->create_dtel_queue_report(&qreport.queueReportOid, + gSwitchId, (uint32_t)qreport.queue_report_attr.size(), qreport.queue_report_attr.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to enable queue report on port %s, queue %d", port.c_str(), qreport.q_ind); + return status; + } + + return status; +} + +void DTelOrch::doDtelQueueReportTableTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + sai_status_t status; + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + string key = kfvKey(t); + size_t found = key.find('|'); + string port = key.substr(0, found); + string queue_id = key.substr(found + 1); + Port port_obj; + uint32_t q_ind = stoi(queue_id); + string op = kfvOp(t); + + if (op == SET_COMMAND) + { + vector queue_report_attr; + sai_attribute_t qr_attr; + DTelQueueReportEntry *qreport; + + /* If queue report is already enabled in port/queue, disable it first */ + if (isQueueReportEnabled(port, queue_id)) + { + if (!disableQueueReport(port, queue_id)) + { + goto queue_report_table_continue; + } + + if (!removePortQueue(port, queue_id)) + { + goto queue_report_table_continue; + } + } + + if (!addPortQueue(port, queue_id, &qreport)) + { + goto queue_report_table_continue; + } + + qreport->q_ind = q_ind; + + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == REPORT_TAIL_DROP) + { + qr_attr.id = SAI_DTEL_QUEUE_REPORT_ATTR_TAIL_DROP; + qr_attr.value.booldata = (fvValue(i) == ENABLED) ? true : false; + qreport->queue_report_attr.push_back(qr_attr); + } + else if (fvField(i) == QUEUE_DEPTH_THRESHOLD) + { + qr_attr.id = SAI_DTEL_QUEUE_REPORT_ATTR_DEPTH_THRESHOLD; + qr_attr.value.u32 = to_uint(fvValue(i)); + qreport->queue_report_attr.push_back(qr_attr); + } + else if (fvField(i) == QUEUE_LATENCY_THRESHOLD) + { + qr_attr.id = SAI_DTEL_QUEUE_REPORT_ATTR_LATENCY_THRESHOLD; + qr_attr.value.u32 = to_uint(fvValue(i)); + qreport->queue_report_attr.push_back(qr_attr); + } + else if (fvField(i) == THRESHOLD_BREACH_QUOTA) + { + qr_attr.id = SAI_DTEL_QUEUE_REPORT_ATTR_BREACH_QUOTA; + qr_attr.value.u32 = to_uint(fvValue(i)); + qreport->queue_report_attr.push_back(qr_attr); + } + } + + if (m_portOrch->getPort(port, port_obj)) + { + if (port_obj.m_type != Port::PHY) + { + SWSS_LOG_ERROR("DTEL ERROR: Queue reporting applies only to physical ports. %s is not a physical port", port.c_str()); + goto queue_report_table_continue; + } + + qreport->queueOid = port_obj.m_queue_ids[qreport->q_ind]; + } else { + SWSS_LOG_ERROR("DTEL ERROR: Port for queue reporting %s doesn't exist", port.c_str()); + qreport->queueOid = 0; + } + + status = enableQueueReport(port, *qreport); + if (status != SAI_STATUS_SUCCESS) + { + goto queue_report_table_continue; + } + } + else if (op == DEL_COMMAND) + { + if (!disableQueueReport(port, queue_id)) + { + goto queue_report_table_continue; + } + + if (!removePortQueue(port, queue_id)) + { + goto queue_report_table_continue; + } + } + +queue_report_table_continue: + it = consumer.m_toSync.erase(it); + } +} + +bool DTelOrch::unConfigureEvent(string &event) +{ + sai_object_id_t event_oid; + sai_status_t status; + + if (!isEventConfigured(event)) + { + SWSS_LOG_ERROR("DTEL ERROR: Event is not configured %s", event.c_str()); + return false; + } + + if (!getEventOid(event, event_oid)) + { + SWSS_LOG_ERROR("DTEL ERROR: Could not get event oid for event %s", event.c_str()); + return false; + } + + status = sai_dtel_api->remove_dtel_event(event_oid); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to remove event %s", event.c_str()); + return false; + } + + removeEvent(event); + return true; +} + +void DTelOrch::doDtelEventTableTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + sai_status_t status; + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + sai_object_id_t event_oid; + string report_session_id; + + KeyOpFieldsValuesTuple t = it->second; + string event = kfvKey(t); + + string op = kfvOp(t); + + if (op == SET_COMMAND) + { + vector event_attr; + sai_attribute_t e_attr; + e_attr.id = SAI_DTEL_EVENT_ATTR_TYPE; + e_attr.value.s32 = dTelEventLookup[event]; + event_attr.push_back(e_attr); + + /* If event is already configured, un-configure it first */ + if (isEventConfigured(event)) + { + if (!unConfigureEvent(event)) + { + goto event_table_continue; + } + } + + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == EVENT_REPORT_SESSION) + { + if (!reportSessionExists(fvValue(i))) + { + goto event_table_skip; + } + + e_attr.id = SAI_DTEL_EVENT_ATTR_REPORT_SESSION; + + if (!getReportSessionOid(fvValue(i), e_attr.value.oid)) + { + SWSS_LOG_ERROR("DTEL ERROR: Could not get report session oid for event %s, session %s", fvValue(i).c_str(), event.c_str()); + goto event_table_continue; + } + event_attr.push_back(e_attr); + report_session_id = fvValue(i); + } + else if (fvField(i) == EVENT_DSCP_VALUE) + { + e_attr.id = SAI_DTEL_EVENT_ATTR_DSCP_VALUE; + e_attr.value.u8 = to_uint(fvValue(i)); + event_attr.push_back(e_attr); + } + } + + status = sai_dtel_api->create_dtel_event(&event_oid, + gSwitchId, (uint32_t)event_attr.size(), event_attr.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to create event %s", event.c_str()); + goto event_table_continue; + } + + addEvent(event, event_oid, report_session_id); + } + else if (op == DEL_COMMAND) + { + if (!unConfigureEvent(event)) + { + goto event_table_continue; + } + } + +event_table_continue: + it = consumer.m_toSync.erase(it); + continue; + +event_table_skip: + it++; + } +} + +void DTelOrch::doTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + string table_name = consumer.getTableName(); + + if (table_name == CFG_DTEL_TABLE_NAME) + { + doDtelTableTask(consumer); + } + else if (table_name == CFG_DTEL_REPORT_SESSION_TABLE_NAME) + { + doDtelReportSessionTableTask(consumer); + } + else if (table_name == CFG_DTEL_INT_SESSION_TABLE_NAME) + { + doDtelINTSessionTableTask(consumer); + } + else if (table_name == CFG_DTEL_QUEUE_REPORT_TABLE_NAME) + { + doDtelQueueReportTableTask(consumer); + } + else if (table_name == CFG_DTEL_EVENT_TABLE_NAME) + { + doDtelEventTableTask(consumer); + } + else + { + SWSS_LOG_ERROR("DTEL ERROR: Invalid table %s", table_name.c_str()); + } +} diff --git a/orchagent/dtelorch.h b/orchagent/dtelorch.h new file mode 100644 index 000000000000..0a0cd2d6b1f8 --- /dev/null +++ b/orchagent/dtelorch.h @@ -0,0 +1,174 @@ +#ifndef SWSS_DTELORCH_H +#define SWSS_DTELORCH_H + +#include "orch.h" +#include "producerstatetable.h" +#include "portsorch.h" + +#include +#include + +#define INT_ENDPOINT "INT_ENDPOINT" +#define INT_TRANSIT "INT_TRANSIT" +#define POSTCARD "POSTCARD" +#define DROP_REPORT "DROP_REPORT" +#define QUEUE_REPORT "QUEUE_REPORT" +#define SWITCH_ID "SWITCH_ID" +#define FLOW_STATE_CLEAR_CYCLE "FLOW_STATE_CLEAR_CYCLE" +#define LATENCY_SENSITIVITY "LATENCY_SENSITIVITY" +#define SINK_PORT_LIST "SINK_PORT_LIST" +#define INT_L4_DSCP "INT_L4_DSCP" +#define INT_L4_DSCP_VALUE "INT_L4_DSCP_VALUE" +#define INT_L4_DSCP_MASK "INT_L4_DSCP_MASK" +#define SRC_IP "SRC_IP" +#define DST_IP_LIST "DST_IP_LIST" +#define VRF "VRF" +#define TRUNCATE_SIZE "TRUNCATE_SIZE" +#define UDP_DEST_PORT "UDP_DEST_PORT" +#define MAX_HOP_COUNT "MAX_HOP_COUNT" +#define COLLECT_SWITCH_ID "COLLECT_SWITCH_ID" +#define COLLECT_INGRESS_TIMESTAMP "COLLECT_INGRESS_TIMESTAMP" +#define COLLECT_EGRESS_TIMESTAMP "COLLECT_EGRESS_TIMESTAMP" +#define COLLECT_SWITCH_PORTS "COLLECT_SWITCH_PORTS" +#define COLLECT_QUEUE_INFO "COLLECT_QUEUE_INFO" +#define QUEUE_DEPTH_THRESHOLD "QUEUE_DEPTH_THRESHOLD" +#define QUEUE_LATENCY_THRESHOLD "QUEUE_LATENCY_THRESHOLD" +#define THRESHOLD_BREACH_QUOTA "THRESHOLD_BREACH_QUOTA" +#define REPORT_TAIL_DROP "REPORT_TAIL_DROP" +#define EVENT_REPORT_SESSION "EVENT_REPORT_SESSION" +#define EVENT_DSCP_VALUE "EVENT_DSCP_VALUE" +#define EVENT_TYPE_FLOW_STATE "EVENT_TYPE_FLOW_STATE" +#define EVENT_TYPE_FLOW_REPORT_ALL_PACKETS "EVENT_TYPE_FLOW_REPORT_ALL_PACKETS" +#define EVENT_TYPE_FLOW_TCPFLAG "EVENT_TYPE_FLOW_TCPFLAG" +#define EVENT_TYPE_QUEUE_REPORT_THRESHOLD_BREACH "EVENT_TYPE_QUEUE_REPORT_THRESHOLD_BREACH" +#define EVENT_TYPE_QUEUE_REPORT_TAIL_DROP "EVENT_TYPE_QUEUE_REPORT_TAIL_DROP" +#define EVENT_TYPE_DROP_REPORT "EVENT_TYPE_DROP_REPORT" + +#define ENABLED "TRUE" +#define DISABLED "FALSE" + +struct DTelINTSessionEntry +{ + sai_object_id_t intSessionOid; + int64_t refCount; + + DTelINTSessionEntry() : + intSessionOid(0), + refCount(1) + { + } +}; + +struct DTelReportSessionEntry +{ + sai_object_id_t reportSessionOid; + int64_t refCount; + + DTelReportSessionEntry() : + reportSessionOid(0), + refCount(1) + { + } +}; + +struct DTelQueueReportEntry +{ + sai_object_id_t queueReportOid; + vector queue_report_attr; + uint32_t q_ind; + sai_object_id_t queueOid; + + DTelQueueReportEntry() : + queueReportOid(0), + q_ind(0) + { + } +}; + +typedef map dTelPortQueueTable_t; + +struct DTelPortEntry +{ + dTelPortQueueTable_t queueTable; +}; + +struct DTelEventEntry +{ + sai_object_id_t eventOid; + string reportSessionId; + + DTelEventEntry() : + eventOid(0), + reportSessionId("") + { + } +}; + + +typedef map dTelINTSessionTable_t; +typedef map dTelReportSessionTable_t; +typedef map dTelPortTable_t; +typedef map dtelEventTable_t; +typedef map dtelEventLookup_t; +typedef map dtelSinkPortList_t; + +struct DTelINTSessionUpdate +{ + string session_id; + bool active; +}; + +class DTelOrch : public Orch, public Subject +{ +public: + DTelOrch(DBConnector *db, vector tableNames, PortsOrch *portOrch); + ~DTelOrch(); + + bool increaseINTSessionRefCount(const string&); + bool decreaseINTSessionRefCount(const string&); + bool getINTSessionOid(const string& name, sai_object_id_t& oid); + void update(SubjectType, void *); + +private: + + bool intSessionExists(const string& name); + void doTask(Consumer &consumer); + void doDtelTableTask(Consumer &consumer); + void doDtelReportSessionTableTask(Consumer &consumer); + void doDtelINTSessionTableTask(Consumer &consumer); + void doDtelQueueReportTableTask(Consumer &consumer); + void doDtelEventTableTask(Consumer &consumer); + + int64_t getINTSessionRefCount(const string& name); + bool reportSessionExists(const string& name); + bool getReportSessionOid(const string& name, sai_object_id_t& oid); + bool increaseReportSessionRefCount(const string& name); + bool decreaseReportSessionRefCount(const string& name); + int64_t getReportSessionRefCount(const string& name); + bool isQueueReportEnabled(const string& port, const string& queue); + bool getQueueReportOid(const string& port, const string& queue, sai_object_id_t& oid); + bool addPortQueue(const string& port, const string& queue, DTelQueueReportEntry **qreport); + bool removePortQueue(const string& port, const string& queue); + bool isEventConfigured(const string& event); + bool getEventOid(const string& event, sai_object_id_t& oid); + void addEvent(const string& event, const sai_object_id_t& event_oid, const string& report_session_id); + void removeEvent(const string& event); + bool deleteReportSession(const string& report_session_id); + bool deleteINTSession(const string& int_session_id); + bool disableQueueReport(const string& port, const string& queue); + bool unConfigureEvent(string& event); + sai_status_t updateSinkPortList(); + bool addSinkPortToCache(const Port& port); + bool removeSinkPortFromCache(const string& port_alias); + sai_status_t enableQueueReport(const string& port, DTelQueueReportEntry& qreport); + + PortsOrch *m_portOrch; + dTelINTSessionTable_t m_dTelINTSessionTable; + dTelReportSessionTable_t m_dTelReportSessionTable; + dTelPortTable_t m_dTelPortTable; + dtelEventTable_t m_dtelEventTable; + sai_object_id_t dtelId; + dtelSinkPortList_t sinkPortList; +}; + +#endif /* SWSS_DTELORCH_H */ diff --git a/orchagent/observer.h b/orchagent/observer.h index 4a86d959b5fe..2853aa266309 100644 --- a/orchagent/observer.h +++ b/orchagent/observer.h @@ -14,6 +14,8 @@ enum SubjectType SUBJECT_TYPE_LAG_MEMBER_CHANGE, SUBJECT_TYPE_VLAN_MEMBER_CHANGE, SUBJECT_TYPE_MIRROR_SESSION_CHANGE, + SUBJECT_TYPE_INT_SESSION_CHANGE, + SUBJECT_TYPE_PORT_CHANGE, }; class Observer diff --git a/orchagent/orch.h b/orchagent/orch.h index eddece4081c2..475e6b58e21e 100644 --- a/orchagent/orch.h +++ b/orchagent/orch.h @@ -33,6 +33,8 @@ const char config_db_key_delimiter = '|'; #define MLNX_PLATFORM_SUBSTRING "mellanox" #define BRCM_PLATFORM_SUBSTRING "broadcom" +#define BFN_PLATFORM_SUBSTRING "barefoot" +#define VS_PLATFORM_SUBSTRING "vs" #define CONFIGDB_KEY_SEPARATOR "|" #define DEFAULT_KEY_SEPARATOR ":" diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index 1cc1e4dfc258..b0a0b64c340c 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -108,9 +108,54 @@ bool OrchDaemon::init() stateDbLagTable }; - gAclOrch = new AclOrch(acl_table_connectors, gPortsOrch, mirror_orch, gNeighOrch, gRouteOrch); + vector dtel_tables = { + CFG_DTEL_TABLE_NAME, + CFG_DTEL_REPORT_SESSION_TABLE_NAME, + CFG_DTEL_INT_SESSION_TABLE_NAME, + CFG_DTEL_QUEUE_REPORT_TABLE_NAME, + CFG_DTEL_EVENT_TABLE_NAME + }; + + m_orchList = { switch_orch, gCrmOrch, gPortsOrch, intfs_orch, gNeighOrch, gRouteOrch, copp_orch, tunnel_decap_orch, qos_orch, gBufferOrch, mirror_orch }; + + bool initialize_dtel = false; + if (platform == BFN_PLATFORM_SUBSTRING || platform == VS_PLATFORM_SUBSTRING) + { + sai_attr_capability_t capability; + capability.create_implemented = true; + + /* Will uncomment this when saiobject.h support is added to SONiC */ + /* + sai_status_t status; + + status = sai_query_attribute_capability(gSwitchId, SAI_OBJECT_TYPE_DTEL, SAI_DTEL_ATTR_SWITCH_ID, &capability); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Could not query Dataplane telemetry capability %d", status); + exit(EXIT_FAILURE); + } + */ + + if (capability.create_implemented) + { + initialize_dtel = true; + } + } + + DTelOrch *dtel_orch = NULL; + if (initialize_dtel) + { + dtel_orch = new DTelOrch(m_configDb, dtel_tables, gPortsOrch); + m_orchList.push_back(dtel_orch); + gAclOrch = new AclOrch(acl_table_connectors, gPortsOrch, mirror_orch, gNeighOrch, gRouteOrch, dtel_orch); + } else { + gAclOrch = new AclOrch(acl_table_connectors, gPortsOrch, mirror_orch, gNeighOrch, gRouteOrch); + } - m_orchList = { switch_orch, gCrmOrch, gPortsOrch, intfs_orch, gNeighOrch, gRouteOrch, copp_orch, tunnel_decap_orch, qos_orch, gBufferOrch, mirror_orch, gAclOrch, gFdbOrch, vrf_orch }; + m_orchList.push_back(gAclOrch); + m_orchList.push_back(gFdbOrch); + m_orchList.push_back(vrf_orch); + m_select = new Select(); diff --git a/orchagent/pfc_detect_barefoot.lua b/orchagent/pfc_detect_barefoot.lua new file mode 100644 index 000000000000..892be8b5fcb4 --- /dev/null +++ b/orchagent/pfc_detect_barefoot.lua @@ -0,0 +1,91 @@ +-- KEYS - queue IDs +-- ARGV[1] - counters db index +-- ARGV[2] - counters table name +-- ARGV[3] - poll time interval +-- return queue Ids that satisfy criteria + +local counters_db = ARGV[1] +local counters_table_name = ARGV[2] +local poll_time = tonumber(ARGV[3]) + +local rets = {} + +redis.call('SELECT', counters_db) + +-- Iterate through each queue +local n = table.getn(KEYS) +for i = n, 1, -1 do + local counter_keys = redis.call('HKEYS', counters_table_name .. ':' .. KEYS[i]) + local counter_num = 0 + local old_counter_num = 0 + local is_deadlock = false + local pfc_wd_status = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_STATUS') + local pfc_wd_action = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_ACTION') + if pfc_wd_status == 'operational' or pfc_wd_action == 'alert' then + local detection_time = tonumber(redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_DETECTION_TIME')) + local time_left = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_DETECTION_TIME_LEFT') + if not time_left then + time_left = detection_time + else + time_left = tonumber(time_left) + end + + local queue_index = redis.call('HGET', 'COUNTERS_QUEUE_INDEX_MAP', KEYS[i]) + local port_id = redis.call('HGET', 'COUNTERS_QUEUE_PORT_MAP', KEYS[i]) + local pfc_rx_pkt_key = 'SAI_PORT_STAT_PFC_' .. queue_index .. '_RX_PKTS' + local pfc_on2off_key = 'SAI_PORT_STAT_PFC_' .. queue_index .. '_ON2OFF_RX_PKTS' + + + -- Get all counters + local occupancy_bytes = tonumber(redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_STAT_CURR_OCCUPANCY_BYTES')) + local packets = tonumber(redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_STAT_PACKETS')) + local pfc_rx_packets = tonumber(redis.call('HGET', counters_table_name .. ':' .. port_id, pfc_rx_pkt_key)) + local pfc_on2off = tonumber(redis.call('HGET', counters_table_name .. ':' .. port_id, pfc_on2off_key)) + local queue_pause_status = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_ATTR_PAUSE_STATUS') + + local packets_last = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_STAT_PACKETS_last') + local pfc_rx_packets_last = redis.call('HGET', counters_table_name .. ':' .. port_id, pfc_rx_pkt_key .. '_last') + local pfc_on2off_last = redis.call('HGET', counters_table_name .. ':' .. port_id, pfc_on2off_key .. '_last') + local queue_pause_status_last = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_ATTR_PAUSE_STATUS_last') + + -- DEBUG CODE START. Uncomment to enable + local debug_storm = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'DEBUG_STORM') + -- DEBUG CODE END. + + -- If this is not a first run, then we have last values available + if packets_last and pfc_rx_packets_last and pfc_on2off_last and queue_pause_status_last then + packets_last = tonumber(packets_last) + pfc_rx_packets_last = tonumber(pfc_rx_packets_last) + pfc_on2off_last = tonumber(pfc_on2off_last) + + -- Check actual condition of queue being in PFC storm + if (occupancy_bytes > 0 and packets - packets_last == 0 and pfc_rx_packets - pfc_rx_packets_last > 0) or + -- DEBUG CODE START. Uncomment to enable + (debug_storm == "enabled") or + -- DEBUG CODE END. + (occupancy_bytes == 0 and pfc_rx_packets - pfc_rx_packets_last > 0 and pfc_on2off - pfc_on2off_last == 0 and queue_pause_status_last == 'true' and queue_pause_status == 'true') then + if time_left <= poll_time then + redis.call('PUBLISH', 'PFC_WD', '["' .. KEYS[i] .. '","storm"]') + is_deadlock = true + time_left = detection_time + else + time_left = time_left - poll_time + end + else + if pfc_wd_action == 'alert' and pfc_wd_status ~= 'operational' then + redis.call('PUBLISH', 'PFC_WD', '["' .. KEYS[i] .. '","restore"]') + end + time_left = detection_time + end + end + + -- Save values for next run + redis.call('HSET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_ATTR_PAUSE_STATUS_last', queue_pause_status) + redis.call('HSET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_STAT_PACKETS_last', packets) + redis.call('HSET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_DETECTION_TIME_LEFT', time_left) + redis.call('HSET', counters_table_name .. ':' .. port_id, pfc_rx_pkt_key .. '_last', pfc_rx_packets) + redis.call('HSET', counters_table_name .. ':' .. port_id, pfc_on2off_key .. '_last', pfc_on2off) + end +end + +return rets diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index 1ba93ffbdffe..ff708e2f2d6b 100644 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -1080,6 +1080,13 @@ bool PortsOrch::removePort(sai_object_id_t port_id) { SWSS_LOG_ENTER(); + Port p; + if (getPort(port_id, p)) + { + PortUpdate update = {p, false }; + notify(SUBJECT_TYPE_PORT_CHANGE, static_cast(&update)); + } + sai_status_t status = sai_port_api->remove_port(port_id); if (status != SAI_STATUS_SUCCESS) { @@ -1149,6 +1156,9 @@ bool PortsOrch::initPort(const string &alias, const set &lane_set) m_flexCounterTable->set(key, fields); + PortUpdate update = {p, true }; + notify(SUBJECT_TYPE_PORT_CHANGE, static_cast(&update)); + SWSS_LOG_NOTICE("Initialized port %s", alias.c_str()); } else @@ -1281,7 +1291,7 @@ void PortsOrch::doPortTask(Consumer &consumer) if (m_lanesAliasSpeedMap.find(it->first) == m_lanesAliasSpeedMap.end()) { char *platform = getenv("platform"); - if (platform && strstr(platform, MLNX_PLATFORM_SUBSTRING)) + if (platform && (strstr(platform, BFN_PLATFORM_SUBSTRING) || strstr(platform, MLNX_PLATFORM_SUBSTRING))) { if (!removePort(it->second)) { @@ -1310,7 +1320,7 @@ void PortsOrch::doPortTask(Consumer &consumer) // work around to avoid syncd termination on SAI error due missing create_port SAI API // can be removed when SAI redis return NotImplemented error char *platform = getenv("platform"); - if (platform && strstr(platform, MLNX_PLATFORM_SUBSTRING)) + if (platform && (strstr(platform, BFN_PLATFORM_SUBSTRING) || strstr(platform, MLNX_PLATFORM_SUBSTRING))) { if (!addPort(it->first, get<1>(it->second), get<2>(it->second), get<3>(it->second))) { diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h index 967486f3d3bc..6f6221e96add 100644 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -28,6 +28,12 @@ static const map oper_status_strings = { SAI_PORT_OPER_STATUS_NOT_PRESENT, "not present" } }; +struct PortUpdate +{ + Port port; + bool add; +}; + struct LagMemberUpdate { Port lag; diff --git a/orchagent/saihelper.cpp b/orchagent/saihelper.cpp index 58e9eb48c4b7..7381e7443098 100644 --- a/orchagent/saihelper.cpp +++ b/orchagent/saihelper.cpp @@ -1,4 +1,5 @@ extern "C" { + #include "sai.h" #include "saistatus.h" } @@ -37,6 +38,7 @@ sai_buffer_api_t* sai_buffer_api; sai_acl_api_t* sai_acl_api; sai_mirror_api_t* sai_mirror_api; sai_fdb_api_t* sai_fdb_api; +sai_dtel_api_t* sai_dtel_api; extern sai_object_id_t gSwitchId; extern bool gSairedisRecord; @@ -124,6 +126,7 @@ void initSaiApi() sai_api_query(SAI_API_BUFFER, (void **)&sai_buffer_api); sai_api_query(SAI_API_SCHEDULER_GROUP, (void **)&sai_scheduler_group_api); sai_api_query(SAI_API_ACL, (void **)&sai_acl_api); + sai_api_query(SAI_API_DTEL, (void **)&sai_dtel_api); sai_log_set(SAI_API_SWITCH, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_BRIDGE, SAI_LOG_LEVEL_NOTICE); @@ -148,6 +151,7 @@ void initSaiApi() sai_log_set(SAI_API_BUFFER, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_SCHEDULER_GROUP, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_ACL, SAI_LOG_LEVEL_NOTICE); + sai_log_set(SAI_API_DTEL, SAI_LOG_LEVEL_NOTICE); } void initSaiRedis(const string &record_location) diff --git a/tests/conftest.py b/tests/conftest.py index d7388125bc7a..3df6c6c1eeb1 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -50,8 +50,22 @@ def __init__(self, dvs): atbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") keys = atbl.getKeys() - assert len(keys) == 1 - self.default_acl_table = keys[0] + assert len(keys) >= 1 + + # Filter out DTel Acl tables + default_table_found = False + for k in keys: + if default_table_found: + break + (status, fvs) = atbl.get(k) + for item in fvs: + if item[0] == "SAI_ACL_TABLE_ATTR_ACL_BIND_POINT_TYPE_LIST": + if 'SAI_ACL_BIND_POINT_TYPE_PORT' in item[1]: + self.default_acl_table = k + default_table_found = True + break + else: + break atbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") keys = atbl.getKeys() diff --git a/tests/test_acl.py b/tests/test_acl.py index 794162ede63e..450e034610fd 100644 --- a/tests/test_acl.py +++ b/tests/test_acl.py @@ -10,9 +10,19 @@ def get_acl_table_id(self, dvs, adb): assert dvs.asicdb.default_acl_table in keys acl_tables = [k for k in keys if k not in dvs.asicdb.default_acl_table] - assert len(acl_tables) == 1 + assert len(acl_tables) >= 1 - return acl_tables[0] + # Filter out DTel Acl tables + for k in acl_tables: + (status, fvs) = atbl.get(k) + for item in fvs: + if item[0] == "SAI_ACL_TABLE_ATTR_ACL_BIND_POINT_TYPE_LIST": + if 'SAI_ACL_BIND_POINT_TYPE_PORT' in item[1] or 'SAI_ACL_BIND_POINT_TYPE_LAG' in item[1]: + return k + else: + break + + return None def verify_if_any_acl_table_created(self, dvs, adb): atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") @@ -20,10 +30,17 @@ def verify_if_any_acl_table_created(self, dvs, adb): assert dvs.asicdb.default_acl_table in keys acl_tables = [k for k in keys if k not in dvs.asicdb.default_acl_table] - if (len(acl_tables) != 0): - return True - else: - return False + # Filter out DTel Acl tables + for k in acl_tables: + (status, fvs) = atbl.get(k) + for item in fvs: + if item[0] == "SAI_ACL_TABLE_ATTR_ACL_BIND_POINT_TYPE_LIST": + if 'SAI_ACL_BIND_POINT_TYPE_PORT' in item[1] or 'SAI_ACL_BIND_POINT_TYPE_LAG' in item[1]: + return True + else: + break + + return False def clean_up_left_over(self, dvs): adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) @@ -220,8 +237,8 @@ def test_AclTableDeletion(self, dvs): atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") keys = atbl.getKeys() - # only the default table was left - assert len(keys) == 1 + # only the default table was left along with DTel tables + assert len(keys) >= 1 def test_V6AclTableCreation(self, dvs): @@ -848,7 +865,7 @@ def test_V6AclTableDeletion(self, dvs): atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") keys = atbl.getKeys() # only the default table was left - assert len(keys) == 1 + assert len(keys) >= 1 #helper function to verify if rule exists def check_rule_existence(self, entry, rules, verifs): @@ -961,7 +978,7 @@ def test_InsertAclRuleBetweenPriorities(self, dvs): atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") keys = atbl.getKeys() # only the default table was left - assert len(keys) == 1 + assert len(keys) >= 1 def test_RulesWithDiffMaskLengths(self, dvs): db = swsscommon.DBConnector(4, dvs.redis_sock, 0) @@ -1046,7 +1063,7 @@ def test_RulesWithDiffMaskLengths(self, dvs): atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") keys = atbl.getKeys() - assert len(keys) == 1 + assert len(keys) >= 1 def test_AclTableCreationOnLAGMember(self, dvs): # prepare db and tables @@ -1203,4 +1220,4 @@ def test_AclTableCreationBeforeLAG(self, dvs): self.verify_acl_lag_binding(adb, lag_ids) tbl = swsscommon.Table(db, "ACL_TABLE") - tbl._del("test_LAG_2") \ No newline at end of file + tbl._del("test_LAG_2") diff --git a/tests/test_crm.py b/tests/test_crm.py deleted file mode 100644 index fa859de63b9e..000000000000 --- a/tests/test_crm.py +++ /dev/null @@ -1,529 +0,0 @@ -from swsscommon import swsscommon -import os -import re -import time -import json -import redis - - -def getCrmCounterValue(dvs, key, counter): - - counters_db = swsscommon.DBConnector(swsscommon.COUNTERS_DB, dvs.redis_sock, 0) - crm_stats_table = swsscommon.Table(counters_db, 'CRM') - - for k in crm_stats_table.get(key)[1]: - if k[0] == counter: - return int(k[1]) - - -def setReadOnlyAttr(dvs, obj, attr, val): - - db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - tbl = swsscommon.Table(db, "ASIC_STATE:{0}".format(obj)) - keys = tbl.getKeys() - - assert len(keys) == 1 - - swVid = keys[0] - r = redis.Redis(unix_socket_path=dvs.redis_sock, db=swsscommon.ASIC_DB) - swRid = r.hget("VIDTORID", swVid) - - assert swRid is not None - - ntf = swsscommon.NotificationProducer(db, "SAI_VS_UNITTEST_CHANNEL") - fvp = swsscommon.FieldValuePairs() - ntf.send("enable_unittests", "true", fvp) - fvp = swsscommon.FieldValuePairs([(attr, val)]) - key = "SAI_OBJECT_TYPE_SWITCH:" + swRid - - ntf.send("set_ro", key, fvp) - - -def test_CrmFdbEntry(dvs): - - dvs.runcmd("crm config polling interval 1") - - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_FDB_ENTRY', '1000') - - time.sleep(2) - - # get counters - used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_fdb_entry_used') - avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_fdb_entry_available') - - app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - cfg_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - # create a FDB entry - tbl = swsscommon.ProducerStateTable(app_db, "FDB_TABLE") - fvs = swsscommon.FieldValuePairs([("port","Ethernet8"),("type","dynamic")]) - tbl.set("Vlan2:52-54-00-25-06-E9", fvs) - - # create vlan - tbl = swsscommon.Table(cfg_db, "VLAN") - fvs = swsscommon.FieldValuePairs([("vlanid", "2")]) - tbl.set("Vlan2", fvs) - - # create vlan member - tbl = swsscommon.Table(cfg_db, "VLAN_MEMBER") - fvs = swsscommon.FieldValuePairs([("tagging_mode", "untagged")]) - tbl.set("Vlan2|Ethernet8", fvs) - - # update available counter - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_FDB_ENTRY', '999') - - time.sleep(2) - - # get counters - new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_fdb_entry_used') - new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_fdb_entry_available') - - assert new_used_counter - used_counter == 1 - assert avail_counter - new_avail_counter == 1 - - # update available counter - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_FDB_ENTRY', '1000') - - time.sleep(2) - - # get counters - new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_fdb_entry_available') - - assert new_avail_counter == avail_counter - - -def test_CrmIpv4Route(dvs): - - dvs.runcmd("ifconfig Ethernet0 10.0.0.0/31 up") - - dvs.runcmd("crm config polling interval 1") - - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_ROUTE_ENTRY', '1000') - - # add static neighbor - dvs.runcmd("ip neigh replace 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") - - db = swsscommon.DBConnector(0, dvs.redis_sock, 0) - ps = swsscommon.ProducerStateTable(db, "ROUTE_TABLE") - fvs = swsscommon.FieldValuePairs([("nexthop","10.0.0.1"), ("ifname", "Ethernet0")]) - - time.sleep(2) - - # get counters - used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_route_used') - avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_route_available') - - # add route and update available counter - ps.set("2.2.2.0/24", fvs) - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_ROUTE_ENTRY', '999') - - time.sleep(2) - - # get counters - new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_route_used') - new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_route_available') - - assert new_used_counter - used_counter == 1 - assert avail_counter - new_avail_counter == 1 - - # remove route and update available counter - ps._del("2.2.2.0/24") - dvs.runcmd("ip neigh del 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_ROUTE_ENTRY', '1000') - - time.sleep(2) - - # get counters - new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_route_used') - new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_route_available') - - assert new_used_counter == used_counter - assert new_avail_counter == avail_counter - - -def test_CrmIpv6Route(dvs): - - # Enable IPv6 routing - dvs.runcmd("sysctl net.ipv6.conf.all.disable_ipv6=0") - time.sleep(2) - - dvs.runcmd("ifconfig Ethernet0 inet6 add fc00::1/126 up") - - dvs.servers[0].runcmd("ifconfig eth0 inet6 add fc00::2/126") - dvs.servers[0].runcmd("ip -6 route add default via fc00::1") - - dvs.runcmd("crm config polling interval 1") - - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_ROUTE_ENTRY', '1000') - - # get neighbor and arp entry - dvs.servers[0].runcmd("ping6 -c 4 fc00::1") - - db = swsscommon.DBConnector(0, dvs.redis_sock, 0) - ps = swsscommon.ProducerStateTable(db, "ROUTE_TABLE") - fvs = swsscommon.FieldValuePairs([("nexthop","fc00::2"), ("ifname", "Ethernet0")]) - - time.sleep(2) - - # get counters - used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_route_used') - avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_route_available') - - # add route and update available counter - ps.set("2001::/64", fvs) - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_ROUTE_ENTRY', '999') - - time.sleep(2) - - # get counters - new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_route_used') - new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_route_available') - - assert new_used_counter - used_counter == 1 - assert avail_counter - new_avail_counter == 1 - - # remove route and update available counter - ps._del("2001::/64") - dvs.runcmd("ip -6 neigh del fc00::2 lladdr 11:22:33:44:55:66 dev Ethernet0") - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_ROUTE_ENTRY', '1000') - - time.sleep(2) - - # get counters - new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_route_used') - new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_route_available') - - assert new_used_counter == used_counter - assert new_avail_counter == avail_counter - - -def test_CrmIpv4Nexthop(dvs): - - dvs.runcmd("ifconfig Ethernet0 10.0.0.0/31 up") - - dvs.runcmd("crm config polling interval 1") - - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_NEXTHOP_ENTRY', '1000') - - time.sleep(2) - - # get counters - used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_nexthop_used') - avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_nexthop_available') - - # add nexthop and update available counter - dvs.runcmd("ip neigh replace 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_NEXTHOP_ENTRY', '999') - - time.sleep(2) - - # get counters - new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_nexthop_used') - new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_nexthop_available') - - assert new_used_counter - used_counter == 1 - assert avail_counter - new_avail_counter == 1 - - # remove nexthop and update available counter - dvs.runcmd("ip neigh del 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_NEXTHOP_ENTRY', '1000') - - time.sleep(2) - - # get counters - new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_nexthop_used') - new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_nexthop_available') - - assert new_used_counter == used_counter - assert new_avail_counter == avail_counter - - -def test_CrmIpv6Nexthop(dvs): - - # Enable IPv6 routing - dvs.runcmd("sysctl net.ipv6.conf.all.disable_ipv6=0") - time.sleep(2) - - dvs.runcmd("ifconfig Ethernet0 inet6 add fc00::1/126 up") - - dvs.runcmd("crm config polling interval 1") - - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_NEXTHOP_ENTRY', '1000') - - time.sleep(2) - - # get counters - used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_nexthop_used') - avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_nexthop_available') - - # add nexthop and update available counter - dvs.runcmd("ip -6 neigh replace fc00::2 lladdr 11:22:33:44:55:66 dev Ethernet0") - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_NEXTHOP_ENTRY', '999') - - time.sleep(2) - - # get counters - new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_nexthop_used') - new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_nexthop_available') - - assert new_used_counter - used_counter == 1 - assert avail_counter - new_avail_counter == 1 - - # remove nexthop and update available counter - dvs.runcmd("ip -6 neigh del fc00::2 lladdr 11:22:33:44:55:66 dev Ethernet0") - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_NEXTHOP_ENTRY', '1000') - - time.sleep(2) - - # get counters - new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_nexthop_used') - new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_nexthop_available') - - assert new_used_counter == used_counter - assert new_avail_counter == avail_counter - - -def test_CrmIpv4Neighbor(dvs): - - dvs.runcmd("ifconfig Ethernet0 10.0.0.0/31 up") - - dvs.runcmd("crm config polling interval 1") - - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_NEIGHBOR_ENTRY', '1000') - - time.sleep(2) - - # get counters - used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_neighbor_used') - avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_neighbor_available') - - # add neighbor and update available counter - dvs.runcmd("ip neigh replace 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_NEIGHBOR_ENTRY', '999') - - time.sleep(2) - - # get counters - new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_neighbor_used') - new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_neighbor_available') - - assert new_used_counter - used_counter == 1 - assert avail_counter - new_avail_counter == 1 - - # remove neighbor and update available counter - dvs.runcmd("ip neigh del 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_NEIGHBOR_ENTRY', '1000') - - time.sleep(2) - - # get counters - new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_neighbor_used') - new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_neighbor_available') - - assert new_used_counter == used_counter - assert new_avail_counter == avail_counter - - -def test_CrmIpv6Neighbor(dvs): - - # Enable IPv6 routing - dvs.runcmd("sysctl net.ipv6.conf.all.disable_ipv6=0") - time.sleep(2) - - dvs.runcmd("ifconfig Ethernet0 inet6 add fc00::1/126 up") - - dvs.runcmd("crm config polling interval 1") - - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_NEIGHBOR_ENTRY', '1000') - - time.sleep(2) - - # get counters - used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_neighbor_used') - avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_neighbor_available') - - # add neighbor and update available counter - dvs.runcmd("ip -6 neigh replace fc00::2 lladdr 11:22:33:44:55:66 dev Ethernet0") - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_NEIGHBOR_ENTRY', '999') - - time.sleep(2) - - # get counters - new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_neighbor_used') - new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_neighbor_available') - - assert new_used_counter - used_counter == 1 - assert avail_counter - new_avail_counter == 1 - - # remove neighbor and update available counter - dvs.runcmd("ip -6 neigh del fc00::2 lladdr 11:22:33:44:55:66 dev Ethernet0") - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_NEIGHBOR_ENTRY', '1000') - - time.sleep(2) - - # get counters - new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_neighbor_used') - new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_neighbor_available') - - assert new_used_counter == used_counter - assert new_avail_counter == avail_counter - - -def test_CrmNexthopGroup(dvs): - - dvs.runcmd("ifconfig Ethernet0 10.0.0.0/31 up") - dvs.runcmd("ifconfig Ethernet4 10.0.0.2/31 up") - - dvs.runcmd("crm config polling interval 1") - - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_NEXT_HOP_GROUP_ENTRY', '1000') - - # add neighbors - dvs.runcmd("ip neigh replace 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") - dvs.runcmd("ip neigh replace 10.0.0.3 lladdr 11:22:33:44:55:66 dev Ethernet4") - - db = swsscommon.DBConnector(0, dvs.redis_sock, 0) - ps = swsscommon.ProducerStateTable(db, "ROUTE_TABLE") - fvs = swsscommon.FieldValuePairs([("nexthop","10.0.0.1,10.0.0.3"), ("ifname", "Ethernet0,Ethernet4")]) - - time.sleep(2) - - # get counters - used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_used') - avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_available') - - # add route and update available counter - ps.set("2.2.2.0/24", fvs) - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_NEXT_HOP_GROUP_ENTRY', '999') - - time.sleep(2) - - # get counters - new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_used') - new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_available') - - assert new_used_counter - used_counter == 1 - assert avail_counter - new_avail_counter == 1 - - # remove route and update available counter - ps._del("2.2.2.0/24") - dvs.runcmd("ip neigh del 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") - dvs.runcmd("ip neigh del 10.0.0.3 lladdr 11:22:33:44:55:66 dev Ethernet4") - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_NEXT_HOP_GROUP_ENTRY', '1000') - - time.sleep(2) - - # get counters - new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_used') - new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_available') - - assert new_used_counter == used_counter - assert new_avail_counter == avail_counter - - -def test_CrmNexthopGroupMember(dvs): - - dvs.runcmd("ifconfig Ethernet0 10.0.0.0/31 up") - dvs.runcmd("ifconfig Ethernet4 10.0.0.2/31 up") - - dvs.runcmd("crm config polling interval 1") - - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_NEXT_HOP_GROUP_MEMBER_ENTRY', '1000') - - # add neighbors - dvs.runcmd("ip neigh replace 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") - dvs.runcmd("ip neigh replace 10.0.0.3 lladdr 11:22:33:44:55:66 dev Ethernet4") - - db = swsscommon.DBConnector(0, dvs.redis_sock, 0) - ps = swsscommon.ProducerStateTable(db, "ROUTE_TABLE") - fvs = swsscommon.FieldValuePairs([("nexthop","10.0.0.1,10.0.0.3"), ("ifname", "Ethernet0,Ethernet4")]) - - time.sleep(2) - - # get counters - used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_member_used') - avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_member_available') - - # add route and update available counter - ps.set("2.2.2.0/24", fvs) - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_NEXT_HOP_GROUP_MEMBER_ENTRY', '998') - - time.sleep(2) - - # get counters - new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_member_used') - new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_member_available') - - assert new_used_counter - used_counter == 2 - assert avail_counter - new_avail_counter == 2 - - # remove route and update available counter - ps._del("2.2.2.0/24") - dvs.runcmd("ip neigh del 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") - dvs.runcmd("ip neigh del 10.0.0.3 lladdr 11:22:33:44:55:66 dev Ethernet4") - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_NEXT_HOP_GROUP_MEMBER_ENTRY', '1000') - - time.sleep(2) - - # get counters - new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_member_used') - new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_member_available') - - assert new_used_counter == used_counter - assert new_avail_counter == avail_counter - - -def test_CrmAcl(dvs): - - db = swsscommon.DBConnector(4, dvs.redis_sock, 0) - adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) - - dvs.runcmd("crm config polling interval 1") - - bind_ports = ["Ethernet0", "Ethernet4"] - - # create ACL table - ttbl = swsscommon.Table(db, "ACL_TABLE") - fvs = swsscommon.FieldValuePairs([("policy_desc", "test"), ("type", "L3"), ("ports", ",".join(bind_ports))]) - ttbl.set("test", fvs) - - # create ACL rule - rtbl = swsscommon.Table(db, "ACL_RULE") - fvs = swsscommon.FieldValuePairs([("priority", "55"), ("PACKET_ACTION", "FORWARD"), ("L4_SRC_PORT", "65000")]) - rtbl.set("test|acl_test_rule", fvs) - - time.sleep(2) - - table_used_counter = getCrmCounterValue(dvs, 'ACL_STATS:INGRESS:PORT', 'crm_stats_acl_table_used') - assert table_used_counter == 1 - - # get ACL table key - atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") - acl_tables = [k for k in atbl.getKeys() if k not in dvs.asicdb.default_acl_table] - key = "ACL_TABLE_STATS:{0}".format(acl_tables[0].replace('oid:', '')) - - entry_used_counter = getCrmCounterValue(dvs, key, 'crm_stats_acl_entry_used') - assert entry_used_counter == 1 - - cnt_used_counter = getCrmCounterValue(dvs, key, 'crm_stats_acl_counter_used') - assert entry_used_counter == 1 - - # remove ACL rule - rtbl._del("test|acl_test_rule") - - time.sleep(2) - - entry_used_counter = getCrmCounterValue(dvs, key, 'crm_stats_acl_entry_used') - assert entry_used_counter == 0 - - cnt_used_counter = getCrmCounterValue(dvs, key, 'crm_stats_acl_counter_used') - assert cnt_used_counter == 0 - - # remove ACL table - ttbl._del("test") - - time.sleep(2) - - table_used_counter = getCrmCounterValue(dvs, 'ACL_STATS:INGRESS:PORT', 'crm_stats_acl_table_used') - assert table_used_counter == 0 - diff --git a/tests/test_dtel.py b/tests/test_dtel.py new file mode 100644 index 000000000000..8cdebbf425f1 --- /dev/null +++ b/tests/test_dtel.py @@ -0,0 +1,296 @@ +from swsscommon import swsscommon + + +import time +import re +import json + +class TestDtel(object): + def test_DtelGlobalAttribs(self, dvs): + + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + # create DTel global attributes in config db + tbl = swsscommon.Table(db, "DTEL") + + fvs = swsscommon.FieldValuePairs([("SWITCH_ID", "1")]) + tbl.set("SWITCH_ID", fvs) + + fvs = swsscommon.FieldValuePairs([("FLOW_STATE_CLEAR_CYCLE", "10")]) + tbl.set("FLOW_STATE_CLEAR_CYCLE", fvs) + + fvs = swsscommon.FieldValuePairs([("LATENCY_SENSITIVITY", "100")]) + tbl.set("LATENCY_SENSITIVITY", fvs) + + fvs = swsscommon.FieldValuePairs([("INT_ENDPOINT", "TRUE")]) + tbl.set("INT_ENDPOINT", fvs) + + fvs = swsscommon.FieldValuePairs([("INT_TRANSIT", "TRUE")]) + tbl.set("INT_TRANSIT", fvs) + + fvs = swsscommon.FieldValuePairs([("POSTCARD", "TRUE")]) + tbl.set("POSTCARD", fvs) + + fvs = swsscommon.FieldValuePairs([("DROP_REPORT", "TRUE")]) + tbl.set("DROP_REPORT", fvs) + + fvs = swsscommon.FieldValuePairs([("QUEUE_REPORT", "TRUE")]) + tbl.set("QUEUE_REPORT", fvs) + + fvs = swsscommon.FieldValuePairs([("Ethernet0", "Ethernet0"), ("Ethernet4", "Ethernet4")]) + tbl.set("SINK_PORT_LIST", fvs) + + fvs = swsscommon.FieldValuePairs([("INT_L4_DSCP_VALUE", "128"), ("INT_L4_DSCP_MASK", "255")]) + tbl.set("INT_L4_DSCP", fvs) + + time.sleep(1) + + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_DTEL") + keys = atbl.getKeys() + assert len(keys) > 0 + + for k in keys: + (status, fvs) = atbl.get(k) + assert status == True + + for fv in fvs: + if fv[0] == "SAI_DTEL_ATTR_SWITCH_ID": + assert fv[1] == "1" + elif fv[0] == "SAI_DTEL_ATTR_FLOW_STATE_CLEAR_CYCLE": + assert fv[1] == "10" + elif fv[0] == "SAI_DTEL_ATTR_LATENCY_SENSITIVITY": + assert fv[1] == "100" + elif fv[0] == "SAI_DTEL_ATTR_INT_ENDPOINT_ENABLE": + assert fv[1] == "true" + elif fv[0] == "SAI_DTEL_ATTR_INT_TRANSIT_ENABLE": + assert fv[1] == "true" + elif fv[0] == "SAI_DTEL_ATTR_POSTCARD_ENABLE": + assert fv[1] == "true" + elif fv[0] == "SAI_DTEL_ATTR_DROP_REPORT_ENABLE": + assert fv[1] == "true" + elif fv[0] == "SAI_DTEL_ATTR_QUEUE_REPORT_ENABLE": + assert fv[1] == "true" + elif fv[0] == "SAI_DTEL_ATTR_INT_L4_DSCP": + assert fv[1] == "128&mask:0xff" + elif fv[0] == "SAI_DTEL_ATTR_SINK_PORT_LIST": + assert True + + tbl._del("SWITCH_ID") + tbl._del("FLOW_STATE_CLEAR_CYCLE") + tbl._del("LATENCY_SENSITIVITY") + tbl._del("INT_ENDPOINT") + tbl._del("INT_TRANSIT") + tbl._del("POSTCARD") + tbl._del("DROP_REPORT") + tbl._del("QUEUE_REPORT") + tbl._del("SINK_PORT_LIST") + + def test_DtelReportSessionAttribs(self, dvs): + + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + # create DTel report session attributes in config db + tbl = swsscommon.Table(db, "DTEL_REPORT_SESSION") + + fvs = swsscommon.FieldValuePairs([("SRC_IP", "10.10.10.1"), + ("DST_IP_LIST", "20.20.20.1;20.20.20.2;20.20.20.3"), + ("VRF", "default"), + ("TRUNCATE_SIZE", "256"), + ("UDP_DEST_PORT", "2000")]) + tbl.set("RS-1", fvs) + + time.sleep(1) + + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_DTEL_REPORT_SESSION") + keys = atbl.getKeys() + assert len(keys) > 0 + + for k in keys: + (status, fvs) = atbl.get(k) + assert status == True + + for fv in fvs: + if fv[0] == "SAI_DTEL_REPORT_SESSION_ATTR_SRC_IP": + assert fv[1] == "10.10.10.1" + elif fv[0] == "SAI_DTEL_REPORT_SESSION_ATTR_DST_IP_LIST": + assert fv[1] == "3:20.20.20.1,20.20.20.2,20.20.20.3" + elif fv[0] == "SAI_DTEL_REPORT_SESSION_ATTR_VIRTUAL_ROUTER_ID": + assert True + elif fv[0] == "SAI_DTEL_REPORT_SESSION_ATTR_TRUNCATE_SIZE": + assert fv[1] == "256" + elif fv[0] == "SAI_DTEL_REPORT_SESSION_ATTR_UDP_DST_PORT": + assert fv[1] == "2000" + else: + assert False + + tbl._del("RS-1") + + def test_DtelINTSessionAttribs(self, dvs): + + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + # create DTel INT session attributes in config db + tbl = swsscommon.Table(db, "DTEL_INT_SESSION") + + fvs = swsscommon.FieldValuePairs([("MAX_HOP_COUNT", "50"), + ("COLLECT_SWITCH_ID", "TRUE"), + ("COLLECT_INGRESS_TIMESTAMP", "TRUE"), + ("COLLECT_EGRESS_TIMESTAMP", "TRUE"), + ("COLLECT_SWITCH_PORTS", "TRUE"), + ("COLLECT_QUEUE_INFO", "TRUE")]) + tbl.set("INT-1", fvs) + + time.sleep(1) + + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_DTEL_INT_SESSION") + keys = atbl.getKeys() + assert len(keys) > 0 + + for k in keys: + (status, fvs) = atbl.get(k) + assert status == True + + for fv in fvs: + if fv[0] == "SAI_DTEL_INT_SESSION_ATTR_MAX_HOP_COUNT": + assert fv[1] == "50" + elif fv[0] == "SAI_DTEL_INT_SESSION_ATTR_COLLECT_SWITCH_ID": + assert fv[1] == "true" + elif fv[0] == "SAI_DTEL_INT_SESSION_ATTR_COLLECT_INGRESS_TIMESTAMP": + assert fv[1] == "true" + elif fv[0] == "SAI_DTEL_INT_SESSION_ATTR_COLLECT_EGRESS_TIMESTAMP": + assert fv[1] == "true" + elif fv[0] == "SAI_DTEL_INT_SESSION_ATTR_COLLECT_SWITCH_PORTS": + assert fv[1] == "true" + elif fv[0] == "SAI_DTEL_INT_SESSION_ATTR_COLLECT_QUEUE_INFO": + assert fv[1] == "true" + else: + assert False + + tbl._del("INT-1") + + def test_DtelQueueReportAttribs(self, dvs): + + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + # create DTel queue report attributes in config db + tbl = swsscommon.Table(db, "DTEL_QUEUE_REPORT") + + fvs = swsscommon.FieldValuePairs([("QUEUE_DEPTH_THRESHOLD", "1000"), + ("QUEUE_LATENCY_THRESHOLD", "2000"), + ("THRESHOLD_BREACH_QUOTA", "3000"), + ("REPORT_TAIL_DROP", "TRUE")]) + tbl.set("Ethernet0|0", fvs) + + time.sleep(1) + + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_DTEL_QUEUE_REPORT") + keys = atbl.getKeys() + assert len(keys) > 0 + + for k in keys: + (status, fvs) = atbl.get(k) + assert status == True + + for fv in fvs: + if fv[0] == "SAI_DTEL_QUEUE_REPORT_ATTR_DEPTH_THRESHOLD": + assert fv[1] == "1000" + elif fv[0] == "SAI_DTEL_QUEUE_REPORT_ATTR_LATENCY_THRESHOLD": + assert fv[1] == "2000" + elif fv[0] == "SAI_DTEL_QUEUE_REPORT_ATTR_BREACH_QUOTA": + assert fv[1] == "3000" + elif fv[0] == "SAI_DTEL_QUEUE_REPORT_ATTR_TAIL_DROP": + assert fv[1] == "true" + elif fv[0] == "SAI_DTEL_QUEUE_REPORT_ATTR_QUEUE_ID": + assert True + else: + assert False + + tbl._del("Ethernet0|0") + + + def test_DtelEventAttribs(self, dvs): + + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + # first create DTel report session in config db + rtbl = swsscommon.Table(db, "DTEL_REPORT_SESSION") + + fvs = swsscommon.FieldValuePairs([("SRC_IP", "10.10.10.1"), + ("DST_IP_LIST", "20.20.20.1;20.20.20.2;20.20.20.3"), + ("VRF", "default"), + ("TRUNCATE_SIZE", "256"), + ("UDP_DEST_PORT", "2000")]) + rtbl.set("RS-1", fvs) + + # create DTel event attributes in config db + tbl = swsscommon.Table(db, "DTEL_EVENT") + + fvs = swsscommon.FieldValuePairs([("EVENT_REPORT_SESSION", "RS-1"), + ("EVENT_DSCP_VALUE", "65")]) + tbl.set("EVENT_TYPE_FLOW_STATE", fvs) + + fvs = swsscommon.FieldValuePairs([("EVENT_REPORT_SESSION", "RS-1"), + ("EVENT_DSCP_VALUE", "64")]) + tbl.set("EVENT_TYPE_FLOW_REPORT_ALL_PACKETS", fvs) + + fvs = swsscommon.FieldValuePairs([("EVENT_REPORT_SESSION", "RS-1"), + ("EVENT_DSCP_VALUE", "63")]) + tbl.set("EVENT_TYPE_FLOW_TCPFLAG", fvs) + + fvs = swsscommon.FieldValuePairs([("EVENT_REPORT_SESSION", "RS-1"), + ("EVENT_DSCP_VALUE", "62")]) + tbl.set("EVENT_TYPE_QUEUE_REPORT_THRESHOLD_BREACH", fvs) + + fvs = swsscommon.FieldValuePairs([("EVENT_REPORT_SESSION", "RS-1"), + ("EVENT_DSCP_VALUE", "61")]) + tbl.set("EVENT_TYPE_QUEUE_REPORT_TAIL_DROP", fvs) + + fvs = swsscommon.FieldValuePairs([("EVENT_REPORT_SESSION", "RS-1"), + ("EVENT_DSCP_VALUE", "60")]) + tbl.set("EVENT_TYPE_DROP_REPORT", fvs) + + time.sleep(1) + + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_DTEL_EVENT") + keys = atbl.getKeys() + assert len(keys) > 0 + + for k in keys: + (status, fvs) = atbl.get(k) + assert status == True + + expected_dscp = None + actual_dscp = None + for fv in fvs: + if fv[0] == "SAI_DTEL_EVENT_ATTR_TYPE": + if fv[1] == "SAI_DTEL_EVENT_TYPE_QUEUE_REPORT_TAIL_DROP": + expected_dscp = "61" + elif fv[1] == "SAI_DTEL_EVENT_TYPE_DROP_REPORT": + expected_dscp = "60" + elif fv[1] == "SAI_DTEL_EVENT_TYPE_QUEUE_REPORT_THRESHOLD_BREACH": + expected_dscp = "62" + elif fv[1] == "SAI_DTEL_EVENT_TYPE_FLOW_TCPFLAG": + expected_dscp = "63" + elif fv[1] == "SAI_DTEL_EVENT_TYPE_FLOW_REPORT_ALL_PACKETS": + expected_dscp = "64" + elif fv[1] == "SAI_DTEL_EVENT_TYPE_FLOW_STATE": + expected_dscp = "65" + elif fv[0] == "SAI_DTEL_EVENT_ATTR_REPORT_SESSION": + assert True + elif fv[0] == "SAI_DTEL_EVENT_ATTR_DSCP_VALUE": + actual_dscp = fv[1] + + assert actual_dscp == expected_dscp + + rtbl._del("RS-1") + tbl._del("EVENT_TYPE_FLOW_STATE") + tbl._del("EVENT_TYPE_FLOW_REPORT_ALL_PACKETS") + tbl._del("EVENT_TYPE_FLOW_TCPFLAG") + tbl._del("EVENT_TYPE_QUEUE_REPORT_THRESHOLD_BREACH") + tbl._del("EVENT_TYPE_QUEUE_REPORT_TAIL_DROP") + tbl._del("EVENT_TYPE_DROP_REPORT") diff --git a/tests/test_mirror.py b/tests/test_mirror.py index 4070246b09bf..9dba03e541b0 100644 --- a/tests/test_mirror.py +++ b/tests/test_mirror.py @@ -13,9 +13,19 @@ def get_acl_table_id(self, dvs): assert dvs.asicdb.default_acl_table in keys acl_tables = [k for k in keys if k not in dvs.asicdb.default_acl_table] - assert len(acl_tables) == 1 + assert len(acl_tables) >= 1 - return acl_tables[0] + # Filter out DTel Acl tables + for k in acl_tables: + (status, fvs) = tbl.get(k) + for item in fvs: + if item[0] == "SAI_ACL_TABLE_ATTR_ACL_BIND_POINT_TYPE_LIST": + if 'SAI_ACL_BIND_POINT_TYPE_PORT' in item[1] or 'SAI_ACL_BIND_POINT_TYPE_LAG' in item[1]: + return k + else: + break + + return None def get_mirror_session_id(self, dvs): adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) @@ -217,7 +227,7 @@ def test_AclMirrorTableDeletion(self, dvs): # assert the ACL table is removed tbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") acl_table_ids = tbl.getKeys() - assert len(acl_table_ids) == 1 + assert len(acl_table_ids) == 3 tbl = swsscommon.Table(cdb, "MIRROR_SESSION") # remove the mirror session diff --git a/tests/test_setro.py b/tests/test_setro.py deleted file mode 100755 index 1f619e66464e..000000000000 --- a/tests/test_setro.py +++ /dev/null @@ -1,40 +0,0 @@ -from swsscommon import swsscommon -import time -import json -import redis -from pprint import pprint - -def test_SetReadOnlyAttribute(dvs): - - db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - tbl = swsscommon.Table(db, "ASIC_STATE:SAI_OBJECT_TYPE_SWITCH") - - keys = tbl.getKeys() - - assert len(keys) == 1 - - swVid = keys[0] - - r = redis.Redis(unix_socket_path=dvs.redis_sock, db=swsscommon.ASIC_DB) - - swRid = r.hget("VIDTORID", swVid) - - assert swRid is not None - - ntf = swsscommon.NotificationProducer(db, "SAI_VS_UNITTEST_CHANNEL") - - fvp = swsscommon.FieldValuePairs() - - ntf.send("enable_unittests", "true", fvp) - - fvp = swsscommon.FieldValuePairs([('SAI_SWITCH_ATTR_PORT_MAX_MTU', '42')]) - - key = "SAI_OBJECT_TYPE_SWITCH:" + swRid - - print key - - ntf.send("set_ro", key, fvp) - - # make action on appdb so orchagent will get RO value - # read asic db to see if orchagent behaved correctly