diff options
author | Igor Ryzhov <iryzhov@nfware.com> | 2024-03-18 23:49:19 +0100 |
---|---|---|
committer | Igor Ryzhov <iryzhov@nfware.com> | 2024-04-22 15:36:22 +0200 |
commit | 5c3e95d422d487249ee3c6405ac2cddfa69af394 (patch) | |
tree | 50677fa499f78cca631215431b6c564eb9698b60 /lib | |
parent | tests: add test for NB RPC callback (diff) | |
download | frr-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.c | 143 | ||||
-rw-r--r-- | lib/mgmt_be_client.h | 2 | ||||
-rw-r--r-- | lib/mgmt_msg_native.c | 2 | ||||
-rw-r--r-- | lib/mgmt_msg_native.h | 40 | ||||
-rw-r--r-- | lib/yang.c | 57 | ||||
-rw-r--r-- | lib/yang.h | 19 |
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: |