syncd/VirtualOidTranslator.cpp (372 lines of code) (raw):
#include "VirtualOidTranslator.h"
#include "VirtualObjectIdManager.h"
#include "RedisClient.h"
#include "swss/logger.h"
#include "meta/sai_serialize.h"
#include <inttypes.h>
using namespace syncd;
VirtualOidTranslator::VirtualOidTranslator(
_In_ std::shared_ptr<RedisClient> client,
_In_ std::shared_ptr<sairedis::VirtualObjectIdManager> virtualObjectIdManager,
_In_ std::shared_ptr<sairedis::SaiInterface> vendorSai):
m_virtualObjectIdManager(virtualObjectIdManager),
m_vendorSai(vendorSai),
m_client(client)
{
SWSS_LOG_ENTER();
// empty
}
bool VirtualOidTranslator::tryTranslateRidToVid(
_In_ sai_object_id_t rid,
_Out_ sai_object_id_t &vid)
{
SWSS_LOG_ENTER();
std::lock_guard<std::mutex> lock(m_mutex);
if (rid == SAI_NULL_OBJECT_ID)
{
SWSS_LOG_DEBUG("translated RID null to VID null");
vid = SAI_NULL_OBJECT_ID;
return true;
}
auto it = m_rid2vid.find(vid);
if (it != m_rid2vid.end())
{
vid = it->second;
return true;
}
vid = m_client->getVidForRid(rid);
if (vid == SAI_NULL_OBJECT_ID)
{
SWSS_LOG_DEBUG("translated RID %s to VID null", sai_serialize_object_id(rid).c_str());
return false;
}
return true;
}
sai_object_id_t VirtualOidTranslator::translateRidToVid(
_In_ sai_object_id_t rid,
_In_ sai_object_id_t switchVid,
_In_ bool translateRemoved)
{
SWSS_LOG_ENTER();
std::lock_guard<std::mutex> lock(m_mutex);
/*
* NOTE: switch_vid here is Virtual ID of switch for which we need
* create VID for given RID.
*/
if (rid == SAI_NULL_OBJECT_ID)
{
SWSS_LOG_DEBUG("translated RID null to VID null");
return SAI_NULL_OBJECT_ID;
}
auto it = m_rid2vid.find(rid);
if (it != m_rid2vid.end())
{
return it->second;
}
std::string strRid = sai_serialize_object_id(rid);
auto vid = m_client->getVidForRid(rid);
if (vid != SAI_NULL_OBJECT_ID)
{
// object exists
SWSS_LOG_DEBUG("translated RID %s to VID %s",
sai_serialize_object_id(rid).c_str(),
sai_serialize_object_id(vid).c_str());
return vid;
}
if (translateRemoved)
{
auto itr = m_removedRid2vid.find(rid);
if (itr != m_removedRid2vid.end())
{
SWSS_LOG_WARN("translating removed RID %s, to VID %s",
sai_serialize_object_id(rid).c_str(),
sai_serialize_object_id(itr->second).c_str());
return itr->second;
}
}
SWSS_LOG_DEBUG("spotted new RID %s", sai_serialize_object_id(rid).c_str());
sai_object_type_t object_type = m_vendorSai->objectTypeQuery(rid); // TODO move to std::function or wrapper class
if (object_type == SAI_OBJECT_TYPE_NULL)
{
SWSS_LOG_THROW("vendorSai->objectTypeQuery returned NULL type for RID 0x%" PRIx64, rid);
}
if (object_type == SAI_OBJECT_TYPE_SWITCH)
{
/*
* Switch ID should be already inside local db or redis db when we
* created switch, so we should never get here.
*/
SWSS_LOG_THROW("RID 0x%" PRIx64 " is switch object, but not in local or redis db, bug!", rid);
}
vid = m_virtualObjectIdManager->allocateNewObjectId(object_type, switchVid); // TODO to std::function or separate object
SWSS_LOG_INFO("translated RID %s to VID %s",
sai_serialize_object_id(rid).c_str(),
sai_serialize_object_id(vid).c_str());
m_client->insertVidAndRid(vid, rid);
m_rid2vid[rid] = vid;
m_vid2rid[vid] = rid;
return vid;
}
bool VirtualOidTranslator::checkRidExists(
_In_ sai_object_id_t rid,
_In_ bool checkRemoved)
{
SWSS_LOG_ENTER();
std::lock_guard<std::mutex> lock(m_mutex);
if (rid == SAI_NULL_OBJECT_ID)
return true;
if (m_rid2vid.find(rid) != m_rid2vid.end())
return true;
auto vid = m_client->getVidForRid(rid);
if (vid != SAI_NULL_OBJECT_ID)
return true;
if (checkRemoved && (m_removedRid2vid.find(rid) != m_removedRid2vid.end()))
{
SWSS_LOG_WARN("removed RID %s exists", sai_serialize_object_id(rid).c_str());
return true;
}
return false;
}
void VirtualOidTranslator::translateRidToVid(
_Inout_ sai_object_list_t &element,
_In_ sai_object_id_t switchVid,
_In_ bool translateRemoved)
{
SWSS_LOG_ENTER();
for (uint32_t i = 0; i < element.count; i++)
{
element.list[i] = translateRidToVid(element.list[i], switchVid, translateRemoved);
}
}
void VirtualOidTranslator::translateRidToVid(
_In_ sai_object_type_t objectType,
_In_ sai_object_id_t switchVid,
_In_ uint32_t attr_count,
_Inout_ sai_attribute_t *attrList,
_In_ bool translateRemoved)
{
SWSS_LOG_ENTER();
/*
* We receive real id's here, if they are new then create new VIDs for them
* and put in db, if entry exists in db, use it.
*
* NOTE: switch_id is VID of switch on which those RIDs are provided.
*/
for (uint32_t i = 0; i < attr_count; i++)
{
sai_attribute_t &attr = attrList[i];
auto meta = sai_metadata_get_attr_metadata(objectType, attr.id);
if (meta == NULL)
{
SWSS_LOG_THROW("unable to get metadata for object type %x, attribute %d", objectType, attr.id);
}
/*
* TODO: Many times we do switch for list of attributes to perform some
* operation on each oid from that attribute, we should provide clever
* way via sai metadata utils to get that.
*/
switch (meta->attrvaluetype)
{
case SAI_ATTR_VALUE_TYPE_OBJECT_ID:
attr.value.oid = translateRidToVid(attr.value.oid, switchVid, translateRemoved);
break;
case SAI_ATTR_VALUE_TYPE_OBJECT_LIST:
translateRidToVid(attr.value.objlist, switchVid, translateRemoved);
break;
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_OBJECT_ID:
if (attr.value.aclfield.enable)
attr.value.aclfield.data.oid = translateRidToVid(attr.value.aclfield.data.oid, switchVid, translateRemoved);
break;
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_OBJECT_LIST:
if (attr.value.aclfield.enable)
translateRidToVid(attr.value.aclfield.data.objlist, switchVid, translateRemoved);
break;
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_OBJECT_ID:
if (attr.value.aclaction.enable)
attr.value.aclaction.parameter.oid = translateRidToVid(attr.value.aclaction.parameter.oid, switchVid, translateRemoved);
break;
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_OBJECT_LIST:
if (attr.value.aclaction.enable)
translateRidToVid(attr.value.aclaction.parameter.objlist, switchVid, translateRemoved);
break;
default:
/*
* If in future new attribute with object id will be added this
* will make sure that we will need to add handler here.
*/
if (meta->isoidattribute)
{
SWSS_LOG_THROW("attribute %s is object id, but not processed, FIXME", meta->attridname);
}
break;
}
}
}
sai_object_id_t VirtualOidTranslator::translateVidToRid(
_In_ sai_object_id_t vid)
{
SWSS_LOG_ENTER();
std::lock_guard<std::mutex> lock(m_mutex);
if (vid == SAI_NULL_OBJECT_ID)
{
SWSS_LOG_DEBUG("translated VID null to RID null");
return SAI_NULL_OBJECT_ID;
}
auto it = m_vid2rid.find(vid);
if (it != m_vid2rid.end())
{
return it->second;
}
auto rid = m_client->getRidForVid(vid);
if (rid == SAI_NULL_OBJECT_ID)
{
/*
* If user created object that is object id, then it should not
* query attributes of this object in init view mode, because he
* knows all attributes passed to that object.
*
* NOTE: This may be a problem for some objects in init view mode.
* We will need to revisit this after checking with real SAI
* implementation. Problem here may be that user will create some
* object and actually will need to to query some of it's values,
* like buffer limitations etc, mostly probably this will happen on
* SWITCH object.
*/
// SWSS_LOG_THROW("can't get RID in init view mode - don't query created objects");
SWSS_LOG_THROW("unable to get RID for VID %s",
sai_serialize_object_id(vid).c_str());
}
/*
* We got this RID from redis db, so put it also to local db so it will be
* faster to retrieve it late on.
*/
m_vid2rid[vid] = rid;
SWSS_LOG_DEBUG("translated VID %s to RID %s",
sai_serialize_object_id(vid).c_str(),
sai_serialize_object_id(rid).c_str());
return rid;
}
/*
* NOTE: We could have in metadata utils option to execute function on each
* object on oid like this. Problem is that we can't then add extra
* parameters.
*/
bool VirtualOidTranslator::tryTranslateVidToRid(
_In_ sai_object_id_t vid,
_Out_ sai_object_id_t& rid)
{
SWSS_LOG_ENTER();
std::lock_guard<std::mutex> lock(m_mutex);
if (vid == SAI_NULL_OBJECT_ID)
{
SWSS_LOG_DEBUG("translated VID null to RID null");
rid = SAI_NULL_OBJECT_ID;
return true;
}
auto it = m_vid2rid.find(vid);
if (it != m_vid2rid.end())
{
rid = it->second;
return true;
}
rid = m_client->getRidForVid(vid);
if (rid == SAI_NULL_OBJECT_ID)
{
SWSS_LOG_INFO("unable to get RID for VID %s",
sai_serialize_object_id(vid).c_str());
return false;
}
/*
* We got this RID from redis db, so put it also to local db so it will be
* faster to retrieve it late on.
*/
m_vid2rid[vid] = rid;
SWSS_LOG_DEBUG("translated VID %s to RID %s",
sai_serialize_object_id(vid).c_str(),
sai_serialize_object_id(rid).c_str());
return true;
}
bool VirtualOidTranslator::tryTranslateVidToRid(
_Inout_ sai_object_meta_key_t &metaKey)
{
SWSS_LOG_ENTER();
auto info = sai_metadata_get_object_type_info(metaKey.objecttype);
if (info->isobjectid)
{
return tryTranslateVidToRid(
metaKey.objectkey.key.object_id,
metaKey.objectkey.key.object_id);
}
for (size_t j = 0; j < info->structmemberscount; ++j)
{
const sai_struct_member_info_t *m = info->structmembers[j];
if (m->membervaluetype == SAI_ATTR_VALUE_TYPE_OBJECT_ID)
{
sai_object_id_t vid = m->getoid(&metaKey);
sai_object_id_t rid;
if (tryTranslateVidToRid(vid, rid))
{
m->setoid(&metaKey, rid);
continue;
}
return false;
}
}
return true;
}
void VirtualOidTranslator::translateVidToRid(
_Inout_ sai_object_list_t &element)
{
SWSS_LOG_ENTER();
for (uint32_t i = 0; i < element.count; i++)
{
element.list[i] = translateVidToRid(element.list[i]);
}
}
void VirtualOidTranslator::translateVidToRid(
_In_ sai_object_type_t objectType,
_In_ uint32_t attr_count,
_Inout_ sai_attribute_t *attrList)
{
SWSS_LOG_ENTER();
/*
* All id's received from sairedis should be virtual, so lets translate
* them to real id's before we execute actual api.
*/
for (uint32_t i = 0; i < attr_count; i++)
{
sai_attribute_t &attr = attrList[i];
auto meta = sai_metadata_get_attr_metadata(objectType, attr.id);
if (meta == NULL)
{
SWSS_LOG_THROW("unable to get metadata for object type %x, attribute %d", objectType, attr.id);
}
switch (meta->attrvaluetype)
{
case SAI_ATTR_VALUE_TYPE_OBJECT_ID:
attr.value.oid = translateVidToRid(attr.value.oid);
break;
case SAI_ATTR_VALUE_TYPE_OBJECT_LIST:
translateVidToRid(attr.value.objlist);
break;
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_OBJECT_ID:
if (attr.value.aclfield.enable)
attr.value.aclfield.data.oid = translateVidToRid(attr.value.aclfield.data.oid);
break;
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_OBJECT_LIST:
if (attr.value.aclfield.enable)
translateVidToRid(attr.value.aclfield.data.objlist);
break;
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_OBJECT_ID:
if (attr.value.aclaction.enable)
attr.value.aclaction.parameter.oid = translateVidToRid(attr.value.aclaction.parameter.oid);
break;
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_OBJECT_LIST:
if (attr.value.aclaction.enable)
translateVidToRid(attr.value.aclaction.parameter.objlist);
break;
default:
/*
* If in future new attribute with object id will be added this
* will make sure that we will need to add handler here.
*/
if (meta->isoidattribute)
{
SWSS_LOG_THROW("attribute %s is object id, but not processed, FIXME", meta->attridname);
}
break;
}
}
}
void VirtualOidTranslator::translateVidToRid(
_Inout_ sai_object_meta_key_t &metaKey)
{
SWSS_LOG_ENTER();
auto info = sai_metadata_get_object_type_info(metaKey.objecttype);
if (info->isobjectid)
{
metaKey.objectkey.key.object_id =
translateVidToRid(metaKey.objectkey.key.object_id);
return;
}
for (size_t j = 0; j < info->structmemberscount; ++j)
{
const sai_struct_member_info_t *m = info->structmembers[j];
if (m->membervaluetype == SAI_ATTR_VALUE_TYPE_OBJECT_ID)
{
sai_object_id_t vid = m->getoid(&metaKey);
sai_object_id_t rid = translateVidToRid(vid);
m->setoid(&metaKey, rid);
}
}
}
void VirtualOidTranslator::insertRidAndVid(
_In_ sai_object_id_t rid,
_In_ sai_object_id_t vid)
{
SWSS_LOG_ENTER();
std::lock_guard<std::mutex> lock(m_mutex);
// to support multiple switches vid/rid map must be per switch
m_rid2vid[rid] = vid;
m_vid2rid[vid] = rid;
m_client->insertVidAndRid(vid, rid);
}
void VirtualOidTranslator::eraseRidAndVid(
_In_ sai_object_id_t rid,
_In_ sai_object_id_t vid)
{
SWSS_LOG_ENTER();
std::lock_guard<std::mutex> lock(m_mutex);
m_client->removeVidAndRid(vid, rid);
// remove from local vid2rid and rid2vid map
m_rid2vid.erase(rid);
m_vid2rid.erase(vid);
m_removedRid2vid[rid] = vid;
}
void VirtualOidTranslator::clearLocalCache()
{
SWSS_LOG_ENTER();
std::lock_guard<std::mutex> lock(m_mutex);
m_rid2vid.clear();
m_vid2rid.clear();
m_removedRid2vid.clear();
}