Skip to content

Commit

Permalink
lib: rework northbound RPC callback
Browse files Browse the repository at this point in the history
Change input/output arguments of the RPC callback from lists of
(xpath/value) tuples to YANG data trees.

Signed-off-by: Igor Ryzhov <[email protected]>
  • Loading branch information
idryzhov committed Apr 22, 2024
1 parent 7f7bcb1 commit 58a8ebc
Show file tree
Hide file tree
Showing 13 changed files with 177 additions and 222 deletions.
7 changes: 2 additions & 5 deletions lib/northbound.c
Original file line number Diff line number Diff line change
Expand Up @@ -1820,14 +1820,11 @@ const void *nb_callback_lookup_next(const struct nb_node *nb_node,
}

int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath,
const struct list *input, struct list *output, char *errmsg,
size_t errmsg_len)
const struct lyd_node *input, struct lyd_node *output,
char *errmsg, size_t errmsg_len)
{
struct nb_cb_rpc_args args = {};

if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS))
return 0;

DEBUGD(&nb_dbg_cbs_rpc, "northbound RPC: %s", xpath);

args.xpath = xpath;
Expand Down
10 changes: 5 additions & 5 deletions lib/northbound.h
Original file line number Diff line number Diff line change
Expand Up @@ -274,11 +274,11 @@ struct nb_cb_rpc_args {
/* XPath of the YANG RPC or action. */
const char *xpath;

/* Read-only list of input parameters. */
const struct list *input;
/* Read-only "input" tree of the RPC/action. */
const struct lyd_node *input;

/* List of output parameters to be populated by the callback. */
struct list *output;
/* The "output" tree of the RPC/action to be populated by the callback. */
struct lyd_node *output;

/* Buffer to store human-readable error message in case of error. */
char *errmsg;
Expand Down Expand Up @@ -833,7 +833,7 @@ extern const void *nb_callback_lookup_next(const struct nb_node *nb_node,
const void *parent_list_entry,
const struct yang_list_keys *keys);
extern int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath,
const struct list *input, struct list *output,
const struct lyd_node *input, struct lyd_node *output,
char *errmsg, size_t errmsg_len);
extern void nb_callback_notify(const struct nb_node *nb_node, const char *xpath,
struct lyd_node *dnode);
Expand Down
59 changes: 57 additions & 2 deletions lib/northbound_cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -275,10 +275,31 @@ int nb_cli_apply_changes_clear_pending(struct vty *vty,
return nb_cli_apply_changes_internal(vty, xpath_base_abs, true);
}

int nb_cli_rpc(struct vty *vty, const char *xpath, struct list *input,
struct list *output)
int nb_cli_rpc_enqueue(struct vty *vty, const char *xpath, const char *value)
{
struct nb_cfg_change *param;

if (vty->num_rpc_params == VTY_MAXCFGCHANGES) {
/* Not expected to happen. */
vty_out(vty,
"%% Exceeded the maximum number of params (%u) for a single command\n\n",
VTY_MAXCFGCHANGES);
return CMD_WARNING;
}

param = &vty->rpc_params[vty->num_rpc_params++];
strlcpy(param->xpath, xpath, sizeof(param->xpath));
param->value = value;

return CMD_SUCCESS;
}

int nb_cli_rpc(struct vty *vty, const char *xpath, struct lyd_node **output_p)
{
struct nb_node *nb_node;
struct lyd_node *input = NULL;
struct lyd_node *output = NULL;
LY_ERR err;
int ret;
char errmsg[BUFSIZ] = {0};

Expand All @@ -289,12 +310,46 @@ int nb_cli_rpc(struct vty *vty, const char *xpath, struct list *input,
return CMD_WARNING;
}

/* create input tree */
err = lyd_new_path2(NULL, ly_native_ctx, xpath, NULL, 0, 0, 0, NULL,
&input);
assert(err == LY_SUCCESS);

for (size_t i = 0; i < vty->num_rpc_params; i++) {
err = lyd_new_path(input, ly_native_ctx,
vty->rpc_params[i].xpath,
vty->rpc_params[i].value, 0, NULL);
assert(err == LY_SUCCESS);
}

/* validate input tree to create implicit defaults */
err = lyd_validate_op(input, NULL, LYD_TYPE_RPC_YANG, NULL);
assert(err == LY_SUCCESS);

/* create output tree root for population in the callback */
err = lyd_new_path2(NULL, ly_native_ctx, xpath, NULL, 0, 0, 0, NULL,
&output);
assert(err == LY_SUCCESS);

ret = nb_callback_rpc(nb_node, xpath, input, output, errmsg,
sizeof(errmsg));

/* validate output tree to create implicit defaults */
err = lyd_validate_op(output, NULL, LYD_TYPE_REPLY_YANG, NULL);
assert(err == LY_SUCCESS);

lyd_free_all(input);
vty->num_rpc_params = 0;

switch (ret) {
case NB_OK:
if (output_p)
*output_p = output;
else
lyd_free_all(output);
return CMD_SUCCESS;
default:
lyd_free_all(output);
if (strlen(errmsg))
vty_show_nb_errors(vty, ret, errmsg);
return CMD_WARNING;
Expand Down
34 changes: 23 additions & 11 deletions lib/northbound_cli.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,28 +80,40 @@ extern int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt,
...) PRINTFRR(2, 3);

/*
* Execute a YANG RPC or Action.
* Add an input child node for an RPC or an action.
*
* vty
* The vty context.
*
* xpath
* XPath of the child being added, relative to the input container.
*
* value
* Value of the child being added. Can be NULL for containers and leafs of
* type 'empty'.
*/
extern int nb_cli_rpc_enqueue(struct vty *vty, const char *xpath,
const char *value);

/*
* Execute a YANG RPC or Action using the enqueued input parameters.
*
* vty
* The vty terminal to dump any error.
*
* xpath
* XPath of the YANG RPC or Action node.
*
* input
* List of 'yang_data' structures containing the RPC input parameters. It
* can be set to NULL when there are no input parameters.
*
* output
* List of 'yang_data' structures used to retrieve the RPC output parameters.
* It can be set to NULL when it's known that the given YANG RPC or Action
* doesn't have any output parameters.
* output_p
* A pointer to the libyang data node that will hold the output data tree.
* It can be set to NULL if the caller is not interested in processing the
* output. The caller is responsible for freeing the output data tree.
*
* Returns:
* CMD_SUCCESS on success, CMD_WARNING otherwise.
*/
extern int nb_cli_rpc(struct vty *vty, const char *xpath, struct list *input,
struct list *output);
extern int nb_cli_rpc(struct vty *vty, const char *xpath,
struct lyd_node **output_p);

/*
* Show CLI commands associated to the given YANG data node.
Expand Down
61 changes: 43 additions & 18 deletions lib/northbound_grpc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1011,12 +1011,11 @@ grpc::Status HandleUnaryExecute(
grpc_debug("%s: entered", __func__);

struct nb_node *nb_node;
struct list *input_list;
struct list *output_list;
struct listnode *node;
struct yang_data *data;
struct lyd_node *input_tree, *output_tree, *child;
const char *xpath;
char errmsg[BUFSIZ] = {0};
char path[XPATH_MAXLEN];
LY_ERR err;

// Request: string path = 1;
xpath = tag->request.path().c_str();
Expand All @@ -1032,40 +1031,66 @@ grpc::Status HandleUnaryExecute(
return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
"Unknown data path");

input_list = yang_data_list_new();
output_list = yang_data_list_new();
// Create input data tree.
err = lyd_new_path2(NULL, ly_native_ctx, xpath, NULL, 0,
(LYD_ANYDATA_VALUETYPE)0, 0, NULL, &input_tree);
if (err != LY_SUCCESS) {
return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
"Invalid data path");
}

// Read input parameters.
auto input = tag->request.input();
for (const frr::PathValue &pv : input) {
// Request: repeated PathValue input = 2;
data = yang_data_new(pv.path().c_str(), pv.value().c_str());
listnode_add(input_list, data);
err = lyd_new_path(input_tree, ly_native_ctx, pv.path().c_str(),
pv.value().c_str(), 0, NULL);
if (err != LY_SUCCESS) {
lyd_free_tree(input_tree);
return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
"Invalid input data");
}
}

// Validate input data.
err = lyd_validate_op(input_tree, NULL, LYD_TYPE_RPC_YANG, NULL);
if (err != LY_SUCCESS) {
lyd_free_tree(input_tree);
return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
"Invalid input data");
}

// Create output data tree.
err = lyd_new_path2(NULL, ly_native_ctx, xpath, NULL, 0,
(LYD_ANYDATA_VALUETYPE)0, 0, NULL, &output_tree);
if (err != LY_SUCCESS) {
lyd_free_tree(input_tree);
return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
"Invalid data path");
}

// Execute callback registered for this XPath.
if (nb_callback_rpc(nb_node, xpath, input_list, output_list, errmsg,
sizeof(errmsg))
!= NB_OK) {
if (nb_callback_rpc(nb_node, xpath, input_tree, output_tree, errmsg,
sizeof(errmsg)) != NB_OK) {
flog_warn(EC_LIB_NB_CB_RPC, "%s: rpc callback failed: %s",
__func__, xpath);
list_delete(&input_list);
list_delete(&output_list);
lyd_free_tree(input_tree);
lyd_free_tree(output_tree);

return grpc::Status(grpc::StatusCode::INTERNAL, "RPC failed");
}

// Process output parameters.
for (ALL_LIST_ELEMENTS_RO(output_list, node, data)) {
LY_LIST_FOR (lyd_child(output_tree), child) {
// Response: repeated PathValue output = 1;
frr::PathValue *pv = tag->response.add_output();
pv->set_path(data->xpath);
pv->set_value(data->value);
pv->set_path(lyd_path(child, LYD_PATH_STD, path, sizeof(path)));
pv->set_value(yang_dnode_get_string(child, NULL));
}

// Release memory.
list_delete(&input_list);
list_delete(&output_list);
lyd_free_tree(input_tree);
lyd_free_tree(output_tree);

return grpc::Status::OK;
}
Expand Down
65 changes: 6 additions & 59 deletions lib/northbound_sysrepo.c
Original file line number Diff line number Diff line change
Expand Up @@ -377,16 +377,11 @@ static int frr_sr_state_cb(sr_session_ctx_t *session, uint32_t sub_id,
return SR_ERR_OK;
}
static int frr_sr_config_rpc_cb(sr_session_ctx_t *session, uint32_t sub_id,
const char *xpath, const sr_val_t *sr_input,
const size_t input_cnt, sr_event_t sr_ev,
uint32_t request_id, sr_val_t **sr_output,
size_t *sr_output_cnt, void *private_ctx)
const char *xpath, const struct lyd_node *input,
sr_event_t sr_ev, uint32_t request_id,
struct lyd_node *output, void *private_ctx)
{
struct nb_node *nb_node;
struct list *input;
struct list *output;
struct yang_data *data;
size_t cb_output_cnt;
int ret = SR_ERR_OK;
char errmsg[BUFSIZ] = {0};

Expand All @@ -397,64 +392,15 @@ static int frr_sr_config_rpc_cb(sr_session_ctx_t *session, uint32_t sub_id,
return SR_ERR_INTERNAL;
}

input = yang_data_list_new();
output = yang_data_list_new();

/* Process input. */
for (size_t i = 0; i < input_cnt; i++) {
char value_str[YANG_VALUE_MAXLEN];

sr_val_to_buff(&sr_input[i], value_str, sizeof(value_str));

data = yang_data_new(xpath, value_str);
listnode_add(input, data);
}

/* Execute callback registered for this XPath. */
if (nb_callback_rpc(nb_node, xpath, input, output, errmsg,
sizeof(errmsg))
!= NB_OK) {
flog_warn(EC_LIB_NB_CB_RPC, "%s: rpc callback failed: %s",
__func__, xpath);
ret = SR_ERR_OPERATION_FAILED;
goto exit;
}

/* Process output. */
if (listcount(output) > 0) {
sr_val_t *values = NULL;
struct listnode *node;
int i = 0;

cb_output_cnt = listcount(output);
ret = sr_new_values(cb_output_cnt, &values);
if (ret != SR_ERR_OK) {
flog_err(EC_LIB_LIBSYSREPO, "%s: sr_new_values(): %s",
__func__, sr_strerror(ret));
goto exit;
}

for (ALL_LIST_ELEMENTS_RO(output, node, data)) {
if (yang_data_frr2sr(data, &values[i++]) != 0) {
flog_err(
EC_LIB_SYSREPO_DATA_CONVERT,
"%s: failed to convert data to Sysrepo format",
__func__);
ret = SR_ERR_INTERNAL;
sr_free_values(values, cb_output_cnt);
goto exit;
}
}

*sr_output = values;
*sr_output_cnt = cb_output_cnt;
}

exit:
/* Release memory. */
list_delete(&input);
list_delete(&output);

return ret;
}

Expand Down Expand Up @@ -579,8 +525,9 @@ static int frr_sr_subscribe_rpc(const struct lysc_node *snode, void *arg)
DEBUGD(&nb_dbg_client_sysrepo, "sysrepo: providing RPC to '%s'",
nb_node->xpath);

ret = sr_rpc_subscribe(session, nb_node->xpath, frr_sr_config_rpc_cb,
NULL, 0, 0, &module->sr_subscription);
ret = sr_rpc_subscribe_tree(session, nb_node->xpath,
frr_sr_config_rpc_cb, NULL, 0, 0,
&module->sr_subscription);
if (ret != SR_ERR_OK)
flog_err(EC_LIB_LIBSYSREPO, "sr_rpc_subscribe(): %s",
sr_strerror(ret));
Expand Down
4 changes: 4 additions & 0 deletions lib/vty.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ struct vty {
size_t num_cfg_changes;
struct nb_cfg_change cfg_changes[VTY_MAXCFGCHANGES];

/* Input parameters */
size_t num_rpc_params;
struct nb_cfg_change rpc_params[VTY_MAXCFGCHANGES];

/* XPath of the current node */
int xpath_index;
char xpath[VTY_MAXDEPTH][XPATH_MAXLEN];
Expand Down
9 changes: 4 additions & 5 deletions ripd/rip_nb_rpcs.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,11 @@ static void clear_rip_route(struct rip *rip)
int clear_rip_route_rpc(struct nb_cb_rpc_args *args)
{
struct rip *rip;
struct yang_data *yang_vrf;

yang_vrf = yang_data_list_find(args->input, "%s/%s", args->xpath,
"input/vrf");
if (yang_vrf) {
rip = rip_lookup_by_vrf_name(yang_vrf->value);
if (args->input && yang_dnode_exists(args->input, "vrf")) {
const char *name = yang_dnode_get_string(args->input, "vrf");

rip = rip_lookup_by_vrf_name(name);
if (rip)
clear_rip_route(rip);
} else {
Expand Down
Loading

0 comments on commit 58a8ebc

Please sign in to comment.