diff options
| -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 f483d48d8d..6e2fb05e84 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 cd8b237526..7ad0589bdb 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 09ea43ece0..39ce9abae6 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 b7c29862aa..342f6f04cd 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 f506d8bf23..06d29bb9c4 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 1903079d1c..57131f478b 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:  | 
