Merge pull request #15594 from idryzhov/mgmt-rpc

mgmtd: implement YANG RPC/action support
This commit is contained in:
Christian Hopps 2024-05-06 16:05:50 -04:00 committed by GitHub
commit c54bc7a8dd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
43 changed files with 1403 additions and 299 deletions

View file

@ -79,6 +79,7 @@ message BeSubscribeReq {
repeated string config_xpaths = 2; repeated string config_xpaths = 2;
repeated string oper_xpaths = 3; repeated string oper_xpaths = 3;
repeated string notif_xpaths = 4; repeated string notif_xpaths = 4;
repeated string rpc_xpaths = 5;
} }
message BeSubscribeReply { message BeSubscribeReply {

View file

@ -915,6 +915,143 @@ static void be_client_handle_get_tree(struct mgmt_be_client *client,
be_client_send_tree_data_batch, args); be_client_send_tree_data_batch, args);
} }
static void be_client_send_rpc_reply(struct mgmt_be_client *client,
uint64_t txn_id, uint64_t req_id,
uint8_t result_type,
struct lyd_node *output)
{
struct mgmt_msg_rpc_reply *rpc_reply_msg;
uint8_t **darrp;
LY_ERR err;
int ret = NB_OK;
rpc_reply_msg = mgmt_msg_native_alloc_msg(struct mgmt_msg_rpc_reply, 0,
MTYPE_MSG_NATIVE_RPC_REPLY);
rpc_reply_msg->refer_id = txn_id;
rpc_reply_msg->req_id = req_id;
rpc_reply_msg->code = MGMT_MSG_CODE_RPC_REPLY;
rpc_reply_msg->result_type = result_type;
if (output) {
darrp = mgmt_msg_native_get_darrp(rpc_reply_msg);
err = yang_print_tree_append(darrp, output, result_type,
LYD_PRINT_SHRINK);
lyd_free_all(output);
if (err) {
ret = NB_ERR;
goto done;
}
}
(void)be_client_send_native_msg(client, rpc_reply_msg,
mgmt_msg_native_get_msg_len(
rpc_reply_msg),
false);
done:
mgmt_msg_native_free_msg(rpc_reply_msg);
if (ret != NB_OK)
be_client_send_error(client, txn_id, req_id, false, -EINVAL,
"Can't format RPC reply");
}
/*
* Process the RPC request.
*/
static void be_client_handle_rpc(struct mgmt_be_client *client, uint64_t txn_id,
void *msgbuf, size_t msg_len)
{
struct mgmt_msg_rpc *rpc_msg = msgbuf;
struct nb_node *nb_node;
struct lyd_node *input, *output;
const char *xpath;
const char *data;
char errmsg[BUFSIZ] = { 0 };
LY_ERR err;
int ret;
debug_be_client("Received RPC request for client %s txn-id %" PRIu64
" req-id %" PRIu64,
client->name, txn_id, rpc_msg->req_id);
xpath = mgmt_msg_native_xpath_data_decode(rpc_msg, msg_len, data);
if (!xpath) {
be_client_send_error(client, txn_id, rpc_msg->req_id, false,
-EINVAL, "Corrupt RPC message");
return;
}
nb_node = nb_node_find(xpath);
if (!nb_node) {
be_client_send_error(client, txn_id, rpc_msg->req_id, false,
-EINVAL, "No schema found for RPC: %s",
xpath);
return;
}
if (!nb_node->cbs.rpc) {
be_client_send_error(client, txn_id, rpc_msg->req_id, false,
-EINVAL, "No RPC callback for: %s", xpath);
return;
}
if (data) {
err = yang_parse_rpc(xpath, rpc_msg->request_type, data, false,
&input);
if (err) {
be_client_send_error(client, txn_id, rpc_msg->req_id,
false, -EINVAL,
"Can't parse RPC data for: %s",
xpath);
return;
}
} else {
/*
* If there's no input data, create an empty input container.
* It is especially needed for actions, because their parents
* may hold necessary information.
*/
err = lyd_new_path2(NULL, ly_native_ctx, xpath, NULL, 0, 0, 0,
NULL, &input);
if (err) {
be_client_send_error(client, txn_id, rpc_msg->req_id,
false, -EINVAL,
"Can't create input node for RPC: %s",
xpath);
return;
}
}
err = lyd_new_path2(NULL, ly_native_ctx, xpath, NULL, 0, 0, 0, NULL,
&output);
if (err) {
lyd_free_all(input);
be_client_send_error(client, txn_id, rpc_msg->req_id, false,
-EINVAL,
"Can't create output node for RPC: %s",
xpath);
return;
}
ret = nb_callback_rpc(nb_node, xpath, input, output, errmsg,
sizeof(errmsg));
if (ret != NB_OK) {
lyd_free_all(input);
lyd_free_all(output);
be_client_send_error(client, txn_id, rpc_msg->req_id, false,
-EINVAL, "%s", errmsg);
return;
}
lyd_free_all(input);
if (!lyd_child(output)) {
lyd_free_all(output);
output = NULL;
}
be_client_send_rpc_reply(client, txn_id, rpc_msg->req_id,
rpc_msg->request_type, output);
}
/* /*
* Process the notification. * Process the notification.
*/ */
@ -975,6 +1112,9 @@ static void be_client_handle_native_msg(struct mgmt_be_client *client,
case MGMT_MSG_CODE_GET_TREE: case MGMT_MSG_CODE_GET_TREE:
be_client_handle_get_tree(client, txn_id, msg, msg_len); be_client_handle_get_tree(client, txn_id, msg, msg_len);
break; break;
case MGMT_MSG_CODE_RPC:
be_client_handle_rpc(client, txn_id, msg, msg_len);
break;
case MGMT_MSG_CODE_NOTIFY: case MGMT_MSG_CODE_NOTIFY:
be_client_handle_notify(client, msg, msg_len); be_client_handle_notify(client, msg, msg_len);
break; break;
@ -1040,6 +1180,9 @@ int mgmt_be_send_subscr_req(struct mgmt_be_client *client_ctx,
subscr_req.n_notif_xpaths = client_ctx->cbs.nnotif_xpaths; subscr_req.n_notif_xpaths = client_ctx->cbs.nnotif_xpaths;
subscr_req.notif_xpaths = (char **)client_ctx->cbs.notif_xpaths; subscr_req.notif_xpaths = (char **)client_ctx->cbs.notif_xpaths;
subscr_req.n_rpc_xpaths = client_ctx->cbs.nrpc_xpaths;
subscr_req.rpc_xpaths = (char **)client_ctx->cbs.rpc_xpaths;
mgmtd__be_message__init(&be_msg); mgmtd__be_message__init(&be_msg);
be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REQ; be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REQ;
be_msg.subscr_req = &subscr_req; be_msg.subscr_req = &subscr_req;

View file

@ -75,6 +75,8 @@ struct mgmt_be_client_cbs {
const char **notif_xpaths; const char **notif_xpaths;
uint nnotif_xpaths; uint nnotif_xpaths;
const char **rpc_xpaths;
uint nrpc_xpaths;
}; };
/*************************************************************** /***************************************************************

View file

@ -360,6 +360,33 @@ int mgmt_fe_send_edit_req(struct mgmt_fe_client *client, uint64_t session_id,
return ret; return ret;
} }
int mgmt_fe_send_rpc_req(struct mgmt_fe_client *client, uint64_t session_id,
uint64_t req_id, LYD_FORMAT request_type,
const char *xpath, const char *data)
{
struct mgmt_msg_rpc *msg;
int ret;
msg = mgmt_msg_native_alloc_msg(struct mgmt_msg_rpc, 0,
MTYPE_MSG_NATIVE_RPC);
msg->refer_id = session_id;
msg->req_id = req_id;
msg->code = MGMT_MSG_CODE_RPC;
msg->request_type = request_type;
mgmt_msg_native_xpath_encode(msg, xpath);
if (data)
mgmt_msg_native_append(msg, data, strlen(data) + 1);
debug_fe_client("Sending RPC_REQ session-id %" PRIu64 " req-id %" PRIu64
" xpath: %s",
session_id, req_id, xpath);
ret = mgmt_msg_native_send_msg(&client->client.conn, msg, false);
mgmt_msg_native_free_msg(msg);
return ret;
}
static int mgmt_fe_client_handle_msg(struct mgmt_fe_client *client, static int mgmt_fe_client_handle_msg(struct mgmt_fe_client *client,
Mgmtd__FeMessage *fe_msg) Mgmtd__FeMessage *fe_msg)
{ {
@ -534,6 +561,7 @@ static void fe_client_handle_native_msg(struct mgmt_fe_client *client,
struct mgmt_msg_notify_data *notify_msg; struct mgmt_msg_notify_data *notify_msg;
struct mgmt_msg_tree_data *tree_msg; struct mgmt_msg_tree_data *tree_msg;
struct mgmt_msg_edit_reply *edit_msg; struct mgmt_msg_edit_reply *edit_msg;
struct mgmt_msg_rpc_reply *rpc_msg;
struct mgmt_msg_error *err_msg; struct mgmt_msg_error *err_msg;
const char *xpath = NULL; const char *xpath = NULL;
const char *data = NULL; const char *data = NULL;
@ -608,6 +636,23 @@ static void fe_client_handle_native_msg(struct mgmt_fe_client *client,
session->user_ctx, msg->req_id, session->user_ctx, msg->req_id,
xpath); xpath);
break; break;
case MGMT_MSG_CODE_RPC_REPLY:
if (!session->client->cbs.rpc_notify)
return;
rpc_msg = (typeof(rpc_msg))msg;
if (msg_len < sizeof(*rpc_msg)) {
log_err_fe_client("Corrupt rpc-reply msg recv");
return;
}
dlen = msg_len - sizeof(*rpc_msg);
session->client->cbs.rpc_notify(client, client->user_data,
session->client_id,
msg->refer_id,
session->user_ctx, msg->req_id,
dlen ? rpc_msg->data : NULL);
break;
case MGMT_MSG_CODE_NOTIFY: case MGMT_MSG_CODE_NOTIFY:
if (!session->client->cbs.async_notification) if (!session->client->cbs.async_notification)
return; return;

View file

@ -120,6 +120,12 @@ struct mgmt_fe_client_cbs {
uintptr_t session_ctx, uint64_t req_id, uintptr_t session_ctx, uint64_t req_id,
const char *xpath); const char *xpath);
/* Called when RPC result is returned */
int (*rpc_notify)(struct mgmt_fe_client *client, uintptr_t user_data,
uint64_t client_id, uint64_t session_id,
uintptr_t session_ctx, uint64_t req_id,
const char *result);
/* Called with asynchronous notifications from backends */ /* Called with asynchronous notifications from backends */
int (*async_notification)(struct mgmt_fe_client *client, int (*async_notification)(struct mgmt_fe_client *client,
uintptr_t user_data, uint64_t client_id, uintptr_t user_data, uint64_t client_id,
@ -454,6 +460,35 @@ extern int mgmt_fe_send_edit_req(struct mgmt_fe_client *client,
uint8_t flags, uint8_t operation, uint8_t flags, uint8_t operation,
const char *xpath, const char *data); const char *xpath, const char *data);
/*
* Send RPC request to MGMTD daemon.
*
* client
* Client object.
*
* session_id
* Client session ID.
*
* req_id
* Client request ID.
*
* result_type
* The LYD_FORMAT of the result.
*
* xpath
* the xpath of the RPC.
*
* data
* the data tree.
*
* Returns:
* 0 on success, otherwise msg_conn_send_msg() return values.
*/
extern int mgmt_fe_send_rpc_req(struct mgmt_fe_client *client,
uint64_t session_id, uint64_t req_id,
LYD_FORMAT request_type, const char *xpath,
const char *data);
/* /*
* Destroy library and cleanup everything. * Destroy library and cleanup everything.
*/ */

View file

@ -16,6 +16,8 @@ DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_GET_DATA, "native get data msg");
DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_NOTIFY, "native get data msg"); DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_NOTIFY, "native get data msg");
DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_EDIT, "native edit msg"); DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_EDIT, "native edit msg");
DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_EDIT_REPLY, "native edit reply msg"); DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_EDIT_REPLY, "native edit reply msg");
DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_RPC, "native RPC msg");
DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_RPC_REPLY, "native RPC reply msg");
int vmgmt_msg_native_send_error(struct msg_conn *conn, uint64_t sess_or_txn_id, int vmgmt_msg_native_send_error(struct msg_conn *conn, uint64_t sess_or_txn_id,
uint64_t req_id, bool short_circuit_ok, uint64_t req_id, bool short_circuit_ok,

View file

@ -152,6 +152,8 @@ DECLARE_MTYPE(MSG_NATIVE_GET_DATA);
DECLARE_MTYPE(MSG_NATIVE_NOTIFY); DECLARE_MTYPE(MSG_NATIVE_NOTIFY);
DECLARE_MTYPE(MSG_NATIVE_EDIT); DECLARE_MTYPE(MSG_NATIVE_EDIT);
DECLARE_MTYPE(MSG_NATIVE_EDIT_REPLY); DECLARE_MTYPE(MSG_NATIVE_EDIT_REPLY);
DECLARE_MTYPE(MSG_NATIVE_RPC);
DECLARE_MTYPE(MSG_NATIVE_RPC_REPLY);
/* /*
* Native message codes * Native message codes
@ -163,6 +165,8 @@ DECLARE_MTYPE(MSG_NATIVE_EDIT_REPLY);
#define MGMT_MSG_CODE_NOTIFY 4 #define MGMT_MSG_CODE_NOTIFY 4
#define MGMT_MSG_CODE_EDIT 5 #define MGMT_MSG_CODE_EDIT 5
#define MGMT_MSG_CODE_EDIT_REPLY 6 #define MGMT_MSG_CODE_EDIT_REPLY 6
#define MGMT_MSG_CODE_RPC 7
#define MGMT_MSG_CODE_RPC_REPLY 8
/* /*
* Datastores * Datastores
@ -377,6 +381,42 @@ _Static_assert(sizeof(struct mgmt_msg_edit_reply) ==
offsetof(struct mgmt_msg_edit_reply, data), offsetof(struct mgmt_msg_edit_reply, data),
"Size mismatch"); "Size mismatch");
/**
* struct mgmt_msg_rpc - RPC/action request.
*
* @request_type: ``LYD_FORMAT`` for the @data.
* @data: the xpath followed by the tree data for the operation.
*/
struct mgmt_msg_rpc {
struct mgmt_msg_header;
uint8_t request_type;
uint8_t resv2[7];
alignas(8) char data[];
};
_Static_assert(sizeof(struct mgmt_msg_rpc) ==
offsetof(struct mgmt_msg_rpc, data),
"Size mismatch");
/**
* struct mgmt_msg_rpc_reply - RPC/action reply.
*
* @result_type: ``LYD_FORMAT`` for the @data.
* @data: the tree data for the reply.
*/
struct mgmt_msg_rpc_reply {
struct mgmt_msg_header;
uint8_t result_type;
uint8_t resv2[7];
alignas(8) char data[];
};
_Static_assert(sizeof(struct mgmt_msg_rpc_reply) ==
offsetof(struct mgmt_msg_rpc_reply, data),
"Size mismatch");
/* /*
* Validate that the message ends in a NUL terminating byte * Validate that the message ends in a NUL terminating byte
*/ */
@ -569,7 +609,10 @@ extern int vmgmt_msg_native_send_error(struct msg_conn *conn,
const char *__s = NULL; \ const char *__s = NULL; \
if (msg->vsplit && msg->vsplit <= __len && \ if (msg->vsplit && msg->vsplit <= __len && \
msg->data[msg->vsplit - 1] == 0) { \ msg->data[msg->vsplit - 1] == 0) { \
(__data) = msg->data + msg->vsplit; \ if (msg->vsplit < __len) \
(__data) = msg->data + msg->vsplit; \
else \
(__data) = NULL; \
__s = msg->data; \ __s = msg->data; \
} \ } \
__s; \ __s; \

View file

@ -932,14 +932,22 @@ static int nb_candidate_edit_tree_add(struct nb_config *candidate,
/* if replace failed, restore the original node */ /* if replace failed, restore the original node */
if (existing) { if (existing) {
if (root) { if (root) {
/* Restoring the whole config. */
candidate->dnode = existing; candidate->dnode = existing;
} else if (ex_parent) {
/*
* Restoring a nested node. Insert it as a
* child.
*/
lyd_insert_child(ex_parent, existing);
} else { } else {
if (ex_parent) /*
lyd_insert_child(ex_parent, existing); * Restoring a top-level node. Insert it as a
else * sibling to candidate->dnode to make sure
lyd_insert_sibling(candidate->dnode, * the linkage is correct.
existing, */
&candidate->dnode); lyd_insert_sibling(candidate->dnode, existing,
&candidate->dnode);
} }
} }
yang_print_errors(ly_native_ctx, errmsg, errmsg_len); yang_print_errors(ly_native_ctx, errmsg, errmsg_len);
@ -1820,14 +1828,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, int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath,
const struct list *input, struct list *output, char *errmsg, const struct lyd_node *input, struct lyd_node *output,
size_t errmsg_len) char *errmsg, size_t errmsg_len)
{ {
struct nb_cb_rpc_args args = {}; 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); DEBUGD(&nb_dbg_cbs_rpc, "northbound RPC: %s", xpath);
args.xpath = xpath; args.xpath = xpath;

View file

@ -274,11 +274,11 @@ struct nb_cb_rpc_args {
/* XPath of the YANG RPC or action. */ /* XPath of the YANG RPC or action. */
const char *xpath; const char *xpath;
/* Read-only list of input parameters. */ /* Read-only "input" tree of the RPC/action. */
const struct list *input; const struct lyd_node *input;
/* List of output parameters to be populated by the callback. */ /* The "output" tree of the RPC/action to be populated by the callback. */
struct list *output; struct lyd_node *output;
/* Buffer to store human-readable error message in case of error. */ /* Buffer to store human-readable error message in case of error. */
char *errmsg; char *errmsg;
@ -833,7 +833,7 @@ extern const void *nb_callback_lookup_next(const struct nb_node *nb_node,
const void *parent_list_entry, const void *parent_list_entry,
const struct yang_list_keys *keys); const struct yang_list_keys *keys);
extern int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath, 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); char *errmsg, size_t errmsg_len);
extern void nb_callback_notify(const struct nb_node *nb_node, const char *xpath, extern void nb_callback_notify(const struct nb_node *nb_node, const char *xpath,
struct lyd_node *dnode); struct lyd_node *dnode);

View file

@ -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); return nb_cli_apply_changes_internal(vty, xpath_base_abs, true);
} }
int nb_cli_rpc(struct vty *vty, const char *xpath, struct list *input, int nb_cli_rpc_enqueue(struct vty *vty, const char *xpath, const char *value)
struct list *output) {
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 nb_node *nb_node;
struct lyd_node *input = NULL;
struct lyd_node *output = NULL;
LY_ERR err;
int ret; int ret;
char errmsg[BUFSIZ] = {0}; char errmsg[BUFSIZ] = {0};
@ -289,12 +310,62 @@ int nb_cli_rpc(struct vty *vty, const char *xpath, struct list *input,
return CMD_WARNING; 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);
}
if (vty_mgmt_fe_enabled()) {
char *data = NULL;
err = lyd_print_mem(&data, input, LYD_JSON, LYD_PRINT_SHRINK);
assert(err == LY_SUCCESS);
ret = vty_mgmt_send_rpc_req(vty, LYD_JSON, xpath, data);
free(data);
lyd_free_all(input);
if (ret < 0)
return CMD_WARNING;
return CMD_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, ret = nb_callback_rpc(nb_node, xpath, input, output, errmsg,
sizeof(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) { switch (ret) {
case NB_OK: case NB_OK:
if (output_p)
*output_p = output;
else
lyd_free_all(output);
return CMD_SUCCESS; return CMD_SUCCESS;
default: default:
lyd_free_all(output);
if (strlen(errmsg)) if (strlen(errmsg))
vty_show_nb_errors(vty, ret, errmsg); vty_show_nb_errors(vty, ret, errmsg);
return CMD_WARNING; return CMD_WARNING;

View file

@ -80,7 +80,23 @@ extern int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt,
...) PRINTFRR(2, 3); ...) 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 * vty
* The vty terminal to dump any error. * The vty terminal to dump any error.
@ -88,20 +104,16 @@ extern int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt,
* xpath * xpath
* XPath of the YANG RPC or Action node. * XPath of the YANG RPC or Action node.
* *
* input * output_p
* List of 'yang_data' structures containing the RPC input parameters. It * A pointer to the libyang data node that will hold the output data tree.
* can be set to NULL when there are no input parameters. * 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.
* 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.
* *
* Returns: * Returns:
* CMD_SUCCESS on success, CMD_WARNING otherwise. * CMD_SUCCESS on success, CMD_WARNING otherwise.
*/ */
extern int nb_cli_rpc(struct vty *vty, const char *xpath, struct list *input, extern int nb_cli_rpc(struct vty *vty, const char *xpath,
struct list *output); struct lyd_node **output_p);
/* /*
* Show CLI commands associated to the given YANG data node. * Show CLI commands associated to the given YANG data node.

View file

@ -1011,12 +1011,11 @@ grpc::Status HandleUnaryExecute(
grpc_debug("%s: entered", __func__); grpc_debug("%s: entered", __func__);
struct nb_node *nb_node; struct nb_node *nb_node;
struct list *input_list; struct lyd_node *input_tree, *output_tree, *child;
struct list *output_list;
struct listnode *node;
struct yang_data *data;
const char *xpath; const char *xpath;
char errmsg[BUFSIZ] = {0}; char errmsg[BUFSIZ] = {0};
char path[XPATH_MAXLEN];
LY_ERR err;
// Request: string path = 1; // Request: string path = 1;
xpath = tag->request.path().c_str(); xpath = tag->request.path().c_str();
@ -1032,40 +1031,66 @@ grpc::Status HandleUnaryExecute(
return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
"Unknown data path"); "Unknown data path");
input_list = yang_data_list_new(); // Create input data tree.
output_list = yang_data_list_new(); 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. // Read input parameters.
auto input = tag->request.input(); auto input = tag->request.input();
for (const frr::PathValue &pv : input) { for (const frr::PathValue &pv : input) {
// Request: repeated PathValue input = 2; // Request: repeated PathValue input = 2;
data = yang_data_new(pv.path().c_str(), pv.value().c_str()); err = lyd_new_path(input_tree, ly_native_ctx, pv.path().c_str(),
listnode_add(input_list, data); 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. // Execute callback registered for this XPath.
if (nb_callback_rpc(nb_node, xpath, input_list, output_list, errmsg, if (nb_callback_rpc(nb_node, xpath, input_tree, output_tree, errmsg,
sizeof(errmsg)) sizeof(errmsg)) != NB_OK) {
!= NB_OK) {
flog_warn(EC_LIB_NB_CB_RPC, "%s: rpc callback failed: %s", flog_warn(EC_LIB_NB_CB_RPC, "%s: rpc callback failed: %s",
__func__, xpath); __func__, xpath);
list_delete(&input_list); lyd_free_tree(input_tree);
list_delete(&output_list); lyd_free_tree(output_tree);
return grpc::Status(grpc::StatusCode::INTERNAL, "RPC failed"); return grpc::Status(grpc::StatusCode::INTERNAL, "RPC failed");
} }
// Process output parameters. // 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; // Response: repeated PathValue output = 1;
frr::PathValue *pv = tag->response.add_output(); frr::PathValue *pv = tag->response.add_output();
pv->set_path(data->xpath); pv->set_path(lyd_path(child, LYD_PATH_STD, path, sizeof(path)));
pv->set_value(data->value); pv->set_value(yang_dnode_get_string(child, NULL));
} }
// Release memory. // Release memory.
list_delete(&input_list); lyd_free_tree(input_tree);
list_delete(&output_list); lyd_free_tree(output_tree);
return grpc::Status::OK; return grpc::Status::OK;
} }

View file

@ -377,16 +377,11 @@ static int frr_sr_state_cb(sr_session_ctx_t *session, uint32_t sub_id,
return SR_ERR_OK; return SR_ERR_OK;
} }
static int frr_sr_config_rpc_cb(sr_session_ctx_t *session, uint32_t sub_id, 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 char *xpath, const struct lyd_node *input,
const size_t input_cnt, sr_event_t sr_ev, sr_event_t sr_ev, uint32_t request_id,
uint32_t request_id, sr_val_t **sr_output, struct lyd_node *output, void *private_ctx)
size_t *sr_output_cnt, void *private_ctx)
{ {
struct nb_node *nb_node; 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; int ret = SR_ERR_OK;
char errmsg[BUFSIZ] = {0}; char errmsg[BUFSIZ] = {0};
@ -397,19 +392,6 @@ static int frr_sr_config_rpc_cb(sr_session_ctx_t *session, uint32_t sub_id,
return SR_ERR_INTERNAL; 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. */ /* Execute callback registered for this XPath. */
if (nb_callback_rpc(nb_node, xpath, input, output, errmsg, if (nb_callback_rpc(nb_node, xpath, input, output, errmsg,
sizeof(errmsg)) sizeof(errmsg))
@ -417,44 +399,8 @@ static int frr_sr_config_rpc_cb(sr_session_ctx_t *session, uint32_t sub_id,
flog_warn(EC_LIB_NB_CB_RPC, "%s: rpc callback failed: %s", flog_warn(EC_LIB_NB_CB_RPC, "%s: rpc callback failed: %s",
__func__, xpath); __func__, xpath);
ret = SR_ERR_OPERATION_FAILED; 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; return ret;
} }
@ -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'", DEBUGD(&nb_dbg_client_sysrepo, "sysrepo: providing RPC to '%s'",
nb_node->xpath); nb_node->xpath);
ret = sr_rpc_subscribe(session, nb_node->xpath, frr_sr_config_rpc_cb, ret = sr_rpc_subscribe_tree(session, nb_node->xpath,
NULL, 0, 0, &module->sr_subscription); frr_sr_config_rpc_cb, NULL, 0, 0,
&module->sr_subscription);
if (ret != SR_ERR_OK) if (ret != SR_ERR_OK)
flog_err(EC_LIB_LIBSYSREPO, "sr_rpc_subscribe(): %s", flog_err(EC_LIB_LIBSYSREPO, "sr_rpc_subscribe(): %s",
sr_strerror(ret)); sr_strerror(ret));

View file

@ -3843,6 +3843,26 @@ static int vty_mgmt_edit_result_notified(struct mgmt_fe_client *client,
return 0; return 0;
} }
static int vty_mgmt_rpc_result_notified(struct mgmt_fe_client *client,
uintptr_t user_data, uint64_t client_id,
uint64_t session_id,
uintptr_t session_ctx, uint64_t req_id,
const char *result)
{
struct vty *vty = (struct vty *)session_ctx;
debug_fe_client("RPC request for client 0x%" PRIx64 " req-id %" PRIu64
" was successful",
client_id, req_id);
if (result)
vty_out(vty, "%s\n", result);
vty_mgmt_resume_response(vty, CMD_SUCCESS);
return 0;
}
static int vty_mgmt_error_notified(struct mgmt_fe_client *client, static int vty_mgmt_error_notified(struct mgmt_fe_client *client,
uintptr_t user_data, uint64_t client_id, uintptr_t user_data, uint64_t client_id,
uint64_t session_id, uintptr_t session_ctx, uint64_t session_id, uintptr_t session_ctx,
@ -3885,6 +3905,7 @@ static struct mgmt_fe_client_cbs mgmt_cbs = {
.get_data_notify = vty_mgmt_get_data_result_notified, .get_data_notify = vty_mgmt_get_data_result_notified,
.get_tree_notify = vty_mgmt_get_tree_result_notified, .get_tree_notify = vty_mgmt_get_tree_result_notified,
.edit_notify = vty_mgmt_edit_result_notified, .edit_notify = vty_mgmt_edit_result_notified,
.rpc_notify = vty_mgmt_rpc_result_notified,
.error_notify = vty_mgmt_error_notified, .error_notify = vty_mgmt_error_notified,
}; };
@ -4162,6 +4183,25 @@ int vty_mgmt_send_edit_req(struct vty *vty, uint8_t datastore,
return 0; return 0;
} }
int vty_mgmt_send_rpc_req(struct vty *vty, LYD_FORMAT request_type,
const char *xpath, const char *data)
{
vty->mgmt_req_id++;
if (mgmt_fe_send_rpc_req(mgmt_fe_client, vty->mgmt_session_id,
vty->mgmt_req_id, request_type, xpath, data)) {
zlog_err("Failed to send RPC to MGMTD session-id: %" PRIu64
" req-id %" PRIu64 ".",
vty->mgmt_session_id, vty->mgmt_req_id);
vty_out(vty, "Failed to send RPC to MGMTD!\n");
return -1;
}
vty->mgmt_req_pending_cmd = "MESSAGE_RPC_REQ";
return 0;
}
/* Install vty's own commands like `who' command. */ /* Install vty's own commands like `who' command. */
void vty_init(struct event_loop *master_thread, bool do_command_logging) void vty_init(struct event_loop *master_thread, bool do_command_logging)
{ {

View file

@ -122,6 +122,10 @@ struct vty {
size_t num_cfg_changes; size_t num_cfg_changes;
struct nb_cfg_change cfg_changes[VTY_MAXCFGCHANGES]; 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 */ /* XPath of the current node */
int xpath_index; int xpath_index;
char xpath[VTY_MAXDEPTH][XPATH_MAXLEN]; char xpath[VTY_MAXDEPTH][XPATH_MAXLEN];
@ -423,6 +427,8 @@ extern int vty_mgmt_send_edit_req(struct vty *vty, uint8_t datastore,
LYD_FORMAT request_type, uint8_t flags, LYD_FORMAT request_type, uint8_t flags,
uint8_t operation, const char *xpath, uint8_t operation, const char *xpath,
const char *data); const char *data);
extern int vty_mgmt_send_rpc_req(struct vty *vty, LYD_FORMAT request_type,
const char *xpath, const char *data);
extern int vty_mgmt_send_lockds_req(struct vty *vty, Mgmtd__DatastoreId ds_id, extern int vty_mgmt_send_lockds_req(struct vty *vty, Mgmtd__DatastoreId ds_id,
bool lock, bool scok); bool lock, bool scok);
extern void vty_mgmt_resume_response(struct vty *vty, int ret); extern void vty_mgmt_resume_response(struct vty *vty, int ret);

View file

@ -653,6 +653,16 @@ void yang_dnode_free(struct lyd_node *dnode)
lyd_free_all(dnode); lyd_free_all(dnode);
} }
void yang_dnode_rpc_output_add(struct lyd_node *output, const char *xpath,
const char *value)
{
LY_ERR err;
err = lyd_new_path(output, ly_native_ctx, xpath, value,
LYD_NEW_PATH_OUTPUT | LYD_NEW_PATH_UPDATE, NULL);
assert(err == LY_SUCCESS);
}
struct yang_data *yang_data_new(const char *xpath, const char *value) struct yang_data *yang_data_new(const char *xpath, const char *value)
{ {
struct yang_data *data; struct yang_data *data;
@ -764,6 +774,63 @@ LY_ERR yang_parse_notification(const char *xpath, LYD_FORMAT format,
return LY_SUCCESS; return LY_SUCCESS;
} }
LY_ERR yang_parse_rpc(const char *xpath, LYD_FORMAT format, const char *data,
bool reply, struct lyd_node **rpc)
{
const struct lysc_node *snode;
struct lyd_node *parent = NULL;
struct ly_in *in = NULL;
LY_ERR err;
snode = lys_find_path(ly_native_ctx, NULL, xpath, 0);
if (!snode) {
zlog_err("Failed to find RPC/action schema node: %s", xpath);
return LY_ENOTFOUND;
}
/* If it's an action, create its parent */
if (snode->nodetype == LYS_ACTION) {
char *parent_xpath = XSTRDUP(MTYPE_TMP, xpath);
if (yang_xpath_pop_node(parent_xpath) != NB_OK) {
XFREE(MTYPE_TMP, parent_xpath);
zlog_err("Invalid action xpath: %s", xpath);
return LY_EINVAL;
}
err = lyd_new_path2(NULL, ly_native_ctx, parent_xpath, NULL, 0,
0, 0, NULL, &parent);
XFREE(MTYPE_TMP, parent_xpath);
if (err) {
zlog_err("Failed to create parent node for action: %s",
ly_last_errmsg());
return err;
}
} else if (snode->nodetype != LYS_RPC) {
zlog_err("Schema node is not an RPC/action: %s", xpath);
return LY_EINVAL;
}
err = ly_in_new_memory(data, &in);
if (err) {
lyd_free_all(parent);
zlog_err("Failed to initialize ly_in: %s", ly_last_errmsg());
return err;
}
err = lyd_parse_op(ly_native_ctx, parent, in, format,
reply ? LYD_TYPE_REPLY_YANG : LYD_TYPE_RPC_YANG,
NULL, rpc);
ly_in_free(in, 0);
if (err) {
lyd_free_all(parent);
zlog_err("Failed to parse RPC/action: %s", ly_last_errmsg());
return err;
}
return LY_SUCCESS;
}
static ssize_t yang_print_darr(void *arg, const void *buf, size_t count) static ssize_t yang_print_darr(void *arg, const void *buf, size_t count)
{ {
uint8_t *dst = darr_append_n(*(uint8_t **)arg, count); uint8_t *dst = darr_append_n(*(uint8_t **)arg, count);

View file

@ -535,6 +535,21 @@ extern struct lyd_node *yang_dnode_dup(const struct lyd_node *dnode);
*/ */
extern void yang_dnode_free(struct lyd_node *dnode); extern void yang_dnode_free(struct lyd_node *dnode);
/*
* Add a libyang data node to an RPC/action output container.
*
* output
* RPC/action output container.
*
* xpath
* XPath of the data node to add, relative to the output container.
*
* value
* String representing the value of the data node.
*/
extern void yang_dnode_rpc_output_add(struct lyd_node *output,
const char *xpath, const char *value);
/* /*
* Create a new yang_data structure. * Create a new yang_data structure.
* *
@ -619,6 +634,25 @@ extern void yang_debugging_set(bool enable);
extern LY_ERR yang_parse_notification(const char *xpath, LYD_FORMAT format, extern LY_ERR yang_parse_notification(const char *xpath, LYD_FORMAT format,
const char *data, struct lyd_node **notif); const char *data, struct lyd_node **notif);
/*
* Parse a YANG RPC.
*
* Args:
* xpath: xpath of an RPC/action.
* format: LYD_FORMAT of input data.
* data: input data.
* reply: true if the data represents a reply to an RPC/action.
* rpc: pointer to the libyang data tree to store the parsed RPC/action.
* If data represents an action, the pointer to the action node is
* still returned, but it's part of the full data tree with all its
* parents.
*
* Returns:
* LY_ERR from underlying calls.
*/
LY_ERR yang_parse_rpc(const char *xpath, LYD_FORMAT format, const char *data,
bool reply, struct lyd_node **rpc);
/* /*
* "Print" the yang tree in `root` into dynamic sized array. * "Print" the yang tree in `root` into dynamic sized array.
* *

View file

@ -98,6 +98,10 @@ static const char *const ripd_oper_xpaths[] = {
"/ietf-key-chain:key-chains", "/ietf-key-chain:key-chains",
NULL, NULL,
}; };
static const char *const ripd_rpc_xpaths[] = {
"/frr-ripd",
NULL,
};
#endif #endif
#if HAVE_RIPNGD #if HAVE_RIPNGD
@ -113,6 +117,10 @@ static const char *const ripngd_oper_xpaths[] = {
"/frr-ripngd:ripngd", "/frr-ripngd:ripngd",
NULL, NULL,
}; };
static const char *const ripngd_rpc_xpaths[] = {
"/frr-ripngd",
NULL,
};
#endif #endif
#if HAVE_STATICD #if HAVE_STATICD
@ -147,6 +155,15 @@ static const char *const *be_client_oper_xpaths[MGMTD_BE_CLIENT_ID_MAX] = {
[MGMTD_BE_CLIENT_ID_ZEBRA] = zebra_oper_xpaths, [MGMTD_BE_CLIENT_ID_ZEBRA] = zebra_oper_xpaths,
}; };
static const char *const *be_client_rpc_xpaths[MGMTD_BE_CLIENT_ID_MAX] = {
#ifdef HAVE_RIPD
[MGMTD_BE_CLIENT_ID_RIPD] = ripd_rpc_xpaths,
#endif
#ifdef HAVE_RIPNGD
[MGMTD_BE_CLIENT_ID_RIPNGD] = ripngd_rpc_xpaths,
#endif
};
/* /*
* We would like to have a better ADT than one with O(n) comparisons * We would like to have a better ADT than one with O(n) comparisons
* *
@ -159,6 +176,7 @@ static const char *const *be_client_oper_xpaths[MGMTD_BE_CLIENT_ID_MAX] = {
static struct mgmt_be_xpath_map *be_cfg_xpath_map; static struct mgmt_be_xpath_map *be_cfg_xpath_map;
static struct mgmt_be_xpath_map *be_oper_xpath_map; static struct mgmt_be_xpath_map *be_oper_xpath_map;
static struct mgmt_be_xpath_map *be_notif_xpath_map; static struct mgmt_be_xpath_map *be_notif_xpath_map;
static struct mgmt_be_xpath_map *be_rpc_xpath_map;
static struct event_loop *mgmt_loop; static struct event_loop *mgmt_loop;
static struct msg_server mgmt_be_server = {.fd = -1}; static struct msg_server mgmt_be_server = {.fd = -1};
@ -173,8 +191,8 @@ static struct mgmt_be_client_adapter
static void static void
mgmt_be_adapter_sched_init_event(struct mgmt_be_client_adapter *adapter); mgmt_be_adapter_sched_init_event(struct mgmt_be_client_adapter *adapter);
static bool be_is_client_interested(const char *xpath, static bool be_is_client_interested(const char *xpath, enum mgmt_be_client_id id,
enum mgmt_be_client_id id, bool config); enum mgmt_be_xpath_subscr_type type);
const char *mgmt_be_client_id2name(enum mgmt_be_client_id id) const char *mgmt_be_client_id2name(enum mgmt_be_client_id id)
{ {
@ -223,16 +241,25 @@ mgmt_be_find_adapter_by_name(const char *name)
} }
static void mgmt_register_client_xpath(enum mgmt_be_client_id id, static void mgmt_register_client_xpath(enum mgmt_be_client_id id,
const char *xpath, bool config, bool oper) const char *xpath,
enum mgmt_be_xpath_subscr_type type)
{ {
struct mgmt_be_xpath_map **maps, *map; struct mgmt_be_xpath_map **maps, *map;
if (config) switch (type) {
case MGMT_BE_XPATH_SUBSCR_TYPE_CFG:
maps = &be_cfg_xpath_map; maps = &be_cfg_xpath_map;
else if (oper) break;
case MGMT_BE_XPATH_SUBSCR_TYPE_OPER:
maps = &be_oper_xpath_map; maps = &be_oper_xpath_map;
else break;
case MGMT_BE_XPATH_SUBSCR_TYPE_NOTIF:
maps = &be_notif_xpath_map; maps = &be_notif_xpath_map;
break;
case MGMT_BE_XPATH_SUBSCR_TYPE_RPC:
maps = &be_rpc_xpath_map;
break;
}
darr_foreach_p (*maps, map) { darr_foreach_p (*maps, map) {
if (!strcmp(xpath, map->xpath_prefix)) { if (!strcmp(xpath, map->xpath_prefix)) {
@ -260,18 +287,28 @@ static void mgmt_be_xpath_map_init(void)
/* Initialize the common config init map */ /* Initialize the common config init map */
for (init = be_client_config_xpaths[id]; init && *init; init++) { for (init = be_client_config_xpaths[id]; init && *init; init++) {
__dbg(" - CFG XPATH: '%s'", *init); __dbg(" - CFG XPATH: '%s'", *init);
mgmt_register_client_xpath(id, *init, true, false); mgmt_register_client_xpath(id, *init,
MGMT_BE_XPATH_SUBSCR_TYPE_CFG);
} }
/* Initialize the common oper init map */ /* Initialize the common oper init map */
for (init = be_client_oper_xpaths[id]; init && *init; init++) { for (init = be_client_oper_xpaths[id]; init && *init; init++) {
__dbg(" - OPER XPATH: '%s'", *init); __dbg(" - OPER XPATH: '%s'", *init);
mgmt_register_client_xpath(id, *init, false, true); mgmt_register_client_xpath(id, *init,
MGMT_BE_XPATH_SUBSCR_TYPE_OPER);
}
/* Initialize the common RPC init map */
for (init = be_client_rpc_xpaths[id]; init && *init; init++) {
__dbg(" - RPC XPATH: '%s'", *init);
mgmt_register_client_xpath(id, *init,
MGMT_BE_XPATH_SUBSCR_TYPE_RPC);
} }
} }
__dbg("Total Cfg XPath Maps: %u", darr_len(be_cfg_xpath_map)); __dbg("Total Cfg XPath Maps: %u", darr_len(be_cfg_xpath_map));
__dbg("Total Oper XPath Maps: %u", darr_len(be_oper_xpath_map)); __dbg("Total Oper XPath Maps: %u", darr_len(be_oper_xpath_map));
__dbg("Total RPC XPath Maps: %u", darr_len(be_rpc_xpath_map));
} }
static void mgmt_be_xpath_map_cleanup(void) static void mgmt_be_xpath_map_cleanup(void)
@ -289,6 +326,10 @@ static void mgmt_be_xpath_map_cleanup(void)
darr_foreach_p (be_notif_xpath_map, map) darr_foreach_p (be_notif_xpath_map, map)
XFREE(MTYPE_MGMTD_XPATH, map->xpath_prefix); XFREE(MTYPE_MGMTD_XPATH, map->xpath_prefix);
darr_free(be_notif_xpath_map); darr_free(be_notif_xpath_map);
darr_foreach_p (be_rpc_xpath_map, map)
XFREE(MTYPE_MGMTD_XPATH, map->xpath_prefix);
darr_free(be_rpc_xpath_map);
} }
@ -405,11 +446,12 @@ mgmt_be_adapter_handle_msg(struct mgmt_be_client_adapter *adapter,
*/ */
switch ((int)be_msg->message_case) { switch ((int)be_msg->message_case) {
case MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REQ: case MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REQ:
__dbg("Got SUBSCR_REQ from '%s' to register xpaths config: %zu oper: %zu notif: %zu", __dbg("Got SUBSCR_REQ from '%s' to register xpaths config: %zu oper: %zu notif: %zu rpc: %zu",
be_msg->subscr_req->client_name, be_msg->subscr_req->client_name,
be_msg->subscr_req->n_config_xpaths, be_msg->subscr_req->n_config_xpaths,
be_msg->subscr_req->n_oper_xpaths, be_msg->subscr_req->n_oper_xpaths,
be_msg->subscr_req->n_notif_xpaths); be_msg->subscr_req->n_notif_xpaths,
be_msg->subscr_req->n_rpc_xpaths);
if (strlen(be_msg->subscr_req->client_name)) { if (strlen(be_msg->subscr_req->client_name)) {
strlcpy(adapter->name, be_msg->subscr_req->client_name, strlcpy(adapter->name, be_msg->subscr_req->client_name,
@ -432,22 +474,29 @@ mgmt_be_adapter_handle_msg(struct mgmt_be_client_adapter *adapter,
num = be_msg->subscr_req->n_config_xpaths; num = be_msg->subscr_req->n_config_xpaths;
for (i = 0; i < num; i++) { for (i = 0; i < num; i++) {
xpath = be_msg->subscr_req->config_xpaths[i]; xpath = be_msg->subscr_req->config_xpaths[i];
mgmt_register_client_xpath(adapter->id, xpath, true, mgmt_register_client_xpath(adapter->id, xpath,
false); MGMT_BE_XPATH_SUBSCR_TYPE_CFG);
} }
num = be_msg->subscr_req->n_oper_xpaths; num = be_msg->subscr_req->n_oper_xpaths;
for (i = 0; i < num; i++) { for (i = 0; i < num; i++) {
xpath = be_msg->subscr_req->oper_xpaths[i]; xpath = be_msg->subscr_req->oper_xpaths[i];
mgmt_register_client_xpath(adapter->id, xpath, false, mgmt_register_client_xpath(adapter->id, xpath,
true); MGMT_BE_XPATH_SUBSCR_TYPE_OPER);
} }
num = be_msg->subscr_req->n_notif_xpaths; num = be_msg->subscr_req->n_notif_xpaths;
for (i = 0; i < num; i++) { for (i = 0; i < num; i++) {
xpath = be_msg->subscr_req->notif_xpaths[i]; xpath = be_msg->subscr_req->notif_xpaths[i];
mgmt_register_client_xpath(adapter->id, xpath, false, mgmt_register_client_xpath(adapter->id, xpath,
false); MGMT_BE_XPATH_SUBSCR_TYPE_NOTIF);
}
num = be_msg->subscr_req->n_rpc_xpaths;
for (i = 0; i < num; i++) {
xpath = be_msg->subscr_req->rpc_xpaths[i];
mgmt_register_client_xpath(adapter->id, xpath,
MGMT_BE_XPATH_SUBSCR_TYPE_RPC);
} }
mgmt_be_send_subscr_reply(adapter, true); mgmt_be_send_subscr_reply(adapter, true);
@ -638,6 +687,7 @@ static void be_adapter_handle_native_msg(struct mgmt_be_client_adapter *adapter,
{ {
struct mgmt_msg_notify_data *notify_msg; struct mgmt_msg_notify_data *notify_msg;
struct mgmt_msg_tree_data *tree_msg; struct mgmt_msg_tree_data *tree_msg;
struct mgmt_msg_rpc_reply *rpc_msg;
struct mgmt_msg_error *error_msg; struct mgmt_msg_error *error_msg;
/* get the transaction */ /* get the transaction */
@ -662,6 +712,15 @@ static void be_adapter_handle_native_msg(struct mgmt_be_client_adapter *adapter,
/* Forward the reply to the txn module */ /* Forward the reply to the txn module */
mgmt_txn_notify_tree_data_reply(adapter, tree_msg, msg_len); mgmt_txn_notify_tree_data_reply(adapter, tree_msg, msg_len);
break; break;
case MGMT_MSG_CODE_RPC_REPLY:
/* RPC reply from a backend client */
rpc_msg = (typeof(rpc_msg))msg;
__dbg("Got RPC_REPLY from '%s' txn-id %" PRIx64, adapter->name,
msg->refer_id);
/* Forward the reply to the txn module */
mgmt_txn_notify_rpc_reply(adapter, rpc_msg, msg_len);
break;
case MGMT_MSG_CODE_NOTIFY: case MGMT_MSG_CODE_NOTIFY:
notify_msg = (typeof(notify_msg))msg; notify_msg = (typeof(notify_msg))msg;
__dbg("Got NOTIFY from '%s'", adapter->name); __dbg("Got NOTIFY from '%s'", adapter->name);
@ -882,7 +941,8 @@ void mgmt_be_get_adapter_config(struct mgmt_be_client_adapter *adapter,
goto walk_cont; goto walk_cont;
xpath = lyd_path(dnode, LYD_PATH_STD, NULL, 0); xpath = lyd_path(dnode, LYD_PATH_STD, NULL, 0);
if (be_is_client_interested(xpath, adapter->id, true)) if (be_is_client_interested(xpath, adapter->id,
MGMT_BE_XPATH_SUBSCR_TYPE_CFG))
nb_config_diff_add_change(*changes, NB_CB_CREATE, &seq, dnode); nb_config_diff_add_change(*changes, NB_CB_CREATE, &seq, dnode);
else else
LYD_TREE_DFS_continue = 1; /* skip any subtree */ LYD_TREE_DFS_continue = 1; /* skip any subtree */
@ -893,13 +953,27 @@ void mgmt_be_get_adapter_config(struct mgmt_be_client_adapter *adapter,
} }
} }
uint64_t mgmt_be_interested_clients(const char *xpath, bool config) uint64_t mgmt_be_interested_clients(const char *xpath,
enum mgmt_be_xpath_subscr_type type)
{ {
struct mgmt_be_xpath_map *maps, *map; struct mgmt_be_xpath_map *maps = NULL, *map;
enum mgmt_be_client_id id; enum mgmt_be_client_id id;
uint64_t clients; uint64_t clients;
maps = config ? be_cfg_xpath_map : be_oper_xpath_map; switch (type) {
case MGMT_BE_XPATH_SUBSCR_TYPE_CFG:
maps = be_cfg_xpath_map;
break;
case MGMT_BE_XPATH_SUBSCR_TYPE_OPER:
maps = be_oper_xpath_map;
break;
case MGMT_BE_XPATH_SUBSCR_TYPE_NOTIF:
maps = be_notif_xpath_map;
break;
case MGMT_BE_XPATH_SUBSCR_TYPE_RPC:
maps = be_rpc_xpath_map;
break;
}
clients = 0; clients = 0;
@ -928,8 +1002,8 @@ uint64_t mgmt_be_interested_clients(const char *xpath, bool config)
* Returns: * Returns:
* Interested or not. * Interested or not.
*/ */
static bool be_is_client_interested(const char *xpath, static bool be_is_client_interested(const char *xpath, enum mgmt_be_client_id id,
enum mgmt_be_client_id id, bool config) enum mgmt_be_xpath_subscr_type type)
{ {
uint64_t clients; uint64_t clients;
@ -938,7 +1012,7 @@ static bool be_is_client_interested(const char *xpath,
__dbg("Checking client: %s for xpath: '%s'", mgmt_be_client_id2name(id), __dbg("Checking client: %s for xpath: '%s'", mgmt_be_client_id2name(id),
xpath); xpath);
clients = mgmt_be_interested_clients(xpath, config); clients = mgmt_be_interested_clients(xpath, type);
if (IS_IDBIT_SET(clients, id)) { if (IS_IDBIT_SET(clients, id)) {
__dbg("client: %s: interested", mgmt_be_client_id2name(id)); __dbg("client: %s: interested", mgmt_be_client_id2name(id));
return true; return true;
@ -998,23 +1072,32 @@ void mgmt_be_xpath_register_write(struct vty *vty)
darr_len(be_oper_xpath_map)); darr_len(be_oper_xpath_map));
darr_foreach_p (be_oper_xpath_map, map) darr_foreach_p (be_oper_xpath_map, map)
be_show_xpath_register(vty, map); be_show_xpath_register(vty, map);
vty_out(vty, "\nMGMTD Backend RPC XPath Registry: Count: %u\n",
darr_len(be_rpc_xpath_map));
darr_foreach_p (be_rpc_xpath_map, map)
be_show_xpath_register(vty, map);
} }
void mgmt_be_show_xpath_registries(struct vty *vty, const char *xpath) void mgmt_be_show_xpath_registries(struct vty *vty, const char *xpath)
{ {
enum mgmt_be_client_id id; enum mgmt_be_client_id id;
struct mgmt_be_client_adapter *adapter; struct mgmt_be_client_adapter *adapter;
uint64_t cclients, oclients, combined; uint64_t cclients, oclients, rclients, combined;
cclients = mgmt_be_interested_clients(xpath, true); cclients = mgmt_be_interested_clients(xpath,
oclients = mgmt_be_interested_clients(xpath, false); MGMT_BE_XPATH_SUBSCR_TYPE_CFG);
oclients = mgmt_be_interested_clients(xpath,
MGMT_BE_XPATH_SUBSCR_TYPE_OPER);
rclients = mgmt_be_interested_clients(xpath,
MGMT_BE_XPATH_SUBSCR_TYPE_RPC);
combined = cclients | oclients; combined = cclients | oclients;
vty_out(vty, "XPath: '%s'\n", xpath); vty_out(vty, "XPath: '%s'\n", xpath);
FOREACH_BE_CLIENT_BITS (id, combined) { FOREACH_BE_CLIENT_BITS (id, combined) {
vty_out(vty, " -- Client: '%s'\tconfig:%d oper:%d\n", vty_out(vty, " -- Client: '%s'\tconfig:%d oper:%d rpc:%d\n",
mgmt_be_client_id2name(id), IS_IDBIT_SET(cclients, id), mgmt_be_client_id2name(id), IS_IDBIT_SET(cclients, id),
IS_IDBIT_SET(oclients, id)); IS_IDBIT_SET(oclients, id), IS_IDBIT_SET(rclients, id));
adapter = mgmt_be_get_adapter_by_id(id); adapter = mgmt_be_get_adapter_by_id(id);
if (adapter) if (adapter)
vty_out(vty, " -- Adapter: %p\n", adapter); vty_out(vty, " -- Adapter: %p\n", adapter);

View file

@ -235,15 +235,23 @@ extern void mgmt_be_xpath_register_write(struct vty *vty);
*/ */
extern int mgmt_be_send_native(enum mgmt_be_client_id id, void *msg); extern int mgmt_be_send_native(enum mgmt_be_client_id id, void *msg);
enum mgmt_be_xpath_subscr_type {
MGMT_BE_XPATH_SUBSCR_TYPE_CFG,
MGMT_BE_XPATH_SUBSCR_TYPE_OPER,
MGMT_BE_XPATH_SUBSCR_TYPE_NOTIF,
MGMT_BE_XPATH_SUBSCR_TYPE_RPC,
};
/** /**
* Lookup the clients which are subscribed to a given `xpath` * Lookup the clients which are subscribed to a given `xpath`
* and the way they are subscribed. * and the way they are subscribed.
* *
* Args: * Args:
* xpath - the xpath to check for subscription information. * xpath - the xpath to check for subscription information.
* config - true for config interest false for oper interest. * type - type of subscription to check for.
*/ */
extern uint64_t mgmt_be_interested_clients(const char *xpath, bool config); extern uint64_t mgmt_be_interested_clients(const char *xpath,
enum mgmt_be_xpath_subscr_type type);
/** /**
* mgmt_fe_adapter_send_notify() - notify FE clients of a notification. * mgmt_fe_adapter_send_notify() - notify FE clients of a notification.

View file

@ -1101,6 +1101,47 @@ done:
return ret; return ret;
} }
static int fe_adapter_send_rpc_reply(struct mgmt_fe_session_ctx *session,
uint64_t req_id, uint8_t result_type,
const struct lyd_node *result)
{
struct mgmt_msg_rpc_reply *msg;
uint8_t **darrp = NULL;
int ret;
msg = mgmt_msg_native_alloc_msg(struct mgmt_msg_rpc_reply, 0,
MTYPE_MSG_NATIVE_RPC_REPLY);
msg->refer_id = session->session_id;
msg->req_id = req_id;
msg->code = MGMT_MSG_CODE_RPC_REPLY;
msg->result_type = result_type;
if (result) {
darrp = mgmt_msg_native_get_darrp(msg);
ret = yang_print_tree_append(darrp, result, result_type, 0);
if (ret != LY_SUCCESS) {
__log_err("Error building rpc-reply result for client %s session-id %" PRIu64
" req-id %" PRIu64 " result type %u",
session->adapter->name, session->session_id,
req_id, result_type);
goto done;
}
}
__dbg("Sending rpc-reply from adapter %s to session-id %" PRIu64
" req-id %" PRIu64 " len %u",
session->adapter->name, session->session_id, req_id,
mgmt_msg_native_get_msg_len(msg));
ret = fe_adapter_send_native_msg(session->adapter, msg,
mgmt_msg_native_get_msg_len(msg),
false);
done:
mgmt_msg_native_free_msg(msg);
return ret;
}
static int fe_adapter_send_edit_reply(struct mgmt_fe_session_ctx *session, static int fe_adapter_send_edit_reply(struct mgmt_fe_session_ctx *session,
uint64_t req_id, const char *xpath) uint64_t req_id, const char *xpath)
{ {
@ -1215,7 +1256,8 @@ static void fe_adapter_handle_get_data(struct mgmt_fe_session_ctx *session,
} }
darr_free(snodes); darr_free(snodes);
clients = mgmt_be_interested_clients(msg->xpath, false); clients = mgmt_be_interested_clients(msg->xpath,
MGMT_BE_XPATH_SUBSCR_TYPE_OPER);
if (!clients && !CHECK_FLAG(msg->flags, GET_DATA_FLAG_CONFIG)) { if (!clients && !CHECK_FLAG(msg->flags, GET_DATA_FLAG_CONFIG)) {
__dbg("No backends provide xpath: %s for txn-id: %" PRIu64 __dbg("No backends provide xpath: %s for txn-id: %" PRIu64
" session-id: %" PRIu64, " session-id: %" PRIu64,
@ -1270,7 +1312,7 @@ static void fe_adapter_handle_edit(struct mgmt_fe_session_ctx *session,
} }
xpath = mgmt_msg_native_xpath_data_decode(msg, msg_len, data); xpath = mgmt_msg_native_xpath_data_decode(msg, msg_len, data);
if (!xpath || !data) { if (!xpath) {
fe_adapter_send_error(session, msg->req_id, false, -EINVAL, fe_adapter_send_error(session, msg->req_id, false, -EINVAL,
"Invalid message"); "Invalid message");
return; return;
@ -1359,6 +1401,96 @@ static void fe_adapter_handle_edit(struct mgmt_fe_session_ctx *session,
} }
} }
/**
* fe_adapter_handle_rpc() - Handle an RPC message from an FE client.
* @session: the client session.
* @msg_raw: the message data.
* @msg_len: the length of the message data.
*/
static void fe_adapter_handle_rpc(struct mgmt_fe_session_ctx *session,
void *__msg, size_t msg_len)
{
struct mgmt_msg_rpc *msg = __msg;
const struct lysc_node *snode;
const char *xpath, *data;
uint64_t req_id = msg->req_id;
uint64_t clients;
int ret;
__dbg("Received RPC request from client %s for session-id %" PRIu64
" req-id %" PRIu64,
session->adapter->name, session->session_id, msg->req_id);
xpath = mgmt_msg_native_xpath_data_decode(msg, msg_len, data);
if (!xpath) {
fe_adapter_send_error(session, req_id, false, -EINVAL,
"Invalid message");
return;
}
if (session->txn_id != MGMTD_TXN_ID_NONE) {
fe_adapter_send_error(session, req_id, false, -EINPROGRESS,
"Transaction in progress txn-id: %" PRIu64
" for session-id: %" PRIu64,
session->txn_id, session->session_id);
return;
}
snode = lys_find_path(ly_native_ctx, NULL, xpath, 0);
if (!snode) {
fe_adapter_send_error(session, req_id, false, -ENOENT,
"No such path: %s", xpath);
return;
}
if (snode->nodetype == LYS_RPC)
clients =
mgmt_be_interested_clients(xpath,
MGMT_BE_XPATH_SUBSCR_TYPE_RPC);
else if (snode->nodetype == LYS_ACTION)
clients =
mgmt_be_interested_clients(xpath,
MGMT_BE_XPATH_SUBSCR_TYPE_CFG);
else {
fe_adapter_send_error(session, req_id, false, -EINVAL,
"Not an RPC or action path: %s", xpath);
return;
}
if (!clients) {
__dbg("No backends implement xpath: %s for txn-id: %" PRIu64
" session-id: %" PRIu64,
xpath, session->txn_id, session->session_id);
fe_adapter_send_error(session, req_id, false, -ENOENT,
"No backends implement xpath: %s", xpath);
return;
}
/* Start a RPC Transaction */
session->txn_id = mgmt_create_txn(session->session_id,
MGMTD_TXN_TYPE_RPC);
if (session->txn_id == MGMTD_SESSION_ID_NONE) {
fe_adapter_send_error(session, req_id, false, -EINPROGRESS,
"Failed to create an RPC transaction");
return;
}
__dbg("Created new rpc txn-id: %" PRIu64 " for session-id: %" PRIu64,
session->txn_id, session->session_id);
/* Create an RPC request under the transaction */
ret = mgmt_txn_send_rpc(session->txn_id, req_id, clients,
msg->request_type, xpath, data,
mgmt_msg_native_data_len_decode(msg, msg_len));
if (ret) {
/* destroy the just created txn */
mgmt_destroy_txn(&session->txn_id);
fe_adapter_send_error(session, req_id, false, -EINPROGRESS,
"Failed to create an RPC transaction");
}
}
/** /**
* Handle a native encoded message from the FE client. * Handle a native encoded message from the FE client.
*/ */
@ -1383,6 +1515,9 @@ static void fe_adapter_handle_native_msg(struct mgmt_fe_client_adapter *adapter,
case MGMT_MSG_CODE_EDIT: case MGMT_MSG_CODE_EDIT:
fe_adapter_handle_edit(session, msg, msg_len); fe_adapter_handle_edit(session, msg, msg_len);
break; break;
case MGMT_MSG_CODE_RPC:
fe_adapter_handle_rpc(session, msg, msg_len);
break;
default: default:
__log_err("unknown native message session-id %" PRIu64 __log_err("unknown native message session-id %" PRIu64
" req-id %" PRIu64 " code %u to FE adapter %s", " req-id %" PRIu64 " code %u to FE adapter %s",
@ -1622,6 +1757,24 @@ int mgmt_fe_adapter_send_tree_data(uint64_t session_id, uint64_t txn_id,
return ret; return ret;
} }
int mgmt_fe_adapter_send_rpc_reply(uint64_t session_id, uint64_t txn_id,
uint64_t req_id, LYD_FORMAT result_type,
const struct lyd_node *result)
{
struct mgmt_fe_session_ctx *session;
int ret;
session = mgmt_session_id2ctx(session_id);
if (!session || session->txn_id != txn_id)
return -1;
ret = fe_adapter_send_rpc_reply(session, req_id, result_type, result);
mgmt_destroy_txn(&session->txn_id);
return ret;
}
int mgmt_fe_adapter_send_edit_reply(uint64_t session_id, uint64_t txn_id, int mgmt_fe_adapter_send_edit_reply(uint64_t session_id, uint64_t txn_id,
uint64_t req_id, bool unlock, bool commit, uint64_t req_id, bool unlock, bool commit,
const char *xpath, int16_t error, const char *xpath, int16_t error,

View file

@ -162,6 +162,26 @@ mgmt_fe_adapter_send_tree_data(uint64_t session_id, uint64_t txn_id,
uint32_t wd_options, const struct lyd_node *tree, uint32_t wd_options, const struct lyd_node *tree,
int partial_error, bool short_circuit_ok); int partial_error, bool short_circuit_ok);
/**
* Send RPC reply back to client.
*
* This also cleans up and frees the transaction.
*
* Args:
* session_id: the session.
* txn_id: the txn_id this data pertains to
* req_id: the req id for the rpc message
* result_type: the format of the result data.
* result: the results.
*
* Return:
* the return value from the underlying send function.
*/
extern int mgmt_fe_adapter_send_rpc_reply(uint64_t session_id, uint64_t txn_id,
uint64_t req_id,
LYD_FORMAT result_type,
const struct lyd_node *result);
/** /**
* Send edit reply back to client. If error is not 0, a native error is sent. * Send edit reply back to client. If error is not 0, a native error is sent.
* *

View file

@ -20,6 +20,7 @@
DEFINE_MGROUP(MGMTD, "mgmt"); DEFINE_MGROUP(MGMTD, "mgmt");
DEFINE_MTYPE(MGMTD, MGMTD, "instance"); DEFINE_MTYPE(MGMTD, MGMTD, "instance");
DEFINE_MTYPE(MGMTD, MGMTD_XPATH, "xpath regex"); DEFINE_MTYPE(MGMTD, MGMTD_XPATH, "xpath regex");
DEFINE_MTYPE(MGMTD, MGMTD_ERR, "error");
DEFINE_MTYPE(MGMTD, MGMTD_BE_ADPATER, "backend adapter"); DEFINE_MTYPE(MGMTD, MGMTD_BE_ADPATER, "backend adapter");
DEFINE_MTYPE(MGMTD, MGMTD_FE_ADPATER, "frontend adapter"); DEFINE_MTYPE(MGMTD, MGMTD_FE_ADPATER, "frontend adapter");
DEFINE_MTYPE(MGMTD, MGMTD_FE_SESSION, "frontend session"); DEFINE_MTYPE(MGMTD, MGMTD_FE_SESSION, "frontend session");
@ -30,5 +31,6 @@ DEFINE_MTYPE(MGMTD, MGMTD_TXN_COMMCFG_REQ, "txn commit-config requests");
DEFINE_MTYPE(MGMTD, MGMTD_TXN_GETDATA_REQ, "txn get-data requests"); DEFINE_MTYPE(MGMTD, MGMTD_TXN_GETDATA_REQ, "txn get-data requests");
DEFINE_MTYPE(MGMTD, MGMTD_TXN_GETDATA_REPLY, "txn get-data replies"); DEFINE_MTYPE(MGMTD, MGMTD_TXN_GETDATA_REPLY, "txn get-data replies");
DEFINE_MTYPE(MGMTD, MGMTD_TXN_GETTREE_REQ, "txn get-tree requests"); DEFINE_MTYPE(MGMTD, MGMTD_TXN_GETTREE_REQ, "txn get-tree requests");
DEFINE_MTYPE(MGMTD, MGMTD_TXN_RPC_REQ, "txn rpc requests");
DEFINE_MTYPE(MGMTD, MGMTD_TXN_CFG_BATCH, "txn config batches"); DEFINE_MTYPE(MGMTD, MGMTD_TXN_CFG_BATCH, "txn config batches");
DEFINE_MTYPE(MGMTD, MGMTD_CMT_INFO, "commit info"); DEFINE_MTYPE(MGMTD, MGMTD_CMT_INFO, "commit info");

View file

@ -14,6 +14,7 @@
DECLARE_MGROUP(MGMTD); DECLARE_MGROUP(MGMTD);
DECLARE_MTYPE(MGMTD); DECLARE_MTYPE(MGMTD);
DECLARE_MTYPE(MGMTD_XPATH); DECLARE_MTYPE(MGMTD_XPATH);
DECLARE_MTYPE(MGMTD_ERR);
DECLARE_MTYPE(MGMTD_BE_ADPATER); DECLARE_MTYPE(MGMTD_BE_ADPATER);
DECLARE_MTYPE(MGMTD_FE_ADPATER); DECLARE_MTYPE(MGMTD_FE_ADPATER);
DECLARE_MTYPE(MGMTD_FE_SESSION); DECLARE_MTYPE(MGMTD_FE_SESSION);
@ -24,6 +25,7 @@ DECLARE_MTYPE(MGMTD_TXN_COMMCFG_REQ);
DECLARE_MTYPE(MGMTD_TXN_GETDATA_REQ); DECLARE_MTYPE(MGMTD_TXN_GETDATA_REQ);
DECLARE_MTYPE(MGMTD_TXN_GETDATA_REPLY); DECLARE_MTYPE(MGMTD_TXN_GETDATA_REPLY);
DECLARE_MTYPE(MGMTD_TXN_GETTREE_REQ); DECLARE_MTYPE(MGMTD_TXN_GETTREE_REQ);
DECLARE_MTYPE(MGMTD_TXN_RPC_REQ);
DECLARE_MTYPE(MGMTD_TXN_CFG_BATCH); DECLARE_MTYPE(MGMTD_TXN_CFG_BATCH);
DECLARE_MTYPE(MGMTD_BE_ADAPTER_MSG_BUF); DECLARE_MTYPE(MGMTD_BE_ADAPTER_MSG_BUF);
DECLARE_MTYPE(MGMTD_CMT_INFO); DECLARE_MTYPE(MGMTD_CMT_INFO);

View file

@ -18,6 +18,7 @@
/* ---------------- */ /* ---------------- */
static void async_notification(struct nb_cb_notify_args *args); static void async_notification(struct nb_cb_notify_args *args);
static int rpc_callback(struct nb_cb_rpc_args *args);
static void sigusr1(void); static void sigusr1(void);
static void sigint(void); static void sigint(void);
@ -86,6 +87,10 @@ static const struct frr_yang_module_info frr_ripd_info = {
.xpath = "/frr-ripd:authentication-failure", .xpath = "/frr-ripd:authentication-failure",
.cbs.notify = async_notification, .cbs.notify = async_notification,
}, },
{
.xpath = "/frr-ripd:clear-rip-route",
.cbs.rpc = rpc_callback,
},
{ {
.xpath = NULL, .xpath = NULL,
} }
@ -113,6 +118,7 @@ FRR_DAEMON_INFO(mgmtd_testc, MGMTD_TESTC,
/* clang-format on */ /* clang-format on */
const char **__notif_xpaths; const char **__notif_xpaths;
const char **__rpc_xpaths;
struct mgmt_be_client_cbs __client_cbs = {}; struct mgmt_be_client_cbs __client_cbs = {};
struct event *event_timeout; struct event *event_timeout;
@ -134,6 +140,7 @@ static void quit(int exit_code)
{ {
EVENT_OFF(event_timeout); EVENT_OFF(event_timeout);
darr_free(__client_cbs.notif_xpaths); darr_free(__client_cbs.notif_xpaths);
darr_free(__client_cbs.rpc_xpaths);
frr_fini(); frr_fini();
@ -152,6 +159,12 @@ static void timeout(struct event *event)
quit(1); quit(1);
} }
static void success(struct event *event)
{
zlog_notice("Success, exiting");
quit(0);
}
static void async_notification(struct nb_cb_notify_args *args) static void async_notification(struct nb_cb_notify_args *args)
{ {
zlog_notice("Received YANG notification"); zlog_notice("Received YANG notification");
@ -163,6 +176,23 @@ static void async_notification(struct nb_cb_notify_args *args)
quit(0); quit(0);
} }
static int rpc_callback(struct nb_cb_rpc_args *args)
{
const char *vrf = NULL;
zlog_notice("Received YANG RPC");
if (yang_dnode_exists(args->input, "vrf"))
vrf = yang_dnode_get_string(args->input, "vrf");
printf("{\"frr-ripd:clear-rip-route\": {\"vrf\": \"%s\"}}\n", vrf);
event_cancel(&event_timeout);
event_add_timer(master, success, NULL, 1, NULL);
return 0;
}
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
int f_listen = 0; int f_listen = 0;
@ -217,6 +247,10 @@ int main(int argc, char **argv)
__client_cbs.nnotif_xpaths = darr_len(__notif_xpaths); __client_cbs.nnotif_xpaths = darr_len(__notif_xpaths);
} }
darr_push(__rpc_xpaths, "/frr-ripd:clear-rip-route");
__client_cbs.rpc_xpaths = __rpc_xpaths;
__client_cbs.nrpc_xpaths = darr_len(__rpc_xpaths);
mgmt_be_client = mgmt_be_client_create("mgmtd-testc", &__client_cbs, 0, mgmt_be_client = mgmt_be_client_create("mgmtd-testc", &__client_cbs, 0,
master); master);

View file

@ -29,6 +29,7 @@ enum mgmt_txn_event {
MGMTD_TXN_PROC_COMMITCFG, MGMTD_TXN_PROC_COMMITCFG,
MGMTD_TXN_PROC_GETCFG, MGMTD_TXN_PROC_GETCFG,
MGMTD_TXN_PROC_GETTREE, MGMTD_TXN_PROC_GETTREE,
MGMTD_TXN_PROC_RPC,
MGMTD_TXN_COMMITCFG_TIMEOUT, MGMTD_TXN_COMMITCFG_TIMEOUT,
}; };
@ -188,6 +189,15 @@ struct txn_req_get_tree {
struct lyd_node *client_results; /* result tree from clients */ struct lyd_node *client_results; /* result tree from clients */
}; };
struct txn_req_rpc {
char *xpath; /* xpath of rpc/action to invoke */
uint64_t sent_clients; /* Bitmask of clients sent req to */
uint64_t recv_clients; /* Bitmask of clients recv reply from */
uint8_t result_type; /* LYD_FORMAT for results */
char *errstr; /* error string */
struct lyd_node *client_results; /* result tree from clients */
};
struct mgmt_txn_req { struct mgmt_txn_req {
struct mgmt_txn_ctx *txn; struct mgmt_txn_ctx *txn;
enum mgmt_txn_event req_event; enum mgmt_txn_event req_event;
@ -196,6 +206,7 @@ struct mgmt_txn_req {
struct mgmt_set_cfg_req *set_cfg; struct mgmt_set_cfg_req *set_cfg;
struct mgmt_get_data_req *get_data; struct mgmt_get_data_req *get_data;
struct txn_req_get_tree *get_tree; struct txn_req_get_tree *get_tree;
struct txn_req_rpc *rpc;
struct mgmt_commit_cfg_req commit_cfg; struct mgmt_commit_cfg_req commit_cfg;
} req; } req;
@ -221,6 +232,7 @@ struct mgmt_txn_ctx {
struct event *proc_get_tree; struct event *proc_get_tree;
struct event *comm_cfg_timeout; struct event *comm_cfg_timeout;
struct event *get_tree_timeout; struct event *get_tree_timeout;
struct event *rpc_timeout;
struct event *clnup; struct event *clnup;
/* List of backend adapters involved in this transaction */ /* List of backend adapters involved in this transaction */
@ -252,6 +264,10 @@ struct mgmt_txn_ctx {
* List of pending get-tree requests. * List of pending get-tree requests.
*/ */
struct mgmt_txn_reqs_head get_tree_reqs; struct mgmt_txn_reqs_head get_tree_reqs;
/*
* List of pending rpc requests.
*/
struct mgmt_txn_reqs_head rpc_reqs;
/* /*
* There will always be one commit-config allowed for a given * There will always be one commit-config allowed for a given
* transaction/session. No need to maintain lists for it. * transaction/session. No need to maintain lists for it.
@ -416,6 +432,15 @@ static struct mgmt_txn_req *mgmt_txn_req_alloc(struct mgmt_txn_ctx *txn,
" session-id: %" PRIu64, " session-id: %" PRIu64,
txn_req->req_id, txn->txn_id, txn->session_id); txn_req->req_id, txn->txn_id, txn->session_id);
break; break;
case MGMTD_TXN_PROC_RPC:
txn_req->req.rpc = XCALLOC(MTYPE_MGMTD_TXN_RPC_REQ,
sizeof(struct txn_req_rpc));
assert(txn_req->req.rpc);
mgmt_txn_reqs_add_tail(&txn->rpc_reqs, txn_req);
__dbg("Added a new RPC req-id: %" PRIu64 " txn-id: %" PRIu64
" session-id: %" PRIu64,
txn_req->req_id, txn->txn_id, txn->session_id);
break;
case MGMTD_TXN_COMMITCFG_TIMEOUT: case MGMTD_TXN_COMMITCFG_TIMEOUT:
break; break;
} }
@ -506,6 +531,15 @@ static void mgmt_txn_req_free(struct mgmt_txn_req **txn_req)
XFREE(MTYPE_MGMTD_XPATH, (*txn_req)->req.get_tree->xpath); XFREE(MTYPE_MGMTD_XPATH, (*txn_req)->req.get_tree->xpath);
XFREE(MTYPE_MGMTD_TXN_GETTREE_REQ, (*txn_req)->req.get_tree); XFREE(MTYPE_MGMTD_TXN_GETTREE_REQ, (*txn_req)->req.get_tree);
break; break;
case MGMTD_TXN_PROC_RPC:
__dbg("Deleting RPC req-id: %" PRIu64 " txn-id: %" PRIu64,
(*txn_req)->req_id, (*txn_req)->txn->txn_id);
req_list = &(*txn_req)->txn->rpc_reqs;
lyd_free_all((*txn_req)->req.rpc->client_results);
XFREE(MTYPE_MGMTD_ERR, (*txn_req)->req.rpc->errstr);
XFREE(MTYPE_MGMTD_XPATH, (*txn_req)->req.rpc->xpath);
XFREE(MTYPE_MGMTD_TXN_RPC_REQ, (*txn_req)->req.rpc);
break;
case MGMTD_TXN_COMMITCFG_TIMEOUT: case MGMTD_TXN_COMMITCFG_TIMEOUT:
break; break;
} }
@ -880,7 +914,9 @@ static int mgmt_txn_create_config_batches(struct mgmt_txn_req *txn_req,
__dbg("XPATH: %s, Value: '%s'", xpath, value ? value : "NIL"); __dbg("XPATH: %s, Value: '%s'", xpath, value ? value : "NIL");
clients = mgmt_be_interested_clients(xpath, true); clients =
mgmt_be_interested_clients(xpath,
MGMT_BE_XPATH_SUBSCR_TYPE_CFG);
chg_clients = 0; chg_clients = 0;
@ -1306,6 +1342,33 @@ static int txn_get_tree_data_done(struct mgmt_txn_ctx *txn,
return ret; return ret;
} }
static int txn_rpc_done(struct mgmt_txn_ctx *txn, struct mgmt_txn_req *txn_req)
{
struct txn_req_rpc *rpc = txn_req->req.rpc;
uint64_t req_id = txn_req->req_id;
/* cancel timer and send reply onward */
EVENT_OFF(txn->rpc_timeout);
if (rpc->errstr)
mgmt_fe_adapter_txn_error(txn->txn_id, req_id, false, -1,
rpc->errstr);
else if (mgmt_fe_adapter_send_rpc_reply(txn->session_id, txn->txn_id,
req_id, rpc->result_type,
rpc->client_results)) {
__log_err("Error sending the results of RPC for txn-id %" PRIu64
" req_id %" PRIu64 " to requested type %u",
txn->txn_id, req_id, rpc->result_type);
(void)mgmt_fe_adapter_txn_error(txn->txn_id, req_id, false, -1,
"Error converting results of RPC");
}
/* we're done with the request */
mgmt_txn_req_free(&txn_req);
return 0;
}
static void txn_get_tree_timeout(struct event *thread) static void txn_get_tree_timeout(struct event *thread)
{ {
@ -1333,6 +1396,31 @@ static void txn_get_tree_timeout(struct event *thread)
txn_get_tree_data_done(txn, txn_req); txn_get_tree_data_done(txn, txn_req);
} }
static void txn_rpc_timeout(struct event *thread)
{
struct mgmt_txn_ctx *txn;
struct mgmt_txn_req *txn_req;
txn_req = (struct mgmt_txn_req *)EVENT_ARG(thread);
txn = txn_req->txn;
assert(txn);
assert(txn->type == MGMTD_TXN_TYPE_RPC);
__log_err("Backend timeout txn-id: %" PRIu64 " ending rpc", txn->txn_id);
/*
* Send a get-tree data reply.
*
* NOTE: The transaction cleanup will be triggered from Front-end
* adapter.
*/
txn_req->req.rpc->errstr =
XSTRDUP(MTYPE_MGMTD_ERR, "Operation on the backend timed-out");
txn_rpc_done(txn, txn_req);
}
/* /*
* Send CFG_APPLY_REQs to all the backend client. * Send CFG_APPLY_REQs to all the backend client.
* *
@ -1516,6 +1604,7 @@ static void mgmt_txn_send_getcfg_reply_data(struct mgmt_txn_req *txn_req,
case MGMTD_TXN_PROC_SETCFG: case MGMTD_TXN_PROC_SETCFG:
case MGMTD_TXN_PROC_COMMITCFG: case MGMTD_TXN_PROC_COMMITCFG:
case MGMTD_TXN_PROC_GETTREE: case MGMTD_TXN_PROC_GETTREE:
case MGMTD_TXN_PROC_RPC:
case MGMTD_TXN_COMMITCFG_TIMEOUT: case MGMTD_TXN_COMMITCFG_TIMEOUT:
__log_err("Invalid Txn-Req-Event %u", txn_req->req_event); __log_err("Invalid Txn-Req-Event %u", txn_req->req_event);
break; break;
@ -1721,6 +1810,7 @@ static struct mgmt_txn_ctx *mgmt_txn_create_new(uint64_t session_id,
mgmt_txn_reqs_init(&txn->set_cfg_reqs); mgmt_txn_reqs_init(&txn->set_cfg_reqs);
mgmt_txn_reqs_init(&txn->get_cfg_reqs); mgmt_txn_reqs_init(&txn->get_cfg_reqs);
mgmt_txn_reqs_init(&txn->get_tree_reqs); mgmt_txn_reqs_init(&txn->get_tree_reqs);
mgmt_txn_reqs_init(&txn->rpc_reqs);
txn->commit_cfg_req = NULL; txn->commit_cfg_req = NULL;
txn->refcount = 0; txn->refcount = 0;
if (!mgmt_txn_mm->next_txn_id) if (!mgmt_txn_mm->next_txn_id)
@ -1890,6 +1980,7 @@ static void mgmt_txn_register_event(struct mgmt_txn_ctx *txn,
&txn->comm_cfg_timeout); &txn->comm_cfg_timeout);
break; break;
case MGMTD_TXN_PROC_GETTREE: case MGMTD_TXN_PROC_GETTREE:
case MGMTD_TXN_PROC_RPC:
assert(!"code bug do not register this event"); assert(!"code bug do not register this event");
break; break;
} }
@ -2496,6 +2587,64 @@ reply:
return 0; return 0;
} }
int mgmt_txn_send_rpc(uint64_t txn_id, uint64_t req_id, uint64_t clients,
LYD_FORMAT result_type, const char *xpath,
const char *data, size_t data_len)
{
struct mgmt_txn_ctx *txn;
struct mgmt_txn_req *txn_req;
struct mgmt_msg_rpc *msg;
struct txn_req_rpc *rpc;
uint64_t id;
int ret;
txn = mgmt_txn_id2ctx(txn_id);
if (!txn)
return -1;
txn_req = mgmt_txn_req_alloc(txn, req_id, MGMTD_TXN_PROC_RPC);
rpc = txn_req->req.rpc;
rpc->xpath = XSTRDUP(MTYPE_MGMTD_XPATH, xpath);
rpc->result_type = result_type;
msg = mgmt_msg_native_alloc_msg(struct mgmt_msg_rpc, 0,
MTYPE_MSG_NATIVE_RPC);
msg->refer_id = txn_id;
msg->req_id = req_id;
msg->code = MGMT_MSG_CODE_RPC;
msg->request_type = result_type;
mgmt_msg_native_xpath_encode(msg, xpath);
if (data)
mgmt_msg_native_append(msg, data, data_len);
assert(clients);
FOREACH_BE_CLIENT_BITS (id, clients) {
ret = mgmt_be_send_native(id, msg);
if (ret) {
__log_err("Could not send rpc message to backend client %s",
mgmt_be_client_id2name(id));
continue;
}
__dbg("Sent rpc req to backend client %s",
mgmt_be_client_id2name(id));
/* record that we sent the request to the client */
rpc->sent_clients |= (1u << id);
}
mgmt_msg_native_free_msg(msg);
if (!rpc->sent_clients)
return txn_rpc_done(txn, txn_req);
event_add_timer(mgmt_txn_tm, txn_rpc_timeout, txn_req,
MGMTD_TXN_RPC_MAX_DELAY_SEC, &txn->rpc_timeout);
return 0;
}
/* /*
* Error reply from the backend client. * Error reply from the backend client.
*/ */
@ -2506,6 +2655,7 @@ int mgmt_txn_notify_error(struct mgmt_be_client_adapter *adapter,
enum mgmt_be_client_id id = adapter->id; enum mgmt_be_client_id id = adapter->id;
struct mgmt_txn_ctx *txn = mgmt_txn_id2ctx(txn_id); struct mgmt_txn_ctx *txn = mgmt_txn_id2ctx(txn_id);
struct txn_req_get_tree *get_tree; struct txn_req_get_tree *get_tree;
struct txn_req_rpc *rpc;
struct mgmt_txn_req *txn_req; struct mgmt_txn_req *txn_req;
if (!txn) { if (!txn) {
@ -2518,6 +2668,10 @@ int mgmt_txn_notify_error(struct mgmt_be_client_adapter *adapter,
FOREACH_TXN_REQ_IN_LIST (&txn->get_tree_reqs, txn_req) FOREACH_TXN_REQ_IN_LIST (&txn->get_tree_reqs, txn_req)
if (txn_req->req_id == req_id) if (txn_req->req_id == req_id)
break; break;
if (!txn_req)
FOREACH_TXN_REQ_IN_LIST (&txn->rpc_reqs, txn_req)
if (txn_req->req_id == req_id)
break;
if (!txn_req) { if (!txn_req) {
__log_err("Error reply from %s for txn-id %" PRIu64 __log_err("Error reply from %s for txn-id %" PRIu64
" cannot find req_id %" PRIu64, " cannot find req_id %" PRIu64,
@ -2538,6 +2692,15 @@ int mgmt_txn_notify_error(struct mgmt_be_client_adapter *adapter,
if (get_tree->recv_clients != get_tree->sent_clients) if (get_tree->recv_clients != get_tree->sent_clients)
return 0; return 0;
return txn_get_tree_data_done(txn, txn_req); return txn_get_tree_data_done(txn, txn_req);
case MGMTD_TXN_PROC_RPC:
rpc = txn_req->req.rpc;
rpc->recv_clients |= (1u << id);
rpc->errstr = XSTRDUP(MTYPE_MGMTD_ERR, errstr);
/* check if done yet */
if (rpc->recv_clients != rpc->sent_clients)
return 0;
return txn_rpc_done(txn, txn_req);
/* non-native message events */ /* non-native message events */
case MGMTD_TXN_PROC_SETCFG: case MGMTD_TXN_PROC_SETCFG:
@ -2625,6 +2788,64 @@ int mgmt_txn_notify_tree_data_reply(struct mgmt_be_client_adapter *adapter,
return txn_get_tree_data_done(txn, txn_req); return txn_get_tree_data_done(txn, txn_req);
} }
int mgmt_txn_notify_rpc_reply(struct mgmt_be_client_adapter *adapter,
struct mgmt_msg_rpc_reply *reply_msg,
size_t msg_len)
{
uint64_t txn_id = reply_msg->refer_id;
uint64_t req_id = reply_msg->req_id;
enum mgmt_be_client_id id = adapter->id;
struct mgmt_txn_ctx *txn = mgmt_txn_id2ctx(txn_id);
struct mgmt_txn_req *txn_req;
struct txn_req_rpc *rpc;
size_t data_len = msg_len - sizeof(*reply_msg);
LY_ERR err;
if (!txn) {
__log_err("RPC reply from %s for a missing txn-id %" PRIu64,
adapter->name, txn_id);
return -1;
}
/* Find the request. */
FOREACH_TXN_REQ_IN_LIST (&txn->rpc_reqs, txn_req)
if (txn_req->req_id == req_id)
break;
if (!txn_req) {
__log_err("RPC reply from %s for txn-id %" PRIu64
" missing req_id %" PRIu64,
adapter->name, txn_id, req_id);
return -1;
}
rpc = txn_req->req.rpc;
/* we don't expect more than one daemon to provide output for an RPC */
if (!rpc->client_results && data_len > 0) {
err = yang_parse_rpc(rpc->xpath, reply_msg->result_type,
reply_msg->data, true,
&rpc->client_results);
if (err) {
__log_err("RPC reply from %s for txn-id %" PRIu64
" req_id %" PRIu64
" error parsing result of type %u",
adapter->name, txn_id, req_id,
reply_msg->result_type);
rpc->errstr =
XSTRDUP(MTYPE_MGMTD_ERR,
"Cannot parse result from the backend");
}
}
rpc->recv_clients |= (1u << id);
/* check if done yet */
if (rpc->recv_clients != rpc->sent_clients)
return 0;
return txn_rpc_done(txn, txn_req);
}
void mgmt_txn_status_write(struct vty *vty) void mgmt_txn_status_write(struct vty *vty)
{ {
struct mgmt_txn_ctx *txn; struct mgmt_txn_ctx *txn;

View file

@ -21,6 +21,7 @@
#define MGMTD_TXN_CFG_COMMIT_MAX_DELAY_SEC 600 #define MGMTD_TXN_CFG_COMMIT_MAX_DELAY_SEC 600
#define MGMTD_TXN_GET_TREE_MAX_DELAY_SEC 600 #define MGMTD_TXN_GET_TREE_MAX_DELAY_SEC 600
#define MGMTD_TXN_RPC_MAX_DELAY_SEC 60
#define MGMTD_TXN_CLEANUP_DELAY_USEC 10 #define MGMTD_TXN_CLEANUP_DELAY_USEC 10
@ -48,7 +49,8 @@ struct mgmt_edit_req;
enum mgmt_txn_type { enum mgmt_txn_type {
MGMTD_TXN_TYPE_NONE = 0, MGMTD_TXN_TYPE_NONE = 0,
MGMTD_TXN_TYPE_CONFIG, MGMTD_TXN_TYPE_CONFIG,
MGMTD_TXN_TYPE_SHOW MGMTD_TXN_TYPE_SHOW,
MGMTD_TXN_TYPE_RPC,
}; };
static inline const char *mgmt_txn_type2str(enum mgmt_txn_type type) static inline const char *mgmt_txn_type2str(enum mgmt_txn_type type)
@ -60,6 +62,8 @@ static inline const char *mgmt_txn_type2str(enum mgmt_txn_type type)
return "CONFIG"; return "CONFIG";
case MGMTD_TXN_TYPE_SHOW: case MGMTD_TXN_TYPE_SHOW:
return "SHOW"; return "SHOW";
case MGMTD_TXN_TYPE_RPC:
return "RPC";
} }
return "Unknown"; return "Unknown";
@ -246,6 +250,25 @@ mgmt_txn_send_edit(uint64_t txn_id, uint64_t req_id, Mgmtd__DatastoreId ds_id,
LYD_FORMAT request_type, uint8_t flags, uint8_t operation, LYD_FORMAT request_type, uint8_t flags, uint8_t operation,
const char *xpath, const char *data); const char *xpath, const char *data);
/**
* Send RPC request.
*
* Args:
* txn_id: Transaction identifier.
* req_id: FE client request identifier.
* clients: Bitmask of clients to send RPC to.
* result_type: LYD_FORMAT result format.
* xpath: The xpath of the RPC.
* data: The input parameters data tree.
* data_len: The length of the input parameters data.
*
* Return:
* 0 on success.
*/
extern int mgmt_txn_send_rpc(uint64_t txn_id, uint64_t req_id, uint64_t clients,
LYD_FORMAT result_type, const char *xpath,
const char *data, size_t data_len);
/* /*
* Notifiy backend adapter on connection. * Notifiy backend adapter on connection.
*/ */
@ -312,6 +335,18 @@ extern int mgmt_txn_notify_tree_data_reply(struct mgmt_be_client_adapter *adapte
struct mgmt_msg_tree_data *data_msg, struct mgmt_msg_tree_data *data_msg,
size_t msg_len); size_t msg_len);
/**
* Process a reply from a backend client to our RPC request
*
* Args:
* adapter: The adapter that received the result.
* reply_msg: The message from the backend.
* msg_len: Total length of the message.
*/
extern int mgmt_txn_notify_rpc_reply(struct mgmt_be_client_adapter *adapter,
struct mgmt_msg_rpc_reply *reply_msg,
size_t msg_len);
/* /*
* Dump transaction status to vty. * Dump transaction status to vty.
*/ */

View file

@ -296,6 +296,21 @@ DEFPY(mgmt_edit, mgmt_edit_cmd,
return CMD_SUCCESS; return CMD_SUCCESS;
} }
DEFPY(mgmt_rpc, mgmt_rpc_cmd,
"mgmt rpc XPATH [json|xml]$fmt [DATA]",
MGMTD_STR
"Invoke RPC\n"
"XPath expression specifying the YANG data path\n"
"JSON input format (default)\n"
"XML input format\n"
"Input data tree\n")
{
LYD_FORMAT format = (fmt && fmt[0] == 'x') ? LYD_XML : LYD_JSON;
vty_mgmt_send_rpc_req(vty, format, xpath, data);
return CMD_SUCCESS;
}
DEFPY(show_mgmt_get_config, show_mgmt_get_config_cmd, DEFPY(show_mgmt_get_config, show_mgmt_get_config_cmd,
"show mgmt get-config [candidate|operational|running]$dsname WORD$path", "show mgmt get-config [candidate|operational|running]$dsname WORD$path",
SHOW_STR MGMTD_STR SHOW_STR MGMTD_STR
@ -702,6 +717,7 @@ void mgmt_vty_init(void)
install_element(CONFIG_NODE, &mgmt_remove_config_data_cmd); install_element(CONFIG_NODE, &mgmt_remove_config_data_cmd);
install_element(CONFIG_NODE, &mgmt_replace_config_data_cmd); install_element(CONFIG_NODE, &mgmt_replace_config_data_cmd);
install_element(CONFIG_NODE, &mgmt_edit_cmd); install_element(CONFIG_NODE, &mgmt_edit_cmd);
install_element(CONFIG_NODE, &mgmt_rpc_cmd);
install_element(CONFIG_NODE, &mgmt_load_config_cmd); install_element(CONFIG_NODE, &mgmt_load_config_cmd);
install_element(CONFIG_NODE, &mgmt_save_config_cmd); install_element(CONFIG_NODE, &mgmt_save_config_cmd);
install_element(CONFIG_NODE, &mgmt_rollback_cmd); install_element(CONFIG_NODE, &mgmt_rollback_cmd);

View file

@ -1233,6 +1233,23 @@ DEFPY_YANG(no_rip_distribute_list_prefix,
return nb_cli_apply_changes(vty, NULL); return nb_cli_apply_changes(vty, NULL);
} }
/*
* XPath: /frr-ripd:clear-rip-route
*/
DEFPY_YANG (clear_ip_rip,
clear_ip_rip_cmd,
"clear ip rip [vrf WORD]",
CLEAR_STR
IP_STR
"Clear IP RIP database\n"
VRF_CMD_HELP_STR)
{
if (vrf)
nb_cli_rpc_enqueue(vty, "vrf", vrf);
return nb_cli_rpc(vty, "/frr-ripd:clear-rip-route", NULL);
}
/* RIP node structure. */ /* RIP node structure. */
static struct cmd_node rip_node = { static struct cmd_node rip_node = {
.name = "rip", .name = "rip",
@ -1295,6 +1312,8 @@ void rip_cli_init(void)
install_element(INTERFACE_NODE, &ip_rip_bfd_profile_cmd); install_element(INTERFACE_NODE, &ip_rip_bfd_profile_cmd);
install_element(INTERFACE_NODE, &no_ip_rip_bfd_profile_cmd); install_element(INTERFACE_NODE, &no_ip_rip_bfd_profile_cmd);
install_element(ENABLE_NODE, &clear_ip_rip_cmd);
if_rmap_init(RIP_NODE); if_rmap_init(RIP_NODE);
} }
/* clang-format off */ /* clang-format off */

View file

@ -68,12 +68,11 @@ static void clear_rip_route(struct rip *rip)
int clear_rip_route_rpc(struct nb_cb_rpc_args *args) int clear_rip_route_rpc(struct nb_cb_rpc_args *args)
{ {
struct rip *rip; struct rip *rip;
struct yang_data *yang_vrf;
yang_vrf = yang_data_list_find(args->input, "%s/%s", args->xpath, if (args->input && yang_dnode_exists(args->input, "vrf")) {
"input/vrf"); const char *name = yang_dnode_get_string(args->input, "vrf");
if (yang_vrf) {
rip = rip_lookup_by_vrf_name(yang_vrf->value); rip = rip_lookup_by_vrf_name(name);
if (rip) if (rip)
clear_rip_route(rip); clear_rip_route(rip);
} else { } else {

View file

@ -3254,38 +3254,6 @@ DEFUN (show_ip_rip_status,
return CMD_SUCCESS; return CMD_SUCCESS;
} }
#include "ripd/ripd_clippy.c"
/*
* XPath: /frr-ripd:clear-rip-route
*/
DEFPY_YANG (clear_ip_rip,
clear_ip_rip_cmd,
"clear ip rip [vrf WORD]",
CLEAR_STR
IP_STR
"Clear IP RIP database\n"
VRF_CMD_HELP_STR)
{
struct list *input;
int ret;
input = list_new();
if (vrf) {
struct yang_data *yang_vrf;
yang_vrf = yang_data_new("/frr-ripd:clear-rip-route/input/vrf",
vrf);
listnode_add(input, yang_vrf);
}
ret = nb_cli_rpc(vty, "/frr-ripd:clear-rip-route", input, NULL);
list_delete(&input);
return ret;
}
/* Distribute-list update functions. */ /* Distribute-list update functions. */
static void rip_distribute_update(struct distribute_ctx *ctx, static void rip_distribute_update(struct distribute_ctx *ctx,
struct distribute *dist) struct distribute *dist)
@ -3660,7 +3628,6 @@ void rip_init(void)
/* Install rip commands. */ /* Install rip commands. */
install_element(VIEW_NODE, &show_ip_rip_cmd); install_element(VIEW_NODE, &show_ip_rip_cmd);
install_element(VIEW_NODE, &show_ip_rip_status_cmd); install_element(VIEW_NODE, &show_ip_rip_status_cmd);
install_element(ENABLE_NODE, &clear_ip_rip_cmd);
/* Debug related init. */ /* Debug related init. */
rip_debug_init(); rip_debug_init();

View file

@ -33,7 +33,6 @@ ripd_ripd_SOURCES = \
clippy_scan += \ clippy_scan += \
ripd/rip_bfd.c \ ripd/rip_bfd.c \
ripd/rip_cli.c \ ripd/rip_cli.c \
ripd/ripd.c \
# end # end
noinst_HEADERS += \ noinst_HEADERS += \

View file

@ -624,6 +624,23 @@ DEFPY_YANG(no_ripng_ipv6_distribute_list_prefix,
return nb_cli_apply_changes(vty, NULL); return nb_cli_apply_changes(vty, NULL);
} }
/*
* XPath: /frr-ripngd:clear-ripng-route
*/
DEFPY_YANG (clear_ipv6_rip,
clear_ipv6_rip_cmd,
"clear ipv6 ripng [vrf WORD]",
CLEAR_STR
IPV6_STR
"Clear IPv6 RIP database\n"
VRF_CMD_HELP_STR)
{
if (vrf)
nb_cli_rpc_enqueue(vty, "vrf", vrf);
return nb_cli_rpc(vty, "/frr-ripngd:clear-ripng-route", NULL);
}
/* RIPng node structure. */ /* RIPng node structure. */
static struct cmd_node cmd_ripng_node = { static struct cmd_node cmd_ripng_node = {
.name = "ripng", .name = "ripng",
@ -663,6 +680,8 @@ void ripng_cli_init(void)
install_element(INTERFACE_NODE, &ipv6_ripng_split_horizon_cmd); install_element(INTERFACE_NODE, &ipv6_ripng_split_horizon_cmd);
install_element(ENABLE_NODE, &clear_ipv6_rip_cmd);
if_rmap_init(RIPNG_NODE); if_rmap_init(RIPNG_NODE);
} }

View file

@ -70,12 +70,11 @@ static void clear_ripng_route(struct ripng *ripng)
int clear_ripng_route_rpc(struct nb_cb_rpc_args *args) int clear_ripng_route_rpc(struct nb_cb_rpc_args *args)
{ {
struct ripng *ripng; struct ripng *ripng;
struct yang_data *yang_vrf;
yang_vrf = yang_data_list_find(args->input, "%s/%s", args->xpath, if (args->input && yang_dnode_exists(args->input, "vrf")) {
"input/vrf"); const char *name = yang_dnode_get_string(args->input, "vrf");
if (yang_vrf) {
ripng = ripng_lookup_by_vrf_name(yang_vrf->value); ripng = ripng_lookup_by_vrf_name(name);
if (ripng) if (ripng)
clear_ripng_route(ripng); clear_ripng_route(ripng);
} else { } else {

View file

@ -2231,38 +2231,6 @@ DEFUN (show_ipv6_ripng_status,
return CMD_SUCCESS; return CMD_SUCCESS;
} }
#include "ripngd/ripngd_clippy.c"
/*
* XPath: /frr-ripngd:clear-ripng-route
*/
DEFPY_YANG (clear_ipv6_rip,
clear_ipv6_rip_cmd,
"clear ipv6 ripng [vrf WORD]",
CLEAR_STR
IPV6_STR
"Clear IPv6 RIP database\n"
VRF_CMD_HELP_STR)
{
struct list *input;
int ret;
input = list_new();
if (vrf) {
struct yang_data *yang_vrf;
yang_vrf = yang_data_new(
"/frr-ripngd:clear-ripng-route/input/vrf", vrf);
listnode_add(input, yang_vrf);
}
ret = nb_cli_rpc(vty, "/frr-ripngd:clear-ripng-route", input, NULL);
list_delete(&input);
return ret;
}
/* Update ECMP routes to zebra when ECMP is disabled. */ /* Update ECMP routes to zebra when ECMP is disabled. */
void ripng_ecmp_disable(struct ripng *ripng) void ripng_ecmp_disable(struct ripng *ripng)
{ {
@ -2680,7 +2648,6 @@ void ripng_init(void)
/* Install ripng commands. */ /* Install ripng commands. */
install_element(VIEW_NODE, &show_ipv6_ripng_cmd); install_element(VIEW_NODE, &show_ipv6_ripng_cmd);
install_element(VIEW_NODE, &show_ipv6_ripng_status_cmd); install_element(VIEW_NODE, &show_ipv6_ripng_status_cmd);
install_element(ENABLE_NODE, &clear_ipv6_rip_cmd);
ripng_if_init(); ripng_if_init();
ripng_debug_init(); ripng_debug_init();

View file

@ -27,7 +27,6 @@ ripngd_ripngd_SOURCES = \
clippy_scan += \ clippy_scan += \
ripngd/ripng_cli.c \ ripngd/ripng_cli.c \
ripngd/ripngd.c \
# end # end
noinst_HEADERS += \ noinst_HEADERS += \

View file

@ -14,6 +14,7 @@
#include "lib_vty.h" #include "lib_vty.h"
#include "log.h" #include "log.h"
#include "northbound.h" #include "northbound.h"
#include "northbound_cli.h"
static struct event_loop *master; static struct event_loop *master;
@ -200,6 +201,19 @@ static struct yang_data *frr_test_module_vrfs_vrf_routes_route_active_get_elem(
return NULL; return NULL;
} }
/*
* XPath: /frr-test-module:frr-test-module/vrfs/vrf/ping
*/
static int frr_test_module_vrfs_vrf_ping(struct nb_cb_rpc_args *args)
{
const char *vrf = yang_dnode_get_string(args->input, "../name");
const char *data = yang_dnode_get_string(args->input, "data");
yang_dnode_rpc_output_add(args->output, "vrf", vrf);
yang_dnode_rpc_output_add(args->output, "data-out", data);
return NB_OK;
}
/* /*
* XPath: /frr-test-module:frr-test-module/c1value * XPath: /frr-test-module:frr-test-module/c1value
@ -262,6 +276,10 @@ const struct frr_yang_module_info frr_test_module_info = {
.xpath = "/frr-test-module:frr-test-module/vrfs/vrf/routes/route/active", .xpath = "/frr-test-module:frr-test-module/vrfs/vrf/routes/route/active",
.cbs.get_elem = frr_test_module_vrfs_vrf_routes_route_active_get_elem, .cbs.get_elem = frr_test_module_vrfs_vrf_routes_route_active_get_elem,
}, },
{
.xpath = "/frr-test-module:frr-test-module/vrfs/vrf/ping",
.cbs.rpc = frr_test_module_vrfs_vrf_ping,
},
{ {
.xpath = "/frr-test-module:frr-test-module/c1value", .xpath = "/frr-test-module:frr-test-module/c1value",
.cbs.get_elem = frr_test_module_c1value_get_elem, .cbs.get_elem = frr_test_module_c1value_get_elem,
@ -277,6 +295,33 @@ const struct frr_yang_module_info frr_test_module_info = {
}; };
/* clang-format on */ /* clang-format on */
DEFUN(test_rpc, test_rpc_cmd, "test rpc",
"Test\n"
"RPC\n")
{
struct lyd_node *output = NULL;
char xpath[XPATH_MAXLEN];
int ret;
snprintf(xpath, sizeof(xpath),
"/frr-test-module:frr-test-module/vrfs/vrf[name='testname']/ping");
nb_cli_rpc_enqueue(vty, "data", "testdata");
ret = nb_cli_rpc(vty, xpath, &output);
if (ret != CMD_SUCCESS) {
vty_out(vty, "RPC failed\n");
return ret;
}
vty_out(vty, "vrf %s data %s\n", yang_dnode_get_string(output, "vrf"),
yang_dnode_get_string(output, "data-out"));
yang_dnode_free(output);
return CMD_SUCCESS;
}
static const struct frr_yang_module_info *const modules[] = { static const struct frr_yang_module_info *const modules[] = {
&frr_test_module_info, &frr_test_module_info,
}; };
@ -416,6 +461,8 @@ int main(int argc, char **argv)
lib_cmd_init(); lib_cmd_init();
nb_init(master, modules, array_size(modules), false); nb_init(master, modules, array_size(modules), false);
install_element(ENABLE_NODE, &test_rpc_cmd);
/* Create artificial data. */ /* Create artificial data. */
create_data(num_vrfs, num_interfaces, num_routes); create_data(num_vrfs, num_interfaces, num_routes);

View file

@ -1 +1,2 @@
show yang operational-data /frr-test-module:frr-test-module show yang operational-data /frr-test-module:frr-test-module
test rpc

View file

@ -119,5 +119,7 @@ test# show yang operational-data /frr-test-module:frr-test-module
} }
} }
} }
test# test rpc
vrf testname data testdata
test# test#
end. end.

View file

View file

@ -0,0 +1,74 @@
# -*- coding: utf-8 eval: (blacken-mode 1) -*-
# SPDX-License-Identifier: ISC
#
# March 21 2024, Igor Ryzhov <iryzhov@nfware.com>
#
# Copyright (c) 2024, NFWare Inc.
#
"""
Test YANG Notifications
"""
import json
import os
import threading
import pytest
from lib.topogen import Topogen
from lib.topotest import json_cmp
pytestmark = [pytest.mark.ripd, pytest.mark.mgmtd]
CWD = os.path.dirname(os.path.realpath(__file__))
@pytest.fixture(scope="module")
def tgen(request):
"Setup/Teardown the environment and provide tgen argument to tests"
topodef = {"s1": ("r1",)}
tgen = Topogen(topodef, request.module.__name__)
tgen.start_topology()
router_list = tgen.routers()
for rname, router in router_list.items():
router.load_frr_config("frr.conf")
tgen.start_router()
yield tgen
tgen.stop_topology()
def test_backend_rpc(tgen):
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
r1 = tgen.gears["r1"]
be_client_path = "/usr/lib/frr/mgmtd_testc"
rc, _, _ = r1.net.cmd_status(be_client_path + " --help")
if rc:
pytest.skip("No mgmtd_testc")
out = []
def run_testc():
output = r1.net.cmd_raises(
be_client_path + " --timeout 10 --log file:mgmt_testc.log"
)
out.append(json.loads(output))
t = threading.Thread(target=run_testc)
t.start()
r1.vtysh_cmd("clear ip rip vrf testname")
t.join()
jsout = out[0]
expected = {"frr-ripd:clear-rip-route": {"vrf": "testname"}}
result = json_cmp(jsout, expected)
assert result is None

View file

@ -80,6 +80,23 @@ module frr-test-module {
} }
} }
} }
action ping {
input {
leaf data {
type string;
}
}
output {
leaf vrf {
type string;
}
// can't use the same name in input and output
// because of a bug in libyang < 2.1.148
leaf data-out {
type string;
}
}
}
} }
} }
choice achoice { choice achoice {

View file

@ -20,48 +20,30 @@ int clear_evpn_dup_addr_rpc(struct nb_cb_rpc_args *args)
{ {
struct zebra_vrf *zvrf; struct zebra_vrf *zvrf;
int ret = NB_OK; int ret = NB_OK;
struct yang_data *yang_dup_choice = NULL, *yang_dup_vni = NULL,
*yang_dup_ip = NULL, *yang_dup_mac = NULL;
yang_dup_choice = yang_data_list_find(args->input, "%s/%s", args->xpath,
"input/clear-dup-choice");
zvrf = zebra_vrf_get_evpn(); zvrf = zebra_vrf_get_evpn();
if (yang_dup_choice if (yang_dnode_exists(args->input, "all-vnis")) {
&& strcmp(yang_dup_choice->value, "all-case") == 0) {
zebra_vxlan_clear_dup_detect_vni_all(zvrf); zebra_vxlan_clear_dup_detect_vni_all(zvrf);
} else { } else {
vni_t vni; vni_t vni = yang_dnode_get_uint32(args->input, "vni-id");
struct ipaddr host_ip = {.ipa_type = IPADDR_NONE}; struct ipaddr host_ip = {.ipa_type = IPADDR_NONE};
struct ethaddr mac; struct ethaddr mac;
yang_dup_vni = yang_data_list_find( if (yang_dnode_exists(args->input, "mac-addr")) {
args->input, "%s/%s", args->xpath, yang_dnode_get_mac(&mac, args->input, "mac-addr");
"input/clear-dup-choice/single-case/vni-id"); ret = zebra_vxlan_clear_dup_detect_vni_mac(zvrf, vni,
if (yang_dup_vni) { &mac,
vni = yang_str2uint32(yang_dup_vni->value); args->errmsg,
args->errmsg_len);
yang_dup_mac = yang_data_list_find( } else if (yang_dnode_exists(args->input, "vni-ipaddr")) {
args->input, "%s/%s", args->xpath, yang_dnode_get_ip(&host_ip, args->input, "vni-ipaddr");
"input/clear-dup-choice/single-case/vni-id/mac-addr"); ret = zebra_vxlan_clear_dup_detect_vni_ip(zvrf, vni,
yang_dup_ip = yang_data_list_find( &host_ip,
args->input, "%s/%s", args->xpath, args->errmsg,
"input/clear-dup-choice/single-case/vni-id/vni-ipaddr"); args->errmsg_len);
} else {
if (yang_dup_mac) { ret = zebra_vxlan_clear_dup_detect_vni(zvrf, vni);
yang_str2mac(yang_dup_mac->value, &mac);
ret = zebra_vxlan_clear_dup_detect_vni_mac(
zvrf, vni, &mac, args->errmsg,
args->errmsg_len);
} else if (yang_dup_ip) {
yang_str2ip(yang_dup_ip->value, &host_ip);
ret = zebra_vxlan_clear_dup_detect_vni_ip(
zvrf, vni, &host_ip, args->errmsg,
args->errmsg_len);
} else
ret = zebra_vxlan_clear_dup_detect_vni(zvrf,
vni);
} }
} }
if (ret < 0) if (ret < 0)

View file

@ -3553,56 +3553,17 @@ DEFPY (clear_evpn_dup_addr,
"IPv4 address\n" "IPv4 address\n"
"IPv6 address\n") "IPv6 address\n")
{ {
struct ipaddr host_ip = {.ipa_type = IPADDR_NONE };
int ret = CMD_SUCCESS;
struct list *input;
struct yang_data *yang_dup = NULL, *yang_dup_ip = NULL,
*yang_dup_mac = NULL;
input = list_new();
if (!vni_str) { if (!vni_str) {
yang_dup = yang_data_new( nb_cli_rpc_enqueue(vty, "all-vnis", NULL);
"/frr-zebra:clear-evpn-dup-addr/input/clear-dup-choice",
"all-case");
} else { } else {
yang_dup = yang_data_new_uint32( nb_cli_rpc_enqueue(vty, "vni-id", vni_str);
"/frr-zebra:clear-evpn-dup-addr/input/clear-dup-choice/single-case/vni-id", if (mac_str)
vni); nb_cli_rpc_enqueue(vty, "mac-addr", mac_str);
if (!is_zero_mac(&mac->eth_addr)) { else if (ip_str)
yang_dup_mac = yang_data_new_mac( nb_cli_rpc_enqueue(vty, "vni-ipaddr", ip_str);
"/frr-zebra:clear-evpn-dup-addr/input/clear-dup-choice/single-case/vni-id/mac-addr",
&mac->eth_addr);
if (yang_dup_mac)
listnode_add(input, yang_dup_mac);
} else if (ip) {
if (sockunion_family(ip) == AF_INET) {
host_ip.ipa_type = IPADDR_V4;
host_ip.ipaddr_v4.s_addr = sockunion2ip(ip);
} else {
host_ip.ipa_type = IPADDR_V6;
memcpy(&host_ip.ipaddr_v6, &ip->sin6.sin6_addr,
sizeof(struct in6_addr));
}
yang_dup_ip = yang_data_new_ip(
"/frr-zebra:clear-evpn-dup-addr/input/clear-dup-choice/single-case/vni-id/vni-ipaddr",
&host_ip);
if (yang_dup_ip)
listnode_add(input, yang_dup_ip);
}
} }
if (yang_dup) { return nb_cli_rpc(vty, "/frr-zebra:clear-evpn-dup-addr", NULL);
listnode_add(input, yang_dup);
ret = nb_cli_rpc(vty, "/frr-zebra:clear-evpn-dup-addr", input,
NULL);
}
list_delete(&input);
return ret;
} }
DEFPY_HIDDEN (evpn_accept_bgp_seq, DEFPY_HIDDEN (evpn_accept_bgp_seq,