Bump sysrepo, netopeer,libyang and libnetconf2

A lot of changes in sysrepo, required to add extra patches (bugfixes) to sysrepo,
sysrepo-cpp and rousette.
This commit is contained in:
Mattias Walström
2025-01-20 09:18:09 +01:00
parent 94cd526772
commit 5cc73ab97c
41 changed files with 5285 additions and 70 deletions
@@ -0,0 +1,82 @@
From d0f6422fee7a46fcb7445c88f499f61b3eb0ead0 Mon Sep 17 00:00:00 2001
From: Adam Piecek <Adam.Piecek@cesnet.cz>
Date: Wed, 23 Oct 2024 14:37:09 +0200
Subject: [PATCH 1/8] added support for RpcYang in Context::parseOp
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Addiva Elektronik
Change-Id: I25182ea2d042be1e6e4246e18aee260cc032e547
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
src/Context.cpp | 6 ++++--
tests/context.cpp | 21 +++++++++++++++++++++
2 files changed, 25 insertions(+), 2 deletions(-)
diff --git a/src/Context.cpp b/src/Context.cpp
index b844bd9..287f8c8 100644
--- a/src/Context.cpp
+++ b/src/Context.cpp
@@ -221,7 +221,8 @@ std::optional<DataNode> Context::parseExtData(
* - a NETCONF RPC,
* - a NETCONF notification,
* - a RESTCONF notification,
- * - a YANG notification.
+ * - a YANG notification,
+ * - a YANG RPC.
*
* Parsing any of these requires just the schema (which is available through the Context), and the textual payload.
* All the other information are encoded in the textual payload as per the standard.
@@ -243,6 +244,7 @@ ParsedOp Context::parseOp(const std::string& input, const DataFormat format, con
auto in = wrap_ly_in_new_memory(input);
switch (opType) {
+ case OperationType::RpcYang:
case OperationType::RpcNetconf:
case OperationType::NotificationNetconf:
case OperationType::NotificationRestconf:
@@ -254,7 +256,7 @@ ParsedOp Context::parseOp(const std::string& input, const DataFormat format, con
ParsedOp res;
res.tree = tree ? std::optional{libyang::wrapRawNode(tree)} : std::nullopt;
- if (opType == OperationType::NotificationYang) {
+ if ((opType == OperationType::NotificationYang) || (opType == OperationType::RpcYang)) {
res.op = op && tree ? std::optional{DataNode(op, res.tree->m_refs)} : std::nullopt;
} else {
res.op = op ? std::optional{libyang::wrapRawNode(op)} : std::nullopt;
diff --git a/tests/context.cpp b/tests/context.cpp
index 71ae873..11019eb 100644
--- a/tests/context.cpp
+++ b/tests/context.cpp
@@ -464,6 +464,27 @@ TEST_CASE("context")
REQUIRE(data->findPath("/example-schema:leafInt8")->asTerm().valueStr() == "-43");
}
+ DOCTEST_SUBCASE("Context::parseOp")
+ {
+ DOCTEST_SUBCASE("RPC")
+ {
+ ctx->parseModule(example_schema, libyang::SchemaFormat::YANG);
+ std::string dataJson = R"({"example-schema:myRpc":{"inputLeaf":"str"}})";
+ auto pop = ctx->parseOp(dataJson, libyang::DataFormat::JSON, libyang::OperationType::RpcYang);
+ REQUIRE(pop.op->schema().name() == "myRpc");
+ REQUIRE(pop.tree->findPath("/example-schema:myRpc/inputLeaf")->asTerm().valueStr() == "str");
+ }
+ DOCTEST_SUBCASE("action")
+ {
+ ctx->parseModule(example_schema, libyang::SchemaFormat::YANG);
+ std::string datajson = R"({"example-schema:person":[{"name":"john", "poke":{}}]})";
+ auto pop = ctx->parseOp(datajson, libyang::DataFormat::JSON, libyang::OperationType::RpcYang);
+ REQUIRE(pop.op->schema().name() == "poke");
+ REQUIRE(pop.tree->findPath("/example-schema:person[name='john']/poke")->schema().nodeType() == libyang::NodeType::Action);
+ }
+ }
+
+
DOCTEST_SUBCASE("Context::parseExt")
{
ctx->setSearchDir(TESTS_DIR / "yang");
--
2.43.0
@@ -0,0 +1,53 @@
From 7e015f3486bdbb54f1dcc2e2ce51102b1d623081 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Pecka?= <tomas.pecka@cesnet.cz>
Date: Wed, 23 Oct 2024 12:52:24 +0200
Subject: [PATCH 2/8] throw when lyd_validate_all returns error
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Addiva Elektronik
Bug: https://github.com/CESNET/libyang-cpp/issues/20
Change-Id: I005a2f1b057978573a4046e7b4cc31d77e36fde3
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
src/DataNode.cpp | 4 +++-
tests/data_node.cpp | 7 +++++++
2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/src/DataNode.cpp b/src/DataNode.cpp
index 51c86c8..2ef17f2 100644
--- a/src/DataNode.cpp
+++ b/src/DataNode.cpp
@@ -1170,7 +1170,9 @@ void validateAll(std::optional<libyang::DataNode>& node, const std::optional<Val
}
// TODO: support the `diff` argument
- lyd_validate_all(node ? &node->m_node : nullptr, nullptr, opts ? utils::toValidationOptions(*opts) : 0, nullptr);
+ auto ret = lyd_validate_all(node ? &node->m_node : nullptr, nullptr, opts ? utils::toValidationOptions(*opts) : 0, nullptr);
+ throwIfError(ret, "libyang:validateAll: lyd_validate_all failed");
+
if (!node->m_node) {
node = std::nullopt;
}
diff --git a/tests/data_node.cpp b/tests/data_node.cpp
index a23e4c2..8a2610e 100644
--- a/tests/data_node.cpp
+++ b/tests/data_node.cpp
@@ -489,6 +489,13 @@ TEST_CASE("Data Node manipulation")
REQUIRE_THROWS_WITH_AS(libyang::validateAll(node, libyang::ValidationOptions::NoState), "validateAll: Node is not a unique reference", libyang::Error);
}
+ DOCTEST_SUBCASE("validateAll throws on validation failure")
+ {
+ ctx.parseModule(type_module, libyang::SchemaFormat::YANG);
+ auto node = std::optional{ctx.newPath("/type_module:leafWithConfigFalse", "hi")};
+ REQUIRE_THROWS_WITH_AS(libyang::validateAll(node, libyang::ValidationOptions::NoState), "libyang:validateAll: lyd_validate_all failed: LY_EVALID", libyang::ErrorWithCode);
+ }
+
DOCTEST_SUBCASE("unlink")
{
auto root = ctx.parseData(data2, libyang::DataFormat::JSON);
--
2.43.0
@@ -0,0 +1,38 @@
From 490d8bb242d33213b948485f5b94c55e22cf86a6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= <jan.kundrat@cesnet.cz>
Date: Thu, 21 Nov 2024 11:32:44 +0100
Subject: [PATCH 3/8] remove a misleading comment
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Addiva Elektronik
The whole intention within action's input/output handling here was to
put some emphasis on the fact that we aren't tracking the input/output
nodes directly. However, looking at all the other classes this is a bit
redundant, we're using a pattern like this all the time. Just drop the
comment.
Change-Id: Ibd9bf9f1e83c650dda3bc43ef48e61dd6d95da5a
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
src/SchemaNode.cpp | 3 ---
1 file changed, 3 deletions(-)
diff --git a/src/SchemaNode.cpp b/src/SchemaNode.cpp
index 81e938f..9934cea 100644
--- a/src/SchemaNode.cpp
+++ b/src/SchemaNode.cpp
@@ -640,9 +640,6 @@ bool List::isUserOrdered() const
*/
ActionRpcInput ActionRpc::input() const
{
- // I need a lysc_node* for ActionRpcInput, but m_node->input is a lysp_node_action_inout. lysp_node_action_inout is
- // still just a lysc_node, so I'll just convert to lysc_node.
- // This is not very pretty, but I don't want to introduce another member for ActionRpcInput and ActionRpcOutput.
return ActionRpcInput{reinterpret_cast<const lysc_node*>(&reinterpret_cast<const lysc_node_action*>(m_node)->input), m_ctx};
}
--
2.43.0
@@ -0,0 +1,35 @@
From e1b17386cf61048d2fe27fffb3b763981a225f52 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bed=C5=99ich=20Schindler?= <bedrich.schindler@gmail.com>
Date: Wed, 27 Nov 2024 09:47:47 +0100
Subject: [PATCH 4/8] schema: improve `List::keys()` not to use `std::move`
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Addiva Elektronik
`List::keys()` used `std::move` while iterating over array of leafs.
This was solved without using `std::move`.
Change-Id: I8cbf8780ecd8848e46c1de5d4123a08624536bba
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
src/SchemaNode.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/SchemaNode.cpp b/src/SchemaNode.cpp
index 9934cea..20e2aff 100644
--- a/src/SchemaNode.cpp
+++ b/src/SchemaNode.cpp
@@ -593,8 +593,7 @@ std::vector<Leaf> List::keys() const
LY_LIST_FOR(list->child, elem)
{
if (lysc_is_key(elem)) {
- Leaf leaf(elem, m_ctx);
- res.emplace_back(std::move(leaf));
+ res.emplace_back(Leaf(elem, m_ctx));
}
}
--
2.43.0
@@ -0,0 +1,124 @@
From 1102ecdcafbc9206f59b383769687e418557838e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bed=C5=99ich=20Schindler?= <bedrich.schindler@gmail.com>
Date: Mon, 25 Nov 2024 15:54:02 +0100
Subject: [PATCH 5/8] schema: make leaf-list's `default` statement available
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Addiva Elektronik
Make leaf-list's `default` statement available so that it can be
accessed if end-user requires reading schema nodes.
`LeafList::defaultValuesStr()` returns array of canonized string default
values.
Change-Id: Idc42cd877f1fd3d717d491d09c46b59492527bff
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
include/libyang-cpp/SchemaNode.hpp | 1 +
src/SchemaNode.cpp | 17 +++++++++++++++++
tests/context.cpp | 1 +
tests/example_schema.hpp | 8 ++++++++
tests/schema_node.cpp | 7 +++++++
5 files changed, 34 insertions(+)
diff --git a/include/libyang-cpp/SchemaNode.hpp b/include/libyang-cpp/SchemaNode.hpp
index 83afc06..8ddf9be 100644
--- a/include/libyang-cpp/SchemaNode.hpp
+++ b/include/libyang-cpp/SchemaNode.hpp
@@ -169,6 +169,7 @@ class LIBYANG_CPP_EXPORT LeafList : public SchemaNode {
public:
bool isMandatory() const;
types::Type valueType() const;
+ std::vector<std::string> defaultValuesStr() const;
libyang::types::constraints::ListSize maxElements() const;
libyang::types::constraints::ListSize minElements() const;
std::optional<std::string> units() const;
diff --git a/src/SchemaNode.cpp b/src/SchemaNode.cpp
index 9934cea..95bc09b 100644
--- a/src/SchemaNode.cpp
+++ b/src/SchemaNode.cpp
@@ -472,6 +472,23 @@ types::Type LeafList::valueType() const
return types::Type{reinterpret_cast<const lysc_node_leaflist*>(m_node)->type, typeParsed, m_ctx};
}
+/**
+ * @brief Retrieves the default string values for this leaf-list.
+ *
+ * @return The default values, or an empty vector if the leaf-list does not have default values.
+ *
+ * Wraps `lysc_node_leaflist::dflts`.
+ */
+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));
+ }
+ return res;
+}
+
/**
* @brief Retrieves the units for this leaf.
* @return The units, or std::nullopt if no units are available.
diff --git a/tests/context.cpp b/tests/context.cpp
index 11019eb..902faf6 100644
--- a/tests/context.cpp
+++ b/tests/context.cpp
@@ -733,6 +733,7 @@ TEST_CASE("context")
+--rw iid-valid? instance-identifier
+--rw iid-relaxed? instance-identifier
+--rw leafListBasic* string
+ +--rw leafListWithDefault* int32
+--rw leafListWithMinMaxElements* int32
+--rw leafListWithUnits* int32
+--rw listBasic* [primary-key]
diff --git a/tests/example_schema.hpp b/tests/example_schema.hpp
index c093f50..2861b1a 100644
--- a/tests/example_schema.hpp
+++ b/tests/example_schema.hpp
@@ -525,6 +525,14 @@ module type_module {
ordered-by user;
}
+ leaf-list leafListWithDefault {
+ type int32;
+ default -1;
+ default +512;
+ default 0x400;
+ default 04000;
+ }
+
leaf-list leafListWithMinMaxElements {
type int32;
min-elements 1;
diff --git a/tests/schema_node.cpp b/tests/schema_node.cpp
index a86900d..80c7407 100644
--- a/tests/schema_node.cpp
+++ b/tests/schema_node.cpp
@@ -200,6 +200,7 @@ TEST_CASE("SchemaNode")
"/type_module:iid-valid",
"/type_module:iid-relaxed",
"/type_module:leafListBasic",
+ "/type_module:leafListWithDefault",
"/type_module:leafListWithMinMaxElements",
"/type_module:leafListWithUnits",
"/type_module:listBasic",
@@ -606,6 +607,12 @@ TEST_CASE("SchemaNode")
REQUIRE(!ctx->findPath("/type_module:leafListBasic").asLeafList().isMandatory());
}
+ 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:leafListBasic").asLeafList().defaultValuesStr().size() == 0);
+ }
+
DOCTEST_SUBCASE("LeafList::maxElements")
{
REQUIRE(ctx->findPath("/type_module:leafListWithMinMaxElements").asLeafList().maxElements() == 5);
--
2.43.0
@@ -0,0 +1,434 @@
From 01f2633cef60495d5cafc4b4b1f25273b03ab3cd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bed=C5=99ich=20Schindler?= <bedrich.schindler@gmail.com>
Date: Tue, 22 Oct 2024 15:11:30 +0200
Subject: [PATCH 6/8] schema: Make choice and case statements available
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Addiva Elektronik
Make choice and case statements available so that they can be accessed
if end-user requires reading schema nodes.
By design, choice and case statements do not exist in data tree directly.
Only children of one case can be present in the data tree at one time.
That means that choice and case children are not instantiable, thus
`SchemaNode::immediateChildren` must be used (instead of
`SchemaNode::childInstantibles`) if end-user wants to access choice
and case substatements.
Change-Id: Ib089672ad21dda8a0344895835d92d3432fcccb8
Co-authored-by: Jan Kundrát <jan.kundrat@cesnet.cz>
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
include/libyang-cpp/SchemaNode.hpp | 34 +++++++++++++
src/SchemaNode.cpp | 68 ++++++++++++++++++++++++++
tests/context.cpp | 77 +++++++++++++++++++-----------
tests/example_schema.hpp | 60 +++++++++++++++++++++++
tests/schema_node.cpp | 72 ++++++++++++++++++++++++++++
5 files changed, 284 insertions(+), 27 deletions(-)
diff --git a/include/libyang-cpp/SchemaNode.hpp b/include/libyang-cpp/SchemaNode.hpp
index 8ddf9be..0f1a4c4 100644
--- a/include/libyang-cpp/SchemaNode.hpp
+++ b/include/libyang-cpp/SchemaNode.hpp
@@ -22,6 +22,8 @@ class AnyDataAnyXML;
class ActionRpc;
class ActionRpcInput;
class ActionRpcOutput;
+class Case;
+class Choice;
class Container;
class Leaf;
class LeafList;
@@ -62,6 +64,8 @@ public:
// drectly by the user.
// TODO: turn these into a templated `as<>` method.
AnyDataAnyXML asAnyDataAnyXML() const;
+ Case asCase() const;
+ Choice asChoice() const;
Container asContainer() const;
Leaf asLeaf() const;
LeafList asLeafList() const;
@@ -129,6 +133,36 @@ private:
using SchemaNode::SchemaNode;
};
+/**
+ * @brief Class representing a schema definition of a `case` node.
+ *
+ * Wraps `lysc_node_case`.
+ */
+class LIBYANG_CPP_EXPORT Case : public SchemaNode {
+public:
+ friend SchemaNode;
+ friend Choice;
+
+private:
+ using SchemaNode::SchemaNode;
+};
+
+/**
+ * @brief Class representing a schema definition of a `choice` node.
+ *
+ * Wraps `lysc_node_choice`.
+ */
+class LIBYANG_CPP_EXPORT Choice : public SchemaNode {
+public:
+ bool isMandatory() const;
+ std::vector<Case> cases() const;
+ std::optional<Case> defaultCase() const;
+ friend SchemaNode;
+
+private:
+ using SchemaNode::SchemaNode;
+};
+
/**
* @brief Class representing a schema definition of a `container` node.
*/
diff --git a/src/SchemaNode.cpp b/src/SchemaNode.cpp
index bd20402..26b5099 100644
--- a/src/SchemaNode.cpp
+++ b/src/SchemaNode.cpp
@@ -191,6 +191,32 @@ NodeType SchemaNode::nodeType() const
return utils::toNodeType(m_node->nodetype);
}
+/**
+ * @brief Try to cast this SchemaNode to a Case node.
+ * @throws Error If this node is not a case.
+ */
+Case SchemaNode::asCase() const
+{
+ if (nodeType() != NodeType::Case) {
+ throw Error("Schema node is not a case: " + path());
+ }
+
+ return Case{m_node, m_ctx};
+}
+
+/**
+ * @brief Try to cast this SchemaNode to a Choice node.
+ * @throws Error If this node is not a choice.
+ */
+Choice SchemaNode::asChoice() const
+{
+ if (nodeType() != NodeType::Choice) {
+ throw Error("Schema node is not a choice: " + path());
+ }
+
+ return Choice{m_node, m_ctx};
+}
+
/**
* @brief Try to cast this SchemaNode to a Container node.
* @throws Error If this node is not a container.
@@ -401,6 +427,48 @@ bool AnyDataAnyXML::isMandatory() const
return m_node->flags & LYS_MAND_TRUE;
}
+/**
+ * @brief Checks whether this choice is mandatory.
+ *
+ * Wraps flag `LYS_MAND_TRUE`.
+ */
+bool Choice::isMandatory() const
+{
+ return m_node->flags & LYS_MAND_TRUE;
+}
+
+/**
+ * @brief Retrieves the list of cases for this choice.
+ *
+ * Wraps `lysc_node_choice::cases`.
+ */
+std::vector<Case> Choice::cases() const
+{
+ auto choice = reinterpret_cast<const lysc_node_choice*>(m_node);
+ auto cases = reinterpret_cast<lysc_node*>(choice->cases);
+ std::vector<Case> res;
+ lysc_node* elem;
+ LY_LIST_FOR(cases, elem)
+ {
+ res.emplace_back(Case(elem, m_ctx));
+ }
+ return res;
+}
+
+/**
+ * @brief Retrieves the default case for this choice.
+ *
+ * Wraps `lysc_node_choice::dflt`.
+ */
+std::optional<Case> Choice::defaultCase() const
+{
+ auto choice = reinterpret_cast<const lysc_node_choice*>(m_node);
+ if (!choice->dflt) {
+ return std::nullopt;
+ }
+ return Case{reinterpret_cast<lysc_node*>(choice->dflt), m_ctx};
+}
+
/**
* @brief Checks whether this container is mandatory.
*
diff --git a/tests/context.cpp b/tests/context.cpp
index 902faf6..5929b75 100644
--- a/tests/context.cpp
+++ b/tests/context.cpp
@@ -709,33 +709,56 @@ TEST_CASE("context")
auto mod = ctx_pp->parseModule(type_module, libyang::SchemaFormat::YANG);
REQUIRE(mod.printStr(libyang::SchemaOutputFormat::Tree) == R"(module: type_module
- +--rw anydataBasic? anydata
- +--rw anydataWithMandatoryChild anydata
- +--rw anyxmlBasic? anyxml
- +--rw anyxmlWithMandatoryChild anyxml
- +--rw leafBinary? binary
- +--rw leafBits? bits
- +--rw leafEnum? enumeration
- +--rw leafEnum2? enumeration
- +--rw leafNumber? int32
- +--rw leafRef? -> /custom-prefix:listAdvancedWithOneKey/lol
- +--rw leafRefRelaxed? -> /custom-prefix:listAdvancedWithOneKey/lol
- +--rw leafString? string
- +--rw leafUnion? union
- +--rw meal? identityref
- +--ro leafWithConfigFalse? string
- +--rw leafWithDefaultValue? string
- +--rw leafWithDescription? string
- +--rw leafWithMandatoryTrue string
- x--rw leafWithStatusDeprecated? string
- o--rw leafWithStatusObsolete? string
- +--rw leafWithUnits? int32
- +--rw iid-valid? instance-identifier
- +--rw iid-relaxed? instance-identifier
- +--rw leafListBasic* string
- +--rw leafListWithDefault* int32
- +--rw leafListWithMinMaxElements* int32
- +--rw leafListWithUnits* int32
+ +--rw anydataBasic? anydata
+ +--rw anydataWithMandatoryChild anydata
+ +--rw anyxmlBasic? anyxml
+ +--rw anyxmlWithMandatoryChild anyxml
+ +--rw choiceBasicContainer
+ | +--rw (choiceBasic)?
+ | +--:(case1)
+ | | +--rw l? string
+ | | +--rw ll* string
+ | +--:(case2)
+ | +--rw l2? string
+ +--rw choiceWithMandatoryContainer
+ | +--rw (choiceWithMandatory)
+ | +--:(case3)
+ | | +--rw l3? string
+ | +--:(case4)
+ | +--rw l4? string
+ +--rw choiceWithDefaultContainer
+ | +--rw (choiceWithDefault)?
+ | +--:(case5)
+ | | +--rw l5? string
+ | +--:(case6)
+ | +--rw l6? string
+ +--rw implicitCaseContainer
+ | +--rw (implicitCase)?
+ | +--:(implicitLeaf)
+ | +--rw implicitLeaf? string
+ +--rw leafBinary? binary
+ +--rw leafBits? bits
+ +--rw leafEnum? enumeration
+ +--rw leafEnum2? enumeration
+ +--rw leafNumber? int32
+ +--rw leafRef? -> /custom-prefix:listAdvancedWithOneKey/lol
+ +--rw leafRefRelaxed? -> /custom-prefix:listAdvancedWithOneKey/lol
+ +--rw leafString? string
+ +--rw leafUnion? union
+ +--rw meal? identityref
+ +--ro leafWithConfigFalse? string
+ +--rw leafWithDefaultValue? string
+ +--rw leafWithDescription? string
+ +--rw leafWithMandatoryTrue string
+ x--rw leafWithStatusDeprecated? string
+ o--rw leafWithStatusObsolete? string
+ +--rw leafWithUnits? int32
+ +--rw iid-valid? instance-identifier
+ +--rw iid-relaxed? instance-identifier
+ +--rw leafListBasic* string
+ +--rw leafListWithDefault* int32
+ +--rw leafListWithMinMaxElements* int32
+ +--rw leafListWithUnits* int32
+--rw listBasic* [primary-key]
| +--rw primary-key string
+--rw listAdvancedWithOneKey* [lol]
diff --git a/tests/example_schema.hpp b/tests/example_schema.hpp
index 2861b1a..ae3b4de 100644
--- a/tests/example_schema.hpp
+++ b/tests/example_schema.hpp
@@ -390,6 +390,66 @@ module type_module {
mandatory true;
}
+ container choiceBasicContainer {
+ choice choiceBasic {
+ case case1 {
+ leaf l {
+ type string;
+ }
+ leaf-list ll {
+ type string;
+ ordered-by user;
+ }
+ }
+ case case2 {
+ leaf l2 {
+ type string;
+ }
+ }
+ }
+ }
+
+ container choiceWithMandatoryContainer {
+ choice choiceWithMandatory {
+ mandatory true;
+ case case3 {
+ leaf l3 {
+ type string;
+ }
+ }
+ case case4 {
+ leaf l4 {
+ type string;
+ }
+ }
+ }
+ }
+
+ container choiceWithDefaultContainer {
+ choice choiceWithDefault {
+ default case5;
+ case case5 {
+ leaf l5 {
+ type string;
+ }
+ }
+ case case6 {
+ leaf l6 {
+ type string;
+ }
+ }
+ }
+ }
+
+ container implicitCaseContainer {
+ choice implicitCase {
+ leaf implicitLeaf {
+ type string;
+ }
+ }
+ }
+
+
leaf leafBinary {
type binary;
}
diff --git a/tests/schema_node.cpp b/tests/schema_node.cpp
index 80c7407..8d74bd2 100644
--- a/tests/schema_node.cpp
+++ b/tests/schema_node.cpp
@@ -58,6 +58,9 @@ TEST_CASE("SchemaNode")
],
"type_module:anydataWithMandatoryChild": {"content": "test-string"},
"type_module:anyxmlWithMandatoryChild": {"content": "test-string"},
+ "type_module:choiceWithMandatoryContainer": {
+ "l4": "test-string"
+ },
"type_module:containerWithMandatoryChild": {
"leafWithMandatoryTrue": "test-string"
},
@@ -180,6 +183,10 @@ TEST_CASE("SchemaNode")
"/type_module:anydataWithMandatoryChild",
"/type_module:anyxmlBasic",
"/type_module:anyxmlWithMandatoryChild",
+ "/type_module:choiceBasicContainer",
+ "/type_module:choiceWithMandatoryContainer",
+ "/type_module:choiceWithDefaultContainer",
+ "/type_module:implicitCaseContainer",
"/type_module:leafBinary",
"/type_module:leafBits",
"/type_module:leafEnum",
@@ -417,6 +424,71 @@ TEST_CASE("SchemaNode")
REQUIRE(!ctx->findPath("/type_module:anyxmlBasic").asAnyDataAnyXML().isMandatory());
}
+ DOCTEST_SUBCASE("Choice and Case")
+ {
+ std::string xpath;
+ bool isMandatory = false;
+ std::optional<std::string> defaultCase;
+ std::vector<std::string> caseNames;
+ std::optional<libyang::SchemaNode> root;
+
+ DOCTEST_SUBCASE("two cases with nothing fancy")
+ {
+ root = ctx->findPath("/type_module:choiceBasicContainer");
+ caseNames = {"case1", "case2"};
+ }
+
+ DOCTEST_SUBCASE("mandatory choice") {
+ root = ctx->findPath("/type_module:choiceWithMandatoryContainer");
+ isMandatory = true;
+ caseNames = {"case3", "case4"};
+ }
+
+ DOCTEST_SUBCASE("default choice") {
+ root = ctx->findPath("/type_module:choiceWithDefaultContainer");
+ defaultCase = "case5";
+ caseNames = {"case5", "case6"};
+ }
+
+ DOCTEST_SUBCASE("implicit case") {
+ root = ctx->findPath("/type_module:implicitCaseContainer");
+ caseNames = {"implicitLeaf"};
+ }
+
+ // For testing purposes, we have each choice in its own container. As choice and case are not directly instantiable,
+ // we wrap them in a container to simplify the testing process. It allows us to simply address the choice by its
+ // container and then get the choice from it. It also prevents polluting the test schema with unnecessary nodes
+ // and isolates the choice from other nodes.
+ auto container = root->asContainer();
+ auto choice = container.immediateChildren().begin()->asChoice();
+ REQUIRE(choice.isMandatory() == isMandatory);
+ REQUIRE(!!choice.defaultCase() == !!defaultCase);
+ if (defaultCase) {
+ REQUIRE(choice.defaultCase()->name() == *defaultCase);
+ }
+ std::vector<std::string> actualCaseNames;
+ for (const auto& case_ : choice.cases()) {
+ actualCaseNames.push_back(case_.name());
+ }
+ REQUIRE(actualCaseNames == caseNames);
+
+ // Also test child node access for one arbitrary choice/case combination
+ if (root->path() == "/type_module:choiceBasicContainer") {
+ REQUIRE(choice.cases().size() == 2);
+ auto case1 = choice.cases()[0];
+ auto children = case1.immediateChildren();
+ auto it = children.begin();
+ REQUIRE(it->asLeaf().name() == "l");
+ ++it;
+ REQUIRE(it->asLeafList().name() == "ll");
+
+ auto case2 = choice.cases()[1];
+ children = case2.immediateChildren();
+ it = children.begin();
+ REQUIRE(it->asLeaf().name() == "l2");
+ }
+ }
+
DOCTEST_SUBCASE("Container::isMandatory")
{
REQUIRE(ctx->findPath("/type_module:containerWithMandatoryChild").asContainer().isMandatory());
--
2.43.0
@@ -0,0 +1,129 @@
From a1acdc794facf8cbf113f73274ecebd5898c81a1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= <jan.kundrat@cesnet.cz>
Date: Tue, 17 Dec 2024 15:08:43 +0100
Subject: [PATCH 7/8] Wrap lyd_change_term for changing the value for a
terminal node
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Addiva Elektronik
Previously, the code would require a newPath(...,
libyang::CreationOptions::Update), which is quite a mouthful.
Change-Id: I8a908c0fdd3e48dda830819758522a511adedd3b
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
include/libyang-cpp/DataNode.hpp | 8 ++++++
src/DataNode.cpp | 21 ++++++++++++++++
tests/data_node.cpp | 42 ++++++++++++++++++++++++++------
3 files changed, 63 insertions(+), 8 deletions(-)
diff --git a/include/libyang-cpp/DataNode.hpp b/include/libyang-cpp/DataNode.hpp
index 2211415..851681b 100644
--- a/include/libyang-cpp/DataNode.hpp
+++ b/include/libyang-cpp/DataNode.hpp
@@ -212,6 +212,14 @@ public:
Value value() const;
types::Type valueType() const;
+ /** @brief Was the value changed? */
+ enum class ValueChange {
+ Changed, /**< Yes, this is an actual change of the stored value */
+ ExplicitNonDefault, /**< It still holds the default value, but it's been set explicitly now */
+ EqualValueNotChanged, /**< No change, the previous value is the same as the new one, and it isn't an implicit default */
+ };
+ ValueChange changeValue(const std::string value);
+
private:
using DataNode::DataNode;
};
diff --git a/src/DataNode.cpp b/src/DataNode.cpp
index 2ef17f2..84591e5 100644
--- a/src/DataNode.cpp
+++ b/src/DataNode.cpp
@@ -903,6 +903,27 @@ types::Type DataNodeTerm::valueType() const
return impl(reinterpret_cast<const lyd_node_term*>(m_node)->value);
}
+/** @short Change the term's value
+ *
+ * Wraps `lyd_change_term`.
+ * */
+DataNodeTerm::ValueChange DataNodeTerm::changeValue(const std::string value)
+{
+ auto ret = lyd_change_term(m_node, value.c_str());
+
+ switch (ret) {
+ case LY_SUCCESS:
+ return ValueChange::Changed;
+ case LY_EEXIST:
+ return ValueChange::ExplicitNonDefault;
+ case LY_ENOT:
+ return ValueChange::EqualValueNotChanged;
+ default:
+ throwIfError(ret, "DataNodeTerm::changeValue failed");
+ __builtin_unreachable();
+ }
+}
+
/**
* @brief Returns a collection for iterating depth-first over the subtree this instance points to.
*
diff --git a/tests/data_node.cpp b/tests/data_node.cpp
index 8a2610e..45fd6c1 100644
--- a/tests/data_node.cpp
+++ b/tests/data_node.cpp
@@ -456,15 +456,41 @@ TEST_CASE("Data Node manipulation")
REQUIRE(node.hasDefaultValue());
REQUIRE(node.isImplicitDefault());
- data->newPath("/example-schema3:leafWithDefault", "not-default-value", libyang::CreationOptions::Update);
- node = data->findPath("/example-schema3:leafWithDefault")->asTerm();
- REQUIRE(!node.hasDefaultValue());
- REQUIRE(!node.isImplicitDefault());
+ DOCTEST_SUBCASE("newPath")
+ {
+ data->newPath("/example-schema3:leafWithDefault", "not-default-value", libyang::CreationOptions::Update);
+ node = data->findPath("/example-schema3:leafWithDefault")->asTerm();
+ REQUIRE(!node.hasDefaultValue());
+ REQUIRE(!node.isImplicitDefault());
- data->newPath("/example-schema3:leafWithDefault", "AHOJ", libyang::CreationOptions::Update);
- node = data->findPath("/example-schema3:leafWithDefault")->asTerm();
- REQUIRE(node.hasDefaultValue());
- REQUIRE(!node.isImplicitDefault());
+ data->newPath("/example-schema3:leafWithDefault", "AHOJ", libyang::CreationOptions::Update);
+ node = data->findPath("/example-schema3:leafWithDefault")->asTerm();
+ REQUIRE(node.hasDefaultValue());
+ REQUIRE(!node.isImplicitDefault());
+ }
+
+ DOCTEST_SUBCASE("changing values")
+ {
+ auto node = data->findPath("/example-schema3:leafWithDefault");
+ REQUIRE(!!node);
+ auto term = node->asTerm();
+
+ DOCTEST_SUBCASE("to an arbitrary value") {
+ REQUIRE(term.changeValue("cau") == libyang::DataNodeTerm::ValueChange::Changed);
+ }
+
+ DOCTEST_SUBCASE("from an implicit default to an explicit default") {
+ REQUIRE(term.changeValue("AHOJ") == libyang::DataNodeTerm::ValueChange::ExplicitNonDefault);
+ REQUIRE(term.changeValue("AHOJ") == libyang::DataNodeTerm::ValueChange::EqualValueNotChanged);
+ REQUIRE(term.changeValue("cau") == libyang::DataNodeTerm::ValueChange::Changed);
+ REQUIRE(term.changeValue("cau") == libyang::DataNodeTerm::ValueChange::EqualValueNotChanged);
+ }
+
+ DOCTEST_SUBCASE("from an implicit default to something else") {
+ REQUIRE(term.changeValue("cau") == libyang::DataNodeTerm::ValueChange::Changed);
+ REQUIRE(term.changeValue("cau") == libyang::DataNodeTerm::ValueChange::EqualValueNotChanged);
+ }
+ }
}
DOCTEST_SUBCASE("isTerm")
--
2.43.0
@@ -0,0 +1,46 @@
From 39c7530caa510144c17521278b721ba1e6d8ff40 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Pecka?= <tomas.pecka@cesnet.cz>
Date: Thu, 9 Jan 2025 15:31:37 +0100
Subject: [PATCH 8/8] upstream stopped reporting schema-mounts node
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Addiva Elektronik
Change-Id: I940769d38d56fcfda3e1408c92331fdb00c161e9
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
CMakeLists.txt | 2 +-
tests/context.cpp | 3 +--
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3d86809..732f52b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -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.4.2 IMPORTED_TARGET)
+pkg_check_modules(LIBYANG REQUIRED libyang>=3.7.8 IMPORTED_TARGET)
set(LIBYANG_CPP_PKG_VERSION "3")
# FIXME from gcc 14.1 on we should be able to use the calendar/time from libstdc++ and thus remove the date dependency
diff --git a/tests/context.cpp b/tests/context.cpp
index 5929b75..9d38fea 100644
--- a/tests/context.cpp
+++ b/tests/context.cpp
@@ -509,8 +509,7 @@ TEST_CASE("context")
"error-message": "hi"
}
]
- },
- "ietf-yang-schema-mount:schema-mounts": {}
+ }
}
)");
}
--
2.43.0
@@ -0,0 +1,62 @@
From f7307481fc4ca167592acf925136e5f8a51e2150 Mon Sep 17 00:00:00 2001
From: Jan Kundrát <jan.kundrat@cesnet.cz>
Date: Wed, 18 Dec 2024 17:26:15 +0100
Subject: [PATCH] Update dependencies
Depends-on: https://gerrit.cesnet.cz/c/CzechLight/dependencies/+/8134
Change-Id: Ib170aa61600ffb9089460247f762ff5a947dd6c6
---
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 22bce32..5ae8062 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -74,7 +74,7 @@
pkg_check_modules(nghttp2 REQUIRED IMPORTED_TARGET libnghttp2_asio>=0.0.90 libnghttp2)
find_package(Boost REQUIRED CONFIG COMPONENTS system thread)
-pkg_check_modules(SYSREPO-CPP REQUIRED IMPORTED_TARGET sysrepo-cpp>=3)
+pkg_check_modules(SYSREPO-CPP REQUIRED IMPORTED_TARGET sysrepo-cpp>=4)
pkg_check_modules(LIBYANG-CPP REQUIRED IMPORTED_TARGET libyang-cpp>=3)
pkg_check_modules(SYSTEMD IMPORTED_TARGET libsystemd)
pkg_check_modules(PAM REQUIRED IMPORTED_TARGET pam)
diff --git a/tests/restconf-writing.cpp b/tests/restconf-writing.cpp
index 0932984..d37ab03 100644
--- a/tests/restconf-writing.cpp
+++ b/tests/restconf-writing.cpp
@@ -772,7 +772,7 @@
{
"error-type": "protocol",
"error-tag": "invalid-value",
- "error-message": "Session::applyChanges: Couldn't apply changes: SR_ERR_NOT_FOUND\u000A Node \"lst\" instance to insert next to not found. (SR_ERR_NOT_FOUND)\u000A Applying operation \"replace\" failed. (SR_ERR_NOT_FOUND)"
+ "error-message": "Session::applyChanges: Couldn't apply changes: SR_ERR_NOT_FOUND\u000A Node \"lst\" instance to insert next to not found. (SR_ERR_NOT_FOUND)"
}
]
}
@@ -855,7 +855,7 @@
{
"error-type": "protocol",
"error-tag": "invalid-value",
- "error-message": "Session::applyChanges: Couldn't apply changes: SR_ERR_NOT_FOUND\u000A Node \"ll\" instance to insert next to not found. (SR_ERR_NOT_FOUND)\u000A Applying operation \"replace\" failed. (SR_ERR_NOT_FOUND)"
+ "error-message": "Session::applyChanges: Couldn't apply changes: SR_ERR_NOT_FOUND\u000A Node \"ll\" instance to insert next to not found. (SR_ERR_NOT_FOUND)"
}
]
}
@@ -1560,7 +1560,7 @@
{
"error-type": "protocol",
"error-tag": "invalid-value",
- "error-message": "Session::applyChanges: Couldn't apply changes: SR_ERR_NOT_FOUND\u000A Node \"lst\" instance to insert next to not found. (SR_ERR_NOT_FOUND)\u000A Applying operation \"create\" failed. (SR_ERR_NOT_FOUND)"
+ "error-message": "Session::applyChanges: Couldn't apply changes: SR_ERR_NOT_FOUND\u000A Node \"lst\" instance to insert next to not found. (SR_ERR_NOT_FOUND)"
}
]
}
@@ -1626,7 +1626,7 @@
{
"error-type": "protocol",
"error-tag": "invalid-value",
- "error-message": "Session::applyChanges: Couldn't apply changes: SR_ERR_NOT_FOUND\u000A Node \"ll\" instance to insert next to not found. (SR_ERR_NOT_FOUND)\u000A Applying operation \"create\" failed. (SR_ERR_NOT_FOUND)"
+ "error-message": "Session::applyChanges: Couldn't apply changes: SR_ERR_NOT_FOUND\u000A Node \"ll\" instance to insert next to not found. (SR_ERR_NOT_FOUND)"
}
]
}
@@ -0,0 +1,73 @@
From 23fa270a747c35d65aeeba691599ed6b21b501f0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= <jan.kundrat@cesnet.cz>
Date: Tue, 29 Oct 2024 15:15:52 +0100
Subject: [PATCH 1/6] error handling changes in upstream sysrepo
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Addiva Elektronik
Upstream commit 396e331e4634a417420a71ad723567b42d75c443 removed these
extra error entries.
Change-Id: Ifeda21194db7b7f7fdae8b6ae13c1e2d1f6b8d3d
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
CMakeLists.txt | 2 +-
tests/subscriptions.cpp | 16 ++++++----------
2 files changed, 7 insertions(+), 11 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index eb5f900..1eb625f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -28,7 +28,7 @@ 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>=2.11.7 IMPORTED_TARGET)
+pkg_check_modules(SYSREPO REQUIRED sysrepo>=2.12.0 IMPORTED_TARGET)
set(SYSREPO_CPP_PKG_VERSION "3")
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
diff --git a/tests/subscriptions.cpp b/tests/subscriptions.cpp
index 0e14fc0..36fd61c 100644
--- a/tests/subscriptions.cpp
+++ b/tests/subscriptions.cpp
@@ -494,11 +494,11 @@ TEST_CASE("subscriptions")
sess.applyChanges();
} catch (const sysrepo::ErrorWithCode&) {
auto errors = sess.getErrors();
- REQUIRE(errors.size() == 2);
- REQUIRE(errors.at(0).errorMessage == message);
- REQUIRE(errors.at(0).code == sysrepo::ErrorCode::OperationFailed);
- REQUIRE(errors.at(1).errorMessage == "User callback failed.");
- REQUIRE(errors.at(1).code == sysrepo::ErrorCode::CallbackFailed);
+ REQUIRE(errors.size() == 1);
+ REQUIRE(errors.at(0) == sysrepo::ErrorInfo{
+ .code = sysrepo::ErrorCode::OperationFailed,
+ .errorMessage = message
+ });
}
// The callback does not fail the second time.
@@ -548,15 +548,11 @@ TEST_CASE("subscriptions")
sess.setItem("/test_module:leafInt32", "123");
REQUIRE_THROWS_AS(sess.applyChanges(), sysrepo::ErrorWithCode);
auto errors = sess.getErrors();
- REQUIRE(errors.size() == 2);
+ REQUIRE(errors.size() == 1);
REQUIRE(errors.at(0) == sysrepo::ErrorInfo{
.code = sysrepo::ErrorCode::OperationFailed,
.errorMessage = "Test callback failure.",
});
- REQUIRE(errors.at(1) == sysrepo::ErrorInfo{
- .code = sysrepo::ErrorCode::CallbackFailed,
- .errorMessage = "User callback failed."
- });
auto ncErrors = sess.getNetconfErrors();
REQUIRE(ncErrors.size() == 1);
REQUIRE(ncErrors.front() == errToSet);
--
2.43.0
@@ -0,0 +1,35 @@
From a73ffe1a5bbab51a67cf56dd2864c71a29c6685b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= <jan.kundrat@cesnet.cz>
Date: Thu, 21 Nov 2024 11:18:25 +0100
Subject: [PATCH 2/6] Fix error message when sr_session_start fails
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Addiva Elektronik
Thanks to Jason Patterson for sending a bugreport; its investigation led
to discovering this bug.
See-also: https://github.com/CESNET/rousette/issues/15
Change-Id: I082987d60380f860e08f26b436d0f9db4a231bec
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
src/Connection.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Connection.cpp b/src/Connection.cpp
index de65d64..716fd1a 100644
--- a/src/Connection.cpp
+++ b/src/Connection.cpp
@@ -53,7 +53,7 @@ Session Connection::sessionStart(sysrepo::Datastore datastore)
sr_session_ctx_t* sess;
auto res = sr_session_start(ctx.get(), toDatastore(datastore), &sess);
- throwIfError(res, "Couldn't connect to sysrepo");
+ throwIfError(res, "Couldn't start sysrepo session");
return Session{sess, ctx};
}
--
2.43.0
@@ -0,0 +1,73 @@
From 552a366990fe1fe2cdd8d155fa5cecb3ff3fbc13 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= <jan.kundrat@cesnet.cz>
Date: Wed, 4 Dec 2024 15:51:41 +0100
Subject: [PATCH 3/6] We don't support sysrepo v3 yet
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Addiva Elektronik
That version introduced some backward-incompatible changes to the way
how operational data are treated, especially in presence of "discards"
of this data. It's a breaking behavior, and before we release a fix,
let's pin the version to something which still works. The latest sysrepo
semi-release is v 2.12.0, which is unfortunately not tagged.
Change-Id: I3673de9893d6e806926767a72b1ef83ee62bd610
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
.zuul.yaml | 4 ++--
CMakeLists.txt | 2 +-
README.md | 1 +
3 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/.zuul.yaml b/.zuul.yaml
index dc87c6f..c3aea22 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -6,7 +6,7 @@
- name: github/CESNET/libyang
override-checkout: devel
- name: github/sysrepo/sysrepo
- override-checkout: devel
+ override-checkout: cesnet/2024-10-before-oper-changes
- name: github/onqtam/doctest
override-checkout: v2.4.8
- name: github/rollbear/trompeloeil
@@ -17,7 +17,7 @@
- name: github/CESNET/libyang
override-checkout: devel
- name: github/sysrepo/sysrepo
- override-checkout: devel
+ override-checkout: cesnet/2024-10-before-oper-changes
- name: github/onqtam/doctest
override-checkout: v2.4.11
- name: github/rollbear/trompeloeil
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1eb625f..119bdd7 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -28,7 +28,7 @@ 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>=2.12.0 IMPORTED_TARGET)
+pkg_check_modules(SYSREPO REQUIRED sysrepo>=2.12.0 sysrepo<3 IMPORTED_TARGET)
set(SYSREPO_CPP_PKG_VERSION "3")
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
diff --git a/README.md b/README.md
index 27dc748..e14b772 100644
--- a/README.md
+++ b/README.md
@@ -9,6 +9,7 @@ It uses RAII for automatic memory management.
## Dependencies
- [sysrepo](https://github.com/sysrepo/sysrepo) - the `devel` branch (even for the `master` branch of *sysrepo-cpp*)
+ - we temporarily require pre-v3 sysrepo which introduced backward-incompatible changes to operational data handling
- [libyang-cpp](https://github.com/CESNET/libyang-cpp) - C++ bindings for *libyang*
- C++20 compiler (e.g., GCC 10.x+, clang 10+)
- CMake 3.19+
--
2.43.0
@@ -0,0 +1,85 @@
From f2db30721d909d127645721f4149de0cd36d2b1c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= <jan.kundrat@cesnet.cz>
Date: Tue, 17 Dec 2024 11:41:10 +0100
Subject: [PATCH 4/6] refactor: don't use `module` as a variable name
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Addiva Elektronik
...to unify stuff with all other occurrences, and to not use that magic
identifier.
Change-Id: I8c38777a925271e7659560d0380a7f7968f4cfa7
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
include/sysrepo-cpp/Session.hpp | 4 ++--
src/Session.cpp | 12 ++++++------
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/include/sysrepo-cpp/Session.hpp b/include/sysrepo-cpp/Session.hpp
index 7da11cb..477e604 100644
--- a/include/sysrepo-cpp/Session.hpp
+++ b/include/sysrepo-cpp/Session.hpp
@@ -95,7 +95,7 @@ public:
void copyConfig(const Datastore source, const std::optional<std::string>& moduleName = std::nullopt, std::chrono::milliseconds timeout = std::chrono::milliseconds{0});
libyang::DataNode sendRPC(libyang::DataNode input, std::chrono::milliseconds timeout = std::chrono::milliseconds{0});
void sendNotification(libyang::DataNode notification, const Wait wait, std::chrono::milliseconds timeout = std::chrono::milliseconds{0});
- void replaceConfig(std::optional<libyang::DataNode> config, const std::optional<std::string>& module = std::nullopt, std::chrono::milliseconds timeout = std::chrono::milliseconds{0});
+ void replaceConfig(std::optional<libyang::DataNode> config, const std::optional<std::string>& moduleName = std::nullopt, std::chrono::milliseconds timeout = std::chrono::milliseconds{0});
void setNacmUser(const std::string& user);
[[nodiscard]] Subscription initNacm(
@@ -172,7 +172,7 @@ public:
*
* Wraps `sr_lock`.
*/
- explicit Lock(Session session, std::optional<std::string> module = std::nullopt, std::optional<std::chrono::milliseconds> timeout = std::nullopt);
+ explicit Lock(Session session, std::optional<std::string> moduleName = std::nullopt, std::optional<std::chrono::milliseconds> timeout = std::nullopt);
/** @brief Release the lock
*
* Wraps `sr_unlock`.
diff --git a/src/Session.cpp b/src/Session.cpp
index 2706e1c..94a0fad 100644
--- a/src/Session.cpp
+++ b/src/Session.cpp
@@ -331,17 +331,17 @@ void Session::sendNotification(libyang::DataNode notification, const Wait wait,
* Wraps `sr_replace_config`.
*
* @param config Libyang tree to use as a complete datastore content, or nullopt
- * @param module If provided, a module name to limit the operation to
+ * @param moduleName If provided, a module name to limit the operation to
* @param timeout Optional timeout to wait for
*/
-void Session::replaceConfig(std::optional<libyang::DataNode> config, const std::optional<std::string>& module, std::chrono::milliseconds timeout)
+void Session::replaceConfig(std::optional<libyang::DataNode> config, const std::optional<std::string>& moduleName, std::chrono::milliseconds timeout)
{
std::optional<libyang::DataNode> thrashable;
if (config) {
thrashable = config->duplicateWithSiblings(libyang::DuplicationOptions::Recursive | libyang::DuplicationOptions::WithParents);
}
auto res = sr_replace_config(
- m_sess.get(), module ? module->c_str() : nullptr,
+ m_sess.get(), moduleName ? moduleName->c_str() : nullptr,
config ? libyang::releaseRawNode(*thrashable) : nullptr,
timeout.count());
throwIfError(res, "sr_replace_config failed", m_sess.get());
@@ -723,12 +723,12 @@ uint32_t Session::getId() const
return sr_session_get_id(m_sess.get());
}
-Lock::Lock(Session session, std::optional<std::string> module, std::optional<std::chrono::milliseconds> timeout)
+Lock::Lock(Session session, std::optional<std::string> moduleName, std::optional<std::chrono::milliseconds> timeout)
: m_session(session)
, m_lockedDs(m_session.activeDatastore())
- , m_module(module)
+ , m_module(moduleName)
{
- auto res = sr_lock(getRawSession(m_session), module ? module->c_str() : nullptr, timeout ? timeout->count() : 0);
+ auto res = sr_lock(getRawSession(m_session), moduleName ? moduleName->c_str() : nullptr, timeout ? timeout->count() : 0);
throwIfError(res, "Cannot lock session", getRawSession(m_session));
}
--
2.43.0
@@ -0,0 +1,253 @@
From a923f490350dc61b9f3e6000c06c1d7950a78a61 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= <jan.kundrat@cesnet.cz>
Date: Wed, 18 Dec 2024 16:12:14 +0100
Subject: [PATCH 5/6] DRY: a dummy leaf XPath
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Addiva Elektronik
Change-Id: I47fca61d27380af07b2327f3ee5b984a8a8afb66
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
tests/session.cpp | 81 ++++++++++++++++++++++++-----------------------
1 file changed, 41 insertions(+), 40 deletions(-)
diff --git a/tests/session.cpp b/tests/session.cpp
index 5b384bb..f762f10 100644
--- a/tests/session.cpp
+++ b/tests/session.cpp
@@ -20,6 +20,7 @@ TEST_CASE("session")
std::optional<sysrepo::Connection> conn{std::in_place};
auto sess = conn->sessionStart();
sess.copyConfig(sysrepo::Datastore::Startup);
+ const auto leaf = "/test_module:leafInt32"s;
DOCTEST_SUBCASE("Session should be still valid even after the Connection class gets freed")
{
@@ -29,39 +30,39 @@ TEST_CASE("session")
DOCTEST_SUBCASE("Session lifetime is prolonged with data from getData")
{
- sess.setItem("/test_module:leafInt32", "123");
+ sess.setItem(leaf, "123");
sess.applyChanges();
- auto data = sysrepo::Connection{}.sessionStart().getData("/test_module:leafInt32");
+ auto data = sysrepo::Connection{}.sessionStart().getData(leaf);
REQUIRE(data->asTerm().valueStr() == "123");
}
DOCTEST_SUBCASE("basic data manipulation")
{
- auto data = sess.getData("/test_module:leafInt32");
+ auto data = sess.getData(leaf);
REQUIRE(!data);
- sess.setItem("/test_module:leafInt32", "123");
+ sess.setItem(leaf, "123");
sess.applyChanges();
- data = sess.getData("/test_module:leafInt32");
+ data = sess.getData(leaf);
REQUIRE(data->asTerm().valueStr() == "123");
- auto node = sess.getOneNode("/test_module:leafInt32");
+ auto node = sess.getOneNode(leaf);
REQUIRE(node.asTerm().valueStr() == "123");
- sess.setItem("/test_module:leafInt32", "420");
+ sess.setItem(leaf, "420");
sess.applyChanges();
- data = sess.getData("/test_module:leafInt32");
+ data = sess.getData(leaf);
REQUIRE(data->asTerm().valueStr() == "420");
- sess.deleteItem("/test_module:leafInt32");
+ sess.deleteItem(leaf);
sess.applyChanges();
- data = sess.getData("/test_module:leafInt32");
+ data = sess.getData(leaf);
REQUIRE(!data);
- sess.setItem("/test_module:leafInt32", "123");
+ sess.setItem(leaf, "123");
sess.discardChanges();
- data = sess.getData("/test_module:leafInt32");
+ data = sess.getData(leaf);
REQUIRE(!data);
- REQUIRE_THROWS_WITH_AS(sess.getOneNode("/test_module:leafInt32"),
+ REQUIRE_THROWS_WITH_AS(sess.getOneNode(leaf),
"Session::getOneNode: Couldn't get '/test_module:leafInt32': SR_ERR_NOT_FOUND",
sysrepo::ErrorWithCode);
@@ -175,7 +176,7 @@ TEST_CASE("session")
{
sess.switchDatastore(sysrepo::Datastore::Operational);
sess.setItem("/test_module:stateLeaf", "42");
- sess.setItem("/test_module:leafInt32", "1");
+ sess.setItem(leaf, "1");
sess.applyChanges();
DOCTEST_SUBCASE("Default options")
@@ -183,7 +184,7 @@ TEST_CASE("session")
auto data = sess.getData("/test_module:*");
REQUIRE(data);
REQUIRE(data->findPath("/test_module:stateLeaf"));
- REQUIRE(data->findPath("/test_module:leafInt32"));
+ REQUIRE(data->findPath(leaf));
}
DOCTEST_SUBCASE("No state data")
@@ -191,7 +192,7 @@ TEST_CASE("session")
auto data = sess.getData("/test_module:*", 0, sysrepo::GetOptions::OperNoState);
REQUIRE(data);
REQUIRE(!data->findPath("/test_module:stateLeaf"));
- REQUIRE(data->findPath("/test_module:leafInt32"));
+ REQUIRE(data->findPath(leaf));
}
}
@@ -213,42 +214,42 @@ TEST_CASE("session")
DOCTEST_SUBCASE("Session::deleteOperItem")
{
// Set some arbitrary leaf.
- sess.setItem("/test_module:leafInt32", "123");
+ sess.setItem(leaf, "123");
sess.applyChanges();
// The leaf is accesible from the running datastore.
- REQUIRE(sess.getData("/test_module:leafInt32")->asTerm().valueStr() == "123");
+ REQUIRE(sess.getData(leaf)->asTerm().valueStr() == "123");
// The leaf is NOT accesible from the operational datastore without a subscription.
sess.switchDatastore(sysrepo::Datastore::Operational);
- REQUIRE(!sess.getData("/test_module:leafInt32"));
+ REQUIRE(!sess.getData(leaf));
// When we create a subscription, the leaf is accesible from the operational datastore.
sess.switchDatastore(sysrepo::Datastore::Running);
auto sub = sess.onModuleChange("test_module", [] (auto, auto, auto, auto, auto, auto) { return sysrepo::ErrorCode::Ok; });
sess.switchDatastore(sysrepo::Datastore::Operational);
- REQUIRE(sess.getData("/test_module:leafInt32")->asTerm().valueStr() == "123");
+ REQUIRE(sess.getData(leaf)->asTerm().valueStr() == "123");
// After using deleteItem, the leaf is no longer accesible from the operational datastore.
- sess.deleteItem("/test_module:leafInt32");
+ sess.deleteItem(leaf);
sess.applyChanges();
- REQUIRE(!sess.getData("/test_module:leafInt32"));
+ REQUIRE(!sess.getData(leaf));
// Using discardItems makes the leaf visible again (in the operational datastore).
- sess.discardItems("/test_module:leafInt32");
+ sess.discardItems(leaf);
sess.applyChanges();
- REQUIRE(sess.getData("/test_module:leafInt32")->asTerm().valueStr() == "123");
+ REQUIRE(sess.getData(leaf)->asTerm().valueStr() == "123");
}
DOCTEST_SUBCASE("edit batch")
{
- auto data = sess.getData("/test_module:leafInt32");
+ auto data = sess.getData(leaf);
REQUIRE(!data);
- auto batch = sess.getContext().newPath("/test_module:leafInt32", "1230");
+ auto batch = sess.getContext().newPath(leaf, "1230");
sess.editBatch(batch, sysrepo::DefaultOperation::Merge);
sess.applyChanges();
- data = sess.getData("/test_module:leafInt32");
+ data = sess.getData(leaf);
REQUIRE(data->asTerm().valueStr() == "1230");
}
@@ -307,8 +308,8 @@ TEST_CASE("session")
DOCTEST_SUBCASE("Session::getPendingChanges")
{
REQUIRE(sess.getPendingChanges() == std::nullopt);
- sess.setItem("/test_module:leafInt32", "123");
- REQUIRE(sess.getPendingChanges().value().findPath("/test_module:leafInt32")->asTerm().valueStr() == "123");
+ sess.setItem(leaf, "123");
+ REQUIRE(sess.getPendingChanges().value().findPath(leaf)->asTerm().valueStr() == "123");
DOCTEST_SUBCASE("apply")
{
@@ -328,7 +329,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_THROWS_AS(sess.setItem("/test_module:leafInt32", "123"), sysrepo::ErrorWithCode);
+ REQUIRE_THROWS_AS(sess.setItem(leaf, "123"), sysrepo::ErrorWithCode);
}
DOCTEST_SUBCASE("session IDs")
@@ -391,9 +392,9 @@ TEST_CASE("session")
DOCTEST_SUBCASE("replace config")
{
- REQUIRE(!sess.getData("/test_module:leafInt32"));
+ REQUIRE(!sess.getData(leaf));
// some "reasonable data" for two modules
- sess.setItem("/test_module:leafInt32", "666");
+ sess.setItem(leaf, "666");
sess.setItem("/ietf-netconf-acm:nacm/groups/group[name='ahoj']/user-name[.='foo']", "");
sess.applyChanges();
@@ -401,16 +402,16 @@ TEST_CASE("session")
REQUIRE(!!conf);
// override a single leaf
- REQUIRE(sess.getOneNode("/test_module:leafInt32").asTerm().valueStr() == "666");
- sess.setItem("/test_module:leafInt32", "123");
+ REQUIRE(sess.getOneNode(leaf).asTerm().valueStr() == "666");
+ sess.setItem(leaf, "123");
sess.setItem("/ietf-netconf-acm:nacm/groups/group[name='ahoj']/user-name[.='bar']", "");
sess.applyChanges();
- REQUIRE(sess.getOneNode("/test_module:leafInt32").asTerm().valueStr() == "123");
+ REQUIRE(sess.getOneNode(leaf).asTerm().valueStr() == "123");
DOCTEST_SUBCASE("this module empty config")
{
sess.replaceConfig(std::nullopt, "test_module");
- REQUIRE(!sess.getData("/test_module:leafInt32"));
+ REQUIRE(!sess.getData(leaf));
REQUIRE(sess.getOneNode("/ietf-netconf-acm:nacm/groups/group[name='ahoj']/user-name[.='foo']").asTerm().valueStr() == "foo");
REQUIRE(sess.getOneNode("/ietf-netconf-acm:nacm/groups/group[name='ahoj']/user-name[.='bar']").asTerm().valueStr() == "bar");
}
@@ -418,7 +419,7 @@ TEST_CASE("session")
DOCTEST_SUBCASE("this module")
{
sess.replaceConfig(conf, "test_module");
- REQUIRE(sess.getOneNode("/test_module:leafInt32").asTerm().valueStr() == "666");
+ REQUIRE(sess.getOneNode(leaf).asTerm().valueStr() == "666");
REQUIRE(sess.getOneNode("/ietf-netconf-acm:nacm/groups/group[name='ahoj']/user-name[.='foo']").asTerm().valueStr() == "foo");
REQUIRE(sess.getOneNode("/ietf-netconf-acm:nacm/groups/group[name='ahoj']/user-name[.='bar']").asTerm().valueStr() == "bar");
}
@@ -426,7 +427,7 @@ TEST_CASE("session")
DOCTEST_SUBCASE("other module")
{
sess.replaceConfig(std::nullopt, "ietf-netconf-acm");
- REQUIRE(sess.getOneNode("/test_module:leafInt32").asTerm().valueStr() == "123");
+ REQUIRE(sess.getOneNode(leaf).asTerm().valueStr() == "123");
REQUIRE(!sess.getData("/ietf-netconf-acm:nacm/groups/group[name='ahoj']/user-name[.='foo']"));
REQUIRE(!sess.getData("/ietf-netconf-acm:nacm/groups/group[name='ahoj']/user-name[.='bar']"));
}
@@ -434,7 +435,7 @@ TEST_CASE("session")
DOCTEST_SUBCASE("entire datastore empty config")
{
sess.replaceConfig(std::nullopt);
- REQUIRE(!sess.getData("/test_module:leafInt32"));
+ REQUIRE(!sess.getData(leaf));
REQUIRE(!sess.getData("/ietf-netconf-acm:nacm/groups/group[name='ahoj']/user-name[.='foo']"));
REQUIRE(!sess.getData("/ietf-netconf-acm:nacm/groups/group[name='ahoj']/user-name[.='bar']"));
}
@@ -442,7 +443,7 @@ TEST_CASE("session")
DOCTEST_SUBCASE("entire datastore")
{
sess.replaceConfig(conf);
- REQUIRE(sess.getOneNode("/test_module:leafInt32").asTerm().valueStr() == "666");
+ REQUIRE(sess.getOneNode(leaf).asTerm().valueStr() == "666");
REQUIRE(sess.getOneNode("/ietf-netconf-acm:nacm/groups/group[name='ahoj']/user-name[.='foo']").asTerm().valueStr() == "foo");
REQUIRE(!sess.getData("/ietf-netconf-acm:nacm/groups/group[name='ahoj']/user-name[.='bar']"));
}
--
2.43.0
@@ -0,0 +1,42 @@
From 80e9659b41ea1de2e20d62f94319fe5af26adcee Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= <jan.kundrat@cesnet.cz>
Date: Wed, 18 Dec 2024 17:57:36 +0100
Subject: [PATCH 6/6] build: a single place to define package version
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Addiva Elektronik
Don't ask me how I found out that I need this :).
Change-Id: I2cd7397895ed4852f852e99b97543dde76eaff8f
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
CMakeLists.txt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 119bdd7..68ade53 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -20,7 +20,8 @@ add_custom_target(sysrepo-cpp-version-cmake
cmake/ProjectGitVersionRunner.cmake
)
include(cmake/ProjectGitVersion.cmake)
-prepare_git_version(SYSREPO_CPP_VERSION "3")
+set(SYSREPO_CPP_PKG_VERSION "3")
+prepare_git_version(SYSREPO_CPP_VERSION ${SYSREPO_CPP_PKG_VERSION})
find_package(Doxygen)
option(WITH_DOCS "Create and install internal documentation (needs Doxygen)" ${DOXYGEN_FOUND})
@@ -29,7 +30,6 @@ 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>=2.12.0 sysrepo<3 IMPORTED_TARGET)
-set(SYSREPO_CPP_PKG_VERSION "3")
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
--
2.43.0
@@ -0,0 +1,342 @@
From b34fb200812f9e6bc72e80bf46bd006ebc06a9da Mon Sep 17 00:00:00 2001
From: Jan Kundrát <jan.kundrat@cesnet.cz>
Date: Wed, 18 Dec 2024 17:16:55 +0100
Subject: [PATCH] API/ABI break: Change how pushed ops data work
Upstream has changed the way how pushed operational data are stored and
managed. From now on, each session has a separate edit, these edits are
applied on top of each other using a given priority (priority setting is
not implemented in the C++ wrapper at this time, patches welcome). Each
session can retrieve and manipulate the stored edit.
The key difference is that these edits are now per-session. This has a
wide-ranging impact, and the way how previously set nodes are "undone"
has changed. Unfortunately, upstream refused to rename the relevant
functions due to "API stability concerns", so we're taking care of
avoiding the possible confusion at the C++ layer:
- `Session::dropForeignOperationalContent` (aka `sr_discard_items`),
which is now used to ensure that matching content from "previous
sources" is discarded. These "previous sources" are either content of
`running`, or stuff that was stored/pushed by other sessions with
lower priority. It *cannot* be used to remove stuff that was
previously pushed by the current session.
- `Session::discardOperationalChanges` (aka `sr_discard_oper_changes`)
discards previously pushed content from *this session*.
- It is now possible to fully manage the edit (which incrementally
builds the `operational` DS) of the current session. Use
`Session::operationalChanges()` to retrieve a full libyang::DataNode
forest, modify it in whichever ways are needed, and store it back via
`Session::editBatch(..., sysrepo::DefaultOperation::Replace)` followed
by `Session::applyChanges()`.
Change-Id: Iba05a88411fd4c47c03d8c2b48cb7aadfd5dcd2a
---
diff --git a/.zuul.yaml b/.zuul.yaml
index c3aea22..dc87c6f 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -6,7 +6,7 @@
- name: github/CESNET/libyang
override-checkout: devel
- name: github/sysrepo/sysrepo
- override-checkout: cesnet/2024-10-before-oper-changes
+ override-checkout: devel
- name: github/onqtam/doctest
override-checkout: v2.4.8
- name: github/rollbear/trompeloeil
@@ -17,7 +17,7 @@
- name: github/CESNET/libyang
override-checkout: devel
- name: github/sysrepo/sysrepo
- override-checkout: cesnet/2024-10-before-oper-changes
+ override-checkout: devel
- name: github/onqtam/doctest
override-checkout: v2.4.11
- name: github/rollbear/trompeloeil
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 68ade53..cc4dba9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -20,7 +20,7 @@
cmake/ProjectGitVersionRunner.cmake
)
include(cmake/ProjectGitVersion.cmake)
-set(SYSREPO_CPP_PKG_VERSION "3")
+set(SYSREPO_CPP_PKG_VERSION "4")
prepare_git_version(SYSREPO_CPP_VERSION ${SYSREPO_CPP_PKG_VERSION})
find_package(Doxygen)
@@ -29,7 +29,7 @@
find_package(PkgConfig)
pkg_check_modules(LIBYANG_CPP REQUIRED libyang-cpp>=3 IMPORTED_TARGET)
-pkg_check_modules(SYSREPO REQUIRED sysrepo>=2.12.0 sysrepo<3 IMPORTED_TARGET)
+pkg_check_modules(SYSREPO REQUIRED sysrepo>=3.4.0 IMPORTED_TARGET)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
diff --git a/README.md b/README.md
index e14b772..27dc748 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,6 @@
## Dependencies
- [sysrepo](https://github.com/sysrepo/sysrepo) - the `devel` branch (even for the `master` branch of *sysrepo-cpp*)
- - we temporarily require pre-v3 sysrepo which introduced backward-incompatible changes to operational data handling
- [libyang-cpp](https://github.com/CESNET/libyang-cpp) - C++ bindings for *libyang*
- C++20 compiler (e.g., GCC 10.x+, clang 10+)
- CMake 3.19+
diff --git a/include/sysrepo-cpp/Session.hpp b/include/sysrepo-cpp/Session.hpp
index 477e604..238f480 100644
--- a/include/sysrepo-cpp/Session.hpp
+++ b/include/sysrepo-cpp/Session.hpp
@@ -85,13 +85,15 @@
void setItem(const std::string& path, const std::optional<std::string>& value, const EditOptions opts = sysrepo::EditOptions::Default);
void editBatch(libyang::DataNode edit, const DefaultOperation op);
void deleteItem(const std::string& path, const EditOptions opts = sysrepo::EditOptions::Default);
- void discardItems(const std::optional<std::string>& xpath);
void moveItem(const std::string& path, const MovePosition move, const std::optional<std::string>& keys_or_value, const std::optional<std::string>& origin = std::nullopt, const EditOptions opts = sysrepo::EditOptions::Default);
std::optional<libyang::DataNode> getData(const std::string& path, int maxDepth = 0, const GetOptions opts = sysrepo::GetOptions::Default, std::chrono::milliseconds timeout = std::chrono::milliseconds{0}) const;
libyang::DataNode getOneNode(const std::string& path, std::chrono::milliseconds timeout = std::chrono::milliseconds{0}) const;
std::optional<const libyang::DataNode> getPendingChanges() const;
void applyChanges(std::chrono::milliseconds timeout = std::chrono::milliseconds{0});
- void discardChanges();
+ void discardChanges(const std::optional<std::string>& xpath = std::nullopt);
+ std::optional<libyang::DataNode> operationalChanges(const std::optional<std::string>& moduleName = std::nullopt) const;
+ void discardOperationalChanges(const std::optional<std::string>& moduleName = std::nullopt, std::chrono::milliseconds timeout = std::chrono::milliseconds{0});
+ void dropForeignOperationalContent(const std::optional<std::string>& xpath);
void copyConfig(const Datastore source, const std::optional<std::string>& moduleName = std::nullopt, std::chrono::milliseconds timeout = std::chrono::milliseconds{0});
libyang::DataNode sendRPC(libyang::DataNode input, std::chrono::milliseconds timeout = std::chrono::milliseconds{0});
void sendNotification(libyang::DataNode notification, const Wait wait, std::chrono::milliseconds timeout = std::chrono::milliseconds{0});
diff --git a/src/Session.cpp b/src/Session.cpp
index 94a0fad..634e450 100644
--- a/src/Session.cpp
+++ b/src/Session.cpp
@@ -23,6 +23,26 @@
using namespace std::string_literals;
namespace sysrepo {
+
+namespace {
+libyang::DataNode wrapSrData(std::shared_ptr<sr_session_ctx_s> sess, sr_data_t* data)
+{
+ // Since the lyd_node came from sysrepo and it is wrapped in a sr_data_t, we have to postpone calling the
+ // sr_release_data() until after we're "done" with the libyang::DataNode.
+ //
+ // Normally, sr_release_data() would free the lyd_data as well. However, it is possible that the user wants to
+ // manipulate the data tree (think unlink()) in a way which might have needed to overwrite the tree->data pointer.
+ // Just delegate all the freeing to the C++ wrapper around lyd_data. The sysrepo library doesn't care about this.
+ auto tree = std::exchange(data->tree, nullptr);
+
+ // Use wrapRawNode, not wrapUnmanagedRawNode because we want to let the C++ wrapper manage memory.
+ // Note: We're capturing the session inside the lambda.
+ return libyang::wrapRawNode(tree, std::shared_ptr<sr_data_t>(data, [extend_session_lifetime = sess] (sr_data_t* data) {
+ sr_release_data(data);
+ }));
+}
+}
+
/**
* Wraps a pointer to sr_session_ctx_s and manages the lifetime of it. Also extends the lifetime of the connection
* specified by the `conn` argument.
@@ -119,20 +139,61 @@
}
/**
- * Prepare to discard nodes matching the specified xpath (or all if not set) previously set by the session connection.
- * Usable only for sysrepo::Datastore::Operational. The changes are applied only after calling Session::applyChanges.
+ * Prepare to drop "earlier content" from other sources in the operational DS for nodes matching the specified XPath
+ *
+ * The "earlier content" might come from the `running` datastore, or be pushed into the `operational` DS from
+ * another session, with a lower priority. This function prepares a special node into the current session's
+ * stored edit which effectively discards any matching content from previous, lower-priority sources.
+ *
+ * This function cannot be used to remove an edit which was pushed via the current session. To do that,
+ * use discardChanges(), or retrieve the stored edit and manipulate its libyang data tree.
+ *
+ * The changes are applied only after calling Session::applyChanges.
*
* Wraps `sr_discard_items`.
*
* @param xpath Expression filtering the nodes to discard, nullopt for all nodes.
*/
-void Session::discardItems(const std::optional<std::string>& xpath)
+void Session::dropForeignOperationalContent(const std::optional<std::string>& xpath)
{
auto res = sr_discard_items(m_sess.get(), xpath ? xpath->c_str() : nullptr);
throwIfError(res, "Session::discardItems: Can't discard "s + (xpath ? "'"s + *xpath + "'" : "all nodes"s), m_sess.get());
}
+/** @short Get a copy of the stored push-operational data for this session
+ *
+ * To modify the stored push operational data, modify this tree in-place and pass it to editBatch()
+ * with the "replace" operation.
+ *
+ * Wraps `sr_get_oper_changes`.
+ */
+std::optional<libyang::DataNode> Session::operationalChanges(const std::optional<std::string>& moduleName) const
+{
+ sr_data_t* data;
+ auto res = sr_get_oper_changes(m_sess.get(), moduleName ? moduleName->c_str() : nullptr, &data);
+
+ throwIfError(res, "Session::operationalChanges: Couldn't retrieve data'"s + (moduleName ? " for \"" + *moduleName + '"' : ""s), m_sess.get());
+
+ if (!data) {
+ return std::nullopt;
+ }
+
+ return wrapSrData(m_sess, data);
+
+}
+
+/** @short Discard push operational changes of a module for this session
+ *
+ * Wraps `sr_discard_oper_changes`.
+ *
+ * */
+void Session::discardOperationalChanges(const std::optional<std::string>& moduleName, std::chrono::milliseconds timeout)
+{
+ auto res = sr_discard_oper_changes(nullptr, 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());
+}
+
/**
* Moves item (a list or a leaf-list) specified by `path`.
* @param path Node to move.
@@ -155,25 +216,6 @@
throwIfError(res, "Session::moveItem: Can't move '"s + path + "'", m_sess.get());
}
-namespace {
-libyang::DataNode wrapSrData(std::shared_ptr<sr_session_ctx_s> sess, sr_data_t* data)
-{
- // Since the lyd_node came from sysrepo and it is wrapped in a sr_data_t, we have to postpone calling the
- // sr_release_data() until after we're "done" with the libyang::DataNode.
- //
- // Normally, sr_release_data() would free the lyd_data as well. However, it is possible that the user wants to
- // manipulate the data tree (think unlink()) in a way which might have needed to overwrite the tree->data pointer.
- // Just delegate all the freeing to the C++ wrapper around lyd_data. The sysrepo library doesn't care about this.
- auto tree = std::exchange(data->tree, nullptr);
-
- // Use wrapRawNode, not wrapUnmanagedRawNode because we want to let the C++ wrapper manage memory.
- // Note: We're capturing the session inside the lambda.
- return libyang::wrapRawNode(tree, std::shared_ptr<sr_data_t>(data, [extend_session_lifetime = sess] (sr_data_t* data) {
- sr_release_data(data);
- }));
-}
-}
-
/**
* @brief Returns a tree which contains all nodes that match the provided XPath.
*
@@ -263,13 +305,15 @@
}
/**
- * Discards changes made in this Session.
+ * Discards changes made earlier in this Session, optionally only below a given XPath
*
- * Wraps `sr_discard_changes`.
+ * The changes are applied only after calling Session::applyChanges.
+ *
+ * Wraps `sr_discard_changes_xpath`.
*/
-void Session::discardChanges()
+void Session::discardChanges(const std::optional<std::string>& xpath)
{
- auto res = sr_discard_changes(m_sess.get());
+ auto res = sr_discard_changes_xpath(m_sess.get(), xpath ? xpath->c_str() : nullptr);
throwIfError(res, "Session::discardChanges: Couldn't discard changes", m_sess.get());
}
diff --git a/tests/session.cpp b/tests/session.cpp
index f762f10..2637f67 100644
--- a/tests/session.cpp
+++ b/tests/session.cpp
@@ -211,7 +211,7 @@
}
}
- DOCTEST_SUBCASE("Session::deleteOperItem")
+ DOCTEST_SUBCASE("push operational data and deleting stuff")
{
// Set some arbitrary leaf.
sess.setItem(leaf, "123");
@@ -230,15 +230,51 @@
sess.switchDatastore(sysrepo::Datastore::Operational);
REQUIRE(sess.getData(leaf)->asTerm().valueStr() == "123");
- // After using deleteItem, the leaf is no longer accesible from the operational datastore.
- sess.deleteItem(leaf);
- sess.applyChanges();
- REQUIRE(!sess.getData(leaf));
+ DOCTEST_SUBCASE("discardOperationalChanges")
+ {
+ // apply a change which makes the leaf disappear
+ sess.dropForeignOperationalContent(leaf);
+ REQUIRE(!!sess.getData(leaf));
+ sess.applyChanges();
+ REQUIRE(!sess.getData(leaf));
- // Using discardItems makes the leaf visible again (in the operational datastore).
- sess.discardItems(leaf);
- sess.applyChanges();
- REQUIRE(sess.getData(leaf)->asTerm().valueStr() == "123");
+ // Using discardOperationalChanges makes the leaf visible again (in the operational datastore).
+ // Also, no need to applyChanges().
+ sess.discardOperationalChanges("test_module");
+ REQUIRE(sess.getData(leaf)->asTerm().valueStr() == "123");
+ }
+
+ DOCTEST_SUBCASE("direct edit of a libyang::DataNode")
+ {
+ // at first, set the leaf to some random value
+ sess.setItem(leaf, "456");
+ sess.applyChanges();
+ REQUIRE(sess.getData(leaf)->asTerm().valueStr() == "456");
+
+ // change the edit in-place
+ auto pushed = sess.operationalChanges();
+ REQUIRE(pushed->path() == leaf);
+ pushed->asTerm().changeValue("666");
+ sess.editBatch(*pushed, sysrepo::DefaultOperation::Replace);
+ sess.applyChanges();
+ REQUIRE(sess.getData(leaf)->asTerm().valueStr() == "666");
+
+ // Remove that previous edit in-place. Since the new edit cannot be empty, set some other leaf.
+ pushed = sess.operationalChanges();
+ auto another = "/test_module:popelnice/s"s;
+ pushed->newPath(another, "xxx");
+ pushed = *pushed->findPath(another);
+ pushed->findPath(leaf)->unlink();
+ // "the edit" for sysrepo must refer to a top-level node
+ while (pushed->parent()) {
+ pushed = *pushed->parent();
+ }
+ REQUIRE(!pushed->findPath(leaf));
+ REQUIRE(!!pushed->findPath(another));
+ sess.editBatch(*pushed, sysrepo::DefaultOperation::Replace);
+ sess.applyChanges();
+ REQUIRE(sess.getData(leaf)->asTerm().valueStr() == "123");
+ }
}
DOCTEST_SUBCASE("edit batch")
@@ -321,6 +357,11 @@
sess.discardChanges();
}
+ DOCTEST_SUBCASE("discard XPath")
+ {
+ sess.discardChanges(leaf);
+ }
+
REQUIRE(sess.getPendingChanges() == std::nullopt);
}
@@ -1,19 +1,23 @@
From 063f9b1696a4570aa61f02eb443d6752cab11c0c Mon Sep 17 00:00:00 2001
From 2950046cd17bf9296a0b70b8f6b38114f6985864 Mon Sep 17 00:00:00 2001
From: Joachim Wiberg <troglobit@gmail.com>
Date: Thu, 22 Jun 2023 10:24:57 +0200
Subject: [PATCH 1/3] Allow 'factory' as copy-from (only) in rpc copy-config
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Addiva Elektronik
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
src/netconf.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/netconf.c b/src/netconf.c
index e0b790f..8ae4160 100644
index f1cc6b3..fdd7297 100644
--- a/src/netconf.c
+++ b/src/netconf.c
@@ -361,6 +361,8 @@ np2srv_rpc_copyconfig_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), con
@@ -369,6 +369,8 @@ np2srv_rpc_copyconfig_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), con
ds = SR_DS_STARTUP;
} else if (!strcmp(nodeset->dnodes[0]->schema->name, "candidate")) {
ds = SR_DS_CANDIDATE;
@@ -1,4 +1,4 @@
From 26e36c7db9ebf11bf057a53e0c16238bda14792c Mon Sep 17 00:00:00 2001
From d2e4e60838761e2bdd02d651b8e0bea47cc2dcb5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mattias=20Walstr=C3=B6m?= <lazzer@gmail.com>
Date: Tue, 2 Jul 2024 14:56:15 +0200
Subject: [PATCH 2/3] Disable local users (backwards compat with older model)
@@ -32,10 +32,10 @@ index 25e531b..13234e1 100644
"ietf-tls-server@2023-12-28.yang -e server-ident-x509-cert -e client-auth-supported -e client-auth-x509-cert"
"ietf-netconf-server@2023-12-28.yang -e ssh-listen -e tls-listen -e ssh-call-home -e tls-call-home -e central-netconf-server-supported"
diff --git a/scripts/merge_config.sh b/scripts/merge_config.sh
index 4b476d6..e588369 100755
index eddada7..61af617 100755
--- a/scripts/merge_config.sh
+++ b/scripts/merge_config.sh
@@ -81,9 +81,6 @@ CONFIG="<netconf-server xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-server\
@@ -89,9 +89,6 @@ CONFIG="<netconf-server xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-server\
</public-key>
</host-key>
</server-identity>
@@ -0,0 +1,36 @@
From 2137bc0d614ad0aeabc37fd0041d05bc341a9007 Mon Sep 17 00:00:00 2001
From: Michal Vasko <mvasko@cesnet.cz>
Date: Fri, 10 Jan 2025 08:55:38 +0100
Subject: [PATCH 01/22] modinfo BUGFIX add NP containers to push oper data
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Addiva Elektronik
... so that oper get subscriptions for the
containers are called.
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
src/modinfo.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/modinfo.c b/src/modinfo.c
index a58717f8..14d0b2ae 100644
--- a/src/modinfo.c
+++ b/src/modinfo.c
@@ -1459,6 +1459,11 @@ sr_module_oper_data_update(struct sr_mod_info_mod_s *mod, const char *orig_name,
if ((err_info = sr_module_oper_data_load(mod, conn, 0, NULL, data))) {
return err_info;
}
+
+ /* add any missing NP containers in the data */
+ if ((err_info = sr_lyd_new_implicit_module(data, mod->ly_mod, LYD_IMPLICIT_NO_DEFAULTS, NULL))) {
+ return err_info;
+ }
}
if (get_oper_opts & SR_OPER_NO_SUBS) {
--
2.43.0
@@ -0,0 +1,30 @@
From d4cded05509b63083283ba3a9104b27663c7eb39 Mon Sep 17 00:00:00 2001
From: Michal Vasko <mvasko@cesnet.cz>
Date: Fri, 10 Jan 2025 08:56:29 +0100
Subject: [PATCH 02/22] sysrepo BUGFIX wrong param
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Addiva Elektronik
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
src/sysrepo.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/sysrepo.c b/src/sysrepo.c
index 844e11a5..7c96243c 100644
--- a/src/sysrepo.c
+++ b/src/sysrepo.c
@@ -3578,7 +3578,7 @@ sr_delete_item(sr_session_ctx_t *session, const char *path, const sr_edit_option
/* just delete the selected node */
node = NULL;
if (session->dt[session->ds].edit->tree &&
- (err_info = sr_lyd_find_path(session->dt[session->ds].edit->tree, path, 1, &node))) {
+ (err_info = sr_lyd_find_path(session->dt[session->ds].edit->tree, path, 0, &node))) {
goto cleanup;
}
if (node) {
--
2.43.0
@@ -0,0 +1,30 @@
From 526da167043615ab9a0a8d36d4dd4a92644a0f58 Mon Sep 17 00:00:00 2001
From: Michal Vasko <mvasko@cesnet.cz>
Date: Fri, 10 Jan 2025 08:56:54 +0100
Subject: [PATCH 03/22] SOVERSION bump to version 7.29.11
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Addiva Elektronik
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
CMakeLists.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 84990130..4ecfe4d9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -62,7 +62,7 @@ set(SYSREPO_VERSION ${SYSREPO_MAJOR_VERSION}.${SYSREPO_MINOR_VERSION}.${SYSREPO_
# with backward compatible change and micro version is connected with any internal change of the library.
set(SYSREPO_MAJOR_SOVERSION 7)
set(SYSREPO_MINOR_SOVERSION 29)
-set(SYSREPO_MICRO_SOVERSION 10)
+set(SYSREPO_MICRO_SOVERSION 11)
set(SYSREPO_SOVERSION_FULL ${SYSREPO_MAJOR_SOVERSION}.${SYSREPO_MINOR_SOVERSION}.${SYSREPO_MICRO_SOVERSION})
set(SYSREPO_SOVERSION ${SYSREPO_MAJOR_SOVERSION})
--
2.43.0
@@ -0,0 +1,30 @@
From 880e15c69312a471c85a8f125e5c0ade3a93dcb1 Mon Sep 17 00:00:00 2001
From: Michal Vasko <mvasko@cesnet.cz>
Date: Fri, 10 Jan 2025 08:57:04 +0100
Subject: [PATCH 04/22] VERSION bump to version 3.3.11
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Addiva Elektronik
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
CMakeLists.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4ecfe4d9..9f3cfde0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -54,7 +54,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS 1)
# micro version is changed with a set of small changes or bugfixes anywhere in the project.
set(SYSREPO_MAJOR_VERSION 3)
set(SYSREPO_MINOR_VERSION 3)
-set(SYSREPO_MICRO_VERSION 10)
+set(SYSREPO_MICRO_VERSION 11)
set(SYSREPO_VERSION ${SYSREPO_MAJOR_VERSION}.${SYSREPO_MINOR_VERSION}.${SYSREPO_MICRO_VERSION})
# Version of the library
--
2.43.0
@@ -0,0 +1,963 @@
From 31af3459bd8ea24228e2b13193176af6c34c289e Mon Sep 17 00:00:00 2001
From: Michal Vasko <mvasko@cesnet.cz>
Date: Mon, 13 Jan 2025 11:19:30 +0100
Subject: [PATCH 05/22] sysrepo UPDATE generate notifications on module changes
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Addiva Elektronik
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
modules/sysrepo-notifications@2025-01-10.yang | 97 ++++++
modules/sysrepo_notifications_yang.h | 208 ++++++++++++
src/common.c | 295 +++++++++++++++++-
src/common.h | 42 ++-
src/lyd_mods.c | 4 +
src/modinfo.c | 2 +-
src/replay.c | 42 +--
src/replay.h | 10 +-
src/sysrepo.c | 20 +-
tests/test_oper_pull.c | 6 +
10 files changed, 693 insertions(+), 33 deletions(-)
create mode 100644 modules/sysrepo-notifications@2025-01-10.yang
create mode 100644 modules/sysrepo_notifications_yang.h
diff --git a/modules/sysrepo-notifications@2025-01-10.yang b/modules/sysrepo-notifications@2025-01-10.yang
new file mode 100644
index 00000000..73df4353
--- /dev/null
+++ b/modules/sysrepo-notifications@2025-01-10.yang
@@ -0,0 +1,97 @@
+module sysrepo-notifications {
+ namespace "http://www.sysrepo.org/yang/sysrepo-notifications";
+ prefix srn;
+
+ yang-version 1.1;
+
+ import ietf-yang-library {
+ prefix yanglib;
+ }
+
+ organization
+ "CESNET";
+
+ contact
+ "Author: Michal Vasko
+ <mvasko@cesnet.cz>";
+
+ description
+ "Sysrepo YANG notifications for various events.";
+
+ revision "2025-01-10" {
+ description
+ "Initial revision.";
+ }
+
+ typedef module-revision {
+ type union {
+ type yanglib:revision-identifier;
+ type string {
+ length "0";
+ }
+ }
+ description
+ "The YANG module revision date.
+ A zero-length string is used if no revision statement
+ is present in the YANG module.";
+ }
+
+ notification module-change {
+ description
+ "YANG module change occurred.";
+
+ leaf name {
+ type string;
+ mandatory true;
+ description
+ "Name of the changed module.";
+ }
+
+ leaf revision {
+ type module-revision;
+ mandatory true;
+ description
+ "Revision of the module.";
+ }
+
+ leaf change {
+ type enumeration {
+ enum installed {
+ description
+ "New module was installed.";
+ }
+ enum uninstalled {
+ description
+ "Module was uninstalled.";
+ }
+ enum updated {
+ description
+ "Module was updated to a newer revision.";
+ }
+ enum feature-enabled {
+ description
+ "Feature was enabled in a module.";
+ }
+ enum feature-disabled {
+ description
+ "Feature was disabled in a module.";
+ }
+ }
+ mandatory true;
+ description
+ "Type of the module change.";
+ }
+
+ leaf old-revision {
+ when "../change = 'updated'";
+ type module-revision;
+ mandatory true;
+ }
+
+ leaf feature-name {
+ when "../change = ('feature-enabled' or 'feature-disabled')";
+ type string;
+ mandatory true;
+ }
+ }
+}
diff --git a/modules/sysrepo_notifications_yang.h b/modules/sysrepo_notifications_yang.h
new file mode 100644
index 00000000..8f99c518
--- /dev/null
+++ b/modules/sysrepo_notifications_yang.h
@@ -0,0 +1,208 @@
+char sysrepo_notifications_yang[] = {
+ 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73, 0x79, 0x73, 0x72, 0x65,
+ 0x70, 0x6f, 0x2d, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6e,
+ 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20, 0x22, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x73, 0x79, 0x73,
+ 0x72, 0x65, 0x70, 0x6f, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x79, 0x61, 0x6e,
+ 0x67, 0x2f, 0x73, 0x79, 0x73, 0x72, 0x65, 0x70, 0x6f, 0x2d, 0x6e, 0x6f,
+ 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22,
+ 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78,
+ 0x20, 0x73, 0x72, 0x6e, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x79,
+ 0x61, 0x6e, 0x67, 0x2d, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20,
+ 0x31, 0x2e, 0x31, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6d,
+ 0x70, 0x6f, 0x72, 0x74, 0x20, 0x69, 0x65, 0x74, 0x66, 0x2d, 0x79, 0x61,
+ 0x6e, 0x67, 0x2d, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x20, 0x7b,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x65,
+ 0x66, 0x69, 0x78, 0x20, 0x79, 0x61, 0x6e, 0x67, 0x6c, 0x69, 0x62, 0x3b,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x43, 0x45,
+ 0x53, 0x4e, 0x45, 0x54, 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x22, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x3a,
+ 0x20, 0x4d, 0x69, 0x63, 0x68, 0x61, 0x6c, 0x20, 0x56, 0x61, 0x73, 0x6b,
+ 0x6f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6d, 0x76, 0x61, 0x73,
+ 0x6b, 0x6f, 0x40, 0x63, 0x65, 0x73, 0x6e, 0x65, 0x74, 0x2e, 0x63, 0x7a,
+ 0x3e, 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x73,
+ 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x53, 0x79, 0x73, 0x72, 0x65, 0x70,
+ 0x6f, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x6e, 0x6f, 0x74, 0x69, 0x66,
+ 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x66, 0x6f, 0x72,
+ 0x20, 0x76, 0x61, 0x72, 0x69, 0x6f, 0x75, 0x73, 0x20, 0x65, 0x76, 0x65,
+ 0x6e, 0x74, 0x73, 0x2e, 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x22, 0x32, 0x30,
+ 0x32, 0x35, 0x2d, 0x30, 0x31, 0x2d, 0x31, 0x30, 0x22, 0x20, 0x7b, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x73, 0x63,
+ 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x49, 0x6e, 0x69,
+ 0x74, 0x69, 0x61, 0x6c, 0x20, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f,
+ 0x6e, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20,
+ 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2d, 0x72, 0x65, 0x76, 0x69, 0x73,
+ 0x69, 0x6f, 0x6e, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x75, 0x6e, 0x69, 0x6f, 0x6e,
+ 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x79, 0x61, 0x6e, 0x67,
+ 0x6c, 0x69, 0x62, 0x3a, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e,
+ 0x2d, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x3b,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
+ 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x6e, 0x67, 0x74,
+ 0x68, 0x20, 0x22, 0x30, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69,
+ 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x22, 0x54, 0x68, 0x65, 0x20, 0x59, 0x41, 0x4e, 0x47,
+ 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x72, 0x65, 0x76, 0x69,
+ 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x41, 0x20, 0x7a, 0x65, 0x72, 0x6f, 0x2d, 0x6c, 0x65, 0x6e, 0x67, 0x74,
+ 0x68, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x73, 0x20,
+ 0x75, 0x73, 0x65, 0x64, 0x20, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x20, 0x72,
+ 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x74, 0x61, 0x74,
+ 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x73, 0x20, 0x70, 0x72,
+ 0x65, 0x73, 0x65, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
+ 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2d, 0x63,
+ 0x68, 0x61, 0x6e, 0x67, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
+ 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x22, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x6d, 0x6f,
+ 0x64, 0x75, 0x6c, 0x65, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x20,
+ 0x6f, 0x63, 0x63, 0x75, 0x72, 0x72, 0x65, 0x64, 0x2e, 0x22, 0x3b, 0x0a,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x61,
+ 0x66, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70,
+ 0x65, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3b, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61,
+ 0x6e, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x20, 0x74, 0x72, 0x75, 0x65,
+ 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
+ 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x4e, 0x61, 0x6d, 0x65, 0x20,
+ 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67,
+ 0x65, 0x64, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2e, 0x22, 0x3b,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x61, 0x66,
+ 0x20, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x7b, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x74, 0x79, 0x70, 0x65, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2d,
+ 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x3b, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61,
+ 0x6e, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x20, 0x74, 0x72, 0x75, 0x65,
+ 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
+ 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x52, 0x65, 0x76, 0x69, 0x73,
+ 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d,
+ 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x61, 0x66, 0x20, 0x63, 0x68, 0x61,
+ 0x6e, 0x67, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x65,
+ 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x7b,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x75, 0x6d, 0x20, 0x69, 0x6e,
+ 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x20, 0x7b, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69,
+ 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x4e, 0x65, 0x77, 0x20, 0x6d,
+ 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x77, 0x61, 0x73, 0x20, 0x69, 0x6e,
+ 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x2e, 0x22, 0x3b, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x75,
+ 0x6d, 0x20, 0x75, 0x6e, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65,
+ 0x64, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x22, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x77, 0x61, 0x73, 0x20,
+ 0x75, 0x6e, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x2e,
+ 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x65, 0x6e, 0x75, 0x6d, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65,
+ 0x64, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x22, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x77, 0x61, 0x73, 0x20,
+ 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x61,
+ 0x20, 0x6e, 0x65, 0x77, 0x65, 0x72, 0x20, 0x72, 0x65, 0x76, 0x69, 0x73,
+ 0x69, 0x6f, 0x6e, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x75, 0x6d, 0x20, 0x66, 0x65,
+ 0x61, 0x74, 0x75, 0x72, 0x65, 0x2d, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65,
+ 0x64, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x22, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x77, 0x61, 0x73,
+ 0x20, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20,
+ 0x61, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2e, 0x22, 0x3b, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e,
+ 0x75, 0x6d, 0x20, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x2d, 0x64,
+ 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x20, 0x7b, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69,
+ 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x46, 0x65, 0x61, 0x74, 0x75,
+ 0x72, 0x65, 0x20, 0x77, 0x61, 0x73, 0x20, 0x64, 0x69, 0x73, 0x61, 0x62,
+ 0x6c, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x20, 0x6d, 0x6f, 0x64,
+ 0x75, 0x6c, 0x65, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x6d, 0x61, 0x6e, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x79,
+ 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72,
+ 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22,
+ 0x54, 0x79, 0x70, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67,
+ 0x65, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x6c, 0x65, 0x61, 0x66, 0x20, 0x6f, 0x6c, 0x64, 0x2d, 0x72, 0x65, 0x76,
+ 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x68, 0x65, 0x6e,
+ 0x20, 0x22, 0x2e, 0x2e, 0x2f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x20,
+ 0x3d, 0x20, 0x27, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x27, 0x22,
+ 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
+ 0x65, 0x2d, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x3b, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x6d, 0x61, 0x6e, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x20, 0x74, 0x72,
+ 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c,
+ 0x65, 0x61, 0x66, 0x20, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x2d,
+ 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20,
+ 0x22, 0x2e, 0x2e, 0x2f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x20, 0x3d,
+ 0x20, 0x28, 0x27, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x2d, 0x65,
+ 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x27, 0x20, 0x6f, 0x72, 0x20, 0x27,
+ 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x2d, 0x64, 0x69, 0x73, 0x61,
+ 0x62, 0x6c, 0x65, 0x64, 0x27, 0x29, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70,
+ 0x65, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3b, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61,
+ 0x6e, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x20, 0x74, 0x72, 0x75, 0x65,
+ 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x7d, 0x0a, 0x00
+};
diff --git a/src/common.c b/src/common.c
index 10a379c0..72f3fc93 100644
--- a/src/common.c
+++ b/src/common.c
@@ -55,10 +55,12 @@
#include "modinfo.h"
#include "plugins_datastore.h"
#include "plugins_notification.h"
+#include "replay.h"
#include "shm_ext.h"
#include "shm_main.h"
#include "shm_mod.h"
#include "shm_sub.h"
+#include "subscr.h"
#include "sysrepo.h"
#define SR_IS_YANG_ID_CHAR(c) (isalpha(c) || isdigit(c) || ((c) == '_') || ((c) == '-') || ((c) == '.'))
@@ -965,9 +967,9 @@ sr_module_default_mode(const struct lys_module *ly_mod)
if (!strcmp(ly_mod->name, "sysrepo")) {
return SR_INTMOD_MAIN_FILE_PERM;
} else if (sr_is_module_internal(ly_mod)) {
- if (!strcmp(ly_mod->name, "sysrepo-plugind") || !strcmp(ly_mod->name, "ietf-yang-schema-mount") ||
- !strcmp(ly_mod->name, "ietf-yang-library") || !strcmp(ly_mod->name, "ietf-netconf-notifications") ||
- !strcmp(ly_mod->name, "ietf-netconf")) {
+ if (!strcmp(ly_mod->name, "sysrepo-plugind") || !strcmp(ly_mod->name, "sysrepo-notifications") ||
+ !strcmp(ly_mod->name, "ietf-yang-schema-mount") || !strcmp(ly_mod->name, "ietf-yang-library") ||
+ !strcmp(ly_mod->name, "ietf-netconf-notifications") || !strcmp(ly_mod->name, "ietf-netconf")) {
return SR_INTMOD_WITHDATA_FILE_PERM;
} else if (!strcmp(ly_mod->name, "ietf-netconf-acm") || !strcmp(ly_mod->name, "sysrepo-monitoring")) {
return SR_INTMOD_NACM_SRMON_FILE_PERM;
@@ -5551,3 +5553,290 @@ sr_is_prod_env(void)
}
return sr_prod_env;
}
+
+/**
+ * @brief Check whether a 'sysrepo-notifications' notification even need to be generated.
+ *
+ * @param[in] conn Connection to use.
+ * @param[in] shm_mod SHM mod.
+ * @param[out] subs_or_replay Set if there are any subscribers or replay is enabled for the module.
+ * @return err_info, NULL on success.
+ */
+static sr_error_info_t *
+sr_generate_notif_has_subs_or_replay(sr_conn_ctx_t *conn, sr_mod_t *shm_mod, int *subs_or_replay)
+{
+ sr_error_info_t *err_info = NULL;
+ sr_mod_notif_sub_t *notif_subs;
+ uint32_t notif_sub_count;
+
+ /* EXT READ LOCK */
+ if ((err_info = sr_shmext_conn_remap_lock(conn, SR_LOCK_READ, 0, __func__))) {
+ goto cleanup;
+ }
+
+ /* get subscriber count */
+ err_info = sr_notif_find_subscriber(conn, "sysrepo-notifications", &notif_subs, &notif_sub_count, NULL);
+
+ /* EXT READ UNLOCK */
+ sr_shmext_conn_remap_unlock(conn, SR_LOCK_READ, 0, __func__);
+
+ if (err_info) {
+ goto cleanup;
+ }
+
+ /* check replay support and subscribers */
+ if (!shm_mod->replay_supp && !notif_sub_count) {
+ /* no subscribers and replay off */
+ *subs_or_replay = 0;
+ } else {
+ *subs_or_replay = 1;
+ }
+
+cleanup:
+ return err_info;
+}
+
+/**
+ * @brief Send a generated notification.
+ *
+ * @param[in] conn Connection to use.
+ * @param[in] shm_mod SHM mod.
+ * @param[in] notif Notification to send.
+ * @return err_info, NULL on success.
+ */
+static sr_error_info_t *
+sr_generate_notif_send(sr_conn_ctx_t *conn, sr_mod_t *shm_mod, const struct lyd_node *notif)
+{
+ sr_error_info_t *err_info = NULL;
+ struct timespec notif_ts_mono, notif_ts_real;
+
+ /* NOTIF SUB READ LOCK */
+ if ((err_info = sr_rwlock(&shm_mod->notif_lock, SR_SHMEXT_SUB_LOCK_TIMEOUT, SR_LOCK_READ, conn->cid,
+ __func__, NULL, NULL))) {
+ goto cleanup;
+ }
+
+ /* remember when the notification was generated */
+ sr_timeouttime_get(&notif_ts_mono, 0);
+ sr_realtime_get(&notif_ts_real);
+
+ /* send the notification (non-validated, must be valid) */
+ err_info = sr_shmsub_notif_notify(conn, notif, notif_ts_mono, notif_ts_real, NULL, NULL, 0, 0);
+
+ /* NOTIF SUB READ UNLOCK */
+ sr_rwunlock(&shm_mod->notif_lock, SR_SHMEXT_SUB_LOCK_TIMEOUT, SR_LOCK_READ, conn->cid, __func__);
+
+ if (err_info) {
+ goto cleanup;
+ }
+
+ /* store the notification for a replay */
+ if ((err_info = sr_replay_store(conn, NULL, notif, notif_ts_real))) {
+ goto cleanup;
+ }
+
+cleanup:
+ return err_info;
+}
+
+void
+sr_generate_notif_module_change_installed(sr_conn_ctx_t *conn, sr_int_install_mod_t *new_mods, uint32_t new_mod_count)
+{
+ sr_error_info_t *err_info = NULL;
+ struct lyd_node *notif = NULL;
+ sr_mod_t *shm_mod;
+ uint32_t i;
+ int subs_or_replay;
+
+ /* get this module */
+ shm_mod = sr_shmmod_find_module(SR_CONN_MOD_SHM(conn), "sysrepo-notifications");
+ SR_CHECK_INT_GOTO(!shm_mod, err_info, cleanup);
+
+ /* check whether to even generate any notifications */
+ if ((err_info = sr_generate_notif_has_subs_or_replay(conn, shm_mod, &subs_or_replay)) || !subs_or_replay) {
+ goto cleanup;
+ }
+
+ for (i = 0; i < new_mod_count; ++i) {
+ /* generate the notifcation */
+ if ((err_info = sr_lyd_new_path(NULL, conn->ly_ctx, "/sysrepo-notifications:module-change", NULL, 0, NULL,
+ &notif))) {
+ goto cleanup;
+ }
+ if ((err_info = sr_lyd_new_term(notif, NULL, "name", new_mods[i].ly_mod->name))) {
+ goto cleanup;
+ }
+ if ((err_info = sr_lyd_new_term(notif, NULL, "revision", new_mods[i].ly_mod->revision))) {
+ goto cleanup;
+ }
+ if ((err_info = sr_lyd_new_term(notif, NULL, "change", "installed"))) {
+ goto cleanup;
+ }
+
+ /* send it */
+ if ((err_info = sr_generate_notif_send(conn, shm_mod, notif))) {
+ goto cleanup;
+ }
+
+ lyd_free_siblings(notif);
+ notif = NULL;
+ }
+
+cleanup:
+ lyd_free_siblings(notif);
+ sr_errinfo_free(&err_info);
+}
+
+void
+sr_generate_notif_module_change_uninstalled(sr_conn_ctx_t *conn, struct ly_set *mod_set)
+{
+ sr_error_info_t *err_info = NULL;
+ struct lyd_node *notif = NULL;
+ const struct lys_module *ly_mod;
+ sr_mod_t *shm_mod;
+ uint32_t i;
+ int subs_or_replay;
+
+ /* get this module */
+ shm_mod = sr_shmmod_find_module(SR_CONN_MOD_SHM(conn), "sysrepo-notifications");
+ SR_CHECK_INT_GOTO(!shm_mod, err_info, cleanup);
+
+ /* check whether to even generate any notifications */
+ if ((err_info = sr_generate_notif_has_subs_or_replay(conn, shm_mod, &subs_or_replay)) || !subs_or_replay) {
+ goto cleanup;
+ }
+
+ for (i = 0; i < mod_set->count; ++i) {
+ ly_mod = mod_set->objs[i];
+
+ /* generate the notifcation */
+ if ((err_info = sr_lyd_new_path(NULL, conn->ly_ctx, "/sysrepo-notifications:module-change", NULL, 0, NULL,
+ &notif))) {
+ goto cleanup;
+ }
+ if ((err_info = sr_lyd_new_term(notif, NULL, "name", ly_mod->name))) {
+ goto cleanup;
+ }
+ if ((err_info = sr_lyd_new_term(notif, NULL, "revision", ly_mod->revision))) {
+ goto cleanup;
+ }
+ if ((err_info = sr_lyd_new_term(notif, NULL, "change", "uninstalled"))) {
+ goto cleanup;
+ }
+
+ /* send it */
+ if ((err_info = sr_generate_notif_send(conn, shm_mod, notif))) {
+ goto cleanup;
+ }
+
+ lyd_free_siblings(notif);
+ notif = NULL;
+ }
+
+cleanup:
+ lyd_free_siblings(notif);
+ sr_errinfo_free(&err_info);
+}
+
+void
+sr_generate_notif_module_change_updated(sr_conn_ctx_t *conn, struct ly_set *old_mod_set, struct ly_set *upd_mod_set)
+{
+ sr_error_info_t *err_info = NULL;
+ struct lyd_node *notif = NULL;
+ const struct lys_module *ly_mod;
+ sr_mod_t *shm_mod;
+ uint32_t i;
+ int subs_or_replay;
+
+ /* get this module */
+ shm_mod = sr_shmmod_find_module(SR_CONN_MOD_SHM(conn), "sysrepo-notifications");
+ SR_CHECK_INT_GOTO(!shm_mod, err_info, cleanup);
+
+ /* check whether to even generate any notifications */
+ if ((err_info = sr_generate_notif_has_subs_or_replay(conn, shm_mod, &subs_or_replay)) || !subs_or_replay) {
+ goto cleanup;
+ }
+
+ for (i = 0; i < upd_mod_set->count; ++i) {
+ ly_mod = upd_mod_set->objs[i];
+
+ /* generate the notifcation */
+ if ((err_info = sr_lyd_new_path(NULL, conn->ly_ctx, "/sysrepo-notifications:module-change", NULL, 0, NULL,
+ &notif))) {
+ goto cleanup;
+ }
+ if ((err_info = sr_lyd_new_term(notif, NULL, "name", ly_mod->name))) {
+ goto cleanup;
+ }
+ if ((err_info = sr_lyd_new_term(notif, NULL, "revision", ly_mod->revision))) {
+ goto cleanup;
+ }
+ if ((err_info = sr_lyd_new_term(notif, NULL, "change", "updated"))) {
+ goto cleanup;
+ }
+
+ ly_mod = old_mod_set->objs[i];
+ if ((err_info = sr_lyd_new_term(notif, NULL, "old-revision", ly_mod->revision))) {
+ goto cleanup;
+ }
+
+ /* send it */
+ if ((err_info = sr_generate_notif_send(conn, shm_mod, notif))) {
+ goto cleanup;
+ }
+
+ lyd_free_siblings(notif);
+ notif = NULL;
+ }
+
+cleanup:
+ lyd_free_siblings(notif);
+ sr_errinfo_free(&err_info);
+}
+
+void
+sr_generate_notif_module_change_feature(sr_conn_ctx_t *conn, const struct lys_module *ly_mod, const char *feature_name,
+ int enabled)
+{
+ sr_error_info_t *err_info = NULL;
+ struct lyd_node *notif = NULL;
+ sr_mod_t *shm_mod;
+ int subs_or_replay;
+
+ /* get this module */
+ shm_mod = sr_shmmod_find_module(SR_CONN_MOD_SHM(conn), "sysrepo-notifications");
+ SR_CHECK_INT_GOTO(!shm_mod, err_info, cleanup);
+
+ /* check whether to even generate any notifications */
+ if ((err_info = sr_generate_notif_has_subs_or_replay(conn, shm_mod, &subs_or_replay)) || !subs_or_replay) {
+ goto cleanup;
+ }
+
+ /* generate the notifcation */
+ if ((err_info = sr_lyd_new_path(NULL, conn->ly_ctx, "/sysrepo-notifications:module-change", NULL, 0, NULL,
+ &notif))) {
+ goto cleanup;
+ }
+ if ((err_info = sr_lyd_new_term(notif, NULL, "name", ly_mod->name))) {
+ goto cleanup;
+ }
+ if ((err_info = sr_lyd_new_term(notif, NULL, "revision", ly_mod->revision))) {
+ goto cleanup;
+ }
+ if ((err_info = sr_lyd_new_term(notif, NULL, "change", enabled ? "feature-enabled" : "feature-disabled"))) {
+ goto cleanup;
+ }
+
+ if ((err_info = sr_lyd_new_term(notif, NULL, "feature-name", feature_name))) {
+ goto cleanup;
+ }
+
+ /* send it */
+ if ((err_info = sr_generate_notif_send(conn, shm_mod, notif))) {
+ goto cleanup;
+ }
+
+cleanup:
+ lyd_free_siblings(notif);
+ sr_errinfo_free(&err_info);
+}
diff --git a/src/common.h b/src/common.h
index 99304bd7..9fc675d4 100644
--- a/src/common.h
+++ b/src/common.h
@@ -4,8 +4,8 @@
* @brief common routines header
*
* @copyright
- * Copyright (c) 2018 - 2023 Deutsche Telekom AG.
- * Copyright (c) 2018 - 2023 CESNET, z.s.p.o.
+ * Copyright (c) 2018 - 2025 Deutsche Telekom AG.
+ * Copyright (c) 2018 - 2025 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -1371,4 +1371,42 @@ sr_error_info_t *sr_conn_info(sr_cid_t **cids, pid_t **pids, uint32_t *count, sr
*/
int sr_is_prod_env(void);
+/**
+ * @brief Generate the 'module-change' notification for newly installed modules.
+ *
+ * @param[in] conn Connection to use.
+ * @param[in] new_mods Array of new modules.
+ * @param[in] new_mod_count Count of @p new_mods.
+ */
+void sr_generate_notif_module_change_installed(sr_conn_ctx_t *conn, sr_int_install_mod_t *new_mods,
+ uint32_t new_mod_count);
+
+/**
+ * @brief Generate the 'module-change' notification for uninstalled modules.
+ *
+ * @param[in] conn Connection to use.
+ * @param[in] mod_set Set of uninstalled modules.
+ */
+void sr_generate_notif_module_change_uninstalled(sr_conn_ctx_t *conn, struct ly_set *mod_set);
+
+/**
+ * @brief Generate the 'module-change' notification for updated modules.
+ *
+ * @param[in] conn Connection to use.
+ * @param[in] old_mod_set Set of old modules.
+ * @param[in] upd_mod_set Set of updated modules.
+ */
+void sr_generate_notif_module_change_updated(sr_conn_ctx_t *conn, struct ly_set *old_mod_set, struct ly_set *upd_mod_set);
+
+/**
+ * @brief Generate the 'module-change' notification for a module feature change.
+ *
+ * @param[in] conn Connection to use.
+ * @param[in] ly_mod Changed module.
+ * @param[in] feature_name Changed feature name.
+ * @param[in] enabled Whether the feature was enabled or disabled.
+ */
+void sr_generate_notif_module_change_feature(sr_conn_ctx_t *conn, const struct lys_module *ly_mod,
+ const char *feature_name, int enabled);
+
#endif /* _COMMON_H */
diff --git a/src/lyd_mods.c b/src/lyd_mods.c
index 256f292e..77f8cd7b 100644
--- a/src/lyd_mods.c
+++ b/src/lyd_mods.c
@@ -65,6 +65,7 @@
#include "../modules/ietf_netconf_yang.h"
#include "../modules/ietf_origin_yang.h"
#include "../modules/sysrepo_monitoring_yang.h"
+#include "../modules/sysrepo_notifications_yang.h"
#include "../modules/sysrepo_plugind_yang.h"
/**
@@ -1000,6 +1001,9 @@ sr_lydmods_create(sr_conn_ctx_t *conn, struct ly_ctx *ly_ctx, struct lyd_node **
/* install sysrepo-plugind */
SR_INSTALL_INT_MOD(ly_ctx, sysrepo_plugind_yang, 0, new_mods, new_mod_count);
+ /* install sysrepo-notifications */
+ SR_INSTALL_INT_MOD(ly_ctx, sysrepo_notifications_yang, 0, new_mods, new_mod_count);
+
/* install ietf-netconf (implemented dependency) and ietf-netconf-with-defaults */
SR_INSTALL_INT_MOD(ly_ctx, ietf_netconf_yang, 1, new_mods, new_mod_count);
SR_INSTALL_INT_MOD(ly_ctx, ietf_netconf_with_defaults_yang, 0, new_mods, new_mod_count);
diff --git a/src/modinfo.c b/src/modinfo.c
index 14d0b2ae..7414121e 100644
--- a/src/modinfo.c
+++ b/src/modinfo.c
@@ -3824,7 +3824,7 @@ sr_modinfo_generate_config_change_notif(struct sr_mod_info_s *mod_info, sr_sessi
}
/* store the notification for a replay */
- if ((err_info = sr_replay_store(session, notif, notif_ts_real))) {
+ if ((err_info = sr_replay_store(mod_info->conn, session, notif, notif_ts_real))) {
goto cleanup;
}
diff --git a/src/replay.c b/src/replay.c
index c31043de..00e63eec 100644
--- a/src/replay.c
+++ b/src/replay.c
@@ -127,7 +127,7 @@ cleanup:
}
sr_error_info_t *
-sr_replay_store(sr_session_ctx_t *sess, const struct lyd_node *notif, struct timespec notif_ts)
+sr_replay_store(sr_conn_ctx_t *conn, sr_session_ctx_t *sess, const struct lyd_node *notif, struct timespec notif_ts)
{
sr_error_info_t *err_info = NULL;
sr_mod_t *shm_mod;
@@ -146,7 +146,7 @@ sr_replay_store(sr_session_ctx_t *sess, const struct lyd_node *notif, struct tim
SR_CHECK_INT_RET(notif_op->schema->nodetype != LYS_NOTIF, err_info);
/* find SHM mod for replay lock and check if replay is even supported */
- shm_mod = sr_shmmod_find_module(SR_CONN_MOD_SHM(sess->conn), ly_mod->name);
+ shm_mod = sr_shmmod_find_module(SR_CONN_MOD_SHM(conn), ly_mod->name);
SR_CHECK_INT_RET(!shm_mod, err_info);
if (!shm_mod->replay_supp) {
@@ -154,32 +154,34 @@ sr_replay_store(sr_session_ctx_t *sess, const struct lyd_node *notif, struct tim
return NULL;
}
- /* MUTEX LOCK */
- sr_timeouttime_get(&timeout_ts, SR_NOTIF_BUF_LOCK_TIMEOUT);
- if ((r = pthread_mutex_clocklock(&sess->notif_buf.lock.mutex, COMPAT_CLOCK_ID, &timeout_ts))) {
- SR_ERRINFO_LOCK(&err_info, __func__, r);
- return err_info;
- }
+ if (sess) {
+ /* MUTEX LOCK */
+ sr_timeouttime_get(&timeout_ts, SR_NOTIF_BUF_LOCK_TIMEOUT);
+ if ((r = pthread_mutex_clocklock(&sess->notif_buf.lock.mutex, COMPAT_CLOCK_ID, &timeout_ts))) {
+ SR_ERRINFO_LOCK(&err_info, __func__, r);
+ return err_info;
+ }
- if (sess->notif_buf.thread_running) {
- /* store the notification in the buffer */
- has_buf = 1;
- err_info = sr_notif_buf_store(sess, notif, notif_ts);
+ if (sess->notif_buf.thread_running) {
+ /* store the notification in the buffer */
+ has_buf = 1;
+ err_info = sr_notif_buf_store(sess, notif, notif_ts);
- /* broadcast condition */
- sr_cond_broadcast(&sess->notif_buf.lock.cond);
- }
+ /* broadcast condition */
+ sr_cond_broadcast(&sess->notif_buf.lock.cond);
+ }
- /* MUTEX UNLOCK */
- pthread_mutex_unlock(&sess->notif_buf.lock.mutex);
+ /* MUTEX UNLOCK */
+ pthread_mutex_unlock(&sess->notif_buf.lock.mutex);
- if (err_info) {
- return err_info;
+ if (err_info) {
+ return err_info;
+ }
}
if (!has_buf) {
/* write the notification to a replay file */
- if ((err_info = sr_notif_write(sess->conn, shm_mod, notif, notif_ts))) {
+ if ((err_info = sr_notif_write(conn, shm_mod, notif, notif_ts))) {
return err_info;
}
}
diff --git a/src/replay.h b/src/replay.h
index ecf0cf25..7f3a851a 100644
--- a/src/replay.h
+++ b/src/replay.h
@@ -4,8 +4,8 @@
* @brief header for notification replay routines
*
* @copyright
- * Copyright (c) 2018 - 2021 Deutsche Telekom AG.
- * Copyright (c) 2018 - 2021 CESNET, z.s.p.o.
+ * Copyright (c) 2018 - 2025 Deutsche Telekom AG.
+ * Copyright (c) 2018 - 2025 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -26,12 +26,14 @@
/**
* @brief Store a notification for replay.
*
- * @param[in] sess Session to use.
+ * @param[in] conn Connection to use.
+ * @param[in] sess Session to use, if available.
* @param[in] notif Notification to store.
* @param[in] notif_ts Notification timestamp to store.
* @return err_info, NULL on success.
*/
-sr_error_info_t *sr_replay_store(sr_session_ctx_t *sess, const struct lyd_node *notif, struct timespec notif_ts);
+sr_error_info_t *sr_replay_store(sr_conn_ctx_t *conn, sr_session_ctx_t *sess, const struct lyd_node *notif,
+ struct timespec notif_ts);
/**
* @brief Notification buffer thread.
diff --git a/src/sysrepo.c b/src/sysrepo.c
index 7c96243c..614b5454 100644
--- a/src/sysrepo.c
+++ b/src/sysrepo.c
@@ -4,8 +4,8 @@
* @brief sysrepo API routines
*
* @copyright
- * Copyright (c) 2018 - 2024 Deutsche Telekom AG.
- * Copyright (c) 2018 - 2024 CESNET, z.s.p.o.
+ * Copyright (c) 2018 - 2025 Deutsche Telekom AG.
+ * Copyright (c) 2018 - 2025 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -1541,6 +1541,9 @@ _sr_install_modules(sr_conn_ctx_t *conn, const char *search_dirs, const char *da
SR_CONN_MAIN_SHM(conn)->content_id = ly_ctx_get_modules_hash(new_ctx);
sr_conn_ctx_switch(conn, &new_ctx, &old_ctx);
+ /* send the notification */
+ sr_generate_notif_module_change_installed(conn, *new_mods, *new_mod_count);
+
goto cleanup;
error:
@@ -1859,6 +1862,9 @@ sr_remove_modules(sr_conn_ctx_t *conn, const char **module_names, int force)
SR_CONN_MAIN_SHM(conn)->content_id = ly_ctx_get_modules_hash(new_ctx);
sr_conn_ctx_switch(conn, &new_ctx, &old_ctx);
+ /* send the notification */
+ sr_generate_notif_module_change_uninstalled(conn, &mod_set);
+
cleanup:
sr_lycc_update_data_clear(&data_info);
lyd_free_siblings(sr_mods);
@@ -2104,6 +2110,9 @@ sr_update_modules(sr_conn_ctx_t *conn, const char **schema_paths, const char *se
SR_CONN_MAIN_SHM(conn)->content_id = ly_ctx_get_modules_hash(new_ctx);
sr_conn_ctx_switch(conn, &new_ctx, &old_ctx);
+ /* send the notification */
+ sr_generate_notif_module_change_updated(conn, &old_mod_set, &upd_mod_set);
+
cleanup:
sr_lycc_update_data_clear(&data_info);
lyd_free_siblings(sr_mods);
@@ -2606,6 +2615,9 @@ sr_change_module_feature(sr_conn_ctx_t *conn, const char *module_name, const cha
SR_CONN_MAIN_SHM(conn)->content_id = ly_ctx_get_modules_hash(new_ctx);
sr_conn_ctx_switch(conn, &new_ctx, &old_ctx);
+ /* send the notification */
+ sr_generate_notif_module_change_feature(conn, ly_mod, feature_name, enable);
+
cleanup:
sr_lycc_update_data_clear(&data_info);
lyd_free_siblings(sr_mods);
@@ -2735,6 +2747,8 @@ sr_is_module_internal(const struct lys_module *ly_mod)
return 1;
} else if (!strcmp(ly_mod->name, "sysrepo-plugind")) {
return 1;
+ } else if (!strcmp(ly_mod->name, "sysrepo-notifications")) {
+ return 1;
} else if (!strcmp(ly_mod->name, "ietf-netconf-acm")) {
return 1;
}
@@ -7407,7 +7421,7 @@ sr_notif_send_tree(sr_session_ctx_t *session, struct lyd_node *notif, uint32_t t
}
/* store the notification for a replay */
- if ((err_info = sr_replay_store(session, notif_top, notif_ts_real))) {
+ if ((err_info = sr_replay_store(session->conn, session, notif_top, notif_ts_real))) {
goto cleanup;
}
diff --git a/tests/test_oper_pull.c b/tests/test_oper_pull.c
index c9654faa..91ba526e 100644
--- a/tests/test_oper_pull.c
+++ b/tests/test_oper_pull.c
@@ -406,6 +406,9 @@ test_sr_mon(void **state)
" </datastore>\n"
" </module>\n"
" <module>\n"
+ " <name>sysrepo-notifications</name>\n"
+ " </module>\n"
+ " <module>\n"
" <name>ietf-netconf</name>\n"
" </module>\n"
" <module>\n"
@@ -669,6 +672,9 @@ test_sr_mon(void **state)
" </ds-lock>\n"
" </module>\n"
" <module>\n"
+ " <name>sysrepo-notifications</name>\n"
+ " </module>\n"
+ " <module>\n"
" <name>ietf-netconf</name>\n"
" </module>\n"
" <module>\n"
--
2.43.0
@@ -0,0 +1,30 @@
From 05ef34960277dca49e1dda26eb2f4ffd5f1ba354 Mon Sep 17 00:00:00 2001
From: Michal Vasko <mvasko@cesnet.cz>
Date: Mon, 13 Jan 2025 11:19:51 +0100
Subject: [PATCH 06/22] SOVERSION bump to version 7.29.12
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Addiva Elektronik
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
CMakeLists.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9f3cfde0..fcb49a24 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -62,7 +62,7 @@ set(SYSREPO_VERSION ${SYSREPO_MAJOR_VERSION}.${SYSREPO_MINOR_VERSION}.${SYSREPO_
# with backward compatible change and micro version is connected with any internal change of the library.
set(SYSREPO_MAJOR_SOVERSION 7)
set(SYSREPO_MINOR_SOVERSION 29)
-set(SYSREPO_MICRO_SOVERSION 11)
+set(SYSREPO_MICRO_SOVERSION 12)
set(SYSREPO_SOVERSION_FULL ${SYSREPO_MAJOR_SOVERSION}.${SYSREPO_MINOR_SOVERSION}.${SYSREPO_MICRO_SOVERSION})
set(SYSREPO_SOVERSION ${SYSREPO_MAJOR_SOVERSION})
--
2.43.0
@@ -0,0 +1,32 @@
From 74f5fa11043e579b6c007a2ff1f5f1d880637b92 Mon Sep 17 00:00:00 2001
From: Michal Vasko <mvasko@cesnet.cz>
Date: Mon, 13 Jan 2025 11:20:00 +0100
Subject: [PATCH 07/22] VERSION bump to version 3.4.0
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Addiva Elektronik
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
CMakeLists.txt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index fcb49a24..7d49cedb 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -53,8 +53,8 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS 1)
# minor version changes with added functionality (new tool, functionality of the tool or library, ...) and
# micro version is changed with a set of small changes or bugfixes anywhere in the project.
set(SYSREPO_MAJOR_VERSION 3)
-set(SYSREPO_MINOR_VERSION 3)
-set(SYSREPO_MICRO_VERSION 11)
+set(SYSREPO_MINOR_VERSION 4)
+set(SYSREPO_MICRO_VERSION 0)
set(SYSREPO_VERSION ${SYSREPO_MAJOR_VERSION}.${SYSREPO_MINOR_VERSION}.${SYSREPO_MICRO_VERSION})
# Version of the library
--
2.43.0
@@ -0,0 +1,62 @@
From a0e02874aaf24a9e94c501ac95c5b9f55c316500 Mon Sep 17 00:00:00 2001
From: IrfanMohammad <irfan.haslanded@gmail.com>
Date: Mon, 13 Jan 2025 08:19:04 +0000
Subject: [PATCH 08/22] shmext REFACTOR conn_remap_unlock remove last hole
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Addiva Elektronik
The logic in sr_shmext_conn_remap_unlock can be cleaner.
A call to sr_file_get_size() can be avoided since we already know it
as `conn->ext_shm.size`.
Also cleanup the somewhat complex cast + arithmetic in a if condition.
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
src/shm_ext.c | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)
diff --git a/src/shm_ext.c b/src/shm_ext.c
index f4103837..78930469 100644
--- a/src/shm_ext.c
+++ b/src/shm_ext.c
@@ -131,8 +131,8 @@ sr_shmext_conn_remap_unlock(sr_conn_ctx_t *conn, sr_lock_mode_t mode, int ext_lo
{
sr_error_info_t *err_info = NULL;
sr_ext_hole_t *iter, *last = NULL;
- uint32_t last_size;
- size_t shm_file_size = 0;
+ uint32_t new_size;
+ char *last_hole_end;
/* make ext SHM smaller if there is a memory hole at its end */
if (((mode == SR_LOCK_WRITE) || (mode == SR_LOCK_WRITE_URGE)) && ext_lock) {
@@ -140,17 +140,15 @@ sr_shmext_conn_remap_unlock(sr_conn_ctx_t *conn, sr_lock_mode_t mode, int ext_lo
last = iter;
}
- if (last && ((uint32_t)((char *)last - conn->ext_shm.addr) + last->size == conn->ext_shm.size)) {
- if ((err_info = sr_file_get_size(conn->ext_shm.fd, &shm_file_size))) {
- goto cleanup_unlock;
- }
+ /* cast `last` as a char* for correct pointer arithmetic. */
+ last_hole_end = last ? ((char *)last + last->size) : NULL;
+ if (last_hole_end == conn->ext_shm.addr + conn->ext_shm.size) {
/* remove the hole */
- last_size = last->size;
+ new_size = conn->ext_shm.size - last->size;
sr_ext_hole_del(SR_CONN_EXT_SHM(conn), last);
-
/* remap (and truncate) ext SHM */
- if ((err_info = sr_shm_remap(&conn->ext_shm, shm_file_size - last_size))) {
+ if ((err_info = sr_shm_remap(&conn->ext_shm, new_size))) {
goto cleanup_unlock;
}
}
--
2.43.0
@@ -0,0 +1,250 @@
From e0f1e2480a3f69e291deed06a2626ed9b4df1662 Mon Sep 17 00:00:00 2001
From: IrfanMohammad <irfan.haslanded@gmail.com>
Date: Mon, 13 Jan 2025 13:46:28 +0000
Subject: [PATCH 09/22] modinfo UPDATE sess push oper data module cache
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Addiva Elektronik
Maintain a list of modules whose push oper data were modified
by the session.
Acquiring the global ext_lock is significantly more expensive.
Previously this was implemented for connections in
3b0165d656709b5f0cd3e8debc2e55c76c73663a
But in sysrepo v3.x, oper push data is tied to the session and the logic
needs to be re-introduced.
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
src/common_types.h | 7 +++-
src/modinfo.c | 93 ++++++++++++++++++++++++++--------------------
src/sysrepo.c | 14 ++++++-
3 files changed, 71 insertions(+), 43 deletions(-)
diff --git a/src/common_types.h b/src/common_types.h
index 915d9196..3ce27ad4 100644
--- a/src/common_types.h
+++ b/src/common_types.h
@@ -180,7 +180,12 @@ struct sr_session_ctx_s {
char *user; /**< Session (system) user. */
char *nacm_user; /**< Optional NACM user. If set, NACM is applied. */
sr_error_info_t *err_info; /**< Session error information. */
- int push_oper_data; /**< Whether the session has stored any push operational data. */
+
+ struct sr_oper_push_cache_s {
+ char *name; /**< Module name whose push oper data were ever modified by this session. */
+ int has_data; /**< Flag if there are any actual data currently. */
+ } *oper_push_mods;
+ uint32_t oper_push_mod_count; /**< Count of modules with modified push oper data by this session. */
char *orig_name; /**< Originator name used for all events sent on this session. */
void *orig_data; /**< Originator data used for all events sent on this session. */
diff --git a/src/modinfo.c b/src/modinfo.c
index 7414121e..d5c27672 100644
--- a/src/modinfo.c
+++ b/src/modinfo.c
@@ -322,42 +322,26 @@ sr_error_info_t *
sr_modinfo_collect_oper_sess(sr_session_ctx_t *sess, const struct lys_module *ly_mod, struct sr_mod_info_s *mod_info)
{
sr_error_info_t *err_info = NULL;
- sr_mod_shm_t *mod_shm;
- sr_mod_t *shm_mod;
- sr_mod_oper_push_t *oper_push;
const struct lys_module *ly_mod2;
- uint32_t i, j;
-
- mod_shm = SR_CONN_MOD_SHM(sess->conn);
-
- /* EXT READ LOCK */
- if ((err_info = sr_shmext_conn_remap_lock(sess->conn, SR_LOCK_READ, 1, __func__))) {
- return err_info;
- }
-
- /* go through all the SHM modules */
- for (i = 0; i < mod_shm->mod_count; ++i) {
- shm_mod = SR_SHM_MOD_IDX(mod_shm, i);
+ uint32_t i;
- if (ly_mod && strcmp(ly_mod->name, ((char *)mod_shm) + shm_mod->name)) {
+ /* add only the cached modules */
+ for (i = 0; i < sess->oper_push_mod_count; ++i) {
+ if (ly_mod && strcmp(ly_mod->name, sess->oper_push_mods[i].name)) {
continue;
}
- /* go through the sessions with oper data */
- for (j = 0; j < shm_mod->oper_push_data_count; ++j) {
- oper_push = &((sr_mod_oper_push_t *)(sess->conn->ext_shm.addr + shm_mod->oper_push_data))[j];
- if ((oper_push->sid == sess->sid) && oper_push->has_data) {
- break;
- }
+ if (!sess->oper_push_mods[i].has_data) {
+ continue;
}
- if (j < shm_mod->oper_push_data_count) {
- /* remember the module */
- ly_mod2 = ly_ctx_get_module_implemented(sess->conn->ly_ctx, ((char *)mod_shm) + shm_mod->name);
- SR_CHECK_INT_GOTO(!ly_mod2, err_info, cleanup_unlock);
- if ((err_info = sr_modinfo_add(ly_mod2, NULL, 0, 0, mod_info))) {
- goto cleanup_unlock;
- }
+ ly_mod2 = ly_ctx_get_module_implemented(sess->conn->ly_ctx, sess->oper_push_mods[i].name);
+ if (!ly_mod2) {
+ /* could have been removed */
+ continue;
+ }
+ if ((err_info = sr_modinfo_add(ly_mod2, NULL, 0, 0, mod_info))) {
+ return err_info;
}
if (ly_mod) {
@@ -365,11 +349,7 @@ sr_modinfo_collect_oper_sess(sr_session_ctx_t *sess, const struct lys_module *ly
}
}
-cleanup_unlock:
- /* EXT READ UNLOCK */
- sr_shmext_conn_remap_unlock(sess->conn, SR_LOCK_READ, 1, __func__);
-
- return err_info;
+ return NULL;
}
sr_error_info_t *
@@ -2664,7 +2644,7 @@ sr_modinfo_module_data_load(struct sr_mod_info_s *mod_info, struct sr_mod_info_m
struct lyd_node *mod_data = NULL;
const char **xpaths;
uint32_t xpath_count;
- int modified, has_data;
+ int modified, has_data = 0;
char *orig_name = NULL;
void *orig_data = NULL;
@@ -2674,8 +2654,8 @@ sr_modinfo_module_data_load(struct sr_mod_info_s *mod_info, struct sr_mod_info_m
if ((mod_info->ds == SR_DS_OPERATIONAL) && (mod_info->ds2 == SR_DS_OPERATIONAL)) {
assert(sess);
- /* check whether this session even has any push oper data for this module */
- if ((err_info = sr_shmext_oper_push_get(conn, mod->shm_mod, mod->ly_mod->name, sess->sid, NULL, &has_data,
+ /* check whether this session even has any push oper data and next push data for this module */
+ if (sess->oper_push_mod_count && (err_info = sr_shmext_oper_push_get(conn, mod->shm_mod, mod->ly_mod->name, sess->sid, NULL, &has_data,
SR_LOCK_READ))) {
return err_info;
}
@@ -2735,7 +2715,7 @@ sr_modinfo_module_data_load(struct sr_mod_info_s *mod_info, struct sr_mod_info_m
/* no cached data or unusable */
if ((mod_info->ds == SR_DS_OPERATIONAL) && (mod_info->ds2 == SR_DS_RUNNING)) {
- /* we need the whole runnign DS to avoid not getting parents of oper pull subscriptions and so considering
+ /* we need the whole running DS to avoid not getting parents of oper pull subscriptions and so considering
* them incorrectly as non-existent */
xpaths = NULL;
xpath_count = 0;
@@ -3839,6 +3819,35 @@ cleanup:
return err_info;
}
+static
+sr_error_info_t *
+sr_modinfo_push_oper_mod_update_sess(sr_session_ctx_t *sess, const char *mod_name, int has_data)
+{
+ sr_error_info_t *err_info = NULL;
+ uint32_t i;
+ void *mem;
+
+ for (i = 0; i < sess->oper_push_mod_count; ++i) {
+ if (!strcmp(sess->oper_push_mods[i].name, mod_name)) {
+ /* already added, update has_data */
+ sess->oper_push_mods[i].has_data = has_data;
+ return NULL;
+ }
+ }
+
+ /* add new module */
+ mem = realloc(sess->oper_push_mods, (i + 1) * sizeof *(sess->oper_push_mods));
+ SR_CHECK_MEM_GOTO(!mem, err_info, cleanup);
+ sess->oper_push_mods = mem;
+
+ sess->oper_push_mods[i].name = strdup(mod_name);
+ SR_CHECK_MEM_GOTO(!sess->oper_push_mods[i].name, err_info, cleanup);
+ sess->oper_push_mods[i].has_data = has_data;
+ ++sess->oper_push_mod_count;
+cleanup:
+ return err_info;
+}
+
sr_error_info_t *
sr_modinfo_data_store(struct sr_mod_info_s *mod_info, sr_session_ctx_t *session, int shmmod_session_del)
{
@@ -3911,12 +3920,14 @@ sr_modinfo_data_store(struct sr_mod_info_s *mod_info, sr_session_ctx_t *session,
} else {
/* stored oper data, update info in mod/ext SHM */
if ((err_info = sr_shmext_oper_push_update(mod_info->conn, mod->shm_mod, mod->ly_mod->name,
- sid, 0, mod_data ? 1 : 0, SR_LOCK_WRITE))) {
+ sid, 0, !!mod_data, SR_LOCK_WRITE))) {
goto cleanup;
}
- /* remember to discard the push oper data on session stop */
- session->push_oper_data = 1;
+ /* cache the modified module in the session */
+ if ((err_info = sr_modinfo_push_oper_mod_update_sess(session, mod->ly_mod->name, !!mod_data))) {
+ goto cleanup;
+ }
}
}
}
diff --git a/src/sysrepo.c b/src/sysrepo.c
index 614b5454..797de6aa 100644
--- a/src/sysrepo.c
+++ b/src/sysrepo.c
@@ -713,6 +713,7 @@ _sr_session_stop(sr_session_ctx_t *session)
{
sr_error_info_t *err_info = NULL, *tmp_err;
sr_datastore_t ds;
+ uint32_t i;
/* subscriptions need to be freed before, with a WRITE lock */
assert(!session->subscription_count && !session->subscriptions);
@@ -721,7 +722,7 @@ _sr_session_stop(sr_session_ctx_t *session)
tmp_err = sr_session_notif_buf_stop(session);
sr_errinfo_merge(&err_info, tmp_err);
- if (session->push_oper_data) {
+ if (session->oper_push_mod_count) {
/* free any stored operational data and the SHM ext push oper data entries */
_sr_discard_oper_changes(session, NULL, 1, 0);
}
@@ -755,6 +756,13 @@ _sr_session_stop(sr_session_ctx_t *session)
sr_release_data(session->dt[ds].edit);
lyd_free_all(session->dt[ds].diff);
}
+
+ /* free any push oper module names */
+ for (i = 0; i < session->oper_push_mod_count; ++i) {
+ free(session->oper_push_mods[i].name);
+ }
+ free(session->oper_push_mods);
+
sr_rwlock_destroy(&session->notif_buf.lock);
free(session);
@@ -4708,6 +4716,10 @@ sr_get_oper_changes(sr_session_ctx_t *session, const char *module_name, sr_data_
*data = NULL;
+ if (!session->oper_push_mod_count) {
+ return sr_api_ret(session, err_info);
+ }
+
SR_MODINFO_INIT(mod_info, conn, SR_DS_OPERATIONAL, SR_DS_OPERATIONAL);
/* CONTEXT LOCK */
--
2.43.0
@@ -0,0 +1,39 @@
From fef86228fcb8636d53faf43b36afd92b7161162b Mon Sep 17 00:00:00 2001
From: Michal Vasko <mvasko@cesnet.cz>
Date: Tue, 14 Jan 2025 08:53:59 +0100
Subject: [PATCH 10/22] modinfo REFACTOR formatting and missing doxygen
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Addiva Elektronik
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
src/modinfo.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/src/modinfo.c b/src/modinfo.c
index d5c27672..ff6ffc40 100644
--- a/src/modinfo.c
+++ b/src/modinfo.c
@@ -3819,8 +3819,15 @@ cleanup:
return err_info;
}
-static
-sr_error_info_t *
+/**
+ * @brief Update push oper mod data in a session, add a new module if not yet present.
+ *
+ * @param[in] sess Session to update.
+ * @param[in] mod_name Module name.
+ * @param[in] has_data Flag to store.
+ * @return err_info, NULL on success.
+ */
+static sr_error_info_t *
sr_modinfo_push_oper_mod_update_sess(sr_session_ctx_t *sess, const char *mod_name, int has_data)
{
sr_error_info_t *err_info = NULL;
--
2.43.0
@@ -0,0 +1,50 @@
From a593e1c993ae4dd7aa2942aa046633c8aaeb5ee4 Mon Sep 17 00:00:00 2001
From: Irfan <irfan.haslanded@gmail.com>
Date: Tue, 14 Jan 2025 08:59:44 +0100
Subject: [PATCH 11/22] shm_ext BUGFIX remap READ lock is enough (#3496)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Addiva Elektronik
`sr_shmext_rpc_sub_remove_dead()` calls `sr_shmext_rpc_sub_stop()`
which doesn't need a WRITE `ext_remap_lock`.
sr_shmext_recover_sub_rpc() already only acquire a READ lock.
Subscriptions can only be removed, which does not remap the SHM.
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
src/shm_ext.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/shm_ext.c b/src/shm_ext.c
index 78930469..917a049a 100644
--- a/src/shm_ext.c
+++ b/src/shm_ext.c
@@ -1476,8 +1476,8 @@ sr_shmext_rpc_sub_remove_dead(sr_conn_ctx_t *conn, off_t *subs, uint32_t *sub_co
sr_mod_rpc_sub_t *shm_sub;
char *path = NULL;
- /* EXT WRITE LOCK */
- if ((err_info = sr_shmext_conn_remap_lock(conn, SR_LOCK_WRITE, 1, __func__))) {
+ /* EXT READ LOCK */
+ if ((err_info = sr_shmext_conn_remap_lock(conn, SR_LOCK_READ, 1, __func__))) {
sr_errinfo_free(&err_info);
return;
}
@@ -1502,8 +1502,8 @@ sr_shmext_rpc_sub_remove_dead(sr_conn_ctx_t *conn, off_t *subs, uint32_t *sub_co
path = NULL;
}
- /* EXT WRITE UNLOCK */
- sr_shmext_conn_remap_unlock(conn, SR_LOCK_WRITE, 1, __func__);
+ /* EXT READ UNLOCK */
+ sr_shmext_conn_remap_unlock(conn, SR_LOCK_READ, 1, __func__);
}
sr_error_info_t *
--
2.43.0
@@ -0,0 +1,162 @@
From ce908d743bc9f49d5986c748dba71be194b7df89 Mon Sep 17 00:00:00 2001
From: Michal Vasko <mvasko@cesnet.cz>
Date: Tue, 14 Jan 2025 09:57:49 +0100
Subject: [PATCH 12/22] modinfo OPTIMIZE avoid long ext lock holding
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Addiva Elektronik
Fixes #3497
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
src/modinfo.c | 77 +++++++++++++++++++++++++++++++--------------------
1 file changed, 47 insertions(+), 30 deletions(-)
diff --git a/src/modinfo.c b/src/modinfo.c
index ff6ffc40..f969f363 100644
--- a/src/modinfo.c
+++ b/src/modinfo.c
@@ -4,8 +4,8 @@
* @brief routines for working with modinfo structure
*
* @copyright
- * Copyright (c) 2018 - 2023 Deutsche Telekom AG.
- * Copyright (c) 2018 - 2023 CESNET, z.s.p.o.
+ * Copyright (c) 2018 - 2025 Deutsche Telekom AG.
+ * Copyright (c) 2018 - 2025 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -1254,49 +1254,69 @@ sr_module_oper_data_load(struct sr_mod_info_mod_s *mod, sr_conn_ctx_t *conn, uin
struct lyd_node **mod_oper_data, struct lyd_node **data)
{
sr_error_info_t *err_info = NULL;
- sr_mod_oper_push_t *oper_push;
+ sr_mod_oper_push_t *oper_push_dup = NULL, *oper_push_ext;
struct lyd_node *mod_data = NULL, *next, *node;
const struct lys_module *ly_mod;
const char *xpath;
struct ly_set *set;
- uint32_t i, j, merge_opts;
- int last_sid = 0;
+ uint32_t i, j, merge_opts, oper_push_count = 0;
+ int last_sid = 0, dead_cid = 0;
/* EXT READ LOCK */
if ((err_info = sr_shmext_conn_remap_lock(conn, SR_LOCK_READ, 1, __func__))) {
- return err_info;
+ goto cleanup;
}
- oper_push = (sr_mod_oper_push_t *)(conn->ext_shm.addr + mod->shm_mod->oper_push_data);
- for (i = 0; i < mod->shm_mod->oper_push_data_count; ++i) {
- if (!sr_conn_is_alive(oper_push[i].cid)) {
- /* EXT READ UNLOCK */
- sr_shmext_conn_remap_unlock(conn, SR_LOCK_READ, 1, __func__);
-
- /* recover oper push data of all dead connections */
- if ((err_info = sr_shmmod_del_module_oper_data(conn, mod->ly_mod, &mod->state, mod->shm_mod, 1))) {
- goto cleanup;
+ if (mod->shm_mod->oper_push_data_count) {
+ /* make a local copy of the array, with only alive connections */
+ oper_push_dup = malloc(mod->shm_mod->oper_push_data_count * sizeof *oper_push_dup);
+ if (!oper_push_dup) {
+ SR_ERRINFO_MEM(&err_info);
+ } else {
+ oper_push_ext = (sr_mod_oper_push_t *)(conn->ext_shm.addr + mod->shm_mod->oper_push_data);
+ for (i = 0; i < mod->shm_mod->oper_push_data_count; ++i) {
+ if (!sr_conn_is_alive(oper_push_ext[i].cid)) {
+ /* remember to remove the dead connections */
+ dead_cid = 1;
+ } else {
+ oper_push_dup[oper_push_count] = oper_push_ext[i];
+ ++oper_push_count;
+ }
}
+ }
+ }
+
+ /* EXT READ UNLOCK */
+ sr_shmext_conn_remap_unlock(conn, SR_LOCK_READ, 1, __func__);
+
+ if (err_info) {
+ goto cleanup;
+ }
- /* call the function again */
- err_info = sr_module_oper_data_load(mod, conn, sid, mod_oper_data, data);
+ if (dead_cid) {
+ /* recover oper push data of all dead connections */
+ if ((err_info = sr_shmmod_del_module_oper_data(conn, mod->ly_mod, &mod->state, mod->shm_mod, 1))) {
goto cleanup;
- } else if (!oper_push[i].has_data) {
+ }
+ }
+
+ for (i = 0; i < oper_push_count; ++i) {
+ if (!oper_push_dup[i].has_data) {
/* no push oper data */
continue;
}
/* push data entries are ordered */
- assert(!i || (oper_push[i].order > oper_push[i - 1].order));
+ assert(!i || (oper_push_dup[i].order > oper_push_dup[i - 1].order));
- if (oper_push[i].sid == sid) {
+ if (oper_push_dup[i].sid == sid) {
last_sid = 1;
}
if (!last_sid || !mod_oper_data) {
/* load push oper data for the session */
- if ((err_info = sr_module_file_data_append(mod->ly_mod, mod->ds_handle, SR_DS_OPERATIONAL, oper_push[i].cid,
- oper_push[i].sid, NULL, 0, &mod_data))) {
- goto cleanup_unlock;
+ if ((err_info = sr_module_file_data_append(mod->ly_mod, mod->ds_handle, SR_DS_OPERATIONAL,
+ oper_push_dup[i].cid, oper_push_dup[i].sid, NULL, 0, &mod_data))) {
+ goto cleanup;
}
} else {
use_mod_oper_data:
@@ -1324,12 +1344,12 @@ use_mod_oper_data:
/* select the nodes to remove */
if ((err_info = sr_lyd_find_xpath(*data, xpath, &set))) {
- goto cleanup_unlock;
+ goto cleanup;
}
/* get rid of all redundant results that are descendants of another result */
if ((err_info = sr_xpath_set_filter_subtrees(set))) {
- goto cleanup_unlock;
+ goto cleanup;
}
/* free all the selected subtrees */
@@ -1346,7 +1366,7 @@ use_mod_oper_data:
merge_opts = LYD_MERGE_WITH_FLAGS;
}
if ((err_info = sr_lyd_merge_module(data, mod_data, mod->ly_mod, sr_oper_data_merge_cb, NULL, merge_opts))) {
- goto cleanup_unlock;
+ goto cleanup;
}
mod_data = NULL;
@@ -1362,11 +1382,8 @@ use_mod_oper_data:
goto use_mod_oper_data;
}
-cleanup_unlock:
- /* EXT READ UNLOCK */
- sr_shmext_conn_remap_unlock(conn, SR_LOCK_READ, 1, __func__);
-
cleanup:
+ free(oper_push_dup);
lyd_free_siblings(mod_data);
return err_info;
}
--
2.43.0
@@ -0,0 +1,30 @@
From 1977326b9a63bc9e1b7746dc0a5becb9b2aece26 Mon Sep 17 00:00:00 2001
From: Michal Vasko <mvasko@cesnet.cz>
Date: Tue, 14 Jan 2025 13:36:59 +0100
Subject: [PATCH 14/22] SOVERSION bump to version 7.29.13
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Addiva Elektronik
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
CMakeLists.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7d49cedb..040d2242 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -62,7 +62,7 @@ set(SYSREPO_VERSION ${SYSREPO_MAJOR_VERSION}.${SYSREPO_MINOR_VERSION}.${SYSREPO_
# with backward compatible change and micro version is connected with any internal change of the library.
set(SYSREPO_MAJOR_SOVERSION 7)
set(SYSREPO_MINOR_SOVERSION 29)
-set(SYSREPO_MICRO_SOVERSION 12)
+set(SYSREPO_MICRO_SOVERSION 13)
set(SYSREPO_SOVERSION_FULL ${SYSREPO_MAJOR_SOVERSION}.${SYSREPO_MINOR_SOVERSION}.${SYSREPO_MICRO_SOVERSION})
set(SYSREPO_SOVERSION ${SYSREPO_MAJOR_SOVERSION})
--
2.43.0
@@ -0,0 +1,30 @@
From df88c15e014d75dbd6e48d4ff1291fb6068f296d Mon Sep 17 00:00:00 2001
From: Michal Vasko <mvasko@cesnet.cz>
Date: Tue, 14 Jan 2025 13:37:09 +0100
Subject: [PATCH 15/22] VERSION bump to version 3.4.1
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Addiva Elektronik
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
CMakeLists.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 040d2242..7b86c0f3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -54,7 +54,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS 1)
# micro version is changed with a set of small changes or bugfixes anywhere in the project.
set(SYSREPO_MAJOR_VERSION 3)
set(SYSREPO_MINOR_VERSION 4)
-set(SYSREPO_MICRO_VERSION 0)
+set(SYSREPO_MICRO_VERSION 1)
set(SYSREPO_VERSION ${SYSREPO_MAJOR_VERSION}.${SYSREPO_MINOR_VERSION}.${SYSREPO_MICRO_VERSION})
# Version of the library
--
2.43.0
@@ -1,20 +1,24 @@
From e8daa7e3e6d24e66e1a74b00237af4c32438cb92 Mon Sep 17 00:00:00 2001
From 401c5b8ccce07b84e3b364283a8c789908f62f01 Mon Sep 17 00:00:00 2001
From: Joachim Wiberg <troglobit@gmail.com>
Date: Tue, 28 Mar 2023 10:37:53 +0200
Subject: [PATCH 1/7] sysrepo-plugind: add support for running in foreground
Subject: [PATCH 16/22] sysrepo-plugind: add support for running in foreground
with syslog
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Addiva Elektronik
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
src/executables/sysrepo-plugind.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/src/executables/sysrepo-plugind.c b/src/executables/sysrepo-plugind.c
index a6a817cc..5baebec3 100644
index 3b66cfa6..c531ca60 100644
--- a/src/executables/sysrepo-plugind.c
+++ b/src/executables/sysrepo-plugind.c
@@ -98,6 +98,7 @@ help_print(void)
@@ -100,6 +100,7 @@ help_print(void)
" Change verbosity to a level (none, error, warning, info, debug) or\n"
" number (0, 1, 2, 3, 4).\n"
" -d, --debug Debug mode - is not daemonized and logs to stderr instead of syslog.\n"
@@ -22,7 +26,7 @@ index a6a817cc..5baebec3 100644
" -P, --plugin-install <path>\n"
" Install a sysrepo-plugind plugin. The plugin is simply copied\n"
" to the designated plugin directory.\n"
@@ -201,6 +202,8 @@ daemon_init(int debug, sr_log_level_t log_level)
@@ -214,6 +215,8 @@ daemon_init(int debug, sr_log_level_t log_level)
if (debug) {
handle_signals();
@@ -31,7 +35,7 @@ index a6a817cc..5baebec3 100644
sr_log_stderr(log_level);
return;
}
@@ -241,6 +244,7 @@ daemon_init(int debug, sr_log_level_t log_level)
@@ -254,6 +257,7 @@ daemon_init(int debug, sr_log_level_t log_level)
close(fd);
}
@@ -39,7 +43,7 @@ index a6a817cc..5baebec3 100644
/* set verbosity */
sr_log_syslog("sysrepo-plugind", log_level);
}
@@ -466,6 +470,7 @@ main(int argc, char **argv)
@@ -479,6 +483,7 @@ main(int argc, char **argv)
{"version", no_argument, NULL, 'V'},
{"verbosity", required_argument, NULL, 'v'},
{"debug", no_argument, NULL, 'd'},
@@ -47,7 +51,7 @@ index a6a817cc..5baebec3 100644
{"plugin-install", required_argument, NULL, 'P'},
{"pid-file", required_argument, NULL, 'p'},
{"fatal-plugin-fail", no_argument, NULL, 'f'},
@@ -474,7 +479,7 @@ main(int argc, char **argv)
@@ -487,7 +492,7 @@ main(int argc, char **argv)
/* process options */
opterr = 0;
@@ -56,7 +60,7 @@ index a6a817cc..5baebec3 100644
switch (opt) {
case 'h':
version_print();
@@ -506,6 +511,9 @@ main(int argc, char **argv)
@@ -519,6 +524,9 @@ main(int argc, char **argv)
case 'd':
debug = 1;
break;
@@ -1,7 +1,10 @@
From 936d4f34a12ef1a2fe3372df051b77ea19ce5d59 Mon Sep 17 00:00:00 2001
From 6cba2363b8d3a56755365f59af7830f5bd25824a Mon Sep 17 00:00:00 2001
From: Joachim Wiberg <troglobit@gmail.com>
Date: Tue, 7 May 2024 15:41:53 +0200
Subject: [PATCH 2/7] Allow SR_EV_DONE to return any error to sysrepocfg
Subject: [PATCH 17/22] Allow SR_EV_DONE to return any error to sysrepocfg
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Addiva Elektronik
Importing a system configuration with sysrepocfg the model callbacks do
@@ -17,17 +20,18 @@ This patch is a clumsy way of forcing the (first) error to bubble up to
the surface and cause a non-zero exit code from sysrepocfg.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
src/shm_sub.c | 36 ++++++++++++++++++++++++++++--------
src/shm_sub.h | 2 +-
src/sysrepo.c | 2 +-
3 files changed, 30 insertions(+), 10 deletions(-)
src/sysrepo.c | 4 ++--
3 files changed, 31 insertions(+), 11 deletions(-)
diff --git a/src/shm_sub.c b/src/shm_sub.c
index 000f5906..4c59cbbf 100644
index fd6f105b..588cce09 100644
--- a/src/shm_sub.c
+++ b/src/shm_sub.c
@@ -1659,7 +1659,7 @@ cleanup:
@@ -1766,7 +1766,7 @@ cleanup:
sr_error_info_t *
sr_shmsub_change_notify_change_done(struct sr_mod_info_s *mod_info, const char *orig_name, const void *orig_data,
@@ -36,7 +40,7 @@ index 000f5906..4c59cbbf 100644
{
sr_error_info_t *err_info = NULL;
struct sr_mod_info_mod_s *mod = NULL;
@@ -1791,12 +1791,19 @@ sr_shmsub_change_notify_change_done(struct sr_mod_info_s *mod_info, const char *
@@ -1911,12 +1911,19 @@ sr_shmsub_change_notify_change_done(struct sr_mod_info_s *mod_info, const char *
sr_rwunlock(&nsub->sub_shm->lock, 0, SR_LOCK_WRITE, cid, __func__);
nsub->lock = SR_LOCK_NONE;
@@ -44,7 +48,7 @@ index 000f5906..4c59cbbf 100644
- sr_errinfo_free(&nsub->cb_err_info);
-
SR_LOG_DBG("EV ORIGIN: \"%s\" \"%s\" ID %" PRIu32 " priority %" PRIu32 " succeeded.",
nsub->mod->ly_mod->name, sr_ev2str(SR_SUB_EV_DONE), nsub->mod->request_id, nsub->cur_priority);
nsub->mod->ly_mod->name, sr_ev2str(SR_SUB_EV_DONE), mod_info->operation_id, nsub->cur_priority);
+ /*
+ * unexpected critical error, merge and cleanup, let
@@ -59,7 +63,7 @@ index 000f5906..4c59cbbf 100644
nsub->pending_event = 0;
}
} while (1);
@@ -3278,7 +3285,7 @@ sr_shmsub_change_listen_relock(sr_sub_shm_t *sub_shm, sr_lock_mode_t mode, struc
@@ -3424,7 +3431,7 @@ sr_shmsub_change_listen_relock(sr_sub_shm_t *sub_shm, sr_lock_mode_t mode, struc
sr_error_info_t *
sr_shmsub_change_listen_process_module_events(struct modsub_change_s *change_subs, sr_conn_ctx_t *conn)
{
@@ -68,7 +72,7 @@ index 000f5906..4c59cbbf 100644
uint32_t i, data_len = 0, valid_subscr_count;
char *data = NULL, *shm_data_ptr;
int ret = SR_ERR_OK, filter_valid;
@@ -3406,6 +3413,11 @@ process_event:
@@ -3553,6 +3560,11 @@ process_event:
}
break;
}
@@ -80,7 +84,7 @@ index 000f5906..4c59cbbf 100644
}
/* subscription processed this event */
@@ -3438,6 +3450,11 @@ process_event:
@@ -3585,6 +3597,11 @@ process_event:
}
break;
case SR_SUB_EV_DONE:
@@ -92,7 +96,7 @@ index 000f5906..4c59cbbf 100644
case SR_SUB_EV_ABORT:
/* nothing to do */
break;
@@ -3457,14 +3474,17 @@ process_event:
@@ -3604,14 +3621,17 @@ process_event:
/* SUB WRITE URGE LOCK */
if (sr_shmsub_change_listen_relock(sub_shm, SR_LOCK_WRITE_URGE, &sub_info, change_sub, change_subs->module_name,
@@ -112,7 +116,7 @@ index 000f5906..4c59cbbf 100644
goto cleanup;
}
@@ -3953,7 +3973,7 @@ finish_iter:
@@ -4101,7 +4121,7 @@ finish_iter:
sr_errinfo_free(&cb_err_info);
/* publish "done" event */
@@ -122,7 +126,7 @@ index 000f5906..4c59cbbf 100644
}
diff --git a/src/shm_sub.h b/src/shm_sub.h
index 62050e98..04637223 100644
index 505dab39..617a88b7 100644
--- a/src/shm_sub.h
+++ b/src/shm_sub.h
@@ -144,7 +144,7 @@ sr_error_info_t *sr_shmsub_change_notify_change(struct sr_mod_info_s *mod_info,
@@ -135,15 +139,24 @@ index 62050e98..04637223 100644
/**
* @brief Notify about (generate) a change "abort" event.
diff --git a/src/sysrepo.c b/src/sysrepo.c
index 06428bd8..3f08a627 100644
index 97537207..a1bbc61f 100644
--- a/src/sysrepo.c
+++ b/src/sysrepo.c
@@ -3913,7 +3913,7 @@ store:
@@ -3994,7 +3994,7 @@ sr_error_info_t *
sr_changes_notify_store(struct sr_mod_info_s *mod_info, sr_session_ctx_t *session, int shmmod_session_del,
uint32_t timeout_ms, sr_error_info_t **err_info2)
{
- sr_error_info_t *err_info = NULL;
+ sr_error_info_t *err_info = NULL, *cb_err_info = NULL;
struct sr_denied denied = {0};
sr_lock_mode_t change_sub_lock = SR_LOCK_NONE;
uint32_t sid = 0, err_count;
@@ -4141,7 +4141,7 @@ store:
}
/* publish "done" event, all changes were applied */
- if ((err_info = sr_shmsub_change_notify_change_done(mod_info, orig_name, orig_data, timeout_ms))) {
+ if ((err_info = sr_shmsub_change_notify_change_done(mod_info, orig_name, orig_data, timeout_ms, cb_err_info))) {
+ if ((err_info = sr_shmsub_change_notify_change_done(mod_info, orig_name, orig_data, timeout_ms, &cb_err_info))) {
goto cleanup;
}
@@ -1,18 +1,22 @@
From e518c0ee5b4a328c5cbc7e7f25d329d094183798 Mon Sep 17 00:00:00 2001
From e328151d027e408befb8b941418f84e2909f92e6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mattias=20Walstr=C3=B6m?= <lazzer@gmail.com>
Date: Wed, 8 May 2024 17:00:50 +0200
Subject: [PATCH 3/7] Allow to copy from factory default
Subject: [PATCH 18/22] Allow to copy from factory default
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Addiva Elektronik
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
src/sysrepo.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/sysrepo.c b/src/sysrepo.c
index 3f08a627..3b11c0b8 100644
index a1bbc61f..22ee9fe4 100644
--- a/src/sysrepo.c
+++ b/src/sysrepo.c
@@ -4161,7 +4161,7 @@ sr_copy_config(sr_session_ctx_t *session, const char *module_name, sr_datastore_
@@ -4535,7 +4535,7 @@ sr_copy_config(sr_session_ctx_t *session, const char *module_name, sr_datastore_
struct sr_mod_info_s mod_info;
const struct lys_module *ly_mod = NULL;
@@ -1,11 +1,16 @@
From a7e91aac7d7669c6b82cb06a1b4e3c3e3d79275f Mon Sep 17 00:00:00 2001
From f833cf10647b0c76386fe0850011204a321bb0d9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mattias=20Walstr=C3=B6m?= <lazzer@gmail.com>
Date: Mon, 6 May 2024 14:49:32 +0200
Subject: [PATCH 4/7] Add -z switch to sysrepoctl to install factory config
Subject: [PATCH 19/22] Add -z switch to sysrepoctl to install factory config
from a json file
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Addiva Elektronik
This to be able to load the yang modules during build time instead on boot.
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
src/executables/sysrepoctl.c | 20 +++++++++++++--
src/lyd_mods.h | 7 ++++++
@@ -84,10 +89,10 @@ index 1960bc06..a8ca5add 100644
+
#endif
diff --git a/src/sysrepo.c b/src/sysrepo.c
index 3b11c0b8..2e02de39 100644
index 22ee9fe4..518f34e6 100644
--- a/src/sysrepo.c
+++ b/src/sysrepo.c
@@ -1646,6 +1646,55 @@ sr_free_int_install_mods(sr_int_install_mod_t *new_mods, uint32_t new_mod_count)
@@ -1621,6 +1621,55 @@ sr_free_int_install_mods(sr_int_install_mod_t *new_mods, uint32_t new_mod_count)
free(new_mods);
}
@@ -144,7 +149,7 @@ index 3b11c0b8..2e02de39 100644
sr_install_module(sr_conn_ctx_t *conn, const char *schema_path, const char *search_dirs, const char **features)
{
diff --git a/src/sysrepo.h b/src/sysrepo.h
index 8a6a70b8..41c9e52b 100644
index 6881830d..0280244a 100644
--- a/src/sysrepo.h
+++ b/src/sysrepo.h
@@ -34,7 +34,6 @@ extern "C" {
@@ -155,7 +160,7 @@ index 8a6a70b8..41c9e52b 100644
/**
* @defgroup log_api Logging API
* @{
@@ -708,6 +707,15 @@ int sr_get_module_info(sr_conn_ctx_t *conn, sr_data_t **sysrepo_data);
@@ -717,6 +716,15 @@ int sr_get_module_info(sr_conn_ctx_t *conn, sr_data_t **sysrepo_data);
*/
int sr_is_module_internal(const struct lys_module *ly_mod);
@@ -1,7 +1,10 @@
From a4060077214c573f34f2a82b18083ccd27600a38 Mon Sep 17 00:00:00 2001
From fa14b6da3db9cf08d10108ce95fdfaecf7f76bf7 Mon Sep 17 00:00:00 2001
From: Joachim Wiberg <troglobit@gmail.com>
Date: Wed, 21 Aug 2024 16:00:35 +0200
Subject: [PATCH 5/7] Introduce new log level [SEC] for audit trails
Subject: [PATCH 20/22] Introduce new log level [SEC] for audit trails
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Addiva Elektronik
This adds a new log level for security and audit trail related log
@@ -19,6 +22,7 @@ system log daemon, dropping any [SEVERITY] prefix. Also, \n is most
often dropped by log daemons.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
src/log.c | 18 +++++++++++++++++-
src/log.h | 1 +
@@ -70,10 +74,10 @@ index e15055ac..25eab8fa 100644
/* logging callback */
diff --git a/src/log.h b/src/log.h
index d7e65b88..8722e51d 100644
index 9a60f1f6..acd7eead 100644
--- a/src/log.h
+++ b/src/log.h
@@ -32,6 +32,7 @@
@@ -31,6 +31,7 @@
#define SR_LOG_WRN(...) sr_log(SR_LL_WRN, __VA_ARGS__)
#define SR_LOG_INF(...) sr_log(SR_LL_INF, __VA_ARGS__)
@@ -82,7 +86,7 @@ index d7e65b88..8722e51d 100644
#define SR_CHECK_MEM_GOTO(cond, err_info, go) if (cond) { SR_ERRINFO_MEM(&(err_info)); goto go; }
diff --git a/src/sysrepo_types.h b/src/sysrepo_types.h
index 9f820b84..9a0de2be 100644
index 337ee51a..bec7317d 100644
--- a/src/sysrepo_types.h
+++ b/src/sysrepo_types.h
@@ -65,7 +65,8 @@ typedef enum {
@@ -1,7 +1,10 @@
From 3ee457861d98c62b2659b4fa49ec4571e78c83d4 Mon Sep 17 00:00:00 2001
From 0c28a5f1c99d8fe200f23647cf06ba640e767166 Mon Sep 17 00:00:00 2001
From: Joachim Wiberg <troglobit@gmail.com>
Date: Wed, 21 Aug 2024 16:04:43 +0200
Subject: [PATCH 6/7] Add audit trail for high priority system changes
Subject: [PATCH 21/22] Add audit trail for high priority system changes
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Addiva Elektronik
Committing a change to running, copying to a datastore, or calling an
@@ -13,15 +16,16 @@ is when the system actually activates the changes. Copying to startup
or other datastores is handled separately.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
src/sysrepo.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/src/sysrepo.c b/src/sysrepo.c
index 2e02de39..85a8e41c 100644
index 518f34e6..a7e6563a 100644
--- a/src/sysrepo.c
+++ b/src/sysrepo.c
@@ -3961,6 +3961,9 @@ store:
@@ -4189,6 +4189,9 @@ store:
goto cleanup;
}
@@ -29,9 +33,9 @@ index 2e02de39..85a8e41c 100644
+ SR_LOG_SEC("user \"%s\" commiting changes to %s ...", session->nacm_user, sr_ds2str(mod_info->ds));
+
/* publish "done" event, all changes were applied */
if ((err_info = sr_shmsub_change_notify_change_done(mod_info, orig_name, orig_data, timeout_ms, cb_err_info))) {
if ((err_info = sr_shmsub_change_notify_change_done(mod_info, orig_name, orig_data, timeout_ms, &cb_err_info))) {
goto cleanup;
@@ -3972,6 +3975,9 @@ store:
@@ -4200,6 +4203,9 @@ store:
}
cleanup:
@@ -41,7 +45,7 @@ index 2e02de39..85a8e41c 100644
if (change_sub_lock) {
assert(change_sub_lock == SR_LOCK_READ);
@@ -4300,6 +4306,9 @@ sr_copy_config(sr_session_ctx_t *session, const char *module_name, sr_datastore_
@@ -4671,6 +4677,9 @@ sr_copy_config(sr_session_ctx_t *session, const char *module_name, sr_datastore_
}
}
@@ -51,7 +55,7 @@ index 2e02de39..85a8e41c 100644
cleanup:
/* MODULES UNLOCK */
sr_shmmod_modinfo_unlock(&mod_info);
@@ -6580,6 +6589,9 @@ sr_rpc_send_tree(sr_session_ctx_t *session, struct lyd_node *input, uint32_t tim
@@ -7132,6 +7141,9 @@ sr_rpc_send_tree(sr_session_ctx_t *session, struct lyd_node *input, uint32_t tim
}
}
@@ -1,7 +1,10 @@
From be7579a3a787d7089acbda7536a21a2a9edc487d Mon Sep 17 00:00:00 2001
From 72be8d7e245fe8cd30bd1e2b5d308aacbf0f7b07 Mon Sep 17 00:00:00 2001
From: Joachim Wiberg <troglobit@gmail.com>
Date: Fri, 23 Aug 2024 12:22:06 +0200
Subject: [PATCH 7/7] On error in sr_shmsub_listen_thread(), exit process
Subject: [PATCH 22/22] On error in sr_shmsub_listen_thread(), exit process
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Addiva Elektronik
If processing callback events in, e.g., sysrepo-plugind, make sure to
@@ -9,23 +12,16 @@ log the error and exit(1) the entire process so the system can decide
to handle the problem. For example, restart all dependent services.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
src/shm_sub.c | 3 +++
1 file changed, 3 insertions(+)
src/shm_sub.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/shm_sub.c b/src/shm_sub.c
index 4c59cbbf..3d703cac 100644
index 588cce09..07d8960f 100644
--- a/src/shm_sub.c
+++ b/src/shm_sub.c
@@ -4825,6 +4825,7 @@ sr_shmsub_listen_thread(void *arg)
* another event is generated, our event pipe will not get notified */
continue;
} else if (ret) {
+ SR_LOG_WRN("failed sr_subscription_process_events(), ret:%d", ret);
goto error;
}
@@ -4865,5 +4866,7 @@ error:
@@ -4987,5 +4987,7 @@ error:
/* free our own resources */
ATOMIC_STORE_RELAXED(subscr->thread_running, 0);
pthread_detach(pthread_self());