diff --git a/lib/ClientSai.cpp b/lib/ClientSai.cpp index 809f50e73..f35163b86 100644 --- a/lib/ClientSai.cpp +++ b/lib/ClientSai.cpp @@ -961,16 +961,6 @@ sai_status_t ClientSai::getStats( return waitForGetStatsResponse(number_of_counters, counters); } -sai_status_t ClientSai::queryStatsCapability( - _In_ sai_object_id_t switchId, - _In_ sai_object_type_t objectType, - _Inout_ sai_stat_capability_list_t *stats_capability) -{ - SWSS_LOG_ENTER(); - - return SAI_STATUS_NOT_IMPLEMENTED; -} - sai_status_t ClientSai::waitForGetStatsResponse( _In_ uint32_t number_of_counters, _Out_ uint64_t *counters) @@ -999,6 +989,104 @@ sai_status_t ClientSai::waitForGetStatsResponse( return status; } +sai_status_t ClientSai::queryStatsCapability( + _In_ sai_object_id_t switchId, + _In_ sai_object_type_t objectType, + _Inout_ sai_stat_capability_list_t *stats_capability) +{ + MUTEX(); + SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + + if (stats_capability && stats_capability->list) + { + // clear input list, since we use serialize to transfer values + for (uint32_t idx = 0; idx < stats_capability->count; idx++) + { + stats_capability->list[idx].stat_enum = 0; + stats_capability->list[idx].stat_modes = 0; + } + } + + auto switchIdStr = sai_serialize_object_id(switchId); + auto objectTypeStr = sai_serialize_object_type(objectType); + const std::string list_size = std::to_string(stats_capability->count); + + const std::vector entry = + { + swss::FieldValueTuple("OBJECT_TYPE", objectTypeStr), + swss::FieldValueTuple("LIST_SIZE", list_size) + }; + + SWSS_LOG_DEBUG( + "Query arguments: switch %s, object type: %s, count: %s", + switchIdStr.c_str(), + objectTypeStr.c_str(), + list_size.c_str() + ); + + // This query will not put any data into the ASIC view, just into the + // message queue + + m_communicationChannel->set(switchIdStr, entry, REDIS_ASIC_STATE_COMMAND_STATS_CAPABILITY_QUERY); + + auto status = waitForQueryStatsCapabilityResponse(stats_capability); + + return status; +} + +sai_status_t ClientSai::waitForQueryStatsCapabilityResponse( + _Inout_ sai_stat_capability_list_t* stats_capability) +{ + SWSS_LOG_ENTER(); + + swss::KeyOpFieldsValuesTuple kco; + + auto status = m_communicationChannel->wait(REDIS_ASIC_STATE_COMMAND_STATS_CAPABILITY_RESPONSE, kco); + + if (status == SAI_STATUS_SUCCESS) + { + const std::vector &values = kfvFieldsValues(kco); + + if (values.size() != 3) + { + SWSS_LOG_ERROR("Invalid response from syncd: expected 3 value, received %zu", values.size()); + + return SAI_STATUS_FAILURE; + } + + const std::string &stat_enum_str = fvValue(values[0]); + const std::string &stat_modes_str = fvValue(values[1]); + const uint32_t num_capabilities = std::stoi(fvValue(values[2])); + + SWSS_LOG_DEBUG("Received payload: stat_enums = '%s', stat_modes = '%s', count = %d", + stat_enum_str.c_str(), stat_modes_str.c_str(), num_capabilities); + + stats_capability->count = num_capabilities; + + sai_deserialize_stat_capability_list(stats_capability, stat_enum_str, stat_modes_str); + } + else if (status == SAI_STATUS_BUFFER_OVERFLOW) + { + const std::vector &values = kfvFieldsValues(kco); + + if (values.size() != 1) + { + SWSS_LOG_ERROR("Invalid response from syncd: expected 1 value, received %zu", values.size()); + + return SAI_STATUS_FAILURE; + } + + const uint32_t num_capabilities = std::stoi(fvValue(values[0])); + + SWSS_LOG_DEBUG("Received payload: count = %u", num_capabilities); + + stats_capability->count = num_capabilities; + } + + return status; +} + sai_status_t ClientSai::getStatsExt( _In_ sai_object_type_t object_type, _In_ sai_object_id_t object_id, diff --git a/lib/ClientSai.h b/lib/ClientSai.h index 05d0f5888..4eeccd099 100644 --- a/lib/ClientSai.h +++ b/lib/ClientSai.h @@ -285,6 +285,9 @@ namespace sairedis sai_status_t waitForObjectTypeGetAvailabilityResponse( _In_ uint64_t *count); + sai_status_t waitForQueryStatsCapabilityResponse( + _Inout_ sai_stat_capability_list_t* stats_capability); + private: void handleNotification( diff --git a/lib/ClientServerSai.cpp b/lib/ClientServerSai.cpp index 60366aaef..b98299ce1 100644 --- a/lib/ClientServerSai.cpp +++ b/lib/ClientServerSai.cpp @@ -256,7 +256,10 @@ sai_status_t ClientServerSai::queryStatsCapability( SWSS_LOG_ENTER(); REDIS_CHECK_API_INITIALIZED(); - return SAI_STATUS_NOT_IMPLEMENTED; + return m_sai->queryStatsCapability( + switchId, + objectType, + stats_capability); } sai_status_t ClientServerSai::getStatsExt( diff --git a/lib/Recorder.cpp b/lib/Recorder.cpp index 32bc2d0d8..481cf2e8c 100644 --- a/lib/Recorder.cpp +++ b/lib/Recorder.cpp @@ -370,6 +370,24 @@ void Recorder::recordObjectTypeGetAvailabilityResponse( recordLine("Q|object_type_get_availability|" + sai_serialize_status(status) + "|" + Globals::joinFieldValues(arguments)); } +void Recorder::recordQueryStatsCapability( + _In_ const std::string& key, + _In_ const std::vector& arguments) +{ + SWSS_LOG_ENTER(); + + recordLine("q|stats_capability|" + key + "|" + Globals::joinFieldValues(arguments)); +} + +void Recorder::recordQueryStatsCapabilityResponse( + _In_ sai_status_t status, + _In_ const std::string& arguments) +{ + SWSS_LOG_ENTER(); + + recordLine("Q|stats_capability|" + sai_serialize_status(status) + "|" + arguments); +} + void Recorder::recordNotifySyncd( _In_ const std::string& key) { @@ -1093,6 +1111,67 @@ void Recorder::recordQueryAattributeEnumValuesCapabilityResponse( recordQueryAttributeEnumValuesCapabilityResponse(status, values); } +void Recorder::recordQueryStatsCapability( + _In_ sai_object_id_t switchId, + _In_ sai_object_type_t object_type, + _Inout_ sai_stat_capability_list_t* stats_capability) +{ + SWSS_LOG_ENTER(); + + auto key = sai_serialize_object_type(SAI_OBJECT_TYPE_SWITCH) + ":" + sai_serialize_object_id(switchId); + + auto object_type_str = sai_serialize_object_type(object_type); + const std::string list_size = std::to_string(stats_capability->count); + const std::vector values = + { + swss::FieldValueTuple("OBJECT_TYPE", object_type_str), + swss::FieldValueTuple("LIST_SIZE", list_size) + }; + + SWSS_LOG_DEBUG("Query arguments: switch %s, object_type: %s, count: %s", + key.c_str(), + object_type_str.c_str(), + list_size.c_str()); + + recordQueryStatsCapability (key, values); +} + +void Recorder::recordQueryStatsCapabilityResponse( + _In_ sai_status_t status, + _In_ sai_object_type_t objectType, + _In_ const sai_stat_capability_list_t *stats_capability) +{ + SWSS_LOG_ENTER(); + + std::string str_stats_list; + + auto meta = sai_metadata_get_object_type_info(objectType); + + if (meta == NULL) + { + SWSS_LOG_ERROR("Failed to find object metadata: object type %s", + sai_serialize_object_type(objectType).c_str()); + + return; + } + + if (meta->statenum == NULL) + { + SWSS_LOG_ERROR("%s does not support stats", meta->objecttypename); + + return; + } + + bool countOnly = (status == SAI_STATUS_BUFFER_OVERFLOW); + + if (status == SAI_STATUS_SUCCESS || status == SAI_STATUS_BUFFER_OVERFLOW) + { + str_stats_list = sai_serialize_stat_capability_list(*stats_capability, meta->statenum, countOnly); + } + + recordQueryStatsCapabilityResponse(status, str_stats_list); +} + void Recorder::recordNotifySyncd( _In_ sai_object_id_t switchId, _In_ sai_redis_notify_syncd_t redisNotifySyncd) diff --git a/lib/Recorder.h b/lib/Recorder.h index ccda2bacb..c2d82b5a4 100644 --- a/lib/Recorder.h +++ b/lib/Recorder.h @@ -353,6 +353,16 @@ namespace sairedis _In_ sai_attr_id_t attrId, _In_ const sai_s32_list_t* enumValuesCapability); + void recordQueryStatsCapability( + _In_ sai_object_id_t switch_id, + _In_ sai_object_type_t object_type, + _Inout_ sai_stat_capability_list_t* stats_capability); + + void recordQueryStatsCapabilityResponse( + _In_ sai_status_t status, + _In_ sai_object_type_t objectType, + _In_ const sai_stat_capability_list_t *stats_capability); + // TODO move to private void recordQueryAttributeCapability( _In_ const std::string& key, @@ -378,6 +388,14 @@ namespace sairedis _In_ sai_status_t status, _In_ const std::vector& arguments); + void recordQueryStatsCapability( + _In_ const std::string& key, + _In_ const std::vector& arguments); + + void recordQueryStatsCapabilityResponse( + _In_ sai_status_t status, + _In_ const std::string& arguments); + public: // SAI notifications void recordNotification( diff --git a/lib/RedisRemoteSaiInterface.cpp b/lib/RedisRemoteSaiInterface.cpp index 4e0e284b9..82ab810f6 100644 --- a/lib/RedisRemoteSaiInterface.cpp +++ b/lib/RedisRemoteSaiInterface.cpp @@ -1205,16 +1205,6 @@ sai_status_t RedisRemoteSaiInterface::getStats( return waitForGetStatsResponse(number_of_counters, counters); } -sai_status_t RedisRemoteSaiInterface::queryStatsCapability( - _In_ sai_object_id_t switchId, - _In_ sai_object_type_t objectType, - _Inout_ sai_stat_capability_list_t *stats_capability) -{ - SWSS_LOG_ENTER(); - - return SAI_STATUS_NOT_IMPLEMENTED; -} - sai_status_t RedisRemoteSaiInterface::waitForGetStatsResponse( _In_ uint32_t number_of_counters, _Out_ uint64_t *counters) @@ -1243,6 +1233,106 @@ sai_status_t RedisRemoteSaiInterface::waitForGetStatsResponse( return status; } +sai_status_t RedisRemoteSaiInterface::queryStatsCapability( + _In_ sai_object_id_t switchId, + _In_ sai_object_type_t objectType, + _Inout_ sai_stat_capability_list_t *stats_capability) +{ + SWSS_LOG_ENTER(); + + if (stats_capability && stats_capability->list) + { + // clear input list, since we use serialize to transfer values + for (uint32_t idx = 0; idx < stats_capability->count; idx++) + { + stats_capability->list[idx].stat_enum = 0; + stats_capability->list[idx].stat_modes = 0; + } + } + + auto switchIdStr = sai_serialize_object_id(switchId); + auto objectTypeStr = sai_serialize_object_type(objectType); + const std::string list_size = std::to_string(stats_capability->count); + + const std::vector entry = + { + swss::FieldValueTuple("OBJECT_TYPE", objectTypeStr), + swss::FieldValueTuple("LIST_SIZE", list_size) + }; + + SWSS_LOG_DEBUG( + "Query arguments: switch %s, object type: %s, count: %s", + switchIdStr.c_str(), + objectTypeStr.c_str(), + list_size.c_str() + ); + + // This query will not put any data into the ASIC view, just into the + // message queue + + m_recorder->recordQueryStatsCapability(switchId, objectType, stats_capability); + + m_communicationChannel->set(switchIdStr, entry, REDIS_ASIC_STATE_COMMAND_STATS_CAPABILITY_QUERY); + + auto status = waitForQueryStatsCapabilityResponse(stats_capability); + + m_recorder->recordQueryStatsCapabilityResponse(status, objectType, stats_capability); + + return status; +} + +sai_status_t RedisRemoteSaiInterface::waitForQueryStatsCapabilityResponse( + _Inout_ sai_stat_capability_list_t* stats_capability) +{ + SWSS_LOG_ENTER(); + + swss::KeyOpFieldsValuesTuple kco; + + auto status = m_communicationChannel->wait(REDIS_ASIC_STATE_COMMAND_STATS_CAPABILITY_RESPONSE, kco); + + if (status == SAI_STATUS_SUCCESS) + { + const std::vector &values = kfvFieldsValues(kco); + + if (values.size() != 3) + { + SWSS_LOG_ERROR("Invalid response from syncd: expected 3 value, received %zu", values.size()); + + return SAI_STATUS_FAILURE; + } + + const std::string &stat_enum_str = fvValue(values[0]); + const std::string &stat_modes_str = fvValue(values[1]); + const uint32_t num_capabilities = std::stoi(fvValue(values[2])); + + SWSS_LOG_DEBUG("Received payload: stat_enums = '%s', stat_modes = '%s', count = %d", + stat_enum_str.c_str(), stat_modes_str.c_str(), num_capabilities); + + stats_capability->count = num_capabilities; + + sai_deserialize_stat_capability_list(stats_capability, stat_enum_str, stat_modes_str); + } + else if (status == SAI_STATUS_BUFFER_OVERFLOW) + { + const std::vector &values = kfvFieldsValues(kco); + + if (values.size() != 1) + { + SWSS_LOG_ERROR("Invalid response from syncd: expected 1 value, received %zu", values.size()); + + return SAI_STATUS_FAILURE; + } + + const uint32_t num_capabilities = std::stoi(fvValue(values[0])); + + SWSS_LOG_DEBUG("Received payload: count = %u", num_capabilities); + + stats_capability->count = num_capabilities; + } + + return status; +} + sai_status_t RedisRemoteSaiInterface::getStatsExt( _In_ sai_object_type_t object_type, _In_ sai_object_id_t object_id, diff --git a/lib/RedisRemoteSaiInterface.h b/lib/RedisRemoteSaiInterface.h index 8d5b01c1f..54ed30333 100644 --- a/lib/RedisRemoteSaiInterface.h +++ b/lib/RedisRemoteSaiInterface.h @@ -325,6 +325,9 @@ namespace sairedis sai_status_t waitForObjectTypeGetAvailabilityResponse( _In_ uint64_t *count); + sai_status_t waitForQueryStatsCapabilityResponse( + _Inout_ sai_stat_capability_list_t* stats_capability); + private: // notify syncd response sai_status_t waitForNotifySyncdResponse(); diff --git a/lib/Sai.cpp b/lib/Sai.cpp index 15725dc24..4110dd3b5 100644 --- a/lib/Sai.cpp +++ b/lib/Sai.cpp @@ -356,9 +356,15 @@ sai_status_t Sai::queryStatsCapability( _In_ sai_object_type_t objectType, _Inout_ sai_stat_capability_list_t *stats_capability) { + MUTEX(); SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); + REDIS_CHECK_CONTEXT(switchId); - return SAI_STATUS_NOT_IMPLEMENTED; + return context->m_meta->queryStatsCapability( + switchId, + objectType, + stats_capability); } sai_status_t Sai::getStatsExt( diff --git a/lib/ServerSai.cpp b/lib/ServerSai.cpp index 24bd4043a..448222d3a 100644 --- a/lib/ServerSai.cpp +++ b/lib/ServerSai.cpp @@ -274,9 +274,14 @@ sai_status_t ServerSai::queryStatsCapability( _In_ sai_object_type_t objectType, _Inout_ sai_stat_capability_list_t *stats_capability) { + MUTEX(); SWSS_LOG_ENTER(); + REDIS_CHECK_API_INITIALIZED(); - return SAI_STATUS_NOT_IMPLEMENTED; + return m_sai->queryStatsCapability( + switchId, + objectType, + stats_capability); } sai_status_t ServerSai::getStatsExt( @@ -729,6 +734,9 @@ sai_status_t ServerSai::processSingleEvent( if (op == REDIS_ASIC_STATE_COMMAND_OBJECT_TYPE_GET_AVAILABILITY_QUERY) return processObjectTypeGetAvailabilityQuery(kco); + if (op == REDIS_ASIC_STATE_COMMAND_STATS_CAPABILITY_QUERY) + return processStatsCapabilityQuery(kco); + SWSS_LOG_THROW("event op '%s' is not implemented, FIXME", op.c_str()); } @@ -2027,6 +2035,87 @@ sai_status_t ServerSai::processObjectTypeGetAvailabilityQuery( return status; } +sai_status_t ServerSai::processStatsCapabilityQuery( + _In_ const swss::KeyOpFieldsValuesTuple &kco) +{ + SWSS_LOG_ENTER(); + + auto& strSwitchOid = kfvKey(kco); + + sai_object_id_t switchOid; + sai_deserialize_object_id(strSwitchOid, switchOid); + + auto& values = kfvFieldsValues(kco); + + if (values.size() != 2) + { + SWSS_LOG_ERROR("Invalid input: expected 2 arguments, received %zu", values.size()); + + m_selectableChannel->set(sai_serialize_status(SAI_STATUS_INVALID_PARAMETER), {}, REDIS_ASIC_STATE_COMMAND_STATS_CAPABILITY_RESPONSE); + + return SAI_STATUS_INVALID_PARAMETER; + } + + sai_object_type_t objectType; + sai_deserialize_object_type(fvValue(values[0]), objectType); + + uint32_t list_size = std::stoi(fvValue(values[1])); + + std::vector stat_capability_list(list_size); + + sai_stat_capability_list_t statCapList; + + statCapList.count = list_size; + statCapList.list = stat_capability_list.data(); + + sai_status_t status = m_sai->queryStatsCapability(switchOid, objectType, &statCapList); + + std::vector entry; + + if (status == SAI_STATUS_SUCCESS) + { + std::vector vec_stat_enum; + std::vector vec_stat_modes; + + for (uint32_t it = 0; it < statCapList.count; it++) + { + vec_stat_enum.push_back(std::to_string(statCapList.list[it].stat_enum)); + vec_stat_modes.push_back(std::to_string(statCapList.list[it].stat_modes)); + } + + std::ostringstream join_stat_enum; + std::copy(vec_stat_enum.begin(), vec_stat_enum.end(), std::ostream_iterator(join_stat_enum, ",")); + auto strCapEnum = join_stat_enum.str(); + + std::ostringstream join_stat_modes; + std::copy(vec_stat_modes.begin(), vec_stat_modes.end(), std::ostream_iterator(join_stat_modes, ",")); + auto strCapModes = join_stat_modes.str(); + + entry = + { + swss::FieldValueTuple("STAT_ENUM", strCapEnum), + swss::FieldValueTuple("STAT_MODES", strCapModes), + swss::FieldValueTuple("STAT_COUNT", std::to_string(statCapList.count)) + }; + + SWSS_LOG_DEBUG("Sending response: stat_enums = '%s', stat_modes = '%s', count = %d", + strCapEnum.c_str(), strCapModes.c_str(), statCapList.count); + } + else if (status == SAI_STATUS_BUFFER_OVERFLOW) + { + entry = + { + swss::FieldValueTuple("STAT_COUNT", std::to_string(statCapList.count)) + }; + + SWSS_LOG_DEBUG("Sending response: count = %u", statCapList.count); + } + + m_selectableChannel->set(sai_serialize_status(status), entry, REDIS_ASIC_STATE_COMMAND_STATS_CAPABILITY_RESPONSE); + + return status; +} + sai_status_t ServerSai::processFdbFlush( _In_ const swss::KeyOpFieldsValuesTuple &kco) { diff --git a/lib/ServerSai.h b/lib/ServerSai.h index b67c4ed1c..a0193a890 100644 --- a/lib/ServerSai.h +++ b/lib/ServerSai.h @@ -282,6 +282,9 @@ namespace sairedis sai_status_t processObjectTypeGetAvailabilityQuery( _In_ const swss::KeyOpFieldsValuesTuple &kco); + sai_status_t processStatsCapabilityQuery( + _In_ const swss::KeyOpFieldsValuesTuple &kco); + private: bool m_apiInitialized; diff --git a/lib/sai_redis_interfacequery.cpp b/lib/sai_redis_interfacequery.cpp index 0525c1105..43bbeef6f 100644 --- a/lib/sai_redis_interfacequery.cpp +++ b/lib/sai_redis_interfacequery.cpp @@ -250,7 +250,10 @@ sai_status_t sai_query_stats_capability( { SWSS_LOG_ENTER(); - return SAI_STATUS_NOT_IMPLEMENTED; + return redis_sai->queryStatsCapability( + switch_id, + object_type, + stats_capability); } sai_status_t sai_query_api_version( diff --git a/lib/sairediscommon.h b/lib/sairediscommon.h index 7a6a997d9..e712737a9 100644 --- a/lib/sairediscommon.h +++ b/lib/sairediscommon.h @@ -52,6 +52,9 @@ #define REDIS_ASIC_STATE_COMMAND_OBJECT_TYPE_GET_AVAILABILITY_QUERY "object_type_get_availability_query" #define REDIS_ASIC_STATE_COMMAND_OBJECT_TYPE_GET_AVAILABILITY_RESPONSE "object_type_get_availability_response" +#define REDIS_ASIC_STATE_COMMAND_STATS_CAPABILITY_QUERY "stats_capability_query" +#define REDIS_ASIC_STATE_COMMAND_STATS_CAPABILITY_RESPONSE "stats_capability_response" + /** * @brief Redis virtual object id counter key name. * diff --git a/meta/DummySaiInterface.cpp b/meta/DummySaiInterface.cpp index fa3ede4fe..cf3a0c264 100644 --- a/meta/DummySaiInterface.cpp +++ b/meta/DummySaiInterface.cpp @@ -279,7 +279,7 @@ sai_status_t DummySaiInterface::queryStatsCapability( { SWSS_LOG_ENTER(); - return SAI_STATUS_NOT_IMPLEMENTED; + return m_status; } sai_status_t DummySaiInterface::getStatsExt( diff --git a/meta/Meta.cpp b/meta/Meta.cpp index b844c6f7a..8a709ac91 100644 --- a/meta/Meta.cpp +++ b/meta/Meta.cpp @@ -24,6 +24,14 @@ return _status; \ } \ } +#define VALIDATION_STATS_LIST(cnt,lst) \ +{ \ + if ((cnt > MAX_LIST_COUNT) || ((cnt == 0) && (lst != NULL)) || ((cnt > 0) && (lst == NULL)))\ + { \ + SWSS_LOG_ERROR("Invalid list and list-count"); \ + return SAI_STATUS_INVALID_PARAMETER; \ + } \ +} #define VALIDATION_LIST_GET(md, list) \ { \ @@ -979,37 +987,6 @@ sai_status_t Meta::meta_validate_stats( return SAI_STATUS_SUCCESS; } -sai_status_t Meta::meta_validate_query_stats_capability( - _In_ sai_object_type_t object_type, - _In_ sai_object_id_t object_id) -{ - SWSS_LOG_ENTER(); - - PARAMETER_CHECK_OBJECT_TYPE_VALID(object_type); - PARAMETER_CHECK_OID_OBJECT_TYPE(object_id, object_type); - PARAMETER_CHECK_OID_EXISTS(object_id, object_type); - - sai_object_id_t switch_id = switchIdQuery(object_id); - - // checks also if object type is OID - sai_status_t status = meta_sai_validate_oid(object_type, &object_id, switch_id, false); - - CHECK_STATUS_SUCCESS(status); - - auto info = sai_metadata_get_object_type_info(object_type); - - PARAMETER_CHECK_IF_NOT_NULL(info); - - if (info->statenum == nullptr) - { - SWSS_LOG_ERROR("%s does not support stats", info->objecttypename); - - return SAI_STATUS_INVALID_PARAMETER; - } - - return SAI_STATUS_SUCCESS; -} - sai_status_t Meta::getStats( _In_ sai_object_type_t object_type, _In_ sai_object_id_t object_id, @@ -1037,11 +1014,24 @@ sai_status_t Meta::queryStatsCapability( { SWSS_LOG_ENTER(); - auto status = meta_validate_query_stats_capability(objectType, switchId); + PARAMETER_CHECK_OID_OBJECT_TYPE(switchId, SAI_OBJECT_TYPE_SWITCH); + PARAMETER_CHECK_OID_EXISTS(switchId, SAI_OBJECT_TYPE_SWITCH); + PARAMETER_CHECK_OBJECT_TYPE_VALID(objectType); + PARAMETER_CHECK_IF_NOT_NULL(stats_capability); + VALIDATION_STATS_LIST(stats_capability->count, stats_capability->list); + + auto info = sai_metadata_get_object_type_info(objectType); - CHECK_STATUS_SUCCESS(status); + PARAMETER_CHECK_IF_NOT_NULL(info); + + if (info->statenum == nullptr) + { + SWSS_LOG_ERROR("%s does not support stats", info->objecttypename); + + return SAI_STATUS_INVALID_PARAMETER; + } - status = m_implementation->queryStatsCapability(switchId, objectType, stats_capability); + auto status = m_implementation->queryStatsCapability(switchId, objectType, stats_capability); // no post validation required diff --git a/meta/Meta.h b/meta/Meta.h index 0eae42b51..0e8ca42d4 100644 --- a/meta/Meta.h +++ b/meta/Meta.h @@ -427,10 +427,6 @@ namespace saimeta _Out_ uint64_t *counters, _In_ sai_stats_mode_t mode); - sai_status_t meta_validate_query_stats_capability( - _In_ sai_object_type_t object_type, - _In_ sai_object_id_t object_id); - private: // validate OID sai_status_t meta_sai_validate_oid( diff --git a/meta/SaiSerialize.cpp b/meta/SaiSerialize.cpp index 7b865360d..ea0cb3a10 100644 --- a/meta/SaiSerialize.cpp +++ b/meta/SaiSerialize.cpp @@ -2573,6 +2573,66 @@ std::string sai_serialize_redis_communication_mode( } } +json sai_serialize_stat_capability( + _In_ const sai_stat_capability_t& stat_capability, + _In_ const sai_enum_metadata_t* meta) +{ + SWSS_LOG_ENTER(); + + json j; + + j["stat_enum"] = sai_serialize_enum(stat_capability.stat_enum, meta); + + json arr = json::array(); + + for (uint32_t i = 0; i < sai_metadata_enum_sai_stats_mode_t.valuescount; ++i) + { + if (stat_capability.stat_modes & sai_metadata_enum_sai_stats_mode_t.values[i]) + { + json item = sai_serialize_enum(sai_metadata_enum_sai_stats_mode_t.values[i], + &sai_metadata_enum_sai_stats_mode_t); + + arr.push_back(item); + } + } + + j["stat_modes"] = arr; + + return j; +} + +std::string sai_serialize_stat_capability_list( + _In_ const sai_stat_capability_list_t& stat_capability_list, + _In_ const sai_enum_metadata_t* meta, + _In_ bool countOnly) +{ + SWSS_LOG_ENTER(); + + json j; + + j["count"] = stat_capability_list.count; + + if (stat_capability_list.list == NULL || countOnly) + { + j["list"] = nullptr; + + return j.dump(); + } + + json arr = json::array(); + + for (uint32_t i = 0; i < stat_capability_list.count; ++i) + { + json item = sai_serialize_stat_capability(stat_capability_list.list[i], meta); + + arr.push_back(item); + } + + j["list"] = arr; + + return j.dump(); +} + // deserialize void sai_deserialize_bool( @@ -4991,3 +5051,79 @@ void sai_deserialize_redis_communication_mode( SWSS_LOG_THROW("enum '%s' not found in sai_redis_communication_mode_t", s.c_str()); } } + +/************************************************************* +* Function : sai_deserialize_stat_capability_list +* Parameters : +* stats_capability - stats capability enum list +* stat_enum_str - SAI stat enum list as string +* stat_modes_str - SAI stat mode list as string +* Description : +* 1. Iterate thru stat_enum_str and populate +* the stats_capability with respective +* stat_enum (String to Enum conversion) +* 2. Iterate thru stat_modes_str and populate +* the stats_capability with respective +* stat_modes (String to Enum conversion) +**************************************************************/ +void sai_deserialize_stat_capability_list( + _Inout_ sai_stat_capability_list_t *stats_capability, + _In_ const std::string& stat_enum_str, + _In_ const std::string& stat_modes_str) +{ + SWSS_LOG_ENTER(); + + if (stats_capability == NULL) + { + SWSS_LOG_THROW("Stats capability pointer in deserialize is NULL"); + } + + uint32_t num_capabilities = stats_capability->count; + size_t stat_enum_position = 0; + size_t stat_modes_position = 0; + + for (uint32_t i = 0; i < num_capabilities; i++) + { + /* 1. Populate stat_enum */ + size_t old_stat_enum_position = stat_enum_position; + stat_enum_position = stat_enum_str.find(",", old_stat_enum_position); + std::string stat_enum = stat_enum_str.substr(old_stat_enum_position, + stat_enum_position - old_stat_enum_position); + stats_capability->list[i].stat_enum = std::stoi(stat_enum); + + /* We have run out of values to add to our list */ + if (stat_enum_position == std::string::npos) + { + if (num_capabilities != i + 1) + { + SWSS_LOG_WARN("Lesser stat_enums than expected: expected %d, received %d", + num_capabilities, i+1); + } + + break; + } + + /* 2. Populate stat_modes */ + size_t old_stat_modes_position = stat_modes_position; + stat_modes_position = stat_modes_str.find(",", old_stat_modes_position); + std::string stat_modes = stat_modes_str.substr(old_stat_modes_position, + stat_modes_position - old_stat_modes_position); + stats_capability->list[i].stat_modes = std::stoi(stat_modes); + + /* We have run out of values to add to our list */ + if (stat_modes_position == std::string::npos) + { + if (num_capabilities != i + 1) + { + SWSS_LOG_WARN("Lesser stat_modes than expected: expected %d, received %d", + num_capabilities, i+1); + } + + break; + } + + /* Skip the commas */ + stat_enum_position++; + stat_modes_position++; + } +} diff --git a/meta/sai_serialize.h b/meta/sai_serialize.h index ea6a52404..1047622d1 100644 --- a/meta/sai_serialize.h +++ b/meta/sai_serialize.h @@ -250,6 +250,11 @@ std::string sai_serialize_nat_entry_type( std::string sai_serialize_qos_map_item( _In_ const sai_qos_map_t& qosmap); +std::string sai_serialize_stat_capability_list( + _In_ const sai_stat_capability_list_t& stat_capability_list, + _In_ const sai_enum_metadata_t* meta, + _In_ bool countOnly); + // serialize notifications std::string sai_serialize_fdb_event_ntf( @@ -526,3 +531,8 @@ sai_redis_notify_syncd_t sai_deserialize_redis_notify_syncd( void sai_deserialize_redis_communication_mode( _In_ const std::string& s, _Out_ sai_redis_communication_mode_t& value); + +void sai_deserialize_stat_capability_list( + _Inout_ sai_stat_capability_list_t *stats_capability, + _In_ const std::string& stat_enum_str, + _In_ const std::string& stat_modes_str); diff --git a/syncd/FlexCounter.cpp b/syncd/FlexCounter.cpp index 4352efded..9775d81d2 100644 --- a/syncd/FlexCounter.cpp +++ b/syncd/FlexCounter.cpp @@ -30,6 +30,8 @@ static const std::string ATTR_TYPE_QUEUE = "Queue Attribute"; static const std::string ATTR_TYPE_PG = "Priority Group Attribute"; static const std::string ATTR_TYPE_MACSEC_SA = "MACSEC SA Attribute"; static const std::string ATTR_TYPE_ACL_COUNTER = "ACL Counter Attribute"; +static const std::string COUNTER_TYPE_WRED_ECN_QUEUE = "WRED Queue Counter"; +static const std::string COUNTER_TYPE_WRED_ECN_PORT = "WRED Port Counter"; BaseCounterContext::BaseCounterContext(const std::string &name): m_name(name) @@ -1139,6 +1141,10 @@ void FlexCounter::addCounterPlugin( { getCounterContext(COUNTER_TYPE_QUEUE)->addPlugins(shaStrings); } + else if (field == WRED_QUEUE_PLUGIN_FIELD) + { + getCounterContext(COUNTER_TYPE_WRED_ECN_QUEUE)->addPlugins(shaStrings); + } else if (field == PG_PLUGIN_FIELD) { getCounterContext(COUNTER_TYPE_PG)->addPlugins(shaStrings); @@ -1147,6 +1153,10 @@ void FlexCounter::addCounterPlugin( { getCounterContext(COUNTER_TYPE_PORT)->addPlugins(shaStrings); } + else if (field == WRED_PORT_PLUGIN_FIELD) + { + getCounterContext(COUNTER_TYPE_WRED_ECN_PORT)->addPlugins(shaStrings); + } else if (field == RIF_PLUGIN_FIELD) { getCounterContext(COUNTER_TYPE_RIF)->addPlugins(shaStrings); @@ -1229,6 +1239,12 @@ std::shared_ptr FlexCounter::createCounterContext( context->always_check_supported_counters = true; return context; } + else if (context_name == COUNTER_TYPE_WRED_ECN_PORT) + { + auto context = std::make_shared>(context_name, SAI_OBJECT_TYPE_PORT, m_vendorSai.get(), m_statsMode); + context->always_check_supported_counters = true; + return context; + } else if (context_name == COUNTER_TYPE_PORT_DEBUG) { auto context = std::make_shared>(context_name, SAI_OBJECT_TYPE_PORT, m_vendorSai.get(), m_statsMode); @@ -1245,6 +1261,13 @@ std::shared_ptr FlexCounter::createCounterContext( context->double_confirm_supported_counters = true; return context; } + else if (context_name == COUNTER_TYPE_WRED_ECN_QUEUE) + { + auto context = std::make_shared>(context_name, SAI_OBJECT_TYPE_QUEUE, m_vendorSai.get(), m_statsMode); + context->always_check_supported_counters = true; + context->double_confirm_supported_counters = true; + return context; + } else if (context_name == COUNTER_TYPE_PG) { auto context = std::make_shared>(context_name, SAI_OBJECT_TYPE_INGRESS_PRIORITY_GROUP, m_vendorSai.get(), m_statsMode); @@ -1494,6 +1517,10 @@ void FlexCounter::removeCounter( { getCounterContext(COUNTER_TYPE_PORT_DEBUG)->removeObject(vid); } + if (hasCounterContext(COUNTER_TYPE_WRED_ECN_PORT)) + { + getCounterContext(COUNTER_TYPE_WRED_ECN_PORT)->removeObject(vid); + } } else if (objectType == SAI_OBJECT_TYPE_QUEUE) { @@ -1501,6 +1528,10 @@ void FlexCounter::removeCounter( { getCounterContext(COUNTER_TYPE_QUEUE)->removeObject(vid); } + if (hasCounterContext(COUNTER_TYPE_WRED_ECN_QUEUE)) + { + getCounterContext(COUNTER_TYPE_WRED_ECN_QUEUE)->removeObject(vid); + } if (hasCounterContext(ATTR_TYPE_QUEUE)) { getCounterContext(ATTR_TYPE_QUEUE)->removeObject(vid); diff --git a/syncd/Syncd.cpp b/syncd/Syncd.cpp index 0f331d334..d4211e12d 100644 --- a/syncd/Syncd.cpp +++ b/syncd/Syncd.cpp @@ -373,6 +373,9 @@ sai_status_t Syncd::processSingleEvent( if (op == REDIS_ASIC_STATE_COMMAND_OBJECT_TYPE_GET_AVAILABILITY_QUERY) return processObjectTypeGetAvailabilityQuery(kco); + if (op == REDIS_ASIC_STATE_COMMAND_STATS_CAPABILITY_QUERY) + return processStatsCapabilityQuery(kco); + SWSS_LOG_THROW("event op '%s' is not implemented, FIXME", op.c_str()); } @@ -558,6 +561,89 @@ sai_status_t Syncd::processObjectTypeGetAvailabilityQuery( return status; } +sai_status_t Syncd::processStatsCapabilityQuery( + _In_ const swss::KeyOpFieldsValuesTuple &kco) +{ + SWSS_LOG_ENTER(); + + auto& strSwitchVid = kfvKey(kco); + + sai_object_id_t switchVid; + sai_deserialize_object_id(strSwitchVid, switchVid); + + sai_object_id_t switchRid = m_translator->translateVidToRid(switchVid); + + auto& values = kfvFieldsValues(kco); + + if (values.size() != 2) + { + SWSS_LOG_ERROR("Invalid input: expected 2 arguments, received %zu", values.size()); + + m_selectableChannel->set(sai_serialize_status(SAI_STATUS_INVALID_PARAMETER), {}, REDIS_ASIC_STATE_COMMAND_STATS_CAPABILITY_RESPONSE); + + return SAI_STATUS_INVALID_PARAMETER; + } + + sai_object_type_t objectType; + sai_deserialize_object_type(fvValue(values[0]), objectType); + + uint32_t list_size = std::stoi(fvValue(values[1])); + + std::vector stat_capability_list(list_size); + + sai_stat_capability_list_t statCapList; + + statCapList.count = list_size; + statCapList.list = stat_capability_list.data(); + + sai_status_t status = m_vendorSai->queryStatsCapability(switchRid, objectType, &statCapList); + + std::vector entry; + + if (status == SAI_STATUS_SUCCESS) + { + std::vector vec_stat_enum; + std::vector vec_stat_modes; + + for (uint32_t it = 0; it < statCapList.count; it++) + { + vec_stat_enum.push_back(std::to_string(statCapList.list[it].stat_enum)); + vec_stat_modes.push_back(std::to_string(statCapList.list[it].stat_modes)); + } + + std::ostringstream join_stat_enum; + std::copy(vec_stat_enum.begin(), vec_stat_enum.end(), std::ostream_iterator(join_stat_enum, ",")); + auto strCapEnum = join_stat_enum.str(); + + std::ostringstream join_stat_modes; + std::copy(vec_stat_modes.begin(), vec_stat_modes.end(), std::ostream_iterator(join_stat_modes, ",")); + auto strCapModes = join_stat_modes.str(); + + entry = + { + swss::FieldValueTuple("STAT_ENUM", strCapEnum), + swss::FieldValueTuple("STAT_MODES", strCapModes), + swss::FieldValueTuple("STAT_COUNT", std::to_string(statCapList.count)) + }; + + SWSS_LOG_DEBUG("Sending response: stat_enums = '%s', stat_modes = '%s', count = %d", + strCapEnum.c_str(), strCapModes.c_str(), statCapList.count); + } + else if (status == SAI_STATUS_BUFFER_OVERFLOW) + { + entry = + { + swss::FieldValueTuple("STAT_COUNT", std::to_string(statCapList.count)) + }; + + SWSS_LOG_DEBUG("Sending response: count = %u", statCapList.count); + } + + m_selectableChannel->set(sai_serialize_status(status), entry, REDIS_ASIC_STATE_COMMAND_STATS_CAPABILITY_RESPONSE); + + return status; +} + sai_status_t Syncd::processFdbFlush( _In_ const swss::KeyOpFieldsValuesTuple &kco) { diff --git a/syncd/Syncd.h b/syncd/Syncd.h index b7622bf01..cab8476f0 100644 --- a/syncd/Syncd.h +++ b/syncd/Syncd.h @@ -134,6 +134,9 @@ namespace syncd sai_status_t processObjectTypeGetAvailabilityQuery( _In_ const swss::KeyOpFieldsValuesTuple &kco); + sai_status_t processStatsCapabilityQuery( + _In_ const swss::KeyOpFieldsValuesTuple &kco); + sai_status_t processFdbFlush( _In_ const swss::KeyOpFieldsValuesTuple &kco); diff --git a/tests/TestClient.cpp b/tests/TestClient.cpp index c7b24f019..61f3e164c 100644 --- a/tests/TestClient.cpp +++ b/tests/TestClient.cpp @@ -362,6 +362,22 @@ void TestClient::test_query_api() NULL, &count)); + /* Test queue stats capability get */ + sai_stat_capability_list_t queue_stats_capability; + sai_stat_capability_t stat_initializer; + stat_initializer.stat_enum = 0; + stat_initializer.stat_modes = 0; + std::vector qstat_cap_list(15, stat_initializer); + queue_stats_capability.count = 15; + queue_stats_capability.list = qstat_cap_list.data(); + + SWSS_LOG_NOTICE(" * sai_query_stats_capability"); + + ASSERT_SUCCESS(sai_query_stats_capability( + m_switch_id, + SAI_OBJECT_TYPE_QUEUE, + &queue_stats_capability)); + teardown(); } diff --git a/unittest/lib/test_sai_redis_interfacequery.cpp b/unittest/lib/test_sai_redis_interfacequery.cpp index 0fbf297e6..b4bd5c6a5 100644 --- a/unittest/lib/test_sai_redis_interfacequery.cpp +++ b/unittest/lib/test_sai_redis_interfacequery.cpp @@ -78,7 +78,7 @@ TEST(libsairedis, sai_get_object_key) TEST(libsairedis, sai_query_stats_capability) { - EXPECT_EQ(SAI_STATUS_NOT_IMPLEMENTED, sai_query_stats_capability(0,SAI_OBJECT_TYPE_NULL,0)); + EXPECT_EQ(SAI_STATUS_INVALID_PARAMETER, sai_query_stats_capability(0,SAI_OBJECT_TYPE_NULL,0)); } TEST(libsairedis, sai_bulk_object_get_stats) diff --git a/unittest/meta/TestMeta.cpp b/unittest/meta/TestMeta.cpp index 63427e6c2..e3786f942 100644 --- a/unittest/meta/TestMeta.cpp +++ b/unittest/meta/TestMeta.cpp @@ -938,6 +938,37 @@ TEST(Meta, queryAattributeEnumValuesCapability) EXPECT_EQ(SAI_STATUS_INVALID_PARAMETER, m.queryAattributeEnumValuesCapability(switchId, SAI_OBJECT_TYPE_SWITCH, 10000, &list)); } +TEST(Meta, queryStatsCapability) +{ + Meta m(std::make_shared()); + + sai_object_id_t switchId = 0; + + sai_attribute_t attr; + + attr.id = SAI_SWITCH_ATTR_INIT_SWITCH; + attr.value.booldata = true; + + EXPECT_EQ(SAI_STATUS_SUCCESS, m.create(SAI_OBJECT_TYPE_SWITCH, &switchId, SAI_NULL_OBJECT_ID, 1, &attr)); + + sai_stat_capability_list_t queue_stats_capability; + sai_stat_capability_t stat_initializer; + stat_initializer.stat_enum = 0; + stat_initializer.stat_modes = 0; + std::vector qstat_cap_list(20, stat_initializer); + queue_stats_capability.count = 15; + queue_stats_capability.list = qstat_cap_list.data(); + + + EXPECT_EQ(SAI_STATUS_SUCCESS, m.queryStatsCapability(switchId, SAI_OBJECT_TYPE_QUEUE, &queue_stats_capability)); + + // set count without list; + queue_stats_capability.list = nullptr; + + EXPECT_EQ(SAI_STATUS_INVALID_PARAMETER, m.queryStatsCapability(switchId, SAI_OBJECT_TYPE_QUEUE, &queue_stats_capability)); + +} + TEST(Meta, meta_validate_stats) { MockMeta m(std::make_shared()); diff --git a/unittest/vslib/test_sai_vs_interfacequery.cpp b/unittest/vslib/test_sai_vs_interfacequery.cpp index f10592aa9..1811c0a5d 100644 --- a/unittest/vslib/test_sai_vs_interfacequery.cpp +++ b/unittest/vslib/test_sai_vs_interfacequery.cpp @@ -80,3 +80,7 @@ TEST(libsaivs, sai_bulk_object_clear_stats) SAI_STATS_MODE_BULK_CLEAR, nullptr)); } +TEST(libsaivs, sai_query_stats_capability) +{ + EXPECT_EQ(SAI_STATUS_INVALID_PARAMETER, sai_query_stats_capability(0,SAI_OBJECT_TYPE_NULL,0)); +}