blob: 57e66e5f8fb0b9eeefcbdbc10e5c4eb42f2e64a6 [file] [log] [blame]
Václav Kubernát0d4db442018-07-18 17:18:43 +02001/*
2 * Copyright (C) 2018 CESNET, https://photonics.cesnet.cz/
3 * Copyright (C) 2018 FIT CVUT, https://fit.cvut.cz/
4 *
5 * Written by Václav Kubernát <kubervac@fit.cvut.cz>
6 *
7*/
8
9#include <libyang/Libyang.hpp>
10#include <libyang/Tree_Schema.hpp>
11#include <string_view>
12#include "utils.hpp"
13#include "yang_schema.hpp"
14
15class YangLoadError : public std::runtime_error {
16public:
17 using std::runtime_error::runtime_error;
18 ~YangLoadError() override = default;
19};
20
21class UnsupportedYangTypeException : public std::runtime_error {
22public:
23 using std::runtime_error::runtime_error;
24 ~UnsupportedYangTypeException() override = default;
25};
26
27class InvalidSchemaQueryException : public std::runtime_error {
28public:
29 using std::runtime_error::runtime_error;
30 ~InvalidSchemaQueryException() override = default;
31};
32
33std::string pathToYangAbsSchemPath(const path_& path)
34{
35 std::string res = "/";
36 std::string currentModule;
37
38 for (const auto& it : path.m_nodes) {
39 const auto name = nodeToSchemaString(it);
40
41 if (it.m_suffix.type() == typeid(module_)) {
42 currentModule = name;
43 continue;
44 } else {
45 res += currentModule + ":";
46 res += name + "/";
47 }
48 }
49
50 return res;
51}
52
53YangSchema::YangSchema()
Václav Kubernáta6c5fff2018-09-07 15:16:25 +020054 : m_context(std::make_shared<libyang::Context>(nullptr, LY_CTX_DISABLE_SEARCHDIRS | LY_CTX_DISABLE_SEARCHDIR_CWD))
Václav Kubernát0d4db442018-07-18 17:18:43 +020055{
Václav Kubernát0d4db442018-07-18 17:18:43 +020056}
57
58YangSchema::~YangSchema() = default;
59
60void YangSchema::addSchemaString(const char* schema)
61{
62 if (!m_context->parse_module_mem(schema, LYS_IN_YANG)) {
63 throw YangLoadError("Couldn't load schema");
64 }
65}
66
67void YangSchema::addSchemaDirectory(const char* directoryName)
68{
69 if (m_context->set_searchdir(directoryName)) {
70 throw YangLoadError("Couldn't add schema search directory");
71 }
72}
73
74void YangSchema::addSchemaFile(const char* filename)
75{
76 if (!m_context->parse_module_path(filename, LYS_IN_YANG)) {
77 throw YangLoadError("Couldn't load schema");
78 }
79}
80
81bool YangSchema::isModule(const path_&, const std::string& name) const
82{
83 const auto set = modules();
84 return set.find(name) != set.end();
85}
86
87bool YangSchema::isContainer(const path_& location, const ModuleNodePair& node) const
88{
89 const auto schemaNode = getSchemaNode(location, node);
90 return schemaNode && schemaNode->nodetype() == LYS_CONTAINER;
91}
92
93bool YangSchema::isLeaf(const path_& location, const ModuleNodePair& node) const
94{
95 const auto schemaNode = getSchemaNode(location, node);
96 return schemaNode && schemaNode->nodetype() == LYS_LEAF;
97}
98
99bool YangSchema::isList(const path_& location, const ModuleNodePair& node) const
100{
101 const auto schemaNode = getSchemaNode(location, node);
102 return schemaNode && schemaNode->nodetype() == LYS_LIST;
103}
104
105bool YangSchema::isPresenceContainer(const path_& location, const ModuleNodePair& node) const
106{
107 if (!isContainer(location, node))
108 return false;
Jan Kundrát4ed0e9f2018-08-23 16:56:58 +0200109 return libyang::Schema_Node_Container(getSchemaNode(location, node)).presence();
Václav Kubernát0d4db442018-07-18 17:18:43 +0200110}
111
112bool YangSchema::leafEnumHasValue(const path_& location, const ModuleNodePair& node, const std::string& value) const
113{
114 if (!isLeaf(location, node) || leafType(location, node) != yang::LeafDataTypes::Enum)
115 return false;
116
Jan Kundrát4ed0e9f2018-08-23 16:56:58 +0200117 libyang::Schema_Node_Leaf leaf(getSchemaNode(location, node));
Jan Kundrát4030b772018-08-23 15:54:56 +0200118 const auto& enm = leaf.type()->info()->enums()->enm();
Václav Kubernát0d4db442018-07-18 17:18:43 +0200119
Jan Kundrát4030b772018-08-23 15:54:56 +0200120 return std::any_of(enm.begin(), enm.end(), [=](const auto& x) { return x->name() == value; });
Václav Kubernát0d4db442018-07-18 17:18:43 +0200121}
122
123bool YangSchema::listHasKey(const path_& location, const ModuleNodePair& node, const std::string& key) const
124{
125 if (!isList(location, node))
126 return false;
127 const auto keys = listKeys(location, node);
128 return keys.find(key) != keys.end();
129}
130
131bool YangSchema::nodeExists(const std::string& location, const std::string& node) const
132{
133 const auto absPath = location + "/" + node;
134 const auto set = m_context->find_path(absPath.c_str());
135 return set->number() == 1;
136}
137
Jan Kundrát4ed0e9f2018-08-23 16:56:58 +0200138libyang::S_Set YangSchema::getNodeSet(const path_& location, const ModuleNodePair& node) const
Václav Kubernát0d4db442018-07-18 17:18:43 +0200139{
140 std::string absPath = location.m_nodes.empty() ? "" : "/";
141 absPath += pathToAbsoluteSchemaString(location) + "/" + fullNodeName(location, node);
142 return m_context->find_path(absPath.c_str());
143}
144
Jan Kundrát4ed0e9f2018-08-23 16:56:58 +0200145libyang::S_Schema_Node YangSchema::getSchemaNode(const path_& location, const ModuleNodePair& node) const
Václav Kubernát0d4db442018-07-18 17:18:43 +0200146{
147 const auto set = getNodeSet(location, node);
148 if (!set)
149 return nullptr;
Jan Kundrát4030b772018-08-23 15:54:56 +0200150 const auto& schemaSet = set->schema();
Václav Kubernát0d4db442018-07-18 17:18:43 +0200151 if (set->number() != 1)
152 return nullptr;
Jan Kundrát4030b772018-08-23 15:54:56 +0200153 return *schemaSet.begin();
Václav Kubernát0d4db442018-07-18 17:18:43 +0200154}
155
156const std::set<std::string> YangSchema::listKeys(const path_& location, const ModuleNodePair& node) const
157{
158 std::set<std::string> keys;
159 if (!isList(location, node))
160 return keys;
Jan Kundrát4ed0e9f2018-08-23 16:56:58 +0200161 libyang::Schema_Node_List list(getSchemaNode(location, node));
Jan Kundrát4030b772018-08-23 15:54:56 +0200162 const auto& keysVec = list.keys();
Václav Kubernát0d4db442018-07-18 17:18:43 +0200163
Jan Kundrát4030b772018-08-23 15:54:56 +0200164 std::transform(keysVec.begin(), keysVec.end(), std::inserter(keys, keys.begin()),
Václav Kubernát0d4db442018-07-18 17:18:43 +0200165 [] (const auto& it) {return it->name();});
166 return keys;
167}
168
169yang::LeafDataTypes YangSchema::leafType(const path_& location, const ModuleNodePair& node) const
170{
171 using namespace std::string_literals;
172 if (!isLeaf(location, node))
173 throw InvalidSchemaQueryException(fullNodeName(location, node) + " is not a leaf");
174
Jan Kundrát4ed0e9f2018-08-23 16:56:58 +0200175 libyang::Schema_Node_Leaf leaf(getSchemaNode(location, node));
Václav Kubernát0d4db442018-07-18 17:18:43 +0200176 switch (leaf.type()->base()) {
177 case LY_TYPE_STRING:
178 return yang::LeafDataTypes::String;
179 case LY_TYPE_DEC64:
180 return yang::LeafDataTypes::Decimal;
181 case LY_TYPE_BOOL:
182 return yang::LeafDataTypes::Bool;
183 case LY_TYPE_INT32:
184 return yang::LeafDataTypes::Int;
185 case LY_TYPE_UINT32:
186 return yang::LeafDataTypes::Uint;
187 case LY_TYPE_ENUM:
188 return yang::LeafDataTypes::Enum;
189 default:
190 throw UnsupportedYangTypeException("the type of "s + fullNodeName(location, node) + " is not supported");
191 }
192}
193
194std::set<std::string> YangSchema::modules() const
195{
Jan Kundrát4030b772018-08-23 15:54:56 +0200196 const auto& modules = m_context->get_module_iter();
Václav Kubernát0d4db442018-07-18 17:18:43 +0200197
198 std::set<std::string> res;
Jan Kundrát4030b772018-08-23 15:54:56 +0200199 std::transform(modules.begin(), modules.end(),
Václav Kubernát0d4db442018-07-18 17:18:43 +0200200 std::inserter(res, res.end()),
201 [] (const auto module) { return module->name(); });
202 return res;
203}
204
Václav Kubernáte7d4aea2018-09-11 18:15:48 +0200205std::set<std::string> YangSchema::childNodes(const path_& path, const Recursion recursion) const
Václav Kubernát0d4db442018-07-18 17:18:43 +0200206{
207 using namespace std::string_view_literals;
208 std::set<std::string> res;
Václav Kubernáte7d4aea2018-09-11 18:15:48 +0200209 std::vector<libyang::S_Schema_Node> nodes;
210
Václav Kubernát0d4db442018-07-18 17:18:43 +0200211 if (path.m_nodes.empty()) {
Václav Kubernáte7d4aea2018-09-11 18:15:48 +0200212 nodes = m_context->data_instantiables(0);
Václav Kubernát0d4db442018-07-18 17:18:43 +0200213 } else {
214 const auto absolutePath = "/" + pathToAbsoluteSchemaString(path);
215 const auto set = m_context->find_path(absolutePath.c_str());
Václav Kubernáte7d4aea2018-09-11 18:15:48 +0200216 const auto schemaSet = set->schema();
Jan Kundrát4030b772018-08-23 15:54:56 +0200217 for (auto it = (*schemaSet.begin())->child(); it; it = it->next()) {
Václav Kubernáte7d4aea2018-09-11 18:15:48 +0200218 nodes.push_back(it);
Václav Kubernát0d4db442018-07-18 17:18:43 +0200219 }
220 }
Václav Kubernáte7d4aea2018-09-11 18:15:48 +0200221
222 for (const auto node : nodes) {
223 if (node->module()->name() == "ietf-yang-library"sv)
Václav Kubernát89728d82018-09-13 16:28:28 +0200224 continue;
Václav Kubernáte7d4aea2018-09-11 18:15:48 +0200225 if (recursion == Recursion::Recursive) {
226 for (auto it : node->tree_dfs()) {
227 res.insert(it->path(LYS_PATH_FIRST_PREFIX));
228 }
229 } else {
230 res.insert(std::string(node->module()->name()) + ":" + node->name());
231 }
232 }
233
Václav Kubernát0d4db442018-07-18 17:18:43 +0200234 return res;
235}
Václav Kubernáta6c5fff2018-09-07 15:16:25 +0200236
237void YangSchema::loadModule(const std::string& moduleName)
238{
239 m_context->load_module(moduleName.c_str());
240}
241
242void YangSchema::registerModuleCallback(const std::function<std::string(const char*, const char*, const char*)>& clb)
243{
244 auto lambda = [clb](const char* mod_name, const char* mod_revision, const char* submod_name, const char* submod_revision) {
245 (void)submod_revision;
246 auto moduleSource = clb(mod_name, mod_revision, submod_name);
247 if (moduleSource.empty()) {
248 return libyang::Context::mod_missing_cb_return{LYS_IN_YANG, nullptr};
249 }
250 return libyang::Context::mod_missing_cb_return{LYS_IN_YANG, strdup(moduleSource.c_str())};
251 };
252
253 auto deleter = [](void* data) {
254 free(data);
255 };
256 m_context->add_missing_module_callback(lambda, deleter);
257}