blob: 1dc05dbaa864117a4c2bdc07285fa1bd25bfd079 [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{
Václav Kubernátcfdb9222021-07-07 22:36:24 +020048 m_context.parseModuleMem(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{
Václav Kubernátcfdb9222021-07-07 22:36:24 +020058 m_context.parseModulePath(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
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200140std::set<enum_> enumValues(const libyang::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
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200148std::set<identityRef_> validIdentities(const libyang::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
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200170std::string leafrefPath(const libyang::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();
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200181 std::function<yang::TypeInfo(const libyang::Type&)> resolveType;
182 resolveType = [&resolveType, leaf, leafUnits](const libyang::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 }
Václav Kubernát2984f442020-02-20 17:43:35 +0100249 default:
250 using namespace std::string_literals;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200251 throw UnsupportedYangTypeException("the type of "s +
252 std::string{leaf->name()} +
253 " is not supported: " +
254 std::to_string(std::underlying_type_t<libyang::LeafBaseType>(leaf->valueType().base())));
Václav Kubernát2984f442020-02-20 17:43:35 +0100255 }
Václav Kubernáted4e3782022-03-02 23:57:33 +0100256 std::optional<std::string_view> typeDesc;
Václav Kubernát13b23d72020-04-16 21:49:51 +0200257
Václav Kubernáted4e3782022-03-02 23:57:33 +0100258 try {
259 typeDesc = type.description();
260 } catch (libyang::ParsedInfoUnavailable&) {
261 // libyang context doesn't have the parsed info.
262 }
Václav Kubernát13b23d72020-04-16 21:49:51 +0200263
Václav Kubernáted4e3782022-03-02 23:57:33 +0100264 return yang::TypeInfo(resType, std::optional<std::string>{leafUnits}, std::optional<std::string>{typeDesc});
Václav Kubernát13b23d72020-04-16 21:49:51 +0200265 };
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200266 return resolveType(leaf->valueType());
Václav Kubernát6a8d1d92019-04-24 20:30:36 +0200267}
268
Václav Kubernát13b23d72020-04-16 21:49:51 +0200269yang::TypeInfo YangSchema::leafType(const schemaPath_& location, const ModuleNodePair& node) const
Václav Kubernát9bf36852020-02-18 17:47:56 +0100270{
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200271 auto lyNode = getSchemaNode(location, node);
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200272 switch (lyNode->nodeType()) {
273 case libyang::NodeType::Leaf:
274 return impl_leafType(lyNode->asLeaf());
275 case libyang::NodeType::Leaflist:
276 return impl_leafType(lyNode->asLeafList());
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200277 default:
278 throw std::logic_error("YangSchema::leafType: type must be leaf or leaflist");
279 }
Václav Kubernát9bf36852020-02-18 17:47:56 +0100280}
281
Václav Kubernát13b23d72020-04-16 21:49:51 +0200282yang::TypeInfo YangSchema::leafType(const std::string& path) const
Václav Kubernát9bf36852020-02-18 17:47:56 +0100283{
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200284 auto lyNode = getSchemaNode(path);
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200285 switch (lyNode->nodeType()) {
286 case libyang::NodeType::Leaf:
287 return impl_leafType(lyNode->asLeaf());
288 case libyang::NodeType::Leaflist:
289 return impl_leafType(lyNode->asLeafList());
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200290 default:
291 throw std::logic_error("YangSchema::leafType: type must be leaf or leaflist");
292 }
Václav Kubernát9bf36852020-02-18 17:47:56 +0100293}
Václav Kubernát6a8d1d92019-04-24 20:30:36 +0200294
Václav Kubernát6fcd0282020-02-21 16:33:08 +0100295std::optional<std::string> YangSchema::leafTypeName(const std::string& path) const
296{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200297 auto leaf = getSchemaNode(path)->asLeaf();
Václav Kubernáted4e3782022-03-02 23:57:33 +0100298 try {
299 return std::string{leaf.valueType().name()};
300 } catch (libyang::ParsedInfoUnavailable&) {
301 return std::nullopt;
302 }
Václav Kubernát6fcd0282020-02-21 16:33:08 +0100303}
304
Václav Kubernátbd5e3c22020-02-19 15:22:00 +0100305std::string YangSchema::leafrefPath(const std::string& leafrefPath) const
306{
307 using namespace std::string_literals;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200308 return ::leafrefPath(getSchemaNode(leafrefPath)->asLeaf().valueType());
Václav Kubernátbd5e3c22020-02-19 15:22:00 +0100309}
Václav Kubernát0d4db442018-07-18 17:18:43 +0200310
311std::set<std::string> YangSchema::modules() const
312{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200313 const auto& modules = m_context.modules();
Václav Kubernát0d4db442018-07-18 17:18:43 +0200314
315 std::set<std::string> res;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200316 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 +0200317 return res;
318}
319
Václav Kubernát95b08872020-04-28 01:04:17 +0200320std::set<ModuleNodePair> YangSchema::availableNodes(const boost::variant<dataPath_, schemaPath_, module_>& path, const Recursion recursion) const
Václav Kubernát0d4db442018-07-18 17:18:43 +0200321{
322 using namespace std::string_view_literals;
Václav Kubernát95b08872020-04-28 01:04:17 +0200323 std::set<ModuleNodePair> res;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200324 std::vector<libyang::ChildInstanstiables> nodeCollections;
Václav Kubernát3a823f42020-04-29 23:40:21 +0200325 std::string topLevelModule;
Václav Kubernáte7d4aea2018-09-11 18:15:48 +0200326
Václav Kubernát3a823f42020-04-29 23:40:21 +0200327 if (path.type() == typeid(module_)) {
Jan Kundrátf59b83c2022-03-18 18:12:08 +0100328 nodeCollections.emplace_back(m_context.getModule(boost::get<module_>(path).m_name)->childInstantiables());
Václav Kubernát0d4db442018-07-18 17:18:43 +0200329 } else {
Václav Kubernát3a823f42020-04-29 23:40:21 +0200330 auto schemaPath = anyPathToSchemaPath(path);
331 if (schemaPath.m_nodes.empty()) {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200332 for (const auto& module : m_context.modules()) {
333 if (module.implemented()) {
334 nodeCollections.emplace_back(module.childInstantiables());
335 }
336 }
Václav Kubernát3a823f42020-04-29 23:40:21 +0200337 } else {
338 const auto pathString = pathToSchemaString(schemaPath, Prefixes::Always);
339 const auto node = getSchemaNode(pathString);
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200340 nodeCollections.emplace_back(node->childInstantiables());
Václav Kubernát3a823f42020-04-29 23:40:21 +0200341 topLevelModule = schemaPath.m_nodes.begin()->m_prefix->m_name;
342 }
Václav Kubernát0d4db442018-07-18 17:18:43 +0200343 }
Václav Kubernáte7d4aea2018-09-11 18:15:48 +0200344
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200345 for (const auto& coll : nodeCollections) {
346 for (const auto& node : coll) {
347 if (node.module().name() == "ietf-yang-library"sv) {
348 continue;
349 }
Václav Kubernátaaafeae2020-05-05 15:41:45 +0200350
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200351 if (node.module().name() == "ietf-yang-schema-mount"sv) {
352 continue;
Václav Kubernáte7d4aea2018-09-11 18:15:48 +0200353 }
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200354
355 if (recursion == Recursion::Recursive) {
356 for (auto it : node.childrenDfs()) {
357 res.insert(ModuleNodePair(boost::none, it.path()));
358 }
359 } else {
360 ModuleNodePair toInsert;
361 if (topLevelModule.empty() || topLevelModule != node.module().name()) {
362 toInsert.first = std::string{node.module().name()};
363 }
364 toInsert.second = node.name();
365 res.insert(toInsert);
Václav Kubernát4f77a252019-02-19 16:51:30 +0100366 }
Václav Kubernáte7d4aea2018-09-11 18:15:48 +0200367 }
368 }
369
Václav Kubernát0d4db442018-07-18 17:18:43 +0200370 return res;
371}
Václav Kubernáta6c5fff2018-09-07 15:16:25 +0200372
373void YangSchema::loadModule(const std::string& moduleName)
374{
Jan Kundrátf59b83c2022-03-18 18:12:08 +0100375 m_context.loadModule(moduleName);
Václav Kubernáta6c5fff2018-09-07 15:16:25 +0200376}
377
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200378void YangSchema::setEnabledFeatures(const std::string& moduleName, const std::vector<std::string>& features)
Václav Kubernátbf9c6112019-11-04 16:03:35 +0100379{
Václav Kubernát7aaf6dc2020-06-26 18:03:57 +0200380 using namespace std::string_literals;
Václav Kubernátf44bdda2020-06-22 15:58:41 +0200381 auto module = getYangModule(moduleName);
382 if (!module) {
Václav Kubernátf44bdda2020-06-22 15:58:41 +0200383 throw std::runtime_error("Module \""s + moduleName + "\" doesn't exist.");
384 }
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200385 try {
386 module->setImplemented(features);
387 } catch (libyang::ErrorWithCode&) {
388 throw std::runtime_error("Can't enable features for module \"" + moduleName + "\".");
Václav Kubernát7aaf6dc2020-06-26 18:03:57 +0200389 }
Václav Kubernátbf9c6112019-11-04 16:03:35 +0100390}
391
Jan Kundrátf59b83c2022-03-18 18:12:08 +0100392void 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 +0200393{
Jan Kundrátf59b83c2022-03-18 18:12:08 +0100394 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 +0200395 (void)submod_revision;
Václav Kubernátb52dc252019-12-04 13:03:39 +0100396 auto moduleSource = clb(mod_name, mod_revision, submod_name, submod_revision);
Václav Kubernáta6c5fff2018-09-07 15:16:25 +0200397 if (moduleSource.empty()) {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200398 return std::nullopt;
Václav Kubernáta6c5fff2018-09-07 15:16:25 +0200399 }
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200400 return libyang::ModuleInfo {
Jan Kundrátf59b83c2022-03-18 18:12:08 +0100401 .data = moduleSource,
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200402 .format = libyang::SchemaFormat::YANG
403
404 };
Václav Kubernáta6c5fff2018-09-07 15:16:25 +0200405 };
406
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200407 m_context.registerModuleCallback(lambda);
Václav Kubernáta6c5fff2018-09-07 15:16:25 +0200408}
Václav Kubernátc31bd602019-03-07 11:44:48 +0100409
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200410libyang::CreatedNodes YangSchema::dataNodeFromPath(const std::string& path, const std::optional<const std::string> value) const
Václav Kubernátc31bd602019-03-07 11:44:48 +0100411{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200412 auto options = [this, &path, &value] {
413 // If we're creating a node without a value and it's not the "empty" type, then we also need the Opaque flag.
414 auto schema = getSchemaNode(path);
415 if (schema->nodeType() == libyang::NodeType::Leaf &&
416 schema->asLeaf().valueType().base() != libyang::LeafBaseType::Empty &&
417 !value) {
418 return std::optional<libyang::CreationOptions>{libyang::CreationOptions::Opaque};
419 }
420
421 return std::optional<libyang::CreationOptions>{};
422 }();
Jan Kundrátf59b83c2022-03-18 18:12:08 +0100423 return m_context.newPath2(path, value, options);
Václav Kubernátc31bd602019-03-07 11:44:48 +0100424}
425
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200426std::optional<libyang::Module> YangSchema::getYangModule(const std::string& name)
Václav Kubernátc31bd602019-03-07 11:44:48 +0100427{
Jan Kundrátf59b83c2022-03-18 18:12:08 +0100428 return m_context.getModuleImplemented(name);
Václav Kubernátc31bd602019-03-07 11:44:48 +0100429}
Václav Kubernát34ee85a2020-02-18 17:12:12 +0100430
431namespace {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200432yang::NodeTypes impl_nodeType(const libyang::SchemaNode& node)
Václav Kubernát34ee85a2020-02-18 17:12:12 +0100433{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200434 switch (node.nodeType()) {
435 case libyang::NodeType::Container:
436 return node.asContainer().isPresence() ? yang::NodeTypes::PresenceContainer : yang::NodeTypes::Container;
437 case libyang::NodeType::Leaf:
Václav Kubernát34ee85a2020-02-18 17:12:12 +0100438 return yang::NodeTypes::Leaf;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200439 case libyang::NodeType::List:
Václav Kubernát34ee85a2020-02-18 17:12:12 +0100440 return yang::NodeTypes::List;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200441 case libyang::NodeType::RPC:
Václav Kubernátaaafeae2020-05-05 15:41:45 +0200442 return yang::NodeTypes::Rpc;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200443 case libyang::NodeType::Action:
Václav Kubernátaaafeae2020-05-05 15:41:45 +0200444 return yang::NodeTypes::Action;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200445 case libyang::NodeType::Notification:
Václav Kubernátaaafeae2020-05-05 15:41:45 +0200446 return yang::NodeTypes::Notification;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200447 case libyang::NodeType::AnyXML:
Václav Kubernátaaafeae2020-05-05 15:41:45 +0200448 return yang::NodeTypes::AnyXml;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200449 case libyang::NodeType::Leaflist:
Václav Kubernátaaafeae2020-05-05 15:41:45 +0200450 return yang::NodeTypes::LeafList;
Václav Kubernát34ee85a2020-02-18 17:12:12 +0100451 default:
Václav Kubernát2a141392020-02-18 17:12:32 +0100452 throw InvalidNodeException(); // FIXME: Implement all types.
Václav Kubernát34ee85a2020-02-18 17:12:12 +0100453 }
454}
455}
456
457yang::NodeTypes YangSchema::nodeType(const schemaPath_& location, const ModuleNodePair& node) const
458{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200459 return impl_nodeType(*getSchemaNode(location, node));
Václav Kubernát34ee85a2020-02-18 17:12:12 +0100460}
461
462yang::NodeTypes YangSchema::nodeType(const std::string& path) const
463{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200464 return impl_nodeType(*getSchemaNode(path));
Václav Kubernát34ee85a2020-02-18 17:12:12 +0100465}
Václav Kubernát1e09bd62020-02-17 15:13:38 +0100466
467std::optional<std::string> YangSchema::description(const std::string& path) const
468{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200469 auto desc = getSchemaNode(path.c_str())->description();
470 return desc ? std::optional<std::string>{desc} : std::nullopt;
471
Václav Kubernát1e09bd62020-02-17 15:13:38 +0100472}
Václav Kubernát0599e9f2020-04-21 09:51:33 +0200473
Václav Kubernáta1c4c9e2020-04-22 00:37:52 +0200474yang::Status YangSchema::status(const std::string& location) const
475{
476 auto node = getSchemaNode(location.c_str());
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200477 switch (node->status()) {
478 case libyang::Status::Deprecated:
Václav Kubernáta1c4c9e2020-04-22 00:37:52 +0200479 return yang::Status::Deprecated;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200480 case libyang::Status::Obsolete:
Václav Kubernáta1c4c9e2020-04-22 00:37:52 +0200481 return yang::Status::Obsolete;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200482 case libyang::Status::Current:
Václav Kubernáta1c4c9e2020-04-22 00:37:52 +0200483 return yang::Status::Current;
484 }
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200485
486 __builtin_unreachable();
Václav Kubernáta1c4c9e2020-04-22 00:37:52 +0200487}
488
Václav Kubernátd8408e02020-12-02 05:13:27 +0100489bool YangSchema::hasInputNodes(const std::string& path) const
490{
491 auto node = getSchemaNode(path.c_str());
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200492 if (auto type = node->nodeType(); type != libyang::NodeType::Action && type != libyang::NodeType::RPC) {
Václav Kubernátd8408e02020-12-02 05:13:27 +0100493 throw std::logic_error("StaticSchema::hasInputNodes called with non-RPC/action path");
494 }
495
496 // 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 +0200497 return node->child()->child().has_value();
Václav Kubernátd8408e02020-12-02 05:13:27 +0100498}
499
Václav Kubernát0599e9f2020-04-21 09:51:33 +0200500bool YangSchema::isConfig(const std::string& path) const
501{
Václav Kubernáte7248b22020-06-26 15:38:59 +0200502 auto node = getSchemaNode(path.c_str());
Václav Kubernátc8e5ed02022-03-23 12:46:54 +0100503 if (node->isInput()) {
504 return true;
505 }
506
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200507 try {
508 if (node->config() == libyang::Config::True) {
Václav Kubernáte7248b22020-06-26 15:38:59 +0200509 return true;
510 }
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200511 } catch (libyang::Error&) {
512 // For non-data nodes (like `rpc`), the config value can't be retrieved. In this case, we'll just default to
513 // "false".
Václav Kubernáte7248b22020-06-26 15:38:59 +0200514 }
515
516 return false;
Václav Kubernát0599e9f2020-04-21 09:51:33 +0200517}
Václav Kubernátb1a75c62020-04-21 15:20:16 +0200518
519std::optional<std::string> YangSchema::defaultValue(const std::string& leafPath) const
520{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200521 return std::optional<std::string>{getSchemaNode(leafPath)->asLeaf().defaultValueStr()};
Václav Kubernátb1a75c62020-04-21 15:20:16 +0200522}
Václav Kubernáta8789602020-07-20 15:18:19 +0200523
524std::string YangSchema::dataPathToSchemaPath(const std::string& path)
525{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200526 return std::string{getSchemaNode(path)->path()};
Václav Kubernáta8789602020-07-20 15:18:19 +0200527}