saiplayer/SaiPlayer.cpp (2,148 lines of code) (raw):
#include "SaiPlayer.h"
#include "sairedis.h"
#include "sairediscommon.h"
#include "VirtualObjectIdManager.h"
#include "meta/sai_serialize.h"
#include "meta/PerformanceIntervalTimer.h"
#include "meta/SaiAttributeList.h"
#include "swss/logger.h"
#include "swss/tokenize.h"
#include <inttypes.h>
#include <getopt.h>
#include <unistd.h>
#include <string.h>
#include <iostream>
#include <stdexcept>
#include <sstream>
#include <string>
/*
* Since this is player, we record actions from orchagent. No special case
* should be needed for switch in case it contains some oid values (like in
* syncd cold restart) since orchagent should never create switch with oid
* values set at creation time.
*/
using namespace saiplayer;
using namespace saimeta;
using namespace sairediscommon;
using namespace std::placeholders;
#define CALL_BULK_CREATE_API_WITH_TIMER(entry) \
SWSS_LOG_INFO("executing BULK create "#entry", count = %zu ", entries.size()); \
static PerformanceIntervalTimer timer("SaiPlayer::handle_bulk_entry::bulkCreate("#entry")"); \
timer.start(); \
status = m_sai->bulkCreate(object_count, entries.data(), attr_counts.data(), \
attr_lists.data(), mode, statuses.data()); \
timer.stop(); \
timer.inc(entries.size());
#define CALL_BULK_REMOVE_API_WITH_TIMER(entry) \
SWSS_LOG_INFO("executing BULK remove " #entry ", count = %zu ", entries.size()); \
static PerformanceIntervalTimer timer("SaiPlayer::handle_bulk_entry::bulkRemove("#entry")"); \
timer.start(); \
status = m_sai->bulkRemove(object_count, entries.data(), \
mode, statuses.data()); \
timer.stop(); \
timer.inc(entries.size());
#define CALL_BULK_SET_API_WITH_TIMER(entry) \
SWSS_LOG_INFO("executing BULK set " #entry ", count = %zu ", entries.size()); \
static PerformanceIntervalTimer timer("SaiPlayer::handle_bulk_entry::bulkSet("#entry")"); \
timer.start(); \
status = m_sai->bulkSet(object_count, entries.data(), \
attr_lists.data(), mode, statuses.data()); \
timer.stop(); \
timer.inc(entries.size());
SaiPlayer::SaiPlayer(
_In_ std::shared_ptr<sairedis::SaiInterface> sai,
_In_ std::shared_ptr<CommandLineOptions> cmd):
m_sai(sai),
m_commandLineOptions(cmd)
{
SWSS_LOG_ENTER();
SWSS_LOG_NOTICE("cmd: %s", cmd->getCommandLineString().c_str());
loadProfileMap();
if (cmd->m_contextConfig.size())
{
SWSS_LOG_NOTICE("using command line contextConfig instead SAI_REDIS_KEY_CONTEXT_CONFIG in profile.ini");
m_profileMap[SAI_REDIS_KEY_CONTEXT_CONFIG] = cmd->m_contextConfig;
}
m_profileIter = m_profileMap.begin();
m_smt.profileGetValue = std::bind(&SaiPlayer::profileGetValue, this, _1, _2);
m_smt.profileGetNextValue = std::bind(&SaiPlayer::profileGetNextValue, this, _1, _2, _3);
m_sn.onFdbEvent = std::bind(&SaiPlayer::onFdbEvent, this, _1, _2);
m_sn.onPortStateChange = std::bind(&SaiPlayer::onPortStateChange, this, _1, _2);
m_sn.onQueuePfcDeadlock = std::bind(&SaiPlayer::onQueuePfcDeadlock, this, _1, _2);
m_sn.onSwitchAsicSdkHealthEvent = std::bind(&SaiPlayer::onSwitchAsicSdkHealthEvent, this, _1, _2, _3, _4, _5, _6);
m_sn.onSwitchShutdownRequest = std::bind(&SaiPlayer::onSwitchShutdownRequest, this, _1);
m_sn.onSwitchStateChange = std::bind(&SaiPlayer::onSwitchStateChange, this, _1, _2);
m_sn.onBfdSessionStateChange = std::bind(&SaiPlayer::onBfdSessionStateChange, this, _1, _2);
m_sn.onPortHostTxReady = std::bind(&SaiPlayer::onPortHostTxReady, this, _1, _2, _3);
m_switchNotifications= m_sn.getSwitchNotifications();
}
SaiPlayer::~SaiPlayer()
{
SWSS_LOG_ENTER();
// empty
}
void SaiPlayer::loadProfileMap()
{
SWSS_LOG_ENTER();
if (m_commandLineOptions->m_profileMapFile.size() == 0)
{
SWSS_LOG_NOTICE("profile map file not specified");
return;
}
std::ifstream profile(m_commandLineOptions->m_profileMapFile);
if (!profile.is_open())
{
SWSS_LOG_ERROR("failed to open profile map file: %s: %s",
m_commandLineOptions->m_profileMapFile.c_str(),
strerror(errno));
exit(EXIT_FAILURE);
}
std::string line;
while (getline(profile, line))
{
if (line.size() > 0 && (line[0] == '#' || line[0] == ';'))
{
continue;
}
size_t pos = line.find("=");
if (pos == std::string::npos)
{
SWSS_LOG_WARN("not found '=' in line %s", line.c_str());
continue;
}
std::string key = line.substr(0, pos);
std::string value = line.substr(pos + 1);
m_profileMap[key] = value;
SWSS_LOG_INFO("insert: %s:%s", key.c_str(), value.c_str());
}
}
void SaiPlayer::onFdbEvent(
_In_ uint32_t count,
_In_ const sai_fdb_event_notification_data_t *data)
{
SWSS_LOG_ENTER();
// empty
}
void SaiPlayer::onPortStateChange(
_In_ uint32_t count,
_In_ const sai_port_oper_status_notification_t *data)
{
SWSS_LOG_ENTER();
// empty
}
void SaiPlayer::onBfdSessionStateChange(
_In_ uint32_t count,
_In_ const sai_bfd_session_state_notification_t *data)
{
SWSS_LOG_ENTER();
// empty
}
void SaiPlayer::onPortHostTxReady(
_In_ sai_object_id_t switch_id,
_In_ sai_object_id_t port_id,
_In_ sai_port_host_tx_ready_status_t host_tx_ready_status)
{
SWSS_LOG_ENTER();
// empty
}
void SaiPlayer::onQueuePfcDeadlock(
_In_ uint32_t count,
_In_ const sai_queue_deadlock_notification_data_t *data)
{
SWSS_LOG_ENTER();
// empty
}
void SaiPlayer::onSwitchAsicSdkHealthEvent(
_In_ sai_object_id_t switch_id,
_In_ sai_switch_asic_sdk_health_severity_t severity,
_In_ sai_timespec_t timestamp,
_In_ sai_switch_asic_sdk_health_category_t category,
_In_ sai_switch_health_data_t data,
_In_ const sai_u8_list_t description)
{
SWSS_LOG_ENTER();
// empty
}
void SaiPlayer::onSwitchShutdownRequest(
_In_ sai_object_id_t switch_id)
{
SWSS_LOG_ENTER();
SWSS_LOG_ERROR("got shutdown request, syncd failed!");
exit(EXIT_FAILURE);
}
void SaiPlayer::onSwitchStateChange(
_In_ sai_object_id_t switch_id,
_In_ sai_switch_oper_status_t switch_oper_status)
{
SWSS_LOG_ENTER();
// empty
}
#define EXIT_ON_ERROR(x)\
{\
sai_status_t s = (x);\
if (s != SAI_STATUS_SUCCESS)\
{\
SWSS_LOG_THROW("fail status: %s", sai_serialize_status(s).c_str());\
}\
}
sai_object_id_t SaiPlayer::translate_local_to_redis(
_In_ sai_object_id_t rid)
{
SWSS_LOG_ENTER();
SWSS_LOG_DEBUG("translating local RID %s",
sai_serialize_object_id(rid).c_str());
if (rid == SAI_NULL_OBJECT_ID)
{
return SAI_NULL_OBJECT_ID;
}
auto it = m_local_to_redis.find(rid);
if (it == m_local_to_redis.end())
{
SWSS_LOG_THROW("failed to translate local RID %s",
sai_serialize_object_id(rid).c_str());
}
return it->second;
}
void SaiPlayer::translate_local_to_redis(
_Inout_ sai_object_list_t& element)
{
SWSS_LOG_ENTER();
for (uint32_t i = 0; i < element.count; i++)
{
element.list[i] = translate_local_to_redis(element.list[i]);
}
}
void SaiPlayer::translate_local_to_redis(
_In_ sai_object_type_t object_type,
_In_ uint32_t attr_count,
_In_ sai_attribute_t *attr_list)
{
SWSS_LOG_ENTER();
for (uint32_t i = 0; i < attr_count; i++)
{
sai_attribute_t &attr = attr_list[i];
auto meta = sai_metadata_get_attr_metadata(object_type, attr.id);
if (meta == NULL)
{
SWSS_LOG_THROW("unable to get metadata for object type %s, attribute %d",
sai_serialize_object_type(object_type).c_str(),
attr.id);
}
switch (meta->attrvaluetype)
{
case SAI_ATTR_VALUE_TYPE_OBJECT_ID:
attr.value.oid = translate_local_to_redis(attr.value.oid);
break;
case SAI_ATTR_VALUE_TYPE_OBJECT_LIST:
translate_local_to_redis(attr.value.objlist);
break;
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_OBJECT_ID:
if (attr.value.aclfield.enable)
attr.value.aclfield.data.oid = translate_local_to_redis(attr.value.aclfield.data.oid);
break;
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_OBJECT_LIST:
if (attr.value.aclfield.enable)
translate_local_to_redis( 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 = translate_local_to_redis(attr.value.aclaction.parameter.oid);
break;
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_OBJECT_LIST:
if (attr.value.aclaction.enable)
translate_local_to_redis(attr.value.aclaction.parameter.objlist);
break;
default:
if (meta->isoidattribute)
{
SWSS_LOG_THROW("attribute %s is oid attribute but not handled, FIXME", meta->attridname);
}
break;
}
}
}
sai_object_type_t SaiPlayer::deserialize_object_type(
_In_ const std::string& s)
{
SWSS_LOG_ENTER();
sai_object_type_t object_type;
sai_deserialize_object_type(s, object_type);
return object_type;
}
const std::vector<swss::FieldValueTuple> SaiPlayer::get_values(
_In_ const std::vector<std::string>& items)
{
SWSS_LOG_ENTER();
std::vector<swss::FieldValueTuple> values;
// timestamp|action|objecttype:objectid|attrid=value,...
for (size_t i = 3; i <items.size(); ++i)
{
const std::string& item = items[i];
auto start = item.find_first_of("=");
auto field = item.substr(0, start);
auto value = item.substr(start + 1);
swss::FieldValueTuple entry(field, value);
values.push_back(entry);
}
return values;
}
#define CHECK_LIST(x) \
if (attr.x.count != get_attr.x.count) { \
SWSS_LOG_THROW("get response list count not match recording %u vs %u (expected)", get_attr.x.count, attr.x.count); }
void SaiPlayer::match_list_lengths(
_In_ sai_object_type_t object_type,
_In_ uint32_t get_attr_count,
_In_ sai_attribute_t* get_attr_list,
_In_ uint32_t attr_count,
_In_ sai_attribute_t* attr_list)
{
SWSS_LOG_ENTER();
if (get_attr_count != attr_count)
{
SWSS_LOG_THROW("list number don't match %u != %u (%s)", get_attr_count, attr_count,
sai_serialize_object_type(object_type).c_str());
}
for (uint32_t i = 0; i < attr_count; ++i)
{
sai_attribute_t &get_attr = get_attr_list[i];
sai_attribute_t &attr = attr_list[i];
auto meta = sai_metadata_get_attr_metadata(object_type, attr.id);
if (meta == NULL)
{
SWSS_LOG_THROW("unable to get metadata for object type %s, attribute %d",
sai_serialize_object_type(object_type).c_str(),
attr.id);
}
switch (meta->attrvaluetype)
{
case SAI_ATTR_VALUE_TYPE_OBJECT_LIST:
CHECK_LIST(value.objlist);
break;
case SAI_ATTR_VALUE_TYPE_UINT8_LIST:
CHECK_LIST(value.u8list);
break;
case SAI_ATTR_VALUE_TYPE_INT8_LIST:
CHECK_LIST(value.s8list);
break;
case SAI_ATTR_VALUE_TYPE_UINT16_LIST:
CHECK_LIST(value.u16list);
break;
case SAI_ATTR_VALUE_TYPE_INT16_LIST:
CHECK_LIST(value.s16list);
break;
case SAI_ATTR_VALUE_TYPE_UINT32_LIST:
CHECK_LIST(value.u32list);
break;
case SAI_ATTR_VALUE_TYPE_INT32_LIST:
CHECK_LIST(value.s32list);
break;
case SAI_ATTR_VALUE_TYPE_VLAN_LIST:
CHECK_LIST(value.vlanlist);
break;
case SAI_ATTR_VALUE_TYPE_QOS_MAP_LIST:
CHECK_LIST(value.qosmap);
break;
case SAI_ATTR_VALUE_TYPE_IP_ADDRESS_LIST:
CHECK_LIST(value.ipaddrlist);
break;
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_OBJECT_LIST:
CHECK_LIST(value.aclfield.data.objlist);
break;
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_UINT8_LIST:
CHECK_LIST(value.aclfield.data.u8list);
CHECK_LIST(value.aclfield.mask.u8list);
break;
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_OBJECT_LIST:
CHECK_LIST(value.aclaction.parameter.objlist);
break;
default:
break;
}
}
}
void SaiPlayer::match_redis_with_rec(
_In_ sai_object_id_t get_oid,
_In_ sai_object_id_t oid)
{
SWSS_LOG_ENTER();
auto it = m_redis_to_local.find(get_oid);
if (it == m_redis_to_local.end())
{
m_redis_to_local[get_oid] = oid;
m_local_to_redis[oid] = get_oid;
}
if (oid != m_redis_to_local[get_oid])
{
SWSS_LOG_THROW("match failed, oid order is mismatch :( oid 0x%" PRIx64 " get_oid 0x%" PRIx64 " second 0x%" PRIx64,
oid,
get_oid,
m_redis_to_local[get_oid]);
}
SWSS_LOG_DEBUG("map size: %zu", m_local_to_redis.size());
}
void SaiPlayer::match_redis_with_rec(
_In_ sai_object_list_t get_objlist,
_In_ sai_object_list_t objlist)
{
SWSS_LOG_ENTER();
for (uint32_t i = 0 ; i < get_objlist.count; ++i)
{
match_redis_with_rec(get_objlist.list[i], objlist.list[i]);
}
}
void SaiPlayer::match_redis_with_rec(
_In_ sai_object_type_t object_type,
_In_ uint32_t get_attr_count,
_In_ sai_attribute_t* get_attr_list,
_In_ uint32_t attr_count,
_In_ sai_attribute_t* attr_list)
{
SWSS_LOG_ENTER();
if (get_attr_count != attr_count)
{
SWSS_LOG_THROW("list number don't match %u != %u (%s)", get_attr_count, attr_count,
sai_serialize_object_type(object_type).c_str());
}
for (uint32_t i = 0; i < attr_count; ++i)
{
sai_attribute_t &get_attr = get_attr_list[i];
sai_attribute_t &attr = attr_list[i];
auto meta = sai_metadata_get_attr_metadata(object_type, attr.id);
if (meta == NULL)
{
SWSS_LOG_THROW("unable to get metadata for object type %s, attribute %d",
sai_serialize_object_type(object_type).c_str(),
attr.id);
}
switch (meta->attrvaluetype)
{
case SAI_ATTR_VALUE_TYPE_OBJECT_ID:
match_redis_with_rec(get_attr.value.oid, attr.value.oid);
break;
case SAI_ATTR_VALUE_TYPE_OBJECT_LIST:
match_redis_with_rec(get_attr.value.objlist, attr.value.objlist);
break;
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_OBJECT_ID:
if (attr.value.aclfield.enable)
match_redis_with_rec(get_attr.value.aclfield.data.oid, attr.value.aclfield.data.oid);
break;
case SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA_OBJECT_LIST:
if (attr.value.aclfield.enable)
match_redis_with_rec(get_attr.value.aclfield.data.objlist, attr.value.aclfield.data.objlist);
break;
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_OBJECT_ID:
if (attr.value.aclaction.enable)
match_redis_with_rec(get_attr.value.aclaction.parameter.oid, attr.value.aclaction.parameter.oid);
break;
case SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA_OBJECT_LIST:
if (attr.value.aclaction.enable)
match_redis_with_rec(get_attr.value.aclaction.parameter.objlist, attr.value.aclaction.parameter.objlist);
break;
default:
// XXX if (meta->isoidattribute)
if (meta->allowedobjecttypeslength > 0)
{
SWSS_LOG_THROW("attribute %s is oid attribute but not handled, FIXME", meta->attridname);
}
break;
}
}
}
sai_status_t SaiPlayer::handle_fdb(
_In_ const std::string &str_object_id,
_In_ sai_common_api_t api,
_In_ uint32_t attr_count,
_In_ sai_attribute_t *attr_list)
{
SWSS_LOG_ENTER();
sai_fdb_entry_t fdb_entry;
sai_deserialize_fdb_entry(str_object_id, fdb_entry);
fdb_entry.switch_id = translate_local_to_redis(fdb_entry.switch_id);
fdb_entry.bv_id = translate_local_to_redis(fdb_entry.bv_id);
switch (api)
{
case SAI_COMMON_API_CREATE:
return m_sai->create(&fdb_entry, attr_count, attr_list);
case SAI_COMMON_API_REMOVE:
return m_sai->remove(&fdb_entry);
case SAI_COMMON_API_SET:
return m_sai->set(&fdb_entry, attr_list);
case SAI_COMMON_API_GET:
return m_sai->get(&fdb_entry, attr_count, attr_list);
default:
SWSS_LOG_THROW("fdb other apis not implemented");
}
return SAI_STATUS_SUCCESS;
}
sai_status_t SaiPlayer::handle_neighbor(
_In_ const std::string &str_object_id,
_In_ sai_common_api_t api,
_In_ uint32_t attr_count,
_In_ sai_attribute_t *attr_list)
{
SWSS_LOG_ENTER();
sai_neighbor_entry_t neighbor_entry;
sai_deserialize_neighbor_entry(str_object_id, neighbor_entry);
neighbor_entry.switch_id = translate_local_to_redis(neighbor_entry.switch_id);
neighbor_entry.rif_id = translate_local_to_redis(neighbor_entry.rif_id);
switch (api)
{
case SAI_COMMON_API_CREATE:
return m_sai->create(&neighbor_entry, attr_count, attr_list);
case SAI_COMMON_API_REMOVE:
return m_sai->remove(&neighbor_entry);
case SAI_COMMON_API_SET:
return m_sai->set(&neighbor_entry, attr_list);
case SAI_COMMON_API_GET:
return m_sai->get(&neighbor_entry, attr_count, attr_list);
default:
SWSS_LOG_THROW("neighbor other apis not implemented");
}
}
sai_status_t SaiPlayer::handle_route(
_In_ const std::string &str_object_id,
_In_ sai_common_api_t api,
_In_ uint32_t attr_count,
_In_ sai_attribute_t *attr_list)
{
SWSS_LOG_ENTER();
sai_route_entry_t route_entry;
sai_deserialize_route_entry(str_object_id, route_entry);
route_entry.switch_id = translate_local_to_redis(route_entry.switch_id);
route_entry.vr_id = translate_local_to_redis(route_entry.vr_id);
switch (api)
{
case SAI_COMMON_API_CREATE:
return m_sai->create(&route_entry, attr_count, attr_list);
case SAI_COMMON_API_REMOVE:
return m_sai->remove(&route_entry);
case SAI_COMMON_API_SET:
return m_sai->set(&route_entry, attr_list);
case SAI_COMMON_API_GET:
return m_sai->get(&route_entry, attr_count, attr_list);
default:
SWSS_LOG_THROW("route other apis not implemented");
}
}
sai_status_t SaiPlayer::handle_inseg(
_In_ const std::string &str_object_id,
_In_ sai_common_api_t api,
_In_ uint32_t attr_count,
_In_ sai_attribute_t *attr_list)
{
SWSS_LOG_ENTER();
sai_inseg_entry_t inseg_entry;
sai_deserialize_inseg_entry(str_object_id, inseg_entry);
inseg_entry.switch_id = translate_local_to_redis(inseg_entry.switch_id);
switch(api)
{
case SAI_COMMON_API_CREATE:
return sai_metadata_sai_mpls_api->create_inseg_entry(&inseg_entry, attr_count, attr_list);
case SAI_COMMON_API_REMOVE:
return sai_metadata_sai_mpls_api->remove_inseg_entry(&inseg_entry);
case SAI_COMMON_API_SET:
return sai_metadata_sai_mpls_api->set_inseg_entry_attribute(&inseg_entry, attr_list);
case SAI_COMMON_API_GET:
return sai_metadata_sai_mpls_api->get_inseg_entry_attribute(&inseg_entry, attr_count, attr_list);
default:
SWSS_LOG_THROW("inseg other apis not implemented");
}
}
sai_status_t SaiPlayer::handle_dash_direction_lookup(
_In_ const std::string &str_object_id,
_In_ sai_common_api_t api,
_In_ uint32_t attr_count,
_In_ sai_attribute_t *attr_list)
{
SWSS_LOG_ENTER();
sai_direction_lookup_entry_t entry;
sai_deserialize_direction_lookup_entry(str_object_id, entry);
entry.switch_id = translate_local_to_redis(entry.switch_id);
switch (api)
{
case SAI_COMMON_API_CREATE:
return m_sai->create(&entry, attr_count, attr_list);
case SAI_COMMON_API_REMOVE:
return m_sai->remove(&entry);
case SAI_COMMON_API_SET:
return m_sai->set(&entry, attr_list);
case SAI_COMMON_API_GET:
return m_sai->get(&entry, attr_count, attr_list);
default:
SWSS_LOG_THROW("DASH direction_lookup other apis not implemented");
}
}
sai_status_t SaiPlayer::handle_dash_eni_ether_address_map(
_In_ const std::string &str_object_id,
_In_ sai_common_api_t api,
_In_ uint32_t attr_count,
_In_ sai_attribute_t *attr_list)
{
SWSS_LOG_ENTER();
sai_eni_ether_address_map_entry_t entry;
sai_deserialize_eni_ether_address_map_entry(str_object_id, entry);
entry.switch_id = translate_local_to_redis(entry.switch_id);
switch (api)
{
case SAI_COMMON_API_CREATE:
return m_sai->create(&entry, attr_count, attr_list);
case SAI_COMMON_API_REMOVE:
return m_sai->remove(&entry);
case SAI_COMMON_API_SET:
return m_sai->set(&entry, attr_list);
case SAI_COMMON_API_GET:
return m_sai->get(&entry, attr_count, attr_list);
default:
SWSS_LOG_THROW("DASH eni_ether_address_map_entry other apis not implemented");
}
}
sai_status_t SaiPlayer::handle_dash_vip(
_In_ const std::string &str_object_id,
_In_ sai_common_api_t api,
_In_ uint32_t attr_count,
_In_ sai_attribute_t *attr_list)
{
SWSS_LOG_ENTER();
sai_vip_entry_t entry;
sai_deserialize_vip_entry(str_object_id, entry);
entry.switch_id = translate_local_to_redis(entry.switch_id);
switch (api)
{
case SAI_COMMON_API_CREATE:
return m_sai->create(&entry, attr_count, attr_list);
case SAI_COMMON_API_REMOVE:
return m_sai->remove(&entry);
case SAI_COMMON_API_SET:
return m_sai->set(&entry, attr_list);
case SAI_COMMON_API_GET:
return m_sai->get(&entry, attr_count, attr_list);
default:
SWSS_LOG_THROW("DASH vip other apis not implemented");
}
}
sai_status_t SaiPlayer::handle_dash_inbound_routing(
_In_ const std::string &str_object_id,
_In_ sai_common_api_t api,
_In_ uint32_t attr_count,
_In_ sai_attribute_t *attr_list)
{
SWSS_LOG_ENTER();
sai_inbound_routing_entry_t entry;
sai_deserialize_inbound_routing_entry(str_object_id, entry);
entry.switch_id = translate_local_to_redis(entry.switch_id);
entry.eni_id = translate_local_to_redis(entry.eni_id);
switch (api)
{
case SAI_COMMON_API_CREATE:
return m_sai->create(&entry, attr_count, attr_list);
case SAI_COMMON_API_REMOVE:
return m_sai->remove(&entry);
case SAI_COMMON_API_SET:
return m_sai->set(&entry, attr_list);
case SAI_COMMON_API_GET:
return m_sai->get(&entry, attr_count, attr_list);
default:
SWSS_LOG_THROW("DASH inbound_routing other apis not implemented");
}
}
sai_status_t SaiPlayer::handle_dash_pa_validation(
_In_ const std::string &str_object_id,
_In_ sai_common_api_t api,
_In_ uint32_t attr_count,
_In_ sai_attribute_t *attr_list)
{
SWSS_LOG_ENTER();
sai_pa_validation_entry_t entry;
sai_deserialize_pa_validation_entry(str_object_id, entry);
entry.switch_id = translate_local_to_redis(entry.switch_id);
entry.vnet_id = translate_local_to_redis(entry.vnet_id);
switch (api)
{
case SAI_COMMON_API_CREATE:
return m_sai->create(&entry, attr_count, attr_list);
case SAI_COMMON_API_REMOVE:
return m_sai->remove(&entry);
case SAI_COMMON_API_SET:
return m_sai->set(&entry, attr_list);
case SAI_COMMON_API_GET:
return m_sai->get(&entry, attr_count, attr_list);
default:
SWSS_LOG_THROW("DASH pa_validation other apis not implemented");
}
}
sai_status_t SaiPlayer::handle_dash_outbound_routing(
_In_ const std::string &str_object_id,
_In_ sai_common_api_t api,
_In_ uint32_t attr_count,
_In_ sai_attribute_t *attr_list)
{
SWSS_LOG_ENTER();
sai_outbound_routing_entry_t entry;
sai_deserialize_outbound_routing_entry(str_object_id, entry);
entry.switch_id = translate_local_to_redis(entry.switch_id);
entry.outbound_routing_group_id = translate_local_to_redis(entry.outbound_routing_group_id);
switch (api)
{
case SAI_COMMON_API_CREATE:
return m_sai->create(&entry, attr_count, attr_list);
case SAI_COMMON_API_REMOVE:
return m_sai->remove(&entry);
case SAI_COMMON_API_SET:
return m_sai->set(&entry, attr_list);
case SAI_COMMON_API_GET:
return m_sai->get(&entry, attr_count, attr_list);
default:
SWSS_LOG_THROW("DASH outbound_routing other apis not implemented");
}
}
sai_status_t SaiPlayer::handle_dash_outbound_ca_to_pa(
_In_ const std::string &str_object_id,
_In_ sai_common_api_t api,
_In_ uint32_t attr_count,
_In_ sai_attribute_t *attr_list)
{
SWSS_LOG_ENTER();
sai_outbound_ca_to_pa_entry_t entry;
sai_deserialize_outbound_ca_to_pa_entry(str_object_id, entry);
entry.switch_id = translate_local_to_redis(entry.switch_id);
entry.dst_vnet_id = translate_local_to_redis(entry.dst_vnet_id);
switch (api)
{
case SAI_COMMON_API_CREATE:
return m_sai->create(&entry, attr_count, attr_list);
case SAI_COMMON_API_REMOVE:
return m_sai->remove(&entry);
case SAI_COMMON_API_SET:
return m_sai->set(&entry, attr_list);
case SAI_COMMON_API_GET:
return m_sai->get(&entry, attr_count, attr_list);
default:
SWSS_LOG_THROW("DASH outbound_ca_to_pa other apis not implemented");
}
}
void SaiPlayer::update_notifications_pointers(
_In_ uint32_t attr_count,
_Inout_ sai_attribute_t *attr_list)
{
SWSS_LOG_ENTER();
/*
* Sairedis is updating notifications pointers based on attribute, so when
* we will do replay it will have invalid pointers from orchagent, so we
* need to override them after create, and after set.
*/
sai_metadata_update_attribute_notification_pointers(&m_switchNotifications, attr_count, attr_list);
}
sai_status_t SaiPlayer::handle_generic(
_In_ sai_object_type_t object_type,
_In_ const std::string &str_object_id,
_In_ sai_common_api_t api,
_In_ uint32_t attr_count,
_In_ sai_attribute_t *attr_list)
{
SWSS_LOG_ENTER();
sai_object_id_t local_id;
sai_deserialize_object_id(str_object_id, local_id);
SWSS_LOG_DEBUG("generic %s for %s:%s",
sai_serialize_common_api(api).c_str(),
sai_serialize_object_type(object_type).c_str(),
str_object_id.c_str());
sai_object_meta_key_t meta_key;
meta_key.objecttype = object_type;
std::vector<sai_attribute_t> swattr;
switch (api)
{
case SAI_COMMON_API_CREATE:
{
sai_object_id_t switch_id = m_sai->switchIdQuery(local_id);
if (switch_id == SAI_NULL_OBJECT_ID)
{
SWSS_LOG_THROW("invalid switch_id translated from VID %s",
sai_serialize_object_id(local_id).c_str());
}
if (object_type == SAI_OBJECT_TYPE_SWITCH)
{
update_notifications_pointers(attr_count, attr_list);
/*
* We are creating switch, in both cases local and redis
* switch id is deterministic and should be the same.
*/
/*
* Since now recording could contain switches from multiple
* contexts, we need to pass extra attribute to point to
* the right context when creating switch.
*/
swattr.resize(attr_count + 1);
memcpy(swattr.data(), attr_list, attr_count * sizeof(sai_attribute_t));
swattr[attr_count].id = SAI_REDIS_SWITCH_ATTR_CONTEXT;
swattr[attr_count].value.u32 = sairedis::VirtualObjectIdManager::getGlobalContext(switch_id);
attr_count++;
attr_list = swattr.data();
}
else
{
/*
* When we creating switch, then switch_id parameter is
* ignored, but we can't convert it using vid to rid map,
* since rid don't exist yet, so skip translate for switch,
* but use translate for all other objects.
*/
switch_id = translate_local_to_redis(switch_id);
}
sai_status_t status = m_sai->create(meta_key, switch_id, attr_count, attr_list);
if (status == SAI_STATUS_SUCCESS)
{
sai_object_id_t rid = meta_key.objectkey.key.object_id;
match_redis_with_rec(rid, local_id);
SWSS_LOG_INFO("saved VID %s to RID %s",
sai_serialize_object_id(local_id).c_str(),
sai_serialize_object_id(rid).c_str());
}
else
{
SWSS_LOG_ERROR("failed to create %s",
sai_serialize_status(status).c_str());
}
return status;
}
case SAI_COMMON_API_REMOVE:
{
meta_key.objectkey.key.object_id = translate_local_to_redis(local_id);
return m_sai->remove(meta_key);
}
case SAI_COMMON_API_SET:
{
if (object_type == SAI_OBJECT_TYPE_SWITCH)
{
update_notifications_pointers(1, attr_list);
}
meta_key.objectkey.key.object_id = translate_local_to_redis(local_id);
return m_sai->set(meta_key, attr_list);
}
case SAI_COMMON_API_GET:
{
meta_key.objectkey.key.object_id = translate_local_to_redis(local_id);
return m_sai->get(meta_key, attr_count, attr_list);
}
default:
SWSS_LOG_THROW("generic other apis not implemented");
}
}
void SaiPlayer::handle_get_response(
_In_ sai_object_type_t object_type,
_In_ uint32_t get_attr_count,
_In_ sai_attribute_t* get_attr_list,
_In_ const std::string& response,
_In_ sai_status_t status)
{
SWSS_LOG_ENTER();
// timestamp|action|objecttype:objectid|attrid=value,...
auto v = swss::tokenize(response, '|');
if (status != SAI_STATUS_SUCCESS)
{
sai_status_t expectedStatus;
sai_deserialize_status(v.at(2), expectedStatus);
if (status == expectedStatus)
{
// GET api was not successful but status is equal to recording
return;
}
SWSS_LOG_WARN("status is: %s but expected: %s",
sai_serialize_status(status).c_str(),
sai_serialize_status(expectedStatus).c_str());
return;
}
//std::cout << "processing " << response << std::endl;
auto values = get_values(v);
SaiAttributeList list(object_type, values, false);
sai_attribute_t *attr_list = list.get_attr_list();
uint32_t attr_count = list.get_attr_count();
match_list_lengths(object_type, get_attr_count, get_attr_list, attr_count, attr_list);
SWSS_LOG_DEBUG("list match");
match_redis_with_rec(object_type, get_attr_count, get_attr_list, attr_count, attr_list);
// NOTE: Primitive values are not matched (recording vs switch/vs), we can add that check
}
void SaiPlayer::performSleep(
_In_ const std::string& line)
{
SWSS_LOG_ENTER();
// timestamp|action|sleeptime
auto v = swss::tokenize(line, '|');
if (v.size() < 3)
{
SWSS_LOG_THROW("invalid line %s", line.c_str());
}
uint32_t useconds;
sai_deserialize_number(v[2], useconds);
if (useconds > 0)
{
useconds *= 1000; // 1ms resolution is enough for sleep
SWSS_LOG_NOTICE("usleep(%u)", useconds);
usleep(useconds);
}
}
void SaiPlayer::performNotifySyncd(
_In_ const std::string& request,
_In_ const std::string& response)
{
SWSS_LOG_ENTER();
// timestamp|action|data
auto r = swss::tokenize(request, '|');
auto R = swss::tokenize(response, '|');
if (r[1] != "a" || R[1] != "A")
{
SWSS_LOG_THROW("invalid syncd notify request/response %s/%s", request.c_str(), response.c_str());
}
if (m_commandLineOptions->m_skipNotifySyncd)
{
SWSS_LOG_NOTICE("skipping notify syncd, selected by user");
return;
}
// tell syncd that we are compiling new view
sai_attribute_t attr;
attr.id = SAI_REDIS_SWITCH_ATTR_NOTIFY_SYNCD;
attr.value.s32 = sai_deserialize_redis_notify_syncd(r[2]);
/*
* NOTE: We don't need actual switch to set those attributes.
*/
sai_object_id_t switch_id = SAI_NULL_OBJECT_ID;
sai_status_t status = m_sai->set(SAI_OBJECT_TYPE_SWITCH, switch_id, &attr);
const std::string& responseStatus = R[2];
sai_status_t response_status;
sai_deserialize_status(responseStatus, response_status);
if (status != response_status)
{
SWSS_LOG_THROW("response status %s is different than syncd status %s",
responseStatus.c_str(),
sai_serialize_status(status).c_str());
}
if (status != SAI_STATUS_SUCCESS)
{
SWSS_LOG_THROW("failed to notify syncd %s",
sai_serialize_status(status).c_str());
}
// OK
}
void SaiPlayer::performFdbFlush(
_In_ const std::string& request,
_In_ const std::string response)
{
SWSS_LOG_ENTER();
// 2017-05-13.20:47:24.883499|f|SAI_OBJECT_TYPE_FDB_FLUSH:oid:0x21000000000000|
// 2017-05-13.20:47:24.883499|F|SAI_STATUS_SUCCESS
// timestamp|action|data
auto r = swss::tokenize(request, '|');
auto R = swss::tokenize(response, '|');
if (r[1] != "f" || R[1] != "F")
{
SWSS_LOG_THROW("invalid fdb flush request/response %s/%s", request.c_str(), response.c_str());
}
if (r.size() > 3 && r[3].size() != 0)
{
SWSS_LOG_NOTICE("%zu %zu, %s", r.size(), r[3].size(), r[3].c_str());
// TODO currently we support only flush fdb entries with no attributes
SWSS_LOG_THROW("currently fdb flush supports only no attributes, but some given: %s", request.c_str());
}
// objecttype:objectid (object id may contain ':')
auto& data = r[2];
auto start = data.find_first_of(":");
auto str_object_type = data.substr(0, start);
auto str_object_id = data.substr(start + 1);
sai_object_type_t ot = deserialize_object_type(str_object_type);
if (ot != SAI_OBJECT_TYPE_FDB_FLUSH)
{
SWSS_LOG_THROW("expected object type %s, but got: %s on %s",
sai_serialize_object_type(SAI_OBJECT_TYPE_FDB_FLUSH).c_str(),
str_object_type.c_str(),
request.c_str());
}
sai_object_id_t local_switch_id;
sai_deserialize_object_id(str_object_id, local_switch_id);
if (m_sai->switchIdQuery(local_switch_id) != local_switch_id)
{
SWSS_LOG_THROW("fdb flush object is not switch id: %s, switch_id_query: %s",
str_object_id.c_str(),
sai_serialize_object_id(m_sai->switchIdQuery(local_switch_id)).c_str());
}
auto switch_id = translate_local_to_redis(local_switch_id);
// TODO currently we support only flush fdb entries with no attributes
sai_status_t status = m_sai->flushFdbEntries(switch_id, 0, NULL);
// check status
sai_status_t expected_status;
sai_deserialize_status(R[2], expected_status);
if (status != expected_status)
{
SWSS_LOG_THROW("fdb flush got status %s, but expecting: %s",
sai_serialize_status(status).c_str(),
R[2].c_str());
}
// fdb flush OK
}
std::vector<std::string> SaiPlayer::tokenize(
_In_ std::string input,
_In_ const std::string &delim)
{
SWSS_LOG_ENTER();
/*
* input is modified so it can't be passed as reference
*/
std::vector<std::string> tokens;
size_t pos = 0;
while ((pos = input.find(delim)) != std::string::npos)
{
std::string token = input.substr(0, pos);
input.erase(0, pos + delim.length());
tokens.push_back(token);
}
tokens.push_back(input);
return tokens;
}
sai_status_t SaiPlayer::handle_bulk_route(
_In_ const std::vector<std::string> &object_ids,
_In_ sai_common_api_t api,
_In_ const std::vector<std::shared_ptr<SaiAttributeList>> &attributes,
_In_ const std::vector<sai_status_t>& expectedStatuses)
{
SWSS_LOG_ENTER();
std::vector<sai_route_entry_t> routes;
for (size_t i = 0; i < object_ids.size(); ++i)
{
sai_route_entry_t route_entry;
sai_deserialize_route_entry(object_ids[i], route_entry);
route_entry.vr_id = translate_local_to_redis(route_entry.vr_id);
routes.push_back(route_entry);
SWSS_LOG_DEBUG("route: %s", object_ids[i].c_str());
}
std::vector<sai_status_t> statuses;
statuses.resize(expectedStatuses.size());
if (api == SAI_COMMON_API_BULK_SET)
{
/*
* TODO: since SDK don't support bulk route api yet, we just use our
* implementation, and later on we can switch to SDK api.
*
* TODO: we need to get operation type from recording, currently is not
* serialized and it is hard coded here.
*/
std::vector<sai_attribute_t> attrs;
for (const auto &a: attributes)
{
/*
* Set has only 1 attribute, so we can just join them nicely here.
*/
attrs.push_back(a->get_attr_list()[0]);
}
SWSS_LOG_INFO("executing BULK set route_entry with %zu routes", routes.size());
sai_status_t status = m_sai->bulkSet(
(uint32_t)routes.size(),
routes.data(),
attrs.data(),
SAI_BULK_OP_ERROR_MODE_IGNORE_ERROR, // TODO we need to get that from recording
statuses.data());
// even if API will fail, we swill need to compare all statuses for each entry
for (size_t i = 0; i < statuses.size(); ++i)
{
if (statuses[i] != expectedStatuses[i])
{
/*
* If recorded statuses are different than received, throw
* exception since data don't match.
*/
SWSS_LOG_THROW("recorded status is %s but returned is %s on %s",
sai_serialize_status(expectedStatuses[i]).c_str(),
sai_serialize_status(statuses[i]).c_str(),
object_ids[i].c_str());
}
}
return status;
}
else if (api == SAI_COMMON_API_BULK_CREATE)
{
std::vector<uint32_t> attr_count;
std::vector<const sai_attribute_t*> attr_list;
// route can have multiple attributes, so we need to handle them all
for (const auto &alist: attributes)
{
attr_list.push_back(alist->get_attr_list());
attr_count.push_back(alist->get_attr_count());
}
SWSS_LOG_INFO("executing BULK create route_entry with %zu routes", routes.size());
static PerformanceIntervalTimer timer("SaiPlayer::handle_bulk_route::bulkCreate(route_entry)");
timer.start();
sai_status_t status = m_sai->bulkCreate(
(uint32_t)routes.size(),
routes.data(),
attr_count.data(),
attr_list.data(),
SAI_BULK_OP_ERROR_MODE_IGNORE_ERROR, // TODO we need to get that from recording
statuses.data());
timer.stop();
timer.inc(routes.size());
for (size_t i = 0; i < statuses.size(); ++i)
{
if (statuses[i] != expectedStatuses[i])
{
/*
* If recorded statuses are different than received, throw
* exception since data don't match.
*/
SWSS_LOG_THROW("recorded status is %s but returned is %s on %s",
sai_serialize_status(expectedStatuses[i]).c_str(),
sai_serialize_status(statuses[i]).c_str(),
object_ids[i].c_str());
}
}
return status;
}
else
{
SWSS_LOG_THROW("api %d is not supported in bulk route", api);
}
}
sai_status_t SaiPlayer::handle_bulk_generic(
_In_ sai_object_type_t objectType,
_In_ const std::vector<std::string>& strObjectIds,
_In_ sai_common_api_t api,
_In_ const std::vector<std::shared_ptr<SaiAttributeList>>& attributes,
_In_ const std::vector<sai_status_t>& expectedStatuses)
{
SWSS_LOG_ENTER();
std::vector<sai_object_id_t> objectIds;
for (const auto& oid: strObjectIds)
{
sai_object_id_t objectId;
sai_deserialize_object_id(oid, objectId);
if (api != SAI_COMMON_API_BULK_CREATE)
{
// when creating OID objects, ID don't exists yet, it needs to be created
// and after success create, it should be matched to local redis db
objectId = translate_local_to_redis(objectId);
}
objectIds.push_back(objectId);
}
std::vector<sai_status_t> statuses;
statuses.resize(expectedStatuses.size());
if (api == SAI_COMMON_API_BULK_CREATE)
{
std::vector<sai_object_id_t> oids;
oids.resize(expectedStatuses.size());
std::vector<uint32_t> attr_count;
std::vector<const sai_attribute_t*> attr_list;
// object can have multiple attributes, so we need to handle them all
for (const auto &alist: attributes)
{
attr_list.push_back(alist->get_attr_list());
attr_count.push_back(alist->get_attr_count());
}
SWSS_LOG_INFO("executing BULK create %s with %zu entries",
sai_serialize_object_type(objectType).c_str(),
objectIds.size());
// get switch ID from first local object
sai_object_id_t localSwitchId = m_sai->switchIdQuery(objectIds[0]);
sai_object_id_t switchId = translate_local_to_redis(localSwitchId);
sai_status_t status = m_sai->bulkCreate(
objectType,
switchId,
(uint32_t)oids.size(),
attr_count.data(),
attr_list.data(),
SAI_BULK_OP_ERROR_MODE_IGNORE_ERROR, // TODO we need to get that from recording
oids.data(),
statuses.data());
for (size_t i = 0; i < statuses.size(); ++i)
{
if (statuses[i] != expectedStatuses[i])
{
/*
* If recorded statuses are different than received, throw
* exception since data don't match.
*/
SWSS_LOG_THROW("recorded status is %s but returned is %s on %s",
sai_serialize_status(expectedStatuses[i]).c_str(),
sai_serialize_status(statuses[i]).c_str(),
strObjectIds[i].c_str());
}
if (statuses[i] == SAI_STATUS_SUCCESS)
{
// object was created, we need to match local id and redis id
match_redis_with_rec(oids[i], objectIds[i]);
}
}
return status;
}
else if (api == SAI_COMMON_API_BULK_REMOVE)
{
SWSS_LOG_INFO("executing BULK remove %s with %zu entries",
sai_serialize_object_type(objectType).c_str(),
objectIds.size());
sai_status_t status = m_sai->bulkRemove(
objectType,
(uint32_t)objectIds.size(),
objectIds.data(),
SAI_BULK_OP_ERROR_MODE_IGNORE_ERROR, // TODO we need to get that from recording
statuses.data());
// even if API will fail, we swill need to compare all statuses for each entry
for (size_t i = 0; i < statuses.size(); ++i)
{
if (statuses[i] != expectedStatuses[i])
{
/*
* If recorded statuses are different than received, throw
* exception since data don't match.
*/
SWSS_LOG_THROW("recorded status is %s but returned is %s on %s",
sai_serialize_status(expectedStatuses[i]).c_str(),
sai_serialize_status(statuses[i]).c_str(),
strObjectIds[i].c_str());
}
}
return status;
}
else if (api == SAI_COMMON_API_BULK_SET)
{
std::vector<sai_attribute_t> attrs;
for (const auto &a: attributes)
{
// set has only 1 attribute, so we can just join them nicely here.
attrs.push_back(a->get_attr_list()[0]);
}
SWSS_LOG_INFO("executing BULK set %s with %zu entries",
sai_serialize_object_type(objectType).c_str(),
objectIds.size());
sai_status_t status = m_sai->bulkSet(
objectType,
(uint32_t)objectIds.size(),
objectIds.data(),
attrs.data(),
SAI_BULK_OP_ERROR_MODE_IGNORE_ERROR, // TODO we need to get that from recording
statuses.data());
// even if API will fail, we swill need to compare all statuses for each entry
for (size_t i = 0; i < statuses.size(); ++i)
{
if (statuses[i] != expectedStatuses[i])
{
/*
* If recorded statuses are different than received, throw
* exception since data don't match.
*/
SWSS_LOG_THROW("recorded status is %s but returned is %s on %s",
sai_serialize_status(expectedStatuses[i]).c_str(),
sai_serialize_status(statuses[i]).c_str(),
strObjectIds[i].c_str());
}
}
return status;
}
else
{
SWSS_LOG_THROW("api %s is not supported in bulk %s",
sai_serialize_common_api(api).c_str(),
sai_serialize_object_type(objectType).c_str());
}
}
sai_status_t SaiPlayer::handle_bulk_entry(
_In_ const std::vector<std::string> &object_ids,
_In_ sai_object_type_t object_type,
_In_ sai_common_api_t api,
_In_ const std::vector<std::shared_ptr<SaiAttributeList>> &attributes,
_Out_ std::vector<sai_status_t>& statuses)
{
SWSS_LOG_ENTER();
sai_bulk_op_error_mode_t mode = SAI_BULK_OP_ERROR_MODE_IGNORE_ERROR; // TODO we need to get that from recording
uint32_t object_count = (uint32_t) object_ids.size();
sai_status_t status = SAI_STATUS_SUCCESS;
if (api == SAI_COMMON_API_BULK_CREATE)
{
std::vector<uint32_t> attr_counts(object_count);
std::vector<const sai_attribute_t*> attr_lists(object_count);
for (uint32_t idx = 0; idx < object_count; idx++)
{
attr_counts[idx] = attributes[idx]->get_attr_count();
attr_lists[idx] = attributes[idx]->get_attr_list();
}
switch ((int)object_type)
{
case SAI_OBJECT_TYPE_ROUTE_ENTRY:
{
std::vector<sai_route_entry_t> entries(object_count);
for (size_t it = 0; it < object_count; it++)
{
sai_deserialize_route_entry(object_ids[it], entries[it]);
entries[it].switch_id = translate_local_to_redis(entries[it].switch_id);
entries[it].vr_id = translate_local_to_redis(entries[it].vr_id);
}
CALL_BULK_CREATE_API_WITH_TIMER("route_entry");
}
break;
case SAI_OBJECT_TYPE_NEIGHBOR_ENTRY:
{
std::vector<sai_neighbor_entry_t> entries(object_count);
for (size_t it = 0; it < object_count; it++)
{
sai_deserialize_neighbor_entry(object_ids[it], entries[it]);
entries[it].switch_id = translate_local_to_redis(entries[it].switch_id);
entries[it].rif_id = translate_local_to_redis(entries[it].rif_id);
}
CALL_BULK_CREATE_API_WITH_TIMER("neighbor_entry");
}
break;
case SAI_OBJECT_TYPE_FDB_ENTRY:
{
std::vector<sai_fdb_entry_t> entries(object_count);
for (size_t it = 0; it < object_count; it++)
{
sai_deserialize_fdb_entry(object_ids[it], entries[it]);
entries[it].switch_id = translate_local_to_redis(entries[it].switch_id);
entries[it].bv_id = translate_local_to_redis(entries[it].bv_id);
}
CALL_BULK_CREATE_API_WITH_TIMER("fdb_entry");
}
break;
case SAI_OBJECT_TYPE_NAT_ENTRY:
{
std::vector<sai_nat_entry_t> entries(object_count);
for (size_t it = 0; it < object_count; it++)
{
sai_deserialize_nat_entry(object_ids[it], entries[it]);
entries[it].switch_id = translate_local_to_redis(entries[it].switch_id);
entries[it].vr_id = translate_local_to_redis(entries[it].vr_id);
}
CALL_BULK_CREATE_API_WITH_TIMER("nat_entry");
}
break;
case SAI_OBJECT_TYPE_DIRECTION_LOOKUP_ENTRY:
{
std::vector<sai_direction_lookup_entry_t> entries(object_count);
for (uint32_t it = 0; it < object_count; it++)
{
sai_deserialize_direction_lookup_entry(object_ids[it], entries[it]);
entries[it].switch_id = translate_local_to_redis(entries[it].switch_id);
}
CALL_BULK_CREATE_API_WITH_TIMER("direction_lookup_entry");
}
break;
case SAI_OBJECT_TYPE_ENI_ETHER_ADDRESS_MAP_ENTRY:
{
std::vector<sai_eni_ether_address_map_entry_t> entries(object_count);
for (uint32_t it = 0; it < object_count; it++)
{
sai_deserialize_eni_ether_address_map_entry(object_ids[it], entries[it]);
entries[it].switch_id = translate_local_to_redis(entries[it].switch_id);
}
CALL_BULK_CREATE_API_WITH_TIMER("eni_ether_address_map_entry");
}
break;
case SAI_OBJECT_TYPE_VIP_ENTRY:
{
std::vector<sai_vip_entry_t> entries(object_count);
for (uint32_t it = 0; it < object_count; it++)
{
sai_deserialize_vip_entry(object_ids[it], entries[it]);
entries[it].switch_id = translate_local_to_redis(entries[it].switch_id);
}
CALL_BULK_CREATE_API_WITH_TIMER("vip_entry");
}
break;
case SAI_OBJECT_TYPE_INBOUND_ROUTING_ENTRY:
{
std::vector<sai_inbound_routing_entry_t> entries(object_count);
for (uint32_t it = 0; it < object_count; it++)
{
sai_deserialize_inbound_routing_entry(object_ids[it], entries[it]);
entries[it].switch_id = translate_local_to_redis(entries[it].switch_id);
entries[it].eni_id = translate_local_to_redis(entries[it].eni_id);
}
CALL_BULK_CREATE_API_WITH_TIMER("inbound_routing_entry");
}
break;
case SAI_OBJECT_TYPE_PA_VALIDATION_ENTRY:
{
std::vector<sai_pa_validation_entry_t> entries(object_count);
for (uint32_t it = 0; it < object_count; it++)
{
sai_deserialize_pa_validation_entry(object_ids[it], entries[it]);
entries[it].switch_id = translate_local_to_redis(entries[it].switch_id);
entries[it].vnet_id = translate_local_to_redis(entries[it].vnet_id);
}
CALL_BULK_CREATE_API_WITH_TIMER("pa_validation_entry");
}
break;
case SAI_OBJECT_TYPE_OUTBOUND_ROUTING_ENTRY:
{
std::vector<sai_outbound_routing_entry_t> entries(object_count);
for (uint32_t it = 0; it < object_count; it++)
{
sai_deserialize_outbound_routing_entry(object_ids[it], entries[it]);
entries[it].switch_id = translate_local_to_redis(entries[it].switch_id);
entries[it].outbound_routing_group_id = translate_local_to_redis(entries[it].outbound_routing_group_id);
}
CALL_BULK_CREATE_API_WITH_TIMER("outbound_routing_entry");
}
break;
case SAI_OBJECT_TYPE_OUTBOUND_CA_TO_PA_ENTRY:
{
std::vector<sai_outbound_ca_to_pa_entry_t> entries(object_count);
for (uint32_t it = 0; it < object_count; it++)
{
sai_deserialize_outbound_ca_to_pa_entry(object_ids[it], entries[it]);
entries[it].switch_id = translate_local_to_redis(entries[it].switch_id);
entries[it].dst_vnet_id = translate_local_to_redis(entries[it].dst_vnet_id);
}
CALL_BULK_CREATE_API_WITH_TIMER("outbound_ca_to_pa_entry");
}
break;
default:
SWSS_LOG_THROW("api %s is not supported in bulk", sai_serialize_common_api(api).c_str());
}
}
else if (api == SAI_COMMON_API_BULK_REMOVE)
{
switch ((int)object_type)
{
case SAI_OBJECT_TYPE_ROUTE_ENTRY:
{
std::vector<sai_route_entry_t> entries(object_count);
for (size_t it = 0; it < object_count; it++)
{
sai_deserialize_route_entry(object_ids[it], entries[it]);
entries[it].switch_id = translate_local_to_redis(entries[it].switch_id);
entries[it].vr_id = translate_local_to_redis(entries[it].vr_id);
}
CALL_BULK_REMOVE_API_WITH_TIMER("route_entry");
}
break;
case SAI_OBJECT_TYPE_NEIGHBOR_ENTRY:
{
std::vector<sai_neighbor_entry_t> entries(object_count);
for (size_t it = 0; it < object_count; it++)
{
sai_deserialize_neighbor_entry(object_ids[it], entries[it]);
entries[it].switch_id = translate_local_to_redis(entries[it].switch_id);
entries[it].rif_id = translate_local_to_redis(entries[it].rif_id);
}
CALL_BULK_REMOVE_API_WITH_TIMER("neighbor_entry");
}
break;
case SAI_OBJECT_TYPE_FDB_ENTRY:
{
std::vector<sai_fdb_entry_t> entries(object_count);
for (size_t it = 0; it < object_count; it++)
{
sai_deserialize_fdb_entry(object_ids[it], entries[it]);
entries[it].switch_id = translate_local_to_redis(entries[it].switch_id);
entries[it].bv_id = translate_local_to_redis(entries[it].bv_id);
}
CALL_BULK_REMOVE_API_WITH_TIMER("fdb_entry");
}
break;
case SAI_OBJECT_TYPE_NAT_ENTRY:
{
std::vector<sai_nat_entry_t> entries(object_count);
for (size_t it = 0; it < object_count; it++)
{
sai_deserialize_nat_entry(object_ids[it], entries[it]);
entries[it].switch_id = translate_local_to_redis(entries[it].switch_id);
entries[it].vr_id = translate_local_to_redis(entries[it].vr_id);
}
CALL_BULK_REMOVE_API_WITH_TIMER("nat_entry");
}
break;
case SAI_OBJECT_TYPE_DIRECTION_LOOKUP_ENTRY:
{
std::vector<sai_direction_lookup_entry_t> entries(object_count);
for (uint32_t it = 0; it < object_count; it++)
{
sai_deserialize_direction_lookup_entry(object_ids[it], entries[it]);
entries[it].switch_id = translate_local_to_redis(entries[it].switch_id);
}
CALL_BULK_REMOVE_API_WITH_TIMER("direction_lookup_entry");
}
break;
case SAI_OBJECT_TYPE_ENI_ETHER_ADDRESS_MAP_ENTRY:
{
std::vector<sai_eni_ether_address_map_entry_t> entries(object_count);
for (uint32_t it = 0; it < object_count; it++)
{
sai_deserialize_eni_ether_address_map_entry(object_ids[it], entries[it]);
entries[it].switch_id = translate_local_to_redis(entries[it].switch_id);
}
CALL_BULK_REMOVE_API_WITH_TIMER("eni_ether_address_map_entry");
}
break;
case SAI_OBJECT_TYPE_VIP_ENTRY:
{
std::vector<sai_vip_entry_t> entries(object_count);
for (uint32_t it = 0; it < object_count; it++)
{
sai_deserialize_vip_entry(object_ids[it], entries[it]);
entries[it].switch_id = translate_local_to_redis(entries[it].switch_id);
}
CALL_BULK_REMOVE_API_WITH_TIMER("vip_entry");
}
break;
case SAI_OBJECT_TYPE_INBOUND_ROUTING_ENTRY:
{
std::vector<sai_inbound_routing_entry_t> entries(object_count);
for (uint32_t it = 0; it < object_count; it++)
{
sai_deserialize_inbound_routing_entry(object_ids[it], entries[it]);
entries[it].switch_id = translate_local_to_redis(entries[it].switch_id);
entries[it].eni_id = translate_local_to_redis(entries[it].eni_id);
}
CALL_BULK_REMOVE_API_WITH_TIMER("inbound_routing_entry");
}
break;
case SAI_OBJECT_TYPE_PA_VALIDATION_ENTRY:
{
std::vector<sai_pa_validation_entry_t> entries(object_count);
for (uint32_t it = 0; it < object_count; it++)
{
sai_deserialize_pa_validation_entry(object_ids[it], entries[it]);
entries[it].switch_id = translate_local_to_redis(entries[it].switch_id);
entries[it].vnet_id = translate_local_to_redis(entries[it].vnet_id);
}
CALL_BULK_REMOVE_API_WITH_TIMER("pa_validation_entry");
}
break;
case SAI_OBJECT_TYPE_OUTBOUND_ROUTING_ENTRY:
{
std::vector<sai_outbound_routing_entry_t> entries(object_count);
for (uint32_t it = 0; it < object_count; it++)
{
sai_deserialize_outbound_routing_entry(object_ids[it], entries[it]);
entries[it].switch_id = translate_local_to_redis(entries[it].switch_id);
entries[it].outbound_routing_group_id = translate_local_to_redis(entries[it].outbound_routing_group_id);
}
CALL_BULK_REMOVE_API_WITH_TIMER("outbound_routing_entry");
}
break;
case SAI_OBJECT_TYPE_OUTBOUND_CA_TO_PA_ENTRY:
{
std::vector<sai_outbound_ca_to_pa_entry_t> entries(object_count);
for (uint32_t it = 0; it < object_count; it++)
{
sai_deserialize_outbound_ca_to_pa_entry(object_ids[it], entries[it]);
entries[it].switch_id = translate_local_to_redis(entries[it].switch_id);
entries[it].dst_vnet_id = translate_local_to_redis(entries[it].dst_vnet_id);
}
CALL_BULK_REMOVE_API_WITH_TIMER("outbound_ca_to_pa_entry");
}
break;
default:
SWSS_LOG_THROW("api %s is not supported in bulk", sai_serialize_common_api(api).c_str());
}
}
else if (api == SAI_COMMON_API_BULK_SET)
{
std::vector<sai_attribute_t> attr_lists;
for (size_t it = 0; it < object_count; it++)
{
attr_lists.push_back(attributes[it]->get_attr_list()[0]);
}
switch ((int)object_type)
{
case SAI_OBJECT_TYPE_ROUTE_ENTRY:
{
std::vector<sai_route_entry_t> entries(object_count);
for (size_t it = 0; it < object_count; it++)
{
sai_deserialize_route_entry(object_ids[it], entries[it]);
entries[it].switch_id = translate_local_to_redis(entries[it].switch_id);
entries[it].vr_id = translate_local_to_redis(entries[it].vr_id);
}
CALL_BULK_SET_API_WITH_TIMER("route_entry");
}
break;
case SAI_OBJECT_TYPE_NEIGHBOR_ENTRY:
{
std::vector<sai_neighbor_entry_t> entries(object_count);
for (size_t it = 0; it < object_count; it++)
{
sai_deserialize_neighbor_entry(object_ids[it], entries[it]);
entries[it].switch_id = translate_local_to_redis(entries[it].switch_id);
entries[it].rif_id = translate_local_to_redis(entries[it].rif_id);
}
CALL_BULK_SET_API_WITH_TIMER("neighbor_entry");
}
break;
case SAI_OBJECT_TYPE_FDB_ENTRY:
{
std::vector<sai_fdb_entry_t> entries(object_count);
for (size_t it = 0; it < object_count; it++)
{
sai_deserialize_fdb_entry(object_ids[it], entries[it]);
entries[it].switch_id = translate_local_to_redis(entries[it].switch_id);
entries[it].bv_id = translate_local_to_redis(entries[it].bv_id);
}
CALL_BULK_SET_API_WITH_TIMER("fdb_entry");
}
break;
case SAI_OBJECT_TYPE_NAT_ENTRY:
{
std::vector<sai_nat_entry_t> entries(object_count);
for (size_t it = 0; it < object_count; it++)
{
sai_deserialize_nat_entry(object_ids[it], entries[it]);
entries[it].switch_id = translate_local_to_redis(entries[it].switch_id);
entries[it].vr_id = translate_local_to_redis(entries[it].vr_id);
}
CALL_BULK_SET_API_WITH_TIMER("nat_entry");
}
break;
case SAI_OBJECT_TYPE_DIRECTION_LOOKUP_ENTRY:
{
std::vector<sai_direction_lookup_entry_t> entries(object_count);
for (uint32_t it = 0; it < object_count; it++)
{
sai_deserialize_direction_lookup_entry(object_ids[it], entries[it]);
entries[it].switch_id = translate_local_to_redis(entries[it].switch_id);
}
CALL_BULK_SET_API_WITH_TIMER("direction_lookup_entry");
}
break;
case SAI_OBJECT_TYPE_ENI_ETHER_ADDRESS_MAP_ENTRY:
{
std::vector<sai_eni_ether_address_map_entry_t> entries(object_count);
for (uint32_t it = 0; it < object_count; it++)
{
sai_deserialize_eni_ether_address_map_entry(object_ids[it], entries[it]);
entries[it].switch_id = translate_local_to_redis(entries[it].switch_id);
}
CALL_BULK_SET_API_WITH_TIMER("eni_ether_address_map_entry");
}
break;
case SAI_OBJECT_TYPE_VIP_ENTRY:
{
std::vector<sai_vip_entry_t> entries(object_count);
for (uint32_t it = 0; it < object_count; it++)
{
sai_deserialize_vip_entry(object_ids[it], entries[it]);
entries[it].switch_id = translate_local_to_redis(entries[it].switch_id);
}
CALL_BULK_SET_API_WITH_TIMER("vip_entry");
}
break;
case SAI_OBJECT_TYPE_INBOUND_ROUTING_ENTRY:
{
std::vector<sai_inbound_routing_entry_t> entries(object_count);
for (uint32_t it = 0; it < object_count; it++)
{
sai_deserialize_inbound_routing_entry(object_ids[it], entries[it]);
entries[it].switch_id = translate_local_to_redis(entries[it].switch_id);
entries[it].eni_id = translate_local_to_redis(entries[it].eni_id);
}
CALL_BULK_SET_API_WITH_TIMER("inbound_routing_entry");
}
break;
case SAI_OBJECT_TYPE_PA_VALIDATION_ENTRY:
{
std::vector<sai_pa_validation_entry_t> entries(object_count);
for (uint32_t it = 0; it < object_count; it++)
{
sai_deserialize_pa_validation_entry(object_ids[it], entries[it]);
entries[it].switch_id = translate_local_to_redis(entries[it].switch_id);
entries[it].vnet_id = translate_local_to_redis(entries[it].vnet_id);
}
CALL_BULK_SET_API_WITH_TIMER("pa_validation_entry");
}
break;
case SAI_OBJECT_TYPE_OUTBOUND_ROUTING_ENTRY:
{
std::vector<sai_outbound_routing_entry_t> entries(object_count);
for (uint32_t it = 0; it < object_count; it++)
{
sai_deserialize_outbound_routing_entry(object_ids[it], entries[it]);
entries[it].switch_id = translate_local_to_redis(entries[it].switch_id);
entries[it].outbound_routing_group_id = translate_local_to_redis(entries[it].outbound_routing_group_id);
}
CALL_BULK_SET_API_WITH_TIMER("outbound_routing_entry");
}
break;
case SAI_OBJECT_TYPE_OUTBOUND_CA_TO_PA_ENTRY:
{
std::vector<sai_outbound_ca_to_pa_entry_t> entries(object_count);
for (uint32_t it = 0; it < object_count; it++)
{
sai_deserialize_outbound_ca_to_pa_entry(object_ids[it], entries[it]);
entries[it].switch_id = translate_local_to_redis(entries[it].switch_id);
entries[it].dst_vnet_id = translate_local_to_redis(entries[it].dst_vnet_id);
}
CALL_BULK_SET_API_WITH_TIMER("outbound_ca_to_pa_entry");
}
break;
default:
SWSS_LOG_THROW("api %s is not supported in bulk", sai_serialize_common_api(api).c_str());
}
}
else
{
SWSS_LOG_THROW("api %s is not supported in bulk", sai_serialize_common_api(api).c_str());
}
return status;
}
sai_status_t SaiPlayer::handle_bulk_object(
_In_ sai_object_type_t object_type,
_In_ const std::vector<std::string> &object_ids,
_In_ sai_common_api_t api,
_In_ const std::vector<std::shared_ptr<SaiAttributeList>> &attributes,
_Out_ std::vector<sai_status_t> &statuses)
{
SWSS_LOG_ENTER();
if (!object_ids.size())
{
SWSS_LOG_ERROR("Object ids is empty in handle bulk generic");
return SAI_STATUS_FAILURE;
}
sai_status_t status;
uint32_t object_count = (uint32_t) object_ids.size();
std::vector<sai_object_id_t> local_ids(object_count);
sai_bulk_op_error_mode_t mode = SAI_BULK_OP_ERROR_MODE_IGNORE_ERROR; // TODO we need to get that from recording
SWSS_LOG_DEBUG("generic %s for %s:%s",
sai_serialize_common_api(api).c_str(),
sai_serialize_object_type(object_type).c_str(),
object_ids[0].c_str());
for (uint32_t it = 0; it < object_count; it++)
{
sai_deserialize_object_id(object_ids[it], local_ids[it]);
}
switch (api)
{
case SAI_COMMON_API_BULK_CREATE:
{
sai_object_id_t switch_id = m_sai->switchIdQuery(local_ids[0]);
std::vector<sai_object_id_t> ids(object_count);
for (uint32_t it = 0; it < object_count; it++)
{
if (m_sai->switchIdQuery(local_ids[it]) != switch_id ||
switch_id == SAI_NULL_OBJECT_ID)
{
SWSS_LOG_THROW("invalid switch_id translated from VID %s",
sai_serialize_object_id(local_ids[it]).c_str());
}
}
std::vector<uint32_t> attr_counts(object_count);
std::vector<const sai_attribute_t*> attr_lists(object_count);
for (uint32_t idx = 0; idx < object_count; idx++)
{
attr_counts[idx] = attributes[idx]->get_attr_count();
attr_lists[idx] = attributes[idx]->get_attr_list();
}
switch_id = translate_local_to_redis(switch_id);
status = m_sai->bulkCreate(object_type,
switch_id,
object_count,
attr_counts.data(),
attr_lists.data(),
mode,
ids.data(),
statuses.data());
if (status == SAI_STATUS_SUCCESS)
{
for (uint32_t it = 0; it < object_count; it++)
{
match_redis_with_rec(ids[it], local_ids[it]);
SWSS_LOG_INFO("saved VID %s to RID %s",
sai_serialize_object_id(local_ids[it]).c_str(),
sai_serialize_object_id(ids[it]).c_str());
}
}
return status;
}
break;
case SAI_COMMON_API_BULK_REMOVE:
{
std::vector<sai_object_id_t> ids(object_count);
for (uint32_t it = 0; it < object_count; it++)
{
ids[it] = translate_local_to_redis(local_ids[it]);
}
status = m_sai->bulkRemove(object_type, object_count, ids.data(), mode, statuses.data());
}
break;
case SAI_COMMON_API_BULK_SET:
{
std::vector<sai_object_id_t> ids(object_count);
for (uint32_t it = 0; it < object_count; it++)
{
ids[it] = translate_local_to_redis(local_ids[it]);
}
std::vector<sai_attribute_t> attr_list;
// route can have multiple attributes, so we need to handle them all
for (const auto &alist: attributes)
{
attr_list.push_back(alist->get_attr_list()[0]);
}
status = m_sai->bulkSet(object_type, object_count, ids.data(), attr_list.data(), mode, statuses.data());
}
break;
case SAI_COMMON_API_BULK_GET:
{
std::vector<sai_object_id_t> ids(object_count);
for (uint32_t it = 0; it < object_count; it++)
{
ids[it] = translate_local_to_redis(local_ids[it]);
}
std::vector<uint32_t> attr_counts(object_count);
std::vector<sai_attribute_t*> attr_lists(object_count);
for (uint32_t idx = 0; idx < object_count; idx++)
{
attr_counts[idx] = attributes[idx]->get_attr_count();
attr_lists[idx] = attributes[idx]->get_attr_list();
}
status = m_sai->bulkGet(object_type,
object_count,
ids.data(),
attr_counts.data(),
attr_lists.data(),
mode,
statuses.data());
return status;
}
break;
default:
SWSS_LOG_THROW("generic other apis not implemented");
}
return status;
}
void SaiPlayer::processBulk(
_In_ sai_common_api_t api,
_In_ const std::string &line)
{
SWSS_LOG_ENTER();
if (!line.size())
{
return;
}
if (api != SAI_COMMON_API_BULK_SET &&
api != SAI_COMMON_API_BULK_CREATE &&
api != SAI_COMMON_API_BULK_GET &&
api != SAI_COMMON_API_BULK_REMOVE)
{
SWSS_LOG_THROW("bulk common api %d is not supported yet, FIXME", api);
}
/*
* Here we know we have bulk SET api
*/
// timestamp|action|objecttype||objectid|attrid=value|...||objectid||objectid|attrid=value|...||...
auto fields = tokenize(line, "||");
auto first = fields.at(0); // timestamp|action|objecttype
std::string str_object_type = swss::tokenize(first, '|').at(2);
sai_object_type_t object_type = deserialize_object_type(str_object_type);
std::vector<std::string> object_ids;
std::vector<std::shared_ptr<SaiAttributeList>> attributes;
std::vector<sai_status_t> statuses(fields.size());
// TODO currently we expect bulk API except BULK_GET will always succeed in sync mode
// we will need to update that, needs to be obtained from recording file
std::vector<sai_status_t> expectedStatuses(fields.size(), SAI_STATUS_SUCCESS);
for (size_t idx = 1; idx < fields.size(); ++idx)
{
// object_id|attr=value|...
const std::string &joined = fields[idx];
auto split = swss::tokenize(joined, '|');
std::string str_object_id = split.front();
object_ids.push_back(str_object_id);
std::vector<swss::FieldValueTuple> entries; // attributes per object id
// skip front object_id and back status
SWSS_LOG_DEBUG("processing: %s", joined.c_str());
for (size_t i = 1; i < split.size(); ++i)
{
const auto &item = split[i];
auto start = item.find_first_of("=");
auto field = item.substr(0, start);
auto value = item.substr(start + 1);
swss::FieldValueTuple entry(field, value);
entries.push_back(entry);
}
// since now we converted this to proper list, we can extract attributes
std::shared_ptr<SaiAttributeList> list =
std::make_shared<SaiAttributeList>(object_type, entries, false);
sai_attribute_t *attr_list = list->get_attr_list();
uint32_t attr_count = list->get_attr_count();
if (api != SAI_COMMON_API_BULK_GET)
{
translate_local_to_redis(object_type, attr_count, attr_list);
}
attributes.push_back(list);
}
sai_status_t status = SAI_STATUS_SUCCESS;
auto info = sai_metadata_get_object_type_info(object_type);
switch ((int)object_type)
{
case SAI_OBJECT_TYPE_ROUTE_ENTRY:
case SAI_OBJECT_TYPE_NEIGHBOR_ENTRY:
case SAI_OBJECT_TYPE_FDB_ENTRY:
case SAI_OBJECT_TYPE_NAT_ENTRY:
case SAI_OBJECT_TYPE_DIRECTION_LOOKUP_ENTRY:
case SAI_OBJECT_TYPE_ENI_ETHER_ADDRESS_MAP_ENTRY:
case SAI_OBJECT_TYPE_VIP_ENTRY:
case SAI_OBJECT_TYPE_INBOUND_ROUTING_ENTRY:
case SAI_OBJECT_TYPE_PA_VALIDATION_ENTRY:
case SAI_OBJECT_TYPE_OUTBOUND_ROUTING_ENTRY:
case SAI_OBJECT_TYPE_OUTBOUND_CA_TO_PA_ENTRY:
status = handle_bulk_entry(object_ids, object_type, api, attributes, statuses);
break;
default:
if (info->isnonobjectid)
{
SWSS_LOG_THROW("object %s is non object id, but not handled, FIXME",
sai_serialize_object_type(object_type).c_str());
}
status = handle_bulk_object(object_type, object_ids, api, attributes, statuses);
break;
}
if (api == SAI_COMMON_API_BULK_GET)
{
std::string response;
do
{
// this line may be notification, we need to skip
std::getline(m_infile, response);
}
while (response[response.find_first_of("|") + 1] == 'n');
const auto tokens = tokenize(response, "||");
const auto opAndStatus = tokenize(tokens.at(0), "|");
sai_status_t expectedStatus;
sai_deserialize_status(opAndStatus.at(2), expectedStatus);
if (status != expectedStatus)
{
SWSS_LOG_WARN("status is: %s but expected: %s",
sai_serialize_status(status).c_str(),
sai_serialize_status(expectedStatus).c_str());
return;
}
auto valuesIter = tokens.begin() + 1; // Skip operation and status
for (size_t idx = 0; idx < object_ids.size(); idx++, valuesIter++)
{
auto attrValues = tokenize(*valuesIter, "|");
sai_status_t expectedObjectStatus;
sai_deserialize_status(attrValues.at(0), expectedObjectStatus);
if (statuses[idx] != expectedObjectStatus)
{
SWSS_LOG_WARN("object status is: %s but expected: %s",
sai_serialize_status(statuses[idx]).c_str(),
sai_serialize_status(expectedObjectStatus).c_str());
continue;
}
std::vector<swss::FieldValueTuple> values;
for (size_t attrIdx = 1; attrIdx < attrValues.size(); attrIdx++) // skip status
{
auto& attrStr = attrValues[attrIdx];
auto start = attrStr.find_first_of("=");
auto field = attrStr.substr(0, start);
auto value = attrStr.substr(start + 1);
swss::FieldValueTuple entry(field, value);
values.push_back(entry);
}
SaiAttributeList list(object_type, values, false);
sai_attribute_t *attr_list = list.get_attr_list();
uint32_t attr_count = list.get_attr_count();
match_list_lengths(object_type, attributes[idx]->get_attr_count(),
attributes[idx]->get_attr_list(), attr_count, attr_list);
SWSS_LOG_DEBUG("list match");
match_redis_with_rec(object_type, attributes[idx]->get_attr_count(),
attributes[idx]->get_attr_list(), attr_count, attr_list);
// NOTE: Primitive values are not matched (recording vs switch/vs), we can add that check
}
}
if (status != SAI_STATUS_SUCCESS)
{
SWSS_LOG_ERROR("handle bulk executed with failure, status = %s", sai_serialize_status(status).c_str());
}
// even if API will fail, we will need to compare all statuses for each entry
for (size_t i = 0; i < statuses.size(); ++i)
{
if (statuses[i] != expectedStatuses[i])
{
/*
* If expected statuses are different than received, throw
* exception since data don't match.
*/
SWSS_LOG_THROW("expected status is %s but returned is %s on %s",
sai_serialize_status(expectedStatuses[i]).c_str(),
sai_serialize_status(statuses[i]).c_str(),
object_ids[i].c_str());
}
}
}
int SaiPlayer::replay()
{
//swss::Logger::getInstance().setMinPrio(swss::Logger::SWSS_DEBUG);
SWSS_LOG_ENTER();
if (m_commandLineOptions->m_files.size() == 0)
{
fprintf(stderr, "ERR: need to specify filename\n");
return -1;
}
auto filename = m_commandLineOptions->m_files.at(0);
SWSS_LOG_NOTICE("using file: %s", filename.c_str());
m_infile = std::ifstream{filename};
if (!m_infile.is_open())
{
SWSS_LOG_ERROR("failed to open file %s", filename.c_str());
return -1;
}
std::string line;
while (std::getline(m_infile, line))
{
// std::cout << "processing " << line << std::endl;
sai_common_api_t api = SAI_COMMON_API_CREATE;
auto p = line.find_first_of("|");
char op = line[p+1];
switch (op)
{
case 'a':
{
std::string response;
do
{
// this line may be notification, we need to skip
if (!std::getline(m_infile, response))
{
SWSS_LOG_THROW("failed to read next file from file, previous: %s", line.c_str());
}
}
while (response[response.find_first_of("|") + 1] == 'n');
performNotifySyncd(line, response);
}
continue;
case 'f':
{
std::string response;
do
{
// this line may be notification, we need to skip
if (!std::getline(m_infile, response))
{
SWSS_LOG_THROW("failed to read next file from file, previous: %s", line.c_str());
}
}
while (response[response.find_first_of("|") + 1] == 'n');
performFdbFlush(line, response);
}
continue;
case '@':
performSleep(line);
continue;
case 'c':
api = SAI_COMMON_API_CREATE;
break;
case 'r':
api = SAI_COMMON_API_REMOVE;
break;
case 's':
api = SAI_COMMON_API_SET;
break;
case 'B':
processBulk(SAI_COMMON_API_BULK_GET, line);
continue;
case 'S':
processBulk(SAI_COMMON_API_BULK_SET, line);
continue;
case 'C':
processBulk(SAI_COMMON_API_BULK_CREATE, line);
continue;
case 'R':
processBulk(SAI_COMMON_API_BULK_REMOVE, line);
continue;
case 'g':
api = SAI_COMMON_API_GET;
break;
case 'q':
// TODO: implement SAI player support for query commands
continue;
case 'p':
// TODO: implement SAI player support for counter polling commands
continue;
case 'Q':
continue; // skip over query responses
case '#':
case 'n':
SWSS_LOG_INFO("skipping op %c line %s", op, line.c_str());
continue; // skip comment and notification
default:
SWSS_LOG_THROW("unknown op %c on line %s", op, line.c_str());
}
// timestamp|action|objecttype:objectid|attrid=value,...
auto fields = swss::tokenize(line, '|');
// objecttype:objectid (object id may contain ':')
auto start = fields[2].find_first_of(":");
auto str_object_type = fields[2].substr(0, start);
auto str_object_id = fields[2].substr(start + 1);
sai_object_type_t object_type = deserialize_object_type(str_object_type);
auto values = get_values(fields);
SaiAttributeList list(object_type, values, false);
sai_attribute_t *attr_list = list.get_attr_list();
uint32_t attr_count = list.get_attr_count();
SWSS_LOG_DEBUG("attr count: %u", list.get_attr_count());
if (api != SAI_COMMON_API_GET)
{
translate_local_to_redis(object_type, attr_count, attr_list);
}
sai_status_t status;
auto info = sai_metadata_get_object_type_info(object_type);
switch ((int)object_type)
{
case SAI_OBJECT_TYPE_FDB_ENTRY:
status = handle_fdb(str_object_id, api, attr_count, attr_list);
break;
case SAI_OBJECT_TYPE_NEIGHBOR_ENTRY:
status = handle_neighbor(str_object_id, api, attr_count, attr_list);
break;
case SAI_OBJECT_TYPE_ROUTE_ENTRY:
status = handle_route(str_object_id, api, attr_count, attr_list);
break;
case SAI_OBJECT_TYPE_INSEG_ENTRY:
status = handle_inseg(str_object_id, api, attr_count, attr_list);
break;
case SAI_OBJECT_TYPE_DIRECTION_LOOKUP_ENTRY:
status = handle_dash_direction_lookup(str_object_id, api, attr_count, attr_list);
break;
case SAI_OBJECT_TYPE_ENI_ETHER_ADDRESS_MAP_ENTRY:
status = handle_dash_eni_ether_address_map(str_object_id, api, attr_count, attr_list);
break;
case SAI_OBJECT_TYPE_VIP_ENTRY:
status = handle_dash_vip(str_object_id, api, attr_count, attr_list);
break;
case SAI_OBJECT_TYPE_INBOUND_ROUTING_ENTRY:
status = handle_dash_inbound_routing(str_object_id, api, attr_count, attr_list);
break;
case SAI_OBJECT_TYPE_PA_VALIDATION_ENTRY:
status = handle_dash_pa_validation(str_object_id, api, attr_count, attr_list);
break;
case SAI_OBJECT_TYPE_OUTBOUND_ROUTING_ENTRY:
status = handle_dash_outbound_routing(str_object_id, api, attr_count, attr_list);
break;
case SAI_OBJECT_TYPE_OUTBOUND_CA_TO_PA_ENTRY:
status = handle_dash_outbound_ca_to_pa(str_object_id, api, attr_count, attr_list);
break;
default:
if (info->isnonobjectid)
{
SWSS_LOG_THROW("object %s:%s is non object id, but not handled, FIXME",
sai_serialize_object_type(object_type).c_str(),
str_object_id.c_str());
}
status = handle_generic(object_type, str_object_id, api, attr_count, attr_list);
break;
}
if (status != SAI_STATUS_SUCCESS)
{
if (api == SAI_COMMON_API_GET)
{
// GET status is checked in handle response
}
else
SWSS_LOG_THROW("failed to execute api: %c: %s", op, sai_serialize_status(status).c_str());
}
if (api == SAI_COMMON_API_GET)
{
std::string response;
do
{
// this line may be notification, we need to skip
std::getline(m_infile, response);
}
while (response[response.find_first_of("|") + 1] == 'n');
try
{
handle_get_response(object_type, attr_count, attr_list, response, status);
}
catch (const std::exception &e)
{
SWSS_LOG_NOTICE("line: %s", line.c_str());
SWSS_LOG_NOTICE("resp (expected): %s", response.c_str());
SWSS_LOG_NOTICE("got: %s", sai_serialize_status(status).c_str());
if (api == SAI_COMMON_API_GET && (status == SAI_STATUS_SUCCESS || status == SAI_STATUS_BUFFER_OVERFLOW))
{
// log each get parameter
for (uint32_t i = 0; i < attr_count; ++i)
{
auto meta = sai_metadata_get_attr_metadata(object_type, attr_list[i].id);
auto val = sai_serialize_attr_value(*meta, attr_list[i]);
SWSS_LOG_NOTICE(" - %s:%s", meta->attridname, val.c_str());
}
}
exit(EXIT_FAILURE);
}
if (api == SAI_COMMON_API_GET && (status == SAI_STATUS_SUCCESS || status == SAI_STATUS_BUFFER_OVERFLOW))
{
// log each get parameter
for (uint32_t i = 0; i < attr_count; ++i)
{
auto meta = sai_metadata_get_attr_metadata(object_type, attr_list[i].id);
auto val = sai_serialize_attr_value(*meta, attr_list[i]);
SWSS_LOG_NOTICE(" - %s:%s", meta->attridname, val.c_str());
}
}
}
}
m_infile.close();
SWSS_LOG_NOTICE("finished replaying %s with SUCCESS", filename.c_str());
if (m_commandLineOptions->m_sleep)
{
fprintf(stderr, "Reply SUCCESS, sleeping, watching for notifications\n");
sleep(-1);
}
return 0;
}
const char* SaiPlayer::profileGetValue(
_In_ sai_switch_profile_id_t profile_id,
_In_ const char* variable)
{
SWSS_LOG_ENTER();
if (variable == NULL)
{
SWSS_LOG_WARN("variable is null");
return NULL;
}
auto it = m_profileMap.find(variable);
if (it == m_profileMap.end())
{
SWSS_LOG_NOTICE("%s: NULL", variable);
return NULL;
}
SWSS_LOG_NOTICE("%s: %s", variable, it->second.c_str());
return it->second.c_str();
}
int SaiPlayer::profileGetNextValue(
_In_ sai_switch_profile_id_t profile_id,
_Out_ const char** variable,
_Out_ const char** value)
{
SWSS_LOG_ENTER();
if (value == NULL)
{
SWSS_LOG_INFO("resetting profile map iterator");
m_profileIter = m_profileMap.begin();
return 0;
}
if (variable == NULL)
{
SWSS_LOG_WARN("variable is null");
return -1;
}
if (m_profileIter == m_profileMap.end())
{
SWSS_LOG_INFO("iterator reached end");
return -1;
}
*variable = m_profileIter->first.c_str();
*value = m_profileIter->second.c_str();
SWSS_LOG_INFO("key: %s:%s", *variable, *value);
m_profileIter++;
return 0;
}
int SaiPlayer::run()
{
SWSS_LOG_ENTER();
if (m_commandLineOptions->m_enableDebug)
{
swss::Logger::getInstance().setMinPrio(swss::Logger::SWSS_NOTICE);
}
m_test_services = m_smt.getServiceMethodTable();
EXIT_ON_ERROR(m_sai->apiInitialize(0, &m_test_services));
sai_attribute_t attr;
/*
* Notice that we use null object id as switch id, which is fine since
* those attributes don't need switch.
*/
sai_object_id_t switch_id = SAI_NULL_OBJECT_ID;
if (m_commandLineOptions->m_inspectAsic)
{
attr.id = SAI_REDIS_SWITCH_ATTR_NOTIFY_SYNCD;
attr.value.s32 = SAI_REDIS_NOTIFY_SYNCD_INSPECT_ASIC;
EXIT_ON_ERROR(m_sai->set(SAI_OBJECT_TYPE_SWITCH, switch_id, &attr));
}
if (m_commandLineOptions->m_syncMode)
{
SWSS_LOG_WARN("sync mode is depreacated, please use communication mode");
attr.id = SAI_REDIS_SWITCH_ATTR_SYNC_MODE;
attr.value.booldata = true;
EXIT_ON_ERROR(m_sai->set(SAI_OBJECT_TYPE_SWITCH, switch_id, &attr));
m_commandLineOptions->m_redisCommunicationMode = SAI_REDIS_COMMUNICATION_MODE_REDIS_SYNC;
}
attr.id = SAI_REDIS_SWITCH_ATTR_REDIS_COMMUNICATION_MODE;
attr.value.s32 = m_commandLineOptions->m_redisCommunicationMode;
EXIT_ON_ERROR(m_sai->set(SAI_OBJECT_TYPE_SWITCH, switch_id, &attr));
attr.id = SAI_REDIS_SWITCH_ATTR_RECORD;
attr.value.booldata = m_commandLineOptions->m_enableRecording;
EXIT_ON_ERROR(m_sai->set(SAI_OBJECT_TYPE_SWITCH, switch_id, &attr));
int exitcode = 0;
if (m_commandLineOptions->m_files.size() > 0)
{
attr.id = SAI_REDIS_SWITCH_ATTR_USE_TEMP_VIEW;
attr.value.booldata = m_commandLineOptions->m_useTempView;
EXIT_ON_ERROR(m_sai->set(SAI_OBJECT_TYPE_SWITCH, switch_id, &attr));
exitcode = replay();
}
m_sai->apiUninitialize();
return exitcode;
}