From 4f3ae3b9a4bd46d22738540501f32dc881a80556 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Walstr=C3=B6m?= Date: Tue, 6 Feb 2024 09:11:21 +0100 Subject: [PATCH] Add new package: Rousette - a RESTConf server --- board/common/rootfs/etc/pam.d/rousette | 20 ++ buildroot | 2 +- package/Config.in | 5 + package/confd/confd.mk | 7 +- package/libyang-cpp/Config.in | 6 + package/libyang-cpp/libyang-cpp.hash | 3 + package/libyang-cpp/libyang-cpp.mk | 14 + package/nghttp2-asio/Config.in | 7 + package/nghttp2-asio/LICENSE | 23 ++ package/nghttp2-asio/nghttp2-asio.hash | 4 + package/nghttp2-asio/nghttp2-asio.mk | 17 + package/rousette/Config.in | 16 + package/rousette/rousette.hash | 3 + package/rousette/rousette.mk | 22 ++ package/sysrepo-cpp/Config.in | 7 + package/sysrepo-cpp/sysrepo-cpp.hash | 3 + package/sysrepo-cpp/sysrepo-cpp.mk | 15 + .../UPSTREAM-port-to-libyang3.patch | 300 ++++++++++++++++++ .../rousette/0001-Make-systemd-optional.patch | 135 ++++++++ src/confd/scripts/setup.sh | 3 +- src/confd/yang/ietf-restconf@2017-01-26.yang | 278 ++++++++++++++++ 21 files changed, 887 insertions(+), 3 deletions(-) create mode 100644 board/common/rootfs/etc/pam.d/rousette create mode 100644 package/libyang-cpp/Config.in create mode 100644 package/libyang-cpp/libyang-cpp.hash create mode 100644 package/libyang-cpp/libyang-cpp.mk create mode 100644 package/nghttp2-asio/Config.in create mode 100644 package/nghttp2-asio/LICENSE create mode 100644 package/nghttp2-asio/nghttp2-asio.hash create mode 100644 package/nghttp2-asio/nghttp2-asio.mk create mode 100644 package/rousette/Config.in create mode 100644 package/rousette/rousette.hash create mode 100644 package/rousette/rousette.mk create mode 100644 package/sysrepo-cpp/Config.in create mode 100644 package/sysrepo-cpp/sysrepo-cpp.hash create mode 100644 package/sysrepo-cpp/sysrepo-cpp.mk create mode 100644 patches/libyang-cpp/UPSTREAM-port-to-libyang3.patch create mode 100644 patches/rousette/0001-Make-systemd-optional.patch create mode 100644 src/confd/yang/ietf-restconf@2017-01-26.yang diff --git a/board/common/rootfs/etc/pam.d/rousette b/board/common/rootfs/etc/pam.d/rousette new file mode 100644 index 00000000..ca894fe8 --- /dev/null +++ b/board/common/rootfs/etc/pam.d/rousette @@ -0,0 +1,20 @@ +#%PAM-1.0 +auth requisite pam_nologin.so +auth [success=1 default=ignore] pam_unix.so nullok +auth requisite pam_deny.so +auth required pam_permit.so + +account requisite pam_nologin.so +account [success=1 default=ignore] pam_unix.so +account requisite pam_deny.so +account required pam_permit.so + +password [success=1 default=ignore] pam_unix.so +password requisite pam_deny.so +password required pam_permit.so + +session required pam_loginuid.so +session include common-session +session optional pam_keyinit.so force revoke +session optional pam_lastlog.so showfailed +session optional pam_motd.so diff --git a/buildroot b/buildroot index 9dcd908b..9e67c4f9 160000 --- a/buildroot +++ b/buildroot @@ -1 +1 @@ -Subproject commit 9dcd908bd1f82f62a1d8c807d1701deb9c9afe23 +Subproject commit 9e67c4f9cb5eec9ea5d3bfcb3f5df008d57deda7 diff --git a/package/Config.in b/package/Config.in index cc21c0be..9b5dbd55 100644 --- a/package/Config.in +++ b/package/Config.in @@ -24,3 +24,8 @@ source "$BR2_EXTERNAL_INFIX_PATH/package/podman/Config.in" source "$BR2_EXTERNAL_INFIX_PATH/package/python-statd/Config.in" source "$BR2_EXTERNAL_INFIX_PATH/package/skeleton-init-finit/Config.in" source "$BR2_EXTERNAL_INFIX_PATH/package/tetris/Config.in" +source "$BR2_EXTERNAL_INFIX_PATH/package/libyang-cpp/Config.in" +source "$BR2_EXTERNAL_INFIX_PATH/package/sysrepo-cpp/Config.in" +source "$BR2_EXTERNAL_INFIX_PATH/package/rousette/Config.in" +source "$BR2_EXTERNAL_INFIX_PATH/package/nghttp2-asio/Config.in" + diff --git a/package/confd/confd.mk b/package/confd/confd.mk index 8b3c54cb..a76400bd 100644 --- a/package/confd/confd.mk +++ b/package/confd/confd.mk @@ -34,7 +34,12 @@ define CONFD_INSTALL_EXTRA cp $(CONFD_PKGDIR)/avahi.service $(TARGET_DIR)/etc/avahi/services/netconf.service endef define CONFD_INSTALL_YANG_MODULES - USE_CONTAINERS=$(BR2_PACKAGE_PODMAN) SYSREPO_SHM_PREFIX=$(CONFD_SYSREPO_SHM_PREFIX) SYSREPOCTL_EXECUTABLE="$(HOST_DIR)/bin/sysrepoctl" SYSREPOCFG_EXECUTABLE="$(HOST_DIR)/bin/sysrepocfg" SEARCH_PATH="$(TARGET_DIR)/usr/share/yang/modules/confd/" $(@D)/scripts/setup.sh + USE_CONTAINERS=$(BR2_PACKAGE_PODMAN) \ + SYSREPO_SHM_PREFIX=$(CONFD_SYSREPO_SHM_PREFIX) \ + SYSREPOCTL_EXECUTABLE="$(HOST_DIR)/bin/sysrepoctl" \ + SYSREPOCFG_EXECUTABLE="$(HOST_DIR)/bin/sysrepocfg" \ + SEARCH_PATH="$(TARGET_DIR)/usr/share/yang/modules/confd/" \ + $(@D)/scripts/setup.sh endef define CONFD_PERMISSIONS /etc/sysrepo/data/ r 660 root wheel - - - - - diff --git a/package/libyang-cpp/Config.in b/package/libyang-cpp/Config.in new file mode 100644 index 00000000..b3dd3558 --- /dev/null +++ b/package/libyang-cpp/Config.in @@ -0,0 +1,6 @@ +config BR2_PACKAGE_LIBYANG_CPP + bool "libyang-cpp" + depends on BR2_USE_MMU + select BR2_PACKAGE_LIBYANG + help + Libyang CPP bindings diff --git a/package/libyang-cpp/libyang-cpp.hash b/package/libyang-cpp/libyang-cpp.hash new file mode 100644 index 00000000..41dac1de --- /dev/null +++ b/package/libyang-cpp/libyang-cpp.hash @@ -0,0 +1,3 @@ +# Locally calculated +sha256 82e3758011ec44c78e98d0777799d6e12aec5b8a64b32ebb20d0fe50e32488bb LICENSE +sha256 fc5ae5218d441afef697d4fc7912ddbe322060103c053d50fc2db829bd359458 libyang-cpp-b852ea3b9a2729da364f2c4122db05d882df37f2-br1.tar.gz diff --git a/package/libyang-cpp/libyang-cpp.mk b/package/libyang-cpp/libyang-cpp.mk new file mode 100644 index 00000000..31ac25cf --- /dev/null +++ b/package/libyang-cpp/libyang-cpp.mk @@ -0,0 +1,14 @@ +################################################################################ +# +# CPP bindings for libyang +# +################################################################################ +LIBYANG_CPP_VERSION = b852ea3b9a2729da364f2c4122db05d882df37f2 +LIBYANG_CPP_SITE = https://github.com/kernelkit/libyang-cpp.git +LIBYANG_CPP_SITE_METHOD = git +LIBYANG_CPP_LICENSE = BSD-3-Clause +LIBYANG_CPP_LICENSE_FILES = LICENCE +LIBYANG_CPP_DEPENDENCIES = libyang +LIBYANG_CPP_INSTALL_STAGING = YES + +$(eval $(cmake-package)) diff --git a/package/nghttp2-asio/Config.in b/package/nghttp2-asio/Config.in new file mode 100644 index 00000000..5a393000 --- /dev/null +++ b/package/nghttp2-asio/Config.in @@ -0,0 +1,7 @@ +config BR2_PACKAGE_LIBNGHTTP2_ASIO + bool "libnghttp2-asio" + depends on BR2_USE_MMU + select BR2_PACKAGE_NGHTTP2 + select BR2_PACKAGE_BOOST + help + C++ bindings for nghttp2 diff --git a/package/nghttp2-asio/LICENSE b/package/nghttp2-asio/LICENSE new file mode 100644 index 00000000..80201792 --- /dev/null +++ b/package/nghttp2-asio/LICENSE @@ -0,0 +1,23 @@ +The MIT License + +Copyright (c) 2012, 2014, 2015, 2016 Tatsuhiro Tsujikawa +Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/package/nghttp2-asio/nghttp2-asio.hash b/package/nghttp2-asio/nghttp2-asio.hash new file mode 100644 index 00000000..a1562b6d --- /dev/null +++ b/package/nghttp2-asio/nghttp2-asio.hash @@ -0,0 +1,4 @@ +# Locally calculated +sha256 6b94f3abc1aabd0c72a7c7d92a77f79dda7c8a0cb3df839a97890b4116a2de2a LICENSE +sha256 6bd96a33f41f73d6ca524efa946b5e592227a5dd6dd21f193fadf4be3583552d nghttp2-asio-e877868abe06a83ed0a6ac6e245c07f6f20866b5-br1.tar.gz + diff --git a/package/nghttp2-asio/nghttp2-asio.mk b/package/nghttp2-asio/nghttp2-asio.mk new file mode 100644 index 00000000..129010d3 --- /dev/null +++ b/package/nghttp2-asio/nghttp2-asio.mk @@ -0,0 +1,17 @@ +################################################################################ +# +# nghttp2-asio +# +################################################################################ + +NGHTTP2_ASIO_VERSION = e877868abe06a83ed0a6ac6e245c07f6f20866b5 +NGHTTP2_ASIO_SITE = https://github.com/kernelkit/nghttp2-asio.git +NGHTTP2_ASIO_SITE_METHOD = git +NGHTTP2_ASIO_LICENSE = MIT +NGHTTP2_ASIO_LICENSE_FILES = LICENCE +NGHTTP2_ASIO_DEPENDENCIES = boost nghttp2 +NGHTTP2_ASIO_INSTALL_STAGING = YES +NGHTTP2_ASIO_AUTOGEN = YES +NGHTTP2_ASIO_AUTORECONF = YES + +$(eval $(cmake-package)) diff --git a/package/rousette/Config.in b/package/rousette/Config.in new file mode 100644 index 00000000..ebf5a6b5 --- /dev/null +++ b/package/rousette/Config.in @@ -0,0 +1,16 @@ +config BR2_PACKAGE_ROUSETTE + bool "rousette" + depends on BR2_USE_MMU + select BR2_PACKAGE_BOOST + select BR2_PACKAGE_BOOST_ATOMIC + select BR2_PACKAGE_BOOST_THREAD + select BR2_PACKAGE_BOOST_SYSTEM + select BR2_PACKAGE_DOCOPT_CPP + select BR2_PACKAGE_LIBYANG_CPP + select BR2_PACKAGE_NGHTTP2 + select BR2_PACKAGE_LIBNGHTTP2_ASIO + select BR2_PACKAGE_NGHTTP2_ASIO + select BR2_PACKAGE_SPDLOG + select BR2_PACKAGE_SYSREPO_CPP + help + RESTconf server diff --git a/package/rousette/rousette.hash b/package/rousette/rousette.hash new file mode 100644 index 00000000..7fda0937 --- /dev/null +++ b/package/rousette/rousette.hash @@ -0,0 +1,3 @@ +# Locally calculated +sha256 cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30 LICENSE +sha256 74b0d3115e73335399c5f8847fb7baa4d23497be75d12fccf56a754015af4c76 rousette-226a2410376e5befd0bf6dc180618a108b11b256-br1.tar.gz diff --git a/package/rousette/rousette.mk b/package/rousette/rousette.mk new file mode 100644 index 00000000..6359693c --- /dev/null +++ b/package/rousette/rousette.mk @@ -0,0 +1,22 @@ +################################################################################ +# +# Rousette RESTconf server +# +################################################################################ + +ROUSETTE_VERSION = 226a2410376e5befd0bf6dc180618a108b11b256 +ROUSETTE_SITE = https://github.com/kernelkit/rousette.git +ROUSETTE_SITE_METHOD = git +ROUSETTE_LICENSE = Apache-2.0 +ROUSETTE_LICENSE_FILES = LICENCE +ROUSETTE_DEPENDENCIES = boost docopt-cpp nghttp2-asio spdlog sysrepo-cpp linux-pam + +ROUSETTE_CONF_OPTS = \ + -DTESTAR=on \ + -DTHREADS_PTHREAD_ARG:STRING=-pthread + +define ROUSETTE_USERS + yangnobody 333666 yangnobody 333666 * - - - Unauthenticated operations via RESTCONF +endef +$(eval $(cmake-package)) + diff --git a/package/sysrepo-cpp/Config.in b/package/sysrepo-cpp/Config.in new file mode 100644 index 00000000..ff3519c4 --- /dev/null +++ b/package/sysrepo-cpp/Config.in @@ -0,0 +1,7 @@ +config BR2_PACKAGE_SYSREPO_CPP + bool "sysrepo-cpp" + depends on BR2_USE_MMU + select BR2_PACKAGE_LIBYANG_CPP + select BR2_PACKAGE_SYSREPO + help + Sysrepo CPP bindings diff --git a/package/sysrepo-cpp/sysrepo-cpp.hash b/package/sysrepo-cpp/sysrepo-cpp.hash new file mode 100644 index 00000000..35979bf6 --- /dev/null +++ b/package/sysrepo-cpp/sysrepo-cpp.hash @@ -0,0 +1,3 @@ +# Locally calculated +sha256 82e3758011ec44c78e98d0777799d6e12aec5b8a64b32ebb20d0fe50e32488bb LICENSE +sha256 74d7d89307c9fc188c7f23ae5128fe320a569f455a325286122eaa927a0d95ed sysrepo-cpp-99747f74e57a09c664251ea1f4e059e3f4f8e66a-br1.tar.gz diff --git a/package/sysrepo-cpp/sysrepo-cpp.mk b/package/sysrepo-cpp/sysrepo-cpp.mk new file mode 100644 index 00000000..b95ba106 --- /dev/null +++ b/package/sysrepo-cpp/sysrepo-cpp.mk @@ -0,0 +1,15 @@ +################################################################################ +# +# CPP bindings for sysrepo +# +################################################################################ + +SYSREPO_CPP_VERSION = 99747f74e57a09c664251ea1f4e059e3f4f8e66a +SYSREPO_CPP_SITE = git@github.com:sysrepo/sysrepo-cpp.git +SYSREPO_CPP_SITE_METHOD = git +SYSREPO_CPP_LICENSE = BSD-3-Clause +SYSREPO_CPP_LICENSE_FILES = LICENCE +SYSREPO_CPP_DEPENDENCIES = sysrepo libyang-cpp +SYSREPO_CPP_INSTALL_STAGING = YES + +$(eval $(cmake-package)) diff --git a/patches/libyang-cpp/UPSTREAM-port-to-libyang3.patch b/patches/libyang-cpp/UPSTREAM-port-to-libyang3.patch new file mode 100644 index 00000000..463a5e11 --- /dev/null +++ b/patches/libyang-cpp/UPSTREAM-port-to-libyang3.patch @@ -0,0 +1,300 @@ +From 0a09b00dab204bb5890978a17bb23c4c25280580 Mon Sep 17 00:00:00 2001 +From: Jan Kundrát +Date: Sun, 24 Mar 2024 20:41:25 +0100 +Subject: [PATCH] Port to libyang v3 + +Four parts, really: + +- some enum reshuffling when creating new nodes + +- a more capable XPath finding, but we have not been exporting any + "non-JSON XPaths", whatever that might be + +- system-ordered lists are now ordered alphabetically + +- a more capable error handling API + +Change-Id: Ic6de06f004c16e56cef93fae0136c07d903394c0 +--- + +diff --git a/.zuul.yaml b/.zuul.yaml +index d9d95e1..b41c490 100644 +--- a/.zuul.yaml ++++ b/.zuul.yaml +@@ -4,13 +4,13 @@ + - f38-gcc-cover: + required-projects: + - name: github/CESNET/libyang +- override-checkout: cesnet/2024-03--v2-latest ++ override-checkout: devel + - name: github/onqtam/doctest + override-checkout: v2.3.6 + - f38-clang-asan-ubsan: + required-projects: &projects + - name: github/CESNET/libyang +- override-checkout: cesnet/2024-03--v2-latest ++ override-checkout: devel + - name: github/onqtam/doctest + override-checkout: v2.4.11 + - f38-clang-tsan: +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 288b62a..6e54707 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -28,7 +28,7 @@ + option(BUILD_SHARED_LIBS "By default, shared libs are enabled. Turn off for a static build." ON) + + find_package(PkgConfig REQUIRED) +-pkg_check_modules(LIBYANG REQUIRED libyang>=2.1.140 IMPORTED_TARGET) ++pkg_check_modules(LIBYANG REQUIRED libyang>=2.2.3 IMPORTED_TARGET) + set(LIBYANG_CPP_PKG_VERSION "1.1.0") + + include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) +diff --git a/README.md b/README.md +index 5e79a23..9009a54 100644 +--- a/README.md ++++ b/README.md +@@ -8,8 +8,7 @@ + Object lifetimes are managed automatically via RAII. + + ## Dependencies +-- [libyang](https://github.com/CESNET/libyang) - ~~the `devel` branch (even for the `master` branch of *libyang-cpp*)~~ +- - temporarily (March 2024) this requires the [pre-v3 API of libyang (commit `b3d079fc3`)](https://github.com/CESNET/libyang/tree/b3d079fc37ac119f5a2a4a120b86d1c7102c3a08) ++- [libyang v3](https://github.com/CESNET/libyang) - the `devel` branch (even for the `master` branch of *libyang-cpp*) + - C++20 compiler (e.g., GCC 10.x+, clang 10+) + - CMake 3.19+ + +diff --git a/include/libyang-cpp/Context.hpp b/include/libyang-cpp/Context.hpp +index 1dab9f3..6f6e233 100644 +--- a/include/libyang-cpp/Context.hpp ++++ b/include/libyang-cpp/Context.hpp +@@ -69,7 +69,9 @@ + LogLevel level; + std::string message; + ErrorCode code; +- std::optional path; ++ std::optional dataPath; ++ std::optional schemaPath; ++ uint64_t line; + ValidationErrorCode validationCode; + }; + +diff --git a/include/libyang-cpp/Enum.hpp b/include/libyang-cpp/Enum.hpp +index 3ded4d2..85dcc83 100644 +--- a/include/libyang-cpp/Enum.hpp ++++ b/include/libyang-cpp/Enum.hpp +@@ -115,11 +115,15 @@ + }; + + enum class CreationOptions : uint32_t { +- Update = 0x01, +- Output = 0x02, +- Opaque = 0x04, +- // BinaryLyb = 0x08, TODO +- CanonicalValue = 0x10 ++ Output = 0x01, ++ StoreOnly = 0x02, ++ // BinaryLyb = 0x04, TODO ++ CanonicalValue = 0x08, ++ ClearDefaultFromParents = 0x10, ++ Update = 0x20, ++ Opaque = 0x40, ++ PathWithOpaque = 0x80, ++ // LYD_NEW_ANY_USE_VALUE is not relevant + }; + + /** +diff --git a/src/Context.cpp b/src/Context.cpp +index 337070f..713c50f 100644 +--- a/src/Context.cpp ++++ b/src/Context.cpp +@@ -579,8 +579,10 @@ + .appTag = errIt->apptag ? std::optional{errIt->apptag} : std::nullopt, + .level = utils::toLogLevel(errIt->level), + .message = errIt->msg, +- .code = static_cast(errIt->no), +- .path = errIt->path ? std::optional{errIt->path} : std::nullopt, ++ .code = static_cast(errIt->err), ++ .dataPath = errIt->data_path ? std::optional{errIt->data_path} : std::nullopt, ++ .schemaPath = errIt->schema_path ? std::optional{errIt->schema_path} : std::nullopt, ++ .line = errIt->line, + .validationCode = utils::toValidationErrorCode(errIt->vecode) + }); + +diff --git a/src/DataNode.cpp b/src/DataNode.cpp +index ab8cb4a..0e609c4 100644 +--- a/src/DataNode.cpp ++++ b/src/DataNode.cpp +@@ -1187,7 +1187,8 @@ + const std::string& xpath) + { + ly_set* set; +- auto ret = lyd_find_xpath3(contextNode ? contextNode->m_node : nullptr, forest.m_node, xpath.c_str(), nullptr, &set); ++ auto ret = lyd_find_xpath3(contextNode ? contextNode->m_node : nullptr, forest.m_node, xpath.c_str(), ++ LY_VALUE_JSON, nullptr, nullptr, &set); + + throwIfError(ret, "libyang::findXPathAt:"); + +diff --git a/src/utils/enum.hpp b/src/utils/enum.hpp +index 7674fc6..5e02d24 100644 +--- a/src/utils/enum.hpp ++++ b/src/utils/enum.hpp +@@ -72,11 +72,14 @@ + { + return static_cast(flags); + } +-static_assert(LYD_NEW_PATH_UPDATE == toCreationOptions(CreationOptions::Update)); +-static_assert(LYD_NEW_PATH_OUTPUT == toCreationOptions(CreationOptions::Output)); +-static_assert(LYD_NEW_PATH_OPAQ == toCreationOptions(CreationOptions::Opaque)); ++static_assert(LYD_NEW_VAL_OUTPUT == toCreationOptions(CreationOptions::Output)); ++static_assert(LYD_NEW_VAL_STORE_ONLY == toCreationOptions(CreationOptions::StoreOnly)); + // static_assert(LYD_NEW_PATH_BIN_VALUE == toCreationOptions(CreationOptions::BinaryLyb)); +-static_assert(LYD_NEW_PATH_CANON_VALUE == toCreationOptions(CreationOptions::CanonicalValue)); ++static_assert(LYD_NEW_VAL_CANON == toCreationOptions(CreationOptions::CanonicalValue)); ++static_assert(LYD_NEW_META_CLEAR_DFLT == toCreationOptions(CreationOptions::ClearDefaultFromParents)); ++static_assert(LYD_NEW_PATH_UPDATE == toCreationOptions(CreationOptions::Update)); ++static_assert(LYD_NEW_PATH_OPAQ == toCreationOptions(CreationOptions::Opaque)); ++static_assert(LYD_NEW_PATH_WITH_OPAQ == toCreationOptions(CreationOptions::PathWithOpaque)); + + constexpr uint32_t toDuplicationOptions(const DuplicationOptions options) + { +diff --git a/tests/context.cpp b/tests/context.cpp +index d19f78b..cb629ef 100644 +--- a/tests/context.cpp ++++ b/tests/context.cpp +@@ -457,7 +457,9 @@ + .level = libyang::LogLevel::Error, + .message = "Invalid character sequence \"invalid\", expected a keyword.", + .code = libyang::ErrorCode::ValidationFailure, +- .path = "Line number 1.", ++ .dataPath = std::nullopt, ++ .schemaPath = std::nullopt, ++ .line = 1, + .validationCode = libyang::ValidationErrorCode::Syntax, + } + }; +@@ -476,7 +478,9 @@ + .level = libyang::LogLevel::Error, + .message = "Value \"9001\" is out of type int8 min/max bounds.", + .code = libyang::ErrorCode::ValidationFailure, +- .path = "Schema location \"/example-schema:leafInt8\".", ++ .dataPath = std::nullopt, ++ .schemaPath = "/example-schema:leafInt8", ++ .line = 0, + .validationCode = libyang::ValidationErrorCode::Data, + } + }; +@@ -491,7 +495,9 @@ + .level = libyang::LogLevel::Error, + .message = "Invalid type int8 empty value.", + .code = libyang::ErrorCode::ValidationFailure, +- .path = "Schema location \"/example-schema:leafInt8\".", ++ .dataPath = std::nullopt, ++ .schemaPath = "/example-schema:leafInt8", ++ .line = 0, + .validationCode = libyang::ValidationErrorCode::Data, + }, + libyang::ErrorInfo { +@@ -499,7 +505,9 @@ + .level = libyang::LogLevel::Error, + .message = "Value \"9001\" is out of type int8 min/max bounds.", + .code = libyang::ErrorCode::ValidationFailure, +- .path = "Schema location \"/example-schema:leafInt8\".", ++ .dataPath = std::nullopt, ++ .schemaPath = "/example-schema:leafInt8", ++ .line = 0, + .validationCode = libyang::ValidationErrorCode::Data, + } + }; +diff --git a/tests/data_node.cpp b/tests/data_node.cpp +index 15fb727..2d27979 100644 +--- a/tests/data_node.cpp ++++ b/tests/data_node.cpp +@@ -1026,12 +1026,12 @@ + "/example-schema:bigTree/one", + "/example-schema:bigTree/one/myLeaf", + "/example-schema:bigTree/two", +- "/example-schema:bigTree/two/myList[thekey='43221']", +- "/example-schema:bigTree/two/myList[thekey='43221']/thekey", ++ "/example-schema:bigTree/two/myList[thekey='213']", ++ "/example-schema:bigTree/two/myList[thekey='213']/thekey", + "/example-schema:bigTree/two/myList[thekey='432']", + "/example-schema:bigTree/two/myList[thekey='432']/thekey", +- "/example-schema:bigTree/two/myList[thekey='213']", +- "/example-schema:bigTree/two/myList[thekey='213']/thekey" ++ "/example-schema:bigTree/two/myList[thekey='43221']", ++ "/example-schema:bigTree/two/myList[thekey='43221']/thekey", + }; + + REQUIRE(res == expected); +@@ -1255,6 +1255,7 @@ + + DOCTEST_SUBCASE("DataNode::findXPath") + { ++ // libyang v3 sorts these alphabetically + const auto data3 = R"({ + "example-schema:person": [ + { +@@ -1308,13 +1309,13 @@ + auto set = node->findXPath("/example-schema:person"); + REQUIRE(set.size() == 3); + +- REQUIRE(set.front().path() == "/example-schema:person[name='John']"); +- REQUIRE(set.back().path() == "/example-schema:person[name='David']"); ++ REQUIRE(set.front().path() == "/example-schema:person[name='Dan']"); ++ REQUIRE(set.back().path() == "/example-schema:person[name='John']"); + + auto iter = set.begin(); +- REQUIRE((iter++)->path() == "/example-schema:person[name='John']"); + REQUIRE((iter++)->path() == "/example-schema:person[name='Dan']"); + REQUIRE((iter++)->path() == "/example-schema:person[name='David']"); ++ REQUIRE((iter++)->path() == "/example-schema:person[name='John']"); + REQUIRE(iter == set.end()); + REQUIRE_THROWS_WITH_AS(*iter, "Dereferenced an .end() iterator", std::out_of_range); + } +@@ -1324,23 +1325,23 @@ + auto set = node->findXPath("/example-schema:person"); + + REQUIRE((set.begin() + 0) == set.begin()); +- REQUIRE((set.begin() + 0)->path() == "/example-schema:person[name='John']"); +- REQUIRE((set.begin() + 1)->path() == "/example-schema:person[name='Dan']"); +- REQUIRE((set.begin() + 2)->path() == "/example-schema:person[name='David']"); ++ REQUIRE((set.begin() + 0)->path() == "/example-schema:person[name='Dan']"); ++ REQUIRE((set.begin() + 1)->path() == "/example-schema:person[name='David']"); ++ REQUIRE((set.begin() + 2)->path() == "/example-schema:person[name='John']"); + REQUIRE((set.begin() + 3) == set.end()); + REQUIRE_THROWS(set.begin() + 4); + + REQUIRE((set.end() - 0) == set.end()); +- REQUIRE((set.end() - 1)->path() == "/example-schema:person[name='David']"); +- REQUIRE((set.end() - 2)->path() == "/example-schema:person[name='Dan']"); +- REQUIRE((set.end() - 3)->path() == "/example-schema:person[name='John']"); ++ REQUIRE((set.end() - 1)->path() == "/example-schema:person[name='John']"); ++ REQUIRE((set.end() - 2)->path() == "/example-schema:person[name='David']"); ++ REQUIRE((set.end() - 3)->path() == "/example-schema:person[name='Dan']"); + REQUIRE((set.end() - 3) == set.begin()); + REQUIRE_THROWS(set.end() - 4); + + auto iter = set.end(); ++ REQUIRE((--iter)->path() == "/example-schema:person[name='John']"); + REQUIRE((--iter)->path() == "/example-schema:person[name='David']"); + REQUIRE((--iter)->path() == "/example-schema:person[name='Dan']"); +- REQUIRE((--iter)->path() == "/example-schema:person[name='John']"); + REQUIRE_THROWS(--iter); + } + +diff --git a/tests/pretty_printers.hpp b/tests/pretty_printers.hpp +index 6ae8934..6bbc16f 100644 +--- a/tests/pretty_printers.hpp ++++ b/tests/pretty_printers.hpp +@@ -59,7 +59,9 @@ + oss << "appTag: " << (err.appTag ? *err.appTag : "std::nullopt") << "\n "; + oss << "code: " << err.code << "\n "; + oss << "message: " << err.message << "\n "; +- oss << "path: " << (err.path ? *err.path : "std::nullopt") << "\n "; ++ oss << "dataPath: " << (err.dataPath ? *err.dataPath : "std::nullopt") << "\n "; ++ oss << "schemaPath: " << (err.schemaPath ? *err.schemaPath : "std::nullopt") << "\n "; ++ oss << "line: " << err.line << "\n "; + oss << "level: " << err.level << "\n "; + oss << "validationCode: " << err.validationCode << "\n }"; + return oss.str(); diff --git a/patches/rousette/0001-Make-systemd-optional.patch b/patches/rousette/0001-Make-systemd-optional.patch new file mode 100644 index 00000000..516024a6 --- /dev/null +++ b/patches/rousette/0001-Make-systemd-optional.patch @@ -0,0 +1,135 @@ +From 6277d5f51f3a66df027bdd1752ea0ad58beb26fa Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Mattias=20Walstr=C3=B6m?= +Date: Mon, 27 May 2024 18:29:15 +0200 +Subject: [PATCH] Make systemd optional +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit +Organization: Addiva Elektronik + +If compile with systemd, set USE_SYSTEMD variable + +Signed-off-by: Mattias Walström +--- + CMakeLists.txt | 13 ++++++++++--- + src/restconf/main.cpp | 36 +++++++++++++++++++++++++++--------- + 2 files changed, 37 insertions(+), 12 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 7f82660..0f5bed9 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -72,10 +72,12 @@ find_package(spdlog REQUIRED) + find_package(PkgConfig) + pkg_check_modules(nghttp2 REQUIRED IMPORTED_TARGET libnghttp2_asio>=0.0.90 libnghttp2) + find_package(Boost REQUIRED COMPONENTS system thread) +- + pkg_check_modules(SYSREPO-CPP REQUIRED IMPORTED_TARGET sysrepo-cpp>=1.1.0) + pkg_check_modules(LIBYANG-CPP REQUIRED IMPORTED_TARGET libyang-cpp>=1.1.0) +-pkg_check_modules(SYSTEMD REQUIRED IMPORTED_TARGET libsystemd) ++if(USE_SYSTEMD) ++ add_definitions(-DUSE_SYSTEMD=1) ++ pkg_check_modules(SYSTEMD REQUIRED IMPORTED_TARGET libsystemd) ++endif() + pkg_check_modules(PAM REQUIRED IMPORTED_TARGET pam) + + add_library(rousette-http STATIC +@@ -116,7 +118,12 @@ add_executable(watch-operational-ds + target_link_libraries(watch-operational-ds PUBLIC rousette-sysrepo) + + add_executable(rousette src/restconf/main.cpp) +-target_link_libraries(rousette PUBLIC rousette-restconf PkgConfig::SYSTEMD) ++ ++if(USE_SYSTEMD) ++ target_link_libraries(rousette PUBLIC rousette-restconf PkgConfig::SYSTEMD) ++else() ++ target_link_libraries(rousette PUBLIC rousette-restconf) ++endif() + + install(TARGETS + # clock-demo +diff --git a/src/restconf/main.cpp b/src/restconf/main.cpp +index bba3cbf..3162dcd 100644 +--- a/src/restconf/main.cpp ++++ b/src/restconf/main.cpp +@@ -3,13 +3,17 @@ + * + * Written by Jan Kundrát + * +-*/ ++ */ + + #include + #include + #include + #include ++#ifdef USE_SYSTEMD + #include ++#else ++#include ++#endif + #include + #include + #include +@@ -19,6 +23,7 @@ + #include "restconf/Server.h" + + namespace { ++#ifdef USE_SYSTEMD + /** @short Is stderr connected to journald? Not thread safe. */ + bool is_journald_active() + { +@@ -39,29 +44,42 @@ bool is_journald_active() + } + + /** @short Provide better levels, see https://github.com/gabime/spdlog/pull/1292#discussion_r340777258 */ +-template ++template + class journald_sink : public spdlog::sinks::systemd_sink { + public: + journald_sink() + { + this->syslog_levels_ = {/* spdlog::level::trace */ LOG_DEBUG, +- /* spdlog::level::debug */ LOG_INFO, +- /* spdlog::level::info */ LOG_NOTICE, +- /* spdlog::level::warn */ LOG_WARNING, +- /* spdlog::level::err */ LOG_ERR, +- /* spdlog::level::critical */ LOG_CRIT, +- /* spdlog::level::off */ LOG_ALERT}; ++ /* spdlog::level::debug */ LOG_INFO, ++ /* spdlog::level::info */ LOG_NOTICE, ++ /* spdlog::level::warn */ LOG_WARNING, ++ /* spdlog::level::err */ LOG_ERR, ++ /* spdlog::level::critical */ LOG_CRIT, ++ /* spdlog::level::off */ LOG_ALERT}; + } + }; ++#else ++void configure_syslog_logger() ++{ ++ auto syslog_sink = std::make_shared("rousette", LOG_PID, LOG_USER, true); ++ auto logger = std::make_shared("rousette", syslog_sink); ++ spdlog::set_default_logger(logger); ++ spdlog::set_level(spdlog::level::trace); ++} ++#endif + } + +-int main(int argc [[maybe_unused]], char* argv [[maybe_unused]] []) ++int main(int argc [[maybe_unused]], char* argv [[maybe_unused]][]) + { ++#ifdef USE_SYSTEMD + if (is_journald_active()) { + auto sink = std::make_shared>(); + auto logger = std::make_shared("rousette", sink); + spdlog::set_default_logger(logger); + } ++#else ++ configure_syslog_logger(); ++#endif + spdlog::set_level(spdlog::level::trace); + + /* We will parse URIs using boost::spirit's alnum/alpha/... matchers which are locale-dependent. +-- +2.34.1 + diff --git a/src/confd/scripts/setup.sh b/src/confd/scripts/setup.sh index 2973eb9b..4c55cc31 100755 --- a/src/confd/scripts/setup.sh +++ b/src/confd/scripts/setup.sh @@ -68,12 +68,13 @@ MODULES=( "ietf-keystore@2023-12-28.yang -e central-keystore-supported -e inline-definitions-supported -e asymmetric-keys -e symmetric-keys" "ietf-ssh-server@2023-12-28.yang -e local-user-auth-password -e local-user-auth-publickey" "ietf-tls-server@2023-12-28.yang -e server-ident-raw-public-key -e server-ident-x509-cert" - + "ietf-restconf@2017-01-26.yang" ) if [ -n "$USE_CONTAINERS" ]; then CONTAINERS=" -e containers"; MODULES+=("infix-containers@2024-03-27.yang") fi + MODULES+=("infix-interfaces@2024-01-15.yang $CONTAINERS -e vlan-filtering") CMD_INSTALL= diff --git a/src/confd/yang/ietf-restconf@2017-01-26.yang b/src/confd/yang/ietf-restconf@2017-01-26.yang new file mode 100644 index 00000000..b47455b8 --- /dev/null +++ b/src/confd/yang/ietf-restconf@2017-01-26.yang @@ -0,0 +1,278 @@ +module ietf-restconf { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-restconf"; + prefix "rc"; + + organization + "IETF NETCONF (Network Configuration) Working Group"; + + contact + "WG Web: + WG List: + + Author: Andy Bierman + + + Author: Martin Bjorklund + + + Author: Kent Watsen + "; + + description + "This module contains conceptual YANG specifications + for basic RESTCONF media type definitions used in + RESTCONF protocol messages. + + Note that the YANG definitions within this module do not + represent configuration data of any kind. + The 'restconf-media-type' YANG extension statement + provides a normative syntax for XML and JSON + message-encoding purposes. + + Copyright (c) 2017 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 8040; see + the RFC itself for full legal notices."; + + revision 2017-01-26 { + description + "Initial revision."; + reference + "RFC 8040: RESTCONF Protocol."; + } + + extension yang-data { + argument name { + yin-element true; + } + description + "This extension is used to specify a YANG data template that + represents conceptual data defined in YANG. It is + intended to describe hierarchical data independent of + protocol context or specific message-encoding format. + Data definition statements within a yang-data extension + specify the generic syntax for the specific YANG data + template, whose name is the argument of the 'yang-data' + extension statement. + + Note that this extension does not define a media type. + A specification using this extension MUST specify the + message-encoding rules, including the content media type. + + The mandatory 'name' parameter value identifies the YANG + data template that is being defined. It contains the + template name. + + This extension is ignored unless it appears as a top-level + statement. It MUST contain data definition statements + that result in exactly one container data node definition. + An instance of a YANG data template can thus be translated + into an XML instance document, whose top-level element + corresponds to the top-level container. + The module name and namespace values for the YANG module using + the extension statement are assigned to instance document data + conforming to the data definition statements within + this extension. + + The substatements of this extension MUST follow the + 'data-def-stmt' rule in the YANG ABNF. + + The XPath document root is the extension statement itself, + such that the child nodes of the document root are + represented by the data-def-stmt substatements within + this extension. This conceptual document is the context + for the following YANG statements: + + - must-stmt + - when-stmt + - path-stmt + - min-elements-stmt + - max-elements-stmt + - mandatory-stmt + - unique-stmt + - ordered-by + - instance-identifier data type + + The following data-def-stmt substatements are constrained + when used within a 'yang-data' extension statement. + + - The list-stmt is not required to have a key-stmt defined. + - The if-feature-stmt is ignored if present. + - The config-stmt is ignored if present. + - The available identity values for any 'identityref' + leaf or leaf-list nodes are limited to the module + containing this extension statement and the modules + imported into that module. + "; + } + + rc:yang-data yang-errors { + uses errors; + } + + rc:yang-data yang-api { + uses restconf; + } + + grouping errors { + description + "A grouping that contains a YANG container + representing the syntax and semantics of a + YANG Patch error report within a response message."; + + container errors { + description + "Represents an error report returned by the server if + a request results in an error."; + + list error { + description + "An entry containing information about one + specific error that occurred while processing + a RESTCONF request."; + reference + "RFC 6241, Section 4.3."; + + leaf error-type { + type enumeration { + enum transport { + description + "The transport layer."; + } + enum rpc { + description + "The rpc or notification layer."; + } + enum protocol { + description + "The protocol operation layer."; + } + enum application { + description + "The server application layer."; + } + } + mandatory true; + description + "The protocol layer where the error occurred."; + } + + leaf error-tag { + type string; + mandatory true; + description + "The enumerated error-tag."; + } + + leaf error-app-tag { + type string; + description + "The application-specific error-tag."; + } + + leaf error-path { + type instance-identifier; + description + "The YANG instance identifier associated + with the error node."; + } + + leaf error-message { + type string; + description + "A message describing the error."; + } + + anydata error-info { + description + "This anydata value MUST represent a container with + zero or more data nodes representing additional + error information."; + } + } + } + } + + grouping restconf { + description + "Conceptual grouping representing the RESTCONF + root resource."; + + container restconf { + description + "Conceptual container representing the RESTCONF + root resource."; + + container data { + description + "Container representing the datastore resource. + Represents the conceptual root of all state data + and configuration data supported by the server. + The child nodes of this container can be any data + resources that are defined as top-level data nodes + from the YANG modules advertised by the server in + the 'ietf-yang-library' module."; + } + + container operations { + description + "Container for all operation resources. + + Each resource is represented as an empty leaf with the + name of the RPC operation from the YANG 'rpc' statement. + + For example, the 'system-restart' RPC operation defined + in the 'ietf-system' module would be represented as + an empty leaf in the 'ietf-system' namespace. This is + a conceptual leaf and will not actually be found in + the module: + + module ietf-system { + leaf system-reset { + type empty; + } + } + + To invoke the 'system-restart' RPC operation: + + POST /restconf/operations/ietf-system:system-restart + + To discover the RPC operations supported by the server: + + GET /restconf/operations + + In XML, the YANG module namespace identifies the module: + + + + In JSON, the YANG module name identifies the module: + + { 'ietf-system:system-restart' : [null] } + "; + } + leaf yang-library-version { + type string { + pattern '\d{4}-\d{2}-\d{2}'; + } + config false; + mandatory true; + description + "Identifies the revision date of the 'ietf-yang-library' + module that is implemented by this RESTCONF server. + Indicates the year, month, and day in YYYY-MM-DD + numeric format."; + } + } + } + +}