diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index f94e6d3bfa..4cdd6c65f7 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -115,7 +115,8 @@ orchagent_SOURCES = \ dash/dashtagmgr.cpp \ dash/pbutils.cpp \ twamporch.cpp \ - stporch.cpp + stporch.cpp \ + arsorch.cpp orchagent_SOURCES += flex_counter/flex_counter_manager.cpp flex_counter/flex_counter_stat_manager.cpp flex_counter/flow_counter_handler.cpp flex_counter/flowcounterrouteorch.cpp orchagent_SOURCES += debug_counter/debug_counter.cpp debug_counter/drop_counter.cpp diff --git a/orchagent/aclorch.cpp b/orchagent/aclorch.cpp index c7c81362f6..ef8bc8bf45 100644 --- a/orchagent/aclorch.cpp +++ b/orchagent/aclorch.cpp @@ -11,6 +11,7 @@ #include "timer.h" #include "crmorch.h" #include "sai_serialize.h" +#include "directory.h" using namespace std; using namespace swss; @@ -30,6 +31,7 @@ extern PortsOrch* gPortsOrch; extern CrmOrch *gCrmOrch; extern SwitchOrch *gSwitchOrch; extern string gMySwitchType; +extern Directory gDirectory; #define MIN_VLAN_ID 1 // 0 is a reserved VLAN ID #define MAX_VLAN_ID 4095 // 4096 is a reserved VLAN ID @@ -125,6 +127,11 @@ static acl_rule_attr_lookup_t aclOtherActionLookup = { ACTION_COUNTER, SAI_ACL_ENTRY_ATTR_ACTION_COUNTER} }; +static acl_rule_attr_lookup_t aclArsActionLookup = +{ + { ACTION_DISABLE_ARS_FORWARDING, SAI_ACL_ENTRY_ATTR_ACTION_DISABLE_ARS_FORWARDING} +}; + static acl_packet_action_lookup_t aclPacketActionLookup = { { PACKET_ACTION_FORWARD, SAI_PACKET_ACTION_FORWARD }, @@ -401,6 +408,16 @@ static acl_table_action_list_lookup_t defaultAclActionList = } } } + }, + { + // ARS + TABLE_TYPE_ARS, + { + ACL_STAGE_INGRESS, + { + SAI_ACL_ACTION_TYPE_DISABLE_ARS_FORWARDING + } + } } }; @@ -771,6 +788,8 @@ bool AclTableTypeParser::parseAclTableTypeActions(const std::string& value, AclT auto dtelAction = aclDTelActionLookup.find(action); auto otherAction = aclOtherActionLookup.find(action); auto metadataAction = aclMetadataDscpActionLookup.find(action); + auto arsAction = aclArsActionLookup.find(action); + if (l3Action != aclL3ActionLookup.end()) { saiActionAttr = l3Action->second; @@ -791,6 +810,10 @@ bool AclTableTypeParser::parseAclTableTypeActions(const std::string& value, AclT { saiActionAttr = metadataAction->second; } + else if (arsAction != aclArsActionLookup.end()) + { + saiActionAttr = arsAction->second; + } else { SWSS_LOG_ERROR("Unknown action %s", action.c_str()); @@ -1729,6 +1752,11 @@ shared_ptr AclRule::makeShared(AclOrch *acl, MirrorOrch *mirror, DTelOr return make_shared(acl, dtel, rule, table); } + else if (aclArsActionLookup.find(action) != aclArsActionLookup.cend()) + { + ArsOrch* ars_orch = gDirectory.get(); + return make_shared(acl, ars_orch, rule, table); + } } if (!aclRule) @@ -3383,6 +3411,7 @@ void AclOrch::init(vector& connectors, PortsOrch *portOrch, Mirr m_switchMetaDataCapabilities[TABLE_ACL_ENTRY_ATTR_META_CAPABLE] = "true"; m_switchMetaDataCapabilities[TABLE_ACL_ENTRY_ACTION_META_CAPABLE] = "true"; m_metaDataMgr.populateRange(1,7); + m_switchArsCapabilities[ACL_ENTRY_ACTION_DISABLE_ARS_CAPABLE] = "true"; } else { @@ -3400,6 +3429,7 @@ void AclOrch::init(vector& connectors, PortsOrch *portOrch, Mirr m_switchMetaDataCapabilities[TABLE_ACL_USER_META_DATA_RANGE_CAPABLE] = "false"; m_switchMetaDataCapabilities[TABLE_ACL_ENTRY_ATTR_META_CAPABLE] = "false"; m_switchMetaDataCapabilities[TABLE_ACL_ENTRY_ACTION_META_CAPABLE] = "false"; + m_switchArsCapabilities[ACL_ENTRY_ACTION_DISABLE_ARS_CAPABLE] = "false"; status = sai_query_attribute_capability(gSwitchId, SAI_OBJECT_TYPE_SWITCH, SAI_SWITCH_ATTR_ACL_USER_META_DATA_RANGE, &capability); if (status != SAI_STATUS_SUCCESS) @@ -3475,7 +3505,23 @@ void AclOrch::init(vector& connectors, PortsOrch *portOrch, Mirr m_metaDataMgr.populateRange(metadataMin, metadataMax); + status = sai_query_attribute_capability(gSwitchId, SAI_OBJECT_TYPE_ACL_ENTRY, SAI_ACL_ENTRY_ATTR_ACTION_DISABLE_ARS_FORWARDING, &capability); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_WARN("Could not query SAI_ACL_ENTRY_ATTR_ACTION_DISABLE_ARS_FORWARDING %d", status); + } + else + { + if (capability.set_implemented) + { + m_switchArsCapabilities[ACL_ENTRY_ACTION_DISABLE_ARS_CAPABLE] = "true"; + } + + SWSS_LOG_NOTICE("SAI_ACL_ENTRY_ATTR_ACTION_DISABLE_ARS_FORWARDING capability %d", capability.set_implemented); + } } + + // Store the capabilities in state database // TODO: Move this part of the code into syncd vector fvVector; @@ -3784,6 +3830,28 @@ void AclOrch::initDefaultTableTypes(const string& platform, const string& sub_pl } // Placeholder for control plane tables addAclTableType(builder.withName(TABLE_TYPE_CTRLPLANE).build()); + + addAclTableType( + builder.withName(TABLE_TYPE_ARS) + .withBindPointType(SAI_ACL_BIND_POINT_TYPE_PORT) + .withBindPointType(SAI_ACL_BIND_POINT_TYPE_LAG) + .withMatch(make_shared(SAI_ACL_TABLE_ATTR_FIELD_ETHER_TYPE)) + .withMatch(make_shared(SAI_ACL_TABLE_ATTR_FIELD_OUTER_VLAN_ID)) + .withMatch(make_shared(SAI_ACL_TABLE_ATTR_FIELD_ACL_IP_TYPE)) + .withMatch(make_shared(SAI_ACL_TABLE_ATTR_FIELD_SRC_IP)) + .withMatch(make_shared(SAI_ACL_TABLE_ATTR_FIELD_DST_IP)) + .withMatch(make_shared(SAI_ACL_TABLE_ATTR_FIELD_ICMP_TYPE)) + .withMatch(make_shared(SAI_ACL_TABLE_ATTR_FIELD_ICMP_CODE)) + .withMatch(make_shared(SAI_ACL_TABLE_ATTR_FIELD_IP_PROTOCOL)) + .withMatch(make_shared(SAI_ACL_TABLE_ATTR_FIELD_L4_SRC_PORT)) + .withMatch(make_shared(SAI_ACL_TABLE_ATTR_FIELD_L4_DST_PORT)) + .withMatch(make_shared(SAI_ACL_TABLE_ATTR_FIELD_TCP_FLAGS)) + .withMatch(make_shared(SAI_ACL_TABLE_ATTR_FIELD_OUT_PORTS)) + .withMatch(make_shared(set{ + {SAI_ACL_RANGE_TYPE_L4_SRC_PORT_RANGE, SAI_ACL_RANGE_TYPE_L4_DST_PORT_RANGE}})) + .build() + ); + } void AclOrch::queryAclActionCapability() @@ -3879,12 +3947,16 @@ void AclOrch::putAclActionCapabilityInDB(acl_stage_type_t stage) string delimiter; ostringstream acl_action_value_stream; ostringstream is_action_list_mandatory_stream; - acl_rule_attr_lookup_t metadataActionLookup = {}; + acl_rule_attr_lookup_t metadataActionLookup = {}, arsActionLookup = {}; if (isAclMetaDataSupported()) { metadataActionLookup = aclMetadataDscpActionLookup; } - for (const auto& action_map: {aclL3ActionLookup, aclMirrorStageLookup, aclDTelActionLookup, metadataActionLookup}) + if (isAclArsSupported()) + { + arsActionLookup = aclArsActionLookup; + } + for (const auto& action_map: {aclL3ActionLookup, aclMirrorStageLookup, aclDTelActionLookup, metadataActionLookup, arsActionLookup}) { for (const auto& it: action_map) { @@ -5093,6 +5165,15 @@ uint16_t AclOrch::getAclMetaDataMax() const return 0; } +bool AclOrch::isAclArsSupported() const +{ + if (m_switchArsCapabilities[ACL_ENTRY_ACTION_DISABLE_ARS_CAPABLE] == "true") + { + return true; + } + return false; +} + bool AclOrch::isUsingEgrSetDscp(const string& table) const { if (m_egrSetDscpRef.find(table) != m_egrSetDscpRef.end()) @@ -6000,3 +6081,79 @@ void MetaDataMgr::recycleMetaData(uint16_t metadata) SWSS_LOG_ERROR("Unexpected: Metadata free before Initialization complete."); } } + +bool AclRuleArs::validateAddAction(string attr_name, string attr_value) +{ + SWSS_LOG_ENTER(); + + SWSS_LOG_INFO("Name %s Value %s", attr_name.c_str(), attr_value.c_str()); + + sai_acl_entry_attr_t action; + const auto it = aclArsActionLookup.find(attr_name); + if (it != aclArsActionLookup.cend()) + { + action = it->second; + } + else + { + return false; + } + + sai_acl_action_data_t actionData; + actionData.enable = true; + actionData.parameter.booldata = (attr_value == "true") ? true : false; + return setAction(action, actionData); +} + +bool AclRuleArs::createRule() +{ + SWSS_LOG_ENTER(); + + return activate(); +} + +bool AclRuleArs::removeRule() +{ + SWSS_LOG_ENTER(); + + return deactivate(); +} + +bool AclRuleArs::activate() +{ + SWSS_LOG_ENTER(); + sai_object_id_t oid = SAI_NULL_OBJECT_ID; + + for (auto& it: m_actions) + { + auto attr = it.second.getSaiAttr(); + attr.value.aclaction.enable = true; + attr.value.aclaction.parameter.objlist.list = &oid; + attr.value.aclaction.parameter.objlist.count = 1; + setAction(it.first, attr.value.aclaction); + } + + if (!AclRule::createRule()) + { + return false; + } + + m_state = true; + return true; +} + +bool AclRuleArs::deactivate() +{ + SWSS_LOG_ENTER(); + if (!m_state) + { + return true; + } + if (!AclRule::removeRule()) + { + return false; + } + + m_state = false; + return true; +} \ No newline at end of file diff --git a/orchagent/aclorch.h b/orchagent/aclorch.h index 9e27be2ac0..c37e05b0f3 100644 --- a/orchagent/aclorch.h +++ b/orchagent/aclorch.h @@ -73,6 +73,7 @@ #define ACTION_COUNTER "COUNTER" #define ACTION_META_DATA "META_DATA_ACTION" #define ACTION_DSCP "DSCP_ACTION" +#define ACTION_DISABLE_ARS_FORWARDING "DISABLE_ARS_FORWARDING" #define PACKET_ACTION_FORWARD "FORWARD" #define PACKET_ACTION_DROP "DROP" @@ -112,6 +113,7 @@ #define TABLE_ACL_USER_META_DATA_MAX "ACL_USER_META_DATA_MAX" #define TABLE_ACL_ENTRY_ATTR_META_CAPABLE "ACL_ENTRY_ATTR_META_CAPABLE" #define TABLE_ACL_ENTRY_ACTION_META_CAPABLE "ACL_ENTRY_ACTION_META_CAPABLE" +#define TABLE_ACL_ENTRY_ACTION_DISABLE_ARS_CAPABLE "ACL_ENTRY_ACTION_DISABLE_ARS_CAPABLE" enum AclObjectStatus { @@ -584,6 +586,7 @@ class AclOrch : public Orch, public Observer bool isAclMetaDataSupported() const; uint16_t getAclMetaDataMin() const; uint16_t getAclMetaDataMax() const; + bool isAclArsSupported() const; void addMetaDataRef(string key, uint16_t metadata); void removeMetaDataRef(string key, uint16_t metadata); @@ -594,6 +597,7 @@ class AclOrch : public Orch, public Observer map m_mirrorTableCapabilities; map m_L3V4V6Capability; map m_switchMetaDataCapabilities; + map m_switchArsCapabilities; void registerFlexCounter(const AclRule& rule); void deregisterFlexCounter(const AclRule& rule); @@ -682,4 +686,19 @@ class AclOrch : public Orch, public Observer FlexCounterManager m_flex_counter_manager; }; +class AclRuleArs: public AclRule +{ +public: + AclRuleArs (AclOrch *m_pAclOrch, string rule, string table): AclRule(aclOrch, rule, table, createCounter); + bool validateAddAction(string attr_name, string attr_value); + bool validate(); + bool createRule(); + bool removeRule(); + bool activate(); + bool deactivate(); +protected: + protected: + bool m_state {false}; +}; + #endif /* SWSS_ACLORCH_H */ diff --git a/orchagent/acltable.h b/orchagent/acltable.h index 8f51fd9a09..863baef0a6 100644 --- a/orchagent/acltable.h +++ b/orchagent/acltable.h @@ -40,6 +40,7 @@ extern "C" { #define TABLE_TYPE_EGR_SET_DSCP "EGR_SET_DSCP" #define TABLE_TYPE_UNDERLAY_SET_DSCP "UNDERLAY_SET_DSCP" #define TABLE_TYPE_UNDERLAY_SET_DSCPV6 "UNDERLAY_SET_DSCPV6" +#define TABLE_TYPE_ARS "ARS" typedef enum { diff --git a/orchagent/arsorch.cpp b/orchagent/arsorch.cpp new file mode 100644 index 0000000000..c3f47fd6ab --- /dev/null +++ b/orchagent/arsorch.cpp @@ -0,0 +1,1763 @@ +#include +#include +#include "arsorch.h" +#include "routeorch.h" +#include "portsorch.h" +#include "logger.h" +#include "swssnet.h" +#include +#include +#include "sai_serialize.h" +#include "flow_counter_handler.h" + +extern sai_object_id_t gVirtualRouterId; +extern sai_object_id_t gSwitchId; + +extern sai_ars_profile_api_t* sai_ars_profile_api; +extern sai_ars_api_t* sai_ars_api; +extern sai_port_api_t* sai_port_api; +extern sai_next_hop_group_api_t* sai_next_hop_group_api; +extern sai_route_api_t* sai_route_api; +extern sai_switch_api_t* sai_switch_api; + +extern RouteOrch *gRouteOrch; +extern PortsOrch *gPortsOrch; +extern bool gTraditionalFlexCounter; + +static const map lag_counter_type_map = { + {"LAG_PACKETS_DROP", SAI_LAG_ATTR_ARS_PACKET_DROPS}, + {"LAG_PORT_REASSIGNMENT", SAI_LAG_ATTR_ARS_PORT_REASSIGNMENTS} +}; + +static const map nhg_counter_type_map = { + {"NHG_PACKETS_DROP", SAI_NEXT_HOP_GROUP_ATTR_ARS_PACKET_DROPS}, + {"NHG_MEMBER_REASSIGNMENT", SAI_NEXT_HOP_GROUP_ATTR_ARS_NEXT_HOP_REASSIGNMENTS} + {"NHG_PORT_REASSIGNMENT", SAI_NEXT_HOP_GROUP_ATTR_ARS_PORT_REASSIGNMENTS} +}; + +#define ARS_LAG_STAT_COUNTER_FLEX_COUNTER_GROUP "ARS_LAG_STAT_COUNTER" +#define ARS_NHG_STAT_COUNTER_FLEX_COUNTER_GROUP "ARS_NEXTHOP_GROUP_STAT_COUNTER" +#define ARS_STAT_FLEX_COUNTER_POLLING_INTERVAL_MS 10000 + + + +const vector lag_counter_stat_ids = { + + SAI_LAG_ATTR_ARS_PACKET_DROPS, + SAI_LAG_ATTR_ARS_PORT_REASSIGNMENTS +}; + +const vector nhg_counter_stat_ids = { + + SAI_NEXT_HOP_GROUP_ATTR_ARS_PACKET_DROPS, + SAI_NEXT_HOP_GROUP_ATTR_ARS_NEXT_HOP_REASSIGNMENTS, + SAI_NEXT_HOP_GROUP_ATTR_ARS_PORT_REASSIGNMENTS +}; + +typedef struct _ars_sai_attr_t +{ + std::string attr_name; + bool create_implemented; + bool set_implemented; + bool get_implemented; + struct _ars_sai_attr_t(std::string name): attr_name(name), create_implemented(false), set_implemented(false), get_implemented(false) {} +} ars_sai_attr_t; + +typedef std::map ars_sai_attr_lookup_t; +typedef std::map ars_sai_feature_data_t; +typedef std::map ars_sai_feature_lookup_t; + +ars_sai_attr_lookup_t ars_profile_attrs = { + {SAI_ARS_PROFILE_ATTR_ALGO, {"SAI_ARS_PROFILE_ATTR_ALGO"}}, + {SAI_ARS_PROFILE_ATTR_SAMPLING_INTERVAL, {"SAI_ARS_PROFILE_ATTR_SAMPLING_INTERVAL"}}, + {SAI_ARS_PROFILE_ATTR_ARS_RANDOM_SEED, {"SAI_ARS_PROFILE_ATTR_ARS_RANDOM_SEED"}}, + {SAI_ARS_PROFILE_ATTR_ECMP_ARS_MAX_GROUPS, {"SAI_ARS_PROFILE_ATTR_ECMP_ARS_MAX_GROUPS"}}, + {SAI_ARS_PROFILE_ATTR_ECMP_ARS_MAX_MEMBERS_PER_GROUP, {"SAI_ARS_PROFILE_ATTR_ECMP_ARS_MAX_MEMBERS_PER_GROUP"}}, + {SAI_ARS_PROFILE_ATTR_LAG_ARS_MAX_GROUPS, {"SAI_ARS_PROFILE_ATTR_LAG_ARS_MAX_GROUPS"}}, + {SAI_ARS_PROFILE_ATTR_LAG_ARS_MAX_MEMBERS_PER_GROUP, {"SAI_ARS_PROFILE_ATTR_LAG_ARS_MAX_MEMBERS_PER_GROUP"}}, + {SAI_ARS_PROFILE_ATTR_PORT_LOAD_PAST, {"SAI_ARS_PROFILE_ATTR_PORT_LOAD_PAST"}}, + {SAI_ARS_PROFILE_ATTR_PORT_LOAD_PAST_WEIGHT, {"SAI_ARS_PROFILE_ATTR_PORT_LOAD_PAST_WEIGHT"}}, + {SAI_ARS_PROFILE_ATTR_PORT_LOAD_FUTURE, {"SAI_ARS_PROFILE_ATTR_PORT_LOAD_FUTURE"}}, + {SAI_ARS_PROFILE_ATTR_PORT_LOAD_FUTURE_WEIGHT, {"SAI_ARS_PROFILE_ATTR_PORT_LOAD_FUTURE_WEIGHT"}}, + {SAI_ARS_PROFILE_ATTR_PORT_LOAD_CURRENT, {"SAI_ARS_PROFILE_ATTR_PORT_LOAD_CURRENT"}}, + {SAI_ARS_PROFILE_ATTR_PORT_LOAD_EXPONENT, {"SAI_ARS_PROFILE_ATTR_PORT_LOAD_EXPONENT"}}, + {SAI_ARS_PROFILE_ATTR_QUANT_BANDS, {"SAI_ARS_PROFILE_ATTR_QUANT_BANDS"}}, + {SAI_ARS_PROFILE_ATTR_QUANT_BAND_0_MIN_THRESHOLD, {"SAI_ARS_PROFILE_ATTR_QUANT_BAND_0_MIN_THRESHOLD"}}, + {SAI_ARS_PROFILE_ATTR_QUANT_BAND_0_MAX_THRESHOLD, {"SAI_ARS_PROFILE_ATTR_QUANT_BAND_0_MAX_THRESHOLD"}}, + {SAI_ARS_PROFILE_ATTR_QUANT_BAND_1_MIN_THRESHOLD, {"SAI_ARS_PROFILE_ATTR_QUANT_BAND_1_MIN_THRESHOLD"}}, + {SAI_ARS_PROFILE_ATTR_QUANT_BAND_1_MAX_THRESHOLD, {"SAI_ARS_PROFILE_ATTR_QUANT_BAND_1_MAX_THRESHOLD"}}, + {SAI_ARS_PROFILE_ATTR_QUANT_BAND_2_MIN_THRESHOLD, {"SAI_ARS_PROFILE_ATTR_QUANT_BAND_2_MIN_THRESHOLD"}}, + {SAI_ARS_PROFILE_ATTR_QUANT_BAND_2_MAX_THRESHOLD, {"SAI_ARS_PROFILE_ATTR_QUANT_BAND_2_MAX_THRESHOLD"}}, + {SAI_ARS_PROFILE_ATTR_QUANT_BAND_3_MIN_THRESHOLD, {"SAI_ARS_PROFILE_ATTR_QUANT_BAND_3_MIN_THRESHOLD"}}, + {SAI_ARS_PROFILE_ATTR_QUANT_BAND_3_MAX_THRESHOLD, {"SAI_ARS_PROFILE_ATTR_QUANT_BAND_3_MAX_THRESHOLD"}}, + {SAI_ARS_PROFILE_ATTR_QUANT_BAND_4_MIN_THRESHOLD, {"SAI_ARS_PROFILE_ATTR_QUANT_BAND_4_MIN_THRESHOLD"}}, + {SAI_ARS_PROFILE_ATTR_QUANT_BAND_4_MAX_THRESHOLD, {"SAI_ARS_PROFILE_ATTR_QUANT_BAND_4_MAX_THRESHOLD"}}, + {SAI_ARS_PROFILE_ATTR_QUANT_BAND_5_MIN_THRESHOLD, {"SAI_ARS_PROFILE_ATTR_QUANT_BAND_5_MIN_THRESHOLD"}}, + {SAI_ARS_PROFILE_ATTR_QUANT_BAND_5_MAX_THRESHOLD, {"SAI_ARS_PROFILE_ATTR_QUANT_BAND_5_MAX_THRESHOLD"}}, + {SAI_ARS_PROFILE_ATTR_QUANT_BAND_6_MIN_THRESHOLD, {"SAI_ARS_PROFILE_ATTR_QUANT_BAND_6_MIN_THRESHOLD"}}, + {SAI_ARS_PROFILE_ATTR_QUANT_BAND_6_MAX_THRESHOLD, {"SAI_ARS_PROFILE_ATTR_QUANT_BAND_6_MAX_THRESHOLD"}}, + {SAI_ARS_PROFILE_ATTR_QUANT_BAND_7_MIN_THRESHOLD, {"SAI_ARS_PROFILE_ATTR_QUANT_BAND_7_MIN_THRESHOLD"}}, + {SAI_ARS_PROFILE_ATTR_QUANT_BAND_7_MAX_THRESHOLD, {"SAI_ARS_PROFILE_ATTR_QUANT_BAND_7_MAX_THRESHOLD"}}, + {SAI_ARS_PROFILE_ATTR_ENABLE_IPV4, {"SAI_ARS_PROFILE_ATTR_ENABLE_IPV4"}}, + {SAI_ARS_PROFILE_ATTR_ENABLE_IPV6, {"SAI_ARS_PROFILE_ATTR_ENABLE_IPV6"}}, + {SAI_ARS_PROFILE_ATTR_LOAD_PAST_MIN_VAL, {"SAI_ARS_PROFILE_ATTR_LOAD_PAST_MIN_VAL"}}, + {SAI_ARS_PROFILE_ATTR_LOAD_PAST_MAX_VAL, {"SAI_ARS_PROFILE_ATTR_LOAD_PAST_MAX_VAL"}}, + {SAI_ARS_PROFILE_ATTR_QUANT_BAND_MIN_THRESHOLD_LIST_LOAD_PAST, {"SAI_ARS_PROFILE_ATTR_QUANT_BAND_MIN_THRESHOLD_LIST_LOAD_PAST"}}, + {SAI_ARS_PROFILE_ATTR_QUANT_BAND_MAX_THRESHOLD_LIST_LOAD_PAST, {"SAI_ARS_PROFILE_ATTR_QUANT_BAND_MAX_THRESHOLD_LIST_LOAD_PAST"}}, + {SAI_ARS_PROFILE_ATTR_LOAD_FUTURE_MIN_VAL, {"SAI_ARS_PROFILE_ATTR_LOAD_FUTURE_MIN_VAL"}}, + {SAI_ARS_PROFILE_ATTR_LOAD_FUTURE_MAX_VAL, {"SAI_ARS_PROFILE_ATTR_LOAD_FUTURE_MAX_VAL"}}, + {SAI_ARS_PROFILE_ATTR_QUANT_BAND_MIN_THRESHOLD_LIST_LOAD_FUTURE, {"SAI_ARS_PROFILE_ATTR_QUANT_BAND_MIN_THRESHOLD_LIST_LOAD_FUTURE"}}, + {SAI_ARS_PROFILE_ATTR_QUANT_BAND_MAX_THRESHOLD_LIST_LOAD_FUTURE, {"SAI_ARS_PROFILE_ATTR_QUANT_BAND_MAX_THRESHOLD_LIST_LOAD_FUTURE"}}, + {SAI_ARS_PROFILE_ATTR_LOAD_CURRENT_MIN_VAL, {"SAI_ARS_PROFILE_ATTR_LOAD_CURRENT_MIN_VAL"}}, + {SAI_ARS_PROFILE_ATTR_LOAD_CURRENT_MAX_VAL, {"SAI_ARS_PROFILE_ATTR_LOAD_CURRENT_MAX_VAL"}}, + {SAI_ARS_PROFILE_ATTR_QUANT_BAND_MIN_THRESHOLD_LIST_LOAD_CURRENT, {"SAI_ARS_PROFILE_ATTR_QUANT_BAND_MIN_THRESHOLD_LIST_LOAD_CURRENT"}}, + {SAI_ARS_PROFILE_ATTR_QUANT_BAND_MAX_THRESHOLD_LIST_LOAD_CURRENT, {"SAI_ARS_PROFILE_ATTR_QUANT_BAND_MAX_THRESHOLD_LIST_LOAD_CURRENT"}}, + {SAI_ARS_PROFILE_ATTR_MAX_FLOWS, {"SAI_ARS_PROFILE_ATTR_MAX_FLOWS"}} +}; +ars_sai_attr_lookup_t ars_sai_attrs = { + {SAI_ARS_ATTR_MODE, {"SAI_ARS_ATTR_MODE"}}, + {SAI_ARS_ATTR_IDLE_TIME, {"SAI_ARS_ATTR_IDLE_TIME"}}, + {SAI_ARS_ATTR_MAX_FLOWS, {"SAI_ARS_ATTR_MAX_FLOWS"}}, + {SAI_ARS_ATTR_MON_ENABLE, {"SAI_ARS_ATTR_MON_ENABLE"}}, + {SAI_ARS_ATTR_SAMPLEPACKET_ENABLE, {"SAI_ARS_ATTR_SAMPLEPACKET_ENABLE"}}, + {SAI_ARS_ATTR_MAX_ALT_MEMEBERS_PER_GROUP, {"SAI_ARS_ATTR_MAX_ALT_MEMEBERS_PER_GROUP"}}, + {SAI_ARS_ATTR_MAX_PRIMARY_MEMEBERS_PER_GROUP, {"SAI_ARS_ATTR_MAX_PRIMARY_MEMEBERS_PER_GROUP"}}, + {SAI_ARS_ATTR_PRIMARY_PATH_QUALITY_THRESHOLD, {"SAI_ARS_ATTR_PRIMARY_PATH_QUALITY_THRESHOLD"}}, + {SAI_ARS_ATTR_ALTERNATE_PATH_COST, {"SAI_ARS_ATTR_ALTERNATE_PATH_COST"}}, + {SAI_ARS_ATTR_ALTERNATE_PATH_BIAS, {"SAI_ARS_ATTR_ALTERNATE_PATH_BIAS"}} +}; +ars_sai_attr_lookup_t ars_port_attrs = { + {SAI_PORT_ATTR_ARS_ENABLE, {"SAI_PORT_ATTR_ARS_ENABLE"}}, + {SAI_PORT_ATTR_ARS_PORT_LOAD_SCALING_FACTOR, {"SAI_PORT_ATTR_ARS_PORT_LOAD_SCALING_FACTOR"}, + {SAI_PORT_ATTR_ARS_ALTERNATE_PATH, {"SAI_PORT_ATTR_ARS_ALTERNATE_PATH"}} +}; + +ars_sai_attr_lookup_t ars_nhg_attrs = { + {SAI_NEXT_HOP_GROUP_ATTR_ARS_OBJECT_ID, {"SAI_NEXT_HOP_GROUP_ATTR_ARS_OBJECT_ID"}}, + {SAI_NEXT_HOP_GROUP_ATTR_ARS_PACKET_DROPS, {"SAI_NEXT_HOP_GROUP_ATTR_ARS_PACKET_DROPS"}}, + {SAI_NEXT_HOP_GROUP_ATTR_ARS_NEXT_HOP_REASSIGNMENTS, {"SAI_NEXT_HOP_GROUP_ATTR_ARS_NEXT_HOP_REASSIGNMENTS"}}, + {SAI_NEXT_HOP_GROUP_ATTR_ARS_PORT_REASSIGNMENTS, {"SAI_NEXT_HOP_GROUP_ATTR_ARS_PORT_REASSIGNMENTS"}} +}; + +ars_sai_attr_lookup_t ars_switch_attrs = { + {SAI_SWITCH_ATTR_ARS_PROFILE, {"SAI_SWITCH_ATTR_ARS_PROFILE"}} +}; + +ars_sai_attr_lookup_t ars_lag_attrs = { + {SAI_LAG_ATTR_ARS_OBJECT_ID, {"SAI_LAG_ATTR_ARS_OBJECT_ID"}}, + {SAI_LAG_ATTR_ARS_PACKET_DROPS, {"SAI_LAG_ATTR_ARS_PACKET_DROPS"}}, + {SAI_LAG_ATTR_ARS_PORT_REASSIGNMENTS, {"SAI_LAG_ATTR_ARS_PORT_REASSIGNMENTS"}} +}; + +ars_sai_feature_data_t ars_feature_switch_data = + {{"SAI_OBJECT_TYPE_SWITCH",ars_switch_attrs}}; + +ars_sai_feature_data_t ars_feature_profile_data = + {{"SAI_OBJECT_TYPE_ARS_PROFILE",ars_profile_attrs}}; + +ars_sai_feature_data_t ars_feature_ars_data = + {{"SAI_OBJECT_TYPE_ARS",ars_sai_attrs}}; + +ars_sai_feature_data_t ars_feature_port_data = + {{"SAI_OBJECT_TYPE_PORT",ars_port_attrs}}; + +ars_sai_feature_data_t ars_feature_nhg_data = + {{"SAI_OBJECT_TYPE_NEXT_HOP_GROUP",ars_nhg_attrs}}; + +ars_sai_feature_data_t ars_feature_lag_data = + {{"SAI_OBJECT_TYPE_LAG",ars_lag_attrs}}; + +ars_sai_feature_lookup_t ars_features = +{ + {SAI_OBJECT_TYPE_SWITCH, ars_feature_switch_data}, + {SAI_OBJECT_TYPE_ARS_PROFILE, ars_feature_profile_data}, + {SAI_OBJECT_TYPE_ARS, ars_feature_ars_data}, + {SAI_OBJECT_TYPE_PORT, ars_feature_port_data}, + {SAI_OBJECT_TYPE_NEXT_HOP_GROUP, ars_feature_nhg_data}, + {SAI_OBJECT_TYPE_LAG, ars_feature_lag_data} +}; + +map ArsOrch::m_arsProfiles; + +#define ARS_FIELD_NAME_MAX_FLOWS "max_flows" +#define ARS_FIELD_NAME_ALGORITHM "algorithm" +#define ARS_FIELD_NAME_SAMPLE_INTERVAL "sample_interval" +#define ARS_FIELD_NAME_PAST_LOAD_MIN_VALUE "past_load_min_value" +#define ARS_FIELD_NAME_PAST_LOAD_MAX_VALUE "past_load_max_value" +#define ARS_FIELD_NAME_PAST_LOAD_WEIGHT "past_load_weight" +#define ARS_FIELD_NAME_FUTURE_LOAD_MIN_VALUE "future_load_min_value" +#define ARS_FIELD_NAME_FUTURE_LOAD_MAX_VALUE "future_load_max_value" +#define ARS_FIELD_NAME_FUTURE_LOAD_WEIGHT "future_load_weight" +#define ARS_FIELD_NAME_CURRENT_LOAD_MIN_VALUE "current_load_min_value" +#define ARS_FIELD_NAME_CURRENT_LOAD_MAX_VALUE "current_load_max_value" +#define ARS_FIELD_NAME_MIN_VALUE "min_value" +#define ARS_FIELD_NAME_MAX_VALUE "max_value" +#define ARS_FIELD_NAME_WEIGHT "weight" +#define ARS_FIELD_NAME_FUTURE_LOAD "future_load" +#define ARS_FIELD_NAME_CURRENT_LOAD "current_load" +#define ARS_FIELD_NAME_QUANTIZATION_BANDS_LIST "quantization_bands_list" +#define ARS_FIELD_NAME_INDEX "index" +#define ARS_FIELD_NAME_IPV4_ENABLE "ipv4_enable" +#define ARS_FIELD_NAME_IPV6_ENABLE "ipv6_enable" + +#define ARS_FIELD_NAME_PROFILE_NAME "profile_name" +#define ARS_FIELD_NAME_ARS_NAME "ars_name" +#define ARS_FIELD_NAME_ASSIGN_MODE "assign_mode" +#define ARS_FIELD_NAME_PER_FLOWLET "per_flowlet_quality" +#define ARS_FIELD_NAME_PER_PACKET "per_packet" +#define ARS_FIELD_NAME_IDLE_TIME "flowlet_idle_time" +#define ARS_FIELD_NAME_QUALITY_THRESHOLD "quality_threshold" +#define ARS_FIELD_NAME_PRIMARY_PATH_THRESHOLD "primary_path_threshold" +#define ARS_FIELD_NAME_ALT_PATH_COST "alternative_path_cost" +#define ARS_FIELD_NAME_ALT_PATH_MEMBERS "alternative_path_members" + +ArsOrch::ArsOrch(DBConnector *config_db, DBConnector *appDb, DBConnector *stateDb, vector &tableNames, VRFOrch *vrfOrch) : + Orch(db, tableNames), + m_vrfOrch(vrfOrch), + m_counter_db(std::shared_ptr(new DBConnector("COUNTERS_DB", 0))), + m_asic_db(std::shared_ptr(new DBConnector("ASIC_DB", 0))), + m_lag_counter_table(std::unique_ptr(new Table(m_counter_db.get(), COUNTERS_ARS_LAG_NAME_MAP))), + m_nhg_counter_table(std::unique_ptr
(new Table(m_counter_db.get(), COUNTERS_ARS_NEXTHOP_GROUP_NAME_MAP))), + m_vidToRidTable(std::unique_ptr
(new Table(m_asic_db.get(), "VIDTORID"))), + m_lag_counter_manager(ARS_LAG_STAT_COUNTER_FLEX_COUNTER_GROUP, StatsMode::READ, ARS_STAT_FLEX_COUNTER_POLLING_INTERVAL_MS, false) + m_nhg_counter_manager(ARS_NHG_STAT_COUNTER_FLEX_COUNTER_GROUP, StatsMode::READ, ARS_STAT_FLEX_COUNTER_POLLING_INTERVAL_MS, false), + m_arsProfileStateTable(std::unique_ptr
(new Table(stateDb, STATE_ARS_PROFILE_TABLE_NAME))), + m_arsIfStateTable(std::unique_ptr
(new Table(stateDb, STATE_ARS_INTERFACE_TABLE_NAME))), + m_arsNhgStateTable(std::unique_ptr
(new Table(stateDb, STATE_ARS_NEXTHOP_GROUP_TABLE_NAME))), + m_arsLagStateTable(std::unique_ptr
(new Table(stateDb, STATE_ARS_LAG_TABLE_NAME))), +{ + SWSS_LOG_ENTER(); + + initCapabilities(); + + if (m_isArsSupported) + { + gPortsOrch->attach(this); + + auto intervT = timespec { .tv_sec = 1 , .tv_nsec = 0 }; + + if (isGetImplemented(SAI_OBJECT_TYPE_LAG, SAI_LAG_ATTR_ARS_PACKET_DROPS) || isGetImplemented(SAI_OBJECT_TYPE_LAG, SAI_LAG_ATTR_ARS_PORT_REASSIGNMENTS)) + { + m_LagFlexCounterUpdTimer = new SelectableTimer(intervT); + auto executorT = new ExecutableTimer(m_LagFlexCounterUpdTimer, this, "ARS_LAG_FLEX_COUNTER_UPD_TIMER"); + Orch::addExecutor(executorT); + } + if (isGetImplemented(SAI_OBJECT_TYPE_NEXT_HOP_GROUP, SAI_NEXT_HOP_GROUP_ATTR_ARS_PACKET_DROPS) || + isGetImplemented(SAI_OBJECT_TYPE_NEXT_HOP_GROUP, SAI_NEXT_HOP_GROUP_ATTR_ARS_NEXT_HOP_REASSIGNMENTS) || + isGetImplemented(SAI_OBJECT_TYPE_NEXT_HOP_GROUP, SAI_NEXT_HOP_GROUP_ATTR_ARS_PORT_REASSIGNMENTS)) + { + m_NhgFlexCounterUpdTimer = new SelectableTimer(intervT); + auto executorT = new ExecutableTimer(m_NhgFlexCounterUpdTimer, this, "ARS_NHG_FLEX_COUNTER_UPD_TIMER"); + Orch::addExecutor(executorT); + } + } +} + +void ArsOrch::update(SubjectType type, void *cntx) +{ + SWSS_LOG_ENTER(); + assert(cntx); + + if (m_arsProfiles.empty()) + { + SWSS_LOG_INFO("ARS not enabled - no action on interface or nexthop state change"); + return; + } + + switch(type) { + case SUBJECT_TYPE_PORT_OPER_STATE_CHANGE: + { + /* confgiure port scaling factor when port speed becomes available */ + PortOperStateUpdate *update = reinterpret_cast(cntx); + bool is_found = (m_arsEnabledInterfaces.find(update->port.m_alias) != m_arsEnabledInterfaces.end()); + if (is_found) + { + SWSS_LOG_INFO("Interface %s %senabled for ARS - %s ARS", + update->port.m_alias.c_str(), + is_found ? "" : "not ", + update->operStatus == SAI_PORT_OPER_STATUS_UP ? "enable" : "disable"); + if (isSetImplemented(SAI_OBJECT_TYPE_PORT, SAI_PORT_ATTR_ARS_PORT_LOAD_SCALING_FACTOR)) + { + if (!gPortsOrch->setPortArsEnable(update->port, is_found)) + { + SWSS_LOG_ERROR("Failed to set ars enable for port %s", port.m_alias.c_str()); + } + } + } + break; + } + case SUBJECT_TYPE_PORT_CHANGE: + { + /* configure port and lag ARS enable and alternative path memebers */ + PortUpdate *update = reinterpret_cast(cntx); + if (!update->add) + { + if (update->port.m_lag_id && m_arsLags.find(lag.m_alias) != m_arsLags.end()) + { + /* remove from counter db */ + removeLagFromFlexCounter(update->port.m_lag_id, update->port.m_alias); + } + break; + } + + bool is_found = (m_arsEnabledInterfaces.find(update->port.m_alias) != m_arsEnabledInterfaces.end()); + SWSS_LOG_INFO("Interface %s is %s - %s", + update->port.m_alias.c_str(), + update->add ? "added" : "deleted", + is_found ? "enable ARS" : "ignore"); + if (is_found) + { + if (isSetImplemented(SAI_OBJECT_TYPE_PORT, SAI_PORT_ATTR_ARS_ENABLE)) + { + if ((update->operStatus == SAI_PORT_OPER_STATUS_UP) && !gPortsOrch->setPortArsEnable(update->port)) + { + SWSS_LOG_ERROR("Failed to set ars load scaling factor for port %s", update->port.m_alias.c_str()); + } + } + } + + /* check if this is lag or member of lag */ + if (update->port.m_lag_id) + { + Port lag; + if (gPortsOrch->getPort(update->port.m_lag_id, lag)) + { + auto lag_object = m_arsLags.find(lag.m_alias); + sai_attribute_t attr; + attr.id = SAI_LAG_ATTR_ARS_OBJECT_ID; + bool enabled = false, do_action = false; + + /* check if ars object id needs updating or should be removed */ + if (isSetImplemented(SAI_OBJECT_TYPE_LAG, SAI_LAG_ATTR_ARS_OBJECT_ID)) + { + if (isGetImplemented(SAI_OBJECT_TYPE_LAG, SAI_LAG_ATTR_ARS_OBJECT_ID)) + { + + sai_status_t status = sai_lag_api->get_lag_attribute(update->port.m_lag_id, 1, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to get ars id of lag: %s", update->port.m_alias.c_str()); + } + } + + if (lag_object != m_arsLags.end()) + { + if (attr.value.u32 != lag_object->second->m_sai_ars_id) + { + attr.value.u32 = lag_object->second->m_sai_ars_id; + do_action = true; + } + } + else if (enabled) + { + if (attr.value.u32 != SAI_NULL_OBJECT_ID) + { + attr.value.u32 = SAI_NULL_OBJECT_ID; + do_action = true; + } + } + if (do_action) + { + sai_status_t status = sai_lag_api->set_lag_attribute(update->port.m_lag_id, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set pvid %u to lag: %s", attr.value.u32, update->port.m_alias.c_str()); + } + addLagToFlexCounter(update->port.m_lag_id, update->port.m_alias); + } + } + /* check if this is lag member */ + if (update->port->m_lag_member_id) + { + if ((lag_object != m_arsLags.end()) && isSetImplemented(SAI_OBJECT_TYPE_PORT, SAI_PORT_ATTR_ARS_ENABLE)) + { + bool enable = (lag_object.second->altPathPorts.find(update->port->m_alias)) ? true : false; + gPortsOrch->setPortArsAltPath(update->port->m_alias, enable); + } + } + } + } + break; + } + case SUBJECT_TYPE_NEXTHOP_CHANGE: + { + NextHopUpdate *update = reinterpret_cast(cntx); + /* verify that it is a ARS NHG */ + std::string vrf_name = m_vrfOrch->getVRFname(update->vrf_id); + if (vrf_id == gVirtualRouterId) + { + vrf_name = "default"; + } + if (vrf_name == "") + { + break; + } + auto nhg_table = m_arsNexthopGroupPrefixes.find(vrf_name); + if (nhg_table == m_arsNexthopGroupPrefixes.end()) + { + break; + } + auto ars_object = nhg_table->second.find(prefix); + if (ars_object == nhg_table->second.end()) + { + break; + } + /* don't rely on reported nhg - use cached info */ + auto nhg_sai_id = gRouteOrch->getNextHopGroupId(update->nexthopGroup); + if (nhg_sa_id == SAI_NULL_OBJECT_ID) + { + break; + } + if (update->nexthopGroup.getSize() > 1) + { + if (gRouteOrch->updateNexthopGroupArsState(nhg_sai_id, ars_object->second.m_ars_object_id)) + { + addNhgToFlexCounter(nhg_sai_id, ip_prefix, vrf_name); + } + gRouteOrch->updateNexthopArsState(update->nexthopGroup, ars_object->second.altPathNexthops); + } + break; + } + default: + break; + } +} + +bool ArsOrch::bake() +{ + SWSS_LOG_ENTER(); + + if (!m_isArsSupported) + { + SWSS_LOG_NOTICE("ARS not supported - no action"); + return true; + } + + SWSS_LOG_NOTICE("Warm reboot: placeholder"); + + return Orch::bake(); +} + +bool ArsOrch::createArsProfile(ArsProfileEntry &profile, vector ars_attrs) +{ + SWSS_LOG_ENTER(); + + sai_attribute_t ars_attr; + sai_status_t status = SAI_STATUS_NOT_SUPPORTED; + vector supported_ars_attrs; + + /* go over set of attr and set only supported attributes */ + for (auto a : ars_attrs) + { + if (isCreateImplemented(SAI_OBJECT_TYPE_ARS_PROFILE, a.id)) + { + supported_ars_attrs.push_back(a); + SWSS_LOG_NBOTICE("ARS profile %s (oid %" PRIx64 "). Setting Attr %d value %lu", + profile->profile_name.c_str(), object->ars_object_id, a.id, a.value.u32); + } + else + { + if (a.id == SAI_ARS_PROFILE_ATTR_ENABLE_IPV4 || a.id == SAI_ARS_PROFILE_ATTR_ENABLE_IPV6) + { + SWSS_LOG_WARN("Setting Attr %d is not supported. Failed to set ARS profile %s (oid %" PRIx64 ") value %s", + a.id, profile->profile_name.c_str(), a.value.booldata ? "true" : "false"); + } + else + { + SWSS_LOG_WARN("Setting Attr %d is not supported. Failed to set ARS profile %s (oid %" PRIx64 ") value %lu", + a.id, profile->profile_name.c_str(), a.value.u32); + } + continue; + } + } + + if (supported_ars_attrs.empty()) + { + SWSS_LOG_WARN("No supported attributes found for ARS profile %s", profile->profile_name.c_str()); + return false; + } + + status = sai_ars_api->create_ars_profile(&profile->m_sai_ars_id, + gSwitchId, + (uint32_t)supported_ars_attrs.size(), + supported_ars_attrs.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create ars profile %s: %d", profile->profile_name.c_str(), status); + task_process_status handle_status = handleSaiSetStatus(SAI_API_ARS_PROFILE, status); + if (handle_status != task_success) + { + return parseHandleSaiStatusFailure(handle_status); + } + } + + return true; +} + +bool ArsOrch::setArsProfile(ArsProfileEntry &profile, vector ars_attrs) +{ + SWSS_LOG_ENTER(); + + sai_attribute_t ars_attr; + sai_status_t status = SAI_STATUS_NOT_SUPPORTED; + + /* go over set of attr and set only supported attributes */ + for (auto a : ars_attrs) + { + if (!isSetImplemented(SAI_OBJECT_TYPE_ARS_PROFILE, a.id)) + { + if (a.id == SAI_ARS_PROFILE_ATTR_ENABLE_IPV4 || a.id == SAI_ARS_PROFILE_ATTR_ENABLE_IPV6) + { + SWSS_LOG_WARN("Setting Attr %d is not supported. Failed to set ARS profile %s (oid %" PRIx64 ") value %s", + a.id, profile->profile_name.c_str(), a.value.booldata ? "true" : "false"); + } + else + { + SWSS_LOG_WARN("Setting Attr %d is not supported. Failed to set ARS profile %s (oid %" PRIx64 ") value %lu", + a.id, profile->profile_name.c_str(), profile->m_sai_ars_id, a.value.u32); + } + continue; + } + + status = sai_ars_api->set_ars_profile_attribute(profile->m_sai_ars_id, &a); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set ars profile %s (oid %" PRIx64 ") attr %d: %d", + object->name.c_str(), object->ars_object_id, a.id, status); + task_process_status handle_status = handleSaiSetStatus(SAI_API_ARS, status); + if (handle_status != task_success) + { + return parseHandleSaiStatusFailure(handle_status); + } + } + } + + return true; +} + +bool ArsOrch::createArsObject(ArsObjectEntry &object, vector ars_attrs) +{ + SWSS_LOG_ENTER(); + + sai_attribute_t ars_attr; + sai_status_t status = SAI_STATUS_NOT_SUPPORTED; + vector supported_ars_attrs; + + /* go over set of attr and set only supported attributes */ + for (auto a : ars_attrs) + { + if (isCreateImplemented(SAI_OBJECT_TYPE_ARS, a.id)) + { + supported_ars_attrs.push_back(a); + SWSS_LOG_NBOTICE("ARS %s. Setting Attr %d value %lu", + object->name.c_str(), a.id, a.value.u32); + } + else + { + SWSS_LOG_WARN("Setting Attr %d is not supported. Failed to set ARS %s value %lu", + a.id, object->name.c_str(), a.value.u32); + continue; + } + } + + if (supported_ars_attrs.empty()) + { + SWSS_LOG_WARN("No supported attributes found for ARS %s", object->name.c_str()); + return false; + } + + status = sai_ars_api->create_ars(&object->ars_object_id, + gSwitchId, + (uint32_t)supported_ars_attrs.size(), + supported_ars_attrs.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create ars %s: %d", object->name.c_str(), status); + task_process_status handle_status = handleSaiSetStatus(SAI_API_ARS, status); + if (handle_status != task_success) + { + return parseHandleSaiStatusFailure(handle_status); + } + } + + return true; +} + +bool ArsOrch::setArsObject(ArsObjectEntry &object, vector ars_attrs) +{ + SWSS_LOG_ENTER(); + + sai_attribute_t ars_attr; + sai_status_t status = SAI_STATUS_NOT_SUPPORTED; + + /* go over set of attr and set only supported attributes */ + for (auto a : ars_attrs) + { + if (!isSetImplemented(SAI_OBJECT_TYPE_ARS, a.id)) + { + SWSS_LOG_WARN("Setting Attr %d is not supported. Failed to set ARS %s (oid %" PRIx64 ") value %lu", + a.id, object->name.c_str(), object->ars_object_id, a.value.u32); + continue; + } + + status = sai_ars_api->set_ars_attribute(object->ars_object_id, &a); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set ars %s (oid %" PRIx64 ") attr %d: %d", + object->name.c_str(), object->ars_object_id, a.id, status); + task_process_status handle_status = handleSaiSetStatus(SAI_API_ARS, status); + if (handle_status != task_success) + { + return parseHandleSaiStatusFailure(handle_status); + } + } + } + + return true; +} + +bool ArsOrch::delArsObject(ArsObjectEntry &object) +{ + SWSS_LOG_ENTER(); + + sai_status_t status = SAI_STATUS_NOT_SUPPORTED; + + status = sai_ars_api->remove_ars(object->ars_object_id); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove ars %s (oid %" PRIx64 ")", + object->name.c_str(), object->ars_object_id, status); + task_process_status handle_status = handleSaiSetStatus(SAI_API_ARS, status); + if (handle_status != task_success) + { + return parseHandleSaiStatusFailure(handle_status); + } + } + + return true; +} + +bool ArsOrch::updateArsEnabledInterface(const Port &port, const bool is_enable) +{ + SWSS_LOG_ENTER(); + + if (isSetImplemented(SAI_OBJECT_TYPE_PORT, SAI_PORT_ATTR_ARS_ENABLE)) + { + if (!gPortsOrch->setPortArsEnable(port, is_enable)) + { + SWSS_LOG_ERROR("Failed to set ars enable for port %s", port.m_alias.c_str()); + return false; + } + } + + if (isSetImplemented(SAI_OBJECT_TYPE_PORT, SAI_PORT_ATTR_ARS_PORT_LOAD_SCALING_FACTOR)) + { + if (is_enable && !gPortsOrch->setPortArsLoadScaling(port)) + { + SWSS_LOG_ERROR("Failed to set ars load scaling factor for port %s", port.m_alias.c_str()); + return false; + } + } + + SWSS_LOG_NOTICE("Interface %s - %sable ARS on interface", + port.m_alias.c_str(), + is_enable ? "en" : "dis"); + + return true; +} + +void ArsOrch::doTaskArsProfile(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + 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(consumer.getConsumerTable()->getTableNameSeparator().c_str()); + string table_id = key.substr(0, found); + string ars_profile_name = key.substr(found + 1); + string op = kfvOp(t); + + uint32_t max_flows = 0, sampling_interval = 0, past_load_min_val = 0, past_load_max_val = 0, past_load_weight = 0, band_min_value = 0, band_max_value = 0; + uint32_t future_load_min_val = 0, future_load_max_val = 0, future_load_weight = 0, current_load_min_val = 0, current_load_max_val = 0; + bool ipv6_enable = false, ipv4_enable = false, band_min_value_found, band_max_value_found; + map quantization_bands; + ArsAlgorithm algo = ARS_ALGORITHM_EWMA; + + bool is_new_entry = false; + sai_attribute_t ars_attr; + vector ars_attrs; + + SWSS_LOG_NOTICE("OP: %s, Profile: %s", op.c_str(), ars_profile_name.c_str()); + + auto arsProfile_entry = m_arsProfiles.find(ars_profile_name); + + if (op == SET_COMMAND) + { + vector fvVector; + FieldValueTuple name("profile_name", ars_profile_name); + fvVector.push_back(name); + + if (arsProfile_entry != m_arsProfiles.end()) + { + max_flows = arsProfile_entry->second.max_flows; + algo = arsProfile_entry->second.algorithm; + sampling_interval = arsProfile_entry->second.sampling_interval; + ipv4_enable = arsProfile_entry->second.ipv4_enable; + ipv6_enable = arsProfile_entry->second.ipv6_enable; + past_load_min_val = arsProfile_entry->second.past_load_min_val; + past_load_max_val = arsProfile_entry->second.past_load_max_val; + past_load_weight = arsProfile_entry->second.past_load_weight; + future_load_min_val = arsProfile_entry->second.future_load_min_val; + future_load_max_val = arsProfile_entry->second.future_load_max_val; + future_load_weight = arsProfile_entry->second.future_load_weight; + current_load_min_val = arsProfile_entry->second.current_load_min_val; + current_load_max_val = arsProfile_entry->second.current_load_max_val; + } + + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == ARS_FIELD_NAME_MAX_FLOWS) + { + max_flows = stoi(fvValue(i)); + ars_attr.id = SAI_ARS_PROFILE_ATTR_MAX_FLOWS; + ars_attr.value.u32 = max_flows; + ars_attrs.push_back(ars_attr); + } + else if (fvField(i) == ARS_FIELD_NAME_ALGORITHM) + { + algo = (ArsAlgorithm)stoi(fvValue(i)); + ars_attr.id = SAI_ARS_PROFILE_ATTR_ALGO; + ars_attr.value.u32 = algo; + ars_attrs.push_back(ars_attr); + } + else if (fvField(i) == ARS_FIELD_NAME_SAMPLE_INTERVAL) + { + sampling_interval = stoi(fvValue(i)); + ars_attr.id = SAI_ARS_PROFILE_ATTR_SAMPLING_INTERVAL; + ars_attr.value.u32 = sampling_interval; + ars_attrs.push_back(ars_attr); + } + else if (fvField(i) == ARS_FIELD_NAME_IPV4_ENABLE) + { + ipv4_enable = (fvValue(i) == "true"); + ars_attr.id = SAI_ARS_PROFILE_ATTR_ENABLE_IPV4; + ars_attr.value.booldata = ipv4_enable; + ars_attrs.push_back(ars_attr); + } + else if (fvField(i) == ARS_FIELD_NAME_IPV6_ENABLE) + { + ipv6_enable = (fvValue(i) == "true"); + ars_attr.id = SAI_ARS_PROFILE_ATTR_ENABLE_IPV6; + ars_attr.value.booldata = ipv6_enable; + ars_attrs.push_back(ars_attr); + } + else if (fvField(k) == ARS_FIELD_NAME_PAST_LOAD_MIN_VALUE) + { + past_load_min_val = stoi(fvValue(k)); + ars_attr.id = SAI_ARS_PROFILE_ATTR_LOAD_PAST_MIN_VAL; + ars_attr.value.u32 = past_load_min_val; + ars_attrs.push_back(ars_attr); + } + else if (fvField(k) == ARS_FIELD_NAME_PAST_LOAD_MAX_VALUE) + { + past_load_max_val = stoi(fvValue(k)); + ars_attr.id = SAI_ARS_PROFILE_ATTR_LOAD_PAST_MAX_VAL; + ars_attr.value.u32 = past_load_max_val; + ars_attrs.push_back(ars_attr); + } + else if (fvField(k) == ARS_FIELD_NAME_PAST_LOAD_WEIGHT) + { + past_load_weight = stoi(fvValue(k)); + ars_attr.id = SAI_ARS_PROFILE_ATTR_PORT_LOAD_PAST_WEIGHT; + ars_attr.value.u32 = past_load_weight; + ars_attrs.push_back(ars_attr); + } + else if (fvField(k) == ARS_FIELD_NAME_FUTURE_LOAD_MIN_VALUE) + { + future_load_min_val = stoi(fvValue(k)); + ars_attr.id = SAI_ARS_PROFILE_ATTR_LOAD_FUTURE_MIN_VAL; + ars_attr.value.u32 = future_load_min_val; + ars_attrs.push_back(ars_attr); + } + else if (fvField(k) == ARS_FIELD_NAME_FUTURE_LOAD_MAX_VALUE) + { + future_load_max_val = stoi(fvValue(k)); + ars_attr.id = SAI_ARS_PROFILE_ATTR_LOAD_FUTURE_MAX_VAL; + ars_attr.value.u32 = future_load_max_val; + ars_attrs.push_back(ars_attr); + } + else if (fvField(k) == ARS_FIELD_NAME_FUTURE_LOAD_WEIGHT) + { + future_load_weight = stoi(fvValue(k)); + ars_attr.id = SAI_ARS_PROFILE_ATTR_PORT_LOAD_FUTURE_WEIGHT; + ars_attr.value.u32 = future_load_weight; + ars_attrs.push_back(ars_attr); + } + else if (fvField(k) == ARS_FIELD_NAME_CURRENT_LOAD_MIN_VALUE) + { + current_load_min_val = stoi(fvValue(k)); + ars_attr.id = SAI_ARS_PROFILE_ATTR_LOAD_CURRENT_MIN_VAL; + ars_attr.value.u32 = current_load_min_val; + ars_attrs.push_back(ars_attr); + } + else if (fvField(k) == ARS_FIELD_NAME_CURRENT_LOAD_MAX_VALUE) + { + current_load_max_val = stoi(fvValue(k)); + ars_attr.id = SAI_ARS_PROFILE_ATTR_LOAD_CURRENT_MAX_VAL; + ars_attr.value.u32 = current_load_max_val; + ars_attrs.push_back(ars_attr); + } + else + { + SWSS_LOG_WARN("Received unsupported field %s", fvField(i).c_str()); + break; + } + FieldValueTuple value(fvField(i), fvValue(i)); + fvVector.push_back(value); + } + + if (arsProfile_entry == arsProfile_entry.end()) + { + ArsProfileEntry arsProfileEntry; + arsProfileEntry.profile_name = ars_profile_name; + arsProfile_entry[ars_profile_name] = arsObjectEntry; + arsProfile_entry = m_arsProfiles.find(ars_profile_name); + SWSS_LOG_NOTICE("Added new ARS profile %s", ars_profile_name.c_str()); + is_new_entry = true; + } + + arsProfile_entry->second.max_flows = max_flows; + arsProfile_entry->second.algorithm = algo; + arsProfile_entry->second.sampling_interval = sampling_interval; + arsProfile_entry->second.ipv4_enable = ipv4_enable; + arsProfile_entry->second.ipv6_enable = ipv6_enable; + arsProfile_entry->second.past_load_min_val = past_load_min_val; + arsProfile_entry->second.past_load_max_val = past_load_max_val; + arsProfile_entry->second.past_load_weight = past_load_weight; + arsProfile_entry->second.future_load_min_val = future_load_min_val; + arsProfile_entry->second.future_load_max_val = future_load_max_val; + arsProfile_entry->second.future_load_weight = future_load_weight; + arsProfile_entry->second.current_load_min_val = current_load_min_val; + arsProfile_entry->second.current_load_max_val = current_load_max_val; + + if (is_new_entry) + { + createArsProfile(arsProfile_entry->second, ars_attrs); + } + else + { + setArsProfile(arsProfile_entry->second, ars_attrs); + } + + m_arsProfileStateTable.set(ars_profile_name, fvVector); + } + else if (op == DEL_COMMAND) + { + if (arsProfile_entry == m_arsProfiles.end()) + { + SWSS_LOG_NOTICE("Received delete call for non-existent entry %s", ars_profile_name.c_str()); + } + else + { + /* Check if there are no child objects associated prior to deleting */ + if (arsProfile_entry->second.ars_objects.empty() && + m_arsEnabledInterfaces.empty()) + { + SWSS_LOG_INFO("Received delete call for valid entry with no further dependencies, deleting %s", + ars_object_namars_profile_namee.c_str()); + } + else + { + SWSS_LOG_NOTICE("Child Prefix/Member entries are still associated with this ARS profile %s", + ars_profile_name.c_str()); + continue; + } + + m_arsProfiles.erase(arsProfile_entry); + m_arsProfileStateTable.del(ars_profile_name); + } + } + + it = consumer.m_toSync.erase(it); + } +} + +void ArsOrch::doTaskArsQuantizationBands(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + 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(consumer.getConsumerTable()->getTableNameSeparator().c_str()); + string key_without_table = key.substr(found + 1); + size_t found = key_without_table.find(consumer.getConsumerTable()->getTableNameSeparator().c_str()); + string ars_profile_name = key_without_table.substr(0, found); + uint8_t band_index = stoi(key_without_table.substr(found + 1)); + string op = kfvOp(t); + map quantization_bands; + uint32_t band_min_value = 0, band_max_value = 0; + + sai_attribute_t ars_attr; + vector ars_attrs; + + SWSS_LOG_NOTICE("OP: %s, Profile: %s Index: %d", op.c_str(), ars_profile_name.c_str(), index); + + auto arsProfile_entry = m_arsProfiles.find(ars_profile_name); + if (arsProfile_entry == m_arsProfiles.end()) + { + SWSS_LOG_WARN("Received ARS quantization bands configuration for non-existent profile %s", ars_profile_name.c_str()); + continue; + } + + if (op == SET_COMMAND) + { + vector fvVector; + + quantization_bands = arsProfile_entry->second.quantization_bands; + + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == ARS_FIELD_NAME_MIN_VALUE) + { + band_min_value = stoi(fvValue(k)); + ars_attr.id = SAI_ARS_PROFILE_ATTR_QUANT_BAND_0_MIN_THRESHOLD + (band_index * 2); + ars_attr.value.u32 = band_min_value; + ars_attrs.push_back(ars_attr); + } + else if (fvField(i) == ARS_FIELD_NAME_MAX_VALUE) + { + band_max_value = stoi(fvValue(k)); + ars_attr.id = SAI_ARS_PROFILE_ATTR_QUANT_BAND_0_MAX_THRESHOLD + (band_index * 2); + ars_attr.value.u32 = band_max_value; + ars_attrs.push_back(ars_attr); + } + else + { + SWSS_LOG_WARN("Received unsupported quantization bands field %s", fvField(k).c_str()); + continue; + } + FieldValueTuple value("ARS_QUANTIZATION_BANDS_" + std::to_string(band_index) + "_" + fvField(i), fvValue(i)); + fvVector.push_back(value); + } + + if (!ars_attrs.emtpy()) + { + quantization_bands.insert({band_index, {band_min_value, band_max_value}}); + } + + arsProfile_entry->second.quantization_bands = quantization_bands; + m_arsProfileStateTable.set(ars_profile_name, fvVector); + + setArsProfile(arsProfile_entry->second, ars_attrs); + } + else if (op == DEL_COMMAND) + { + arsProfile_entry->second.quantization_bands.erase(band_index); + for (auto i = 0; i < 8; i ++) + { + m_arsProfileStateTable.hdel(ars_profile_name, "ARS_QUANTIZATION_BANDS_" + std::to_string(i) + "_" + ARS_FIELD_NAME_MIN_VALUE); + m_arsProfileStateTable.hdel(ars_profile_name, "ARS_QUANTIZATION_BANDS_" + std::to_string(i) + "_" + ARS_FIELD_NAME_MAX_VALUE); + } + } + + it = consumer.m_toSync.erase(it); + } +} + +bool ArsOrch::doTaskArsInterfaces(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + if (m_arsProfiles.empty()) + { + SWSS_LOG_WARN("No ARS profiles exist"); + return false; + } + + 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(consumer.getConsumerTable()->getTableNameSeparator().c_str()); + string table_id = key.substr(0, found); + string if_name = key.substr(found + 1); + string op = kfvOp(t); + bool is_new_entry = false; + sai_attribute_t sai_attr; + vector ars_attrs; + Port p; + + SWSS_LOG_NOTICE("ARS Path Op %s Interface %s", op.c_str(), if_name.c_str()); + + if (op == SET_COMMAND) + { + if (m_arsEnabledInterfaces.find(if_name) != m_arsEnabledInterfaces.end()) + { + SWSS_LOG_WARN("Tried to add already added interface %s - skipped", if_name.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + m_arsEnabledInterfaces.insert(if_name); + SWSS_LOG_NOTICE("Added new interface %s to ARS entry %s", + if_name.c_str(), profile_name.c_str()); + } + else if (op == DEL_COMMAND) + { + if (m_arsEnabledInterfaces.find(if_name) == m_arsEnabledInterfaces.end()) + { + SWSS_LOG_INFO("Received delete call for non-existent interface %s", if_name.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + else + { + m_arsEnabledInterfaces.erase(if_name); + SWSS_LOG_INFO("Removed interface %s", if_name.c_str()); + } + } + + if (!gPortsOrch->getPort(if_name, p) || p.m_port_id == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_WARN("Tried to %s non-existent/down interface %s - skipped", op,c_str(), if_name.c_str()); + } + else + { + updateArsEnabledInterface(p, (op == SET_COMMAND)); + } + + it = consumer.m_toSync.erase(it); + } + + string ifnames = ""; + for (auto ifname : m_arsEnabledInterfaces) + { + ifnames += ifname + " "; + } + FieldValueTuple tmp("ifname", ifnames); + vector fvVector; + fvVector.push_back(tmp); + m_arsIfStateTable.set("", fvVector); + + return true; +} + +bool ArsOrch::doTaskArsNexthopGroup(const KeyOpFieldsValuesTuple & t) +{ + SWSS_LOG_ENTER(); + + 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(consumer.getConsumerTable()->getTableNameSeparator().c_str()); + IpPrefix ip_prefix = IpPrefix(key.substr(0, found)); + string vrf_name = key.substr(found + 1); + string op = kfvOp(t); + uint32_t max_flows = 0, flowlet_idle_time = 0, primary_path_threshold = 0, alt_path_cost = 0; + ArsAssignMode assign_mode = PER_FLOWLET_QUALITY; + bool is_new_entry = false; + sai_attribute_t ars_attr; + vector ars_attrs; + string ars_profile_name = ""; + + SWSS_LOG_NOTICE("ARS Prefix Op %s, Profile: %s, Vrf %s, Prefix %s", op.c_str(), ars_profile_name.c_str(), vrf_name.c_str(), ip_prefix.to_string().c_str()); + + auto table = m_arsNexthopGroupPrefixes.find(vrf_name); + sai_object_id_t &vrf_id = m_vrfOrch->getVRFid(vrf_name); + NextHopGroupKey nhg = gRouteOrch->getSyncdRouteNhgKey(vrf_id, ip_prefix); + + ArsObjectEntry *arsObject_entry = nullptr; + if (op == SET_COMMAND) + { + vector fvVector; + FieldValueTuple name("profile_name", ars_profile_name); + fvVector.push_back(name); + if (table == m_arsNexthopGroupPrefixes.end()) + { + is_new_entry = true; + ArsNexthopGroupPrefixes nhg_table; + ArsObjectEntry arsObjectEntry; + arsObjectEntry.profile_name = ars_profile_name; + nhg_table[ip_prefix] = arsObjectEntry; + auto temp = nhg_table.find(ip_prefix); + m_arsNexthopGroupPrefixes[vrf_name] = temp; + table = m_arsNexthopGroupPrefixes.find(vrf_name); + } + + arsObject_entry = table.find(ip_prefix); + + if (arsObject_entry == table.end()) + { + is_new_entry = true; + ArsObjectEntry arsObjectEntry; + arsObject_entry = table.find(ip_prefix); + table[ip_prefix] = arsObjectEntry; + } + + std::set altPathInterfaces; + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == ARS_FIELD_NAME_PROFILE_NAME) + { + ars_profile_name = fvValue(i); + arsObject_entry->second.profile_name = ars_profile_name; + } + else if (fvField(i) == ARS_FIELD_NAME_MAX_FLOWS) + { + arsObject_entry->second.max_flows = stoi(fvValue(i)); + ars_attr.id = SAI_ARS_ATTR_MAX_FLOWS; + ars_attr.value.u32 = max_flows; + ars_attrs.push_back(ars_attr); + } + else if (fvField(i) == ARS_FIELD_NAME_IDLE_TIME) + { + arsObject_entry->second.flowlet_idle_time = stoi(fvValue(i)); + ars_attr.id = SAI_ARS_ATTR_IDLE_TIME; + ars_attr.value.u32 = flowlet_idle_time; + ars_attrs.push_back(ars_attr); + } + else if (fvField(i) == ARS_FIELD_NAME_ASSIGN_MODE) + { + arsObject_entry->second.assign_mode = PER_FLOWLET_QUALITY; + ars_attr.id = SAI_ARS_ATTR_MODE; + ars_attr.value.u32 = SAI_ARS_MODE_FLOWLET_QUALITY; + if (fvValue(i) == ARS_FIELD_NAME_PER_PACKET) + { + arsObject_entry->second.assign_mode = PER_PACKET; + ars_attr.value.u32 = SAI_ARS_MODE_PER_PACKET_QUALITY; + } + else if (fvValue(i) != ARS_FIELD_NAME_PER_FLOWLET) + { + SWSS_LOG_WARN("Received unsupported assign_mode %s, defaulted to per_flowlet_quality", + fvValue(i).c_str()); + break; + } + ars_attrs.push_back(ars_attr); + } + else if (fvField(i) == ARS_FIELD_NAME_PRIMARY_PATH_THRESHOLD) + { + arsObject_entry->second.path_quality.primary_threshold = stoi(fvValue(i)); + ars_attr.id = SAI_ARS_ATTR_PRIMARY_PATH_QUALITY_THRESHOLD; + ars_attr.value.u32 = primary_path_threshold; + ars_attrs.push_back(ars_attr); + } + else if (fvField(i) == ARS_FIELD_NAME_ALT_PATH_COST) + { + arsObject_entry->second.path_quality.alt_threshold = stoi(fvValue(i)); + ars_attr.id = SAI_ARS_ATTR_ALTERNATE_PATH_COST; + ars_attr.value.u32 = alt_path_cost; + ars_attrs.push_back(ars_attr); + } + else if (fvField(i) == ARS_FIELD_NAME_ALT_PATH_MEMBERS) + { + const auto &members = tokenize(fvFieldValues(i), ','); + for (auto &m : members) + { + altPathInterfaces.push_back(IpAddress(m)); + } + } + else + { + SWSS_LOG_WARN("Received unsupported field %s", fvField(i).c_str()); + continue; + } + if (fvField(i) != ARS_FIELD_NAME_ALT_PATH_MEMBERS) + { + FieldValueTuple value(fvField(i), fvValue(i)); + fvVector.push_back(value); + } + } + + auto arsProfile_entry = m_arsProfiles.find(ars_profile_name); + if (arsProfile_entry == m_arsProfiles.end()) + { + SWSS_LOG_WARN("Received ARS NHG Vrf %s, Prefix %s for non-existent profile %s", vrf_name.c_str(), ip_prefix.to_string().c_str(), ars_profile_name.c_str()); + continue; + } + + if (is_new_entry) + { + arsObject_entry->second.profile_name = ars_profile_name; + } + + arsObject_entry->second.altPathNexthops = altPathInterfaces; + arsObject_entry->second.prefixes.push_back(ip_prefix.to_string()); + + FieldValueTuple tmp("nexthops", nhg.to_string()); + fvVector.push_back(tmp); + + if (is_new_entry) + { + createArsObject(arsObject_entry, ars_attrs); + gRouteOrch->attach(this, ip_prefix.getIp(), vrf_id); + arsProfile_entry->second.ref_count++; + } + else + { + setArsObject(arsObject_entry, ars_attrs); + } + + SWSS_LOG_NOTICE("Ars entry added for profile %s, prefix %s, NHs", ars_profile_name.c_str(), ip_prefix.to_string().c_str(), nhg.to_string().c_str()); + + m_arsNhgStateTable.set(vrf_name + "_" + ip_prefix.to_string(), fvVector); + + if (nhg.getSize() > 0) + { + /* Handling ARS over already configured nexthop groups */ + /* Enable ARS over NHG */ + auto nhg_sai_id = gRouteOrch->getNextHopGroupId(nhg); + if (nhg_sai_id) + { + if (!isSetImplemented(SAI_OBJECT_TYPE_NEXT_HOP_GROUP, SAI_NEXT_HOP_GROUP_ATTR_ARS_OBJECT_ID)) + { + SWSS_LOG_NOTICE("Remove existing NHG and create ARS-enabled"); + if (gRouteOrch->reconfigureNexthopGroupWithArsState(nhg, nhg_sai_id, arsObject_entry->second.m_ars_object_id)) + { + addNhgToFlexCounter(nhg_sai_id, ip_prefix, vrf_name); + } + } + else if (gRouteOrch->updateNexthopGroupArsState(nhg_sai_id, arsObject_entry->second.m_ars_object_id)) + { + addNhgToFlexCounter(nhg_sai_id, ip_prefix, vrf_name); + } + } + /* enable ARS alternative path over all nexthops in group */ + gRouteOrch->updateNexthopArsState(nhg, arsObject_entry->second.altPathNexthops); + } + } + else if (op == DEL_COMMAND) + { + auto table = m_arsNexthopGroupPrefixes.find(vrf_name); + if (table == m_arsNexthopGroupPrefixes.end() || table.find(ip_prefix) == table.end()) + { + SWSS_LOG_NOTICE("ARS_NHG_PREFIX doesn't exists, ignore"); + it = consumer.m_toSync.erase(it); + continue; + } + else if (!isSetImplemented(SAI_OBJECT_TYPE_NEXT_HOP_GROUP, SAI_NEXT_HOP_GROUP_ATTR_ARS_OBJECT_ID)) + { + SWSS_LOG_NOTICE("Disabling ARS NHG is not supported"); + it = consumer.m_toSync.erase(it); + continue; + } + else + { + arsObject_entry = table.find(ip_prefix); + if (arsObject->second.m_ars_object_id) + { + /* handle existing nexthop group */ + if (nhg.getSize() > 0) + { + /* Disable ARS over all nexthops in group */ + gRouteOrch->updateNexthopArsState(nhg, {}); + /* Disable ARS over NHG */ + auto sai_id = gRouteOrch->getNextHopGroupId(nhg); + if (sai_id) + { + removeNhgFromFlexCounter(sai_id, ip_prefix, vrf_name); + gRouteOrch->updateNexthopGroupArsState(sai_id, SAI_NULL_OBJECT_ID); + } + } + gRouteOrch->detach(this, ip_prefix.getIp(), vrf_id); + + /* remove ars sai object */ + delArsObject(arsObject_entry->second.m_ars_object_id); + } + + arsObject_entry->second->prefixes.erase(ip_prefix.to_string()); + table.erase(ip_prefix); + if (table.empty()) + { + m_arsNexthopGroupPrefixes.erase(vrf_name); + } + SWSS_LOG_NOTICE("Ars entry removed for profile %s, vrf_name %s,prefix %s", ars_profile_name.c_str(), vrf_name.c_str(), ip_prefix.to_string().c_str()); + + m_arsNhgStateTable.erase(vrf_name + "_" + ip_prefix.to_string()); + + auto arsProfile_entry = m_arsProfiles.find(arsObject_entry->second.profile_name); + if (arsProfile_entry == m_arsProfiles.end()) + { + SWSS_LOG_ERROR("Received ARS NHG Vrf %s, Prefix %s for non-existent profile %s", vrf_name.c_str(), ip_prefix.to_string().c_str(), arsObject_entry->second.profile_name.c_str()); + } + else + { + arsProfile_entry->second.ref_count--; + } + } + } + + it = consumer.m_toSync.erase(it); + } + + return true; +} + + +bool ArsOrch::doTaskArsLag(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + 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(consumer.getConsumerTable()->getTableNameSeparator().c_str()); + string if_name = key.substr(0, found); + string op = kfvOp(t); + uint32_t max_flows = 0, flowlet_idle_time = 0, primary_path_threshold = 0, alt_path_cost = 0; + ArsAssignMode assign_mode = PER_FLOWLET_QUALITY; + bool is_new_entry = false; + sai_attribute_t ars_attr; + vector ars_attrs; + Port p; + string ars_profile_name = ""; + + SWSS_LOG_NOTICE("ARS Lag Op %s, Profile: %s, name %s", op.c_str(), ars_profile_name.c_str(), if_name.c_str()); + + auto table = m_arsLags.find(if_name); + + ArsObjectEntry *arsObject_entry = nullptr; + if (op == SET_COMMAND) + { + if (table == m_arsLags.end()) + { + is_new_entry = true; + ArsObjectEntry arsObjectEntry; + arsObjectEntry.profile_name = ars_profile_name; + m_arsLags[if_name] = arsObjectEntry; + } + + arsObject_entry = table.find(if_name); + + if (arsObject_entry == table.end()) + { + is_new_entry = true; + ArsObjectEntry arsObjectEntry; + arsObjectEntry.profile_name = ars_profile_name; + table[if_name] = arsObjectEntry; + arsObject_entry = table.find(if_name); + } + + std::set altPathInterfaces; + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == ARS_FIELD_NAME_PROFILE_NAME) + { + ars_profile_name = fvValue(i); + arsObject_entry->second.profile_name = ars_profile_name; + } + else if (fvField(i) == ARS_FIELD_NAME_MAX_FLOWS) + { + arsObject_entry->second.max_flows = stoi(fvValue(i)); + ars_attr.id = SAI_ARS_ATTR_MAX_FLOWS; + ars_attr.value.u32 = max_flows; + ars_attrs.push_back(ars_attr); + } + else if (fvField(i) == ARS_FIELD_NAME_IDLE_TIME) + { + arsObject_entry->second.flowlet_idle_time = stoi(fvValue(i)); + ars_attr.id = SAI_ARS_ATTR_IDLE_TIME; + ars_attr.value.u32 = flowlet_idle_time; + ars_attrs.push_back(ars_attr); + } + else if (fvField(i) == ARS_FIELD_NAME_ASSIGN_MODE) + { + arsObject_entry->second.assign_mode = PER_FLOWLET_QUALITY; + ars_attr.id = SAI_ARS_ATTR_MODE; + ars_attr.value.u32 = SAI_ARS_MODE_FLOWLET_QUALITY; + if (fvValue(i) == ARS_FIELD_NAME_PER_PACKET) + { + arsObject_entry->second.assign_mode = PER_PACKET; + ars_attr.value.u32 = SAI_ARS_MODE_PER_PACKET_QUALITY; + } + else if (fvValue(i) != ARS_FIELD_NAME_PER_FLOWLET) + { + SWSS_LOG_WARN("Received unsupported assign_mode %s, defaulted to per_flowlet_quality", + fvValue(i).c_str()); + break; + } + ars_attrs.push_back(ars_attr); + } + else if (fvField(i) == ARS_FIELD_NAME_PRIMARY_PATH_THRESHOLD) + { + arsObject_entry->second.path_quality.primary_threshold = stoi(fvValue(i)); + ars_attr.id = SAI_ARS_ATTR_PRIMARY_PATH_QUALITY_THRESHOLD; + ars_attr.value.u32 = primary_path_threshold; + ars_attrs.push_back(ars_attr); + } + else if (fvField(i) == ARS_FIELD_NAME_ALT_PATH_COST) + { + arsObject_entry->second.path_quality.alt_threshold = stoi(fvValue(i)); + ars_attr.id = SAI_ARS_ATTR_ALTERNATE_PATH_COST; + ars_attr.value.u32 = alt_path_cost; + ars_attrs.push_back(ars_attr); + } + else if (fvField(i) == ARS_FIELD_NAME_ALT_PATH_MEMBERS) + { + const auto &members = tokenize(fvFieldValues(i), ','); + for (auto &m : members) + { + altPathInterfaces.push_back(m); + } + } + else + { + SWSS_LOG_WARN("Received unsupported field %s", fvField(i).c_str()); + continue; + } + if (fvField(i) != ARS_FIELD_NAME_ALT_PATH_MEMBERS) + { + FieldValueTuple value(fvField(i), fvValue(i)); + fvVector.push_back(value); + } + } + + auto arsProfile_entry = m_arsProfiles.find(ars_profile_name); + + if (arsProfile_entry == m_arsProfiles.end()) + { + SWSS_LOG_WARN("Received ARS Lag name %s for non-existent profile %s", if_name.c_str(), ars_profile_name.c_str()); + continue; + } + + if (is_new_entry) + { + arsObject_entry->profile_name = ars_profile_name; + } + + vector fvVector; + FieldValueTuple name("profile_name", ars_profile_name); + fvVector.push_back(name); + + arsObject_entry->second.altPathPorts = altPathInterfaces; + arsObject_entry->second.lags.push_back(if_name); + + string ifnames = ""; + for (auto ifname : arsObject_entry->second.altPathPorts) + { + ifnames += ifname + " "; + } + FieldValueTuple tmp("alt_ports", ifnames); + vector fvVector; + fvVector.push_back(tmp); + + if (is_new_entry) + { + createArsObject(arsObject_entry, ars_attrs); + arsProfile_entry->second.ref_count++; + } + else + { + setArsObject(arsObject_entry, ars_attrs); + } + + m_lags[if_name] = arsObject_entry->second; + + m_arsLagStateTable.set(if_name, fvVector); + + SWSS_LOG_NOTICE("ARS Adding LAG %s", if_name.c_str()); + } + else if (op == DEL_COMMAND) + { + if (m_lags.find(if_name) == m_lags.end()) + { + SWSS_LOG_INFO("Received delete call for non-existent interface %s", if_name.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + else if (!isSetImplemented(SAI_OBJECT_TYPE_LAG, SAI_LAG_ATTR_ARS_OBJECT_ID)) + { + SWSS_LOG_NOTICE("Disabling ARS LAG is not supported"); + it = consumer.m_toSync.erase(it); + continue; + } + else + { + arsObject_entry = table.find(if_name); + if (arsObject->second.m_ars_object_id) + { + /* remove ars sai object */ + delArsObject(arsObject_entry->second.m_ars_object_id); + } + m_lags.erase(if_name); + auto arsProfile_entry = m_arsProfiles.find(arsObject_entry->second.profile_name); + if (arsProfile_entry == m_arsProfiles.end()) + { + SWSS_LOG_ERROR("Received ARS NHG Vrf %s, Prefix %s for non-existent profile %s", vrf_name.c_str(), ip_prefix.to_string().c_str(), arsObject_entry->second.profile_name.c_str()); + } + else + { + arsProfile_entry->second.ref_count--; + } + m_arsLagStateTable.del(if_name); + SWSS_LOG_INFO("Removed interface %s", if_name.c_str()); + } + } + + if (!gPortsOrch->getPort(if_name, p) || (p.m_lag_id == SAI_NULL_OBJECT_ID)) + { + SWSS_LOG_WARN("Tried to %s non-existent interface %s - skipped", op.c_str(), if_name.c_str()); + } + else + { + sai_attribute_t attr; + attr.id = SAI_LAG_ATTR_ARS_OBJECT_ID; + attr.value.oid = (op == SET_COMMAND) ? arsObject_entry->second.m_ars_object_id : SAI_NULL_OBJECT_ID; + sai_status_t status = sai_lag_api->set_lag_attribute(p.m_lag_id, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set ars object %" PRIx64 " to lag: %s", attr.value.oid, port.m_alias.c_str()); + } + else + { + addLagToFlexCounter(p.m_lag_id, p.m_alias); + } + + /* set lag memebers if needed */ + vector portv; + gPortsOrch->getLagMember(p, portv); + if (!portv.empty()) + { + for (auto &lag_port: portv) + { + gPortsOrch->getPort(lag_port, port); + gPortsOrch->setPortArsAltPath(port, arsObject_entry->second.altPathPorts.find(lag_port) != arsObject_entry->second.altPathPorts.end()) + } + } + } + + it = consumer.m_toSync.erase(it); + } + + return true; +} + +void ArsOrch::doTask(Consumer& consumer) +{ + SWSS_LOG_ENTER(); + const string & table_name = consumer.getTableName(); + + if (!m_isArsSupported) + { + SWSS_LOG_WARN("ARS is not supported"); + return; + } + + if (table_name == CFG_ARS_PROFILE || table_name == APP_ARS_PROFILE_TABLE_NAME) + { + doTaskArsProfile(consumer); + } + if (table_name == CFG_ARS_QUANTIZATION_BANDS || table_name == APP_ARS_QUANTIZATION_BANDS_TABLE_NAME) + { + doTaskArsQuantizationBands(consumer); + } + else if (table_name == CFG_ARS_INTERFACE || table_name == APP_ARS_INTERFACE_TABLE_NAME) + { + doTaskArsInterfaces(consumer); + } + else if (table_name == CFG_ARS_NEXT_HOP_GROUP_PREFIX || table_name == APP_ARS_NEXT_HOP_GROUP_PREFIX_TABLE_NAME) + { + doTaskArsNexthopGroup(consumer); + } + else if (table_name == CFG_ARS_PORTCHANNEL || table_name == APP_ARS_PORTCHANNEL_TABLE_NAME) + { + doTaskArsLag(consumer); + } + else + { + SWSS_LOG_ERROR("Unknown table : %s", table_name.c_str()); + } +} + +string ArsOrch::getLagFlexCounterTableKey(string key) +{ + return string(ARS_LAG_STAT_COUNTER_FLEX_COUNTER_GROUP) + ":" + key; +} + +string ArsOrch::getNhgFlexCounterTableKey(string key) +{ + return string(ARS_NHG_STAT_COUNTER_FLEX_COUNTER_GROUP) + ":" + key; +} + +std::unordered_set ArsOrch::generateLagCounterStats() +{ + std::unordered_set counter_stats; + for (const auto& it: lag_counter_stat_ids) + { + if (isGetImplemented(SAI_OBJECT_TYPE_LAG, it)) + { + counter_stats.emplace(sai_serialize_attr_id(it)); + } + } + return counter_stats; +} + +std::unordered_set ArsOrch::generateNhgCounterStats() +{ + std::unordered_set counter_stats; + for (const auto& it: nhg_counter_stat_ids) + { + if (isGetImplemented(SAI_OBJECT_TYPE_NEXT_HOP_GROUP, it)) + { + counter_stats.emplace(sai_serialize_attr_id(it)); + } + } + return counter_stats; +} + +void ArsOrch::generateLagCounterMap() +{ + if (m_isLagCounterMapGenerated) + { + return; + } + + if (m_LagFlexCounterUpdTimer) + { + m_LagFlexCounterUpdTimer->start(); + m_isLagCounterMapGenerated = true; + } +} + +void ArsOrch::generateNexthopGroupCounterMap() +{ + if (m_isNhgCounterMapGenerated) + { + return; + } + + if (m_NhgFlexCounterUpdTimer) + { + m_NhgFlexCounterUpdTimer->start(); + m_isNhgCounterMapGenerated = true; + } +} + +void ArsOrch::addLagToFlexCounter(sai_object_id_t oid, const string &name) +{ + m_pendingLagAddToFlexCntr[oid] = name; +} + +void ArsOrch::addNhgToFlexCounter(sai_object_id_t oid, const IpPrefix &prefix, const string &vrf) +{ + m_pendingNhgAddToFlexCntr[oid] = prefix.to_string() + ":" + vrf; +} + +void ArsOrch::removeLagFromFlexCounter(sai_object_id_t id, const string &name) +{ + SWSS_LOG_ENTER(); + + std::string counter_oid_str; + counter_oid_str = sai_serialize_object_id(id); + + auto update_iter = m_pendingLagAddToFlexCntr.find(id); + if (update_iter == m_pendingLagAddToFlexCntr.end()) + { + m_lag_counter_manager.clearCounterIdList(id); + } + else + { + m_lag_counter_manager.erase(update_iter); + } + + /* remove it from COUNTERS_DB maps */ + m_counter_table->hdel("", name); + m_vidToRidTable->hdel("", counter_oid_str); + + SWSS_LOG_DEBUG("Unregistered interface %s from Flex counter", name.c_str()); +} + +void ArsOrch::removeNhgFromFlexCounter(sai_object_id_t id, const IpPrefix &prefix, const string &vrf) +{ + SWSS_LOG_ENTER(); + + std::string counter_oid_str; + counter_oid_str = sai_serialize_object_id(id); + + auto update_iter = m_pendingNhgAddToFlexCntr.find(id); + if (update_iter == m_pendingNhgAddToFlexCntr.end()) + { + m_lag_counter_manager.clearCounterIdList(id); + } + else + { + m_lag_counter_manager.erase(update_iter); + } + + /* remove it from COUNTERS_DB maps */ + m_counter_table->hdel("", prefix.to_string() + ":" + vrf); + m_vidToRidTable->hdel("", counter_oid_str); + + SWSS_LOG_DEBUG("Unregistered lag %s from Flex counter", name.c_str()); +} + +void ArsOrch::doTask(SelectableTimer &timer) +{ + string value; + + if (!m_isArsSupported) + { + SWSS_LOG_INFO("ARS is not supported"); + return; + } + + if (m_LagFlexCounterUpdTimer == timer) + { + for (auto it = m_pendingLagAddToFlexCntr.begin(); it != m_pendingLagAddToFlexCntr.end(); ) + { + const auto id = sai_serialize_object_id(it->first); + if (!gTraditionalFlexCounter || m_vidToRidTable->hget("", id, value)) + { + vector lagNameVector; + + lagNameVector.emplace_back(it->second.c_str(), id); + m_counter_table->set("", lagNameVector); + m_counter_db->hset(COUNTERS_ARS_LAG_NAME_MAP, it->second.c_str(), id.c_str()); + + auto lag_counter_stats = generateLagCounterStats(); + m_lag_counter_manager.setCounterIdList(it->first, CounterType::ARS_LAG, lag_counter_stats, gSwitchId); + SWSS_LOG_DEBUG("inserted %s to flex counter", it->second.c_str()); + it = m_pendingLagAddToFlexCntr.erase(it); + } + else + { + ++it; + } + } + } + + if (m_NhgFlexCounterUpdTimer == timer) + { + for (auto it = m_pendingNhgAddToFlexCntr.begin(); it != m_pendingNhgAddToFlexCntr.end(); ) + { + const auto id = sai_serialize_object_id(it->first); + if (!gTraditionalFlexCounter || m_vidToRidTable->hget("", id, value)) + { + vector nhgNameVector; + + nhgNameVector.emplace_back(it->second.c_str(), id); + m_counter_table->set("", nhgNameVector); + m_counter_db->hset(COUNTERS_ARS_NEXTHOP_GROUP_NAME_MAP, it->second.c_str(), id.c_str()); + + auto nhg_counter_stats = generateLagCounterStats(); + m_lag_counter_manager.setCounterIdList(it->first, CounterType::ARS_NEXTHOP_GROUP, nhg_counter_stats, gSwitchId); + SWSS_LOG_DEBUG("inserted %s to flex counter", it->second.c_str()); + it = m_pendingNhgAddToFlexCntr.erase(it); + } + else + { + ++it; + } + } + } +} \ No newline at end of file diff --git a/orchagent/arsorch.h b/orchagent/arsorch.h new file mode 100644 index 0000000000..28b40330b2 --- /dev/null +++ b/orchagent/arsorch.h @@ -0,0 +1,231 @@ +#ifndef SWSS_ARSORCH_H +#define SWSS_ARSORCH_H + +#include "orch.h" +#include "observer.h" +#include "intfsorch.h" +#include "neighorch.h" +#include "producerstatetable.h" + +#include "ipaddress.h" +#include "ipaddresses.h" +#include "ipprefix.h" +#include "nexthopgroupkey.h" + +#include + +enum ArsMatchMode +{ + MATCH_ROUTE_BASED, + MATCH_NEXTHOP_BASED +}; +enum ArsAssignMode +{ + PER_FLOWLET_QUALITY, + PER_PACKET +}; + +typedef struct ArsObjectEntry +{ + std::string profile_name; // Name of ARS profile this object belong to + ArsAssignMode assign_mode; // Stores an assign_mode from ArsAssignModes + uint32_t flowlet_idle_time; // Flowlet idle time in micro seconds + uint32_t max_flows; // Maximum number of flows in a flowlet + struct + { + uint32_t primary_threshold; // Primary path threshold + uint32_t alt_threshold; // Alternate path threshold + } quality_threshold; + std::set altPathInterfaces; // ARS-Enabled interfaces which compose alternative/non-minimal path + std:set prefixes; // Prefixes pointing to ARS-enabled NHG + std::set altPathNexthops; // ARS-Enabled nexthops which compose alternative/non-minimal path + std::set(string) lags; // ARS-enabled LAGs + std::set altPathPorts; // Ports of ARS-Enabled LAGs which compose alternative/non-minimal path + sai_object_id_t ars_object_id; // ARS Object ID if already created +} ArsObjectEntry; + +typedef struct +{ + uint32_t min_value; + uint32_t max_value; +} ArsQuantizationBandValues; + +typedef enum +{ + ARS_ALGORITHM_EWMA +} ArsAlgorithm; + +typedef struct ArsProfileEntry +{ + string profile_name; // Name of ARS profile configured by user + ArsAlgorithm algorithm; // ARS algorithm + uint32_t max_flows; // Maximum number of supported flows + uint32_t sampling_interval; // Sampling interval in micro seconds + struct + { + struct { + uint32_t min_value; // Minimum value of the load + uint32_t max_value; // Maximum value of the load + uint32_t weight; // Weight of the metric + } past_load; + + struct { + uint32_t min_value; // Minimum value of the load + uint32_t max_value; // Maximum value of the load + } current_load; + + struct { + uint32_t min_value; // Minimum value of the load + uint32_t max_value; // Maximum value of the load + uint32_t weight; // Weight of the metric + } future_load; + } path_metrics; + + map quantization_bands; // Quantization bands + + bool ipv4_enabled; // Enable IPv4 + bool ipv6_enabled; // Enable IPv6 + uint32_t ref_count; // Number of objects using this profile + sai_object_id_t m_sai_ars_id; // SAI ARS profile OID +} ArsProfileEntry; + +/* Map from IP prefix to ARS object */ +typedef std::map ArsNexthopGroupPrefixes; +typedef std::map ArsPrefixesTables; +/* Map from LAG name to ARS object */ +typedef std::map ArsLags; +/* Main structure to hold user configuration */ +typedef std::map ArsProfiles; +typedef std::map ArsObjects; +/* list of ARS-enabled Interfaces */ +typedef std::set ArsEnabledInterfaces; // ARS-Enabled interfaces for the profile + + +class ArsOrch : public Orch, public Observer +{ +public: + ArsOrch(DBConnector *db, DBConnector *appDb, DBConnector *stateDb, vector &tableNames, VRFOrch *vrfOrch); + + bool setArsProfile(ArsProfileEntry &profile, vector &ars_attrs); + bool createArsProfile(ArsProfileEntry &profile, vector &ars_attrs); + bool setArsObject(ArsObjectEntry &object, vector &ars_attrs); + bool createArsObject(ArsObjectEntry &object, vector &ars_attrs); + bool isRouteArs(sai_object_id_t vrf_id, const IpPrefix &ipPrefix, sai_object_id_t * ars_object_id, std::set &altPathMembers); + bool isLagArs(const std::string if_name, sai_object_id_t * ars_object_id, std::set &altPathMembers); + + // warm reboot support + bool bake() override; + void update(SubjectType type, void *cntx); + +protected: + VRFOrch *m_vrfOrch; + std::shared_ptr m_counter_db; + std::shared_ptr m_asic_db; + std::unique_ptr
m_lag_counter_table; + std::unique_ptr
m_nhg_counter_table; + std::unique_ptr
m_vidToRidTable; + + SelectableTimer* m_LagFlexCounterUpdTimer = nullptr; + SelectableTimer* m_NhgFlexCounterUpdTimer = nullptr; + + std::map m_pendingLagAddToFlexCntr; + std::map m_pendingNhgAddToFlexCntr; + +private: + bool m_isArsSupported = false; + ArsProfiles m_arsProfiles; + ArsObjects m_arsObjects; + ArsPrefixesTables m_arsNexthopGroupPrefixes; + ArsEnabledInterfaces m_arsEnabledInterfaces; + ArsLags m_arsLags; + FlexCounterManager m_lag_counter_manager; + FlexCounterManager m_nhg_counter_manager; + std::unique_ptr
m_arsProfileStateTable; + std::unique_ptr
m_arsIfStateTable; + std::unique_ptr
m_arsNhgStateTable; + std::unique_ptr
m_arsLagStateTable; + + bool updateArsInterface(ArsProfileEntry &profile, const Port &port, const bool is_enable); + bool doTaskArsObject(const KeyOpFieldsValuesTuple&); + bool doTaskArsProfile(const KeyOpFieldsValuesTuple&); + bool doTaskArsInterfaces(const KeyOpFieldsValuesTuple&); + bool doTaskArsNexthopGroup(const KeyOpFieldsValuesTuple&); + bool doTaskArsLag(const KeyOpFieldsValuesTuple&); + void doTask(Consumer& consumer); + + isSetImplemented(sai_object_type_t object_type, sai_attr_id_t attr_id) + { + auto feature = ars_features.find(object_type); + if (feature == ars_features.end()) + { + return false; + } + auto attr = feature->second.find(attr_id); + if (attr == feature->second.end()) + { + return false; + } + return attr->second.set_implemented; + } + + isCreateImplemented(sai_object_type_t object_type, sai_attr_id_t attr_id) + { + auto feature = ars_features.find(object_type); + if (feature == ars_features.end()) + { + return false; + } + auto attr = feature->second.find(attr_id); + if (attr == feature->second.end()) + { + return false; + } + return attr->second.create_implemented; + } + + isGetImplemented(sai_object_type_t object_type, sai_attr_id_t attr_id) + { + auto feature = ars_features.find(object_type); + if (feature == ars_features.end()) + { + return false; + } + auto attr = feature->second.find(attr_id); + if (attr == feature->second.end()) + { + return false; + } + return attr->second.get_implemented; + } + + void initCapabilities() + { + SWSS_LOG_ENTER(); + + for (auto it = ars_features.begin(); it != ars_features.end(); it++) + { + for (auto it2 = it->second.begin()->second.begin(); it2 != it->second.begin()->second.end(); it2++) + { + if (sai_query_attribute_capability(gSwitchId, (sai_object_type_t)it->first, + (sai_attr_id_t)it2->first, + &capability) == SAI_STATUS_SUCCESS) + { + SWSS_LOG_NOTICE("Feature %s Attr %s is supported. Create %s Set %s Get %s", it->second.begin()->first.c_str(), it2->second.attr_name.c_str(), capability.create_implemented ? "Y" : "N", capability.set_implemented ? "Y" : "N", capability.get_implemented ? "Y" : "N"); + } + else + { + SWSS_LOG_NOTICE("Feature %s Attr %s is NOT supported", it->second.begin()->first.c_str(), it2->second.c_str()); + } + + it2->second.create_implemented = capability.create_implemented; + it2->second.set_implemented = capability.set_implemented; + it2->second.get_implemented = capability.get_implemented; + } + } + + m_isArsSupported = isCreateImplemented(SAI_OBJECT_TYPE_SWITCH, SAI_SWITCH_ATTR_ARS_PROFILE); + } + +}; + +#endif /* SWSS_ARSORCH_H */ diff --git a/orchagent/flex_counter/flex_counter_manager.cpp b/orchagent/flex_counter/flex_counter_manager.cpp index 635c757601..965cf185d9 100644 --- a/orchagent/flex_counter/flex_counter_manager.cpp +++ b/orchagent/flex_counter/flex_counter_manager.cpp @@ -38,18 +38,20 @@ const unordered_map FlexCounterManager::status_lookup = const unordered_map FlexCounterManager::counter_id_field_lookup = { - { CounterType::PORT_DEBUG, PORT_DEBUG_COUNTER_ID_LIST }, - { CounterType::SWITCH_DEBUG, SWITCH_DEBUG_COUNTER_ID_LIST }, - { CounterType::PORT, PORT_COUNTER_ID_LIST }, - { CounterType::QUEUE, QUEUE_COUNTER_ID_LIST }, - { CounterType::MACSEC_SA_ATTR, MACSEC_SA_ATTR_ID_LIST }, - { CounterType::MACSEC_SA, MACSEC_SA_COUNTER_ID_LIST }, - { CounterType::MACSEC_FLOW, MACSEC_FLOW_COUNTER_ID_LIST }, - { CounterType::ACL_COUNTER, ACL_COUNTER_ATTR_ID_LIST }, - { CounterType::TUNNEL, TUNNEL_COUNTER_ID_LIST }, - { CounterType::HOSTIF_TRAP, FLOW_COUNTER_ID_LIST }, - { CounterType::ROUTE, FLOW_COUNTER_ID_LIST }, - { CounterType::ENI, ENI_COUNTER_ID_LIST }, + { CounterType::PORT_DEBUG, PORT_DEBUG_COUNTER_ID_LIST }, + { CounterType::SWITCH_DEBUG, SWITCH_DEBUG_COUNTER_ID_LIST }, + { CounterType::PORT, PORT_COUNTER_ID_LIST }, + { CounterType::QUEUE, QUEUE_COUNTER_ID_LIST }, + { CounterType::MACSEC_SA_ATTR, MACSEC_SA_ATTR_ID_LIST }, + { CounterType::MACSEC_SA, MACSEC_SA_COUNTER_ID_LIST }, + { CounterType::MACSEC_FLOW, MACSEC_FLOW_COUNTER_ID_LIST }, + { CounterType::ACL_COUNTER, ACL_COUNTER_ATTR_ID_LIST }, + { CounterType::TUNNEL, TUNNEL_COUNTER_ID_LIST }, + { CounterType::HOSTIF_TRAP, FLOW_COUNTER_ID_LIST }, + { CounterType::ROUTE, FLOW_COUNTER_ID_LIST }, + { CounterType::ENI, ENI_COUNTER_ID_LIST }, + { CounterType::ARS_NEXTHOP_GROUP,ARS_NEXTHOP_GROUP_COUNTER_ID_LIST }, + { CounterType::ARS_LAG, ARS_LAG_COUNTER_ID_LIST }, }; FlexManagerDirectory g_FlexManagerDirectory; diff --git a/orchagent/flex_counter/flex_counter_manager.h b/orchagent/flex_counter/flex_counter_manager.h index b9e6e4c487..c1a0db2ace 100644 --- a/orchagent/flex_counter/flex_counter_manager.h +++ b/orchagent/flex_counter/flex_counter_manager.h @@ -32,7 +32,9 @@ enum class CounterType TUNNEL, HOSTIF_TRAP, ROUTE, - ENI + ENI, + ARS_NEXTHOP_GROUP, + ARS_LAG }; // FlexCounterManager allows users to manage a group of flex counters. diff --git a/orchagent/flexcounterorch.cpp b/orchagent/flexcounterorch.cpp index 2832f0bd12..b6f577b62f 100644 --- a/orchagent/flexcounterorch.cpp +++ b/orchagent/flexcounterorch.cpp @@ -15,6 +15,7 @@ #include "macsecorch.h" #include "dash/dashorch.h" #include "flowcounterrouteorch.h" +#include "arsorch.h" extern sai_port_api_t *sai_port_api; extern sai_switch_api_t *sai_switch_api; @@ -41,6 +42,8 @@ extern sai_object_id_t gSwitchId; #define FLOW_CNT_TRAP_KEY "FLOW_CNT_TRAP" #define FLOW_CNT_ROUTE_KEY "FLOW_CNT_ROUTE" #define ENI_KEY "ENI" +#define ARS_NEXTHOP_GROUP_KEY "ARS_NEXTHOP_GROUP" +#define ARS_LAG_KEY "ARS_LAG" unordered_map flexCounterGroupMap = { @@ -63,7 +66,9 @@ unordered_map flexCounterGroupMap = {"MACSEC_SA", COUNTERS_MACSEC_SA_GROUP}, {"MACSEC_SA_ATTR", COUNTERS_MACSEC_SA_ATTR_GROUP}, {"MACSEC_FLOW", COUNTERS_MACSEC_FLOW_GROUP}, - {"ENI", ENI_STAT_COUNTER_FLEX_COUNTER_GROUP} + {"ENI", ENI_STAT_COUNTER_FLEX_COUNTER_GROUP}, + {ARS_NEXTHOP_GROUP_KEY, ARS_NEXTHOP_GROUP_FLEX_COUNTER_GROUP}, + {ARS_LAG_KEY, ARS_LAG_KEY_FLEX_COUNTER_GROUP} }; @@ -88,6 +93,8 @@ void FlexCounterOrch::doTask(Consumer &consumer) VxlanTunnelOrch* vxlan_tunnel_orch = gDirectory.get(); DashOrch* dash_orch = gDirectory.get(); + ArsOrch* ars_orch = gDirectory.get(); + if (gPortsOrch && !gPortsOrch->allPortsReady()) { return; @@ -234,6 +241,17 @@ void FlexCounterOrch::doTask(Consumer &consumer) m_route_flow_counter_enabled = false; } } + if (ars_orch) + { + if (key == ARS_NEXTHOP_GROUP_KEY) + { + ars_orch->generateNexthopGroupCounterMap(); + } + if (key == ARS_LAG_KEY) + { + ars_orch->generateLagCounterMap(); + } + } setFlexCounterGroupOperation(flexCounterGroupMap[key], value); diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index 13ef89c487..bdc9bedc42 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -422,6 +422,26 @@ bool OrchDaemon::init() gNhgMapOrch = new NhgMapOrch(m_applDb, APP_FC_TO_NHG_INDEX_MAP_TABLE_NAME); + const int arsorch_pri = 15; + + vector ars_tables = { + { CFG_ARS_PROFILE, arsorch_pri }, + { CFG_ARS_QUANTIZATION_BANDS, arsorch_pri }, + { CFG_ARS_OBJECT, arsorch_pri }, + { CFG_ARS_INTERFACE, arsorch_pri }, + { CFG_ARS_NEXTHOP_GROUP, arsorch_pri }, + { CFG_ARS_PORTCHANNEL, arsorch_pri }, + { APP_ARS_PROFILE_TABLE_NAME, arsorch_pri }, + { APP_ARS_QUANTIZATION_BANDS_TABLE_NAME, arsorch_pri }, + { APP_ARS_OBJECT_TABLE_NAME, arsorch_pri }, + { APP_ARS_INTERFACE_TABLE_NAME, arsorch_pri }, + { APP_ARS_NEXTHOP_GROUP_TABLE_NAME, arsorch_pri }, + { APP_ARS_PORTCHANNEL_TABLE_NAME, arsorch_pri } + }; + + ArsOrch* ars_orch = new ArsOrch(m_configDb, m_applDb, m_stateDb, ars_tables, vrf_orch); + gDirectory.set(ars_orch); + /* * The order of the orch list is important for state restore of warm start and * the queued processing in m_toSync map after gPortsOrch->allPortsReady() is set. @@ -528,6 +548,7 @@ bool OrchDaemon::init() m_orchList.push_back(gMlagOrch); m_orchList.push_back(gIsoGrpOrch); m_orchList.push_back(gFgNhgOrch); + m_orchList.push_back(ars_orch); m_orchList.push_back(mux_st_orch); m_orchList.push_back(nvgre_tunnel_orch); m_orchList.push_back(nvgre_tunnel_map_orch); diff --git a/orchagent/orchdaemon.h b/orchagent/orchdaemon.h index 6a1c0b999a..808eac989e 100644 --- a/orchagent/orchdaemon.h +++ b/orchagent/orchdaemon.h @@ -53,6 +53,7 @@ #include "dash/dashrouteorch.h" #include "dash/dashvnetorch.h" #include +#include "arsorch.h" using namespace swss; diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index b84760fa3b..1cf255494d 100644 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -9868,6 +9868,66 @@ bool PortsOrch::setPortPtTam(const Port& port, sai_object_id_t tam_id) return true; } +bool PortsOrch::setPortArsEnable(const Port& port, bool is_enable) +{ + sai_attribute_t attr; + + attr.id = SAI_PORT_ATTR_ARS_ENABLE; + attr.value.booldata = is_enable; + sai_status_t status = sai_port_api->set_port_attribute(port.m_port_id, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + task_process_status handle_status = handleSaiSetStatus(SAI_API_PORT, status); + if (handle_status != task_success) + { + return parseHandleSaiStatusFailure(handle_status); + } + } + + return true; +} + +bool PortsOrch::setPortArsLoadScaling(const Port& port) +{ + sai_attribute_t attr; + + attr.id = SAI_PORT_ATTR_ARS_PORT_LOAD_SCALING_FACTOR; + attr.value.u32 = port.m_speed / 10000; + sai_status_t status = sai_port_api->set_port_attribute(port.m_port_id, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + task_process_status handle_status = handleSaiSetStatus(SAI_API_PORT, status); + if (handle_status != task_success) + { + return parseHandleSaiStatusFailure(handle_status); + } + } + + return true; +} + +bool PortsOrch::setPortArsAltPath(const Port& port, bool is_enable) +{ + sai_attribute_t attr; + + attr.id = SAI_PORT_ATTR_ARS_ALTERNATE_PATH; + attr.value.booldata = is_enable; + sai_status_t status = sai_port_api->set_port_attribute(port.m_port_id, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + task_process_status handle_status = handleSaiSetStatus(SAI_API_PORT, status); + if (handle_status != task_success) + { + return parseHandleSaiStatusFailure(handle_status); + } + } + + return true; +} + bool PortsOrch::createPtTam() { SWSS_LOG_ENTER(); diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h index 4d01626cb9..0b33bad0bc 100644 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -16,6 +16,7 @@ #include "lagid.h" #include "flexcounterorch.h" #include "events.h" +#include "arsorch.h" #include "port/port_capabilities.h" #include "port/porthlpr.h" @@ -247,6 +248,9 @@ class PortsOrch : public Orch, public Subject bool setPortPtIntfId(const Port& port, sai_uint16_t intf_id); bool setPortPtTimestampTemplate(const Port& port, sai_port_path_tracing_timestamp_type_t ts_type); + bool setPortArsEnable(const Port& port, bool is_enable); + bool setPortArsLoadScaling(const Port& port); + bool setPortArsAltPath(const Port& port, bool is_enable); private: unique_ptr
m_counterTable; diff --git a/orchagent/routeorch.cpp b/orchagent/routeorch.cpp index ef6da962e3..901f21e9d6 100644 --- a/orchagent/routeorch.cpp +++ b/orchagent/routeorch.cpp @@ -48,7 +48,8 @@ RouteOrch::RouteOrch(DBConnector *db, vector &tableNames, m_nextHopGroupCount(0), m_srv6Orch(srv6Orch), m_resync(false), - m_appTunnelDecapTermProducer(db, APP_TUNNEL_DECAP_TERM_TABLE_NAME) + m_appTunnelDecapTermProducer(db, APP_TUNNEL_DECAP_TERM_TABLE_NAME), + m_gArsOrch(gArsOrch) { SWSS_LOG_ENTER(); @@ -1274,7 +1275,7 @@ bool RouteOrch::removeFineGrainedNextHopGroup(sai_object_id_t &next_hop_group_id return true; } -bool RouteOrch::addNextHopGroup(const NextHopGroupKey &nexthops) +bool RouteOrch::addNextHopGroup(const NextHopGroupKey& nexthops, vector &nhg_attrs) { SWSS_LOG_ENTER(); @@ -1337,13 +1338,6 @@ bool RouteOrch::addNextHopGroup(const NextHopGroupKey &nexthops) SWSS_LOG_INFO("Skipping creation of nexthop group as none of nexthop are active"); return false; } - sai_attribute_t nhg_attr; - vector nhg_attrs; - - nhg_attr.id = SAI_NEXT_HOP_GROUP_ATTR_TYPE; - nhg_attr.value.s32 = m_switchOrch->checkOrderedEcmpEnable() ? SAI_NEXT_HOP_GROUP_TYPE_DYNAMIC_ORDERED_ECMP : SAI_NEXT_HOP_GROUP_TYPE_ECMP; - nhg_attrs.push_back(nhg_attr); - sai_object_id_t next_hop_group_id; sai_status_t status = sai_next_hop_group_api->create_next_hop_group(&next_hop_group_id, gSwitchId, @@ -1455,6 +1449,18 @@ bool RouteOrch::addNextHopGroup(const NextHopGroupKey &nexthops) return true; } +bool RouteOrch::addNextHopGroup(const NextHopGroupKey &nextHops) +{ + sai_attribute_t nhg_attr; + vector nhg_attrs; + + nhg_attr.id = SAI_NEXT_HOP_GROUP_ATTR_TYPE; + nhg_attr.value.s32 = m_switchOrch->checkOrderedEcmpEnable() ? SAI_NEXT_HOP_GROUP_TYPE_DYNAMIC_ORDERED_ECMP : SAI_NEXT_HOP_GROUP_TYPE_ECMP; + nhg_attrs.push_back(nhg_attr); + + return addNextHopGroup(nextHops, nhg_attrs); +} + bool RouteOrch::removeNextHopGroup(const NextHopGroupKey &nexthops) { SWSS_LOG_ENTER(); @@ -1762,6 +1768,7 @@ bool RouteOrch::addRoute(RouteBulkContext& ctx, const NextHopGroupKey &nextHops) bool isFineGrainedNextHopIdChanged = false; bool blackhole = false; bool srv6_nh = false; + std::set altPathMembers; if (m_syncdRoutes.find(vrf_id) == m_syncdRoutes.end()) { @@ -1908,8 +1915,25 @@ bool RouteOrch::addRoute(RouteBulkContext& ctx, const NextHopGroupKey &nextHops) return false; } } + + + sai_attribute_t nhg_attr; + vector nhg_attrs; + + nhg_attr.id = SAI_NEXT_HOP_GROUP_ATTR_TYPE; + nhg_attr.value.s32 = m_switchOrch->checkOrderedEcmpEnable() ? SAI_NEXT_HOP_GROUP_TYPE_DYNAMIC_ORDERED_ECMP : SAI_NEXT_HOP_GROUP_TYPE_ECMP; + nhg_attrs.push_back(nhg_attr); + + sai_object_id_t ars_object_id; + if (m_gArsOrch && m_gArsOrch->isRouteArs(vrf_id, ipPrefix, &ars_object_id), &altPathMembers) + { + nhg_attr.id = SAI_NEXT_HOP_GROUP_ATTR_ARS_OBJECT_ID; + nhg_attr.value.oid = ars_object_id; + nhg_attrs.push_back(nhg_attr); + } + /* Try to create a new next hop group */ - if (!addNextHopGroup(nextHops)) + if (!addNextHopGroup(nextHops, nhg_attrs)) { for(auto it = nextHops.getNextHops().begin(); it != nextHops.getNextHops().end(); ++it) { @@ -1964,6 +1988,10 @@ bool RouteOrch::addRoute(RouteBulkContext& ctx, const NextHopGroupKey &nextHops) } next_hop_id = m_syncdNextHopGroups[nextHops].next_hop_group_id; + if (!updateNexthopArsState(nextHops, altPathMembers)) + { + return false; + } } /* Sync the route entry */ @@ -2899,3 +2927,75 @@ inline void RouteOrch::removeVipRouteSubnetDecapTerm(const IpPrefix &ipPrefix) m_appTunnelDecapTermProducer.del(key); m_SubnetDecapTermsCreated.erase(it); } + + +bool RouteOrch::reconfigureRoute(sai_object_id_t vrf_id, IpPrefix& ip_prefix, const NextHopGroupKey& nhg) +{ + SWSS_LOG_NOTICE("Reconfiguring nexthops %s for prefix %s", nhg.to_string().c_str(), ip_prefix.to_string().c_str()); + std::set altPathMembers; + auto it_route = m_syncdRoutes.at(vrf_id).find(ip_prefix); + if (it_route != m_syncdRoutes.at(vrf_id).end()) + { + if (!removeNextHopGroup(nhg)) + { + SWSS_LOG_ERROR("Failed to remove nexthopgroup %s for prefix %s", nhg.to_string().c_str(), ip_prefix.to_string().c_str()); + return false; + } + + sai_attribute_t nhg_attr; + vector nhg_attrs; + + nhg_attr.id = SAI_NEXT_HOP_GROUP_ATTR_TYPE; + nhg_attr.value.s32 = m_switchOrch->checkOrderedEcmpEnable() ? SAI_NEXT_HOP_GROUP_TYPE_DYNAMIC_ORDERED_ECMP : SAI_NEXT_HOP_GROUP_TYPE_ECMP; + nhg_attrs.push_back(nhg_attr); + + sai_object_id_t ars_object_id; + if (m_gArsOrch && m_gArsOrch->isRouteArs(vrf_id, ip_prefix, &ars_object_id, &altPathMembers)) + { + nhg_attr.id = SAI_NEXT_HOP_GROUP_ATTR_ARS_OBJECT_ID; + nhg_attr.value.oid = ars_object_id; + nhg_attrs.push_back(nhg_attr); + } + + if (!addNextHopGroup(nhg, nhg_attrs)) + { + SWSS_LOG_ERROR("Failed to add nexthopgroup %s for prefix %s", nhg.to_string().c_str(), ip_prefix.to_string().c_str()); + return false; + } + if (!updateNexthopArsState(nhg, altPathMembers)) + { + return false; + } + } + SWSS_LOG_NOTICE("Reconfigured nexthopgroup %s for prefix %s", nhg.to_string().c_str(), ip_prefix.to_string().c_str()); + + return true; +} +bool RouteOrch::updateNexthopArsState(const NextHopGroupKey& nhg, const std::set& altPathMembers) +{ + if (m_gArsOrch && !altPathMembers.empty()) + { + for (auto it: altPathMembers) + { + auto nexthop = m_syncdNextHopGroups[nhg].nhopgroup_members.find(it); + if (nexthop != m_syncdNextHopGroups[nhg].nhopgroup_members.end()) + { + sia_attribute_t nhgm_attr; + nhgm_attr.id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_ARS_OBJECT_ID; + nhgm_attr.value.booldata = true; + sai_status_t sai_status = sai_next_hop_group_api->set_next_hop_group_member(nexthop->second.next_hop_id, &nhgm_attr); + if (sai_status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to enable ARS on next hop member %s(oid %" PRIx64 ") : %d", + nexthop.to_string().c_str(), nexthop->second.next_hop_id, status); + task_process_status handle_status = handleSaiCreateStatus(SAI_API_NEXT_HOP_GROUP, sai_status); + if (handle_status != task_success) + { + return parseHandleSaiStatusFailure(handle_status); + } + } + } + } + } + return true; +} diff --git a/orchagent/routeorch.h b/orchagent/routeorch.h old mode 100644 new mode 100755 index 595af46081..27fb8c81f9 --- a/orchagent/routeorch.h +++ b/orchagent/routeorch.h @@ -15,6 +15,7 @@ #include "nexthopgroupkey.h" #include "bulker.h" #include "fgnhgorch.h" +#include "arsorch.h" #include /* Maximum next hop group number */ @@ -184,7 +185,7 @@ struct LabelRouteBulkContext class RouteOrch : public Orch, public Subject { public: - RouteOrch(DBConnector *db, vector &tableNames, SwitchOrch *switchOrch, NeighOrch *neighOrch, IntfsOrch *intfsOrch, VRFOrch *vrfOrch, FgNhgOrch *fgNhgOrch, Srv6Orch *srv6Orch); + RouteOrch(DBConnector *db, vector &tableNames, SwitchOrch *switchOrch, NeighOrch *neighOrch, IntfsOrch *intfsOrch, VRFOrch *vrfOrch, FgNhgOrch *fgNhgOrch, Srv6Orch *srv6Orch, ArsOrch *gArsOrch); bool hasNextHopGroup(const NextHopGroupKey&) const; sai_object_id_t getNextHopGroupId(const NextHopGroupKey&); @@ -229,6 +230,11 @@ class RouteOrch : public Orch, public Subject void decreaseNextHopGroupCount(); bool checkNextHopGroupCount(); const RouteTables& getSyncdRoutes() const { return m_syncdRoutes; } + bool reconfigureRoute(sai_object_id_t vrf_id, IpPrefix& ip_prefix, const NextHopGroupKey& nhg); + + bool updateNexthopArsState(const NextHopGroupKey& nhg, const std::set& altPathMembers); + bool updateNexthopGroupArsState(const sai_object_id next_hop_group_id, const sai_object_id ars_object_id); + bool reconfigureNexthopGroupWithArsState(NextHopGroupKey nexthopGroupKey, sai_object_id * next_hop_group_id, const sai_object_id ars_object_id); private: SwitchOrch *m_switchOrch; @@ -237,6 +243,7 @@ class RouteOrch : public Orch, public Subject VRFOrch *m_vrfOrch; FgNhgOrch *m_fgNhgOrch; Srv6Orch *m_srv6Orch; + ArsOrch *m_gArsOrch; unsigned int m_nextHopGroupCount; unsigned int m_maxNextHopGroupCount; @@ -288,6 +295,9 @@ class RouteOrch : public Orch, public Subject bool isVipRoute(const IpPrefix &ipPrefix, const NextHopGroupKey &nextHops); void createVipRouteSubnetDecapTerm(const IpPrefix &ipPrefix); void removeVipRouteSubnetDecapTerm(const IpPrefix &ipPrefix); + + bool addNextHopGroup(const NextHopGroupKey&, vector &nhg_attrs); + bool updateNexthopArsState(const NextHopGroupKey& nhg, const std::set& altPathMembers); }; #endif /* SWSS_ROUTEORCH_H */ diff --git a/orchagent/saihelper.cpp b/orchagent/saihelper.cpp index e7cf7fb018..e455bc9a15 100644 --- a/orchagent/saihelper.cpp +++ b/orchagent/saihelper.cpp @@ -87,6 +87,8 @@ sai_dash_direction_lookup_api_t* sai_dash_direction_lookup_api; sai_twamp_api_t* sai_twamp_api; sai_tam_api_t* sai_tam_api; sai_stp_api_t* sai_stp_api; +sai_ars_api_t* sai_ars_api; +sai_ars_profile_api_t* sai_ars_profile_api; extern sai_object_id_t gSwitchId; extern bool gTraditionalFlexCounter; @@ -236,6 +238,8 @@ void initSaiApi() sai_api_query(SAI_API_TWAMP, (void **)&sai_twamp_api); sai_api_query(SAI_API_TAM, (void **)&sai_tam_api); sai_api_query(SAI_API_STP, (void **)&sai_stp_api); + sai_api_query(SAI_API_ARS, (void **)&sai_ars_api); + sai_api_query(SAI_API_ARS_PROFILE, (void **)&sai_ars_profile_api); sai_log_set(SAI_API_SWITCH, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_BRIDGE, SAI_LOG_LEVEL_NOTICE); @@ -278,6 +282,8 @@ void initSaiApi() sai_log_set(SAI_API_TWAMP, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_TAM, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_STP, SAI_LOG_LEVEL_NOTICE); + sai_log_set(SAI_API_ARS, SAI_LOG_LEVEL_NOTICE); + sai_log_set(SAI_API_ARS_PROFILE, SAI_LOG_LEVEL_NOTICE); } void initFlexCounterTables() diff --git a/tests/mock_tests/Makefile.am b/tests/mock_tests/Makefile.am index 2236b3a6ae..2d996789ac 100644 --- a/tests/mock_tests/Makefile.am +++ b/tests/mock_tests/Makefile.am @@ -142,6 +142,7 @@ tests_SOURCES = aclorch_ut.cpp \ $(top_srcdir)/cfgmgr/coppmgr.cpp \ $(top_srcdir)/orchagent/twamporch.cpp \ $(top_srcdir)/orchagent/stporch.cpp + $(top_srcdir)/orchagent/arsorch.cpp tests_SOURCES += $(FLEX_CTR_DIR)/flex_counter_manager.cpp $(FLEX_CTR_DIR)/flex_counter_stat_manager.cpp $(FLEX_CTR_DIR)/flow_counter_handler.cpp $(FLEX_CTR_DIR)/flowcounterrouteorch.cpp tests_SOURCES += $(DEBUG_CTR_DIR)/debug_counter.cpp $(DEBUG_CTR_DIR)/drop_counter.cpp diff --git a/tests/mock_tests/aclorch_ut.cpp b/tests/mock_tests/aclorch_ut.cpp index 575f56e5d5..5d563a1fdd 100644 --- a/tests/mock_tests/aclorch_ut.cpp +++ b/tests/mock_tests/aclorch_ut.cpp @@ -430,7 +430,7 @@ namespace aclorch_test { APP_ROUTE_TABLE_NAME, routeorch_pri }, { APP_LABEL_ROUTE_TABLE_NAME, routeorch_pri } }; - gRouteOrch = new RouteOrch(m_app_db.get(), route_tables, gSwitchOrch, gNeighOrch, gIntfsOrch, gVrfOrch, gFgNhgOrch, gSrv6Orch); + gRouteOrch = new RouteOrch(m_app_db.get(), route_tables, gSwitchOrch, gNeighOrch, gIntfsOrch, gVrfOrch, gFgNhgOrch, gSrv6Orch, nullptr); vector policer_tables = { TableConnector(m_config_db.get(), CFG_POLICER_TABLE_NAME), diff --git a/tests/mock_tests/flowcounterrouteorch_ut.cpp b/tests/mock_tests/flowcounterrouteorch_ut.cpp index 1b031515b3..e424cb2ffd 100644 --- a/tests/mock_tests/flowcounterrouteorch_ut.cpp +++ b/tests/mock_tests/flowcounterrouteorch_ut.cpp @@ -219,7 +219,7 @@ namespace flowcounterrouteorch_test { APP_ROUTE_TABLE_NAME, routeorch_pri }, { APP_LABEL_ROUTE_TABLE_NAME, routeorch_pri } }; - gRouteOrch = new RouteOrch(m_app_db.get(), route_tables, gSwitchOrch, gNeighOrch, gIntfsOrch, gVrfOrch, gFgNhgOrch, gSrv6Orch); + gRouteOrch = new RouteOrch(m_app_db.get(), route_tables, gSwitchOrch, gNeighOrch, gIntfsOrch, gVrfOrch, gFgNhgOrch, gSrv6Orch, nullptr); gNhgOrch = new NhgOrch(m_app_db.get(), APP_NEXTHOP_GROUP_TABLE_NAME); // Recreate buffer orch to read populated data diff --git a/tests/mock_tests/intfsorch_ut.cpp b/tests/mock_tests/intfsorch_ut.cpp index b7c2bc7397..5da8d204ec 100644 --- a/tests/mock_tests/intfsorch_ut.cpp +++ b/tests/mock_tests/intfsorch_ut.cpp @@ -208,7 +208,7 @@ namespace intfsorch_test { APP_ROUTE_TABLE_NAME, routeorch_pri }, { APP_LABEL_ROUTE_TABLE_NAME, routeorch_pri } }; - gRouteOrch = new RouteOrch(m_app_db.get(), route_tables, gSwitchOrch, gNeighOrch, gIntfsOrch, gVrfOrch, gFgNhgOrch, gSrv6Orch); + gRouteOrch = new RouteOrch(m_app_db.get(), route_tables, gSwitchOrch, gNeighOrch, gIntfsOrch, gVrfOrch, gFgNhgOrch, gSrv6Orch, nullptr); gNhgOrch = new NhgOrch(m_app_db.get(), APP_NEXTHOP_GROUP_TABLE_NAME); // Recreate buffer orch to read populated data diff --git a/tests/mock_tests/mock_orch_test.cpp b/tests/mock_tests/mock_orch_test.cpp index c6898188b4..d4f87d5bb5 100644 --- a/tests/mock_tests/mock_orch_test.cpp +++ b/tests/mock_tests/mock_orch_test.cpp @@ -201,7 +201,7 @@ void MockOrchTest::SetUp() { APP_ROUTE_TABLE_NAME, routeorch_pri }, { APP_LABEL_ROUTE_TABLE_NAME, routeorch_pri } }; - gRouteOrch = new RouteOrch(m_app_db.get(), route_tables, gSwitchOrch, gNeighOrch, gIntfsOrch, gVrfOrch, gFgNhgOrch, gSrv6Orch); + gRouteOrch = new RouteOrch(m_app_db.get(), route_tables, gSwitchOrch, gNeighOrch, gIntfsOrch, gVrfOrch, gFgNhgOrch, gSrv6Orch, nullptr); gDirectory.set(gRouteOrch); ut_orch_list.push_back((Orch **)&gRouteOrch); TableConnector stateDbMirrorSession(m_state_db.get(), STATE_MIRROR_SESSION_TABLE_NAME); @@ -276,4 +276,4 @@ void MockOrchTest::TearDown() ut_helper::uninitSaiApi(); } -} \ No newline at end of file +} diff --git a/tests/mock_tests/mock_orchagent_main.h b/tests/mock_tests/mock_orchagent_main.h index 5ffda0dd41..5bb13d1fdd 100644 --- a/tests/mock_tests/mock_orchagent_main.h +++ b/tests/mock_tests/mock_orchagent_main.h @@ -32,6 +32,7 @@ #define private public #include "stporch.h" #undef private +#include "arsorch.h" #include "directory.h" extern int gBatchSize; diff --git a/tests/mock_tests/routeorch_ut.cpp b/tests/mock_tests/routeorch_ut.cpp index 043b7b6b0a..6df8f0bb35 100644 --- a/tests/mock_tests/routeorch_ut.cpp +++ b/tests/mock_tests/routeorch_ut.cpp @@ -259,7 +259,7 @@ namespace routeorch_test { APP_ROUTE_TABLE_NAME, routeorch_pri }, { APP_LABEL_ROUTE_TABLE_NAME, routeorch_pri } }; - gRouteOrch = new RouteOrch(m_app_db.get(), route_tables, gSwitchOrch, gNeighOrch, gIntfsOrch, gVrfOrch, gFgNhgOrch, gSrv6Orch); + gRouteOrch = new RouteOrch(m_app_db.get(), route_tables, gSwitchOrch, gNeighOrch, gIntfsOrch, gVrfOrch, gFgNhgOrch, gSrv6Orch, nullptr); gNhgOrch = new NhgOrch(m_app_db.get(), APP_NEXTHOP_GROUP_TABLE_NAME); // Recreate buffer orch to read populated data