blob: 6507f084a9c3fdd57c238f51556b5a6b0a208a4f [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>
Václav Kubernátc31bd602019-03-07 11:44:48 +010010#include <libyang/Tree_Data.hpp>
Václav Kubernát0d4db442018-07-18 17:18:43 +020011#include <libyang/Tree_Schema.hpp>
12#include <string_view>
Václav Kubernát26b56082020-02-03 18:28:56 +010013#include "UniqueResource.hpp"
Václav Kubernát0d4db442018-07-18 17:18:43 +020014#include "utils.hpp"
15#include "yang_schema.hpp"
16
17class YangLoadError : public std::runtime_error {
18public:
19 using std::runtime_error::runtime_error;
20 ~YangLoadError() override = default;
21};
22
23class UnsupportedYangTypeException : public std::runtime_error {
24public:
25 using std::runtime_error::runtime_error;
26 ~UnsupportedYangTypeException() override = default;
27};
28
29class InvalidSchemaQueryException : public std::runtime_error {
30public:
31 using std::runtime_error::runtime_error;
32 ~InvalidSchemaQueryException() override = default;
33};
34
Václav Kubernát0d4db442018-07-18 17:18:43 +020035YangSchema::YangSchema()
Václav Kubernát51d08012020-06-08 15:20:12 +020036 : m_context(std::make_shared<libyang::Context>(nullptr, LY_CTX_DISABLE_SEARCHDIR_CWD))
Václav Kubernát0d4db442018-07-18 17:18:43 +020037{
Václav Kubernát0d4db442018-07-18 17:18:43 +020038}
39
Václav Kubernát1d50a5b2020-02-03 16:44:22 +010040YangSchema::YangSchema(std::shared_ptr<libyang::Context> lyCtx)
41 : m_context(lyCtx)
42{
Václav Kubernát1d50a5b2020-02-03 16:44:22 +010043}
44
Václav Kubernát0d4db442018-07-18 17:18:43 +020045YangSchema::~YangSchema() = default;
46
47void YangSchema::addSchemaString(const char* schema)
48{
49 if (!m_context->parse_module_mem(schema, LYS_IN_YANG)) {
50 throw YangLoadError("Couldn't load schema");
51 }
52}
53
54void YangSchema::addSchemaDirectory(const char* directoryName)
55{
56 if (m_context->set_searchdir(directoryName)) {
57 throw YangLoadError("Couldn't add schema search directory");
58 }
59}
60
61void YangSchema::addSchemaFile(const char* filename)
62{
63 if (!m_context->parse_module_path(filename, LYS_IN_YANG)) {
64 throw YangLoadError("Couldn't load schema");
65 }
66}
67
Václav Kubernát75877de2019-11-20 17:43:02 +010068bool YangSchema::isModule(const std::string& name) const
Václav Kubernát0d4db442018-07-18 17:18:43 +020069{
70 const auto set = modules();
71 return set.find(name) != set.end();
72}
73
Václav Kubernát2db124c2020-05-28 21:58:36 +020074bool YangSchema::listHasKey(const schemaPath_& listPath, const std::string& key) const
75{
76 const auto keys = listKeys(listPath);
77 return keys.find(key) != keys.end();
78}
79
Václav Kubernátc3866792020-02-20 14:12:56 +010080bool YangSchema::leafIsKey(const std::string& leafPath) const
81{
82 auto node = getSchemaNode(leafPath);
Václav Kubernát3a433232020-07-08 17:52:50 +020083 if (!node || node->nodetype() != LYS_LEAF) {
Václav Kubernátc3866792020-02-20 14:12:56 +010084 return false;
Václav Kubernát3a433232020-07-08 17:52:50 +020085 }
Václav Kubernátc3866792020-02-20 14:12:56 +010086
87 return libyang::Schema_Node_Leaf{node}.is_key().get();
88}
89
Václav Kubernát47a3f672019-11-08 15:42:43 +010090libyang::S_Schema_Node YangSchema::impl_getSchemaNode(const std::string& node) const
Václav Kubernát0d4db442018-07-18 17:18:43 +020091{
Václav Kubernátf2b91e02019-04-11 15:36:48 +020092 // If no node is found find_path prints an error message, so we have to
93 // disable logging
94 // https://github.com/CESNET/libyang/issues/753
95 {
96 int oldOptions;
97 auto logBlocker = make_unique_resource(
98 [&oldOptions]() {
99 oldOptions = libyang::set_log_options(0);
100 },
101 [&oldOptions]() {
102 libyang::set_log_options(oldOptions);
103 });
Václav Kubernáte7248b22020-06-26 15:38:59 +0200104 auto res = m_context->get_node(nullptr, node.c_str());
105 if (!res) { // If no node is found, try output rpc nodes too.
106 res = m_context->get_node(nullptr, node.c_str(), 1);
107 }
108 return res;
Václav Kubernátf2b91e02019-04-11 15:36:48 +0200109 }
Václav Kubernát0d4db442018-07-18 17:18:43 +0200110}
111
Václav Kubernát47a3f672019-11-08 15:42:43 +0100112
113libyang::S_Schema_Node YangSchema::getSchemaNode(const std::string& node) const
114{
115 return impl_getSchemaNode(node);
116}
117
118libyang::S_Schema_Node YangSchema::getSchemaNode(const schemaPath_& location, const ModuleNodePair& node) const
119{
Václav Kubernátefcac932020-01-10 15:26:32 +0100120 std::string absPath = joinPaths(pathToSchemaString(location, Prefixes::Always), fullNodeName(location, node));
Václav Kubernát47a3f672019-11-08 15:42:43 +0100121
122 return impl_getSchemaNode(absPath);
123}
124
Václav Kubernát2db124c2020-05-28 21:58:36 +0200125libyang::S_Schema_Node YangSchema::getSchemaNode(const schemaPath_& listPath) const
126{
127 std::string absPath = pathToSchemaString(listPath, Prefixes::Always);
128 return impl_getSchemaNode(absPath);
129}
130
Václav Kubernát912b9492020-05-29 02:03:40 +0200131const std::set<std::string> YangSchema::listKeys(const schemaPath_& listPath) const
Václav Kubernát0d4db442018-07-18 17:18:43 +0200132{
Václav Kubernát912b9492020-05-29 02:03:40 +0200133 auto node = getSchemaNode(listPath);
134 if (node->nodetype() != LYS_LIST) {
135 return {};
136 }
137
138 auto list = std::make_shared<libyang::Schema_Node_List>(node);
Václav Kubernát0d4db442018-07-18 17:18:43 +0200139 std::set<std::string> keys;
Václav Kubernát2db124c2020-05-28 21:58:36 +0200140 const auto& keysVec = list->keys();
Václav Kubernát0d4db442018-07-18 17:18:43 +0200141
Václav Kubernát90de9502019-11-20 17:19:44 +0100142 std::transform(keysVec.begin(), keysVec.end(), std::inserter(keys, keys.begin()), [](const auto& it) { return it->name(); });
Václav Kubernát0d4db442018-07-18 17:18:43 +0200143 return keys;
144}
145
Václav Kubernát3a99f002020-03-31 02:27:41 +0200146namespace {
Václav Kubernátfa36c822021-01-25 07:51:02 +0100147enum class ResolveMode {
148 Enum,
149 Identity
150};
151/** @brief Resolves a typedef to a type which defines values.
152 * When we need allowed values of a type and that type is a typedef, we need to recurse into the typedef until we find a
153 * type which defines values. These values are the allowed values.
154 * Example:
155 *
156 * typedef MyOtherEnum {
157 * type enumeration {
158 * enum "A";
159 * enum "B";
160 * }
161 * }
162 *
163 * typedef MyEnum {
164 * type MyOtherEnum;
165 * }
166 *
167 * If `toResolve` points to MyEnum, then just doing ->enums()->enm() returns nothing and that means that this particular
168 * typedef (MyEnum) did not say which values are allowed. So, we need to dive into the parent enum (MyOtherEnum) with
169 * ->der()->type(). This typedef (MyOtherEnum) DID specify allowed values and enums()->enm() WILL contain them. These
170 * values are the only relevant values and we don't care about other parent typedefs. We return these values to the
171 * caller.
172 *
173 * For enums, this function simply returns all allowed enums.
174 * For identities, this function returns which bases `toResolve` has.
175 */
176template <ResolveMode TYPE>
177auto resolveTypedef(const libyang::S_Type& toResolve)
Václav Kubernát0d4db442018-07-18 17:18:43 +0200178{
Václav Kubernátfa36c822021-01-25 07:51:02 +0100179 auto type = toResolve;
180 auto getValuesFromType = [] (const libyang::S_Type& type) {
181 if constexpr (TYPE == ResolveMode::Identity) {
182 return type->info()->ident()->ref();
183 } else {
184 return type->info()->enums()->enm();
185 }
186 };
187 auto values = getValuesFromType(type);
188 while (values.empty()) {
Václav Kubernát3a99f002020-03-31 02:27:41 +0200189 type = type->der()->type();
Václav Kubernátfa36c822021-01-25 07:51:02 +0100190 values = getValuesFromType(type);
Václav Kubernát6a8d1d92019-04-24 20:30:36 +0200191 }
Václav Kubernát3a99f002020-03-31 02:27:41 +0200192
Václav Kubernátfa36c822021-01-25 07:51:02 +0100193 return values;
194}
195
196std::set<enum_> enumValues(const libyang::S_Type& type)
197{
198 auto values = resolveTypedef<ResolveMode::Enum>(type);
199
Václav Kubernát3a99f002020-03-31 02:27:41 +0200200 std::vector<libyang::S_Type_Enum> enabled;
Václav Kubernátfa36c822021-01-25 07:51:02 +0100201 std::copy_if(values.begin(), values.end(), std::back_inserter(enabled), [](const libyang::S_Type_Enum& it) {
Václav Kubernát3a99f002020-03-31 02:27:41 +0200202 auto iffeatures = it->iffeature();
203 return std::all_of(iffeatures.begin(), iffeatures.end(), [](auto it) { return it->value(); });
204 });
205
206 std::set<enum_> enumSet;
207 std::transform(enabled.begin(), enabled.end(), std::inserter(enumSet, enumSet.end()), [](auto it) { return enum_{it->name()}; });
208 return enumSet;
Václav Kubernát6a8d1d92019-04-24 20:30:36 +0200209}
210
Václav Kubernát2984f442020-02-20 17:43:35 +0100211std::set<identityRef_> validIdentities(const libyang::S_Type& type)
Václav Kubernát3a99f002020-03-31 02:27:41 +0200212{
213 std::set<identityRef_> identSet;
214
Václav Kubernátfa36c822021-01-25 07:51:02 +0100215 for (auto base : resolveTypedef<ResolveMode::Identity>(type)) { // Iterate over all bases
Václav Kubernát3a99f002020-03-31 02:27:41 +0200216 identSet.emplace(base->module()->name(), base->name());
217 // Iterate over derived identities (this is recursive!)
218 for (auto derived : base->der()->schema()) {
219 identSet.emplace(derived->module()->name(), derived->name());
220 }
221 }
222
223 return identSet;
224}
225
Václav Kubernát2984f442020-02-20 17:43:35 +0100226std::string leafrefPath(const libyang::S_Type& type)
Václav Kubernát3a99f002020-03-31 02:27:41 +0200227{
Václav Kubernát2984f442020-02-20 17:43:35 +0100228 return type->info()->lref()->target()->path(LYS_PATH_FIRST_PREFIX);
Václav Kubernát3a99f002020-03-31 02:27:41 +0200229}
230}
231
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200232template <typename NodeType>
Václav Kubernát13b23d72020-04-16 21:49:51 +0200233yang::TypeInfo YangSchema::impl_leafType(const libyang::S_Schema_Node& node) const
Václav Kubernát6a8d1d92019-04-24 20:30:36 +0200234{
235 using namespace std::string_literals;
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200236 auto leaf = std::make_shared<NodeType>(node);
Václav Kubernát13b23d72020-04-16 21:49:51 +0200237 auto leafUnits = leaf->units();
238 std::function<yang::TypeInfo(std::shared_ptr<libyang::Type>)> resolveType;
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100239 resolveType = [this, &resolveType, leaf, leafUnits](std::shared_ptr<libyang::Type> type) -> yang::TypeInfo {
Václav Kubernát13b23d72020-04-16 21:49:51 +0200240 yang::LeafDataType resType;
Václav Kubernát2984f442020-02-20 17:43:35 +0100241 switch (type->base()) {
242 case LY_TYPE_STRING:
Václav Kubernát13b23d72020-04-16 21:49:51 +0200243 resType.emplace<yang::String>();
244 break;
Václav Kubernát2984f442020-02-20 17:43:35 +0100245 case LY_TYPE_DEC64:
Václav Kubernát13b23d72020-04-16 21:49:51 +0200246 resType.emplace<yang::Decimal>();
247 break;
Václav Kubernát2984f442020-02-20 17:43:35 +0100248 case LY_TYPE_BOOL:
Václav Kubernát13b23d72020-04-16 21:49:51 +0200249 resType.emplace<yang::Bool>();
250 break;
Václav Kubernát2984f442020-02-20 17:43:35 +0100251 case LY_TYPE_INT8:
Václav Kubernát13b23d72020-04-16 21:49:51 +0200252 resType.emplace<yang::Int8>();
253 break;
Václav Kubernát2984f442020-02-20 17:43:35 +0100254 case LY_TYPE_INT16:
Václav Kubernát13b23d72020-04-16 21:49:51 +0200255 resType.emplace<yang::Int16>();
256 break;
Václav Kubernát2984f442020-02-20 17:43:35 +0100257 case LY_TYPE_INT32:
Václav Kubernát13b23d72020-04-16 21:49:51 +0200258 resType.emplace<yang::Int32>();
259 break;
Václav Kubernát2984f442020-02-20 17:43:35 +0100260 case LY_TYPE_INT64:
Václav Kubernát13b23d72020-04-16 21:49:51 +0200261 resType.emplace<yang::Int64>();
262 break;
Václav Kubernát2984f442020-02-20 17:43:35 +0100263 case LY_TYPE_UINT8:
Václav Kubernát13b23d72020-04-16 21:49:51 +0200264 resType.emplace<yang::Uint8>();
265 break;
Václav Kubernát2984f442020-02-20 17:43:35 +0100266 case LY_TYPE_UINT16:
Václav Kubernát13b23d72020-04-16 21:49:51 +0200267 resType.emplace<yang::Uint16>();
268 break;
Václav Kubernát2984f442020-02-20 17:43:35 +0100269 case LY_TYPE_UINT32:
Václav Kubernát13b23d72020-04-16 21:49:51 +0200270 resType.emplace<yang::Uint32>();
271 break;
Václav Kubernát2984f442020-02-20 17:43:35 +0100272 case LY_TYPE_UINT64:
Václav Kubernát13b23d72020-04-16 21:49:51 +0200273 resType.emplace<yang::Uint64>();
274 break;
Václav Kubernát2984f442020-02-20 17:43:35 +0100275 case LY_TYPE_BINARY:
Václav Kubernát13b23d72020-04-16 21:49:51 +0200276 resType.emplace<yang::Binary>();
277 break;
Jan Kundrát379bb572020-05-07 03:23:13 +0200278 case LY_TYPE_EMPTY:
279 resType.emplace<yang::Empty>();
280 break;
Václav Kubernát2984f442020-02-20 17:43:35 +0100281 case LY_TYPE_ENUM:
Václav Kubernát13b23d72020-04-16 21:49:51 +0200282 resType.emplace<yang::Enum>(enumValues(type));
283 break;
Václav Kubernát2984f442020-02-20 17:43:35 +0100284 case LY_TYPE_IDENT:
Václav Kubernát13b23d72020-04-16 21:49:51 +0200285 resType.emplace<yang::IdentityRef>(validIdentities(type));
286 break;
Václav Kubernát2984f442020-02-20 17:43:35 +0100287 case LY_TYPE_LEAFREF:
Václav Kubernát13b23d72020-04-16 21:49:51 +0200288 resType.emplace<yang::LeafRef>(::leafrefPath(type), std::make_unique<yang::TypeInfo>(leafType(::leafrefPath(type))));
289 break;
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100290 case LY_TYPE_BITS: {
291 auto resBits = yang::Bits{};
292 for (const auto& bit : type->info()->bits()->bit()) {
293 resBits.m_allowedValues.emplace(bit->name());
Václav Kubernátdab73ca2020-10-26 23:44:43 +0100294 }
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100295 resType.emplace<yang::Bits>(std::move(resBits));
296 break;
297 }
298 case LY_TYPE_UNION: {
299 auto resUnion = yang::Union{};
300 for (auto unionType : type->info()->uni()->types()) {
301 resUnion.m_unionTypes.emplace_back(resolveType(unionType));
Václav Kubernát2984f442020-02-20 17:43:35 +0100302 }
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100303 resType.emplace<yang::Union>(std::move(resUnion));
304 break;
305 }
Václav Kubernát2984f442020-02-20 17:43:35 +0100306 default:
307 using namespace std::string_literals;
308 throw UnsupportedYangTypeException("the type of "s + leaf->name() + " is not supported: " + std::to_string(leaf->type()->base()));
309 }
Václav Kubernát13b23d72020-04-16 21:49:51 +0200310
311 std::optional<std::string> resUnits;
312
313 if (leafUnits) {
314 resUnits = leafUnits;
315 } else {
316 for (auto parentTypedef = type->der(); parentTypedef; parentTypedef = parentTypedef->type()->der()) {
317 auto units = parentTypedef->units();
318 if (units) {
319 resUnits = units;
320 break;
321 }
322 }
323 }
324
Václav Kubernát1ae24f42020-12-01 02:32:04 +0100325 std::optional<std::string> resDescription;
326
327 // checking for parentTypedef->type()->der() means I'm going to enter inside base types like "string". These
328 // also have a description, but it isn't too helpful ("human-readable string")
329 for (auto parentTypedef = type->der(); parentTypedef && parentTypedef->type()->der(); parentTypedef = parentTypedef->type()->der()) {
330 auto dsc = parentTypedef->dsc();
331 if (dsc) {
332 resDescription = dsc;
333 break;
334 }
335 }
336
337 return yang::TypeInfo(resType, resUnits, resDescription);
Václav Kubernát13b23d72020-04-16 21:49:51 +0200338 };
Václav Kubernát2984f442020-02-20 17:43:35 +0100339 return resolveType(leaf->type());
Václav Kubernát6a8d1d92019-04-24 20:30:36 +0200340}
341
Václav Kubernát13b23d72020-04-16 21:49:51 +0200342yang::TypeInfo YangSchema::leafType(const schemaPath_& location, const ModuleNodePair& node) const
Václav Kubernát9bf36852020-02-18 17:47:56 +0100343{
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200344 auto lyNode = getSchemaNode(location, node);
345 switch (lyNode->nodetype()) {
346 case LYS_LEAF:
347 return impl_leafType<libyang::Schema_Node_Leaf>(lyNode);
348 case LYS_LEAFLIST:
349 return impl_leafType<libyang::Schema_Node_Leaflist>(lyNode);
350 default:
351 throw std::logic_error("YangSchema::leafType: type must be leaf or leaflist");
352 }
Václav Kubernát9bf36852020-02-18 17:47:56 +0100353}
354
Václav Kubernát13b23d72020-04-16 21:49:51 +0200355yang::TypeInfo YangSchema::leafType(const std::string& path) const
Václav Kubernát9bf36852020-02-18 17:47:56 +0100356{
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200357 auto lyNode = getSchemaNode(path);
358 switch (lyNode->nodetype()) {
359 case LYS_LEAF:
360 return impl_leafType<libyang::Schema_Node_Leaf>(lyNode);
361 case LYS_LEAFLIST:
362 return impl_leafType<libyang::Schema_Node_Leaflist>(lyNode);
363 default:
364 throw std::logic_error("YangSchema::leafType: type must be leaf or leaflist");
365 }
Václav Kubernát9bf36852020-02-18 17:47:56 +0100366}
Václav Kubernát6a8d1d92019-04-24 20:30:36 +0200367
Václav Kubernát6fcd0282020-02-21 16:33:08 +0100368std::optional<std::string> YangSchema::leafTypeName(const std::string& path) const
369{
370 libyang::Schema_Node_Leaf leaf(getSchemaNode(path));
Václav Kubernát76ba4ec2020-05-18 13:26:56 +0200371 return leaf.type()->der().get() && leaf.type()->der()->type()->der().get() ? std::optional{leaf.type()->der()->name()} : std::nullopt;
Václav Kubernát6fcd0282020-02-21 16:33:08 +0100372}
373
Václav Kubernátbd5e3c22020-02-19 15:22:00 +0100374std::string YangSchema::leafrefPath(const std::string& leafrefPath) const
375{
376 using namespace std::string_literals;
377 libyang::Schema_Node_Leaf leaf(getSchemaNode(leafrefPath));
378 return leaf.type()->info()->lref()->target()->path(LYS_PATH_FIRST_PREFIX);
379}
Václav Kubernát0d4db442018-07-18 17:18:43 +0200380
381std::set<std::string> YangSchema::modules() const
382{
Jan Kundrát4030b772018-08-23 15:54:56 +0200383 const auto& modules = m_context->get_module_iter();
Václav Kubernát0d4db442018-07-18 17:18:43 +0200384
385 std::set<std::string> res;
Václav Kubernát90de9502019-11-20 17:19:44 +0100386 std::transform(modules.begin(), modules.end(), std::inserter(res, res.end()), [](const auto module) { return module->name(); });
Václav Kubernát0d4db442018-07-18 17:18:43 +0200387 return res;
388}
389
Václav Kubernát95b08872020-04-28 01:04:17 +0200390std::set<ModuleNodePair> YangSchema::availableNodes(const boost::variant<dataPath_, schemaPath_, module_>& path, const Recursion recursion) const
Václav Kubernát0d4db442018-07-18 17:18:43 +0200391{
392 using namespace std::string_view_literals;
Václav Kubernát95b08872020-04-28 01:04:17 +0200393 std::set<ModuleNodePair> res;
Václav Kubernáte7d4aea2018-09-11 18:15:48 +0200394 std::vector<libyang::S_Schema_Node> nodes;
Václav Kubernát3a823f42020-04-29 23:40:21 +0200395 std::string topLevelModule;
Václav Kubernáte7d4aea2018-09-11 18:15:48 +0200396
Václav Kubernát3a823f42020-04-29 23:40:21 +0200397 if (path.type() == typeid(module_)) {
398 nodes = m_context->get_module(boost::get<module_>(path).m_name.c_str())->data_instantiables(0);
Václav Kubernát0d4db442018-07-18 17:18:43 +0200399 } else {
Václav Kubernát3a823f42020-04-29 23:40:21 +0200400 auto schemaPath = anyPathToSchemaPath(path);
401 if (schemaPath.m_nodes.empty()) {
402 nodes = m_context->data_instantiables(0);
403 } else {
404 const auto pathString = pathToSchemaString(schemaPath, Prefixes::Always);
405 const auto node = getSchemaNode(pathString);
406 nodes = node->child_instantiables(0);
407 topLevelModule = schemaPath.m_nodes.begin()->m_prefix->m_name;
408 }
Václav Kubernát0d4db442018-07-18 17:18:43 +0200409 }
Václav Kubernáte7d4aea2018-09-11 18:15:48 +0200410
Václav Kubernátef085742020-04-21 09:28:44 +0200411 for (const auto& node : nodes) {
Václav Kubernát3a433232020-07-08 17:52:50 +0200412 if (node->module()->name() == "ietf-yang-library"sv) {
Václav Kubernát89728d82018-09-13 16:28:28 +0200413 continue;
Václav Kubernát3a433232020-07-08 17:52:50 +0200414 }
Václav Kubernátaaafeae2020-05-05 15:41:45 +0200415
Václav Kubernáte7d4aea2018-09-11 18:15:48 +0200416 if (recursion == Recursion::Recursive) {
417 for (auto it : node->tree_dfs()) {
Václav Kubernát95b08872020-04-28 01:04:17 +0200418 res.insert(ModuleNodePair(boost::none, it->path(LYS_PATH_FIRST_PREFIX)));
Václav Kubernáte7d4aea2018-09-11 18:15:48 +0200419 }
420 } else {
Václav Kubernát95b08872020-04-28 01:04:17 +0200421 ModuleNodePair toInsert;
Václav Kubernát3a823f42020-04-29 23:40:21 +0200422 if (topLevelModule.empty() || topLevelModule != node->module()->name()) {
Václav Kubernát82d74632020-05-11 15:59:53 +0200423 toInsert.first = node->module()->type() == 0 ? node->module()->name() : libyang::Submodule(node->module()).belongsto()->name();
Václav Kubernát4f77a252019-02-19 16:51:30 +0100424 }
Václav Kubernát95b08872020-04-28 01:04:17 +0200425 toInsert.second = node->name();
Václav Kubernát4f77a252019-02-19 16:51:30 +0100426 res.insert(toInsert);
Václav Kubernáte7d4aea2018-09-11 18:15:48 +0200427 }
428 }
429
Václav Kubernát0d4db442018-07-18 17:18:43 +0200430 return res;
431}
Václav Kubernáta6c5fff2018-09-07 15:16:25 +0200432
433void YangSchema::loadModule(const std::string& moduleName)
434{
435 m_context->load_module(moduleName.c_str());
436}
437
Václav Kubernátbf9c6112019-11-04 16:03:35 +0100438void YangSchema::enableFeature(const std::string& moduleName, const std::string& featureName)
439{
Václav Kubernát7aaf6dc2020-06-26 18:03:57 +0200440 using namespace std::string_literals;
Václav Kubernátf44bdda2020-06-22 15:58:41 +0200441 auto module = getYangModule(moduleName);
442 if (!module) {
Václav Kubernátf44bdda2020-06-22 15:58:41 +0200443 throw std::runtime_error("Module \""s + moduleName + "\" doesn't exist.");
444 }
Václav Kubernát7aaf6dc2020-06-26 18:03:57 +0200445 if (module->feature_enable(featureName.c_str())) {
446 throw std::runtime_error("Can't enable feature \""s + featureName + "\" for module \"" + moduleName + "\".");
447 }
Václav Kubernátbf9c6112019-11-04 16:03:35 +0100448}
449
Václav Kubernátb52dc252019-12-04 13:03:39 +0100450void YangSchema::registerModuleCallback(const std::function<std::string(const char*, const char*, const char*, const char*)>& clb)
Václav Kubernáta6c5fff2018-09-07 15:16:25 +0200451{
452 auto lambda = [clb](const char* mod_name, const char* mod_revision, const char* submod_name, const char* submod_revision) {
453 (void)submod_revision;
Václav Kubernátb52dc252019-12-04 13:03:39 +0100454 auto moduleSource = clb(mod_name, mod_revision, submod_name, submod_revision);
Václav Kubernáta6c5fff2018-09-07 15:16:25 +0200455 if (moduleSource.empty()) {
456 return libyang::Context::mod_missing_cb_return{LYS_IN_YANG, nullptr};
457 }
458 return libyang::Context::mod_missing_cb_return{LYS_IN_YANG, strdup(moduleSource.c_str())};
459 };
460
461 auto deleter = [](void* data) {
Václav Kubernáte3d282a2020-07-09 10:32:12 +0200462 free(data); // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc)
Václav Kubernáta6c5fff2018-09-07 15:16:25 +0200463 };
464 m_context->add_missing_module_callback(lambda, deleter);
465}
Václav Kubernátc31bd602019-03-07 11:44:48 +0100466
467std::shared_ptr<libyang::Data_Node> YangSchema::dataNodeFromPath(const std::string& path, const std::optional<const std::string> value) const
468{
469 return std::make_shared<libyang::Data_Node>(m_context,
470 path.c_str(),
471 value ? value.value().c_str() : nullptr,
472 LYD_ANYDATA_CONSTSTRING,
Václav Kubernátab612e92019-11-26 19:51:31 +0100473 LYD_PATH_OPT_EDIT);
Václav Kubernátc31bd602019-03-07 11:44:48 +0100474}
475
476std::shared_ptr<libyang::Module> YangSchema::getYangModule(const std::string& name)
477{
Václav Kubernátf44bdda2020-06-22 15:58:41 +0200478 return m_context->get_module(name.c_str());
Václav Kubernátc31bd602019-03-07 11:44:48 +0100479}
Václav Kubernát34ee85a2020-02-18 17:12:12 +0100480
481namespace {
482yang::NodeTypes impl_nodeType(const libyang::S_Schema_Node& node)
483{
484 if (!node) {
485 throw InvalidNodeException();
486 }
487 switch (node->nodetype()) {
488 case LYS_CONTAINER:
489 return libyang::Schema_Node_Container{node}.presence() ? yang::NodeTypes::PresenceContainer : yang::NodeTypes::Container;
490 case LYS_LEAF:
491 return yang::NodeTypes::Leaf;
492 case LYS_LIST:
493 return yang::NodeTypes::List;
Václav Kubernátaaafeae2020-05-05 15:41:45 +0200494 case LYS_RPC:
495 return yang::NodeTypes::Rpc;
496 case LYS_ACTION:
497 return yang::NodeTypes::Action;
498 case LYS_NOTIF:
499 return yang::NodeTypes::Notification;
500 case LYS_ANYXML:
501 return yang::NodeTypes::AnyXml;
502 case LYS_LEAFLIST:
503 return yang::NodeTypes::LeafList;
Václav Kubernát34ee85a2020-02-18 17:12:12 +0100504 default:
Václav Kubernát2a141392020-02-18 17:12:32 +0100505 throw InvalidNodeException(); // FIXME: Implement all types.
Václav Kubernát34ee85a2020-02-18 17:12:12 +0100506 }
507}
508}
509
510yang::NodeTypes YangSchema::nodeType(const schemaPath_& location, const ModuleNodePair& node) const
511{
512 return impl_nodeType(getSchemaNode(location, node));
513}
514
515yang::NodeTypes YangSchema::nodeType(const std::string& path) const
516{
517 return impl_nodeType(getSchemaNode(path));
518}
Václav Kubernát1e09bd62020-02-17 15:13:38 +0100519
520std::optional<std::string> YangSchema::description(const std::string& path) const
521{
522 auto node = getSchemaNode(path.c_str());
523 return node->dsc() ? std::optional{node->dsc()} : std::nullopt;
524}
Václav Kubernát0599e9f2020-04-21 09:51:33 +0200525
Václav Kubernáta1c4c9e2020-04-22 00:37:52 +0200526yang::Status YangSchema::status(const std::string& location) const
527{
528 auto node = getSchemaNode(location.c_str());
529 if (node->flags() & LYS_STATUS_DEPRC) {
530 return yang::Status::Deprecated;
531 } else if (node->flags() & LYS_STATUS_OBSLT) {
532 return yang::Status::Obsolete;
533 } else {
534 return yang::Status::Current;
535 }
536}
537
Václav Kubernátd8408e02020-12-02 05:13:27 +0100538bool YangSchema::hasInputNodes(const std::string& path) const
539{
540 auto node = getSchemaNode(path.c_str());
541 if (auto type = node->nodetype(); type != LYS_ACTION && type != LYS_RPC) {
542 throw std::logic_error("StaticSchema::hasInputNodes called with non-RPC/action path");
543 }
544
545 // The first child gives the /input node and then I check whether it has a child.
546 return node->child()->child().get();
547}
548
Václav Kubernát0599e9f2020-04-21 09:51:33 +0200549bool YangSchema::isConfig(const std::string& path) const
550{
Václav Kubernáte7248b22020-06-26 15:38:59 +0200551 auto node = getSchemaNode(path.c_str());
552 if (node->flags() & LYS_CONFIG_W) {
553 return true;
554 }
555
556 // Node can still be an input node.
557 while (node->parent()) {
558 node = node->parent();
559 if (node->nodetype() == LYS_INPUT) {
560 return true;
561 }
562 }
563
564 return false;
Václav Kubernát0599e9f2020-04-21 09:51:33 +0200565}
Václav Kubernátb1a75c62020-04-21 15:20:16 +0200566
567std::optional<std::string> YangSchema::defaultValue(const std::string& leafPath) const
568{
569 libyang::Schema_Node_Leaf leaf(getSchemaNode(leafPath));
570
571 if (auto leafDefault = leaf.dflt()) {
572 return leafDefault;
573 }
574
575 for (auto type = leaf.type()->der(); type != nullptr; type = type->type()->der()) {
576 if (auto defaultValue = type->dflt()) {
577 return defaultValue;
578 }
579 }
580
581 return std::nullopt;
582}
Václav Kubernáta8789602020-07-20 15:18:19 +0200583
584std::string YangSchema::dataPathToSchemaPath(const std::string& path)
585{
586 return getSchemaNode(path)->path(LYS_PATH_FIRST_PREFIX);
587}