mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-22 01:13:00 +02:00
buildroot: Update the patches to reflect the upgraded buildroot
This commit is contained in:
+20
-16
@@ -1,8 +1,11 @@
|
||||
From 392735fcb41b303b6dbe3517fab9ba734ff2e288 Mon Sep 17 00:00:00 2001
|
||||
From f9008d2d58b215886961ae2ce90caba473207309 Mon Sep 17 00:00:00 2001
|
||||
From: Joachim Wiberg <troglobit@gmail.com>
|
||||
Date: Tue, 5 Mar 2024 09:41:46 +0100
|
||||
Subject: [PATCH 1/2] iplink_bridge: add mcast_flood_always bridge option
|
||||
Organization: Addiva Elektronik
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Organization: Wires
|
||||
|
||||
- Break out boolopt handling to simplify parsing and setting
|
||||
- Add set/get support for mcast_flood_always
|
||||
@@ -10,6 +13,7 @@ Organization: Addiva Elektronik
|
||||
|
||||
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
|
||||
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
|
||||
Signed-off-by: Mattias Walström <lazzer@gmail.com>
|
||||
---
|
||||
include/uapi/linux/if_bridge.h | 1 +
|
||||
ip/iplink_bridge.c | 19 +++++++++++++++++++
|
||||
@@ -17,22 +21,22 @@ Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
|
||||
3 files changed, 32 insertions(+)
|
||||
|
||||
diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h
|
||||
index c206cf6..84ef423 100644
|
||||
index bed5e0c1..637ad93c 100644
|
||||
--- a/include/uapi/linux/if_bridge.h
|
||||
+++ b/include/uapi/linux/if_bridge.h
|
||||
@@ -830,6 +830,7 @@ enum br_boolopt_id {
|
||||
BR_BOOLOPT_NO_LL_LEARN,
|
||||
@@ -832,6 +832,7 @@ enum br_boolopt_id {
|
||||
BR_BOOLOPT_MCAST_VLAN_SNOOPING,
|
||||
BR_BOOLOPT_MST_ENABLE,
|
||||
BR_BOOLOPT_MDB_OFFLOAD_FAIL_NOTIFICATION,
|
||||
+ BR_BOOLOPT_MCAST_FLOOD_ALWAYS,
|
||||
BR_BOOLOPT_MAX
|
||||
};
|
||||
|
||||
diff --git a/ip/iplink_bridge.c b/ip/iplink_bridge.c
|
||||
index 1fe8955..d4f2c80 100644
|
||||
index 76e69086..827a0716 100644
|
||||
--- a/ip/iplink_bridge.c
|
||||
+++ b/ip/iplink_bridge.c
|
||||
@@ -41,6 +41,7 @@ static void print_explain(FILE *f)
|
||||
@@ -42,6 +42,7 @@ static void print_explain(FILE *f)
|
||||
" [ vlan_default_pvid VLAN_DEFAULT_PVID ]\n"
|
||||
" [ vlan_stats_enabled VLAN_STATS_ENABLED ]\n"
|
||||
" [ vlan_stats_per_port VLAN_STATS_PER_PORT ]\n"
|
||||
@@ -40,7 +44,7 @@ index 1fe8955..d4f2c80 100644
|
||||
" [ mcast_snooping MULTICAST_SNOOPING ]\n"
|
||||
" [ mcast_vlan_snooping MULTICAST_VLAN_SNOOPING ]\n"
|
||||
" [ mcast_router MULTICAST_ROUTER ]\n"
|
||||
@@ -245,6 +246,18 @@ static int bridge_parse_opt(struct link_util *lu, int argc, char **argv,
|
||||
@@ -247,6 +248,18 @@ static int bridge_parse_opt(struct link_util *lu, int argc, char **argv,
|
||||
bm.optval |= mcvl_bit;
|
||||
else
|
||||
bm.optval &= ~mcvl_bit;
|
||||
@@ -59,7 +63,7 @@ index 1fe8955..d4f2c80 100644
|
||||
} else if (matches(*argv, "mcast_query_use_ifaddr") == 0) {
|
||||
__u8 mcast_qui;
|
||||
|
||||
@@ -623,6 +636,7 @@ static void bridge_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
|
||||
@@ -638,6 +651,7 @@ static void bridge_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
|
||||
__u32 mcvl_bit = 1 << BR_BOOLOPT_MCAST_VLAN_SNOOPING;
|
||||
__u32 no_ll_learn_bit = 1 << BR_BOOLOPT_NO_LL_LEARN;
|
||||
__u32 mst_bit = 1 << BR_BOOLOPT_MST_ENABLE;
|
||||
@@ -67,10 +71,10 @@ index 1fe8955..d4f2c80 100644
|
||||
struct br_boolopt_multi *bm;
|
||||
|
||||
bm = RTA_DATA(tb[IFLA_BR_MULTI_BOOLOPT]);
|
||||
@@ -641,6 +655,11 @@ static void bridge_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
|
||||
"mst_enabled",
|
||||
"mst_enabled %u ",
|
||||
!!(bm->optval & mst_bit));
|
||||
@@ -661,6 +675,11 @@ static void bridge_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
|
||||
"mdb_offload_fail_notification",
|
||||
"mdb_offload_fail_notification %u ",
|
||||
!!(bm->optval & mofn_bit));
|
||||
+ if (bm->optmask & mcfl_bit)
|
||||
+ print_uint(PRINT_ANY,
|
||||
+ "mcast_flood_always",
|
||||
@@ -80,10 +84,10 @@ index 1fe8955..d4f2c80 100644
|
||||
|
||||
if (tb[IFLA_BR_MCAST_ROUTER])
|
||||
diff --git a/man/man8/ip-link.8.in b/man/man8/ip-link.8.in
|
||||
index efb6248..2308835 100644
|
||||
index e3297c57..f2852adf 100644
|
||||
--- a/man/man8/ip-link.8.in
|
||||
+++ b/man/man8/ip-link.8.in
|
||||
@@ -1712,6 +1712,8 @@ the following additional arguments are supported:
|
||||
@@ -1737,6 +1737,8 @@ the following additional arguments are supported:
|
||||
] [
|
||||
.BI vlan_stats_per_port " VLAN_STATS_PER_PORT "
|
||||
] [
|
||||
@@ -92,7 +96,7 @@ index efb6248..2308835 100644
|
||||
.BI mcast_snooping " MULTICAST_SNOOPING "
|
||||
] [
|
||||
.BI mcast_vlan_snooping " MULTICAST_VLAN_SNOOPING "
|
||||
@@ -1860,6 +1862,16 @@ or disable
|
||||
@@ -1887,6 +1889,16 @@ or disable
|
||||
.RI ( VLAN_STATS_PER_PORT " == 0) "
|
||||
per-VLAN per-port stats accounting. Can be changed only when there are no port VLANs configured.
|
||||
|
||||
+8
-4
@@ -1,8 +1,11 @@
|
||||
From a035696129b543952e8105c27689ed4f9312eb3b Mon Sep 17 00:00:00 2001
|
||||
From 2aac8bf90e25e33e022c850702d8581d7d6cba25 Mon Sep 17 00:00:00 2001
|
||||
From: Joachim Wiberg <troglobit@gmail.com>
|
||||
Date: Mon, 20 May 2024 21:35:11 +0200
|
||||
Subject: [PATCH 2/2] ipaddress: accept symbolic names also for show
|
||||
Organization: Addiva Elektronik
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Organization: Wires
|
||||
|
||||
This is a follow-up to 709063e, which in turn was a follow-up to
|
||||
bdb8d85, to add support for 'ip addr show proto static', where
|
||||
@@ -10,15 +13,16 @@ bdb8d85, to add support for 'ip addr show proto static', where
|
||||
|
||||
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
|
||||
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
|
||||
Signed-off-by: Mattias Walström <lazzer@gmail.com>
|
||||
---
|
||||
ip/ipaddress.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/ip/ipaddress.c b/ip/ipaddress.c
|
||||
index 4e1f934f..437072f9 100644
|
||||
index 4d93a04a..1bff689e 100644
|
||||
--- a/ip/ipaddress.c
|
||||
+++ b/ip/ipaddress.c
|
||||
@@ -2224,7 +2224,7 @@ static int ipaddr_list_flush_or_save(int argc, char **argv, int action)
|
||||
@@ -2248,7 +2248,7 @@ static int ipaddr_list_flush_or_save(int argc, char **argv, int action)
|
||||
__u8 proto;
|
||||
|
||||
NEXT_ARG();
|
||||
@@ -0,0 +1,135 @@
|
||||
From 22b41e7ed8268b94ccc139138e2037c390a3a616 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Bed=C5=99ich=20Schindler?= <bedrich.schindler@gmail.com>
|
||||
Date: Thu, 10 Jul 2025 15:00:45 +0200
|
||||
Subject: [PATCH 1/7] Implement `SchemaNode::actionRpcs()`
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Organization: Wires
|
||||
|
||||
This function allows to access list of actions
|
||||
within schema node.
|
||||
|
||||
It is implemented same as `Module::actionRpcs`
|
||||
to follow same approach, so there is no need to use
|
||||
collections.
|
||||
|
||||
Change-Id: Ie99908cfdd334433fd9ae6f1a909f196e61139fd
|
||||
Signed-off-by: Jan Kundrát <jan.kundrat@cesnet.cz>
|
||||
Signed-off-by: Mattias Walström <lazzer@gmail.com>
|
||||
---
|
||||
include/libyang-cpp/SchemaNode.hpp | 1 +
|
||||
src/SchemaNode.cpp | 12 ++++++++++++
|
||||
tests/example_schema.hpp | 28 ++++++++++++++++++++++++++++
|
||||
tests/schema_node.cpp | 21 +++++++++++++++++++++
|
||||
4 files changed, 62 insertions(+)
|
||||
|
||||
diff --git a/include/libyang-cpp/SchemaNode.hpp b/include/libyang-cpp/SchemaNode.hpp
|
||||
index 0f1a4c4..9f49fd7 100644
|
||||
--- a/include/libyang-cpp/SchemaNode.hpp
|
||||
+++ b/include/libyang-cpp/SchemaNode.hpp
|
||||
@@ -79,6 +79,7 @@ public:
|
||||
Collection<SchemaNode, IterationType::Sibling> siblings() const;
|
||||
Collection<SchemaNode, IterationType::Sibling> immediateChildren() const;
|
||||
std::vector<ExtensionInstance> extensionInstances() const;
|
||||
+ std::vector<SchemaNode> actionRpcs() const;
|
||||
|
||||
std::vector<When> when() const;
|
||||
|
||||
diff --git a/src/SchemaNode.cpp b/src/SchemaNode.cpp
|
||||
index 26b5099..ce24203 100644
|
||||
--- a/src/SchemaNode.cpp
|
||||
+++ b/src/SchemaNode.cpp
|
||||
@@ -117,6 +117,18 @@ Collection<SchemaNode, IterationType::Sibling> SchemaNode::immediateChildren() c
|
||||
return c ? c->siblings() : Collection<SchemaNode, IterationType::Sibling>{nullptr, nullptr};
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * @brief Returns a collection of action nodes (not RPC nodes) as SchemaNode
|
||||
+ */
|
||||
+std::vector<SchemaNode> SchemaNode::actionRpcs() const
|
||||
+{
|
||||
+ std::vector<SchemaNode> res;
|
||||
+ for (auto action = reinterpret_cast<const struct lysc_node*>(lysc_node_actions(m_node)); action; action = action->next) {
|
||||
+ res.emplace_back(SchemaNode{action, m_ctx});
|
||||
+ }
|
||||
+ return res;
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* Returns the YANG description of the node.
|
||||
*
|
||||
diff --git a/tests/example_schema.hpp b/tests/example_schema.hpp
|
||||
index 0d8acb9..02d3d74 100644
|
||||
--- a/tests/example_schema.hpp
|
||||
+++ b/tests/example_schema.hpp
|
||||
@@ -214,6 +214,34 @@ module example-schema {
|
||||
}
|
||||
|
||||
container bigTree {
|
||||
+ action firstAction {
|
||||
+ input {
|
||||
+ leaf inputLeaf1 {
|
||||
+ type string;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ output {
|
||||
+ leaf outputLeaf1 {
|
||||
+ type string;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ action secondAction {
|
||||
+ input {
|
||||
+ leaf inputLeaf2 {
|
||||
+ type string;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ output {
|
||||
+ leaf outputLeaf2 {
|
||||
+ type string;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
container one {
|
||||
leaf myLeaf {
|
||||
type string;
|
||||
diff --git a/tests/schema_node.cpp b/tests/schema_node.cpp
|
||||
index 0001377..65ef462 100644
|
||||
--- a/tests/schema_node.cpp
|
||||
+++ b/tests/schema_node.cpp
|
||||
@@ -544,6 +544,27 @@ TEST_CASE("SchemaNode")
|
||||
REQUIRE(elem.extensionInstances()[2].argument() == "last-modified");
|
||||
}
|
||||
|
||||
+ DOCTEST_SUBCASE("SchemaNode::actionRpcs")
|
||||
+ {
|
||||
+ DOCTEST_SUBCASE("no actions")
|
||||
+ {
|
||||
+ auto actions = ctx->findPath("/example-schema:presenceContainer").actionRpcs();
|
||||
+ REQUIRE(actions.size() == 0);
|
||||
+ }
|
||||
+
|
||||
+ DOCTEST_SUBCASE("two actions")
|
||||
+ {
|
||||
+ auto actions = ctx->findPath("/example-schema:bigTree").actionRpcs();
|
||||
+ REQUIRE(actions.size() == 2);
|
||||
+ REQUIRE(actions[0].asActionRpc().name() == "firstAction");
|
||||
+ REQUIRE(actions[0].asActionRpc().input().child()->name() == "inputLeaf1");
|
||||
+ REQUIRE(actions[0].asActionRpc().output().child()->name() == "outputLeaf1");
|
||||
+ REQUIRE(actions[1].asActionRpc().name() == "secondAction");
|
||||
+ REQUIRE(actions[1].asActionRpc().input().child()->name() == "inputLeaf2");
|
||||
+ REQUIRE(actions[1].asActionRpc().output().child()->name() == "outputLeaf2");
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
DOCTEST_SUBCASE("SchemaNode::operator==")
|
||||
{
|
||||
auto a = ctx->findPath("/type_module:leafString");
|
||||
--
|
||||
2.43.0
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
From 1f8e502cf0308faa0336b1077308184de6a83ee0 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= <jan.kundrat@cesnet.cz>
|
||||
Date: Mon, 20 Oct 2025 11:45:06 +0200
|
||||
Subject: [PATCH 2/7] CI: pin to libyang v3
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Organization: Wires
|
||||
|
||||
Change-Id: Idba085ad9c9c137874801f1d4089f8ee6bb390c7
|
||||
Signed-off-by: Mattias Walström <lazzer@gmail.com>
|
||||
---
|
||||
.zuul.yaml | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/.zuul.yaml b/.zuul.yaml
|
||||
index 7b92766..fd89549 100644
|
||||
--- a/.zuul.yaml
|
||||
+++ b/.zuul.yaml
|
||||
@@ -4,13 +4,13 @@
|
||||
- f38-gcc-cover:
|
||||
required-projects:
|
||||
- name: github/CESNET/libyang
|
||||
- override-checkout: devel
|
||||
+ override-checkout: cesnet/2025-08-07
|
||||
- name: github/doctest/doctest
|
||||
override-checkout: v2.3.6
|
||||
- f38-clang-asan-ubsan:
|
||||
required-projects: &projects
|
||||
- name: github/CESNET/libyang
|
||||
- override-checkout: devel
|
||||
+ override-checkout: cesnet/2025-08-07
|
||||
- name: github/doctest/doctest
|
||||
override-checkout: v2.4.11
|
||||
- f38-clang-tsan:
|
||||
--
|
||||
2.43.0
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
From b27a65d5d5f6ffffbd2c26c1b5bea57a6de584d0 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Pecka?= <tomas.pecka@cesnet.cz>
|
||||
Date: Mon, 20 Oct 2025 18:46:38 +0200
|
||||
Subject: [PATCH 3/7] wrap lyd_validate_op
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Organization: Wires
|
||||
|
||||
This patch wraps lyd_validate_op from libyang. Downstream users can now
|
||||
validate operations (RPC input, replies, and notifications).
|
||||
|
||||
Change-Id: Ib03070bbc3e1d0dccd6bf38eda82e944db1093b1
|
||||
Signed-off-by: Mattias Walström <lazzer@gmail.com>
|
||||
---
|
||||
include/libyang-cpp/DataNode.hpp | 2 +
|
||||
src/DataNode.cpp | 24 +++++++++++
|
||||
tests/context.cpp | 2 +-
|
||||
tests/data_node.cpp | 72 ++++++++++++++++++++++++++++++++
|
||||
tests/example_schema.hpp | 22 ++++++++++
|
||||
5 files changed, 121 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/include/libyang-cpp/DataNode.hpp b/include/libyang-cpp/DataNode.hpp
|
||||
index 50d6c0e..e202a96 100644
|
||||
--- a/include/libyang-cpp/DataNode.hpp
|
||||
+++ b/include/libyang-cpp/DataNode.hpp
|
||||
@@ -59,6 +59,7 @@ template <typename Operation, typename Siblings>
|
||||
void handleLyTreeOperation(DataNode* affectedNode, Operation operation, Siblings siblings, std::shared_ptr<internal_refcount> newRefs);
|
||||
|
||||
LIBYANG_CPP_EXPORT void validateAll(std::optional<libyang::DataNode>& node, const std::optional<ValidationOptions>& opts = std::nullopt);
|
||||
+LIBYANG_CPP_EXPORT void validateOp(libyang::DataNode& input, const std::optional<libyang::DataNode>& opsTree, OperationType opType);
|
||||
|
||||
LIBYANG_CPP_EXPORT Set<DataNode> findXPathAt(
|
||||
const std::optional<libyang::DataNode>& contextNode,
|
||||
@@ -147,6 +148,7 @@ public:
|
||||
friend LIBYANG_CPP_EXPORT lyd_node* getRawNode(DataNode node);
|
||||
|
||||
friend LIBYANG_CPP_EXPORT void validateAll(std::optional<libyang::DataNode>& node, const std::optional<ValidationOptions>& opts);
|
||||
+ friend LIBYANG_CPP_EXPORT void validateOp(libyang::DataNode& input, const std::optional<libyang::DataNode>& opsTree, OperationType opType);
|
||||
friend LIBYANG_CPP_EXPORT Set<DataNode> findXPathAt(const std::optional<libyang::DataNode>& contextNode, const libyang::DataNode& forest, const std::string& xpath);
|
||||
|
||||
bool operator==(const DataNode& node) const;
|
||||
diff --git a/src/DataNode.cpp b/src/DataNode.cpp
|
||||
index 7e87917..e28ef30 100644
|
||||
--- a/src/DataNode.cpp
|
||||
+++ b/src/DataNode.cpp
|
||||
@@ -1226,6 +1226,30 @@ void validateAll(std::optional<libyang::DataNode>& node, const std::optional<Val
|
||||
}
|
||||
}
|
||||
|
||||
+/** @brief Validate op after parsing with lyd_parse_op.
|
||||
+ *
|
||||
+ * Wraps `lyd_validate_op`.
|
||||
+ *
|
||||
+ * @param input The tree with the op to validate.
|
||||
+ * @param opsTree The optional data tree to validate the input against.
|
||||
+ * @param opType The operation type. Contrary to `lyd_validate_op`, we accept not only YANG but also NETCONF and RESTCONF operation types (and internally convert them to YANG).
|
||||
+ */
|
||||
+void validateOp(libyang::DataNode& input, const std::optional<libyang::DataNode>& opsTree, OperationType opType)
|
||||
+{
|
||||
+ if (opType == OperationType::RpcYang || opType == OperationType::RpcRestconf || opType == OperationType::RpcNetconf) {
|
||||
+ opType = OperationType::RpcYang;
|
||||
+ } else if (opType == OperationType::ReplyYang || opType == OperationType::ReplyRestconf || opType == OperationType::ReplyNetconf) {
|
||||
+ opType = OperationType::ReplyYang;
|
||||
+ } else if (opType == OperationType::NotificationYang || opType == OperationType::NotificationRestconf || opType == OperationType::NotificationNetconf) {
|
||||
+ opType = OperationType::NotificationYang;
|
||||
+ } else {
|
||||
+ throw Error("validateOp: DataYang datatype is not supported");
|
||||
+ }
|
||||
+
|
||||
+ auto ret = lyd_validate_op(input.m_node, opsTree ? opsTree->m_node : nullptr, utils::toOpType(opType), nullptr);
|
||||
+ throwIfError(ret, "libyang:validateOp: lyd_validate_op failed");
|
||||
+}
|
||||
+
|
||||
/** @short Find instances matching the provided XPath
|
||||
*
|
||||
* @param contextNode The node which serves as the "context node" for XPath evaluation. Use nullopt to start at root.
|
||||
diff --git a/tests/context.cpp b/tests/context.cpp
|
||||
index 6c5dde8..c0b7e09 100644
|
||||
--- a/tests/context.cpp
|
||||
+++ b/tests/context.cpp
|
||||
@@ -154,7 +154,7 @@ TEST_CASE("context")
|
||||
{
|
||||
auto mod = ctx->parseModule(example_schema, libyang::SchemaFormat::YANG);
|
||||
auto rpcs = mod.actionRpcs();
|
||||
- REQUIRE(rpcs.size() == 1);
|
||||
+ REQUIRE(rpcs.size() == 2);
|
||||
REQUIRE(rpcs[0].module().name() == "example-schema");
|
||||
REQUIRE(rpcs[0].name() == "myRpc");
|
||||
|
||||
diff --git a/tests/data_node.cpp b/tests/data_node.cpp
|
||||
index db5a28e..9215b12 100644
|
||||
--- a/tests/data_node.cpp
|
||||
+++ b/tests/data_node.cpp
|
||||
@@ -2430,6 +2430,78 @@ TEST_CASE("Data Node manipulation")
|
||||
"Can't parse into operation data tree: LY_EVALID", libyang::Error);
|
||||
}
|
||||
}
|
||||
+
|
||||
+ DOCTEST_SUBCASE("Validation")
|
||||
+ {
|
||||
+ DOCTEST_SUBCASE("Valid input")
|
||||
+ {
|
||||
+ std::string rpcInput;
|
||||
+ std::string rpcPath;
|
||||
+ std::string expected;
|
||||
+ std::optional<libyang::DataNode> depTree;
|
||||
+
|
||||
+ DOCTEST_SUBCASE("RPC")
|
||||
+ {
|
||||
+ rpcInput = R"({"example-schema:input": { "number": 42 } })";
|
||||
+ rpcPath = "/example-schema:rpc-with-choice";
|
||||
+ expected = R"({
|
||||
+ "example-schema:rpc-with-choice": {
|
||||
+ "number": 42
|
||||
+ }
|
||||
+}
|
||||
+)";
|
||||
+ }
|
||||
+
|
||||
+ DOCTEST_SUBCASE("Action")
|
||||
+ {
|
||||
+ rpcInput = R"({ "example-schema:input": { "friend": "Kuba" } })";
|
||||
+ rpcPath = "/example-schema:person[name='Franta']/poke-a-friend";
|
||||
+ expected = R"({
|
||||
+ "example-schema:poke-a-friend": {
|
||||
+ "friend": "Kuba"
|
||||
+ }
|
||||
+}
|
||||
+)";
|
||||
+ depTree = ctx.newPath("/example-schema:person[name='Kuba']");
|
||||
+ }
|
||||
+
|
||||
+ auto [parent, rpcTree] = ctx.newPath2(rpcPath);
|
||||
+ auto rpcOp = rpcTree->parseOp(rpcInput, dataTypeFor(rpcInput), libyang::OperationType::RpcRestconf);
|
||||
+
|
||||
+ REQUIRE(!rpcOp.op);
|
||||
+ REQUIRE(rpcOp.tree);
|
||||
+
|
||||
+ libyang::validateOp(*rpcTree, depTree, libyang::OperationType::RpcRestconf);
|
||||
+ REQUIRE(*rpcTree->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::KeepEmptyCont) == expected);
|
||||
+ }
|
||||
+
|
||||
+ DOCTEST_SUBCASE("Nodes in disjunctive cases defined together")
|
||||
+ {
|
||||
+ auto rpcInput = R"({ "example-schema:input": { "number": 42, "text": "The ultimate answer" } })";
|
||||
+
|
||||
+ auto rpcTree = ctx.newPath("/example-schema:rpc-with-choice");
|
||||
+ auto rpcOp = rpcTree.parseOp(rpcInput, dataTypeFor(rpcInput), libyang::OperationType::RpcRestconf);
|
||||
+
|
||||
+ REQUIRE(!rpcOp.op);
|
||||
+ REQUIRE(rpcOp.tree);
|
||||
+
|
||||
+ REQUIRE_THROWS_WITH_AS(libyang::validateOp(rpcTree, std::nullopt, libyang::OperationType::RpcRestconf), "libyang:validateOp: lyd_validate_op failed: LY_EVALID", libyang::Error);
|
||||
+ }
|
||||
+
|
||||
+ DOCTEST_SUBCASE("Action without the leafref node")
|
||||
+ {
|
||||
+ auto rpcInput = R"({ "example-schema:input": { "friend": "Kuba" } })";
|
||||
+ auto rpcPath = "/example-schema:person[name='Franta']/poke-a-friend";
|
||||
+
|
||||
+ auto [parent, rpcTree] = ctx.newPath2(rpcPath);
|
||||
+ auto rpcOp = rpcTree->parseOp(rpcInput, dataTypeFor(rpcInput), libyang::OperationType::RpcRestconf);
|
||||
+
|
||||
+ REQUIRE(!rpcOp.op);
|
||||
+ REQUIRE(rpcOp.tree);
|
||||
+
|
||||
+ REQUIRE_THROWS_WITH_AS(libyang::validateOp(*rpcTree, std::nullopt, libyang::OperationType::RpcRestconf), "libyang:validateOp: lyd_validate_op failed: LY_EVALID", libyang::Error);
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
DOCTEST_SUBCASE("comparing") {
|
||||
diff --git a/tests/example_schema.hpp b/tests/example_schema.hpp
|
||||
index 02d3d74..acc6ecc 100644
|
||||
--- a/tests/example_schema.hpp
|
||||
+++ b/tests/example_schema.hpp
|
||||
@@ -108,6 +108,16 @@ module example-schema {
|
||||
}
|
||||
|
||||
action poke { }
|
||||
+
|
||||
+ action poke-a-friend {
|
||||
+ input {
|
||||
+ leaf friend {
|
||||
+ type leafref {
|
||||
+ path '../../../person/name';
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
leaf bossPerson {
|
||||
@@ -284,6 +294,18 @@ module example-schema {
|
||||
}
|
||||
}
|
||||
}
|
||||
+ rpc rpc-with-choice {
|
||||
+ input {
|
||||
+ choice the-impossible-choice {
|
||||
+ leaf text {
|
||||
+ type string;
|
||||
+ }
|
||||
+ leaf number {
|
||||
+ type int32;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
|
||||
anydata myData {
|
||||
}
|
||||
--
|
||||
2.43.0
|
||||
|
||||
@@ -0,0 +1,435 @@
|
||||
From 4c00c24dee6bbc694d62e7174b7ac14f8aa95f14 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= <jan.kundrat@cesnet.cz>
|
||||
Date: Thu, 30 Oct 2025 21:16:13 +0100
|
||||
Subject: [PATCH 4/7] port to libyang v4
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Organization: Wires
|
||||
|
||||
Apart from the usual API/ABI changes, the default values of a `leaf` or
|
||||
a `leaf-list` are no longer canonicalized.
|
||||
|
||||
This used to be missing from libyang v4, but it was added recently.
|
||||
Sill, chances are that the form which was used in the YANG model's
|
||||
source code is actually *the* intended presentation, so let's skip
|
||||
canonicalization altogether.
|
||||
|
||||
Upstream has removed some flags and defines, so let's sync their list
|
||||
with whatever is currently available in the `devel` branch.
|
||||
|
||||
Change-Id: I52dcec88eeee003b45dd6cf400f4d6875abbcc05
|
||||
Signed-off-by: Mattias Walström <lazzer@gmail.com>
|
||||
---
|
||||
.zuul.yaml | 4 ++--
|
||||
CMakeLists.txt | 4 ++--
|
||||
README.md | 2 +-
|
||||
include/libyang-cpp/Context.hpp | 6 +++++-
|
||||
include/libyang-cpp/DataNode.hpp | 5 ++++-
|
||||
include/libyang-cpp/Enum.hpp | 15 +++++++++++++--
|
||||
src/Context.cpp | 12 ++++++++++--
|
||||
src/DataNode.cpp | 11 +++++++++--
|
||||
src/SchemaNode.cpp | 6 +++---
|
||||
src/Type.cpp | 2 +-
|
||||
src/utils/enum.hpp | 21 +++++++++++++++++++--
|
||||
tests/context.cpp | 32 +++++++++++++++++---------------
|
||||
tests/data_node.cpp | 2 +-
|
||||
tests/schema_node.cpp | 4 ++--
|
||||
14 files changed, 89 insertions(+), 37 deletions(-)
|
||||
|
||||
diff --git a/.zuul.yaml b/.zuul.yaml
|
||||
index fd89549..7b92766 100644
|
||||
--- a/.zuul.yaml
|
||||
+++ b/.zuul.yaml
|
||||
@@ -4,13 +4,13 @@
|
||||
- f38-gcc-cover:
|
||||
required-projects:
|
||||
- name: github/CESNET/libyang
|
||||
- override-checkout: cesnet/2025-08-07
|
||||
+ override-checkout: devel
|
||||
- name: github/doctest/doctest
|
||||
override-checkout: v2.3.6
|
||||
- f38-clang-asan-ubsan:
|
||||
required-projects: &projects
|
||||
- name: github/CESNET/libyang
|
||||
- override-checkout: cesnet/2025-08-07
|
||||
+ override-checkout: devel
|
||||
- name: github/doctest/doctest
|
||||
override-checkout: v2.4.11
|
||||
- f38-clang-tsan:
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index c6db320..90f9aaa 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -20,7 +20,7 @@ add_custom_target(libyang-cpp-version-cmake
|
||||
cmake/ProjectGitVersionRunner.cmake
|
||||
)
|
||||
include(cmake/ProjectGitVersion.cmake)
|
||||
-set(LIBYANG_CPP_PKG_VERSION "4")
|
||||
+set(LIBYANG_CPP_PKG_VERSION "5")
|
||||
prepare_git_version(LIBYANG_CPP_VERSION ${LIBYANG_CPP_PKG_VERSION})
|
||||
|
||||
find_package(Doxygen)
|
||||
@@ -28,7 +28,7 @@ option(WITH_DOCS "Create and install internal documentation (needs Doxygen)" ${D
|
||||
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>=3.10.1 IMPORTED_TARGET)
|
||||
+pkg_check_modules(LIBYANG REQUIRED libyang>=4.1.0 IMPORTED_TARGET)
|
||||
|
||||
# FIXME from gcc 14.1 on we should be able to use the calendar/time from libstdc++ and thus remove the date dependency
|
||||
find_package(date)
|
||||
diff --git a/README.md b/README.md
|
||||
index 8291e00..0cef2ff 100644
|
||||
--- a/README.md
|
||||
+++ b/README.md
|
||||
@@ -8,7 +8,7 @@
|
||||
Object lifetimes are managed automatically via RAII.
|
||||
|
||||
## Dependencies
|
||||
-- [libyang v3](https://github.com/CESNET/libyang) - the `devel` branch (even for the `master` branch of *libyang-cpp*)
|
||||
+- [libyang v4](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+
|
||||
- optionally for built-in tests, [Doctest](https://github.com/doctest/doctest/) as a C++ unit test framework
|
||||
diff --git a/include/libyang-cpp/Context.hpp b/include/libyang-cpp/Context.hpp
|
||||
index ca89063..e695914 100644
|
||||
--- a/include/libyang-cpp/Context.hpp
|
||||
+++ b/include/libyang-cpp/Context.hpp
|
||||
@@ -108,7 +108,11 @@ public:
|
||||
std::optional<SubmoduleParsed> getSubmodule(const std::string& name, const std::optional<std::string>& revision) const;
|
||||
void registerModuleCallback(std::function<ModuleCallback> callback);
|
||||
|
||||
- ParsedOp parseOp(const std::string& input, const DataFormat format, const OperationType opType) const;
|
||||
+ ParsedOp parseOp(
|
||||
+ const std::string& input,
|
||||
+ const DataFormat format,
|
||||
+ const OperationType opType,
|
||||
+ const std::optional<ParseOptions> parseOpts = std::nullopt) const;
|
||||
|
||||
DataNode newPath(const std::string& path, const std::optional<std::string>& value = std::nullopt, const std::optional<CreationOptions> options = std::nullopt) const;
|
||||
CreatedNodes newPath2(const std::string& path, const std::optional<std::string>& value = std::nullopt, const std::optional<CreationOptions> options = std::nullopt) const;
|
||||
diff --git a/include/libyang-cpp/DataNode.hpp b/include/libyang-cpp/DataNode.hpp
|
||||
index e202a96..35e5936 100644
|
||||
--- a/include/libyang-cpp/DataNode.hpp
|
||||
+++ b/include/libyang-cpp/DataNode.hpp
|
||||
@@ -123,7 +123,10 @@ public:
|
||||
Collection<DataNode, IterationType::Sibling> siblings() const;
|
||||
Collection<DataNode, IterationType::Sibling> immediateChildren() const;
|
||||
|
||||
- ParsedOp parseOp(const std::string& input, const DataFormat format, const OperationType opType) const;
|
||||
+ ParsedOp parseOp(const std::string& input,
|
||||
+ const DataFormat format,
|
||||
+ const OperationType opType,
|
||||
+ const std::optional<ParseOptions> parseOpts = std::nullopt) const;
|
||||
|
||||
void parseSubtree(
|
||||
const std::string& data,
|
||||
diff --git a/include/libyang-cpp/Enum.hpp b/include/libyang-cpp/Enum.hpp
|
||||
index 85dcc83..32142f9 100644
|
||||
--- a/include/libyang-cpp/Enum.hpp
|
||||
+++ b/include/libyang-cpp/Enum.hpp
|
||||
@@ -136,6 +136,7 @@ enum class DuplicationOptions : uint32_t {
|
||||
WithFlags = 0x08,
|
||||
NoExt = 0x10,
|
||||
WithPriv = 0x20,
|
||||
+ NoLyds = 0x40,
|
||||
};
|
||||
|
||||
enum class NodeType : uint16_t {
|
||||
@@ -167,6 +168,13 @@ enum class ContextOptions : uint16_t {
|
||||
PreferSearchDirs = 0x20,
|
||||
SetPrivParsed = 0x40,
|
||||
ExplicitCompile = 0x80,
|
||||
+ EnableImpFeatures = 0x100,
|
||||
+ CompileObsolete = 0x200,
|
||||
+ LybHashes = 0x400,
|
||||
+ LeafrefExtended = 0x800,
|
||||
+ LeafrefLinking = 0x1000,
|
||||
+ BuiltinPluginsOnly = 0x2000,
|
||||
+ StaticPluginsOnly = 0x4000,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -203,7 +211,6 @@ enum class AnydataValueType : uint32_t {
|
||||
String,
|
||||
XML,
|
||||
JSON,
|
||||
- LYB
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -252,6 +259,7 @@ enum class ValidationOptions {
|
||||
MultiError = 0x0004,
|
||||
Operational = 0x0008,
|
||||
NoDefaults = 0x0010,
|
||||
+ NotFinal = 0x0020,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -262,11 +270,14 @@ enum class ParseOptions {
|
||||
Strict = 0x020000,
|
||||
Opaque = 0x040000,
|
||||
NoState = 0x080000,
|
||||
- LybModUpdate = 0x100000,
|
||||
+ LybSkipCtxCheck = 0x100000,
|
||||
Ordered = 0x200000,
|
||||
Subtree = 0x400000, /**< Do not use this one for parsing of data subtrees */
|
||||
WhenTrue = 0x800000,
|
||||
NoNew = 0x1000000,
|
||||
+ StoreOnly = 0x2010000,
|
||||
+ JsonNull = 0x4000000,
|
||||
+ JsonStringDataTypes = 0x8000000,
|
||||
};
|
||||
|
||||
/**
|
||||
diff --git a/src/Context.cpp b/src/Context.cpp
|
||||
index fec2f27..f5bdc71 100644
|
||||
--- a/src/Context.cpp
|
||||
+++ b/src/Context.cpp
|
||||
@@ -239,7 +239,7 @@ std::optional<DataNode> Context::parseExtData(
|
||||
* Note: to parse a NETCONF RPC reply, you MUST parse the original NETCONF RPC request (that is, you have to use
|
||||
* this method with OperationType::RpcNetconf).
|
||||
*/
|
||||
-ParsedOp Context::parseOp(const std::string& input, const DataFormat format, const OperationType opType) const
|
||||
+ParsedOp Context::parseOp(const std::string& input, const DataFormat format, const OperationType opType, const std::optional<ParseOptions> parseOpts) const
|
||||
{
|
||||
auto in = wrap_ly_in_new_memory(input);
|
||||
|
||||
@@ -251,7 +251,15 @@ ParsedOp Context::parseOp(const std::string& input, const DataFormat format, con
|
||||
case OperationType::NotificationYang: {
|
||||
lyd_node* op = nullptr;
|
||||
lyd_node* tree = nullptr;
|
||||
- auto err = lyd_parse_op(m_ctx.get(), nullptr, in.get(), utils::toLydFormat(format), utils::toOpType(opType), &tree, &op);
|
||||
+ auto err = lyd_parse_op(
|
||||
+ m_ctx.get(),
|
||||
+ nullptr,
|
||||
+ in.get(),
|
||||
+ utils::toLydFormat(format),
|
||||
+ utils::toOpType(opType),
|
||||
+ parseOpts ? utils::toParseOptions(*parseOpts) : 0,
|
||||
+ &tree,
|
||||
+ &op);
|
||||
|
||||
ParsedOp res;
|
||||
res.tree = tree ? std::optional{libyang::wrapRawNode(tree)} : std::nullopt;
|
||||
diff --git a/src/DataNode.cpp b/src/DataNode.cpp
|
||||
index e28ef30..edabf9b 100644
|
||||
--- a/src/DataNode.cpp
|
||||
+++ b/src/DataNode.cpp
|
||||
@@ -391,7 +391,7 @@ DataNodeAny DataNode::asAny() const
|
||||
*
|
||||
* Wraps `lyd_parse_op`.
|
||||
*/
|
||||
-ParsedOp DataNode::parseOp(const std::string& input, const DataFormat format, const OperationType opType) const
|
||||
+ParsedOp DataNode::parseOp(const std::string& input, const DataFormat format, const OperationType opType, const std::optional<ParseOptions> parseOpts) const
|
||||
{
|
||||
auto in = wrap_ly_in_new_memory(input);
|
||||
|
||||
@@ -401,7 +401,14 @@ ParsedOp DataNode::parseOp(const std::string& input, const DataFormat format, co
|
||||
case OperationType::ReplyRestconf: {
|
||||
lyd_node* op = nullptr;
|
||||
lyd_node* tree = nullptr;
|
||||
- auto err = lyd_parse_op(m_node->schema->module->ctx, m_node, in.get(), utils::toLydFormat(format), utils::toOpType(opType), &tree, nullptr);
|
||||
+ auto err = lyd_parse_op(m_node->schema->module->ctx,
|
||||
+ m_node,
|
||||
+ in.get(),
|
||||
+ utils::toLydFormat(format),
|
||||
+ utils::toOpType(opType),
|
||||
+ parseOpts ? utils::toParseOptions(*parseOpts) : 0,
|
||||
+ &tree,
|
||||
+ nullptr);
|
||||
ParsedOp res{
|
||||
.tree = tree ? std::optional{libyang::wrapRawNode(tree)} : std::nullopt,
|
||||
.op = op ? std::optional{libyang::wrapRawNode(op)} : std::nullopt
|
||||
diff --git a/src/SchemaNode.cpp b/src/SchemaNode.cpp
|
||||
index ce24203..2e0351a 100644
|
||||
--- a/src/SchemaNode.cpp
|
||||
+++ b/src/SchemaNode.cpp
|
||||
@@ -564,7 +564,7 @@ std::vector<std::string> LeafList::defaultValuesStr() const
|
||||
auto dflts = reinterpret_cast<const lysc_node_leaflist*>(m_node)->dflts;
|
||||
std::vector<std::string> res;
|
||||
for (const auto& it : std::span(dflts, LY_ARRAY_COUNT(dflts))) {
|
||||
- res.emplace_back(lyd_value_get_canonical(m_ctx.get(), it));
|
||||
+ res.emplace_back(it.str);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@@ -660,8 +660,8 @@ bool LeafList::isUserOrdered() const
|
||||
std::optional<std::string> Leaf::defaultValueStr() const
|
||||
{
|
||||
auto dflt = reinterpret_cast<const lysc_node_leaf*>(m_node)->dflt;
|
||||
- if (dflt) {
|
||||
- return lyd_value_get_canonical(m_ctx.get(), dflt);
|
||||
+ if (dflt.str) {
|
||||
+ return std::string{dflt.str};
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
diff --git a/src/Type.cpp b/src/Type.cpp
|
||||
index 01b2c4f..4d2db46 100644
|
||||
--- a/src/Type.cpp
|
||||
+++ b/src/Type.cpp
|
||||
@@ -240,7 +240,7 @@ std::optional<std::string> Type::description() const
|
||||
*/
|
||||
std::string Type::internalPluginId() const
|
||||
{
|
||||
- return m_type->plugin->id;
|
||||
+ return lysc_get_type_plugin(m_type->plugin_ref)->id;
|
||||
}
|
||||
|
||||
/**
|
||||
diff --git a/src/utils/enum.hpp b/src/utils/enum.hpp
|
||||
index 5e02d24..0b2e2d7 100644
|
||||
--- a/src/utils/enum.hpp
|
||||
+++ b/src/utils/enum.hpp
|
||||
@@ -91,6 +91,7 @@ static_assert(LYD_DUP_WITH_FLAGS == toDuplicationOptions(DuplicationOptions::Wit
|
||||
static_assert(LYD_DUP_WITH_PARENTS == toDuplicationOptions(DuplicationOptions::WithParents));
|
||||
static_assert(LYD_DUP_NO_EXT == toDuplicationOptions(DuplicationOptions::NoExt));
|
||||
static_assert(LYD_DUP_WITH_PRIV == toDuplicationOptions(DuplicationOptions::WithPriv));
|
||||
+static_assert(LYD_DUP_NO_LYDS == toDuplicationOptions(DuplicationOptions::NoLyds));
|
||||
|
||||
static_assert((LYD_DUP_NO_META | LYD_DUP_NO_EXT) ==
|
||||
toDuplicationOptions(DuplicationOptions::NoMeta | DuplicationOptions::NoExt));
|
||||
@@ -131,6 +132,13 @@ static_assert(toContextOptions(ContextOptions::DisableSearchCwd) == LY_CTX_DISAB
|
||||
static_assert(toContextOptions(ContextOptions::PreferSearchDirs) == LY_CTX_PREFER_SEARCHDIRS);
|
||||
static_assert(toContextOptions(ContextOptions::SetPrivParsed) == LY_CTX_SET_PRIV_PARSED);
|
||||
static_assert(toContextOptions(ContextOptions::ExplicitCompile) == LY_CTX_EXPLICIT_COMPILE);
|
||||
+static_assert(toContextOptions(ContextOptions::EnableImpFeatures) == LY_CTX_ENABLE_IMP_FEATURES);
|
||||
+static_assert(toContextOptions(ContextOptions::CompileObsolete) == LY_CTX_COMPILE_OBSOLETE);
|
||||
+static_assert(toContextOptions(ContextOptions::LybHashes) == LY_CTX_LYB_HASHES);
|
||||
+static_assert(toContextOptions(ContextOptions::LeafrefExtended) == LY_CTX_LEAFREF_EXTENDED);
|
||||
+static_assert(toContextOptions(ContextOptions::LeafrefLinking) == LY_CTX_LEAFREF_LINKING);
|
||||
+static_assert(toContextOptions(ContextOptions::BuiltinPluginsOnly) == LY_CTX_BUILTIN_PLUGINS_ONLY);
|
||||
+static_assert(toContextOptions(ContextOptions::StaticPluginsOnly) == LY_CTX_STATIC_PLUGINS_ONLY);
|
||||
|
||||
constexpr uint16_t toLogOptions(const LogOptions options)
|
||||
{
|
||||
@@ -198,6 +206,10 @@ constexpr uint32_t toValidationOptions(const ValidationOptions opts)
|
||||
|
||||
static_assert(toValidationOptions(ValidationOptions::NoState) == LYD_VALIDATE_NO_STATE);
|
||||
static_assert(toValidationOptions(ValidationOptions::Present) == LYD_VALIDATE_PRESENT);
|
||||
+static_assert(toValidationOptions(ValidationOptions::MultiError) == LYD_VALIDATE_MULTI_ERROR);
|
||||
+static_assert(toValidationOptions(ValidationOptions::Operational) == LYD_VALIDATE_OPERATIONAL);
|
||||
+static_assert(toValidationOptions(ValidationOptions::NoDefaults) == LYD_VALIDATE_NO_DEFAULTS);
|
||||
+static_assert(toValidationOptions(ValidationOptions::NotFinal) == LYD_VALIDATE_NOT_FINAL);
|
||||
|
||||
constexpr uint32_t toParseOptions(const ParseOptions opts)
|
||||
{
|
||||
@@ -208,8 +220,14 @@ static_assert(toParseOptions(ParseOptions::ParseOnly) == LYD_PARSE_ONLY);
|
||||
static_assert(toParseOptions(ParseOptions::Strict) == LYD_PARSE_STRICT);
|
||||
static_assert(toParseOptions(ParseOptions::Opaque) == LYD_PARSE_OPAQ);
|
||||
static_assert(toParseOptions(ParseOptions::NoState) == LYD_PARSE_NO_STATE);
|
||||
-static_assert(toParseOptions(ParseOptions::LybModUpdate) == LYD_PARSE_LYB_MOD_UPDATE);
|
||||
+static_assert(toParseOptions(ParseOptions::LybSkipCtxCheck) == LYD_PARSE_LYB_SKIP_CTX_CHECK);
|
||||
static_assert(toParseOptions(ParseOptions::Ordered) == LYD_PARSE_ORDERED);
|
||||
+static_assert(toParseOptions(ParseOptions::Subtree) == LYD_PARSE_SUBTREE);
|
||||
+static_assert(toParseOptions(ParseOptions::WhenTrue) == LYD_PARSE_WHEN_TRUE);
|
||||
+static_assert(toParseOptions(ParseOptions::NoNew) == LYD_PARSE_NO_NEW);
|
||||
+static_assert(toParseOptions(ParseOptions::StoreOnly) == LYD_PARSE_STORE_ONLY);
|
||||
+static_assert(toParseOptions(ParseOptions::JsonNull) == LYD_PARSE_JSON_NULL);
|
||||
+static_assert(toParseOptions(ParseOptions::JsonStringDataTypes) == LYD_PARSE_JSON_STRING_DATATYPES);
|
||||
|
||||
constexpr lyd_type toOpType(const OperationType type)
|
||||
{
|
||||
@@ -242,7 +260,6 @@ static_assert(toAnydataValueType(AnydataValueType::DataTree) == LYD_ANYDATA_DATA
|
||||
static_assert(toAnydataValueType(AnydataValueType::String) == LYD_ANYDATA_STRING);
|
||||
static_assert(toAnydataValueType(AnydataValueType::XML) == LYD_ANYDATA_XML);
|
||||
static_assert(toAnydataValueType(AnydataValueType::JSON) == LYD_ANYDATA_JSON);
|
||||
-static_assert(toAnydataValueType(AnydataValueType::LYB) == LYD_ANYDATA_LYB);
|
||||
|
||||
constexpr LYS_OUTFORMAT toLysOutFormat(const SchemaOutputFormat format)
|
||||
{
|
||||
diff --git a/tests/context.cpp b/tests/context.cpp
|
||||
index c0b7e09..eaedebf 100644
|
||||
--- a/tests/context.cpp
|
||||
+++ b/tests/context.cpp
|
||||
@@ -359,25 +359,27 @@ TEST_CASE("context")
|
||||
ctx->loadModule("mod1", std::nullopt, {});
|
||||
ctx->parseModule(valid_yang_model, libyang::SchemaFormat::YANG);
|
||||
auto modules = ctx->modules();
|
||||
- REQUIRE(modules.size() == 8);
|
||||
+ REQUIRE(modules.size() == 9);
|
||||
REQUIRE(modules.at(0).name() == "ietf-yang-metadata");
|
||||
REQUIRE(modules.at(0).ns() == "urn:ietf:params:xml:ns:yang:ietf-yang-metadata");
|
||||
REQUIRE(modules.at(1).name() == "yang");
|
||||
REQUIRE(modules.at(1).ns() == "urn:ietf:params:xml:ns:yang:1");
|
||||
- REQUIRE(modules.at(2).name() == "ietf-inet-types");
|
||||
- REQUIRE(modules.at(2).ns() == "urn:ietf:params:xml:ns:yang:ietf-inet-types");
|
||||
- REQUIRE(modules.at(3).name() == "ietf-yang-types");
|
||||
- REQUIRE(modules.at(3).ns() == "urn:ietf:params:xml:ns:yang:ietf-yang-types");
|
||||
- REQUIRE(modules.at(4).name() == "ietf-yang-schema-mount");
|
||||
- REQUIRE(modules.at(4).ns() == "urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount");
|
||||
- REQUIRE(modules.at(5).name() == "ietf-yang-structure-ext");
|
||||
- REQUIRE(modules.at(5).ns() == "urn:ietf:params:xml:ns:yang:ietf-yang-structure-ext");
|
||||
- REQUIRE(modules.at(6).name() == "mod1");
|
||||
- REQUIRE(modules.at(6).ns() == "http://example.com");
|
||||
- REQUIRE(*modules.at(6).revision() == "2021-11-15");
|
||||
- REQUIRE(modules.at(7).name() == "test");
|
||||
+ REQUIRE(modules.at(2).name() == "default");
|
||||
+ REQUIRE(modules.at(2).ns() == "urn:ietf:params:xml:ns:netconf:default:1.0");
|
||||
+ REQUIRE(modules.at(3).name() == "ietf-inet-types");
|
||||
+ REQUIRE(modules.at(3).ns() == "urn:ietf:params:xml:ns:yang:ietf-inet-types");
|
||||
+ REQUIRE(modules.at(4).name() == "ietf-yang-types");
|
||||
+ REQUIRE(modules.at(4).ns() == "urn:ietf:params:xml:ns:yang:ietf-yang-types");
|
||||
+ REQUIRE(modules.at(5).name() == "ietf-yang-schema-mount");
|
||||
+ REQUIRE(modules.at(5).ns() == "urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount");
|
||||
+ REQUIRE(modules.at(6).name() == "ietf-yang-structure-ext");
|
||||
+ REQUIRE(modules.at(6).ns() == "urn:ietf:params:xml:ns:yang:ietf-yang-structure-ext");
|
||||
+ REQUIRE(modules.at(7).name() == "mod1");
|
||||
REQUIRE(modules.at(7).ns() == "http://example.com");
|
||||
- REQUIRE(modules.at(7).revision() == std::nullopt);
|
||||
+ REQUIRE(*modules.at(7).revision() == "2021-11-15");
|
||||
+ REQUIRE(modules.at(8).name() == "test");
|
||||
+ REQUIRE(modules.at(8).ns() == "http://example.com");
|
||||
+ REQUIRE(modules.at(8).revision() == std::nullopt);
|
||||
}
|
||||
|
||||
DOCTEST_SUBCASE("Module comparison")
|
||||
@@ -704,7 +706,7 @@ TEST_CASE("context")
|
||||
|
||||
DOCTEST_SUBCASE("schema printing")
|
||||
{
|
||||
- std::optional<libyang::Context> ctx_pp{std::in_place, std::nullopt, libyang::ContextOptions::NoYangLibrary | libyang::ContextOptions::DisableSearchCwd | libyang::ContextOptions::SetPrivParsed};
|
||||
+ std::optional<libyang::Context> ctx_pp{std::in_place, std::nullopt, libyang::ContextOptions::NoYangLibrary | libyang::ContextOptions::DisableSearchCwd | libyang::ContextOptions::SetPrivParsed | libyang::ContextOptions::CompileObsolete};
|
||||
auto mod = ctx_pp->parseModule(type_module, libyang::SchemaFormat::YANG);
|
||||
|
||||
REQUIRE(mod.printStr(libyang::SchemaOutputFormat::Tree) == R"(module: type_module
|
||||
diff --git a/tests/data_node.cpp b/tests/data_node.cpp
|
||||
index 9215b12..88ca5b9 100644
|
||||
--- a/tests/data_node.cpp
|
||||
+++ b/tests/data_node.cpp
|
||||
@@ -2426,7 +2426,7 @@ TEST_CASE("Data Node manipulation")
|
||||
"WTF": "foo bar baz"
|
||||
}
|
||||
}
|
||||
- )", libyang::DataFormat::JSON, libyang::OperationType::RpcRestconf),
|
||||
+ )", libyang::DataFormat::JSON, libyang::OperationType::RpcRestconf, libyang::ParseOptions::Strict),
|
||||
"Can't parse into operation data tree: LY_EVALID", libyang::Error);
|
||||
}
|
||||
}
|
||||
diff --git a/tests/schema_node.cpp b/tests/schema_node.cpp
|
||||
index 65ef462..58152a4 100644
|
||||
--- a/tests/schema_node.cpp
|
||||
+++ b/tests/schema_node.cpp
|
||||
@@ -19,7 +19,7 @@ using namespace std::string_literals;
|
||||
TEST_CASE("SchemaNode")
|
||||
{
|
||||
std::optional<libyang::Context> ctx{std::in_place, std::nullopt,
|
||||
- libyang::ContextOptions::NoYangLibrary | libyang::ContextOptions::DisableSearchCwd};
|
||||
+ libyang::ContextOptions::NoYangLibrary | libyang::ContextOptions::DisableSearchCwd | libyang::ContextOptions::CompileObsolete};
|
||||
std::optional<libyang::Context> ctxWithParsed{std::in_place, std::nullopt,
|
||||
libyang::ContextOptions::SetPrivParsed | libyang::ContextOptions::NoYangLibrary | libyang::ContextOptions::DisableSearchCwd};
|
||||
ctx->parseModule(example_schema, libyang::SchemaFormat::YANG);
|
||||
@@ -841,7 +841,7 @@ TEST_CASE("SchemaNode")
|
||||
|
||||
DOCTEST_SUBCASE("LeafList::defaultValuesStr")
|
||||
{
|
||||
- REQUIRE(ctx->findPath("/type_module:leafListWithDefault").asLeafList().defaultValuesStr() == std::vector<std::string>{"-1", "512", "1024", "2048"});
|
||||
+ REQUIRE(ctx->findPath("/type_module:leafListWithDefault").asLeafList().defaultValuesStr() == std::vector<std::string>{"-1", "+512", "0x400", "04000"});
|
||||
REQUIRE(ctx->findPath("/type_module:leafListBasic").asLeafList().defaultValuesStr().size() == 0);
|
||||
}
|
||||
|
||||
--
|
||||
2.43.0
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
From 51d7457bd281e34bac92ff8231978766c7784657 Mon Sep 17 00:00:00 2001
|
||||
From: Martin Bohal <mb@mbohal.cz>
|
||||
Date: Tue, 4 Nov 2025 14:26:05 +0100
|
||||
Subject: [PATCH 5/7] Add SchemaNode::isOutput() method
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Organization: Wires
|
||||
|
||||
This commit introduces the `isOutput()` method to the `SchemaNode`
|
||||
class, which checks whether the schema node is part of an output
|
||||
statement subtree.
|
||||
|
||||
It complements an already existing `isInput()` method.
|
||||
|
||||
Change-Id: I8fe75f2e607c0159d8295d685a59d301155ca92e
|
||||
Signed-off-by: Mattias Walström <lazzer@gmail.com>
|
||||
---
|
||||
include/libyang-cpp/SchemaNode.hpp | 1 +
|
||||
src/SchemaNode.cpp | 10 ++++++++++
|
||||
tests/schema_node.cpp | 7 +++++++
|
||||
3 files changed, 18 insertions(+)
|
||||
|
||||
diff --git a/include/libyang-cpp/SchemaNode.hpp b/include/libyang-cpp/SchemaNode.hpp
|
||||
index 9f49fd7..db5a63e 100644
|
||||
--- a/include/libyang-cpp/SchemaNode.hpp
|
||||
+++ b/include/libyang-cpp/SchemaNode.hpp
|
||||
@@ -58,6 +58,7 @@ public:
|
||||
Status status() const;
|
||||
Config config() const;
|
||||
bool isInput() const;
|
||||
+ bool isOutput() const;
|
||||
NodeType nodeType() const;
|
||||
// It is possible to cast SchemaNode to another type via the following methods. The types are children classes of
|
||||
// SchemaNode. No problems with slicing can occur, because these types are value-based and aren't constructible
|
||||
diff --git a/src/SchemaNode.cpp b/src/SchemaNode.cpp
|
||||
index 2e0351a..9241b23 100644
|
||||
--- a/src/SchemaNode.cpp
|
||||
+++ b/src/SchemaNode.cpp
|
||||
@@ -193,6 +193,16 @@ bool SchemaNode::isInput() const
|
||||
return m_node->flags & LYS_INPUT;
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * @brief Checks whether this node is inside a subtree of an output statement.
|
||||
+ *
|
||||
+ * Wraps `LYS_OUTPUT`.
|
||||
+ */
|
||||
+bool SchemaNode::isOutput() const
|
||||
+{
|
||||
+ return m_node->flags & LYS_OUTPUT;
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* Returns the node type of this node (e.g. leaf, container...).
|
||||
*
|
||||
diff --git a/tests/schema_node.cpp b/tests/schema_node.cpp
|
||||
index 58152a4..01c2f19 100644
|
||||
--- a/tests/schema_node.cpp
|
||||
+++ b/tests/schema_node.cpp
|
||||
@@ -135,6 +135,13 @@ TEST_CASE("SchemaNode")
|
||||
REQUIRE(!ctx->findPath("/type_module:leafString").isInput());
|
||||
}
|
||||
|
||||
+ DOCTEST_SUBCASE("SchemaNode::isOutput")
|
||||
+ {
|
||||
+ REQUIRE(ctx->findPath("/example-schema:myRpc/outputLeaf", libyang::InputOutputNodes::Output).isOutput());
|
||||
+ REQUIRE(!ctx->findPath("/example-schema:myRpc/inputLeaf").isOutput());
|
||||
+ REQUIRE(!ctx->findPath("/type_module:leafString").isOutput());
|
||||
+ }
|
||||
+
|
||||
DOCTEST_SUBCASE("SchemaNode::module")
|
||||
{
|
||||
REQUIRE(ctx->findPath("/type_module:leafString").module().name() == "type_module");
|
||||
--
|
||||
2.43.0
|
||||
|
||||
@@ -0,0 +1,364 @@
|
||||
From 38d5c10009df602594e1b9a53bac41111a9ba430 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= <jan.kundrat@cesnet.cz>
|
||||
Date: Thu, 20 Nov 2025 15:32:03 +0100
|
||||
Subject: [PATCH 6/7] adapt to changes in libyang v4.2
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Organization: Wires
|
||||
|
||||
I was *so* tempted to leave the C++ enums as-is, just to save us from
|
||||
this needless sed exercise, but hey, let's not start confusing any
|
||||
possible downstream consumers now that upstream has changed these
|
||||
identifiers.
|
||||
|
||||
Change-Id: I9b34585822b41be0ca1578051f1c0eb8588a0a51
|
||||
Signed-off-by: Mattias Walström <lazzer@gmail.com>
|
||||
---
|
||||
CMakeLists.txt | 4 +--
|
||||
README.md | 2 +-
|
||||
include/libyang-cpp/Enum.hpp | 4 +--
|
||||
src/utils/enum.hpp | 4 +--
|
||||
tests/context.cpp | 10 +++----
|
||||
tests/data_node.cpp | 58 ++++++++++++++++++------------------
|
||||
6 files changed, 41 insertions(+), 41 deletions(-)
|
||||
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index 90f9aaa..cbaf82a 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -20,7 +20,7 @@ add_custom_target(libyang-cpp-version-cmake
|
||||
cmake/ProjectGitVersionRunner.cmake
|
||||
)
|
||||
include(cmake/ProjectGitVersion.cmake)
|
||||
-set(LIBYANG_CPP_PKG_VERSION "5")
|
||||
+set(LIBYANG_CPP_PKG_VERSION "6")
|
||||
prepare_git_version(LIBYANG_CPP_VERSION ${LIBYANG_CPP_PKG_VERSION})
|
||||
|
||||
find_package(Doxygen)
|
||||
@@ -28,7 +28,7 @@ option(WITH_DOCS "Create and install internal documentation (needs Doxygen)" ${D
|
||||
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>=4.1.0 IMPORTED_TARGET)
|
||||
+pkg_check_modules(LIBYANG REQUIRED libyang>=4.2.1 IMPORTED_TARGET)
|
||||
|
||||
# FIXME from gcc 14.1 on we should be able to use the calendar/time from libstdc++ and thus remove the date dependency
|
||||
find_package(date)
|
||||
diff --git a/README.md b/README.md
|
||||
index 0cef2ff..842573f 100644
|
||||
--- a/README.md
|
||||
+++ b/README.md
|
||||
@@ -8,7 +8,7 @@
|
||||
Object lifetimes are managed automatically via RAII.
|
||||
|
||||
## Dependencies
|
||||
-- [libyang v4](https://github.com/CESNET/libyang) - the `devel` branch (even for the `master` branch of *libyang-cpp*)
|
||||
+- [libyang v4.2+](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+
|
||||
- optionally for built-in tests, [Doctest](https://github.com/doctest/doctest/) as a C++ unit test framework
|
||||
diff --git a/include/libyang-cpp/Enum.hpp b/include/libyang-cpp/Enum.hpp
|
||||
index 32142f9..096e030 100644
|
||||
--- a/include/libyang-cpp/Enum.hpp
|
||||
+++ b/include/libyang-cpp/Enum.hpp
|
||||
@@ -67,9 +67,9 @@ enum class OperationType : uint32_t {
|
||||
*/
|
||||
enum class PrintFlags : uint32_t {
|
||||
WithDefaultsExplicit = 0x00,
|
||||
- WithSiblings = 0x01,
|
||||
+ Siblings = 0x01,
|
||||
Shrink = 0x02,
|
||||
- KeepEmptyCont = 0x04,
|
||||
+ EmptyContainers = 0x04,
|
||||
WithDefaultsTrim = 0x10,
|
||||
WithDefaultsAll = 0x20,
|
||||
WithDefaultsAllTag = 0x40,
|
||||
diff --git a/src/utils/enum.hpp b/src/utils/enum.hpp
|
||||
index 0b2e2d7..a8902ff 100644
|
||||
--- a/src/utils/enum.hpp
|
||||
+++ b/src/utils/enum.hpp
|
||||
@@ -33,7 +33,7 @@ constexpr uint32_t toPrintFlags(const PrintFlags flags)
|
||||
}
|
||||
// These tests ensure that I used the right numbers when defining my enum.
|
||||
// TODO: add asserts for operator|(PrintFlags, PrintFlags)
|
||||
-static_assert(LYD_PRINT_KEEPEMPTYCONT == toPrintFlags(PrintFlags::KeepEmptyCont));
|
||||
+static_assert(LYD_PRINT_EMPTY_CONT == toPrintFlags(PrintFlags::EmptyContainers));
|
||||
static_assert(LYD_PRINT_SHRINK == toPrintFlags(PrintFlags::Shrink));
|
||||
static_assert(LYD_PRINT_WD_ALL == toPrintFlags(PrintFlags::WithDefaultsAll));
|
||||
static_assert(LYD_PRINT_WD_ALL_TAG == toPrintFlags(PrintFlags::WithDefaultsAllTag));
|
||||
@@ -41,7 +41,7 @@ static_assert(LYD_PRINT_WD_EXPLICIT == toPrintFlags(PrintFlags::WithDefaultsExpl
|
||||
static_assert(LYD_PRINT_WD_IMPL_TAG == toPrintFlags(PrintFlags::WithDefaultsImplicitTag));
|
||||
static_assert(LYD_PRINT_WD_MASK == toPrintFlags(PrintFlags::WithDefaultsMask));
|
||||
static_assert(LYD_PRINT_WD_TRIM == toPrintFlags(PrintFlags::WithDefaultsTrim));
|
||||
-static_assert(LYD_PRINT_WITHSIBLINGS == toPrintFlags(PrintFlags::WithSiblings));
|
||||
+static_assert(LYD_PRINT_SIBLINGS == toPrintFlags(PrintFlags::Siblings));
|
||||
|
||||
#ifndef _MSC_VER
|
||||
// MSVC doesn't respect the underlying enum size
|
||||
diff --git a/tests/context.cpp b/tests/context.cpp
|
||||
index eaedebf..355860b 100644
|
||||
--- a/tests/context.cpp
|
||||
+++ b/tests/context.cpp
|
||||
@@ -502,7 +502,7 @@ TEST_CASE("context")
|
||||
auto errorsNode = node->findXPath("/ietf-restconf:errors");
|
||||
REQUIRE(errorsNode.size() == 1);
|
||||
REQUIRE(errorsNode.begin()->path() == "/ietf-restconf:errors");
|
||||
- REQUIRE(*errorsNode.begin()->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings | libyang::PrintFlags::KeepEmptyCont) == R"({
|
||||
+ REQUIRE(*errorsNode.begin()->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Siblings | libyang::PrintFlags::EmptyContainers) == R"({
|
||||
"ietf-restconf:errors": {
|
||||
"error": [
|
||||
{
|
||||
@@ -589,7 +589,7 @@ TEST_CASE("context")
|
||||
|
||||
auto firstValue = edits.begin()->findPath("value");
|
||||
REQUIRE(firstValue);
|
||||
- REQUIRE(*firstValue->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::KeepEmptyCont) == R"({
|
||||
+ REQUIRE(*firstValue->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::EmptyContainers) == R"({
|
||||
"ietf-yang-patch:value": {
|
||||
"example-schema:person": {
|
||||
"name": "John"
|
||||
@@ -597,7 +597,7 @@ TEST_CASE("context")
|
||||
}
|
||||
}
|
||||
)");
|
||||
- REQUIRE(*firstValue->printStr(libyang::DataFormat::XML, libyang::PrintFlags::KeepEmptyCont) == R"(<value xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
|
||||
+ REQUIRE(*firstValue->printStr(libyang::DataFormat::XML, libyang::PrintFlags::EmptyContainers) == R"(<value xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
|
||||
<person xmlns="http://example.com/coze">
|
||||
<name>John</name>
|
||||
</person>
|
||||
@@ -607,8 +607,8 @@ TEST_CASE("context")
|
||||
auto secondValueNode = (edits.begin() + 1)->findPath("value");
|
||||
REQUIRE(secondValueNode);
|
||||
auto secondValue = std::get<libyang::DataNode>(secondValueNode->asAny().releaseValue().value());
|
||||
- REQUIRE(*secondValue.printStr(libyang::DataFormat::JSON, libyang::PrintFlags::KeepEmptyCont) == "{\n \"example-schema:dummy\": \"I am a dummy\"\n}\n");
|
||||
- REQUIRE(*secondValue.printStr(libyang::DataFormat::XML, libyang::PrintFlags::KeepEmptyCont) == "<dummy xmlns=\"http://example.com/coze\">I am a dummy</dummy>\n");
|
||||
+ REQUIRE(*secondValue.printStr(libyang::DataFormat::JSON, libyang::PrintFlags::EmptyContainers) == "{\n \"example-schema:dummy\": \"I am a dummy\"\n}\n");
|
||||
+ REQUIRE(*secondValue.printStr(libyang::DataFormat::XML, libyang::PrintFlags::EmptyContainers) == "<dummy xmlns=\"http://example.com/coze\">I am a dummy</dummy>\n");
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/tests/data_node.cpp b/tests/data_node.cpp
|
||||
index 88ca5b9..3f874de 100644
|
||||
--- a/tests/data_node.cpp
|
||||
+++ b/tests/data_node.cpp
|
||||
@@ -137,7 +137,7 @@ TEST_CASE("Data Node manipulation")
|
||||
DOCTEST_SUBCASE("Printing")
|
||||
{
|
||||
auto node = ctx.parseData(data, libyang::DataFormat::JSON);
|
||||
- auto str = node->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings | libyang::PrintFlags::KeepEmptyCont);
|
||||
+ auto str = node->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Siblings | libyang::PrintFlags::EmptyContainers);
|
||||
const auto expected = R"({
|
||||
"example-schema:leafInt32": 420,
|
||||
"example-schema:first": {
|
||||
@@ -158,7 +158,7 @@ TEST_CASE("Data Node manipulation")
|
||||
REQUIRE(str == expected);
|
||||
|
||||
auto emptyCont = ctx.newPath("/example-schema:first");
|
||||
- REQUIRE(emptyCont.printStr(libyang::DataFormat::XML, libyang::PrintFlags::WithSiblings) == std::nullopt);
|
||||
+ REQUIRE(emptyCont.printStr(libyang::DataFormat::XML, libyang::PrintFlags::Siblings) == std::nullopt);
|
||||
}
|
||||
|
||||
DOCTEST_SUBCASE("Overwriting a tree with a different tree")
|
||||
@@ -504,7 +504,7 @@ TEST_CASE("Data Node manipulation")
|
||||
{
|
||||
auto node = std::optional{ctx.newPath("/example-schema:leafInt32", "420")};
|
||||
libyang::validateAll(node, libyang::ValidationOptions::NoState);
|
||||
- auto str = node->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings | libyang::PrintFlags::KeepEmptyCont);
|
||||
+ auto str = node->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Siblings | libyang::PrintFlags::EmptyContainers);
|
||||
REQUIRE(str == data);
|
||||
}
|
||||
|
||||
@@ -725,7 +725,7 @@ TEST_CASE("Data Node manipulation")
|
||||
}
|
||||
|
||||
// The original tree should still be accesible.
|
||||
- node->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings);
|
||||
+ node->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Siblings);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -855,7 +855,7 @@ TEST_CASE("Data Node manipulation")
|
||||
auto cont = ctx.newPath2("/example-schema2:contWithTwoNodes").createdNode;
|
||||
data->unlinkWithSiblings();
|
||||
cont->insertChild(*data);
|
||||
- REQUIRE(*cont->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings) == R"({
|
||||
+ REQUIRE(*cont->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Siblings) == R"({
|
||||
"example-schema2:contWithTwoNodes": {
|
||||
"one": 333,
|
||||
"two": 666
|
||||
@@ -1501,9 +1501,9 @@ TEST_CASE("Data Node manipulation")
|
||||
REQUIRE(std::holds_alternative<libyang::DataNode>(rawVal));
|
||||
auto retrieved = std::get<libyang::DataNode>(rawVal);
|
||||
REQUIRE(retrieved.path() == "/key");
|
||||
- REQUIRE(*retrieved.printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Shrink | libyang::PrintFlags::WithSiblings)
|
||||
+ REQUIRE(*retrieved.printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Shrink | libyang::PrintFlags::Siblings)
|
||||
== R"|({"key":"value"})|");
|
||||
- REQUIRE(*retrieved.printStr(libyang::DataFormat::XML, libyang::PrintFlags::Shrink | libyang::PrintFlags::WithSiblings)
|
||||
+ REQUIRE(*retrieved.printStr(libyang::DataFormat::XML, libyang::PrintFlags::Shrink | libyang::PrintFlags::Siblings)
|
||||
== R"|(<key>value</key>)|");
|
||||
}
|
||||
}
|
||||
@@ -1525,9 +1525,9 @@ TEST_CASE("Data Node manipulation")
|
||||
REQUIRE(std::holds_alternative<libyang::DataNode>(rawVal));
|
||||
auto retrieved = std::get<libyang::DataNode>(rawVal);
|
||||
REQUIRE(retrieved.path() == "/something");
|
||||
- REQUIRE(*retrieved.printStr(libyang::DataFormat::XML, libyang::PrintFlags::Shrink | libyang::PrintFlags::WithSiblings)
|
||||
+ REQUIRE(*retrieved.printStr(libyang::DataFormat::XML, libyang::PrintFlags::Shrink | libyang::PrintFlags::Siblings)
|
||||
== R"|(<something>lol</something>)|");
|
||||
- REQUIRE(*retrieved.printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Shrink | libyang::PrintFlags::WithSiblings)
|
||||
+ REQUIRE(*retrieved.printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Shrink | libyang::PrintFlags::Siblings)
|
||||
== R"|({"something":"lol"})|");
|
||||
}
|
||||
}
|
||||
@@ -1572,9 +1572,9 @@ TEST_CASE("Data Node manipulation")
|
||||
val = jsonAnyXmlNode.createdNode->asAny().releaseValue();
|
||||
}
|
||||
|
||||
- REQUIRE(*jsonAnyXmlNode.createdNode->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Shrink | libyang::PrintFlags::WithSiblings)
|
||||
+ REQUIRE(*jsonAnyXmlNode.createdNode->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Shrink | libyang::PrintFlags::Siblings)
|
||||
== R"|({"example-schema:ax":[1,2,3]})|"s);
|
||||
- REQUIRE(*jsonAnyXmlNode.createdNode->printStr(libyang::DataFormat::XML, libyang::PrintFlags::Shrink | libyang::PrintFlags::WithSiblings)
|
||||
+ REQUIRE(*jsonAnyXmlNode.createdNode->printStr(libyang::DataFormat::XML, libyang::PrintFlags::Shrink | libyang::PrintFlags::Siblings)
|
||||
== R"|(<ax xmlns="http://example.com/coze">)|"s + origJSON + "</ax>");
|
||||
}
|
||||
|
||||
@@ -1603,9 +1603,9 @@ TEST_CASE("Data Node manipulation")
|
||||
}
|
||||
|
||||
REQUIRE(root);
|
||||
- REQUIRE(*root->printStr(libyang::DataFormat::XML, libyang::PrintFlags::Shrink | libyang::PrintFlags::WithSiblings)
|
||||
+ REQUIRE(*root->printStr(libyang::DataFormat::XML, libyang::PrintFlags::Shrink | libyang::PrintFlags::Siblings)
|
||||
== origXML);
|
||||
- REQUIRE(*root->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Shrink | libyang::PrintFlags::WithSiblings)
|
||||
+ REQUIRE(*root->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Shrink | libyang::PrintFlags::Siblings)
|
||||
== origJSON);
|
||||
|
||||
auto node = root->findPath("/example-schema:ax");
|
||||
@@ -1665,9 +1665,9 @@ TEST_CASE("Data Node manipulation")
|
||||
auto retrieved = std::get<libyang::DataNode>(*val);
|
||||
val.reset();
|
||||
REQUIRE(retrieved.path() == "/a");
|
||||
- REQUIRE(*retrieved.printStr(libyang::DataFormat::XML, libyang::PrintFlags::Shrink | libyang::PrintFlags::WithSiblings)
|
||||
+ REQUIRE(*retrieved.printStr(libyang::DataFormat::XML, libyang::PrintFlags::Shrink | libyang::PrintFlags::Siblings)
|
||||
== origXML);
|
||||
- REQUIRE(*retrieved.printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Shrink | libyang::PrintFlags::WithSiblings)
|
||||
+ REQUIRE(*retrieved.printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Shrink | libyang::PrintFlags::Siblings)
|
||||
== origJSON);
|
||||
}
|
||||
}
|
||||
@@ -1806,7 +1806,7 @@ TEST_CASE("Data Node manipulation")
|
||||
|
||||
nodeX.parseSubtree(data, libyang::DataFormat::JSON,
|
||||
libyang::ParseOptions::Strict | libyang::ParseOptions::NoState | libyang::ParseOptions::ParseOnly);
|
||||
- REQUIRE(*nodeX.printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings) == R"({
|
||||
+ REQUIRE(*nodeX.printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Siblings) == R"({
|
||||
"example-schema5:x": {
|
||||
"x_b": {
|
||||
"x_b_leaf": 666
|
||||
@@ -1948,7 +1948,7 @@ TEST_CASE("Data Node manipulation")
|
||||
{
|
||||
netconfDeletePresenceCont.newMeta(netconf, "operation", "delete");
|
||||
netconfDeletePresenceCont.newMeta(ietfOrigin, "origin", "ietf-origin:default");
|
||||
- REQUIRE(*netconfDeletePresenceCont.printStr(libyang::DataFormat::XML, libyang::PrintFlags::WithSiblings)
|
||||
+ REQUIRE(*netconfDeletePresenceCont.printStr(libyang::DataFormat::XML, libyang::PrintFlags::Siblings)
|
||||
== R"(<presenceContainer xmlns="http://example.com/coze" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" nc:operation="delete" xmlns:or="urn:ietf:params:xml:ns:yang:ietf-origin" or:origin="or:default"/>)" "\n");
|
||||
}
|
||||
|
||||
@@ -1957,7 +1957,7 @@ TEST_CASE("Data Node manipulation")
|
||||
auto opaqueLeaf = ctx.newPath("/example-schema:leafInt32", std::nullopt, libyang::CreationOptions::Opaque);
|
||||
REQUIRE_THROWS(opaqueLeaf.newMeta(netconf, "operation", "delete"));
|
||||
opaqueLeaf.newAttrOpaqueJSON("ietf-netconf", "operation", "delete");
|
||||
- REQUIRE(*opaqueLeaf.printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings)
|
||||
+ REQUIRE(*opaqueLeaf.printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Siblings)
|
||||
== R"({
|
||||
"example-schema:leafInt32": "",
|
||||
"@example-schema:leafInt32": {
|
||||
@@ -1976,7 +1976,7 @@ TEST_CASE("Data Node manipulation")
|
||||
auto discard2 = ctx.newOpaqueJSON(libyang::OpaqueName{"sysrepo", "sysrepo", "discard-items"}, libyang::JSON{"/example-schema:b"});
|
||||
REQUIRE(!!discard2);
|
||||
discard1->insertSibling(*discard2);
|
||||
- REQUIRE(*discard1->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings)
|
||||
+ REQUIRE(*discard1->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Siblings)
|
||||
== R"({
|
||||
"sysrepo:discard-items": "/example-schema:a",
|
||||
"sysrepo:discard-items": "/example-schema:b"
|
||||
@@ -1991,7 +1991,7 @@ TEST_CASE("Data Node manipulation")
|
||||
|
||||
auto leafInt16 = ctx.newPath("/example-schema:leafInt16", "666");
|
||||
leafInt16.insertSibling(*discard1);
|
||||
- REQUIRE(*discard1->firstSibling().printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings)
|
||||
+ REQUIRE(*discard1->firstSibling().printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Siblings)
|
||||
== R"({
|
||||
"example-schema:leafInt16": 666,
|
||||
"sysrepo:discard-items": "/example-schema:a",
|
||||
@@ -2020,7 +2020,7 @@ TEST_CASE("Data Node manipulation")
|
||||
|
||||
dummy.insertSibling(*discard3);
|
||||
leafInt16.insertSibling(dummy);
|
||||
- REQUIRE(*discard1->firstSibling().printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings)
|
||||
+ REQUIRE(*discard1->firstSibling().printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Siblings)
|
||||
== R"({
|
||||
"example-schema:dummy": "blah",
|
||||
"example-schema:leafInt16": 666,
|
||||
@@ -2112,8 +2112,8 @@ TEST_CASE("Data Node manipulation")
|
||||
data->unlinkWithSiblings();
|
||||
out->insertChild(*data);
|
||||
|
||||
- REQUIRE(*out->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings) == expectedJson);
|
||||
- REQUIRE(*out->printStr(libyang::DataFormat::XML, libyang::PrintFlags::WithSiblings) == expectedXml);
|
||||
+ REQUIRE(*out->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Siblings) == expectedJson);
|
||||
+ REQUIRE(*out->printStr(libyang::DataFormat::XML, libyang::PrintFlags::Siblings) == expectedXml);
|
||||
}
|
||||
|
||||
DOCTEST_SUBCASE("libyang internal metadata")
|
||||
@@ -2148,7 +2148,7 @@ TEST_CASE("Data Node manipulation")
|
||||
auto node = ctx.newExtPath(ext, "/ietf-restconf:errors", std::nullopt, std::nullopt);
|
||||
REQUIRE(node);
|
||||
REQUIRE(node->schema().name() == "errors");
|
||||
- REQUIRE(*node->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings | libyang::PrintFlags::KeepEmptyCont) == R"({
|
||||
+ REQUIRE(*node->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Siblings | libyang::PrintFlags::EmptyContainers) == R"({
|
||||
"ietf-restconf:errors": {}
|
||||
}
|
||||
)");
|
||||
@@ -2157,7 +2157,7 @@ TEST_CASE("Data Node manipulation")
|
||||
REQUIRE(node->newPath("ietf-restconf:error[1]/error-tag", "invalid-attribute"));
|
||||
REQUIRE(node->newExtPath(ext, "/ietf-restconf:errors/error[1]/error-message", "ahoj"));
|
||||
REQUIRE_THROWS_WITH(node->newPath("ietf-restconf:error[1]/error-message", "duplicate create"), "Couldn't create a node with path 'ietf-restconf:error[1]/error-message': LY_EEXIST");
|
||||
- REQUIRE(*node->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings | libyang::PrintFlags::KeepEmptyCont) == R"({
|
||||
+ REQUIRE(*node->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Siblings | libyang::PrintFlags::EmptyContainers) == R"({
|
||||
"ietf-restconf:errors": {
|
||||
"error": [
|
||||
{
|
||||
@@ -2173,7 +2173,7 @@ TEST_CASE("Data Node manipulation")
|
||||
REQUIRE(node->newExtPath(ext, "/ietf-restconf:errors/error[2]/error-type", "transport"));
|
||||
REQUIRE(node->newExtPath(ext, "/ietf-restconf:errors/error[2]/error-tag", "invalid-attribute"));
|
||||
REQUIRE(node->newPath("ietf-restconf:error[2]/error-message", "aaa"));
|
||||
- REQUIRE(*node->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings | libyang::PrintFlags::KeepEmptyCont) == R"({
|
||||
+ REQUIRE(*node->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Siblings | libyang::PrintFlags::EmptyContainers) == R"({
|
||||
"ietf-restconf:errors": {
|
||||
"error": [
|
||||
{
|
||||
@@ -2397,8 +2397,8 @@ TEST_CASE("Data Node manipulation")
|
||||
REQUIRE(response.tree->path() == "/example-schema:output");
|
||||
REQUIRE(response.tree->isOpaque());
|
||||
REQUIRE(!response.tree->child()); // nothing gets "parsed" here, the result is put into the tree that parseOp() operated on (!)
|
||||
- CAPTURE(*response.tree->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings | libyang::PrintFlags::KeepEmptyCont));
|
||||
- CAPTURE(*replyTree.printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings | libyang::PrintFlags::KeepEmptyCont));
|
||||
+ CAPTURE(*response.tree->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Siblings | libyang::PrintFlags::EmptyContainers));
|
||||
+ CAPTURE(*replyTree.printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Siblings | libyang::PrintFlags::EmptyContainers));
|
||||
|
||||
node = replyTree.findPath("/example-schema:myRpc/outputLeaf", libyang::InputOutputNodes::Output);
|
||||
REQUIRE(!!node);
|
||||
@@ -2472,7 +2472,7 @@ TEST_CASE("Data Node manipulation")
|
||||
REQUIRE(rpcOp.tree);
|
||||
|
||||
libyang::validateOp(*rpcTree, depTree, libyang::OperationType::RpcRestconf);
|
||||
- REQUIRE(*rpcTree->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::KeepEmptyCont) == expected);
|
||||
+ REQUIRE(*rpcTree->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::EmptyContainers) == expected);
|
||||
}
|
||||
|
||||
DOCTEST_SUBCASE("Nodes in disjunctive cases defined together")
|
||||
--
|
||||
2.43.0
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
From af187d1bc27a7390b358d7ad3b9f8f44cabe41ad Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= <jan.kundrat@cesnet.cz>
|
||||
Date: Thu, 20 Nov 2025 12:46:49 +0100
|
||||
Subject: [PATCH 7/7] don't segfault when listing module's features
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Organization: Wires
|
||||
|
||||
This is especially relevant when working with the "printed context" from
|
||||
sysrepo, because that context has all the parsed info removed. As a
|
||||
result, it is not possible to query a module's list of features. Let's
|
||||
stop dereferencing a null pointer in that case.
|
||||
|
||||
Bug: https://github.com/sysrepo/sysrepo/issues/3695
|
||||
Change-Id: I6258f580b4e5aa02cae2d60260b84593aefcf587
|
||||
Signed-off-by: Mattias Walström <lazzer@gmail.com>
|
||||
---
|
||||
CMakeLists.txt | 1 +
|
||||
include/libyang-cpp/Utils.hpp | 1 +
|
||||
src/Module.cpp | 3 +++
|
||||
src/utils/exception.cpp | 5 +++++
|
||||
tests/context.cpp | 12 ++++++++++++
|
||||
5 files changed, 22 insertions(+)
|
||||
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index cbaf82a..b9da66b 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -118,6 +118,7 @@ if(BUILD_TESTING)
|
||||
endfunction()
|
||||
|
||||
libyang_cpp_test(context)
|
||||
+ target_link_libraries(test_context PkgConfig::LIBYANG)
|
||||
libyang_cpp_test(data_node)
|
||||
target_link_libraries(test_data_node PkgConfig::LIBYANG)
|
||||
libyang_cpp_test(schema_node)
|
||||
diff --git a/include/libyang-cpp/Utils.hpp b/include/libyang-cpp/Utils.hpp
|
||||
index 934714d..428b265 100644
|
||||
--- a/include/libyang-cpp/Utils.hpp
|
||||
+++ b/include/libyang-cpp/Utils.hpp
|
||||
@@ -35,6 +35,7 @@ public:
|
||||
class LIBYANG_CPP_EXPORT ParsedInfoUnavailable : public Error {
|
||||
public:
|
||||
explicit ParsedInfoUnavailable();
|
||||
+ explicit ParsedInfoUnavailable(const std::string& what);
|
||||
};
|
||||
|
||||
/**
|
||||
diff --git a/src/Module.cpp b/src/Module.cpp
|
||||
index d6d4023..8eb8fb4 100644
|
||||
--- a/src/Module.cpp
|
||||
+++ b/src/Module.cpp
|
||||
@@ -155,6 +155,9 @@ void Module::setImplemented(const AllFeatures)
|
||||
*/
|
||||
std::vector<Feature> Module::features() const
|
||||
{
|
||||
+ if (!m_module->parsed) {
|
||||
+ throw ParsedInfoUnavailable{"Module::features: lys_module::parsed is not available"};
|
||||
+ }
|
||||
std::vector<Feature> res;
|
||||
for (const auto& feature : std::span(m_module->parsed->features, LY_ARRAY_COUNT(m_module->parsed->features))) {
|
||||
res.emplace_back(Feature{&feature, m_ctx});
|
||||
diff --git a/src/utils/exception.cpp b/src/utils/exception.cpp
|
||||
index ecd2b85..a8d908b 100644
|
||||
--- a/src/utils/exception.cpp
|
||||
+++ b/src/utils/exception.cpp
|
||||
@@ -22,6 +22,11 @@ ParsedInfoUnavailable::ParsedInfoUnavailable()
|
||||
{
|
||||
}
|
||||
|
||||
+ParsedInfoUnavailable::ParsedInfoUnavailable(const std::string& what)
|
||||
+ : Error(what)
|
||||
+{
|
||||
+}
|
||||
+
|
||||
ErrorCode ErrorWithCode::code() const
|
||||
{
|
||||
return m_errCode;
|
||||
diff --git a/tests/context.cpp b/tests/context.cpp
|
||||
index 355860b..5fce0f4 100644
|
||||
--- a/tests/context.cpp
|
||||
+++ b/tests/context.cpp
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <fstream>
|
||||
#include <libyang-cpp/Context.hpp>
|
||||
#include <libyang-cpp/Utils.hpp>
|
||||
+#include <libyang/context.h>
|
||||
#include "example_schema.hpp"
|
||||
#include "pretty_printers.hpp"
|
||||
#include "test_vars.hpp"
|
||||
@@ -333,6 +334,17 @@ TEST_CASE("context")
|
||||
REQUIRE(enabledFeatures == expectedEnabledFeatures);
|
||||
}
|
||||
|
||||
+ DOCTEST_SUBCASE("printed context")
|
||||
+ {
|
||||
+ ctx->setSearchDir(TESTS_DIR / "yang");
|
||||
+ auto mod = ctx->loadModule("mod1", std::nullopt, {"*"});
|
||||
+ REQUIRE(mod.features().size() == 3);
|
||||
+ ly_ctx_free_parsed(retrieveContext(*ctx));
|
||||
+ REQUIRE_THROWS_WITH_AS(mod.features(),
|
||||
+ "Module::features: lys_module::parsed is not available",
|
||||
+ libyang::ParsedInfoUnavailable);
|
||||
+ }
|
||||
+
|
||||
DOCTEST_SUBCASE("Module::setImplemented")
|
||||
{
|
||||
ctx->setSearchDir(TESTS_DIR / "yang");
|
||||
--
|
||||
2.43.0
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
--- a/src/daemon/lldpd.c
|
||||
+++ b/src/daemon/lldpd.c
|
||||
@@ -1581,6 +1581,8 @@
|
||||
struct group *group;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
+#else
|
||||
+ struct group *group;
|
||||
#endif
|
||||
|
||||
saved_argv = argv;
|
||||
@@ -1780,6 +1782,8 @@
|
||||
if ((group = getgrnam(PRIVSEP_GROUP)) == NULL)
|
||||
fatalx("main", "no " PRIVSEP_GROUP " group for privilege separation, please create it");
|
||||
gid = group->gr_gid;
|
||||
+#else
|
||||
+ group = getgrnam("wheel");
|
||||
#endif
|
||||
|
||||
/* Create and setup socket */
|
||||
@@ -1815,6 +1819,13 @@
|
||||
S_IRUSR | S_IWUSR | S_IXUSR |
|
||||
S_IRGRP | S_IWGRP | S_IXGRP) == -1)
|
||||
log_warn("main", "unable to chmod control socket");
|
||||
+#else
|
||||
+ if (group) {
|
||||
+ if (chown(ctlname, -1, group->gr_gid))
|
||||
+ log_warn("main", "unable to chown control socket");
|
||||
+ else
|
||||
+ chmod(ctlname, 0770);
|
||||
+ }
|
||||
#endif
|
||||
|
||||
/* Create associated advisory lock file */
|
||||
@@ -1,12 +0,0 @@
|
||||
diff --git a/src/bundle.c b/src/bundle.c
|
||||
index 05ec358..d5888d4 100644
|
||||
--- a/src/bundle.c
|
||||
+++ b/src/bundle.c
|
||||
@@ -1313,6 +1313,7 @@ static gboolean is_remote_scheme(const gchar *scheme)
|
||||
{
|
||||
return (g_strcmp0(scheme, "http") == 0) ||
|
||||
(g_strcmp0(scheme, "https") == 0) ||
|
||||
+ (g_strcmp0(scheme, "tftp") == 0) ||
|
||||
(g_strcmp0(scheme, "sftp") == 0) ||
|
||||
(g_strcmp0(scheme, "ftp") == 0) ||
|
||||
(g_strcmp0(scheme, "ftps") == 0);
|
||||
@@ -1,84 +0,0 @@
|
||||
diff --git a/src/main.c b/src/main.c
|
||||
index 8e851b4..62d0bec 100644
|
||||
--- a/src/main.c
|
||||
+++ b/src/main.c
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <locale.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
+#include <syslog.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -2183,6 +2184,38 @@ static gboolean unknown_start(int argc, char **argv)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
+static int log_level(GLogLevelFlags level)
|
||||
+{
|
||||
+ if (level & G_LOG_FLAG_FATAL)
|
||||
+ return LOG_EMERG;
|
||||
+ if (level & G_LOG_FLAG_RECURSION)
|
||||
+ return LOG_ALERT;
|
||||
+ if (level & G_LOG_LEVEL_CRITICAL)
|
||||
+ return LOG_CRIT;
|
||||
+ if (level & G_LOG_LEVEL_ERROR)
|
||||
+ return LOG_ERR;
|
||||
+ if (level & G_LOG_LEVEL_WARNING)
|
||||
+ return LOG_WARNING;
|
||||
+ if (level & G_LOG_LEVEL_MESSAGE)
|
||||
+ return LOG_NOTICE;
|
||||
+ if (level & G_LOG_LEVEL_INFO)
|
||||
+ return LOG_INFO;
|
||||
+ if (level & G_LOG_LEVEL_DEBUG)
|
||||
+ return LOG_DEBUG;
|
||||
+
|
||||
+ /* Fallback to INFO for unknown levels */
|
||||
+ return LOG_INFO;
|
||||
+}
|
||||
+
|
||||
+static void syslog_handler(const gchar *domain, GLogLevelFlags level, const gchar *message, gpointer arg)
|
||||
+{
|
||||
+ /* unused */
|
||||
+ (void)domain;
|
||||
+ (void)arg;
|
||||
+
|
||||
+ syslog(log_level(level), "%s", message);
|
||||
+}
|
||||
+
|
||||
typedef enum {
|
||||
UNKNOWN = 0,
|
||||
INSTALL,
|
||||
@@ -2370,7 +2403,7 @@ static void create_option_groups(void)
|
||||
|
||||
static void cmdline_handler(int argc, char **argv)
|
||||
{
|
||||
- gboolean help = FALSE, debug = FALSE, version = FALSE;
|
||||
+ gboolean help = FALSE, debug = FALSE, use_syslog = FALSE, version = FALSE;
|
||||
g_autofree gchar *confpath = NULL, *keyring = NULL, *mount = NULL;
|
||||
char *cmdarg = NULL;
|
||||
g_autoptr(GOptionContext) context = NULL;
|
||||
@@ -2383,6 +2416,7 @@ static void cmdline_handler(int argc, char **argv)
|
||||
{"intermediate", '\0', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_FILENAME_ARRAY, &intermediate, "intermediate CA file or PKCS#11 URL", "PEMFILE|PKCS11-URL"},
|
||||
{"mount", '\0', 0, G_OPTION_ARG_FILENAME, &mount, "mount prefix", "PATH"},
|
||||
{"debug", 'd', 0, G_OPTION_ARG_NONE, &debug, "enable debug output", NULL},
|
||||
+ {"syslog", 's', 0, G_OPTION_ARG_NONE, &use_syslog, "use syslog instead of stdout", NULL},
|
||||
{"version", '\0', 0, G_OPTION_ARG_NONE, &version, "display version", NULL},
|
||||
{"help", 'h', 0, G_OPTION_ARG_NONE, &help, "display help and exit", NULL},
|
||||
{0}
|
||||
@@ -2500,6 +2534,15 @@ static void cmdline_handler(int argc, char **argv)
|
||||
g_message("Debug log domains: '%s'", domains);
|
||||
}
|
||||
|
||||
+ if (use_syslog) {
|
||||
+ GLogLevelFlags levels = G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION;
|
||||
+ const char *ident = "rauc";
|
||||
+
|
||||
+ /* XXX: facility should be configurable */
|
||||
+ openlog(ident, LOG_PID | LOG_NOWAIT, LOG_LOCAL0);
|
||||
+ g_log_set_handler(ident, levels, syslog_handler, NULL);
|
||||
+ }
|
||||
+
|
||||
/* get first parameter without dashes */
|
||||
for (gint i = 1; i <= argc; i++) {
|
||||
if (argv[i] && !g_str_has_prefix(argv[i], "-")) {
|
||||
@@ -1,29 +0,0 @@
|
||||
This makes the output more user friendly on systems where the bootname
|
||||
is the common way to refer to a slot.
|
||||
|
||||
Example output:
|
||||
...
|
||||
40% Checking slot rootfs.1 (secondary)
|
||||
46% Checking slot rootfs.1 (secondary) done.
|
||||
...
|
||||
|
||||
diff --git a/src/install.c b/src/install.c
|
||||
index ea7c95b3..fb38bcab 100644
|
||||
--- a/src/install.c
|
||||
+++ b/src/install.c
|
||||
@@ -965,7 +965,11 @@ static gboolean handle_slot_install_plan(const RaucManifest *manifest, const RIm
|
||||
|
||||
install_args_update(args, "Checking slot %s", plan->target_slot->name);
|
||||
|
||||
- r_context_begin_step_weighted_formatted("check_slot", 0, 1, "Checking slot %s", plan->target_slot->name);
|
||||
+ r_context_begin_step_weighted_formatted("check_slot", 0, 1, "Checking slot %s%s%s%s",
|
||||
+ plan->target_slot->name,
|
||||
+ plan->target_slot->bootname ? " (" : "",
|
||||
+ plan->target_slot->bootname ? plan->target_slot->bootname : "",
|
||||
+ plan->target_slot->bootname ? ")" : "");
|
||||
|
||||
r_slot_status_load(plan->target_slot);
|
||||
slot_state = plan->target_slot->status;
|
||||
--
|
||||
2.34.1
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
Print "the standard" warning banner at start of installation.
|
||||
|
||||
diff --git a/src/main.c b/src/main.c
|
||||
index 8e851b47..bba1d012 100644
|
||||
--- a/src/main.c
|
||||
+++ b/src/main.c
|
||||
@@ -258,6 +258,14 @@ static gboolean install_start(int argc, char **argv)
|
||||
if (access_args.http_headers)
|
||||
args->access_args.http_headers = g_strdupv(access_args.http_headers);
|
||||
|
||||
+ g_print("+------------------------------------------------------------------------------+\n");
|
||||
+ g_print("| |\n");
|
||||
+ g_print("| [!] Installation might take a while. DO NOT power off the device. |\n");
|
||||
+ g_print("| |\n");
|
||||
+ g_print("| Please wait while the upgrade completes. |\n");
|
||||
+ g_print("| |\n");
|
||||
+ g_print("+------------------------------------------------------------------------------+\n");
|
||||
+
|
||||
r_loop = g_main_loop_new(NULL, FALSE);
|
||||
if (ENABLE_SERVICE) {
|
||||
g_auto(GVariantDict) dict = G_VARIANT_DICT_INIT(NULL);
|
||||
--
|
||||
2.34.1
|
||||
|
||||
+20
-13
@@ -1,8 +1,11 @@
|
||||
From c4e4ad9d69b3bc62ee53f8088d6a192d288c6645 Mon Sep 17 00:00:00 2001
|
||||
From 20b63b112df3092b51b8f1d2f9ef26c238fbc3dc Mon Sep 17 00:00:00 2001
|
||||
From: Joachim Wiberg <troglobit@gmail.com>
|
||||
Date: Thu, 23 Nov 2023 18:49:36 +0100
|
||||
Subject: [PATCH 2/2] src/main: add optional syslog support
|
||||
Organization: Addiva Elektronik
|
||||
Subject: [PATCH] src/main: add optional syslog support
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Organization: Wires
|
||||
|
||||
Instead of having to redirect (colored) logs to stdout/stderr, this
|
||||
patch adds support for logging directly to syslog with approximate
|
||||
@@ -13,14 +16,15 @@ logged to LOG_LOCAL0 facility. This should of course be configurable
|
||||
but is not at this stage.
|
||||
|
||||
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
|
||||
Signed-off-by: Mattias Walström <lazzer@gmail.com>
|
||||
---
|
||||
src/main.c | 45 ++++++++++++++++++++++++++++++++++++++++++++-
|
||||
1 file changed, 44 insertions(+), 1 deletion(-)
|
||||
|
||||
|
||||
diff -urN rauc-1.13.orig/src/main.c rauc-1.13/src/main.c
|
||||
--- rauc-1.13.orig/src/main.c 2025-03-04 14:55:59.671534612 +0100
|
||||
+++ rauc-1.13/src/main.c 2025-03-04 14:57:13.022772424 +0100
|
||||
diff --git a/src/main.c b/src/main.c
|
||||
index b3192a38..0ca5af21 100644
|
||||
--- a/src/main.c
|
||||
+++ b/src/main.c
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <locale.h>
|
||||
#include <stdio.h>
|
||||
@@ -29,7 +33,7 @@ diff -urN rauc-1.13.orig/src/main.c rauc-1.13/src/main.c
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -2460,6 +2461,38 @@
|
||||
@@ -2540,6 +2541,38 @@ static gboolean unknown_start(int argc, char **argv)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -68,16 +72,16 @@ diff -urN rauc-1.13.orig/src/main.c rauc-1.13/src/main.c
|
||||
typedef enum {
|
||||
UNKNOWN = 0,
|
||||
INSTALL,
|
||||
@@ -2676,7 +2709,7 @@
|
||||
@@ -2774,7 +2807,7 @@ static gboolean collect_config_values(const gchar *option_name, const gchar *val
|
||||
|
||||
static void cmdline_handler(int argc, char **argv)
|
||||
{
|
||||
- gboolean help = FALSE, debug = FALSE, version = FALSE;
|
||||
+ gboolean help = FALSE, debug = FALSE, use_syslog = FALSE, version = FALSE;
|
||||
g_autofree gchar *confpath = NULL, *keyring = NULL, *mount = NULL;
|
||||
+ gboolean help = FALSE, debug = FALSE, version = FALSE, use_syslog = FALSE;
|
||||
g_autofree gchar *confpath = NULL, *mount = NULL;
|
||||
char *cmdarg = NULL;
|
||||
g_autoptr(GOptionContext) context = NULL;
|
||||
@@ -2690,6 +2723,7 @@
|
||||
@@ -2789,6 +2822,7 @@ static void cmdline_handler(int argc, char **argv)
|
||||
{"intermediate", '\0', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_FILENAME_ARRAY, &intermediate, "intermediate CA file or PKCS#11 URL", "PEMFILE|PKCS11-URL"},
|
||||
{"mount", '\0', 0, G_OPTION_ARG_FILENAME, &mount, "mount prefix", "PATH"},
|
||||
{"debug", 'd', 0, G_OPTION_ARG_NONE, &debug, "enable debug output", NULL},
|
||||
@@ -85,7 +89,7 @@ diff -urN rauc-1.13.orig/src/main.c rauc-1.13/src/main.c
|
||||
{"version", '\0', 0, G_OPTION_ARG_NONE, &version, "display version", NULL},
|
||||
{"help", 'h', 0, G_OPTION_ARG_NONE, &help, "display help and exit", NULL},
|
||||
{0}
|
||||
@@ -2816,6 +2850,15 @@
|
||||
@@ -2916,6 +2950,15 @@ static void cmdline_handler(int argc, char **argv)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -101,3 +105,6 @@ diff -urN rauc-1.13.orig/src/main.c rauc-1.13/src/main.c
|
||||
/* get first parameter without dashes */
|
||||
for (gint i = 1; i <= argc; i++) {
|
||||
if (argv[i] && !g_str_has_prefix(argv[i], "-")) {
|
||||
--
|
||||
2.43.0
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
From ee0ce2ea31f3ecba4a4753a761df08f0698c2726 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= <jan.kundrat@cesnet.cz>
|
||||
Date: Mon, 20 Oct 2025 11:47:26 +0200
|
||||
Subject: [PATCH 1/9] CI: pin to libyang v3
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Organization: Wires
|
||||
|
||||
Depends-on: https://gerrit.cesnet.cz/c/CzechLight/libyang-cpp/+/8978
|
||||
Change-Id: I2521ad3a62d6197e757e636b88478deeb3bb5592
|
||||
Signed-off-by: Mattias Walström <lazzer@gmail.com>
|
||||
---
|
||||
.zuul.yaml | 8 ++++----
|
||||
1 file changed, 4 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/.zuul.yaml b/.zuul.yaml
|
||||
index 7ada6f7..cef174f 100644
|
||||
--- a/.zuul.yaml
|
||||
+++ b/.zuul.yaml
|
||||
@@ -4,9 +4,9 @@
|
||||
- f38-gcc-cover:
|
||||
required-projects:
|
||||
- name: github/CESNET/libyang
|
||||
- override-checkout: devel
|
||||
+ override-checkout: cesnet/2025-07-08
|
||||
- name: github/sysrepo/sysrepo
|
||||
- override-checkout: devel
|
||||
+ override-checkout: cesnet/2025-06-02
|
||||
- name: github/doctest/doctest
|
||||
override-checkout: v2.4.8
|
||||
- name: github/rollbear/trompeloeil
|
||||
@@ -15,9 +15,9 @@
|
||||
- f38-clang-asan-ubsan:
|
||||
required-projects: &projects
|
||||
- name: github/CESNET/libyang
|
||||
- override-checkout: devel
|
||||
+ override-checkout: cesnet/2025-07-08
|
||||
- name: github/sysrepo/sysrepo
|
||||
- override-checkout: devel
|
||||
+ override-checkout: cesnet/2025-06-02
|
||||
- name: github/doctest/doctest
|
||||
override-checkout: v2.4.11
|
||||
- name: github/rollbear/trompeloeil
|
||||
--
|
||||
2.43.0
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
From e49463aba8b44b932fdd342d1c1d117e70414aba Mon Sep 17 00:00:00 2001
|
||||
From: Edoardo Bortolozzo <edoardo.bortolozzo@thinkquantum.com>
|
||||
Date: Fri, 17 Oct 2025 18:08:47 +0200
|
||||
Subject: [PATCH 2/9] fix: implicit dependency on optional
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Organization: Wires
|
||||
|
||||
chrono imports optional in gcc >=13.
|
||||
added the explicit include to compile in gcc <=12.
|
||||
|
||||
Change-Id: I18cc1a45ab522ee6738d4bc7080696cba8284903
|
||||
Signed-off-by: Mattias Walström <lazzer@gmail.com>
|
||||
---
|
||||
include/sysrepo-cpp/Connection.hpp | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
diff --git a/include/sysrepo-cpp/Connection.hpp b/include/sysrepo-cpp/Connection.hpp
|
||||
index bf72bcd..a469a2c 100644
|
||||
--- a/include/sysrepo-cpp/Connection.hpp
|
||||
+++ b/include/sysrepo-cpp/Connection.hpp
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
+#include <optional>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
--
|
||||
2.43.0
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
From b8e349aacae4c7063517a1bc652e9e93f90e28ba Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= <jan.kundrat@cesnet.cz>
|
||||
Date: Fri, 24 Oct 2025 17:05:52 +0200
|
||||
Subject: [PATCH 3/9] fix #include for for our public headers
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Organization: Wires
|
||||
|
||||
Change-Id: Idcbfe38348e3ca2e4331fc1fad542ae759b8d0f1
|
||||
Signed-off-by: Mattias Walström <lazzer@gmail.com>
|
||||
---
|
||||
src/utils/utils.cpp | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/utils/utils.cpp b/src/utils/utils.cpp
|
||||
index ff4a32c..c13b394 100644
|
||||
--- a/src/utils/utils.cpp
|
||||
+++ b/src/utils/utils.cpp
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
#include <sysrepo-cpp/Connection.hpp>
|
||||
-#include "sysrepo-cpp/utils/exception.hpp"
|
||||
+#include <sysrepo-cpp/utils/exception.hpp>
|
||||
extern "C" {
|
||||
#include <sysrepo.h>
|
||||
}
|
||||
--
|
||||
2.43.0
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
From 753a3e6eb346f2a9f6263b2d9fcd1c6dbb508247 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= <jan.kundrat@cesnet.cz>
|
||||
Date: Fri, 24 Oct 2025 17:06:56 +0200
|
||||
Subject: [PATCH 4/9] reduce number of files which are named "utils.hpp"
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Organization: Wires
|
||||
|
||||
We used to have three (one in publicheaders, one in src/utils/, one
|
||||
below tests/). Let's cut that by one to increase our collective sanity.
|
||||
|
||||
Change-Id: I9e3d93c65dffb29ba18b3ae325ddd7712a8be7a4
|
||||
Signed-off-by: Mattias Walström <lazzer@gmail.com>
|
||||
---
|
||||
src/Connection.cpp | 2 +-
|
||||
src/Session.cpp | 2 +-
|
||||
src/Subscription.cpp | 2 +-
|
||||
src/utils/{utils.hpp => misc.hpp} | 0
|
||||
src/utils/utils.cpp | 2 +-
|
||||
5 files changed, 4 insertions(+), 4 deletions(-)
|
||||
rename src/utils/{utils.hpp => misc.hpp} (100%)
|
||||
|
||||
diff --git a/src/Connection.cpp b/src/Connection.cpp
|
||||
index 0297ebe..1717b9a 100644
|
||||
--- a/src/Connection.cpp
|
||||
+++ b/src/Connection.cpp
|
||||
@@ -13,7 +13,7 @@ extern "C" {
|
||||
#include <sysrepo-cpp/utils/exception.hpp>
|
||||
#include "utils/enum.hpp"
|
||||
#include "utils/exception.hpp"
|
||||
-#include "utils/utils.hpp"
|
||||
+#include "utils/misc.hpp"
|
||||
|
||||
namespace sysrepo {
|
||||
|
||||
diff --git a/src/Session.cpp b/src/Session.cpp
|
||||
index dfe1044..994ce87 100644
|
||||
--- a/src/Session.cpp
|
||||
+++ b/src/Session.cpp
|
||||
@@ -21,7 +21,7 @@ extern "C" {
|
||||
#include <utility>
|
||||
#include "utils/enum.hpp"
|
||||
#include "utils/exception.hpp"
|
||||
-#include "utils/utils.hpp"
|
||||
+#include "utils/misc.hpp"
|
||||
|
||||
using namespace std::string_literals;
|
||||
namespace sysrepo {
|
||||
diff --git a/src/Subscription.cpp b/src/Subscription.cpp
|
||||
index 303b54c..5bb724e 100644
|
||||
--- a/src/Subscription.cpp
|
||||
+++ b/src/Subscription.cpp
|
||||
@@ -16,7 +16,7 @@ extern "C" {
|
||||
}
|
||||
#include "utils/enum.hpp"
|
||||
#include "utils/exception.hpp"
|
||||
-#include "utils/utils.hpp"
|
||||
+#include "utils/misc.hpp"
|
||||
|
||||
namespace sysrepo {
|
||||
/**
|
||||
diff --git a/src/utils/utils.hpp b/src/utils/misc.hpp
|
||||
similarity index 100%
|
||||
rename from src/utils/utils.hpp
|
||||
rename to src/utils/misc.hpp
|
||||
diff --git a/src/utils/utils.cpp b/src/utils/utils.cpp
|
||||
index c13b394..2e8d8b4 100644
|
||||
--- a/src/utils/utils.cpp
|
||||
+++ b/src/utils/utils.cpp
|
||||
@@ -12,7 +12,7 @@ extern "C" {
|
||||
#include <sysrepo.h>
|
||||
}
|
||||
#include "enum.hpp"
|
||||
-#include "utils.hpp"
|
||||
+#include "misc.hpp"
|
||||
|
||||
namespace sysrepo {
|
||||
/**
|
||||
--
|
||||
2.43.0
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
From dc118efa44b13508e1af47d924ad02ce58c58cea Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= <jan.kundrat@cesnet.cz>
|
||||
Date: Fri, 24 Oct 2025 17:18:49 +0200
|
||||
Subject: [PATCH 5/9] Fix a different function return type
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Organization: Wires
|
||||
|
||||
The prototype that was specified in the header file did not match what
|
||||
was used in the corresponding .cpp implementation. This has not been a
|
||||
problem because that implementation .cpp file was not including the
|
||||
actual header, so this was not detected. Then, at runtime, this was not
|
||||
a problem due to the way how derived types are laid out in memory.
|
||||
|
||||
Fixes: 6ed5442 Utilities for working with existing sysrepo:discard-items nodes
|
||||
Change-Id: Ie4cf8c0e39e9c8486a6289c0328510ad6f7360fb
|
||||
Signed-off-by: Mattias Walström <lazzer@gmail.com>
|
||||
---
|
||||
src/utils/utils.cpp | 7 ++++---
|
||||
1 file changed, 4 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/src/utils/utils.cpp b/src/utils/utils.cpp
|
||||
index 2e8d8b4..61d0c7f 100644
|
||||
--- a/src/utils/utils.cpp
|
||||
+++ b/src/utils/utils.cpp
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <sysrepo-cpp/Connection.hpp>
|
||||
#include <sysrepo-cpp/utils/exception.hpp>
|
||||
+#include <sysrepo-cpp/utils/utils.hpp>
|
||||
extern "C" {
|
||||
#include <sysrepo.h>
|
||||
}
|
||||
@@ -78,7 +79,7 @@ void checkNoThreadFlag(const SubscribeOptions opts, const std::optional<FDHandli
|
||||
* @see Session::operationalChanges()
|
||||
* @see Session::dropForeignOperationalContent()
|
||||
*/
|
||||
-std::optional<libyang::DataNode> findMatchingDiscard(libyang::DataNode root, const std::string& xpath)
|
||||
+std::optional<libyang::DataNodeOpaque> findMatchingDiscard(libyang::DataNode root, const std::string& xpath)
|
||||
{
|
||||
auto discard = root.firstOpaqueSibling();
|
||||
while (discard) {
|
||||
@@ -97,11 +98,11 @@ std::optional<libyang::DataNode> findMatchingDiscard(libyang::DataNode root, con
|
||||
/**
|
||||
* @short Find all sysrepo:discard-items nodes which match the given XPath or the descendants of this XPath
|
||||
*/
|
||||
-std::vector<libyang::DataNode> findMatchingDiscardPrefixes(libyang::DataNode root, const std::string& xpathPrefix)
|
||||
+std::vector<libyang::DataNodeOpaque> findMatchingDiscardPrefixes(libyang::DataNode root, const std::string& xpathPrefix)
|
||||
{
|
||||
auto withSlash = (xpathPrefix.empty() || xpathPrefix[xpathPrefix.size() - 1] == '/') ? xpathPrefix : xpathPrefix + '/';
|
||||
auto withBracket = (xpathPrefix.empty() || xpathPrefix[xpathPrefix.size() - 1] == '[') ? xpathPrefix : xpathPrefix + '[';
|
||||
- std::vector<libyang::DataNode> res;
|
||||
+ std::vector<libyang::DataNodeOpaque> res;
|
||||
auto discard = root.firstOpaqueSibling();
|
||||
while (discard) {
|
||||
if (discard->name().matches("sysrepo", "discard-items")) {
|
||||
--
|
||||
2.43.0
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
From b943faea3cb00c5e86f21710647a2ca370e41f33 Mon Sep 17 00:00:00 2001
|
||||
From: Edoardo Bortolozzo <edoardo.bortolozzo@thinkquantum.com>
|
||||
Date: Fri, 24 Oct 2025 13:45:00 +0200
|
||||
Subject: [PATCH 6/9] fix: raw strings and macros break gcc < 13
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Organization: Wires
|
||||
|
||||
see bug report:
|
||||
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55971
|
||||
|
||||
Change-Id: I04afc2b3bee86a7e4b038b277aad0e3a150a1940
|
||||
Signed-off-by: Mattias Walström <lazzer@gmail.com>
|
||||
---
|
||||
tests/subscriptions-dynamic.cpp | 21 ++++++++++++++-------
|
||||
1 file changed, 14 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/tests/subscriptions-dynamic.cpp b/tests/subscriptions-dynamic.cpp
|
||||
index 5ed173b..561df0c 100644
|
||||
--- a/tests/subscriptions-dynamic.cpp
|
||||
+++ b/tests/subscriptions-dynamic.cpp
|
||||
@@ -39,21 +39,28 @@
|
||||
REQUIRE(pipeStatus((SUBSCRIPTION).fd(), -1) == PipeStatus::DataReady); \
|
||||
(SUBSCRIPTION).processEvent(cbNotif);
|
||||
|
||||
-#define SUBSCRIPTION_TERMINATED(SUBSCRIPTION) R"({
|
||||
+auto SUBSCRIPTION_TERMINATED(const auto& SUBSCRIPTION)
|
||||
+{
|
||||
+ return R"({
|
||||
"ietf-subscribed-notifications:subscription-terminated": {
|
||||
- "id": )" + std::to_string((SUBSCRIPTION).subscriptionId()) + R"(,
|
||||
+ "id": )"
|
||||
+ + std::to_string((SUBSCRIPTION).subscriptionId()) + R"(,
|
||||
"reason": "no-such-subscription"
|
||||
}
|
||||
}
|
||||
-)"
|
||||
+)";
|
||||
+};
|
||||
|
||||
-#define REPLAY_COMPLETED(SUBSCRIPTION) R"({
|
||||
+auto REPLAY_COMPLETED(const auto& SUBSCRIPTION)
|
||||
+{
|
||||
+ return R"({
|
||||
"ietf-subscribed-notifications:replay-completed": {
|
||||
- "id": )" \
|
||||
- + std::to_string((SUBSCRIPTION).subscriptionId()) + R"(
|
||||
+ "id": )"
|
||||
+ + std::to_string((SUBSCRIPTION).subscriptionId()) + R"(
|
||||
}
|
||||
}
|
||||
-)"
|
||||
+)";
|
||||
+};
|
||||
|
||||
#define REQUIRE_YANG_PUSH_UPDATE(SUBSCRIPTION, NOTIFICATION) \
|
||||
TROMPELOEIL_REQUIRE_CALL(rec, recordYangPushUpdate((SUBSCRIPTION).subscriptionId(), NOTIFICATION)).IN_SEQUENCE(seq);
|
||||
--
|
||||
2.43.0
|
||||
|
||||
@@ -0,0 +1,305 @@
|
||||
From 5fd8cf4fb8db1d65b6a20d2777bad22647e7f5e0 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= <jan.kundrat@cesnet.cz>
|
||||
Date: Thu, 23 Oct 2025 16:04:10 +0200
|
||||
Subject: [PATCH 7/9] Port to libyang v4
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Organization: Wires
|
||||
|
||||
I'm rather disappointed with the upstream changes that implement that
|
||||
shared libyang "printed context" mapped to a fixed address, I think it's
|
||||
effectively a step backwards. Upstream is happy with their solution, so
|
||||
here we are.
|
||||
|
||||
Change-Id: I6b89422ec66bc694dc2c6ecd0d40aa1e7f76a741
|
||||
Depends-on: https://gerrit.cesnet.cz/c/CzechLight/libyang-cpp/+/8984
|
||||
Signed-off-by: Mattias Walström <lazzer@gmail.com>
|
||||
---
|
||||
.zuul.yaml | 8 +++---
|
||||
CMakeLists.txt | 6 ++---
|
||||
ci/build.sh | 4 +++
|
||||
include/sysrepo-cpp/Enum.hpp | 18 ++++++++++---
|
||||
include/sysrepo-cpp/utils/utils.hpp | 5 ++++
|
||||
src/Connection.cpp | 3 +--
|
||||
src/Session.cpp | 2 +-
|
||||
src/utils/enum.hpp | 18 +++++++------
|
||||
src/utils/utils.cpp | 18 +++++++++++++
|
||||
tests/session.cpp | 39 +++++++++++++++++++++--------
|
||||
10 files changed, 90 insertions(+), 31 deletions(-)
|
||||
|
||||
diff --git a/.zuul.yaml b/.zuul.yaml
|
||||
index cef174f..7ada6f7 100644
|
||||
--- a/.zuul.yaml
|
||||
+++ b/.zuul.yaml
|
||||
@@ -4,9 +4,9 @@
|
||||
- f38-gcc-cover:
|
||||
required-projects:
|
||||
- name: github/CESNET/libyang
|
||||
- override-checkout: cesnet/2025-07-08
|
||||
+ override-checkout: devel
|
||||
- name: github/sysrepo/sysrepo
|
||||
- override-checkout: cesnet/2025-06-02
|
||||
+ override-checkout: devel
|
||||
- name: github/doctest/doctest
|
||||
override-checkout: v2.4.8
|
||||
- name: github/rollbear/trompeloeil
|
||||
@@ -15,9 +15,9 @@
|
||||
- f38-clang-asan-ubsan:
|
||||
required-projects: &projects
|
||||
- name: github/CESNET/libyang
|
||||
- override-checkout: cesnet/2025-07-08
|
||||
+ override-checkout: devel
|
||||
- name: github/sysrepo/sysrepo
|
||||
- override-checkout: cesnet/2025-06-02
|
||||
+ override-checkout: devel
|
||||
- name: github/doctest/doctest
|
||||
override-checkout: v2.4.11
|
||||
- name: github/rollbear/trompeloeil
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index f2a9b09..49338dd 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -19,7 +19,7 @@ add_custom_target(sysrepo-cpp-version-cmake
|
||||
cmake/ProjectGitVersionRunner.cmake
|
||||
)
|
||||
include(cmake/ProjectGitVersion.cmake)
|
||||
-set(SYSREPO_CPP_PKG_VERSION "6")
|
||||
+set(SYSREPO_CPP_PKG_VERSION "7")
|
||||
prepare_git_version(SYSREPO_CPP_VERSION ${SYSREPO_CPP_PKG_VERSION})
|
||||
|
||||
find_package(Doxygen)
|
||||
@@ -27,8 +27,8 @@ option(WITH_DOCS "Create and install internal documentation (needs Doxygen)" ${D
|
||||
option(WITH_EXAMPLES "Build examples" ON)
|
||||
|
||||
find_package(PkgConfig)
|
||||
-pkg_check_modules(LIBYANG_CPP REQUIRED libyang-cpp>=3 IMPORTED_TARGET)
|
||||
-pkg_check_modules(SYSREPO REQUIRED sysrepo>=3.7.4 IMPORTED_TARGET)
|
||||
+pkg_check_modules(LIBYANG_CPP REQUIRED libyang-cpp>=5 IMPORTED_TARGET)
|
||||
+pkg_check_modules(SYSREPO REQUIRED sysrepo>=4.2.4 IMPORTED_TARGET)
|
||||
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
|
||||
diff --git a/ci/build.sh b/ci/build.sh
|
||||
index 785a811..67bc7d3 100755
|
||||
--- a/ci/build.sh
|
||||
+++ b/ci/build.sh
|
||||
@@ -47,6 +47,10 @@ if [[ $ZUUL_JOB_NAME =~ .*-tsan ]]; then
|
||||
# Our TSAN does not have interceptors for a variety of "less common" functions such as pthread_mutex_clocklock.
|
||||
# Disable all functions which are optional in sysrepo/libnetconf2/Netopeer2.
|
||||
CMAKE_OPTIONS="${CMAKE_OPTIONS} -DHAVE_PTHREAD_MUTEX_TIMEDLOCK=OFF -DHAVE_PTHREAD_MUTEX_CLOCKLOCK=OFF -DHAVE_PTHREAD_RWLOCK_CLOCKRDLOCK=OFF -DHAVE_PTHREAD_RWLOCK_CLOCKWRLOCK=OFF -DHAVE_PTHREAD_COND_CLOCKWAIT=OFF"
|
||||
+
|
||||
+ # This is currently broken on TSAN ("Failed to map the printed context (Operation not permitted).").
|
||||
+ # Even if it wasn't broken, I want at least one build in the matrix with this (mis)feature disabled.
|
||||
+ CMAKE_OPTIONS="${CMAKE_OPTIONS} -DPRINTED_CONTEXT_ADDRESS=0"
|
||||
fi
|
||||
|
||||
if [[ $ZUUL_JOB_NAME =~ .*-cover.* ]]; then
|
||||
diff --git a/include/sysrepo-cpp/Enum.hpp b/include/sysrepo-cpp/Enum.hpp
|
||||
index 48467c6..505797a 100644
|
||||
--- a/include/sysrepo-cpp/Enum.hpp
|
||||
+++ b/include/sysrepo-cpp/Enum.hpp
|
||||
@@ -176,12 +176,24 @@ enum class NotificationType : uint32_t {
|
||||
};
|
||||
|
||||
/**
|
||||
- * Wraps `sr_conn_flag_e`.
|
||||
+ * Wraps `sr_context_flag_t`.
|
||||
+ */
|
||||
+enum class ContextFlags : uint32_t {
|
||||
+ Default = 0x00, /**< SR_CTX_DEFAULT */
|
||||
+ NoPrinted = 0x01, /**< SR_CTX_NO_PRINTED */
|
||||
+ LibYangPrivParsed = 0x02, /**< SR_CTX_SET_PRIV_PARSED */
|
||||
+};
|
||||
+
|
||||
+constexpr ContextFlags operator|(const ContextFlags a, const ContextFlags b)
|
||||
+{
|
||||
+ return implEnumBitOr(a, b);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * Wraps `sr_conn_flag_t`.
|
||||
*/
|
||||
enum class ConnectionFlags : uint32_t {
|
||||
Default = 0x00, /**< SR_CONN_DEFAULT */
|
||||
- CacheRunning = 0x01, /**< SR_CONN_CACHE_RUNNING */
|
||||
- LibYangPrivParsed = 0x02, /**< SR_CONN_CTX_SET_PRIV_PARSED */
|
||||
};
|
||||
|
||||
constexpr ConnectionFlags operator|(const ConnectionFlags a, const ConnectionFlags b)
|
||||
diff --git a/include/sysrepo-cpp/utils/utils.hpp b/include/sysrepo-cpp/utils/utils.hpp
|
||||
index 64b1c2e..6f2446f 100644
|
||||
--- a/include/sysrepo-cpp/utils/utils.hpp
|
||||
+++ b/include/sysrepo-cpp/utils/utils.hpp
|
||||
@@ -12,6 +12,11 @@
|
||||
namespace sysrepo {
|
||||
Session wrapUnmanagedSession(sr_session_ctx_s* session);
|
||||
void setLogLevelStderr(const LogLevel);
|
||||
+enum class GlobalContextEffect {
|
||||
+ Lazy, /**< Set these flags the next time the shared context is applied */
|
||||
+ Immediate, /**< Apply immediately */
|
||||
+};
|
||||
+ContextFlags setGlobalContextOptions(const ContextFlags flags, const GlobalContextEffect when);
|
||||
std::optional<libyang::DataNodeOpaque> findMatchingDiscard(libyang::DataNode root, const std::string& xpath);
|
||||
std::vector<libyang::DataNodeOpaque> findMatchingDiscardPrefixes(libyang::DataNode root, const std::string& xpathPrefix);
|
||||
void unlinkFromForest(std::optional<libyang::DataNode>& root, libyang::DataNode node);
|
||||
diff --git a/src/Connection.cpp b/src/Connection.cpp
|
||||
index 1717b9a..96e340c 100644
|
||||
--- a/src/Connection.cpp
|
||||
+++ b/src/Connection.cpp
|
||||
@@ -26,7 +26,7 @@ Connection::Connection(const ConnectionFlags options)
|
||||
: ctx(nullptr)
|
||||
{
|
||||
sr_conn_ctx_t* ctx;
|
||||
- auto res = sr_connect(static_cast<sr_conn_options_t>(options), &ctx);
|
||||
+ auto res = sr_connect(static_cast<sr_conn_flag_t>(options), &ctx);
|
||||
|
||||
throwIfError(res, "Couldn't connect to sysrepo");
|
||||
this->ctx = std::shared_ptr<sr_conn_ctx_t>(ctx, sr_disconnect);
|
||||
@@ -96,5 +96,4 @@ uint32_t Connection::getId() const
|
||||
{
|
||||
return sr_get_cid(ctx.get());
|
||||
}
|
||||
-
|
||||
}
|
||||
diff --git a/src/Session.cpp b/src/Session.cpp
|
||||
index 994ce87..95b8f24 100644
|
||||
--- a/src/Session.cpp
|
||||
+++ b/src/Session.cpp
|
||||
@@ -241,7 +241,7 @@ std::optional<libyang::DataNode> Session::operationalChanges(const std::optional
|
||||
void Session::discardOperationalChanges(const std::optional<std::string>& moduleName, std::chrono::milliseconds timeout)
|
||||
{
|
||||
SYSREPO_CPP_SESSION_MTX;
|
||||
- auto res = sr_discard_oper_changes(nullptr, m_sess.get(), moduleName ? nullptr : moduleName->c_str(), timeout.count());
|
||||
+ auto res = sr_discard_oper_changes(m_sess.get(), moduleName ? nullptr : moduleName->c_str(), timeout.count());
|
||||
throwIfError(res, "Session::discardOoperationalChanges: Couldn't discard "s + (moduleName ? "for module \"" + *moduleName + "\"" : "globally"s), m_sess.get());
|
||||
}
|
||||
|
||||
diff --git a/src/utils/enum.hpp b/src/utils/enum.hpp
|
||||
index 2d3a298..e917b21 100644
|
||||
--- a/src/utils/enum.hpp
|
||||
+++ b/src/utils/enum.hpp
|
||||
@@ -151,13 +151,16 @@ static_assert(toNotificationType(SR_EV_NOTIF_MODIFIED) == NotificationType::Modi
|
||||
static_assert(toNotificationType(SR_EV_NOTIF_SUSPENDED) == NotificationType::Suspended);
|
||||
static_assert(toNotificationType(SR_EV_NOTIF_RESUMED) == NotificationType::Resumed);
|
||||
|
||||
-static_assert(std::is_same_v<sr_conn_options_t, std::underlying_type_t<ConnectionFlags>>);
|
||||
+static_assert(std::is_same_v<std::underlying_type_t<sr_conn_flag_t>, std::underlying_type_t<ConnectionFlags>>);
|
||||
static_assert(static_cast<ConnectionFlags>(SR_CONN_DEFAULT) == ConnectionFlags::Default);
|
||||
-static_assert(static_cast<ConnectionFlags>(SR_CONN_CACHE_RUNNING) == ConnectionFlags::CacheRunning);
|
||||
-static_assert(static_cast<ConnectionFlags>(SR_CONN_CTX_SET_PRIV_PARSED) == ConnectionFlags::LibYangPrivParsed);
|
||||
-static_assert(static_cast<ConnectionFlags>(SR_CONN_CACHE_RUNNING | SR_CONN_CTX_SET_PRIV_PARSED) == (ConnectionFlags::CacheRunning | ConnectionFlags::LibYangPrivParsed));
|
||||
|
||||
-static_assert(std::is_same_v<sr_get_options_t, std::underlying_type_t<GetOptions>>);
|
||||
+static_assert(std::is_same_v<std::underlying_type_t<sr_context_flag_t>, std::underlying_type_t<ContextFlags>>);
|
||||
+static_assert(static_cast<ContextFlags>(SR_CTX_DEFAULT) == ContextFlags::Default);
|
||||
+static_assert(static_cast<ContextFlags>(SR_CTX_SET_PRIV_PARSED) == ContextFlags::LibYangPrivParsed);
|
||||
+static_assert(static_cast<ContextFlags>(SR_CTX_NO_PRINTED) == ContextFlags::NoPrinted);
|
||||
+static_assert(static_cast<ContextFlags>(SR_CTX_SET_PRIV_PARSED | SR_CTX_NO_PRINTED) == (ContextFlags::LibYangPrivParsed | ContextFlags::NoPrinted));
|
||||
+
|
||||
+// upstream project provides no dedicated type for these
|
||||
static_assert(static_cast<GetOptions>(SR_OPER_DEFAULT) == GetOptions::Default);
|
||||
static_assert(static_cast<GetOptions>(SR_OPER_NO_STATE) == GetOptions::OperNoState);
|
||||
static_assert(static_cast<GetOptions>(SR_OPER_NO_CONFIG) == GetOptions::OperNoConfig);
|
||||
@@ -168,9 +171,10 @@ static_assert(static_cast<GetOptions>(SR_OPER_NO_POLL_CACHED) == GetOptions::Ope
|
||||
static_assert(static_cast<GetOptions>(SR_OPER_NO_RUN_CACHED) == GetOptions::OperNoRunningCached);
|
||||
static_assert(static_cast<GetOptions>(SR_GET_NO_FILTER) == GetOptions::NoFilter);
|
||||
|
||||
-constexpr sr_get_options_t toGetOptions(const GetOptions opts)
|
||||
+// upstream project provides no dedicated type for these
|
||||
+constexpr uint32_t toGetOptions(const GetOptions opts)
|
||||
{
|
||||
- return static_cast<sr_get_options_t>(opts);
|
||||
+ return static_cast<uint32_t>(opts);
|
||||
}
|
||||
|
||||
static_assert(static_cast<YangPushChange>(SRSN_YP_CHANGE_CREATE) == YangPushChange::Create);
|
||||
diff --git a/src/utils/utils.cpp b/src/utils/utils.cpp
|
||||
index 61d0c7f..dbbc5ce 100644
|
||||
--- a/src/utils/utils.cpp
|
||||
+++ b/src/utils/utils.cpp
|
||||
@@ -13,6 +13,7 @@ extern "C" {
|
||||
#include <sysrepo.h>
|
||||
}
|
||||
#include "enum.hpp"
|
||||
+#include "exception.hpp"
|
||||
#include "misc.hpp"
|
||||
|
||||
namespace sysrepo {
|
||||
@@ -37,6 +38,23 @@ void setLogLevelStderr(const LogLevel level)
|
||||
sr_log_stderr(toLogLevel(level));
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * @brief Set global sysrepo-level context options
|
||||
+ *
|
||||
+ * Be advised of consequences of manipulating a shared global state, especially when using multiple connections.
|
||||
+ *
|
||||
+ * Wraps `sr_context_options`.
|
||||
+ */
|
||||
+ContextFlags setGlobalContextOptions(const ContextFlags flags, const GlobalContextEffect when)
|
||||
+{
|
||||
+ uint32_t old;
|
||||
+ throwIfError(
|
||||
+ sr_context_options(static_cast<uint32_t>(flags), when == GlobalContextEffect::Immediate, &old),
|
||||
+ "sr_context_options failed",
|
||||
+ nullptr);
|
||||
+ return static_cast<ContextFlags>(old);
|
||||
+}
|
||||
+
|
||||
std::timespec toTimespec(std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> tp)
|
||||
{
|
||||
// https://embeddedartistry.com/blog/2019/01/31/converting-between-timespec-stdchrono#-std-chrono-time_point-to-timespec-
|
||||
diff --git a/tests/session.cpp b/tests/session.cpp
|
||||
index 47b5631..a709a09 100644
|
||||
--- a/tests/session.cpp
|
||||
+++ b/tests/session.cpp
|
||||
@@ -627,17 +627,34 @@ TEST_CASE("session")
|
||||
|
||||
DOCTEST_SUBCASE("libyang context flags")
|
||||
{
|
||||
- sess.setItem("/test_module:popelnice/s", "666");
|
||||
- REQUIRE(sess.getOneNode("/test_module:popelnice/s").asTerm().valueStr() == "666");
|
||||
- // Parsed type info is not preserved by libyang unless its context is constructed with a flag,
|
||||
- // and that flag is not used by sysrepo by default...
|
||||
- REQUIRE_THROWS_AS(sess.getOneNode("/test_module:popelnice/s").schema().asLeaf().valueType().asString().length(), libyang::ParsedInfoUnavailable);
|
||||
-
|
||||
- // ...unless we pass that flag explicitly as a parameter to the connection.
|
||||
- auto sess2 = sysrepo::Connection{sysrepo::ConnectionFlags::LibYangPrivParsed}.sessionStart();
|
||||
- sess2.setItem("/test_module:popelnice/s", "333");
|
||||
- REQUIRE(sess2.getOneNode("/test_module:popelnice/s").asTerm().valueStr() == "333");
|
||||
- REQUIRE(sess2.getOneNode("/test_module:popelnice/s").schema().asLeaf().valueType().asString().length().parts[0].max == 10);
|
||||
+ DOCTEST_SUBCASE("default")
|
||||
+ {
|
||||
+ sess.setItem("/test_module:popelnice/s", "666");
|
||||
+ REQUIRE(sess.getOneNode("/test_module:popelnice/s").asTerm().valueStr() == "666");
|
||||
+ // Parsed type info is not preserved by libyang unless its context is constructed with a flag,
|
||||
+ // and that flag is not used by sysrepo by default.
|
||||
+ REQUIRE_THROWS_AS(sess.getOneNode("/test_module:popelnice/s").schema().asLeaf().valueType().asString().length(), libyang::ParsedInfoUnavailable);
|
||||
+
|
||||
+ // pending changes hold the context lock, so we cannot change the global context options
|
||||
+ REQUIRE_THROWS_WITH_AS(
|
||||
+ sysrepo::setGlobalContextOptions(
|
||||
+ sysrepo::ContextFlags::LibYangPrivParsed | sysrepo::ContextFlags::NoPrinted,
|
||||
+ sysrepo::GlobalContextEffect::Immediate),
|
||||
+ "sr_context_options failed: SR_ERR_TIME_OUT",
|
||||
+ sysrepo::ErrorWithCode);
|
||||
+ }
|
||||
+
|
||||
+ // Since this is actually a shared global state, changing it is "tricky". The best bet is to actually
|
||||
+ // ensure that there are no other connections (which we cannot do here, we already have a connection opened
|
||||
+ // from the common code at the very top of this test case), and especially no internal sysrepo-level locks
|
||||
+ // on the shared libyang context. For example, no uncommited changes -- hence a new subcase.
|
||||
+ DOCTEST_SUBCASE("Connection::setGlobalContextOptions LibYangPrivParsed")
|
||||
+ {
|
||||
+ sysrepo::setGlobalContextOptions(sysrepo::ContextFlags::LibYangPrivParsed | sysrepo::ContextFlags::NoPrinted, sysrepo::GlobalContextEffect::Immediate);
|
||||
+ sess.setItem("/test_module:popelnice/s", "333");
|
||||
+ REQUIRE(sess.getOneNode("/test_module:popelnice/s").asTerm().valueStr() == "333");
|
||||
+ REQUIRE(sess.getOneNode("/test_module:popelnice/s").schema().asLeaf().valueType().asString().length().parts[0].max == 10);
|
||||
+ }
|
||||
}
|
||||
|
||||
DOCTEST_SUBCASE("replay support")
|
||||
--
|
||||
2.43.0
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
From 6e9dbd8513ce75e51f2f714df69c08af5b556d1d Mon Sep 17 00:00:00 2001
|
||||
From: Edoardo Bortolozzo <edoardo.bortolozzo@thinkquantum.com>
|
||||
Date: Tue, 28 Oct 2025 13:24:13 +0100
|
||||
Subject: [PATCH 8/9] fix: onOperGet unconsistent with C library.
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Organization: Wires
|
||||
|
||||
Fixes: https://github.com/sysrepo/sysrepo-cpp/issues/29
|
||||
Change-Id: I5a21069889c59466df8e9457c3a316b58e2571bb
|
||||
Signed-off-by: Mattias Walström <lazzer@gmail.com>
|
||||
---
|
||||
include/sysrepo-cpp/Session.hpp | 2 +-
|
||||
include/sysrepo-cpp/Subscription.hpp | 2 +-
|
||||
src/Session.cpp | 4 ++--
|
||||
src/Subscription.cpp | 4 ++--
|
||||
4 files changed, 6 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/include/sysrepo-cpp/Session.hpp b/include/sysrepo-cpp/Session.hpp
|
||||
index aae7068..3af4b10 100644
|
||||
--- a/include/sysrepo-cpp/Session.hpp
|
||||
+++ b/include/sysrepo-cpp/Session.hpp
|
||||
@@ -127,7 +127,7 @@ public:
|
||||
[[nodiscard]] Subscription onOperGet(
|
||||
const std::string& moduleName,
|
||||
OperGetCb cb,
|
||||
- const std::optional<std::string>& xpath = std::nullopt,
|
||||
+ const std::string& path,
|
||||
const SubscribeOptions opts = SubscribeOptions::Default,
|
||||
ExceptionHandler handler = nullptr,
|
||||
const std::optional<FDHandling>& callbacks = std::nullopt);
|
||||
diff --git a/include/sysrepo-cpp/Subscription.hpp b/include/sysrepo-cpp/Subscription.hpp
|
||||
index c8f3ff8..14ecce4 100644
|
||||
--- a/include/sysrepo-cpp/Subscription.hpp
|
||||
+++ b/include/sysrepo-cpp/Subscription.hpp
|
||||
@@ -32,7 +32,7 @@ public:
|
||||
Subscription& operator=(Subscription&&) noexcept;
|
||||
|
||||
void onModuleChange(const std::string& moduleName, ModuleChangeCb cb, const std::optional<std::string>& xpath = std::nullopt, uint32_t priority = 0, const SubscribeOptions opts = SubscribeOptions::Default);
|
||||
- void onOperGet(const std::string& moduleName, OperGetCb cb, const std::optional<std::string>& xpath, const SubscribeOptions opts = SubscribeOptions::Default);
|
||||
+ void onOperGet(const std::string& moduleName, OperGetCb cb, const std::string& path, const SubscribeOptions opts = SubscribeOptions::Default);
|
||||
void onRPCAction(const std::string& xpath, RpcActionCb cb, uint32_t priority = 0, const SubscribeOptions opts = SubscribeOptions::Default);
|
||||
void onNotification(
|
||||
const std::string& moduleName,
|
||||
diff --git a/src/Session.cpp b/src/Session.cpp
|
||||
index 95b8f24..6f0327d 100644
|
||||
--- a/src/Session.cpp
|
||||
+++ b/src/Session.cpp
|
||||
@@ -505,14 +505,14 @@ Subscription Session::onModuleChange(
|
||||
Subscription Session::onOperGet(
|
||||
const std::string& moduleName,
|
||||
OperGetCb cb,
|
||||
- const std::optional<std::string>& xpath,
|
||||
+ const std::string& path,
|
||||
const SubscribeOptions opts,
|
||||
ExceptionHandler handler,
|
||||
const std::optional<FDHandling>& callbacks)
|
||||
{
|
||||
checkNoThreadFlag(opts, callbacks);
|
||||
auto sub = Subscription{*this, handler, callbacks};
|
||||
- sub.onOperGet(moduleName, cb, xpath, opts);
|
||||
+ sub.onOperGet(moduleName, cb, path, opts);
|
||||
return sub;
|
||||
}
|
||||
|
||||
diff --git a/src/Subscription.cpp b/src/Subscription.cpp
|
||||
index 5bb724e..dca1176 100644
|
||||
--- a/src/Subscription.cpp
|
||||
+++ b/src/Subscription.cpp
|
||||
@@ -215,14 +215,14 @@ void Subscription::onModuleChange(const std::string& moduleName, ModuleChangeCb
|
||||
* @param xpath XPath that identifies which data this subscription is able to provide.
|
||||
* @param opts Options further changing the behavior of this method.
|
||||
*/
|
||||
-void Subscription::onOperGet(const std::string& moduleName, OperGetCb cb, const std::optional<std::string>& xpath, const SubscribeOptions opts)
|
||||
+void Subscription::onOperGet(const std::string& moduleName, OperGetCb cb, const std::string& path, const SubscribeOptions opts)
|
||||
{
|
||||
SYSREPO_CPP_SESSION_MTX_OF(m_sess);
|
||||
checkNoThreadFlag(opts, m_customEventLoopCbs);
|
||||
|
||||
auto& privRef = m_operGetCbs.emplace_back(PrivData{cb, m_exceptionHandler.get()});
|
||||
sr_subscription_ctx_s* ctx = m_sub.get();
|
||||
- auto res = sr_oper_get_subscribe(m_sess.m_sess.get(), moduleName.c_str(), xpath ? xpath->c_str() : nullptr, operGetItemsCb, reinterpret_cast<void*>(&privRef), toSubscribeOptions(opts), &ctx);
|
||||
+ auto res = sr_oper_get_subscribe(m_sess.m_sess.get(), moduleName.c_str(), path.c_str(), operGetItemsCb, reinterpret_cast<void*>(&privRef), toSubscribeOptions(opts), &ctx);
|
||||
throwIfError(res, "Couldn't create operational get items subscription", m_sess.m_sess.get());
|
||||
|
||||
saveContext(ctx);
|
||||
--
|
||||
2.43.0
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
From 60d729e4791e5447cf0d969be6da9f8f1878af64 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= <jan.kundrat@cesnet.cz>
|
||||
Date: Thu, 20 Nov 2025 15:38:04 +0100
|
||||
Subject: [PATCH 9/9] Update to libyang v4.2
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Organization: Wires
|
||||
|
||||
Depends-on: https://gerrit.cesnet.cz/c/CzechLight/libyang-cpp/+/9009
|
||||
Change-Id: Ia4c53dba0befa8dade38e728489edc6473aa5f4f
|
||||
Signed-off-by: Mattias Walström <lazzer@gmail.com>
|
||||
---
|
||||
CMakeLists.txt | 4 ++--
|
||||
tests/session.cpp | 14 +++++++-------
|
||||
tests/subscriptions-dynamic.cpp | 4 ++--
|
||||
3 files changed, 11 insertions(+), 11 deletions(-)
|
||||
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index 49338dd..8234464 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -27,8 +27,8 @@ option(WITH_DOCS "Create and install internal documentation (needs Doxygen)" ${D
|
||||
option(WITH_EXAMPLES "Build examples" ON)
|
||||
|
||||
find_package(PkgConfig)
|
||||
-pkg_check_modules(LIBYANG_CPP REQUIRED libyang-cpp>=5 IMPORTED_TARGET)
|
||||
-pkg_check_modules(SYSREPO REQUIRED sysrepo>=4.2.4 IMPORTED_TARGET)
|
||||
+pkg_check_modules(LIBYANG_CPP REQUIRED libyang-cpp>=6 IMPORTED_TARGET)
|
||||
+pkg_check_modules(SYSREPO REQUIRED sysrepo>=4.2.8 IMPORTED_TARGET)
|
||||
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
|
||||
diff --git a/tests/session.cpp b/tests/session.cpp
|
||||
index a709a09..0e92e65 100644
|
||||
--- a/tests/session.cpp
|
||||
+++ b/tests/session.cpp
|
||||
@@ -105,7 +105,7 @@ TEST_CASE("session")
|
||||
|
||||
auto data = sess.getData("/test_module:popelnice", 0);
|
||||
REQUIRE(data);
|
||||
- REQUIRE(*data->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::KeepEmptyCont) == R"({
|
||||
+ REQUIRE(*data->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::EmptyContainers) == R"({
|
||||
"test_module:popelnice": {
|
||||
"content": {
|
||||
"trash": [
|
||||
@@ -126,14 +126,14 @@ TEST_CASE("session")
|
||||
|
||||
data = sess.getData("/test_module:popelnice", 1);
|
||||
REQUIRE(data);
|
||||
- REQUIRE(*data->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::KeepEmptyCont) == R"({
|
||||
+ REQUIRE(*data->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::EmptyContainers) == R"({
|
||||
"test_module:popelnice": {}
|
||||
}
|
||||
)");
|
||||
|
||||
data = sess.getData("/test_module:popelnice", 2);
|
||||
REQUIRE(data);
|
||||
- REQUIRE(*data->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::KeepEmptyCont) == R"({
|
||||
+ REQUIRE(*data->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::EmptyContainers) == R"({
|
||||
"test_module:popelnice": {
|
||||
"content": {}
|
||||
}
|
||||
@@ -143,7 +143,7 @@ TEST_CASE("session")
|
||||
// If a list should be returned, its keys are always returned as well.
|
||||
data = sess.getData("/test_module:popelnice", 3);
|
||||
REQUIRE(data);
|
||||
- REQUIRE(*data->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::KeepEmptyCont) == R"({
|
||||
+ REQUIRE(*data->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::EmptyContainers) == R"({
|
||||
"test_module:popelnice": {
|
||||
"content": {
|
||||
"trash": [
|
||||
@@ -161,7 +161,7 @@ TEST_CASE("session")
|
||||
|
||||
data = sess.getData("/test_module:popelnice", 4);
|
||||
REQUIRE(data);
|
||||
- REQUIRE(*data->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::KeepEmptyCont) == R"({
|
||||
+ REQUIRE(*data->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::EmptyContainers) == R"({
|
||||
"test_module:popelnice": {
|
||||
"content": {
|
||||
"trash": [
|
||||
@@ -497,7 +497,7 @@ TEST_CASE("session")
|
||||
{
|
||||
sess.switchDatastore(sysrepo::Datastore::FactoryDefault);
|
||||
auto data = sess.getData("/*");
|
||||
- REQUIRE(*data->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings) == "{\n\n}\n");
|
||||
+ REQUIRE(*data->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Siblings) == "{\n\n}\n");
|
||||
REQUIRE_THROWS_AS(sess.setItem(leaf, "123"), sysrepo::ErrorWithCode);
|
||||
}
|
||||
|
||||
@@ -622,7 +622,7 @@ TEST_CASE("session")
|
||||
}
|
||||
|
||||
// the original tree is not corrupted
|
||||
- REQUIRE(*conf->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings) != "");
|
||||
+ REQUIRE(*conf->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Siblings) != "");
|
||||
}
|
||||
|
||||
DOCTEST_SUBCASE("libyang context flags")
|
||||
diff --git a/tests/subscriptions-dynamic.cpp b/tests/subscriptions-dynamic.cpp
|
||||
index 561df0c..0d4ce7c 100644
|
||||
--- a/tests/subscriptions-dynamic.cpp
|
||||
+++ b/tests/subscriptions-dynamic.cpp
|
||||
@@ -140,12 +140,12 @@ TEST_CASE("Dynamic subscriptions")
|
||||
const auto subId = std::get<uint32_t>(idNode->asTerm().value());
|
||||
idNode->unlink();
|
||||
|
||||
- rec.recordYangPushUpdate(subId, tree->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings));
|
||||
+ rec.recordYangPushUpdate(subId, tree->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Siblings));
|
||||
};
|
||||
|
||||
auto cbNotif = [&](const std::optional<libyang::DataNode>& tree, auto) {
|
||||
REQUIRE(tree);
|
||||
- rec.recordNotification(tree->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings));
|
||||
+ rec.recordNotification(tree->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Siblings));
|
||||
};
|
||||
|
||||
// write some initial data
|
||||
--
|
||||
2.43.0
|
||||
|
||||
Reference in New Issue
Block a user