Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add VS support for setting stats via redis DB channel (#366)
Browse files Browse the repository at this point in the history
* Add VS support for setting stats via redis DB channel

* Remove SET description
kcudnik authored and lguohan committed Oct 29, 2018

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent 39e70cd commit 1fdaf47
Showing 3 changed files with 317 additions and 89 deletions.
7 changes: 7 additions & 0 deletions vslib/inc/sai_vs.h
Original file line number Diff line number Diff line change
@@ -41,6 +41,13 @@ extern "C" {
*/
#define SAI_VS_UNITTEST_SET_RO_OP "set_ro"

/**
* @def SAI_VS_UNITTEST_SET_STATS
*
* Notification operation for "SET" stats on specific object.
*/
#define SAI_VS_UNITTEST_SET_STATS_OP "set_stats"

/**
* @def SAI_VS_UNITTEST_ENABLE
*
340 changes: 251 additions & 89 deletions vslib/src/sai_vs_interfacequery.cpp
Original file line number Diff line number Diff line change
@@ -4,6 +4,8 @@
#include <string.h>
#include <unistd.h>

#include <algorithm>

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

@@ -21,140 +23,293 @@ std::shared_ptr<swss::DBConnector> g_dbNtf;
volatile bool g_fdbAgingThreadRun;
std::shared_ptr<std::thread> g_fdbAgingThread;

void handleUnittestChannelOp(
_In_ const std::string &op,

void channelOpEnableUnittests(
_In_ const std::string &key,
_In_ const std::vector<swss::FieldValueTuple> &values)
{
MUTEX();
SWSS_LOG_ENTER();

bool enable = (key == "true");

meta_unittests_enable(enable);
}

void channelOpSetReadOnlyAttribute(
_In_ const std::string &key,
_In_ const std::vector<swss::FieldValueTuple> &values)
{
SWSS_LOG_ENTER();

/*
* 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.
*/
for (const auto &v: values)
{
SWSS_LOG_DEBUG("attr: %s: %s", fvField(v).c_str(), fvValue(v).c_str());
}

SWSS_LOG_NOTICE("read only SET: op = %s, key = %s", op.c_str(), key.c_str());
if (values.size() != 1)
{
SWSS_LOG_ERROR("expected 1 value only, but given: %zu", values.size());
return;
}

if (op == SAI_VS_UNITTEST_ENABLE_UNITTESTS)
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)
{
bool enable = (key == "true");
SWSS_LOG_ERROR("invalid object type: %d", object_type);
return;
}

auto info = sai_metadata_get_object_type_info(object_type);

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

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)
{
for (const auto &v: values)
{
SWSS_LOG_DEBUG("attr: %s: %s", fvField(v).c_str(), fvValue(v).c_str());
}
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;
}

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

const std::string &str_object_type = key.substr(0, key.find(":"));
const std::string &str_object_id = key.substr(key.find(":") + 1);
if (switch_id == SAI_NULL_OBJECT_ID)
{
SWSS_LOG_ERROR("failed to find switch id for oid %s", str_object_id.c_str());
return;
}

sai_object_type_t object_type;
sai_deserialize_object_type(str_object_type, object_type);
// oid is validated and we got switch id

if (object_type == SAI_OBJECT_TYPE_NULL || object_type >= SAI_OBJECT_TYPE_MAX)
{
SWSS_LOG_ERROR("invalid object type: %d", object_type);
return;
}
const std::string &str_attr_id = fvField(values.at(0));
const std::string &str_attr_value = fvValue(values.at(0));

auto info = sai_metadata_get_object_type_info(object_type);
auto meta = sai_metadata_get_attr_metadata_by_attr_id_name(str_attr_id.c_str());

if (info->isnonobjectid)
{
SWSS_LOG_ERROR("non object id %s is not supported yet", str_object_type.c_str());
return;
}
if (meta == NULL)
{
SWSS_LOG_ERROR("failed to find attr %s", str_attr_id.c_str());
return;
}

sai_object_id_t object_id;
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;
}

sai_deserialize_object_id(str_object_id, object_id);
// we got attr metadata

sai_object_type_t ot = sai_object_type_query(object_id);
sai_attribute_t attr;

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;
}
attr.id = meta->attrid;

sai_object_id_t switch_id = sai_switch_id_query(object_id);
sai_deserialize_attr_value(str_attr_value, *meta, attr);

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

// oid is validated and we got switch id
sai_status_t status = meta_unittests_allow_readonly_set_once(meta->objecttype, meta->attrid);

const std::string &str_attr_id = fvField(values.at(0));
const std::string &str_attr_value = fvValue(values.at(0));
if (status != SAI_STATUS_SUCCESS)
{
SWSS_LOG_ERROR("failed to enable SET readonly attribute once: %s", sai_serialize_status(status).c_str());
return;
}

auto meta = sai_metadata_get_attr_metadata_by_attr_id_name(str_attr_id.c_str());
sai_object_meta_key_t meta_key = { .objecttype = ot, .objectkey = { .key = { .object_id = object_id } } };

if (meta == NULL)
{
SWSS_LOG_ERROR("failed to find attr %s", str_attr_id.c_str());
return;
}
status = info->set(&meta_key, &attr);

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

// we got attr metadata
sai_deserialize_free_attribute_value(meta->attrvaluetype, attr);
}

sai_attribute_t attr;
void channelOpSetStats(
_In_ const std::string &key,
_In_ const std::vector<swss::FieldValueTuple> &values)
{
SWSS_LOG_ENTER();

attr.id = meta->attrid;
// NOTE: we need to find stats for specific object, later SAI already have
// this feature and this search could be optimized here:
// https://github.com/opencomputeproject/SAI/commit/acc83933ff21c68e8ef10c9826de45807fdc0438

sai_deserialize_attr_value(str_attr_value, *meta, attr);
sai_object_id_t oid;

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

sai_status_t status = meta_unittests_allow_readonly_set_once(meta->objecttype, meta->attrid);
sai_object_type_t ot = sai_object_type_query(oid);

if (status != SAI_STATUS_SUCCESS)
if (ot == SAI_OBJECT_TYPE_NULL)
{
SWSS_LOG_ERROR("invalid object id: %s", key.c_str());
return;
}

sai_object_id_t switch_id = sai_switch_id_query(oid);

if (switch_id == SAI_NULL_OBJECT_ID)
{
SWSS_LOG_ERROR("unable to get switch_id from oid: %s", key.c_str());
return;
}

/*
* Check if object for statistics was created and exists on switch.
*/

auto &objectHash = g_switch_state_map.at(switch_id)->objectHash.at(ot);

auto it = objectHash.find(key.c_str());

if (it == objectHash.end())
{
SWSS_LOG_ERROR("object not found: %s", key.c_str());
return;
}

/*
* Check if object for statistics have statistic map created, if not
* create empty map.
*/

auto &countersMap = g_switch_state_map.at(switch_id)->countersMap;

auto mapit = countersMap.find(key);

if (mapit == countersMap.end())
countersMap[key] = std::map<int,uint64_t>();

/*
* Find stats enum based on object type. In new metadata we have enum on
* object type, but here we need to find it manually enum is in format
* "sai_" + object_type + "_stat_t"
*/

std::string lower_ot = sai_serialize_object_type(ot).substr(16); // 16 = skip "SAI_OBJECT_TYPE_"

std::transform(lower_ot.begin(), lower_ot.end(), lower_ot.begin(), ::tolower);

std::string stat_enum_name = "sai_" + lower_ot + "_stat_t";

const sai_enum_metadata_t* statenum = NULL;

for (size_t i = 0; i < sai_metadata_all_enums_count; ++i)
{
if (sai_metadata_all_enums[i]->name == stat_enum_name)
{
SWSS_LOG_ERROR("failed to enable SET readonly attribute once: %s", sai_serialize_status(status).c_str());
return;
SWSS_LOG_INFO("found enum %s", stat_enum_name.c_str());
// found
statenum = sai_metadata_all_enums[i];
break;
}
}

if (statenum == NULL)
{
SWSS_LOG_ERROR("failed to find stat enum: %s", stat_enum_name.c_str());
return;
}

sai_object_meta_key_t meta_key = { .objecttype = ot, .objectkey = { .key = { .object_id = object_id } } };
for (auto v: values)
{
// value format: stat_enum_name:uint64

status = info->set(&meta_key, &attr);
auto name = fvField(v);

if (status != SAI_STATUS_SUCCESS)
uint64_t value;

if (sscanf(fvValue(v).c_str(), "%lu", &value) != 1)
{
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());
SWSS_LOG_ERROR("failed to deserialize %s as couner value uint64_t", fvValue(v).c_str());
}
else

// linear search

int enumvalue = -1;

for (size_t i = 0; i < statenum->valuescount; ++i)
{
if (statenum->valuesnames[i] == name)
{
enumvalue = statenum->values[i];
break;
}
}

if (enumvalue == -1)
{
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());
SWSS_LOG_ERROR("failed to find enum value: %s", name.c_str());
continue;
}

sai_deserialize_free_attribute_value(meta->attrvaluetype, attr);
SWSS_LOG_DEBUG("writting %s = %lu on %s", name.c_str(), value, key.c_str());

countersMap.at(key)[enumvalue] = value;
}
}

void handleUnittestChannelOp(
_In_ const std::string &op,
_In_ const std::string &key,
_In_ const std::vector<swss::FieldValueTuple> &values)
{
MUTEX();

SWSS_LOG_ENTER();

/*
* 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.
*/

SWSS_LOG_NOTICE("op = %s, key = %s", op.c_str(), key.c_str());

for (const auto &v: values)
{
SWSS_LOG_DEBUG("attr: %s: %s", fvField(v).c_str(), fvValue(v).c_str());
}

if (op == SAI_VS_UNITTEST_ENABLE_UNITTESTS)
{
channelOpEnableUnittests(key, values);
}
else if (op == SAI_VS_UNITTEST_SET_RO_OP)
{
channelOpSetReadOnlyAttribute(key, values);
}
else if (op == SAI_VS_UNITTEST_SET_STATS_OP)
{
channelOpSetStats(key, values);
}
else
{
@@ -197,7 +352,14 @@ void unittestChannelThreadProc()

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

handleUnittestChannelOp(op, data, values);
try
{
handleUnittestChannelOp(op, data, values);
}
catch (const std::exception &e)
{
SWSS_LOG_ERROR("Exception: op = %s, data = %s, %s", op.c_str(), data.c_str(), e.what());
}
}
}

59 changes: 59 additions & 0 deletions vslib/src/tests.cpp
Original file line number Diff line number Diff line change
@@ -244,6 +244,63 @@ void test_set_readonly_attribute_via_redis()
ASSERT_TRUE(sai_metadata_sai_switch_api->set_switch_attribute(switch_id, &attr) != SAI_STATUS_SUCCESS);
}

void test_set_stats_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));

std::vector<sai_object_id_t> ports;

uint32_t expected_ports = 32;

ports.resize(expected_ports);

attr.id = SAI_SWITCH_ATTR_PORT_LIST;
attr.value.objlist.count = expected_ports;
attr.value.objlist.list = ports.data();

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

// this scope contains all operations needed to perform set stats on object
{
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_PORT_STAT_IF_IN_ERRORS";
std::string value = "42"; // uint64_t

swss::FieldValueTuple fvt(field, value);

entry.push_back(fvt);

std::string data = sai_serialize_object_id(ports.at(0)); // set on 1st port

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

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

sai_port_stat_t ids[1] = { SAI_PORT_STAT_IF_IN_ERRORS };
uint64_t counters[1] = { 0 } ;

SUCCESS(sai_metadata_sai_port_api->get_port_stats(ports.at(0), 1, ids, counters));

ASSERT_TRUE(counters[0] == 42);
}

void sai_fdb_event_notification(
_In_ uint32_t count,
@@ -701,6 +758,8 @@ int main()

test_get_stats();

test_set_stats_via_redis();

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

0 comments on commit 1fdaf47

Please sign in to comment.