blob: 2f046c7b9086f7f40a762815ac798c0c88cbad72 [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
Václav Kubernátcfdb9222021-07-07 22:36:24 +02009#include <libyang-cpp/Enum.hpp>
10#include <libyang-cpp/Utils.hpp>
Václav Kubernát0d4db442018-07-18 17:18:43 +020011#include <string_view>
Václav Kubernát26b56082020-02-03 18:28:56 +010012#include "UniqueResource.hpp"
Václav Kubernát0d4db442018-07-18 17:18:43 +020013#include "utils.hpp"
14#include "yang_schema.hpp"
15
16class YangLoadError : public std::runtime_error {
17public:
18 using std::runtime_error::runtime_error;
19 ~YangLoadError() override = default;
20};
21
22class UnsupportedYangTypeException : public std::runtime_error {
23public:
24 using std::runtime_error::runtime_error;
25 ~UnsupportedYangTypeException() override = default;
26};
27
28class InvalidSchemaQueryException : public std::runtime_error {
29public:
30 using std::runtime_error::runtime_error;
31 ~InvalidSchemaQueryException() override = default;
32};
33
Václav Kubernát0d4db442018-07-18 17:18:43 +020034YangSchema::YangSchema()
Jan Kundrátf59b83c2022-03-18 18:12:08 +010035 : m_context(std::nullopt, libyang::ContextOptions::DisableSearchDirs | libyang::ContextOptions::SetPrivParsed)
Václav Kubernát0d4db442018-07-18 17:18:43 +020036{
Václav Kubernát0d4db442018-07-18 17:18:43 +020037}
38
Václav Kubernátcfdb9222021-07-07 22:36:24 +020039YangSchema::YangSchema(libyang::Context lyCtx)
Václav Kubernát1d50a5b2020-02-03 16:44:22 +010040 : m_context(lyCtx)
41{
Václav Kubernát1d50a5b2020-02-03 16:44:22 +010042}
43
Václav Kubernát0d4db442018-07-18 17:18:43 +020044YangSchema::~YangSchema() = default;
45
46void YangSchema::addSchemaString(const char* schema)
47{
Jan Kundrát95c55822023-05-18 17:12:20 +020048 m_context.parseModule(std::string{schema}, libyang::SchemaFormat::YANG);
Václav Kubernát0d4db442018-07-18 17:18:43 +020049}
50
51void YangSchema::addSchemaDirectory(const char* directoryName)
52{
Václav Kubernátcfdb9222021-07-07 22:36:24 +020053 m_context.setSearchDir(directoryName);
Václav Kubernát0d4db442018-07-18 17:18:43 +020054}
55
56void YangSchema::addSchemaFile(const char* filename)
57{
Jan Kundrát95c55822023-05-18 17:12:20 +020058 m_context.parseModule(std::filesystem::path{filename}, libyang::SchemaFormat::YANG);
Václav Kubernát0d4db442018-07-18 17:18:43 +020059}
60
Václav Kubernát75877de2019-11-20 17:43:02 +010061bool YangSchema::isModule(const std::string& name) const
Václav Kubernát0d4db442018-07-18 17:18:43 +020062{
Jan Kundrátf59b83c2022-03-18 18:12:08 +010063 return m_context.getModuleImplemented(name).has_value();
Václav Kubernát0d4db442018-07-18 17:18:43 +020064}
65
Václav Kubernát2db124c2020-05-28 21:58:36 +020066bool YangSchema::listHasKey(const schemaPath_& listPath, const std::string& key) const
67{
68 const auto keys = listKeys(listPath);
69 return keys.find(key) != keys.end();
70}
71
Václav Kubernátc3866792020-02-20 14:12:56 +010072bool YangSchema::leafIsKey(const std::string& leafPath) const
73{
74 auto node = getSchemaNode(leafPath);
Václav Kubernátcfdb9222021-07-07 22:36:24 +020075 if (!node || node->nodeType() != libyang::NodeType::Leaf) {
Václav Kubernátc3866792020-02-20 14:12:56 +010076 return false;
Václav Kubernát3a433232020-07-08 17:52:50 +020077 }
Václav Kubernátc3866792020-02-20 14:12:56 +010078
Václav Kubernátcfdb9222021-07-07 22:36:24 +020079 return node->asLeaf().isKey();
Václav Kubernátc3866792020-02-20 14:12:56 +010080}
81
Václav Kubernátcfdb9222021-07-07 22:36:24 +020082std::optional<libyang::SchemaNode> YangSchema::impl_getSchemaNode(const std::string& node) const
Václav Kubernát0d4db442018-07-18 17:18:43 +020083{
Václav Kubernátcfdb9222021-07-07 22:36:24 +020084 // libyang::Context::findPath throws an exception, when no matching schema node is found. This exception has the
85 // ValidationFailure error code. We will catch that exception (and rethrow if it's not the correct error code.
86 //
87 // Also, we need to use findPath twice if we're trying to find output nodes.
88 try {
Jan Kundrátf59b83c2022-03-18 18:12:08 +010089 return m_context.findPath(node);
Václav Kubernátcfdb9222021-07-07 22:36:24 +020090 } catch (libyang::ErrorWithCode& err) {
91 if (err.code() != libyang::ErrorCode::ValidationFailure) {
92 throw;
Václav Kubernáte7248b22020-06-26 15:38:59 +020093 }
Václav Kubernátf2b91e02019-04-11 15:36:48 +020094 }
Václav Kubernátcfdb9222021-07-07 22:36:24 +020095 try {
Jan Kundrátf59b83c2022-03-18 18:12:08 +010096 return m_context.findPath(node, libyang::OutputNodes::Yes);
Václav Kubernátcfdb9222021-07-07 22:36:24 +020097 } catch (libyang::ErrorWithCode& err) {
98 if (err.code() != libyang::ErrorCode::ValidationFailure) {
99 throw;
100 }
101 }
102
103 // We didn't find a matching node.
104 return std::nullopt;
Václav Kubernát0d4db442018-07-18 17:18:43 +0200105}
106
Václav Kubernát47a3f672019-11-08 15:42:43 +0100107
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200108std::optional<libyang::SchemaNode> YangSchema::getSchemaNode(const std::string& node) const
Václav Kubernát47a3f672019-11-08 15:42:43 +0100109{
110 return impl_getSchemaNode(node);
111}
112
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200113std::optional<libyang::SchemaNode> YangSchema::getSchemaNode(const schemaPath_& location, const ModuleNodePair& node) const
Václav Kubernát47a3f672019-11-08 15:42:43 +0100114{
Václav Kubernátefcac932020-01-10 15:26:32 +0100115 std::string absPath = joinPaths(pathToSchemaString(location, Prefixes::Always), fullNodeName(location, node));
Václav Kubernát47a3f672019-11-08 15:42:43 +0100116
117 return impl_getSchemaNode(absPath);
118}
119
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200120std::optional<libyang::SchemaNode> YangSchema::getSchemaNode(const schemaPath_& listPath) const
Václav Kubernát2db124c2020-05-28 21:58:36 +0200121{
122 std::string absPath = pathToSchemaString(listPath, Prefixes::Always);
123 return impl_getSchemaNode(absPath);
124}
125
Václav Kubernát912b9492020-05-29 02:03:40 +0200126const std::set<std::string> YangSchema::listKeys(const schemaPath_& listPath) const
Václav Kubernát0d4db442018-07-18 17:18:43 +0200127{
Václav Kubernát912b9492020-05-29 02:03:40 +0200128 auto node = getSchemaNode(listPath);
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200129 if (node->nodeType() != libyang::NodeType::List) {
Václav Kubernát912b9492020-05-29 02:03:40 +0200130 return {};
131 }
132
Václav Kubernát0d4db442018-07-18 17:18:43 +0200133 std::set<std::string> keys;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200134 auto keysVec = node->asList().keys();
Václav Kubernát0d4db442018-07-18 17:18:43 +0200135
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200136 std::transform(keysVec.begin(), keysVec.end(), std::inserter(keys, keys.begin()), [](const auto& it) { return std::string{it.name()}; });
Václav Kubernát0d4db442018-07-18 17:18:43 +0200137 return keys;
138}
139
Petr Gotthard0aae7682023-05-16 17:53:45 +0200140std::set<enum_> enumValues(const libyang::types::Type& type)
Václav Kubernát0d4db442018-07-18 17:18:43 +0200141{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200142 auto enums = type.asEnum().items();
Václav Kubernát3a99f002020-03-31 02:27:41 +0200143 std::set<enum_> enumSet;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200144 std::transform(enums.begin(), enums.end(), std::inserter(enumSet, enumSet.end()), [](auto it) { return enum_{std::string{it.name}}; });
Václav Kubernát3a99f002020-03-31 02:27:41 +0200145 return enumSet;
Václav Kubernát6a8d1d92019-04-24 20:30:36 +0200146}
147
Petr Gotthard0aae7682023-05-16 17:53:45 +0200148std::set<identityRef_> validIdentities(const libyang::types::Type& type)
Václav Kubernát3a99f002020-03-31 02:27:41 +0200149{
150 std::set<identityRef_> identSet;
151
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200152 std::function<void(const std::vector<libyang::Identity>&)> impl = [&identSet, &impl] (const std::vector<libyang::Identity>& idents) {
153 if (idents.empty()) {
154 return;
Václav Kubernát3a99f002020-03-31 02:27:41 +0200155 }
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200156
157 for (const auto& ident : idents) {
158 identSet.emplace(std::string{ident.module().name()}, std::string{ident.name()});
159 impl(ident.derived());
160 }
161 };
162
163 for (const auto& base : type.asIdentityRef().bases()) {
164 impl(base.derived());
Václav Kubernát3a99f002020-03-31 02:27:41 +0200165 }
166
167 return identSet;
168}
169
Petr Gotthard0aae7682023-05-16 17:53:45 +0200170std::string leafrefPath(const libyang::types::Type& type)
Václav Kubernát3a99f002020-03-31 02:27:41 +0200171{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200172 return std::string{type.asLeafRef().path()};
Václav Kubernát3a99f002020-03-31 02:27:41 +0200173}
174
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200175template <typename NodeType>
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200176yang::TypeInfo YangSchema::impl_leafType(const NodeType& node) const
Václav Kubernát6a8d1d92019-04-24 20:30:36 +0200177{
178 using namespace std::string_literals;
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200179 auto leaf = std::make_shared<NodeType>(node);
Václav Kubernát13b23d72020-04-16 21:49:51 +0200180 auto leafUnits = leaf->units();
Petr Gotthard0aae7682023-05-16 17:53:45 +0200181 std::function<yang::TypeInfo(const libyang::types::Type&)> resolveType;
182 resolveType = [&resolveType, leaf, leafUnits](const libyang::types::Type& type) -> yang::TypeInfo {
Václav Kubernát13b23d72020-04-16 21:49:51 +0200183 yang::LeafDataType resType;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200184 switch (type.base()) {
185 case libyang::LeafBaseType::String:
Václav Kubernát13b23d72020-04-16 21:49:51 +0200186 resType.emplace<yang::String>();
187 break;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200188 case libyang::LeafBaseType::Dec64:
Václav Kubernát13b23d72020-04-16 21:49:51 +0200189 resType.emplace<yang::Decimal>();
190 break;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200191 case libyang::LeafBaseType::Bool:
Václav Kubernát13b23d72020-04-16 21:49:51 +0200192 resType.emplace<yang::Bool>();
193 break;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200194 case libyang::LeafBaseType::Int8:
Václav Kubernát13b23d72020-04-16 21:49:51 +0200195 resType.emplace<yang::Int8>();
196 break;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200197 case libyang::LeafBaseType::Int16:
Václav Kubernát13b23d72020-04-16 21:49:51 +0200198 resType.emplace<yang::Int16>();
199 break;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200200 case libyang::LeafBaseType::Int32:
Václav Kubernát13b23d72020-04-16 21:49:51 +0200201 resType.emplace<yang::Int32>();
202 break;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200203 case libyang::LeafBaseType::Int64:
Václav Kubernát13b23d72020-04-16 21:49:51 +0200204 resType.emplace<yang::Int64>();
205 break;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200206 case libyang::LeafBaseType::Uint8:
Václav Kubernát13b23d72020-04-16 21:49:51 +0200207 resType.emplace<yang::Uint8>();
208 break;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200209 case libyang::LeafBaseType::Uint16:
Václav Kubernát13b23d72020-04-16 21:49:51 +0200210 resType.emplace<yang::Uint16>();
211 break;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200212 case libyang::LeafBaseType::Uint32:
Václav Kubernát13b23d72020-04-16 21:49:51 +0200213 resType.emplace<yang::Uint32>();
214 break;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200215 case libyang::LeafBaseType::Uint64:
Václav Kubernát13b23d72020-04-16 21:49:51 +0200216 resType.emplace<yang::Uint64>();
217 break;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200218 case libyang::LeafBaseType::Binary:
Václav Kubernát13b23d72020-04-16 21:49:51 +0200219 resType.emplace<yang::Binary>();
220 break;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200221 case libyang::LeafBaseType::Empty:
Jan Kundrát379bb572020-05-07 03:23:13 +0200222 resType.emplace<yang::Empty>();
223 break;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200224 case libyang::LeafBaseType::Enum:
Václav Kubernát13b23d72020-04-16 21:49:51 +0200225 resType.emplace<yang::Enum>(enumValues(type));
226 break;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200227 case libyang::LeafBaseType::IdentityRef:
Václav Kubernát13b23d72020-04-16 21:49:51 +0200228 resType.emplace<yang::IdentityRef>(validIdentities(type));
229 break;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200230 case libyang::LeafBaseType::Leafref:
231 resType.emplace<yang::LeafRef>(::leafrefPath(type), std::make_unique<yang::TypeInfo>(resolveType(type.asLeafRef().resolvedType())));
Václav Kubernát13b23d72020-04-16 21:49:51 +0200232 break;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200233 case libyang::LeafBaseType::Bits: {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100234 auto resBits = yang::Bits{};
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200235 for (const auto& bit : type.asBits().items()) {
236 resBits.m_allowedValues.emplace(std::string{bit.name});
Václav Kubernátdab73ca2020-10-26 23:44:43 +0100237 }
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100238 resType.emplace<yang::Bits>(std::move(resBits));
239 break;
240 }
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200241 case libyang::LeafBaseType::Union: {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100242 auto resUnion = yang::Union{};
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200243 for (auto unionType : type.asUnion().types()) {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100244 resUnion.m_unionTypes.emplace_back(resolveType(unionType));
Václav Kubernát2984f442020-02-20 17:43:35 +0100245 }
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100246 resType.emplace<yang::Union>(std::move(resUnion));
247 break;
248 }
Jan Kundrátbb7aa852023-08-30 11:51:43 +0200249 case libyang::LeafBaseType::InstanceIdentifier: {
250 resType.emplace<yang::InstanceIdentifier>();
251 break;
252 }
Václav Kubernát2984f442020-02-20 17:43:35 +0100253 default:
254 using namespace std::string_literals;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200255 throw UnsupportedYangTypeException("the type of "s +
256 std::string{leaf->name()} +
257 " is not supported: " +
258 std::to_string(std::underlying_type_t<libyang::LeafBaseType>(leaf->valueType().base())));
Václav Kubernát2984f442020-02-20 17:43:35 +0100259 }
Tomáš Peckac6b7ba52023-04-07 10:01:29 +0200260 // note https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109434
261 std::optional<std::string> typeDesc;
Václav Kubernát13b23d72020-04-16 21:49:51 +0200262
Václav Kubernáted4e3782022-03-02 23:57:33 +0100263 try {
264 typeDesc = type.description();
265 } catch (libyang::ParsedInfoUnavailable&) {
266 // libyang context doesn't have the parsed info.
267 }
Václav Kubernát13b23d72020-04-16 21:49:51 +0200268
Tomáš Peckac6b7ba52023-04-07 10:01:29 +0200269 return yang::TypeInfo(resType, std::optional<std::string>{leafUnits}, typeDesc);
Václav Kubernát13b23d72020-04-16 21:49:51 +0200270 };
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200271 return resolveType(leaf->valueType());
Václav Kubernát6a8d1d92019-04-24 20:30:36 +0200272}
273
Václav Kubernát13b23d72020-04-16 21:49:51 +0200274yang::TypeInfo YangSchema::leafType(const schemaPath_& location, const ModuleNodePair& node) const
Václav Kubernát9bf36852020-02-18 17:47:56 +0100275{
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200276 auto lyNode = getSchemaNode(location, node);
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200277 switch (lyNode->nodeType()) {
278 case libyang::NodeType::Leaf:
279 return impl_leafType(lyNode->asLeaf());
280 case libyang::NodeType::Leaflist:
281 return impl_leafType(lyNode->asLeafList());
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200282 default:
283 throw std::logic_error("YangSchema::leafType: type must be leaf or leaflist");
284 }
Václav Kubernát9bf36852020-02-18 17:47:56 +0100285}
286
Václav Kubernát13b23d72020-04-16 21:49:51 +0200287yang::TypeInfo YangSchema::leafType(const std::string& path) const
Václav Kubernát9bf36852020-02-18 17:47:56 +0100288{
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200289 auto lyNode = getSchemaNode(path);
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200290 switch (lyNode->nodeType()) {
291 case libyang::NodeType::Leaf:
292 return impl_leafType(lyNode->asLeaf());
293 case libyang::NodeType::Leaflist:
294 return impl_leafType(lyNode->asLeafList());
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200295 default:
296 throw std::logic_error("YangSchema::leafType: type must be leaf or leaflist");
297 }
Václav Kubernát9bf36852020-02-18 17:47:56 +0100298}
Václav Kubernát6a8d1d92019-04-24 20:30:36 +0200299
Václav Kubernát6fcd0282020-02-21 16:33:08 +0100300std::optional<std::string> YangSchema::leafTypeName(const std::string& path) const
301{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200302 auto leaf = getSchemaNode(path)->asLeaf();
Václav Kubernáted4e3782022-03-02 23:57:33 +0100303 try {
304 return std::string{leaf.valueType().name()};
305 } catch (libyang::ParsedInfoUnavailable&) {
306 return std::nullopt;
307 }
Václav Kubernát6fcd0282020-02-21 16:33:08 +0100308}
309
Václav Kubernátbd5e3c22020-02-19 15:22:00 +0100310std::string YangSchema::leafrefPath(const std::string& leafrefPath) const
311{
312 using namespace std::string_literals;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200313 return ::leafrefPath(getSchemaNode(leafrefPath)->asLeaf().valueType());
Václav Kubernátbd5e3c22020-02-19 15:22:00 +0100314}
Václav Kubernát0d4db442018-07-18 17:18:43 +0200315
316std::set<std::string> YangSchema::modules() const
317{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200318 const auto& modules = m_context.modules();
Václav Kubernát0d4db442018-07-18 17:18:43 +0200319
320 std::set<std::string> res;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200321 std::transform(modules.begin(), modules.end(), std::inserter(res, res.end()), [](const auto module) { return std::string{module.name()}; });
Václav Kubernát0d4db442018-07-18 17:18:43 +0200322 return res;
323}
324
Václav Kubernát95b08872020-04-28 01:04:17 +0200325std::set<ModuleNodePair> YangSchema::availableNodes(const boost::variant<dataPath_, schemaPath_, module_>& path, const Recursion recursion) const
Václav Kubernát0d4db442018-07-18 17:18:43 +0200326{
327 using namespace std::string_view_literals;
Václav Kubernát95b08872020-04-28 01:04:17 +0200328 std::set<ModuleNodePair> res;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200329 std::vector<libyang::ChildInstanstiables> nodeCollections;
Václav Kubernát3a823f42020-04-29 23:40:21 +0200330 std::string topLevelModule;
Václav Kubernáte7d4aea2018-09-11 18:15:48 +0200331
Václav Kubernát3a823f42020-04-29 23:40:21 +0200332 if (path.type() == typeid(module_)) {
Jan Kundrátf59b83c2022-03-18 18:12:08 +0100333 nodeCollections.emplace_back(m_context.getModule(boost::get<module_>(path).m_name)->childInstantiables());
Václav Kubernát0d4db442018-07-18 17:18:43 +0200334 } else {
Václav Kubernát3a823f42020-04-29 23:40:21 +0200335 auto schemaPath = anyPathToSchemaPath(path);
336 if (schemaPath.m_nodes.empty()) {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200337 for (const auto& module : m_context.modules()) {
338 if (module.implemented()) {
339 nodeCollections.emplace_back(module.childInstantiables());
340 }
341 }
Václav Kubernát3a823f42020-04-29 23:40:21 +0200342 } else {
343 const auto pathString = pathToSchemaString(schemaPath, Prefixes::Always);
344 const auto node = getSchemaNode(pathString);
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200345 nodeCollections.emplace_back(node->childInstantiables());
Václav Kubernát3a823f42020-04-29 23:40:21 +0200346 topLevelModule = schemaPath.m_nodes.begin()->m_prefix->m_name;
347 }
Václav Kubernát0d4db442018-07-18 17:18:43 +0200348 }
Václav Kubernáte7d4aea2018-09-11 18:15:48 +0200349
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200350 for (const auto& coll : nodeCollections) {
351 for (const auto& node : coll) {
352 if (node.module().name() == "ietf-yang-library"sv) {
353 continue;
354 }
Václav Kubernátaaafeae2020-05-05 15:41:45 +0200355
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200356 if (node.module().name() == "ietf-yang-schema-mount"sv) {
357 continue;
Václav Kubernáte7d4aea2018-09-11 18:15:48 +0200358 }
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200359
360 if (recursion == Recursion::Recursive) {
361 for (auto it : node.childrenDfs()) {
362 res.insert(ModuleNodePair(boost::none, it.path()));
363 }
364 } else {
365 ModuleNodePair toInsert;
366 if (topLevelModule.empty() || topLevelModule != node.module().name()) {
367 toInsert.first = std::string{node.module().name()};
368 }
369 toInsert.second = node.name();
370 res.insert(toInsert);
Václav Kubernát4f77a252019-02-19 16:51:30 +0100371 }
Václav Kubernáte7d4aea2018-09-11 18:15:48 +0200372 }
373 }
374
Václav Kubernát0d4db442018-07-18 17:18:43 +0200375 return res;
376}
Václav Kubernáta6c5fff2018-09-07 15:16:25 +0200377
378void YangSchema::loadModule(const std::string& moduleName)
379{
Jan Kundrátf59b83c2022-03-18 18:12:08 +0100380 m_context.loadModule(moduleName);
Václav Kubernáta6c5fff2018-09-07 15:16:25 +0200381}
382
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200383void YangSchema::setEnabledFeatures(const std::string& moduleName, const std::vector<std::string>& features)
Václav Kubernátbf9c6112019-11-04 16:03:35 +0100384{
Václav Kubernát7aaf6dc2020-06-26 18:03:57 +0200385 using namespace std::string_literals;
Václav Kubernátf44bdda2020-06-22 15:58:41 +0200386 auto module = getYangModule(moduleName);
387 if (!module) {
Václav Kubernátf44bdda2020-06-22 15:58:41 +0200388 throw std::runtime_error("Module \""s + moduleName + "\" doesn't exist.");
389 }
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200390 try {
391 module->setImplemented(features);
392 } catch (libyang::ErrorWithCode&) {
393 throw std::runtime_error("Can't enable features for module \"" + moduleName + "\".");
Václav Kubernát7aaf6dc2020-06-26 18:03:57 +0200394 }
Václav Kubernátbf9c6112019-11-04 16:03:35 +0100395}
396
Jan Kundrátf59b83c2022-03-18 18:12:08 +0100397void YangSchema::registerModuleCallback(const std::function<std::string(const std::string_view, const std::optional<std::string_view>, const std::optional<std::string_view>, const std::optional<std::string_view>)>& clb)
Václav Kubernáta6c5fff2018-09-07 15:16:25 +0200398{
Jan Kundrátf59b83c2022-03-18 18:12:08 +0100399 auto lambda = [clb](const auto mod_name, const auto mod_revision, const auto submod_name, const auto submod_revision) -> std::optional<libyang::ModuleInfo> {
Václav Kubernáta6c5fff2018-09-07 15:16:25 +0200400 (void)submod_revision;
Václav Kubernátb52dc252019-12-04 13:03:39 +0100401 auto moduleSource = clb(mod_name, mod_revision, submod_name, submod_revision);
Václav Kubernáta6c5fff2018-09-07 15:16:25 +0200402 if (moduleSource.empty()) {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200403 return std::nullopt;
Václav Kubernáta6c5fff2018-09-07 15:16:25 +0200404 }
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200405 return libyang::ModuleInfo {
Jan Kundrátf59b83c2022-03-18 18:12:08 +0100406 .data = moduleSource,
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200407 .format = libyang::SchemaFormat::YANG
408
409 };
Václav Kubernáta6c5fff2018-09-07 15:16:25 +0200410 };
411
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200412 m_context.registerModuleCallback(lambda);
Václav Kubernáta6c5fff2018-09-07 15:16:25 +0200413}
Václav Kubernátc31bd602019-03-07 11:44:48 +0100414
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200415libyang::CreatedNodes YangSchema::dataNodeFromPath(const std::string& path, const std::optional<const std::string> value) const
Václav Kubernátc31bd602019-03-07 11:44:48 +0100416{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200417 auto options = [this, &path, &value] {
418 // If we're creating a node without a value and it's not the "empty" type, then we also need the Opaque flag.
419 auto schema = getSchemaNode(path);
420 if (schema->nodeType() == libyang::NodeType::Leaf &&
421 schema->asLeaf().valueType().base() != libyang::LeafBaseType::Empty &&
422 !value) {
423 return std::optional<libyang::CreationOptions>{libyang::CreationOptions::Opaque};
424 }
425
426 return std::optional<libyang::CreationOptions>{};
427 }();
Jan Kundrátf59b83c2022-03-18 18:12:08 +0100428 return m_context.newPath2(path, value, options);
Václav Kubernátc31bd602019-03-07 11:44:48 +0100429}
430
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200431std::optional<libyang::Module> YangSchema::getYangModule(const std::string& name)
Václav Kubernátc31bd602019-03-07 11:44:48 +0100432{
Jan Kundrátf59b83c2022-03-18 18:12:08 +0100433 return m_context.getModuleImplemented(name);
Václav Kubernátc31bd602019-03-07 11:44:48 +0100434}
Václav Kubernát34ee85a2020-02-18 17:12:12 +0100435
436namespace {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200437yang::NodeTypes impl_nodeType(const libyang::SchemaNode& node)
Václav Kubernát34ee85a2020-02-18 17:12:12 +0100438{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200439 switch (node.nodeType()) {
440 case libyang::NodeType::Container:
441 return node.asContainer().isPresence() ? yang::NodeTypes::PresenceContainer : yang::NodeTypes::Container;
442 case libyang::NodeType::Leaf:
Václav Kubernát34ee85a2020-02-18 17:12:12 +0100443 return yang::NodeTypes::Leaf;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200444 case libyang::NodeType::List:
Václav Kubernát34ee85a2020-02-18 17:12:12 +0100445 return yang::NodeTypes::List;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200446 case libyang::NodeType::RPC:
Václav Kubernátaaafeae2020-05-05 15:41:45 +0200447 return yang::NodeTypes::Rpc;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200448 case libyang::NodeType::Action:
Václav Kubernátaaafeae2020-05-05 15:41:45 +0200449 return yang::NodeTypes::Action;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200450 case libyang::NodeType::Notification:
Václav Kubernátaaafeae2020-05-05 15:41:45 +0200451 return yang::NodeTypes::Notification;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200452 case libyang::NodeType::AnyXML:
Václav Kubernátaaafeae2020-05-05 15:41:45 +0200453 return yang::NodeTypes::AnyXml;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200454 case libyang::NodeType::Leaflist:
Václav Kubernátaaafeae2020-05-05 15:41:45 +0200455 return yang::NodeTypes::LeafList;
Václav Kubernát34ee85a2020-02-18 17:12:12 +0100456 default:
Václav Kubernát2a141392020-02-18 17:12:32 +0100457 throw InvalidNodeException(); // FIXME: Implement all types.
Václav Kubernát34ee85a2020-02-18 17:12:12 +0100458 }
459}
460}
461
462yang::NodeTypes YangSchema::nodeType(const schemaPath_& location, const ModuleNodePair& node) const
463{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200464 return impl_nodeType(*getSchemaNode(location, node));
Václav Kubernát34ee85a2020-02-18 17:12:12 +0100465}
466
467yang::NodeTypes YangSchema::nodeType(const std::string& path) const
468{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200469 return impl_nodeType(*getSchemaNode(path));
Václav Kubernát34ee85a2020-02-18 17:12:12 +0100470}
Václav Kubernát1e09bd62020-02-17 15:13:38 +0100471
472std::optional<std::string> YangSchema::description(const std::string& path) const
473{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200474 auto desc = getSchemaNode(path.c_str())->description();
475 return desc ? std::optional<std::string>{desc} : std::nullopt;
476
Václav Kubernát1e09bd62020-02-17 15:13:38 +0100477}
Václav Kubernát0599e9f2020-04-21 09:51:33 +0200478
Václav Kubernáta1c4c9e2020-04-22 00:37:52 +0200479yang::Status YangSchema::status(const std::string& location) const
480{
481 auto node = getSchemaNode(location.c_str());
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200482 switch (node->status()) {
483 case libyang::Status::Deprecated:
Václav Kubernáta1c4c9e2020-04-22 00:37:52 +0200484 return yang::Status::Deprecated;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200485 case libyang::Status::Obsolete:
Václav Kubernáta1c4c9e2020-04-22 00:37:52 +0200486 return yang::Status::Obsolete;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200487 case libyang::Status::Current:
Václav Kubernáta1c4c9e2020-04-22 00:37:52 +0200488 return yang::Status::Current;
489 }
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200490
491 __builtin_unreachable();
Václav Kubernáta1c4c9e2020-04-22 00:37:52 +0200492}
493
Václav Kubernátd8408e02020-12-02 05:13:27 +0100494bool YangSchema::hasInputNodes(const std::string& path) const
495{
496 auto node = getSchemaNode(path.c_str());
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200497 if (auto type = node->nodeType(); type != libyang::NodeType::Action && type != libyang::NodeType::RPC) {
Václav Kubernátd8408e02020-12-02 05:13:27 +0100498 throw std::logic_error("StaticSchema::hasInputNodes called with non-RPC/action path");
499 }
500
501 // The first child gives the /input node and then I check whether it has a child.
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200502 return node->child()->child().has_value();
Václav Kubernátd8408e02020-12-02 05:13:27 +0100503}
504
Václav Kubernát0599e9f2020-04-21 09:51:33 +0200505bool YangSchema::isConfig(const std::string& path) const
506{
Václav Kubernáte7248b22020-06-26 15:38:59 +0200507 auto node = getSchemaNode(path.c_str());
Václav Kubernátc8e5ed02022-03-23 12:46:54 +0100508 if (node->isInput()) {
509 return true;
510 }
511
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200512 try {
513 if (node->config() == libyang::Config::True) {
Václav Kubernáte7248b22020-06-26 15:38:59 +0200514 return true;
515 }
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200516 } catch (libyang::Error&) {
517 // For non-data nodes (like `rpc`), the config value can't be retrieved. In this case, we'll just default to
518 // "false".
Václav Kubernáte7248b22020-06-26 15:38:59 +0200519 }
520
521 return false;
Václav Kubernát0599e9f2020-04-21 09:51:33 +0200522}
Václav Kubernátb1a75c62020-04-21 15:20:16 +0200523
524std::optional<std::string> YangSchema::defaultValue(const std::string& leafPath) const
525{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200526 return std::optional<std::string>{getSchemaNode(leafPath)->asLeaf().defaultValueStr()};
Václav Kubernátb1a75c62020-04-21 15:20:16 +0200527}
Václav Kubernáta8789602020-07-20 15:18:19 +0200528
529std::string YangSchema::dataPathToSchemaPath(const std::string& path)
530{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200531 return std::string{getSchemaNode(path)->path()};
Václav Kubernáta8789602020-07-20 15:18:19 +0200532}