From f14116c9216a448cdc2877fcd1e934bd9af1fbd2 Mon Sep 17 00:00:00 2001 From: Stefan Gula Date: Sun, 3 Mar 2024 11:40:00 +0100 Subject: [PATCH] data: add leafref_nodes and leafref_link_node_tree This patch adds API, which allows user to: - determine if DNode is being referenced by some other leafref DNodes. - trigger process for creating cross-references between leafref DNodes and target DNodes. Closes: https://github.com/CESNET/libyang-python/pull/108 Signed-off-by: Stefan Gula Signed-off-by: Samuel Gauthier --- cffi/cdefs.h | 9 +++++++++ libyang/data.py | 24 +++++++++++++++++++++++- tests/test_data.py | 26 ++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/cffi/cdefs.h b/cffi/cdefs.h index 24688fc..6079ecf 100644 --- a/cffi/cdefs.h +++ b/cffi/cdefs.h @@ -1248,5 +1248,14 @@ struct lyd_attr { LY_ERR lyd_new_attr(struct lyd_node *, const char *, const char *, const char *, struct lyd_attr **); void lyd_free_attr_single(const struct ly_ctx *ctx, struct lyd_attr *attr); +struct lyd_leafref_links_rec { + const struct lyd_node_term *node; + const struct lyd_node_term **leafref_nodes; + const struct lyd_node_term **target_nodes; +}; + +LY_ERR lyd_leafref_get_links(const struct lyd_node_term *e, const struct lyd_leafref_links_rec **); +LY_ERR lyd_leafref_link_node_tree(struct lyd_node *); + /* from libc, needed to free allocated strings */ void free(void *); diff --git a/libyang/data.py b/libyang/data.py index 96b792b..00490db 100644 --- a/libyang/data.py +++ b/libyang/data.py @@ -18,7 +18,7 @@ SRpc, Type, ) -from .util import DataType, IOType, LibyangError, c2str, str2c +from .util import DataType, IOType, LibyangError, c2str, ly_array_iter, str2c LOG = logging.getLogger(__name__) @@ -991,6 +991,28 @@ def free(self, with_siblings: bool = True) -> None: finally: self.cdata = ffi.NULL + def leafref_link_node_tree(self) -> None: + """ + Traverse through data tree including root node siblings and adds + leafrefs links to the given nodes. + + Requires leafref_linking to be set on the libyang context. + """ + lib.lyd_leafref_link_node_tree(self.cdata) + + def leafref_nodes(self) -> Iterator["DNode"]: + """ + Gets the leafref links record for given node. + + Requires leafref_linking to be set on the libyang context. + """ + term_node = ffi.cast("struct lyd_node_term *", self.cdata) + out = ffi.new("const struct lyd_leafref_links_rec **") + if lib.lyd_leafref_get_links(term_node, out) != lib.LY_SUCCESS: + return + for n in ly_array_iter(out[0].leafref_nodes): + yield DNode.new(self.context, n) + def __repr__(self): cls = self.__class__ return "<%s.%s: %s>" % (cls.__module__, cls.__name__, str(self)) diff --git a/tests/test_data.py b/tests/test_data.py index d43f212..9ed35dc 100644 --- a/tests/test_data.py +++ b/tests/test_data.py @@ -21,6 +21,7 @@ DRpc, IOType, LibyangError, + Module, ) from libyang.data import dict_to_dnode @@ -1066,3 +1067,28 @@ def test_dnode_attrs_set_and_remove_multiple(self): attrs.remove("ietf-netconf:operation") self.assertEqual(len(attrs), 0) + + def test_dnode_leafref_linking(self): + MAIN = """{ + "yolo-leafref-extended:list1": [{ + "leaf1": "val1", + "leaflist2": ["val2", "val3"] + }], + "yolo-leafref-extended:ref1": "val1" + }""" + self.ctx.destroy() + self.ctx = Context(YANG_DIR, leafref_extended=True, leafref_linking=True) + mod = self.ctx.load_module("yolo-leafref-extended") + self.assertIsInstance(mod, Module) + dnode1 = self.ctx.parse_data_mem(MAIN, "json", parse_only=True) + self.assertIsInstance(dnode1, DList) + dnode2 = next(dnode1.siblings(include_self=False)) + self.assertIsInstance(dnode2, DLeaf) + dnode3 = next(dnode1.children()) + self.assertIsInstance(dnode3, DLeaf) + self.assertIsNone(next(dnode3.leafref_nodes(), None)) + dnode2.leafref_link_node_tree() + dnode4 = next(dnode3.leafref_nodes()) + self.assertIsInstance(dnode4, DLeaf) + self.assertEqual(dnode4.cdata, dnode2.cdata) + dnode1.free()