diff --git a/lib/ClientSai.cpp b/lib/ClientSai.cpp index 74e09a3a8..015972cbd 100644 --- a/lib/ClientSai.cpp +++ b/lib/ClientSai.cpp @@ -981,16 +981,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) @@ -1019,6 +1009,109 @@ 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(); + + auto switchIdStr = sai_serialize_object_id(switchId); + auto objectTypeStr = sai_serialize_object_type(objectType); + + if (stats_capability == NULL) + { + SWSS_LOG_ERROR("Failed to find stats-capability: switch %s object type %s", switchIdStr.c_str(), objectTypeStr.c_str()); + return SAI_STATUS_INVALID_PARAMETER; + } + + if (stats_capability && stats_capability->list && (stats_capability->count)) + { + // 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; + } + } + + const std::string listSize = std::to_string(stats_capability->count); + + const std::vector entry = + { + swss::FieldValueTuple("OBJECT_TYPE", objectTypeStr), + swss::FieldValueTuple("LIST_SIZE", listSize) + }; + + SWSS_LOG_DEBUG( + "Query arguments: switch %s, object type: %s, count: %s", + switchIdStr.c_str(), + objectTypeStr.c_str(), + listSize.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); + + return waitForQueryStatsCapabilityResponse(stats_capability); +} + +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_stats_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 cbe6a3204..91f3771b3 100644 --- a/lib/ClientSai.h +++ b/lib/ClientSai.h @@ -297,6 +297,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 e3a9f808e..d0bc75776 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 1ba3828d9..ffd876a5b 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) { @@ -1104,6 +1122,67 @@ void Recorder::recordQueryAttributeEnumValuesCapabilityResponse( 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_stats_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 f30c22b9d..5e90791cd 100644 --- a/lib/Recorder.h +++ b/lib/Recorder.h @@ -298,6 +298,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, @@ -323,6 +333,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 4c0247109..62f2f2b17 100644 --- a/lib/RedisRemoteSaiInterface.cpp +++ b/lib/RedisRemoteSaiInterface.cpp @@ -1356,16 +1356,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) @@ -1394,6 +1384,113 @@ 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(); + + auto switchIdStr = sai_serialize_object_id(switchId); + auto objectTypeStr = sai_serialize_object_type(objectType); + + if (stats_capability == NULL) + { + SWSS_LOG_ERROR("Failed to find stats-capability: switch %s, object type %s", switchIdStr.c_str(), objectTypeStr.c_str()); + return SAI_STATUS_INVALID_PARAMETER; + } + + if (stats_capability && stats_capability->list && (stats_capability->count)) + { + // clear input list, since we use serialize to transfer the 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; + } + } + + const std::string listSize = std::to_string(stats_capability->count); + + const std::vector entry = + { + swss::FieldValueTuple("OBJECT_TYPE", objectTypeStr), + swss::FieldValueTuple("LIST_SIZE", listSize) + }; + + SWSS_LOG_DEBUG( + "Query arguments: switch %s, object type: %s, count: %s", + switchIdStr.c_str(), + objectTypeStr.c_str(), + listSize.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_stats_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 a97d34b62..921b81b55 100644 --- a/lib/RedisRemoteSaiInterface.h +++ b/lib/RedisRemoteSaiInterface.h @@ -340,6 +340,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 433a421cf..d02028214 100644 --- a/lib/Sai.cpp +++ b/lib/Sai.cpp @@ -362,9 +362,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 6a8f5ad92..faa8de2e9 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( @@ -777,6 +782,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()); } @@ -2075,6 +2083,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 e0a17eab3..af20d3961 100644 --- a/lib/ServerSai.h +++ b/lib/ServerSai.h @@ -294,6 +294,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/sairediscommon.h b/lib/sairediscommon.h index d09ddc0ab..f9bc6cde4 100644 --- a/lib/sairediscommon.h +++ b/lib/sairediscommon.h @@ -57,6 +57,10 @@ #define REDIS_FLEX_COUNTER_COMMAND_SET_GROUP "set_counter_group" #define REDIS_FLEX_COUNTER_COMMAND_DEL_GROUP "del_counter_group" #define REDIS_FLEX_COUNTER_COMMAND_RESPONSE "counter_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/lib/tests.cpp b/lib/tests.cpp index eb3d9ad70..b6e1912fb 100644 --- a/lib/tests.cpp +++ b/lib/tests.cpp @@ -977,6 +977,101 @@ void test_tokenize_bulk_route_entry() std::cout << "s: " << (double)us.count()/1000000.0 << " for total routes: " <<( n * per) << std::endl; } +static void test_recorder_stats_capability_query_request( + _In_ sai_object_id_t switch_id, + _In_ sai_object_type_t object_type, + _In_ const std::vector& expectedOutput) +{ + SWSS_LOG_ENTER(); + + remove(SairedisRecFilename.c_str()); + + Recorder recorder; + + recorder.enableRecording(true); + + sai_stat_capability_list_t stats_capability; + sai_stat_capability_t cap_list; + + cap_list.stat_enum = SAI_QUEUE_STAT_WRED_ECN_MARKED_BYTES; + cap_list.stat_modes = SAI_STATS_MODE_READ; + + stats_capability.count = 1; + stats_capability.list = &cap_list; + + recorder.recordQueryStatsCapability(1, SAI_OBJECT_TYPE_QUEUE, &stats_capability); + + auto tokens = parseFirstRecordedAPI(); + + ASSERT_EQ(tokens, expectedOutput); +} + +static void test_recorder_stats_capability_query_response( + _In_ sai_object_id_t switch_id, + _In_ sai_object_type_t object_type, + _In_ const std::vector& expectedOutput) +{ + SWSS_LOG_ENTER(); + + remove(SairedisRecFilename.c_str()); + + Recorder recorder; + + recorder.enableRecording(true); + + sai_stat_capability_list_t stats_capability; + sai_stat_capability_t cap_list; + + cap_list.stat_enum = SAI_QUEUE_STAT_WRED_ECN_MARKED_BYTES; + cap_list.stat_modes = SAI_STATS_MODE_READ; + + stats_capability.count = 1; + stats_capability.list = &cap_list; + + recorder.recordQueryStatsCapabilityResponse(SAI_STATUS_SUCCESS , SAI_OBJECT_TYPE_QUEUE, &stats_capability); + + auto tokens = parseFirstRecordedAPI(); + + recorder.recordQueryStatsCapabilityResponse(SAI_STATUS_SUCCESS , SAI_OBJECT_TYPE_MAX, &stats_capability); + recorder.recordQueryStatsCapabilityResponse(SAI_STATUS_SUCCESS , SAI_OBJECT_TYPE_BRIDGE_PORT , &stats_capability); + + ASSERT_EQ(tokens, expectedOutput); +} + +static void test_recorder_stats_capability_query() +{ + SWSS_LOG_ENTER(); + + Recorder recorder; + remove(SairedisRecFilename.c_str()); + recorder.enableRecording(true); + + const std::vector expectedOutputQuery = + { + "q", + "stats_capability", + "SAI_OBJECT_TYPE_SWITCH:oid:0x1", + "OBJECT_TYPE=SAI_OBJECT_TYPE_QUEUE", + "LIST_SIZE=1", + }; + const std::vector expectedOutputResponse = + { + "Q", + "stats_capability", + "SAI_STATUS_SUCCESS", + "{\"count\":1,\"list\":[{\"stat_enum\":\"SAI_QUEUE_STAT_WRED_ECN_MARKED_BYTES\",\"stat_modes\":[\"SAI_STATS_MODE_READ\"]}]}", + }; + + test_recorder_stats_capability_query_request(1, + SAI_OBJECT_TYPE_QUEUE, + expectedOutputQuery + ); + test_recorder_stats_capability_query_response(1, + SAI_OBJECT_TYPE_QUEUE, + expectedOutputResponse + ); +} + int main() { SWSS_LOG_ENTER(); @@ -1020,6 +1115,7 @@ int main() std::cout << " * test recorder" << std::endl; test_recorder_enum_value_capability_query(); + test_recorder_stats_capability_query(); return 0; } diff --git a/meta/Meta.cpp b/meta/Meta.cpp index a9851d201..a19483e03 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) \ { \ @@ -1003,37 +1011,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, @@ -1061,11 +1038,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 ce8a0a2dd..c2d718c5a 100644 --- a/meta/Meta.h +++ b/meta/Meta.h @@ -459,10 +459,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 0916cac02..c25ac2cb8 100644 --- a/meta/SaiSerialize.cpp +++ b/meta/SaiSerialize.cpp @@ -2852,6 +2852,66 @@ std::string sai_serialize_redis_link_event_damping_aied_config( return j.dump(); } +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_stats_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(); +} + std::string sai_serialize_poe_port_active_channel_type( _In_ const sai_poe_port_active_channel_type_t value) { @@ -5661,3 +5721,80 @@ void sai_deserialize_redis_link_event_damping_aied_config( sai_deserialize_number(j["decay_half_life"], value.decay_half_life, false); sai_deserialize_number(j["flap_penalty"], value.flap_penalty, false); } + +/** +* @brief deserialize the stats capability list +* +* Iterates thru stat_enum_str and populate +* the stats_capability with respective +* stat_enum (String to Enum conversion). +* Also iterates thru stat_modes_str and populate +* the stats_capability with respective +* stat_modes (String to Enum conversion) +* +* @param stats_capability stats capability enum list +* @param stat_enum_str SAI stat enum list as string +* @param stat_modes_str SAI stat mode list as string +* @return Void +*/ +void sai_deserialize_stats_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_THROW("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_THROW("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 0ef79e9cb..7eb521e47 100644 --- a/meta/sai_serialize.h +++ b/meta/sai_serialize.h @@ -291,6 +291,11 @@ std::string sai_serialize_twamp_session_stat( std::string sai_serialize_poe_port_power_consumption( _In_ const sai_poe_port_power_consumption_t& pppc); +std::string sai_serialize_stats_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( @@ -645,3 +650,8 @@ void sai_deserialize_redis_link_event_damping_algorithm( void sai_deserialize_redis_link_event_damping_aied_config( _In_ const std::string& s, _Out_ sai_redis_link_event_damping_algo_aied_config_t& value); + +void sai_deserialize_stats_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 f907f4f07..59a20bf99 100644 --- a/syncd/FlexCounter.cpp +++ b/syncd/FlexCounter.cpp @@ -32,6 +32,9 @@ 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"; + const std::map FlexCounter::m_plugIn2CounterType = { {QUEUE_PLUGIN_FIELD, COUNTER_TYPE_QUEUE}, {PG_PLUGIN_FIELD, COUNTER_TYPE_PG}, @@ -39,7 +42,9 @@ const std::map FlexCounter::m_plugIn2CounterType = { {RIF_PLUGIN_FIELD, COUNTER_TYPE_RIF}, {BUFFER_POOL_PLUGIN_FIELD, COUNTER_TYPE_BUFFER_POOL}, {TUNNEL_PLUGIN_FIELD, COUNTER_TYPE_TUNNEL}, - {FLOW_COUNTER_PLUGIN_FIELD, COUNTER_TYPE_FLOW}}; + {FLOW_COUNTER_PLUGIN_FIELD, COUNTER_TYPE_FLOW}, + {WRED_QUEUE_PLUGIN_FIELD, COUNTER_TYPE_WRED_ECN_QUEUE}, + {WRED_PORT_PLUGIN_FIELD, COUNTER_TYPE_WRED_ECN_PORT}}; BaseCounterContext::BaseCounterContext(const std::string &name): m_name(name) @@ -1618,6 +1623,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); @@ -1634,6 +1645,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); @@ -1893,6 +1911,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) { @@ -1900,6 +1922,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 e7efff34e..81f1e9b1b 100644 --- a/syncd/Syncd.cpp +++ b/syncd/Syncd.cpp @@ -410,6 +410,9 @@ sai_status_t Syncd::processSingleEvent( if (op == REDIS_FLEX_COUNTER_COMMAND_DEL_GROUP) return processFlexCounterGroupEvent(key, DEL_COMMAND, kfvFieldsValues(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()); } @@ -595,6 +598,86 @@ 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 7cbd0575c..a6a9c7c2d 100644 --- a/syncd/Syncd.h +++ b/syncd/Syncd.h @@ -136,6 +136,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/syncd/tests.cpp b/syncd/tests.cpp index 801a21334..e08e37016 100644 --- a/syncd/tests.cpp +++ b/syncd/tests.cpp @@ -20,6 +20,7 @@ extern "C" { #include "meta/OidRefCounter.h" #include "meta/SaiAttrWrapper.h" #include "meta/SaiObjectCollection.h" +#include "meta/RedisSelectableChannel.h" #include "swss/logger.h" #include "swss/dbconnector.h" @@ -63,6 +64,7 @@ namespace swss { } static std::shared_ptr g_db1; +static std::shared_ptr g_syncd_obj; static sai_next_hop_group_api_t test_next_hop_group_api; static std::vector>> created_next_hop_group_member; @@ -829,6 +831,8 @@ void syncdThread() auto syncd = std::make_shared(vendorSai, commandLineOptions, isWarmStart); + g_syncd_obj = syncd; + SWSS_LOG_WARN("starting run"); syncd->run(); } @@ -889,6 +893,39 @@ void test_watchdog_timer_clock_rollback() twd.setEndTime(); } +void test_query_stats_capability_query() +{ + SWSS_LOG_ENTER(); + + MetadataLogger::initialize(); + + sai_object_id_t switch_id = 0x21000000000000; + + auto switchIdStr = sai_serialize_object_id(switch_id); + + auto objectTypeStr = sai_serialize_object_type(SAI_OBJECT_TYPE_QUEUE); + const std::string list_size = std::to_string(1); + const std::string op = "stats_capability_query"; + + const std::vector entry = + { + swss::FieldValueTuple("OBJECT_TYPE", objectTypeStr), + swss::FieldValueTuple("LIST_SIZE", list_size) + }; + + auto consumer = sairedis::RedisSelectableChannel( + g_db1, + ASIC_STATE_TABLE, + REDIS_TABLE_GETRESPONSE, + TEMP_PREFIX, + false); + + consumer.set(switchIdStr, entry, op); + + sleep(1); + g_syncd_obj->processEvent(consumer); +} + int main() { swss::Logger::getInstance().setMinPrio(swss::Logger::SWSS_DEBUG); @@ -919,7 +956,10 @@ int main() test_invoke_dump(); + test_query_stats_capability_query(); + printf("\n[ %s ]\n\n", sai_serialize_status(SAI_STATUS_SUCCESS).c_str()); + } catch (const std::exception &e) { diff --git a/tests/TestClient.cpp b/tests/TestClient.cpp index c7b24f019..9361d0464 100644 --- a/tests/TestClient.cpp +++ b/tests/TestClient.cpp @@ -362,6 +362,33 @@ void TestClient::test_query_api() NULL, &count)); + /* Test queue stats capability get */ + sai_stat_capability_list_t queue_stats_capability; + + queue_stats_capability.count = 1; + queue_stats_capability.list = nullptr; + + SWSS_LOG_NOTICE(" * sai_query_stats_capability"); + + auto rc = sai_query_stats_capability( + m_switch_id, + SAI_OBJECT_TYPE_QUEUE, + &queue_stats_capability); + ASSERT_TRUE(rc == SAI_STATUS_BUFFER_OVERFLOW); + + sai_stat_capability_t stat_initializer; + stat_initializer.stat_enum = 0; + stat_initializer.stat_modes = 0; + std::vector qstat_cap_list(queue_stats_capability.count, stat_initializer); + 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/Makefile.am b/unittest/lib/Makefile.am index 62e3a187f..bfa208c33 100644 --- a/unittest/lib/Makefile.am +++ b/unittest/lib/Makefile.am @@ -26,7 +26,8 @@ tests_SOURCES = \ TestClientSai.cpp \ TestRedisRemoteSaiInterface.cpp \ TestServerSai.cpp \ - TestSai.cpp + TestSai.cpp \ + MockSaiInterface.cpp tests_CXXFLAGS = $(DBGFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS_COMMON) tests_LDADD = $(LDADD_GTEST) $(top_srcdir)/lib/libSaiRedis.a -lhiredis -lswsscommon -lpthread -L$(top_srcdir)/meta/.libs -lsaimetadata -lsaimeta -lzmq $(CODE_COVERAGE_LIBS) diff --git a/unittest/lib/MockSaiInterface.h b/unittest/lib/MockSaiInterface.h new file mode 100644 index 000000000..a6570839a --- /dev/null +++ b/unittest/lib/MockSaiInterface.h @@ -0,0 +1,27 @@ +#ifndef MOCK_SAI_INTERFACE_H +#define MOCK_SAI_INTERFACE_H + +#include "DummySaiInterface.h" +#include + +class MockSaiInterface : public SaiInterface +{ + public: + + MockSaiInterface(); + + virtual ~MockSaiInterface(); + + public: + + virtual sai_status_t apiInitialize( + _In_ uint64_t flags, + _In_ const sai_service_method_table_t *service_method_table) override; + virtual sai_status_t apiUninitialize(void) override; +public: + MOCK_METHOD(sai_status_t, queryStatsCapability, + (sai_object_id_t switchOid, sai_object_type_t objectType, sai_stat_capability_list_t* statCapList), + (override)); +}; + +#endif diff --git a/unittest/lib/TestClientServerSai.cpp b/unittest/lib/TestClientServerSai.cpp index 1f6c06a50..e442dc22d 100644 --- a/unittest/lib/TestClientServerSai.cpp +++ b/unittest/lib/TestClientServerSai.cpp @@ -94,6 +94,25 @@ TEST(ClientServerSai, switchIdQuery) EXPECT_EQ(SAI_NULL_OBJECT_ID, css->switchIdQuery(0x1111111111111111L)); } +TEST(ClientServerSai, queryStatsCapability) +{ + auto css = std::make_shared(); + + 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(2, stat_initializer); + queue_stats_capability.count = 2; + queue_stats_capability.list = qstat_cap_list.data(); + queue_stats_capability.list[0].stat_enum = SAI_QUEUE_STAT_WRED_ECN_MARKED_PACKETS; + queue_stats_capability.list[0].stat_modes = SAI_STATS_MODE_READ; + queue_stats_capability.list[1].stat_enum = SAI_QUEUE_STAT_PACKETS; + queue_stats_capability.list[1].stat_modes = SAI_STATS_MODE_READ; + + EXPECT_EQ(SAI_STATUS_FAILURE, css->queryStatsCapability(SAI_NULL_OBJECT_ID, SAI_OBJECT_TYPE_QUEUE, &queue_stats_capability)); +} + TEST(ClientServerSai, logSet) { auto css = std::make_shared(); diff --git a/unittest/lib/TestRedisRemoteSaiInterface.cpp b/unittest/lib/TestRedisRemoteSaiInterface.cpp index a638228a0..1cfd2e1ef 100644 --- a/unittest/lib/TestRedisRemoteSaiInterface.cpp +++ b/unittest/lib/TestRedisRemoteSaiInterface.cpp @@ -28,3 +28,16 @@ TEST(RedisRemoteSaiInterface, bulkGet) statuses)); } +TEST(RedisRemoteSaiInterface, queryStatsCapabilityNegative) +{ + auto ctx = ContextConfigContainer::loadFromFile("foo"); + auto rec = std::make_shared(); + + RedisRemoteSaiInterface sai(ctx->get(0), nullptr, rec); + + EXPECT_EQ(SAI_STATUS_INVALID_PARAMETER, + sai.queryStatsCapability(0, + SAI_OBJECT_TYPE_NULL, + 0)); +} + diff --git a/unittest/lib/TestServerSai.cpp b/unittest/lib/TestServerSai.cpp index 91185c025..177fee9de 100644 --- a/unittest/lib/TestServerSai.cpp +++ b/unittest/lib/TestServerSai.cpp @@ -1,9 +1,28 @@ #include "ServerSai.h" -#include +#include "sai_serialize.h" +#include "RequestShutdown.h" +#include "vslib/ContextConfigContainer.h" +#include "vslib/VirtualSwitchSaiInterface.h" +#include "vslib/Sai.h" +#include "lib/Sai.h" + +#include "swss/dbconnector.h" + +#include "sairediscommon.h" +#include "MockSaiInterface.h" +#include "CommandLineOptions.h" +#include "sairediscommon.h" +#include "SelectableChannel.h" +#include "swss/dbconnector.h" +#include "swss/redisreply.h" + +#include +#include #include + using namespace sairedis; static const char* profile_get_value( @@ -50,3 +69,126 @@ TEST(ServerSai, bulkGet) statuses)); } +using namespace ::testing; + +#ifdef MOCK_METHOD +class MockSelectableChannel : public sairedis::SelectableChannel { +public: + MOCK_METHOD(bool, empty, (), (override)); + MOCK_METHOD(void, pop, (swss::KeyOpFieldsValuesTuple& kco, bool initViewMode), (override)); + MOCK_METHOD(void, set, (const std::string& key, const std::vector& values, const std::string& op), (override)); + MOCK_METHOD(int, getFd, (), (override)); + MOCK_METHOD(uint64_t, readData, (), (override)); +}; + +void clearDB() + +class ServerSaiTest : public ::testing::Test +{ +protected: + ServerSai serverSai; + MockSaiInterface* mockSai; + MockSelectableChannel* mockSelectableChannel; + + void SetUp() override + { + mockSai = new MockSaiInterface(); + mockSelectableChannel = new MockSelectableChannel(); + + serverSai.m_sai = mockSai; + serverSai.m_selectableChannel = mockSelectableChannel; + } + + void TearDown() override + { + delete mockSai; + delete mockSelectableChannel; + } +}; + +TEST_F(ServerSaiTest, ProcessStatsCapabilityQuery_InvalidInput) +{ + ServerSai serverSai; + + serverSai.apiInitialize(0,&test_services); + + swss::KeyOpFieldsValuesTuple kco("oid:0x21000000000000", "SET", { + {"SAI_OBJECT_TYPE_PORT", "1"} + }); + + EXPECT_CALL(*mockSelectableChannel, set( + StrEq("SAI_STATUS_INVALID_PARAMETER"), + IsEmpty(), + StrEq(REDIS_ASIC_STATE_COMMAND_STATS_CAPABILITY_RESPONSE) + )).Times(1); + + sai_status_t status = serverSai.processStatsCapabilityQuery(kco); + + EXPECT_EQ(status, SAI_STATUS_INVALID_PARAMETER); +} + +TEST_F(ServerSaiTest, ProcessStatsCapabilityQuery_ValidInputSuccess) +{ + ServerSai serverSai; + + serverSai.apiInitialize(0,&test_services); + + swss::KeyOpFieldsValuesTuple kco("oid:0x21000000000000", "SET", { + {"SAI_OBJECT_TYPE_PORT", "1"}, + {"5"} + }); + + sai_stat_capability_t statList[5] = { + {1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10} + }; + + sai_stat_capability_list_t statCapList; + statCapList.count = 5; + statCapList.list = statList; + + EXPECT_CALL(*mockSai, queryStatsCapability(_, _, _)) + .WillOnce(DoAll(SetArgPointee<2>(statCapList), Return(SAI_STATUS_SUCCESS))); + + EXPECT_CALL(*mockSelectableChannel, set( + StrEq("SAI_STATUS_SUCCESS"), + AllOf( + Contains(FieldValueTuple("STAT_ENUM", "1,3,5,7,9")), + Contains(FieldValueTuple("STAT_MODES", "2,4,6,8,10")), + Contains(FieldValueTuple("STAT_COUNT", "5")) + ), + StrEq(REDIS_ASIC_STATE_COMMAND_STATS_CAPABILITY_RESPONSE) + )).Times(1); + + sai_status_t status = serverSai.processStatsCapabilityQuery(kco); + + EXPECT_EQ(status, SAI_STATUS_SUCCESS); +} + +TEST_F(ServerSaiTest, ProcessStatsCapabilityQuery_BufferOverflow) +{ + ServerSai serverSai; + + serverSai.apiInitialize(0,&test_services); + + swss::KeyOpFieldsValuesTuple kco("oid:0x21000000000000", "SET", { + {"SAI_OBJECT_TYPE_PORT", "1"}, + {"0"} + }); + + sai_stat_capability_list_t statCapList; + statCapList.count = 31; + + EXPECT_CALL(*mockSai, queryStatsCapability(_, _, _)) + .WillOnce(DoAll(SetArgPointee<2>(statCapList), Return(SAI_STATUS_BUFFER_OVERFLOW))); + + EXPECT_CALL(*mockSelectableChannel, set( + StrEq("SAI_STATUS_BUFFER_OVERFLOW"), + Contains(FieldValueTuple("STAT_COUNT", "31")), + StrEq(REDIS_ASIC_STATE_COMMAND_STATS_CAPABILITY_RESPONSE) + )).Times(1); + + sai_status_t status = serverSai.processStatsCapabilityQuery(kco); + + EXPECT_EQ(status, SAI_STATUS_BUFFER_OVERFLOW); +} +#endif diff --git a/unittest/meta/TestMeta.cpp b/unittest/meta/TestMeta.cpp index 0b0d35b25..db9e650d8 100644 --- a/unittest/meta/TestMeta.cpp +++ b/unittest/meta/TestMeta.cpp @@ -938,6 +938,36 @@ TEST(Meta, queryAttributeEnumValuesCapability) EXPECT_EQ(SAI_STATUS_INVALID_PARAMETER, m.queryAttributeEnumValuesCapability(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)); + + 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/meta/TestSaiSerialize.cpp b/unittest/meta/TestSaiSerialize.cpp index b7e0c97d6..b88a004f8 100644 --- a/unittest/meta/TestSaiSerialize.cpp +++ b/unittest/meta/TestSaiSerialize.cpp @@ -1347,3 +1347,71 @@ TEST(SaiSerialize, serialize_number) EXPECT_EQ(sn, -0x12345678); EXPECT_EQ(u, 0x12345678); } +TEST(SaiSerialize, serialize_stat_capability_list) +{ + SWSS_LOG_ENTER(); + + extern const sai_enum_metadata_t sai_metadata_enum_sai_stats_mode_t; + 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(2, stat_initializer); + queue_stats_capability.count = 2; + queue_stats_capability.list = qstat_cap_list.data(); + queue_stats_capability.list[0].stat_enum = SAI_QUEUE_STAT_WRED_ECN_MARKED_PACKETS; + queue_stats_capability.list[0].stat_modes = SAI_STATS_MODE_READ; + queue_stats_capability.list[1].stat_enum = SAI_QUEUE_STAT_PACKETS; + queue_stats_capability.list[1].stat_modes = SAI_STATS_MODE_READ; + + std::string capab_count = sai_serialize_stats_capability_list(queue_stats_capability, &sai_metadata_enum_sai_stats_mode_t, true); + std::string capab_str = sai_serialize_stats_capability_list(queue_stats_capability, &sai_metadata_enum_sai_stats_mode_t, false); + + std::string exp_count_str = "{\"count\":2,\"list\":null}"; + EXPECT_EQ(capab_count, exp_count_str); + + std::string exp_capab_str = "{\"count\":2,\"list\":[{\"stat_enum\":\"34\",\"stat_modes\":[\"SAI_STATS_MODE_READ\"]},{\"stat_enum\":\"0\",\"stat_modes\":[\"SAI_STATS_MODE_READ\"]}]}"; + EXPECT_EQ(capab_str, exp_capab_str); + + std::vector vec_stat_enum; + std::vector vec_stat_modes; + + for (uint32_t it = 0; it < queue_stats_capability.count; it++) + { + vec_stat_enum.push_back(std::to_string(queue_stats_capability.list[it].stat_enum)); + vec_stat_modes.push_back(std::to_string(queue_stats_capability.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(); + + sai_stat_capability_list_t stats_capability; + std::vector stat_cap_list(queue_stats_capability.count, stat_initializer); + stats_capability.count = queue_stats_capability.count; + stats_capability.list = stat_cap_list.data(); + + // deserialize + EXPECT_THROW(sai_deserialize_stats_capability_list(NULL, strCapEnum, strCapModes), std::runtime_error); + + sai_deserialize_stats_capability_list(&stats_capability, strCapEnum, strCapModes); + + EXPECT_EQ(stats_capability.count, queue_stats_capability.count); + EXPECT_EQ(stats_capability.list[0].stat_modes, SAI_STATS_MODE_READ); + EXPECT_EQ(stats_capability.list[1].stat_modes, SAI_STATS_MODE_READ); + int is_expected_enum = false; + + if ((stats_capability.list[0].stat_enum == SAI_QUEUE_STAT_WRED_ECN_MARKED_PACKETS)||(stats_capability.list[1].stat_enum == SAI_QUEUE_STAT_PACKETS)) + { + is_expected_enum = true; + } + if ((stats_capability.list[1].stat_enum == SAI_QUEUE_STAT_WRED_ECN_MARKED_PACKETS)||(stats_capability.list[0].stat_enum == SAI_QUEUE_STAT_PACKETS)) + { + is_expected_enum = true; + } + EXPECT_EQ(is_expected_enum, true); +} diff --git a/unittest/syncd/MockableSaiInterface.cpp b/unittest/syncd/MockableSaiInterface.cpp index df2fdc302..7dfca382f 100644 --- a/unittest/syncd/MockableSaiInterface.cpp +++ b/unittest/syncd/MockableSaiInterface.cpp @@ -180,7 +180,7 @@ sai_status_t MockableSaiInterface::queryStatsCapability( return mock_queryStatsCapability(switch_id, object_type, stats_capability); } - return SAI_STATUS_NOT_SUPPORTED; + return SAI_STATUS_SUCCESS; } sai_status_t MockableSaiInterface::getStatsExt( diff --git a/unittest/syncd/TestFlexCounter.cpp b/unittest/syncd/TestFlexCounter.cpp index e5d374e17..4e03dea7c 100644 --- a/unittest/syncd/TestFlexCounter.cpp +++ b/unittest/syncd/TestFlexCounter.cpp @@ -476,7 +476,9 @@ TEST(FlexCounter, addRemoveCounterPlugin) RIF_PLUGIN_FIELD, BUFFER_POOL_PLUGIN_FIELD, TUNNEL_PLUGIN_FIELD, - FLOW_COUNTER_PLUGIN_FIELD}; + FLOW_COUNTER_PLUGIN_FIELD, + WRED_QUEUE_PLUGIN_FIELD, + WRED_PORT_PLUGIN_FIELD}; for (auto &field : fields) { testAddRemovePlugin(field); @@ -564,7 +566,7 @@ TEST(FlexCounter, addRemoveCounterForPort) fc.addCounter(counterVid, counterRid, values); EXPECT_EQ(fc.isEmpty(), false); - usleep(1000*1000); + usleep(1000*2000); countersTable.hget(expectedKey, "SAI_PORT_STAT_IF_IN_OCTETS", value); EXPECT_EQ(value, "100"); countersTable.hget(expectedKey, "SAI_PORT_STAT_IF_IN_ERRORS", value); diff --git a/unittest/syncd/TestSyncd.cpp b/unittest/syncd/TestSyncd.cpp index 9816aa912..b838368d8 100644 --- a/unittest/syncd/TestSyncd.cpp +++ b/unittest/syncd/TestSyncd.cpp @@ -1,4 +1,5 @@ #include "Syncd.h" +#include "sai_serialize.h" #include "RequestShutdown.h" #include "vslib/ContextConfigContainer.h" #include "vslib/VirtualSwitchSaiInterface.h" @@ -259,4 +260,40 @@ TEST_F(SyncdTest, processNotifySyncd) })); syncd_object.processEvent(consumer); } +TEST_F(SyncdTest, processStatsCapabilityQuery) +{ + auto sai = std::make_shared(); + auto opt = std::make_shared(); + opt->m_enableTempView = true; + opt->m_startType = SAI_START_TYPE_FASTFAST_BOOT; + syncd::Syncd syncd_object(sai, opt, false); + + auto translator = syncd_object.m_translator; + translator->insertRidAndVid(0x11000000000000, 0x21000000000000); + + MockSelectableChannel consumer; + EXPECT_CALL(consumer, empty()).WillOnce(testing::Return(true)); + EXPECT_CALL(consumer, pop(testing::_, testing::_)) + .Times(1) + .WillRepeatedly(testing::Invoke([](swss::KeyOpFieldsValuesTuple& kco, bool initViewMode) { + static int callCount = 0; + if (callCount == 0) + { + kfvKey(kco) = "oid:0x21000000000000"; + kfvOp(kco) = REDIS_ASIC_STATE_COMMAND_STATS_CAPABILITY_QUERY; + kfvFieldsValues(kco) = { + std::make_pair("OBJECT_TYPE", "SAI_OBJECT_TYPE_QUEUE"), + std::make_pair("LIST_SIZE", "1") + }; + } + else + { + kfvKey(kco) = ""; + kfvOp(kco) = ""; + kfvFieldsValues(kco).clear(); + } + ++callCount; + })); + syncd_object.processEvent(consumer); +} #endif diff --git a/unittest/vslib/TestSwitchMLNX2700.cpp b/unittest/vslib/TestSwitchMLNX2700.cpp index f885e1d41..62cf8aab3 100644 --- a/unittest/vslib/TestSwitchMLNX2700.cpp +++ b/unittest/vslib/TestSwitchMLNX2700.cpp @@ -577,3 +577,56 @@ TEST(SwitchMLNX2700, test_port_autoneg_fec_override_support) EXPECT_EQ(attr_capability.set_implemented, true); EXPECT_EQ(attr_capability.get_implemented, true); } + +TEST(SwitchMLNX2700, test_stats_query_capability) +{ + auto sc = std::make_shared(0, ""); + auto signal = std::make_shared(); + auto eventQueue = std::make_shared(signal); + + sc->m_saiSwitchType = SAI_SWITCH_TYPE_NPU; + sc->m_switchType = SAI_VS_SWITCH_TYPE_MLNX2700; + sc->m_bootType = SAI_VS_BOOT_TYPE_COLD; + sc->m_useTapDevice = false; + sc->m_laneMap = LaneMap::getDefaultLaneMap(0); + sc->m_eventQueue = eventQueue; + + auto scc = std::make_shared(); + + scc->insert(sc); + + SwitchMLNX2700 sw( + 0x2100000000, + std::make_shared(0, scc), + sc); + + sai_stat_capability_t capability_list[51]; + sai_stat_capability_list_t stats_capability; + stats_capability.count = 1; + stats_capability.list = capability_list; + /* Get queue stats capability */ + EXPECT_EQ(sw.queryStatsCapability(0x2100000000, + SAI_OBJECT_TYPE_QUEUE, + &stats_capability), + SAI_STATUS_BUFFER_OVERFLOW); + + stats_capability.count = SAI_QUEUE_STAT_DELAY_WATERMARK_NS; + + EXPECT_EQ(sw.queryStatsCapability(0x2100000000, + SAI_OBJECT_TYPE_QUEUE, + &stats_capability), + SAI_STATUS_SUCCESS); + + /* Get port stats capability */ + stats_capability.count = 1; + EXPECT_EQ(sw.queryStatsCapability(0x2100000000, + SAI_OBJECT_TYPE_PORT, + &stats_capability), + SAI_STATUS_BUFFER_OVERFLOW); + stats_capability.count = 51; + + EXPECT_EQ(sw.queryStatsCapability(0x2100000000, + SAI_OBJECT_TYPE_PORT, + &stats_capability), + SAI_STATUS_SUCCESS); +} diff --git a/unittest/vslib/TestVirtualSwitchSaiInterface.cpp b/unittest/vslib/TestVirtualSwitchSaiInterface.cpp index 445bc56b6..ecb08b232 100644 --- a/unittest/vslib/TestVirtualSwitchSaiInterface.cpp +++ b/unittest/vslib/TestVirtualSwitchSaiInterface.cpp @@ -140,3 +140,41 @@ TEST_F(VirtualSwitchSaiInterfaceTest, bulkGet) statuses)); } +TEST_F(VirtualSwitchSaiInterfaceTest, queryStatsCapability) +{ + sai_stat_capability_t capability_list[51]; + sai_stat_capability_list_t stats_capability; + stats_capability.list = capability_list; + + /* Queue stats capability get */ + stats_capability.count = 1; + + EXPECT_EQ(SAI_STATUS_BUFFER_OVERFLOW, + m_vssai->queryStatsCapability( + m_swid, + SAI_OBJECT_TYPE_QUEUE, + &stats_capability)); + + EXPECT_EQ(SAI_STATUS_SUCCESS, + m_vssai->queryStatsCapability( + m_swid, + SAI_OBJECT_TYPE_QUEUE, + &stats_capability)); + + /* Port stats capability get */ + stats_capability.count = 1; + + EXPECT_EQ(SAI_STATUS_BUFFER_OVERFLOW, + m_vssai->queryStatsCapability( + m_swid, + SAI_OBJECT_TYPE_PORT, + &stats_capability)); + + stats_capability.count = 51; + EXPECT_EQ(SAI_STATUS_SUCCESS, + m_vssai->queryStatsCapability( + m_swid, + SAI_OBJECT_TYPE_PORT, + &stats_capability)); +} + diff --git a/vslib/SwitchStateBase.cpp b/vslib/SwitchStateBase.cpp index 8fa4d7761..7aeacf04f 100644 --- a/vslib/SwitchStateBase.cpp +++ b/vslib/SwitchStateBase.cpp @@ -3844,7 +3844,6 @@ sai_status_t SwitchStateBase::queryAttrEnumValuesCapability( return SAI_STATUS_NOT_SUPPORTED; } - sai_status_t SwitchStateBase::queryAttributeCapability( _In_ sai_object_id_t switch_id, _In_ sai_object_type_t object_type, @@ -3864,3 +3863,103 @@ sai_status_t SwitchStateBase::queryAttributeCapability( return SAI_STATUS_SUCCESS; } + +sai_status_t SwitchStateBase::queryStatsCapability( + _In_ sai_object_id_t switchId, + _In_ sai_object_type_t objectType, + _Inout_ sai_stat_capability_list_t *stats_capability) +{ + SWSS_LOG_ENTER(); + uint32_t i = 0; + uint32_t stats_count = 0; + + if (objectType == SAI_OBJECT_TYPE_QUEUE) + { + stats_count = SAI_QUEUE_STAT_DELAY_WATERMARK_NS; + if (stats_capability->count < stats_count ) + { + stats_capability->count = stats_count; + return SAI_STATUS_BUFFER_OVERFLOW; + } + + stats_capability->count = stats_count; + + for(i = 0; i < stats_capability->count; i++) + { + stats_capability->list[i].stat_modes = SAI_STATS_MODE_READ_AND_CLEAR | SAI_STATS_MODE_READ ; + stats_capability->list[i].stat_enum = i; + } + + return SAI_STATUS_SUCCESS; + } + else if (objectType == SAI_OBJECT_TYPE_PORT) + { + if (stats_capability->count < 51) + { + stats_capability->count = 51; + return SAI_STATUS_BUFFER_OVERFLOW; + } + + stats_capability->count = 51; + stats_capability->list[0].stat_enum = SAI_PORT_STAT_IF_IN_OCTETS; + stats_capability->list[1].stat_enum = SAI_PORT_STAT_IF_IN_UCAST_PKTS; + stats_capability->list[2].stat_enum = SAI_PORT_STAT_IF_IN_NON_UCAST_PKTS; + stats_capability->list[3].stat_enum = SAI_PORT_STAT_IF_IN_DISCARDS; + stats_capability->list[4].stat_enum = SAI_PORT_STAT_IF_IN_ERRORS; + stats_capability->list[5].stat_enum = SAI_PORT_STAT_IF_IN_UNKNOWN_PROTOS; + stats_capability->list[6].stat_enum = SAI_PORT_STAT_IF_IN_BROADCAST_PKTS; + stats_capability->list[7].stat_enum = SAI_PORT_STAT_IF_IN_MULTICAST_PKTS; + stats_capability->list[8].stat_enum = SAI_PORT_STAT_IF_IN_VLAN_DISCARDS; + stats_capability->list[9].stat_enum = SAI_PORT_STAT_IF_OUT_OCTETS; + stats_capability->list[10].stat_enum = SAI_PORT_STAT_IF_OUT_UCAST_PKTS; + stats_capability->list[11].stat_enum = SAI_PORT_STAT_IF_OUT_NON_UCAST_PKTS; + stats_capability->list[12].stat_enum = SAI_PORT_STAT_IF_OUT_DISCARDS; + stats_capability->list[13].stat_enum = SAI_PORT_STAT_IF_OUT_ERRORS; + stats_capability->list[14].stat_enum = SAI_PORT_STAT_IF_OUT_QLEN; + stats_capability->list[15].stat_enum = SAI_PORT_STAT_IF_OUT_BROADCAST_PKTS; + stats_capability->list[16].stat_enum = SAI_PORT_STAT_IF_OUT_MULTICAST_PKTS; + stats_capability->list[17].stat_enum = SAI_PORT_STAT_ETHER_STATS_DROP_EVENTS; + stats_capability->list[18].stat_enum = SAI_PORT_STAT_ETHER_STATS_MULTICAST_PKTS; + stats_capability->list[19].stat_enum = SAI_PORT_STAT_ETHER_STATS_BROADCAST_PKTS; + stats_capability->list[20].stat_enum = SAI_PORT_STAT_ETHER_STATS_UNDERSIZE_PKTS; + stats_capability->list[21].stat_enum = SAI_PORT_STAT_ETHER_STATS_FRAGMENTS; + stats_capability->list[22].stat_enum = SAI_PORT_STAT_ETHER_STATS_PKTS_64_OCTETS; + stats_capability->list[23].stat_enum = SAI_PORT_STAT_ETHER_STATS_PKTS_65_TO_127_OCTETS; + stats_capability->list[24].stat_enum = SAI_PORT_STAT_ETHER_STATS_PKTS_128_TO_255_OCTETS; + stats_capability->list[25].stat_enum = SAI_PORT_STAT_ETHER_STATS_PKTS_256_TO_511_OCTETS; + stats_capability->list[26].stat_enum = SAI_PORT_STAT_ETHER_STATS_PKTS_512_TO_1023_OCTETS; + stats_capability->list[27].stat_enum = SAI_PORT_STAT_ETHER_STATS_PKTS_1024_TO_1518_OCTETS; + stats_capability->list[28].stat_enum = SAI_PORT_STAT_ETHER_STATS_PKTS_1519_TO_2047_OCTETS; + stats_capability->list[29].stat_enum = SAI_PORT_STAT_ETHER_STATS_PKTS_2048_TO_4095_OCTETS; + stats_capability->list[30].stat_enum = SAI_PORT_STAT_ETHER_STATS_PKTS_4096_TO_9216_OCTETS; + stats_capability->list[31].stat_enum = SAI_PORT_STAT_ETHER_STATS_PKTS_9217_TO_16383_OCTETS; + stats_capability->list[32].stat_enum = SAI_PORT_STAT_ETHER_STATS_OVERSIZE_PKTS; + stats_capability->list[33].stat_enum = SAI_PORT_STAT_ETHER_RX_OVERSIZE_PKTS; + stats_capability->list[34].stat_enum = SAI_PORT_STAT_ETHER_TX_OVERSIZE_PKTS; + stats_capability->list[35].stat_enum = SAI_PORT_STAT_ETHER_STATS_JABBERS; + stats_capability->list[36].stat_enum = SAI_PORT_STAT_ETHER_STATS_OCTETS; + stats_capability->list[37].stat_enum = SAI_PORT_STAT_ETHER_STATS_PKTS; + stats_capability->list[38].stat_enum = SAI_PORT_STAT_ETHER_STATS_COLLISIONS; + stats_capability->list[39].stat_enum = SAI_PORT_STAT_ETHER_STATS_CRC_ALIGN_ERRORS; + stats_capability->list[40].stat_enum = SAI_PORT_STAT_ETHER_STATS_TX_NO_ERRORS; + stats_capability->list[41].stat_enum = SAI_PORT_STAT_ETHER_STATS_RX_NO_ERRORS; + stats_capability->list[42].stat_enum = SAI_PORT_STAT_GREEN_WRED_DROPPED_PACKETS; + stats_capability->list[43].stat_enum = SAI_PORT_STAT_GREEN_WRED_DROPPED_BYTES; + stats_capability->list[44].stat_enum = SAI_PORT_STAT_YELLOW_WRED_DROPPED_PACKETS; + stats_capability->list[45].stat_enum = SAI_PORT_STAT_YELLOW_WRED_DROPPED_BYTES; + stats_capability->list[46].stat_enum = SAI_PORT_STAT_RED_WRED_DROPPED_PACKETS; + stats_capability->list[47].stat_enum = SAI_PORT_STAT_RED_WRED_DROPPED_BYTES; + stats_capability->list[48].stat_enum = SAI_PORT_STAT_WRED_DROPPED_PACKETS; + stats_capability->list[49].stat_enum = SAI_PORT_STAT_WRED_DROPPED_BYTES; + stats_capability->list[50].stat_enum = SAI_PORT_STAT_ECN_MARKED_PACKETS; + + for(i = 0; i < stats_capability->count; i++) + { + stats_capability->list[i].stat_modes = SAI_STATS_MODE_READ_AND_CLEAR | SAI_STATS_MODE_READ ; + } + + return SAI_STATUS_SUCCESS; + } + + return SAI_STATUS_NOT_SUPPORTED; +} diff --git a/vslib/SwitchStateBase.h b/vslib/SwitchStateBase.h index 22e111a34..77c46d1d4 100644 --- a/vslib/SwitchStateBase.h +++ b/vslib/SwitchStateBase.h @@ -300,6 +300,11 @@ namespace saivs _In_ sai_attr_id_t attr_id, _Inout_ sai_s32_list_t *enum_values_capability); + virtual sai_status_t queryStatsCapability( + _In_ sai_object_id_t switchId, + _In_ sai_object_type_t objectType, + _Inout_ sai_stat_capability_list_t *stats_capability); + virtual sai_status_t queryAttributeCapability( _In_ sai_object_id_t switch_id, _In_ sai_object_type_t object_type, diff --git a/vslib/VirtualSwitchSaiInterface.cpp b/vslib/VirtualSwitchSaiInterface.cpp index d866f5ac1..b940bc794 100644 --- a/vslib/VirtualSwitchSaiInterface.cpp +++ b/vslib/VirtualSwitchSaiInterface.cpp @@ -981,6 +981,8 @@ sai_status_t VirtualSwitchSaiInterface::queryStatsCapability( { SWSS_LOG_ENTER(); + uint32_t i=0; + if (m_switchStateMap.find(switchId) == m_switchStateMap.end()) { SWSS_LOG_ERROR("failed to find switch %s in switch state map", sai_serialize_object_id(switchId).c_str()); @@ -990,6 +992,94 @@ sai_status_t VirtualSwitchSaiInterface::queryStatsCapability( auto ss = m_switchStateMap.at(switchId); + + if (objectType == SAI_OBJECT_TYPE_QUEUE) + { + if (stats_capability->count < SAI_OBJECT_TYPE_QUEUE) + { + stats_capability->count = SAI_QUEUE_STAT_DELAY_WATERMARK_NS; + return SAI_STATUS_BUFFER_OVERFLOW; + } + + stats_capability->count = SAI_QUEUE_STAT_DELAY_WATERMARK_NS; + + for(i = 0; i < stats_capability->count; i++) + { + stats_capability->list[i].stat_modes = SAI_STATS_MODE_READ_AND_CLEAR | SAI_STATS_MODE_READ ; + stats_capability->list[i].stat_enum = i; + } + + return SAI_STATUS_SUCCESS; + } + else if (objectType == SAI_OBJECT_TYPE_PORT) + { + if (stats_capability->count < 51) + { + stats_capability->count = 51; + return SAI_STATUS_BUFFER_OVERFLOW; + } + + stats_capability->count = 51; + stats_capability->list[0].stat_enum = SAI_PORT_STAT_IF_IN_OCTETS; + stats_capability->list[1].stat_enum = SAI_PORT_STAT_IF_IN_UCAST_PKTS; + stats_capability->list[2].stat_enum = SAI_PORT_STAT_IF_IN_NON_UCAST_PKTS; + stats_capability->list[3].stat_enum = SAI_PORT_STAT_IF_IN_DISCARDS; + stats_capability->list[4].stat_enum = SAI_PORT_STAT_IF_IN_ERRORS; + stats_capability->list[5].stat_enum = SAI_PORT_STAT_IF_IN_UNKNOWN_PROTOS; + stats_capability->list[6].stat_enum = SAI_PORT_STAT_IF_IN_BROADCAST_PKTS; + stats_capability->list[7].stat_enum = SAI_PORT_STAT_IF_IN_MULTICAST_PKTS; + stats_capability->list[8].stat_enum = SAI_PORT_STAT_IF_IN_VLAN_DISCARDS; + stats_capability->list[9].stat_enum = SAI_PORT_STAT_IF_OUT_OCTETS; + stats_capability->list[10].stat_enum = SAI_PORT_STAT_IF_OUT_UCAST_PKTS; + stats_capability->list[11].stat_enum = SAI_PORT_STAT_IF_OUT_NON_UCAST_PKTS; + stats_capability->list[12].stat_enum = SAI_PORT_STAT_IF_OUT_DISCARDS; + stats_capability->list[13].stat_enum = SAI_PORT_STAT_IF_OUT_ERRORS; + stats_capability->list[14].stat_enum = SAI_PORT_STAT_IF_OUT_QLEN; + stats_capability->list[15].stat_enum = SAI_PORT_STAT_IF_OUT_BROADCAST_PKTS; + stats_capability->list[16].stat_enum = SAI_PORT_STAT_IF_OUT_MULTICAST_PKTS; + stats_capability->list[17].stat_enum = SAI_PORT_STAT_ETHER_STATS_DROP_EVENTS; + stats_capability->list[18].stat_enum = SAI_PORT_STAT_ETHER_STATS_MULTICAST_PKTS; + stats_capability->list[19].stat_enum = SAI_PORT_STAT_ETHER_STATS_BROADCAST_PKTS; + stats_capability->list[20].stat_enum = SAI_PORT_STAT_ETHER_STATS_UNDERSIZE_PKTS; + stats_capability->list[21].stat_enum = SAI_PORT_STAT_ETHER_STATS_FRAGMENTS; + stats_capability->list[22].stat_enum = SAI_PORT_STAT_ETHER_STATS_PKTS_64_OCTETS; + stats_capability->list[23].stat_enum = SAI_PORT_STAT_ETHER_STATS_PKTS_65_TO_127_OCTETS; + stats_capability->list[24].stat_enum = SAI_PORT_STAT_ETHER_STATS_PKTS_128_TO_255_OCTETS; + stats_capability->list[25].stat_enum = SAI_PORT_STAT_ETHER_STATS_PKTS_256_TO_511_OCTETS; + stats_capability->list[26].stat_enum = SAI_PORT_STAT_ETHER_STATS_PKTS_512_TO_1023_OCTETS; + stats_capability->list[27].stat_enum = SAI_PORT_STAT_ETHER_STATS_PKTS_1024_TO_1518_OCTETS; + stats_capability->list[28].stat_enum = SAI_PORT_STAT_ETHER_STATS_PKTS_1519_TO_2047_OCTETS; + stats_capability->list[29].stat_enum = SAI_PORT_STAT_ETHER_STATS_PKTS_2048_TO_4095_OCTETS; + stats_capability->list[30].stat_enum = SAI_PORT_STAT_ETHER_STATS_PKTS_4096_TO_9216_OCTETS; + stats_capability->list[31].stat_enum = SAI_PORT_STAT_ETHER_STATS_PKTS_9217_TO_16383_OCTETS; + stats_capability->list[32].stat_enum = SAI_PORT_STAT_ETHER_STATS_OVERSIZE_PKTS; + stats_capability->list[33].stat_enum = SAI_PORT_STAT_ETHER_RX_OVERSIZE_PKTS; + stats_capability->list[34].stat_enum = SAI_PORT_STAT_ETHER_TX_OVERSIZE_PKTS; + stats_capability->list[35].stat_enum = SAI_PORT_STAT_ETHER_STATS_JABBERS; + stats_capability->list[36].stat_enum = SAI_PORT_STAT_ETHER_STATS_OCTETS; + stats_capability->list[37].stat_enum = SAI_PORT_STAT_ETHER_STATS_PKTS; + stats_capability->list[38].stat_enum = SAI_PORT_STAT_ETHER_STATS_COLLISIONS; + stats_capability->list[39].stat_enum = SAI_PORT_STAT_ETHER_STATS_CRC_ALIGN_ERRORS; + stats_capability->list[40].stat_enum = SAI_PORT_STAT_ETHER_STATS_TX_NO_ERRORS; + stats_capability->list[41].stat_enum = SAI_PORT_STAT_ETHER_STATS_RX_NO_ERRORS; + stats_capability->list[42].stat_enum = SAI_PORT_STAT_GREEN_WRED_DROPPED_PACKETS; + stats_capability->list[43].stat_enum = SAI_PORT_STAT_GREEN_WRED_DROPPED_BYTES; + stats_capability->list[44].stat_enum = SAI_PORT_STAT_YELLOW_WRED_DROPPED_PACKETS; + stats_capability->list[45].stat_enum = SAI_PORT_STAT_YELLOW_WRED_DROPPED_BYTES; + stats_capability->list[46].stat_enum = SAI_PORT_STAT_RED_WRED_DROPPED_PACKETS; + stats_capability->list[47].stat_enum = SAI_PORT_STAT_RED_WRED_DROPPED_BYTES; + stats_capability->list[48].stat_enum = SAI_PORT_STAT_WRED_DROPPED_PACKETS; + stats_capability->list[49].stat_enum = SAI_PORT_STAT_WRED_DROPPED_BYTES; + stats_capability->list[50].stat_enum = SAI_PORT_STAT_ECN_MARKED_PACKETS; + + for(i = 0; i < stats_capability->count; i++) + { + stats_capability->list[i].stat_modes = SAI_STATS_MODE_READ_AND_CLEAR | SAI_STATS_MODE_READ ; + } + + return SAI_STATUS_SUCCESS; + } + return ss->queryStatsCapability( switchId, objectType,