lib: northbound: add basic oper-state update functions

Signed-off-by: Christian Hopps <chopps@labn.net>
This commit is contained in:
Christian Hopps 2025-01-07 06:46:23 -05:00
parent bbebaa5d6b
commit 8a52b69ca4
6 changed files with 391 additions and 0 deletions

View File

@ -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

156
lib/northbound_notif.c Normal file
View File

@ -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;
}

View File

@ -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");

View File

@ -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 \

View File

@ -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;

View File

@ -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.
*