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