lib/ClientSai.cpp (1,135 lines of code) (raw):
#include "ClientSai.h"
#include "SaiInternal.h"
#include "RedisRemoteSaiInterface.h"
#include "ZeroMQChannel.h"
#include "Utils.h"
#include "sairediscommon.h"
#include "ClientConfig.h"
#include "swss/logger.h"
#include "meta/sai_serialize.h"
#include "meta/NotificationFactory.h"
#include "meta/SaiAttributeList.h"
#include "meta/PerformanceIntervalTimer.h"
#include "meta/Globals.h"
#include <inttypes.h>
#define REDIS_CHECK_API_INITIALIZED() \
if (!m_apiInitialized) { \
SWSS_LOG_ERROR("%s: api not initialized", __PRETTY_FUNCTION__); \
return SAI_STATUS_FAILURE; }
using namespace sairedis;
using namespace sairediscommon;
using namespace saimeta;
using namespace std::placeholders;
// TODO how to tell if current SAI is in init view or apply view ?
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);
ClientSai::ClientSai()
{
SWSS_LOG_ENTER();
m_apiInitialized = false;
}
ClientSai::~ClientSai()
{
SWSS_LOG_ENTER();
if (m_apiInitialized)
{
apiUninitialize();
}
}
sai_status_t ClientSai::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("already initialized");
return SAI_STATUS_FAILURE;
}
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;
}
// TODO support context config
m_switchContainer = std::make_shared<SwitchContainer>();
auto clientConfig = service_method_table->profile_get_value(0, SAI_REDIS_KEY_CLIENT_CONFIG);
auto cc = ClientConfig::loadFromFile(clientConfig);
m_communicationChannel = std::make_shared<ZeroMQChannel>(
cc->m_zmqEndpoint,
cc->m_zmqNtfEndpoint,
std::bind(&ClientSai::handleNotification, this, _1, _2, _3));
m_apiInitialized = true;
return SAI_STATUS_SUCCESS;
}
sai_status_t ClientSai::apiUninitialize(void)
{
SWSS_LOG_ENTER();
REDIS_CHECK_API_INITIALIZED();
SWSS_LOG_NOTICE("begin");
m_communicationChannel = nullptr;
m_apiInitialized = false;
SWSS_LOG_NOTICE("end");
return SAI_STATUS_SUCCESS;
}
// QUAD API
sai_status_t ClientSai::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();
// NOTE: in client mode, each create oid must be retrieved from server since
// server is actually creating new oid, and it's value must be transferred
// over communication channel
*objectId = SAI_NULL_OBJECT_ID;
if (objectType == SAI_OBJECT_TYPE_SWITCH && attr_count > 0 && attr_list)
{
auto initSwitchAttr = sai_metadata_get_attr_by_id(SAI_SWITCH_ATTR_INIT_SWITCH, attr_count, attr_list);
if (initSwitchAttr && initSwitchAttr->value.booldata == false)
{
// TODO for connect, we may not need to actually call server, if we
// have hwinfo and context and context container information, we
// could allocate switch object ID locally
auto hwinfo = Globals::getHardwareInfo(attr_count, attr_list);
// TODO support context
SWSS_LOG_NOTICE("request to connect switch (context: 0) and hwinfo='%s'", hwinfo.c_str());
for (uint32_t i = 0; i < attr_count; ++i)
{
auto meta = sai_metadata_get_attr_metadata(SAI_OBJECT_TYPE_SWITCH, attr_list[i].id);
if (meta == NULL)
{
SWSS_LOG_THROW("failed to find metadata for switch attribute %d", attr_list[i].id);
}
if (meta->attrid == SAI_SWITCH_ATTR_INIT_SWITCH)
continue;
if (meta->attrid == SAI_SWITCH_ATTR_SWITCH_HARDWARE_INFO)
continue;
if (meta->attrvaluetype == SAI_ATTR_VALUE_TYPE_POINTER)
{
SWSS_LOG_ERROR("notifications not supported yet, FIXME");
return SAI_STATUS_FAILURE;
}
SWSS_LOG_ERROR("attribute %s not supported during INIT_SWITCH=false, expected HARDWARE_INFO or notification pointer", meta->attridname);
return SAI_STATUS_FAILURE;
}
}
else
{
SWSS_LOG_ERROR("creating new switch not supported yet, use SAI_SWITCH_ATTR_INIT_SWITCH=false");
return SAI_STATUS_FAILURE;
}
}
auto status = create(
objectType,
sai_serialize_object_id(switchId), // using switch ID instead of oid to transfer to server
attr_count,
attr_list);
if (status == SAI_STATUS_SUCCESS)
{
// since user requested create, OID value was created remotely and it
// was returned in m_lastCreateOids
*objectId = m_lastCreateOids.at(0);
}
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.
*/
SWSS_LOG_NOTICE("create switch OID = %s", sai_serialize_object_id(*objectId).c_str());
auto sw = std::make_shared<Switch>(*objectId, attr_count, attr_list);
m_switchContainer->insert(sw);
}
return status;
}
sai_status_t ClientSai::remove(
_In_ sai_object_type_t objectType,
_In_ sai_object_id_t objectId)
{
MUTEX();
SWSS_LOG_ENTER();
REDIS_CHECK_API_INITIALIZED();
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());
// remove switch from container
m_switchContainer->removeSwitch(objectId);
}
return status;
}
sai_status_t ClientSai::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))
{
SWSS_LOG_ERROR("sairedis extension attributes are not supported in CLIENT mode");
return SAI_STATUS_FAILURE;
}
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 ClientSai::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();
return get(
objectType,
sai_serialize_object_id(objectId),
attr_count,
attr_list);
}
// QUAD ENTRY API
#define DECLARE_CREATE_ENTRY(OT,ot) \
sai_status_t ClientSai::create( \
_In_ const sai_ ## ot ## _t* ot, \
_In_ uint32_t attr_count, \
_In_ const sai_attribute_t *attr_list) \
{ \
MUTEX(); \
SWSS_LOG_ENTER(); \
REDIS_CHECK_API_INITIALIZED(); \
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_REMOVE_ENTRY(OT,ot) \
sai_status_t ClientSai::remove( \
_In_ const sai_ ## ot ## _t* ot) \
{ \
MUTEX(); \
SWSS_LOG_ENTER(); \
REDIS_CHECK_API_INITIALIZED(); \
return remove( \
(sai_object_type_t)SAI_OBJECT_TYPE_ ## OT, \
sai_serialize_ ## ot(*ot)); \
}
SAIREDIS_DECLARE_EVERY_ENTRY(DECLARE_REMOVE_ENTRY);
#define DECLARE_SET_ENTRY(OT,ot) \
sai_status_t ClientSai::set( \
_In_ const sai_ ## ot ## _t* ot, \
_In_ const sai_attribute_t *attr) \
{ \
MUTEX(); \
SWSS_LOG_ENTER(); \
REDIS_CHECK_API_INITIALIZED(); \
return set( \
(sai_object_type_t)SAI_OBJECT_TYPE_ ## OT, \
sai_serialize_ ## ot(*ot), \
attr); \
}
SAIREDIS_DECLARE_EVERY_ENTRY(DECLARE_SET_ENTRY);
#define DECLARE_GET_ENTRY(OT,ot) \
sai_status_t ClientSai::get( \
_In_ const sai_ ## ot ## _t* ot, \
_In_ uint32_t attr_count, \
_Inout_ sai_attribute_t *attr_list) \
{ \
MUTEX(); \
SWSS_LOG_ENTER(); \
REDIS_CHECK_API_INITIALIZED(); \
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);
#define DECLARE_BULK_CREATE_ENTRY(OT,ot) \
sai_status_t ClientSai::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) \
{ \
MUTEX(); \
SWSS_LOG_ENTER(); \
REDIS_CHECK_API_INITIALIZED(); \
static PerformanceIntervalTimer timer("ClientSai::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 ClientSai::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) \
{ \
MUTEX(); \
SWSS_LOG_ENTER(); \
REDIS_CHECK_API_INITIALIZED(); \
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 ClientSai::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) \
{ \
MUTEX(); \
SWSS_LOG_ENTER(); \
REDIS_CHECK_API_INITIALIZED(); \
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_ROUTE_ENTRY, 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 ClientSai::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(); \
SWSS_LOG_ERROR("FIXME not implemented"); \
return SAI_STATUS_NOT_IMPLEMENTED; \
}
SAIREDIS_DECLARE_EVERY_BULK_ENTRY(DECLARE_BULK_GET_ENTRY)
// QUAD API HELPERS
sai_status_t ClientSai::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_communicationChannel->set(key, entry, REDIS_ASIC_STATE_COMMAND_CREATE);
auto status = waitForResponse(SAI_COMMON_API_CREATE);
return status;
}
sai_status_t ClientSai::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_communicationChannel->set(key, {}, REDIS_ASIC_STATE_COMMAND_REMOVE);
auto status = waitForResponse(SAI_COMMON_API_REMOVE);
return status;
}
sai_status_t ClientSai::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_communicationChannel->set(key, entry, REDIS_ASIC_STATE_COMMAND_SET);
auto status = waitForResponse(SAI_COMMON_API_SET);
return status;
}
sai_status_t ClientSai::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());
// 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);
return status;
}
// QUAD API RESPONSE HELPERS
sai_status_t ClientSai::waitForResponse(
_In_ sai_common_api_t api)
{
SWSS_LOG_ENTER();
swss::KeyOpFieldsValuesTuple kco;
auto status = m_communicationChannel->wait(REDIS_ASIC_STATE_COMMAND_GETRESPONSE, kco);
if (api == SAI_COMMON_API_CREATE && status == SAI_STATUS_SUCCESS)
{
m_lastCreateOids.clear();
// since last api was create, we need to extract OID that was created in that api
// if create was for entry, oid is NULL
const auto& entry = kfvFieldsValues(kco);
const auto& field = fvField(entry.at(0));
const auto& value = fvValue(entry.at(0));
SWSS_LOG_INFO("parsing response: %s:%s", field.c_str(), value.c_str());
if (field == "oid")
{
sai_object_id_t oid;
sai_deserialize_object_id(value, oid);
m_lastCreateOids.push_back(oid);
}
}
return status;
}
sai_status_t ClientSai::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;
}
// FLUSH FDB ENTRIES
sai_status_t ClientSai::flushFdbEntries(
_In_ sai_object_id_t switchId,
_In_ uint32_t attrCount,
_In_ const sai_attribute_t *attrList)
{
MUTEX();
SWSS_LOG_ENTER();
REDIS_CHECK_API_INITIALIZED();
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_communicationChannel->set(key, entry, REDIS_ASIC_STATE_COMMAND_FLUSH);
auto status = waitForFlushFdbEntriesResponse();
return status;
}
sai_status_t ClientSai::waitForFlushFdbEntriesResponse()
{
SWSS_LOG_ENTER();
swss::KeyOpFieldsValuesTuple kco;
auto status = m_communicationChannel->wait(REDIS_ASIC_STATE_COMMAND_FLUSHRESPONSE, kco);
return status;
}
// OBJECT TYPE GET AVAILABILITY
sai_status_t ClientSai::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();
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
// 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);
return status;
}
sai_status_t ClientSai::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;
}
// QUERY ATTRIBUTE CAPABILITY
sai_status_t ClientSai::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)
{
MUTEX();
SWSS_LOG_ENTER();
REDIS_CHECK_API_INITIALIZED();
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_communicationChannel->set(switchIdStr, entry, REDIS_ASIC_STATE_COMMAND_ATTR_CAPABILITY_QUERY);
auto status = waitForQueryAttributeCapabilityResponse(capability);
return status;
}
sai_status_t ClientSai::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;
}
// QUERY ATTRIBUTE ENUM CAPABILITY
sai_status_t ClientSai::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)
{
MUTEX();
SWSS_LOG_ENTER();
REDIS_CHECK_API_INITIALIZED();
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_communicationChannel->set(switch_id_str, entry, REDIS_ASIC_STATE_COMMAND_ATTR_ENUM_VALUES_CAPABILITY_QUERY);
auto status = waitForQueryAattributeEnumValuesCapabilityResponse(enumValuesCapability);
return status;
}
sai_status_t ClientSai::waitForQueryAattributeEnumValuesCapabilityResponse(
_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)
{
// TODO on sai status overflow we should populate correct count on the list
SWSS_LOG_ERROR("TODO need to handle SAI_STATUS_BUFFER_OVERFLOW, FIXME");
}
return status;
}
// STATS API
sai_status_t ClientSai::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();
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 ClientSai::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 ClientSai::queryStatsCapability(
_In_ sai_object_id_t switchId,
_In_ sai_object_type_t objectType,
_Inout_ sai_stat_capability_list_t *stats_capability)
{
MUTEX();
SWSS_LOG_ENTER();
REDIS_CHECK_API_INITIALIZED();
auto switchIdStr = sai_serialize_object_id(switchId);
auto objectTypeStr = sai_serialize_object_type(objectType);
if (stats_capability == NULL)
{
SWSS_LOG_ERROR("Failed to find stats-capability: switch %s object type %s", switchIdStr.c_str(), objectTypeStr.c_str());
return SAI_STATUS_INVALID_PARAMETER;
}
if (stats_capability && stats_capability->list && (stats_capability->count))
{
// clear input list, since we use serialize to transfer values
for (uint32_t idx = 0; idx < stats_capability->count; idx++)
{
stats_capability->list[idx].stat_enum = 0;
stats_capability->list[idx].stat_modes = 0;
}
}
const std::string listSize = std::to_string(stats_capability->count);
const std::vector<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_communicationChannel->set(switchIdStr, entry, REDIS_ASIC_STATE_COMMAND_STATS_CAPABILITY_QUERY);
return waitForQueryStatsCapabilityResponse(stats_capability);
}
sai_status_t ClientSai::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();
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;
}
// TODO: Fix me
return SAI_STATUS_NOT_IMPLEMENTED;
}
sai_status_t ClientSai::waitForQueryStatsCapabilityResponse(
_Inout_ sai_stat_capability_list_t* stats_capability)
{
SWSS_LOG_ENTER();
swss::KeyOpFieldsValuesTuple kco;
auto status = m_communicationChannel->wait(REDIS_ASIC_STATE_COMMAND_STATS_CAPABILITY_RESPONSE, kco);
if (status == SAI_STATUS_SUCCESS)
{
const std::vector<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 ClientSai::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();
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 ClientSai::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();
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_communicationChannel->set(key, values, REDIS_ASIC_STATE_COMMAND_CLEAR_STATS);
auto status = waitForClearStatsResponse();
return status;
}
sai_status_t ClientSai::waitForClearStatsResponse()
{
SWSS_LOG_ENTER();
swss::KeyOpFieldsValuesTuple kco;
auto status = m_communicationChannel->wait(REDIS_ASIC_STATE_COMMAND_GETRESPONSE, kco);
return status;
}
sai_status_t ClientSai::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)
{
MUTEX();
SWSS_LOG_ENTER();
REDIS_CHECK_API_INITIALIZED();
SWSS_LOG_ERROR("not implemented");
return SAI_STATUS_NOT_IMPLEMENTED;
}
sai_status_t ClientSai::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)
{
MUTEX();
SWSS_LOG_ENTER();
REDIS_CHECK_API_INITIALIZED();
SWSS_LOG_ERROR("not implemented");
return SAI_STATUS_NOT_IMPLEMENTED;
}
// BULK CREATE
sai_status_t ClientSai::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();
// NOTE: in client mode, each create oid must be retrieved from server since
// server is actually creating new oid, and it's value must be transferred
// over communication channel
// TODO support mode
std::vector<std::string> serialized_object_ids;
// server is responsible for generate new OID but for that we need switch ID
// to be sent to server as well, so instead of sending empty oids we will
// send switch IDs
for (uint32_t idx = 0; idx < object_count; idx++)
{
serialized_object_ids.emplace_back(sai_serialize_object_id(switch_id));
}
auto status = bulkCreate(
object_type,
serialized_object_ids,
attr_count,
attr_list,
mode,
object_statuses);
for (uint32_t idx = 0; idx < object_count; idx++)
{
// since user requested create, OID value was created remotely and it
// was returned in m_lastCreateOids
if (object_statuses[idx] == SAI_STATUS_SUCCESS)
{
object_id[idx] = m_lastCreateOids.at(idx);
}
else
{
object_id[idx] = SAI_NULL_OBJECT_ID;
}
}
return status;
}
sai_status_t ClientSai::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_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);
}
// BULK REMOVE
sai_status_t ClientSai::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();
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);
}
// BULK REMOVE HELPERS
sai_status_t ClientSai::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_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);
}
// BULK SET
sai_status_t ClientSai::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();
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);
}
// BULK SET HELPERS
sai_status_t ClientSai::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)
{
MUTEX();
SWSS_LOG_ENTER();
REDIS_CHECK_API_INITIALIZED();
// 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_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);
}
// BULK GET
sai_status_t ClientSai::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();
SWSS_LOG_ERROR("not implemented, FIXME");
return SAI_STATUS_NOT_IMPLEMENTED;
}
// BULK RESPONSE HELPERS
sai_status_t ClientSai::waitForBulkResponse(
_In_ sai_common_api_t api,
_In_ uint32_t object_count,
_Out_ sai_status_t *object_statuses)
{
SWSS_LOG_ENTER();
swss::KeyOpFieldsValuesTuple kco;
auto status = m_communicationChannel->wait(REDIS_ASIC_STATE_COMMAND_GETRESPONSE, kco);
auto &values = kfvFieldsValues(kco);
// double object count may show up when bulk create api is executed
if ((values.size() != 2 * object_count) && (values.size() != object_count))
{
SWSS_LOG_THROW("wrong number of counters, 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]);
}
if (api == SAI_COMMON_API_BULK_CREATE)
{
m_lastCreateOids.clear();
// since last api was create, we need to extract OID that was created in that api
// if create was for entry, oid is NULL
for (uint32_t idx = object_count; idx < 2 * object_count; idx++)
{
const auto& field = fvField(values.at(idx));
const auto& value = fvValue(values.at(idx));
if (field == "oid")
{
sai_object_id_t oid;
sai_deserialize_object_id(value, oid);
m_lastCreateOids.push_back(oid);
}
}
}
return status;
}
void ClientSai::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.
auto notification = NotificationFactory::deserialize(name, serializedNotification);
if (notification)
{
MUTEX();
if (!m_apiInitialized)
{
SWSS_LOG_ERROR("%s: api not initialized", __PRETTY_FUNCTION__);
return;
}
auto sn = syncProcessNotification(notification);
// execute callback from notification thread
notification->executeCallback(sn);
}
}
sai_switch_notifications_t ClientSai::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 objectId = notification->getAnyObjectId();
auto switchId = switchIdQuery(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 { };
}
sai_object_type_t ClientSai::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 ClientSai::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_OBJECT_TYPE_NULL;
}
return VirtualObjectIdManager::switchIdQuery(objectId);
}
sai_status_t ClientSai::logSet(
_In_ sai_api_t api,
_In_ sai_log_level_t log_level)
{
SWSS_LOG_ENTER();
return SAI_STATUS_SUCCESS;
}
sai_status_t ClientSai::queryApiVersion(
_Out_ sai_api_version_t *version)
{
MUTEX();
SWSS_LOG_ENTER();
REDIS_CHECK_API_INITIALIZED();
SWSS_LOG_ERROR("queryApiVersion not implemented, FIXME");
return SAI_STATUS_NOT_IMPLEMENTED;
}