summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Hopps <chopps@labn.net>2025-01-07 12:46:23 +0100
committerChristian Hopps <chopps@labn.net>2025-01-14 05:40:52 +0100
commit8a52b69ca4c1fb34d46f04cc348faba191ad6ae3 (patch)
treee9ea5b318d00855394d912caa882e3779c680455
parentMerge pull request #17843 from LabNConsulting/chopps/new-docker-and-doc (diff)
downloadfrr-8a52b69ca4c1fb34d46f04cc348faba191ad6ae3.tar.xz
frr-8a52b69ca4c1fb34d46f04cc348faba191ad6ae3.zip
lib: northbound: add basic oper-state update functions
Signed-off-by: Christian Hopps <chopps@labn.net>
-rw-r--r--lib/northbound.h62
-rw-r--r--lib/northbound_notif.c156
-rw-r--r--lib/northbound_oper.c1
-rw-r--r--lib/subdir.am1
-rw-r--r--lib/yang.c111
-rw-r--r--lib/yang.h60
6 files changed, 391 insertions, 0 deletions
diff --git a/lib/northbound.h b/lib/northbound.h
index 38d8c2bdc..e3f96c7bc 100644
--- a/lib/northbound.h
+++ b/lib/northbound.h
@@ -1744,6 +1744,68 @@ extern void nb_oper_init(struct event_loop *loop);
extern void nb_oper_terminate(void);
extern bool nb_oper_is_yang_lib_query(const char *xpath);
+
+/**
+ * nb_op_update() - Create new state data.
+ * @tree: subtree @path is relative to or NULL in which case @path must be
+ * absolute.
+ * @path: The path of the state node to create.
+ * @value: The canonical value of the state.
+ *
+ * Return: The new libyang node.
+ */
+extern struct lyd_node *nb_op_update(struct lyd_node *tree, const char *path, const char *value);
+
+/**
+ * nb_op_update_delete() - Delete state data.
+ * @tree: subtree @path is relative to or NULL in which case @path must be
+ * absolute.
+ * @path: The path of the state node to delete, or NULL if @tree should just be
+ * deleted.
+ */
+extern void nb_op_update_delete(struct lyd_node *tree, const char *path);
+
+/**
+ * nb_op_update_pathf() - Create new state data.
+ * @tree: subtree @path_fmt is relative to or NULL in which case @path_fmt must
+ * be absolute.
+ * @path_fmt: The path format string of the state node to create.
+ * @value: The canonical value of the state.
+ * @...: The values to substitute into @path_fmt.
+ *
+ * Return: The new libyang node.
+ */
+extern struct lyd_node *nb_op_update_pathf(struct lyd_node *tree, const char *path_fmt,
+ const char *value, ...) PRINTFRR(2, 4);
+extern struct lyd_node *nb_op_update_vpathf(struct lyd_node *tree, const char *path_fmt,
+ const char *value, va_list ap);
+/**
+ * nb_op_update_delete_pathf() - Delete state data.
+ * @tree: subtree @path_fmt is relative to or NULL in which case @path_fmt must
+ * be absolute.
+ * @path: The path of the state node to delete.
+ * @...: The values to substitute into @path_fmt.
+ */
+extern void nb_op_update_delete_pathf(struct lyd_node *tree, const char *path_fmt, ...)
+ PRINTFRR(2, 3);
+extern void nb_op_update_delete_vpathf(struct lyd_node *tree, const char *path_fmt, va_list ap);
+
+/**
+ * nb_op_updatef() - Create new state data.
+ * @tree: subtree @path is relative to or NULL in which case @path must be
+ * absolute.
+ * @path: The path of the state node to create.
+ * @val_fmt: The value format string to set the canonical value of the state.
+ * @...: The values to substitute into @val_fmt.
+ *
+ * Return: The new libyang node.
+ */
+extern struct lyd_node *nb_op_updatef(struct lyd_node *tree, const char *path, const char *val_fmt,
+ ...) PRINTFRR(3, 4);
+
+extern struct lyd_node *nb_op_vupdatef(struct lyd_node *tree, const char *path, const char *val_fmt,
+ va_list ap);
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/northbound_notif.c b/lib/northbound_notif.c
new file mode 100644
index 000000000..42aaa092b
--- /dev/null
+++ b/lib/northbound_notif.c
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * December 1 2024, Christian Hopps <chopps@labn.net>
+ *
+ * Copyright (c) 2024, LabN Consulting, L.L.C.
+ *
+ */
+#include <zebra.h>
+#include "debug.h"
+#include "typesafe.h"
+#include "northbound.h"
+
+#define __dbg(fmt, ...) DEBUGD(&nb_dbg_notif, "NB_OP_CHANGE: %s: " fmt, __func__, ##__VA_ARGS__)
+#define __log_err(fmt, ...) zlog_err("NB_OP_CHANGE: %s: ERROR: " fmt, __func__, ##__VA_ARGS__)
+
+static void nb_notif_add(const char *path)
+{
+}
+
+
+static void nb_notif_delete(const char *path)
+{
+}
+
+struct lyd_node *nb_op_update(struct lyd_node *tree, const char *path, const char *value)
+{
+ struct lyd_node *dnode;
+ const char *abs_path = NULL;
+
+
+ __dbg("updating path: %s with value: %s", path, value);
+
+ dnode = yang_state_new(tree, path, value);
+
+ if (path[0] == '/')
+ abs_path = path;
+ else {
+ abs_path = lyd_path(dnode, LYD_PATH_STD, NULL, 0);
+ }
+
+ nb_notif_add(abs_path);
+
+ if (abs_path != path)
+ free((char *)abs_path);
+
+ return dnode;
+}
+
+void nb_op_update_delete(struct lyd_node *tree, const char *path)
+{
+ char *abs_path = NULL;
+
+ __dbg("deleting path: %s", path);
+
+ if (path && path[0] == '/')
+ abs_path = (char *)path;
+ else {
+ assert(tree);
+ abs_path = lyd_path(tree, LYD_PATH_STD, NULL, 0);
+ assert(abs_path);
+ if (path) {
+ char *tmp = darr_strdup(abs_path);
+ free(abs_path);
+ abs_path = tmp;
+ if (*darr_last(abs_path) != '/')
+ darr_in_strcat(abs_path, "/");
+ assert(abs_path); /* silence bad CLANG NULL warning */
+ darr_in_strcat(abs_path, path);
+ }
+ }
+
+ yang_state_delete(tree, path);
+
+ nb_notif_delete(abs_path);
+
+ if (abs_path != path) {
+ if (path)
+ darr_free(abs_path);
+ else
+ free(abs_path);
+ }
+}
+
+PRINTFRR(2, 0)
+struct lyd_node *nb_op_update_vpathf(struct lyd_node *tree, const char *path_fmt, const char *value,
+ va_list ap)
+{
+ struct lyd_node *dnode;
+ char *path;
+
+ path = darr_vsprintf(path_fmt, ap);
+ dnode = nb_op_update(tree, path, value);
+ darr_free(path);
+
+ return dnode;
+}
+
+struct lyd_node *nb_op_update_pathf(struct lyd_node *tree, const char *path_fmt, const char *value,
+ ...)
+{
+ struct lyd_node *dnode;
+ va_list ap;
+
+ va_start(ap, value);
+ dnode = nb_op_update_vpathf(tree, path_fmt, value, ap);
+ va_end(ap);
+
+ return dnode;
+}
+
+PRINTFRR(2, 0)
+void nb_op_update_delete_vpathf(struct lyd_node *tree, const char *path_fmt, va_list ap)
+{
+ char *path;
+
+ path = darr_vsprintf(path_fmt, ap);
+ nb_op_update_delete(tree, path);
+ darr_free(path);
+}
+
+void nb_op_update_delete_pathf(struct lyd_node *tree, const char *path_fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, path_fmt);
+ nb_op_update_delete_vpathf(tree, path_fmt, ap);
+ va_end(ap);
+}
+
+
+PRINTFRR(3, 0)
+struct lyd_node *nb_op_vupdatef(struct lyd_node *tree, const char *path, const char *val_fmt,
+ va_list ap)
+{
+ struct lyd_node *dnode;
+ char *value;
+
+ value = darr_vsprintf(val_fmt, ap);
+ dnode = nb_op_update(tree, path, value);
+ darr_free(value);
+
+ return dnode;
+}
+
+
+struct lyd_node *nb_op_updatef(struct lyd_node *tree, const char *path, const char *val_fmt, ...)
+{
+ struct lyd_node *dnode;
+ va_list ap;
+
+ va_start(ap, val_fmt);
+ dnode = nb_op_vupdatef(tree, path, val_fmt, ap);
+ va_end(ap);
+
+ return dnode;
+}
diff --git a/lib/northbound_oper.c b/lib/northbound_oper.c
index c80cdc116..dc4be2161 100644
--- a/lib/northbound_oper.c
+++ b/lib/northbound_oper.c
@@ -35,6 +35,7 @@
* We must also process containers with lookup-next descendants last.
*/
+DEFINE_MTYPE_STATIC(LIB, NB_STATE, "Northbound State");
DEFINE_MTYPE_STATIC(LIB, NB_YIELD_STATE, "NB Yield State");
DEFINE_MTYPE_STATIC(LIB, NB_NODE_INFOS, "NB Node Infos");
diff --git a/lib/subdir.am b/lib/subdir.am
index 4bcce9a2b..984e92ff1 100644
--- a/lib/subdir.am
+++ b/lib/subdir.am
@@ -84,6 +84,7 @@ lib_libfrr_la_SOURCES = \
lib/northbound.c \
lib/northbound_cli.c \
lib/northbound_db.c \
+ lib/northbound_notif.c \
lib/northbound_oper.c \
lib/ntop.c \
lib/openbsd-tree.c \
diff --git a/lib/yang.c b/lib/yang.c
index b847b8b77..2aa353925 100644
--- a/lib/yang.c
+++ b/lib/yang.c
@@ -14,6 +14,7 @@
#include <libyang/version.h>
#include "northbound.h"
#include "frrstr.h"
+#include "darr.h"
#include "lib/config_paths.h"
@@ -680,6 +681,116 @@ void yang_dnode_rpc_output_add(struct lyd_node *output, const char *xpath,
assert(err == LY_SUCCESS);
}
+struct lyd_node *yang_state_new(struct lyd_node *tree, const char *path, const char *value)
+{
+ struct lyd_node *dnode, *parent;
+ LY_ERR err;
+
+ err = lyd_new_path2(tree, ly_native_ctx, path, value, 0, 0, LYD_NEW_PATH_UPDATE, &parent,
+ &dnode);
+ assert(err == LY_SUCCESS);
+
+ /*
+ * If the node exists and isn't updated returned dnode will be NULL, so
+ * we need to find it. But even if returned it can be the first newly
+ * created node (could be container of path) not the actual path dnode.
+ * So we always find.
+ */
+ err = lyd_find_path(tree ?: parent, path, false, &dnode);
+ assert(err == LY_SUCCESS);
+
+ return dnode;
+}
+
+void yang_state_delete(struct lyd_node *tree, const char *path)
+{
+ LY_ERR err;
+
+ if (!tree)
+ return;
+
+ if (path) {
+ err = lyd_find_path(tree, path, false, &tree);
+ if (err != LY_SUCCESS) {
+ zlog_info("State %s has already been deleted", path);
+ return;
+ }
+ }
+ lyd_free_tree(tree);
+}
+
+PRINTFRR(2, 0)
+struct lyd_node *yang_state_new_vpathf(struct lyd_node *tree, const char *path_fmt,
+ const char *value, va_list ap)
+{
+ struct lyd_node *dnode;
+ char *path;
+
+ path = darr_vsprintf(path_fmt, ap);
+ dnode = yang_state_new(tree, path, value);
+ darr_free(path);
+
+ return dnode;
+}
+
+struct lyd_node *yang_state_new_pathf(struct lyd_node *tree, const char *path_fmt,
+ const char *value, ...)
+{
+ struct lyd_node *dnode;
+ va_list ap;
+
+ va_start(ap, value);
+ dnode = yang_state_new_vpathf(tree, path_fmt, value, ap);
+ va_end(ap);
+
+ return dnode;
+}
+
+PRINTFRR(2, 0)
+void yang_state_delete_vpathf(struct lyd_node *tree, const char *path_fmt, va_list ap)
+{
+ char *path;
+
+ path = darr_vsprintf(path_fmt, ap);
+ yang_state_delete(tree, path);
+ darr_free(path);
+}
+
+void yang_state_delete_pathf(struct lyd_node *tree, const char *path_fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, path_fmt);
+ yang_state_delete_vpathf(tree, path_fmt, ap);
+ va_end(ap);
+}
+
+PRINTFRR(3, 0)
+struct lyd_node *yang_state_vnewf(struct lyd_node *tree, const char *path, const char *val_fmt,
+ va_list ap)
+{
+ struct lyd_node *dnode;
+ char *value;
+
+ value = darr_vsprintf(val_fmt, ap);
+ dnode = yang_state_new(tree, path, value);
+ darr_free(value);
+
+ return dnode;
+}
+
+struct lyd_node *yang_state_newf(struct lyd_node *tree, const char *path, const char *val_fmt, ...)
+{
+ struct lyd_node *dnode;
+ va_list ap;
+
+ va_start(ap, val_fmt);
+ dnode = yang_state_vnewf(tree, path, val_fmt, ap);
+ va_end(ap);
+
+ return dnode;
+}
+
struct yang_data *yang_data_new(const char *xpath, const char *value)
{
struct yang_data *data;
diff --git a/lib/yang.h b/lib/yang.h
index 52857ecf0..eed2fa8db 100644
--- a/lib/yang.h
+++ b/lib/yang.h
@@ -535,6 +535,66 @@ extern struct lyd_node *yang_dnode_dup(const struct lyd_node *dnode);
*/
extern void yang_dnode_free(struct lyd_node *dnode);
+/**
+ * yang_state_new() - Create new state data.
+ * @tree: subtree @path is relative to or NULL in which case @path must be
+ * absolute.
+ * @path: The path of the state node to create.
+ * @value: The canonical value of the state.
+ *
+ * Return: The new libyang node.
+ */
+extern struct lyd_node *yang_state_new(struct lyd_node *tree, const char *path, const char *value);
+
+/**
+ * yang_state_delete() - Delete state data.
+ * @tree: subtree @path is relative to or NULL in which case @path must be
+ * absolute.
+ * @path: The path of the state node to delete, or NULL if @tree should just be
+ * deleted.
+ */
+extern void yang_state_delete(struct lyd_node *tree, const char *path);
+
+/**
+ * yang_state_new_pathf() - Create new state data.
+ * @tree: subtree @path_fmt is relative to or NULL in which case @path_fmt must
+ * be absolute.
+ * @path_fmt: The path format string of the state node to create.
+ * @value: The canonical value of the state.
+ * @...: The values to substitute into @path_fmt.
+ *
+ * Return: The new libyang node.
+ */
+extern struct lyd_node *yang_state_new_pathf(struct lyd_node *tree, const char *path_fmt,
+ const char *value, ...) PRINTFRR(2, 4);
+extern struct lyd_node *yang_state_new_vpathf(struct lyd_node *tree, const char *path_fmt,
+ const char *value, va_list ap);
+/**
+ * yang_state_delete_pathf() - Delete state data.
+ * @tree: subtree @path_fmt is relative to or NULL in which case @path_fmt must
+ * be absolute.
+ * @path: The path of the state node to delete.
+ * @...: The values to substitute into @path_fmt.
+ */
+extern void yang_state_delete_pathf(struct lyd_node *tree, const char *path_fmt, ...) PRINTFRR(2, 3);
+extern void yang_state_delete_vpathf(struct lyd_node *tree, const char *path_fmt, va_list ap);
+
+/**
+ * yang_state_newf() - Create new state data.
+ * @tree: subtree @path is relative to or NULL in which case @path must be
+ * absolute.
+ * @path: The path of the state node to create.
+ * @val_fmt: The value format string to set the canonical value of the state.
+ * @...: The values to substitute into @val_fmt.
+ *
+ * Return: The new libyang node.
+ */
+extern struct lyd_node *yang_state_newf(struct lyd_node *tree, const char *path,
+ const char *val_fmt, ...) PRINTFRR(3, 4);
+
+extern struct lyd_node *yang_state_vnewf(struct lyd_node *tree, const char *path,
+ const char *val_fmt, va_list ap);
+
/*
* Add a libyang data node to an RPC/action output container.
*