unittest/syncd/TestSyncd.cpp (494 lines of code) (raw):
#include "Syncd.h"
#include "sai_serialize.h"
#include "RequestShutdown.h"
#include "vslib/ContextConfigContainer.h"
#include "vslib/VirtualSwitchSaiInterface.h"
#include "vslib/Sai.h"
#include "lib/Sai.h"
#include "swss/dbconnector.h"
#include "sairediscommon.h"
#include "MockableSaiInterface.h"
#include "CommandLineOptions.h"
#include "sairediscommon.h"
#include "SelectableChannel.h"
#include "swss/dbconnector.h"
#include "swss/redisreply.h"
#include <gtest/gtest.h>
#include <gmock/gmock.h>
using namespace syncd;
using namespace saivs;
using namespace testing;
static void syncd_thread(
_In_ std::shared_ptr<Syncd> syncd)
{
SWSS_LOG_ENTER();
SWSS_LOG_NOTICE("thread stared");
syncd->run();
SWSS_LOG_NOTICE("thread end");
}
static std::map<std::string, std::string> profileMap;
static std::map<std::string, std::string>::iterator profileIter;
static const char* 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 = profileMap.find(variable);
if (it == 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();
}
static int 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");
profileIter = profileMap.begin();
return 0;
}
if (variable == NULL)
{
SWSS_LOG_WARN("variable is null");
return -1;
}
if (profileIter == profileMap.end())
{
SWSS_LOG_INFO("iterator reached end");
return -1;
}
*variable = profileIter->first.c_str();
*value = profileIter->second.c_str();
SWSS_LOG_INFO("key: %s:%s", *variable, *value);
profileIter++;
return 0;
}
TEST(Syncd, inspectAsic)
{
auto db = std::make_shared<swss::DBConnector>("ASIC_DB", 0, true);
swss::RedisReply r(db.get(), "FLUSHALL", REDIS_REPLY_STATUS);
r.checkStatusOK();
sai_service_method_table_t smt;
smt.profile_get_value = &profileGetValue;
smt.profile_get_next_value = &profileGetNextValue;
auto vssai = std::make_shared<saivs::Sai>();
auto cmd = std::make_shared<CommandLineOptions>();
cmd->m_redisCommunicationMode = SAI_REDIS_COMMUNICATION_MODE_REDIS_SYNC;
cmd->m_enableTempView = true;
cmd->m_profileMapFile = "profile.ini";
auto syncd = std::make_shared<Syncd>(vssai, cmd, false);
std::thread thread(syncd_thread, syncd);
auto sai = std::make_shared<sairedis::Sai>();
EXPECT_EQ(SAI_STATUS_SUCCESS, sai->apiInitialize(0, &smt));
sai_attribute_t attr;
attr.id = SAI_REDIS_SWITCH_ATTR_REDIS_COMMUNICATION_MODE;
attr.value.s32 = SAI_REDIS_COMMUNICATION_MODE_REDIS_SYNC;
// set syncd mode on sairedis
EXPECT_EQ(SAI_STATUS_SUCCESS, sai->set(SAI_OBJECT_TYPE_SWITCH, SAI_NULL_OBJECT_ID, &attr));
attr.id = SAI_SWITCH_ATTR_INIT_SWITCH;
attr.value.booldata = true;
sai_object_id_t switchId;
// create switch
EXPECT_EQ(SAI_STATUS_SUCCESS, sai->create(SAI_OBJECT_TYPE_SWITCH, &switchId, SAI_NULL_OBJECT_ID, 1, &attr));
attr.id = SAI_SWITCH_ATTR_DEFAULT_VIRTUAL_ROUTER_ID;
// get default virtual router
EXPECT_EQ(SAI_STATUS_SUCCESS, sai->get(SAI_OBJECT_TYPE_SWITCH, switchId, 1, &attr));
sai_object_id_t routerId = attr.value.oid;
sai_object_id_t list[32];
attr.id = SAI_SWITCH_ATTR_PORT_LIST;
attr.value.objlist.count = 32;
attr.value.objlist.list = list;
// get port list
EXPECT_EQ(SAI_STATUS_SUCCESS, sai->get(SAI_OBJECT_TYPE_SWITCH, switchId, 1, &attr));
sai_attribute_t attrs[4];
attrs[0].id = SAI_ROUTER_INTERFACE_ATTR_VIRTUAL_ROUTER_ID;
attrs[0].value.oid = routerId;
attrs[1].id = SAI_ROUTER_INTERFACE_ATTR_SRC_MAC_ADDRESS;
attrs[2].id = SAI_ROUTER_INTERFACE_ATTR_TYPE;
attrs[2].value.s32 = SAI_ROUTER_INTERFACE_TYPE_PORT;
attrs[3].id = SAI_ROUTER_INTERFACE_ATTR_PORT_ID;
attrs[3].value.oid = list[0];
// create router interface, we need oid with oid attributes
// to validate inspect asic routine
sai_object_id_t rifId;
EXPECT_EQ(SAI_STATUS_SUCCESS, sai->create(SAI_OBJECT_TYPE_ROUTER_INTERFACE, &rifId, switchId, 4, attrs));
attr.id = SAI_REDIS_SWITCH_ATTR_NOTIFY_SYNCD;
attr.value.s32 = SAI_REDIS_NOTIFY_SYNCD_INSPECT_ASIC;
// inspect asic on cold boot
EXPECT_EQ(SAI_STATUS_SUCCESS, sai->set(SAI_OBJECT_TYPE_SWITCH, switchId, &attr));
// request shutdown
auto opt = std::make_shared<RequestShutdownCommandLineOptions>();
opt->setRestartType(SYNCD_RESTART_TYPE_WARM);
RequestShutdown rs(opt);
rs.send();
// join thread for syncd
thread.join();
syncd = nullptr;
// TODO inspect asic on warm boot
EXPECT_EQ(SAI_STATUS_SUCCESS, sai->apiUninitialize());
}
using namespace syncd;
#ifdef MOCK_METHOD
class MockSelectableChannel : public sairedis::SelectableChannel {
public:
MOCK_METHOD(bool, empty, (), (override));
MOCK_METHOD(void, pop, (swss::KeyOpFieldsValuesTuple& kco, bool initViewMode), (override));
MOCK_METHOD(void, set, (const std::string& key, const std::vector<swss::FieldValueTuple>& values, const std::string& op), (override));
MOCK_METHOD(int, getFd, (), (override));
MOCK_METHOD(uint64_t, readData, (), (override));
};
void clearDB()
{
SWSS_LOG_ENTER();
swss::DBConnector db("ASIC_DB", 0, true);
swss::RedisReply r(&db, "FLUSHALL", REDIS_REPLY_STATUS);
r.checkStatusOK();
}
class MockSaiSwitch : public SaiSwitch {
public:
MockSaiSwitch(sai_object_id_t switchVid, sai_object_id_t switchRid,
std::shared_ptr<RedisClient> client,
std::shared_ptr<VirtualOidTranslator> translator,
std::shared_ptr<MockableSaiInterface> sai, bool warmBoot)
: SaiSwitch(switchVid, switchRid, client, translator, sai, warmBoot) {}
MOCK_METHOD(void, postPortRemove, (sai_object_id_t portRid), (override));
MOCK_METHOD(void, removeExistingObjectReference, (sai_object_id_t rid), (override));
MOCK_METHOD(void, eraseRidAndVid, (sai_object_id_t rid, sai_object_id_t vid));
};
class SyncdTest : public ::testing::Test
{
protected:
void SetUp() override
{
m_sai = std::make_shared<MockableSaiInterface>();
m_opt = std::make_shared<CommandLineOptions>();
m_syncd = std::make_shared<Syncd>(m_sai, m_opt, false);
m_opt->m_enableTempView = true;
m_opt->m_startType = SAI_START_TYPE_FASTFAST_BOOT;
clearDB();
}
void TearDown() override
{
clearDB();
}
std::shared_ptr<MockableSaiInterface> m_sai;
std::shared_ptr<CommandLineOptions> m_opt;
std::shared_ptr<Syncd> m_syncd;
};
TEST_F(SyncdTest, processNotifySyncd)
{
auto sai = std::make_shared<MockableSaiInterface>();
auto opt = std::make_shared<syncd::CommandLineOptions>();
opt->m_enableTempView = true;
opt->m_startType = SAI_START_TYPE_FASTFAST_BOOT;
syncd::Syncd syncd_object(sai, opt, false);
MockSelectableChannel consumer;
EXPECT_CALL(consumer, empty()).WillOnce(testing::Return(true));
EXPECT_CALL(consumer, pop(testing::_, testing::_)).WillOnce(testing::Invoke([](swss::KeyOpFieldsValuesTuple& kco, bool initViewMode) {
kfvKey(kco) = SYNCD_APPLY_VIEW;
kfvOp(kco) = REDIS_ASIC_STATE_COMMAND_NOTIFY;
}));
syncd_object.processEvent(consumer);
}
TEST_F(SyncdTest, processStatsCapabilityQuery)
{
auto sai = std::make_shared<MockableSaiInterface>();
auto opt = std::make_shared<syncd::CommandLineOptions>();
opt->m_enableTempView = true;
opt->m_startType = SAI_START_TYPE_FASTFAST_BOOT;
syncd::Syncd syncd_object(sai, opt, false);
auto translator = syncd_object.m_translator;
translator->insertRidAndVid(0x11000000000000, 0x21000000000000);
MockSelectableChannel consumer;
EXPECT_CALL(consumer, empty()).WillOnce(testing::Return(true));
EXPECT_CALL(consumer, pop(testing::_, testing::_))
.Times(1)
.WillRepeatedly(testing::Invoke([](swss::KeyOpFieldsValuesTuple& kco, bool initViewMode) {
static int callCount = 0;
if (callCount == 0)
{
kfvKey(kco) = "oid:0x21000000000000";
kfvOp(kco) = REDIS_ASIC_STATE_COMMAND_STATS_CAPABILITY_QUERY;
kfvFieldsValues(kco) = {
std::make_pair("OBJECT_TYPE", "SAI_OBJECT_TYPE_QUEUE"),
std::make_pair("LIST_SIZE", "1")
};
}
else
{
kfvKey(kco) = "";
kfvOp(kco) = "";
kfvFieldsValues(kco).clear();
}
++callCount;
}));
syncd_object.processEvent(consumer);
}
TEST_F(SyncdTest, BulkCreateTest)
{
m_opt->m_enableSaiBulkSupport = true;
sai_object_id_t switchVid = 0x21000000000000;
sai_object_id_t switchRid = 0x11000000000001;
sai_object_id_t portVid = 0x10000000000002;
sai_object_id_t portRid = 0x11000000000002;
auto translator = m_syncd->m_translator;
translator->insertRidAndVid(switchRid, switchVid);
translator->insertRidAndVid(portRid, portVid);
std::vector<uint32_t> lanes = {52, 53};
m_syncd->m_client->setPortLanes(switchVid, portRid, lanes);
m_sai->mock_objectTypeQuery = [switchRid, portRid](sai_object_id_t oid) {
sai_object_type_t ot = SAI_OBJECT_TYPE_NULL;
if (oid == switchRid)
ot = SAI_OBJECT_TYPE_SWITCH;
else if (oid == portRid)
ot = SAI_OBJECT_TYPE_PORT;
else
ot = SAI_OBJECT_TYPE_QUEUE;
return ot;
};
m_sai->mock_switchIdQuery = [switchVid](sai_object_id_t oid) {
return switchVid;
};
m_sai->mock_get = [switchRid, portVid, portRid](sai_object_type_t objectType, sai_object_id_t objectId, uint32_t attrCount, sai_attribute_t* attrList) -> sai_status_t {
if (attrCount != 1) {
return SAI_STATUS_INVALID_PARAMETER;
}
if (attrList[0].id == SAI_SWITCH_ATTR_PORT_LIST) {
if (objectType == SAI_OBJECT_TYPE_SWITCH && objectId == switchRid) {
attrList[0].value.objlist.count = 1;
attrList[0].value.objlist.list[0] = portRid;
return SAI_STATUS_SUCCESS;
}
return SAI_STATUS_INVALID_PARAMETER;
}
if (attrList[0].id == SAI_SWITCH_ATTR_PORT_NUMBER) {
attrList[0].value.u32 = 1;
return SAI_STATUS_SUCCESS;
}
if (objectType == SAI_OBJECT_TYPE_PORT) {
if (attrList[0].id == SAI_PORT_ATTR_HW_LANE_LIST) {
if (objectId == portRid) {
if (attrList[0].value.u32list.list != nullptr) {
attrList[0].value.u32list.count = 2;
static uint32_t hw_lanes[2] = {52, 53};
memcpy(attrList[0].value.u32list.list, hw_lanes, sizeof(uint32_t) * 2);
} else {
return SAI_STATUS_BUFFER_OVERFLOW;
}
}
}
if (attrList[0].id == SAI_PORT_ATTR_PORT_SERDES_ID) {
attrList[0].value.oid = SAI_NULL_OBJECT_ID;
return SAI_STATUS_SUCCESS;
}
return SAI_STATUS_SUCCESS;
}
if (attrList[0].id == SAI_SWITCH_ATTR_SRC_MAC_ADDRESS) {
if (objectType == SAI_OBJECT_TYPE_SWITCH && objectId == switchRid) {
static sai_mac_t mac = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55};
memcpy(attrList[0].value.mac, mac, sizeof(sai_mac_t));
return SAI_STATUS_SUCCESS;
}
return SAI_STATUS_INVALID_PARAMETER;
}
return SAI_STATUS_NOT_SUPPORTED;
};
// Create a mock switch and set expectations
auto mockSwitch = std::make_shared<MockSaiSwitch>(switchVid, switchRid, m_syncd->m_client, translator, m_sai, false);
m_syncd->m_switches[switchVid] = mockSwitch;
EXPECT_CALL(*mockSwitch, postPortRemove(testing::_))
.WillRepeatedly(testing::Invoke([](sai_object_id_t rid) {
}));
EXPECT_CALL(*mockSwitch, removeExistingObjectReference(testing::_))
.WillRepeatedly(testing::Invoke([](sai_object_id_t rid) {
}));
EXPECT_CALL(*mockSwitch, eraseRidAndVid(testing::_, testing::_))
.WillRepeatedly(testing::Invoke([](sai_object_id_t rid, sai_object_id_t vid) {
}));
swss::KeyOpFieldsValuesTuple kco;
std::string key = "SAI_OBJECT_TYPE_PORT:bulk:1";
std::string op = "bulkcreate";
std::vector<swss::FieldValueTuple> values = {
{"oid:0x10000000000002", "SAI_PORT_ATTR_ADMIN_STATE=true"}
};
kco = std::make_tuple(key, op, values);
auto channel = std::make_shared<MockSelectableChannel>();
int popCallCount = 0;
EXPECT_CALL(*channel, empty())
.WillRepeatedly([&popCallCount]() {
return popCallCount > 0; // Return true after the first pop call
});
EXPECT_CALL(*channel, pop(testing::_, testing::_))
.Times(testing::AnyNumber())
.WillRepeatedly(testing::DoAll(
testing::SetArgReferee<0>(kco),
testing::Invoke([&popCallCount](swss::KeyOpFieldsValuesTuple&, bool) {
popCallCount++;
})
));
m_sai->mock_bulkCreate = [](
sai_object_type_t,
sai_object_id_t,
uint32_t,
const uint32_t*,
const sai_attribute_t**,
sai_bulk_op_error_mode_t,
sai_object_id_t*,
sai_status_t*) -> sai_status_t {
return SAI_STATUS_NOT_IMPLEMENTED;
};
m_syncd->processEvent(*channel);
}
TEST_F(SyncdTest, BulkSetTest)
{
m_opt->m_enableSaiBulkSupport = true;
auto translator = m_syncd->m_translator;
translator->insertRidAndVid(0x11000000000001, 0x21000000000000); // Switch
translator->insertRidAndVid(0x1100000000000d, 0x1000000000000d); // Port 1
translator->insertRidAndVid(0x1100000000000e, 0x1000000000000e); // Port 2
swss::KeyOpFieldsValuesTuple kco;
std::string key = "SAI_OBJECT_TYPE_PORT:bulk:1";
std::string op = "bulkset";
std::vector<swss::FieldValueTuple> values = {
{"oid:0x1000000000000d", "SAI_PORT_ATTR_ADMIN_STATE=true"},
{"oid:0x1000000000000e", "SAI_PORT_ATTR_ADMIN_STATE=false"}
};
kco = std::make_tuple(key, op, values);
auto channel = std::make_shared<MockSelectableChannel>();
EXPECT_CALL(*channel, empty())
.WillOnce(testing::Return(false))
.WillRepeatedly(testing::Return(true));
EXPECT_CALL(*channel, pop(testing::_, testing::_))
.Times(testing::AnyNumber())
.WillRepeatedly(testing::DoAll(
testing::SetArgReferee<0>(kco),
testing::Return()
));
m_sai->mock_bulkSet = [](
sai_object_type_t,
uint32_t,
const sai_object_id_t*,
const sai_attribute_t*,
sai_bulk_op_error_mode_t,
sai_status_t*) -> sai_status_t {
return SAI_STATUS_NOT_IMPLEMENTED;
};
m_syncd->processEvent(*channel);
}
TEST_F(SyncdTest, BulkRemoveTest)
{
m_opt->m_enableSaiBulkSupport = true;
sai_object_id_t switchVid = 0x21000000000000;
sai_object_id_t switchRid = 0x11000000000001;
sai_object_id_t portVid = 0x10000000000002;
sai_object_id_t portRid = 0x11000000000002;
auto translator = m_syncd->m_translator;
translator->insertRidAndVid(switchRid, switchVid);
translator->insertRidAndVid(portRid, portVid);
std::vector<uint32_t> lanes = {52, 53};
m_syncd->m_client->setPortLanes(switchVid, portRid, lanes);
std::set<sai_object_id_t> removedRids;
m_sai->mock_objectTypeQuery = [switchRid, portRid, &removedRids](sai_object_id_t oid) {
sai_object_type_t ot = SAI_OBJECT_TYPE_NULL;
if (removedRids.find(oid) != removedRids.end()) {
return SAI_OBJECT_TYPE_NULL;
}
if (oid == switchRid)
ot = SAI_OBJECT_TYPE_SWITCH;
else if (oid == portRid)
ot = SAI_OBJECT_TYPE_PORT;
else
ot = SAI_OBJECT_TYPE_QUEUE;
return ot;
};
m_sai->mock_switchIdQuery = [switchVid](sai_object_id_t oid) {
return switchVid;
};
m_sai->mock_get = [switchRid, portVid, portRid](sai_object_type_t objectType, sai_object_id_t objectId, uint32_t attrCount, sai_attribute_t* attrList) -> sai_status_t {
if (attrCount != 1) {
return SAI_STATUS_INVALID_PARAMETER;
}
if (attrList[0].id == SAI_SWITCH_ATTR_PORT_LIST) {
if (objectType == SAI_OBJECT_TYPE_SWITCH && objectId == switchRid) {
attrList[0].value.objlist.count = 1;
attrList[0].value.objlist.list[0] = portRid;
return SAI_STATUS_SUCCESS;
}
return SAI_STATUS_INVALID_PARAMETER;
}
if (attrList[0].id == SAI_SWITCH_ATTR_PORT_NUMBER) {
attrList[0].value.u32 = 1;
return SAI_STATUS_SUCCESS;
}
if (objectType == SAI_OBJECT_TYPE_PORT) {
if (attrList[0].id == SAI_PORT_ATTR_HW_LANE_LIST) {
if (objectId == portRid) {
if (attrList[0].value.u32list.list != nullptr) {
attrList[0].value.u32list.count = 2;
static uint32_t hw_lanes[2] = {52, 53};
memcpy(attrList[0].value.u32list.list, hw_lanes, sizeof(uint32_t) * 2);
} else {
return SAI_STATUS_BUFFER_OVERFLOW;
}
}
}
if (attrList[0].id == SAI_PORT_ATTR_PORT_SERDES_ID) {
attrList[0].value.oid = SAI_NULL_OBJECT_ID;
return SAI_STATUS_SUCCESS;
}
return SAI_STATUS_SUCCESS;
}
if (attrList[0].id == SAI_SWITCH_ATTR_SRC_MAC_ADDRESS) {
if (objectType == SAI_OBJECT_TYPE_SWITCH && objectId == switchRid) {
static sai_mac_t mac = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55};
memcpy(attrList[0].value.mac, mac, sizeof(sai_mac_t));
return SAI_STATUS_SUCCESS;
}
return SAI_STATUS_INVALID_PARAMETER;
}
return SAI_STATUS_NOT_SUPPORTED;
};
// Create a mock switch and set expectations
auto mockSwitch = std::make_shared<MockSaiSwitch>(switchVid, switchRid, m_syncd->m_client, translator, m_sai, false);
m_syncd->m_switches[switchVid] = mockSwitch;
EXPECT_CALL(*mockSwitch, postPortRemove(testing::_))
.WillRepeatedly(testing::Invoke([](sai_object_id_t rid) {
}));
EXPECT_CALL(*mockSwitch, removeExistingObjectReference(testing::_))
.WillRepeatedly(testing::Invoke([](sai_object_id_t rid) {
}));
EXPECT_CALL(*mockSwitch, eraseRidAndVid(testing::_, testing::_))
.WillRepeatedly(testing::Invoke([](sai_object_id_t rid, sai_object_id_t vid) {
}));
swss::KeyOpFieldsValuesTuple kco;
std::string key = "SAI_OBJECT_TYPE_PORT:bulk:1";
std::string op = "bulkremove";
std::vector<swss::FieldValueTuple> values = {
{"oid:0x10000000000002", "SAI_PORT_ATTR_ADMIN_STATE=true"}
};
kco = std::make_tuple(key, op, values);
auto channel = std::make_shared<MockSelectableChannel>();
int popCallCount = 0;
EXPECT_CALL(*channel, empty())
.WillRepeatedly([&popCallCount]() {
return popCallCount > 0; // Return true after the first pop call
});
EXPECT_CALL(*channel, pop(testing::_, testing::_))
.Times(testing::AnyNumber())
.WillRepeatedly(testing::DoAll(
testing::SetArgReferee<0>(kco),
testing::Invoke([&popCallCount](swss::KeyOpFieldsValuesTuple&, bool) {
popCallCount++;
})
));
m_sai->mock_bulkRemove = [](
sai_object_type_t,
uint32_t,
const sai_object_id_t*,
sai_bulk_op_error_mode_t,
sai_status_t*) -> sai_status_t {
return SAI_STATUS_NOT_IMPLEMENTED;
};
m_syncd->processEvent(*channel);
}
#endif