Skip to content

Commit

Permalink
Add virtual switch unittests channel via redis (sonic-net#274)
Browse files Browse the repository at this point in the history
For easier testing when using python framework
  • Loading branch information
kcudnik authored Jan 10, 2018
1 parent 5b97659 commit 2afbae5
Show file tree
Hide file tree
Showing 3 changed files with 300 additions and 2 deletions.
23 changes: 22 additions & 1 deletion vslib/inc/sai_vs.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,33 @@ extern "C" {
*
* By default this flag is set to false.
*/
#define SAI_KEY_VS_HOSTIF_USE_TAP_DEVICE "SAI_VS_HOSTIF_USE_TAP_DEVICE"
#define SAI_KEY_VS_HOSTIF_USE_TAP_DEVICE "SAI_VS_HOSTIF_USE_TAP_DEVICE"

// TODO probaby should be per switch
#define SAI_VALUE_VS_SWITCH_TYPE_BCM56850 "SAI_VS_SWITCH_TYPE_BCM56850"
#define SAI_VALUE_VS_SWITCH_TYPE_MLNX2700 "SAI_VS_SWITCH_TYPE_MLNX2700"

/**
* @def SAI_VS_UNITTEST_CHANNEL
*
* Notification channel for redis database.
*/
#define SAI_VS_UNITTEST_CHANNEL "SAI_VS_UNITTEST_CHANNEL"

/**
* @def SAI_VS_UNITTEST_SET_RO_OP
*
* Notification operation for "SET" READ_ONLY attribute.
*/
#define SAI_VS_UNITTEST_SET_RO_OP "set_ro"

/**
* @def SAI_VS_UNITTEST_ENABLE
*
* Notificatio operation for enabling unittests.
*/
#define SAI_VS_UNITTEST_ENABLE_UNITTESTS "enable_unittests"

#define SAI_VS_VETH_PREFIX "v"

typedef enum _sai_vs_switch_type_t
Expand Down
208 changes: 208 additions & 0 deletions vslib/src/sai_vs_interfacequery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,205 @@
#include "sai_vs_state.h"
#include <string.h>

#include "swss/notificationconsumer.h"
#include "swss/select.h"

bool g_api_initialized = false;
bool g_vs_hostif_use_tap_device = false;
sai_vs_switch_type_t g_vs_switch_type = SAI_VS_SWITCH_TYPE_NONE;
std::recursive_mutex g_recursive_mutex;

bool g_unittestChannelRun;
swss::SelectableEvent g_unittestChannelThreadEvent;
std::shared_ptr<std::thread> g_unittestChannelThread;
std::shared_ptr<swss::NotificationConsumer> g_unittestChannelNotificationConsumer;
std::shared_ptr<swss::DBConnector> g_dbNtf;

void handleUnittestChannelOp(
_In_ const std::string &op,
_In_ const std::string &key,
_In_ const std::vector<swss::FieldValueTuple> &values)
{
/*
* Since we will access and modify DB we need to be under mutex.
*
* NOTE: since this unittest channel is handled in thread, then that means
* there is a DELAY from producer and consumer thread in VS, so if user
* will set value on the specific READ_ONLY value he should wait for some
* time until that value will be propagated to virtual switch.
*/

MUTEX();

SWSS_LOG_ENTER();

SWSS_LOG_NOTICE("read only SET: op = %s, key = %s", op.c_str(), key.c_str());

if (op == SAI_VS_UNITTEST_ENABLE_UNITTESTS)
{
bool enable = (key == "true");

meta_unittests_enable(enable);
}
else if (op == SAI_VS_UNITTEST_SET_RO_OP)
{
for (const auto &v: values)
{
SWSS_LOG_DEBUG("attr: %s: %s", fvField(v).c_str(), fvValue(v).c_str());
}

if (values.size() != 1)
{
SWSS_LOG_ERROR("expected 1 value only, but given: %zu", values.size());
return;
}

const std::string &str_object_type = key.substr(0, key.find(":"));
const std::string &str_object_id = key.substr(key.find(":") + 1);

sai_object_type_t object_type;
sai_deserialize_object_type(str_object_type, object_type);

if (object_type == SAI_OBJECT_TYPE_NULL || object_type >= SAI_OBJECT_TYPE_MAX)
{
SWSS_LOG_ERROR("invalid object type: %d", object_type);
return;
}

auto info = sai_metadata_get_object_type_info(object_type);

if (info->isnonobjectid)
{
SWSS_LOG_ERROR("non object id %s is not supported yet", str_object_type.c_str());
return;
}

sai_object_id_t object_id;

sai_deserialize_object_id(str_object_id, object_id);

sai_object_type_t ot = sai_object_type_query(object_id);

if (ot != object_type)
{
SWSS_LOG_ERROR("object type is differnt than provided %s, but oid is %s",
str_object_type.c_str(), sai_serialize_object_type(ot).c_str());
return;
}

sai_object_id_t switch_id = sai_switch_id_query(object_id);

if (switch_id == SAI_NULL_OBJECT_ID)
{
SWSS_LOG_ERROR("failed to find switch id for oid %s", str_object_id.c_str());
return;
}

// oid is validated and we got switch id

const std::string &str_attr_id = fvField(values.at(0));
const std::string &str_attr_value = fvValue(values.at(0));

auto meta = sai_metadata_get_attr_metadata_by_attr_id_name(str_attr_id.c_str());

if (meta == NULL)
{
SWSS_LOG_ERROR("failed to find attr %s", str_attr_id.c_str());
return;
}

if (meta->objecttype != ot)
{
SWSS_LOG_ERROR("attr %s belongs to differnt object type than oid: %s",
str_attr_id.c_str(), sai_serialize_object_type(ot).c_str());
return;
}

// we got attr metadata

sai_attribute_t attr;

attr.id = meta->attrid;

sai_deserialize_attr_value(str_attr_value, *meta, attr);

SWSS_LOG_NOTICE("switch id is %s", sai_serialize_object_id(switch_id).c_str());

sai_status_t status = meta_unittests_allow_readonly_set_once(meta->objecttype, meta->attrid);

if (status != SAI_STATUS_SUCCESS)
{
SWSS_LOG_ERROR("failed to enable SET readonly attribute once: %s", sai_serialize_status(status).c_str());
return;
}

sai_object_meta_key_t meta_key = { .objecttype = ot, .objectkey = { .key = { .object_id = object_id } } };

status = info->set(&meta_key, &attr);

if (status != SAI_STATUS_SUCCESS)
{
SWSS_LOG_ERROR("failed to set %s to %s on %s",
str_attr_id.c_str(), str_attr_value.c_str(), str_object_id.c_str());
}
else
{
SWSS_LOG_NOTICE("SUCCESS to set %s to %s on %s",
str_attr_id.c_str(), str_attr_value.c_str(), str_object_id.c_str());
}

sai_deserialize_free_attribute_value(meta->attrvaluetype, attr);
}
else
{
SWSS_LOG_ERROR("unknown unittest operation: %s", op.c_str());
}
}

void unittestChannelThreadProc()
{
SWSS_LOG_ENTER();

SWSS_LOG_NOTICE("enter VS unittest channel thread");

swss::Select s;

s.addSelectable(g_unittestChannelNotificationConsumer.get());
s.addSelectable(&g_unittestChannelThreadEvent);

while (g_unittestChannelRun)
{
swss::Selectable *sel;

int fd;

int result = s.select(&sel, &fd);

if (sel == &g_unittestChannelThreadEvent)
{
// user requested shutdown_switch
break;
}

if (result == swss::Select::OBJECT)
{
swss::KeyOpFieldsValuesTuple kco;

std::string op;
std::string data;
std::vector<swss::FieldValueTuple> values;

g_unittestChannelNotificationConsumer->pop(op, data, values);

SWSS_LOG_DEBUG("notification: op = %s, data = %s", op.c_str(), data.c_str());

handleUnittestChannelOp(op, data, values);
}
}

SWSS_LOG_NOTICE("exit VS unittest channel thread");
}

/**
* @brief Serviec method table.
*
Expand Down Expand Up @@ -109,6 +303,13 @@ sai_status_t sai_api_initialize(

clear_local_state();

g_dbNtf = std::make_shared<swss::DBConnector>(ASIC_DB, swss::DBConnector::DEFAULT_UNIXSOCKET, 0);
g_unittestChannelNotificationConsumer = std::make_shared<swss::NotificationConsumer>(g_dbNtf.get(), SAI_VS_UNITTEST_CHANNEL);

g_unittestChannelRun = true;

g_unittestChannelThread = std::make_shared<std::thread>(std::thread(unittestChannelThreadProc));

g_api_initialized = true;

return SAI_STATUS_SUCCESS;
Expand All @@ -129,6 +330,13 @@ sai_status_t sai_api_uninitialize(void)

clear_local_state();

g_unittestChannelRun = false;

//// notify thread that it should end
g_unittestChannelThreadEvent.notify();

g_unittestChannelThread->join();

g_api_initialized = false;

return SAI_STATUS_SUCCESS;
Expand Down
71 changes: 70 additions & 1 deletion vslib/src/tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@
#include <string>
#include <vector>

#include <unistd.h>

#include "swss/logger.h"
#include "swss/dbconnector.h"
#include "swss/schema.h"
#include "swss/notificationproducer.h"

#include "../../meta/saiserialize.h"

extern "C" {
#include <sai.h>
Expand Down Expand Up @@ -174,6 +181,63 @@ void test_set_readonly_attribute()
ASSERT_TRUE(sai_metadata_sai_switch_api->set_switch_attribute(switch_id, &attr) != SAI_STATUS_SUCCESS);
}

void test_set_readonly_attribute_via_redis()
{
SWSS_LOG_ENTER();

sai_attribute_t attr;

sai_object_id_t switch_id;

attr.id = SAI_SWITCH_ATTR_INIT_SWITCH;
attr.value.booldata = true;

SUCCESS(sai_metadata_sai_switch_api->create_switch(&switch_id, 1, &attr));

attr.id = SAI_SWITCH_ATTR_PORT_MAX_MTU;
attr.value.u32 = 42;

// this set should fail
ASSERT_TRUE(sai_metadata_sai_switch_api->set_switch_attribute(switch_id, &attr) != SAI_STATUS_SUCCESS);

// this scope contains all operations needed to perform set operation on readonly attribute
{
swss::DBConnector db(ASIC_DB, "localhost", 6379, 0);
swss::NotificationProducer vsntf(&db, SAI_VS_UNITTEST_CHANNEL);

std::vector<swss::FieldValueTuple> entry;

// needs to be done only once
vsntf.send(SAI_VS_UNITTEST_ENABLE_UNITTESTS, "true", entry);

std::string field = "SAI_SWITCH_ATTR_PORT_MAX_MTU";
std::string value = "42"; // NOTE: normally we need sai_serialize_value()

swss::FieldValueTuple fvt(field, value);

entry.push_back(fvt);

// when using tests from python, user will be required to translate VID2RID here
// need RID here in form 0xYYYYYYYYYYYYYYY
std::string data = "SAI_OBJECT_TYPE_SWITCH:" + sai_serialize_object_id(switch_id);

vsntf.send(SAI_VS_UNITTEST_SET_RO_OP, data, entry);
}

// give some time for notification to propagate to vs via redis
usleep(200*1000);

// just scramble value to make sure that GET will succeed
attr.value.u32 = 1;

SUCCESS(sai_metadata_sai_switch_api->get_switch_attribute(switch_id, 1, &attr));

ASSERT_TRUE(attr.value.u32 == 42);

// second SET should fail
ASSERT_TRUE(sai_metadata_sai_switch_api->set_switch_attribute(switch_id, &attr) != SAI_STATUS_SUCCESS);
}

int main()
{
swss::Logger::getInstance().setMinPrio(swss::Logger::SWSS_DEBUG);
Expand All @@ -186,11 +250,16 @@ int main()

sai_metadata_apis_query(sai_api_query);

//swss::Logger::getInstance().setMinPrio(swss::Logger::SWSS_DEBUG);
//swss::Logger::getInstance().setMinPrio(swss::Logger::SWSS_INFO);

test_ports();

test_set_readonly_attribute();

test_set_readonly_attribute_via_redis();

// make proper unitinialize to close unittest thread
sai_api_uninitialize();

return 0;
}

0 comments on commit 2afbae5

Please sign in to comment.