syncd/RedisClient.cpp (594 lines of code) (raw):
#include "RedisClient.h"
#include "VidManager.h"
#include "sairediscommon.h"
#include "meta/sai_serialize.h"
#include "swss/logger.h"
#include "swss/redisapi.h"
using namespace syncd;
// vid and rid maps contains objects from all switches
#define VIDTORID "VIDTORID"
#define RIDTOVID "RIDTOVID"
// those here are per switch
#define LANES "LANES"
#define HIDDEN "HIDDEN"
#define COLDVIDS "COLDVIDS"
RedisClient::RedisClient(
_In_ std::shared_ptr<swss::DBConnector> dbAsic):
m_dbAsic(dbAsic)
{
SWSS_LOG_ENTER();
std::string fdbFlushLuaScript = swss::loadLuaScript("fdb_flush.lua"); // TODO script must be updated to version 2
m_fdbFlushSha = swss::loadRedisScript(dbAsic.get(), fdbFlushLuaScript);
}
RedisClient::~RedisClient()
{
SWSS_LOG_ENTER();
// empty
}
std::string RedisClient::getRedisLanesKey(
_In_ sai_object_id_t switchVid) const
{
SWSS_LOG_ENTER();
/*
* Each switch will have it's own lanes: LANES:oid:0xYYYYYYYY.
*
* NOTE: To support multiple switches LANES needs to be made per switch.
*
* return std::string(LANES) + ":" + sai_serialize_object_id(m_switch_vid);
*
* Only switch with index 0 and global context 0 will have key "LANES" for
* backward compatibility. We could convert that during runtime at first
* time.
*/
auto index = VidManager::getSwitchIndex(switchVid);
auto context = VidManager::getGlobalContext(switchVid);
if (index == 0 && context == 0)
{
return std::string(LANES);
}
return (LANES ":") + sai_serialize_object_id(switchVid);
}
void RedisClient::clearLaneMap(
_In_ sai_object_id_t switchVid) const
{
SWSS_LOG_ENTER();
auto key = getRedisLanesKey(switchVid);
m_dbAsic->del(key);
}
std::unordered_map<sai_uint32_t, sai_object_id_t> RedisClient::getLaneMap(
_In_ sai_object_id_t switchVid) const
{
SWSS_LOG_ENTER();
auto key = getRedisLanesKey(switchVid);
auto hash = m_dbAsic->hgetall(key);
SWSS_LOG_DEBUG("previous lanes: %lu", hash.size());
std::unordered_map<sai_uint32_t, sai_object_id_t> map;
for (auto &kv: hash)
{
const std::string &str_key = kv.first;
const std::string &str_value = kv.second;
sai_uint32_t lane;
sai_object_id_t portId;
sai_deserialize_number(str_key, lane);
sai_deserialize_object_id(str_value, portId);
map[lane] = portId;
}
return map;
}
void RedisClient::saveLaneMap(
_In_ sai_object_id_t switchVid,
_In_ const std::unordered_map<sai_uint32_t, sai_object_id_t>& map) const
{
SWSS_LOG_ENTER();
clearLaneMap(switchVid);
for (auto const &it: map)
{
sai_uint32_t lane = it.first;
sai_object_id_t portId = it.second;
std::string strLane = sai_serialize_number(lane);
std::string strPortId = sai_serialize_object_id(portId);
auto key = getRedisLanesKey(switchVid);
m_dbAsic->hset(key, strLane, strPortId);
}
}
std::unordered_map<sai_object_id_t, sai_object_id_t> RedisClient::getObjectMap(
_In_ const std::string &key) const
{
SWSS_LOG_ENTER();
auto hash = m_dbAsic->hgetall(key);
std::unordered_map<sai_object_id_t, sai_object_id_t> map;
for (auto &kv: hash)
{
const std::string &str_key = kv.first;
const std::string &str_value = kv.second;
sai_object_id_t objectIdKey;
sai_object_id_t objectIdValue;
sai_deserialize_object_id(str_key, objectIdKey);
sai_deserialize_object_id(str_value, objectIdValue);
map[objectIdKey] = objectIdValue;
}
return map;
}
std::unordered_map<sai_object_id_t, sai_object_id_t> RedisClient::getVidToRidMap(
_In_ sai_object_id_t switchVid) const
{
SWSS_LOG_ENTER();
auto map = getObjectMap(VIDTORID);
std::unordered_map<sai_object_id_t, sai_object_id_t> filtered;
for (auto& v2r: map)
{
auto switchId = VidManager::switchIdQuery(v2r.first);
if (switchId == switchVid)
{
filtered[v2r.first] = v2r.second;
}
}
return filtered;
}
std::unordered_map<sai_object_id_t, sai_object_id_t> RedisClient::getRidToVidMap(
_In_ sai_object_id_t switchVid) const
{
SWSS_LOG_ENTER();
auto map = getObjectMap(RIDTOVID);
std::unordered_map<sai_object_id_t, sai_object_id_t> filtered;
for (auto& r2v: map)
{
auto switchId = VidManager::switchIdQuery(r2v.second);
if (switchId == switchVid)
{
filtered[r2v.first] = r2v.second;
}
}
return filtered;
}
std::unordered_map<sai_object_id_t, sai_object_id_t> RedisClient::getVidToRidMap() const
{
SWSS_LOG_ENTER();
return getObjectMap(VIDTORID);
}
std::unordered_map<sai_object_id_t, sai_object_id_t> RedisClient::getRidToVidMap() const
{
SWSS_LOG_ENTER();
return getObjectMap(RIDTOVID);
}
void RedisClient::setDummyAsicStateObject(
_In_ sai_object_id_t objectVid)
{
SWSS_LOG_ENTER();
sai_object_type_t objectType = VidManager::objectTypeQuery(objectVid);
std::string strObjectType = sai_serialize_object_type(objectType);
std::string strVid = sai_serialize_object_id(objectVid);
std::string strKey = ASIC_STATE_TABLE + (":" + strObjectType + ":" + strVid);
m_dbAsic->hset(strKey, "NULL", "NULL");
}
std::string RedisClient::getRedisColdVidsKey(
_In_ sai_object_id_t switchVid) const
{
SWSS_LOG_ENTER();
/*
* Each switch will have it's own cold vids: COLDVIDS:oid:0xYYYYYYYY.
*
* NOTE: To support multiple switches COLDVIDS needs to be made per switch.
*
* return std::string(COLDVIDS) + ":" + sai_serialize_object_id(m_switch_vid);
*
* Only switch with index 0 and global context 0 will have key "COLDVIDS" for
* backward compatibility. We could convert that during runtime at first
* time.
*/
auto index = VidManager::getSwitchIndex(switchVid);
auto context = VidManager::getGlobalContext(switchVid);
if (index == 0 && context == 0)
{
return std::string(COLDVIDS);
}
return (COLDVIDS ":") + sai_serialize_object_id(switchVid);
}
void RedisClient::saveColdBootDiscoveredVids(
_In_ sai_object_id_t switchVid,
_In_ const std::set<sai_object_id_t>& coldVids)
{
SWSS_LOG_ENTER();
auto key = getRedisColdVidsKey(switchVid);
for (auto vid: coldVids)
{
sai_object_type_t objectType = VidManager::objectTypeQuery(vid);
std::string strObjectType = sai_serialize_object_type(objectType);
std::string strVid = sai_serialize_object_id(vid);
m_dbAsic->hset(key, strVid, strObjectType);
}
}
std::string RedisClient::getRedisHiddenKey(
_In_ sai_object_id_t switchVid) const
{
SWSS_LOG_ENTER();
/*
* Each switch will have it's own hidden: HIDDEN:oid:0xYYYYYYYY.
*
* NOTE: To support multiple switches HIDDEN needs to be made per switch.
*
* return std::string(HIDDEN) + ":" + sai_serialize_object_id(m_switch_vid);
*
* Only switch with index 0 and global context 0 will have key "HIDDEN" for
* backward compatibility. We could convert that during runtime at first
* time.
*/
auto index = VidManager::getSwitchIndex(switchVid);
auto context = VidManager::getGlobalContext(switchVid);
if (index == 0 && context == 0)
{
return std::string(HIDDEN);
}
return (HIDDEN ":") + sai_serialize_object_id(switchVid);
}
std::shared_ptr<std::string> RedisClient::getSwitchHiddenAttribute(
_In_ sai_object_id_t switchVid,
_In_ const std::string& attrIdName)
{
SWSS_LOG_ENTER();
auto key = getRedisHiddenKey(switchVid);
return m_dbAsic->hget(key, attrIdName);
}
void RedisClient::saveSwitchHiddenAttribute(
_In_ sai_object_id_t switchVid,
_In_ const std::string& attrIdName,
_In_ sai_object_id_t objectRid)
{
SWSS_LOG_ENTER();
auto key = getRedisHiddenKey(switchVid);
std::string strRid = sai_serialize_object_id(objectRid);
m_dbAsic->hset(key, attrIdName, strRid);
}
std::set<sai_object_id_t> RedisClient::getColdVids(
_In_ sai_object_id_t switchVid)
{
SWSS_LOG_ENTER();
auto key = getRedisColdVidsKey(switchVid);
auto hash = m_dbAsic->hgetall(key);
/*
* NOTE: some objects may not exists after 2nd restart, like VLAN_MEMBER or
* BRIDGE_PORT, since user could decide to remove them on previous boot.
*/
std::set<sai_object_id_t> coldVids;
for (auto kvp: hash)
{
auto strVid = kvp.first;
sai_object_id_t vid;
sai_deserialize_object_id(strVid, vid);
/*
* Just make sure that vid in COLDVIDS is present in current vid2rid map
*/
auto rid = m_dbAsic->hget(VIDTORID, strVid);
if (rid == nullptr)
{
SWSS_LOG_INFO("no RID for VID %s, probably object was removed previously", strVid.c_str());
}
coldVids.insert(vid);
}
return coldVids;
}
void RedisClient::setPortLanes(
_In_ sai_object_id_t switchVid,
_In_ sai_object_id_t portRid,
_In_ const std::vector<uint32_t>& lanes)
{
SWSS_LOG_ENTER();
auto key = getRedisLanesKey(switchVid);
for (uint32_t lane: lanes)
{
std::string strLane = sai_serialize_number(lane);
std::string strPortRid = sai_serialize_object_id(portRid);
m_dbAsic->hset(key, strLane, strPortRid);
}
}
size_t RedisClient::getAsicObjectsSize(
_In_ sai_object_id_t switchVid) const
{
SWSS_LOG_ENTER();
// NOTE: this goes over all objects, and if we have N switches then it will
// go N times on every switch and it can be slow, we need to find better
// way to do this
auto keys = m_dbAsic->keys(ASIC_STATE_TABLE ":*");
size_t count = 0;
for (auto& key: keys)
{
auto mk = key.substr(key.find_first_of(":") + 1);
sai_object_meta_key_t metaKey;
sai_deserialize_object_meta_key(mk, metaKey);
// we need to check only objects that's belong to requested switch
auto swid = VidManager::switchIdQuery(metaKey.objectkey.key.object_id);
if (swid == switchVid)
{
count++;
}
}
return count;
}
int RedisClient::removePortFromLanesMap(
_In_ sai_object_id_t switchVid,
_In_ sai_object_id_t portRid) const
{
SWSS_LOG_ENTER();
// key - lane number, value - port RID
auto map = getLaneMap(switchVid);
int removed = 0;
auto key = getRedisLanesKey(switchVid);
for (auto& kv: map)
{
if (kv.second == portRid)
{
std::string strLane = sai_serialize_number(kv.first);
m_dbAsic->hdel(key, strLane);
removed++;
}
}
return removed;
}
void RedisClient::removeAsicObject(
_In_ sai_object_id_t objectVid) const
{
SWSS_LOG_ENTER();
sai_object_type_t ot = VidManager::objectTypeQuery(objectVid);
auto strVid = sai_serialize_object_id(objectVid);
std::string key = (ASIC_STATE_TABLE ":") + sai_serialize_object_type(ot) + ":" + strVid;
SWSS_LOG_INFO("removing ASIC DB key: %s", key.c_str());
m_dbAsic->del(key);
}
void RedisClient::removeAsicObject(
_In_ const sai_object_meta_key_t& metaKey)
{
SWSS_LOG_ENTER();
std::string key = (ASIC_STATE_TABLE ":") + sai_serialize_object_meta_key(metaKey);
m_dbAsic->del(key);
}
void RedisClient::removeTempAsicObject(
_In_ const sai_object_meta_key_t& metaKey)
{
SWSS_LOG_ENTER();
std::string key = (TEMP_PREFIX ASIC_STATE_TABLE ":") + sai_serialize_object_meta_key(metaKey);
m_dbAsic->del(key);
}
void RedisClient::removeAsicObjects(
_In_ const std::vector<std::string>& keys)
{
SWSS_LOG_ENTER();
std::vector<std::string> prefixKeys;
// we need to rewrite keys to add table prefix
for (const auto& key: keys)
{
prefixKeys.push_back((ASIC_STATE_TABLE ":") + key);
}
m_dbAsic->del(prefixKeys);
}
void RedisClient::removeTempAsicObjects(
_In_ const std::vector<std::string>& keys)
{
SWSS_LOG_ENTER();
std::vector<std::string> prefixKeys;
// we need to rewrite keys to add table prefix
for (const auto& key: keys)
{
prefixKeys.push_back((TEMP_PREFIX ASIC_STATE_TABLE ":") + key);
}
m_dbAsic->del(prefixKeys);
}
void RedisClient::setAsicObject(
_In_ const sai_object_meta_key_t& metaKey,
_In_ const std::string& attr,
_In_ const std::string& value)
{
SWSS_LOG_ENTER();
std::string key = (ASIC_STATE_TABLE ":") + sai_serialize_object_meta_key(metaKey);
m_dbAsic->hset(key, attr, value);
}
void RedisClient::setTempAsicObject(
_In_ const sai_object_meta_key_t& metaKey,
_In_ const std::string& attr,
_In_ const std::string& value)
{
SWSS_LOG_ENTER();
std::string key = (TEMP_PREFIX ASIC_STATE_TABLE ":") + sai_serialize_object_meta_key(metaKey);
m_dbAsic->hset(key, attr, value);
}
void RedisClient::createAsicObject(
_In_ const sai_object_meta_key_t& metaKey,
_In_ const std::vector<swss::FieldValueTuple>& attrs)
{
SWSS_LOG_ENTER();
std::string key = (ASIC_STATE_TABLE ":") + sai_serialize_object_meta_key(metaKey);
if (attrs.size() == 0)
{
m_dbAsic->hset(key, "NULL", "NULL");
return;
}
for (const auto& e: attrs)
{
m_dbAsic->hset(key, fvField(e), fvValue(e));
}
}
void RedisClient::createTempAsicObject(
_In_ const sai_object_meta_key_t& metaKey,
_In_ const std::vector<swss::FieldValueTuple>& attrs)
{
SWSS_LOG_ENTER();
std::string key = (TEMP_PREFIX ASIC_STATE_TABLE ":") + sai_serialize_object_meta_key(metaKey);
if (attrs.size() == 0)
{
m_dbAsic->hset(key, "NULL", "NULL");
return;
}
for (const auto& e: attrs)
{
m_dbAsic->hset(key, fvField(e), fvValue(e));
}
}
void RedisClient::createAsicObjects(
_In_ const std::unordered_map<std::string, std::vector<swss::FieldValueTuple>>& multiHash)
{
SWSS_LOG_ENTER();
std::unordered_map<std::string, std::vector<std::pair<std::string, std::string>>> hash;
// we need to rewrite hash to add table prefix
for (const auto& kvp: multiHash)
{
hash[(ASIC_STATE_TABLE ":") + kvp.first] = kvp.second;
if (kvp.second.size() == 0)
{
hash[(ASIC_STATE_TABLE ":") + kvp.first].emplace_back(std::make_pair<std::string, std::string>("NULL", "NULL"));
}
}
m_dbAsic->hmset(hash);
}
void RedisClient::createTempAsicObjects(
_In_ const std::unordered_map<std::string, std::vector<swss::FieldValueTuple>>& multiHash)
{
SWSS_LOG_ENTER();
std::unordered_map<std::string, std::vector<std::pair<std::string, std::string>>> hash;
// we need to rewrite hash to add table prefix
for (const auto& kvp: multiHash)
{
hash[(TEMP_PREFIX ASIC_STATE_TABLE ":") + kvp.first] = kvp.second;
if (kvp.second.size() == 0)
{
hash[(TEMP_PREFIX ASIC_STATE_TABLE ":") + kvp.first].emplace_back(std::make_pair<std::string, std::string>("NULL", "NULL"));
}
}
m_dbAsic->hmset(hash);
}
void RedisClient::setVidAndRidMap(
_In_ const std::unordered_map<sai_object_id_t, sai_object_id_t>& map)
{
SWSS_LOG_ENTER();
m_dbAsic->del(VIDTORID);
m_dbAsic->del(RIDTOVID);
for (auto &kv: map)
{
std::string strVid = sai_serialize_object_id(kv.first);
std::string strRid = sai_serialize_object_id(kv.second);
m_dbAsic->hset(VIDTORID, strVid, strRid);
m_dbAsic->hset(RIDTOVID, strRid, strVid);
}
}
std::vector<std::string> RedisClient::getAsicStateKeys() const
{
SWSS_LOG_ENTER();
return m_dbAsic->keys(ASIC_STATE_TABLE ":*");
}
std::vector<std::string> RedisClient::getAsicStateSwitchesKeys() const
{
SWSS_LOG_ENTER();
return m_dbAsic->keys(ASIC_STATE_TABLE ":SAI_OBJECT_TYPE_SWITCH:*");
}
void RedisClient::removeColdVid(
_In_ sai_object_id_t vid)
{
SWSS_LOG_ENTER();
auto strVid = sai_serialize_object_id(vid);
auto key = getRedisColdVidsKey(vid);
m_dbAsic->hdel(key, strVid);
}
std::unordered_map<std::string, std::string> RedisClient::getAttributesFromAsicKey(
_In_ const std::string& key) const
{
SWSS_LOG_ENTER();
std::unordered_map<std::string, std::string> map;
m_dbAsic->hgetall(key, std::inserter(map, map.end()));
return map;
}
bool RedisClient::hasNoHiddenKeysDefined() const
{
SWSS_LOG_ENTER();
auto keys = m_dbAsic->keys(HIDDEN "*");
return keys.size() == 0;
}
void RedisClient::removeVidAndRid(
_In_ sai_object_id_t vid,
_In_ sai_object_id_t rid)
{
SWSS_LOG_ENTER();
auto strVid = sai_serialize_object_id(vid);
auto strRid = sai_serialize_object_id(rid);
m_dbAsic->hdel(VIDTORID, strVid);
m_dbAsic->hdel(RIDTOVID, strRid);
}
void RedisClient::insertVidAndRid(
_In_ sai_object_id_t vid,
_In_ sai_object_id_t rid)
{
SWSS_LOG_ENTER();
auto strVid = sai_serialize_object_id(vid);
auto strRid = sai_serialize_object_id(rid);
m_dbAsic->hset(VIDTORID, strVid, strRid);
m_dbAsic->hset(RIDTOVID, strRid, strVid);
}
sai_object_id_t RedisClient::getVidForRid(
_In_ sai_object_id_t rid)
{
SWSS_LOG_ENTER();
auto strRid = sai_serialize_object_id(rid);
auto pvid = m_dbAsic->hget(RIDTOVID, strRid);
if (pvid == nullptr)
{
// rid2vid map should never contain null, so we can return NULL which
// will mean that mapping don't exists
return SAI_NULL_OBJECT_ID;
}
sai_object_id_t vid;
sai_deserialize_object_id(*pvid, vid);
return vid;
}
sai_object_id_t RedisClient::getRidForVid(
_In_ sai_object_id_t vid)
{
SWSS_LOG_ENTER();
auto strVid = sai_serialize_object_id(vid);
auto prid = m_dbAsic->hget(VIDTORID, strVid);
if (prid == nullptr)
{
// rid2vid map should never contain null, so we can return NULL which
// will mean that mapping don't exists
return SAI_NULL_OBJECT_ID;
}
sai_object_id_t rid;
sai_deserialize_object_id(*prid, rid);
return rid;
}
void RedisClient::removeAsicStateTable()
{
SWSS_LOG_ENTER();
const auto &asicStateKeys = m_dbAsic->keys(ASIC_STATE_TABLE ":*");
for (const auto &key: asicStateKeys)
{
m_dbAsic->del(key);
}
}
void RedisClient::removeTempAsicStateTable()
{
SWSS_LOG_ENTER();
const auto &tempAsicStateKeys = m_dbAsic->keys(TEMP_PREFIX ASIC_STATE_TABLE ":*");
for (const auto &key: tempAsicStateKeys)
{
m_dbAsic->del(key);
}
}
std::map<sai_object_id_t, swss::TableDump> RedisClient::getAsicView()
{
SWSS_LOG_ENTER();
return getAsicView(ASIC_STATE_TABLE);
}
std::map<sai_object_id_t, swss::TableDump> RedisClient::getTempAsicView()
{
SWSS_LOG_ENTER();
return getAsicView(TEMP_PREFIX ASIC_STATE_TABLE);
}
std::map<sai_object_id_t, swss::TableDump> RedisClient::getAsicView(
_In_ const std::string &tableName)
{
SWSS_LOG_ENTER();
SWSS_LOG_TIMER("get asic view from %s", tableName.c_str());
swss::Table table(m_dbAsic.get(), tableName);
swss::TableDump dump;
table.dump(dump);
std::map<sai_object_id_t, swss::TableDump> map;
for (auto& key: dump)
{
sai_object_meta_key_t mk;
sai_deserialize_object_meta_key(key.first, mk);
auto switchVID = VidManager::switchIdQuery(mk.objectkey.key.object_id);
map[switchVID][key.first] = key.second;
}
SWSS_LOG_NOTICE("%s switch count: %zu:", tableName.c_str(), map.size());
for (auto& kvp: map)
{
SWSS_LOG_NOTICE("%s: objects count: %zu",
sai_serialize_object_id(kvp.first).c_str(),
kvp.second.size());
}
return map;
}
void RedisClient::processFlushEvent(
_In_ sai_object_id_t switchVid,
_In_ sai_object_id_t portVid,
_In_ sai_object_id_t bvId,
_In_ sai_fdb_flush_entry_type_t type)
{
SWSS_LOG_ENTER();
// TODO this must be per switch if we will have multiple switches, needs to be filtered by switch ID also
/*
[{ "fdb_entry":"{ \"bridge_id\":\"oid:0x23000000000000\", \"mac\":\"00:00:00:00:00:00\", \"switch_id\":\"oid:0x21000000000000\"}", "fdb_event":"SAI_FDB_EVENT_FLUSHED", "list":[
{"id":"SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID","value":"oid:0x3a0000000009cf"},
{"id":"SAI_FDB_ENTRY_ATTR_TYPE","value":"SAI_FDB_ENTRY_TYPE_DYNAMIC"},
{"id":"SAI_FDB_ENTRY_ATTR_PACKET_ACTION","value":"SAI_PACKET_ACTION_FORWARD"} ] }]
*/
SWSS_LOG_NOTICE("received a flush port fdb event, portVid = %s, bvId = %s",
sai_serialize_object_id(portVid).c_str(),
sai_serialize_object_id(bvId).c_str());
// TODO script must be updated to version 2
std::string pattern = (bvId == SAI_NULL_OBJECT_ID)
? (ASIC_STATE_TABLE ":SAI_OBJECT_TYPE_FDB_ENTRY:*")
: (ASIC_STATE_TABLE ":SAI_OBJECT_TYPE_FDB_ENTRY:*" + sai_serialize_object_id(bvId) + "*");
std::string portStr = (portVid == SAI_NULL_OBJECT_ID) ? "" : sai_serialize_object_id(portVid);
SWSS_LOG_NOTICE("pattern %s, portStr %s", pattern.c_str(), portStr.c_str());
std::vector<int> vals; // 0 - flush dynamic, 1 - flush static
switch (type)
{
case SAI_FDB_FLUSH_ENTRY_TYPE_DYNAMIC:
vals.push_back(0);
break;
case SAI_FDB_FLUSH_ENTRY_TYPE_STATIC:
vals.push_back(1);
break;
case SAI_FDB_FLUSH_ENTRY_TYPE_ALL:
vals.push_back(0);
vals.push_back(1);
break;
default:
SWSS_LOG_THROW("unknown fdb flush entry type: %d", type);
}
for (int flush_static: vals)
{
swss::RedisCommand command;
command.format(
"EVALSHA %s 3 %s %s %s",
m_fdbFlushSha.c_str(),
pattern.c_str(),
portStr.c_str(),
std::to_string(flush_static).c_str());
swss::RedisReply r(m_dbAsic.get(), command);
}
}