void SaiPlayer::processBulk()

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());
        }
    }
}