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