summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorIgor Ryzhov <iryzhov@nfware.com>2024-03-18 23:49:19 +0100
committerIgor Ryzhov <iryzhov@nfware.com>2024-04-22 15:36:22 +0200
commit5c3e95d422d487249ee3c6405ac2cddfa69af394 (patch)
tree50677fa499f78cca631215431b6c564eb9698b60 /lib
parenttests: add test for NB RPC callback (diff)
downloadfrr-5c3e95d422d487249ee3c6405ac2cddfa69af394.tar.xz
frr-5c3e95d422d487249ee3c6405ac2cddfa69af394.zip
lib: add native RPC processing to mgmt backend client
Signed-off-by: Igor Ryzhov <iryzhov@nfware.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/mgmt_be_client.c143
-rw-r--r--lib/mgmt_be_client.h2
-rw-r--r--lib/mgmt_msg_native.c2
-rw-r--r--lib/mgmt_msg_native.h40
-rw-r--r--lib/yang.c57
-rw-r--r--lib/yang.h19
6 files changed, 263 insertions, 0 deletions
diff --git a/lib/mgmt_be_client.c b/lib/mgmt_be_client.c
index f483d48d8..6e2fb05e8 100644
--- a/lib/mgmt_be_client.c
+++ b/lib/mgmt_be_client.c
@@ -915,6 +915,143 @@ static void be_client_handle_get_tree(struct mgmt_be_client *client,
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.
*/
@@ -975,6 +1112,9 @@ static void be_client_handle_native_msg(struct mgmt_be_client *client,
case MGMT_MSG_CODE_GET_TREE:
be_client_handle_get_tree(client, txn_id, msg, msg_len);
break;
+ case MGMT_MSG_CODE_RPC:
+ be_client_handle_rpc(client, txn_id, msg, msg_len);
+ break;
case MGMT_MSG_CODE_NOTIFY:
be_client_handle_notify(client, msg, msg_len);
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.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);
be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REQ;
be_msg.subscr_req = &subscr_req;
diff --git a/lib/mgmt_be_client.h b/lib/mgmt_be_client.h
index cd8b23752..7ad0589bd 100644
--- a/lib/mgmt_be_client.h
+++ b/lib/mgmt_be_client.h
@@ -75,6 +75,8 @@ struct mgmt_be_client_cbs {
const char **notif_xpaths;
uint nnotif_xpaths;
+ const char **rpc_xpaths;
+ uint nrpc_xpaths;
};
/***************************************************************
diff --git a/lib/mgmt_msg_native.c b/lib/mgmt_msg_native.c
index 09ea43ece..39ce9abae 100644
--- a/lib/mgmt_msg_native.c
+++ b/lib/mgmt_msg_native.c
@@ -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_EDIT, "native edit 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,
uint64_t req_id, bool short_circuit_ok,
diff --git a/lib/mgmt_msg_native.h b/lib/mgmt_msg_native.h
index b7c29862a..342f6f04c 100644
--- a/lib/mgmt_msg_native.h
+++ b/lib/mgmt_msg_native.h
@@ -152,6 +152,8 @@ DECLARE_MTYPE(MSG_NATIVE_GET_DATA);
DECLARE_MTYPE(MSG_NATIVE_NOTIFY);
DECLARE_MTYPE(MSG_NATIVE_EDIT);
DECLARE_MTYPE(MSG_NATIVE_EDIT_REPLY);
+DECLARE_MTYPE(MSG_NATIVE_RPC);
+DECLARE_MTYPE(MSG_NATIVE_RPC_REPLY);
/*
* Native message codes
@@ -163,6 +165,8 @@ DECLARE_MTYPE(MSG_NATIVE_EDIT_REPLY);
#define MGMT_MSG_CODE_NOTIFY 4
#define MGMT_MSG_CODE_EDIT 5
#define MGMT_MSG_CODE_EDIT_REPLY 6
+#define MGMT_MSG_CODE_RPC 7
+#define MGMT_MSG_CODE_RPC_REPLY 8
/*
* Datastores
@@ -377,6 +381,42 @@ _Static_assert(sizeof(struct mgmt_msg_edit_reply) ==
offsetof(struct mgmt_msg_edit_reply, data),
"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
*/
diff --git a/lib/yang.c b/lib/yang.c
index f506d8bf2..06d29bb9c 100644
--- a/lib/yang.c
+++ b/lib/yang.c
@@ -774,6 +774,63 @@ LY_ERR yang_parse_notification(const char *xpath, LYD_FORMAT format,
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)
{
uint8_t *dst = darr_append_n(*(uint8_t **)arg, count);
diff --git a/lib/yang.h b/lib/yang.h
index 1903079d1..57131f478 100644
--- a/lib/yang.h
+++ b/lib/yang.h
@@ -635,6 +635,25 @@ extern LY_ERR yang_parse_notification(const char *xpath, LYD_FORMAT format,
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.
*
* Args: