syncd/SaiDiscovery.cpp (219 lines of code) (raw):
#include "SaiDiscovery.h"
#include "swss/logger.h"
#include "meta/sai_serialize.h"
using namespace syncd;
/**
* @def SAI_DISCOVERY_LIST_MAX_ELEMENTS
*
* Defines maximum elements that can be obtained from the OID list when
* performing list attribute query (discovery) on the switch.
*
* This value will be used to allocate memory on the stack for obtaining object
* list, and should be big enough to obtain list for all ports on the switch
* and vlan members.
*/
#define SAI_DISCOVERY_LIST_MAX_ELEMENTS 1024
SaiDiscovery::SaiDiscovery(
_In_ std::shared_ptr<sairedis::SaiInterface> sai):
m_sai(sai)
{
SWSS_LOG_ENTER();
// empty
}
SaiDiscovery::~SaiDiscovery()
{
SWSS_LOG_ENTER();
// empty
}
void SaiDiscovery::discover(
_In_ sai_object_id_t rid,
_Inout_ std::set<sai_object_id_t> &discovered)
{
SWSS_LOG_ENTER();
/*
* NOTE: This method is only good after switch init since we are making
* assumptions that there are no ACL after initialization.
*
* NOTE: Input set could be a map of sets, this way we will also have
* dependency on each oid.
*/
if (rid == SAI_NULL_OBJECT_ID)
{
return;
}
if (discovered.find(rid) != discovered.end())
{
return;
}
sai_object_type_t ot = m_sai->objectTypeQuery(rid);
if (ot == SAI_OBJECT_TYPE_NULL)
{
SWSS_LOG_THROW("objectTypeQuery: rid %s returned NULL object type",
sai_serialize_object_id(rid).c_str());
}
SWSS_LOG_DEBUG("processing %s: %s",
sai_serialize_object_id(rid).c_str(),
sai_serialize_object_type(ot).c_str());
/*
* We will ignore STP ports by now, since when removing bridge port, then
* associated stp port is automatically removed, and we don't use STP in
* out solution. This causing inconsistency with redis ASIC view vs
* actual ASIC asic state.
*
* TODO: This needs to be solved by sending discovered state to sairedis
* metadata db for reference count.
*
* XXX: workaround
*/
if (ot != SAI_OBJECT_TYPE_STP_PORT)
{
discovered.insert(rid);
}
#ifdef SKIP_SAI_PORT_DISCOVERY
if (ot == SAI_OBJECT_TYPE_PORT)
{
return;
}
#endif
const sai_object_type_info_t *info = sai_metadata_get_object_type_info(ot);
/*
* We will query only oid object types
* then we don't need meta key, but we need to add to metadata
* pointers to only generic functions.
*/
sai_object_meta_key_t mk = { .objecttype = ot, .objectkey = { .key = { .object_id = rid } } };
for (int idx = 0; info->attrmetadata[idx] != NULL; ++idx)
{
const sai_attr_metadata_t *md = info->attrmetadata[idx];
/*
* Note that we don't care about ACL object id's since
* we assume that there are no ACLs on switch after init.
*/
sai_attribute_t attr;
attr.id = md->attrid;
if (md->attrvaluetype == SAI_ATTR_VALUE_TYPE_OBJECT_ID)
{
if (md->defaultvaluetype == SAI_DEFAULT_VALUE_TYPE_CONST)
{
/*
* This means that default value for this object is
* SAI_NULL_OBJECT_ID, since this is discovery after
* create, we don't need to query this attribute.
*/
//continue;
}
if (md->objecttype == SAI_OBJECT_TYPE_STP &&
md->attrid == SAI_STP_ATTR_BRIDGE_ID)
{
// XXX workaround (for mlnx)
SWSS_LOG_WARN("skipping since it causes crash: %s", md->attridname);
continue;
}
if (md->objecttype == SAI_OBJECT_TYPE_BRIDGE_PORT)
{
if (md->attrid == SAI_BRIDGE_PORT_ATTR_TUNNEL_ID ||
md->attrid == SAI_BRIDGE_PORT_ATTR_RIF_ID)
{
/*
* We know that bridge port is bound on PORT, no need
* to query those attributes.
*/
continue;
}
}
SWSS_LOG_DEBUG("getting %s for %s", md->attridname,
sai_serialize_object_id(rid).c_str());
sai_status_t status = m_sai->get(mk.objecttype, mk.objectkey.key.object_id, 1, &attr);
if (status != SAI_STATUS_SUCCESS)
{
/*
* We failed to get value, maybe it's not supported ?
*/
SWSS_LOG_INFO("%s: %s on %s",
md->attridname,
sai_serialize_status(status).c_str(),
sai_serialize_object_id(rid).c_str());
continue;
}
m_defaultOidMap[rid][attr.id] = attr.value.oid;
if (!md->allownullobjectid && attr.value.oid == SAI_NULL_OBJECT_ID)
{
// SWSS_LOG_WARN("got null on %s, but not allowed", md->attridname);
}
if (attr.value.oid != SAI_NULL_OBJECT_ID)
{
ot = m_sai->objectTypeQuery(attr.value.oid);
if (ot == SAI_OBJECT_TYPE_NULL)
{
SWSS_LOG_THROW("when query %s (on %s RID %s) got value %s objectTypeQuery returned NULL object type",
md->attridname,
sai_serialize_object_type(md->objecttype).c_str(),
sai_serialize_object_id(rid).c_str(),
sai_serialize_object_id(attr.value.oid).c_str());
}
}
discover(attr.value.oid, discovered); // recursion
}
else if (md->attrvaluetype == SAI_ATTR_VALUE_TYPE_OBJECT_LIST)
{
if (md->defaultvaluetype == SAI_DEFAULT_VALUE_TYPE_EMPTY_LIST)
{
/*
* This means that default value for this object is
* empty list, since this is discovery after
* create, we don't need to query this attribute.
*/
//continue;
}
SWSS_LOG_DEBUG("getting %s for %s", md->attridname,
sai_serialize_object_id(rid).c_str());
sai_object_id_t local[SAI_DISCOVERY_LIST_MAX_ELEMENTS];
attr.value.objlist.count = SAI_DISCOVERY_LIST_MAX_ELEMENTS;
attr.value.objlist.list = local;
sai_status_t status = m_sai->get(mk.objecttype, mk.objectkey.key.object_id, 1, &attr);
if (status != SAI_STATUS_SUCCESS)
{
/*
* We failed to get value, maybe it's not supported ?
*/
SWSS_LOG_INFO("%s: %s on %s",
md->attridname,
sai_serialize_status(status).c_str(),
sai_serialize_object_id(rid).c_str());
continue;
}
SWSS_LOG_DEBUG("list count %s %u", md->attridname, attr.value.objlist.count);
for (uint32_t i = 0; i < attr.value.objlist.count; ++i)
{
sai_object_id_t oid = attr.value.objlist.list[i];
ot = m_sai->objectTypeQuery(oid);
if (ot == SAI_OBJECT_TYPE_NULL)
{
SWSS_LOG_THROW("when query %s (on %s RID %s) got value %s objectTypeQuery returned NULL object type",
md->attridname,
sai_serialize_object_type(md->objecttype).c_str(),
sai_serialize_object_id(rid).c_str(),
sai_serialize_object_id(oid).c_str());
}
discover(oid, discovered); // recursion
}
}
}
}
std::set<sai_object_id_t> SaiDiscovery::discover(
_In_ sai_object_id_t startRid)
{
SWSS_LOG_ENTER();
/*
* Preform discovery on the switch to obtain ASIC view of
* objects that are created internally.
*/
m_defaultOidMap.clear();
std::set<sai_object_id_t> discovered_rids;
{
SWSS_LOG_TIMER("discover");
auto levels = getApiLogLevel();
setApiLogLevel(SAI_LOG_LEVEL_CRITICAL);
discover(startRid, discovered_rids);
setApiLogLevel(levels);
}
SWSS_LOG_NOTICE("discovered objects count: %zu", discovered_rids.size());
std::map<sai_object_type_t, int> map;
for (sai_object_id_t rid: discovered_rids)
{
/*
* We don't need to check for null since saiDiscovery already checked
* that.
*/
map[m_sai->objectTypeQuery(rid)]++;
}
for (const auto &p: map)
{
SWSS_LOG_NOTICE("%s: %d", sai_serialize_object_type(p.first).c_str(), p.second);
}
return discovered_rids;
}
const SaiDiscovery::DefaultOidMap& SaiDiscovery::getDefaultOidMap() const
{
SWSS_LOG_ENTER();
return m_defaultOidMap;
}
void SaiDiscovery::setApiLogLevel(
_In_ sai_log_level_t logLevel)
{
SWSS_LOG_ENTER();
// We start from 1 since 0 is SAI_API_UNSPECIFIED.
for (uint32_t idx = 1; idx < sai_metadata_enum_sai_api_t.valuescount; ++idx)
{
sai_status_t status = m_sai->logSet((sai_api_t)sai_metadata_enum_sai_api_t.values[idx], logLevel);
if (status == SAI_STATUS_SUCCESS)
{
SWSS_LOG_INFO("setting SAI loglevel %s on %s",
sai_serialize_log_level(logLevel).c_str(),
sai_serialize_api((sai_api_t)sai_metadata_enum_sai_api_t.values[idx]).c_str());
}
else
{
SWSS_LOG_INFO("set loglevel failed: %s", sai_serialize_status(status).c_str());
}
}
}
void SaiDiscovery::setApiLogLevel(
_In_ const std::map<sai_api_t, sai_log_level_t>& levels)
{
SWSS_LOG_ENTER();
// We start from 1 since 0 is SAI_API_UNSPECIFIED.
for (uint32_t idx = 1; idx < sai_metadata_enum_sai_api_t.valuescount; ++idx)
{
auto it = levels.find((sai_api_t)sai_metadata_enum_sai_api_t.values[idx]);
sai_log_level_t logLevel = (it == levels.end()) ? SAI_LOG_LEVEL_NOTICE : it->second;
sai_status_t status = m_sai->logSet((sai_api_t)sai_metadata_enum_sai_api_t.values[idx], logLevel);
if (status == SAI_STATUS_SUCCESS)
{
SWSS_LOG_INFO("setting SAI loglevel %s on %s",
sai_serialize_log_level(logLevel).c_str(),
sai_serialize_api((sai_api_t)sai_metadata_enum_sai_api_t.values[idx]).c_str());
}
else
{
SWSS_LOG_INFO("set loglevel failed: %s", sai_serialize_status(status).c_str());
}
}
}
std::map<sai_api_t, sai_log_level_t> SaiDiscovery::getApiLogLevel()
{
SWSS_LOG_ENTER();
std::map<sai_api_t, sai_log_level_t> levels;
// We start from 1 since 0 is SAI_API_UNSPECIFIED.
for (uint32_t idx = 1; idx < sai_metadata_enum_sai_api_t.valuescount; ++idx)
{
levels[(sai_api_t)sai_metadata_enum_sai_api_t.values[idx]] = m_sai->logGet((sai_api_t)sai_metadata_enum_sai_api_t.values[idx]);
}
return levels;
}