diff --git a/lib/inc/sai_redis.h b/lib/inc/sai_redis.h index 85b8d2b00062..590e2d5bd56e 100644 --- a/lib/inc/sai_redis.h +++ b/lib/inc/sai_redis.h @@ -219,6 +219,16 @@ sai_status_t redis_generic_get_route_entry( _In_ uint32_t attr_count, _Out_ sai_attribute_t *attr_list); +// get_stats + +template +sai_status_t redis_generic_get_stats( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t object_id, + _In_ uint32_t count, + _In_ const T* counter_id_list, + _Out_ uint64_t *counter_list); + // notifications void handle_notification( diff --git a/lib/inc/sai_redis_internal.h b/lib/inc/sai_redis_internal.h index cf761c3b9e2d..1606b448e3a6 100644 --- a/lib/inc/sai_redis_internal.h +++ b/lib/inc/sai_redis_internal.h @@ -139,3 +139,23 @@ redis_set_ ## ot ##_attribute, \ redis_get_ ## ot ##_attribute, +// stats + +#define REDIS_GENERIC_GET_STATS(OBJECT_TYPE,object_type) \ + sai_status_t redis_get_ ## object_type ## _stats( \ + _In_ sai_object_id_t object_type ##_id, \ + _In_ uint32_t count, \ + _In_ const sai_ ## object_type ## _stat_t *counter_id_list, \ + _Out_ uint64_t *counter_list) \ + { \ + MUTEX(); \ + SWSS_LOG_ENTER(); \ + \ + return meta_sai_get_stats_oid( \ + SAI_OBJECT_TYPE_ ## OBJECT_TYPE, \ + object_type ## _id, \ + count, \ + counter_id_list, \ + counter_list, \ + &redis_generic_get_stats); \ + } diff --git a/lib/src/Makefile.am b/lib/src/Makefile.am index 841ad59901a2..1b94f5f04675 100644 --- a/lib/src/Makefile.am +++ b/lib/src/Makefile.am @@ -42,7 +42,8 @@ libsairedis_la_SOURCES = \ sai_redis_generic_set.cpp \ sai_redis_generic_get.cpp \ sai_redis_notifications.cpp \ - sai_redis_record.cpp + sai_redis_record.cpp \ + sai_redis_generic_get_stats.cpp libsairedis_la_CPPFLAGS = $(DBGFLAGS) $(AM_CPPFLAGS) $(CFLAGS_COMMON) libsairedis_la_LIBADD = -lhiredis -lswsscommon diff --git a/lib/src/sai_redis_buffer.cpp b/lib/src/sai_redis_buffer.cpp index 45875e3367c2..5d4bfa95f815 100644 --- a/lib/src/sai_redis_buffer.cpp +++ b/lib/src/sai_redis_buffer.cpp @@ -1,18 +1,5 @@ #include "sai_redis.h" -sai_status_t redis_get_ingress_priority_group_stats( - _In_ sai_object_id_t ingress_pg_id, - _In_ uint32_t number_of_counters, - _In_ const sai_ingress_priority_group_stat_t *counter_ids, - _Out_ uint64_t *counters) -{ - MUTEX(); - - SWSS_LOG_ENTER(); - - return SAI_STATUS_NOT_IMPLEMENTED; -} - sai_status_t redis_clear_ingress_priority_group_stats( _In_ sai_object_id_t ingress_pg_id, _In_ uint32_t number_of_counters, @@ -54,6 +41,8 @@ REDIS_GENERIC_QUAD(BUFFER_POOL,buffer_pool); REDIS_GENERIC_QUAD(INGRESS_PRIORITY_GROUP,ingress_priority_group); REDIS_GENERIC_QUAD(BUFFER_PROFILE,buffer_profile); +REDIS_GENERIC_GET_STATS(INGRESS_PRIORITY_GROUP,ingress_priority_group); + const sai_buffer_api_t redis_buffer_api = { REDIS_GENERIC_QUAD_API(buffer_pool) diff --git a/lib/src/sai_redis_generic_get_stats.cpp b/lib/src/sai_redis_generic_get_stats.cpp new file mode 100644 index 000000000000..685e3052f7c3 --- /dev/null +++ b/lib/src/sai_redis_generic_get_stats.cpp @@ -0,0 +1,217 @@ +#include "sai_redis.h" +#include "meta/saiserialize.h" + +sai_status_t internal_redis_get_stats_process( + _In_ sai_object_type_t object_type, + _In_ uint32_t count, + _Out_ uint64_t *counter_list, + _In_ swss::KeyOpFieldsValuesTuple &kco) +{ + SWSS_LOG_ENTER(); + + // key: sai_status + // field: stat_id + // value: stat_value + + const auto &key = kfvKey(kco); + const auto &values = kfvFieldsValues(kco); + + auto str_sai_status = key; + + sai_status_t status; + + sai_deserialize_status(str_sai_status, status); + + if (status == SAI_STATUS_SUCCESS) + { + uint32_t i = 0; + for (const auto &v : values) + { + if (i >= count) + { + SWSS_LOG_ERROR("Received more values than expected"); + status = SAI_STATUS_FAILURE; + break; + } + + uint64_t value = 0; + + value = stoull(fvValue(v)); + counter_list[i] = value; + i++; + } + } + + return status; +} + +template struct stat_traits {}; + +template <> +struct stat_traits +{ + typedef std::string (*serialize_stat)(sai_port_stat_t); + static constexpr serialize_stat serialize_stat_fn = sai_serialize_port_stat; +}; + +template <> +struct stat_traits +{ + typedef std::string (*serialize_stat)(sai_queue_stat_t); + static constexpr serialize_stat serialize_stat_fn = sai_serialize_queue_stat; +}; + +template <> +struct stat_traits +{ + typedef std::string (*serialize_stat)(sai_ingress_priority_group_stat_t); + static constexpr serialize_stat serialize_stat_fn = sai_serialize_ingress_priority_group_stat; +}; + + +template +std::vector serialize_counter_id_list( + _In_ uint32_t count, + _In_ const T *counter_id_list) +{ + SWSS_LOG_ENTER(); + + std::vector values; + + for (uint32_t i = 0; i < count; i++) + { + std::string field = stat_traits::serialize_stat_fn(counter_id_list[i]); + values.emplace_back(field, ""); + } + + return std::move(values); +} + +template +sai_status_t internal_redis_generic_get_stats( + _In_ sai_object_type_t object_type, + _In_ const std::string &serialized_object_id, + _In_ uint32_t count, + _In_ const T *counter_id_list, + _Out_ uint64_t *counter_list) +{ + SWSS_LOG_ENTER(); + + std::vector entry = serialize_counter_id_list( + count, + counter_id_list); + + std::string str_object_type = sai_serialize_object_type(object_type); + + std::string key = str_object_type + ":" + serialized_object_id; + + SWSS_LOG_DEBUG("generic get stats key: %s, fields: %lu", key.c_str(), entry.size()); + + if (g_record) + { + // XXX don't know which character to use for get stats. For now its 'm' + recordLine("m|" + key + "|" + joinFieldValues(entry)); + } + + // get is special, it will not put data + // into asic view, only to message queue + g_asicState->set(key, entry, "get_stats"); + + // wait for response + + swss::Select s; + + s.addSelectable(g_redisGetConsumer.get()); + + while (true) + { + SWSS_LOG_DEBUG("wait for get_stats response"); + + swss::Selectable *sel; + + int fd; + + int result = s.select(&sel, &fd, GET_RESPONSE_TIMEOUT); + + if (result == swss::Select::OBJECT) + { + swss::KeyOpFieldsValuesTuple kco; + + g_redisGetConsumer->pop(kco); + + const std::string &op = kfvOp(kco); + const std::string &opkey = kfvKey(kco); + + SWSS_LOG_DEBUG("response: op = %s, key = %s", opkey.c_str(), op.c_str()); + + if (op != "getresponse") // ignore non response messages + { + continue; + } + + sai_status_t status = internal_redis_get_stats_process( + object_type, + count, + counter_list, + kco); + + if (g_record) + { + const auto &str_status = kfvKey(kco); + const auto &values = kfvFieldsValues(kco); + + // first serialized is status + recordLine("M|" + str_status + "|" + joinFieldValues(values)); + } + + SWSS_LOG_DEBUG("generic get status: %d", status); + + return status; + } + + SWSS_LOG_ERROR("generic get failed due to SELECT operation result"); + break; + } + + if (g_record) + { + recordLine("M|SAI_STATUS_FAILURE"); + } + + SWSS_LOG_ERROR("generic get stats failed to get response"); + + return SAI_STATUS_FAILURE; +} + +template +sai_status_t redis_generic_get_stats( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t object_id, + _In_ uint32_t count, + _In_ const T* counter_id_list, + _Out_ uint64_t *counter_list) +{ + SWSS_LOG_ENTER(); + + std::string str_object_id = sai_serialize_object_id(object_id); + + return internal_redis_generic_get_stats( + object_type, + str_object_id, + count, + counter_id_list, + counter_list); +} + +#define DECLARE_REDIS_GENERIC_GET_STATS(type) \ + template \ + sai_status_t redis_generic_get_stats( \ + _In_ sai_object_type_t object_type, \ + _In_ sai_object_id_t object_id, \ + _In_ uint32_t count, \ + _In_ const sai_ ## type ## _stat_t *counter_id_list, \ + _Out_ uint64_t *counter_list); \ + +DECLARE_REDIS_GENERIC_GET_STATS(port); +DECLARE_REDIS_GENERIC_GET_STATS(queue); +DECLARE_REDIS_GENERIC_GET_STATS(ingress_priority_group); diff --git a/lib/src/sai_redis_port.cpp b/lib/src/sai_redis_port.cpp index a16f73c5844b..a3f962d57272 100644 --- a/lib/src/sai_redis_port.cpp +++ b/lib/src/sai_redis_port.cpp @@ -1,16 +1,5 @@ #include "sai_redis.h" -sai_status_t redis_get_port_stats( - _In_ sai_object_id_t port_id, - _In_ uint32_t number_of_counters, - _In_ const sai_port_stat_t *counter_ids, - _Out_ uint64_t *counters) -{ - SWSS_LOG_ENTER(); - - return SAI_STATUS_NOT_IMPLEMENTED; -} - sai_status_t redis_clear_port_stats( _In_ sai_object_id_t port_id, _In_ uint32_t number_of_counters, @@ -35,6 +24,8 @@ sai_status_t redis_clear_port_all_stats( REDIS_GENERIC_QUAD(PORT,port); +REDIS_GENERIC_GET_STATS(PORT, port); + const sai_port_api_t redis_port_api = { REDIS_GENERIC_QUAD_API(port) diff --git a/lib/src/sai_redis_queue.cpp b/lib/src/sai_redis_queue.cpp index eaf380c69d4f..d0efabce48ca 100644 --- a/lib/src/sai_redis_queue.cpp +++ b/lib/src/sai_redis_queue.cpp @@ -1,19 +1,6 @@ #include "sai_redis.h" #include "sai_redis_internal.h" -sai_status_t redis_get_queue_stats( - _In_ sai_object_id_t queue_id, - _In_ uint32_t number_of_counters, - _In_ const sai_queue_stat_t *counter_ids, - _Out_ uint64_t *counters) -{ - MUTEX(); - - SWSS_LOG_ENTER(); - - return SAI_STATUS_NOT_IMPLEMENTED; -} - sai_status_t redis_clear_queue_stats( _In_ sai_object_id_t queue_id, _In_ uint32_t number_of_counters, @@ -28,6 +15,8 @@ sai_status_t redis_clear_queue_stats( REDIS_GENERIC_QUAD(QUEUE,queue); +REDIS_GENERIC_GET_STATS(QUEUE, queue); + const sai_queue_api_t redis_queue_api = { REDIS_GENERIC_QUAD_API(queue) diff --git a/meta/sai_meta.cpp b/meta/sai_meta.cpp index 6dadef9f5802..72a1a4d7bafb 100644 --- a/meta/sai_meta.cpp +++ b/meta/sai_meta.cpp @@ -2299,6 +2299,46 @@ sai_status_t meta_generic_validation_get( return SAI_STATUS_SUCCESS; } +template +sai_status_t meta_generic_validation_get_stats( + _In_ const sai_object_meta_key_t& meta_key, + _In_ const uint32_t count, + _In_ const T *counter_id_list, + _In_ const uint64_t *counter_list) +{ + SWSS_LOG_ENTER(); + + if (count < 1) + { + SWSS_LOG_ERROR("expected at least 1 stat when calling get_stats, zero given"); + + return SAI_STATUS_INVALID_PARAMETER; + } + + if (count > MAX_LIST_COUNT) + { + SWSS_LOG_ERROR("get stats count is too large %u > then max list count %u", count, MAX_LIST_COUNT); + + return SAI_STATUS_INVALID_PARAMETER; + } + + if (counter_id_list == NULL) + { + SWSS_LOG_ERROR("counter id list pointer is NULL"); + + return SAI_STATUS_INVALID_PARAMETER; + } + + if (counter_list == NULL) + { + SWSS_LOG_ERROR("counter list pointer is NULL"); + + return SAI_STATUS_INVALID_PARAMETER; + } + + return SAI_STATUS_SUCCESS; +} + void meta_generic_validation_post_create( _In_ const sai_object_meta_key_t& meta_key, _In_ sai_object_id_t switch_id, @@ -4437,6 +4477,68 @@ sai_status_t meta_sai_get_oid( return status; } +template +sai_status_t meta_sai_get_stats_oid( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t object_id, + _In_ uint32_t count, + _In_ const T* counter_id_list, + _Out_ uint64_t *counter_list, + _In_ sai_get_generic_stats_fn get) +{ + SWSS_LOG_ENTER(); + + sai_status_t status = meta_sai_validate_oid(object_type, &object_id, SAI_NULL_OBJECT_ID, false); + + if (status != SAI_STATUS_SUCCESS) + { + return status; + } + + sai_object_meta_key_t meta_key = { .objecttype = object_type, .objectkey = { .key = { .object_id = object_id } } }; + + status = meta_generic_validation_get_stats(meta_key, count, counter_id_list, counter_list); + + if (status != SAI_STATUS_SUCCESS) + { + return status; + } + + if (get == NULL) + { + SWSS_LOG_ERROR("get function pointer is NULL"); + + return SAI_STATUS_INVALID_PARAMETER; + } + + status = get(object_type, object_id, count, counter_id_list, counter_list); + + if (status == SAI_STATUS_SUCCESS) + { + SWSS_LOG_DEBUG("get status: %s", sai_serialize_status(status).c_str()); + } + else + { + SWSS_LOG_ERROR("get status: %s", sai_serialize_status(status).c_str()); + } + + return status; +} + +#define DECLARE_META_GET_STATS_OID(type) \ + template \ + sai_status_t meta_sai_get_stats_oid( \ + _In_ sai_object_type_t object_type, \ + _In_ sai_object_id_t object_id, \ + _In_ uint32_t count, \ + _In_ const sai_ ## type ## _stat_t* counter_id_list, \ + _Out_ uint64_t *counter_list, \ + _In_ sai_get_generic_stats_fn get); + +DECLARE_META_GET_STATS_OID(port); +DECLARE_META_GET_STATS_OID(queue); +DECLARE_META_GET_STATS_OID(ingress_priority_group); + // NOTIFICATIONS void meta_sai_on_fdb_event_single( diff --git a/meta/sai_meta.h b/meta/sai_meta.h index 0ec3addb0d6c..26ac8d89f657 100644 --- a/meta/sai_meta.h +++ b/meta/sai_meta.h @@ -38,6 +38,14 @@ typedef sai_status_t (*sai_get_generic_attribute_fn)( _In_ uint32_t attr_count, _Inout_ sai_attribute_t *attr_list); +template +using sai_get_generic_stats_fn = sai_status_t (*)( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t object_id, + _In_ uint32_t count, + _In_ const T* counter_id_list, + _Out_ uint64_t *counter_list); + // META GENERIC extern sai_status_t meta_sai_create_oid( @@ -66,6 +74,15 @@ extern sai_status_t meta_sai_get_oid( _Inout_ sai_attribute_t *attr_list, _In_ sai_get_generic_attribute_fn get); +template +extern sai_status_t meta_sai_get_stats_oid( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t object_id, + _In_ uint32_t count, + _In_ const T* counter_id_list, + _Inout_ uint64_t *counter_list, + _In_ sai_get_generic_stats_fn get); + // META FDB extern sai_status_t meta_sai_create_fdb_entry( diff --git a/meta/saiserialize.cpp b/meta/saiserialize.cpp index ebf3605584d5..a665736327a3 100644 --- a/meta/saiserialize.cpp +++ b/meta/saiserialize.cpp @@ -716,6 +716,14 @@ std::string sai_serialize_queue_stat( return sai_serialize_enum(counter, &sai_metadata_enum_sai_queue_stat_t); } +std::string sai_serialize_ingress_priority_group_stat( + _In_ const sai_ingress_priority_group_stat_t counter) +{ + SWSS_LOG_ENTER(); + + return sai_serialize_enum(counter, &sai_metadata_enum_sai_ingress_priority_group_stat_t); +} + std::string sai_serialize_queue_attr( _In_ const sai_queue_attr_t attr) { @@ -2812,6 +2820,15 @@ void sai_deserialize_queue_stat( sai_deserialize_enum(s, &sai_metadata_enum_sai_queue_stat_t, (int32_t&)stat); } +void sai_deserialize_ingress_priority_group_stat( + _In_ const std::string& s, + _Out_ sai_ingress_priority_group_stat_t& stat) +{ + SWSS_LOG_ENTER(); + + sai_deserialize_enum(s, &sai_metadata_enum_sai_ingress_priority_group_stat_t, (int32_t&)stat); +} + void sai_deserialize_queue_attr( _In_ const std::string& s, _Out_ sai_queue_attr_t& attr) diff --git a/meta/saiserialize.h b/meta/saiserialize.h index f7d093364db3..dae3d32747db 100644 --- a/meta/saiserialize.h +++ b/meta/saiserialize.h @@ -75,6 +75,9 @@ std::string sai_serialize_port_stat( std::string sai_serialize_queue_stat( _In_ const sai_queue_stat_t counter); +std::string sai_serialize_ingress_priority_group_stat( + _In_ const sai_ingress_priority_group_stat_t counter); + std::string sai_serialize_queue_attr( _In_ const sai_queue_attr_t attr); @@ -223,7 +226,12 @@ void sai_deserialize_queue_stat( _In_ const std::string& s, _Out_ sai_queue_stat_t& stat); +void sai_deserialize_ingress_priority_group_stat( + _In_ const std::string& s, + _Out_ sai_ingress_priority_group_stat_t& stat); + void sai_deserialize_queue_attr( _In_ const std::string& s, _Out_ sai_queue_attr_t& attr); + #endif // __SAI_SERIALIZE__ diff --git a/syncd/syncd.cpp b/syncd/syncd.cpp index a6230fbf5333..08592579a35c 100644 --- a/syncd/syncd.cpp +++ b/syncd/syncd.cpp @@ -1570,6 +1570,119 @@ sai_status_t notifySyncd( return SAI_STATUS_SUCCESS; } +template +std::vector extractCounterIdsGeneric( + _In_ const swss::KeyOpFieldsValuesTuple &kco, + _In_ G deserializeIdFn) +{ + SWSS_LOG_ENTER(); + + const auto& values = kfvFieldsValues(kco); + std::vector counterIdList; + for (const auto & v : values) + { + std::string field = fvField(v); + T counterId; + deserializeIdFn(field, counterId); + + counterIdList.push_back(counterId); + } + + return std::move(counterIdList); +} + +template +sai_status_t getStatsGeneric( + _In_ sai_object_id_t object_id, + _In_ const swss::KeyOpFieldsValuesTuple &kco, + _Out_ std::vector& counters, + _In_ F getStatsFn, + _In_ G deserializeIdFn) +{ + SWSS_LOG_ENTER(); + + const std::vector counter_ids = extractCounterIdsGeneric( + kco, + deserializeIdFn); + counters.resize(counter_ids.size()); + + return getStatsFn( + object_id, + (uint32_t)counter_ids.size(), + counter_ids.data(), + counters.data()); +} + +sai_status_t processGetStatsEvent( + _In_ const swss::KeyOpFieldsValuesTuple &kco) +{ + SWSS_LOG_ENTER(); + + const std::string &key = kfvKey(kco); + const std::string &str_object_type = key.substr(0, key.find(":")); + const std::string &str_object_id = key.substr(key.find(":") + 1); + + sai_object_id_t object_id; + sai_deserialize_object_id(str_object_id, object_id); + sai_object_id_t rid = translate_vid_to_rid(object_id); + sai_object_type_t object_type; + sai_deserialize_object_type(str_object_type, object_type); + + std::vector result; + + sai_status_t status = SAI_STATUS_SUCCESS; + + switch (object_type) + { + case SAI_OBJECT_TYPE_PORT: + status = getStatsGeneric( + rid, + kco, + result, + sai_metadata_sai_port_api->get_port_stats, + sai_deserialize_port_stat); + break; + case SAI_OBJECT_TYPE_QUEUE: + status = getStatsGeneric( + rid, + kco, + result, + sai_metadata_sai_queue_api->get_queue_stats, + sai_deserialize_queue_stat); + break; + case SAI_OBJECT_TYPE_INGRESS_PRIORITY_GROUP: + status = getStatsGeneric( + rid, + kco, + result, + sai_metadata_sai_buffer_api->get_ingress_priority_group_stats, + sai_deserialize_ingress_priority_group_stat); + break; + default: + SWSS_LOG_ERROR("SAI object type %s not supported", str_object_type.c_str()); + status = SAI_STATUS_NOT_SUPPORTED; + } + + std::vector entry; + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to get stats"); + } + else + { + const auto& values = kfvFieldsValues(kco); + for (size_t i = 0; i < values.size(); i++) + { + entry.emplace_back(fvField(values[i]), std::to_string(result[i])); + } + } + + getResponse->set(sai_serialize_status(status), entry, "getresponse"); + + return status; +} + void on_switch_create_in_init_view( _In_ sai_object_id_t switch_vid, _In_ uint32_t attr_count, @@ -2194,6 +2307,10 @@ sai_status_t processEvent( { return notifySyncd(key); } + else if (op == "get_stats") + { + return processGetStatsEvent(kco); + } else { SWSS_LOG_THROW("api %s is not implemented", op.c_str());