Fix working with libyang parsed info
Before now, yang_schema expected that parsed info was available,
however, in most cases it was not:
For YangAccess, we directly manage the context, so we'll just use the
flag needed.
For NetconfAccess, we need to create our custom context and supply it.
This context is treated by libnetconf2 as shared and it won't try to
free it. We will hold a reference to this context via a new member
variable inside NetconfAccess so that it stays alive for the whole
netconf client session.
sysrepo does not support this flag as of now. Simply patching sysrepo to
always use this flag doesn't work (it gives internal errors), so it
needs an upstream patch.
Change-Id: Ie7e4567a779a09daa7b3a8b3923e73a3dfd6ba1d
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 573e582..e8db570 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -173,7 +173,7 @@
src/netconf_access.cpp
)
-target_link_libraries(netconfaccess PUBLIC datastoreaccess yangschema ast_values utils PkgConfig::LIBNETCONF2 PRIVATE PkgConfig::LIBYANG-CPP)
+target_link_libraries(netconfaccess PUBLIC datastoreaccess yangschema ast_values utils PkgConfig::LIBNETCONF2 PkgConfig::LIBYANG-CPP)
add_library(yangaccess STATIC
src/yang_access.cpp
diff --git a/src/netconf-client.cpp b/src/netconf-client.cpp
index 0685db6..7171c01 100644
--- a/src/netconf-client.cpp
+++ b/src/netconf-client.cpp
@@ -212,7 +212,7 @@
::nc_session_free(m_session, nullptr);
}
-std::unique_ptr<Session> Session::connectPubkey(const std::string& host, const uint16_t port, const std::string& user, const std::string& pubPath, const std::string& privPath)
+std::unique_ptr<Session> Session::connectPubkey(const std::string& host, const uint16_t port, const std::string& user, const std::string& pubPath, const std::string& privPath, std::optional<libyang::Context> ctx)
{
impl::ClientInit::instance();
@@ -223,14 +223,14 @@
nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PUBLICKEY, 5);
nc_client_ssh_add_keypair(pubPath.c_str(), privPath.c_str());
}
- auto session = std::make_unique<Session>(nc_connect_ssh(host.c_str(), port, nullptr));
+ auto session = std::make_unique<Session>(nc_connect_ssh(host.c_str(), port, ctx ? libyang::retrieveContext(*ctx) : nullptr));
if (!session->m_session) {
throw std::runtime_error{"nc_connect_ssh failed"};
}
return session;
}
-std::unique_ptr<Session> Session::connectKbdInteractive(const std::string& host, const uint16_t port, const std::string& user, const KbdInteractiveCb& callback)
+std::unique_ptr<Session> Session::connectKbdInteractive(const std::string& host, const uint16_t port, const std::string& user, const KbdInteractiveCb& callback, std::optional<libyang::Context> ctx)
{
impl::ClientInit::instance();
@@ -244,29 +244,29 @@
nc_client_ssh_set_username(nullptr);
});
- auto session = std::make_unique<Session>(nc_connect_ssh(host.c_str(), port, nullptr));
+ auto session = std::make_unique<Session>(nc_connect_ssh(host.c_str(), port, ctx ? libyang::retrieveContext(*ctx) : nullptr));
if (!session->m_session) {
throw std::runtime_error{"nc_connect_ssh failed"};
}
return session;
}
-std::unique_ptr<Session> Session::connectFd(const int source, const int sink)
+std::unique_ptr<Session> Session::connectFd(const int source, const int sink, std::optional<libyang::Context> ctx)
{
impl::ClientInit::instance();
- auto session = std::make_unique<Session>(nc_connect_inout(source, sink, nullptr));
+ auto session = std::make_unique<Session>(nc_connect_inout(source, sink, ctx ? libyang::retrieveContext(*ctx) : nullptr));
if (!session->m_session) {
throw std::runtime_error{"nc_connect_inout failed"};
}
return session;
}
-std::unique_ptr<Session> Session::connectSocket(const std::string& path)
+std::unique_ptr<Session> Session::connectSocket(const std::string& path, std::optional<libyang::Context> ctx)
{
impl::ClientInit::instance();
- auto session = std::make_unique<Session>(nc_connect_unix(path.c_str(), nullptr));
+ auto session = std::make_unique<Session>(nc_connect_unix(path.c_str(), ctx ? libyang::retrieveContext(*ctx) : nullptr));
if (!session->m_session) {
throw std::runtime_error{"nc_connect_unix failed"};
}
diff --git a/src/netconf-client.hpp b/src/netconf-client.hpp
index 5a16cf2..719a21d 100644
--- a/src/netconf-client.hpp
+++ b/src/netconf-client.hpp
@@ -3,6 +3,7 @@
#include <functional>
#include <libnetconf2/log.h>
#include <libnetconf2/messages_client.h>
+#include <libyang-cpp/Context.hpp>
#include <memory>
#include <optional>
#include <string>
@@ -10,6 +11,7 @@
#include <vector>
struct nc_session;
+struct ly_ctx;
namespace libyang {
class Context;
@@ -41,10 +43,10 @@
public:
Session(struct nc_session* session);
~Session();
- static std::unique_ptr<Session> connectPubkey(const std::string& host, const uint16_t port, const std::string& user, const std::string& pubPath, const std::string& privPath);
- static std::unique_ptr<Session> connectKbdInteractive(const std::string& host, const uint16_t port, const std::string& user, const KbdInteractiveCb& callback);
- static std::unique_ptr<Session> connectSocket(const std::string& path);
- static std::unique_ptr<Session> connectFd(const int source, const int sink);
+ static std::unique_ptr<Session> connectPubkey(const std::string& host, const uint16_t port, const std::string& user, const std::string& pubPath, const std::string& privPath, std::optional<libyang::Context> ctx = std::nullopt);
+ static std::unique_ptr<Session> connectKbdInteractive(const std::string& host, const uint16_t port, const std::string& user, const KbdInteractiveCb& callback, std::optional<libyang::Context> ctx = std::nullopt);
+ static std::unique_ptr<Session> connectSocket(const std::string& path, std::optional<libyang::Context> ctx = std::nullopt);
+ static std::unique_ptr<Session> connectFd(const int source, const int sink, std::optional<libyang::Context> ctx = std::nullopt);
[[nodiscard]] std::vector<std::string_view> capabilities() const;
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);
diff --git a/src/netconf_access.cpp b/src/netconf_access.cpp
index 2b76e50..0c25cc8 100644
--- a/src/netconf_access.cpp
+++ b/src/netconf_access.cpp
@@ -59,15 +59,17 @@
}
NetconfAccess::NetconfAccess(const std::string& hostname, uint16_t port, const std::string& user, const std::string& pubKey, const std::string& privKey)
- : m_session(libnetconf::client::Session::connectPubkey(hostname, port, user, pubKey, privKey))
- , m_schema(std::make_shared<YangSchema>(m_session->libyangContext()))
+ : m_context(nullptr, libyang::ContextOptions::SetPrivParsed)
+ , m_session(libnetconf::client::Session::connectPubkey(hostname, port, user, pubKey, privKey, m_context))
+ , m_schema(std::make_shared<YangSchema>(m_context))
{
checkNMDA();
}
NetconfAccess::NetconfAccess(const int source, const int sink)
- : m_session(libnetconf::client::Session::connectFd(source, sink))
- , m_schema(std::make_shared<YangSchema>(m_session->libyangContext()))
+ : m_context(nullptr, libyang::ContextOptions::SetPrivParsed)
+ , m_session(libnetconf::client::Session::connectFd(source, sink, m_context))
+ , m_schema(std::make_shared<YangSchema>(m_context))
{
checkNMDA();
}
@@ -80,8 +82,9 @@
}
NetconfAccess::NetconfAccess(const std::string& socketPath)
- : m_session(libnetconf::client::Session::connectSocket(socketPath))
- , m_schema(std::make_shared<YangSchema>(m_session->libyangContext()))
+ : m_context(nullptr, libyang::ContextOptions::SetPrivParsed)
+ , m_session(libnetconf::client::Session::connectSocket(socketPath, m_context))
+ , m_schema(std::make_shared<YangSchema>(m_context))
{
checkNMDA();
}
diff --git a/src/netconf_access.hpp b/src/netconf_access.hpp
index eb678d5..1534dba 100644
--- a/src/netconf_access.hpp
+++ b/src/netconf_access.hpp
@@ -8,6 +8,7 @@
#pragma once
#include <libnetconf2/log.h>
+#include <libyang-cpp/Context.hpp>
#include <string>
#include "datastore_access.hpp"
@@ -59,6 +60,7 @@
bool m_serverHasNMDA;
+ libyang::Context m_context;
std::unique_ptr<libnetconf::client::Session> m_session;
std::shared_ptr<YangSchema> m_schema;
};
diff --git a/src/yang_access.cpp b/src/yang_access.cpp
index 72e38e7..7fd9010 100644
--- a/src/yang_access.cpp
+++ b/src/yang_access.cpp
@@ -16,7 +16,7 @@
}
YangAccess::YangAccess()
- : m_ctx(nullptr, libyang::ContextOptions::DisableSearchCwd)
+ : m_ctx(nullptr, libyang::ContextOptions::DisableSearchCwd | libyang::ContextOptions::SetPrivParsed)
, m_datastore(std::nullopt)
, m_schema(std::make_shared<YangSchema>(m_ctx))
{
diff --git a/src/yang_schema.cpp b/src/yang_schema.cpp
index f96aa46..1c2df80 100644
--- a/src/yang_schema.cpp
+++ b/src/yang_schema.cpp
@@ -253,9 +253,15 @@
" is not supported: " +
std::to_string(std::underlying_type_t<libyang::LeafBaseType>(leaf->valueType().base())));
}
+ std::optional<std::string_view> typeDesc;
+ try {
+ typeDesc = type.description();
+ } catch (libyang::ParsedInfoUnavailable&) {
+ // libyang context doesn't have the parsed info.
+ }
- return yang::TypeInfo(resType, std::optional<std::string>{leafUnits}, std::optional<std::string>{type.description()});
+ return yang::TypeInfo(resType, std::optional<std::string>{leafUnits}, std::optional<std::string>{typeDesc});
};
return resolveType(leaf->valueType());
}
@@ -289,7 +295,11 @@
std::optional<std::string> YangSchema::leafTypeName(const std::string& path) const
{
auto leaf = getSchemaNode(path)->asLeaf();
- return std::string{leaf.valueType().name()};
+ try {
+ return std::string{leaf.valueType().name()};
+ } catch (libyang::ParsedInfoUnavailable&) {
+ return std::nullopt;
+ }
}
std::string YangSchema::leafrefPath(const std::string& leafrefPath) const
diff --git a/tests/datastore_access.cpp b/tests/datastore_access.cpp
index 8a7a9f5..57871c0 100644
--- a/tests/datastore_access.cpp
+++ b/tests/datastore_access.cpp
@@ -1114,6 +1114,19 @@
catching<OnExec>([&] { REQUIRE(proxyDatastore.execute() == output); });
}
+ SECTION("parsed info in yang context")
+ {
+ auto schema = datastore->schema();
+ auto leafTypeName = schema->leafTypeName("/example-schema:typedefedLeaf");
+
+#if defined(sysrepo_BACKEND)
+ // Sysrepo is not available yet, with libyang parsed info context
+ REQUIRE(leafTypeName == std::nullopt);
+#else
+ REQUIRE(leafTypeName == "myType");
+#endif
+ }
+
waitForCompletionAndBitMore(seq1);
}