diff options
author | Christian Hopps <chopps@labn.net> | 2025-01-07 12:46:23 +0100 |
---|---|---|
committer | Christian Hopps <chopps@labn.net> | 2025-01-14 05:40:52 +0100 |
commit | 8a52b69ca4c1fb34d46f04cc348faba191ad6ae3 (patch) | |
tree | e9ea5b318d00855394d912caa882e3779c680455 | |
parent | Merge pull request #17843 from LabNConsulting/chopps/new-docker-and-doc (diff) | |
download | frr-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.h | 62 | ||||
-rw-r--r-- | lib/northbound_notif.c | 156 | ||||
-rw-r--r-- | lib/northbound_oper.c | 1 | ||||
-rw-r--r-- | lib/subdir.am | 1 | ||||
-rw-r--r-- | lib/yang.c | 111 | ||||
-rw-r--r-- | lib/yang.h | 60 |
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. * |