common/defaultvalueprovider.cpp (385 lines of code) (raw):
#include <boost/algorithm/string.hpp>
#include <iostream>
#include <map>
#include <string>
#include <vector>
#include <dirent.h>
#include <stdio.h>
#include "defaultvalueprovider.h"
#include "logger.h"
#include "table.h"
#include "json.h"
#include "armhelper.h"
using namespace std;
using namespace swss;
[[noreturn]] void ThrowRunTimeError(string message)
{
SWSS_LOG_ERROR("DefaultValueProvider: %s", message.c_str());
throw runtime_error(message);
}
TableInfoBase::TableInfoBase()
{
// C++ need this empty ctor
}
shared_ptr<string> TableInfoBase::GetDefaultValue(const string &key, const string &field)
{
SWSS_LOG_DEBUG("TableInfoBase::GetDefaultValue %s %s\n", key.c_str(), field.c_str());
FieldDefaultValueMapping *fieldMappingPtr;
if (!FoundFieldMappingByKey(key, &fieldMappingPtr)) {
SWSS_LOG_DEBUG("Can't found default value mapping for key %s\n", key.c_str());
return nullptr;
}
auto fieldData = fieldMappingPtr->find(field);
if (fieldData == fieldMappingPtr->end())
{
SWSS_LOG_DEBUG("Can't found default value for field %s\n", field.c_str());
return nullptr;
}
SWSS_LOG_DEBUG("Found default value for field %s=%s\n", field.c_str(), fieldData->second.c_str());
return make_shared<string>(fieldData->second);
}
// existedValues and targetValues can be same container.
void TableInfoBase::AppendDefaultValues(const string &key, map<string, string>& existedValues, map<string, string>& targetValues)
{
SWSS_LOG_DEBUG("TableInfoBase::AppendDefaultValues %s\n", key.c_str());
FieldDefaultValueMapping *fieldMappingPtr;
if (!FoundFieldMappingByKey(key, &fieldMappingPtr)) {
SWSS_LOG_DEBUG("Can't found default value mapping for key %s\n", key.c_str());
return;
}
for (auto &defaultValue : *fieldMappingPtr)
{
auto fieldData = existedValues.find(defaultValue.first);
if (fieldData != existedValues.end())
{
// ignore when a field already has value in existedValues
continue;
}
SWSS_LOG_DEBUG("Append default value: %s=%s\n",defaultValue.first.c_str(), defaultValue.second.c_str());
targetValues.emplace(defaultValue.first, defaultValue.second);
}
}
TableInfoDict::TableInfoDict(TableDefaultValueMapping &tableDefaultValueMapping)
{
for (auto& defaultValueMapping : tableDefaultValueMapping)
{
// KeySchema.first is table name
string table = defaultValueMapping.first.first;
m_defaultValueMapping.emplace(table, defaultValueMapping.second);
SWSS_LOG_DEBUG("TableInfoDict::TableInfoDict %s\n", table.c_str());
}
}
bool TableInfoDict::FoundFieldMappingByKey(const string &key, FieldDefaultValueMapping ** foundMappingPtr)
{
SWSS_LOG_DEBUG("TableInfoDict::FoundFieldMappingByKey %s\n", key.c_str());
auto keyResult = m_defaultValueMapping.find(key);
*foundMappingPtr = keyResult->second.get();
return keyResult != m_defaultValueMapping.end();
}
TableInfoSingleList::TableInfoSingleList(TableDefaultValueMapping &tableDefaultValueMapping)
{
m_defaultValueMapping = tableDefaultValueMapping.begin()->second;
}
bool TableInfoSingleList::FoundFieldMappingByKey(const string &key, FieldDefaultValueMapping ** foundMappingPtr)
{
SWSS_LOG_DEBUG("TableInfoSingleList::FoundFieldMappingByKey %s\n", key.c_str());
*foundMappingPtr = m_defaultValueMapping.get();
return true;
}
TableInfoMultipleList::TableInfoMultipleList(TableDefaultValueMapping &tableDefaultValueMapping)
{
for (auto& defaultValueMapping : tableDefaultValueMapping)
{
// KeySchema.second is field count
int fieldCount = defaultValueMapping.first.second;
m_defaultValueMapping.emplace(fieldCount, defaultValueMapping.second);
}
}
bool TableInfoMultipleList::FoundFieldMappingByKey(const string &key, FieldDefaultValueMapping ** foundMappingPtr)
{
SWSS_LOG_DEBUG("TableInfoMultipleList::FoundFieldMappingByKey %s\n", key.c_str());
int fieldCount = (int)count(key.begin(), key.end(), '|') + 1;
auto keySchema = m_defaultValueMapping.find(fieldCount);
// when not found, key_info still a valid iterator
*foundMappingPtr = keySchema->second.get();
// return false when not found
return keySchema != m_defaultValueMapping.end();
}
shared_ptr<KeySchema> DefaultValueHelper::GetKeySchema(struct lys_node* tableChildNode)
{
SWSS_LOG_DEBUG("DefaultValueHelper::GetKeySchema %s\n",tableChildNode->name);
int keyFieldCount = 0;
string keyValue = "";
if (tableChildNode->nodetype == LYS_LIST)
{
SWSS_LOG_DEBUG("Child list: %s\n",tableChildNode->name);
// when a top level container contains list, the key defined by the 'keys' field.
struct lys_node_list *listNode = (struct lys_node_list*)tableChildNode;
if (listNode->keys_str == nullptr)
{
SWSS_LOG_ERROR("Ignore empty key string on list: %s\n",tableChildNode->name);
return nullptr;
}
string key(listNode->keys_str);
keyFieldCount = (int)count(key.begin(), key.end(), ' ') + 1;
}
else if (tableChildNode->nodetype == LYS_CONTAINER)
{
SWSS_LOG_DEBUG("Child container name: %s\n",tableChildNode->name);
// when a top level container not contains any list, the key is child container name
keyValue = string(tableChildNode->name);
}
else
{
SWSS_LOG_DEBUG("Ignore child element: %s\n",tableChildNode->name);
return nullptr;
}
return make_shared<KeySchema>(keyValue, keyFieldCount);
}
void DefaultValueHelper::GetDefaultValueInfoForLeaf(struct lys_node_leaf* leafNode, shared_ptr<FieldDefaultValueMapping> fieldMapping)
{
if (leafNode->dflt)
{
SWSS_LOG_DEBUG("field: %s, default: %s\n",leafNode->name, leafNode->dflt);
fieldMapping->emplace(string(leafNode->name), string(leafNode->dflt));
}
}
void DefaultValueHelper::GetDefaultValueInfoForChoice(struct lys_node_choice* choiceNode, shared_ptr<FieldDefaultValueMapping> fieldMapping)
{
if (choiceNode->dflt == nullptr)
{
return;
}
// Get choice default value according to: https://www.rfc-editor.org/rfc/rfc7950.html#section-7.9
auto dfltChoice = choiceNode->dflt;
auto fieldInChoice = dfltChoice->child;
while (fieldInChoice)
{
if (fieldInChoice->nodetype != LYS_LEAF)
{
SWSS_LOG_ERROR("choice case %s is not a leaf node\n",fieldInChoice->name);
continue;
}
SWSS_LOG_DEBUG("default choice leaf field: %s\n",fieldInChoice->name);
WARNINGS_NO_CAST_ALIGN
struct lys_node_leaf *dfltLeafNode = reinterpret_cast<struct lys_node_leaf*>(fieldInChoice);
WARNINGS_RESET
if (dfltLeafNode->dflt)
{
SWSS_LOG_DEBUG("default choice leaf field: %s, default: %s\n",dfltLeafNode->name, dfltLeafNode->dflt);
fieldMapping->emplace(string(fieldInChoice->name), string(dfltLeafNode->dflt));
}
fieldInChoice = fieldInChoice->next;
}
}
void DefaultValueHelper::GetDefaultValueInfoForLeaflist(struct lys_node_leaflist *listNode, shared_ptr<FieldDefaultValueMapping> fieldMapping)
{
// Get leaf-list default value according to:https://www.rfc-editor.org/rfc/rfc7950.html#section-7.7
if (listNode->dflt == nullptr)
{
return;
}
const char** dfltValues = listNode->dflt;
//convert list default value to json string
string dfltValueJson = JSon::buildJson(dfltValues);
SWSS_LOG_DEBUG("list field: %s, default: %s\n",listNode->name, dfltValueJson.c_str());
fieldMapping->emplace(string(listNode->name), dfltValueJson);
}
FieldDefaultValueMappingPtr DefaultValueHelper::GetDefaultValueInfo(struct lys_node* tableChildNode)
{
SWSS_LOG_DEBUG("DefaultValueHelper::GetDefaultValueInfo %s\n",tableChildNode->name);
auto field = tableChildNode->child;
auto fieldMapping = make_shared<FieldDefaultValueMapping>();
while (field)
{
if (field->nodetype == LYS_LEAF)
{
WARNINGS_NO_CAST_ALIGN
struct lys_node_leaf *leafNode = reinterpret_cast<struct lys_node_leaf*>(field);
WARNINGS_RESET
SWSS_LOG_DEBUG("leaf field: %s\n",leafNode->name);
GetDefaultValueInfoForLeaf(leafNode, fieldMapping);
}
else if (field->nodetype == LYS_CHOICE)
{
struct lys_node_choice *choiceNode = reinterpret_cast<struct lys_node_choice*>(field);
SWSS_LOG_DEBUG("choice field: %s\n",choiceNode->name);
GetDefaultValueInfoForChoice(choiceNode, fieldMapping);
}
else if (field->nodetype == LYS_LEAFLIST)
{
WARNINGS_NO_CAST_ALIGN
struct lys_node_leaflist *listNode = reinterpret_cast<struct lys_node_leaflist*>(field);
WARNINGS_RESET
SWSS_LOG_DEBUG("list field: %s\n",listNode->name);
GetDefaultValueInfoForLeaflist(listNode, fieldMapping);
}
field = field->next;
}
return fieldMapping;
}
int DefaultValueHelper::BuildTableDefaultValueMapping(struct lys_node* table, TableDefaultValueMapping &tableDefaultValueMapping)
{
int childListCount = 0;
auto nextChild = table->child;
while (nextChild)
{
// get key from schema
auto keySchema = GetKeySchema(nextChild);
if (keySchema == nullptr)
{
nextChild = nextChild->next;
continue;
}
else if (keySchema->second != 0)
{
// when key field count not 0, it's a list node.
childListCount++;
}
// get field name to default value mappings from schema
tableDefaultValueMapping.emplace(*keySchema, GetDefaultValueInfo(nextChild));
nextChild = nextChild->next;
}
return childListCount;
}
// Load default value info from yang model and append to default value mapping
void DefaultValueProvider::AppendTableInfoToMapping(struct lys_node* table)
{
SWSS_LOG_DEBUG("DefaultValueProvider::AppendTableInfoToMapping table name: %s\n",table->name);
TableDefaultValueMapping tableDefaultValueMapping;
int listCount = DefaultValueHelper::BuildTableDefaultValueMapping(table, tableDefaultValueMapping);
// create container data by child list count
shared_ptr<TableInfoBase> tableInfoPtr = nullptr;
switch (listCount)
{
case 0:
{
tableInfoPtr = shared_ptr<TableInfoBase>(new TableInfoDict(tableDefaultValueMapping));
}
break;
case 1:
{
tableInfoPtr = shared_ptr<TableInfoBase>(new TableInfoSingleList(tableDefaultValueMapping));
}
break;
default:
{
tableInfoPtr = shared_ptr<TableInfoBase>(new TableInfoMultipleList(tableDefaultValueMapping));
}
break;
}
m_defaultValueMapping.emplace(string(table->name), tableInfoPtr);
}
shared_ptr<TableInfoBase> DefaultValueProvider::FindDefaultValueInfo(const string &table)
{
SWSS_LOG_DEBUG("DefaultValueProvider::FindDefaultValueInfo %s\n", table.c_str());
auto findResult = m_defaultValueMapping.find(table);
if (findResult == m_defaultValueMapping.end())
{
SWSS_LOG_DEBUG("Not found default value info for %s\n", table.c_str());
return nullptr;
}
return findResult->second;
}
shared_ptr<string> DefaultValueProvider::getDefaultValue(const string &table, const string &key, const string &field)
{
SWSS_LOG_DEBUG("DefaultValueProvider::GetDefaultValue %s %s %s\n", table.c_str(), key.c_str(), field.c_str());
Initialize();
auto defaultValueInfo = FindDefaultValueInfo(table);
if (defaultValueInfo == nullptr)
{
SWSS_LOG_DEBUG("Not found default value info for %s\n", table.c_str());
return nullptr;
}
return defaultValueInfo->GetDefaultValue(key, field);
}
void DefaultValueProvider::InternalAppendDefaultValues(const string &table, const string &key, map<string, string>& existedValues, map<string, string>& defaultValues)
{
SWSS_LOG_DEBUG("DefaultValueProvider::InternalAppendDefaultValues %s %s\n", table.c_str(), key.c_str());
auto defaultValueInfo = FindDefaultValueInfo(table);
if (defaultValueInfo == nullptr)
{
SWSS_LOG_DEBUG("Not found default value info for %s\n", table.c_str());
return;
}
defaultValueInfo->AppendDefaultValues(key, existedValues, defaultValues);
}
void DefaultValueProvider::appendDefaultValues(const string &table, const string &key, vector<pair<string, string>> &fvs)
{
Initialize();
map<string, string> existedValues;
map<string, string> defaultValues;
for (auto& fieldValuePair : fvs)
{
existedValues.emplace(fieldValuePair.first, fieldValuePair.second);
}
InternalAppendDefaultValues(table, key, existedValues, defaultValues);
for (auto& fieldValuePair : defaultValues)
{
fvs.emplace_back(fieldValuePair.first, fieldValuePair.second);
}
}
map<string, string> DefaultValueProvider::getDefaultValues(const string &table, const string &key)
{
Initialize();
map<string, string> result;
InternalAppendDefaultValues(table, key, result, result);
return result;
}
DefaultValueProvider::DefaultValueProvider()
{
}
DefaultValueProvider::~DefaultValueProvider()
{
if (m_context)
{
// set private_destructor to NULL because no any private data
ly_ctx_destroy(m_context, NULL);
}
}
void DefaultValueProvider::Initialize(const char* modulePath)
{
if (m_context != nullptr)
{
return;
}
DIR *moduleDir = opendir(modulePath);
if (!moduleDir)
{
ThrowRunTimeError("Open Yang model path " + string(modulePath) + " failed");
}
m_context = ly_ctx_new(modulePath, LY_CTX_ALLIMPLEMENTED);
struct dirent *subDir;
while ((subDir = readdir(moduleDir)) != nullptr)
{
if (subDir->d_type != DT_REG)
{
continue;
}
SWSS_LOG_DEBUG("file name: %s\n", subDir->d_name);
string fileName(subDir->d_name);
int pos = (int)fileName.find(".yang");
string moduleName = fileName.substr(0, pos);
LoadModule(moduleName, fileName, m_context);
}
closedir(moduleDir);
}
void DefaultValueProvider::LoadModule(const string &name, const string &path, struct ly_ctx *context)
{
const struct lys_module *module = ly_ctx_load_module(
context,
name.c_str(),
EMPTY_STR); // Use EMPTY_STR to revision to load the latest revision
if (module == nullptr)
{
const char* err = ly_errmsg(context);
SWSS_LOG_ERROR("Load Yang file %s failed: %s.\n", path.c_str(), err);
return;
}
if (module->data == nullptr)
{
// Not every yang file should contains yang model
SWSS_LOG_WARN("Yang file %s does not contains model %s.\n", path.c_str(), name.c_str());
return;
}
struct lys_node *topLevelNode = module->data;
while (topLevelNode)
{
if (topLevelNode->nodetype != LYS_CONTAINER)
{
SWSS_LOG_DEBUG("ignore top level element %s, tyoe %d\n",topLevelNode->name, topLevelNode->nodetype);
// Config DB table schema is defined by top level container
topLevelNode = topLevelNode->next;
continue;
}
SWSS_LOG_DEBUG("top level container: %s\n",topLevelNode->name);
auto container = topLevelNode->child;
while (container)
{
SWSS_LOG_DEBUG("container name: %s\n",container->name);
AppendTableInfoToMapping(container);
container = container->next;
}
topLevelNode = topLevelNode->next;
}
}