blob: f96aa4600327675c69621c8d7e1dab560bff6738 [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()
Václav Kubernátcfdb9222021-07-07 22:36:24 +020035 : m_context(nullptr, 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{
Václav Kubernátcfdb9222021-07-07 22:36:24 +020063 return m_context.getModuleImplemented(name.c_str()).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 {
89 return m_context.findPath(node.c_str());
90 } 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 {
96 return m_context.findPath(node.c_str(), libyang::OutputNodes::Yes);
97 } 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át13b23d72020-04-16 21:49:51 +0200256
Václav Kubernát13b23d72020-04-16 21:49:51 +0200257
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200258 return yang::TypeInfo(resType, std::optional<std::string>{leafUnits}, std::optional<std::string>{type.description()});
Václav Kubernát13b23d72020-04-16 21:49:51 +0200259 };
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200260 return resolveType(leaf->valueType());
Václav Kubernát6a8d1d92019-04-24 20:30:36 +0200261}
262
Václav Kubernát13b23d72020-04-16 21:49:51 +0200263yang::TypeInfo YangSchema::leafType(const schemaPath_& location, const ModuleNodePair& node) const
Václav Kubernát9bf36852020-02-18 17:47:56 +0100264{
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200265 auto lyNode = getSchemaNode(location, node);
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200266 switch (lyNode->nodeType()) {
267 case libyang::NodeType::Leaf:
268 return impl_leafType(lyNode->asLeaf());
269 case libyang::NodeType::Leaflist:
270 return impl_leafType(lyNode->asLeafList());
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200271 default:
272 throw std::logic_error("YangSchema::leafType: type must be leaf or leaflist");
273 }
Václav Kubernát9bf36852020-02-18 17:47:56 +0100274}
275
Václav Kubernát13b23d72020-04-16 21:49:51 +0200276yang::TypeInfo YangSchema::leafType(const std::string& path) const
Václav Kubernát9bf36852020-02-18 17:47:56 +0100277{
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200278 auto lyNode = getSchemaNode(path);
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200279 switch (lyNode->nodeType()) {
280 case libyang::NodeType::Leaf:
281 return impl_leafType(lyNode->asLeaf());
282 case libyang::NodeType::Leaflist:
283 return impl_leafType(lyNode->asLeafList());
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200284 default:
285 throw std::logic_error("YangSchema::leafType: type must be leaf or leaflist");
286 }
Václav Kubernát9bf36852020-02-18 17:47:56 +0100287}
Václav Kubernát6a8d1d92019-04-24 20:30:36 +0200288
Václav Kubernát6fcd0282020-02-21 16:33:08 +0100289std::optional<std::string> YangSchema::leafTypeName(const std::string& path) const
290{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200291 auto leaf = getSchemaNode(path)->asLeaf();
292 return std::string{leaf.valueType().name()};
Václav Kubernát6fcd0282020-02-21 16:33:08 +0100293}
294
Václav Kubernátbd5e3c22020-02-19 15:22:00 +0100295std::string YangSchema::leafrefPath(const std::string& leafrefPath) const
296{
297 using namespace std::string_literals;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200298 return ::leafrefPath(getSchemaNode(leafrefPath)->asLeaf().valueType());
Václav Kubernátbd5e3c22020-02-19 15:22:00 +0100299}
Václav Kubernát0d4db442018-07-18 17:18:43 +0200300
301std::set<std::string> YangSchema::modules() const
302{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200303 const auto& modules = m_context.modules();
Václav Kubernát0d4db442018-07-18 17:18:43 +0200304
305 std::set<std::string> res;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200306 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 +0200307 return res;
308}
309
Václav Kubernát95b08872020-04-28 01:04:17 +0200310std::set<ModuleNodePair> YangSchema::availableNodes(const boost::variant<dataPath_, schemaPath_, module_>& path, const Recursion recursion) const
Václav Kubernát0d4db442018-07-18 17:18:43 +0200311{
312 using namespace std::string_view_literals;
Václav Kubernát95b08872020-04-28 01:04:17 +0200313 std::set<ModuleNodePair> res;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200314 std::vector<libyang::ChildInstanstiables> nodeCollections;
Václav Kubernát3a823f42020-04-29 23:40:21 +0200315 std::string topLevelModule;
Václav Kubernáte7d4aea2018-09-11 18:15:48 +0200316
Václav Kubernát3a823f42020-04-29 23:40:21 +0200317 if (path.type() == typeid(module_)) {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200318 nodeCollections.emplace_back(m_context.getModule(boost::get<module_>(path).m_name.c_str())->childInstantiables());
Václav Kubernát0d4db442018-07-18 17:18:43 +0200319 } else {
Václav Kubernát3a823f42020-04-29 23:40:21 +0200320 auto schemaPath = anyPathToSchemaPath(path);
321 if (schemaPath.m_nodes.empty()) {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200322 for (const auto& module : m_context.modules()) {
323 if (module.implemented()) {
324 nodeCollections.emplace_back(module.childInstantiables());
325 }
326 }
Václav Kubernát3a823f42020-04-29 23:40:21 +0200327 } else {
328 const auto pathString = pathToSchemaString(schemaPath, Prefixes::Always);
329 const auto node = getSchemaNode(pathString);
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200330 nodeCollections.emplace_back(node->childInstantiables());
Václav Kubernát3a823f42020-04-29 23:40:21 +0200331 topLevelModule = schemaPath.m_nodes.begin()->m_prefix->m_name;
332 }
Václav Kubernát0d4db442018-07-18 17:18:43 +0200333 }
Václav Kubernáte7d4aea2018-09-11 18:15:48 +0200334
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200335 for (const auto& coll : nodeCollections) {
336 for (const auto& node : coll) {
337 if (node.module().name() == "ietf-yang-library"sv) {
338 continue;
339 }
Václav Kubernátaaafeae2020-05-05 15:41:45 +0200340
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200341 if (node.module().name() == "ietf-yang-schema-mount"sv) {
342 continue;
Václav Kubernáte7d4aea2018-09-11 18:15:48 +0200343 }
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200344
345 if (recursion == Recursion::Recursive) {
346 for (auto it : node.childrenDfs()) {
347 res.insert(ModuleNodePair(boost::none, it.path()));
348 }
349 } else {
350 ModuleNodePair toInsert;
351 if (topLevelModule.empty() || topLevelModule != node.module().name()) {
352 toInsert.first = std::string{node.module().name()};
353 }
354 toInsert.second = node.name();
355 res.insert(toInsert);
Václav Kubernát4f77a252019-02-19 16:51:30 +0100356 }
Václav Kubernáte7d4aea2018-09-11 18:15:48 +0200357 }
358 }
359
Václav Kubernát0d4db442018-07-18 17:18:43 +0200360 return res;
361}
Václav Kubernáta6c5fff2018-09-07 15:16:25 +0200362
363void YangSchema::loadModule(const std::string& moduleName)
364{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200365 m_context.loadModule(moduleName.c_str());
Václav Kubernáta6c5fff2018-09-07 15:16:25 +0200366}
367
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200368void YangSchema::setEnabledFeatures(const std::string& moduleName, const std::vector<std::string>& features)
Václav Kubernátbf9c6112019-11-04 16:03:35 +0100369{
Václav Kubernát7aaf6dc2020-06-26 18:03:57 +0200370 using namespace std::string_literals;
Václav Kubernátf44bdda2020-06-22 15:58:41 +0200371 auto module = getYangModule(moduleName);
372 if (!module) {
Václav Kubernátf44bdda2020-06-22 15:58:41 +0200373 throw std::runtime_error("Module \""s + moduleName + "\" doesn't exist.");
374 }
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200375 try {
376 module->setImplemented(features);
377 } catch (libyang::ErrorWithCode&) {
378 throw std::runtime_error("Can't enable features for module \"" + moduleName + "\".");
Václav Kubernát7aaf6dc2020-06-26 18:03:57 +0200379 }
Václav Kubernátbf9c6112019-11-04 16:03:35 +0100380}
381
Václav Kubernátb52dc252019-12-04 13:03:39 +0100382void 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 +0200383{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200384 auto lambda = [clb](const char* mod_name, const char* mod_revision, const char* submod_name, const char* submod_revision) -> std::optional<libyang::ModuleInfo> {
Václav Kubernáta6c5fff2018-09-07 15:16:25 +0200385 (void)submod_revision;
Václav Kubernátb52dc252019-12-04 13:03:39 +0100386 auto moduleSource = clb(mod_name, mod_revision, submod_name, submod_revision);
Václav Kubernáta6c5fff2018-09-07 15:16:25 +0200387 if (moduleSource.empty()) {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200388 return std::nullopt;
Václav Kubernáta6c5fff2018-09-07 15:16:25 +0200389 }
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200390 return libyang::ModuleInfo {
391 .data = moduleSource.c_str(),
392 .format = libyang::SchemaFormat::YANG
393
394 };
Václav Kubernáta6c5fff2018-09-07 15:16:25 +0200395 };
396
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200397 m_context.registerModuleCallback(lambda);
Václav Kubernáta6c5fff2018-09-07 15:16:25 +0200398}
Václav Kubernátc31bd602019-03-07 11:44:48 +0100399
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200400libyang::CreatedNodes YangSchema::dataNodeFromPath(const std::string& path, const std::optional<const std::string> value) const
Václav Kubernátc31bd602019-03-07 11:44:48 +0100401{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200402 auto options = [this, &path, &value] {
403 // If we're creating a node without a value and it's not the "empty" type, then we also need the Opaque flag.
404 auto schema = getSchemaNode(path);
405 if (schema->nodeType() == libyang::NodeType::Leaf &&
406 schema->asLeaf().valueType().base() != libyang::LeafBaseType::Empty &&
407 !value) {
408 return std::optional<libyang::CreationOptions>{libyang::CreationOptions::Opaque};
409 }
410
411 return std::optional<libyang::CreationOptions>{};
412 }();
413 return m_context.newPath2(path.c_str(), value ? value->c_str() : nullptr, options);
Václav Kubernátc31bd602019-03-07 11:44:48 +0100414}
415
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200416std::optional<libyang::Module> YangSchema::getYangModule(const std::string& name)
Václav Kubernátc31bd602019-03-07 11:44:48 +0100417{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200418 return m_context.getModuleImplemented(name.c_str());
Václav Kubernátc31bd602019-03-07 11:44:48 +0100419}
Václav Kubernát34ee85a2020-02-18 17:12:12 +0100420
421namespace {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200422yang::NodeTypes impl_nodeType(const libyang::SchemaNode& node)
Václav Kubernát34ee85a2020-02-18 17:12:12 +0100423{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200424 switch (node.nodeType()) {
425 case libyang::NodeType::Container:
426 return node.asContainer().isPresence() ? yang::NodeTypes::PresenceContainer : yang::NodeTypes::Container;
427 case libyang::NodeType::Leaf:
Václav Kubernát34ee85a2020-02-18 17:12:12 +0100428 return yang::NodeTypes::Leaf;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200429 case libyang::NodeType::List:
Václav Kubernát34ee85a2020-02-18 17:12:12 +0100430 return yang::NodeTypes::List;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200431 case libyang::NodeType::RPC:
Václav Kubernátaaafeae2020-05-05 15:41:45 +0200432 return yang::NodeTypes::Rpc;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200433 case libyang::NodeType::Action:
Václav Kubernátaaafeae2020-05-05 15:41:45 +0200434 return yang::NodeTypes::Action;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200435 case libyang::NodeType::Notification:
Václav Kubernátaaafeae2020-05-05 15:41:45 +0200436 return yang::NodeTypes::Notification;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200437 case libyang::NodeType::AnyXML:
Václav Kubernátaaafeae2020-05-05 15:41:45 +0200438 return yang::NodeTypes::AnyXml;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200439 case libyang::NodeType::Leaflist:
Václav Kubernátaaafeae2020-05-05 15:41:45 +0200440 return yang::NodeTypes::LeafList;
Václav Kubernát34ee85a2020-02-18 17:12:12 +0100441 default:
Václav Kubernát2a141392020-02-18 17:12:32 +0100442 throw InvalidNodeException(); // FIXME: Implement all types.
Václav Kubernát34ee85a2020-02-18 17:12:12 +0100443 }
444}
445}
446
447yang::NodeTypes YangSchema::nodeType(const schemaPath_& location, const ModuleNodePair& node) const
448{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200449 return impl_nodeType(*getSchemaNode(location, node));
Václav Kubernát34ee85a2020-02-18 17:12:12 +0100450}
451
452yang::NodeTypes YangSchema::nodeType(const std::string& path) const
453{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200454 return impl_nodeType(*getSchemaNode(path));
Václav Kubernát34ee85a2020-02-18 17:12:12 +0100455}
Václav Kubernát1e09bd62020-02-17 15:13:38 +0100456
457std::optional<std::string> YangSchema::description(const std::string& path) const
458{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200459 auto desc = getSchemaNode(path.c_str())->description();
460 return desc ? std::optional<std::string>{desc} : std::nullopt;
461
Václav Kubernát1e09bd62020-02-17 15:13:38 +0100462}
Václav Kubernát0599e9f2020-04-21 09:51:33 +0200463
Václav Kubernáta1c4c9e2020-04-22 00:37:52 +0200464yang::Status YangSchema::status(const std::string& location) const
465{
466 auto node = getSchemaNode(location.c_str());
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200467 switch (node->status()) {
468 case libyang::Status::Deprecated:
Václav Kubernáta1c4c9e2020-04-22 00:37:52 +0200469 return yang::Status::Deprecated;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200470 case libyang::Status::Obsolete:
Václav Kubernáta1c4c9e2020-04-22 00:37:52 +0200471 return yang::Status::Obsolete;
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200472 case libyang::Status::Current:
Václav Kubernáta1c4c9e2020-04-22 00:37:52 +0200473 return yang::Status::Current;
474 }
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200475
476 __builtin_unreachable();
Václav Kubernáta1c4c9e2020-04-22 00:37:52 +0200477}
478
Václav Kubernátd8408e02020-12-02 05:13:27 +0100479bool YangSchema::hasInputNodes(const std::string& path) const
480{
481 auto node = getSchemaNode(path.c_str());
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200482 if (auto type = node->nodeType(); type != libyang::NodeType::Action && type != libyang::NodeType::RPC) {
Václav Kubernátd8408e02020-12-02 05:13:27 +0100483 throw std::logic_error("StaticSchema::hasInputNodes called with non-RPC/action path");
484 }
485
486 // 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 +0200487 return node->child()->child().has_value();
Václav Kubernátd8408e02020-12-02 05:13:27 +0100488}
489
Václav Kubernát0599e9f2020-04-21 09:51:33 +0200490bool YangSchema::isConfig(const std::string& path) const
491{
Václav Kubernáte7248b22020-06-26 15:38:59 +0200492 auto node = getSchemaNode(path.c_str());
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200493 try {
494 if (node->config() == libyang::Config::True) {
Václav Kubernáte7248b22020-06-26 15:38:59 +0200495 return true;
496 }
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200497 } catch (libyang::Error&) {
498 // For non-data nodes (like `rpc`), the config value can't be retrieved. In this case, we'll just default to
499 // "false".
Václav Kubernáte7248b22020-06-26 15:38:59 +0200500 }
501
502 return false;
Václav Kubernát0599e9f2020-04-21 09:51:33 +0200503}
Václav Kubernátb1a75c62020-04-21 15:20:16 +0200504
505std::optional<std::string> YangSchema::defaultValue(const std::string& leafPath) const
506{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200507 return std::optional<std::string>{getSchemaNode(leafPath)->asLeaf().defaultValueStr()};
Václav Kubernátb1a75c62020-04-21 15:20:16 +0200508}
Václav Kubernáta8789602020-07-20 15:18:19 +0200509
510std::string YangSchema::dataPathToSchemaPath(const std::string& path)
511{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200512 return std::string{getSchemaNode(path)->path()};
Václav Kubernáta8789602020-07-20 15:18:19 +0200513}