]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#3931] Update NETCONF dependencies to v3
authorAndrei Pavel <andrei@isc.org>
Wed, 4 Jun 2025 09:35:43 +0000 (12:35 +0300)
committerAndrei Pavel <andrei@isc.org>
Tue, 12 Aug 2025 09:19:35 +0000 (12:19 +0300)
doc/sphinx/arm/ext-netconf.rst
hammer.py
src/bin/netconf/tests/netconf_unittests.cc
src/lib/yang/tests/translator_option_data_unittests.cc
src/lib/yang/tests/translator_shared_network_unittests.cc
src/lib/yang/tests/translator_unittests.cc
src/lib/yang/testutils/translator_test.cc

index c4963768163f124b0e2c25d2b657b981fed2b1f1..29c634a205f870dd5ba2b37848295cddcbb388ce 100644 (file)
@@ -22,21 +22,10 @@ Installing NETCONF
 To get its NETCONF capabilities, Kea requires the v2 versions of libyang and
 Sysrepo. The specific versions that have been thoroughly tested with Kea are:
 
-* libyang v2.1.4
-* sysrepo v2.2.12
-* libyang-cpp v1.1.0 (ae7d649ea75da081725c119dd553b2ef3121a6f8)
-* sysrepo-cpp v1.1.0 (02634174ffc60568301c3d9b9b7cf710cff6a586)
-
-.. note::
-
-    For users who are unable to upgrade to one of the versions of libyang
-    and Sysrepo listed above, these are the oldest versions known to work
-    reliably with current Kea releases:
-
-    * libyang v2.0.256 (56d4e07ef1cdeab3eb2e6700247f83ec9148edcc)
-    * sysrepo v2.1.84
-    * libyang-cpp v1.1.0 (7824d9a862f2dc1d8ad4f6a90ab6cee9200f7c81)
-    * sysrepo-cpp v1.1.0 (e66b2f0c53a428eeb743d355cf86fb30e8e491f1)
+* libyang v3.12.2 (da7272e19d9e27d1bfdd68108fa9dce25fbdf5e8)
+* sysrepo v3.6.11 (b2d60c137aa5179af2af0ce1243d4147c4e5f974)
+* libyang-cpp v3 (f3cd4e05462a16e81d6bfd0c4a5b385cf88a8549)
+* sysrepo-cpp v3 (fe4edfa3998fdf312099ee1fb08a06983b6907f6)
 
 .. note::
 
index 926e6587d47f1687e221dca39017c69f0094c855..f223e2b2b07d0b95031519585ab4f677c83ea4c1 100755 (executable)
--- a/hammer.py
+++ b/hammer.py
@@ -7,6 +7,7 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # pylint: disable=broad-exception-caught
+# pylint: disable=logging-fstring-interpolation
 
 """Hammer - Kea development environment management tool."""
 
@@ -1211,23 +1212,25 @@ def _install_gtest_sources():
 
 def _install_libyang_from_sources(ignore_errors=False):
     """Install libyang from sources."""
-    for prefix in ['/usr', '/usr/local']:
-        libyang_so_candidates = [f'{prefix}/lib/libyang.so', f'{prefix}/lib64/libyang.so']
-        libyang_header = f'{prefix}/include/libyang/version.h'
-        if (any(os.path.exists(i) for i in libyang_so_candidates) and os.path.exists(libyang_header) and
-                execute(f"grep -F '#define LY_VERSION_MAJOR 2' '{libyang_header}'", raise_error=False) == 0):
-            log.info('libyang is already installed at %s.', libyang_header)
-            return
-
-    version = 'v2.1.4'
+    version = '3.12.2'
+
+    libdirs = [f'{usr}/{lib}' for usr in ['/usr', '/usr/local'] for lib in ['lib', 'lib64']]
+    for libdir in libdirs:
+        pc_file = f'{libdir}/pkgconfig/libyang.pc'
+        if os.path.exists(pc_file):
+            with open(pc_file, encoding='utf-8') as file:
+                for line in file:
+                    if line.rstrip('\n') == f'Version: {version}':
+                        log.info(f'libyang is already installed: {pc_file}.')
+                        return
 
     execute('rm -rf ~/.hammer-tmp')
     execute('mkdir -p ~/.hammer-tmp')
     try:
         execute('git clone https://github.com/CESNET/libyang.git ~/.hammer-tmp/libyang')
-        execute(f'git checkout {version}', cwd='~/.hammer-tmp/libyang')
+        execute(f'git checkout v{version}', cwd='~/.hammer-tmp/libyang')
         execute('mkdir ~/.hammer-tmp/libyang/build')
-        execute('cmake -DBUILD_TESTING=OFF -DCMAKE_C_FLAGS="-Wno-incompatible-pointer-types" ..',
+        execute('cmake -DBUILD_TESTING=OFF ..',
                 cwd='~/.hammer-tmp/libyang/build')
         execute('make -j $(nproc || gnproc || echo 1)', cwd='~/.hammer-tmp/libyang/build')
         execute('sudo make install', cwd='~/.hammer-tmp/libyang/build')
@@ -1244,15 +1247,17 @@ def _install_libyang_from_sources(ignore_errors=False):
 
 def _install_sysrepo_from_sources(ignore_errors=False):
     """Install sysrepo from sources."""
-    for prefix in ['/usr', '/usr/local']:
-        sysrepo_so_candidates = [f'{prefix}/lib/libsysrepo.so', f'{prefix}/lib64/libsysrepo.so']
-        sysrepo_header = f'{prefix}/include/sysrepo/version.h'
-        if (any(os.path.exists(i) for i in sysrepo_so_candidates) and os.path.exists(sysrepo_header) and
-                execute(f"grep -F '#define SR_VERSION_MAJOR 7' '{sysrepo_header}'", raise_error=False) == 0):
-            log.info('sysrepo is already installed at %s.', sysrepo_header)
-            return
-
-    version = 'v2.2.12'
+    version = '3.6.11'
+
+    libdirs = [f'{usr}/{lib}' for usr in ['/usr', '/usr/local'] for lib in ['lib', 'lib64']]
+    for libdir in libdirs:
+        pc_file = f'{libdir}/pkgconfig/sysrepo.pc'
+        if os.path.exists(pc_file):
+            with open(pc_file, encoding='utf-8') as file:
+                for line in file:
+                    if line.rstrip('\n') == f'Version: {version}':
+                        log.info(f'sysrepo is already installed: {pc_file}.')
+                        return
 
     # Create repository for YANG modules and change ownership to current user.
     execute('sudo mkdir -p /etc/sysrepo')
@@ -1262,7 +1267,7 @@ def _install_sysrepo_from_sources(ignore_errors=False):
     execute('mkdir -p ~/.hammer-tmp')
     try:
         execute('git clone https://github.com/sysrepo/sysrepo.git ~/.hammer-tmp/sysrepo')
-        execute(f'git checkout {version}', cwd='~/.hammer-tmp/sysrepo')
+        execute(f'git checkout v{version}', cwd='~/.hammer-tmp/sysrepo')
         execute('mkdir ~/.hammer-tmp/sysrepo/build')
         execute('cmake -DBUILD_TESTING=OFF -DREPO_PATH=/etc/sysrepo ..', cwd='~/.hammer-tmp/sysrepo/build')
         execute('make -j $(nproc || gnproc || echo 1)', cwd='~/.hammer-tmp/sysrepo/build')
@@ -1280,33 +1285,23 @@ def _install_sysrepo_from_sources(ignore_errors=False):
 
 def _install_libyang_cpp_from_sources(ignore_errors=False):
     """Install libyang-cpp from sources."""
-    for prefix_lib in ['/usr/lib', '/usr/lib64', '/usr/local/lib', '/usr/local/lib64']:
-        libyang_cpp_so = f'{prefix_lib}/libyang-cpp.so'
-        libyang_cpp_pc = f'{prefix_lib}/pkgconfig/libyang-cpp.pc'
-        if (os.path.exists(libyang_cpp_so) and os.path.exists(libyang_cpp_pc) and
-                execute(f"grep -F 'Version: 1.1.0' '{libyang_cpp_pc}'", raise_error=False) == 0):
-            log.info('libyang-cpp is already installed at %s.', libyang_cpp_so)
-            return
-
-    version = 'ae7d649ea75da081725c119dd553b2ef3121a6f8'
+    version = '3'
+
+    libdirs = [f'{usr}/{lib}' for usr in ['/usr', '/usr/local'] for lib in ['lib', 'lib64']]
+    for libdir in libdirs:
+        pc_file = f'{libdir}/pkgconfig/libyang-cpp.pc'
+        if os.path.exists(pc_file):
+            with open(pc_file, encoding='utf-8') as file:
+                for line in file:
+                    if line.rstrip('\n') == f'Version: {version}':
+                        log.info(f'libyang-cpp is already installed: {pc_file}.')
+                        return
 
     execute('rm -rf ~/.hammer-tmp')
     execute('mkdir -p ~/.hammer-tmp')
     try:
         execute('git clone https://github.com/CESNET/libyang-cpp.git ~/.hammer-tmp/libyang-cpp')
-        execute(f'git checkout {version}', cwd='~/.hammer-tmp/libyang-cpp')
-        # New cpp compiler is more picky about missing headers. (ex. Fedora 40)
-        execute("""git apply <<EOF
-diff --git a/src/Context.cpp b/src/Context.cpp
-index b2fe887..add11cc 100644
---- a/src/Context.cpp
-+++ b/src/Context.cpp
-@@ -13,2 +13,3 @@
- #include <libyang/libyang.h>
-+#include <algorithm>
- #include <span>
-EOF
-""", cwd='~/.hammer-tmp/libyang-cpp')
+        execute(f'git checkout v{version}', cwd='~/.hammer-tmp/libyang-cpp')
         execute('mkdir ~/.hammer-tmp/libyang-cpp/build')
         execute('cmake -DBUILD_TESTING=OFF .. ', cwd='~/.hammer-tmp/libyang-cpp/build')
         execute('make -j $(nproc || gnproc || echo 1)', cwd='~/.hammer-tmp/libyang-cpp/build')
@@ -1324,21 +1319,23 @@ EOF
 
 def _install_sysrepo_cpp_from_sources(ignore_errors=False):
     """Install sysrepo-cpp from sources."""
-    for prefix_lib in ['/usr/lib', '/usr/lib64', '/usr/local/lib', '/usr/local/lib64']:
-        sysrepo_cpp_so = f'{prefix_lib}/libsysrepo-cpp.so'
-        sysrepo_cpp_pc = f'{prefix_lib}/pkgconfig/sysrepo-cpp.pc'
-        if (os.path.exists(sysrepo_cpp_so) and os.path.exists(sysrepo_cpp_pc) and
-                execute(f"grep -F 'Version: 1.1.0' '{sysrepo_cpp_pc}'", raise_error=False) == 0):
-            log.info('sysrepo-cpp is already installed at %s.', sysrepo_cpp_so)
-            return
-
-    version = '02634174ffc60568301c3d9b9b7cf710cff6a586'
+    version = '3'
+
+    libdirs = [f'{usr}/{lib}' for usr in ['/usr', '/usr/local'] for lib in ['lib', 'lib64']]
+    for libdir in libdirs:
+        pc_file = f'{libdir}/pkgconfig/sysrepo-cpp.pc'
+        if os.path.exists(pc_file):
+            with open(pc_file, encoding='utf-8') as file:
+                for line in file:
+                    if line.rstrip('\n') == f'Version: {version}':
+                        log.info(f'sysrepo-cpp is already installed: {pc_file}.')
+                        return
 
     execute('rm -rf ~/.hammer-tmp')
     execute('mkdir -p ~/.hammer-tmp')
     try:
         execute('git clone https://github.com/sysrepo/sysrepo-cpp.git ~/.hammer-tmp/sysrepo-cpp')
-        execute(f'git checkout {version}', cwd='~/.hammer-tmp/sysrepo-cpp')
+        execute(f'git checkout v{version}', cwd='~/.hammer-tmp/sysrepo-cpp')
         execute('mkdir ~/.hammer-tmp/sysrepo-cpp/build')
         execute('cmake -DBUILD_TESTING=OFF .. ', cwd='~/.hammer-tmp/sysrepo-cpp/build')
         execute('make -j $(nproc || gnproc || echo 1)', cwd='~/.hammer-tmp/sysrepo-cpp/build')
index 23da4c91b7e816ea24c0f833bdc3f248d3ac4664..822cb7b0f816115a8cef4a23fc23941ffc65c555 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2018-2024 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2018-2025 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -1164,7 +1164,7 @@ TEST_F(NetconfAgentTest, noValidate) {
           "BOGUS", LeafBaseType::String, true }
     });
     EXPECT_THROW_MSG(repr.set(tree1, *agent_->running_sess_), sysrepo::Error,
-                     "Session::applyChanges: Couldn't apply changes: SR_ERR_CALLBACK_FAILED");
+                     "Session::applyChanges: Couldn't apply changes: SR_ERR_VALIDATION_FAILED\n Validation failed (SR_ERR_VALIDATION_FAILED)");
 }
 
 }  // namespace
index 96a827f88b4b0165a89589a321894dce2daac2e4..ca4fea26231164296521611d212ee2cb7ecdfdc8 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2018-2024 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2018-2025 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -183,15 +183,18 @@ TEST_F(TranslatorOptionDataListTestv4, set) {
     EXPECT_THROW_MSG(sess_->setItem(xoption + "/code", s_code), sysrepo::Error,
                      "Session::setItem: Couldn't set "
                      "'/kea-dhcp4-server:config/option-data[code='100'][space='dns'][data='12121212']/code' to "
-                     "'15': SR_ERR_INVAL_ARG");
+                     "'15': SR_ERR_INVAL_ARG\n Editing list key \"code\" is not supported, edit list instances "
+                     "instead. (SR_ERR_INVAL_ARG)");
     EXPECT_THROW_MSG(sess_->setItem(xoption + "/space", s_space), sysrepo::Error,
                      "Session::setItem: Couldn't set "
                      "'/kea-dhcp4-server:config/option-data[code='100'][space='dns'][data='12121212']/space' to "
-                     "'dhcp4': SR_ERR_INVAL_ARG");
+                     "'dhcp4': SR_ERR_INVAL_ARG\n Editing list key \"space\" is not supported, edit list instances "
+                     "instead. (SR_ERR_INVAL_ARG)");
     EXPECT_THROW_MSG(sess_->setItem(xoption + "/data", s_data), sysrepo::Error,
                      "Session::setItem: Couldn't set "
                      "'/kea-dhcp4-server:config/option-data[code='100'][space='dns'][data='12121212']/data' to "
-                     "'12121212': SR_ERR_INVAL_ARG");
+                     "'12121212': SR_ERR_INVAL_ARG\n Editing list key \"data\" is not supported, edit list "
+                     "instances instead. (SR_ERR_INVAL_ARG)");
 
     // Setting the list element directly should work.
     EXPECT_NO_THROW_LOG(sess_->setItem(xoption, std::nullopt));
@@ -228,15 +231,18 @@ TEST_F(TranslatorOptionDataListTestv6, set) {
     EXPECT_THROW_MSG(sess_->setItem(xoption + "/code", s_code), sysrepo::Error,
                      "Session::setItem: Couldn't set "
                      "'/kea-dhcp6-server:config/option-data[code='100'][space='dns'][data='12121212']/code' to "
-                     "'15': SR_ERR_INVAL_ARG");
+                     "'15': SR_ERR_INVAL_ARG\n Editing list key \"code\" is not supported, edit list instances "
+                     "instead. (SR_ERR_INVAL_ARG)");
     EXPECT_THROW_MSG(sess_->setItem(xoption + "/space", s_space), sysrepo::Error,
                      "Session::setItem: Couldn't set "
                      "'/kea-dhcp6-server:config/option-data[code='100'][space='dns'][data='12121212']/space' to "
-                     "'dhcp6': SR_ERR_INVAL_ARG");
+                     "'dhcp6': SR_ERR_INVAL_ARG\n Editing list key \"space\" is not supported, edit list instances "
+                     "instead. (SR_ERR_INVAL_ARG)");
     EXPECT_THROW_MSG(sess_->setItem(xoption + "/data", s_data), sysrepo::Error,
                      "Session::setItem: Couldn't set "
                      "'/kea-dhcp6-server:config/option-data[code='100'][space='dns'][data='12121212']/data' to "
-                     "'12121212': SR_ERR_INVAL_ARG");
+                     "'12121212': SR_ERR_INVAL_ARG\n Editing list key \"data\" is not supported, edit list instances "
+                     "instead. (SR_ERR_INVAL_ARG)");
 
     // Setting the list element directly should work.
     EXPECT_NO_THROW_LOG(sess_->setItem(xoption, std::nullopt));
index e8977132ee8d6f14cb426b992f1f7f8a92748908..36ceadbb4dd95c6c1d6fb834ae882bd45f8b43c6 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2018-2022 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2018-2025 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -155,8 +155,9 @@ TEST_F(TranslatorSharedNetworksTestKeaV6, getList) {
         "\"subnet\": \"2001:db8:101::/48\" }, "
         "{ \"id\": 102, \"subnet\": \"2001:db8:102::/48\" } ] }";
 
+    // Since v3, lists seem to be ordered by keys. Shouldn't be too big of a problem.
     const string exp_both =
-        "[ " + exp_net1 + ", " + exp_net2 + " ]";
+        "[ " + exp_net2 + ", " + exp_net1 + " ]";
 
     // Create the subnet1: 2001:db8:1::/48 #1 in shared network foo.
     const string& xsubnet1 = xnetwork1 + "/subnet6[id='1']/subnet";
index 96e0d2d05e1ac5726da775fd1428b2651dde347f..7bb4e9bab371369a3b9dce7898f682093db02a5b 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2018-2024 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2018-2025 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -344,13 +344,17 @@ TEST_F(TranslatorTest, getItem) {
 
     // Not existing.
     xpath = "/keatest-module:main/no_such_node";
-    EXPECT_NO_THROW_LOG(element = translator->getItemFromAbsoluteXpath(xpath));
+    EXPECT_THROW_MSG(element = translator->getItemFromAbsoluteXpath(xpath), NetconfError,
+                     "getting item at '/keatest-module:main/no_such_node': Session::getData: "
+                     "Couldn't get '/keatest-module:main/no_such_node': SR_ERR_NOT_FOUND");
     EXPECT_FALSE(element);
     element.reset();
 
     // Check error.
     xpath = "null";
-    EXPECT_NO_THROW_LOG(element = translator->getItemFromAbsoluteXpath(xpath));
+    EXPECT_THROW_MSG(element = translator->getItemFromAbsoluteXpath(xpath), NetconfError,
+                     "getting item at 'null': Session::getData: Couldn't get 'null': "
+                     "SR_ERR_NOT_FOUND");
     EXPECT_FALSE(element);
 }
 
index 36012db557f09cb05009d123020dddb6f14637a6..92b2bf2a64b99581da56009fea0ce238610ef8cd 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2018-2022 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2018-2025 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -27,7 +27,7 @@ YangRepr::YangReprItem::getUnionType(Value const& value) {
     static_assert(
         std::is_same<
             Value, std::variant<int8_t, int16_t, int32_t, int64_t, uint8_t, uint16_t, uint32_t,
-                                uint64_t, bool, Empty, Binary, string, optional<DataNode>,
+                                uint64_t, bool, Empty, Binary, string, InstanceIdentifier,
                                 Decimal64, vector<Bit>, Enum, IdentityRef>>::value,
         "Value type has changed. The if statement needs to be adjusted to include all alternatives "
         "of the std::variant.");
@@ -56,7 +56,7 @@ YangRepr::YangReprItem::getUnionType(Value const& value) {
         return LeafBaseType::Binary;
     } else if (holds_alternative<string>(value)) {
         return LeafBaseType::String;
-    } else if (holds_alternative<optional<DataNode>>(value)) {
+    } else if (holds_alternative<InstanceIdentifier>(value)) {
         return LeafBaseType::InstanceIdentifier;
     } else if (holds_alternative<Decimal64>(value)) {
         return LeafBaseType::Dec64;