summaryrefslogtreecommitdiffstats
path: root/mgmtd
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-08-05 09:56:23 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-08-05 09:56:23 +0000
commitc15d6efd40655f717841d00839a43df1ead5cb26 (patch)
tree35d579f9a19170e2b39085669ca92533c2d161b4 /mgmtd
parentAdding upstream version 10.0.1. (diff)
downloadfrr-c15d6efd40655f717841d00839a43df1ead5cb26.tar.xz
frr-c15d6efd40655f717841d00839a43df1ead5cb26.zip
Adding upstream version 10.1.upstream/10.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'mgmtd')
-rw-r--r--mgmtd/mgmt_be_adapter.c165
-rw-r--r--mgmtd/mgmt_be_adapter.h12
-rw-r--r--mgmtd/mgmt_be_nb.c6
-rw-r--r--mgmtd/mgmt_fe_adapter.c352
-rw-r--r--mgmtd/mgmt_fe_adapter.h41
-rw-r--r--mgmtd/mgmt_main.c23
-rw-r--r--mgmtd/mgmt_memory.c2
-rw-r--r--mgmtd/mgmt_memory.h2
-rw-r--r--mgmtd/mgmt_testc.c38
-rw-r--r--mgmtd/mgmt_txn.c368
-rw-r--r--mgmtd/mgmt_txn.h78
-rw-r--r--mgmtd/mgmt_vty.c77
-rw-r--r--mgmtd/subdir.am2
13 files changed, 1054 insertions, 112 deletions
diff --git a/mgmtd/mgmt_be_adapter.c b/mgmtd/mgmt_be_adapter.c
index b311bf4..c7f4fb9 100644
--- a/mgmtd/mgmt_be_adapter.c
+++ b/mgmtd/mgmt_be_adapter.c
@@ -90,10 +90,16 @@ static const char *const ripd_config_xpaths[] = {
"/frr-ripd:ripd",
"/frr-route-map:lib",
"/frr-vrf:lib",
+ "/ietf-key-chain:key-chains",
NULL,
};
static const char *const ripd_oper_xpaths[] = {
"/frr-ripd:ripd",
+ "/ietf-key-chain:key-chains",
+ NULL,
+};
+static const char *const ripd_rpc_xpaths[] = {
+ "/frr-ripd",
NULL,
};
#endif
@@ -111,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
@@ -145,6 +155,18 @@ 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_notif_xpaths[MGMTD_BE_CLIENT_ID_MAX] = {
+};
+
+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
*
@@ -157,6 +179,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};
@@ -171,8 +194,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)
{
@@ -221,16 +244,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)) {
@@ -258,18 +290,36 @@ 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 NOTIF init map */
+ for (init = be_client_notif_xpaths[id]; init && *init; init++) {
+ __dbg(" - NOTIF XPATH: '%s'", *init);
+ mgmt_register_client_xpath(id, *init,
+ MGMT_BE_XPATH_SUBSCR_TYPE_NOTIF);
+ }
+
+ /* 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 Noitf XPath Maps: %u", darr_len(be_notif_xpath_map));
+ __dbg("Total RPC XPath Maps: %u", darr_len(be_rpc_xpath_map));
}
static void mgmt_be_xpath_map_cleanup(void)
@@ -287,6 +337,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);
}
@@ -403,11 +457,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,
@@ -430,22 +485,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);
@@ -636,6 +698,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 */
@@ -660,6 +723,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);
@@ -880,7 +952,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 */
@@ -891,13 +964,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;
@@ -926,8 +1013,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;
@@ -936,7 +1023,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;
@@ -996,23 +1083,41 @@ 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 NOTIFY XPath Registry: Count: %u\n",
+ darr_len(be_notif_xpath_map));
+ darr_foreach_p (be_notif_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;
-
- cclients = mgmt_be_interested_clients(xpath, true);
- oclients = mgmt_be_interested_clients(xpath, false);
- combined = cclients | oclients;
+ uint64_t cclients, nclients, oclients, rclients, combined;
+
+ cclients = mgmt_be_interested_clients(xpath,
+ MGMT_BE_XPATH_SUBSCR_TYPE_CFG);
+ oclients = mgmt_be_interested_clients(xpath,
+ MGMT_BE_XPATH_SUBSCR_TYPE_OPER);
+ nclients = mgmt_be_interested_clients(xpath,
+ MGMT_BE_XPATH_SUBSCR_TYPE_NOTIF);
+ rclients = mgmt_be_interested_clients(xpath,
+ MGMT_BE_XPATH_SUBSCR_TYPE_RPC);
+ combined = cclients | nclients | oclients | rclients;
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 notify:%d oper:%d rpc:%d\n",
mgmt_be_client_id2name(id), IS_IDBIT_SET(cclients, id),
- IS_IDBIT_SET(oclients, id));
+ IS_IDBIT_SET(nclients, 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);
diff --git a/mgmtd/mgmt_be_adapter.h b/mgmtd/mgmt_be_adapter.h
index 491410a..c9f2ab1 100644
--- a/mgmtd/mgmt_be_adapter.h
+++ b/mgmtd/mgmt_be_adapter.h
@@ -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.
diff --git a/mgmtd/mgmt_be_nb.c b/mgmtd/mgmt_be_nb.c
new file mode 100644
index 0000000..613272d
--- /dev/null
+++ b/mgmtd/mgmt_be_nb.c
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "config.h"
+#include "xref.h"
+
+XREF_SETUP();
diff --git a/mgmtd/mgmt_fe_adapter.c b/mgmtd/mgmt_fe_adapter.c
index ec8e773..fc1bde0 100644
--- a/mgmtd/mgmt_fe_adapter.c
+++ b/mgmtd/mgmt_fe_adapter.c
@@ -898,11 +898,13 @@ static int mgmt_fe_session_handle_commit_config_req_msg(
/*
* Create COMMITConfig request under the transaction
*/
- if (mgmt_txn_send_commit_config_req(
- session->cfg_txn_id, commcfg_req->req_id,
- commcfg_req->src_ds_id, src_ds_ctx, commcfg_req->dst_ds_id,
- dst_ds_ctx, commcfg_req->validate_only, commcfg_req->abort,
- false) != 0) {
+ if (mgmt_txn_send_commit_config_req(session->cfg_txn_id,
+ commcfg_req->req_id,
+ commcfg_req->src_ds_id, src_ds_ctx,
+ commcfg_req->dst_ds_id, dst_ds_ctx,
+ commcfg_req->validate_only,
+ commcfg_req->abort, false,
+ NULL) != 0) {
fe_adapter_send_commit_cfg_reply(
session, commcfg_req->src_ds_id, commcfg_req->dst_ds_id,
commcfg_req->req_id, MGMTD_INTERNAL_ERROR,
@@ -1099,6 +1101,74 @@ 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)
+{
+ struct mgmt_msg_edit_reply *msg;
+ int ret;
+
+ msg = mgmt_msg_native_alloc_msg(struct mgmt_msg_edit_reply, 0,
+ MTYPE_MSG_NATIVE_EDIT_REPLY);
+ msg->refer_id = session->session_id;
+ msg->req_id = req_id;
+ msg->code = MGMT_MSG_CODE_EDIT_REPLY;
+
+ mgmt_msg_native_xpath_encode(msg, xpath);
+
+ __dbg("Sending edit-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);
+ mgmt_msg_native_free_msg(msg);
+
+ return ret;
+}
+
/**
* fe_adapter_handle_get_data() - Handle a get-tree message from a FE client.
* @session: the client session.
@@ -1186,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,
@@ -1224,6 +1295,196 @@ done:
darr_free(xpath_resolved);
}
+static void fe_adapter_handle_edit(struct mgmt_fe_session_ctx *session,
+ void *__msg, size_t msg_len)
+{
+ struct mgmt_msg_edit *msg = __msg;
+ Mgmtd__DatastoreId ds_id, rds_id;
+ struct mgmt_ds_ctx *ds_ctx, *rds_ctx;
+ const char *xpath, *data;
+ bool lock, commit;
+ int ret;
+
+ if (msg->datastore != MGMT_MSG_DATASTORE_CANDIDATE) {
+ fe_adapter_send_error(session, msg->req_id, false, -EINVAL,
+ "Unsupported datastore");
+ return;
+ }
+
+ xpath = mgmt_msg_native_xpath_data_decode(msg, msg_len, data);
+ if (!xpath) {
+ fe_adapter_send_error(session, msg->req_id, false, -EINVAL,
+ "Invalid message");
+ return;
+ }
+
+ ds_id = MGMTD_DS_CANDIDATE;
+ ds_ctx = mgmt_ds_get_ctx_by_id(mm, ds_id);
+ assert(ds_ctx);
+
+ rds_id = MGMTD_DS_RUNNING;
+ rds_ctx = mgmt_ds_get_ctx_by_id(mm, rds_id);
+ assert(rds_ctx);
+
+ lock = CHECK_FLAG(msg->flags, EDIT_FLAG_IMPLICIT_LOCK);
+ commit = CHECK_FLAG(msg->flags, EDIT_FLAG_IMPLICIT_COMMIT);
+
+ if (lock) {
+ if (mgmt_fe_session_write_lock_ds(ds_id, ds_ctx, session)) {
+ fe_adapter_send_error(session, msg->req_id, false,
+ -EBUSY,
+ "Candidate DS is locked by another session");
+ return;
+ }
+
+ if (commit) {
+ if (mgmt_fe_session_write_lock_ds(rds_id, rds_ctx,
+ session)) {
+ mgmt_fe_session_unlock_ds(ds_id, ds_ctx,
+ session);
+ fe_adapter_send_error(
+ session, msg->req_id, false, -EBUSY,
+ "Running DS is locked by another session");
+ return;
+ }
+ }
+ } else {
+ if (!session->ds_locked[ds_id]) {
+ fe_adapter_send_error(session, msg->req_id, false,
+ -EBUSY,
+ "Candidate DS is not locked");
+ return;
+ }
+
+ if (commit) {
+ if (!session->ds_locked[rds_id]) {
+ fe_adapter_send_error(session, msg->req_id,
+ false, -EBUSY,
+ "Running DS is not locked");
+ return;
+ }
+ }
+ }
+
+ session->cfg_txn_id = mgmt_create_txn(session->session_id,
+ MGMTD_TXN_TYPE_CONFIG);
+ if (session->cfg_txn_id == MGMTD_SESSION_ID_NONE) {
+ if (lock) {
+ mgmt_fe_session_unlock_ds(ds_id, ds_ctx, session);
+ if (commit)
+ mgmt_fe_session_unlock_ds(rds_id, rds_ctx,
+ session);
+ }
+ fe_adapter_send_error(session, msg->req_id, false, -EBUSY,
+ "Failed to create a configuration transaction");
+ return;
+ }
+
+ __dbg("Created new config txn-id: %" PRIu64 " for session-id: %" PRIu64,
+ session->cfg_txn_id, session->session_id);
+
+ ret = mgmt_txn_send_edit(session->cfg_txn_id, msg->req_id, ds_id,
+ ds_ctx, rds_id, rds_ctx, lock, commit,
+ msg->request_type, msg->flags, msg->operation,
+ xpath, data);
+ if (ret) {
+ /* destroy the just created txn */
+ mgmt_destroy_txn(&session->cfg_txn_id);
+ if (lock) {
+ mgmt_fe_session_unlock_ds(ds_id, ds_ctx, session);
+ if (commit)
+ mgmt_fe_session_unlock_ds(rds_id, rds_ctx,
+ session);
+ }
+ fe_adapter_send_error(session, msg->req_id, false, -EBUSY,
+ "Failed to create a configuration transaction");
+ }
+}
+
+/**
+ * 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 && snode->nodetype != LYS_ACTION) {
+ fe_adapter_send_error(session, req_id, false, -EINVAL,
+ "Not an RPC or action path: %s", xpath);
+ return;
+ }
+
+ clients = mgmt_be_interested_clients(xpath,
+ MGMT_BE_XPATH_SUBSCR_TYPE_RPC);
+ 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.
*/
@@ -1245,6 +1506,12 @@ static void fe_adapter_handle_native_msg(struct mgmt_fe_client_adapter *adapter,
case MGMT_MSG_CODE_GET_DATA:
fe_adapter_handle_get_data(session, msg, msg_len);
break;
+ 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",
@@ -1484,6 +1751,70 @@ 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,
+ const char *errstr)
+{
+ struct mgmt_fe_session_ctx *session;
+ Mgmtd__DatastoreId ds_id, rds_id;
+ struct mgmt_ds_ctx *ds_ctx, *rds_ctx;
+ int ret;
+
+ session = mgmt_session_id2ctx(session_id);
+ if (!session || session->cfg_txn_id != txn_id)
+ return -1;
+
+ if (session->cfg_txn_id != MGMTD_TXN_ID_NONE && commit)
+ mgmt_fe_session_register_event(session,
+ MGMTD_FE_SESSION_CFG_TXN_CLNUP);
+
+ if (unlock) {
+ ds_id = MGMTD_DS_CANDIDATE;
+ ds_ctx = mgmt_ds_get_ctx_by_id(mm, ds_id);
+ assert(ds_ctx);
+
+ mgmt_fe_session_unlock_ds(ds_id, ds_ctx, session);
+
+ if (commit) {
+ rds_id = MGMTD_DS_RUNNING;
+ rds_ctx = mgmt_ds_get_ctx_by_id(mm, rds_id);
+ assert(rds_ctx);
+
+ mgmt_fe_session_unlock_ds(rds_id, rds_ctx, session);
+ }
+ }
+
+ if (error)
+ ret = fe_adapter_send_error(session, req_id, false, error, "%s",
+ errstr);
+ else
+ ret = fe_adapter_send_edit_reply(session, req_id, xpath);
+
+ if (session->cfg_txn_id != MGMTD_TXN_ID_NONE && !commit)
+ mgmt_destroy_txn(&session->cfg_txn_id);
+
+ return ret;
+}
+
/**
* Send an error back to the FE client and cleanup any in-progress txn.
*/
@@ -1586,15 +1917,6 @@ mgmt_fe_adapter_cmt_stats_write(struct vty *vty,
mgmt_realtime_to_string(
&adapter->cmt_stats.txn_create_start,
buf, sizeof(buf)));
- vty_out(vty,
-#ifdef MGMTD_LOCAL_VALIDATIONS_ENABLED
- " Send-Config Start: \t\t%s\n",
-#else
- " Send-Config-Validate Start: \t%s\n",
-#endif
- mgmt_realtime_to_string(
- &adapter->cmt_stats.send_cfg_start, buf,
- sizeof(buf)));
vty_out(vty, " Apply-Config Start: \t\t%s\n",
mgmt_realtime_to_string(
&adapter->cmt_stats.apply_cfg_start,
diff --git a/mgmtd/mgmt_fe_adapter.h b/mgmtd/mgmt_fe_adapter.h
index 2150f86..61d6cfa 100644
--- a/mgmtd/mgmt_fe_adapter.h
+++ b/mgmtd/mgmt_fe_adapter.h
@@ -24,7 +24,6 @@ struct mgmt_commit_stats {
#endif
struct timeval prep_cfg_start;
struct timeval txn_create_start;
- struct timeval send_cfg_start;
struct timeval apply_cfg_start;
struct timeval apply_cfg_end;
struct timeval txn_del_start;
@@ -164,6 +163,46 @@ mgmt_fe_adapter_send_tree_data(uint64_t session_id, uint64_t txn_id,
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.
+ *
+ * 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 edit message
+ * unlock: implicit-lock flag was set in the request
+ * commit: implicit-commit flag was set in the request
+ * xpath: the xpath of the data node that was created
+ * error: the error code, zero for successful request
+ * errstr: the error string, if error is non-zero
+ */
+extern 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, const char *errstr);
+
+/**
* Send an error back to the FE client using native messaging.
*
* This also cleans up and frees the transaction.
diff --git a/mgmtd/mgmt_main.c b/mgmtd/mgmt_main.c
index cce16f5..e181d0d 100644
--- a/mgmtd/mgmt_main.c
+++ b/mgmtd/mgmt_main.c
@@ -10,6 +10,7 @@
#include "lib/version.h"
#include "routemap.h"
#include "filter.h"
+#include "keychain.h"
#include "libfrr.h"
#include "frr_pthread.h"
#include "mgmtd/mgmt.h"
@@ -185,6 +186,8 @@ static const struct frr_yang_module_info *const mgmt_yang_modules[] = {
&frr_zebra_cli_info,
&zebra_route_map_info,
+ &ietf_key_chain_cli_info,
+ &ietf_key_chain_deviation_info,
#ifdef HAVE_RIPD
&frr_ripd_cli_info,
@@ -199,20 +202,20 @@ static const struct frr_yang_module_info *const mgmt_yang_modules[] = {
/* clang-format off */
FRR_DAEMON_INFO(mgmtd, MGMTD,
- .vty_port = MGMTD_VTY_PORT,
- .proghelp = "FRR Management Daemon.",
+ .vty_port = MGMTD_VTY_PORT,
+ .proghelp = "FRR Management Daemon.",
- .signals = mgmt_signals,
- .n_signals = array_size(mgmt_signals),
+ .signals = mgmt_signals,
+ .n_signals = array_size(mgmt_signals),
- .privs = &mgmt_privs,
+ .privs = &mgmt_privs,
- .yang_modules = mgmt_yang_modules,
- .n_yang_modules = array_size(mgmt_yang_modules),
+ .yang_modules = mgmt_yang_modules,
+ .n_yang_modules = array_size(mgmt_yang_modules),
- /* avoid libfrr trying to read our config file for us */
- .flags = FRR_MANUAL_VTY_START | FRR_NO_SPLIT_CONFIG,
-);
+ /* avoid libfrr trying to read our config file for us */
+ .flags = FRR_MANUAL_VTY_START | FRR_NO_SPLIT_CONFIG,
+ );
/* clang-format on */
#define DEPRECATED_OPTIONS ""
diff --git a/mgmtd/mgmt_memory.c b/mgmtd/mgmt_memory.c
index 0fce61a..72ccca0 100644
--- a/mgmtd/mgmt_memory.c
+++ b/mgmtd/mgmt_memory.c
@@ -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");
diff --git a/mgmtd/mgmt_memory.h b/mgmtd/mgmt_memory.h
index d5b6aa6..e28586e 100644
--- a/mgmtd/mgmt_memory.h
+++ b/mgmtd/mgmt_memory.h
@@ -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);
diff --git a/mgmtd/mgmt_testc.c b/mgmtd/mgmt_testc.c
index 7e3ded8..8bb07ed 100644
--- a/mgmtd/mgmt_testc.c
+++ b/mgmtd/mgmt_testc.c
@@ -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);
@@ -87,6 +88,10 @@ static const struct frr_yang_module_info frr_ripd_info = {
.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;
@@ -133,8 +139,11 @@ static void sigusr1(void)
static void quit(int exit_code)
{
EVENT_OFF(event_timeout);
- frr_fini();
darr_free(__client_cbs.notif_xpaths);
+ darr_free(__client_cbs.rpc_xpaths);
+
+ frr_fini();
+
exit(exit_code);
}
@@ -150,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");
@@ -161,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;
@@ -215,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);
diff --git a/mgmtd/mgmt_txn.c b/mgmtd/mgmt_txn.c
index c1cb33f..0f0cccb 100644
--- a/mgmtd/mgmt_txn.c
+++ b/mgmtd/mgmt_txn.c
@@ -29,8 +29,8 @@ enum mgmt_txn_event {
MGMTD_TXN_PROC_COMMITCFG,
MGMTD_TXN_PROC_GETCFG,
MGMTD_TXN_PROC_GETTREE,
+ MGMTD_TXN_PROC_RPC,
MGMTD_TXN_COMMITCFG_TIMEOUT,
- MGMTD_TXN_GETTREE_TIMEOUT,
};
PREDECL_LIST(mgmt_txn_reqs);
@@ -49,7 +49,6 @@ struct mgmt_set_cfg_req {
enum mgmt_commit_phase {
MGMTD_COMMIT_PHASE_PREPARE_CFG = 0,
MGMTD_COMMIT_PHASE_TXN_CREATE,
- MGMTD_COMMIT_PHASE_SEND_CFG,
MGMTD_COMMIT_PHASE_APPLY_CFG,
MGMTD_COMMIT_PHASE_TXN_DELETE,
MGMTD_COMMIT_PHASE_MAX
@@ -62,8 +61,6 @@ static inline const char *mgmt_commit_phase2str(enum mgmt_commit_phase cmt_phase
return "PREP-CFG";
case MGMTD_COMMIT_PHASE_TXN_CREATE:
return "CREATE-TXN";
- case MGMTD_COMMIT_PHASE_SEND_CFG:
- return "SEND-CFG";
case MGMTD_COMMIT_PHASE_APPLY_CFG:
return "APPLY-CFG";
case MGMTD_COMMIT_PHASE_TXN_DELETE:
@@ -95,6 +92,11 @@ DECLARE_LIST(mgmt_txn_batches, struct mgmt_txn_be_cfg_batch, list_linkage);
#define FOREACH_TXN_CFG_BATCH_IN_LIST(list, batch) \
frr_each_safe (mgmt_txn_batches, list, batch)
+struct mgmt_edit_req {
+ char xpath_created[XPATH_MAXLEN];
+ bool unlock;
+};
+
struct mgmt_commit_cfg_req {
Mgmtd__DatastoreId src_ds_id;
struct mgmt_ds_ctx *src_ds_ctx;
@@ -113,6 +115,12 @@ struct mgmt_commit_cfg_req {
enum mgmt_commit_phase be_phase[MGMTD_BE_CLIENT_ID_MAX];
/*
+ * Additional information when the commit is triggered by native edit
+ * request.
+ */
+ struct mgmt_edit_req *edit;
+
+ /*
* Set of config changes to commit. This is used only
* when changes are NOT to be determined by comparing
* candidate and running DSs. This is typically used
@@ -181,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;
@@ -189,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;
@@ -214,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 */
@@ -246,6 +265,10 @@ struct mgmt_txn_ctx {
*/
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.
*/
@@ -409,8 +432,16 @@ 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:
- case MGMTD_TXN_GETTREE_TIMEOUT:
break;
}
@@ -449,6 +480,8 @@ static void mgmt_txn_req_free(struct mgmt_txn_req **txn_req)
cleanup = (ccreq->phase >= MGMTD_COMMIT_PHASE_TXN_CREATE &&
ccreq->phase < MGMTD_COMMIT_PHASE_TXN_DELETE);
+ XFREE(MTYPE_MGMTD_TXN_REQ, ccreq->edit);
+
FOREACH_MGMTD_BE_CLIENT_ID (id) {
/*
* Send TXN_DELETE to cleanup state for this
@@ -498,8 +531,16 @@ 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:
- case MGMTD_TXN_GETTREE_TIMEOUT:
break;
}
@@ -610,7 +651,8 @@ static void mgmt_txn_process_set_cfg(struct event *thread)
->dst_ds_id,
txn_req->req.set_cfg
->dst_ds_ctx,
- false, false, true);
+ false, false, true,
+ NULL);
if (mm->perf_stats_en)
gettimeofday(&cmt_stats->last_start, NULL);
@@ -661,7 +703,8 @@ static int mgmt_txn_send_commit_cfg_reply(struct mgmt_txn_ctx *txn,
* b/c right now that is special cased.. that special casing should be
* removed; however...
*/
- if (!txn->commit_cfg_req->req.commit_cfg.implicit && txn->session_id &&
+ if (!txn->commit_cfg_req->req.commit_cfg.edit &&
+ !txn->commit_cfg_req->req.commit_cfg.implicit && txn->session_id &&
!txn->commit_cfg_req->req.commit_cfg.rollback &&
mgmt_fe_send_commit_cfg_reply(txn->session_id, txn->txn_id,
txn->commit_cfg_req->req.commit_cfg
@@ -677,7 +720,8 @@ static int mgmt_txn_send_commit_cfg_reply(struct mgmt_txn_ctx *txn,
txn->txn_id, txn->session_id);
}
- if (txn->commit_cfg_req->req.commit_cfg.implicit && txn->session_id &&
+ if (!txn->commit_cfg_req->req.commit_cfg.edit &&
+ txn->commit_cfg_req->req.commit_cfg.implicit && txn->session_id &&
!txn->commit_cfg_req->req.commit_cfg.rollback &&
mgmt_fe_send_set_cfg_reply(txn->session_id, txn->txn_id,
txn->commit_cfg_req->req.commit_cfg
@@ -691,6 +735,21 @@ static int mgmt_txn_send_commit_cfg_reply(struct mgmt_txn_ctx *txn,
txn->txn_id, txn->session_id);
}
+ if (txn->commit_cfg_req->req.commit_cfg.edit &&
+ mgmt_fe_adapter_send_edit_reply(txn->session_id, txn->txn_id,
+ txn->commit_cfg_req->req_id,
+ txn->commit_cfg_req->req.commit_cfg
+ .edit->unlock,
+ true,
+ txn->commit_cfg_req->req.commit_cfg
+ .edit->xpath_created,
+ success ? 0 : -1,
+ error_if_any) != 0) {
+ __log_err("Failed to send EDIT-REPLY txn-id: %" PRIu64
+ " session-id: %" PRIu64,
+ txn->txn_id, txn->session_id);
+ }
+
if (success) {
/* Stop the commit-timeout timer */
/* XXX why only on success? */
@@ -855,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;
@@ -919,8 +980,8 @@ static int mgmt_txn_create_config_batches(struct mgmt_txn_req *txn_req,
}
if (!chg_clients)
- __log_err("No connected daemon is interested in XPATH %s",
- xpath);
+ __dbg("Daemons interested in XPATH are not currently connected: %s",
+ xpath);
cmtcfg_req->clients |= chg_clients;
@@ -931,7 +992,7 @@ static int mgmt_txn_create_config_batches(struct mgmt_txn_req *txn_req,
if (!num_chgs) {
(void)mgmt_txn_send_commit_cfg_reply(txn_req->txn,
MGMTD_NO_CFG_CHANGES,
- "No changes found to commit!");
+ "No connected daemons interested in changes");
return -1;
}
@@ -1183,13 +1244,10 @@ static int mgmt_txn_send_be_cfg_data(struct mgmt_txn_ctx *txn,
cmtcfg_req->cmt_stats->last_num_cfgdata_reqs++;
}
- cmtcfg_req->be_phase[adapter->id] = MGMTD_COMMIT_PHASE_SEND_CFG;
-
/*
- * This could be the last Backend Client to send CFGDATA_CREATE_REQ to.
- * Try moving the commit to next phase.
+ * We don't advance the phase here, instead that is driven by the
+ * cfg_reply.
*/
- mgmt_try_move_commit_to_next_phase(txn, cmtcfg_req);
return 0;
}
@@ -1284,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)
{
@@ -1311,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.
*
@@ -1390,24 +1500,6 @@ static void mgmt_txn_process_commit_cfg(struct event *thread)
*/
mgmt_txn_send_be_txn_create(txn);
break;
- case MGMTD_COMMIT_PHASE_SEND_CFG:
- if (mm->perf_stats_en)
- gettimeofday(&cmtcfg_req->cmt_stats->send_cfg_start,
- NULL);
- /*
- * All CFGDATA_CREATE_REQ should have been sent to
- * Backend by now.
- */
-#ifndef MGMTD_LOCAL_VALIDATIONS_ENABLED
- __dbg("txn-id: %" PRIu64 " session-id: %" PRIu64
- " trigger sending CFG_VALIDATE_REQ to all backend clients",
- txn->txn_id, txn->session_id);
-#else /* ifndef MGMTD_LOCAL_VALIDATIONS_ENABLED */
- __dbg("txn-id: %" PRIu64 " session-id: %" PRIu64
- " trigger sending CFG_APPLY_REQ to all backend clients",
- txn->txn_id, txn->session_id);
-#endif /* ifndef MGMTD_LOCAL_VALIDATIONS_ENABLED */
- break;
case MGMTD_COMMIT_PHASE_APPLY_CFG:
if (mm->perf_stats_en)
gettimeofday(&cmtcfg_req->cmt_stats->apply_cfg_start,
@@ -1512,7 +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_GETTREE_TIMEOUT:
+ case MGMTD_TXN_PROC_RPC:
case MGMTD_TXN_COMMITCFG_TIMEOUT:
__log_err("Invalid Txn-Req-Event %u", txn_req->req_event);
break;
@@ -1718,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)
@@ -1886,12 +1979,8 @@ static void mgmt_txn_register_event(struct mgmt_txn_ctx *txn,
MGMTD_TXN_CFG_COMMIT_MAX_DELAY_SEC,
&txn->comm_cfg_timeout);
break;
- case MGMTD_TXN_GETTREE_TIMEOUT:
- event_add_timer(mgmt_txn_tm, txn_get_tree_timeout, txn,
- MGMTD_TXN_GET_TREE_MAX_DELAY_SEC,
- &txn->get_tree_timeout);
- break;
case MGMTD_TXN_PROC_GETTREE:
+ case MGMTD_TXN_PROC_RPC:
assert(!"code bug do not register this event");
break;
}
@@ -2044,7 +2133,7 @@ int mgmt_txn_send_commit_config_req(uint64_t txn_id, uint64_t req_id,
Mgmtd__DatastoreId dst_ds_id,
struct mgmt_ds_ctx *dst_ds_ctx,
bool validate_only, bool abort,
- bool implicit)
+ bool implicit, struct mgmt_edit_req *edit)
{
struct mgmt_txn_ctx *txn;
struct mgmt_txn_req *txn_req;
@@ -2068,6 +2157,7 @@ int mgmt_txn_send_commit_config_req(uint64_t txn_id, uint64_t req_id,
txn_req->req.commit_cfg.validate_only = validate_only;
txn_req->req.commit_cfg.abort = abort;
txn_req->req.commit_cfg.implicit = implicit;
+ txn_req->req.commit_cfg.edit = edit;
txn_req->req.commit_cfg.cmt_stats =
mgmt_fe_get_session_commit_stats(txn->session_id);
@@ -2451,6 +2541,110 @@ state:
return 0;
}
+int mgmt_txn_send_edit(uint64_t txn_id, uint64_t req_id,
+ Mgmtd__DatastoreId ds_id, struct mgmt_ds_ctx *ds_ctx,
+ Mgmtd__DatastoreId commit_ds_id,
+ struct mgmt_ds_ctx *commit_ds_ctx, bool unlock,
+ bool commit, LYD_FORMAT request_type, uint8_t flags,
+ uint8_t operation, const char *xpath, const char *data)
+{
+ struct mgmt_txn_ctx *txn;
+ struct mgmt_edit_req *edit;
+ struct nb_config *nb_config;
+ char errstr[BUFSIZ];
+ int ret;
+
+ txn = mgmt_txn_id2ctx(txn_id);
+ if (!txn)
+ return -1;
+
+ edit = XCALLOC(MTYPE_MGMTD_TXN_REQ, sizeof(struct mgmt_edit_req));
+
+ nb_config = mgmt_ds_get_nb_config(ds_ctx);
+ assert(nb_config);
+
+ ret = nb_candidate_edit_tree(nb_config, operation, request_type, xpath,
+ data, edit->xpath_created, errstr,
+ sizeof(errstr));
+ if (ret)
+ goto reply;
+
+ if (commit) {
+ edit->unlock = unlock;
+
+ mgmt_txn_send_commit_config_req(txn_id, req_id, ds_id, ds_ctx,
+ commit_ds_id, commit_ds_ctx,
+ false, false, true, edit);
+ return 0;
+ }
+reply:
+ mgmt_fe_adapter_send_edit_reply(txn->session_id, txn->txn_id, req_id,
+ unlock, commit, edit->xpath_created,
+ ret ? -1 : 0, errstr);
+
+ XFREE(MTYPE_MGMTD_TXN_REQ, edit);
+
+ 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.
*/
@@ -2461,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) {
@@ -2473,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,
@@ -2493,13 +2692,23 @@ 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);
+ if (errstr) {
+ XFREE(MTYPE_MGMTD_ERR, rpc->errstr);
+ 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:
case MGMTD_TXN_PROC_COMMITCFG:
case MGMTD_TXN_PROC_GETCFG:
case MGMTD_TXN_COMMITCFG_TIMEOUT:
- case MGMTD_TXN_GETTREE_TIMEOUT:
default:
assert(!"non-native req event in native erorr path");
return -1;
@@ -2581,6 +2790,77 @@ 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;
+ struct lyd_node *tree;
+ size_t data_len = msg_len - sizeof(*reply_msg);
+ LY_ERR err = LY_SUCCESS;
+
+ 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;
+
+ tree = NULL;
+ if (data_len)
+ err = yang_parse_rpc(rpc->xpath, reply_msg->result_type,
+ reply_msg->data, true, &tree);
+ if (err) {
+ __log_err("RPC reply from %s for txn-id %" PRIu64
+ " req_id %" PRIu64 " error parsing result of type %u: %s",
+ adapter->name, txn_id, req_id, reply_msg->result_type,
+ ly_strerrcode(err));
+ }
+ if (!err && tree) {
+ if (!rpc->client_results)
+ rpc->client_results = tree;
+ else
+ err = lyd_merge_siblings(&rpc->client_results, tree,
+ LYD_MERGE_DESTRUCT);
+ if (err) {
+ __log_err("RPC reply from %s for txn-id %" PRIu64
+ " req_id %" PRIu64 " error merging result: %s",
+ adapter->name, txn_id, req_id,
+ ly_strerrcode(err));
+ }
+ }
+ if (err) {
+ XFREE(MTYPE_MGMTD_ERR, rpc->errstr);
+ 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;
diff --git a/mgmtd/mgmt_txn.h b/mgmtd/mgmt_txn.h
index b719832..b6ca288 100644
--- a/mgmtd/mgmt_txn.h
+++ b/mgmtd/mgmt_txn.h
@@ -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
@@ -43,11 +44,13 @@
PREDECL_LIST(mgmt_txns);
struct mgmt_master;
+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)
@@ -59,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";
@@ -171,16 +176,17 @@ extern int mgmt_txn_send_set_config_req(uint64_t txn_id, uint64_t req_id,
* implicit
* TRUE if the commit is implicit, FALSE otherwise.
*
+ * edit
+ * Additional info when triggered from native edit request.
+ *
* Returns:
* 0 on success, -1 on failures.
*/
-extern int mgmt_txn_send_commit_config_req(uint64_t txn_id, uint64_t req_id,
- Mgmtd__DatastoreId src_ds_id,
- struct mgmt_ds_ctx *dst_ds_ctx,
- Mgmtd__DatastoreId dst_ds_id,
- struct mgmt_ds_ctx *src_ds_ctx,
- bool validate_only, bool abort,
- bool implicit);
+extern int mgmt_txn_send_commit_config_req(
+ uint64_t txn_id, uint64_t req_id, Mgmtd__DatastoreId src_ds_id,
+ struct mgmt_ds_ctx *dst_ds_ctx, Mgmtd__DatastoreId dst_ds_id,
+ struct mgmt_ds_ctx *src_ds_ctx, bool validate_only, bool abort,
+ bool implicit, struct mgmt_edit_req *edit);
/*
* Send get-{cfg,data} request to be processed later in transaction.
@@ -219,6 +225,50 @@ extern int mgmt_txn_send_get_tree_oper(uint64_t txn_id, uint64_t req_id,
uint32_t wd_options, bool simple_xpath,
const char *xpath);
+/**
+ * Send edit request.
+ *
+ * Args:
+ * txn_id: Transaction identifier.
+ * req_id: FE client request identifier.
+ * ds_id: Datastore ID.
+ * ds_ctx: Datastore context.
+ * commit_ds_id: Commit datastore ID.
+ * commit_ds_ctx: Commit datastore context.
+ * unlock: Unlock datastores after the edit.
+ * commit: Commit the candidate datastore after the edit.
+ * request_type: LYD_FORMAT request type.
+ * flags: option flags for the request.
+ * operation: The operation to perform.
+ * xpath: The xpath of data node to edit.
+ * data: The data tree.
+ */
+extern int
+mgmt_txn_send_edit(uint64_t txn_id, uint64_t req_id, Mgmtd__DatastoreId ds_id,
+ struct mgmt_ds_ctx *ds_ctx, Mgmtd__DatastoreId commit_ds_id,
+ struct mgmt_ds_ctx *commit_ds_ctx, bool unlock, bool commit,
+ 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.
*/
@@ -285,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.
*/
diff --git a/mgmtd/mgmt_vty.c b/mgmtd/mgmt_vty.c
index bbc1077..8ccb463 100644
--- a/mgmtd/mgmt_vty.c
+++ b/mgmtd/mgmt_vty.c
@@ -12,6 +12,7 @@
#include "command.h"
#include "filter.h"
#include "json.h"
+#include "keychain.h"
#include "network.h"
#include "northbound_cli.h"
#include "routemap.h"
@@ -237,6 +238,79 @@ DEFPY(mgmt_replace_config_data, mgmt_replace_config_data_cmd,
return CMD_SUCCESS;
}
+DEFPY(mgmt_edit, mgmt_edit_cmd,
+ "mgmt edit {create|delete|merge|replace|remove}$op XPATH [json|xml]$fmt [lock$lock] [commit$commit] [DATA]",
+ MGMTD_STR
+ "Edit configuration data\n"
+ "Create data\n"
+ "Delete data\n"
+ "Merge data\n"
+ "Replace data\n"
+ "Remove data\n"
+ "XPath expression specifying the YANG data path\n"
+ "JSON input format (default)\n"
+ "XML input format\n"
+ "Lock the datastores automatically\n"
+ "Commit the changes automatically\n"
+ "Data tree\n")
+{
+ LYD_FORMAT format = (fmt && fmt[0] == 'x') ? LYD_XML : LYD_JSON;
+ uint8_t operation;
+ uint8_t flags = 0;
+
+ switch (op[2]) {
+ case 'e':
+ operation = NB_OP_CREATE_EXCL;
+ break;
+ case 'l':
+ operation = NB_OP_DELETE;
+ break;
+ case 'r':
+ operation = NB_OP_MODIFY;
+ break;
+ case 'p':
+ operation = NB_OP_REPLACE;
+ break;
+ case 'm':
+ operation = NB_OP_DESTROY;
+ break;
+ default:
+ vty_out(vty, "Invalid operation!\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (!data && (operation == NB_OP_CREATE_EXCL ||
+ operation == NB_OP_MODIFY || operation == NB_OP_REPLACE)) {
+ vty_out(vty, "Data tree is missing!\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (lock)
+ flags |= EDIT_FLAG_IMPLICIT_LOCK;
+
+ if (commit)
+ flags |= EDIT_FLAG_IMPLICIT_COMMIT;
+
+ vty_mgmt_send_edit_req(vty, MGMT_MSG_DATASTORE_CANDIDATE, format, flags,
+ operation, xpath, data);
+ 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
@@ -600,6 +674,7 @@ void mgmt_vty_init(void)
filter_cli_init();
route_map_cli_init();
affinity_map_init();
+ keychain_cli_init();
/*
* Initialize command handling from VTYSH connection.
@@ -641,6 +716,8 @@ void mgmt_vty_init(void)
install_element(CONFIG_NODE, &mgmt_delete_config_data_cmd);
install_element(CONFIG_NODE, &mgmt_remove_config_data_cmd);
install_element(CONFIG_NODE, &mgmt_replace_config_data_cmd);
+ install_element(CONFIG_NODE, &mgmt_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);
diff --git a/mgmtd/subdir.am b/mgmtd/subdir.am
index 1624c6e..14544c4 100644
--- a/mgmtd/subdir.am
+++ b/mgmtd/subdir.am
@@ -16,6 +16,7 @@ clippy_scan += \
lib_LTLIBRARIES += mgmtd/libmgmt_be_nb.la
mgmtd_libmgmt_be_nb_la_SOURCES = \
+ mgmtd/mgmt_be_nb.c \
zebra/zebra_cli.c \
# end
nodist_mgmtd_libmgmt_be_nb_la_SOURCES = \
@@ -61,7 +62,6 @@ mgmtd_mgmtd_SOURCES = \
nodist_mgmtd_mgmtd_SOURCES = \
yang/frr-zebra.yang.c \
yang/frr-zebra-route-map.yang.c \
- yang/ietf/ietf-netconf-acm.yang.c \
yang/ietf/ietf-netconf.yang.c \
yang/ietf/ietf-netconf-with-defaults.yang.c \
# nothing