in saiplayer/SaiPlayer.cpp [2397:2613]
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());
}
}
}