lib/Sai.cpp (674 lines of code) (raw):
#include "Sai.h"
#include "SaiInternal.h"
#include "SwitchConfigContainer.h"
#include "ContextConfigContainer.h"
#include "meta/Meta.h"
#include "meta/sai_serialize.h"
// TODO - simplify recorder
using namespace sairedis;
using namespace std::placeholders;
#define REDIS_CHECK_API_INITIALIZED() \
if (!m_apiInitialized) { \
SWSS_LOG_ERROR("%s: api not initialized", __PRETTY_FUNCTION__); \
return SAI_STATUS_FAILURE; }
#define REDIS_CHECK_CONTEXT(oid) \
auto _globalContext = VirtualObjectIdManager::getGlobalContext(oid); \
auto context = getContext(_globalContext); \
if (context == nullptr) { \
SWSS_LOG_ERROR("no context at index %u for oid %s", \
_globalContext, \
sai_serialize_object_id(oid).c_str()); \
return SAI_STATUS_FAILURE; }
#define REDIS_CHECK_POINTER(pointer) \
if ((pointer) == nullptr) { \
SWSS_LOG_ERROR("entry pointer " # pointer " is null"); \
return SAI_STATUS_INVALID_PARAMETER; }
Sai::Sai()
{
SWSS_LOG_ENTER();
m_apiInitialized = false;
}
Sai::~Sai()
{
SWSS_LOG_ENTER();
if (m_apiInitialized)
{
apiUninitialize();
}
}
// INITIALIZE UNINITIALIZE
sai_status_t Sai::apiInitialize(
_In_ uint64_t flags,
_In_ const sai_service_method_table_t *service_method_table)
{
MUTEX();
SWSS_LOG_ENTER();
if (m_apiInitialized)
{
SWSS_LOG_ERROR("%s: api already initialized", __PRETTY_FUNCTION__);
return SAI_STATUS_FAILURE;
}
if (flags != 0)
{
SWSS_LOG_ERROR("invalid flags passed to SAI API initialize");
return SAI_STATUS_INVALID_PARAMETER;
}
if ((service_method_table == NULL) ||
(service_method_table->profile_get_next_value == NULL) ||
(service_method_table->profile_get_value == NULL))
{
SWSS_LOG_ERROR("invalid service_method_table handle passed to SAI API initialize");
return SAI_STATUS_INVALID_PARAMETER;
}
memcpy(&m_service_method_table, service_method_table, sizeof(m_service_method_table));
m_recorder = std::make_shared<Recorder>();
const char* contextConfig = service_method_table->profile_get_value(0, SAI_REDIS_KEY_CONTEXT_CONFIG);
auto ccc = ContextConfigContainer::loadFromFile(contextConfig);
for (auto&cc: ccc->getAllContextConfigs())
{
auto context = std::make_shared<Context>(cc, m_recorder, std::bind(&Sai::handle_notification, this, _1, _2));
m_contextMap[cc->m_guid] = context;
}
m_apiInitialized = true;
return SAI_STATUS_SUCCESS;
}
sai_status_t Sai::apiUninitialize(void)
{
SWSS_LOG_ENTER();
REDIS_CHECK_API_INITIALIZED();
SWSS_LOG_NOTICE("begin");
m_contextMap.clear();
m_recorder = nullptr;
m_apiInitialized = false;
SWSS_LOG_NOTICE("end");
return SAI_STATUS_SUCCESS;
}
// QUAD OID
sai_status_t Sai::create(
_In_ sai_object_type_t objectType,
_Out_ sai_object_id_t* objectId,
_In_ sai_object_id_t switchId,
_In_ uint32_t attr_count,
_In_ const sai_attribute_t *attr_list)
{
MUTEX();
SWSS_LOG_ENTER();
REDIS_CHECK_API_INITIALIZED();
REDIS_CHECK_CONTEXT(switchId);
if (objectType == SAI_OBJECT_TYPE_SWITCH && attr_count > 0 && attr_list)
{
uint32_t globalContext = 0; // default
if (attr_list[attr_count - 1].id == SAI_REDIS_SWITCH_ATTR_CONTEXT)
{
globalContext = attr_list[--attr_count].value.u32;
}
SWSS_LOG_NOTICE("request switch create with context %u", globalContext);
context = getContext(globalContext);
if (context == nullptr)
{
SWSS_LOG_ERROR("no global context defined at index %u", globalContext);
return SAI_STATUS_FAILURE;
}
}
auto status = context->m_meta->create(
objectType,
objectId,
switchId,
attr_count,
attr_list);
if (objectType == SAI_OBJECT_TYPE_SWITCH && status == SAI_STATUS_SUCCESS)
{
auto *attr = sai_metadata_get_attr_by_id(
SAI_SWITCH_ATTR_INIT_SWITCH,
attr_count,
attr_list);
if (attr && attr->value.booldata == false)
{
// request to connect to existing switch
SWSS_LOG_NOTICE("Sai::create call context populateMetadata");
context->populateMetadata(*objectId);
}
}
return status;
}
sai_status_t Sai::remove(
_In_ sai_object_type_t objectType,
_In_ sai_object_id_t objectId)
{
MUTEX();
SWSS_LOG_ENTER();
REDIS_CHECK_API_INITIALIZED();
REDIS_CHECK_CONTEXT(objectId);
return context->m_meta->remove(objectType, objectId);
}
sai_status_t Sai::set(
_In_ sai_object_type_t objectType,
_In_ sai_object_id_t objectId,
_In_ const sai_attribute_t *attr)
{
MUTEX();
SWSS_LOG_ENTER();
REDIS_CHECK_API_INITIALIZED();
if (RedisRemoteSaiInterface::isRedisAttribute(objectType, attr))
{
if (attr->id == SAI_REDIS_SWITCH_ATTR_REDIS_COMMUNICATION_MODE)
{
// Since communication mode destroys current channel and creates
// new one, it may happen, that during this SET api execution when
// api mutex is acquired, channel destructor will be blocking on
// thread->join() and channel thread will start processing
// incoming notification. That notification will be synchronized
// with api mutex and will cause deadlock, so to mitigate this
// scenario we will unlock api mutex here.
//
// This is not the perfect, but assuming that communication mode is
// changed in single thread and before switch create then we should
// not hit race condition.
SWSS_LOG_NOTICE("unlocking api mutex for communication mode");
MUTEX_UNLOCK();
}
// skip metadata if attribute is redis extension attribute
bool success = true;
// Setting on all contexts if objectType != SAI_OBJECT_TYPE_SWITCH or objectId == NULL
for (auto& kvp: m_contextMap)
{
if (objectType == SAI_OBJECT_TYPE_SWITCH && objectId != SAI_NULL_OBJECT_ID)
{
if (!kvp.second->m_redisSai->containsSwitch(objectId))
{
continue;
}
}
sai_status_t status = kvp.second->m_redisSai->set(objectType, objectId, attr);
success &= (status == SAI_STATUS_SUCCESS);
SWSS_LOG_INFO("setting attribute 0x%x status: %s",
attr->id,
sai_serialize_status(status).c_str());
}
return success ? SAI_STATUS_SUCCESS : SAI_STATUS_FAILURE;
}
REDIS_CHECK_CONTEXT(objectId);
return context->m_meta->set(objectType, objectId, attr);
}
sai_status_t Sai::get(
_In_ sai_object_type_t objectType,
_In_ sai_object_id_t objectId,
_In_ uint32_t attr_count,
_Inout_ sai_attribute_t *attr_list)
{
MUTEX();
SWSS_LOG_ENTER();
REDIS_CHECK_API_INITIALIZED();
REDIS_CHECK_CONTEXT(objectId);
return context->m_meta->get(
objectType,
objectId,
attr_count,
attr_list);
}
// QUAD ENTRY
#define DECLARE_CREATE_ENTRY(OT,ot) \
sai_status_t Sai::create( \
_In_ const sai_ ## ot ## _t* entry, \
_In_ uint32_t attr_count, \
_In_ const sai_attribute_t *attr_list) \
{ \
MUTEX(); \
SWSS_LOG_ENTER(); \
REDIS_CHECK_API_INITIALIZED(); \
REDIS_CHECK_POINTER(entry) \
REDIS_CHECK_CONTEXT(entry->switch_id); \
return context->m_meta->create(entry, attr_count, attr_list); \
}
SAIREDIS_DECLARE_EVERY_ENTRY(DECLARE_CREATE_ENTRY);
#define DECLARE_REMOVE_ENTRY(OT,ot) \
sai_status_t Sai::remove( \
_In_ const sai_ ## ot ## _t* entry) \
{ \
MUTEX(); \
SWSS_LOG_ENTER(); \
REDIS_CHECK_API_INITIALIZED(); \
REDIS_CHECK_POINTER(entry) \
REDIS_CHECK_CONTEXT(entry->switch_id); \
return context->m_meta->remove(entry); \
}
SAIREDIS_DECLARE_EVERY_ENTRY(DECLARE_REMOVE_ENTRY);
#define DECLARE_SET_ENTRY(OT,ot) \
sai_status_t Sai::set( \
_In_ const sai_ ## ot ## _t* entry, \
_In_ const sai_attribute_t *attr) \
{ \
MUTEX(); \
SWSS_LOG_ENTER(); \
REDIS_CHECK_API_INITIALIZED(); \
REDIS_CHECK_POINTER(entry) \
REDIS_CHECK_CONTEXT(entry->switch_id); \
return context->m_meta->set(entry, attr); \
}
SAIREDIS_DECLARE_EVERY_ENTRY(DECLARE_SET_ENTRY);
#define DECLARE_GET_ENTRY(OT,ot) \
sai_status_t Sai::get( \
_In_ const sai_ ## ot ## _t* entry, \
_In_ uint32_t attr_count, \
_Inout_ sai_attribute_t *attr_list) \
{ \
MUTEX(); \
SWSS_LOG_ENTER(); \
REDIS_CHECK_API_INITIALIZED(); \
REDIS_CHECK_POINTER(entry) \
REDIS_CHECK_CONTEXT(entry->switch_id); \
return context->m_meta->get(entry, attr_count, attr_list); \
}
SAIREDIS_DECLARE_EVERY_ENTRY(DECLARE_GET_ENTRY);
// STATS
sai_status_t Sai::getStats(
_In_ sai_object_type_t object_type,
_In_ sai_object_id_t object_id,
_In_ uint32_t number_of_counters,
_In_ const sai_stat_id_t *counter_ids,
_Out_ uint64_t *counters)
{
MUTEX();
SWSS_LOG_ENTER();
REDIS_CHECK_API_INITIALIZED();
REDIS_CHECK_CONTEXT(object_id);
return context->m_meta->getStats(
object_type,
object_id,
number_of_counters,
counter_ids,
counters);
}
sai_status_t Sai::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();
REDIS_CHECK_CONTEXT(switchId);
return context->m_meta->queryStatsCapability(
switchId,
objectType,
stats_capability);
}
sai_status_t Sai::queryStatsStCapability(
_In_ sai_object_id_t switchId,
_In_ sai_object_type_t objectType,
_Inout_ sai_stat_st_capability_list_t *stats_capability)
{
MUTEX();
SWSS_LOG_ENTER();
REDIS_CHECK_API_INITIALIZED();
REDIS_CHECK_CONTEXT(switchId);
return context->m_meta->queryStatsStCapability(
switchId,
objectType,
stats_capability);
}
sai_status_t Sai::getStatsExt(
_In_ sai_object_type_t object_type,
_In_ sai_object_id_t object_id,
_In_ uint32_t number_of_counters,
_In_ const sai_stat_id_t *counter_ids,
_In_ sai_stats_mode_t mode,
_Out_ uint64_t *counters)
{
MUTEX();
SWSS_LOG_ENTER();
REDIS_CHECK_API_INITIALIZED();
REDIS_CHECK_CONTEXT(object_id);
return context->m_meta->getStatsExt(
object_type,
object_id,
number_of_counters,
counter_ids,
mode,
counters);
}
sai_status_t Sai::clearStats(
_In_ sai_object_type_t object_type,
_In_ sai_object_id_t object_id,
_In_ uint32_t number_of_counters,
_In_ const sai_stat_id_t *counter_ids)
{
MUTEX();
SWSS_LOG_ENTER();
REDIS_CHECK_API_INITIALIZED();
REDIS_CHECK_CONTEXT(object_id);
return context->m_meta->clearStats(
object_type,
object_id,
number_of_counters,
counter_ids);
}
sai_status_t Sai::bulkGetStats(
_In_ sai_object_id_t switchId,
_In_ sai_object_type_t object_type,
_In_ uint32_t object_count,
_In_ const sai_object_key_t *object_key,
_In_ uint32_t number_of_counters,
_In_ const sai_stat_id_t *counter_ids,
_In_ sai_stats_mode_t mode,
_Inout_ sai_status_t *object_statuses,
_Out_ uint64_t *counters)
{
SWSS_LOG_ENTER();
return SAI_STATUS_NOT_IMPLEMENTED;
}
sai_status_t Sai::bulkClearStats(
_In_ sai_object_id_t switchId,
_In_ sai_object_type_t object_type,
_In_ uint32_t object_count,
_In_ const sai_object_key_t *object_key,
_In_ uint32_t number_of_counters,
_In_ const sai_stat_id_t *counter_ids,
_In_ sai_stats_mode_t mode,
_Inout_ sai_status_t *object_statuses)
{
SWSS_LOG_ENTER();
return SAI_STATUS_NOT_IMPLEMENTED;
}
// BULK QUAD OID
sai_status_t Sai::bulkCreate(
_In_ sai_object_type_t object_type,
_In_ sai_object_id_t switch_id,
_In_ uint32_t object_count,
_In_ const uint32_t *attr_count,
_In_ const sai_attribute_t **attr_list,
_In_ sai_bulk_op_error_mode_t mode,
_Out_ sai_object_id_t *object_id,
_Out_ sai_status_t *object_statuses)
{
MUTEX();
SWSS_LOG_ENTER();
REDIS_CHECK_API_INITIALIZED();
REDIS_CHECK_CONTEXT(switch_id);
return context->m_meta->bulkCreate(
object_type,
switch_id,
object_count,
attr_count,
attr_list,
mode,
object_id,
object_statuses);
}
sai_status_t Sai::bulkRemove(
_In_ sai_object_type_t object_type,
_In_ uint32_t object_count,
_In_ const sai_object_id_t *object_id,
_In_ sai_bulk_op_error_mode_t mode,
_Out_ sai_status_t *object_statuses)
{
MUTEX();
SWSS_LOG_ENTER();
REDIS_CHECK_API_INITIALIZED();
REDIS_CHECK_POINTER(object_id);
REDIS_CHECK_CONTEXT(*object_id);
return context->m_meta->bulkRemove(
object_type,
object_count,
object_id,
mode,
object_statuses);
}
sai_status_t Sai::bulkSet(
_In_ sai_object_type_t object_type,
_In_ uint32_t object_count,
_In_ const sai_object_id_t *object_id,
_In_ const sai_attribute_t *attr_list,
_In_ sai_bulk_op_error_mode_t mode,
_Out_ sai_status_t *object_statuses)
{
MUTEX();
SWSS_LOG_ENTER();
REDIS_CHECK_API_INITIALIZED();
REDIS_CHECK_CONTEXT(*object_id);
return context->m_meta->bulkSet(
object_type,
object_count,
object_id,
attr_list,
mode,
object_statuses);
}
sai_status_t Sai::bulkGet(
_In_ sai_object_type_t object_type,
_In_ uint32_t object_count,
_In_ const sai_object_id_t *object_id,
_In_ const uint32_t *attr_count,
_Inout_ sai_attribute_t **attr_list,
_In_ sai_bulk_op_error_mode_t mode,
_Out_ sai_status_t *object_statuses)
{
MUTEX();
SWSS_LOG_ENTER();
REDIS_CHECK_API_INITIALIZED();
REDIS_CHECK_CONTEXT(*object_id);
return context->m_meta->bulkGet(
object_type,
object_count,
object_id,
attr_count,
attr_list,
mode,
object_statuses);
}
// BULK QUAD ENTRY
#define DECLARE_BULK_CREATE_ENTRY(OT,ot) \
sai_status_t Sai::bulkCreate( \
_In_ uint32_t object_count, \
_In_ const sai_ ## ot ## _t* entries, \
_In_ const uint32_t *attr_count, \
_In_ const sai_attribute_t **attr_list, \
_In_ sai_bulk_op_error_mode_t mode, \
_Out_ sai_status_t *object_statuses) \
{ \
MUTEX(); \
SWSS_LOG_ENTER(); \
REDIS_CHECK_API_INITIALIZED(); \
REDIS_CHECK_POINTER(entries) \
REDIS_CHECK_CONTEXT(entries->switch_id); \
return context->m_meta->bulkCreate( \
object_count, \
entries, \
attr_count, \
attr_list, \
mode, \
object_statuses); \
}
SAIREDIS_DECLARE_EVERY_BULK_ENTRY(DECLARE_BULK_CREATE_ENTRY);
// BULK REMOVE
#define DECLARE_BULK_REMOVE_ENTRY(OT,ot) \
sai_status_t Sai::bulkRemove( \
_In_ uint32_t object_count, \
_In_ const sai_ ## ot ## _t *entries, \
_In_ sai_bulk_op_error_mode_t mode, \
_Out_ sai_status_t *object_statuses) \
{ \
MUTEX(); \
SWSS_LOG_ENTER(); \
REDIS_CHECK_API_INITIALIZED(); \
REDIS_CHECK_POINTER(entries) \
REDIS_CHECK_CONTEXT(entries->switch_id); \
return context->m_meta->bulkRemove( \
object_count, \
entries, \
mode, \
object_statuses); \
}
SAIREDIS_DECLARE_EVERY_BULK_ENTRY(DECLARE_BULK_REMOVE_ENTRY);
// BULK SET
#define DECLARE_BULK_SET_ENTRY(OT,ot) \
sai_status_t Sai::bulkSet( \
_In_ uint32_t object_count, \
_In_ const sai_ ## ot ## _t *entries, \
_In_ const sai_attribute_t *attr_list, \
_In_ sai_bulk_op_error_mode_t mode, \
_Out_ sai_status_t *object_statuses) \
{ \
MUTEX(); \
SWSS_LOG_ENTER(); \
REDIS_CHECK_API_INITIALIZED(); \
REDIS_CHECK_POINTER(entries) \
REDIS_CHECK_CONTEXT(entries->switch_id); \
return context->m_meta->bulkSet( \
object_count, \
entries, \
attr_list, \
mode, \
object_statuses); \
}
SAIREDIS_DECLARE_EVERY_BULK_ENTRY(DECLARE_BULK_SET_ENTRY);
// BULK GET
#define DECLARE_BULK_GET_ENTRY(OT,ot) \
sai_status_t Sai::bulkGet( \
_In_ uint32_t object_count, \
_In_ const sai_ ## ot ## _t *ot, \
_In_ const uint32_t *attr_count, \
_Inout_ sai_attribute_t **attr_list, \
_In_ sai_bulk_op_error_mode_t mode, \
_Out_ sai_status_t *object_statuses) \
{ \
MUTEX(); \
SWSS_LOG_ENTER(); \
REDIS_CHECK_API_INITIALIZED(); \
REDIS_CHECK_POINTER(ot); \
REDIS_CHECK_POINTER(attr_count); \
REDIS_CHECK_POINTER(attr_list); \
REDIS_CHECK_POINTER(object_statuses); \
SWSS_LOG_ERROR("FIXME not implemented"); \
return SAI_STATUS_NOT_IMPLEMENTED; \
}
SAIREDIS_DECLARE_EVERY_BULK_ENTRY(DECLARE_BULK_GET_ENTRY);
// NON QUAD API
sai_status_t Sai::flushFdbEntries(
_In_ sai_object_id_t switch_id,
_In_ uint32_t attr_count,
_In_ const sai_attribute_t *attr_list)
{
MUTEX();
SWSS_LOG_ENTER();
REDIS_CHECK_API_INITIALIZED();
REDIS_CHECK_CONTEXT(switch_id);
return context->m_meta->flushFdbEntries(
switch_id,
attr_count,
attr_list);
}
// SAI API
sai_status_t Sai::objectTypeGetAvailability(
_In_ sai_object_id_t switchId,
_In_ sai_object_type_t objectType,
_In_ uint32_t attrCount,
_In_ const sai_attribute_t *attrList,
_Out_ uint64_t *count)
{
MUTEX();
SWSS_LOG_ENTER();
REDIS_CHECK_API_INITIALIZED();
REDIS_CHECK_CONTEXT(switchId);
return context->m_meta->objectTypeGetAvailability(
switchId,
objectType,
attrCount,
attrList,
count);
}
sai_status_t Sai::queryAttributeCapability(
_In_ sai_object_id_t switch_id,
_In_ sai_object_type_t object_type,
_In_ sai_attr_id_t attr_id,
_Out_ sai_attr_capability_t *capability)
{
MUTEX();
SWSS_LOG_ENTER();
REDIS_CHECK_API_INITIALIZED();
REDIS_CHECK_CONTEXT(switch_id);
return context->m_meta->queryAttributeCapability(
switch_id,
object_type,
attr_id,
capability);
}
sai_status_t Sai::queryAttributeEnumValuesCapability(
_In_ sai_object_id_t switch_id,
_In_ sai_object_type_t object_type,
_In_ sai_attr_id_t attr_id,
_Inout_ sai_s32_list_t *enum_values_capability)
{
MUTEX();
SWSS_LOG_ENTER();
REDIS_CHECK_API_INITIALIZED();
REDIS_CHECK_CONTEXT(switch_id);
return context->m_meta->queryAttributeEnumValuesCapability(
switch_id,
object_type,
attr_id,
enum_values_capability);
}
sai_object_type_t Sai::objectTypeQuery(
_In_ sai_object_id_t objectId)
{
SWSS_LOG_ENTER();
if (!m_apiInitialized)
{
SWSS_LOG_ERROR("%s: SAI API not initialized", __PRETTY_FUNCTION__);
return SAI_OBJECT_TYPE_NULL;
}
return VirtualObjectIdManager::objectTypeQuery(objectId);
}
sai_object_id_t Sai::switchIdQuery(
_In_ sai_object_id_t objectId)
{
SWSS_LOG_ENTER();
if (!m_apiInitialized)
{
SWSS_LOG_ERROR("%s: SAI API not initialized", __PRETTY_FUNCTION__);
return SAI_NULL_OBJECT_ID;
}
return VirtualObjectIdManager::switchIdQuery(objectId);
}
sai_status_t Sai::logSet(
_In_ sai_api_t api,
_In_ sai_log_level_t log_level)
{
MUTEX();
SWSS_LOG_ENTER();
REDIS_CHECK_API_INITIALIZED();
for (auto&kvp: m_contextMap)
{
kvp.second->m_meta->logSet(api, log_level);
}
return SAI_STATUS_SUCCESS;
}
sai_status_t Sai::queryApiVersion(
_Out_ sai_api_version_t *version)
{
MUTEX();
SWSS_LOG_ENTER();
REDIS_CHECK_API_INITIALIZED();
// TODO we should use specific context, but we don't know which one since
// there is no object ID parameter, we can use default context or cast
// version as context id same as passed in SAI_REDIS_SWITCH_ATTR_CONTEXT
// currently we will return just first context on context map, since
// user maybe not aware of trick with casting context
for (auto&kvp: m_contextMap)
{
SWSS_LOG_WARN("using first context");
return kvp.second->m_meta->queryApiVersion(version);
}
SWSS_LOG_ERROR("context map is empty");
return SAI_STATUS_FAILURE;
}
/*
* NOTE: Notifications during switch create and switch remove.
*
* It is possible that when we create switch we will immediately start getting
* notifications from it, and it may happen that this switch will not be yet
* put to switch container and notification won't find it. But before
* notification will be processed it will first try to acquire mutex, so create
* switch function will end and switch will be put inside container.
*
* Similar it can happen that we receive notification when we are removing
* switch, then switch will be removed from switch container and notification
* will not find existing switch, but that's ok since switch was removed, and
* notification can be ignored.
*/
sai_switch_notifications_t Sai::handle_notification(
_In_ std::shared_ptr<Notification> notification,
_In_ Context* context)
{
MUTEX();
SWSS_LOG_ENTER();
if (!m_apiInitialized)
{
SWSS_LOG_ERROR("%s: api not initialized", __PRETTY_FUNCTION__);
return { };
}
return context->m_redisSai->syncProcessNotification(notification);
}
std::shared_ptr<Context> Sai::getContext(
_In_ uint32_t globalContext)
{
SWSS_LOG_ENTER();
auto it = m_contextMap.find(globalContext);
if (it == m_contextMap.end())
return nullptr;
return it->second;
}
std::vector<swss::FieldValueTuple> serialize_counter_id_list(
_In_ const sai_enum_metadata_t *stats_enum,
_In_ uint32_t count,
_In_ const sai_stat_id_t*counter_id_list)
{
SWSS_LOG_ENTER();
std::vector<swss::FieldValueTuple> values;
for (uint32_t i = 0; i < count; i++)
{
const char *name = sai_metadata_get_enum_value_name(stats_enum, counter_id_list[i]);
if (name == NULL)
{
SWSS_LOG_THROW("failed to find enum %d in %s", counter_id_list[i], stats_enum->name);
}
values.emplace_back(name, "");
}
return values;
}