lib/RedisRemoteSaiInterface.cpp (1,571 lines of code) (raw):
#include "RedisRemoteSaiInterface.h"
#include "Utils.h"
#include "Recorder.h"
#include "VirtualObjectIdManager.h"
#include "SkipRecordAttrContainer.h"
#include "SwitchContainer.h"
#include "ZeroMQChannel.h"
#include "sairediscommon.h"
#include "meta/NotificationFactory.h"
#include "meta/sai_serialize.h"
#include "meta/SaiAttributeList.h"
#include "meta/PerformanceIntervalTimer.h"
#include "meta/Globals.h"
#include "swss/tokenize.h"
#include "config.h"
#include <inttypes.h>
using namespace sairedis;
using namespace saimeta;
using namespace sairediscommon;
using namespace std::placeholders;
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);
RedisRemoteSaiInterface::RedisRemoteSaiInterface(
_In_ std::shared_ptr<ContextConfig> contextConfig,
_In_ std::function<sai_switch_notifications_t(std::shared_ptr<Notification>)> notificationCallback,
_In_ std::shared_ptr<Recorder> recorder):
m_contextConfig(contextConfig),
m_redisCommunicationMode(SAI_REDIS_COMMUNICATION_MODE_REDIS_ASYNC),
m_recorder(recorder),
m_notificationCallback(notificationCallback)
{
SWSS_LOG_ENTER();
SWSS_LOG_NOTICE("sairedis git revision %s, SAI git revision: %s", SAIREDIS_GIT_REVISION, SAI_GIT_REVISION);
m_initialized = false;
apiInitialize(0, nullptr);
}
RedisRemoteSaiInterface::~RedisRemoteSaiInterface()
{
SWSS_LOG_ENTER();
if (m_initialized)
{
apiUninitialize();
}
}
sai_status_t RedisRemoteSaiInterface::apiInitialize(
_In_ uint64_t flags,
_In_ const sai_service_method_table_t *service_method_table)
{
SWSS_LOG_ENTER();
if (m_initialized)
{
SWSS_LOG_ERROR("already initialized");
return SAI_STATUS_FAILURE;
}
m_skipRecordAttrContainer = std::make_shared<SkipRecordAttrContainer>();
m_asicInitViewMode = false; // default mode is apply mode
m_useTempView = false;
m_syncMode = false;
m_redisCommunicationMode = SAI_REDIS_COMMUNICATION_MODE_REDIS_ASYNC;
if (m_contextConfig->m_zmqEnable)
{
m_communicationChannel = std::make_shared<ZeroMQChannel>(
m_contextConfig->m_zmqEndpoint,
m_contextConfig->m_zmqNtfEndpoint,
std::bind(&RedisRemoteSaiInterface::handleNotification, this, _1, _2, _3));
SWSS_LOG_NOTICE("zmq enabled, forcing sync mode");
m_syncMode = true;
}
else
{
m_communicationChannel = std::make_shared<RedisChannel>(
m_contextConfig->m_dbAsic,
std::bind(&RedisRemoteSaiInterface::handleNotification, this, _1, _2, _3));
}
m_responseTimeoutMs = m_communicationChannel->getResponseTimeout();
m_db = std::make_shared<swss::DBConnector>(m_contextConfig->m_dbAsic, 0);
m_redisVidIndexGenerator = std::make_shared<RedisVidIndexGenerator>(m_db, REDIS_KEY_VIDCOUNTER);
clear_local_state();
// TODO what will happen when we receive notification in init view mode ?
m_initialized = true;
return SAI_STATUS_SUCCESS;
}
sai_status_t RedisRemoteSaiInterface::apiUninitialize(void)
{
SWSS_LOG_ENTER();
SWSS_LOG_NOTICE("begin");
if (!m_initialized)
{
SWSS_LOG_ERROR("not initialized");
return SAI_STATUS_FAILURE;
}
m_communicationChannel = nullptr; // will stop thread
// clear local state after stopping threads
clear_local_state();
m_initialized = false;
SWSS_LOG_NOTICE("end");
return SAI_STATUS_SUCCESS;
}
sai_status_t RedisRemoteSaiInterface::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)
{
SWSS_LOG_ENTER();
*objectId = SAI_NULL_OBJECT_ID;
if (objectType == SAI_OBJECT_TYPE_SWITCH)
{
// for given hardware info we always return same switch id,
// this is required since we could be performing warm boot here
auto hwinfo = Globals::getHardwareInfo(attr_count, attr_list);
if (hwinfo.size())
{
m_recorder->recordComment("SAI_SWITCH_ATTR_SWITCH_HARDWARE_INFO=" + hwinfo);
}
switchId = m_virtualObjectIdManager->allocateNewSwitchObjectId(hwinfo);
*objectId = switchId;
if (switchId == SAI_NULL_OBJECT_ID)
{
SWSS_LOG_ERROR("switch ID allocation failed");
return SAI_STATUS_FAILURE;
}
auto *attr = sai_metadata_get_attr_by_id(
SAI_SWITCH_ATTR_INIT_SWITCH,
attr_count,
attr_list);
if (attr && attr->value.booldata == false)
{
if (m_switchContainer->contains(*objectId))
{
SWSS_LOG_NOTICE("switch container already contains switch %s",
sai_serialize_object_id(*objectId).c_str());
return SAI_STATUS_SUCCESS;
}
refreshTableDump();
if (m_tableDump.find(switchId) == m_tableDump.end())
{
SWSS_LOG_ERROR("failed to find switch %s to connect (init=false)",
sai_serialize_object_id(switchId).c_str());
m_virtualObjectIdManager->releaseObjectId(switchId);
return SAI_STATUS_FAILURE;
}
// when init is false, don't send query to syncd, just return success
// that we found switch and we connected to it
auto sw = std::make_shared<Switch>(*objectId, attr_count, attr_list);
m_switchContainer->insert(sw);
return SAI_STATUS_SUCCESS;
}
}
else
{
*objectId = m_virtualObjectIdManager->allocateNewObjectId(objectType, switchId);
}
if (*objectId == SAI_NULL_OBJECT_ID)
{
SWSS_LOG_ERROR("failed to create %s, with switch id: %s",
sai_serialize_object_type(objectType).c_str(),
sai_serialize_object_id(switchId).c_str());
return SAI_STATUS_INSUFFICIENT_RESOURCES;
}
// NOTE: objectId was allocated by the caller
auto status = create(
objectType,
sai_serialize_object_id(*objectId),
attr_count,
attr_list);
if (objectType == SAI_OBJECT_TYPE_SWITCH && status == SAI_STATUS_SUCCESS)
{
/*
* When doing CREATE operation user may want to update notification
* pointers, since notifications can be defined per switch we need to
* update them.
*
* TODO: should be moved inside to redis_generic_create
*/
auto sw = std::make_shared<Switch>(*objectId, attr_count, attr_list);
m_switchContainer->insert(sw);
}
else if (status != SAI_STATUS_SUCCESS)
{
// if create failed, then release allocated object
m_virtualObjectIdManager->releaseObjectId(*objectId);
}
return status;
}
sai_status_t RedisRemoteSaiInterface::remove(
_In_ sai_object_type_t objectType,
_In_ sai_object_id_t objectId)
{
SWSS_LOG_ENTER();
auto status = remove(
objectType,
sai_serialize_object_id(objectId));
if (objectType == SAI_OBJECT_TYPE_SWITCH && status == SAI_STATUS_SUCCESS)
{
SWSS_LOG_NOTICE("removing switch id %s", sai_serialize_object_id(objectId).c_str());
m_virtualObjectIdManager->releaseObjectId(objectId);
// remove switch from container
m_switchContainer->removeSwitch(objectId);
}
return status;
}
sai_status_t RedisRemoteSaiInterface::setRedisExtensionAttribute(
_In_ sai_object_type_t objectType,
_In_ sai_object_id_t objectId,
_In_ const sai_attribute_t *attr)
{
SWSS_LOG_ENTER();
if (attr == nullptr)
{
SWSS_LOG_ERROR("attr pointer is null");
return SAI_STATUS_FAILURE;
}
/*
* NOTE: that this will work without
* switch being created.
*/
switch (attr->id)
{
case SAI_REDIS_SWITCH_ATTR_PERFORM_LOG_ROTATE:
if (m_recorder)
{
m_recorder->requestLogRotate();
}
return SAI_STATUS_SUCCESS;
case SAI_REDIS_SWITCH_ATTR_RECORD:
if (m_recorder)
{
m_recorder->enableRecording(attr->value.booldata);
}
return SAI_STATUS_SUCCESS;
case SAI_REDIS_SWITCH_ATTR_NOTIFY_SYNCD:
return sai_redis_notify_syncd(objectId, attr);
case SAI_REDIS_SWITCH_ATTR_USE_TEMP_VIEW:
m_useTempView = attr->value.booldata;
return SAI_STATUS_SUCCESS;
case SAI_REDIS_SWITCH_ATTR_RECORD_STATS:
m_recorder->recordStats(attr->value.booldata);
return SAI_STATUS_SUCCESS;
case SAI_REDIS_SWITCH_ATTR_SYNC_OPERATION_RESPONSE_TIMEOUT:
m_responseTimeoutMs = attr->value.u64;
m_communicationChannel->setResponseTimeout(m_responseTimeoutMs);
SWSS_LOG_NOTICE("set response timeout to %lu ms", m_responseTimeoutMs);
return SAI_STATUS_SUCCESS;
case SAI_REDIS_SWITCH_ATTR_SYNC_MODE:
SWSS_LOG_WARN("sync mode is depreacated, use communication mode");
m_syncMode = attr->value.booldata;
if (m_contextConfig->m_zmqEnable)
{
SWSS_LOG_NOTICE("zmq enabled, forcing sync mode");
m_syncMode = true;
}
if (m_syncMode)
{
SWSS_LOG_NOTICE("disabling buffered pipeline in sync mode");
m_communicationChannel->setBuffered(false);
}
return SAI_STATUS_SUCCESS;
case SAI_REDIS_SWITCH_ATTR_REDIS_COMMUNICATION_MODE:
m_redisCommunicationMode = (sai_redis_communication_mode_t)attr->value.s32;
if (m_contextConfig->m_zmqEnable)
{
SWSS_LOG_NOTICE("zmq enabled via context config");
m_redisCommunicationMode = SAI_REDIS_COMMUNICATION_MODE_ZMQ_SYNC;
}
m_communicationChannel = nullptr;
switch (m_redisCommunicationMode)
{
case SAI_REDIS_COMMUNICATION_MODE_REDIS_ASYNC:
SWSS_LOG_NOTICE("enabling redis async mode");
m_syncMode = false;
m_communicationChannel = std::make_shared<RedisChannel>(
m_contextConfig->m_dbAsic,
std::bind(&RedisRemoteSaiInterface::handleNotification, this, _1, _2, _3));
m_communicationChannel->setResponseTimeout(m_responseTimeoutMs);
m_communicationChannel->setBuffered(true);
return SAI_STATUS_SUCCESS;
case SAI_REDIS_COMMUNICATION_MODE_REDIS_SYNC:
SWSS_LOG_NOTICE("enabling redis sync mode");
m_syncMode = true;
m_communicationChannel = std::make_shared<RedisChannel>(
m_contextConfig->m_dbAsic,
std::bind(&RedisRemoteSaiInterface::handleNotification, this, _1, _2, _3));
m_communicationChannel->setResponseTimeout(m_responseTimeoutMs);
m_communicationChannel->setBuffered(false);
return SAI_STATUS_SUCCESS;
case SAI_REDIS_COMMUNICATION_MODE_ZMQ_SYNC:
m_contextConfig->m_zmqEnable = true;
// main communication channel was created at initialize method
// so this command will replace it with zmq channel
m_communicationChannel = std::make_shared<ZeroMQChannel>(
m_contextConfig->m_zmqEndpoint,
m_contextConfig->m_zmqNtfEndpoint,
std::bind(&RedisRemoteSaiInterface::handleNotification, this, _1, _2, _3));
m_communicationChannel->setResponseTimeout(m_responseTimeoutMs);
SWSS_LOG_NOTICE("zmq enabled, forcing sync mode");
m_syncMode = true;
SWSS_LOG_NOTICE("disabling buffered pipeline in sync mode");
m_communicationChannel->setBuffered(false);
return SAI_STATUS_SUCCESS;
default:
SWSS_LOG_ERROR("invalid communication mode value: %d", m_redisCommunicationMode);
return SAI_STATUS_NOT_SUPPORTED;
}
case SAI_REDIS_SWITCH_ATTR_USE_PIPELINE:
if (m_syncMode)
{
SWSS_LOG_WARN("use pipeline is not supported in sync mode");
return SAI_STATUS_NOT_SUPPORTED;
}
m_communicationChannel->setBuffered(attr->value.booldata);
return SAI_STATUS_SUCCESS;
case SAI_REDIS_SWITCH_ATTR_FLUSH:
m_communicationChannel->flush();
return SAI_STATUS_SUCCESS;
case SAI_REDIS_SWITCH_ATTR_RECORDING_OUTPUT_DIR:
if (m_recorder)
{
m_recorder->setRecordingOutputDirectory(*attr);
}
return SAI_STATUS_SUCCESS;
case SAI_REDIS_SWITCH_ATTR_RECORDING_FILENAME:
if (m_recorder)
{
m_recorder->setRecordingFilename(*attr);
}
return SAI_STATUS_SUCCESS;
case SAI_REDIS_SWITCH_ATTR_FLEX_COUNTER_GROUP:
return notifyCounterGroupOperations(objectId,
reinterpret_cast<sai_redis_flex_counter_group_parameter_t*>(attr->value.ptr));
case SAI_REDIS_SWITCH_ATTR_FLEX_COUNTER:
return notifyCounterOperations(objectId,
reinterpret_cast<sai_redis_flex_counter_parameter_t*>(attr->value.ptr));
default:
break;
}
SWSS_LOG_ERROR("unknown redis extension attribute: %d", attr->id);
return SAI_STATUS_FAILURE;
}
bool RedisRemoteSaiInterface::isSaiS8ListValidString(
_In_ const sai_s8_list_t &s8list)
{
SWSS_LOG_ENTER();
if (s8list.list != nullptr && s8list.count > 0)
{
size_t len = strnlen((const char *)s8list.list, s8list.count);
if (len == (size_t)s8list.count)
{
return true;
}
else
{
SWSS_LOG_ERROR("Count (%u) is different than strnlen (%zu)", s8list.count, len);
}
}
return false;
}
bool RedisRemoteSaiInterface::emplaceStrings(
_In_ const sai_s8_list_t &field,
_In_ const sai_s8_list_t &value,
_Out_ std::vector<swss::FieldValueTuple> &entries)
{
SWSS_LOG_ENTER();
bool result = false;
if (isSaiS8ListValidString(field) && isSaiS8ListValidString(value))
{
entries.emplace_back(std::string((const char*)field.list, field.count), std::string((const char*)value.list, value.count));
result = true;
}
return result;
}
bool RedisRemoteSaiInterface::emplaceStrings(
_In_ const char *field,
_In_ const sai_s8_list_t &value,
_Out_ std::vector<swss::FieldValueTuple> &entries)
{
SWSS_LOG_ENTER();
bool result = false;
if (isSaiS8ListValidString(value))
{
entries.emplace_back(field, std::string((const char*)value.list, value.count));
result = true;
}
return result;
}
sai_status_t RedisRemoteSaiInterface::notifyCounterGroupOperations(
_In_ sai_object_id_t objectId,
_In_ const sai_redis_flex_counter_group_parameter_t *flexCounterGroupParam)
{
SWSS_LOG_ENTER();
std::vector<swss::FieldValueTuple> entries;
if (flexCounterGroupParam == nullptr || !isSaiS8ListValidString(flexCounterGroupParam->counter_group_name))
{
SWSS_LOG_ERROR("Invalid parameters when handling counter group operation");
return SAI_STATUS_FAILURE;
}
std::string key((const char*)flexCounterGroupParam->counter_group_name.list, flexCounterGroupParam->counter_group_name.count);
emplaceStrings(POLL_INTERVAL_FIELD, flexCounterGroupParam->poll_interval, entries);
emplaceStrings(BULK_CHUNK_SIZE_FIELD, flexCounterGroupParam->bulk_chunk_size, entries);
emplaceStrings(BULK_CHUNK_SIZE_PER_PREFIX_FIELD, flexCounterGroupParam->bulk_chunk_size_per_prefix, entries);
emplaceStrings(STATS_MODE_FIELD, flexCounterGroupParam->stats_mode, entries);
emplaceStrings(flexCounterGroupParam->plugin_name, flexCounterGroupParam->plugins, entries);
emplaceStrings(FLEX_COUNTER_STATUS_FIELD, flexCounterGroupParam->operation, entries);
m_recorder->recordGenericCounterPolling(key, entries);
m_communicationChannel->set(key,
entries,
(entries.size() != 0) ? REDIS_FLEX_COUNTER_COMMAND_SET_GROUP : REDIS_FLEX_COUNTER_COMMAND_DEL_GROUP);
return waitForResponse(SAI_COMMON_API_SET);
}
sai_status_t RedisRemoteSaiInterface::notifyCounterOperations(
_In_ sai_object_id_t objectId,
_In_ const sai_redis_flex_counter_parameter_t *flexCounterParam)
{
SWSS_LOG_ENTER();
if (flexCounterParam == nullptr || !isSaiS8ListValidString(flexCounterParam->counter_key))
{
SWSS_LOG_ERROR("Invalid parameters when handling counter operation");
return SAI_STATUS_FAILURE;
}
std::vector<swss::FieldValueTuple> entries;
std::string key((const char*)flexCounterParam->counter_key.list, flexCounterParam->counter_key.count);
std::string command;
if (emplaceStrings(flexCounterParam->counter_field_name, flexCounterParam->counter_ids, entries))
{
command = REDIS_FLEX_COUNTER_COMMAND_START_POLL;
emplaceStrings(STATS_MODE_FIELD, flexCounterParam->stats_mode, entries);
}
else
{
command = REDIS_FLEX_COUNTER_COMMAND_STOP_POLL;
}
m_recorder->recordGenericCounterPolling(key, entries);
m_communicationChannel->set(key, entries, command);
return waitForResponse(SAI_COMMON_API_SET);
}
sai_status_t RedisRemoteSaiInterface::set(
_In_ sai_object_type_t objectType,
_In_ sai_object_id_t objectId,
_In_ const sai_attribute_t *attr)
{
SWSS_LOG_ENTER();
if (RedisRemoteSaiInterface::isRedisAttribute(objectType, attr))
{
return setRedisExtensionAttribute(objectType, objectId, attr);
}
auto status = set(
objectType,
sai_serialize_object_id(objectId),
attr);
if (objectType == SAI_OBJECT_TYPE_SWITCH && status == SAI_STATUS_SUCCESS)
{
auto sw = m_switchContainer->getSwitch(objectId);
if (!sw)
{
SWSS_LOG_THROW("failed to find switch %s in container",
sai_serialize_object_id(objectId).c_str());
}
/*
* When doing SET operation user may want to update notification
* pointers.
*/
sw->updateNotifications(1, attr);
}
return status;
}
sai_status_t RedisRemoteSaiInterface::get(
_In_ sai_object_type_t objectType,
_In_ sai_object_id_t objectId,
_In_ uint32_t attr_count,
_Inout_ sai_attribute_t *attr_list)
{
SWSS_LOG_ENTER();
return get(
objectType,
sai_serialize_object_id(objectId),
attr_count,
attr_list);
}
#define DECLARE_REMOVE_ENTRY(OT,ot) \
sai_status_t RedisRemoteSaiInterface::remove( \
_In_ const sai_ ## ot ## _t* ot) \
{ \
SWSS_LOG_ENTER(); \
return remove( \
(sai_object_type_t)SAI_OBJECT_TYPE_ ## OT, \
sai_serialize_ ## ot(*ot)); \
}
SAIREDIS_DECLARE_EVERY_ENTRY(DECLARE_REMOVE_ENTRY);
#define DECLARE_CREATE_ENTRY(OT,ot) \
sai_status_t RedisRemoteSaiInterface::create( \
_In_ const sai_ ## ot ## _t* ot, \
_In_ uint32_t attr_count, \
_In_ const sai_attribute_t *attr_list) \
{ \
SWSS_LOG_ENTER(); \
return create( \
(sai_object_type_t)SAI_OBJECT_TYPE_ ## OT, \
sai_serialize_ ## ot(*ot), \
attr_count, \
attr_list); \
}
SAIREDIS_DECLARE_EVERY_ENTRY(DECLARE_CREATE_ENTRY);
#define DECLARE_SET_ENTRY(OT,ot) \
sai_status_t RedisRemoteSaiInterface::set( \
_In_ const sai_ ## ot ## _t* ot, \
_In_ const sai_attribute_t *attr) \
{ \
SWSS_LOG_ENTER(); \
return set( \
(sai_object_type_t)SAI_OBJECT_TYPE_ ## OT, \
sai_serialize_ ## ot(*ot), \
attr); \
}
SAIREDIS_DECLARE_EVERY_ENTRY(DECLARE_SET_ENTRY);
#define DECLARE_BULK_CREATE_ENTRY(OT,ot) \
sai_status_t RedisRemoteSaiInterface::bulkCreate( \
_In_ uint32_t object_count, \
_In_ const sai_ ## ot ## _t *ot, \
_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) \
{ \
SWSS_LOG_ENTER(); \
static PerformanceIntervalTimer timer("RedisRemoteSaiInterface::bulkCreate(" #ot ")"); \
timer.start(); \
std::vector<std::string> serialized_object_ids; \
for (uint32_t idx = 0; idx < object_count; idx++) \
{ \
std::string str_object_id = sai_serialize_ ##ot (ot[idx]); \
serialized_object_ids.push_back(str_object_id); \
} \
auto status = bulkCreate( \
(sai_object_type_t)SAI_OBJECT_TYPE_ ## OT, \
serialized_object_ids, \
attr_count, \
attr_list, \
mode, \
object_statuses); \
timer.stop(); \
timer.inc(object_count); \
return status; \
}
SAIREDIS_DECLARE_EVERY_BULK_ENTRY(DECLARE_BULK_CREATE_ENTRY);
#define DECLARE_BULK_REMOVE_ENTRY(OT,ot) \
sai_status_t RedisRemoteSaiInterface::bulkRemove( \
_In_ uint32_t object_count, \
_In_ const sai_ ## ot ## _t *ot, \
_In_ sai_bulk_op_error_mode_t mode, \
_Out_ sai_status_t *object_statuses) \
{ \
SWSS_LOG_ENTER(); \
std::vector<std::string> serializedObjectIds; \
for (uint32_t idx = 0; idx < object_count; idx++) \
{ \
serializedObjectIds.emplace_back(sai_serialize_ ##ot (ot[idx])); \
} \
return bulkRemove((sai_object_type_t)SAI_OBJECT_TYPE_ ## OT, serializedObjectIds, mode, object_statuses); \
}
SAIREDIS_DECLARE_EVERY_BULK_ENTRY(DECLARE_BULK_REMOVE_ENTRY);
#define DECLARE_BULK_SET_ENTRY(OT,ot) \
sai_status_t RedisRemoteSaiInterface::bulkSet( \
_In_ uint32_t object_count, \
_In_ const sai_ ## ot ## _t *ot, \
_In_ const sai_attribute_t *attr_list, \
_In_ sai_bulk_op_error_mode_t mode, \
_Out_ sai_status_t *object_statuses) \
{ \
SWSS_LOG_ENTER(); \
std::vector<std::string> serializedObjectIds; \
for (uint32_t idx = 0; idx < object_count; idx++) \
{ \
serializedObjectIds.emplace_back(sai_serialize_ ##ot (ot[idx])); \
} \
return bulkSet((sai_object_type_t)SAI_OBJECT_TYPE_ ## OT, serializedObjectIds, 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 RedisRemoteSaiInterface::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) \
{ \
SWSS_LOG_ENTER(); \
SWSS_LOG_ERROR("FIXME not implemented"); \
return SAI_STATUS_NOT_IMPLEMENTED; \
}
SAIREDIS_DECLARE_EVERY_BULK_ENTRY(DECLARE_BULK_GET_ENTRY);
sai_status_t RedisRemoteSaiInterface::create(
_In_ sai_object_type_t object_type,
_In_ const std::string& serializedObjectId,
_In_ uint32_t attr_count,
_In_ const sai_attribute_t *attr_list)
{
SWSS_LOG_ENTER();
auto entry = SaiAttributeList::serialize_attr_list(
object_type,
attr_count,
attr_list,
false);
if (entry.empty())
{
// make sure that we put object into db
// even if there are no attributes set
swss::FieldValueTuple null("NULL", "NULL");
entry.push_back(null);
}
auto serializedObjectType = sai_serialize_object_type(object_type);
const std::string key = serializedObjectType + ":" + serializedObjectId;
SWSS_LOG_DEBUG("generic create key: %s, fields: %" PRIu64, key.c_str(), entry.size());
m_recorder->recordGenericCreate(key, entry);
m_communicationChannel->set(key, entry, REDIS_ASIC_STATE_COMMAND_CREATE);
auto status = waitForResponse(SAI_COMMON_API_CREATE);
m_recorder->recordGenericCreateResponse(status);
return status;
}
sai_status_t RedisRemoteSaiInterface::remove(
_In_ sai_object_type_t objectType,
_In_ const std::string& serializedObjectId)
{
SWSS_LOG_ENTER();
auto serializedObjectType = sai_serialize_object_type(objectType);
const std::string key = serializedObjectType + ":" + serializedObjectId;
SWSS_LOG_DEBUG("generic remove key: %s", key.c_str());
m_recorder->recordGenericRemove(key);
m_communicationChannel->del(key, REDIS_ASIC_STATE_COMMAND_REMOVE);
auto status = waitForResponse(SAI_COMMON_API_REMOVE);
m_recorder->recordGenericRemoveResponse(status);
return status;
}
sai_status_t RedisRemoteSaiInterface::set(
_In_ sai_object_type_t objectType,
_In_ const std::string &serializedObjectId,
_In_ const sai_attribute_t *attr)
{
SWSS_LOG_ENTER();
auto entry = SaiAttributeList::serialize_attr_list(
objectType,
1,
attr,
false);
auto serializedObjectType = sai_serialize_object_type(objectType);
std::string key = serializedObjectType + ":" + serializedObjectId;
SWSS_LOG_DEBUG("generic set key: %s, fields: %lu", key.c_str(), entry.size());
m_recorder->recordGenericSet(key, entry);
m_communicationChannel->set(key, entry, REDIS_ASIC_STATE_COMMAND_SET);
auto status = waitForResponse(SAI_COMMON_API_SET);
m_recorder->recordGenericSetResponse(status);
return status;
}
sai_status_t RedisRemoteSaiInterface::waitForResponse(
_In_ sai_common_api_t api)
{
SWSS_LOG_ENTER();
if (m_syncMode)
{
swss::KeyOpFieldsValuesTuple kco;
auto status = m_communicationChannel->wait(REDIS_ASIC_STATE_COMMAND_GETRESPONSE, kco);
m_recorder->recordGenericResponse(status);
return status;
}
/*
* By default sync mode is disabled and all create/set/remove are
* considered success operations.
*/
return SAI_STATUS_SUCCESS;
}
sai_status_t RedisRemoteSaiInterface::waitForGetResponse(
_In_ sai_object_type_t objectType,
_In_ uint32_t attr_count,
_Inout_ sai_attribute_t *attr_list)
{
SWSS_LOG_ENTER();
swss::KeyOpFieldsValuesTuple kco;
auto status = m_communicationChannel->wait(REDIS_ASIC_STATE_COMMAND_GETRESPONSE, kco);
auto &values = kfvFieldsValues(kco);
if (status == SAI_STATUS_SUCCESS)
{
if (values.size() == 0)
{
SWSS_LOG_THROW("logic error, get response returned 0 values!, send api response or sync/async issue?");
}
SaiAttributeList list(objectType, values, false);
transfer_attributes(objectType, attr_count, list.get_attr_list(), attr_list, false);
}
else if (status == SAI_STATUS_BUFFER_OVERFLOW)
{
if (values.size() == 0)
{
SWSS_LOG_THROW("logic error, get response returned 0 values!, send api response or sync/async issue?");
}
SaiAttributeList list(objectType, values, true);
// no need for id fix since this is overflow
transfer_attributes(objectType, attr_count, list.get_attr_list(), attr_list, true);
}
return status;
}
sai_status_t RedisRemoteSaiInterface::get(
_In_ sai_object_type_t objectType,
_In_ const std::string& serializedObjectId,
_In_ uint32_t attr_count,
_Inout_ sai_attribute_t *attr_list)
{
SWSS_LOG_ENTER();
/*
* Since user may reuse buffers, then oid list buffers maybe not cleared
* and contain some garbage, let's clean them so we send all oids as null to
* syncd.
*/
Utils::clearOidValues(objectType, attr_count, attr_list);
auto entry = SaiAttributeList::serialize_attr_list(objectType, attr_count, attr_list, false);
std::string serializedObjectType = sai_serialize_object_type(objectType);
std::string key = serializedObjectType + ":" + serializedObjectId;
SWSS_LOG_DEBUG("generic get key: %s, fields: %lu", key.c_str(), entry.size());
bool record = !m_skipRecordAttrContainer->canSkipRecording(objectType, attr_count, attr_list);
if (record)
{
m_recorder->recordGenericGet(key, entry);
}
// get is special, it will not put data
// into asic view, only to message queue
m_communicationChannel->set(key, entry, REDIS_ASIC_STATE_COMMAND_GET);
auto status = waitForGetResponse(objectType, attr_count, attr_list);
if (record)
{
m_recorder->recordGenericGetResponse(status, objectType, attr_count, attr_list);
}
return status;
}
#define DECLARE_GET_ENTRY(OT,ot) \
sai_status_t RedisRemoteSaiInterface::get( \
_In_ const sai_ ## ot ## _t* ot, \
_In_ uint32_t attr_count, \
_Inout_ sai_attribute_t *attr_list) \
{ \
SWSS_LOG_ENTER(); \
return get( \
(sai_object_type_t)SAI_OBJECT_TYPE_ ## OT, \
sai_serialize_ ## ot(*ot), \
attr_count, \
attr_list); \
}
SAIREDIS_DECLARE_EVERY_ENTRY(DECLARE_GET_ENTRY);
sai_status_t RedisRemoteSaiInterface::waitForFlushFdbEntriesResponse()
{
SWSS_LOG_ENTER();
swss::KeyOpFieldsValuesTuple kco;
auto status = m_communicationChannel->wait(REDIS_ASIC_STATE_COMMAND_FLUSHRESPONSE, kco);
return status;
}
sai_status_t RedisRemoteSaiInterface::flushFdbEntries(
_In_ sai_object_id_t switchId,
_In_ uint32_t attrCount,
_In_ const sai_attribute_t *attrList)
{
SWSS_LOG_ENTER();
auto entry = SaiAttributeList::serialize_attr_list(
SAI_OBJECT_TYPE_FDB_FLUSH,
attrCount,
attrList,
false);
std::string serializedObjectId = sai_serialize_object_type(SAI_OBJECT_TYPE_FDB_FLUSH);
// NOTE ! we actually give switch ID since FLUSH is not real object
std::string key = serializedObjectId + ":" + sai_serialize_object_id(switchId);
SWSS_LOG_NOTICE("flush key: %s, fields: %lu", key.c_str(), entry.size());
m_recorder->recordFlushFdbEntries(switchId, attrCount, attrList);
// TODO m_recorder->recordFlushFdbEntries(key, entry)
m_communicationChannel->set(key, entry, REDIS_ASIC_STATE_COMMAND_FLUSH);
auto status = waitForFlushFdbEntriesResponse();
m_recorder->recordFlushFdbEntriesResponse(status);
return status;
}
sai_status_t RedisRemoteSaiInterface::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)
{
SWSS_LOG_ENTER();
auto strSwitchId = sai_serialize_object_id(switchId);
auto entry = SaiAttributeList::serialize_attr_list(objectType, attrCount, attrList, false);
entry.push_back(swss::FieldValueTuple("OBJECT_TYPE", sai_serialize_object_type(objectType)));
SWSS_LOG_DEBUG(
"Query arguments: switch: %s, attributes: %s",
strSwitchId.c_str(),
Globals::joinFieldValues(entry).c_str());
// Syncd will pop this argument off before trying to deserialize the attribute list
m_recorder->recordObjectTypeGetAvailability(switchId, objectType, attrCount, attrList);
// recordObjectTypeGetAvailability(strSwitchId, entry);
// This query will not put any data into the ASIC view, just into the
// message queue
m_communicationChannel->set(strSwitchId, entry, REDIS_ASIC_STATE_COMMAND_OBJECT_TYPE_GET_AVAILABILITY_QUERY);
auto status = waitForObjectTypeGetAvailabilityResponse(count);
m_recorder->recordObjectTypeGetAvailabilityResponse(status, count);
return status;
}
sai_status_t RedisRemoteSaiInterface::waitForObjectTypeGetAvailabilityResponse(
_Inout_ uint64_t *count)
{
SWSS_LOG_ENTER();
swss::KeyOpFieldsValuesTuple kco;
auto status = m_communicationChannel->wait(REDIS_ASIC_STATE_COMMAND_OBJECT_TYPE_GET_AVAILABILITY_RESPONSE, kco);
if (status == SAI_STATUS_SUCCESS)
{
auto &values = kfvFieldsValues(kco);
if (values.size() != 1)
{
SWSS_LOG_THROW("Invalid response from syncd: expected 1 value, received %zu", values.size());
}
const std::string &availability_str = fvValue(values[0]);
*count = std::stoull(availability_str);
SWSS_LOG_DEBUG("Received payload: count = %lu", *count);
}
return status;
}
sai_status_t RedisRemoteSaiInterface::queryAttributeCapability(
_In_ sai_object_id_t switchId,
_In_ sai_object_type_t objectType,
_In_ sai_attr_id_t attrId,
_Out_ sai_attr_capability_t *capability)
{
SWSS_LOG_ENTER();
auto switchIdStr = sai_serialize_object_id(switchId);
auto objectTypeStr = sai_serialize_object_type(objectType);
auto meta = sai_metadata_get_attr_metadata(objectType, attrId);
if (meta == NULL)
{
SWSS_LOG_ERROR("Failed to find attribute metadata: object type %s, attr id %d", objectTypeStr.c_str(), attrId);
return SAI_STATUS_INVALID_PARAMETER;
}
const std::string attrIdStr = meta->attridname;
const std::vector<swss::FieldValueTuple> entry =
{
swss::FieldValueTuple("OBJECT_TYPE", objectTypeStr),
swss::FieldValueTuple("ATTR_ID", attrIdStr)
};
SWSS_LOG_DEBUG(
"Query arguments: switch %s, object type: %s, attribute: %s",
switchIdStr.c_str(),
objectTypeStr.c_str(),
attrIdStr.c_str()
);
// This query will not put any data into the ASIC view, just into the
// message queue
m_recorder->recordQueryAttributeCapability(switchId, objectType, attrId, capability);
m_communicationChannel->set(switchIdStr, entry, REDIS_ASIC_STATE_COMMAND_ATTR_CAPABILITY_QUERY);
auto status = waitForQueryAttributeCapabilityResponse(capability);
m_recorder->recordQueryAttributeCapabilityResponse(status, objectType, attrId, capability);
return status;
}
sai_status_t RedisRemoteSaiInterface::waitForQueryAttributeCapabilityResponse(
_Out_ sai_attr_capability_t* capability)
{
SWSS_LOG_ENTER();
swss::KeyOpFieldsValuesTuple kco;
auto status = m_communicationChannel->wait(REDIS_ASIC_STATE_COMMAND_ATTR_CAPABILITY_RESPONSE, kco);
if (status == SAI_STATUS_SUCCESS)
{
const std::vector<swss::FieldValueTuple> &values = kfvFieldsValues(kco);
if (values.size() != 3)
{
SWSS_LOG_ERROR("Invalid response from syncd: expected 3 values, received %zu", values.size());
return SAI_STATUS_FAILURE;
}
capability->create_implemented = (fvValue(values[0]) == "true" ? true : false);
capability->set_implemented = (fvValue(values[1]) == "true" ? true : false);
capability->get_implemented = (fvValue(values[2]) == "true" ? true : false);
SWSS_LOG_DEBUG("Received payload: create_implemented:%s, set_implemented:%s, get_implemented:%s",
(capability->create_implemented? "true":"false"), (capability->set_implemented? "true":"false"), (capability->get_implemented? "true":"false"));
}
return status;
}
sai_status_t RedisRemoteSaiInterface::queryAttributeEnumValuesCapability(
_In_ sai_object_id_t switchId,
_In_ sai_object_type_t objectType,
_In_ sai_attr_id_t attrId,
_Inout_ sai_s32_list_t *enumValuesCapability)
{
SWSS_LOG_ENTER();
if (enumValuesCapability && enumValuesCapability->list)
{
// clear input list, since we use serialize to transfer values
for (uint32_t idx = 0; idx < enumValuesCapability->count; idx++)
enumValuesCapability->list[idx] = 0;
}
auto switch_id_str = sai_serialize_object_id(switchId);
auto object_type_str = sai_serialize_object_type(objectType);
auto meta = sai_metadata_get_attr_metadata(objectType, attrId);
if (meta == NULL)
{
SWSS_LOG_ERROR("Failed to find attribute metadata: object type %s, attr id %d", object_type_str.c_str(), attrId);
return SAI_STATUS_INVALID_PARAMETER;
}
const std::string attr_id_str = meta->attridname;
const std::string list_size = std::to_string(enumValuesCapability->count);
const std::vector<swss::FieldValueTuple> entry =
{
swss::FieldValueTuple("OBJECT_TYPE", object_type_str),
swss::FieldValueTuple("ATTR_ID", attr_id_str),
swss::FieldValueTuple("LIST_SIZE", list_size)
};
SWSS_LOG_DEBUG(
"Query arguments: switch %s, object type: %s, attribute: %s, count: %s",
switch_id_str.c_str(),
object_type_str.c_str(),
attr_id_str.c_str(),
list_size.c_str()
);
// This query will not put any data into the ASIC view, just into the
// message queue
m_recorder->recordQueryAttributeEnumValuesCapability(switchId, objectType, attrId, enumValuesCapability);
m_communicationChannel->set(switch_id_str, entry, REDIS_ASIC_STATE_COMMAND_ATTR_ENUM_VALUES_CAPABILITY_QUERY);
auto status = waitForQueryAttributeEnumValuesCapabilityResponse(enumValuesCapability);
m_recorder->recordQueryAttributeEnumValuesCapabilityResponse(status, objectType, attrId, enumValuesCapability);
return status;
}
sai_status_t RedisRemoteSaiInterface::waitForQueryAttributeEnumValuesCapabilityResponse(
_Inout_ sai_s32_list_t* enumValuesCapability)
{
SWSS_LOG_ENTER();
swss::KeyOpFieldsValuesTuple kco;
auto status = m_communicationChannel->wait(REDIS_ASIC_STATE_COMMAND_ATTR_ENUM_VALUES_CAPABILITY_RESPONSE, kco);
if (status == SAI_STATUS_SUCCESS)
{
const std::vector<swss::FieldValueTuple> &values = kfvFieldsValues(kco);
if (values.size() != 2)
{
SWSS_LOG_ERROR("Invalid response from syncd: expected 2 values, received %zu", values.size());
return SAI_STATUS_FAILURE;
}
const std::string &capability_str = fvValue(values[0]);
const uint32_t num_capabilities = std::stoi(fvValue(values[1]));
SWSS_LOG_DEBUG("Received payload: capabilities = '%s', count = %d", capability_str.c_str(), num_capabilities);
enumValuesCapability->count = num_capabilities;
size_t position = 0;
for (uint32_t i = 0; i < num_capabilities; i++)
{
size_t old_position = position;
position = capability_str.find(",", old_position);
std::string capability = capability_str.substr(old_position, position - old_position);
enumValuesCapability->list[i] = std::stoi(capability);
// We have run out of values to add to our list
if (position == std::string::npos)
{
if (num_capabilities != i + 1)
{
SWSS_LOG_WARN("Query returned less attributes than expected: expected %d, received %d", num_capabilities, i+1);
}
break;
}
// Skip the commas
position++;
}
}
else if (status == SAI_STATUS_BUFFER_OVERFLOW)
{
const std::vector<swss::FieldValueTuple> &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);
enumValuesCapability->count = num_capabilities;
}
return status;
}
sai_status_t RedisRemoteSaiInterface::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)
{
SWSS_LOG_ENTER();
auto stats_enum = sai_metadata_get_object_type_info(object_type)->statenum;
auto entry = serialize_counter_id_list(stats_enum, number_of_counters, counter_ids);
std::string str_object_type = sai_serialize_object_type(object_type);
std::string key = str_object_type + ":" + sai_serialize_object_id(object_id);
SWSS_LOG_DEBUG("generic get stats key: %s, fields: %lu", key.c_str(), entry.size());
// get_stats will not put data to asic view, only to message queue
m_communicationChannel->set(key, entry, REDIS_ASIC_STATE_COMMAND_GET_STATS);
return waitForGetStatsResponse(number_of_counters, counters);
}
sai_status_t RedisRemoteSaiInterface::waitForGetStatsResponse(
_In_ uint32_t number_of_counters,
_Out_ uint64_t *counters)
{
SWSS_LOG_ENTER();
swss::KeyOpFieldsValuesTuple kco;
auto status = m_communicationChannel->wait(REDIS_ASIC_STATE_COMMAND_GETRESPONSE, kco);
if (status == SAI_STATUS_SUCCESS)
{
auto &values = kfvFieldsValues(kco);
if (values.size () != number_of_counters)
{
SWSS_LOG_THROW("wrong number of counters, got %zu, expected %u", values.size(), number_of_counters);
}
for (uint32_t idx = 0; idx < number_of_counters; idx++)
{
counters[idx] = stoull(fvValue(values[idx]));
}
}
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<swss::FieldValueTuple> 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::queryStatsStCapability(
_In_ sai_object_id_t switchId,
_In_ sai_object_type_t objectType,
_Inout_ sai_stat_st_capability_list_t *stats_capability)
{
SWSS_LOG_ENTER();
// TODO: Fix me
return SAI_STATUS_NOT_IMPLEMENTED;
}
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<swss::FieldValueTuple> &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<swss::FieldValueTuple> &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,
_In_ uint32_t number_of_counters,
_In_ const sai_stat_id_t *counter_ids,
_In_ sai_stats_mode_t mode,
_Out_ uint64_t *counters)
{
SWSS_LOG_ENTER();
SWSS_LOG_ERROR("not implemented");
// TODO could be the same as getStats but put mode at first argument
return SAI_STATUS_NOT_IMPLEMENTED;
}
sai_status_t RedisRemoteSaiInterface::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)
{
SWSS_LOG_ENTER();
auto stats_enum = sai_metadata_get_object_type_info(object_type)->statenum;
auto values = serialize_counter_id_list(stats_enum, number_of_counters, counter_ids);
auto str_object_type = sai_serialize_object_type(object_type);
auto key = str_object_type + ":" + sai_serialize_object_id(object_id);
SWSS_LOG_DEBUG("generic clear stats key: %s, fields: %lu", key.c_str(), values.size());
// clear_stats will not put data into asic view, only to message queue
m_recorder->recordGenericClearStats(object_type, object_id, number_of_counters, counter_ids);
m_communicationChannel->set(key, values, REDIS_ASIC_STATE_COMMAND_CLEAR_STATS);
auto status = waitForClearStatsResponse();
m_recorder->recordGenericClearStatsResponse(status);
return status;
}
sai_status_t RedisRemoteSaiInterface::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();
SWSS_LOG_ERROR("not implemented");
return SAI_STATUS_NOT_IMPLEMENTED;
}
sai_status_t RedisRemoteSaiInterface::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();
SWSS_LOG_ERROR("not implemented");
return SAI_STATUS_NOT_IMPLEMENTED;
}
sai_status_t RedisRemoteSaiInterface::waitForClearStatsResponse()
{
SWSS_LOG_ENTER();
swss::KeyOpFieldsValuesTuple kco;
auto status = m_communicationChannel->wait(REDIS_ASIC_STATE_COMMAND_GETRESPONSE, kco);
return status;
}
sai_status_t RedisRemoteSaiInterface::bulkRemove(
_In_ sai_object_type_t object_type,
_In_ const std::vector<std::string> &serialized_object_ids,
_In_ sai_bulk_op_error_mode_t mode,
_Out_ sai_status_t *object_statuses)
{
SWSS_LOG_ENTER();
// TODO support mode, this will need to go as extra parameter and needs to
// be supported by LUA script passed as first or last entry in values,
// currently mode is ignored
std::string serializedObjectType = sai_serialize_object_type(object_type);
std::vector<swss::FieldValueTuple> entries;
for (size_t idx = 0; idx < serialized_object_ids.size(); ++idx)
{
std::string str_attr = "";
swss::FieldValueTuple fvtNoStatus(serialized_object_ids[idx], str_attr);
entries.push_back(fvtNoStatus);
}
/*
* We are adding number of entries to actually add ':' to be compatible
* with previous
*/
// key: object_type:count
// field: object_id
// value: object_attrs
std::string key = serializedObjectType + ":" + std::to_string(entries.size());
m_recorder->recordBulkGenericRemove(serializedObjectType, entries);
m_communicationChannel->set(key, entries, REDIS_ASIC_STATE_COMMAND_BULK_REMOVE);
return waitForBulkResponse(SAI_COMMON_API_BULK_REMOVE, (uint32_t)serialized_object_ids.size(), object_statuses);
}
sai_status_t RedisRemoteSaiInterface::waitForBulkResponse(
_In_ sai_common_api_t api,
_In_ uint32_t object_count,
_Out_ sai_status_t *object_statuses)
{
SWSS_LOG_ENTER();
if (m_syncMode)
{
swss::KeyOpFieldsValuesTuple kco;
auto status = m_communicationChannel->wait(REDIS_ASIC_STATE_COMMAND_GETRESPONSE, kco);
auto &values = kfvFieldsValues(kco);
if (values.size () != object_count)
{
SWSS_LOG_THROW("wrong number of statuses, got %zu, expected %u", values.size(), object_count);
}
// deserialize statuses for all objects
for (uint32_t idx = 0; idx < object_count; idx++)
{
sai_deserialize_status(fvField(values[idx]), object_statuses[idx]);
}
m_recorder->recordBulkGenericResponse(status, object_count, object_statuses);
return status;
}
/*
* By default sync mode is disabled and all bulk create/set/remove are
* considered success operations.
*/
for (uint32_t idx = 0; idx < object_count; idx++)
{
object_statuses[idx] = SAI_STATUS_SUCCESS;
}
return SAI_STATUS_SUCCESS;
}
sai_status_t RedisRemoteSaiInterface::waitForBulkGetResponse(
_In_ sai_object_type_t objectType,
_In_ uint32_t object_count,
_In_ const uint32_t *attr_count,
_Inout_ sai_attribute_t **attr_list,
_Out_ sai_status_t *object_statuses)
{
SWSS_LOG_ENTER();
swss::KeyOpFieldsValuesTuple kco;
const auto status = m_communicationChannel->wait(REDIS_ASIC_STATE_COMMAND_GETRESPONSE, kco);
const auto &values = kfvFieldsValues(kco);
if (values.size() != object_count)
{
SWSS_LOG_THROW("wrong number of statuses, got %zu, expected %u", values.size(), object_count);
}
for (size_t idx = 0; idx < values.size(); idx++)
{
// field = status
// value = attrid=attrvalue|...
const auto& statusStr = fvField(values[idx]);
const auto& joined = fvValue(values[idx]);
const auto v = swss::tokenize(joined, '|');
std::vector<swss::FieldValueTuple> entries; // attributes per object id
entries.reserve(v.size());
for (size_t i = 0; i < v.size(); i++)
{
const std::string item = v.at(i);
auto start = item.find_first_of("=");
auto field = item.substr(0, start);
auto value = item.substr(start + 1);
entries.emplace_back(field, value);
}
// deserialize statuses for all objects
sai_deserialize_status(statusStr, object_statuses[idx]);
const auto objectStatus = object_statuses[idx];
if (objectStatus == SAI_STATUS_SUCCESS || objectStatus == SAI_STATUS_BUFFER_OVERFLOW)
{
const auto countOnly = (objectStatus == SAI_STATUS_BUFFER_OVERFLOW);
if (values.size() == 0)
{
SWSS_LOG_THROW("logic error, get response returned 0 values!, send api response or sync/async issue?");
}
SaiAttributeList list(objectType, entries, countOnly);
// no need for id fix since this is overflow
transfer_attributes(objectType, attr_count[idx], list.get_attr_list(), attr_list[idx], countOnly);
}
}
m_recorder->recordBulkGenericGetResponse(status, values);
return status;
}
sai_status_t RedisRemoteSaiInterface::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)
{
SWSS_LOG_ENTER();
std::vector<std::string> serializedObjectIds;
for (uint32_t idx = 0; idx < object_count; idx++)
{
serializedObjectIds.emplace_back(sai_serialize_object_id(object_id[idx]));
}
return bulkRemove(object_type, serializedObjectIds, mode, object_statuses);
}
sai_status_t RedisRemoteSaiInterface::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)
{
SWSS_LOG_ENTER();
std::vector<std::string> serializedObjectIds;
for (uint32_t idx = 0; idx < object_count; idx++)
{
serializedObjectIds.emplace_back(sai_serialize_object_id(object_id[idx]));
}
return bulkSet(object_type, serializedObjectIds, attr_list, mode, object_statuses);
}
sai_status_t RedisRemoteSaiInterface::bulkSet(
_In_ sai_object_type_t object_type,
_In_ const std::vector<std::string> &serialized_object_ids,
_In_ const sai_attribute_t *attr_list,
_In_ sai_bulk_op_error_mode_t mode,
_Out_ sai_status_t *object_statuses)
{
SWSS_LOG_ENTER();
// TODO support mode
std::vector<swss::FieldValueTuple> entries;
for (size_t idx = 0; idx < serialized_object_ids.size(); ++idx)
{
auto entry = SaiAttributeList::serialize_attr_list(object_type, 1, &attr_list[idx], false);
std::string str_attr = Globals::joinFieldValues(entry);
swss::FieldValueTuple value(serialized_object_ids[idx], str_attr);
entries.push_back(value);
}
/*
* We are adding number of entries to actually add ':' to be compatible
* with previous
*/
auto serializedObjectType = sai_serialize_object_type(object_type);
std::string key = serializedObjectType + ":" + std::to_string(entries.size());
m_recorder->recordBulkGenericSet(serializedObjectType, entries);
m_communicationChannel->set(key, entries, REDIS_ASIC_STATE_COMMAND_BULK_SET);
return waitForBulkResponse(SAI_COMMON_API_BULK_SET, (uint32_t)serialized_object_ids.size(), object_statuses);
}
sai_status_t RedisRemoteSaiInterface::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)
{
SWSS_LOG_ENTER();
std::vector<std::string> serializedObjectIds;
serializedObjectIds.reserve(object_count);
for (uint32_t idx = 0; idx < object_count; idx++)
{
serializedObjectIds.emplace_back(sai_serialize_object_id(object_id[idx]));
}
return bulkGet(object_type, serializedObjectIds, attr_count, attr_list, mode, object_statuses);
}
sai_status_t RedisRemoteSaiInterface::bulkGet(
_In_ sai_object_type_t object_type,
_In_ const std::vector<std::string> &serialized_object_ids,
_In_ const uint32_t *attr_count,
_Inout_ sai_attribute_t **attr_list,
_In_ sai_bulk_op_error_mode_t mode,
_Inout_ sai_status_t *object_statuses)
{
SWSS_LOG_ENTER();
const auto serializedObjectType = sai_serialize_object_type(object_type);
std::vector<swss::FieldValueTuple> entries;
entries.reserve(serialized_object_ids.size());
for (size_t idx = 0; idx < serialized_object_ids.size(); idx++)
{
/*
* Since user may reuse buffers, then oid list buffers maybe not cleared
* and contain some garbage, let's clean them so we send all oids as null to
* syncd.
*/
Utils::clearOidValues(object_type, attr_count[idx], attr_list[idx]);
const auto entry = SaiAttributeList::serialize_attr_list(object_type, attr_count[idx], attr_list[idx], false);
const auto strAttr = Globals::joinFieldValues(entry);
swss::FieldValueTuple fvt(serialized_object_ids[idx] , strAttr);
entries.push_back(fvt);
}
/*
* We are adding number of entries to actually add ':' to be compatible
* with previous
*/
const auto key = serializedObjectType + ":" + std::to_string(entries.size());
m_communicationChannel->set(key, entries, REDIS_ASIC_STATE_COMMAND_BULK_GET);
m_recorder->recordBulkGenericGet(serializedObjectType, entries);
const auto object_count = static_cast<uint32_t>(serialized_object_ids.size());
const auto status = waitForBulkGetResponse(object_type, object_count, attr_count, attr_list, object_statuses);
return status;
}
sai_status_t RedisRemoteSaiInterface::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)
{
SWSS_LOG_ENTER();
// TODO support mode
for (uint32_t idx = 0; idx < object_count; idx++)
{
object_id[idx] = m_virtualObjectIdManager->allocateNewObjectId(object_type, switch_id);
if (object_id[idx] == SAI_NULL_OBJECT_ID)
{
SWSS_LOG_ERROR("failed to create %s, with switch id: %s",
sai_serialize_object_type(object_type).c_str(),
sai_serialize_object_id(switch_id).c_str());
return SAI_STATUS_INSUFFICIENT_RESOURCES;
}
}
std::vector<std::string> serialized_object_ids;
// on create vid is put in db by syncd
for (uint32_t idx = 0; idx < object_count; idx++)
{
std::string str_object_id = sai_serialize_object_id(object_id[idx]);
serialized_object_ids.push_back(str_object_id);
}
return bulkCreate(
object_type,
serialized_object_ids,
attr_count,
attr_list,
mode,
object_statuses);
}
sai_status_t RedisRemoteSaiInterface::bulkCreate(
_In_ sai_object_type_t object_type,
_In_ const std::vector<std::string> &serialized_object_ids,
_In_ const uint32_t *attr_count,
_In_ const sai_attribute_t **attr_list,
_In_ sai_bulk_op_error_mode_t mode,
_Inout_ sai_status_t *object_statuses)
{
SWSS_LOG_ENTER();
// TODO support mode
std::string str_object_type = sai_serialize_object_type(object_type);
std::vector<swss::FieldValueTuple> entries;
for (size_t idx = 0; idx < serialized_object_ids.size(); ++idx)
{
auto entry = SaiAttributeList::serialize_attr_list(object_type, attr_count[idx], attr_list[idx], false);
if (entry.empty())
{
// make sure that we put object into db
// even if there are no attributes set
swss::FieldValueTuple null("NULL", "NULL");
entry.push_back(null);
}
std::string str_attr = Globals::joinFieldValues(entry);
swss::FieldValueTuple fvtNoStatus(serialized_object_ids[idx] , str_attr);
entries.push_back(fvtNoStatus);
}
/*
* We are adding number of entries to actually add ':' to be compatible
* with previous
*/
// key: object_type:count
// field: object_id
// value: object_attrs
std::string key = str_object_type + ":" + std::to_string(entries.size());
m_recorder->recordBulkGenericCreate(str_object_type, entries);
m_communicationChannel->set(key, entries, REDIS_ASIC_STATE_COMMAND_BULK_CREATE);
return waitForBulkResponse(SAI_COMMON_API_BULK_CREATE, (uint32_t)serialized_object_ids.size(), object_statuses);
}
sai_status_t RedisRemoteSaiInterface::notifySyncd(
_In_ sai_object_id_t switchId,
_In_ sai_redis_notify_syncd_t redisNotifySyncd)
{
SWSS_LOG_ENTER();
std::vector<swss::FieldValueTuple> entry;
auto key = sai_serialize(redisNotifySyncd);
SWSS_LOG_NOTICE("sending syncd: %s", key.c_str());
// we need to use "GET" channel to be sure that
// all previous operations were applied, if we don't
// use GET channel then we may hit race condition
// on syncd side where syncd will start compare view
// when there are still objects in op queue
//
// other solution can be to use notify event
// and then on syncd side read all the asic state queue
// and apply changes before switching to init/apply mode
m_recorder->recordNotifySyncd(switchId, redisNotifySyncd);
m_communicationChannel->set(key, entry, REDIS_ASIC_STATE_COMMAND_NOTIFY);
auto status = waitForNotifySyncdResponse();
m_recorder->recordNotifySyncdResponse(status);
return status;
}
sai_status_t RedisRemoteSaiInterface::waitForNotifySyncdResponse()
{
SWSS_LOG_ENTER();
swss::KeyOpFieldsValuesTuple kco;
auto status = m_communicationChannel->wait(REDIS_ASIC_STATE_COMMAND_NOTIFY, kco);
return status;
}
bool RedisRemoteSaiInterface::isRedisAttribute(
_In_ sai_object_id_t objectType,
_In_ const sai_attribute_t* attr)
{
SWSS_LOG_ENTER();
if ((objectType != SAI_OBJECT_TYPE_SWITCH) || (attr == nullptr) || (attr->id < SAI_SWITCH_ATTR_CUSTOM_RANGE_START))
{
return false;
}
return true;
}
void RedisRemoteSaiInterface::handleNotification(
_In_ const std::string &name,
_In_ const std::string &serializedNotification,
_In_ const std::vector<swss::FieldValueTuple> &values)
{
SWSS_LOG_ENTER();
// TODO to pass switch_id for every notification we could add it to values
// at syncd side
//
// Each global context (syncd) will have it's own notification thread
// handler, so we will know at which context notification arrived, but we
// also need to know at which switch id generated this notification. For
// that we will assign separate notification handlers in syncd itself, and
// each of those notifications will know to which switch id it belongs.
// Then later we could also check whether oids in notification actually
// belongs to given switch id. This way we could find vendor bugs like
// sending notifications from one switch to another switch handler.
//
// But before that we will extract switch id from notification itself.
// TODO record should also be under api mutex, all other apis are
m_recorder->recordNotification(name, serializedNotification, values);
auto notification = NotificationFactory::deserialize(name, serializedNotification);
if (notification)
{
auto sn = m_notificationCallback(notification); // will be synchronized to api mutex
// execute callback from notification thread
notification->executeCallback(sn);
}
}
sai_object_type_t RedisRemoteSaiInterface::objectTypeQuery(
_In_ sai_object_id_t objectId)
{
SWSS_LOG_ENTER();
return m_virtualObjectIdManager->saiObjectTypeQuery(objectId);
}
sai_object_id_t RedisRemoteSaiInterface::switchIdQuery(
_In_ sai_object_id_t objectId)
{
SWSS_LOG_ENTER();
return m_virtualObjectIdManager->saiSwitchIdQuery(objectId);
}
sai_status_t RedisRemoteSaiInterface::logSet(
_In_ sai_api_t api,
_In_ sai_log_level_t log_level)
{
SWSS_LOG_ENTER();
return SAI_STATUS_SUCCESS;
}
sai_status_t RedisRemoteSaiInterface::queryApiVersion(
_Out_ sai_api_version_t *version)
{
SWSS_LOG_ENTER();
if (version)
{
*version = SAI_API_VERSION;
// TODO FIXME implement proper query for syncd, currently this is not an issue since swss is not using this API
SWSS_LOG_WARN("retruning SAI API version %d with sairedis compiled SAI headers, not actual libsai.so", SAI_API_VERSION);
return SAI_STATUS_SUCCESS;
}
SWSS_LOG_ERROR("version parameter is NULL");
return SAI_STATUS_INVALID_PARAMETER;
}
sai_status_t RedisRemoteSaiInterface::sai_redis_notify_syncd(
_In_ sai_object_id_t switchId,
_In_ const sai_attribute_t *attr)
{
SWSS_LOG_ENTER();
auto redisNotifySyncd = (sai_redis_notify_syncd_t)attr->value.s32;
switch (redisNotifySyncd)
{
case SAI_REDIS_NOTIFY_SYNCD_INIT_VIEW:
case SAI_REDIS_NOTIFY_SYNCD_APPLY_VIEW:
case SAI_REDIS_NOTIFY_SYNCD_INSPECT_ASIC:
case SAI_REDIS_NOTIFY_SYNCD_INVOKE_DUMP:
break;
default:
SWSS_LOG_ERROR("invalid notify syncd attr value %s", sai_serialize(redisNotifySyncd).c_str());
return SAI_STATUS_FAILURE;
}
auto status = notifySyncd(switchId, redisNotifySyncd);
if (status == SAI_STATUS_SUCCESS)
{
switch (redisNotifySyncd)
{
case SAI_REDIS_NOTIFY_SYNCD_INIT_VIEW:
SWSS_LOG_NOTICE("switched ASIC to INIT VIEW");
m_asicInitViewMode = true;
SWSS_LOG_NOTICE("clearing current local state since init view is called on initialized switch");
clear_local_state();
break;
case SAI_REDIS_NOTIFY_SYNCD_APPLY_VIEW:
SWSS_LOG_NOTICE("switched ASIC to APPLY VIEW");
m_asicInitViewMode = false;
break;
case SAI_REDIS_NOTIFY_SYNCD_INSPECT_ASIC:
SWSS_LOG_NOTICE("inspect ASIC SUCCEEDED");
break;
case SAI_REDIS_NOTIFY_SYNCD_INVOKE_DUMP:
SWSS_LOG_NOTICE("invoked DUMP succeeded");
break;
default:
break;
}
}
return status;
}
void RedisRemoteSaiInterface::clear_local_state()
{
SWSS_LOG_ENTER();
SWSS_LOG_NOTICE("clearing local state");
// Will need to be executed after init VIEW
// will clear switch container
m_switchContainer = std::make_shared<SwitchContainer>();
m_virtualObjectIdManager =
std::make_shared<VirtualObjectIdManager>(
m_contextConfig->m_guid,
m_contextConfig->m_scc,
m_redisVidIndexGenerator);
auto meta = m_meta.lock();
if (meta)
{
meta->meta_init_db();
}
}
void RedisRemoteSaiInterface::setMeta(
_In_ std::weak_ptr<saimeta::Meta> meta)
{
SWSS_LOG_ENTER();
m_meta = meta;
}
sai_switch_notifications_t RedisRemoteSaiInterface::syncProcessNotification(
_In_ std::shared_ptr<Notification> notification)
{
SWSS_LOG_ENTER();
// NOTE: process metadata must be executed under sairedis API mutex since
// it will access meta database and notification comes from different
// thread, and this method is executed from notifications thread
auto meta = m_meta.lock();
if (!meta)
{
SWSS_LOG_WARN("meta pointer expired");
return { };
}
notification->processMetadata(meta);
auto objectId = notification->getAnyObjectId();
auto switchId = m_virtualObjectIdManager->saiSwitchIdQuery(objectId);
auto sw = m_switchContainer->getSwitch(switchId);
if (sw)
{
return sw->getSwitchNotifications(); // explicit copy
}
SWSS_LOG_WARN("switch %s not present in container, returning empty switch notifications",
sai_serialize_object_id(switchId).c_str());
return { };
}
bool RedisRemoteSaiInterface::containsSwitch(
_In_ sai_object_id_t switchId) const
{
SWSS_LOG_ENTER();
if (!m_switchContainer->contains(switchId))
{
SWSS_LOG_INFO("context %s failed to find switch %s",
m_contextConfig->m_name.c_str(), sai_serialize_object_id(switchId).c_str());
return false;
}
return true;
}
const std::map<sai_object_id_t, swss::TableDump>& RedisRemoteSaiInterface::getTableDump() const
{
SWSS_LOG_ENTER();
return m_tableDump;
}
void RedisRemoteSaiInterface::refreshTableDump()
{
SWSS_LOG_ENTER();
SWSS_LOG_TIMER("get asic view from %s", ASIC_STATE_TABLE);
swss::Table table(m_db.get(), ASIC_STATE_TABLE);
swss::TableDump dump;
table.dump(dump);
auto& map = m_tableDump;
map.clear();
for (auto& key: dump)
{
sai_object_meta_key_t mk;
sai_deserialize_object_meta_key(key.first, mk);
auto switchVID = switchIdQuery(mk.objectkey.key.object_id);
map[switchVID][key.first] = key.second;
}
SWSS_LOG_NOTICE("%s switch count: %zu:", ASIC_STATE_TABLE, map.size());
for (auto& kvp: map)
{
SWSS_LOG_NOTICE("%s: objects count: %zu",
sai_serialize_object_id(kvp.first).c_str(),
kvp.second.size());
}
}