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/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};
}