vslib/VirtualSwitchSaiInterface.cpp (1,269 lines of code) (raw):
#include "VirtualSwitchSaiInterface.h"
#include "swss/logger.h"
#include "swss/exec.h"
#include "swss/converter.h"
#include "meta/sai_serialize.h"
#include "meta/SaiAttributeList.h"
#include "meta/PerformanceIntervalTimer.h"
#include "meta/Globals.h"
#include "SwitchStateBase.h"
#include "SwitchBCM81724.h"
#include "SwitchBCM56850.h"
#include "SwitchBCM56971B0.h"
#include "SwitchMLNX2700.h"
#include "SwitchNvdaMBF2H536C.h"
#include <inttypes.h>
/*
* Max number of counters used in 1 api call
*/
#define VS_MAX_COUNTERS 128
#define MAX_HARDWARE_INFO_LENGTH 0x1000
using namespace saivs;
using namespace saimeta;
using namespace sairediscommon;
VirtualSwitchSaiInterface::VirtualSwitchSaiInterface(
_In_ std::shared_ptr<ContextConfig> contextConfig):
m_contextConfig(contextConfig)
{
SWSS_LOG_ENTER();
m_realObjectIdManager = std::make_shared<RealObjectIdManager>(
m_contextConfig->m_guid,
m_contextConfig->m_scc);
}
VirtualSwitchSaiInterface::~VirtualSwitchSaiInterface()
{
SWSS_LOG_ENTER();
// empty
}
sai_status_t VirtualSwitchSaiInterface::apiInitialize(
_In_ uint64_t flags,
_In_ const sai_service_method_table_t *service_method_table)
{
SWSS_LOG_ENTER();
return SAI_STATUS_SUCCESS;
}
sai_status_t VirtualSwitchSaiInterface::apiUninitialize(void)
{
SWSS_LOG_ENTER();
return SAI_STATUS_SUCCESS;
}
void VirtualSwitchSaiInterface::setMeta(
_In_ std::weak_ptr<saimeta::Meta> meta)
{
SWSS_LOG_ENTER();
m_meta = meta;
}
std::shared_ptr<WarmBootState> VirtualSwitchSaiInterface::extractWarmBootState(
_In_ sai_object_id_t switchId)
{
SWSS_LOG_ENTER();
auto it = m_warmBootState.find(switchId);
if (it == m_warmBootState.end())
{
SWSS_LOG_WARN("no warm boot state for switch %s",
sai_serialize_object_id(switchId).c_str());
return nullptr;
}
auto state = std::make_shared<WarmBootState>(it->second); // copy ctr
// remove warm boot state for switch, each switch can only warm boot once
m_warmBootState.erase(it);
return state;
}
bool VirtualSwitchSaiInterface::validate_switch_warm_boot_atributes(
_In_ uint32_t attr_count,
_In_ const sai_attribute_t *attr_list) const
{
SWSS_LOG_ENTER();
/*
* When in warm boot, as init attributes on switch we only allow
* notifications and init attribute. Actually we should check if
* notifications we pass are the same as the one that we have in dumped db,
* if not we should set missing one to NULL ptr.
*/
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)
continue;
SWSS_LOG_ERROR("attribute %s not supported in warm boot, expected INIT_SWITCH, HARDWARE_INFO or notification pointer", meta->attridname);
return false;
}
return true;
}
void VirtualSwitchSaiInterface::update_local_metadata(
_In_ sai_object_id_t switch_id)
{
SWSS_LOG_ENTER();
auto mmeta = m_meta.lock();
if (!mmeta)
{
SWSS_LOG_THROW("meta pointer expired");
}
/*
* After warm boot we recreated all ASIC state, but since we are using
* meta_* to check all needed data, we need to use post_create/post_set
* methods to recreate state in local metadata so when next APIs will be
* called, we could check the actual state.
*/
const auto &objectHash = m_switchStateMap.at(switch_id)->m_objectHash;//.at(object_type);
// first create switch
// first we need to create all "oid" objects to have reference base
// then set all object attributes on those oids
// then create all non oid like route etc.
/*
* First update switch, since all non switch objects will be using
* sai_switch_id_query to check if oid is valid.
*/
sai_object_meta_key_t mk;
mk.objecttype = SAI_OBJECT_TYPE_SWITCH;
mk.objectkey.key.object_id = switch_id;
mmeta->meta_generic_validation_post_create(mk, switch_id, 0, NULL);
/*
* Create every non object id except switch. Switch object was already
* created above, and non object ids like route may contain other object
* id's inside *_entry struct, and since metadata is checking reference of
* those objects, they must exists first.
*/
for (auto& kvp: objectHash)
{
sai_object_type_t ot = kvp.first;
if (ot == SAI_OBJECT_TYPE_NULL)
continue;
if (ot == SAI_OBJECT_TYPE_SWITCH)
continue;
auto info = sai_metadata_get_object_type_info(ot);
if (info == NULL)
SWSS_LOG_THROW("failed to get object type info for object type %d", ot);
if (info->isnonobjectid)
continue;
mk.objecttype = ot;
for (auto obj: kvp.second)
{
sai_deserialize_object_id(obj.first, mk.objectkey.key.object_id);
mmeta->meta_generic_validation_post_create(mk, switch_id, 0, NULL);
}
}
/*
* Create all non object id's. All oids are created, so objects inside
* *_entry structs can be referenced correctly.
*/
for (auto& kvp: objectHash)
{
sai_object_type_t ot = kvp.first;
if (ot == SAI_OBJECT_TYPE_NULL)
continue;
auto info = sai_metadata_get_object_type_info(ot);
if (info == NULL)
SWSS_LOG_THROW("failed to get object type info for object type %d", ot);
if (info->isobjectid)
continue;
for (auto obj: kvp.second)
{
std::string key = std::string(info->objecttypename) + ":" + obj.first;
sai_deserialize_object_meta_key(key, mk);
mmeta->meta_generic_validation_post_create(mk, switch_id, 0, NULL);
}
}
/*
* Set all attributes on all objects. Since attributes maybe OID attributes
* we need to set them too for correct reference count.
*/
for (auto& kvp: objectHash)
{
sai_object_type_t ot = kvp.first;
if (ot == SAI_OBJECT_TYPE_NULL)
continue;
auto info = sai_metadata_get_object_type_info(ot);
if (info == NULL)
SWSS_LOG_THROW("failed to get object type info for object type %d", ot);
for (auto obj: kvp.second)
{
std::string key = std::string(info->objecttypename) + ":" + obj.first;
sai_deserialize_object_meta_key(key, mk);
for (auto a: obj.second)
{
auto meta = a.second->getAttrMetadata();
if (meta->isreadonly)
continue;
mmeta->meta_generic_validation_post_set(mk, a.second->getAttr());
}
}
}
/*
* Since this method is called inside internal_vs_generic_create next
* meta_generic_validation_post_create will be called after success return
* of meta_sai_create_oid and it would fail since we already created switch
* so we need to notify metadata that this is warm boot.
*/
mmeta->meta_warm_boot_notify();
}
sai_status_t VirtualSwitchSaiInterface::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();
if (!objectId)
{
SWSS_LOG_THROW("objectId pointer is NULL");
}
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);
switchId = m_realObjectIdManager->allocateNewSwitchObjectId(hwinfo);
*objectId = switchId;
if (switchId == SAI_NULL_OBJECT_ID)
{
SWSS_LOG_ERROR("switch ID allocation failed");
return SAI_STATUS_FAILURE;
}
if (m_switchStateMap.find(switchId) != m_switchStateMap.end())
{
if (m_warmBootData.find(switchId) == m_warmBootData.end())
{
SWSS_LOG_ERROR("switch %s with hwinfo '%s' already exists",
sai_serialize_object_id(switchId).c_str(),
hwinfo.c_str());
return SAI_STATUS_FAILURE;
}
}
}
else
{
// create new real object ID
*objectId = m_realObjectIdManager->allocateNewObjectId(objectType, switchId);
}
std::string str_object_id = sai_serialize_object_id(*objectId);
return create(
switchId,
objectType,
str_object_id,
attr_count,
attr_list);
}
sai_status_t VirtualSwitchSaiInterface::remove(
_In_ sai_object_type_t objectType,
_In_ sai_object_id_t objectId)
{
SWSS_LOG_ENTER();
return remove(
switchIdQuery(objectId),
objectType,
sai_serialize_object_id(objectId));
}
sai_status_t VirtualSwitchSaiInterface::preSet(
_In_ sai_object_type_t objectType,
_In_ sai_object_id_t objectId,
_In_ const sai_attribute_t *attr)
{
SWSS_LOG_ENTER();
switch (objectType)
{
case SAI_OBJECT_TYPE_PORT:
return preSetPort(objectId, attr);
default:
return SAI_STATUS_SUCCESS;
}
}
sai_status_t VirtualSwitchSaiInterface::set(
_In_ sai_object_type_t objectType,
_In_ sai_object_id_t objectId,
_In_ const sai_attribute_t *attr)
{
SWSS_LOG_ENTER();
auto status = preSet(objectType, objectId, attr);
if (status != SAI_STATUS_SUCCESS)
return status;
return set(
switchIdQuery(objectId),
objectType,
sai_serialize_object_id(objectId),
attr);
}
sai_status_t VirtualSwitchSaiInterface::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(
switchIdQuery(objectId),
objectType,
sai_serialize_object_id(objectId),
attr_count,
attr_list);
}
#define DECLARE_REMOVE_ENTRY(OT,ot) \
sai_status_t VirtualSwitchSaiInterface::remove( \
_In_ const sai_ ## ot ## _t* entry) \
{ \
SWSS_LOG_ENTER(); \
return remove( \
entry->switch_id, \
(sai_object_type_t)SAI_OBJECT_TYPE_ ## OT, \
sai_serialize_ ## ot(*entry)); \
}
SAIREDIS_DECLARE_EVERY_ENTRY(DECLARE_REMOVE_ENTRY);
#define DECLARE_CREATE_ENTRY(OT,ot) \
sai_status_t VirtualSwitchSaiInterface::create( \
_In_ const sai_ ## ot ## _t* entry, \
_In_ uint32_t attr_count, \
_In_ const sai_attribute_t *attr_list) \
{ \
SWSS_LOG_ENTER(); \
static PerformanceIntervalTimer \
timer("VirtualSwitchSaiInterface::create(" #ot ")"); \
timer.start(); \
auto status = create( \
entry->switch_id, \
(sai_object_type_t)SAI_OBJECT_TYPE_ ## OT, \
sai_serialize_ ## ot(*entry), \
attr_count, \
attr_list); \
timer.stop(); \
timer.inc(); \
return status; \
}
SAIREDIS_DECLARE_EVERY_ENTRY(DECLARE_CREATE_ENTRY);
#define DECLARE_SET_ENTRY(OT,ot) \
sai_status_t VirtualSwitchSaiInterface::set( \
_In_ const sai_ ## ot ## _t* entry, \
_In_ const sai_attribute_t *attr) \
{ \
SWSS_LOG_ENTER(); \
return set( \
entry->switch_id, \
(sai_object_type_t)SAI_OBJECT_TYPE_ ## OT, \
sai_serialize_ ## ot(*entry), \
attr); \
}
SAIREDIS_DECLARE_EVERY_ENTRY(DECLARE_SET_ENTRY);
#define DECLARE_BULK_CREATE_ENTRY(OT,ot) \
sai_status_t VirtualSwitchSaiInterface::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(); \
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); \
} \
return bulkCreate( \
ot->switch_id, \
(sai_object_type_t)SAI_OBJECT_TYPE_ ## OT, \
serialized_object_ids, \
attr_count, \
attr_list, \
mode, \
object_statuses); \
}
SAIREDIS_DECLARE_EVERY_BULK_ENTRY(DECLARE_BULK_CREATE_ENTRY);
#define DECLARE_BULK_REMOVE_ENTRY(OT,ot) \
sai_status_t VirtualSwitchSaiInterface::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( \
ot->switch_id, \
(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 VirtualSwitchSaiInterface::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( \
ot->switch_id, \
(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 VirtualSwitchSaiInterface::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);
std::shared_ptr<SwitchStateBase> VirtualSwitchSaiInterface::init_switch(
_In_ sai_object_id_t switch_id,
_In_ std::shared_ptr<SwitchConfig> config,
_In_ std::shared_ptr<WarmBootState> warmBootState,
_In_ std::weak_ptr<saimeta::Meta> meta,
_In_ uint32_t attr_count,
_In_ const sai_attribute_t *attr_list)
{
SWSS_LOG_ENTER();
SWSS_LOG_TIMER("init");
if (switch_id == SAI_NULL_OBJECT_ID)
{
SWSS_LOG_THROW("init switch with NULL switch id is not allowed");
}
if (m_switchStateMap.find(switch_id) != m_switchStateMap.end())
{
SWSS_LOG_THROW("switch already exists %s", sai_serialize_object_id(switch_id).c_str());
}
switch (config->m_switchType)
{
case SAI_VS_SWITCH_TYPE_BCM56850:
m_switchStateMap[switch_id] = std::make_shared<SwitchBCM56850>(switch_id, m_realObjectIdManager, config, warmBootState);
break;
case SAI_VS_SWITCH_TYPE_BCM56971B0:
m_switchStateMap[switch_id] = std::make_shared<SwitchBCM56971B0>(switch_id, m_realObjectIdManager, config, warmBootState);
break;
case SAI_VS_SWITCH_TYPE_BCM81724:
m_switchStateMap[switch_id] = std::make_shared<SwitchBCM81724>(switch_id, m_realObjectIdManager, config, warmBootState);
break;
case SAI_VS_SWITCH_TYPE_MLNX2700:
m_switchStateMap[switch_id] = std::make_shared<SwitchMLNX2700>(switch_id, m_realObjectIdManager, config, warmBootState);
break;
case SAI_VS_SWITCH_TYPE_NVDA_MBF2H536C:
m_switchStateMap[switch_id] = std::make_shared<SwitchNvdaMBF2H536C>(switch_id, m_realObjectIdManager, config, warmBootState);
break;
default:
SWSS_LOG_WARN("unknown switch type: %d", config->m_switchType);
return nullptr;
}
auto ss = m_switchStateMap.at(switch_id);
ss->setMeta(meta);
if (warmBootState != nullptr)
{
auto status = ss->warm_boot_initialize_objects(); // TODO move to constructor
if (status != SAI_STATUS_SUCCESS)
{
SWSS_LOG_THROW("unable to warm boot init switch %s", sai_serialize_status(status).c_str());
}
SWSS_LOG_NOTICE("initialized switch %s in WARM boot mode", sai_serialize_object_id(switch_id).c_str());
// XXX lane map may be different after warm boot if ports were added/removed
}
else
{
sai_status_t status = ss->initialize_default_objects(attr_count, attr_list); // TODO move to constructor
if (status != SAI_STATUS_SUCCESS)
{
SWSS_LOG_THROW("unable to init switch %s", sai_serialize_status(status).c_str());
}
SWSS_LOG_NOTICE("initialized switch %s", sai_serialize_object_id(switch_id).c_str());
}
return ss;
}
sai_status_t VirtualSwitchSaiInterface::create(
_In_ sai_object_id_t switchId,
_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();
if (object_type == SAI_OBJECT_TYPE_SWITCH)
{
auto switchIndex = RealObjectIdManager::getSwitchIndex(switchId);
auto config = m_contextConfig->m_scc->getConfig(switchIndex);
if (config == nullptr)
{
SWSS_LOG_ERROR("failed to get switch config for switch %s, and index %u",
serializedObjectId.c_str(),
switchIndex);
return SAI_STATUS_FAILURE;
}
std::shared_ptr<WarmBootState> warmBootState = nullptr;
if (config->m_bootType == SAI_VS_BOOT_TYPE_WARM)
{
if (!validate_switch_warm_boot_atributes(attr_count, attr_list))
{
SWSS_LOG_ERROR("invalid attribute passed during warm boot");
return SAI_STATUS_FAILURE;
}
warmBootState = extractWarmBootState(switchId);
if (warmBootState == nullptr)
{
SWSS_LOG_ERROR("warm boot was requested on switch %s, but warm boot state is NULL",
sai_serialize_object_id(switchId).c_str());
return SAI_STATUS_FAILURE;
}
}
auto ss = init_switch(switchId, config, warmBootState, m_meta, attr_count, attr_list);
if (!ss)
{
return SAI_STATUS_FAILURE;
}
if (warmBootState != nullptr)
{
update_local_metadata(switchId);
if (config->m_useTapDevice)
{
ss->vs_recreate_hostif_tap_interfaces();
}
}
}
auto ss = m_switchStateMap.at(switchId);
return ss->create(object_type, serializedObjectId, switchId, attr_count, attr_list);
}
void VirtualSwitchSaiInterface::removeSwitch(
_In_ sai_object_id_t switch_id)
{
SWSS_LOG_ENTER();
if (m_switchStateMap.find(switch_id) == m_switchStateMap.end())
{
SWSS_LOG_THROW("switch doesn't exist 0x%lx", switch_id);
}
SWSS_LOG_NOTICE("remove switch 0x%lx", switch_id);
m_switchStateMap.erase(switch_id);
}
sai_status_t VirtualSwitchSaiInterface::remove(
_In_ sai_object_id_t switchId,
_In_ sai_object_type_t objectType,
_In_ const std::string& serializedObjectId)
{
SWSS_LOG_ENTER();
auto ss = m_switchStateMap.at(switchId);
// Perform db dump if warm restart was requested.
if (objectType == SAI_OBJECT_TYPE_SWITCH)
{
sai_attribute_t attr;
attr.id = SAI_SWITCH_ATTR_RESTART_WARM;
sai_object_id_t object_id;
sai_deserialize_object_id(serializedObjectId, object_id);
if (get(objectType, object_id, 1, &attr) == SAI_STATUS_SUCCESS)
{
SWSS_LOG_NOTICE("switch %s SAI_SWITCH_ATTR_RESTART_WARM = %s",
sai_serialize_object_id(switchId).c_str(),
attr.value.booldata ? "true" : "false");
if (attr.value.booldata)
{
m_warmBootData[switchId] = ss->dump_switch_database_for_warm_restart();
}
}
else
{
SWSS_LOG_ERROR("failed to get SAI_SWITCH_ATTR_RESTART_WARM, no DB dump will be performed");
}
}
auto status = ss->remove(objectType, serializedObjectId);
if (objectType == SAI_OBJECT_TYPE_SWITCH &&
status == SAI_STATUS_SUCCESS)
{
sai_object_id_t object_id;
sai_deserialize_object_id(serializedObjectId, object_id);
SWSS_LOG_NOTICE("removed switch: %s", sai_serialize_object_id(object_id).c_str());
m_realObjectIdManager->releaseObjectId(object_id);
removeSwitch(object_id);
}
return status;
}
sai_status_t VirtualSwitchSaiInterface::set(
_In_ sai_object_id_t switchId,
_In_ sai_object_type_t objectType,
_In_ const std::string &serializedObjectId,
_In_ const sai_attribute_t *attr)
{
SWSS_LOG_ENTER();
auto ss = m_switchStateMap.at(switchId);
return ss->set(objectType, serializedObjectId, attr);
}
sai_status_t VirtualSwitchSaiInterface::get(
_In_ sai_object_id_t switchId,
_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();
auto ss = m_switchStateMap.at(switchId);
return ss->get(objectType, serializedObjectId, attr_count, attr_list);
}
#define DECLARE_GET_ENTRY(OT,ot) \
sai_status_t VirtualSwitchSaiInterface::get( \
_In_ const sai_ ## ot ## _t* entry, \
_In_ uint32_t attr_count, \
_Inout_ sai_attribute_t *attr_list) \
{ \
SWSS_LOG_ENTER(); \
return get( \
entry->switch_id, \
(sai_object_type_t)SAI_OBJECT_TYPE_ ## OT, \
sai_serialize_ ## ot(*entry), \
attr_count, \
attr_list); \
}
SAIREDIS_DECLARE_EVERY_ENTRY(DECLARE_GET_ENTRY);
sai_status_t VirtualSwitchSaiInterface::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();
// TODO: We should generate this metadata for the virtual switch rather
// than hard-coding it here.
if (objectType == SAI_OBJECT_TYPE_DEBUG_COUNTER)
{
*count = 3;
return SAI_STATUS_SUCCESS;
}
// MPLS Inseg and MPLS NH CRM use sai_object_type_get_availability() API.
else if ((objectType == SAI_OBJECT_TYPE_INSEG_ENTRY) ||
((objectType == SAI_OBJECT_TYPE_NEXT_HOP) &&
(attrCount == 1) && attrList &&
(attrList[0].id == SAI_NEXT_HOP_ATTR_TYPE) &&
(attrList[0].value.s32 == SAI_NEXT_HOP_TYPE_MPLS)))
{
std::string cmd_str("sysctl net.mpls.platform_labels");
std::string ret_str;
*count = 1000;
if (!swss::exec(cmd_str, ret_str))
{
std::string match("net.mpls.platform_labels = ");
if (ret_str.find(match) != std::string::npos)
{
*count = std::stoul(ret_str.substr(match.length()).c_str());
}
}
return SAI_STATUS_SUCCESS;
}
else if (objectType == SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MAP)
{
*count = 512;
return SAI_STATUS_SUCCESS;
}
else if ((objectType == (sai_object_type_t)SAI_OBJECT_TYPE_VNET) ||
(objectType == (sai_object_type_t)SAI_OBJECT_TYPE_ENI) ||
(objectType == (sai_object_type_t)SAI_OBJECT_TYPE_ENI_ETHER_ADDRESS_MAP_ENTRY) ||
(objectType == (sai_object_type_t)SAI_OBJECT_TYPE_INBOUND_ROUTING_ENTRY) ||
(objectType == (sai_object_type_t)SAI_OBJECT_TYPE_OUTBOUND_ROUTING_ENTRY) ||
(objectType == (sai_object_type_t)SAI_OBJECT_TYPE_PA_VALIDATION_ENTRY) ||
(objectType == (sai_object_type_t)SAI_OBJECT_TYPE_OUTBOUND_CA_TO_PA_ENTRY) ||
(objectType == (sai_object_type_t)SAI_OBJECT_TYPE_DASH_ACL_GROUP) ||
(objectType == (sai_object_type_t)SAI_OBJECT_TYPE_DASH_ACL_RULE))
{
*count = 100000;
return SAI_STATUS_SUCCESS;
}
return SAI_STATUS_NOT_SUPPORTED;
}
sai_status_t VirtualSwitchSaiInterface::queryAttributeCapability(
_In_ sai_object_id_t switch_id,
_In_ sai_object_type_t object_type,
_In_ sai_attr_id_t attr_id,
_Out_ sai_attr_capability_t *capability)
{
SWSS_LOG_ENTER();
auto ss = m_switchStateMap.at(switch_id);
return ss->queryAttributeCapability(switch_id, object_type, attr_id, capability);
}
sai_status_t VirtualSwitchSaiInterface::queryAttributeEnumValuesCapability(
_In_ sai_object_id_t switch_id,
_In_ sai_object_type_t object_type,
_In_ sai_attr_id_t attr_id,
_Inout_ sai_s32_list_t *enum_values_capability)
{
SWSS_LOG_ENTER();
// TODO: We should generate this metadata for the virtual switch rather
// than hard-coding it here.
if (object_type == SAI_OBJECT_TYPE_DEBUG_COUNTER && attr_id == SAI_DEBUG_COUNTER_ATTR_IN_DROP_REASON_LIST)
{
if (enum_values_capability->count < 3)
{
return SAI_STATUS_BUFFER_OVERFLOW;
}
enum_values_capability->count = 3;
enum_values_capability->list[0] = SAI_IN_DROP_REASON_L2_ANY;
enum_values_capability->list[1] = SAI_IN_DROP_REASON_L3_ANY;
enum_values_capability->list[2] = SAI_IN_DROP_REASON_ACL_ANY;
return SAI_STATUS_SUCCESS;
}
else if (object_type == SAI_OBJECT_TYPE_DEBUG_COUNTER && attr_id == SAI_DEBUG_COUNTER_ATTR_OUT_DROP_REASON_LIST)
{
if (enum_values_capability->count < 2)
{
return SAI_STATUS_BUFFER_OVERFLOW;
}
enum_values_capability->count = 2;
enum_values_capability->list[0] = SAI_OUT_DROP_REASON_L2_ANY;
enum_values_capability->list[1] = SAI_OUT_DROP_REASON_L3_ANY;
return SAI_STATUS_SUCCESS;
}
else if (object_type == SAI_OBJECT_TYPE_DEBUG_COUNTER && attr_id == SAI_DEBUG_COUNTER_ATTR_TYPE)
{
if (enum_values_capability->count < 4)
{
return SAI_STATUS_BUFFER_OVERFLOW;
}
enum_values_capability->count = 4;
enum_values_capability->list[0] = SAI_DEBUG_COUNTER_TYPE_PORT_IN_DROP_REASONS;
enum_values_capability->list[1] = SAI_DEBUG_COUNTER_TYPE_PORT_OUT_DROP_REASONS;
enum_values_capability->list[2] = SAI_DEBUG_COUNTER_TYPE_SWITCH_IN_DROP_REASONS;
enum_values_capability->list[3] = SAI_DEBUG_COUNTER_TYPE_SWITCH_OUT_DROP_REASONS;
return SAI_STATUS_SUCCESS;
}
auto ss = m_switchStateMap.at(switch_id);
return ss->queryAttrEnumValuesCapability(switch_id, object_type, attr_id, enum_values_capability);
}
sai_status_t VirtualSwitchSaiInterface::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();
/*
* Get stats is the same as get stats ext with mode == SAI_STATS_MODE_READ.
*/
return getStatsExt(
object_type,
object_id,
number_of_counters,
counter_ids,
SAI_STATS_MODE_READ,
counters);
}
sai_status_t VirtualSwitchSaiInterface::queryStatsCapability(
_In_ sai_object_id_t switchId,
_In_ sai_object_type_t objectType,
_Inout_ sai_stat_capability_list_t *stats_capability)
{
SWSS_LOG_ENTER();
if (m_switchStateMap.find(switchId) == m_switchStateMap.end())
{
SWSS_LOG_ERROR("failed to find switch %s in switch state map", sai_serialize_object_id(switchId).c_str());
return SAI_STATUS_FAILURE;
}
auto ss = m_switchStateMap.at(switchId);
if (objectType == SAI_OBJECT_TYPE_QUEUE)
{
if (stats_capability->count < SAI_OBJECT_TYPE_QUEUE)
{
stats_capability->count = SAI_QUEUE_STAT_DELAY_WATERMARK_NS;
return SAI_STATUS_BUFFER_OVERFLOW;
}
stats_capability->count = SAI_QUEUE_STAT_DELAY_WATERMARK_NS;
for(uint32_t i = 0; i < stats_capability->count; i++)
{
stats_capability->list[i].stat_modes = SAI_STATS_MODE_READ_AND_CLEAR | SAI_STATS_MODE_READ;
stats_capability->list[i].stat_enum = i;
}
return SAI_STATUS_SUCCESS;
}
else if (objectType == SAI_OBJECT_TYPE_PORT)
{
if (stats_capability->count < 91)
{
stats_capability->count = 91;
return SAI_STATUS_BUFFER_OVERFLOW;
}
stats_capability->count = 91;
stats_capability->list[0].stat_enum = SAI_PORT_STAT_IF_IN_OCTETS;
stats_capability->list[1].stat_enum = SAI_PORT_STAT_IF_IN_UCAST_PKTS;
stats_capability->list[2].stat_enum = SAI_PORT_STAT_IF_IN_NON_UCAST_PKTS;
stats_capability->list[3].stat_enum = SAI_PORT_STAT_IF_IN_DISCARDS;
stats_capability->list[4].stat_enum = SAI_PORT_STAT_IF_IN_ERRORS;
stats_capability->list[5].stat_enum = SAI_PORT_STAT_IF_IN_UNKNOWN_PROTOS;
stats_capability->list[6].stat_enum = SAI_PORT_STAT_IF_IN_BROADCAST_PKTS;
stats_capability->list[7].stat_enum = SAI_PORT_STAT_IF_IN_MULTICAST_PKTS;
stats_capability->list[8].stat_enum = SAI_PORT_STAT_IF_IN_VLAN_DISCARDS;
stats_capability->list[9].stat_enum = SAI_PORT_STAT_IF_OUT_OCTETS;
stats_capability->list[10].stat_enum = SAI_PORT_STAT_IF_OUT_UCAST_PKTS;
stats_capability->list[11].stat_enum = SAI_PORT_STAT_IF_OUT_NON_UCAST_PKTS;
stats_capability->list[12].stat_enum = SAI_PORT_STAT_IF_OUT_DISCARDS;
stats_capability->list[13].stat_enum = SAI_PORT_STAT_IF_OUT_ERRORS;
stats_capability->list[14].stat_enum = SAI_PORT_STAT_IF_OUT_QLEN;
stats_capability->list[15].stat_enum = SAI_PORT_STAT_IF_OUT_BROADCAST_PKTS;
stats_capability->list[16].stat_enum = SAI_PORT_STAT_IF_OUT_MULTICAST_PKTS;
stats_capability->list[17].stat_enum = SAI_PORT_STAT_ETHER_STATS_DROP_EVENTS;
stats_capability->list[18].stat_enum = SAI_PORT_STAT_ETHER_STATS_MULTICAST_PKTS;
stats_capability->list[19].stat_enum = SAI_PORT_STAT_ETHER_STATS_BROADCAST_PKTS;
stats_capability->list[20].stat_enum = SAI_PORT_STAT_ETHER_STATS_UNDERSIZE_PKTS;
stats_capability->list[21].stat_enum = SAI_PORT_STAT_ETHER_STATS_FRAGMENTS;
stats_capability->list[22].stat_enum = SAI_PORT_STAT_ETHER_STATS_PKTS_64_OCTETS;
stats_capability->list[23].stat_enum = SAI_PORT_STAT_ETHER_STATS_PKTS_65_TO_127_OCTETS;
stats_capability->list[24].stat_enum = SAI_PORT_STAT_ETHER_STATS_PKTS_128_TO_255_OCTETS;
stats_capability->list[25].stat_enum = SAI_PORT_STAT_ETHER_STATS_PKTS_256_TO_511_OCTETS;
stats_capability->list[26].stat_enum = SAI_PORT_STAT_ETHER_STATS_PKTS_512_TO_1023_OCTETS;
stats_capability->list[27].stat_enum = SAI_PORT_STAT_ETHER_STATS_PKTS_1024_TO_1518_OCTETS;
stats_capability->list[28].stat_enum = SAI_PORT_STAT_ETHER_STATS_PKTS_1519_TO_2047_OCTETS;
stats_capability->list[29].stat_enum = SAI_PORT_STAT_ETHER_STATS_PKTS_2048_TO_4095_OCTETS;
stats_capability->list[30].stat_enum = SAI_PORT_STAT_ETHER_STATS_PKTS_4096_TO_9216_OCTETS;
stats_capability->list[31].stat_enum = SAI_PORT_STAT_ETHER_STATS_PKTS_9217_TO_16383_OCTETS;
stats_capability->list[32].stat_enum = SAI_PORT_STAT_ETHER_STATS_OVERSIZE_PKTS;
stats_capability->list[33].stat_enum = SAI_PORT_STAT_ETHER_RX_OVERSIZE_PKTS;
stats_capability->list[34].stat_enum = SAI_PORT_STAT_ETHER_TX_OVERSIZE_PKTS;
stats_capability->list[35].stat_enum = SAI_PORT_STAT_ETHER_STATS_JABBERS;
stats_capability->list[36].stat_enum = SAI_PORT_STAT_ETHER_STATS_OCTETS;
stats_capability->list[37].stat_enum = SAI_PORT_STAT_ETHER_STATS_PKTS;
stats_capability->list[38].stat_enum = SAI_PORT_STAT_ETHER_STATS_COLLISIONS;
stats_capability->list[39].stat_enum = SAI_PORT_STAT_ETHER_STATS_CRC_ALIGN_ERRORS;
stats_capability->list[40].stat_enum = SAI_PORT_STAT_ETHER_STATS_TX_NO_ERRORS;
stats_capability->list[41].stat_enum = SAI_PORT_STAT_ETHER_STATS_RX_NO_ERRORS;
stats_capability->list[42].stat_enum = SAI_PORT_STAT_GREEN_WRED_DROPPED_PACKETS;
stats_capability->list[43].stat_enum = SAI_PORT_STAT_GREEN_WRED_DROPPED_BYTES;
stats_capability->list[44].stat_enum = SAI_PORT_STAT_YELLOW_WRED_DROPPED_PACKETS;
stats_capability->list[45].stat_enum = SAI_PORT_STAT_YELLOW_WRED_DROPPED_BYTES;
stats_capability->list[46].stat_enum = SAI_PORT_STAT_RED_WRED_DROPPED_PACKETS;
stats_capability->list[47].stat_enum = SAI_PORT_STAT_RED_WRED_DROPPED_BYTES;
stats_capability->list[48].stat_enum = SAI_PORT_STAT_WRED_DROPPED_PACKETS;
stats_capability->list[49].stat_enum = SAI_PORT_STAT_WRED_DROPPED_BYTES;
stats_capability->list[50].stat_enum = SAI_PORT_STAT_ECN_MARKED_PACKETS;
stats_capability->list[51].stat_enum = SAI_PORT_STAT_PFC_0_RX_PKTS;
stats_capability->list[52].stat_enum = SAI_PORT_STAT_PFC_0_TX_PKTS;
stats_capability->list[53].stat_enum = SAI_PORT_STAT_PFC_1_RX_PKTS;
stats_capability->list[54].stat_enum = SAI_PORT_STAT_PFC_1_TX_PKTS;
stats_capability->list[55].stat_enum = SAI_PORT_STAT_PFC_2_RX_PKTS;
stats_capability->list[56].stat_enum = SAI_PORT_STAT_PFC_2_TX_PKTS;
stats_capability->list[57].stat_enum = SAI_PORT_STAT_PFC_3_RX_PKTS;
stats_capability->list[58].stat_enum = SAI_PORT_STAT_PFC_3_TX_PKTS;
stats_capability->list[59].stat_enum = SAI_PORT_STAT_PFC_4_RX_PKTS;
stats_capability->list[60].stat_enum = SAI_PORT_STAT_PFC_4_TX_PKTS;
stats_capability->list[61].stat_enum = SAI_PORT_STAT_PFC_5_RX_PKTS;
stats_capability->list[62].stat_enum = SAI_PORT_STAT_PFC_5_TX_PKTS;
stats_capability->list[63].stat_enum = SAI_PORT_STAT_PFC_6_RX_PKTS;
stats_capability->list[64].stat_enum = SAI_PORT_STAT_PFC_6_TX_PKTS;
stats_capability->list[65].stat_enum = SAI_PORT_STAT_PFC_7_RX_PKTS;
stats_capability->list[66].stat_enum = SAI_PORT_STAT_PFC_7_TX_PKTS;
stats_capability->list[67].stat_enum = SAI_PORT_STAT_PFC_0_RX_PAUSE_DURATION_US;
stats_capability->list[68].stat_enum = SAI_PORT_STAT_PFC_0_TX_PAUSE_DURATION_US;
stats_capability->list[69].stat_enum = SAI_PORT_STAT_PFC_1_RX_PAUSE_DURATION_US;
stats_capability->list[70].stat_enum = SAI_PORT_STAT_PFC_1_TX_PAUSE_DURATION_US;
stats_capability->list[71].stat_enum = SAI_PORT_STAT_PFC_2_RX_PAUSE_DURATION_US;
stats_capability->list[72].stat_enum = SAI_PORT_STAT_PFC_2_TX_PAUSE_DURATION_US;
stats_capability->list[73].stat_enum = SAI_PORT_STAT_PFC_3_RX_PAUSE_DURATION_US;
stats_capability->list[74].stat_enum = SAI_PORT_STAT_PFC_3_TX_PAUSE_DURATION_US;
stats_capability->list[75].stat_enum = SAI_PORT_STAT_PFC_4_RX_PAUSE_DURATION_US;
stats_capability->list[76].stat_enum = SAI_PORT_STAT_PFC_4_TX_PAUSE_DURATION_US;
stats_capability->list[77].stat_enum = SAI_PORT_STAT_PFC_5_RX_PAUSE_DURATION_US;
stats_capability->list[78].stat_enum = SAI_PORT_STAT_PFC_5_TX_PAUSE_DURATION_US;
stats_capability->list[79].stat_enum = SAI_PORT_STAT_PFC_6_RX_PAUSE_DURATION_US;
stats_capability->list[80].stat_enum = SAI_PORT_STAT_PFC_6_TX_PAUSE_DURATION_US;
stats_capability->list[81].stat_enum = SAI_PORT_STAT_PFC_7_RX_PAUSE_DURATION_US;
stats_capability->list[82].stat_enum = SAI_PORT_STAT_PFC_7_TX_PAUSE_DURATION_US;
stats_capability->list[83].stat_enum = SAI_PORT_STAT_PFC_0_ON2OFF_RX_PKTS;
stats_capability->list[84].stat_enum = SAI_PORT_STAT_PFC_1_ON2OFF_RX_PKTS;
stats_capability->list[85].stat_enum = SAI_PORT_STAT_PFC_2_ON2OFF_RX_PKTS;
stats_capability->list[86].stat_enum = SAI_PORT_STAT_PFC_3_ON2OFF_RX_PKTS;
stats_capability->list[87].stat_enum = SAI_PORT_STAT_PFC_4_ON2OFF_RX_PKTS;
stats_capability->list[88].stat_enum = SAI_PORT_STAT_PFC_5_ON2OFF_RX_PKTS;
stats_capability->list[89].stat_enum = SAI_PORT_STAT_PFC_6_ON2OFF_RX_PKTS;
stats_capability->list[90].stat_enum = SAI_PORT_STAT_PFC_7_ON2OFF_RX_PKTS;
for(uint32_t i = 0; i < stats_capability->count; i++)
{
stats_capability->list[i].stat_modes = SAI_STATS_MODE_READ_AND_CLEAR | SAI_STATS_MODE_READ ;
}
return SAI_STATUS_SUCCESS;
}
return ss->queryStatsCapability(
switchId,
objectType,
stats_capability);
}
sai_status_t VirtualSwitchSaiInterface::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 VirtualSwitchSaiInterface::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();
sai_object_id_t switch_id = SAI_NULL_OBJECT_ID;
if (m_switchStateMap.size() == 0)
{
SWSS_LOG_ERROR("no switch!, was removed but some function still call");
return SAI_STATUS_FAILURE;
}
if (m_switchStateMap.size() == 1)
{
switch_id = m_switchStateMap.begin()->first;
}
else
{
SWSS_LOG_THROW("multiple switches not supported, FIXME");
}
if (m_switchStateMap.find(switch_id) == m_switchStateMap.end())
{
SWSS_LOG_ERROR("failed to find switch %s in switch state map", sai_serialize_object_id(switch_id).c_str());
return SAI_STATUS_FAILURE;
}
auto ss = m_switchStateMap.at(switch_id);
return ss->getStatsExt(
object_type,
object_id,
number_of_counters,
counter_ids,
mode,
counters);
}
sai_status_t VirtualSwitchSaiInterface::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();
/*
* Clear stats is the same as get stats ext with mode ==
* SAI_STATS_MODE_READ_AND_CLEAR and we just read counters locally and
* discard them, in that way.
*/
uint64_t counters[VS_MAX_COUNTERS];
return getStatsExt(
object_type,
object_id,
number_of_counters,
counter_ids,
SAI_STATS_MODE_READ_AND_CLEAR,
counters);
}
sai_status_t VirtualSwitchSaiInterface::bulkGetStats(
_In_ sai_object_id_t switchId,
_In_ sai_object_type_t object_type,
_In_ uint32_t object_count,
_In_ const sai_object_key_t *object_key,
_In_ uint32_t number_of_counters,
_In_ const sai_stat_id_t *counter_ids,
_In_ sai_stats_mode_t mode,
_Inout_ sai_status_t *object_statuses,
_Out_ uint64_t *counters)
{
SWSS_LOG_ENTER();
return SAI_STATUS_NOT_IMPLEMENTED;
}
sai_status_t VirtualSwitchSaiInterface::bulkClearStats(
_In_ sai_object_id_t switchId,
_In_ sai_object_type_t object_type,
_In_ uint32_t object_count,
_In_ const sai_object_key_t *object_key,
_In_ uint32_t number_of_counters,
_In_ const sai_stat_id_t *counter_ids,
_In_ sai_stats_mode_t mode,
_Inout_ sai_status_t *object_statuses)
{
SWSS_LOG_ENTER();
return SAI_STATUS_NOT_IMPLEMENTED;
}
sai_status_t VirtualSwitchSaiInterface::bulkRemove(
_In_ sai_object_id_t switchId,
_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();
auto ss = m_switchStateMap.at(switchId);
return ss->bulkRemove(object_type, serialized_object_ids, mode, object_statuses);
}
sai_status_t VirtualSwitchSaiInterface::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]));
}
auto switchId = switchIdQuery(*object_id);
return bulkRemove(switchId, object_type, serializedObjectIds, mode, object_statuses);
}
sai_status_t VirtualSwitchSaiInterface::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]));
}
auto switchId = switchIdQuery(*object_id);
return bulkSet(switchId, object_type, serializedObjectIds, attr_list, mode, object_statuses);
}
sai_status_t VirtualSwitchSaiInterface::bulkSet(
_In_ sai_object_id_t switchId,
_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();
auto ss = m_switchStateMap.at(switchId);
return ss->bulkSet(object_type, serialized_object_ids, attr_list, mode, object_statuses);
}
sai_status_t VirtualSwitchSaiInterface::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;
for (uint32_t idx = 0; idx < object_count; idx++)
{
serializedObjectIds.emplace_back(sai_serialize_object_id(object_id[idx]));
}
// Get switch ID from the first object ID, assuming all objects are within the same switch.
auto switchId = switchIdQuery(*object_id);
return bulkGet(switchId, object_type, serializedObjectIds, attr_count, attr_list, mode, object_statuses);
}
sai_status_t VirtualSwitchSaiInterface::bulkGet(
_In_ sai_object_id_t switchId,
_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,
_Out_ sai_status_t *object_statuses)
{
SWSS_LOG_ENTER();
auto ss = m_switchStateMap.at(switchId);
return ss->bulkGet(object_type, serialized_object_ids, attr_count, attr_list, mode, object_statuses);
}
sai_status_t VirtualSwitchSaiInterface::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();
// create new real object IDs
for (uint32_t idx = 0; idx < object_count; idx++)
{
object_id[idx] = m_realObjectIdManager->allocateNewObjectId(object_type, switch_id);
}
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++)
{
object_id[idx] = m_realObjectIdManager->allocateNewObjectId(object_type, switch_id);
std::string str_object_id = sai_serialize_object_id(object_id[idx]);
serialized_object_ids.push_back(str_object_id);
}
return bulkCreate(
switch_id,
object_type,
serialized_object_ids,
attr_count,
attr_list,
mode,
object_statuses);
}
sai_status_t VirtualSwitchSaiInterface::bulkCreate(
_In_ sai_object_id_t switchId,
_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();
auto ss = m_switchStateMap.at(switchId);
return ss->bulkCreate(switchId, object_type, serialized_object_ids, attr_count, attr_list, mode, object_statuses);;
}
sai_object_type_t VirtualSwitchSaiInterface::objectTypeQuery(
_In_ sai_object_id_t objectId)
{
SWSS_LOG_ENTER();
return m_realObjectIdManager->saiObjectTypeQuery(objectId);
}
sai_object_id_t VirtualSwitchSaiInterface::switchIdQuery(
_In_ sai_object_id_t objectId)
{
SWSS_LOG_ENTER();
return m_realObjectIdManager->saiSwitchIdQuery(objectId);
}
sai_status_t VirtualSwitchSaiInterface::logSet(
_In_ sai_api_t api,
_In_ sai_log_level_t log_level)
{
SWSS_LOG_ENTER();
return SAI_STATUS_SUCCESS;
}
sai_status_t VirtualSwitchSaiInterface::queryApiVersion(
_Out_ sai_api_version_t *version)
{
SWSS_LOG_ENTER();
if (version)
{
*version = SAI_API_VERSION;
return SAI_STATUS_SUCCESS;
}
SWSS_LOG_ERROR("version parameter is NULL");
return SAI_STATUS_INVALID_PARAMETER;
}
bool VirtualSwitchSaiInterface::writeWarmBootFile(
_In_ const char* warmBootFile) const
{
SWSS_LOG_ENTER();
if (warmBootFile)
{
std::ofstream ofs;
ofs.open(warmBootFile);
if (!ofs.is_open())
{
SWSS_LOG_ERROR("failed to open: %s", warmBootFile);
return false;
}
if (m_warmBootData.size() == 0)
{
SWSS_LOG_WARN("warm boot data is empty, is that what you want?");
}
for (auto& kvp: m_warmBootData)
{
ofs << kvp.second;
}
ofs.close();
return true;
}
if (m_warmBootData.size())
{
SWSS_LOG_WARN("warm boot write file is not specified, but SAI_SWITCH_ATTR_RESTART_WARM was set to true!");
}
return false;
}
bool VirtualSwitchSaiInterface::readWarmBootFile(
_In_ const char* warmBootFile)
{
SWSS_LOG_ENTER();
if (warmBootFile == NULL)
{
SWSS_LOG_ERROR("warm boot read file is NULL");
return false;
}
{
std::ifstream in(warmBootFile, std::ifstream::ate | std::ifstream::binary);
SWSS_LOG_NOTICE("%s file size: %zu", warmBootFile, (size_t)in.tellg());
}
std::ifstream ifs;
ifs.open(warmBootFile);
if (!ifs.is_open())
{
SWSS_LOG_ERROR("failed to open: %s", warmBootFile);
return false;
}
std::string line;
while (std::getline(ifs, line))
{
SWSS_LOG_DEBUG("line: %s", line.c_str());
// line format: OBJECT_TYPE OBJECT_ID ATTR_ID ATTR_VALUE
std::istringstream iss(line);
std::string strObjectType;
std::string strObjectId;
std::string strAttrId;
std::string strAttrValue;
iss >> strObjectType >> strObjectId;
if (strObjectType == SAI_VS_FDB_INFO)
{
/*
* If we read line from fdb info set and use tap device is enabled
* just parse line and repopulate fdb info set.
*/
FdbInfo fi = FdbInfo::deserialize(strObjectId);
auto switchId = switchIdQuery(fi.m_portId);
if (switchId == SAI_NULL_OBJECT_ID)
{
SWSS_LOG_ERROR("switchIdQuery returned NULL on fi.m_port = %s",
sai_serialize_object_id(fi.m_portId).c_str());
m_warmBootState.clear();
return false;
}
m_warmBootState[switchId].m_switchId = switchId;
m_warmBootState[switchId].m_fdbInfoSet.insert(fi);
continue;
}
iss >> strAttrId >> strAttrValue;
sai_object_meta_key_t metaKey;
sai_deserialize_object_meta_key(strObjectType + ":" + strObjectId, metaKey);
/*
* Since all objects we are creating, then during warm boot we need to
* get the biggest object index, so after warm boot we can start
* generating new objects with index value not colliding with objects
* loaded from warm boot scenario. We only need to consider OID
* objects.
*/
m_realObjectIdManager->updateWarmBootObjectIndex(metaKey.objectkey.key.object_id);
// query each object for switch id
auto switchId = switchIdQuery(metaKey.objectkey.key.object_id);
if (switchId == SAI_NULL_OBJECT_ID)
{
SWSS_LOG_ERROR("switchIdQuery returned NULL on oid = %s",
sai_serialize_object_id(metaKey.objectkey.key.object_id).c_str());
m_warmBootState.clear();
return false;
}
m_warmBootState[switchId].m_switchId = switchId;
auto &objectHash = m_warmBootState[switchId].m_objectHash[metaKey.objecttype]; // will create if not exist
if (objectHash.find(strObjectId) == objectHash.end())
{
objectHash[strObjectId] = {};
}
if (strAttrId == "NULL")
{
// skip empty attributes
continue;
}
objectHash[strObjectId][strAttrId] =
std::make_shared<SaiAttrWrap>(strAttrId, strAttrValue);
}
// NOTE notification pointers should be restored by attr_list when creating switch
ifs.close();
SWSS_LOG_NOTICE("warm boot file %s stats, loaded switches: %zu", warmBootFile, m_warmBootState.size());
for (auto& kvp: m_warmBootState)
{
size_t count = 0;
for (auto& o: kvp.second.m_objectHash)
{
count += o.second.size();
}
SWSS_LOG_NOTICE("switch %s loaded %zu objects",
sai_serialize_object_id(kvp.first).c_str(),
count);
SWSS_LOG_NOTICE("switch %s loaded %zu fdb infos",
sai_serialize_object_id(kvp.first).c_str(),
kvp.second.m_fdbInfoSet.size());
}
return true;
}
void VirtualSwitchSaiInterface::debugSetStats(
_In_ sai_object_id_t oid,
_In_ const std::map<sai_stat_id_t, uint64_t>& stats)
{
SWSS_LOG_ENTER();
auto switchId = switchIdQuery(oid);
auto it = m_switchStateMap.find(switchId);
if (it == m_switchStateMap.end())
{
SWSS_LOG_ERROR("oid %s and it switch %s don't exists in switch state map",
sai_serialize_object_id(oid).c_str(),
sai_serialize_object_id(switchId).c_str());
return;
}
it->second->debugSetStats(oid, stats);
}
// those are asynchronous events that are executed under api mutex, we should
// take care and double check if objects received still exists, and we should
// use meta to check that
void VirtualSwitchSaiInterface::syncProcessEventPacket(
_In_ std::shared_ptr<EventPayloadPacket> payload)
{
SWSS_LOG_ENTER();
// this method is executed under mutex, but from other thread so actual
// switch may not exists
auto port = payload->getPort();
auto switchId = switchIdQuery(port);
auto it = m_switchStateMap.find(switchId);
if (it == m_switchStateMap.end())
{
SWSS_LOG_WARN("oid %s and it switch %s don't exists in switch state map",
sai_serialize_object_id(port).c_str(),
sai_serialize_object_id(switchId).c_str());
return;
}
auto& buffer = payload->getBuffer();
it->second->process_packet_for_fdb_event(
port,
payload->getIfName(),
buffer.getData(),
buffer.getSize());
}
void VirtualSwitchSaiInterface::syncProcessEventNetLinkMsg(
_In_ std::shared_ptr<EventPayloadNetLinkMsg> payload)
{
SWSS_LOG_ENTER();
auto switchId = payload->getSwitchId();
auto it = m_switchStateMap.find(switchId);
if (it == m_switchStateMap.end())
{
SWSS_LOG_NOTICE("switch %s don't exists in switch state map",
sai_serialize_object_id(switchId).c_str());
return;
}
it->second->syncOnLinkMsg(payload);
}