forked from Mirror/frr
Merge pull request #15594 from idryzhov/mgmt-rpc
mgmtd: implement YANG RPC/action support
This commit is contained in:
commit
c54bc7a8dd
|
@ -79,6 +79,7 @@ message BeSubscribeReq {
|
|||
repeated string config_xpaths = 2;
|
||||
repeated string oper_xpaths = 3;
|
||||
repeated string notif_xpaths = 4;
|
||||
repeated string rpc_xpaths = 5;
|
||||
}
|
||||
|
||||
message BeSubscribeReply {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -75,6 +75,8 @@ struct mgmt_be_client_cbs {
|
|||
|
||||
const char **notif_xpaths;
|
||||
uint nnotif_xpaths;
|
||||
const char **rpc_xpaths;
|
||||
uint nrpc_xpaths;
|
||||
};
|
||||
|
||||
/***************************************************************
|
||||
|
|
|
@ -360,6 +360,33 @@ int mgmt_fe_send_edit_req(struct mgmt_fe_client *client, uint64_t session_id,
|
|||
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,
|
||||
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_tree_data *tree_msg;
|
||||
struct mgmt_msg_edit_reply *edit_msg;
|
||||
struct mgmt_msg_rpc_reply *rpc_msg;
|
||||
struct mgmt_msg_error *err_msg;
|
||||
const char *xpath = 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,
|
||||
xpath);
|
||||
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:
|
||||
if (!session->client->cbs.async_notification)
|
||||
return;
|
||||
|
|
|
@ -120,6 +120,12 @@ struct mgmt_fe_client_cbs {
|
|||
uintptr_t session_ctx, uint64_t req_id,
|
||||
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 */
|
||||
int (*async_notification)(struct mgmt_fe_client *client,
|
||||
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,
|
||||
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.
|
||||
*/
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
@ -569,7 +609,10 @@ extern int vmgmt_msg_native_send_error(struct msg_conn *conn,
|
|||
const char *__s = NULL; \
|
||||
if (msg->vsplit && msg->vsplit <= __len && \
|
||||
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; \
|
||||
|
|
|
@ -932,14 +932,22 @@ static int nb_candidate_edit_tree_add(struct nb_config *candidate,
|
|||
/* if replace failed, restore the original node */
|
||||
if (existing) {
|
||||
if (root) {
|
||||
/* Restoring the whole config. */
|
||||
candidate->dnode = existing;
|
||||
} else if (ex_parent) {
|
||||
/*
|
||||
* Restoring a nested node. Insert it as a
|
||||
* child.
|
||||
*/
|
||||
lyd_insert_child(ex_parent, existing);
|
||||
} else {
|
||||
if (ex_parent)
|
||||
lyd_insert_child(ex_parent, existing);
|
||||
else
|
||||
lyd_insert_sibling(candidate->dnode,
|
||||
existing,
|
||||
&candidate->dnode);
|
||||
/*
|
||||
* Restoring a top-level node. Insert it as a
|
||||
* sibling to candidate->dnode to make sure
|
||||
* the linkage is correct.
|
||||
*/
|
||||
lyd_insert_sibling(candidate->dnode, existing,
|
||||
&candidate->dnode);
|
||||
}
|
||||
}
|
||||
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,
|
||||
const struct list *input, struct list *output, char *errmsg,
|
||||
size_t errmsg_len)
|
||||
const struct lyd_node *input, struct lyd_node *output,
|
||||
char *errmsg, size_t errmsg_len)
|
||||
{
|
||||
struct nb_cb_rpc_args args = {};
|
||||
|
||||
if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS))
|
||||
return 0;
|
||||
|
||||
DEBUGD(&nb_dbg_cbs_rpc, "northbound RPC: %s", xpath);
|
||||
|
||||
args.xpath = xpath;
|
||||
|
|
|
@ -274,11 +274,11 @@ struct nb_cb_rpc_args {
|
|||
/* XPath of the YANG RPC or action. */
|
||||
const char *xpath;
|
||||
|
||||
/* Read-only list of input parameters. */
|
||||
const struct list *input;
|
||||
/* Read-only "input" tree of the RPC/action. */
|
||||
const struct lyd_node *input;
|
||||
|
||||
/* List of output parameters to be populated by the callback. */
|
||||
struct list *output;
|
||||
/* The "output" tree of the RPC/action to be populated by the callback. */
|
||||
struct lyd_node *output;
|
||||
|
||||
/* Buffer to store human-readable error message in case of error. */
|
||||
char *errmsg;
|
||||
|
@ -833,7 +833,7 @@ extern const void *nb_callback_lookup_next(const struct nb_node *nb_node,
|
|||
const void *parent_list_entry,
|
||||
const struct yang_list_keys *keys);
|
||||
extern int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath,
|
||||
const struct list *input, struct list *output,
|
||||
const struct lyd_node *input, struct lyd_node *output,
|
||||
char *errmsg, size_t errmsg_len);
|
||||
extern void nb_callback_notify(const struct nb_node *nb_node, const char *xpath,
|
||||
struct lyd_node *dnode);
|
||||
|
|
|
@ -275,10 +275,31 @@ int nb_cli_apply_changes_clear_pending(struct vty *vty,
|
|||
return nb_cli_apply_changes_internal(vty, xpath_base_abs, true);
|
||||
}
|
||||
|
||||
int nb_cli_rpc(struct vty *vty, const char *xpath, struct list *input,
|
||||
struct list *output)
|
||||
int nb_cli_rpc_enqueue(struct vty *vty, const char *xpath, const char *value)
|
||||
{
|
||||
struct nb_cfg_change *param;
|
||||
|
||||
if (vty->num_rpc_params == VTY_MAXCFGCHANGES) {
|
||||
/* Not expected to happen. */
|
||||
vty_out(vty,
|
||||
"%% Exceeded the maximum number of params (%u) for a single command\n\n",
|
||||
VTY_MAXCFGCHANGES);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
param = &vty->rpc_params[vty->num_rpc_params++];
|
||||
strlcpy(param->xpath, xpath, sizeof(param->xpath));
|
||||
param->value = value;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
int nb_cli_rpc(struct vty *vty, const char *xpath, struct lyd_node **output_p)
|
||||
{
|
||||
struct nb_node *nb_node;
|
||||
struct lyd_node *input = NULL;
|
||||
struct lyd_node *output = NULL;
|
||||
LY_ERR err;
|
||||
int ret;
|
||||
char errmsg[BUFSIZ] = {0};
|
||||
|
||||
|
@ -289,12 +310,62 @@ int nb_cli_rpc(struct vty *vty, const char *xpath, struct list *input,
|
|||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
/* create input tree */
|
||||
err = lyd_new_path2(NULL, ly_native_ctx, xpath, NULL, 0, 0, 0, NULL,
|
||||
&input);
|
||||
assert(err == LY_SUCCESS);
|
||||
|
||||
for (size_t i = 0; i < vty->num_rpc_params; i++) {
|
||||
err = lyd_new_path(input, ly_native_ctx,
|
||||
vty->rpc_params[i].xpath,
|
||||
vty->rpc_params[i].value, 0, NULL);
|
||||
assert(err == LY_SUCCESS);
|
||||
}
|
||||
|
||||
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,
|
||||
sizeof(errmsg));
|
||||
|
||||
/* validate output tree to create implicit defaults */
|
||||
err = lyd_validate_op(output, NULL, LYD_TYPE_REPLY_YANG, NULL);
|
||||
assert(err == LY_SUCCESS);
|
||||
|
||||
lyd_free_all(input);
|
||||
vty->num_rpc_params = 0;
|
||||
|
||||
switch (ret) {
|
||||
case NB_OK:
|
||||
if (output_p)
|
||||
*output_p = output;
|
||||
else
|
||||
lyd_free_all(output);
|
||||
return CMD_SUCCESS;
|
||||
default:
|
||||
lyd_free_all(output);
|
||||
if (strlen(errmsg))
|
||||
vty_show_nb_errors(vty, ret, errmsg);
|
||||
return CMD_WARNING;
|
||||
|
|
|
@ -80,7 +80,23 @@ extern int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt,
|
|||
...) PRINTFRR(2, 3);
|
||||
|
||||
/*
|
||||
* Execute a YANG RPC or Action.
|
||||
* Add an input child node for an RPC or an action.
|
||||
*
|
||||
* vty
|
||||
* The vty context.
|
||||
*
|
||||
* xpath
|
||||
* XPath of the child being added, relative to the input container.
|
||||
*
|
||||
* value
|
||||
* Value of the child being added. Can be NULL for containers and leafs of
|
||||
* type 'empty'.
|
||||
*/
|
||||
extern int nb_cli_rpc_enqueue(struct vty *vty, const char *xpath,
|
||||
const char *value);
|
||||
|
||||
/*
|
||||
* Execute a YANG RPC or Action using the enqueued input parameters.
|
||||
*
|
||||
* vty
|
||||
* The vty terminal to dump any error.
|
||||
|
@ -88,20 +104,16 @@ extern int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt,
|
|||
* xpath
|
||||
* XPath of the YANG RPC or Action node.
|
||||
*
|
||||
* input
|
||||
* List of 'yang_data' structures containing the RPC input parameters. It
|
||||
* can be set to NULL when there are no input parameters.
|
||||
*
|
||||
* output
|
||||
* List of 'yang_data' structures used to retrieve the RPC output parameters.
|
||||
* It can be set to NULL when it's known that the given YANG RPC or Action
|
||||
* doesn't have any output parameters.
|
||||
* output_p
|
||||
* A pointer to the libyang data node that will hold the output data tree.
|
||||
* It can be set to NULL if the caller is not interested in processing the
|
||||
* output. The caller is responsible for freeing the output data tree.
|
||||
*
|
||||
* Returns:
|
||||
* CMD_SUCCESS on success, CMD_WARNING otherwise.
|
||||
*/
|
||||
extern int nb_cli_rpc(struct vty *vty, const char *xpath, struct list *input,
|
||||
struct list *output);
|
||||
extern int nb_cli_rpc(struct vty *vty, const char *xpath,
|
||||
struct lyd_node **output_p);
|
||||
|
||||
/*
|
||||
* Show CLI commands associated to the given YANG data node.
|
||||
|
|
|
@ -1011,12 +1011,11 @@ grpc::Status HandleUnaryExecute(
|
|||
grpc_debug("%s: entered", __func__);
|
||||
|
||||
struct nb_node *nb_node;
|
||||
struct list *input_list;
|
||||
struct list *output_list;
|
||||
struct listnode *node;
|
||||
struct yang_data *data;
|
||||
struct lyd_node *input_tree, *output_tree, *child;
|
||||
const char *xpath;
|
||||
char errmsg[BUFSIZ] = {0};
|
||||
char path[XPATH_MAXLEN];
|
||||
LY_ERR err;
|
||||
|
||||
// Request: string path = 1;
|
||||
xpath = tag->request.path().c_str();
|
||||
|
@ -1032,40 +1031,66 @@ grpc::Status HandleUnaryExecute(
|
|||
return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
|
||||
"Unknown data path");
|
||||
|
||||
input_list = yang_data_list_new();
|
||||
output_list = yang_data_list_new();
|
||||
// Create input data tree.
|
||||
err = lyd_new_path2(NULL, ly_native_ctx, xpath, NULL, 0,
|
||||
(LYD_ANYDATA_VALUETYPE)0, 0, NULL, &input_tree);
|
||||
if (err != LY_SUCCESS) {
|
||||
return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
|
||||
"Invalid data path");
|
||||
}
|
||||
|
||||
// Read input parameters.
|
||||
auto input = tag->request.input();
|
||||
for (const frr::PathValue &pv : input) {
|
||||
// Request: repeated PathValue input = 2;
|
||||
data = yang_data_new(pv.path().c_str(), pv.value().c_str());
|
||||
listnode_add(input_list, data);
|
||||
err = lyd_new_path(input_tree, ly_native_ctx, pv.path().c_str(),
|
||||
pv.value().c_str(), 0, NULL);
|
||||
if (err != LY_SUCCESS) {
|
||||
lyd_free_tree(input_tree);
|
||||
return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
|
||||
"Invalid input data");
|
||||
}
|
||||
}
|
||||
|
||||
// Validate input data.
|
||||
err = lyd_validate_op(input_tree, NULL, LYD_TYPE_RPC_YANG, NULL);
|
||||
if (err != LY_SUCCESS) {
|
||||
lyd_free_tree(input_tree);
|
||||
return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
|
||||
"Invalid input data");
|
||||
}
|
||||
|
||||
// Create output data tree.
|
||||
err = lyd_new_path2(NULL, ly_native_ctx, xpath, NULL, 0,
|
||||
(LYD_ANYDATA_VALUETYPE)0, 0, NULL, &output_tree);
|
||||
if (err != LY_SUCCESS) {
|
||||
lyd_free_tree(input_tree);
|
||||
return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
|
||||
"Invalid data path");
|
||||
}
|
||||
|
||||
// Execute callback registered for this XPath.
|
||||
if (nb_callback_rpc(nb_node, xpath, input_list, output_list, errmsg,
|
||||
sizeof(errmsg))
|
||||
!= NB_OK) {
|
||||
if (nb_callback_rpc(nb_node, xpath, input_tree, output_tree, errmsg,
|
||||
sizeof(errmsg)) != NB_OK) {
|
||||
flog_warn(EC_LIB_NB_CB_RPC, "%s: rpc callback failed: %s",
|
||||
__func__, xpath);
|
||||
list_delete(&input_list);
|
||||
list_delete(&output_list);
|
||||
lyd_free_tree(input_tree);
|
||||
lyd_free_tree(output_tree);
|
||||
|
||||
return grpc::Status(grpc::StatusCode::INTERNAL, "RPC failed");
|
||||
}
|
||||
|
||||
// Process output parameters.
|
||||
for (ALL_LIST_ELEMENTS_RO(output_list, node, data)) {
|
||||
LY_LIST_FOR (lyd_child(output_tree), child) {
|
||||
// Response: repeated PathValue output = 1;
|
||||
frr::PathValue *pv = tag->response.add_output();
|
||||
pv->set_path(data->xpath);
|
||||
pv->set_value(data->value);
|
||||
pv->set_path(lyd_path(child, LYD_PATH_STD, path, sizeof(path)));
|
||||
pv->set_value(yang_dnode_get_string(child, NULL));
|
||||
}
|
||||
|
||||
// Release memory.
|
||||
list_delete(&input_list);
|
||||
list_delete(&output_list);
|
||||
lyd_free_tree(input_tree);
|
||||
lyd_free_tree(output_tree);
|
||||
|
||||
return grpc::Status::OK;
|
||||
}
|
||||
|
|
|
@ -377,16 +377,11 @@ static int frr_sr_state_cb(sr_session_ctx_t *session, uint32_t sub_id,
|
|||
return SR_ERR_OK;
|
||||
}
|
||||
static int frr_sr_config_rpc_cb(sr_session_ctx_t *session, uint32_t sub_id,
|
||||
const char *xpath, const sr_val_t *sr_input,
|
||||
const size_t input_cnt, sr_event_t sr_ev,
|
||||
uint32_t request_id, sr_val_t **sr_output,
|
||||
size_t *sr_output_cnt, void *private_ctx)
|
||||
const char *xpath, const struct lyd_node *input,
|
||||
sr_event_t sr_ev, uint32_t request_id,
|
||||
struct lyd_node *output, void *private_ctx)
|
||||
{
|
||||
struct nb_node *nb_node;
|
||||
struct list *input;
|
||||
struct list *output;
|
||||
struct yang_data *data;
|
||||
size_t cb_output_cnt;
|
||||
int ret = SR_ERR_OK;
|
||||
char errmsg[BUFSIZ] = {0};
|
||||
|
||||
|
@ -397,19 +392,6 @@ static int frr_sr_config_rpc_cb(sr_session_ctx_t *session, uint32_t sub_id,
|
|||
return SR_ERR_INTERNAL;
|
||||
}
|
||||
|
||||
input = yang_data_list_new();
|
||||
output = yang_data_list_new();
|
||||
|
||||
/* Process input. */
|
||||
for (size_t i = 0; i < input_cnt; i++) {
|
||||
char value_str[YANG_VALUE_MAXLEN];
|
||||
|
||||
sr_val_to_buff(&sr_input[i], value_str, sizeof(value_str));
|
||||
|
||||
data = yang_data_new(xpath, value_str);
|
||||
listnode_add(input, data);
|
||||
}
|
||||
|
||||
/* Execute callback registered for this XPath. */
|
||||
if (nb_callback_rpc(nb_node, xpath, input, output, errmsg,
|
||||
sizeof(errmsg))
|
||||
|
@ -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",
|
||||
__func__, xpath);
|
||||
ret = SR_ERR_OPERATION_FAILED;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Process output. */
|
||||
if (listcount(output) > 0) {
|
||||
sr_val_t *values = NULL;
|
||||
struct listnode *node;
|
||||
int i = 0;
|
||||
|
||||
cb_output_cnt = listcount(output);
|
||||
ret = sr_new_values(cb_output_cnt, &values);
|
||||
if (ret != SR_ERR_OK) {
|
||||
flog_err(EC_LIB_LIBSYSREPO, "%s: sr_new_values(): %s",
|
||||
__func__, sr_strerror(ret));
|
||||
goto exit;
|
||||
}
|
||||
|
||||
for (ALL_LIST_ELEMENTS_RO(output, node, data)) {
|
||||
if (yang_data_frr2sr(data, &values[i++]) != 0) {
|
||||
flog_err(
|
||||
EC_LIB_SYSREPO_DATA_CONVERT,
|
||||
"%s: failed to convert data to Sysrepo format",
|
||||
__func__);
|
||||
ret = SR_ERR_INTERNAL;
|
||||
sr_free_values(values, cb_output_cnt);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
*sr_output = values;
|
||||
*sr_output_cnt = cb_output_cnt;
|
||||
}
|
||||
|
||||
exit:
|
||||
/* Release memory. */
|
||||
list_delete(&input);
|
||||
list_delete(&output);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -579,8 +525,9 @@ static int frr_sr_subscribe_rpc(const struct lysc_node *snode, void *arg)
|
|||
DEBUGD(&nb_dbg_client_sysrepo, "sysrepo: providing RPC to '%s'",
|
||||
nb_node->xpath);
|
||||
|
||||
ret = sr_rpc_subscribe(session, nb_node->xpath, frr_sr_config_rpc_cb,
|
||||
NULL, 0, 0, &module->sr_subscription);
|
||||
ret = sr_rpc_subscribe_tree(session, nb_node->xpath,
|
||||
frr_sr_config_rpc_cb, NULL, 0, 0,
|
||||
&module->sr_subscription);
|
||||
if (ret != SR_ERR_OK)
|
||||
flog_err(EC_LIB_LIBSYSREPO, "sr_rpc_subscribe(): %s",
|
||||
sr_strerror(ret));
|
||||
|
|
40
lib/vty.c
40
lib/vty.c
|
@ -3843,6 +3843,26 @@ static int vty_mgmt_edit_result_notified(struct mgmt_fe_client *client,
|
|||
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,
|
||||
uintptr_t user_data, uint64_t client_id,
|
||||
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_tree_notify = vty_mgmt_get_tree_result_notified,
|
||||
.edit_notify = vty_mgmt_edit_result_notified,
|
||||
.rpc_notify = vty_mgmt_rpc_result_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;
|
||||
}
|
||||
|
||||
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. */
|
||||
void vty_init(struct event_loop *master_thread, bool do_command_logging)
|
||||
{
|
||||
|
|
|
@ -122,6 +122,10 @@ struct vty {
|
|||
size_t num_cfg_changes;
|
||||
struct nb_cfg_change cfg_changes[VTY_MAXCFGCHANGES];
|
||||
|
||||
/* Input parameters */
|
||||
size_t num_rpc_params;
|
||||
struct nb_cfg_change rpc_params[VTY_MAXCFGCHANGES];
|
||||
|
||||
/* XPath of the current node */
|
||||
int xpath_index;
|
||||
char xpath[VTY_MAXDEPTH][XPATH_MAXLEN];
|
||||
|
@ -423,6 +427,8 @@ extern int vty_mgmt_send_edit_req(struct vty *vty, uint8_t datastore,
|
|||
LYD_FORMAT request_type, uint8_t flags,
|
||||
uint8_t operation, const char *xpath,
|
||||
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,
|
||||
bool lock, bool scok);
|
||||
extern void vty_mgmt_resume_response(struct vty *vty, int ret);
|
||||
|
|
67
lib/yang.c
67
lib/yang.c
|
@ -653,6 +653,16 @@ void yang_dnode_free(struct lyd_node *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 *data;
|
||||
|
@ -764,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);
|
||||
|
|
34
lib/yang.h
34
lib/yang.h
|
@ -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);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
|
@ -619,6 +634,25 @@ extern void yang_debugging_set(bool enable);
|
|||
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.
|
||||
*
|
||||
|
|
|
@ -98,6 +98,10 @@ static const char *const ripd_oper_xpaths[] = {
|
|||
"/ietf-key-chain:key-chains",
|
||||
NULL,
|
||||
};
|
||||
static const char *const ripd_rpc_xpaths[] = {
|
||||
"/frr-ripd",
|
||||
NULL,
|
||||
};
|
||||
#endif
|
||||
|
||||
#if HAVE_RIPNGD
|
||||
|
@ -113,6 +117,10 @@ static const char *const ripngd_oper_xpaths[] = {
|
|||
"/frr-ripngd:ripngd",
|
||||
NULL,
|
||||
};
|
||||
static const char *const ripngd_rpc_xpaths[] = {
|
||||
"/frr-ripngd",
|
||||
NULL,
|
||||
};
|
||||
#endif
|
||||
|
||||
#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,
|
||||
};
|
||||
|
||||
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
|
||||
*
|
||||
|
@ -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_oper_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 msg_server mgmt_be_server = {.fd = -1};
|
||||
|
@ -173,8 +191,8 @@ static struct mgmt_be_client_adapter
|
|||
static void
|
||||
mgmt_be_adapter_sched_init_event(struct mgmt_be_client_adapter *adapter);
|
||||
|
||||
static bool be_is_client_interested(const char *xpath,
|
||||
enum mgmt_be_client_id id, bool config);
|
||||
static bool be_is_client_interested(const char *xpath, enum mgmt_be_client_id id,
|
||||
enum mgmt_be_xpath_subscr_type type);
|
||||
|
||||
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,
|
||||
const char *xpath, bool config, bool oper)
|
||||
const char *xpath,
|
||||
enum mgmt_be_xpath_subscr_type type)
|
||||
{
|
||||
struct mgmt_be_xpath_map **maps, *map;
|
||||
|
||||
if (config)
|
||||
switch (type) {
|
||||
case MGMT_BE_XPATH_SUBSCR_TYPE_CFG:
|
||||
maps = &be_cfg_xpath_map;
|
||||
else if (oper)
|
||||
break;
|
||||
case MGMT_BE_XPATH_SUBSCR_TYPE_OPER:
|
||||
maps = &be_oper_xpath_map;
|
||||
else
|
||||
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;
|
||||
}
|
||||
|
||||
darr_foreach_p (*maps, map) {
|
||||
if (!strcmp(xpath, map->xpath_prefix)) {
|
||||
|
@ -260,18 +287,28 @@ static void mgmt_be_xpath_map_init(void)
|
|||
/* Initialize the common config init map */
|
||||
for (init = be_client_config_xpaths[id]; init && *init; 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 */
|
||||
for (init = be_client_oper_xpaths[id]; init && *init; 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 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)
|
||||
|
@ -289,6 +326,10 @@ static void mgmt_be_xpath_map_cleanup(void)
|
|||
darr_foreach_p (be_notif_xpath_map, map)
|
||||
XFREE(MTYPE_MGMTD_XPATH, map->xpath_prefix);
|
||||
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) {
|
||||
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->n_config_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)) {
|
||||
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;
|
||||
for (i = 0; i < num; i++) {
|
||||
xpath = be_msg->subscr_req->config_xpaths[i];
|
||||
mgmt_register_client_xpath(adapter->id, xpath, true,
|
||||
false);
|
||||
mgmt_register_client_xpath(adapter->id, xpath,
|
||||
MGMT_BE_XPATH_SUBSCR_TYPE_CFG);
|
||||
}
|
||||
|
||||
num = be_msg->subscr_req->n_oper_xpaths;
|
||||
for (i = 0; i < num; i++) {
|
||||
xpath = be_msg->subscr_req->oper_xpaths[i];
|
||||
mgmt_register_client_xpath(adapter->id, xpath, false,
|
||||
true);
|
||||
mgmt_register_client_xpath(adapter->id, xpath,
|
||||
MGMT_BE_XPATH_SUBSCR_TYPE_OPER);
|
||||
}
|
||||
|
||||
num = be_msg->subscr_req->n_notif_xpaths;
|
||||
for (i = 0; i < num; i++) {
|
||||
xpath = be_msg->subscr_req->notif_xpaths[i];
|
||||
mgmt_register_client_xpath(adapter->id, xpath, false,
|
||||
false);
|
||||
mgmt_register_client_xpath(adapter->id, xpath,
|
||||
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);
|
||||
|
@ -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_tree_data *tree_msg;
|
||||
struct mgmt_msg_rpc_reply *rpc_msg;
|
||||
struct mgmt_msg_error *error_msg;
|
||||
|
||||
/* 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 */
|
||||
mgmt_txn_notify_tree_data_reply(adapter, tree_msg, msg_len);
|
||||
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:
|
||||
notify_msg = (typeof(notify_msg))msg;
|
||||
__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;
|
||||
|
||||
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);
|
||||
else
|
||||
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;
|
||||
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;
|
||||
|
||||
|
@ -928,8 +1002,8 @@ uint64_t mgmt_be_interested_clients(const char *xpath, bool config)
|
|||
* Returns:
|
||||
* Interested or not.
|
||||
*/
|
||||
static bool be_is_client_interested(const char *xpath,
|
||||
enum mgmt_be_client_id id, bool config)
|
||||
static bool be_is_client_interested(const char *xpath, enum mgmt_be_client_id id,
|
||||
enum mgmt_be_xpath_subscr_type type)
|
||||
{
|
||||
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),
|
||||
xpath);
|
||||
|
||||
clients = mgmt_be_interested_clients(xpath, config);
|
||||
clients = mgmt_be_interested_clients(xpath, type);
|
||||
if (IS_IDBIT_SET(clients, id)) {
|
||||
__dbg("client: %s: interested", mgmt_be_client_id2name(id));
|
||||
return true;
|
||||
|
@ -998,23 +1072,32 @@ void mgmt_be_xpath_register_write(struct vty *vty)
|
|||
darr_len(be_oper_xpath_map));
|
||||
darr_foreach_p (be_oper_xpath_map, 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)
|
||||
{
|
||||
enum mgmt_be_client_id id;
|
||||
struct mgmt_be_client_adapter *adapter;
|
||||
uint64_t cclients, oclients, combined;
|
||||
uint64_t cclients, oclients, rclients, combined;
|
||||
|
||||
cclients = mgmt_be_interested_clients(xpath, true);
|
||||
oclients = mgmt_be_interested_clients(xpath, false);
|
||||
cclients = mgmt_be_interested_clients(xpath,
|
||||
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;
|
||||
|
||||
vty_out(vty, "XPath: '%s'\n", xpath);
|
||||
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),
|
||||
IS_IDBIT_SET(oclients, id));
|
||||
IS_IDBIT_SET(oclients, id), IS_IDBIT_SET(rclients, id));
|
||||
adapter = mgmt_be_get_adapter_by_id(id);
|
||||
if (adapter)
|
||||
vty_out(vty, " -- Adapter: %p\n", adapter);
|
||||
|
|
|
@ -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);
|
||||
|
||||
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`
|
||||
* and the way they are subscribed.
|
||||
*
|
||||
* Args:
|
||||
* 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.
|
||||
|
|
|
@ -1101,6 +1101,47 @@ done:
|
|||
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,
|
||||
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);
|
||||
|
||||
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)) {
|
||||
__dbg("No backends provide xpath: %s for txn-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);
|
||||
if (!xpath || !data) {
|
||||
if (!xpath) {
|
||||
fe_adapter_send_error(session, msg->req_id, false, -EINVAL,
|
||||
"Invalid message");
|
||||
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.
|
||||
*/
|
||||
|
@ -1383,6 +1515,9 @@ static void fe_adapter_handle_native_msg(struct mgmt_fe_client_adapter *adapter,
|
|||
case MGMT_MSG_CODE_EDIT:
|
||||
fe_adapter_handle_edit(session, msg, msg_len);
|
||||
break;
|
||||
case MGMT_MSG_CODE_RPC:
|
||||
fe_adapter_handle_rpc(session, msg, msg_len);
|
||||
break;
|
||||
default:
|
||||
__log_err("unknown native message session-id %" PRIu64
|
||||
" 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;
|
||||
}
|
||||
|
||||
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,
|
||||
uint64_t req_id, bool unlock, bool commit,
|
||||
const char *xpath, int16_t error,
|
||||
|
|
|
@ -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,
|
||||
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.
|
||||
*
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
DEFINE_MGROUP(MGMTD, "mgmt");
|
||||
DEFINE_MTYPE(MGMTD, MGMTD, "instance");
|
||||
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_FE_ADPATER, "frontend adapter");
|
||||
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_REPLY, "txn get-data replies");
|
||||
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_CMT_INFO, "commit info");
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
DECLARE_MGROUP(MGMTD);
|
||||
DECLARE_MTYPE(MGMTD);
|
||||
DECLARE_MTYPE(MGMTD_XPATH);
|
||||
DECLARE_MTYPE(MGMTD_ERR);
|
||||
DECLARE_MTYPE(MGMTD_BE_ADPATER);
|
||||
DECLARE_MTYPE(MGMTD_FE_ADPATER);
|
||||
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_REPLY);
|
||||
DECLARE_MTYPE(MGMTD_TXN_GETTREE_REQ);
|
||||
DECLARE_MTYPE(MGMTD_TXN_RPC_REQ);
|
||||
DECLARE_MTYPE(MGMTD_TXN_CFG_BATCH);
|
||||
DECLARE_MTYPE(MGMTD_BE_ADAPTER_MSG_BUF);
|
||||
DECLARE_MTYPE(MGMTD_CMT_INFO);
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
/* ---------------- */
|
||||
|
||||
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 sigint(void);
|
||||
|
@ -86,6 +87,10 @@ static const struct frr_yang_module_info frr_ripd_info = {
|
|||
.xpath = "/frr-ripd:authentication-failure",
|
||||
.cbs.notify = async_notification,
|
||||
},
|
||||
{
|
||||
.xpath = "/frr-ripd:clear-rip-route",
|
||||
.cbs.rpc = rpc_callback,
|
||||
},
|
||||
{
|
||||
.xpath = NULL,
|
||||
}
|
||||
|
@ -113,6 +118,7 @@ FRR_DAEMON_INFO(mgmtd_testc, MGMTD_TESTC,
|
|||
/* clang-format on */
|
||||
|
||||
const char **__notif_xpaths;
|
||||
const char **__rpc_xpaths;
|
||||
|
||||
struct mgmt_be_client_cbs __client_cbs = {};
|
||||
struct event *event_timeout;
|
||||
|
@ -134,6 +140,7 @@ static void quit(int exit_code)
|
|||
{
|
||||
EVENT_OFF(event_timeout);
|
||||
darr_free(__client_cbs.notif_xpaths);
|
||||
darr_free(__client_cbs.rpc_xpaths);
|
||||
|
||||
frr_fini();
|
||||
|
||||
|
@ -152,6 +159,12 @@ static void timeout(struct event *event)
|
|||
quit(1);
|
||||
}
|
||||
|
||||
static void success(struct event *event)
|
||||
{
|
||||
zlog_notice("Success, exiting");
|
||||
quit(0);
|
||||
}
|
||||
|
||||
static void async_notification(struct nb_cb_notify_args *args)
|
||||
{
|
||||
zlog_notice("Received YANG notification");
|
||||
|
@ -163,6 +176,23 @@ static void async_notification(struct nb_cb_notify_args *args)
|
|||
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 f_listen = 0;
|
||||
|
@ -217,6 +247,10 @@ int main(int argc, char **argv)
|
|||
__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,
|
||||
master);
|
||||
|
||||
|
|
223
mgmtd/mgmt_txn.c
223
mgmtd/mgmt_txn.c
|
@ -29,6 +29,7 @@ enum mgmt_txn_event {
|
|||
MGMTD_TXN_PROC_COMMITCFG,
|
||||
MGMTD_TXN_PROC_GETCFG,
|
||||
MGMTD_TXN_PROC_GETTREE,
|
||||
MGMTD_TXN_PROC_RPC,
|
||||
MGMTD_TXN_COMMITCFG_TIMEOUT,
|
||||
};
|
||||
|
||||
|
@ -188,6 +189,15 @@ struct txn_req_get_tree {
|
|||
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_ctx *txn;
|
||||
enum mgmt_txn_event req_event;
|
||||
|
@ -196,6 +206,7 @@ struct mgmt_txn_req {
|
|||
struct mgmt_set_cfg_req *set_cfg;
|
||||
struct mgmt_get_data_req *get_data;
|
||||
struct txn_req_get_tree *get_tree;
|
||||
struct txn_req_rpc *rpc;
|
||||
struct mgmt_commit_cfg_req commit_cfg;
|
||||
} req;
|
||||
|
||||
|
@ -221,6 +232,7 @@ struct mgmt_txn_ctx {
|
|||
struct event *proc_get_tree;
|
||||
struct event *comm_cfg_timeout;
|
||||
struct event *get_tree_timeout;
|
||||
struct event *rpc_timeout;
|
||||
struct event *clnup;
|
||||
|
||||
/* List of backend adapters involved in this transaction */
|
||||
|
@ -252,6 +264,10 @@ struct mgmt_txn_ctx {
|
|||
* List of pending get-tree requests.
|
||||
*/
|
||||
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
|
||||
* 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,
|
||||
txn_req->req_id, txn->txn_id, txn->session_id);
|
||||
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:
|
||||
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_TXN_GETTREE_REQ, (*txn_req)->req.get_tree);
|
||||
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:
|
||||
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");
|
||||
|
||||
clients = mgmt_be_interested_clients(xpath, true);
|
||||
clients =
|
||||
mgmt_be_interested_clients(xpath,
|
||||
MGMT_BE_XPATH_SUBSCR_TYPE_CFG);
|
||||
|
||||
chg_clients = 0;
|
||||
|
||||
|
@ -1306,6 +1342,33 @@ static int txn_get_tree_data_done(struct mgmt_txn_ctx *txn,
|
|||
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)
|
||||
{
|
||||
|
@ -1333,6 +1396,31 @@ static void txn_get_tree_timeout(struct event *thread)
|
|||
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.
|
||||
*
|
||||
|
@ -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_COMMITCFG:
|
||||
case MGMTD_TXN_PROC_GETTREE:
|
||||
case MGMTD_TXN_PROC_RPC:
|
||||
case MGMTD_TXN_COMMITCFG_TIMEOUT:
|
||||
__log_err("Invalid Txn-Req-Event %u", txn_req->req_event);
|
||||
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->get_cfg_reqs);
|
||||
mgmt_txn_reqs_init(&txn->get_tree_reqs);
|
||||
mgmt_txn_reqs_init(&txn->rpc_reqs);
|
||||
txn->commit_cfg_req = NULL;
|
||||
txn->refcount = 0;
|
||||
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);
|
||||
break;
|
||||
case MGMTD_TXN_PROC_GETTREE:
|
||||
case MGMTD_TXN_PROC_RPC:
|
||||
assert(!"code bug do not register this event");
|
||||
break;
|
||||
}
|
||||
|
@ -2496,6 +2587,64 @@ reply:
|
|||
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.
|
||||
*/
|
||||
|
@ -2506,6 +2655,7 @@ int mgmt_txn_notify_error(struct mgmt_be_client_adapter *adapter,
|
|||
enum mgmt_be_client_id id = adapter->id;
|
||||
struct mgmt_txn_ctx *txn = mgmt_txn_id2ctx(txn_id);
|
||||
struct txn_req_get_tree *get_tree;
|
||||
struct txn_req_rpc *rpc;
|
||||
struct mgmt_txn_req *txn_req;
|
||||
|
||||
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)
|
||||
if (txn_req->req_id == req_id)
|
||||
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) {
|
||||
__log_err("Error reply from %s for txn-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)
|
||||
return 0;
|
||||
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 */
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct mgmt_txn_ctx *txn;
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#define MGMTD_TXN_CFG_COMMIT_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
|
||||
|
||||
|
@ -48,7 +49,8 @@ struct mgmt_edit_req;
|
|||
enum mgmt_txn_type {
|
||||
MGMTD_TXN_TYPE_NONE = 0,
|
||||
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)
|
||||
|
@ -60,6 +62,8 @@ static inline const char *mgmt_txn_type2str(enum mgmt_txn_type type)
|
|||
return "CONFIG";
|
||||
case MGMTD_TXN_TYPE_SHOW:
|
||||
return "SHOW";
|
||||
case MGMTD_TXN_TYPE_RPC:
|
||||
return "RPC";
|
||||
}
|
||||
|
||||
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,
|
||||
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.
|
||||
*/
|
||||
|
@ -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,
|
||||
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.
|
||||
*/
|
||||
|
|
|
@ -296,6 +296,21 @@ DEFPY(mgmt_edit, mgmt_edit_cmd,
|
|||
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,
|
||||
"show mgmt get-config [candidate|operational|running]$dsname WORD$path",
|
||||
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_replace_config_data_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_save_config_cmd);
|
||||
install_element(CONFIG_NODE, &mgmt_rollback_cmd);
|
||||
|
|
|
@ -1233,6 +1233,23 @@ DEFPY_YANG(no_rip_distribute_list_prefix,
|
|||
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. */
|
||||
static struct cmd_node rip_node = {
|
||||
.name = "rip",
|
||||
|
@ -1295,6 +1312,8 @@ void rip_cli_init(void)
|
|||
install_element(INTERFACE_NODE, &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);
|
||||
}
|
||||
/* clang-format off */
|
||||
|
|
|
@ -68,12 +68,11 @@ static void clear_rip_route(struct rip *rip)
|
|||
int clear_rip_route_rpc(struct nb_cb_rpc_args *args)
|
||||
{
|
||||
struct rip *rip;
|
||||
struct yang_data *yang_vrf;
|
||||
|
||||
yang_vrf = yang_data_list_find(args->input, "%s/%s", args->xpath,
|
||||
"input/vrf");
|
||||
if (yang_vrf) {
|
||||
rip = rip_lookup_by_vrf_name(yang_vrf->value);
|
||||
if (args->input && yang_dnode_exists(args->input, "vrf")) {
|
||||
const char *name = yang_dnode_get_string(args->input, "vrf");
|
||||
|
||||
rip = rip_lookup_by_vrf_name(name);
|
||||
if (rip)
|
||||
clear_rip_route(rip);
|
||||
} else {
|
||||
|
|
33
ripd/ripd.c
33
ripd/ripd.c
|
@ -3254,38 +3254,6 @@ DEFUN (show_ip_rip_status,
|
|||
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. */
|
||||
static void rip_distribute_update(struct distribute_ctx *ctx,
|
||||
struct distribute *dist)
|
||||
|
@ -3660,7 +3628,6 @@ void rip_init(void)
|
|||
/* Install rip commands. */
|
||||
install_element(VIEW_NODE, &show_ip_rip_cmd);
|
||||
install_element(VIEW_NODE, &show_ip_rip_status_cmd);
|
||||
install_element(ENABLE_NODE, &clear_ip_rip_cmd);
|
||||
|
||||
/* Debug related init. */
|
||||
rip_debug_init();
|
||||
|
|
|
@ -33,7 +33,6 @@ ripd_ripd_SOURCES = \
|
|||
clippy_scan += \
|
||||
ripd/rip_bfd.c \
|
||||
ripd/rip_cli.c \
|
||||
ripd/ripd.c \
|
||||
# end
|
||||
|
||||
noinst_HEADERS += \
|
||||
|
|
|
@ -624,6 +624,23 @@ DEFPY_YANG(no_ripng_ipv6_distribute_list_prefix,
|
|||
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. */
|
||||
static struct cmd_node cmd_ripng_node = {
|
||||
.name = "ripng",
|
||||
|
@ -663,6 +680,8 @@ void ripng_cli_init(void)
|
|||
|
||||
install_element(INTERFACE_NODE, &ipv6_ripng_split_horizon_cmd);
|
||||
|
||||
install_element(ENABLE_NODE, &clear_ipv6_rip_cmd);
|
||||
|
||||
if_rmap_init(RIPNG_NODE);
|
||||
}
|
||||
|
||||
|
|
|
@ -70,12 +70,11 @@ static void clear_ripng_route(struct ripng *ripng)
|
|||
int clear_ripng_route_rpc(struct nb_cb_rpc_args *args)
|
||||
{
|
||||
struct ripng *ripng;
|
||||
struct yang_data *yang_vrf;
|
||||
|
||||
yang_vrf = yang_data_list_find(args->input, "%s/%s", args->xpath,
|
||||
"input/vrf");
|
||||
if (yang_vrf) {
|
||||
ripng = ripng_lookup_by_vrf_name(yang_vrf->value);
|
||||
if (args->input && yang_dnode_exists(args->input, "vrf")) {
|
||||
const char *name = yang_dnode_get_string(args->input, "vrf");
|
||||
|
||||
ripng = ripng_lookup_by_vrf_name(name);
|
||||
if (ripng)
|
||||
clear_ripng_route(ripng);
|
||||
} else {
|
||||
|
|
|
@ -2231,38 +2231,6 @@ DEFUN (show_ipv6_ripng_status,
|
|||
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. */
|
||||
void ripng_ecmp_disable(struct ripng *ripng)
|
||||
{
|
||||
|
@ -2680,7 +2648,6 @@ void ripng_init(void)
|
|||
/* Install ripng commands. */
|
||||
install_element(VIEW_NODE, &show_ipv6_ripng_cmd);
|
||||
install_element(VIEW_NODE, &show_ipv6_ripng_status_cmd);
|
||||
install_element(ENABLE_NODE, &clear_ipv6_rip_cmd);
|
||||
|
||||
ripng_if_init();
|
||||
ripng_debug_init();
|
||||
|
|
|
@ -27,7 +27,6 @@ ripngd_ripngd_SOURCES = \
|
|||
|
||||
clippy_scan += \
|
||||
ripngd/ripng_cli.c \
|
||||
ripngd/ripngd.c \
|
||||
# end
|
||||
|
||||
noinst_HEADERS += \
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "lib_vty.h"
|
||||
#include "log.h"
|
||||
#include "northbound.h"
|
||||
#include "northbound_cli.h"
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
@ -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",
|
||||
.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",
|
||||
.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 */
|
||||
|
||||
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[] = {
|
||||
&frr_test_module_info,
|
||||
};
|
||||
|
@ -416,6 +461,8 @@ int main(int argc, char **argv)
|
|||
lib_cmd_init();
|
||||
nb_init(master, modules, array_size(modules), false);
|
||||
|
||||
install_element(ENABLE_NODE, &test_rpc_cmd);
|
||||
|
||||
/* Create artificial data. */
|
||||
create_data(num_vrfs, num_interfaces, num_routes);
|
||||
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
show yang operational-data /frr-test-module:frr-test-module
|
||||
test rpc
|
||||
|
|
|
@ -119,5 +119,7 @@ test# show yang operational-data /frr-test-module:frr-test-module
|
|||
}
|
||||
}
|
||||
}
|
||||
test# test rpc
|
||||
vrf testname data testdata
|
||||
test#
|
||||
end.
|
||||
|
|
0
tests/topotests/mgmt_rpc/r1/frr.conf
Normal file
0
tests/topotests/mgmt_rpc/r1/frr.conf
Normal file
74
tests/topotests/mgmt_rpc/test_rpc.py
Normal file
74
tests/topotests/mgmt_rpc/test_rpc.py
Normal 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
|
|
@ -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 {
|
||||
|
|
|
@ -20,48 +20,30 @@ int clear_evpn_dup_addr_rpc(struct nb_cb_rpc_args *args)
|
|||
{
|
||||
struct zebra_vrf *zvrf;
|
||||
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();
|
||||
|
||||
if (yang_dup_choice
|
||||
&& strcmp(yang_dup_choice->value, "all-case") == 0) {
|
||||
if (yang_dnode_exists(args->input, "all-vnis")) {
|
||||
zebra_vxlan_clear_dup_detect_vni_all(zvrf);
|
||||
} else {
|
||||
vni_t vni;
|
||||
vni_t vni = yang_dnode_get_uint32(args->input, "vni-id");
|
||||
struct ipaddr host_ip = {.ipa_type = IPADDR_NONE};
|
||||
struct ethaddr mac;
|
||||
|
||||
yang_dup_vni = yang_data_list_find(
|
||||
args->input, "%s/%s", args->xpath,
|
||||
"input/clear-dup-choice/single-case/vni-id");
|
||||
if (yang_dup_vni) {
|
||||
vni = yang_str2uint32(yang_dup_vni->value);
|
||||
|
||||
yang_dup_mac = yang_data_list_find(
|
||||
args->input, "%s/%s", args->xpath,
|
||||
"input/clear-dup-choice/single-case/vni-id/mac-addr");
|
||||
yang_dup_ip = yang_data_list_find(
|
||||
args->input, "%s/%s", args->xpath,
|
||||
"input/clear-dup-choice/single-case/vni-id/vni-ipaddr");
|
||||
|
||||
if (yang_dup_mac) {
|
||||
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 (yang_dnode_exists(args->input, "mac-addr")) {
|
||||
yang_dnode_get_mac(&mac, args->input, "mac-addr");
|
||||
ret = zebra_vxlan_clear_dup_detect_vni_mac(zvrf, vni,
|
||||
&mac,
|
||||
args->errmsg,
|
||||
args->errmsg_len);
|
||||
} else if (yang_dnode_exists(args->input, "vni-ipaddr")) {
|
||||
yang_dnode_get_ip(&host_ip, args->input, "vni-ipaddr");
|
||||
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)
|
||||
|
|
|
@ -3553,56 +3553,17 @@ DEFPY (clear_evpn_dup_addr,
|
|||
"IPv4 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) {
|
||||
yang_dup = yang_data_new(
|
||||
"/frr-zebra:clear-evpn-dup-addr/input/clear-dup-choice",
|
||||
"all-case");
|
||||
nb_cli_rpc_enqueue(vty, "all-vnis", NULL);
|
||||
} else {
|
||||
yang_dup = yang_data_new_uint32(
|
||||
"/frr-zebra:clear-evpn-dup-addr/input/clear-dup-choice/single-case/vni-id",
|
||||
vni);
|
||||
if (!is_zero_mac(&mac->eth_addr)) {
|
||||
yang_dup_mac = yang_data_new_mac(
|
||||
"/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);
|
||||
}
|
||||
nb_cli_rpc_enqueue(vty, "vni-id", vni_str);
|
||||
if (mac_str)
|
||||
nb_cli_rpc_enqueue(vty, "mac-addr", mac_str);
|
||||
else if (ip_str)
|
||||
nb_cli_rpc_enqueue(vty, "vni-ipaddr", ip_str);
|
||||
}
|
||||
|
||||
if (yang_dup) {
|
||||
listnode_add(input, yang_dup);
|
||||
ret = nb_cli_rpc(vty, "/frr-zebra:clear-evpn-dup-addr", input,
|
||||
NULL);
|
||||
}
|
||||
|
||||
list_delete(&input);
|
||||
|
||||
return ret;
|
||||
return nb_cli_rpc(vty, "/frr-zebra:clear-evpn-dup-addr", NULL);
|
||||
}
|
||||
|
||||
DEFPY_HIDDEN (evpn_accept_bgp_seq,
|
||||
|
|
Loading…
Reference in a new issue