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/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();