Migrate to libyang2
libnetconf2: getSchema and getConfig were no longer used in netconf-cli,
so I deleted them. They can get readded once the bindings get split into
a separate project.
sysrepo_access: Some sr_val stuff was removed.
YangSchema: type descriptions are not available
availableNodes returns only input nodes for RPC nodes
impl_getSchemaNode: no longer disables error printing
libyang: No longer supports leafrefs without the leaf it points to.
Depends-on: https://cesnet-gerrit-czechlight/c/CzechLight/dependencies/+/5171
Depends-on: https://gerrit.cesnet.cz/c/CzechLight/dependencies/+/5171
Change-Id: Ie49381a003a61a7bb028be7b2fa1d9d926ac4e58
diff --git a/src/cli.cpp b/src/cli.cpp
index fec77d1..24ed37e 100644
--- a/src/cli.cpp
+++ b/src/cli.cpp
@@ -138,6 +138,7 @@
}
if (const auto& enableFeatures = args["-e"]) {
namespace x3 = boost::spirit::x3;
+ std::map<std::string, std::vector<std::string>> toEnable;
auto grammar = +(x3::char_-":") >> ":" >> +(x3::char_-":");
for (const auto& enableFeature : enableFeatures.asStringList()) {
std::pair<std::string, std::string> parsed;
@@ -147,12 +148,15 @@
std::cerr << "Error parsing feature enable flags: " << enableFeature << "\n";
return 1;
}
- try {
- datastore->enableFeature(parsed.first, parsed.second);
- } catch (std::runtime_error& ex) {
- std::cerr << ex.what() << "\n";
- return 1;
+ toEnable[parsed.first].emplace_back(parsed.second);
+ }
+ try {
+ for (const auto& [moduleName, features] : toEnable) {
+ datastore->setEnabledFeatures(moduleName, features);
}
+ } catch (std::runtime_error& ex) {
+ std::cerr << ex.what() << "\n";
+ return 1;
}
}
diff --git a/src/libyang_utils.cpp b/src/libyang_utils.cpp
index a6b22cd..f3881fe 100644
--- a/src/libyang_utils.cpp
+++ b/src/libyang_utils.cpp
@@ -1,130 +1,131 @@
#include <boost/algorithm/string/predicate.hpp>
#include <cmath>
+#include <libyang-cpp/Context.hpp>
#include "datastore_access.hpp"
#include "libyang_utils.hpp"
#include "utils.hpp"
-leaf_data_ leafValueFromNode(libyang::S_Data_Node_Leaf_List node)
+struct impl_leafValueFromNode {
+ leaf_data_ operator()(const libyang::Empty) const
+ {
+ return empty_{};
+ }
+
+ leaf_data_ operator()(const libyang::Binary& bin) const
+ {
+ return binary_{std::string{bin.base64}};
+ }
+
+ leaf_data_ operator()(const std::vector<libyang::Bit>& bits) const
+ {
+ bits_ res;
+ std::transform(bits.begin(), bits.end(), std::back_inserter(res.m_bits), [] (const libyang::Bit& bit) {
+ return bit.name;
+ });
+ return res;
+ }
+
+ leaf_data_ operator()(const libyang::Enum& enumVal) const
+ {
+ return enum_{enumVal.name};
+ }
+
+ leaf_data_ operator()(const libyang::IdentityRef& identRef) const
+ {
+ return identityRef_{identRef.module, identRef.name};
+ }
+
+ leaf_data_ operator()(const libyang::Decimal64& dec) const
+ {
+ return dec.number * std::pow(10, -dec.digits);
+ }
+
+ leaf_data_ operator()(const std::optional<libyang::DataNode>&) const
+ {
+ throw std::runtime_error("instance-identifier is not supported");
+ }
+
+ template <typename Type>
+ leaf_data_ operator()(const Type& val) const
+ {
+ return val;
+ }
+};
+
+leaf_data_ leafValueFromNode(libyang::DataNodeTerm node)
{
- std::function<leaf_data_(libyang::S_Data_Node_Leaf_List)> impl = [&impl](libyang::S_Data_Node_Leaf_List node) -> leaf_data_ {
- // value_type() is what's ACTUALLY stored inside `node`
- // Leafrefs sometimes don't hold a reference to another, but they have the actual pointed-to value.
- switch (node->value_type()) {
- case LY_TYPE_ENUM:
- return enum_{node->value()->enm()->name()};
- case LY_TYPE_UINT8:
- return node->value()->uint8();
- case LY_TYPE_UINT16:
- return node->value()->uint16();
- case LY_TYPE_UINT32:
- return node->value()->uint32();
- case LY_TYPE_UINT64:
- return node->value()->uint64();
- case LY_TYPE_INT8:
- return node->value()->int8();
- case LY_TYPE_INT16:
- return node->value()->int16();
- case LY_TYPE_INT32:
- return node->value()->int32();
- case LY_TYPE_INT64:
- return node->value()->int64();
- case LY_TYPE_DEC64: {
- auto v = node->value()->dec64();
- return v.value * std::pow(10, -v.digits);
- }
- case LY_TYPE_BOOL:
- return node->value()->bln();
- case LY_TYPE_STRING:
- return std::string{node->value()->string()};
- case LY_TYPE_BINARY:
- return binary_{node->value()->binary()};
- case LY_TYPE_IDENT:
- return identityRef_{node->value()->ident()->module()->name(), node->value()->ident()->name()};
- case LY_TYPE_EMPTY:
- return empty_{};
- case LY_TYPE_LEAFREF: {
- auto refsTo = node->value()->leafref();
- assert(refsTo);
- return impl(std::make_shared<libyang::Data_Node_Leaf_List>(node->value()->leafref()));
- }
- case LY_TYPE_BITS: {
- auto bits = node->value()->bit();
- std::vector<libyang::S_Type_Bit> filterNull;
- std::copy_if(bits.begin(), bits.end(), std::back_inserter(filterNull), [](auto bit) { return bit; });
- bits_ res;
- std::transform(filterNull.begin(), filterNull.end(), std::inserter(res.m_bits, res.m_bits.end()), [](const auto& bit) { return bit->name(); });
- return bits_{res};
- }
- default:
- return std::string{"(can't print)"};
- }
- };
- return impl(node);
+ return std::visit(impl_leafValueFromNode{},node.value());
}
namespace {
-void impl_lyNodesToTree(DatastoreAccess::Tree& res, const std::vector<std::shared_ptr<libyang::Data_Node>> items, std::optional<std::string> ignoredXPathPrefix)
+template <typename CollectionType>
+void impl_lyNodesToTree(DatastoreAccess::Tree& res, CollectionType items, std::optional<std::string> ignoredXPathPrefix)
{
auto stripXPathPrefix = [&ignoredXPathPrefix](auto path) {
return ignoredXPathPrefix && path.find(*ignoredXPathPrefix) != std::string::npos ? path.substr(ignoredXPathPrefix->size()) : path;
};
for (const auto& it : items) {
- if (it->schema()->nodetype() == LYS_CONTAINER) {
- if (libyang::Schema_Node_Container{it->schema()}.presence()) {
+ if (it.schema().nodeType() == libyang::NodeType::Container) {
+ if (it.schema().asContainer().isPresence()) {
// The fact that the container is included in the data tree
// means that it is present and I don't need to check any
// value.
- res.emplace_back(stripXPathPrefix(it->path()), special_{SpecialValue::PresenceContainer});
+ res.emplace_back(stripXPathPrefix(std::string{it.path()}), special_{SpecialValue::PresenceContainer});
}
}
- if (it->schema()->nodetype() == LYS_LIST) {
- res.emplace_back(stripXPathPrefix(it->path()), special_{SpecialValue::List});
+ if (it.schema().nodeType() == libyang::NodeType::List) {
+ res.emplace_back(stripXPathPrefix(std::string{it.path()}), special_{SpecialValue::List});
}
- if (it->schema()->nodetype() == LYS_LEAF || it->schema()->nodetype() == LYS_LEAFLIST) {
- auto leaf = std::make_shared<libyang::Data_Node_Leaf_List>(it);
- auto value = leafValueFromNode(leaf);
- res.emplace_back(stripXPathPrefix(it->path()), value);
+ if (it.schema().nodeType() == libyang::NodeType::Leaf || it.schema().nodeType() == libyang::NodeType::Leaflist) {
+ auto term = it.asTerm();
+ auto value = leafValueFromNode(term);
+ res.emplace_back(stripXPathPrefix(std::string{it.path()}), value);
}
}
}
}
-void lyNodesToTree(DatastoreAccess::Tree& res, const std::vector<std::shared_ptr<libyang::Data_Node>> items, std::optional<std::string> ignoredXPathPrefix)
+template <typename CollectionType>
+void lyNodesToTree(DatastoreAccess::Tree& res, CollectionType items, std::optional<std::string> ignoredXPathPrefix)
{
- for (auto it = items.begin(); it < items.end(); it++) {
- if ((*it)->schema()->nodetype() == LYS_LEAFLIST) {
- auto leafListPath = stripLeafListValueFromPath((*it)->path());
+ for (auto it = items.begin(); it != items.end(); /* nothing */) {
+ if ((*it).schema().nodeType() == libyang::NodeType::Leaflist) {
+ auto leafListPath = stripLeafListValueFromPath(std::string{(*it).path()});
res.emplace_back(leafListPath, special_{SpecialValue::LeafList});
- while (it != items.end() && boost::starts_with((*it)->path(), leafListPath)) {
- impl_lyNodesToTree(res, (*it)->tree_dfs(), ignoredXPathPrefix);
+ while (it != items.end() && boost::starts_with(std::string{(*it).path()}, leafListPath)) {
+ impl_lyNodesToTree(res, it->childrenDfs(), ignoredXPathPrefix);
it++;
}
} else {
- impl_lyNodesToTree(res, (*it)->tree_dfs(), ignoredXPathPrefix);
+ impl_lyNodesToTree(res, it->childrenDfs(), ignoredXPathPrefix);
+ it++;
}
}
}
-DatastoreAccess::Tree rpcOutputToTree(const std::string& rpcPath, libyang::S_Data_Node output)
+using SiblingColl = libyang::Collection<libyang::DataNode, libyang::IterationType::Sibling>;
+using DfsColl = libyang::Collection<libyang::DataNode, libyang::IterationType::Dfs>;
+
+template
+void lyNodesToTree<SiblingColl>(DatastoreAccess::Tree& res, SiblingColl items, std::optional<std::string> ignoredXPathPrefix);
+template
+void lyNodesToTree<DfsColl>(DatastoreAccess::Tree& res, DfsColl items, std::optional<std::string> ignoredXPathPrefix);
+template
+void lyNodesToTree<libyang::Set<libyang::DataNode>>(DatastoreAccess::Tree& res, libyang::Set<libyang::DataNode> items, std::optional<std::string> ignoredXPathPrefix);
+
+DatastoreAccess::Tree rpcOutputToTree(libyang::DataNode output)
{
DatastoreAccess::Tree res;
- if (output) {
- // The output is "some top-level node". If we actually want the output of our RPC/action we need to use
- // find_path. Also, our `path` is fully prefixed, but the output paths aren't. So we use outputNode->path() to
- // get the unprefixed path.
-
- auto outputNode = output->find_path(rpcPath.c_str())->data().front();
- lyNodesToTree(res, {outputNode}, joinPaths(outputNode->path(), "/"));
- }
+ lyNodesToTree(res, output.siblings(), joinPaths(std::string{output.path()}, "/"));
return res;
}
-libyang::S_Data_Node treeToRpcInput(libyang::S_Context ctx, const std::string& path, DatastoreAccess::Tree in)
+libyang::DataNode treeToRpcInput(libyang::Context ctx, const std::string& path, DatastoreAccess::Tree in)
{
- auto root = std::make_shared<libyang::Data_Node>(ctx, path.c_str(), nullptr, LYD_ANYDATA_CONSTSTRING, LYD_PATH_OPT_UPDATE);
+ auto root = ctx.newPath(path.c_str(), nullptr, libyang::CreationOptions::Update);
for (const auto& [k, v] : in) {
- root->new_path(ctx, k.c_str(), leafDataToString(v).c_str(), LYD_ANYDATA_CONSTSTRING, LYD_PATH_OPT_UPDATE);
+ root.newPath(k.c_str(), leafDataToString(v).c_str(), libyang::CreationOptions::Update);
}
return root;
diff --git a/src/libyang_utils.hpp b/src/libyang_utils.hpp
index b5f5e96..8a470c7 100644
--- a/src/libyang_utils.hpp
+++ b/src/libyang_utils.hpp
@@ -5,11 +5,12 @@
*
*/
-#include <libyang/Tree_Data.hpp>
+#include <libyang-cpp/DataNode.hpp>
#include "ast_values.hpp"
#include "datastore_access.hpp"
-leaf_data_ leafValueFromNode(libyang::S_Data_Node_Leaf_List node);
-void lyNodesToTree(DatastoreAccess::Tree& res, const std::vector<std::shared_ptr<libyang::Data_Node>> items, std::optional<std::string> ignoredXPathPrefix = std::nullopt);
-libyang::S_Data_Node treeToRpcInput(libyang::S_Context ctx, const std::string& path, DatastoreAccess::Tree in);
-DatastoreAccess::Tree rpcOutputToTree(const std::string& rpcPath, libyang::S_Data_Node output);
+leaf_data_ leafValueFromNode(libyang::DataNodeTerm node);
+template <typename CollectionType>
+void lyNodesToTree(DatastoreAccess::Tree& res, CollectionType items, std::optional<std::string> ignoredXPathPrefix = std::nullopt);
+libyang::DataNode treeToRpcInput(libyang::Context ctx, const std::string& path, DatastoreAccess::Tree in);
+DatastoreAccess::Tree rpcOutputToTree(libyang::DataNode output);
diff --git a/src/netconf-client.cpp b/src/netconf-client.cpp
index 4638bcb..0685db6 100644
--- a/src/netconf-client.cpp
+++ b/src/netconf-client.cpp
@@ -7,7 +7,8 @@
*/
#include <cstring>
-#include <libyang/Tree_Data.hpp>
+#include <libyang-cpp/Context.hpp>
+#include <libyang-cpp/DataNode.hpp>
#include <mutex>
extern "C" {
#include <nc_client.h>
@@ -58,15 +59,6 @@
static std::mutex clientOptions;
-inline void custom_free_nc_reply_data(nc_reply_data* reply)
-{
- nc_reply_free(reinterpret_cast<nc_reply*>(reply));
-}
-inline void custom_free_nc_reply_error(nc_reply_error* reply)
-{
- nc_reply_free(reinterpret_cast<nc_reply*>(reply));
-}
-
char* ssh_auth_interactive_cb(const char* auth_name, const char* instruction, const char* prompt, int echo, void* priv)
{
const auto cb = static_cast<const client::KbdInteractiveCb*>(priv);
@@ -74,24 +66,19 @@
return ::strdup(res.c_str());
}
-template <typename Type> using deleter_type_for = void (*)(Type*);
-template <typename Type> deleter_type_for<Type> const deleter_for;
-
-template <> const auto deleter_for<nc_rpc> = nc_rpc_free;
-template <> const auto deleter_for<nc_reply> = nc_reply_free;
-template <> const auto deleter_for<nc_reply_data> = custom_free_nc_reply_data;
-template <> const auto deleter_for<nc_reply_error> = custom_free_nc_reply_error;
-
-template <typename T>
-using unique_ptr_for = std::unique_ptr<T, decltype(deleter_for<T>)>;
-
-template <typename T>
-auto guarded(T* ptr)
+auto guarded(nc_rpc* ptr)
{
- return unique_ptr_for<T>(ptr, deleter_for<T>);
+ return std::unique_ptr<nc_rpc, decltype(&nc_rpc_free)>(ptr, nc_rpc_free);
}
-unique_ptr_for<struct nc_reply> do_rpc(client::Session* session, unique_ptr_for<struct nc_rpc>&& rpc)
+namespace {
+const auto getData_path = "/ietf-netconf-nmda:get-data/data";
+const auto get_path = "/ietf-netconf:get/data";
+}
+
+using managed_rpc = std::invoke_result_t<decltype(guarded), nc_rpc*>;
+
+std::optional<libyang::DataNode> do_rpc(client::Session* session, managed_rpc&& rpc, const char* dataIdentifier)
{
uint64_t msgid;
NC_MSG_TYPE msgtype;
@@ -104,11 +91,11 @@
throw std::runtime_error{"Timeout sending an RPC"};
}
- struct nc_reply* raw_reply;
+ lyd_node* raw_reply;
+ lyd_node* envp;
while (true) {
- msgtype = nc_recv_reply(session->session_internal(), rpc.get(), msgid, 20000, LYD_OPT_DESTRUCT | LYD_OPT_NOSIBLINGS, &raw_reply);
- auto reply = guarded(raw_reply);
- raw_reply = nullptr;
+ msgtype = nc_recv_reply(session->session_internal(), rpc.get(), msgid, 20000, &envp, &raw_reply);
+ auto replyInfo = libyang::wrapRawNode(envp);
switch (msgtype) {
case NC_MSG_ERROR:
@@ -120,76 +107,75 @@
case NC_MSG_NOTIF:
continue;
default:
- return reply;
+ if (!raw_reply) { // <ok> reply, or empty data node, or error
+ std::string msg;
+ for (const auto& child : replyInfo.child()->siblings()) {
+ if (child.asOpaque().name().name == "rpc-error") {
+ for (const auto& error : child.childrenDfs()) {
+ if (error.asOpaque().name().name == "error-message") {
+ msg += "Error: ";
+ msg += error.asOpaque().value();
+ }
+
+ if (error.asOpaque().name().name == "error-path") {
+ msg += "Path: ";
+ msg += error.asOpaque().value();
+ }
+
+ if (error.asOpaque().name().name == "error-type") {
+ msg += "Type: ";
+ msg += error.asOpaque().value();
+ }
+
+ if (error.asOpaque().name().name == "error-tag") {
+ msg += "Tag: ";
+ msg += error.asOpaque().value();
+ }
+
+ if (error.asOpaque().name().name == "error-app-tag") {
+ msg += "App-tag: ";
+ msg += error.asOpaque().value();
+ }
+ }
+
+ msg += "\n";
+ }
+ }
+
+ if (!msg.empty()) {
+ throw client::ReportedError{msg};
+ }
+
+ return std::nullopt;
+ }
+ auto wrapped = libyang::wrapRawNode(raw_reply);
+
+ // If we have a dataIdentifier, then we'll need to look for it.
+ // Some operations don't have that, and then the result data are just the wrapped node.
+ if (!dataIdentifier) {
+ return wrapped;
+ }
+
+ auto anydataValue = wrapped.findPath(dataIdentifier, libyang::OutputNodes::Yes)->asAny().releaseValue();
+
+ // If there's no anydata value, then that means we get empty (but valid) data.
+ if (!anydataValue) {
+ return std::nullopt;
+ }
+
+ return std::get<libyang::DataNode>(*anydataValue);
}
}
__builtin_unreachable();
}
-client::ReportedError make_error(unique_ptr_for<struct nc_reply>&& reply)
+void do_rpc_ok(client::Session* session, managed_rpc&& rpc)
{
- if (reply->type != NC_RPL_ERROR) {
- throw std::logic_error{"Cannot extract an error from a non-error server reply"};
- }
-
- auto errorReply = guarded(reinterpret_cast<struct nc_reply_error*>(reply.release()));
-
- // TODO: capture the error details, not just that human-readable string
- std::ostringstream ss;
- ss << "Server error:" << std::endl;
- for (uint32_t i = 0; i < errorReply->count; ++i) {
- const auto e = errorReply->err[i];
- ss << " #" << i << ": " << e.message;
- if (e.path) {
- ss << " (XPath " << e.path << ")";
- }
- ss << std::endl;
- }
- return client::ReportedError{ss.str()};
-}
-
-std::optional<unique_ptr_for<struct nc_reply_data>> do_rpc_data_or_ok(client::Session* session, unique_ptr_for<struct nc_rpc>&& rpc)
-{
- auto x = do_rpc(session, std::move(rpc));
-
- switch (x->type) {
- case NC_RPL_DATA:
- return guarded(reinterpret_cast<struct nc_reply_data*>(x.release()));
- case NC_RPL_OK:
- return std::nullopt;
- case NC_RPL_ERROR:
- throw make_error(std::move(x));
- default:
- throw std::runtime_error{"Unhandled reply type"};
- }
-}
-
-unique_ptr_for<struct nc_reply_data> do_rpc_data(client::Session* session, unique_ptr_for<struct nc_rpc>&& rpc)
-{
- auto x = do_rpc_data_or_ok(session, std::move(rpc));
- if (!x) {
- throw std::runtime_error{"Received OK instead of a data reply"};
- }
- return std::move(*x);
-}
-
-void do_rpc_ok(client::Session* session, unique_ptr_for<struct nc_rpc>&& rpc)
-{
- auto x = do_rpc_data_or_ok(session, std::move(rpc));
+ auto x = do_rpc(session, std::move(rpc), nullptr);
if (x) {
throw std::runtime_error{"Unexpected DATA reply"};
}
}
-
-std::shared_ptr<libyang::Data_Node> do_get(client::Session* session, unique_ptr_for<nc_rpc> rpc)
-{
- auto reply = impl::do_rpc_data(session, std::move(rpc));
- auto dataNode = libyang::create_new_Data_Node(reply->data);
- // TODO: can we do without copying?
- // If we just default-construct a new node (or use the create_new_Data_Node) and then set reply->data to nullptr,
- // there are mem leaks and even libnetconf2 complains loudly.
- return dataNode ? dataNode->dup_withsiblings(1) : nullptr;
-}
}
namespace client {
@@ -210,9 +196,9 @@
return m_session;
}
-libyang::S_Context Session::libyangContext()
+libyang::Context Session::libyangContext()
{
- return std::make_shared<libyang::Context>(nc_session_get_ctx(m_session), nullptr);
+ return libyang::createUnmanagedContext(const_cast<ly_ctx*>(nc_session_get_ctx(m_session)), nullptr);
}
Session::Session(struct nc_session* session)
@@ -298,13 +284,13 @@
return res;
}
-std::shared_ptr<libyang::Data_Node> Session::get(const std::optional<std::string>& filter)
+std::optional<libyang::DataNode> Session::get(const std::optional<std::string>& filter)
{
auto rpc = impl::guarded(nc_rpc_get(filter ? filter->c_str() : nullptr, NC_WD_ALL, NC_PARAMTYPE_CONST));
if (!rpc) {
throw std::runtime_error("Cannot create get RPC");
}
- return impl::do_get(this, std::move(rpc));
+ return impl::do_rpc(this, std::move(rpc), impl::get_path);
}
const char* datastoreToString(NmdaDatastore datastore)
@@ -322,13 +308,13 @@
__builtin_unreachable();
}
-std::shared_ptr<libyang::Data_Node> Session::getData(const NmdaDatastore datastore, const std::optional<std::string>& filter)
+std::optional<libyang::DataNode> Session::getData(const NmdaDatastore datastore, const std::optional<std::string>& filter)
{
auto rpc = impl::guarded(nc_rpc_getdata(datastoreToString(datastore), filter ? filter->c_str() : nullptr, nullptr, nullptr, 0, 0, 0, 0, NC_WD_ALL, NC_PARAMTYPE_CONST));
if (!rpc) {
throw std::runtime_error("Cannot create get RPC");
}
- return impl::do_get(this, std::move(rpc));
+ return impl::do_rpc(this, std::move(rpc), impl::getData_path);
}
void Session::editData(const NmdaDatastore datastore, const std::string& data)
@@ -340,34 +326,6 @@
return impl::do_rpc_ok(this, std::move(rpc));
}
-std::string Session::getSchema(const std::string_view identifier, const std::optional<std::string_view> version)
-{
- auto rpc = impl::guarded(nc_rpc_getschema(identifier.data(), version ? version.value().data() : nullptr, nullptr, NC_PARAMTYPE_CONST));
- if (!rpc) {
- throw std::runtime_error("Cannot create get-schema RPC");
- }
- auto reply = impl::do_rpc_data(this, std::move(rpc));
-
- auto node = libyang::create_new_Data_Node(reply->data)->dup_withsiblings(1);
- auto set = node->find_path("data");
- for (auto node : set->data()) {
- if (node->schema()->nodetype() == LYS_ANYXML) {
- libyang::Data_Node_Anydata anydata(node);
- return anydata.value().str;
- }
- }
- throw std::runtime_error("Got a reply to get-schema, but it didn't contain the required schema");
-}
-
-std::shared_ptr<libyang::Data_Node> Session::getConfig(const NC_DATASTORE datastore, const std::optional<const std::string> filter)
-{
- auto rpc = impl::guarded(nc_rpc_getconfig(datastore, filter ? filter->c_str() : nullptr, NC_WD_ALL, NC_PARAMTYPE_CONST));
- if (!rpc) {
- throw std::runtime_error("Cannot create get-config RPC");
- }
- return impl::do_get(this, std::move(rpc));
-}
-
void Session::editConfig(const NC_DATASTORE datastore,
const NC_RPC_EDIT_DFLTOP defaultOperation,
const NC_RPC_EDIT_TESTOPT testOption,
@@ -408,19 +366,14 @@
impl::do_rpc_ok(this, std::move(rpc));
}
-std::shared_ptr<libyang::Data_Node> Session::rpc_or_action(const std::string& xmlData)
+std::optional<libyang::DataNode> Session::rpc_or_action(const std::string& xmlData)
{
auto rpc = impl::guarded(nc_rpc_act_generic_xml(xmlData.c_str(), NC_PARAMTYPE_CONST));
if (!rpc) {
throw std::runtime_error("Cannot create generic RPC");
}
- auto reply = impl::do_rpc_data_or_ok(this, std::move(rpc));
- if (reply) {
- auto dataNode = libyang::create_new_Data_Node((*reply)->data);
- return dataNode->dup_withsiblings(1);
- } else {
- return nullptr;
- }
+
+ return impl::do_rpc(this, std::move(rpc), nullptr);
}
void Session::copyConfig(const NC_DATASTORE source, const NC_DATASTORE destination)
diff --git a/src/netconf-client.hpp b/src/netconf-client.hpp
index 839c528..5a16cf2 100644
--- a/src/netconf-client.hpp
+++ b/src/netconf-client.hpp
@@ -12,8 +12,8 @@
struct nc_session;
namespace libyang {
-class Data_Node;
class Context;
+class DataNode;
}
namespace libnetconf {
@@ -46,11 +46,8 @@
static std::unique_ptr<Session> connectSocket(const std::string& path);
static std::unique_ptr<Session> connectFd(const int source, const int sink);
[[nodiscard]] std::vector<std::string_view> capabilities() const;
- std::shared_ptr<libyang::Data_Node> getConfig(const NC_DATASTORE datastore,
- const std::optional<const std::string> filter = std::nullopt); // TODO: arguments...
- std::shared_ptr<libyang::Data_Node> get(const std::optional<std::string>& filter = std::nullopt);
- std::shared_ptr<libyang::Data_Node> getData(const NmdaDatastore datastore, const std::optional<std::string>& filter = std::nullopt);
- std::string getSchema(const std::string_view identifier, const std::optional<std::string_view> version);
+ std::optional<libyang::DataNode> get(const std::optional<std::string>& filter = std::nullopt);
+ std::optional<libyang::DataNode> getData(const NmdaDatastore datastore, const std::optional<std::string>& filter = std::nullopt);
void editConfig(const NC_DATASTORE datastore,
const NC_RPC_EDIT_DFLTOP defaultOperation,
const NC_RPC_EDIT_TESTOPT testOption,
@@ -58,12 +55,12 @@
const std::string& data);
void editData(const NmdaDatastore datastore, const std::string& data);
void copyConfigFromString(const NC_DATASTORE target, const std::string& data);
- std::shared_ptr<libyang::Data_Node> rpc_or_action(const std::string& xmlData);
+ std::optional<libyang::DataNode> rpc_or_action(const std::string& xmlData);
void copyConfig(const NC_DATASTORE source, const NC_DATASTORE destination);
void commit();
void discard();
- std::shared_ptr<libyang::Context> libyangContext();
+ libyang::Context libyangContext();
struct nc_session* session_internal(); // FIXME: remove me
protected:
struct nc_session* m_session;
diff --git a/src/netconf_access.cpp b/src/netconf_access.cpp
index a6dbb7c..2b76e50 100644
--- a/src/netconf_access.cpp
+++ b/src/netconf_access.cpp
@@ -5,8 +5,6 @@
*
*/
-#include <libyang/Libyang.hpp>
-#include <libyang/Tree_Data.hpp>
#include "libyang_utils.hpp"
#include "netconf-client.hpp"
#include "netconf_access.hpp"
@@ -55,7 +53,7 @@
}();
if (config) {
- lyNodesToTree(res, config->tree_for());
+ lyNodesToTree(res, config->siblings());
}
return res;
}
@@ -107,22 +105,28 @@
void NetconfAccess::setLeaf(const std::string& path, leaf_data_ value)
{
auto lyValue = value.type() == typeid(empty_) ? std::nullopt : std::optional(leafDataToString(value));
- auto node = m_schema->dataNodeFromPath(path, lyValue);
- doEditFromDataNode(node);
+ auto nodes = m_schema->dataNodeFromPath(path, lyValue);
+ doEditFromDataNode(*nodes.createdParent);
}
void NetconfAccess::createItem(const std::string& path)
{
- auto node = m_schema->dataNodeFromPath(path);
- doEditFromDataNode(node);
+ auto nodes = m_schema->dataNodeFromPath(path);
+ doEditFromDataNode(*nodes.createdParent);
}
void NetconfAccess::deleteItem(const std::string& path)
{
- auto node = m_schema->dataNodeFromPath(path);
- auto container = *(node->find_path(path.c_str())->data().begin());
- container->insert_attr(m_schema->getYangModule("ietf-netconf"), "operation", "delete");
- doEditFromDataNode(node);
+ auto nodes = m_schema->dataNodeFromPath(path);
+
+ // When deleting leafs, `nodes.newNode` is opaque, because the leaf does not have a value. We need to use
+ // newAttrOpaqueJSON for opaque leafs.
+ if (nodes.createdNode->isOpaque()) {
+ nodes.createdNode->newAttrOpaqueJSON("ietf-netconf", "ietf-netconf:operation", "delete");
+ } else {
+ nodes.createdNode->newMeta(*m_schema->getYangModule("ietf-netconf"), "operation", "delete");
+ }
+ doEditFromDataNode(*nodes.createdParent);
}
struct impl_toYangInsert {
@@ -143,29 +147,29 @@
void NetconfAccess::moveItem(const std::string& source, std::variant<yang::move::Absolute, yang::move::Relative> move)
{
- auto node = m_schema->dataNodeFromPath(source);
- auto sourceNode = *(node->find_path(source.c_str())->data().begin());
- auto yangModule = m_schema->getYangModule("yang");
- sourceNode->insert_attr(yangModule, "insert", toYangInsert(move).c_str());
+ auto nodes = m_schema->dataNodeFromPath(source);
+ auto sourceNode = *(nodes.createdNode->findPath(source.c_str()));
+ auto yangModule = *m_schema->getYangModule("yang");
+ sourceNode.newMeta(yangModule, "insert", toYangInsert(move).c_str());
if (std::holds_alternative<yang::move::Relative>(move)) {
auto relative = std::get<yang::move::Relative>(move);
if (m_schema->nodeType(source) == yang::NodeTypes::LeafList) {
- sourceNode->insert_attr(yangModule, "value", leafDataToString(relative.m_path.at(".")).c_str());
+ sourceNode.newMeta(yangModule, "value", leafDataToString(relative.m_path.at(".")).c_str());
} else {
- sourceNode->insert_attr(yangModule, "key", instanceToString(relative.m_path, node->node_module()->name()).c_str());
+ sourceNode.newMeta(yangModule, "key", instanceToString(relative.m_path, std::string{nodes.createdNode->schema().module().name()}).c_str());
}
}
doEditFromDataNode(sourceNode);
}
-void NetconfAccess::doEditFromDataNode(std::shared_ptr<libyang::Data_Node> dataNode)
+void NetconfAccess::doEditFromDataNode(libyang::DataNode dataNode)
{
- auto data = dataNode->print_mem(LYD_XML, 0);
+ auto data = dataNode.printStr(libyang::DataFormat::XML, libyang::PrintFlags::WithSiblings);
if (m_serverHasNMDA) {
- m_session->editData(targetToDs_set(m_target), data);
+ m_session->editData(targetToDs_set(m_target), std::string{*data});
} else {
- m_session->editConfig(NC_DATASTORE_CANDIDATE, NC_RPC_EDIT_DFLTOP_MERGE, NC_RPC_EDIT_TESTOPT_TESTSET, NC_RPC_EDIT_ERROPT_STOP, data);
+ m_session->editConfig(NC_DATASTORE_CANDIDATE, NC_RPC_EDIT_DFLTOP_MERGE, NC_RPC_EDIT_TESTOPT_TESTSET, NC_RPC_EDIT_ERROPT_STOP, std::string{*data});
}
}
@@ -182,10 +186,13 @@
DatastoreAccess::Tree NetconfAccess::execute(const std::string& path, const Tree& input)
{
auto inputNode = treeToRpcInput(m_session->libyangContext(), path, input);
- auto data = inputNode->print_mem(LYD_XML, 0);
+ auto data = inputNode.printStr(libyang::DataFormat::XML, libyang::PrintFlags::WithSiblings);
- auto output = m_session->rpc_or_action(data);
- return rpcOutputToTree(path, output);
+ auto output = m_session->rpc_or_action(std::string{*data});
+ if (!output) {
+ return {};
+ }
+ return rpcOutputToTree(*output);
}
NC_DATASTORE toNcDatastore(Datastore datastore)
@@ -212,41 +219,41 @@
std::vector<ListInstance> NetconfAccess::listInstances(const std::string& path)
{
std::vector<ListInstance> res;
- auto list = m_schema->dataNodeFromPath(path);
+ auto keys = m_session->libyangContext().findXPath(path.c_str()).front().asList().keys();
+ auto nodes = m_session->libyangContext().newPath2(path.c_str(), nullptr, libyang::CreationOptions::Opaque);
- // This inserts selection nodes - I only want keys not other data
- // To get the keys, I have to call find_path here - otherwise I would get keys of a top-level node (which might not even be a list)
- auto keys = libyang::Schema_Node_List{(*(list->find_path(path.c_str())->data().begin()))->schema()}.keys();
+ // Here we create a tree with "selection leafs" for all they keys of our wanted list. These leafs tell NETCONF, that
+ // we only want the list's keys and not any other data.
for (const auto& keyLeaf : keys) {
- // Have to call find_path here - otherwise I'll have the list, not the leaf inside it
- auto selectionLeaf = *(m_schema->dataNodeFromPath(keyLeaf->path())->find_path(keyLeaf->path().c_str())->data().begin());
- auto actualList = *(list->find_path(path.c_str())->data().begin());
- actualList->insert(selectionLeaf);
+ // Selection leafs need to be inserted directly to the list using relative paths, that's why `newNode` is used
+ // here.
+ nodes.createdNode->newPath(keyLeaf.name().data(), nullptr, libyang::CreationOptions::Opaque);
}
- auto instances = m_session->get(list->print_mem(LYD_XML, 0));
+ // Have to use `newParent` in case our wanted list is a nested list. With `newNode` I would only send the inner
+ // nested list and not the whole tree.
+ auto instances = m_session->get(std::string{*nodes.createdParent->printStr(libyang::DataFormat::XML, libyang::PrintFlags::WithSiblings)});
if (!instances) {
return res;
}
- for (const auto& instance : instances->find_path(path.c_str())->data()) {
+ for (const auto& instance : instances->findXPath(path.c_str())) {
ListInstance instanceRes;
- // I take the first child here, because the first element (the parent of the child()) will be the list
- for (const auto& keyLeaf : instance->child()->tree_for()) {
+ for (const auto& keyLeaf : instance.child()->siblings()) {
// FIXME: even though we specified we only want the key leafs, Netopeer disregards that and sends more data,
// even lists and other stuff. We only want keys, so filter out non-leafs and non-keys
// https://github.com/CESNET/netopeer2/issues/825
- if (keyLeaf->schema()->nodetype() != LYS_LEAF) {
+ if (keyLeaf.schema().nodeType() != libyang::NodeType::Leaf) {
continue;
}
- if (!std::make_shared<libyang::Schema_Node_Leaf>(keyLeaf->schema())->is_key()) {
+ if (!keyLeaf.schema().asLeaf().isKey()) {
continue;
}
- auto leafData = std::make_shared<libyang::Data_Node_Leaf_List>(keyLeaf);
- instanceRes.insert({leafData->schema()->name(), leafValueFromNode(leafData)});
+ auto leafData = keyLeaf.asTerm();
+ instanceRes.insert({std::string{leafData.schema().name()}, leafValueFromNode(leafData)});
}
res.emplace_back(instanceRes);
}
@@ -260,5 +267,10 @@
if (!config) {
return "";
}
- return config->print_mem(format == DataFormat::Xml ? LYD_XML : LYD_JSON, LYP_WITHSIBLINGS | LYP_FORMAT);
+ auto str = config->printStr(format == DataFormat::Xml ? libyang::DataFormat::XML : libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings);
+ if (!str) {
+ return "";
+ }
+
+ return std::string{*str};
}
diff --git a/src/netconf_access.hpp b/src/netconf_access.hpp
index 43ca1e8..eb678d5 100644
--- a/src/netconf_access.hpp
+++ b/src/netconf_access.hpp
@@ -21,10 +21,6 @@
}
}
-namespace libyang {
-class Data_Node;
-}
-
class Schema;
class YangSchema;
@@ -57,7 +53,7 @@
private:
std::vector<ListInstance> listInstances(const std::string& path) override;
- void doEditFromDataNode(std::shared_ptr<libyang::Data_Node> dataNode);
+ void doEditFromDataNode(libyang::DataNode dataNode);
void checkNMDA();
diff --git a/src/sysrepo_access.cpp b/src/sysrepo_access.cpp
index bbdf6d1..34d255a 100644
--- a/src/sysrepo_access.cpp
+++ b/src/sysrepo_access.cpp
@@ -7,86 +7,34 @@
*/
#include <experimental/iterator>
-#include <libyang/Tree_Data.hpp>
-#include <libyang/Tree_Schema.hpp>
#include <sstream>
#include <sysrepo-cpp/Session.hpp>
+#include <sysrepo-cpp/utils/exception.hpp>
#include "libyang_utils.hpp"
#include "sysrepo_access.hpp"
#include "utils.hpp"
#include "yang_schema.hpp"
-const auto OPERATION_TIMEOUT_MS = 1000;
-
-struct valFromValue : boost::static_visitor<sysrepo::S_Val> {
- sysrepo::S_Val operator()(const enum_& value) const
- {
- return std::make_shared<sysrepo::Val>(value.m_value.c_str(), SR_ENUM_T);
- }
-
- sysrepo::S_Val operator()(const binary_& value) const
- {
- return std::make_shared<sysrepo::Val>(value.m_value.c_str(), SR_BINARY_T);
- }
-
- sysrepo::S_Val operator()(const empty_) const
- {
- return std::make_shared<sysrepo::Val>(nullptr, SR_LEAF_EMPTY_T);
- }
-
- sysrepo::S_Val operator()(const identityRef_& value) const
- {
- auto res = value.m_prefix ? (value.m_prefix.value().m_name + ":" + value.m_value) : value.m_value;
- return std::make_shared<sysrepo::Val>(res.c_str(), SR_IDENTITYREF_T);
- }
-
- sysrepo::S_Val operator()(const special_& value) const
- {
- throw std::runtime_error("Tried constructing S_Val from a " + specialValueToString(value));
- }
-
- sysrepo::S_Val operator()(const std::string& value) const
- {
- return std::make_shared<sysrepo::Val>(value.c_str());
- }
-
- sysrepo::S_Val operator()(const bits_& value) const
- {
- std::stringstream ss;
- std::copy(value.m_bits.begin(), value.m_bits.end(), std::experimental::make_ostream_joiner(ss, " "));
- return std::make_shared<sysrepo::Val>(ss.str().c_str(), SR_BITS_T);
- }
-
- template <typename T>
- sysrepo::S_Val operator()(const T& value) const
- {
- return std::make_shared<sysrepo::Val>(value);
- }
-};
+const auto OPERATION_TIMEOUT_MS = std::chrono::milliseconds{1000};
SysrepoAccess::~SysrepoAccess() = default;
-sr_datastore_t toSrDatastore(Datastore datastore)
+sysrepo::Datastore toSrDatastore(Datastore datastore)
{
switch (datastore) {
case Datastore::Running:
- return SR_DS_RUNNING;
+ return sysrepo::Datastore::Running;
case Datastore::Startup:
- return SR_DS_STARTUP;
+ return sysrepo::Datastore::Startup;
}
__builtin_unreachable();
}
SysrepoAccess::SysrepoAccess()
- : m_connection(std::make_shared<sysrepo::Connection>())
- , m_session(std::make_shared<sysrepo::Session>(m_connection))
- , m_schema(std::make_shared<YangSchema>(m_session->get_context()))
+ : m_connection()
+ , m_session(m_connection.sessionStart())
+ , m_schema(std::make_shared<YangSchema>(m_session.getContext()))
{
- try {
- m_session = std::make_shared<sysrepo::Session>(m_connection);
- } catch (sysrepo::sysrepo_exception& ex) {
- reportErrors();
- }
}
namespace {
@@ -94,11 +42,11 @@
{
switch (target) {
case DatastoreTarget::Operational:
- return SR_DS_OPERATIONAL;
+ return sysrepo::Datastore::Operational;
case DatastoreTarget::Running:
- return SR_DS_RUNNING;
+ return sysrepo::Datastore::Running;
case DatastoreTarget::Startup:
- return SR_DS_STARTUP;
+ return sysrepo::Datastore::Startup;
}
__builtin_unreachable();
@@ -110,9 +58,9 @@
case DatastoreTarget::Operational:
case DatastoreTarget::Running:
// TODO: Doing candidate here doesn't work, why?
- return SR_DS_RUNNING;
+ return sysrepo::Datastore::Running;
case DatastoreTarget::Startup:
- return SR_DS_STARTUP;
+ return sysrepo::Datastore::Startup;
}
__builtin_unreachable();
@@ -125,12 +73,12 @@
Tree res;
try {
- m_session->session_switch_ds(targetToDs_get(m_target));
- auto config = m_session->get_data(((path == "/") ? "/*" : path).c_str());
+ m_session.switchDatastore(targetToDs_get(m_target));
+ auto config = m_session.getData(((path == "/") ? "/*" : path).c_str());
if (config) {
- lyNodesToTree(res, config->tree_for());
+ lyNodesToTree(res, config->siblings());
}
- } catch (sysrepo::sysrepo_exception& ex) {
+ } catch (sysrepo::Error& ex) {
reportErrors();
}
return res;
@@ -139,9 +87,10 @@
void SysrepoAccess::setLeaf(const std::string& path, leaf_data_ value)
{
try {
- m_session->session_switch_ds(targetToDs_set(m_target));
- m_session->set_item(path.c_str(), boost::apply_visitor(valFromValue(), value), SR_EDIT_ISOLATE);
- } catch (sysrepo::sysrepo_exception& ex) {
+ m_session.switchDatastore(targetToDs_set(m_target));
+ auto lyValue = value.type() == typeid(empty_) ? "" : leafDataToString(value);
+ m_session.setItem(path.c_str(), lyValue.c_str(), sysrepo::EditOptions::Isolate);
+ } catch (sysrepo::Error& ex) {
reportErrors();
}
}
@@ -149,9 +98,9 @@
void SysrepoAccess::createItem(const std::string& path)
{
try {
- m_session->session_switch_ds(targetToDs_set(m_target));
- m_session->set_item(path.c_str());
- } catch (sysrepo::sysrepo_exception& ex) {
+ m_session.switchDatastore(targetToDs_set(m_target));
+ m_session.setItem(path.c_str(), nullptr);
+ } catch (sysrepo::Error& ex) {
reportErrors();
}
}
@@ -159,27 +108,28 @@
void SysrepoAccess::deleteItem(const std::string& path)
{
try {
- // Have to use SR_EDIT_ISOLATE, because deleting something that's been set without committing is not supported
+ // Have to use sysrepo::EditOptions::Isolate, because deleting something that's been set without committing is
+ // not supported.
// https://github.com/sysrepo/sysrepo/issues/1967#issuecomment-625085090
- m_session->session_switch_ds(targetToDs_set(m_target));
- m_session->delete_item(path.c_str(), SR_EDIT_ISOLATE);
- } catch (sysrepo::sysrepo_exception& ex) {
+ m_session.switchDatastore(targetToDs_set(m_target));
+ m_session.deleteItem(path.c_str(), sysrepo::EditOptions::Isolate);
+ } catch (sysrepo::Error& ex) {
reportErrors();
}
}
struct impl_toSrMoveOp {
- sr_move_position_t operator()(yang::move::Absolute& absolute)
+ sysrepo::MovePosition operator()(yang::move::Absolute& absolute)
{
- return absolute == yang::move::Absolute::Begin ? SR_MOVE_FIRST : SR_MOVE_LAST;
+ return absolute == yang::move::Absolute::Begin ? sysrepo::MovePosition::First : sysrepo::MovePosition::Last;
}
- sr_move_position_t operator()(yang::move::Relative& relative)
+ sysrepo::MovePosition operator()(yang::move::Relative& relative)
{
- return relative.m_position == yang::move::Relative::Position::After ? SR_MOVE_AFTER : SR_MOVE_BEFORE;
+ return relative.m_position == yang::move::Relative::Position::After ? sysrepo::MovePosition::After : sysrepo::MovePosition::Before;
}
};
-sr_move_position_t toSrMoveOp(std::variant<yang::move::Absolute, yang::move::Relative> move)
+sysrepo::MovePosition toSrMoveOp(std::variant<yang::move::Absolute, yang::move::Relative> move)
{
return std::visit(impl_toSrMoveOp{}, move);
}
@@ -195,16 +145,16 @@
destination = instanceToString(relative.m_path);
}
}
- m_session->session_switch_ds(targetToDs_set(m_target));
- m_session->move_item(source.c_str(), toSrMoveOp(move), destination.c_str(), destination.c_str());
+ m_session.switchDatastore(targetToDs_set(m_target));
+ m_session.moveItem(source.c_str(), toSrMoveOp(move), destination.c_str());
}
void SysrepoAccess::commitChanges()
{
try {
- m_session->session_switch_ds(targetToDs_set(m_target));
- m_session->apply_changes(OPERATION_TIMEOUT_MS, 1);
- } catch (sysrepo::sysrepo_exception& ex) {
+ m_session.switchDatastore(targetToDs_set(m_target));
+ m_session.applyChanges(OPERATION_TIMEOUT_MS);
+ } catch (sysrepo::Error& ex) {
reportErrors();
}
}
@@ -212,25 +162,25 @@
void SysrepoAccess::discardChanges()
{
try {
- m_session->session_switch_ds(targetToDs_set(m_target));
- m_session->discard_changes();
- } catch (sysrepo::sysrepo_exception& ex) {
+ m_session.switchDatastore(targetToDs_set(m_target));
+ m_session.discardChanges();
+ } catch (sysrepo::Error& ex) {
reportErrors();
}
}
DatastoreAccess::Tree SysrepoAccess::execute(const std::string& path, const Tree& input)
{
- auto inputNode = treeToRpcInput(m_session->get_context(), path, input);
- m_session->session_switch_ds(targetToDs_set(m_target));
- auto output = m_session->rpc_send(inputNode);
- return rpcOutputToTree(path, output);
+ auto inputNode = treeToRpcInput(m_session.getContext(), path, input);
+ m_session.switchDatastore(targetToDs_set(m_target));
+ auto output = m_session.sendRPC(inputNode);
+ return rpcOutputToTree(output);
}
void SysrepoAccess::copyConfig(const Datastore source, const Datastore destination)
{
- m_session->session_switch_ds(toSrDatastore(destination));
- m_session->copy_config(toSrDatastore(source), nullptr, OPERATION_TIMEOUT_MS, 1);
+ m_session.switchDatastore(toSrDatastore(destination));
+ m_session.copyConfig(toSrDatastore(source), nullptr, OPERATION_TIMEOUT_MS);
}
std::shared_ptr<Schema> SysrepoAccess::schema()
@@ -240,16 +190,14 @@
[[noreturn]] void SysrepoAccess::reportErrors() const
{
- // I only use get_error to get error info, since the error code from
- // sysrepo_exception doesn't really give any meaningful information. For
- // example an "invalid argument" error could mean a node isn't enabled, or
- // it could mean something totally different and there is no documentation
- // for that, so it's better to just use the message sysrepo gives me.
- auto srErrors = m_session->get_error();
std::vector<DatastoreError> res;
- for (size_t i = 0; i < srErrors->error_cnt(); i++) {
- res.emplace_back(srErrors->message(i), srErrors->xpath(i) ? std::optional<std::string>{srErrors->xpath(i)} : std::nullopt);
+ for (const auto& err : m_session.getErrors()) {
+ res.emplace_back(err.errorMessage);
+ }
+
+ for (const auto& err : m_session.getNetconfErrors()) {
+ res.emplace_back(err.message, err.path);
}
throw DatastoreException(res);
@@ -258,47 +206,23 @@
std::vector<ListInstance> SysrepoAccess::listInstances(const std::string& path)
{
std::vector<ListInstance> res;
- auto lists = getItems(path);
+ auto lists = m_session.getData(path.c_str());
+ if (!lists) {
+ return res;
+ }
- decltype(lists) instances;
- auto wantedTree = *(m_schema->dataNodeFromPath(path)->find_path(path.c_str())->data().begin());
- std::copy_if(lists.begin(), lists.end(), std::inserter(instances, instances.end()), [this, pathToCheck = wantedTree->schema()->path()](const auto& item) {
- // This filters out non-instances.
- if (item.second.type() != typeid(special_) || boost::get<special_>(item.second).m_value != SpecialValue::List) {
- return false;
- }
-
- // Now, getItems is recursive: it gives everything including nested lists. So I try create a tree from the instance...
- auto instanceTree = *(m_schema->dataNodeFromPath(item.first)->find_path(item.first.c_str())->data().begin());
- // And then check if its schema path matches the list we actually want. This filters out lists which are not the ones I requested.
- return instanceTree->schema()->path() == pathToCheck;
- });
-
- // If there are no instances, then just return
+ auto instances = lists->findXPath(path.c_str());
if (instances.empty()) {
return res;
}
- // I need to find out which keys does the list have. To do that, I create a
- // tree from the first instance. This is gives me some top level node,
- // which will be our list in case out list is a top-level node. In case it
- // isn't, we have call find_path on the top level node. After that, I just
- // retrieve the keys.
- auto topLevelTree = m_schema->dataNodeFromPath(instances.begin()->first);
- auto list = *(topLevelTree->find_path(path.c_str())->data().begin());
- auto keys = libyang::Schema_Node_List{list->schema()}.keys();
+ auto keys = instances.front().schema().asList().keys();
- // Creating a full tree at the same time from the values sysrepo gives me
- // would be a pain (and after sysrepo switches to libyang meaningless), so
- // I just use this algorithm to create data nodes one by one and get the
- // key values from them.
for (const auto& instance : instances) {
- auto wantedList = *(m_schema->dataNodeFromPath(instance.first)->find_path(path.c_str())->data().begin());
ListInstance instanceRes;
for (const auto& key : keys) {
- auto vec = wantedList->find_path(key->name())->data();
- auto leaf = std::make_shared<libyang::Data_Node_Leaf_List>(*(vec.begin()));
- instanceRes.emplace(key->name(), leafValueFromNode(leaf));
+ auto leaf = instance.findPath(key.name().data());
+ instanceRes.emplace(std::string{leaf->schema().name()}, leafValueFromNode(leaf->asTerm()));
}
res.emplace_back(instanceRes);
}
@@ -308,6 +232,11 @@
std::string SysrepoAccess::dump(const DataFormat format) const
{
- auto root = m_session->get_data("/*");
- return root->print_mem(format == DataFormat::Xml ? LYD_XML : LYD_JSON, LYP_WITHSIBLINGS | LYP_FORMAT);
+ auto root = m_session.getData("/*");
+ auto str = root->printStr(format == DataFormat::Xml ? libyang::DataFormat::XML : libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings);
+ if (!str) {
+ return "";
+ }
+
+ return std::string{*str};
}
diff --git a/src/sysrepo_access.hpp b/src/sysrepo_access.hpp
index 7a02d8b..0f736d1 100644
--- a/src/sysrepo_access.hpp
+++ b/src/sysrepo_access.hpp
@@ -9,6 +9,7 @@
#pragma once
#include <string>
+#include <sysrepo-cpp/Connection.hpp>
#include "datastore_access.hpp"
/*! \class DatastoreAccess
@@ -47,7 +48,7 @@
std::vector<ListInstance> listInstances(const std::string& path) override;
[[noreturn]] void reportErrors() const;
- std::shared_ptr<sysrepo::Connection> m_connection;
- std::shared_ptr<sysrepo::Session> m_session;
+ sysrepo::Connection m_connection;
+ sysrepo::Session m_session;
std::shared_ptr<YangSchema> m_schema;
};
diff --git a/src/yang_access.cpp b/src/yang_access.cpp
index 0555616..72e38e7 100644
--- a/src/yang_access.cpp
+++ b/src/yang_access.cpp
@@ -2,8 +2,8 @@
#include <experimental/iterator>
#include <fstream>
#include <iostream>
-#include <libyang/Tree_Data.hpp>
-#include <libyang/libyang.h>
+#include <libyang-cpp/DataNode.hpp>
+#include <libyang-cpp/Utils.hpp>
#include "UniqueResource.hpp"
#include "libyang_utils.hpp"
#include "utils.hpp"
@@ -11,35 +11,21 @@
#include "yang_schema.hpp"
namespace {
-template <typename Type> using lyPtrDeleter_type = void (*)(Type*);
-template <typename Type> const lyPtrDeleter_type<Type> lyPtrDeleter;
-template <> const auto lyPtrDeleter<ly_set> = ly_set_free;
-template <> const auto lyPtrDeleter<ly_ctx> = static_cast<lyPtrDeleter_type<ly_ctx>>([] (auto* ptr) {ly_ctx_destroy(ptr, nullptr);});
-template <> const auto lyPtrDeleter<lyd_node> = lyd_free_withsiblings;
-
-template <typename Type>
-auto lyWrap(Type* ptr)
-{
- return std::unique_ptr<Type, lyPtrDeleter_type<Type>>{ptr, lyPtrDeleter<Type>};
-}
-
// Convenient for functions that take m_datastore as an argument
-using DatastoreType = std::unique_ptr<lyd_node, lyPtrDeleter_type<lyd_node>>;
+using DatastoreType = std::optional<libyang::DataNode>;
}
YangAccess::YangAccess()
- : m_ctx(lyWrap(ly_ctx_new(nullptr, LY_CTX_DISABLE_SEARCHDIR_CWD)))
- , m_datastore(lyWrap<lyd_node>(nullptr))
- , m_schema(std::make_shared<YangSchema>(libyang::create_new_Context(m_ctx.get())))
- , m_validation_mode(LYD_OPT_DATA)
+ : m_ctx(nullptr, libyang::ContextOptions::DisableSearchCwd)
+ , m_datastore(std::nullopt)
+ , m_schema(std::make_shared<YangSchema>(m_ctx))
{
}
YangAccess::YangAccess(std::shared_ptr<YangSchema> schema)
- : m_ctx(schema->m_context->swig_ctx(), [](auto) {})
- , m_datastore(lyWrap<lyd_node>(nullptr))
+ : m_ctx(schema->m_context)
+ , m_datastore(std::nullopt)
, m_schema(schema)
- , m_validation_mode(LYD_OPT_RPC)
{
}
@@ -47,78 +33,69 @@
[[noreturn]] void YangAccess::getErrorsAndThrow() const
{
- auto errors = libyang::get_ly_errors(libyang::create_new_Context(m_ctx.get()));
std::vector<DatastoreError> errorsRes;
- for (const auto& error : errors) {
- using namespace std::string_view_literals;
- errorsRes.emplace_back(error->errmsg(), error->errpath() != ""sv ? std::optional{error->errpath()} : std::nullopt);
- }
+ for (const auto& err : m_ctx.getErrors()) {
+ errorsRes.emplace_back(err.message, err.path);
+ }
throw DatastoreException(errorsRes);
}
void YangAccess::impl_newPath(const std::string& path, const std::optional<std::string>& value)
{
- auto newNode = lyd_new_path(m_datastore.get(), m_ctx.get(), path.c_str(), value ? (void*)value->c_str() : nullptr, LYD_ANYDATA_CONSTSTRING, LYD_PATH_OPT_UPDATE);
- if (!newNode) {
+ try {
+ if (m_datastore) {
+ m_datastore->newPath(path.c_str(), value ? value->c_str() : nullptr, libyang::CreationOptions::Update);
+ } else {
+ m_datastore = m_ctx.newPath(path.c_str(), value ? value->c_str() : nullptr, libyang::CreationOptions::Update);
+ }
+ } catch (libyang::Error&) {
getErrorsAndThrow();
}
- if (!m_datastore) {
- m_datastore = lyWrap(newNode);
- }
}
namespace {
-void impl_unlink(DatastoreType& datastore, lyd_node* what)
+void impl_unlink(DatastoreType& datastore, libyang::DataNode what)
{
// If the node to be unlinked is the one our datastore variable points to, we need to find a new one to point to (one of its siblings)
- if (datastore.get() == what) {
- auto oldDatastore = datastore.release();
- if (oldDatastore->prev != oldDatastore) {
- datastore = lyWrap(oldDatastore->prev);
- } else {
- datastore = lyWrap(oldDatastore->next);
- }
+
+ if (datastore == what) {
+ auto oldDatastore = datastore;
+ do {
+ datastore = datastore->previousSibling();
+ if (datastore == oldDatastore) {
+ // We have gone all the way back to our original node, which means it's the only node in our
+ // datastore.
+ datastore = std::nullopt;
+ break;
+ }
+ } while (datastore->schema().module().name() == "ietf-yang-library");
}
- lyd_unlink(what);
+ what.unlink();
}
}
void YangAccess::impl_removeNode(const std::string& path)
{
- auto set = lyWrap(lyd_find_path(m_datastore.get(), path.c_str()));
- if (!set || set->number == 0) {
- // Check if schema node exists - lyd_find_path first checks if the first argument is non-null before checking for path validity
- if (!ly_ctx_get_node(m_ctx.get(), nullptr, path.c_str(), 0)) {
- throw DatastoreException{{DatastoreError{"Schema node doesn't exist.", path}}};
- }
- // Check if libyang found another error
- if (ly_err_first(m_ctx.get())) {
- getErrorsAndThrow();
- }
-
+ if (!m_datastore) {
+ // Otherwise the datastore just doesn't contain the wanted node.
+ throw DatastoreException{{DatastoreError{"Datastore is empty.", path}}};
+ }
+ auto toRemove = m_datastore->findPath(path.c_str());
+ if (!toRemove) {
// Otherwise the datastore just doesn't contain the wanted node.
throw DatastoreException{{DatastoreError{"Data node doesn't exist.", path}}};
}
- auto toRemove = set->set.d[0];
-
- impl_unlink(m_datastore, toRemove);
-
- lyd_free(toRemove);
+ impl_unlink(m_datastore, *toRemove);
}
void YangAccess::validate()
{
- auto datastore = m_datastore.release();
-
- if (m_validation_mode == LYD_OPT_RPC) {
- lyd_validate(&datastore, m_validation_mode, nullptr);
- } else {
- lyd_validate(&datastore, m_validation_mode | LYD_OPT_DATA_NO_YANGLIB, m_ctx.get());
+ if (m_datastore) {
+ libyang::validateAll(m_datastore);
}
- m_datastore = lyWrap(datastore);
}
DatastoreAccess::Tree YangAccess::getItems(const std::string& path) const
@@ -128,10 +105,9 @@
return res;
}
- auto set = lyWrap(lyd_find_path(m_datastore.get(), path == "/" ? "/*" : path.c_str()));
- auto setWrapper = libyang::Set(set.get(), nullptr);
- std::optional<std::string> ignoredXPathPrefix;
- lyNodesToTree(res, setWrapper.data());
+ auto set = m_datastore->findXPath(path == "/" ? "/*" : path.c_str());
+
+ lyNodesToTree(res, set);
return res;
}
@@ -154,66 +130,60 @@
namespace {
struct impl_moveItem {
DatastoreType& m_datastore;
- lyd_node* m_sourceNode;
+ libyang::DataNode m_sourceNode;
void operator()(yang::move::Absolute absolute) const
{
- auto set = lyWrap(lyd_find_instance(m_sourceNode, m_sourceNode->schema));
- if (set->number == 1) { // m_sourceNode is the sole instance, do nothing
+ auto set = m_sourceNode.findXPath(m_sourceNode.schema().path().get().get());
+ if (set.size() == 1) { // m_sourceNode is the sole instance, do nothing
return;
}
- doUnlink();
switch (absolute) {
case yang::move::Absolute::Begin:
- if (set->set.d[0] == m_sourceNode) { // List is already at the beginning, do nothing
+ if (set.front() == m_sourceNode) { // List is already at the beginning, do nothing
return;
}
- lyd_insert_before(set->set.d[0], m_sourceNode);
- return;
+ set.front().insertBefore(m_sourceNode);
+ break;
case yang::move::Absolute::End:
- if (set->set.d[set->number - 1] == m_sourceNode) { // List is already at the end, do nothing
+ if (set.back() == m_sourceNode) { // List is already at the end, do nothing
return;
}
- lyd_insert_after(set->set.d[set->number - 1], m_sourceNode);
- return;
+ set.back().insertAfter(m_sourceNode);
+ break;
}
+ m_datastore = m_datastore->firstSibling();
}
void operator()(const yang::move::Relative& relative) const
{
- auto keySuffix = m_sourceNode->schema->nodetype == LYS_LIST ? instanceToString(relative.m_path)
+ auto keySuffix = m_sourceNode.schema().nodeType() == libyang::NodeType::List ? instanceToString(relative.m_path)
: leafDataToString(relative.m_path.at("."));
- lyd_node* destNode;
- lyd_find_sibling_val(m_sourceNode, m_sourceNode->schema, keySuffix.c_str(), &destNode);
+ auto destNode = m_sourceNode.findSiblingVal(m_sourceNode.schema(), keySuffix.c_str());
- doUnlink();
if (relative.m_position == yang::move::Relative::Position::After) {
- lyd_insert_after(destNode, m_sourceNode);
+ destNode->insertAfter(m_sourceNode);
} else {
- lyd_insert_before(destNode, m_sourceNode);
+ destNode->insertBefore(m_sourceNode);
}
}
-
-private:
- void doUnlink() const
- {
- impl_unlink(m_datastore, m_sourceNode);
- }
};
}
void YangAccess::moveItem(const std::string& source, std::variant<yang::move::Absolute, yang::move::Relative> move)
{
- auto set = lyWrap(lyd_find_path(m_datastore.get(), source.c_str()));
- if (!set) { // Error, the node probably doesn't exist in the schema
- getErrorsAndThrow();
+ if (!m_datastore) {
+ throw DatastoreException{{DatastoreError{"Datastore is empty.", source}}};
}
- if (set->number == 0) {
- return;
+
+ auto sourceNode = m_datastore->findPath(source.c_str());
+
+ if (!sourceNode) {
+ // The datastore doesn't contain the wanted node.
+ throw DatastoreException{{DatastoreError{"Data node doesn't exist.", source}}};
}
- auto sourceNode = set->set.d[0];
- std::visit(impl_moveItem{m_datastore, sourceNode}, move);
+ std::visit(impl_moveItem{m_datastore, *sourceNode}, move);
}
void YangAccess::commitChanges()
@@ -227,16 +197,24 @@
[[noreturn]] DatastoreAccess::Tree YangAccess::execute(const std::string& path, const Tree& input)
{
- auto root = lyWrap(lyd_new_path(nullptr, m_ctx.get(), path.c_str(), nullptr, LYD_ANYDATA_CONSTSTRING, 0));
- if (!root) {
- getErrorsAndThrow();
- }
+ auto root = [&path, this] {
+ try {
+ return m_ctx.newPath(path.c_str());
+ } catch (libyang::ErrorWithCode& err) {
+ getErrorsAndThrow();
+ }
+ }();
+
for (const auto& [k, v] : input) {
if (v.type() == typeid(special_) && boost::get<special_>(v).m_value != SpecialValue::PresenceContainer) {
continue;
}
- lyd_new_path(root.get(), m_ctx.get(), k.c_str(), (void*)leafDataToString(v).c_str(), LYD_ANYDATA_CONSTSTRING, LYD_PATH_OPT_UPDATE);
+ try {
+ root.newPath(k.c_str(), leafDataToString(v).c_str(), libyang::CreationOptions::Update);
+ } catch (libyang::ErrorWithCode& err) {
+ getErrorsAndThrow();
+ }
}
throw std::logic_error("in-memory datastore doesn't support executing RPC/action");
}
@@ -244,7 +222,7 @@
void YangAccess::copyConfig(const Datastore source, const Datastore dest)
{
if (source == Datastore::Startup && dest == Datastore::Running) {
- m_datastore = nullptr;
+ m_datastore = std::nullopt;
}
}
@@ -260,16 +238,14 @@
return res;
}
- auto instances = lyWrap(lyd_find_path(m_datastore.get(), path.c_str()));
- auto instancesWrapper = libyang::Set(instances.get(), nullptr);
- for (const auto& list : instancesWrapper.data()) {
+ auto instances = m_datastore->findXPath(path.c_str());
+ for (const auto& list : instances) {
ListInstance instance;
- for (const auto& child : list->child()->tree_for()) {
- if (child->schema()->nodetype() == LYS_LEAF) {
- libyang::Schema_Node_Leaf leafSchema(child->schema());
- if (leafSchema.is_key()) {
- auto leafData = std::make_shared<libyang::Data_Node_Leaf_List>(child);
- instance.insert({leafSchema.name(), leafValueFromNode(leafData)});
+ for (const auto& child : list.child()->siblings()) {
+ if (child.schema().nodeType() == libyang::NodeType::Leaf) {
+ auto leafSchema(child.schema().asLeaf());
+ if (leafSchema.isKey()) {
+ instance.insert({std::string{leafSchema.name()}, leafValueFromNode(child.asTerm())});
}
}
}
@@ -280,16 +256,16 @@
std::string YangAccess::dump(const DataFormat format) const
{
- char* output;
- lyd_print_mem(&output, m_datastore.get(), format == DataFormat::Xml ? LYD_XML : LYD_JSON, LYP_WITHSIBLINGS | LYP_FORMAT);
- std::unique_ptr<char, decltype(&free)> deleter{output, free};
-
- if (output) {
- std::string res = output;
- return res;
+ if (!m_datastore) {
+ return "";
}
- return "";
+ auto str = m_datastore->firstSibling().printStr(format == DataFormat::Xml ? libyang::DataFormat::XML : libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings);
+ if (!str) {
+ return "";
+ }
+
+ return std::string{*str};
}
void YangAccess::loadModule(const std::string& name)
@@ -307,9 +283,9 @@
m_schema->addSchemaDirectory(path.c_str());
}
-void YangAccess::enableFeature(const std::string& module, const std::string& feature)
+void YangAccess::setEnabledFeatures(const std::string& module, const std::vector<std::string>& features)
{
- m_schema->enableFeature(module, feature);
+ m_schema->setEnabledFeatures(module, features);
}
void YangAccess::addDataFile(const std::string& path, const StrictDataParsing strict)
@@ -320,20 +296,16 @@
std::cout << "Parsing \"" << path << "\" as " << (firstChar == '{' ? "JSON" : "XML") << "...\n";
- auto parseFlags = LYD_OPT_DATA | LYD_OPT_DATA_NO_YANGLIB | LYD_OPT_TRUSTED;
- if (strict == StrictDataParsing::Yes) {
- parseFlags |= LYD_OPT_STRICT;
- }
- auto dataNode = lyd_parse_path(m_ctx.get(), path.c_str(), firstChar == '{' ? LYD_JSON : LYD_XML, parseFlags);
-
- if (!dataNode) {
- throw std::runtime_error("Supplied data file " + path + " couldn't be parsed.");
- }
+ auto dataNode = m_ctx.parseDataPath(
+ path.c_str(),
+ firstChar == '{' ? libyang::DataFormat::JSON : libyang::DataFormat::XML,
+ strict == StrictDataParsing::Yes ? std::optional{libyang::ParseOptions::Strict} : std::nullopt,
+ libyang::ValidationOptions::Present);
if (!m_datastore) {
- m_datastore = lyWrap(dataNode);
+ m_datastore = dataNode;
} else {
- lyd_merge(m_datastore.get(), dataNode, LYD_OPT_DESTRUCT);
+ m_datastore->merge(*dataNode);
}
validate();
diff --git a/src/yang_access.hpp b/src/yang_access.hpp
index 64538f0..aab5d81 100644
--- a/src/yang_access.hpp
+++ b/src/yang_access.hpp
@@ -7,6 +7,7 @@
#pragma once
+#include <libyang-cpp/Context.hpp>
#include "datastore_access.hpp"
/*! \class YangAccess
@@ -40,7 +41,7 @@
std::shared_ptr<Schema> schema() override;
- void enableFeature(const std::string& module, const std::string& feature);
+ void setEnabledFeatures(const std::string& module, const std::vector<std::string>& features);
[[nodiscard]] std::string dump(const DataFormat format) const override;
void loadModule(const std::string& name);
@@ -57,8 +58,7 @@
void impl_removeNode(const std::string& path);
void validate();
- std::unique_ptr<ly_ctx, void (*)(ly_ctx*)> m_ctx;
- std::unique_ptr<lyd_node, void (*)(lyd_node*)> m_datastore;
+ libyang::Context m_ctx;
+ std::optional<libyang::DataNode> m_datastore;
std::shared_ptr<YangSchema> m_schema;
- const int m_validation_mode;
};
diff --git a/src/yang_schema.cpp b/src/yang_schema.cpp
index b964d31..f96aa46 100644
--- a/src/yang_schema.cpp
+++ b/src/yang_schema.cpp
@@ -6,9 +6,8 @@
*
*/
-#include <libyang/Libyang.hpp>
-#include <libyang/Tree_Data.hpp>
-#include <libyang/Tree_Schema.hpp>
+#include <libyang-cpp/Enum.hpp>
+#include <libyang-cpp/Utils.hpp>
#include <string_view>
#include "UniqueResource.hpp"
#include "utils.hpp"
@@ -33,11 +32,11 @@
};
YangSchema::YangSchema()
- : m_context(std::make_shared<libyang::Context>(nullptr, LY_CTX_DISABLE_SEARCHDIR_CWD))
+ : m_context(nullptr, libyang::ContextOptions::DisableSearchDirs | libyang::ContextOptions::SetPrivParsed)
{
}
-YangSchema::YangSchema(std::shared_ptr<libyang::Context> lyCtx)
+YangSchema::YangSchema(libyang::Context lyCtx)
: m_context(lyCtx)
{
}
@@ -46,29 +45,22 @@
void YangSchema::addSchemaString(const char* schema)
{
- if (!m_context->parse_module_mem(schema, LYS_IN_YANG)) {
- throw YangLoadError("Couldn't load schema");
- }
+ m_context.parseModuleMem(schema, libyang::SchemaFormat::YANG);
}
void YangSchema::addSchemaDirectory(const char* directoryName)
{
- if (m_context->set_searchdir(directoryName)) {
- throw YangLoadError("Couldn't add schema search directory");
- }
+ m_context.setSearchDir(directoryName);
}
void YangSchema::addSchemaFile(const char* filename)
{
- if (!m_context->parse_module_path(filename, LYS_IN_YANG)) {
- throw YangLoadError("Couldn't load schema");
- }
+ m_context.parseModulePath(filename, libyang::SchemaFormat::YANG);
}
bool YangSchema::isModule(const std::string& name) const
{
- const auto set = modules();
- return set.find(name) != set.end();
+ return m_context.getModuleImplemented(name.c_str()).has_value();
}
bool YangSchema::listHasKey(const schemaPath_& listPath, const std::string& key) const
@@ -80,49 +72,52 @@
bool YangSchema::leafIsKey(const std::string& leafPath) const
{
auto node = getSchemaNode(leafPath);
- if (!node || node->nodetype() != LYS_LEAF) {
+ if (!node || node->nodeType() != libyang::NodeType::Leaf) {
return false;
}
- return libyang::Schema_Node_Leaf{node}.is_key().get();
+ return node->asLeaf().isKey();
}
-libyang::S_Schema_Node YangSchema::impl_getSchemaNode(const std::string& node) const
+std::optional<libyang::SchemaNode> YangSchema::impl_getSchemaNode(const std::string& node) const
{
- // If no node is found find_path prints an error message, so we have to
- // disable logging
- // https://github.com/CESNET/libyang/issues/753
- {
- int oldOptions;
- auto logBlocker = make_unique_resource(
- [&oldOptions]() {
- oldOptions = libyang::set_log_options(0);
- },
- [&oldOptions]() {
- libyang::set_log_options(oldOptions);
- });
- auto res = m_context->get_node(nullptr, node.c_str());
- if (!res) { // If no node is found, try output rpc nodes too.
- res = m_context->get_node(nullptr, node.c_str(), 1);
+ // libyang::Context::findPath throws an exception, when no matching schema node is found. This exception has the
+ // ValidationFailure error code. We will catch that exception (and rethrow if it's not the correct error code.
+ //
+ // Also, we need to use findPath twice if we're trying to find output nodes.
+ try {
+ return m_context.findPath(node.c_str());
+ } catch (libyang::ErrorWithCode& err) {
+ if (err.code() != libyang::ErrorCode::ValidationFailure) {
+ throw;
}
- return res;
}
+ try {
+ return m_context.findPath(node.c_str(), libyang::OutputNodes::Yes);
+ } catch (libyang::ErrorWithCode& err) {
+ if (err.code() != libyang::ErrorCode::ValidationFailure) {
+ throw;
+ }
+ }
+
+ // We didn't find a matching node.
+ return std::nullopt;
}
-libyang::S_Schema_Node YangSchema::getSchemaNode(const std::string& node) const
+std::optional<libyang::SchemaNode> YangSchema::getSchemaNode(const std::string& node) const
{
return impl_getSchemaNode(node);
}
-libyang::S_Schema_Node YangSchema::getSchemaNode(const schemaPath_& location, const ModuleNodePair& node) const
+std::optional<libyang::SchemaNode> YangSchema::getSchemaNode(const schemaPath_& location, const ModuleNodePair& node) const
{
std::string absPath = joinPaths(pathToSchemaString(location, Prefixes::Always), fullNodeName(location, node));
return impl_getSchemaNode(absPath);
}
-libyang::S_Schema_Node YangSchema::getSchemaNode(const schemaPath_& listPath) const
+std::optional<libyang::SchemaNode> YangSchema::getSchemaNode(const schemaPath_& listPath) const
{
std::string absPath = pathToSchemaString(listPath, Prefixes::Always);
return impl_getSchemaNode(absPath);
@@ -131,174 +126,121 @@
const std::set<std::string> YangSchema::listKeys(const schemaPath_& listPath) const
{
auto node = getSchemaNode(listPath);
- if (node->nodetype() != LYS_LIST) {
+ if (node->nodeType() != libyang::NodeType::List) {
return {};
}
- auto list = std::make_shared<libyang::Schema_Node_List>(node);
std::set<std::string> keys;
- const auto& keysVec = list->keys();
+ auto keysVec = node->asList().keys();
- std::transform(keysVec.begin(), keysVec.end(), std::inserter(keys, keys.begin()), [](const auto& it) { return it->name(); });
+ std::transform(keysVec.begin(), keysVec.end(), std::inserter(keys, keys.begin()), [](const auto& it) { return std::string{it.name()}; });
return keys;
}
-namespace {
-enum class ResolveMode {
- Enum,
- Identity
-};
-/** @brief Resolves a typedef to a type which defines values.
- * 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
- * type which defines values. These values are the allowed values.
- * Example:
- *
- * typedef MyOtherEnum {
- * type enumeration {
- * enum "A";
- * enum "B";
- * }
- * }
- *
- * typedef MyEnum {
- * type MyOtherEnum;
- * }
- *
- * If `toResolve` points to MyEnum, then just doing ->enums()->enm() returns nothing and that means that this particular
- * typedef (MyEnum) did not say which values are allowed. So, we need to dive into the parent enum (MyOtherEnum) with
- * ->der()->type(). This typedef (MyOtherEnum) DID specify allowed values and enums()->enm() WILL contain them. These
- * values are the only relevant values and we don't care about other parent typedefs. We return these values to the
- * caller.
- *
- * For enums, this function simply returns all allowed enums.
- * For identities, this function returns which bases `toResolve` has.
- */
-template <ResolveMode TYPE>
-auto resolveTypedef(const libyang::S_Type& toResolve)
+std::set<enum_> enumValues(const libyang::Type& type)
{
- auto type = toResolve;
- auto getValuesFromType = [] (const libyang::S_Type& type) {
- if constexpr (TYPE == ResolveMode::Identity) {
- return type->info()->ident()->ref();
- } else {
- return type->info()->enums()->enm();
- }
- };
- auto values = getValuesFromType(type);
- while (values.empty()) {
- type = type->der()->type();
- values = getValuesFromType(type);
- }
-
- return values;
-}
-
-std::set<enum_> enumValues(const libyang::S_Type& type)
-{
- auto values = resolveTypedef<ResolveMode::Enum>(type);
-
- std::vector<libyang::S_Type_Enum> enabled;
- std::copy_if(values.begin(), values.end(), std::back_inserter(enabled), [](const libyang::S_Type_Enum& it) {
- auto iffeatures = it->iffeature();
- return std::all_of(iffeatures.begin(), iffeatures.end(), [](auto it) { return it->value(); });
- });
-
+ auto enums = type.asEnum().items();
std::set<enum_> enumSet;
- std::transform(enabled.begin(), enabled.end(), std::inserter(enumSet, enumSet.end()), [](auto it) { return enum_{it->name()}; });
+ std::transform(enums.begin(), enums.end(), std::inserter(enumSet, enumSet.end()), [](auto it) { return enum_{std::string{it.name}}; });
return enumSet;
}
-std::set<identityRef_> validIdentities(const libyang::S_Type& type)
+std::set<identityRef_> validIdentities(const libyang::Type& type)
{
std::set<identityRef_> identSet;
- for (auto base : resolveTypedef<ResolveMode::Identity>(type)) { // Iterate over all bases
- // Iterate over derived identities (this is recursive!)
- if (auto der = base->der()) {
- for (auto derived : der->schema()) {
- identSet.emplace(derived->module()->name(), derived->name());
- }
+ std::function<void(const std::vector<libyang::Identity>&)> impl = [&identSet, &impl] (const std::vector<libyang::Identity>& idents) {
+ if (idents.empty()) {
+ return;
}
+
+ for (const auto& ident : idents) {
+ identSet.emplace(std::string{ident.module().name()}, std::string{ident.name()});
+ impl(ident.derived());
+ }
+ };
+
+ for (const auto& base : type.asIdentityRef().bases()) {
+ impl(base.derived());
}
return identSet;
}
-std::string leafrefPath(const libyang::S_Type& type)
+std::string leafrefPath(const libyang::Type& type)
{
- return type->info()->lref()->target()->path(LYS_PATH_FIRST_PREFIX);
-}
+ return std::string{type.asLeafRef().path()};
}
template <typename NodeType>
-yang::TypeInfo YangSchema::impl_leafType(const libyang::S_Schema_Node& node) const
+yang::TypeInfo YangSchema::impl_leafType(const NodeType& node) const
{
using namespace std::string_literals;
auto leaf = std::make_shared<NodeType>(node);
auto leafUnits = leaf->units();
- std::function<yang::TypeInfo(std::shared_ptr<libyang::Type>)> resolveType;
- resolveType = [this, &resolveType, leaf, leafUnits](std::shared_ptr<libyang::Type> type) -> yang::TypeInfo {
+ std::function<yang::TypeInfo(const libyang::Type&)> resolveType;
+ resolveType = [&resolveType, leaf, leafUnits](const libyang::Type& type) -> yang::TypeInfo {
yang::LeafDataType resType;
- switch (type->base()) {
- case LY_TYPE_STRING:
+ switch (type.base()) {
+ case libyang::LeafBaseType::String:
resType.emplace<yang::String>();
break;
- case LY_TYPE_DEC64:
+ case libyang::LeafBaseType::Dec64:
resType.emplace<yang::Decimal>();
break;
- case LY_TYPE_BOOL:
+ case libyang::LeafBaseType::Bool:
resType.emplace<yang::Bool>();
break;
- case LY_TYPE_INT8:
+ case libyang::LeafBaseType::Int8:
resType.emplace<yang::Int8>();
break;
- case LY_TYPE_INT16:
+ case libyang::LeafBaseType::Int16:
resType.emplace<yang::Int16>();
break;
- case LY_TYPE_INT32:
+ case libyang::LeafBaseType::Int32:
resType.emplace<yang::Int32>();
break;
- case LY_TYPE_INT64:
+ case libyang::LeafBaseType::Int64:
resType.emplace<yang::Int64>();
break;
- case LY_TYPE_UINT8:
+ case libyang::LeafBaseType::Uint8:
resType.emplace<yang::Uint8>();
break;
- case LY_TYPE_UINT16:
+ case libyang::LeafBaseType::Uint16:
resType.emplace<yang::Uint16>();
break;
- case LY_TYPE_UINT32:
+ case libyang::LeafBaseType::Uint32:
resType.emplace<yang::Uint32>();
break;
- case LY_TYPE_UINT64:
+ case libyang::LeafBaseType::Uint64:
resType.emplace<yang::Uint64>();
break;
- case LY_TYPE_BINARY:
+ case libyang::LeafBaseType::Binary:
resType.emplace<yang::Binary>();
break;
- case LY_TYPE_EMPTY:
+ case libyang::LeafBaseType::Empty:
resType.emplace<yang::Empty>();
break;
- case LY_TYPE_ENUM:
+ case libyang::LeafBaseType::Enum:
resType.emplace<yang::Enum>(enumValues(type));
break;
- case LY_TYPE_IDENT:
+ case libyang::LeafBaseType::IdentityRef:
resType.emplace<yang::IdentityRef>(validIdentities(type));
break;
- case LY_TYPE_LEAFREF:
- resType.emplace<yang::LeafRef>(::leafrefPath(type), std::make_unique<yang::TypeInfo>(leafType(::leafrefPath(type))));
+ case libyang::LeafBaseType::Leafref:
+ resType.emplace<yang::LeafRef>(::leafrefPath(type), std::make_unique<yang::TypeInfo>(resolveType(type.asLeafRef().resolvedType())));
break;
- case LY_TYPE_BITS: {
+ case libyang::LeafBaseType::Bits: {
auto resBits = yang::Bits{};
- for (const auto& bit : type->info()->bits()->bit()) {
- resBits.m_allowedValues.emplace(bit->name());
+ for (const auto& bit : type.asBits().items()) {
+ resBits.m_allowedValues.emplace(std::string{bit.name});
}
resType.emplace<yang::Bits>(std::move(resBits));
break;
}
- case LY_TYPE_UNION: {
+ case libyang::LeafBaseType::Union: {
auto resUnion = yang::Union{};
- for (auto unionType : type->info()->uni()->types()) {
+ for (auto unionType : type.asUnion().types()) {
resUnion.m_unionTypes.emplace_back(resolveType(unionType));
}
resType.emplace<yang::Union>(std::move(resUnion));
@@ -306,48 +248,26 @@
}
default:
using namespace std::string_literals;
- throw UnsupportedYangTypeException("the type of "s + leaf->name() + " is not supported: " + std::to_string(leaf->type()->base()));
+ throw UnsupportedYangTypeException("the type of "s +
+ std::string{leaf->name()} +
+ " is not supported: " +
+ std::to_string(std::underlying_type_t<libyang::LeafBaseType>(leaf->valueType().base())));
}
- std::optional<std::string> resUnits;
- if (leafUnits) {
- resUnits = leafUnits;
- } else {
- for (auto parentTypedef = type->der(); parentTypedef; parentTypedef = parentTypedef->type()->der()) {
- auto units = parentTypedef->units();
- if (units) {
- resUnits = units;
- break;
- }
- }
- }
-
- std::optional<std::string> resDescription;
-
- // checking for parentTypedef->type()->der() means I'm going to enter inside base types like "string". These
- // also have a description, but it isn't too helpful ("human-readable string")
- for (auto parentTypedef = type->der(); parentTypedef && parentTypedef->type()->der(); parentTypedef = parentTypedef->type()->der()) {
- auto dsc = parentTypedef->dsc();
- if (dsc) {
- resDescription = dsc;
- break;
- }
- }
-
- return yang::TypeInfo(resType, resUnits, resDescription);
+ return yang::TypeInfo(resType, std::optional<std::string>{leafUnits}, std::optional<std::string>{type.description()});
};
- return resolveType(leaf->type());
+ return resolveType(leaf->valueType());
}
yang::TypeInfo YangSchema::leafType(const schemaPath_& location, const ModuleNodePair& node) const
{
auto lyNode = getSchemaNode(location, node);
- switch (lyNode->nodetype()) {
- case LYS_LEAF:
- return impl_leafType<libyang::Schema_Node_Leaf>(lyNode);
- case LYS_LEAFLIST:
- return impl_leafType<libyang::Schema_Node_Leaflist>(lyNode);
+ switch (lyNode->nodeType()) {
+ case libyang::NodeType::Leaf:
+ return impl_leafType(lyNode->asLeaf());
+ case libyang::NodeType::Leaflist:
+ return impl_leafType(lyNode->asLeafList());
default:
throw std::logic_error("YangSchema::leafType: type must be leaf or leaflist");
}
@@ -356,11 +276,11 @@
yang::TypeInfo YangSchema::leafType(const std::string& path) const
{
auto lyNode = getSchemaNode(path);
- switch (lyNode->nodetype()) {
- case LYS_LEAF:
- return impl_leafType<libyang::Schema_Node_Leaf>(lyNode);
- case LYS_LEAFLIST:
- return impl_leafType<libyang::Schema_Node_Leaflist>(lyNode);
+ switch (lyNode->nodeType()) {
+ case libyang::NodeType::Leaf:
+ return impl_leafType(lyNode->asLeaf());
+ case libyang::NodeType::Leaflist:
+ return impl_leafType(lyNode->asLeafList());
default:
throw std::logic_error("YangSchema::leafType: type must be leaf or leaflist");
}
@@ -368,23 +288,22 @@
std::optional<std::string> YangSchema::leafTypeName(const std::string& path) const
{
- libyang::Schema_Node_Leaf leaf(getSchemaNode(path));
- return leaf.type()->der().get() && leaf.type()->der()->type()->der().get() ? std::optional{leaf.type()->der()->name()} : std::nullopt;
+ auto leaf = getSchemaNode(path)->asLeaf();
+ return std::string{leaf.valueType().name()};
}
std::string YangSchema::leafrefPath(const std::string& leafrefPath) const
{
using namespace std::string_literals;
- libyang::Schema_Node_Leaf leaf(getSchemaNode(leafrefPath));
- return leaf.type()->info()->lref()->target()->path(LYS_PATH_FIRST_PREFIX);
+ return ::leafrefPath(getSchemaNode(leafrefPath)->asLeaf().valueType());
}
std::set<std::string> YangSchema::modules() const
{
- const auto& modules = m_context->get_module_iter();
+ const auto& modules = m_context.modules();
std::set<std::string> res;
- std::transform(modules.begin(), modules.end(), std::inserter(res, res.end()), [](const auto module) { return module->name(); });
+ std::transform(modules.begin(), modules.end(), std::inserter(res, res.end()), [](const auto module) { return std::string{module.name()}; });
return res;
}
@@ -392,39 +311,49 @@
{
using namespace std::string_view_literals;
std::set<ModuleNodePair> res;
- std::vector<libyang::S_Schema_Node> nodes;
+ std::vector<libyang::ChildInstanstiables> nodeCollections;
std::string topLevelModule;
if (path.type() == typeid(module_)) {
- nodes = m_context->get_module(boost::get<module_>(path).m_name.c_str())->data_instantiables(0);
+ nodeCollections.emplace_back(m_context.getModule(boost::get<module_>(path).m_name.c_str())->childInstantiables());
} else {
auto schemaPath = anyPathToSchemaPath(path);
if (schemaPath.m_nodes.empty()) {
- nodes = m_context->data_instantiables(0);
+ for (const auto& module : m_context.modules()) {
+ if (module.implemented()) {
+ nodeCollections.emplace_back(module.childInstantiables());
+ }
+ }
} else {
const auto pathString = pathToSchemaString(schemaPath, Prefixes::Always);
const auto node = getSchemaNode(pathString);
- nodes = node->child_instantiables(0);
+ nodeCollections.emplace_back(node->childInstantiables());
topLevelModule = schemaPath.m_nodes.begin()->m_prefix->m_name;
}
}
- for (const auto& node : nodes) {
- if (node->module()->name() == "ietf-yang-library"sv) {
- continue;
- }
+ for (const auto& coll : nodeCollections) {
+ for (const auto& node : coll) {
+ if (node.module().name() == "ietf-yang-library"sv) {
+ continue;
+ }
- if (recursion == Recursion::Recursive) {
- for (auto it : node->tree_dfs()) {
- res.insert(ModuleNodePair(boost::none, it->path(LYS_PATH_FIRST_PREFIX)));
+ if (node.module().name() == "ietf-yang-schema-mount"sv) {
+ continue;
}
- } else {
- ModuleNodePair toInsert;
- if (topLevelModule.empty() || topLevelModule != node->module()->name()) {
- toInsert.first = node->module()->type() == 0 ? node->module()->name() : libyang::Submodule(node->module()).belongsto()->name();
+
+ if (recursion == Recursion::Recursive) {
+ for (auto it : node.childrenDfs()) {
+ res.insert(ModuleNodePair(boost::none, it.path()));
+ }
+ } else {
+ ModuleNodePair toInsert;
+ if (topLevelModule.empty() || topLevelModule != node.module().name()) {
+ toInsert.first = std::string{node.module().name()};
+ }
+ toInsert.second = node.name();
+ res.insert(toInsert);
}
- toInsert.second = node->name();
- res.insert(toInsert);
}
}
@@ -433,71 +362,81 @@
void YangSchema::loadModule(const std::string& moduleName)
{
- m_context->load_module(moduleName.c_str());
+ m_context.loadModule(moduleName.c_str());
}
-void YangSchema::enableFeature(const std::string& moduleName, const std::string& featureName)
+void YangSchema::setEnabledFeatures(const std::string& moduleName, const std::vector<std::string>& features)
{
using namespace std::string_literals;
auto module = getYangModule(moduleName);
if (!module) {
throw std::runtime_error("Module \""s + moduleName + "\" doesn't exist.");
}
- if (module->feature_enable(featureName.c_str())) {
- throw std::runtime_error("Can't enable feature \""s + featureName + "\" for module \"" + moduleName + "\".");
+ try {
+ module->setImplemented(features);
+ } catch (libyang::ErrorWithCode&) {
+ throw std::runtime_error("Can't enable features for module \"" + moduleName + "\".");
}
}
void YangSchema::registerModuleCallback(const std::function<std::string(const char*, const char*, const char*, const char*)>& clb)
{
- auto lambda = [clb](const char* mod_name, const char* mod_revision, const char* submod_name, const char* submod_revision) {
+ auto lambda = [clb](const char* mod_name, const char* mod_revision, const char* submod_name, const char* submod_revision) -> std::optional<libyang::ModuleInfo> {
(void)submod_revision;
auto moduleSource = clb(mod_name, mod_revision, submod_name, submod_revision);
if (moduleSource.empty()) {
- return libyang::Context::mod_missing_cb_return{LYS_IN_YANG, nullptr};
+ return std::nullopt;
}
- return libyang::Context::mod_missing_cb_return{LYS_IN_YANG, strdup(moduleSource.c_str())};
+ return libyang::ModuleInfo {
+ .data = moduleSource.c_str(),
+ .format = libyang::SchemaFormat::YANG
+
+ };
};
- m_context->add_missing_module_callback(lambda, free);
+ m_context.registerModuleCallback(lambda);
}
-std::shared_ptr<libyang::Data_Node> YangSchema::dataNodeFromPath(const std::string& path, const std::optional<const std::string> value) const
+libyang::CreatedNodes YangSchema::dataNodeFromPath(const std::string& path, const std::optional<const std::string> value) const
{
- return std::make_shared<libyang::Data_Node>(m_context,
- path.c_str(),
- value ? value.value().c_str() : nullptr,
- LYD_ANYDATA_CONSTSTRING,
- LYD_PATH_OPT_EDIT);
+ auto options = [this, &path, &value] {
+ // If we're creating a node without a value and it's not the "empty" type, then we also need the Opaque flag.
+ auto schema = getSchemaNode(path);
+ if (schema->nodeType() == libyang::NodeType::Leaf &&
+ schema->asLeaf().valueType().base() != libyang::LeafBaseType::Empty &&
+ !value) {
+ return std::optional<libyang::CreationOptions>{libyang::CreationOptions::Opaque};
+ }
+
+ return std::optional<libyang::CreationOptions>{};
+ }();
+ return m_context.newPath2(path.c_str(), value ? value->c_str() : nullptr, options);
}
-std::shared_ptr<libyang::Module> YangSchema::getYangModule(const std::string& name)
+std::optional<libyang::Module> YangSchema::getYangModule(const std::string& name)
{
- return m_context->get_module(name.c_str());
+ return m_context.getModuleImplemented(name.c_str());
}
namespace {
-yang::NodeTypes impl_nodeType(const libyang::S_Schema_Node& node)
+yang::NodeTypes impl_nodeType(const libyang::SchemaNode& node)
{
- if (!node) {
- throw InvalidNodeException();
- }
- switch (node->nodetype()) {
- case LYS_CONTAINER:
- return libyang::Schema_Node_Container{node}.presence() ? yang::NodeTypes::PresenceContainer : yang::NodeTypes::Container;
- case LYS_LEAF:
+ switch (node.nodeType()) {
+ case libyang::NodeType::Container:
+ return node.asContainer().isPresence() ? yang::NodeTypes::PresenceContainer : yang::NodeTypes::Container;
+ case libyang::NodeType::Leaf:
return yang::NodeTypes::Leaf;
- case LYS_LIST:
+ case libyang::NodeType::List:
return yang::NodeTypes::List;
- case LYS_RPC:
+ case libyang::NodeType::RPC:
return yang::NodeTypes::Rpc;
- case LYS_ACTION:
+ case libyang::NodeType::Action:
return yang::NodeTypes::Action;
- case LYS_NOTIF:
+ case libyang::NodeType::Notification:
return yang::NodeTypes::Notification;
- case LYS_ANYXML:
+ case libyang::NodeType::AnyXML:
return yang::NodeTypes::AnyXml;
- case LYS_LEAFLIST:
+ case libyang::NodeType::Leaflist:
return yang::NodeTypes::LeafList;
default:
throw InvalidNodeException(); // FIXME: Implement all types.
@@ -507,56 +446,57 @@
yang::NodeTypes YangSchema::nodeType(const schemaPath_& location, const ModuleNodePair& node) const
{
- return impl_nodeType(getSchemaNode(location, node));
+ return impl_nodeType(*getSchemaNode(location, node));
}
yang::NodeTypes YangSchema::nodeType(const std::string& path) const
{
- return impl_nodeType(getSchemaNode(path));
+ return impl_nodeType(*getSchemaNode(path));
}
std::optional<std::string> YangSchema::description(const std::string& path) const
{
- auto node = getSchemaNode(path.c_str());
- return node->dsc() ? std::optional{node->dsc()} : std::nullopt;
+ auto desc = getSchemaNode(path.c_str())->description();
+ return desc ? std::optional<std::string>{desc} : std::nullopt;
+
}
yang::Status YangSchema::status(const std::string& location) const
{
auto node = getSchemaNode(location.c_str());
- if (node->flags() & LYS_STATUS_DEPRC) {
+ switch (node->status()) {
+ case libyang::Status::Deprecated:
return yang::Status::Deprecated;
- } else if (node->flags() & LYS_STATUS_OBSLT) {
+ case libyang::Status::Obsolete:
return yang::Status::Obsolete;
- } else {
+ case libyang::Status::Current:
return yang::Status::Current;
}
+
+ __builtin_unreachable();
}
bool YangSchema::hasInputNodes(const std::string& path) const
{
auto node = getSchemaNode(path.c_str());
- if (auto type = node->nodetype(); type != LYS_ACTION && type != LYS_RPC) {
+ if (auto type = node->nodeType(); type != libyang::NodeType::Action && type != libyang::NodeType::RPC) {
throw std::logic_error("StaticSchema::hasInputNodes called with non-RPC/action path");
}
// The first child gives the /input node and then I check whether it has a child.
- return node->child()->child().get();
+ return node->child()->child().has_value();
}
bool YangSchema::isConfig(const std::string& path) const
{
auto node = getSchemaNode(path.c_str());
- if (node->flags() & LYS_CONFIG_W) {
- return true;
- }
-
- // Node can still be an input node.
- while (node->parent()) {
- node = node->parent();
- if (node->nodetype() == LYS_INPUT) {
+ try {
+ if (node->config() == libyang::Config::True) {
return true;
}
+ } catch (libyang::Error&) {
+ // For non-data nodes (like `rpc`), the config value can't be retrieved. In this case, we'll just default to
+ // "false".
}
return false;
@@ -564,22 +504,10 @@
std::optional<std::string> YangSchema::defaultValue(const std::string& leafPath) const
{
- libyang::Schema_Node_Leaf leaf(getSchemaNode(leafPath));
-
- if (auto leafDefault = leaf.dflt()) {
- return leafDefault;
- }
-
- for (auto type = leaf.type()->der(); type != nullptr; type = type->type()->der()) {
- if (auto defaultValue = type->dflt()) {
- return defaultValue;
- }
- }
-
- return std::nullopt;
+ return std::optional<std::string>{getSchemaNode(leafPath)->asLeaf().defaultValueStr()};
}
std::string YangSchema::dataPathToSchemaPath(const std::string& path)
{
- return getSchemaNode(path)->path(LYS_PATH_FIRST_PREFIX);
+ return std::string{getSchemaNode(path)->path()};
}
diff --git a/src/yang_schema.hpp b/src/yang_schema.hpp
index dc45097..82d9acf 100644
--- a/src/yang_schema.hpp
+++ b/src/yang_schema.hpp
@@ -9,26 +9,19 @@
#pragma once
#include <functional>
+#include <libyang-cpp/Context.hpp>
#include <optional>
#include <set>
#include "ast_path.hpp"
#include "schema.hpp"
-namespace libyang {
-class Context;
-class Schema_Node;
-class Schema_Node_Leaf;
-class Data_Node;
-class Module;
-}
-
/*! \class YangSchema
* \brief A schema class, which uses libyang for queries.
* */
class YangSchema : public Schema {
public:
YangSchema();
- YangSchema(std::shared_ptr<libyang::Context> lyCtx);
+ YangSchema(libyang::Context lyCtx);
~YangSchema() override;
[[nodiscard]] yang::NodeTypes nodeType(const std::string& path) const override;
@@ -54,8 +47,8 @@
/** @short Loads a module called moduleName. */
void loadModule(const std::string& moduleName);
- /** @short Enables a feature called featureName on a module called moduleName. */
- void enableFeature(const std::string& moduleName, const std::string& featureName);
+ /** @short Sets enabled features. */
+ void setEnabledFeatures(const std::string& moduleName, const std::vector<std::string>& features);
/** @short Adds a new module passed as a YANG string. */
void addSchemaString(const char* schema);
@@ -67,26 +60,26 @@
void addSchemaDirectory(const char* directoryName);
/** @short Creates a new data node from a path (to be used with NETCONF edit-config) */
- [[nodiscard]] std::shared_ptr<libyang::Data_Node> dataNodeFromPath(const std::string& path, const std::optional<const std::string> value = std::nullopt) const;
- std::shared_ptr<libyang::Module> getYangModule(const std::string& name);
+ [[nodiscard]] libyang::CreatedNodes dataNodeFromPath(const std::string& path, const std::optional<const std::string> value = std::nullopt) const;
+ std::optional<libyang::Module> getYangModule(const std::string& name);
[[nodiscard]] std::string dataPathToSchemaPath(const std::string& path);
private:
friend class YangAccess;
template <typename NodeType>
- [[nodiscard]] yang::TypeInfo impl_leafType(const std::shared_ptr<libyang::Schema_Node>& node) const;
+ [[nodiscard]] yang::TypeInfo impl_leafType(const NodeType& node) const;
[[nodiscard]] std::set<std::string> modules() const;
- /** @short Returns a single Schema_Node if the criteria matches only one, otherwise nullptr. */
- [[nodiscard]] std::shared_ptr<libyang::Schema_Node> getSchemaNode(const std::string& node) const;
- /** @short Returns a single Schema_Node if the criteria matches only one, otherwise nullptr. */
- [[nodiscard]] std::shared_ptr<libyang::Schema_Node> getSchemaNode(const schemaPath_& listPath) const;
+ /** @short Returns a single SchemaNode if the criteria matches only one, otherwise nullopt. */
+ [[nodiscard]] std::optional<libyang::SchemaNode> getSchemaNode(const std::string& node) const;
+ /** @short Returns a single Schema_Node if the criteria matches only one, otherwise nullopt. */
+ [[nodiscard]] std::optional<libyang::SchemaNode> getSchemaNode(const schemaPath_& listPath) const;
/** @short Returns a single Schema_Node if the criteria matches only one, otherwise nullptr. */
- [[nodiscard]] std::shared_ptr<libyang::Schema_Node> getSchemaNode(const schemaPath_& location, const ModuleNodePair& node) const;
- std::shared_ptr<libyang::Context> m_context;
+ [[nodiscard]] std::optional<libyang::SchemaNode> getSchemaNode(const schemaPath_& location, const ModuleNodePair& node) const;
+ libyang::Context m_context;
- [[nodiscard]] std::shared_ptr<libyang::Schema_Node> impl_getSchemaNode(const std::string& node) const;
+ [[nodiscard]] std::optional<libyang::SchemaNode> impl_getSchemaNode(const std::string& node) const;
};