diff --git a/orchagent/switch/switch_capabilities.cpp b/orchagent/switch/switch_capabilities.cpp index d826a9e49f..d1f191bf39 100644 --- a/orchagent/switch/switch_capabilities.cpp +++ b/orchagent/switch/switch_capabilities.cpp @@ -1,9 +1,10 @@ // includes ----------------------------------------------------------------------------------------------------------- extern "C" { +#include #include #include -#include +#include #include } @@ -11,6 +12,7 @@ extern "C" { #include #include #include +#include #include #include @@ -26,8 +28,14 @@ using namespace swss; // defines ------------------------------------------------------------------------------------------------------------ #define SWITCH_CAPABILITY_HASH_NATIVE_HASH_FIELD_LIST_FIELD "HASH|NATIVE_HASH_FIELD_LIST" -#define SWITCH_CAPABILITY_ECMP_HASH_CAPABLE_FIELD "ECMP_HASH_CAPABLE" -#define SWITCH_CAPABILITY_LAG_HASH_CAPABLE_FIELD "LAG_HASH_CAPABLE" + +#define SWITCH_CAPABILITY_ECMP_HASH_CAPABLE_FIELD "ECMP_HASH_CAPABLE" +#define SWITCH_CAPABILITY_LAG_HASH_CAPABLE_FIELD "LAG_HASH_CAPABLE" + +#define SWITCH_CAPABILITY_ECMP_HASH_ALGORITHM_FIELD "ECMP_HASH_ALGORITHM" +#define SWITCH_CAPABILITY_ECMP_HASH_ALGORITHM_CAPABLE_FIELD "ECMP_HASH_ALGORITHM_CAPABLE" +#define SWITCH_CAPABILITY_LAG_HASH_ALGORITHM_FIELD "LAG_HASH_ALGORITHM" +#define SWITCH_CAPABILITY_LAG_HASH_ALGORITHM_CAPABLE_FIELD "LAG_HASH_ALGORITHM_CAPABLE" #define SWITCH_CAPABILITY_KEY "switch" @@ -58,20 +66,45 @@ static const std::unordered_map swHashHash { SAI_NATIVE_HASH_FIELD_INNER_L4_SRC_PORT, SWITCH_HASH_FIELD_INNER_L4_SRC_PORT } }; +static const std::unordered_map swHashAlgorithmMap = +{ + { SAI_HASH_ALGORITHM_CRC, SWITCH_HASH_ALGORITHM_CRC }, + { SAI_HASH_ALGORITHM_XOR, SWITCH_HASH_ALGORITHM_XOR }, + { SAI_HASH_ALGORITHM_RANDOM, SWITCH_HASH_ALGORITHM_RANDOM }, + { SAI_HASH_ALGORITHM_CRC_32LO, SWITCH_HASH_ALGORITHM_CRC_32LO }, + { SAI_HASH_ALGORITHM_CRC_32HI, SWITCH_HASH_ALGORITHM_CRC_32HI }, + { SAI_HASH_ALGORITHM_CRC_CCITT, SWITCH_HASH_ALGORITHM_CRC_CCITT }, + { SAI_HASH_ALGORITHM_CRC_XOR, SWITCH_HASH_ALGORITHM_CRC_XOR } +}; + // variables ---------------------------------------------------------------------------------------------------------- extern sai_object_id_t gSwitchId; // functions ---------------------------------------------------------------------------------------------------------- -static std::string toStr(sai_object_type_t objType, sai_attr_id_t attrId) noexcept +static std::string toStr(sai_object_type_t objType, sai_attr_id_t attrId) { const auto *meta = sai_metadata_get_attr_metadata(objType, attrId); return meta != nullptr ? meta->attridname : "UNKNOWN"; } -static std::string toStr(const std::set &value) noexcept +static std::string toStr(sai_native_hash_field_t value) +{ + const auto *name = sai_metadata_get_native_hash_field_name(value); + + return name != nullptr ? name : "UNKNOWN"; +} + +static std::string toStr(sai_hash_algorithm_t value) +{ + const auto *name = sai_metadata_get_hash_algorithm_name(value); + + return name != nullptr ? name : "UNKNOWN"; +} + +static std::string toStr(const std::set &value) { std::vector strList; @@ -87,11 +120,33 @@ static std::string toStr(const std::set &value) noexcep return join(",", strList.cbegin(), strList.cend()); } -static std::string toStr(bool value) noexcept +static std::string toStr(const std::set &value) +{ + std::vector strList; + + for (const auto &cit1 : value) + { + const auto &cit2 = swHashAlgorithmMap.find(cit1); + if (cit2 != swHashAlgorithmMap.cend()) + { + strList.push_back(cit2->second); + } + } + + return join(",", strList.cbegin(), strList.cend()); +} + +static std::string toStr(bool value) { return value ? "true" : "false"; } +template +static void insertBack(T1 &out, const T2 &in) +{ + out.insert(out.end(), in.cbegin(), in.cend()); +} + // Switch capabilities ------------------------------------------------------------------------------------------------ DBConnector SwitchCapabilities::stateDb(SWITCH_STATE_DB_NAME, SWITCH_STATE_DB_TIMEOUT); @@ -122,8 +177,20 @@ bool SwitchCapabilities::isSwitchLagHashSupported() const return nativeHashFieldList.isAttrSupported && lagHash.isAttrSupported; } +bool SwitchCapabilities::isSwitchEcmpHashAlgorithmSupported() const +{ + return switchCapabilities.ecmpHashAlgorithm.isAttrSupported; +} + +bool SwitchCapabilities::isSwitchLagHashAlgorithmSupported() const +{ + return switchCapabilities.lagHashAlgorithm.isAttrSupported; +} + bool SwitchCapabilities::validateSwitchHashFieldCap(const std::set &hfSet) const { + SWSS_LOG_ENTER(); + if (!hashCapabilities.nativeHashFieldList.isEnumSupported) { return true; @@ -139,7 +206,7 @@ bool SwitchCapabilities::validateSwitchHashFieldCap(const std::set +bool SwitchCapabilities::validateSwitchHashAlgorithmCap(const T &obj, sai_hash_algorithm_t haValue) const +{ + SWSS_LOG_ENTER(); + + if (!obj.isEnumSupported) + { + return true; + } + + if (obj.haSet.empty()) + { + SWSS_LOG_ERROR("Failed to validate hash algorithm: no hash algorithm capabilities"); + return false; + } + + if (obj.haSet.count(haValue) == 0) + { + SWSS_LOG_ERROR("Failed to validate hash algorithm: value(%s) is not supported", toStr(haValue).c_str()); + return false; + } + + return true; +} + FieldValueTuple SwitchCapabilities::makeHashFieldCapDbEntry() const { const auto &nativeHashFieldList = hashCapabilities.nativeHashFieldList; @@ -173,6 +275,42 @@ FieldValueTuple SwitchCapabilities::makeLagHashCapDbEntry() const return FieldValueTuple(field, value); } +std::vector SwitchCapabilities::makeEcmpHashAlgorithmCapDbEntry() const +{ + const auto &ecmpHashAlgorithm = switchCapabilities.ecmpHashAlgorithm; + + std::vector fvList; + + fvList.emplace_back( + SWITCH_CAPABILITY_ECMP_HASH_ALGORITHM_FIELD, + ecmpHashAlgorithm.isEnumSupported ? toStr(ecmpHashAlgorithm.haSet) : "N/A" + ); + fvList.emplace_back( + SWITCH_CAPABILITY_ECMP_HASH_ALGORITHM_CAPABLE_FIELD, + toStr(isSwitchEcmpHashAlgorithmSupported()) + ); + + return fvList; +} + +std::vector SwitchCapabilities::makeLagHashAlgorithmCapDbEntry() const +{ + const auto &lagHashAlgorithm = switchCapabilities.lagHashAlgorithm; + + std::vector fvList; + + fvList.emplace_back( + SWITCH_CAPABILITY_LAG_HASH_ALGORITHM_FIELD, + lagHashAlgorithm.isEnumSupported ? toStr(lagHashAlgorithm.haSet) : "N/A" + ); + fvList.emplace_back( + SWITCH_CAPABILITY_LAG_HASH_ALGORITHM_CAPABLE_FIELD, + toStr(isSwitchLagHashAlgorithmSupported()) + ); + + return fvList; +} + sai_status_t SwitchCapabilities::queryEnumCapabilitiesSai(std::vector &capList, sai_object_type_t objType, sai_attr_id_t attrId) const { sai_s32_list_t enumList = { .count = 0, .list = nullptr }; @@ -256,7 +394,7 @@ void SwitchCapabilities::queryHashCapabilities() queryHashNativeHashFieldListAttrCapabilities(); } -void SwitchCapabilities::querySwitchEcmpHashCapabilities() +void SwitchCapabilities::querySwitchEcmpHashAttrCapabilities() { SWSS_LOG_ENTER(); @@ -286,7 +424,7 @@ void SwitchCapabilities::querySwitchEcmpHashCapabilities() switchCapabilities.ecmpHash.isAttrSupported = true; } -void SwitchCapabilities::querySwitchLagHashCapabilities() +void SwitchCapabilities::querySwitchLagHashAttrCapabilities() { SWSS_LOG_ENTER(); @@ -316,39 +454,160 @@ void SwitchCapabilities::querySwitchLagHashCapabilities() switchCapabilities.lagHash.isAttrSupported = true; } +void SwitchCapabilities::querySwitchEcmpHashAlgorithmEnumCapabilities() +{ + SWSS_LOG_ENTER(); + + std::vector haList; + auto status = queryEnumCapabilitiesSai( + haList, SAI_OBJECT_TYPE_SWITCH, SAI_SWITCH_ATTR_ECMP_DEFAULT_HASH_ALGORITHM + ); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR( + "Failed to get attribute(%s) enum value capabilities", + toStr(SAI_OBJECT_TYPE_SWITCH, SAI_SWITCH_ATTR_ECMP_DEFAULT_HASH_ALGORITHM).c_str() + ); + return; + } + + auto &haSet = switchCapabilities.ecmpHashAlgorithm.haSet; + std::transform( + haList.cbegin(), haList.cend(), std::inserter(haSet, haSet.begin()), + [](sai_int32_t value) { return static_cast(value); } + ); + + switchCapabilities.ecmpHashAlgorithm.isEnumSupported = true; +} + +void SwitchCapabilities::querySwitchEcmpHashAlgorithmAttrCapabilities() +{ + SWSS_LOG_ENTER(); + + sai_attr_capability_t attrCap; + + auto status = queryAttrCapabilitiesSai( + attrCap, SAI_OBJECT_TYPE_SWITCH, SAI_SWITCH_ATTR_ECMP_DEFAULT_HASH_ALGORITHM + ); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR( + "Failed to get attribute(%s) capabilities", + toStr(SAI_OBJECT_TYPE_SWITCH, SAI_SWITCH_ATTR_ECMP_DEFAULT_HASH_ALGORITHM).c_str() + ); + return; + } + + if (!attrCap.set_implemented) + { + SWSS_LOG_WARN( + "Attribute(%s) SET is not implemented in SAI", + toStr(SAI_OBJECT_TYPE_SWITCH, SAI_SWITCH_ATTR_ECMP_DEFAULT_HASH_ALGORITHM).c_str() + ); + return; + } + + switchCapabilities.ecmpHashAlgorithm.isAttrSupported = true; +} + +void SwitchCapabilities::querySwitchLagHashAlgorithmEnumCapabilities() +{ + SWSS_LOG_ENTER(); + + std::vector haList; + auto status = queryEnumCapabilitiesSai( + haList, SAI_OBJECT_TYPE_SWITCH, SAI_SWITCH_ATTR_LAG_DEFAULT_HASH_ALGORITHM + ); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR( + "Failed to get attribute(%s) enum value capabilities", + toStr(SAI_OBJECT_TYPE_SWITCH, SAI_SWITCH_ATTR_LAG_DEFAULT_HASH_ALGORITHM).c_str() + ); + return; + } + + auto &haSet = switchCapabilities.lagHashAlgorithm.haSet; + std::transform( + haList.cbegin(), haList.cend(), std::inserter(haSet, haSet.begin()), + [](sai_int32_t value) { return static_cast(value); } + ); + + switchCapabilities.lagHashAlgorithm.isEnumSupported = true; +} + +void SwitchCapabilities::querySwitchLagHashAlgorithmAttrCapabilities() +{ + SWSS_LOG_ENTER(); + + sai_attr_capability_t attrCap; + + auto status = queryAttrCapabilitiesSai( + attrCap, SAI_OBJECT_TYPE_SWITCH, SAI_SWITCH_ATTR_LAG_DEFAULT_HASH_ALGORITHM + ); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR( + "Failed to get attribute(%s) capabilities", + toStr(SAI_OBJECT_TYPE_SWITCH, SAI_SWITCH_ATTR_LAG_DEFAULT_HASH_ALGORITHM).c_str() + ); + return; + } + + if (!attrCap.set_implemented) + { + SWSS_LOG_WARN( + "Attribute(%s) SET is not implemented in SAI", + toStr(SAI_OBJECT_TYPE_SWITCH, SAI_SWITCH_ATTR_LAG_DEFAULT_HASH_ALGORITHM).c_str() + ); + return; + } + + switchCapabilities.lagHashAlgorithm.isAttrSupported = true; +} + void SwitchCapabilities::querySwitchCapabilities() { - querySwitchEcmpHashCapabilities(); - querySwitchLagHashCapabilities(); + querySwitchEcmpHashAttrCapabilities(); + querySwitchLagHashAttrCapabilities(); + + querySwitchEcmpHashAlgorithmEnumCapabilities(); + querySwitchEcmpHashAlgorithmAttrCapabilities(); + querySwitchLagHashAlgorithmEnumCapabilities(); + querySwitchLagHashAlgorithmAttrCapabilities(); } void SwitchCapabilities::writeHashCapabilitiesToDb() { SWSS_LOG_ENTER(); - auto key = SwitchCapabilities::capTable.getKeyName(SWITCH_CAPABILITY_KEY); - std::vector fvList = { makeHashFieldCapDbEntry() }; SwitchCapabilities::capTable.set(SWITCH_CAPABILITY_KEY, fvList); - SWSS_LOG_NOTICE("Wrote hash enum capabilities to State DB: %s key", key.c_str()); + SWSS_LOG_NOTICE( + "Wrote hash capabilities to State DB: %s key", + SwitchCapabilities::capTable.getKeyName(SWITCH_CAPABILITY_KEY).c_str() + ); } void SwitchCapabilities::writeSwitchCapabilitiesToDb() { SWSS_LOG_ENTER(); - auto key = SwitchCapabilities::capTable.getKeyName(SWITCH_CAPABILITY_KEY); - std::vector fvList = { makeEcmpHashCapDbEntry(), makeLagHashCapDbEntry() }; + insertBack(fvList, makeEcmpHashAlgorithmCapDbEntry()); + insertBack(fvList, makeLagHashAlgorithmCapDbEntry()); SwitchCapabilities::capTable.set(SWITCH_CAPABILITY_KEY, fvList); - SWSS_LOG_NOTICE("Wrote switch hash capabilities to State DB: %s key", key.c_str()); + SWSS_LOG_NOTICE( + "Wrote switch capabilities to State DB: %s key", + SwitchCapabilities::capTable.getKeyName(SWITCH_CAPABILITY_KEY).c_str() + ); } diff --git a/orchagent/switch/switch_capabilities.h b/orchagent/switch/switch_capabilities.h index 47dcb0c7ec..9bff1df497 100644 --- a/orchagent/switch/switch_capabilities.h +++ b/orchagent/switch/switch_capabilities.h @@ -1,9 +1,10 @@ #pragma once extern "C" { -#include #include +#include #include +#include } #include @@ -21,21 +22,39 @@ class SwitchCapabilities final bool isSwitchEcmpHashSupported() const; bool isSwitchLagHashSupported() const; + bool isSwitchEcmpHashAlgorithmSupported() const; + bool isSwitchLagHashAlgorithmSupported() const; + bool validateSwitchHashFieldCap(const std::set &hfSet) const; + bool validateSwitchEcmpHashAlgorithmCap(sai_hash_algorithm_t haValue) const; + bool validateSwitchLagHashAlgorithmCap(sai_hash_algorithm_t haValue) const; + private: + template + bool validateSwitchHashAlgorithmCap(const T &obj, sai_hash_algorithm_t haValue) const; + swss::FieldValueTuple makeHashFieldCapDbEntry() const; + swss::FieldValueTuple makeEcmpHashCapDbEntry() const; swss::FieldValueTuple makeLagHashCapDbEntry() const; + std::vector makeEcmpHashAlgorithmCapDbEntry() const; + std::vector makeLagHashAlgorithmCapDbEntry() const; + sai_status_t queryEnumCapabilitiesSai(std::vector &capList, sai_object_type_t objType, sai_attr_id_t attrId) const; sai_status_t queryAttrCapabilitiesSai(sai_attr_capability_t &attrCap, sai_object_type_t objType, sai_attr_id_t attrId) const; void queryHashNativeHashFieldListEnumCapabilities(); void queryHashNativeHashFieldListAttrCapabilities(); - void querySwitchEcmpHashCapabilities(); - void querySwitchLagHashCapabilities(); + void querySwitchEcmpHashAttrCapabilities(); + void querySwitchLagHashAttrCapabilities(); + + void querySwitchEcmpHashAlgorithmEnumCapabilities(); + void querySwitchEcmpHashAlgorithmAttrCapabilities(); + void querySwitchLagHashAlgorithmEnumCapabilities(); + void querySwitchLagHashAlgorithmAttrCapabilities(); void queryHashCapabilities(); void querySwitchCapabilities(); @@ -61,6 +80,18 @@ class SwitchCapabilities final struct { bool isAttrSupported = false; } lagHash; + + struct { + std::set haSet; + bool isEnumSupported = false; + bool isAttrSupported = false; + } ecmpHashAlgorithm; + + struct { + std::set haSet; + bool isEnumSupported = false; + bool isAttrSupported = false; + } lagHashAlgorithm; } switchCapabilities; static swss::DBConnector stateDb; diff --git a/orchagent/switch/switch_container.h b/orchagent/switch/switch_container.h index c56ae166f0..a51b551427 100644 --- a/orchagent/switch/switch_container.h +++ b/orchagent/switch/switch_container.h @@ -1,6 +1,7 @@ #pragma once extern "C" { +#include #include } @@ -24,5 +25,15 @@ class SwitchHash final bool is_set = false; } lag_hash; + struct { + sai_hash_algorithm_t value; + bool is_set = false; + } ecmp_hash_algorithm; + + struct { + sai_hash_algorithm_t value; + bool is_set = false; + } lag_hash_algorithm; + std::unordered_map fieldValueMap; }; diff --git a/orchagent/switch/switch_helper.cpp b/orchagent/switch/switch_helper.cpp index d91f382b25..23a7c3fd5a 100644 --- a/orchagent/switch/switch_helper.cpp +++ b/orchagent/switch/switch_helper.cpp @@ -1,5 +1,10 @@ // includes ----------------------------------------------------------------------------------------------------------- +extern "C" { +#include +#include +} + #include #include #include @@ -36,6 +41,17 @@ static const std::unordered_map swHashHash { SWITCH_HASH_FIELD_INNER_L4_SRC_PORT, SAI_NATIVE_HASH_FIELD_INNER_L4_SRC_PORT } }; +static const std::unordered_map swHashAlgorithmMap = +{ + { SWITCH_HASH_ALGORITHM_CRC, SAI_HASH_ALGORITHM_CRC }, + { SWITCH_HASH_ALGORITHM_XOR, SAI_HASH_ALGORITHM_XOR }, + { SWITCH_HASH_ALGORITHM_RANDOM, SAI_HASH_ALGORITHM_RANDOM }, + { SWITCH_HASH_ALGORITHM_CRC_32LO, SAI_HASH_ALGORITHM_CRC_32LO }, + { SWITCH_HASH_ALGORITHM_CRC_32HI, SAI_HASH_ALGORITHM_CRC_32HI }, + { SWITCH_HASH_ALGORITHM_CRC_CCITT, SAI_HASH_ALGORITHM_CRC_CCITT }, + { SWITCH_HASH_ALGORITHM_CRC_XOR, SAI_HASH_ALGORITHM_CRC_XOR } +}; + // switch helper ------------------------------------------------------------------------------------------------------ const SwitchHash& SwitchHelper::getSwHash() const @@ -86,6 +102,30 @@ bool SwitchHelper::parseSwHashFieldList(T &obj, const std::string &field, const return true; } +template +bool SwitchHelper::parseSwHashAlgorithm(T &obj, const std::string &field, const std::string &value) const +{ + SWSS_LOG_ENTER(); + + if (value.empty()) + { + SWSS_LOG_ERROR("Failed to parse field(%s): empty value is prohibited", field.c_str()); + return false; + } + + const auto &cit = swHashAlgorithmMap.find(value); + if (cit == swHashAlgorithmMap.cend()) + { + SWSS_LOG_ERROR("Failed to parse field(%s): invalid value(%s)", field.c_str(), value.c_str()); + return false; + } + + obj.value = cit->second; + obj.is_set = true; + + return true; +} + bool SwitchHelper::parseSwHashEcmpHash(SwitchHash &hash, const std::string &field, const std::string &value) const { return parseSwHashFieldList(hash.ecmp_hash, field, value); @@ -96,6 +136,16 @@ bool SwitchHelper::parseSwHashLagHash(SwitchHash &hash, const std::string &field return parseSwHashFieldList(hash.lag_hash, field, value); } +bool SwitchHelper::parseSwHashEcmpHashAlgorithm(SwitchHash &hash, const std::string &field, const std::string &value) const +{ + return parseSwHashAlgorithm(hash.ecmp_hash_algorithm, field, value); +} + +bool SwitchHelper::parseSwHashLagHashAlgorithm(SwitchHash &hash, const std::string &field, const std::string &value) const +{ + return parseSwHashAlgorithm(hash.lag_hash_algorithm, field, value); +} + bool SwitchHelper::parseSwHash(SwitchHash &hash) const { SWSS_LOG_ENTER(); @@ -119,6 +169,20 @@ bool SwitchHelper::parseSwHash(SwitchHash &hash) const return false; } } + else if (field == SWITCH_HASH_ECMP_HASH_ALGORITHM) + { + if (!parseSwHashEcmpHashAlgorithm(hash, field, value)) + { + return false; + } + } + else if (field == SWITCH_HASH_LAG_HASH_ALGORITHM) + { + if (!parseSwHashLagHashAlgorithm(hash, field, value)) + { + return false; + } + } else { SWSS_LOG_WARN("Unknown field(%s): skipping ...", field.c_str()); @@ -132,7 +196,10 @@ bool SwitchHelper::validateSwHash(SwitchHash &hash) const { SWSS_LOG_ENTER(); - if (!hash.ecmp_hash.is_set && !hash.lag_hash.is_set) + auto cond = hash.ecmp_hash.is_set || hash.lag_hash.is_set; + cond = cond || hash.ecmp_hash_algorithm.is_set || hash.lag_hash_algorithm.is_set; + + if (!cond) { SWSS_LOG_ERROR("Validation error: missing valid fields"); return false; diff --git a/orchagent/switch/switch_helper.h b/orchagent/switch/switch_helper.h index 611ce2b6fb..d7be11981f 100644 --- a/orchagent/switch/switch_helper.h +++ b/orchagent/switch/switch_helper.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "switch_container.h" class SwitchHelper final @@ -16,9 +18,13 @@ class SwitchHelper final private: template bool parseSwHashFieldList(T &obj, const std::string &field, const std::string &value) const; + template + bool parseSwHashAlgorithm(T &obj, const std::string &field, const std::string &value) const; bool parseSwHashEcmpHash(SwitchHash &hash, const std::string &field, const std::string &value) const; bool parseSwHashLagHash(SwitchHash &hash, const std::string &field, const std::string &value) const; + bool parseSwHashEcmpHashAlgorithm(SwitchHash &hash, const std::string &field, const std::string &value) const; + bool parseSwHashLagHashAlgorithm(SwitchHash &hash, const std::string &field, const std::string &value) const; bool validateSwHash(SwitchHash &hash) const; diff --git a/orchagent/switch/switch_schema.h b/orchagent/switch/switch_schema.h index c836eff120..16a17f179c 100644 --- a/orchagent/switch/switch_schema.h +++ b/orchagent/switch/switch_schema.h @@ -23,3 +23,14 @@ #define SWITCH_HASH_ECMP_HASH "ecmp_hash" #define SWITCH_HASH_LAG_HASH "lag_hash" + +#define SWITCH_HASH_ALGORITHM_CRC "CRC" +#define SWITCH_HASH_ALGORITHM_XOR "XOR" +#define SWITCH_HASH_ALGORITHM_RANDOM "RANDOM" +#define SWITCH_HASH_ALGORITHM_CRC_32LO "CRC_32LO" +#define SWITCH_HASH_ALGORITHM_CRC_32HI "CRC_32HI" +#define SWITCH_HASH_ALGORITHM_CRC_CCITT "CRC_CCITT" +#define SWITCH_HASH_ALGORITHM_CRC_XOR "CRC_XOR" + +#define SWITCH_HASH_ECMP_HASH_ALGORITHM "ecmp_hash_algorithm" +#define SWITCH_HASH_LAG_HASH_ALGORITHM "lag_hash_algorithm" diff --git a/orchagent/switchorch.cpp b/orchagent/switchorch.cpp index 76c17812d4..06dc36e472 100644 --- a/orchagent/switchorch.cpp +++ b/orchagent/switchorch.cpp @@ -497,6 +497,17 @@ bool SwitchOrch::setSwitchHashFieldListSai(const SwitchHash &hash, bool isEcmpHa return status == SAI_STATUS_SUCCESS; } +bool SwitchOrch::setSwitchHashAlgorithmSai(const SwitchHash &hash, bool isEcmpHash) const +{ + sai_attribute_t attr; + + attr.id = isEcmpHash ? SAI_SWITCH_ATTR_ECMP_DEFAULT_HASH_ALGORITHM : SAI_SWITCH_ATTR_LAG_DEFAULT_HASH_ALGORITHM; + attr.value.s32 = static_cast(isEcmpHash ? hash.ecmp_hash_algorithm.value : hash.lag_hash_algorithm.value); + + auto status = sai_switch_api->set_switch_attribute(gSwitchId, &attr); + return status == SAI_STATUS_SUCCESS; +} + bool SwitchOrch::setSwitchHash(const SwitchHash &hash) { SWSS_LOG_ENTER(); @@ -574,6 +585,76 @@ bool SwitchOrch::setSwitchHash(const SwitchHash &hash) } } + if (hash.ecmp_hash_algorithm.is_set) + { + if (!hObj.ecmp_hash_algorithm.is_set || (hObj.ecmp_hash_algorithm.value != hash.ecmp_hash_algorithm.value)) + { + if (swCap.isSwitchEcmpHashAlgorithmSupported()) + { + if (!swCap.validateSwitchEcmpHashAlgorithmCap(hash.ecmp_hash_algorithm.value)) + { + SWSS_LOG_ERROR("Failed to validate switch ECMP hash algorithm: capability is not supported"); + return false; + } + + if (!setSwitchHashAlgorithmSai(hash, true)) + { + SWSS_LOG_ERROR("Failed to set switch ECMP hash algorithm in SAI"); + return false; + } + + cfgUpd = true; + } + else + { + SWSS_LOG_WARN("Switch ECMP hash algorithm configuration is not supported: skipping ..."); + } + } + } + else + { + if (hObj.ecmp_hash_algorithm.is_set) + { + SWSS_LOG_ERROR("Failed to remove switch ECMP hash algorithm configuration: operation is not supported"); + return false; + } + } + + if (hash.lag_hash_algorithm.is_set) + { + if (!hObj.lag_hash_algorithm.is_set || (hObj.lag_hash_algorithm.value != hash.lag_hash_algorithm.value)) + { + if (swCap.isSwitchLagHashAlgorithmSupported()) + { + if (!swCap.validateSwitchLagHashAlgorithmCap(hash.lag_hash_algorithm.value)) + { + SWSS_LOG_ERROR("Failed to validate switch LAG hash algorithm: capability is not supported"); + return false; + } + + if (!setSwitchHashAlgorithmSai(hash, false)) + { + SWSS_LOG_ERROR("Failed to set switch LAG hash algorithm in SAI"); + return false; + } + + cfgUpd = true; + } + else + { + SWSS_LOG_WARN("Switch LAG hash algorithm configuration is not supported: skipping ..."); + } + } + } + else + { + if (hObj.lag_hash_algorithm.is_set) + { + SWSS_LOG_ERROR("Failed to remove switch LAG hash algorithm configuration: operation is not supported"); + return false; + } + } + // Don't update internal cache when config remains unchanged if (!cfgUpd) { diff --git a/orchagent/switchorch.h b/orchagent/switchorch.h index c6ee9997f9..7135bcdc39 100644 --- a/orchagent/switchorch.h +++ b/orchagent/switchorch.h @@ -58,6 +58,7 @@ class SwitchOrch : public Orch // Switch hash bool setSwitchHashFieldListSai(const SwitchHash &hash, bool isEcmpHash) const; + bool setSwitchHashAlgorithmSai(const SwitchHash &hash, bool isEcmpHash) const; bool setSwitchHash(const SwitchHash &hash); bool getSwitchHashOidSai(sai_object_id_t &oid, bool isEcmpHash) const; diff --git a/tests/conftest.py b/tests/conftest.py index a431f46cc4..9264417214 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -27,6 +27,7 @@ from dvslib import dvs_mirror from dvslib import dvs_policer from dvslib import dvs_hash +from dvslib import dvs_switch from buffer_model import enable_dynamic_buffer @@ -160,6 +161,8 @@ def _populate_default_asic_db_values(self) -> None: self.default_hash_keys = self.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_HASH") + self.default_switch_keys = self.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_SWITCH") + self.default_copp_policers = self.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_POLICER") @@ -1348,6 +1351,7 @@ def get_asic_db(self) -> AsicDbValidator: db.default_acl_tables = self.asicdb.default_acl_tables db.default_acl_entries = self.asicdb.default_acl_entries db.default_hash_keys = self.asicdb.default_hash_keys + db.default_switch_keys = self.asicdb.default_switch_keys db.default_copp_policers = self.asicdb.default_copp_policers db.port_name_map = self.asicdb.portnamemap db.default_vlan_id = self.asicdb.default_vlan_id @@ -1937,6 +1941,10 @@ def dvs_hash_manager(request, dvs): request.cls.dvs_hash = dvs_hash.DVSHash(dvs.get_asic_db(), dvs.get_config_db()) +@pytest.fixture(scope="class") +def dvs_switch_manager(request, dvs): + request.cls.dvs_switch = dvs_switch.DVSSwitch(dvs.get_asic_db()) + ##################### DPB fixtures ########################################### def create_dpb_config_file(dvs): cmd = "sonic-cfggen -j /etc/sonic/init_cfg.json -j /tmp/ports.json --print-data > /tmp/dpb_config_db.json" diff --git a/tests/dvslib/dvs_switch.py b/tests/dvslib/dvs_switch.py new file mode 100644 index 0000000000..b57dc7082f --- /dev/null +++ b/tests/dvslib/dvs_switch.py @@ -0,0 +1,96 @@ +"""Utilities for interacting with SWITCH objects when writing VS tests.""" +from typing import Dict, List + + +class DVSSwitch: + """Manage switch objects on the virtual switch.""" + + ADB_SWITCH = "ASIC_STATE:SAI_OBJECT_TYPE_SWITCH" + + def __init__(self, asic_db): + """Create a new DVS switch manager.""" + self.asic_db = asic_db + + def get_switch_ids( + self, + expected: int = None + ) -> List[str]: + """Get all of the switch ids in ASIC DB. + + Args: + expected: The number of switch ids that are expected to be present in ASIC DB. + + Returns: + The list of switch ids in ASIC DB. + """ + if expected is None: + return self.asic_db.get_keys(self.ADB_SWITCH) + + num_keys = len(self.asic_db.default_switch_keys) + expected + keys = self.asic_db.wait_for_n_keys(self.ADB_SWITCH, num_keys) + + for k in self.asic_db.default_switch_keys: + assert k in keys + + return [k for k in keys if k not in self.asic_db.default_switch_keys] + + def verify_switch_count( + self, + expected: int + ) -> None: + """Verify that there are N switch objects in ASIC DB. + + Args: + expected: The number of switch ids that are expected to be present in ASIC DB. + """ + self.get_switch_ids(expected) + + def verify_switch_generic( + self, + sai_switch_id: str, + sai_qualifiers: Dict[str, str] + ) -> None: + """Verify that switch object has correct ASIC DB representation. + + Args: + sai_switch_id: The specific switch id to check in ASIC DB. + sai_qualifiers: The expected set of SAI qualifiers to be found in ASIC DB. + """ + entry = self.asic_db.wait_for_entry(self.ADB_SWITCH, sai_switch_id) + + for k, v in entry.items(): + if k == "NULL": + continue + elif k in sai_qualifiers: + if k == "SAI_SWITCH_ATTR_ECMP_DEFAULT_HASH_ALGORITHM": + assert sai_qualifiers[k] == v + elif k == "SAI_SWITCH_ATTR_LAG_DEFAULT_HASH_ALGORITHM": + assert sai_qualifiers[k] == v + else: + assert False, "Unknown SAI qualifier: key={}, value={}".format(k, v) + + def verify_switch( + self, + sai_switch_id: str, + sai_qualifiers: Dict[str, str], + strict: bool = False + ) -> None: + """Verify that switch object has correct ASIC DB representation. + + Args: + sai_switch_id: The specific switch id to check in ASIC DB. + sai_qualifiers: The expected set of SAI qualifiers to be found in ASIC DB. + strict: Specifies whether verification should be strict + """ + if strict: + self.verify_switch_generic(sai_switch_id, sai_qualifiers) + return + + entry = self.asic_db.wait_for_entry(self.ADB_SWITCH, sai_switch_id) + + attr_dict = { + **entry, + **sai_qualifiers + } + + self.verify_switch_generic(sai_switch_id, attr_dict) diff --git a/tests/test_hash.py b/tests/test_hash.py index 9c08aabb65..b84dd91eaf 100644 --- a/tests/test_hash.py +++ b/tests/test_hash.py @@ -32,6 +32,15 @@ "ETHERTYPE", "IN_PORT" ] +HASH_ALGORITHM = [ + "CRC", + "XOR", + "RANDOM", + "CRC_32LO", + "CRC_32HI", + "CRC_CCITT", + "CRC_XOR" +] SAI_HASH_FIELD_LIST = [ "SAI_NATIVE_HASH_FIELD_DST_MAC", @@ -59,9 +68,19 @@ "SAI_NATIVE_HASH_FIELD_ETHERTYPE", "SAI_NATIVE_HASH_FIELD_IN_PORT" ] +SAI_HASH_ALGORITHM = [ + "SAI_HASH_ALGORITHM_CRC", + "SAI_HASH_ALGORITHM_XOR", + "SAI_HASH_ALGORITHM_RANDOM", + "SAI_HASH_ALGORITHM_CRC_32LO", + "SAI_HASH_ALGORITHM_CRC_32HI", + "SAI_HASH_ALGORITHM_CRC_CCITT", + "SAI_HASH_ALGORITHM_CRC_XOR" +] @pytest.mark.usefixtures("dvs_hash_manager") +@pytest.mark.usefixtures("dvs_switch_manager") class TestHashBasicFlows: @pytest.fixture(scope="class") def hashData(self, dvs_hash_manager): @@ -83,6 +102,25 @@ def hashData(self, dvs_hash_manager): hashlogger.info("Deinitialize HASH data") + @pytest.fixture(scope="class") + def switchData(self, dvs_switch_manager): + hashlogger.info("Initialize SWITCH data") + + hashlogger.info("Verify SWITCH count") + self.dvs_switch.verify_switch_count(0) + + hashlogger.info("Get SWITCH id") + switchIdList = self.dvs_switch.get_switch_ids() + + # Assumption: VS has only one SWITCH object + meta_dict = { + "id": switchIdList[0] + } + + yield meta_dict + + hashlogger.info("Deinitialize SWITCH data") + @pytest.mark.parametrize( "hash,field", [ pytest.param( @@ -167,6 +205,86 @@ def test_HashDefaultSwitchGlobalConfiguration(self, hash, field, testlog, hashDa sai_qualifiers=sai_attr_dict ) + @pytest.mark.parametrize( + "algorithm,attr,field", [ + pytest.param( + "ecmp", + "SAI_SWITCH_ATTR_ECMP_DEFAULT_HASH_ALGORITHM", + "ecmp_hash_algorithm", + id="ecmp-hash-algorithm" + ), + pytest.param( + "lag", + "SAI_SWITCH_ATTR_LAG_DEFAULT_HASH_ALGORITHM", + "lag_hash_algorithm", + id="lag-hash-algorithm" + ) + ] + ) + @pytest.mark.parametrize( + "value", HASH_ALGORITHM + ) + def test_HashAlgorithmSwitchGlobalConfiguration(self, algorithm, attr, field, value, testlog, switchData): + attr_dict = { + field: value + } + + hashlogger.info("Update {} hash algorithm".format(algorithm.upper())) + self.dvs_hash.update_switch_hash( + qualifiers=attr_dict + ) + + switchId = switchData["id"] + sai_attr_dict = { + attr: SAI_HASH_ALGORITHM[HASH_ALGORITHM.index(value)] + } + + hashlogger.info("Validate {} hash algorithm".format(algorithm.upper())) + self.dvs_switch.verify_switch( + sai_switch_id=switchId, + sai_qualifiers=sai_attr_dict + ) + + @pytest.mark.parametrize( + "algorithm,attr,field", [ + pytest.param( + "ecmp", + "SAI_SWITCH_ATTR_ECMP_DEFAULT_HASH_ALGORITHM", + "ecmp_hash_algorithm", + id="ecmp-hash-algorithm" + ), + pytest.param( + "lag", + "SAI_SWITCH_ATTR_LAG_DEFAULT_HASH_ALGORITHM", + "lag_hash_algorithm", + id="lag-hash-algorithm" + ) + ] + ) + @pytest.mark.parametrize( + "value", [ "CRC" ] + ) + def test_HashDefaultAlgorithmSwitchGlobalConfiguration(self, algorithm, attr, field, value, testlog, switchData): + attr_dict = { + field: value + } + + hashlogger.info("Update {} hash algorithm".format(algorithm.upper())) + self.dvs_hash.update_switch_hash( + qualifiers=attr_dict + ) + + switchId = switchData["id"] + sai_attr_dict = { + attr: SAI_HASH_ALGORITHM[HASH_ALGORITHM.index(value)] + } + + hashlogger.info("Validate {} hash algorithm".format(algorithm.upper())) + self.dvs_switch.verify_switch( + sai_switch_id=switchId, + sai_qualifiers=sai_attr_dict + ) + # Add Dummy always-pass test at end as workaroud # for issue when Flaky fail on final test it invokes module tear-down before retrying