diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 04:24:31 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 04:24:31 +0000 |
commit | acb594b1d825c6e12369cebb941968ec08c840ce (patch) | |
tree | d544788908e7353a4f117e2991f15f4236a0c963 /mgmtd | |
parent | Adding upstream version 9.1. (diff) | |
download | frr-acb594b1d825c6e12369cebb941968ec08c840ce.tar.xz frr-acb594b1d825c6e12369cebb941968ec08c840ce.zip |
Adding upstream version 10.0.upstream/10.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'mgmtd')
-rw-r--r-- | mgmtd/mgmt.c | 17 | ||||
-rw-r--r-- | mgmtd/mgmt.h | 8 | ||||
-rw-r--r-- | mgmtd/mgmt_be_adapter.c | 895 | ||||
-rw-r--r-- | mgmtd/mgmt_be_adapter.h | 107 | ||||
-rw-r--r-- | mgmtd/mgmt_defines.h | 58 | ||||
-rw-r--r-- | mgmtd/mgmt_ds.c | 46 | ||||
-rw-r--r-- | mgmtd/mgmt_ds.h | 6 | ||||
-rw-r--r-- | mgmtd/mgmt_fe_adapter.c | 596 | ||||
-rw-r--r-- | mgmtd/mgmt_fe_adapter.h | 52 | ||||
-rw-r--r-- | mgmtd/mgmt_history.c | 35 | ||||
-rw-r--r-- | mgmtd/mgmt_main.c | 154 | ||||
-rw-r--r-- | mgmtd/mgmt_memory.c | 1 | ||||
-rw-r--r-- | mgmtd/mgmt_memory.h | 1 | ||||
-rw-r--r-- | mgmtd/mgmt_testc.c | 230 | ||||
-rw-r--r-- | mgmtd/mgmt_txn.c | 1392 | ||||
-rw-r--r-- | mgmtd/mgmt_txn.h | 82 | ||||
-rw-r--r-- | mgmtd/mgmt_vty.c | 196 | ||||
-rw-r--r-- | mgmtd/subdir.am | 53 |
18 files changed, 2439 insertions, 1490 deletions
diff --git a/mgmtd/mgmt.c b/mgmtd/mgmt.c index 77c4473..8d41643 100644 --- a/mgmtd/mgmt.c +++ b/mgmtd/mgmt.c @@ -52,17 +52,26 @@ void mgmt_init(void) /* Initialize the MGMTD Frontend Adapter Module */ mgmt_fe_adapter_init(mm->master); - /* Initialize the CLI frontend client */ + /* + * Initialize the CLI frontend client -- this queues an event for the + * client to short-circuit connect to the server (ourselves). + */ vty_init_mgmt_fe(); - /* MGMTD VTY commands installation. */ + /* + * MGMTD VTY commands installation -- the frr lib code will queue an + * event to read the config files which needs to happen after the + * connect from above is made. + */ mgmt_vty_init(); /* * Initialize the MGMTD Backend Adapter Module * - * We do this after the FE stuff so that we always read our config file - * prior to any BE connection. + * We do this after the FE stuff so that we have read our config file + * prior to any BE connection. Setting up the server will queue a + * "socket read" event to accept BE connections. So the code is counting + * on the above 2 events to run prior to any `accept` event from here. */ mgmt_be_adapter_init(mm->master); } diff --git a/mgmtd/mgmt.h b/mgmtd/mgmt.h index d89d76f..665e8d8 100644 --- a/mgmtd/mgmt.h +++ b/mgmtd/mgmt.h @@ -13,14 +13,13 @@ #include "vrf.h" #include "defaults.h" #include "stream.h" +#include "mgmt_defines.h" #include "mgmtd/mgmt_memory.h" -#include "mgmtd/mgmt_defines.h" #include "mgmtd/mgmt_history.h" #include "mgmtd/mgmt_txn.h" #include "mgmtd/mgmt_ds.h" -#define MGMTD_VTY_PORT 2623 #define MGMTD_SOCKET_BUF_SIZE 65535 #define MGMTD_MAX_COMMIT_LIST 10 @@ -70,11 +69,6 @@ struct mgmt_master { extern struct mgmt_master *mm; /* Inline functions */ -static inline unsigned long timeval_elapsed(struct timeval a, struct timeval b) -{ - return (((a.tv_sec - b.tv_sec) * TIMER_SECOND_MICRO) - + (a.tv_usec - b.tv_usec)); -} /* * Remove trailing separator from a string. diff --git a/mgmtd/mgmt_be_adapter.c b/mgmtd/mgmt_be_adapter.c index 399fdaf..b311bf4 100644 --- a/mgmtd/mgmt_be_adapter.c +++ b/mgmtd/mgmt_be_adapter.c @@ -10,79 +10,141 @@ #include <zebra.h> #include "darr.h" #include "frrevent.h" +#include "frrstr.h" #include "sockopt.h" #include "network.h" #include "libfrr.h" #include "mgmt_msg.h" +#include "mgmt_msg_native.h" #include "mgmt_pb.h" #include "mgmtd/mgmt.h" #include "mgmtd/mgmt_memory.h" #include "mgmt_be_client.h" #include "mgmtd/mgmt_be_adapter.h" -#define MGMTD_BE_ADAPTER_DBG(fmt, ...) \ +#define __dbg(fmt, ...) \ DEBUGD(&mgmt_debug_be, "BE-ADAPTER: %s: " fmt, __func__, ##__VA_ARGS__) -#define MGMTD_BE_ADAPTER_ERR(fmt, ...) \ +#define __log_err(fmt, ...) \ zlog_err("BE-ADAPTER: %s: ERROR: " fmt, __func__, ##__VA_ARGS__) #define FOREACH_ADAPTER_IN_LIST(adapter) \ frr_each_safe (mgmt_be_adapters, &mgmt_be_adapters, (adapter)) +/* ---------- */ +/* Client IDs */ +/* ---------- */ + +const char *mgmt_be_client_names[MGMTD_BE_CLIENT_ID_MAX + 1] = { + [MGMTD_BE_CLIENT_ID_TESTC] = "mgmtd-testc", /* always first */ + [MGMTD_BE_CLIENT_ID_ZEBRA] = "zebra", +#ifdef HAVE_RIPD + [MGMTD_BE_CLIENT_ID_RIPD] = "ripd", +#endif +#ifdef HAVE_RIPNGD + [MGMTD_BE_CLIENT_ID_RIPNGD] = "ripngd", +#endif +#ifdef HAVE_STATICD + [MGMTD_BE_CLIENT_ID_STATICD] = "staticd", +#endif + [MGMTD_BE_CLIENT_ID_MAX] = "Unknown/Invalid", +}; + +/* ------------- */ +/* XPATH MAPPING */ +/* ------------- */ + /* - * Mapping of YANG XPath regular expressions to - * their corresponding backend clients. + * Mapping of YANG XPath prefixes to their corresponding backend clients. */ struct mgmt_be_xpath_map { - char *xpath_regexp; - uint subscr_info[MGMTD_BE_CLIENT_ID_MAX]; + char *xpath_prefix; + uint64_t clients; }; -struct mgmt_be_client_xpath { - const char *xpath; - uint subscribed; +/* + * Each client gets their own map, but also union all the strings into the + * above map as well. + */ + +static const char *const zebra_config_xpaths[] = { + "/frr-affinity-map:lib", + "/frr-filter:lib", + "/frr-route-map:lib", + "/frr-zebra:zebra", + "/frr-interface:lib", + "/frr-vrf:lib", + NULL, }; -struct mgmt_be_client_xpath_map { - struct mgmt_be_client_xpath *xpaths; - uint nxpaths; +static const char *const zebra_oper_xpaths[] = { + "/frr-interface:lib/interface", + "/frr-vrf:lib/vrf/frr-zebra:zebra", + "/frr-zebra:zebra", + NULL, }; -struct mgmt_be_get_adapter_config_params { - struct mgmt_be_client_adapter *adapter; - struct nb_config_cbs *cfg_chgs; - uint32_t seq; +#if HAVE_RIPD +static const char *const ripd_config_xpaths[] = { + "/frr-filter:lib", + "/frr-interface:lib/interface", + "/frr-ripd:ripd", + "/frr-route-map:lib", + "/frr-vrf:lib", + NULL, +}; +static const char *const ripd_oper_xpaths[] = { + "/frr-ripd:ripd", + NULL, }; +#endif + +#if HAVE_RIPNGD +static const char *const ripngd_config_xpaths[] = { + "/frr-filter:lib", + "/frr-interface:lib/interface", + "/frr-ripngd:ripngd", + "/frr-route-map:lib", + "/frr-vrf:lib", + NULL, +}; +static const char *const ripngd_oper_xpaths[] = { + "/frr-ripngd:ripngd", + NULL, +}; +#endif -/* - * Each client gets their own map, but also union all the strings into the - * above map as well. - */ #if HAVE_STATICD -static struct mgmt_be_client_xpath staticd_xpaths[] = { - { - .xpath = "/frr-vrf:lib/*", - .subscribed = MGMT_SUBSCR_VALIDATE_CFG | MGMT_SUBSCR_NOTIFY_CFG, - }, - { - .xpath = "/frr-interface:lib/*", - .subscribed = MGMT_SUBSCR_VALIDATE_CFG | MGMT_SUBSCR_NOTIFY_CFG, - }, - { - .xpath = - "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/*", - .subscribed = MGMT_SUBSCR_VALIDATE_CFG | MGMT_SUBSCR_NOTIFY_CFG, - }, +static const char *const staticd_config_xpaths[] = { + "/frr-vrf:lib", + "/frr-interface:lib", + "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd", + NULL, }; #endif -static struct mgmt_be_client_xpath_map - mgmt_client_xpaths[MGMTD_BE_CLIENT_ID_MAX] = { +static const char *const *be_client_config_xpaths[MGMTD_BE_CLIENT_ID_MAX] = { + [MGMTD_BE_CLIENT_ID_ZEBRA] = zebra_config_xpaths, +#ifdef HAVE_RIPD + [MGMTD_BE_CLIENT_ID_RIPD] = ripd_config_xpaths, +#endif +#ifdef HAVE_RIPNGD + [MGMTD_BE_CLIENT_ID_RIPNGD] = ripngd_config_xpaths, +#endif #ifdef HAVE_STATICD - [MGMTD_BE_CLIENT_ID_STATICD] = {staticd_xpaths, - array_size(staticd_xpaths)}, + [MGMTD_BE_CLIENT_ID_STATICD] = staticd_config_xpaths, #endif }; +static const char *const *be_client_oper_xpaths[MGMTD_BE_CLIENT_ID_MAX] = { +#ifdef HAVE_RIPD + [MGMTD_BE_CLIENT_ID_RIPD] = ripd_oper_xpaths, +#endif +#ifdef HAVE_RIPNGD + [MGMTD_BE_CLIENT_ID_RIPNGD] = ripngd_oper_xpaths, +#endif + [MGMTD_BE_CLIENT_ID_ZEBRA] = zebra_oper_xpaths, +}; + /* * We would like to have a better ADT than one with O(n) comparisons * @@ -91,7 +153,10 @@ static struct mgmt_be_client_xpath_map * says this probably involves exact match/no-match on a stem in the map array * or something like that. */ -static struct mgmt_be_xpath_map *mgmt_xpath_map; + +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 event_loop *mgmt_loop; static struct msg_server mgmt_be_server = {.fd = -1}; @@ -101,12 +166,33 @@ static struct mgmt_be_adapters_head mgmt_be_adapters; static struct mgmt_be_client_adapter *mgmt_be_adapters_by_id[MGMTD_BE_CLIENT_ID_MAX]; + /* Forward declarations */ static void mgmt_be_adapter_sched_init_event(struct mgmt_be_client_adapter *adapter); -static uint mgmt_be_get_subscr_for_xpath_and_client( - const char *xpath, enum mgmt_be_client_id client_id, uint subscr_mask); +static bool be_is_client_interested(const char *xpath, + enum mgmt_be_client_id id, bool config); + +const char *mgmt_be_client_id2name(enum mgmt_be_client_id id) +{ + if (id > MGMTD_BE_CLIENT_ID_MAX) + return "invalid client id"; + return mgmt_be_client_names[id]; +} + +static enum mgmt_be_client_id mgmt_be_client_name2id(const char *name) +{ + enum mgmt_be_client_id id; + + FOREACH_MGMTD_BE_CLIENT_ID (id) { + if (!strncmp(mgmt_be_client_names[id], name, + MGMTD_CLIENT_NAME_MAX_LEN)) + return id; + } + + return MGMTD_BE_CLIENT_ID_MAX; +} static struct mgmt_be_client_adapter * mgmt_be_find_adapter_by_fd(int conn_fd) @@ -135,183 +221,103 @@ mgmt_be_find_adapter_by_name(const char *name) } static void mgmt_register_client_xpath(enum mgmt_be_client_id id, - const char *xpath, uint subscribed) + const char *xpath, bool config, bool oper) { - struct mgmt_be_xpath_map *map; + struct mgmt_be_xpath_map **maps, *map; - darr_foreach_p (mgmt_xpath_map, map) - if (!strcmp(xpath, map->xpath_regexp)) { - map->subscr_info[id] = subscribed; + if (config) + maps = &be_cfg_xpath_map; + else if (oper) + maps = &be_oper_xpath_map; + else + maps = &be_notif_xpath_map; + + darr_foreach_p (*maps, map) { + if (!strcmp(xpath, map->xpath_prefix)) { + map->clients |= (1u << id); return; } + } /* we didn't find a matching entry */ - map = darr_append(mgmt_xpath_map); - map->xpath_regexp = XSTRDUP(MTYPE_MGMTD_XPATH, xpath); - map->subscr_info[id] = subscribed; + map = darr_append(*maps); + map->xpath_prefix = XSTRDUP(MTYPE_MGMTD_XPATH, xpath); + map->clients = (1ul << id); } /* - * Load the initial mapping from static init map + * initial the combined maps from per client maps */ static void mgmt_be_xpath_map_init(void) { - struct mgmt_be_client_xpath *init, *end; enum mgmt_be_client_id id; + const char *const *init; - MGMTD_BE_ADAPTER_DBG("Init XPath Maps"); + __dbg("Init XPath Maps"); FOREACH_MGMTD_BE_CLIENT_ID (id) { - init = mgmt_client_xpaths[id].xpaths; - end = init + mgmt_client_xpaths[id].nxpaths; - for (; init < end; init++) { - MGMTD_BE_ADAPTER_DBG(" - XPATH: '%s'", init->xpath); - mgmt_register_client_xpath(id, init->xpath, - init->subscribed); + /* 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); + } + + /* 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); } } - MGMTD_BE_ADAPTER_DBG("Total XPath Maps: %u", darr_len(mgmt_xpath_map)); + __dbg("Total Cfg XPath Maps: %u", darr_len(be_cfg_xpath_map)); + __dbg("Total Oper XPath Maps: %u", darr_len(be_oper_xpath_map)); } static void mgmt_be_xpath_map_cleanup(void) { struct mgmt_be_xpath_map *map; - darr_foreach_p (mgmt_xpath_map, map) - XFREE(MTYPE_MGMTD_XPATH, map->xpath_regexp); - darr_free(mgmt_xpath_map); -} - -static int mgmt_be_eval_regexp_match(const char *xpath_regexp, - const char *xpath) -{ - int match_len = 0, re_indx = 0, xp_indx = 0; - int rexp_len, xpath_len; - bool match = true, re_wild = false, xp_wild = false; - bool delim = false, enter_wild_match = false; - char wild_delim = 0; + darr_foreach_p (be_cfg_xpath_map, map) + XFREE(MTYPE_MGMTD_XPATH, map->xpath_prefix); + darr_free(be_cfg_xpath_map); - rexp_len = strlen(xpath_regexp); - xpath_len = strlen(xpath); - - /* - * Remove the trailing wildcard from the regexp and Xpath. - */ - if (rexp_len && xpath_regexp[rexp_len-1] == '*') - rexp_len--; - if (xpath_len && xpath[xpath_len-1] == '*') - xpath_len--; + darr_foreach_p (be_oper_xpath_map, map) + XFREE(MTYPE_MGMTD_XPATH, map->xpath_prefix); + darr_free(be_oper_xpath_map); - if (!rexp_len || !xpath_len) - return 0; - - for (re_indx = 0, xp_indx = 0; - match && re_indx < rexp_len && xp_indx < xpath_len;) { - match = (xpath_regexp[re_indx] == xpath[xp_indx]); - - /* - * Check if we need to enter wildcard matching. - */ - if (!enter_wild_match && !match && - (xpath_regexp[re_indx] == '*' - || xpath[xp_indx] == '*')) { - /* - * Found wildcard - */ - enter_wild_match = - (xpath_regexp[re_indx-1] == '/' - || xpath_regexp[re_indx-1] == '\'' - || xpath[xp_indx-1] == '/' - || xpath[xp_indx-1] == '\''); - if (enter_wild_match) { - if (xpath_regexp[re_indx] == '*') { - /* - * Begin RE wildcard match. - */ - re_wild = true; - wild_delim = xpath_regexp[re_indx-1]; - } else if (xpath[xp_indx] == '*') { - /* - * Begin XP wildcard match. - */ - xp_wild = true; - wild_delim = xpath[xp_indx-1]; - } - } - } + darr_foreach_p (be_notif_xpath_map, map) + XFREE(MTYPE_MGMTD_XPATH, map->xpath_prefix); + darr_free(be_notif_xpath_map); +} - /* - * Check if we need to exit wildcard matching. - */ - if (enter_wild_match) { - if (re_wild && xpath[xp_indx] == wild_delim) { - /* - * End RE wildcard matching. - */ - re_wild = false; - if (re_indx < rexp_len-1) - re_indx++; - enter_wild_match = false; - } else if (xp_wild - && xpath_regexp[re_indx] == wild_delim) { - /* - * End XP wildcard matching. - */ - xp_wild = false; - if (xp_indx < xpath_len-1) - xp_indx++; - enter_wild_match = false; - } - } - match = (xp_wild || re_wild - || xpath_regexp[re_indx] == xpath[xp_indx]); +/* + * Check if either path or xpath is a prefix of the other. Before checking the + * xpath is converted to a regular path string (e..g, removing key value + * specifiers). + */ +static bool mgmt_be_xpath_prefix(const char *path, const char *xpath) +{ + int xc, pc; - /* - * Check if we found a delimiter in both the Xpaths - */ - if ((xpath_regexp[re_indx] == '/' - && xpath[xp_indx] == '/') - || (xpath_regexp[re_indx] == ']' - && xpath[xp_indx] == ']') - || (xpath_regexp[re_indx] == '[' - && xpath[xp_indx] == '[')) { - /* - * Increment the match count if we have a - * new delimiter. - */ - if (match && re_indx && xp_indx && !delim) - match_len++; - delim = true; - } else { - delim = false; + while ((xc = *xpath++)) { + if (xc == '[') { + xpath = frrstr_skip_over_char(xpath, ']'); + if (!xpath) + return false; + continue; } - - /* - * Proceed to the next character in the RE/XP string as - * necessary. - */ - if (!re_wild) - re_indx++; - if (!xp_wild) - xp_indx++; + pc = *path++; + if (!pc) + return true; + if (pc != xc) + return false; } - - /* - * If we finished matching and the last token was a full match - * increment the match count appropriately. - */ - if (match && !delim && - (xpath_regexp[re_indx] == '/' - || xpath_regexp[re_indx] == ']')) - match_len++; - - return match_len; + return true; } static void mgmt_be_adapter_delete(struct mgmt_be_client_adapter *adapter) { - MGMTD_BE_ADAPTER_DBG("deleting client adapter '%s'", adapter->name); + __dbg("deleting client adapter '%s'", adapter->name); /* * Notify about disconnect for appropriate cleanup @@ -330,8 +336,7 @@ static int mgmt_be_adapter_notify_disconnect(struct msg_conn *conn) { struct mgmt_be_client_adapter *adapter = conn->user; - MGMTD_BE_ADAPTER_DBG("notify disconnect for client adapter '%s'", - adapter->name); + __dbg("notify disconnect for client adapter '%s'", adapter->name); mgmt_be_adapter_delete(adapter); @@ -349,17 +354,14 @@ mgmt_be_adapter_cleanup_old_conn(struct mgmt_be_client_adapter *adapter) /* * We have a Zombie lingering around */ - MGMTD_BE_ADAPTER_DBG( - "Client '%s' (FD:%d) seems to have reconnected. Removing old connection (FD:%d)!", - adapter->name, adapter->conn->fd, - old->conn->fd); + __dbg("Client '%s' (FD:%d) seems to have reconnected. Removing old connection (FD:%d)!", + adapter->name, adapter->conn->fd, old->conn->fd); /* this will/should delete old */ msg_conn_disconnect(old->conn, false); } } } - static int mgmt_be_adapter_send_msg(struct mgmt_be_client_adapter *adapter, Mgmtd__BeMessage *be_msg) { @@ -382,8 +384,8 @@ static int mgmt_be_send_subscr_reply(struct mgmt_be_client_adapter *adapter, be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REPLY; be_msg.subscr_reply = &reply; - MGMTD_FE_CLIENT_DBG("Sending SUBSCR_REPLY client: %s sucess: %u", - adapter->name, success); + __dbg("Sending SUBSCR_REPLY client: %s sucess: %u", adapter->name, + success); return mgmt_be_adapter_send_msg(adapter, &be_msg); } @@ -392,32 +394,30 @@ static int mgmt_be_adapter_handle_msg(struct mgmt_be_client_adapter *adapter, Mgmtd__BeMessage *be_msg) { + const char *xpath; + uint i, num; + /* * protobuf-c adds a max size enum with an internal, and changing by * version, name; cast to an int to avoid unhandled enum warnings */ switch ((int)be_msg->message_case) { case MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REQ: - MGMTD_BE_ADAPTER_DBG( - "Got SUBSCR_REQ from '%s' to %sregister %zu xpaths", - be_msg->subscr_req->client_name, - !be_msg->subscr_req->subscribe_xpaths && - be_msg->subscr_req->n_xpath_reg - ? "de" - : "", - be_msg->subscr_req->n_xpath_reg); + __dbg("Got SUBSCR_REQ from '%s' to register xpaths config: %zu oper: %zu notif: %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); if (strlen(be_msg->subscr_req->client_name)) { strlcpy(adapter->name, be_msg->subscr_req->client_name, sizeof(adapter->name)); adapter->id = mgmt_be_client_name2id(adapter->name); if (adapter->id >= MGMTD_BE_CLIENT_ID_MAX) { - MGMTD_BE_ADAPTER_ERR( - "Unable to resolve adapter '%s' to a valid ID. Disconnecting!", - adapter->name); + __log_err("Unable to resolve adapter '%s' to a valid ID. Disconnecting!", + adapter->name); /* this will/should delete old */ msg_conn_disconnect(adapter->conn, false); - zlog_err("XXX different from original code"); break; } mgmt_be_adapters_by_id[adapter->id] = adapter; @@ -427,19 +427,34 @@ mgmt_be_adapter_handle_msg(struct mgmt_be_client_adapter *adapter, mgmt_be_adapter_sched_init_event(adapter); } - if (be_msg->subscr_req->n_xpath_reg) - /* we aren't handling dynamic xpaths yet */ - mgmt_be_send_subscr_reply(adapter, false); - else - mgmt_be_send_subscr_reply(adapter, true); + 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); + } + + 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); + } + + 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_be_send_subscr_reply(adapter, true); break; case MGMTD__BE_MESSAGE__MESSAGE_TXN_REPLY: - MGMTD_BE_ADAPTER_DBG( - "Got %s TXN_REPLY from '%s' txn-id %" PRIx64 - " with '%s'", - be_msg->txn_reply->create ? "Create" : "Delete", - adapter->name, be_msg->txn_reply->txn_id, - be_msg->txn_reply->success ? "success" : "failure"); + __dbg("Got %s TXN_REPLY from '%s' txn-id %" PRIx64 " with '%s'", + be_msg->txn_reply->create ? "Create" : "Delete", + adapter->name, be_msg->txn_reply->txn_id, + be_msg->txn_reply->success ? "success" : "failure"); /* * Forward the TXN_REPLY to txn module. */ @@ -449,58 +464,40 @@ mgmt_be_adapter_handle_msg(struct mgmt_be_client_adapter *adapter, be_msg->txn_reply->success, adapter); break; case MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REPLY: - MGMTD_BE_ADAPTER_DBG( - "Got CFGDATA_REPLY from '%s' txn-id %" PRIx64 - " batch-id %" PRIu64 " err:'%s'", - adapter->name, be_msg->cfg_data_reply->txn_id, - be_msg->cfg_data_reply->batch_id, - be_msg->cfg_data_reply->error_if_any - ? be_msg->cfg_data_reply->error_if_any - : "None"); + __dbg("Got CFGDATA_REPLY from '%s' txn-id %" PRIx64 " err:'%s'", + adapter->name, be_msg->cfg_data_reply->txn_id, + be_msg->cfg_data_reply->error_if_any + ? be_msg->cfg_data_reply->error_if_any + : "None"); /* * Forward the CGFData-create reply to txn module. */ mgmt_txn_notify_be_cfgdata_reply( be_msg->cfg_data_reply->txn_id, - be_msg->cfg_data_reply->batch_id, be_msg->cfg_data_reply->success, be_msg->cfg_data_reply->error_if_any, adapter); break; case MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REPLY: - MGMTD_BE_ADAPTER_DBG( - "Got %s CFG_APPLY_REPLY from '%s' txn-id %" PRIx64 - " for %zu batches id %" PRIu64 "-%" PRIu64 " err:'%s'", - be_msg->cfg_apply_reply->success ? "successful" - : "failed", - adapter->name, be_msg->cfg_apply_reply->txn_id, - be_msg->cfg_apply_reply->n_batch_ids, - be_msg->cfg_apply_reply->batch_ids[0], - be_msg->cfg_apply_reply->batch_ids - [be_msg->cfg_apply_reply->n_batch_ids - 1], - be_msg->cfg_apply_reply->error_if_any - ? be_msg->cfg_apply_reply->error_if_any - : "None"); + __dbg("Got %s CFG_APPLY_REPLY from '%s' txn-id %" PRIx64 + " err:'%s'", + be_msg->cfg_apply_reply->success ? "successful" : "failed", + adapter->name, be_msg->cfg_apply_reply->txn_id, + be_msg->cfg_apply_reply->error_if_any + ? be_msg->cfg_apply_reply->error_if_any + : "None"); /* * Forward the CGFData-apply reply to txn module. */ mgmt_txn_notify_be_cfg_apply_reply( be_msg->cfg_apply_reply->txn_id, be_msg->cfg_apply_reply->success, - (uint64_t *)be_msg->cfg_apply_reply->batch_ids, - be_msg->cfg_apply_reply->n_batch_ids, be_msg->cfg_apply_reply->error_if_any, adapter); break; - case MGMTD__BE_MESSAGE__MESSAGE_GET_REPLY: - /* - * TODO: Add handling code in future. - */ - break; /* * NOTE: The following messages are always sent from MGMTD to * Backend clients only and/or need not be handled on MGMTd. */ case MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REPLY: - case MGMTD__BE_MESSAGE__MESSAGE_GET_REQ: case MGMTD__BE_MESSAGE__MESSAGE_TXN_REQ: case MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REQ: case MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REQ: @@ -532,14 +529,13 @@ int mgmt_be_send_txn_req(struct mgmt_be_client_adapter *adapter, be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_TXN_REQ; be_msg.txn_req = &txn_req; - MGMTD_BE_ADAPTER_DBG("Sending TXN_REQ to '%s' txn-id: %" PRIu64, - adapter->name, txn_id); + __dbg("Sending TXN_REQ to '%s' txn-id: %" PRIu64, adapter->name, txn_id); return mgmt_be_adapter_send_msg(adapter, &be_msg); } int mgmt_be_send_cfgdata_req(struct mgmt_be_client_adapter *adapter, - uint64_t txn_id, uint64_t batch_id, + uint64_t txn_id, Mgmtd__YangCfgDataReq **cfgdata_reqs, size_t num_reqs, bool end_of_data) { @@ -547,7 +543,6 @@ int mgmt_be_send_cfgdata_req(struct mgmt_be_client_adapter *adapter, Mgmtd__BeCfgDataCreateReq cfgdata_req; mgmtd__be_cfg_data_create_req__init(&cfgdata_req); - cfgdata_req.batch_id = batch_id; cfgdata_req.txn_id = txn_id; cfgdata_req.data_req = cfgdata_reqs; cfgdata_req.n_data_req = num_reqs; @@ -557,10 +552,8 @@ int mgmt_be_send_cfgdata_req(struct mgmt_be_client_adapter *adapter, be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REQ; be_msg.cfg_data_req = &cfgdata_req; - MGMTD_BE_ADAPTER_DBG( - "Sending CFGDATA_CREATE_REQ to '%s' txn-id: %" PRIu64 - " batch-id: %" PRIu64, - adapter->name, txn_id, batch_id); + __dbg("Sending CFGDATA_CREATE_REQ to '%s' txn-id: %" PRIu64 " last: %s", + adapter->name, txn_id, end_of_data ? "yes" : "no"); return mgmt_be_adapter_send_msg(adapter, &be_msg); } @@ -578,42 +571,148 @@ int mgmt_be_send_cfgapply_req(struct mgmt_be_client_adapter *adapter, be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REQ; be_msg.cfg_apply_req = &apply_req; - MGMTD_BE_ADAPTER_DBG("Sending CFG_APPLY_REQ to '%s' txn-id: %" PRIu64, - adapter->name, txn_id); + __dbg("Sending CFG_APPLY_REQ to '%s' txn-id: %" PRIu64, adapter->name, + txn_id); return mgmt_be_adapter_send_msg(adapter, &be_msg); } +int mgmt_be_send_native(enum mgmt_be_client_id id, void *msg) +{ + struct mgmt_be_client_adapter *adapter = mgmt_be_get_adapter_by_id(id); + + if (!adapter) + return -1; + + return mgmt_msg_native_send_msg(adapter->conn, msg, false); +} + +static void mgmt_be_adapter_send_notify(struct mgmt_msg_notify_data *msg, + size_t msglen) +{ + struct mgmt_be_client_adapter *adapter; + struct mgmt_be_xpath_map *map; + struct nb_node *nb_node; + const char *notif; + uint id, len; + + if (!darr_len(be_notif_xpath_map)) + return; + + notif = mgmt_msg_native_xpath_decode(msg, msglen); + if (!notif) { + __log_err("Corrupt notify msg"); + return; + } + + nb_node = nb_node_find(notif); + if (!nb_node) { + __log_err("No schema found for notification: %s", notif); + return; + } + + darr_foreach_p (be_notif_xpath_map, map) { + len = strlen(map->xpath_prefix); + if (strncmp(map->xpath_prefix, nb_node->xpath, len) && + strncmp(map->xpath_prefix, notif, len)) + continue; + + FOREACH_BE_CLIENT_BITS (id, map->clients) { + adapter = mgmt_be_get_adapter_by_id(id); + if (!adapter) + continue; + msg_conn_send_msg(adapter->conn, MGMT_MSG_VERSION_NATIVE, + msg, msglen, NULL, false); + } + } +} + +/* + * Handle a native encoded message + */ +static void be_adapter_handle_native_msg(struct mgmt_be_client_adapter *adapter, + struct mgmt_msg_header *msg, + size_t msg_len) +{ + struct mgmt_msg_notify_data *notify_msg; + struct mgmt_msg_tree_data *tree_msg; + struct mgmt_msg_error *error_msg; + + /* get the transaction */ + + switch (msg->code) { + case MGMT_MSG_CODE_ERROR: + error_msg = (typeof(error_msg))msg; + __dbg("Got ERROR from '%s' txn-id %" PRIx64, adapter->name, + msg->refer_id); + + /* Forward the reply to the txn module */ + mgmt_txn_notify_error(adapter, msg->refer_id, msg->req_id, + error_msg->error, error_msg->errstr); + + break; + case MGMT_MSG_CODE_TREE_DATA: + /* tree data from a backend client */ + tree_msg = (typeof(tree_msg))msg; + __dbg("Got TREE_DATA from '%s' txn-id %" PRIx64, adapter->name, + msg->refer_id); + + /* Forward the reply to the txn module */ + mgmt_txn_notify_tree_data_reply(adapter, tree_msg, msg_len); + break; + case MGMT_MSG_CODE_NOTIFY: + notify_msg = (typeof(notify_msg))msg; + __dbg("Got NOTIFY from '%s'", adapter->name); + mgmt_be_adapter_send_notify(notify_msg, msg_len); + mgmt_fe_adapter_send_notify(notify_msg, msg_len); + break; + default: + __log_err("unknown native message txn-id %" PRIu64 + " req-id %" PRIu64 + " code %u from BE client for adapter %s", + msg->refer_id, msg->req_id, msg->code, adapter->name); + break; + } +} + + static void mgmt_be_adapter_process_msg(uint8_t version, uint8_t *data, size_t len, struct msg_conn *conn) { struct mgmt_be_client_adapter *adapter = conn->user; - Mgmtd__BeMessage *be_msg = mgmtd__be_message__unpack(NULL, len, data); + Mgmtd__BeMessage *be_msg; + + if (version == MGMT_MSG_VERSION_NATIVE) { + struct mgmt_msg_header *msg = (typeof(msg))data; + + if (len >= sizeof(*msg)) + be_adapter_handle_native_msg(adapter, msg, len); + else + __log_err("native message to adapter %s too short %zu", + adapter->name, len); + return; + } + be_msg = mgmtd__be_message__unpack(NULL, len, data); if (!be_msg) { - MGMTD_BE_ADAPTER_DBG( - "Failed to decode %zu bytes for adapter: %s", len, - adapter->name); + __dbg("Failed to decode %zu bytes for adapter: %s", len, + adapter->name); return; } - MGMTD_BE_ADAPTER_DBG("Decoded %zu bytes of message: %u for adapter: %s", - len, be_msg->message_case, adapter->name); + __dbg("Decoded %zu bytes of message: %u for adapter: %s", len, + be_msg->message_case, adapter->name); (void)mgmt_be_adapter_handle_msg(adapter, be_msg); mgmtd__be_message__free_unpacked(be_msg, NULL); } -static void mgmt_be_iter_and_get_cfg(const char *xpath, struct lyd_node *node, - struct nb_node *nb_node, void *ctx) -{ - struct mgmt_be_get_adapter_config_params *parms = ctx; - struct mgmt_be_client_adapter *adapter = parms->adapter; - uint subscr; - - subscr = mgmt_be_get_subscr_for_xpath_and_client( - xpath, adapter->id, MGMT_SUBSCR_NOTIFY_CFG); - if (subscr) - nb_config_diff_created(node, &parms->seq, parms->cfg_chgs); -} +/* + * Args for callback + */ +struct mgmt_be_get_adapter_config_params { + struct mgmt_be_client_adapter *adapter; + struct nb_config_cbs *cfg_chgs; + uint32_t seq; +}; /* * Initialize a BE client over a new connection @@ -626,27 +725,16 @@ static void mgmt_be_adapter_conn_init(struct event *thread) assert(adapter && adapter->conn->fd >= 0); /* - * Check first if the current session can run a CONFIG - * transaction or not. Reschedule if a CONFIG transaction - * from another session is already in progress. - */ - if (mgmt_config_txn_in_progress() != MGMTD_SESSION_ID_NONE) { - zlog_err("XXX txn in progress, retry init"); - mgmt_be_adapter_sched_init_event(adapter); - return; - } - - /* * Notify TXN module to create a CONFIG transaction and * download the CONFIGs identified for this new client. * If the TXN module fails to initiate the CONFIG transaction - * disconnect from the client forcing a reconnect later. - * That should also take care of destroying the adapter. + * retry a bit later. It only fails if there's an existing config + * transaction in progress. */ if (mgmt_txn_notify_be_adapter_conn(adapter, true) != 0) { - zlog_err("XXX notify be adapter conn fail"); - msg_conn_disconnect(adapter->conn, false); - adapter = NULL; + zlog_err("XXX txn in progress, retry init"); + mgmt_be_adapter_sched_init_event(adapter); + return; } } @@ -686,15 +774,18 @@ extern void mgmt_be_adapter_unlock(struct mgmt_be_client_adapter **adapter) */ void mgmt_be_adapter_init(struct event_loop *tm) { + char server_path[MAXPATHLEN]; + assert(!mgmt_loop); mgmt_loop = tm; mgmt_be_adapters_init(&mgmt_be_adapters); mgmt_be_xpath_map_init(); - if (msg_server_init(&mgmt_be_server, MGMTD_BE_SERVER_PATH, tm, - mgmt_be_create_adapter, "backend", - &mgmt_debug_be)) { + snprintf(server_path, sizeof(server_path), MGMTD_BE_SOCK_NAME); + + if (msg_server_init(&mgmt_be_server, server_path, tm, + mgmt_be_create_adapter, "backend", &mgmt_debug_be)) { zlog_err("cannot initialize backend server"); exit(1); } @@ -733,14 +824,17 @@ struct msg_conn *mgmt_be_create_adapter(int conn_fd, union sockunion *from) mgmt_be_adapters_add_tail(&mgmt_be_adapters, adapter); RB_INIT(nb_config_cbs, &adapter->cfg_chgs); - adapter->conn = msg_server_conn_create( - mgmt_loop, conn_fd, mgmt_be_adapter_notify_disconnect, - mgmt_be_adapter_process_msg, MGMTD_BE_MAX_NUM_MSG_PROC, - MGMTD_BE_MAX_NUM_MSG_WRITE, MGMTD_BE_MSG_MAX_LEN, adapter, - "BE-adapter"); + adapter->conn = msg_server_conn_create(mgmt_loop, conn_fd, + mgmt_be_adapter_notify_disconnect, + mgmt_be_adapter_process_msg, + MGMTD_BE_MAX_NUM_MSG_PROC, + MGMTD_BE_MAX_NUM_MSG_WRITE, + MGMTD_BE_MAX_MSG_LEN, adapter, + "BE-adapter"); - MGMTD_BE_ADAPTER_DBG("Added new MGMTD Backend adapter '%s'", - adapter->name); + adapter->conn->debug = DEBUG_MODE_CHECK(&mgmt_debug_be, DEBUG_MODE_ALL); + + __dbg("Added new MGMTD Backend adapter '%s'", adapter->name); return adapter->conn; } @@ -748,8 +842,7 @@ struct msg_conn *mgmt_be_create_adapter(int conn_fd, union sockunion *from) struct mgmt_be_client_adapter * mgmt_be_get_adapter_by_id(enum mgmt_be_client_id id) { - return (id < MGMTD_BE_CLIENT_ID_MAX ? mgmt_be_adapters_by_id[id] - : NULL); + return (id < MGMTD_BE_CLIENT_ID_MAX ? mgmt_be_adapters_by_id[id] : NULL); } struct mgmt_be_client_adapter * @@ -758,100 +851,99 @@ mgmt_be_get_adapter_by_name(const char *name) return mgmt_be_find_adapter_by_name(name); } -int mgmt_be_get_adapter_config(struct mgmt_be_client_adapter *adapter, - struct nb_config_cbs **cfg_chgs) +void mgmt_be_adapter_toggle_client_debug(bool set) { - struct mgmt_be_get_adapter_config_params parms; - struct nb_config *cfg_root = mgmt_ds_get_nb_config(mm->running_ds); - - assert(cfg_chgs); + struct mgmt_be_client_adapter *adapter; - /* - * TODO: we should consider making this an assertable condition and - * guaranteeing it be true when this function is called. B/c what is - * going to happen if there are some changes being sent, and we don't - * gather a new snapshot, what new changes that came after the previous - * snapshot will then be lost? - */ - if (RB_EMPTY(nb_config_cbs, &adapter->cfg_chgs)) { - parms.adapter = adapter; - parms.cfg_chgs = &adapter->cfg_chgs; - parms.seq = 0; + FOREACH_ADAPTER_IN_LIST (adapter) + adapter->conn->debug = set; +} - mgmt_ds_iter_data(MGMTD_DS_RUNNING, cfg_root, "", - mgmt_be_iter_and_get_cfg, (void *)&parms); +/* + * Get a full set of changes for all the config that an adapter is subscribed to + * receive. + */ +void mgmt_be_get_adapter_config(struct mgmt_be_client_adapter *adapter, + struct nb_config_cbs **changes) +{ + const struct lyd_node *root, *dnode; + uint32_t seq = 0; + char *xpath; + + /* We can't be in the middle of sending other chgs when here. */ + assert(RB_EMPTY(nb_config_cbs, &adapter->cfg_chgs)); + + *changes = &adapter->cfg_chgs; + LY_LIST_FOR (running_config->dnode, root) { + LYD_TREE_DFS_BEGIN (root, dnode) { + if (lysc_is_key(dnode->schema)) + goto walk_cont; + + xpath = lyd_path(dnode, LYD_PATH_STD, NULL, 0); + if (be_is_client_interested(xpath, adapter->id, true)) + nb_config_diff_add_change(*changes, NB_CB_CREATE, &seq, dnode); + else + LYD_TREE_DFS_continue = 1; /* skip any subtree */ + free(xpath); + walk_cont: + LYD_TREE_DFS_END(root, dnode); + } } - - *cfg_chgs = &adapter->cfg_chgs; - return 0; } -void mgmt_be_get_subscr_info_for_xpath( - const char *xpath, struct mgmt_be_client_subscr_info *subscr_info) +uint64_t mgmt_be_interested_clients(const char *xpath, bool config) { - struct mgmt_be_xpath_map *map; + struct mgmt_be_xpath_map *maps, *map; enum mgmt_be_client_id id; + uint64_t clients; - memset(subscr_info, 0, sizeof(*subscr_info)); + maps = config ? be_cfg_xpath_map : be_oper_xpath_map; - MGMTD_BE_ADAPTER_DBG("XPATH: '%s'", xpath); - darr_foreach_p (mgmt_xpath_map, map) { - if (!mgmt_be_eval_regexp_match(map->xpath_regexp, xpath)) - continue; - FOREACH_MGMTD_BE_CLIENT_ID (id) { - subscr_info->xpath_subscr[id] |= map->subscr_info[id]; - } - } + clients = 0; + + __dbg("XPATH: '%s'", xpath); + darr_foreach_p (maps, map) + if (mgmt_be_xpath_prefix(map->xpath_prefix, xpath)) + clients |= map->clients; if (DEBUG_MODE_CHECK(&mgmt_debug_be, DEBUG_MODE_ALL)) { - FOREACH_MGMTD_BE_CLIENT_ID (id) { - if (!subscr_info->xpath_subscr[id]) - continue; - MGMTD_BE_ADAPTER_DBG("Cient: %s: subscribed: 0x%x", - mgmt_be_client_id2name(id), - subscr_info->xpath_subscr[id]); - } + FOREACH_BE_CLIENT_BITS (id, clients) + __dbg("Cient: %s: subscribed", + mgmt_be_client_id2name(id)); } + return clients; } /** - * Return the subscription info bits for a given `xpath` for a given - * `client_id`. + * Return true if `client_id` is interested in `xpath` for `config` + * or oper (!`config`). * * Args: - * xpath - the xpath to check for subscription information. + * xpath - the xpath to check for interest. * client_id - the BE client being checked for. - * subscr_mask - The subscr bits the caller is interested in seeing - * if set. + * bool - check for config (vs oper) subscription. * * Returns: - * The subscription info bits. + * Interested or not. */ -static uint mgmt_be_get_subscr_for_xpath_and_client( - const char *xpath, enum mgmt_be_client_id client_id, uint subscr_mask) +static bool be_is_client_interested(const char *xpath, + enum mgmt_be_client_id id, bool config) { - struct mgmt_be_client_xpath_map *map; - uint subscr = 0; - uint i; + uint64_t clients; - assert(client_id < MGMTD_BE_CLIENT_ID_MAX); + assert(id < MGMTD_BE_CLIENT_ID_MAX); - MGMTD_BE_ADAPTER_DBG("Checking client: %s for xpath: '%s'", - mgmt_be_client_id2name(client_id), xpath); + __dbg("Checking client: %s for xpath: '%s'", mgmt_be_client_id2name(id), + xpath); - map = &mgmt_client_xpaths[client_id]; - for (i = 0; i < map->nxpaths; i++) { - if (!mgmt_be_eval_regexp_match(map->xpaths[i].xpath, xpath)) - continue; - MGMTD_BE_ADAPTER_DBG("xpath: %s: matched: %s", - map->xpaths[i].xpath, xpath); - subscr |= map->xpaths[i].subscribed; - if ((subscr & subscr_mask) == subscr_mask) - break; + clients = mgmt_be_interested_clients(xpath, config); + if (IS_IDBIT_SET(clients, id)) { + __dbg("client: %s: interested", mgmt_be_client_id2name(id)); + return true; } - MGMTD_BE_ADAPTER_DBG("client: %s: subscribed: 0x%x", - mgmt_be_client_id2name(client_id), subscr); - return subscr; + + __dbg("client: %s: not interested", mgmt_be_client_id2name(id)); + return false; } void mgmt_be_adapter_status_write(struct vty *vty) @@ -878,56 +970,49 @@ void mgmt_be_adapter_status_write(struct vty *vty) (int)mgmt_be_adapters_count(&mgmt_be_adapters)); } -void mgmt_be_xpath_register_write(struct vty *vty) +static void be_show_xpath_register(struct vty *vty, + struct mgmt_be_xpath_map *map) { - struct mgmt_be_xpath_map *map; enum mgmt_be_client_id id; - struct mgmt_be_client_adapter *adapter; - uint info; - - vty_out(vty, "MGMTD Backend XPath Registry\n"); + const char *astr; - darr_foreach_p (mgmt_xpath_map, map) { - vty_out(vty, " - XPATH: '%s'\n", map->xpath_regexp); - FOREACH_MGMTD_BE_CLIENT_ID (id) { - info = map->subscr_info[id]; - if (!info) - continue; - vty_out(vty, - " -- Client: '%s'\tValidate:%d, Notify:%d, Own:%d\n", - mgmt_be_client_id2name(id), - (info & MGMT_SUBSCR_VALIDATE_CFG) != 0, - (info & MGMT_SUBSCR_NOTIFY_CFG) != 0, - (info & MGMT_SUBSCR_OPER_OWN) != 0); - adapter = mgmt_be_get_adapter_by_id(id); - if (adapter) - vty_out(vty, " -- Adapter: %p\n", adapter); - } + vty_out(vty, " - xpath: '%s'\n", map->xpath_prefix); + FOREACH_BE_CLIENT_BITS (id, map->clients) { + astr = mgmt_be_get_adapter_by_id(id) ? "active" : "inactive"; + vty_out(vty, " -- %s-client: '%s'\n", astr, + mgmt_be_client_id2name(id)); } +} +void mgmt_be_xpath_register_write(struct vty *vty) +{ + struct mgmt_be_xpath_map *map; - vty_out(vty, "Total XPath Registries: %u\n", darr_len(mgmt_xpath_map)); + vty_out(vty, "MGMTD Backend CFG XPath Registry: Count: %u\n", + darr_len(be_oper_xpath_map)); + darr_foreach_p (be_cfg_xpath_map, map) + be_show_xpath_register(vty, map); + + vty_out(vty, "\nMGMTD Backend OPER XPath Registry: Count: %u\n", + darr_len(be_oper_xpath_map)); + darr_foreach_p (be_oper_xpath_map, map) + be_show_xpath_register(vty, map); } -void mgmt_be_xpath_subscr_info_write(struct vty *vty, const char *xpath) +void mgmt_be_show_xpath_registries(struct vty *vty, const char *xpath) { - struct mgmt_be_client_subscr_info subscr; enum mgmt_be_client_id id; struct mgmt_be_client_adapter *adapter; - uint info; + uint64_t cclients, oclients, combined; - mgmt_be_get_subscr_info_for_xpath(xpath, &subscr); + cclients = mgmt_be_interested_clients(xpath, true); + oclients = mgmt_be_interested_clients(xpath, false); + combined = cclients | oclients; vty_out(vty, "XPath: '%s'\n", xpath); - FOREACH_MGMTD_BE_CLIENT_ID (id) { - info = subscr.xpath_subscr[id]; - if (!info) - continue; - vty_out(vty, - " -- Client: '%s'\tValidate:%d, Notify:%d, Own:%d\n", - mgmt_be_client_id2name(id), - (info & MGMT_SUBSCR_VALIDATE_CFG) != 0, - (info & MGMT_SUBSCR_NOTIFY_CFG) != 0, - (info & MGMT_SUBSCR_OPER_OWN) != 0); + FOREACH_BE_CLIENT_BITS (id, combined) { + vty_out(vty, " -- Client: '%s'\tconfig:%d oper:%d\n", + mgmt_be_client_id2name(id), IS_IDBIT_SET(cclients, id), + IS_IDBIT_SET(oclients, 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 ca8f55c..491410a 100644 --- a/mgmtd/mgmt_be_adapter.h +++ b/mgmtd/mgmt_be_adapter.h @@ -12,14 +12,37 @@ #include "mgmt_be_client.h" #include "mgmt_msg.h" -#include "mgmtd/mgmt_defines.h" +#include "mgmt_defines.h" #include "mgmtd/mgmt_ds.h" #define MGMTD_BE_CONN_INIT_DELAY_MSEC 50 -#define MGMTD_FIND_ADAPTER_BY_INDEX(adapter_index) \ +#define MGMTD_FIND_ADAPTER_BY_INDEX(adapter_index) \ mgmt_adaptr_ref[adapter_index] +/** + * CLIENT-ID + * + * Add enum value for each supported component, wrap with + * #ifdef HAVE_COMPONENT + */ +enum mgmt_be_client_id { + MGMTD_BE_CLIENT_ID_TESTC, /* always first */ + MGMTD_BE_CLIENT_ID_ZEBRA, +#ifdef HAVE_RIPD + MGMTD_BE_CLIENT_ID_RIPD, +#endif +#ifdef HAVE_RIPNGD + MGMTD_BE_CLIENT_ID_RIPNGD, +#endif +#ifdef HAVE_STATICD + MGMTD_BE_CLIENT_ID_STATICD, +#endif + MGMTD_BE_CLIENT_ID_MAX +}; +#define MGMTD_BE_CLIENT_ID_MIN 0 + + enum mgmt_be_req_type { MGMTD_BE_REQ_NONE = 0, MGMTD_BE_REQ_CFG_VALIDATE, @@ -49,8 +72,6 @@ struct mgmt_be_client_adapter { enum mgmt_be_client_id id; uint32_t flags; char name[MGMTD_CLIENT_NAME_MAX_LEN]; - uint8_t num_xpath_reg; - char xpath_reg[MGMTD_MAX_NUM_XPATH_REG][MGMTD_MAX_XPATH_LEN]; int refcount; @@ -81,9 +102,36 @@ DECLARE_LIST(mgmt_be_adapters, struct mgmt_be_client_adapter, list_linkage); #define MGMT_SUBSCR_OPER_OWN 0x4 #define MGMT_SUBSCR_ALL 0x7 -struct mgmt_be_client_subscr_info { - uint xpath_subscr[MGMTD_BE_CLIENT_ID_MAX]; -}; +/* --------- */ +/* CLIENT-ID */ +/* --------- */ + +#define FOREACH_MGMTD_BE_CLIENT_ID(id) \ + for ((id) = MGMTD_BE_CLIENT_ID_MIN; (id) < MGMTD_BE_CLIENT_ID_MAX; \ + (id)++) + +#define IS_IDBIT_SET(v, id) (!IS_IDBIT_UNSET(v, id)) +#define IS_IDBIT_UNSET(v, id) (!((v) & (1ull << (id)))) + +#define __GET_NEXT_SET(id, bits) \ + ({ \ + enum mgmt_be_client_id __id = (id); \ + \ + for (; __id < MGMTD_BE_CLIENT_ID_MAX && \ + IS_IDBIT_UNSET(bits, __id); \ + __id++) \ + ; \ + __id; \ + }) + +#define FOREACH_BE_CLIENT_BITS(id, bits) \ + for ((id) = __GET_NEXT_SET(MGMTD_BE_CLIENT_ID_MIN, bits); \ + (id) < MGMTD_BE_CLIENT_ID_MAX; \ + (id) = __GET_NEXT_SET((id) + 1, bits)) + +/* ---------- */ +/* Prototypes */ +/* ---------- */ /* Initialise backend adapter module. */ extern void mgmt_be_adapter_init(struct event_loop *tm); @@ -109,9 +157,15 @@ mgmt_be_get_adapter_by_name(const char *name); extern struct mgmt_be_client_adapter * mgmt_be_get_adapter_by_id(enum mgmt_be_client_id id); +/* Get the client name given a client ID */ +extern const char *mgmt_be_client_id2name(enum mgmt_be_client_id id); + +/* Toggle debug on or off for connected clients. */ +extern void mgmt_be_adapter_toggle_client_debug(bool set); + /* Fetch backend adapter config. */ -extern int mgmt_be_get_adapter_config(struct mgmt_be_client_adapter *adapter, - struct nb_config_cbs **cfg_chgs); +extern void mgmt_be_get_adapter_config(struct mgmt_be_client_adapter *adapter, + struct nb_config_cbs **changes); /* Create/destroy a transaction. */ extern int mgmt_be_send_txn_req(struct mgmt_be_client_adapter *adapter, @@ -126,9 +180,6 @@ extern int mgmt_be_send_txn_req(struct mgmt_be_client_adapter *adapter, * txn_id * Unique transaction identifier. * - * batch_id - * Request batch ID. - * * cfgdata_reqs * An array of pointer to Mgmtd__YangCfgDataReq. * @@ -142,7 +193,7 @@ extern int mgmt_be_send_txn_req(struct mgmt_be_client_adapter *adapter, * 0 on success, -1 on failure. */ extern int mgmt_be_send_cfgdata_req(struct mgmt_be_client_adapter *adapter, - uint64_t txn_id, uint64_t batch_id, + uint64_t txn_id, Mgmtd__YangCfgDataReq **cfgdata_reqs, size_t num_reqs, bool end_of_data); @@ -171,23 +222,39 @@ extern void mgmt_be_adapter_status_write(struct vty *vty); */ extern void mgmt_be_xpath_register_write(struct vty *vty); + +/** + * Send a native message to a backend client + * + * Args: + * adapter: the client to send the message to. + * msg: a native message from mgmt_msg_native_alloc_msg() + * + * Return: + * Any return value from msg_conn_send_msg(). + */ +extern int mgmt_be_send_native(enum mgmt_be_client_id id, void *msg); + /** * 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. - * subscr_info - An array of uint indexed by client id - * each eleemnt holds the subscription info - * for that client. + * config - true for config interest false for oper interest. */ -extern void mgmt_be_get_subscr_info_for_xpath( - const char *xpath, struct mgmt_be_client_subscr_info *subscr_info); +extern uint64_t mgmt_be_interested_clients(const char *xpath, bool config); +/** + * mgmt_fe_adapter_send_notify() - notify FE clients of a notification. + * @msg: the notify message from the backend client. + * @msglen: the length of the notify message. + */ +extern void mgmt_fe_adapter_send_notify(struct mgmt_msg_notify_data *msg, + size_t msglen); /* * Dump backend client information for a given xpath to vty. */ -extern void mgmt_be_xpath_subscr_info_write(struct vty *vty, - const char *xpath); +extern void mgmt_be_show_xpath_registries(struct vty *vty, const char *xpath); #endif /* _FRR_MGMTD_BE_ADAPTER_H_ */ diff --git a/mgmtd/mgmt_defines.h b/mgmtd/mgmt_defines.h deleted file mode 100644 index 40fa670..0000000 --- a/mgmtd/mgmt_defines.h +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * MGMTD public defines. - * - * Copyright (C) 2021 Vmware, Inc. - * Pushpasis Sarkar <spushpasis@vmware.com> - */ - -#ifndef _FRR_MGMTD_DEFINES_H -#define _FRR_MGMTD_DEFINES_H - -#include "yang.h" - -#define MGMTD_CLIENT_NAME_MAX_LEN 32 - -#define MGMTD_MAX_XPATH_LEN XPATH_MAXLEN - -#define MGMTD_MAX_YANG_VALUE_LEN YANG_VALUE_MAXLEN - -#define MGMTD_MAX_NUM_XPATH_REG 128 - -#define MGMTD_MAX_NUM_DATA_REQ_IN_BATCH 32 -#define MGMTD_MAX_NUM_DATA_REPLY_IN_BATCH 8 - -enum mgmt_result { - MGMTD_SUCCESS = 0, - MGMTD_INVALID_PARAM, - MGMTD_INTERNAL_ERROR, - MGMTD_NO_CFG_CHANGES, - MGMTD_DS_LOCK_FAILED, - MGMTD_DS_UNLOCK_FAILED, - MGMTD_UNKNOWN_FAILURE -}; - -enum mgmt_fe_event { - MGMTD_FE_SERVER = 1, - MGMTD_FE_CONN_READ, - MGMTD_FE_CONN_WRITE, - MGMTD_FE_PROC_MSG -}; - -enum mgmt_be_event { - MGMTD_BE_SERVER = 1, - MGMTD_BE_CONN_INIT, - MGMTD_BE_CONN_READ, - MGMTD_BE_CONN_WRITE, - MGMTD_BE_PROC_MSG, - MGMTD_BE_SCHED_CFG_PREPARE, - MGMTD_BE_RESCHED_CFG_PREPARE, - MGMTD_BE_SCHED_CFG_APPLY, - MGMTD_BE_RESCHED_CFG_APPLY, -}; - -#define MGMTD_TXN_ID_NONE 0 - -#define MGMTD_TXN_BATCH_ID_NONE 0 - -#endif /* _FRR_MGMTD_DEFINES_H */ diff --git a/mgmtd/mgmt_ds.c b/mgmtd/mgmt_ds.c index a0e610c..eaf52df 100644 --- a/mgmtd/mgmt_ds.c +++ b/mgmtd/mgmt_ds.c @@ -15,10 +15,9 @@ #include "mgmtd/mgmt_txn.h" #include "libyang/libyang.h" -#define MGMTD_DS_DBG(fmt, ...) \ +#define __dbg(fmt, ...) \ DEBUGD(&mgmt_debug_ds, "DS: %s: " fmt, __func__, ##__VA_ARGS__) -#define MGMTD_DS_ERR(fmt, ...) \ - zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__) +#define __log_err(fmt, ...) zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__) struct mgmt_ds_ctx { Mgmtd__DatastoreId ds_id; @@ -81,8 +80,8 @@ static int mgmt_ds_replace_dst_with_src_ds(struct mgmt_ds_ctx *src, if (!src || !dst) return -1; - MGMTD_DS_DBG("Replacing %s with %s", mgmt_ds_id2name(dst->ds_id), - mgmt_ds_id2name(src->ds_id)); + __dbg("Replacing %s with %s", mgmt_ds_id2name(dst->ds_id), + mgmt_ds_id2name(src->ds_id)); if (src->config_ds && dst->config_ds) nb_config_replace(dst->root.cfg_root, src->root.cfg_root, true); @@ -93,14 +92,6 @@ static int mgmt_ds_replace_dst_with_src_ds(struct mgmt_ds_ctx *src, dst->root.dnode_root = yang_dnode_dup(src->root.dnode_root); } - if (src->ds_id == MGMTD_DS_CANDIDATE) { - /* - * Drop the changes in scratch-buffer. - */ - MGMTD_DS_DBG("Emptying Candidate Scratch buffer!"); - nb_config_diff_del_changes(&src->root.cfg_root->cfg_chgs); - } - return 0; } @@ -112,7 +103,7 @@ static int mgmt_ds_merge_src_with_dst_ds(struct mgmt_ds_ctx *src, if (!src || !dst) return -1; - MGMTD_DS_DBG("Merging DS %d with %d", dst->ds_id, src->ds_id); + __dbg("Merging DS %d with %d", dst->ds_id, src->ds_id); if (src->config_ds && dst->config_ds) ret = nb_config_merge(dst->root.cfg_root, src->root.cfg_root, true); @@ -122,18 +113,10 @@ static int mgmt_ds_merge_src_with_dst_ds(struct mgmt_ds_ctx *src, src->root.dnode_root, 0); } if (ret != 0) { - MGMTD_DS_ERR("merge failed with err: %d", ret); + __log_err("merge failed with err: %d", ret); return ret; } - if (src->ds_id == MGMTD_DS_CANDIDATE) { - /* - * Drop the changes in scratch-buffer. - */ - MGMTD_DS_DBG("Emptying Candidate Scratch buffer!"); - nb_config_diff_del_changes(&src->root.cfg_root->cfg_chgs); - } - return 0; } @@ -315,7 +298,7 @@ static int mgmt_walk_ds_nodes( assert(mgmt_ds_node_iter_fn); - MGMTD_DS_DBG(" -- START: base xpath: '%s'", base_xpath); + __dbg(" -- START: base xpath: '%s'", base_xpath); if (!base_dnode) /* @@ -326,9 +309,9 @@ static int mgmt_walk_ds_nodes( if (!base_dnode) return -1; - MGMTD_DS_DBG(" search base schema: '%s'", - lysc_path(base_dnode->schema, LYSC_PATH_LOG, xpath, - sizeof(xpath))); + __dbg(" search base schema: '%s'", + lysc_path(base_dnode->schema, LYSC_PATH_LOG, xpath, + sizeof(xpath))); nbnode = (struct nb_node *)base_dnode->schema->priv; (*mgmt_ds_node_iter_fn)(base_xpath, base_dnode, nbnode, ctx); @@ -351,7 +334,7 @@ static int mgmt_walk_ds_nodes( (void)lyd_path(dnode, LYD_PATH_STD, xpath, sizeof(xpath)); - MGMTD_DS_DBG(" -- Child xpath: %s", xpath); + __dbg(" -- Child xpath: %s", xpath); ret = mgmt_walk_ds_nodes(root, xpath, dnode, mgmt_ds_node_iter_fn, ctx); @@ -359,7 +342,7 @@ static int mgmt_walk_ds_nodes( break; } - MGMTD_DS_DBG(" -- END: base xpath: '%s'", base_xpath); + __dbg(" -- END: base xpath: '%s'", base_xpath); return ret; } @@ -423,8 +406,7 @@ int mgmt_ds_load_config_from_file(struct mgmt_ds_ctx *dst, return -1; if (mgmt_ds_load_cfg_from_file(file_path, &iter) != 0) { - MGMTD_DS_ERR("Failed to load config from the file %s", - file_path); + __log_err("Failed to load config from the file %s", file_path); return -1; } @@ -467,7 +449,7 @@ int mgmt_ds_iter_data(Mgmtd__DatastoreId ds_id, struct nb_config *root, * Oper-state should be kept in mind though for the prefix walk */ - MGMTD_DS_DBG(" -- START DS walk for DSid: %d", ds_id); + __dbg(" -- START DS walk for DSid: %d", ds_id); /* If the base_xpath is empty then crawl the sibblings */ if (xpath[0] == 0) { diff --git a/mgmtd/mgmt_ds.h b/mgmtd/mgmt_ds.h index 1cf4816..b8e77e3 100644 --- a/mgmtd/mgmt_ds.h +++ b/mgmtd/mgmt_ds.h @@ -11,8 +11,8 @@ #include "mgmt_fe_client.h" #include "northbound.h" +#include "mgmt_defines.h" -#include "mgmtd/mgmt_defines.h" #include "mgmtd/mgmt_be_adapter.h" #include "mgmtd/mgmt_fe_adapter.h" @@ -29,8 +29,8 @@ #define MGMTD_MAX_COMMIT_LIST 10 -#define MGMTD_COMMIT_FILE_PATH DAEMON_DB_DIR "/commit-%s.json" -#define MGMTD_COMMIT_INDEX_FILE_NAME DAEMON_DB_DIR "/commit-index.dat" +#define MGMTD_COMMIT_FILE_PATH(id) "%s/commit-%s.json", frr_libstatedir, id +#define MGMTD_COMMIT_INDEX_FILE_PATH "%s/commit-index.dat", frr_libstatedir extern struct nb_config *running_config; diff --git a/mgmtd/mgmt_fe_adapter.c b/mgmtd/mgmt_fe_adapter.c index 2b2471c..ec8e773 100644 --- a/mgmtd/mgmt_fe_adapter.c +++ b/mgmtd/mgmt_fe_adapter.c @@ -8,11 +8,13 @@ */ #include <zebra.h> +#include "darr.h" #include "sockopt.h" #include "network.h" #include "libfrr.h" #include "mgmt_fe_client.h" #include "mgmt_msg.h" +#include "mgmt_msg_native.h" #include "mgmt_pb.h" #include "hash.h" #include "jhash.h" @@ -21,9 +23,9 @@ #include "mgmtd/mgmt_memory.h" #include "mgmtd/mgmt_fe_adapter.h" -#define MGMTD_FE_ADAPTER_DBG(fmt, ...) \ +#define __dbg(fmt, ...) \ DEBUGD(&mgmt_debug_fe, "FE-ADAPTER: %s: " fmt, __func__, ##__VA_ARGS__) -#define MGMTD_FE_ADAPTER_ERR(fmt, ...) \ +#define __log_err(fmt, ...) \ zlog_err("FE-ADAPTER: %s: ERROR: " fmt, __func__, ##__VA_ARGS__) #define FOREACH_ADAPTER_IN_LIST(adapter) \ @@ -76,20 +78,18 @@ mgmt_fe_session_write_lock_ds(Mgmtd__DatastoreId ds_id, session->session_id, mgmt_ds_id2name(ds_id)); else { if (mgmt_ds_lock(ds_ctx, session->session_id)) { - MGMTD_FE_ADAPTER_DBG( - "Failed to lock the DS:%s for session-id: %" PRIu64 - " from %s!", - mgmt_ds_id2name(ds_id), session->session_id, - session->adapter->name); + __dbg("Failed to lock the DS:%s for session-id: %" PRIu64 + " from %s!", + mgmt_ds_id2name(ds_id), session->session_id, + session->adapter->name); return -1; } session->ds_locked[ds_id] = true; - MGMTD_FE_ADAPTER_DBG( - "Write-Locked the DS:%s for session-id: %" PRIu64 - " from %s", - mgmt_ds_id2name(ds_id), session->session_id, - session->adapter->name); + __dbg("Write-Locked the DS:%s for session-id: %" PRIu64 + " from %s", + mgmt_ds_id2name(ds_id), session->session_id, + session->adapter->name); } return 0; @@ -105,11 +105,10 @@ static void mgmt_fe_session_unlock_ds(Mgmtd__DatastoreId ds_id, session->ds_locked[ds_id] = false; mgmt_ds_unlock(ds_ctx); - MGMTD_FE_ADAPTER_DBG( - "Unlocked DS:%s write-locked earlier by session-id: %" PRIu64 - " from %s", - mgmt_ds_id2name(ds_id), session->session_id, - session->adapter->name); + __dbg("Unlocked DS:%s write-locked earlier by session-id: %" PRIu64 + " from %s", + mgmt_ds_id2name(ds_id), session->session_id, + session->adapter->name); } static void @@ -204,14 +203,13 @@ mgmt_fe_find_session_by_client_id(struct mgmt_fe_client_adapter *adapter, FOREACH_SESSION_IN_LIST (adapter, session) { if (session->client_id == client_id) { - MGMTD_FE_ADAPTER_DBG("Found session-id %" PRIu64 - " using client-id %" PRIu64, - session->session_id, client_id); + __dbg("Found session-id %" PRIu64 + " using client-id %" PRIu64, + session->session_id, client_id); return session; } } - MGMTD_FE_ADAPTER_DBG("Session not found using client-id %" PRIu64, - client_id); + __dbg("Session not found using client-id %" PRIu64, client_id); return NULL; } @@ -246,6 +244,23 @@ mgmt_session_id2ctx(uint64_t session_id) return session; } +void mgmt_fe_adapter_toggle_client_debug(bool set) +{ + struct mgmt_fe_client_adapter *adapter; + + FOREACH_ADAPTER_IN_LIST (adapter) + adapter->conn->debug = set; +} + +static struct mgmt_fe_session_ctx *fe_adapter_session_by_txn_id(uint64_t txn_id) +{ + uint64_t session_id = mgmt_txn_get_session_id(txn_id); + + if (session_id == MGMTD_SESSION_ID_NONE) + return NULL; + return mgmt_session_id2ctx(session_id); +} + static struct mgmt_fe_session_ctx * mgmt_fe_create_session(struct mgmt_fe_client_adapter *adapter, uint64_t client_id) @@ -273,6 +288,14 @@ mgmt_fe_create_session(struct mgmt_fe_client_adapter *adapter, return session; } +static int fe_adapter_send_native_msg(struct mgmt_fe_client_adapter *adapter, + void *msg, size_t len, + bool short_circuit_ok) +{ + return msg_conn_send_msg(adapter->conn, MGMT_MSG_VERSION_NATIVE, msg, + len, NULL, short_circuit_ok); +} + static int fe_adapter_send_msg(struct mgmt_fe_client_adapter *adapter, Mgmtd__FeMessage *fe_msg, bool short_circuit_ok) { @@ -303,9 +326,8 @@ static int fe_adapter_send_session_reply(struct mgmt_fe_client_adapter *adapter, fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_SESSION_REPLY; fe_msg.session_reply = &session_reply; - MGMTD_FE_ADAPTER_DBG( - "Sending SESSION_REPLY message to MGMTD Frontend client '%s'", - adapter->name); + __dbg("Sending SESSION_REPLY message to MGMTD Frontend client '%s'", + adapter->name); return fe_adapter_send_msg(adapter, &fe_msg, true); } @@ -334,9 +356,8 @@ static int fe_adapter_send_lockds_reply(struct mgmt_fe_session_ctx *session, fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_LOCKDS_REPLY; fe_msg.lockds_reply = &lockds_reply; - MGMTD_FE_ADAPTER_DBG( - "Sending LOCK_DS_REPLY message to MGMTD Frontend client '%s' scok: %d", - session->adapter->name, scok); + __dbg("Sending LOCK_DS_REPLY message to MGMTD Frontend client '%s' scok: %d", + session->adapter->name, scok); return fe_adapter_send_msg(session->adapter, &fe_msg, scok); } @@ -369,9 +390,8 @@ static int fe_adapter_send_set_cfg_reply(struct mgmt_fe_session_ctx *session, fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_SETCFG_REPLY; fe_msg.setcfg_reply = &setcfg_reply; - MGMTD_FE_ADAPTER_DBG( - "Sending SETCFG_REPLY message to MGMTD Frontend client '%s'", - session->adapter->name); + __dbg("Sending SETCFG_REPLY message to MGMTD Frontend client '%s'", + session->adapter->name); if (implicit_commit) { if (mm->perf_stats_en) @@ -415,9 +435,8 @@ static int fe_adapter_send_commit_cfg_reply( fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_COMMCFG_REPLY; fe_msg.commcfg_reply = &commcfg_reply; - MGMTD_FE_ADAPTER_DBG( - "Sending COMMIT_CONFIG_REPLY message to MGMTD Frontend client '%s'", - session->adapter->name); + __dbg("Sending COMMIT_CONFIG_REPLY message to MGMTD Frontend client '%s'", + session->adapter->name); /* * Cleanup the CONFIG transaction associated with this session. @@ -457,8 +476,8 @@ static int fe_adapter_send_get_reply(struct mgmt_fe_session_ctx *session, fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_GET_REPLY; fe_msg.get_reply = &get_reply; - MGMTD_FE_ADAPTER_DBG("Sending GET_REPLY message to MGMTD Frontend client '%s'", - session->adapter->name); + __dbg("Sending GET_REPLY message to MGMTD Frontend client '%s'", + session->adapter->name); /* * Cleanup the SHOW transaction associated with this session. @@ -470,6 +489,28 @@ static int fe_adapter_send_get_reply(struct mgmt_fe_session_ctx *session, return fe_adapter_send_msg(session->adapter, &fe_msg, false); } +static int fe_adapter_send_error(struct mgmt_fe_session_ctx *session, + uint64_t req_id, bool short_circuit_ok, + int16_t error, const char *errfmt, ...) + PRINTFRR(5, 6); + +static int fe_adapter_send_error(struct mgmt_fe_session_ctx *session, + uint64_t req_id, bool short_circuit_ok, + int16_t error, const char *errfmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, errfmt); + ret = vmgmt_msg_native_send_error(session->adapter->conn, + session->session_id, req_id, + short_circuit_ok, error, errfmt, ap); + va_end(ap); + + return ret; +} + + static void mgmt_fe_session_cfg_txn_clnup(struct event *thread) { struct mgmt_fe_session_ctx *session; @@ -523,7 +564,7 @@ mgmt_fe_find_adapter_by_fd(int conn_fd) static void mgmt_fe_adapter_delete(struct mgmt_fe_client_adapter *adapter) { struct mgmt_fe_session_ctx *session; - MGMTD_FE_ADAPTER_DBG("deleting client adapter '%s'", adapter->name); + __dbg("deleting client adapter '%s'", adapter->name); /* TODO: notify about client disconnect for appropriate cleanup */ FOREACH_SESSION_IN_LIST (adapter, session) @@ -538,8 +579,7 @@ static int mgmt_fe_adapter_notify_disconnect(struct msg_conn *conn) { struct mgmt_fe_client_adapter *adapter = conn->user; - MGMTD_FE_ADAPTER_DBG("notify disconnect for client adapter '%s'", - adapter->name); + __dbg("notify disconnect for client adapter '%s'", adapter->name); mgmt_fe_adapter_delete(adapter); @@ -560,10 +600,8 @@ mgmt_fe_adapter_cleanup_old_conn(struct mgmt_fe_client_adapter *adapter) if (strncmp(adapter->name, old->name, sizeof(adapter->name))) continue; - MGMTD_FE_ADAPTER_DBG( - "Client '%s' (FD:%d) seems to have reconnected. Removing old connection (FD:%d)", - adapter->name, adapter->conn->fd, - old->conn->fd); + __dbg("Client '%s' (FD:%d) seems to have reconnected. Removing old connection (FD:%d)", + adapter->name, adapter->conn->fd, old->conn->fd); msg_conn_disconnect(old->conn, false); } } @@ -616,11 +654,10 @@ mgmt_fe_session_handle_lockds_req_msg(struct mgmt_fe_session_ctx *session, if (fe_adapter_send_lockds_reply(session, lockds_req->ds_id, lockds_req->req_id, lockds_req->lock, true, NULL) != 0) { - MGMTD_FE_ADAPTER_DBG( - "Failed to send LOCK_DS_REPLY for DS %u session-id: %" PRIu64 - " from %s", - lockds_req->ds_id, session->session_id, - session->adapter->name); + __dbg("Failed to send LOCK_DS_REPLY for DS %u session-id: %" PRIu64 + " from %s", + lockds_req->ds_id, session->session_id, + session->adapter->name); } return 0; @@ -674,9 +711,6 @@ mgmt_fe_session_handle_setcfg_req_msg(struct mgmt_fe_session_ctx *session, } if (session->cfg_txn_id == MGMTD_TXN_ID_NONE) { - /* as we have the lock no-one else should have a config txn */ - assert(mgmt_config_txn_in_progress() == MGMTD_SESSION_ID_NONE); - /* Start a CONFIG Transaction (if not started already) */ session->cfg_txn_id = mgmt_create_txn(session->session_id, MGMTD_TXN_TYPE_CONFIG); @@ -690,14 +724,13 @@ mgmt_fe_session_handle_setcfg_req_msg(struct mgmt_fe_session_ctx *session, } txn_created = true; - MGMTD_FE_ADAPTER_DBG("Created new Config txn-id: %" PRIu64 - " for session-id %" PRIu64, - session->cfg_txn_id, session->session_id); + __dbg("Created new Config txn-id: %" PRIu64 + " for session-id %" PRIu64, + session->cfg_txn_id, session->session_id); } else { - MGMTD_FE_ADAPTER_DBG("Config txn-id: %" PRIu64 - " for session-id: %" PRIu64 - " already created", - session->cfg_txn_id, session->session_id); + __dbg("Config txn-id: %" PRIu64 " for session-id: %" PRIu64 + " already created", + session->cfg_txn_id, session->session_id); if (setcfg_req->implicit_commit) { /* @@ -740,14 +773,8 @@ static int mgmt_fe_session_handle_get_req_msg(struct mgmt_fe_session_ctx *sessio struct nb_config *cfg_root = NULL; Mgmtd__DatastoreId ds_id = get_req->ds_id; uint64_t req_id = get_req->req_id; - bool is_cfg = get_req->config; - bool ds_ok = true; - - if (is_cfg && ds_id != MGMTD_DS_CANDIDATE && ds_id != MGMTD_DS_RUNNING) - ds_ok = false; - else if (!is_cfg && ds_id != MGMTD_DS_OPERATIONAL) - ds_ok = false; - if (!ds_ok) { + + if (ds_id != MGMTD_DS_CANDIDATE && ds_id != MGMTD_DS_RUNNING) { fe_adapter_send_get_reply(session, ds_id, req_id, false, NULL, "get-req on unsupported datastore"); return 0; @@ -768,23 +795,22 @@ static int mgmt_fe_session_handle_get_req_msg(struct mgmt_fe_session_ctx *sessio return -1; } - MGMTD_FE_ADAPTER_DBG("Created new show txn-id: %" PRIu64 - " for session-id: %" PRIu64, - session->txn_id, session->session_id); + __dbg("Created new show txn-id: %" PRIu64 + " for session-id: %" PRIu64, + session->txn_id, session->session_id); } else { fe_adapter_send_get_reply(session, ds_id, req_id, false, NULL, "Request processing for GET failed!"); - MGMTD_FE_ADAPTER_DBG("Transaction in progress txn-id: %" PRIu64 - " for session-id: %" PRIu64, - session->txn_id, session->session_id); + __dbg("Transaction in progress txn-id: %" PRIu64 + " for session-id: %" PRIu64, + session->txn_id, session->session_id); return -1; } /* * Get a copy of the datastore config root, avoids locking. */ - if (is_cfg) - cfg_root = nb_config_dup(mgmt_ds_get_nb_config(ds_ctx)); + cfg_root = nb_config_dup(mgmt_ds_get_nb_config(ds_ctx)); /* * Create a GET request under the transaction. @@ -848,6 +874,9 @@ static int mgmt_fe_session_handle_commit_config_req_msg( } if (session->cfg_txn_id == MGMTD_TXN_ID_NONE) { + /* as we have the lock no-one else should have a config txn */ + assert(!mgmt_config_txn_in_progress()); + /* * Start a CONFIG Transaction (if not started already) */ @@ -861,10 +890,9 @@ static int mgmt_fe_session_handle_commit_config_req_msg( "Failed to create a Configuration session!"); return 0; } - MGMTD_FE_ADAPTER_DBG("Created txn-id: %" PRIu64 - " for session-id %" PRIu64 - " for COMMIT-CFG-REQ", - session->cfg_txn_id, session->session_id); + __dbg("Created txn-id: %" PRIu64 " for session-id %" PRIu64 + " for COMMIT-CFG-REQ", + session->cfg_txn_id, session->session_id); } /* @@ -898,8 +926,8 @@ mgmt_fe_adapter_handle_msg(struct mgmt_fe_client_adapter *adapter, */ switch ((int)fe_msg->message_case) { case MGMTD__FE_MESSAGE__MESSAGE_REGISTER_REQ: - MGMTD_FE_ADAPTER_DBG("Got REGISTER_REQ from '%s'", - fe_msg->register_req->client_name); + __dbg("Got REGISTER_REQ from '%s'", + fe_msg->register_req->client_name); if (strlen(fe_msg->register_req->client_name)) { strlcpy(adapter->name, @@ -912,11 +940,10 @@ mgmt_fe_adapter_handle_msg(struct mgmt_fe_client_adapter *adapter, if (fe_msg->session_req->create && fe_msg->session_req->id_case == MGMTD__FE_SESSION_REQ__ID_CLIENT_CONN_ID) { - MGMTD_FE_ADAPTER_DBG( - "Got SESSION_REQ (create) for client-id %" PRIu64 - " from '%s'", - fe_msg->session_req->client_conn_id, - adapter->name); + __dbg("Got SESSION_REQ (create) for client-id %" PRIu64 + " from '%s'", + fe_msg->session_req->client_conn_id, + adapter->name); session = mgmt_fe_create_session( adapter, fe_msg->session_req->client_conn_id); @@ -926,10 +953,9 @@ mgmt_fe_adapter_handle_msg(struct mgmt_fe_client_adapter *adapter, !fe_msg->session_req->create && fe_msg->session_req->id_case == MGMTD__FE_SESSION_REQ__ID_SESSION_ID) { - MGMTD_FE_ADAPTER_DBG( - "Got SESSION_REQ (destroy) for session-id %" PRIu64 - "from '%s'", - fe_msg->session_req->session_id, adapter->name); + __dbg("Got SESSION_REQ (destroy) for session-id %" PRIu64 + "from '%s'", + fe_msg->session_req->session_id, adapter->name); session = mgmt_session_id2ctx( fe_msg->session_req->session_id); @@ -941,12 +967,11 @@ mgmt_fe_adapter_handle_msg(struct mgmt_fe_client_adapter *adapter, case MGMTD__FE_MESSAGE__MESSAGE_LOCKDS_REQ: session = mgmt_session_id2ctx( fe_msg->lockds_req->session_id); - MGMTD_FE_ADAPTER_DBG( - "Got LOCKDS_REQ (%sLOCK) for DS:%s for session-id %" PRIu64 - " from '%s'", - fe_msg->lockds_req->lock ? "" : "UN", - mgmt_ds_id2name(fe_msg->lockds_req->ds_id), - fe_msg->lockds_req->session_id, adapter->name); + __dbg("Got LOCKDS_REQ (%sLOCK) for DS:%s for session-id %" PRIu64 + " from '%s'", + fe_msg->lockds_req->lock ? "" : "UN", + mgmt_ds_id2name(fe_msg->lockds_req->ds_id), + fe_msg->lockds_req->session_id, adapter->name); mgmt_fe_session_handle_lockds_req_msg( session, fe_msg->lockds_req); break; @@ -954,13 +979,12 @@ mgmt_fe_adapter_handle_msg(struct mgmt_fe_client_adapter *adapter, session = mgmt_session_id2ctx( fe_msg->setcfg_req->session_id); session->adapter->setcfg_stats.set_cfg_count++; - MGMTD_FE_ADAPTER_DBG( - "Got SETCFG_REQ (%d Xpaths, Implicit:%c) on DS:%s for session-id %" PRIu64 - " from '%s'", - (int)fe_msg->setcfg_req->n_data, - fe_msg->setcfg_req->implicit_commit ? 'T' : 'F', - mgmt_ds_id2name(fe_msg->setcfg_req->ds_id), - fe_msg->setcfg_req->session_id, adapter->name); + __dbg("Got SETCFG_REQ (%d Xpaths, Implicit:%c) on DS:%s for session-id %" PRIu64 + " from '%s'", + (int)fe_msg->setcfg_req->n_data, + fe_msg->setcfg_req->implicit_commit ? 'T' : 'F', + mgmt_ds_id2name(fe_msg->setcfg_req->ds_id), + fe_msg->setcfg_req->session_id, adapter->name); mgmt_fe_session_handle_setcfg_req_msg( session, fe_msg->setcfg_req); @@ -968,31 +992,28 @@ mgmt_fe_adapter_handle_msg(struct mgmt_fe_client_adapter *adapter, case MGMTD__FE_MESSAGE__MESSAGE_COMMCFG_REQ: session = mgmt_session_id2ctx( fe_msg->commcfg_req->session_id); - MGMTD_FE_ADAPTER_DBG( - "Got COMMCFG_REQ for src-DS:%s dst-DS:%s (Abort:%c) on session-id %" PRIu64 - " from '%s'", - mgmt_ds_id2name(fe_msg->commcfg_req->src_ds_id), - mgmt_ds_id2name(fe_msg->commcfg_req->dst_ds_id), - fe_msg->commcfg_req->abort ? 'T' : 'F', - fe_msg->commcfg_req->session_id, adapter->name); + __dbg("Got COMMCFG_REQ for src-DS:%s dst-DS:%s (Abort:%c) on session-id %" PRIu64 + " from '%s'", + mgmt_ds_id2name(fe_msg->commcfg_req->src_ds_id), + mgmt_ds_id2name(fe_msg->commcfg_req->dst_ds_id), + fe_msg->commcfg_req->abort ? 'T' : 'F', + fe_msg->commcfg_req->session_id, adapter->name); mgmt_fe_session_handle_commit_config_req_msg( session, fe_msg->commcfg_req); break; case MGMTD__FE_MESSAGE__MESSAGE_GET_REQ: session = mgmt_session_id2ctx(fe_msg->get_req->session_id); - MGMTD_FE_ADAPTER_DBG("Got GET_REQ (iscfg %d) for DS:%s (xpaths: %d) on session-id %" PRIu64 - " from '%s'", - (int)fe_msg->get_req->config, - mgmt_ds_id2name(fe_msg->get_req->ds_id), - (int)fe_msg->get_req->n_data, - fe_msg->get_req->session_id, adapter->name); + __dbg("Got GET_REQ for DS:%s (xpaths: %d) on session-id %" PRIu64 + " from '%s'", + mgmt_ds_id2name(fe_msg->get_req->ds_id), + (int)fe_msg->get_req->n_data, fe_msg->get_req->session_id, + adapter->name); mgmt_fe_session_handle_get_req_msg(session, fe_msg->get_req); break; case MGMTD__FE_MESSAGE__MESSAGE_NOTIFY_DATA_REQ: case MGMTD__FE_MESSAGE__MESSAGE_REGNOTIFY_REQ: - MGMTD_FE_ADAPTER_ERR( - "Got unhandled message of type %u from '%s'", - fe_msg->message_case, adapter->name); + __log_err("Got unhandled message of type %u from '%s'", + fe_msg->message_case, adapter->name); /* * TODO: Add handling code in future. */ @@ -1020,25 +1041,265 @@ mgmt_fe_adapter_handle_msg(struct mgmt_fe_client_adapter *adapter, return 0; } +/** + * Send result of get-tree request back to the FE client. + * + * Args: + * session: the session. + * req_id: the request ID. + * short_circuit_ok: if allowed to short circuit the message. + * result_format: LYD_FORMAT for the sent output. + * tree: the tree to send, can be NULL which will send an empty tree. + * partial_error: if an error occurred during gathering results. + * + * Return: + * Any error that occurs -- the message is likely not sent if non-zero. + */ +static int fe_adapter_send_tree_data(struct mgmt_fe_session_ctx *session, + uint64_t req_id, bool short_circuit_ok, + uint8_t result_type, uint32_t wd_options, + const struct lyd_node *tree, + int partial_error) + +{ + struct mgmt_msg_tree_data *msg; + uint8_t **darrp = NULL; + int ret = 0; + + msg = mgmt_msg_native_alloc_msg(struct mgmt_msg_tree_data, 0, + MTYPE_MSG_NATIVE_TREE_DATA); + msg->refer_id = session->session_id; + msg->req_id = req_id; + msg->code = MGMT_MSG_CODE_TREE_DATA; + msg->partial_error = partial_error; + msg->result_type = result_type; + + darrp = mgmt_msg_native_get_darrp(msg); + ret = yang_print_tree_append(darrp, tree, result_type, + (wd_options | LYD_PRINT_WITHSIBLINGS)); + if (ret != LY_SUCCESS) { + __log_err("Error building get-tree result for client %s session-id %" PRIu64 + " req-id %" PRIu64 " scok %d result type %u", + session->adapter->name, session->session_id, req_id, + short_circuit_ok, result_type); + goto done; + } + + __dbg("Sending get-tree result from adapter %s to session-id %" PRIu64 + " req-id %" PRIu64 " scok %d result type %u len %u", + session->adapter->name, session->session_id, req_id, + short_circuit_ok, result_type, mgmt_msg_native_get_msg_len(msg)); + + ret = fe_adapter_send_native_msg(session->adapter, msg, + mgmt_msg_native_get_msg_len(msg), + short_circuit_ok); +done: + 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. + * @msg_raw: the message data. + * @msg_len: the length of the message data. + */ +static void fe_adapter_handle_get_data(struct mgmt_fe_session_ctx *session, + void *__msg, size_t msg_len) +{ + struct mgmt_msg_get_data *msg = __msg; + struct lysc_node **snodes = NULL; + char *xpath_resolved = NULL; + uint64_t req_id = msg->req_id; + Mgmtd__DatastoreId ds_id; + uint64_t clients; + uint32_t wd_options; + bool simple_xpath; + LY_ERR err; + int ret; + + __dbg("Received get-data request from client %s for session-id %" PRIu64 + " req-id %" PRIu64, + session->adapter->name, session->session_id, msg->req_id); + + if (!MGMT_MSG_VALIDATE_NUL_TERM(msg, msg_len)) { + fe_adapter_send_error(session, req_id, false, -EINVAL, + "Invalid message rcvd from session-id: %" PRIu64, + session->session_id); + goto done; + } + + 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); + goto done; + } + + switch (msg->defaults) { + case GET_DATA_DEFAULTS_EXPLICIT: + wd_options = LYD_PRINT_WD_EXPLICIT; + break; + case GET_DATA_DEFAULTS_TRIM: + wd_options = LYD_PRINT_WD_TRIM; + break; + case GET_DATA_DEFAULTS_ALL: + wd_options = LYD_PRINT_WD_ALL; + break; + case GET_DATA_DEFAULTS_ALL_ADD_TAG: + wd_options = LYD_PRINT_WD_IMPL_TAG; + break; + default: + fe_adapter_send_error(session, req_id, false, -EINVAL, + "Invalid defaults value %u for session-id: %" PRIu64, + msg->defaults, session->session_id); + goto done; + } + + switch (msg->datastore) { + case MGMT_MSG_DATASTORE_CANDIDATE: + ds_id = MGMTD_DS_CANDIDATE; + break; + case MGMT_MSG_DATASTORE_RUNNING: + ds_id = MGMTD_DS_RUNNING; + break; + case MGMT_MSG_DATASTORE_OPERATIONAL: + ds_id = MGMTD_DS_OPERATIONAL; + break; + default: + fe_adapter_send_error(session, req_id, false, -EINVAL, + "Unsupported datastore %" PRIu8 + " requested from session-id: %" PRIu64, + msg->datastore, session->session_id); + goto done; + } + + err = yang_resolve_snode_xpath(ly_native_ctx, msg->xpath, &snodes, + &simple_xpath); + if (err) { + fe_adapter_send_error(session, req_id, false, -EINPROGRESS, + "XPath doesn't resolve for session-id: %" PRIu64, + session->session_id); + goto done; + } + darr_free(snodes); + + clients = mgmt_be_interested_clients(msg->xpath, false); + if (!clients && !CHECK_FLAG(msg->flags, GET_DATA_FLAG_CONFIG)) { + __dbg("No backends provide xpath: %s for txn-id: %" PRIu64 + " session-id: %" PRIu64, + msg->xpath, session->txn_id, session->session_id); + + fe_adapter_send_tree_data(session, req_id, false, + msg->result_type, wd_options, NULL, 0); + goto done; + } + + /* Start a SHOW Transaction */ + session->txn_id = mgmt_create_txn(session->session_id, + MGMTD_TXN_TYPE_SHOW); + if (session->txn_id == MGMTD_SESSION_ID_NONE) { + fe_adapter_send_error(session, req_id, false, -EINPROGRESS, + "failed to create a 'show' txn"); + goto done; + } + + __dbg("Created new show txn-id: %" PRIu64 " for session-id: %" PRIu64, + session->txn_id, session->session_id); + + /* Create a GET-TREE request under the transaction */ + ret = mgmt_txn_send_get_tree_oper(session->txn_id, req_id, clients, + ds_id, msg->result_type, msg->flags, + wd_options, simple_xpath, msg->xpath); + 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 a 'show' txn"); + } +done: + darr_free(snodes); + darr_free(xpath_resolved); +} + +/** + * Handle a native encoded message from the FE client. + */ +static void fe_adapter_handle_native_msg(struct mgmt_fe_client_adapter *adapter, + struct mgmt_msg_header *msg, + size_t msg_len) +{ + struct mgmt_fe_session_ctx *session; + + session = mgmt_session_id2ctx(msg->refer_id); + if (!session) { + __log_err("adapter %s: recv msg unknown session-id %" PRIu64, + adapter->name, msg->refer_id); + return; + } + assert(session->adapter == adapter); + + switch (msg->code) { + case MGMT_MSG_CODE_GET_DATA: + fe_adapter_handle_get_data(session, msg, msg_len); + break; + default: + __log_err("unknown native message session-id %" PRIu64 + " req-id %" PRIu64 " code %u to FE adapter %s", + msg->refer_id, msg->req_id, msg->code, adapter->name); + break; + } +} + + static void mgmt_fe_adapter_process_msg(uint8_t version, uint8_t *data, size_t len, struct msg_conn *conn) { struct mgmt_fe_client_adapter *adapter = conn->user; - Mgmtd__FeMessage *fe_msg = mgmtd__fe_message__unpack(NULL, len, data); + Mgmtd__FeMessage *fe_msg; + + if (version == MGMT_MSG_VERSION_NATIVE) { + struct mgmt_msg_header *msg = (typeof(msg))data; + if (len >= sizeof(*msg)) + fe_adapter_handle_native_msg(adapter, msg, len); + else + __log_err("native message to adapter %s too short %zu", + adapter->name, len); + return; + } + + fe_msg = mgmtd__fe_message__unpack(NULL, len, data); if (!fe_msg) { - MGMTD_FE_ADAPTER_DBG( - "Failed to decode %zu bytes for adapter: %s", len, - adapter->name); + __dbg("Failed to decode %zu bytes for adapter: %s", len, + adapter->name); return; } - MGMTD_FE_ADAPTER_DBG( - "Decoded %zu bytes of message: %u from adapter: %s", len, - fe_msg->message_case, adapter->name); + __dbg("Decoded %zu bytes of message: %u from adapter: %s", len, + fe_msg->message_case, adapter->name); (void)mgmt_fe_adapter_handle_msg(adapter, fe_msg); mgmtd__fe_message__free_unpacked(fe_msg, NULL); } +void mgmt_fe_adapter_send_notify(struct mgmt_msg_notify_data *msg, size_t msglen) +{ + struct mgmt_fe_client_adapter *adapter; + struct mgmt_fe_session_ctx *session; + + assert(msg->refer_id == 0); + + FOREACH_ADAPTER_IN_LIST (adapter) { + FOREACH_SESSION_IN_LIST (adapter, session) { + msg->refer_id = session->session_id; + (void)fe_adapter_send_native_msg(adapter, msg, msglen, + false); + } + } + msg->refer_id = 0; +} + void mgmt_fe_adapter_lock(struct mgmt_fe_client_adapter *adapter) { adapter->refcount++; @@ -1062,6 +1323,8 @@ extern void mgmt_fe_adapter_unlock(struct mgmt_fe_client_adapter **adapter) */ void mgmt_fe_adapter_init(struct event_loop *tm) { + char server_path[MAXPATHLEN]; + assert(!mgmt_loop); mgmt_loop = tm; @@ -1072,9 +1335,10 @@ void mgmt_fe_adapter_init(struct event_loop *tm) hash_create(mgmt_fe_session_hash_key, mgmt_fe_session_hash_cmp, "MGMT Frontend Sessions"); - if (msg_server_init(&mgmt_fe_server, MGMTD_FE_SERVER_PATH, tm, - mgmt_fe_create_adapter, "frontend", - &mgmt_debug_fe)) { + snprintf(server_path, sizeof(server_path), MGMTD_FE_SOCK_NAME); + + if (msg_server_init(&mgmt_fe_server, server_path, tm, + mgmt_fe_create_adapter, "frontend", &mgmt_debug_fe)) { zlog_err("cannot initialize frontend server"); exit(1); } @@ -1084,11 +1348,10 @@ static void mgmt_fe_abort_if_session(void *data) { struct mgmt_fe_session_ctx *session = data; - MGMTD_FE_ADAPTER_ERR("found orphaned session id %" PRIu64 - " client id %" PRIu64 " adapter %s", - session->session_id, session->client_id, - session->adapter ? session->adapter->name - : "NULL"); + __log_err("found orphaned session id %" PRIu64 " client id %" PRIu64 + " adapter %s", + session->session_id, session->client_id, + session->adapter ? session->adapter->name : "NULL"); abort(); } @@ -1129,13 +1392,15 @@ struct msg_conn *mgmt_fe_create_adapter(int conn_fd, union sockunion *from) adapter->conn = msg_server_conn_create( mgmt_loop, conn_fd, mgmt_fe_adapter_notify_disconnect, mgmt_fe_adapter_process_msg, MGMTD_FE_MAX_NUM_MSG_PROC, - MGMTD_FE_MAX_NUM_MSG_WRITE, MGMTD_FE_MSG_MAX_LEN, + MGMTD_FE_MAX_NUM_MSG_WRITE, MGMTD_FE_MAX_MSG_LEN, adapter, "FE-adapter"); + adapter->conn->debug = DEBUG_MODE_CHECK(&mgmt_debug_fe, + DEBUG_MODE_ALL); + adapter->setcfg_stats.min_tm = ULONG_MAX; adapter->cmt_stats.min_tm = ULONG_MAX; - MGMTD_FE_ADAPTER_DBG("Added new MGMTD Frontend adapter '%s'", - adapter->name); + __dbg("Added new MGMTD Frontend adapter '%s'", adapter->name); } return adapter->conn; } @@ -1151,10 +1416,9 @@ int mgmt_fe_send_set_cfg_reply(uint64_t session_id, uint64_t txn_id, session = mgmt_session_id2ctx(session_id); if (!session || session->cfg_txn_id != txn_id) { if (session) - MGMTD_FE_ADAPTER_ERR( - "txn-id doesn't match, session txn-id is %" PRIu64 - " current txnid: %" PRIu64, - session->cfg_txn_id, txn_id); + __log_err("txn-id doesn't match, session txn-id is %" PRIu64 + " current txnid: %" PRIu64, + session->cfg_txn_id, txn_id); return -1; } @@ -1198,8 +1462,56 @@ int mgmt_fe_send_get_reply(uint64_t session_id, uint64_t txn_id, error_if_any); } -struct mgmt_setcfg_stats * -mgmt_fe_get_session_setcfg_stats(uint64_t session_id) +int mgmt_fe_adapter_send_tree_data(uint64_t session_id, uint64_t txn_id, + uint64_t req_id, LYD_FORMAT result_type, + uint32_t wd_options, + const struct lyd_node *tree, + int partial_error, bool short_circuit_ok) +{ + 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_tree_data(session, req_id, short_circuit_ok, + result_type, wd_options, tree, + partial_error); + + mgmt_destroy_txn(&session->txn_id); + + return ret; +} + +/** + * Send an error back to the FE client and cleanup any in-progress txn. + */ +int mgmt_fe_adapter_txn_error(uint64_t txn_id, uint64_t req_id, + bool short_circuit_ok, int16_t error, + const char *errstr) +{ + struct mgmt_fe_session_ctx *session; + int ret; + + session = fe_adapter_session_by_txn_id(txn_id); + if (!session) { + __log_err("failed sending error for txn-id %" PRIu64 + " session not found", + txn_id); + return -ENOENT; + } + + + ret = fe_adapter_send_error(session, req_id, false, error, "%s", errstr); + + mgmt_destroy_txn(&session->txn_id); + + return ret; +} + + +struct mgmt_setcfg_stats *mgmt_fe_get_session_setcfg_stats(uint64_t session_id) { struct mgmt_fe_session_ctx *session; diff --git a/mgmtd/mgmt_fe_adapter.h b/mgmtd/mgmt_fe_adapter.h index d2991ec..2150f86 100644 --- a/mgmtd/mgmt_fe_adapter.h +++ b/mgmtd/mgmt_fe_adapter.h @@ -12,7 +12,7 @@ #include "mgmt_fe_client.h" #include "mgmt_msg.h" -#include "mgmtd/mgmt_defines.h" +#include "mgmt_defines.h" struct mgmt_fe_client_adapter; struct mgmt_master; @@ -138,6 +138,52 @@ extern int mgmt_fe_send_get_reply(uint64_t session_id, uint64_t txn_id, Mgmtd__YangDataReply *data_resp, const char *error_if_any); +/** + * Send get-tree data 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 get_tree message + * result_type: the format of the result data. + * wd_options: with-defaults options. + * tree: the results. + * partial_error: if there were errors while gather results. + * short_circuit_ok: True if OK to short-circuit the call. + * + * Return: + * the return value from the underlying send function. + * + */ +extern int +mgmt_fe_adapter_send_tree_data(uint64_t session_id, uint64_t txn_id, + uint64_t req_id, LYD_FORMAT result_type, + uint32_t wd_options, const struct lyd_node *tree, + int partial_error, bool short_circuit_ok); + +/** + * Send an error back to the FE client using native messaging. + * + * This also cleans up and frees the transaction. + * + * Args: + * txn_id: the txn_id this error pertains to. + * short_circuit_ok: True if OK to short-circuit the call. + * error: An integer error value. + * errfmt: An error format string (i.e., printfrr) + * ...: args for use by the `errfmt` format string. + * + * Return: + * the return value from the underlying send function. + * + */ +extern int mgmt_fe_adapter_txn_error(uint64_t txn_id, uint64_t req_id, + bool short_circuit_ok, int16_t error, + const char *errstr); + + /* Fetch frontend client session set-config stats */ extern struct mgmt_setcfg_stats * mgmt_fe_get_session_setcfg_stats(uint64_t session_id); @@ -149,4 +195,8 @@ mgmt_fe_get_session_commit_stats(uint64_t session_id); extern void mgmt_fe_adapter_status_write(struct vty *vty, bool detail); extern void mgmt_fe_adapter_perf_measurement(struct vty *vty, bool config); extern void mgmt_fe_adapter_reset_perf_stats(struct vty *vty); + +/* Toggle debug on or off for connected clients. */ +extern void mgmt_fe_adapter_toggle_client_debug(bool set); + #endif /* _FRR_MGMTD_FE_ADAPTER_H_ */ diff --git a/mgmtd/mgmt_history.c b/mgmtd/mgmt_history.c index d406932..c97cb7f 100644 --- a/mgmtd/mgmt_history.c +++ b/mgmtd/mgmt_history.c @@ -63,7 +63,7 @@ static struct mgmt_cmt_info_t *mgmt_history_new_cmt_info(void) mgmt_time_to_string(&tv, true, new->time_str, sizeof(new->time_str)); mgmt_time_to_string(&tv, false, new->cmtid_str, sizeof(new->cmtid_str)); snprintf(new->cmt_json_file, sizeof(new->cmt_json_file), - MGMTD_COMMIT_FILE_PATH, new->cmtid_str); + MGMTD_COMMIT_FILE_PATH(new->cmtid_str)); return new; } @@ -104,18 +104,21 @@ mgmt_history_find_cmt_record(const char *cmtid_str) static bool mgmt_history_read_cmt_record_index(void) { + char index_path[MAXPATHLEN]; FILE *fp; struct mgmt_cmt_info_t cmt_info; struct mgmt_cmt_info_t *new; int cnt = 0; - if (!file_exists(MGMTD_COMMIT_FILE_PATH)) - return false; + snprintf(index_path, sizeof(index_path), MGMTD_COMMIT_INDEX_FILE_PATH); - fp = fopen(MGMTD_COMMIT_INDEX_FILE_NAME, "rb"); + fp = fopen(index_path, "rb"); if (!fp) { - zlog_err("Failed to open commit history %s for reading: %s", - MGMTD_COMMIT_INDEX_FILE_NAME, safe_strerror(errno)); + if (errno == ENOENT || errno == ENOTDIR) + return false; + + zlog_err("Failed to open commit history %pSQq for reading: %m", + index_path); return false; } @@ -132,9 +135,8 @@ static bool mgmt_history_read_cmt_record_index(void) memcpy(new, &cmt_info, sizeof(struct mgmt_cmt_info_t)); mgmt_cmt_infos_add_tail(&mm->cmts, new); } else { - zlog_warn( - "More records found in commit history file %s than expected", - MGMTD_COMMIT_INDEX_FILE_NAME); + zlog_warn("More records found in commit history file %pSQq than expected", + index_path); fclose(fp); return false; } @@ -148,16 +150,19 @@ static bool mgmt_history_read_cmt_record_index(void) static bool mgmt_history_dump_cmt_record_index(void) { + char index_path[MAXPATHLEN]; FILE *fp; int ret = 0; struct mgmt_cmt_info_t *cmt_info; struct mgmt_cmt_info_t cmt_info_set[10]; int cnt = 0; - fp = fopen(MGMTD_COMMIT_INDEX_FILE_NAME, "wb"); + snprintf(index_path, sizeof(index_path), MGMTD_COMMIT_INDEX_FILE_PATH); + + fp = fopen(index_path, "wb"); if (!fp) { - zlog_err("Failed to open commit history %s for writing: %s", - MGMTD_COMMIT_INDEX_FILE_NAME, safe_strerror(errno)); + zlog_err("Failed to open commit history %pSQq for writing: %m", + index_path); return false; } @@ -176,7 +181,7 @@ static bool mgmt_history_dump_cmt_record_index(void) fclose(fp); if (ret != cnt) { zlog_err("Failed to write full commit history, removing file"); - remove_file(MGMTD_COMMIT_INDEX_FILE_NAME); + remove_file(index_path); return false; } return true; @@ -261,7 +266,9 @@ failed_unlock: void mgmt_history_rollback_complete(bool success) { - vty_mgmt_resume_response(rollback_vty, success); + vty_mgmt_resume_response(rollback_vty, + success ? CMD_SUCCESS + : CMD_WARNING_CONFIG_FAILED); rollback_vty = NULL; } diff --git a/mgmtd/mgmt_main.c b/mgmtd/mgmt_main.c index 39362fa..cce16f5 100644 --- a/mgmtd/mgmt_main.c +++ b/mgmtd/mgmt_main.c @@ -14,18 +14,21 @@ #include "frr_pthread.h" #include "mgmtd/mgmt.h" #include "mgmtd/mgmt_ds.h" +#include "ripd/rip_nb.h" +#include "ripngd/ripng_nb.h" #include "routing_nb.h" - +#include "affinitymap.h" +#include "zebra/zebra_cli.h" /* mgmt options, we use GNU getopt library. */ static const struct option longopts[] = { {"skip_runas", no_argument, NULL, 'S'}, {"no_zebra", no_argument, NULL, 'Z'}, {"socket_size", required_argument, NULL, 's'}, + {"vrfwnetns", no_argument, NULL, 'n'}, {0}}; static void mgmt_exit(int); -static void mgmt_vrf_terminate(void); /* privileges */ static zebra_capabilities_t _caps_p[] = {ZCAP_BIND, ZCAP_NET_RAW, @@ -45,7 +48,6 @@ struct zebra_privs_t mgmt_privs = { }; static struct frr_daemon_info mgmtd_di; -char backup_config_file[256]; /* SIGHUP handler. */ static void sighup(void) @@ -114,8 +116,6 @@ static __attribute__((__noreturn__)) void mgmt_exit(int status) /* stop pthreads (if any) */ frr_pthread_stop_all(); - mgmt_vrf_terminate(); - frr_fini(); exit(status); } @@ -139,87 +139,81 @@ static struct frr_signal_t mgmt_signals[] = { }, }; -static int mgmt_vrf_new(struct vrf *vrf) -{ - zlog_debug("VRF Created: %s(%u)", vrf->name, vrf->vrf_id); - - return 0; -} - -static int mgmt_vrf_delete(struct vrf *vrf) -{ - zlog_debug("VRF Deletion: %s(%u)", vrf->name, vrf->vrf_id); - - return 0; -} - -static int mgmt_vrf_enable(struct vrf *vrf) -{ - zlog_debug("VRF Enable: %s(%u)", vrf->name, vrf->vrf_id); - - return 0; -} - -static int mgmt_vrf_disable(struct vrf *vrf) -{ - zlog_debug("VRF Disable: %s(%u)", vrf->name, vrf->vrf_id); - - /* Note: This is a callback, the VRF will be deleted by the caller. */ - return 0; -} - -static int mgmt_vrf_config_write(struct vty *vty) -{ - return 0; -} +#ifdef HAVE_STATICD +extern const struct frr_yang_module_info frr_staticd_cli_info; +#endif -static void mgmt_vrf_init(void) -{ - vrf_init(mgmt_vrf_new, mgmt_vrf_enable, mgmt_vrf_disable, - mgmt_vrf_delete); - vrf_cmd_init(mgmt_vrf_config_write); -} +/* + * These are modules that are only needed by mgmtd and hence not included into + * the lib and backend daemons. + */ +const struct frr_yang_module_info ietf_netconf_with_defaults_info = { + .name = "ietf-netconf-with-defaults", + .ignore_cfg_cbs = true, + .nodes = { { .xpath = NULL } }, +}; -static void mgmt_vrf_terminate(void) -{ - vrf_terminate(); -} +/* + * These are stub info structs that are used to load the modules used by backend + * clients into mgmtd. The modules are used by libyang in order to support + * parsing binary data returns from the backend. + */ +const struct frr_yang_module_info zebra_route_map_info = { + .name = "frr-zebra-route-map", + .ignore_cfg_cbs = true, + .nodes = { { .xpath = NULL } }, +}; /* * List of YANG modules to be loaded in the process context of * MGMTd. - * - * NOTE: In future this will also include the YANG modules of - * all individual Backend clients. */ static const struct frr_yang_module_info *const mgmt_yang_modules[] = { - &frr_filter_info, - &frr_interface_info, - &frr_route_map_info, - &frr_routing_info, - &frr_vrf_info, -/* - * YANG module info supported by backend clients get added here. - * NOTE: Always set .ignore_cbs true for to avoid validating - * backend northbound callbacks during loading. - */ + &frr_filter_cli_info, + &frr_interface_cli_info, + &frr_route_map_cli_info, + &frr_routing_cli_info, + &frr_vrf_cli_info, + &frr_affinity_map_cli_info, + + /* mgmtd-only modules */ + &ietf_netconf_with_defaults_info, + + /* + * YANG module info used by backend clients get added here. + */ + + &frr_zebra_cli_info, + &zebra_route_map_info, + +#ifdef HAVE_RIPD + &frr_ripd_cli_info, +#endif +#ifdef HAVE_RIPNGD + &frr_ripngd_cli_info, +#endif #ifdef HAVE_STATICD - &(struct frr_yang_module_info){.name = "frr-staticd", - .ignore_cbs = true}, + &frr_staticd_cli_info, #endif }; -FRR_DAEMON_INFO(mgmtd, MGMTD, .vty_port = MGMTD_VTY_PORT, +/* clang-format off */ +FRR_DAEMON_INFO(mgmtd, MGMTD, + .vty_port = MGMTD_VTY_PORT, + .proghelp = "FRR Management Daemon.", - .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); + /* 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 "" @@ -235,8 +229,9 @@ int main(int argc, char **argv) frr_preinit(&mgmtd_di, argc, argv); frr_opt_add( - "s:" DEPRECATED_OPTIONS, longopts, - " -s, --socket_size Set MGMTD peer socket send buffer size\n"); + "s:n" DEPRECATED_OPTIONS, longopts, + " -s, --socket_size Set MGMTD peer socket send buffer size\n" + " -n, --vrfwnetns Use NetNS as VRF backend\n"); /* Command line argument treatment. */ while (1) { @@ -258,6 +253,9 @@ int main(int argc, char **argv) case 's': buffer_size = atoi(optarg); break; + case 'n': + vrf_configure_backend(VRF_BACKEND_NETNS); + break; default: frr_help_exit(1); break; @@ -267,17 +265,15 @@ int main(int argc, char **argv) /* MGMTD master init. */ mgmt_master_init(frr_init(), buffer_size); - /* VRF Initializations. */ - mgmt_vrf_init(); + /* VRF commands initialization. */ + vrf_cmd_init(NULL); + + /* Interface commands initialization. */ + if_cmd_init(NULL); /* MGMTD related initialization. */ mgmt_init(); - snprintf(backup_config_file, sizeof(backup_config_file), - "%s/zebra.conf", frr_sysconfdir); - mgmtd_di.backup_config_file = backup_config_file; - - /* this will queue a read configs event */ frr_config_fork(); frr_run(mm->master); diff --git a/mgmtd/mgmt_memory.c b/mgmtd/mgmt_memory.c index b2a0f0e..0fce61a 100644 --- a/mgmtd/mgmt_memory.c +++ b/mgmtd/mgmt_memory.c @@ -29,5 +29,6 @@ DEFINE_MTYPE(MGMTD, MGMTD_TXN_SETCFG_REQ, "txn set-config requests"); 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_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 06518e3..d5b6aa6 100644 --- a/mgmtd/mgmt_memory.h +++ b/mgmtd/mgmt_memory.h @@ -23,6 +23,7 @@ DECLARE_MTYPE(MGMTD_TXN_SETCFG_REQ); 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_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 new file mode 100644 index 0000000..7e3ded8 --- /dev/null +++ b/mgmtd/mgmt_testc.c @@ -0,0 +1,230 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * January 29 2024, Christian Hopps <chopps@labn.net> + * + * Copyright (c) 2024, LabN Consulting, L.L.C. + * + */ + +#include <zebra.h> +#include <lib/version.h> +#include "darr.h" +#include "libfrr.h" +#include "mgmt_be_client.h" +#include "northbound.h" + +/* ---------------- */ +/* Local Prototypes */ +/* ---------------- */ + +static void async_notification(struct nb_cb_notify_args *args); + +static void sigusr1(void); +static void sigint(void); + +/* ----------- */ +/* Global Data */ +/* ----------- */ + +/* privileges */ +static zebra_capabilities_t _caps_p[] = {}; + +struct zebra_privs_t __privs = { +#if defined(FRR_USER) && defined(FRR_GROUP) + .user = FRR_USER, + .group = FRR_GROUP, +#endif +#ifdef VTY_GROUP + .vty_group = VTY_GROUP, +#endif + .caps_p = _caps_p, + .cap_num_p = array_size(_caps_p), + .cap_num_i = 0, +}; + +#define OPTION_LISTEN 2000 +#define OPTION_NOTIF_COUNT 2001 +#define OPTION_TIMEOUT 2002 +const struct option longopts[] = { + { "listen", no_argument, NULL, OPTION_LISTEN }, + { "notif-count", required_argument, NULL, OPTION_NOTIF_COUNT }, + { "timeout", required_argument, NULL, OPTION_TIMEOUT }, + { 0 } +}; + + +/* Master of threads. */ +struct event_loop *master; + +struct mgmt_be_client *mgmt_be_client; + +static struct frr_daemon_info mgmtd_testc_di; + +struct frr_signal_t __signals[] = { + { + .signal = SIGUSR1, + .handler = &sigusr1, + }, + { + .signal = SIGINT, + .handler = &sigint, + }, + { + .signal = SIGTERM, + .handler = &sigint, + }, +}; + +#define MGMTD_TESTC_VTY_PORT 2624 + +/* clang-format off */ +static const struct frr_yang_module_info frr_ripd_info = { + .name = "frr-ripd", + .ignore_cfg_cbs = true, + .nodes = { + { + .xpath = "/frr-ripd:authentication-failure", + .cbs.notify = async_notification, + }, + { + .xpath = NULL, + } + } +}; + +static const struct frr_yang_module_info *const mgmt_yang_modules[] = { + &frr_ripd_info, +}; + +FRR_DAEMON_INFO(mgmtd_testc, MGMTD_TESTC, + .proghelp = "FRR Management Daemon Test Client.", + + .signals = __signals, + .n_signals = array_size(__signals), + + .privs = &__privs, + + .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, + ); +/* clang-format on */ + +const char **__notif_xpaths; + +struct mgmt_be_client_cbs __client_cbs = {}; +struct event *event_timeout; + +int o_notif_count = 1; +int o_timeout; + +/* --------- */ +/* Functions */ +/* --------- */ + + +static void sigusr1(void) +{ + zlog_rotate(); +} + +static void quit(int exit_code) +{ + EVENT_OFF(event_timeout); + frr_fini(); + darr_free(__client_cbs.notif_xpaths); + exit(exit_code); +} + +static void sigint(void) +{ + zlog_notice("Terminating on signal"); + quit(0); +} + +static void timeout(struct event *event) +{ + zlog_notice("Timeout, exiting"); + quit(1); +} + +static void async_notification(struct nb_cb_notify_args *args) +{ + zlog_notice("Received YANG notification"); + + printf("{\"frr-ripd:authentication-failure\": {\"interface-name\": \"%s\"}}\n", + yang_dnode_get_string(args->dnode, "interface-name")); + + if (o_notif_count && !--o_notif_count) + quit(0); +} + +int main(int argc, char **argv) +{ + int f_listen = 0; + int i; + + frr_preinit(&mgmtd_testc_di, argc, argv); + frr_opt_add("", longopts, ""); + + while (1) { + int opt; + + opt = frr_getopt(argc, argv, NULL); + + if (opt == EOF) + break; + + switch (opt) { + case OPTION_LISTEN: + f_listen = 1; + break; + case OPTION_NOTIF_COUNT: + o_notif_count = atoi(optarg); + break; + case OPTION_TIMEOUT: + o_timeout = atoi(optarg); + break; + case 0: + break; + default: + frr_help_exit(1); + } + } + + master = frr_init(); + + /* + * Setup notification listen + */ + argv += optind; + argc -= optind; + if (!argc && f_listen) { + fprintf(stderr, + "Must specify at least one notification xpath to listen to\n"); + exit(1); + } + if (argc && f_listen) { + for (i = 0; i < argc; i++) { + zlog_notice("Listen on xpath: %s", argv[i]); + darr_push(__notif_xpaths, argv[i]); + } + __client_cbs.notif_xpaths = __notif_xpaths; + __client_cbs.nnotif_xpaths = darr_len(__notif_xpaths); + } + + mgmt_be_client = mgmt_be_client_create("mgmtd-testc", &__client_cbs, 0, + master); + + frr_config_fork(); + + if (o_timeout) + event_add_timer(master, timeout, NULL, o_timeout, &event_timeout); + + frr_run(master); + + /* Reached. */ + return 0; +} diff --git a/mgmtd/mgmt_txn.c b/mgmtd/mgmt_txn.c index 452f9c8..c1cb33f 100644 --- a/mgmtd/mgmt_txn.c +++ b/mgmtd/mgmt_txn.c @@ -7,17 +7,19 @@ */ #include <zebra.h> +#include "darr.h" #include "hash.h" #include "jhash.h" #include "libfrr.h" +#include "mgmt_msg.h" +#include "mgmt_msg_native.h" #include "mgmtd/mgmt.h" #include "mgmtd/mgmt_memory.h" #include "mgmtd/mgmt_txn.h" -#define MGMTD_TXN_DBG(fmt, ...) \ +#define __dbg(fmt, ...) \ DEBUGD(&mgmt_debug_txn, "TXN: %s: " fmt, __func__, ##__VA_ARGS__) -#define MGMTD_TXN_ERR(fmt, ...) \ - zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__) +#define __log_err(fmt, ...) zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__) #define MGMTD_TXN_LOCK(txn) mgmt_txn_lock(txn, __FILE__, __LINE__) #define MGMTD_TXN_UNLOCK(txn) mgmt_txn_unlock(txn, __FILE__, __LINE__) @@ -26,9 +28,9 @@ enum mgmt_txn_event { MGMTD_TXN_PROC_SETCFG = 1, MGMTD_TXN_PROC_COMMITCFG, MGMTD_TXN_PROC_GETCFG, - MGMTD_TXN_PROC_GETDATA, + MGMTD_TXN_PROC_GETTREE, MGMTD_TXN_COMMITCFG_TIMEOUT, - MGMTD_TXN_CLEANUP + MGMTD_TXN_GETTREE_TIMEOUT, }; PREDECL_LIST(mgmt_txn_reqs); @@ -77,17 +79,14 @@ PREDECL_LIST(mgmt_txn_batches); struct mgmt_txn_be_cfg_batch { struct mgmt_txn_ctx *txn; - uint64_t batch_id; enum mgmt_be_client_id be_id; struct mgmt_be_client_adapter *be_adapter; - uint xp_subscr[MGMTD_MAX_CFG_CHANGES_IN_BATCH]; Mgmtd__YangCfgDataReq cfg_data[MGMTD_MAX_CFG_CHANGES_IN_BATCH]; Mgmtd__YangCfgDataReq *cfg_datap[MGMTD_MAX_CFG_CHANGES_IN_BATCH]; Mgmtd__YangData data[MGMTD_MAX_CFG_CHANGES_IN_BATCH]; Mgmtd__YangDataValue value[MGMTD_MAX_CFG_CHANGES_IN_BATCH]; size_t num_cfg_data; int buf_space_left; - enum mgmt_commit_phase comm_phase; struct mgmt_txn_batches_item list_linkage; }; @@ -106,10 +105,12 @@ struct mgmt_commit_cfg_req { uint8_t abort : 1; uint8_t implicit : 1; uint8_t rollback : 1; + uint8_t init : 1; /* Track commit phases */ - enum mgmt_commit_phase curr_phase; - enum mgmt_commit_phase next_phase; + enum mgmt_commit_phase phase; + + enum mgmt_commit_phase be_phase[MGMTD_BE_CLIENT_ID_MAX]; /* * Set of config changes to commit. This is used only @@ -125,26 +126,17 @@ struct mgmt_commit_cfg_req { * Details on all the Backend Clients associated with * this commit. */ - struct mgmt_be_client_subscr_info subscr_info; + uint64_t clients; /* * List of backend batches for this commit to be validated * and applied at the backend. - * - * FIXME: Need to re-think this design for the case set of - * validators for a given YANG data item is different from - * the set of notifiers for the same. We may need to have - * separate list of batches for VALIDATE and APPLY. */ - struct mgmt_txn_batches_head curr_batches[MGMTD_BE_CLIENT_ID_MAX]; - struct mgmt_txn_batches_head next_batches[MGMTD_BE_CLIENT_ID_MAX]; + struct mgmt_txn_batches_head batches[MGMTD_BE_CLIENT_ID_MAX]; /* - * The last batch added for any backend client. This is always on - * 'curr_batches' + * The last batch added for any backend client. */ struct mgmt_txn_be_cfg_batch *last_be_cfg_batch[MGMTD_BE_CLIENT_ID_MAX]; - struct hash *batches; - uint64_t next_batch_id; struct mgmt_commit_stats *cmt_stats; }; @@ -176,6 +168,19 @@ struct mgmt_get_data_req { int total_reply; }; + +struct txn_req_get_tree { + char *xpath; /* xpath of tree to get */ + uint64_t sent_clients; /* Bitmask of clients sent req to */ + uint64_t recv_clients; /* Bitmask of clients recv reply from */ + int32_t partial_error; /* an error while gather results */ + uint8_t result_type; /* LYD_FORMAT for results */ + uint8_t wd_options; /* LYD_PRINT_WD_* flags for results */ + uint8_t exact; /* if exact node is requested */ + uint8_t simple_xpath; /* if xpath is simple */ + struct lyd_node *client_results; /* result tree from clients */ +}; + struct mgmt_txn_req { struct mgmt_txn_ctx *txn; enum mgmt_txn_event req_event; @@ -183,10 +188,10 @@ struct mgmt_txn_req { union { struct mgmt_set_cfg_req *set_cfg; struct mgmt_get_data_req *get_data; + struct txn_req_get_tree *get_tree; struct mgmt_commit_cfg_req commit_cfg; } req; - bool pending_be_proc; struct mgmt_txn_reqs_item list_linkage; }; @@ -206,7 +211,9 @@ struct mgmt_txn_ctx { struct event *proc_comm_cfg; struct event *proc_get_cfg; struct event *proc_get_data; + struct event *proc_get_tree; struct event *comm_cfg_timeout; + struct event *get_tree_timeout; struct event *clnup; /* List of backend adapters involved in this transaction */ @@ -216,6 +223,10 @@ struct mgmt_txn_ctx { struct mgmt_txns_item list_linkage; + /* TODO: why do we need unique lists for each type of transaction since + * a transaction is of only 1 type? + */ + /* * List of pending set-config requests for a given * transaction/session. Just one list for requests @@ -231,13 +242,9 @@ struct mgmt_txn_ctx { */ struct mgmt_txn_reqs_head get_cfg_reqs; /* - * List of pending get-data requests for a given - * transaction/session Two lists, one for requests - * not processed at all, and one for requests that - * has been sent to backend for processing. + * List of pending get-tree requests. */ - struct mgmt_txn_reqs_head get_data_reqs; - struct mgmt_txn_reqs_head pending_get_datas; + struct mgmt_txn_reqs_head get_tree_reqs; /* * There will always be one commit-config allowed for a given * transaction/session. No need to maintain lists for it. @@ -254,15 +261,12 @@ static int mgmt_txn_send_commit_cfg_reply(struct mgmt_txn_ctx *txn, enum mgmt_result result, const char *error_if_any); -static inline const char *mgmt_txn_commit_phase_str(struct mgmt_txn_ctx *txn, - bool curr) +static inline const char *mgmt_txn_commit_phase_str(struct mgmt_txn_ctx *txn) { if (!txn->commit_cfg_req) return "None"; - return (mgmt_commit_phase2str( - curr ? txn->commit_cfg_req->req.commit_cfg.curr_phase - : txn->commit_cfg_req->req.commit_cfg.next_phase)); + return mgmt_commit_phase2str(txn->commit_cfg_req->req.commit_cfg.phase); } static void mgmt_txn_lock(struct mgmt_txn_ctx *txn, const char *file, int line); @@ -277,9 +281,7 @@ static struct mgmt_master *mgmt_txn_mm; static void mgmt_txn_register_event(struct mgmt_txn_ctx *txn, enum mgmt_txn_event event); -static int -mgmt_move_be_commit_to_next_phase(struct mgmt_txn_ctx *txn, - struct mgmt_be_client_adapter *adapter); +static void mgmt_txn_cleanup_txn(struct mgmt_txn_ctx **txn); static struct mgmt_txn_be_cfg_batch * mgmt_txn_cfg_batch_alloc(struct mgmt_txn_ctx *txn, enum mgmt_be_client_id id, @@ -296,7 +298,7 @@ mgmt_txn_cfg_batch_alloc(struct mgmt_txn_ctx *txn, enum mgmt_be_client_id id, MGMTD_TXN_LOCK(txn); assert(txn->commit_cfg_req); mgmt_txn_batches_add_tail(&txn->commit_cfg_req->req.commit_cfg - .curr_batches[id], + .batches[id], batch); batch->be_adapter = be_adapter; batch->buf_space_left = MGMTD_BE_CFGDATA_MAX_MSG_LEN; @@ -304,11 +306,6 @@ mgmt_txn_cfg_batch_alloc(struct mgmt_txn_ctx *txn, enum mgmt_be_client_id id, mgmt_be_adapter_lock(be_adapter); txn->commit_cfg_req->req.commit_cfg.last_be_cfg_batch[id] = batch; - if (!txn->commit_cfg_req->req.commit_cfg.next_batch_id) - txn->commit_cfg_req->req.commit_cfg.next_batch_id++; - batch->batch_id = txn->commit_cfg_req->req.commit_cfg.next_batch_id++; - hash_get(txn->commit_cfg_req->req.commit_cfg.batches, batch, - hash_alloc_intern); return batch; } @@ -318,15 +315,12 @@ static void mgmt_txn_cfg_batch_free(struct mgmt_txn_be_cfg_batch **batch) size_t indx; struct mgmt_commit_cfg_req *cmtcfg_req; - MGMTD_TXN_DBG(" freeing batch-id: %" PRIu64 " txn-id %" PRIu64, - (*batch)->batch_id, (*batch)->txn->txn_id); + __dbg(" freeing batch txn-id %" PRIu64, (*batch)->txn->txn_id); assert((*batch)->txn && (*batch)->txn->type == MGMTD_TXN_TYPE_CONFIG); cmtcfg_req = &(*batch)->txn->commit_cfg_req->req.commit_cfg; - hash_release(cmtcfg_req->batches, *batch); - mgmt_txn_batches_del(&cmtcfg_req->curr_batches[(*batch)->be_id], *batch); - mgmt_txn_batches_del(&cmtcfg_req->next_batches[(*batch)->be_id], *batch); + mgmt_txn_batches_del(&cmtcfg_req->batches[(*batch)->be_id], *batch); if ((*batch)->be_adapter) mgmt_be_adapter_unlock(&(*batch)->be_adapter); @@ -344,57 +338,13 @@ static void mgmt_txn_cfg_batch_free(struct mgmt_txn_be_cfg_batch **batch) *batch = NULL; } -static unsigned int mgmt_txn_cfgbatch_hash_key(const void *data) -{ - const struct mgmt_txn_be_cfg_batch *batch = data; - - return jhash2((uint32_t *)&batch->batch_id, - sizeof(batch->batch_id) / sizeof(uint32_t), 0); -} - -static bool mgmt_txn_cfgbatch_hash_cmp(const void *d1, const void *d2) -{ - const struct mgmt_txn_be_cfg_batch *batch1 = d1; - const struct mgmt_txn_be_cfg_batch *batch2 = d2; - - return (batch1->batch_id == batch2->batch_id); -} - -static void mgmt_txn_cfgbatch_hash_free(void *data) -{ - struct mgmt_txn_be_cfg_batch *batch = data; - - mgmt_txn_cfg_batch_free(&batch); -} - -static inline struct mgmt_txn_be_cfg_batch * -mgmt_txn_cfgbatch_id2ctx(struct mgmt_txn_ctx *txn, uint64_t batch_id) -{ - struct mgmt_txn_be_cfg_batch key = { 0 }; - struct mgmt_txn_be_cfg_batch *batch; - - if (!txn->commit_cfg_req) - return NULL; - - key.batch_id = batch_id; - batch = hash_lookup(txn->commit_cfg_req->req.commit_cfg.batches, &key); - - return batch; -} - static void mgmt_txn_cleanup_be_cfg_batches(struct mgmt_txn_ctx *txn, enum mgmt_be_client_id id) { struct mgmt_txn_be_cfg_batch *batch; struct mgmt_txn_batches_head *list; - list = &txn->commit_cfg_req->req.commit_cfg.curr_batches[id]; - FOREACH_TXN_CFG_BATCH_IN_LIST (list, batch) - mgmt_txn_cfg_batch_free(&batch); - - mgmt_txn_batches_fini(list); - - list = &txn->commit_cfg_req->req.commit_cfg.next_batches[id]; + list = &txn->commit_cfg_req->req.commit_cfg.batches[id]; FOREACH_TXN_CFG_BATCH_IN_LIST (list, batch) mgmt_txn_cfg_batch_free(&batch); @@ -415,7 +365,6 @@ static struct mgmt_txn_req *mgmt_txn_req_alloc(struct mgmt_txn_ctx *txn, txn_req->txn = txn; txn_req->req_id = req_id; txn_req->req_event = req_event; - txn_req->pending_be_proc = false; switch (txn_req->req_event) { case MGMTD_TXN_PROC_SETCFG: @@ -423,27 +372,24 @@ static struct mgmt_txn_req *mgmt_txn_req_alloc(struct mgmt_txn_ctx *txn, sizeof(struct mgmt_set_cfg_req)); assert(txn_req->req.set_cfg); mgmt_txn_reqs_add_tail(&txn->set_cfg_reqs, txn_req); - MGMTD_TXN_DBG("Added a new SETCFG req-id: %" PRIu64 - " txn-id: %" PRIu64 ", session-id: %" PRIu64, - txn_req->req_id, txn->txn_id, txn->session_id); + __dbg("Added a new SETCFG req-id: %" PRIu64 " txn-id: %" PRIu64 + ", session-id: %" PRIu64, + txn_req->req_id, txn->txn_id, txn->session_id); break; case MGMTD_TXN_PROC_COMMITCFG: txn->commit_cfg_req = txn_req; - MGMTD_TXN_DBG("Added a new COMMITCFG req-id: %" PRIu64 - " txn-id: %" PRIu64 " session-id: %" PRIu64, - txn_req->req_id, txn->txn_id, txn->session_id); + __dbg("Added a new COMMITCFG req-id: %" PRIu64 + " txn-id: %" PRIu64 " session-id: %" PRIu64, + txn_req->req_id, txn->txn_id, txn->session_id); FOREACH_MGMTD_BE_CLIENT_ID (id) { + txn_req->req.commit_cfg.be_phase[id] = + MGMTD_COMMIT_PHASE_PREPARE_CFG; mgmt_txn_batches_init( - &txn_req->req.commit_cfg.curr_batches[id]); - mgmt_txn_batches_init( - &txn_req->req.commit_cfg.next_batches[id]); + &txn_req->req.commit_cfg.batches[id]); } - txn_req->req.commit_cfg.batches = - hash_create(mgmt_txn_cfgbatch_hash_key, - mgmt_txn_cfgbatch_hash_cmp, - "MGMT Config Batches"); + txn_req->req.commit_cfg.phase = MGMTD_COMMIT_PHASE_PREPARE_CFG; break; case MGMTD_TXN_PROC_GETCFG: txn_req->req.get_data = @@ -451,22 +397,20 @@ static struct mgmt_txn_req *mgmt_txn_req_alloc(struct mgmt_txn_ctx *txn, sizeof(struct mgmt_get_data_req)); assert(txn_req->req.get_data); mgmt_txn_reqs_add_tail(&txn->get_cfg_reqs, txn_req); - MGMTD_TXN_DBG("Added a new GETCFG req-id: %" PRIu64 - " txn-id: %" PRIu64 " session-id: %" PRIu64, - txn_req->req_id, txn->txn_id, txn->session_id); + __dbg("Added a new GETCFG req-id: %" PRIu64 " txn-id: %" PRIu64 + " session-id: %" PRIu64, + txn_req->req_id, txn->txn_id, txn->session_id); break; - case MGMTD_TXN_PROC_GETDATA: - txn_req->req.get_data = - XCALLOC(MTYPE_MGMTD_TXN_GETDATA_REQ, - sizeof(struct mgmt_get_data_req)); - assert(txn_req->req.get_data); - mgmt_txn_reqs_add_tail(&txn->get_data_reqs, txn_req); - MGMTD_TXN_DBG("Added a new GETDATA req-id: %" PRIu64 - " txn-id: %" PRIu64 " session-id: %" PRIu64, - txn_req->req_id, txn->txn_id, txn->session_id); + case MGMTD_TXN_PROC_GETTREE: + txn_req->req.get_tree = XCALLOC(MTYPE_MGMTD_TXN_GETTREE_REQ, + sizeof(struct txn_req_get_tree)); + mgmt_txn_reqs_add_tail(&txn->get_tree_reqs, txn_req); + __dbg("Added a new GETTREE 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_CLEANUP: + case MGMTD_TXN_GETTREE_TIMEOUT: break; } @@ -479,49 +423,31 @@ static void mgmt_txn_req_free(struct mgmt_txn_req **txn_req) { int indx; struct mgmt_txn_reqs_head *req_list = NULL; - struct mgmt_txn_reqs_head *pending_list = NULL; enum mgmt_be_client_id id; struct mgmt_be_client_adapter *adapter; struct mgmt_commit_cfg_req *ccreq; + struct mgmt_set_cfg_req *set_cfg; bool cleanup; switch ((*txn_req)->req_event) { case MGMTD_TXN_PROC_SETCFG: - for (indx = 0; indx < (*txn_req)->req.set_cfg->num_cfg_changes; - indx++) { - if ((*txn_req)->req.set_cfg->cfg_changes[indx].value) { - MGMTD_TXN_DBG("Freeing value for %s at %p ==> '%s'", - (*txn_req) - ->req.set_cfg - ->cfg_changes[indx] - .xpath, - (*txn_req) - ->req.set_cfg - ->cfg_changes[indx] - .value, - (*txn_req) - ->req.set_cfg - ->cfg_changes[indx] - .value); - free((void *)(*txn_req) - ->req.set_cfg->cfg_changes[indx] - .value); - } + set_cfg = (*txn_req)->req.set_cfg; + for (indx = 0; indx < set_cfg->num_cfg_changes; indx++) { + if (set_cfg->cfg_changes[indx].value) + free((void *)set_cfg->cfg_changes[indx].value); } req_list = &(*txn_req)->txn->set_cfg_reqs; - MGMTD_TXN_DBG("Deleting SETCFG req-id: %" PRIu64 - " txn-id: %" PRIu64, - (*txn_req)->req_id, (*txn_req)->txn->txn_id); + __dbg("Deleting SETCFG req-id: %" PRIu64 " txn-id: %" PRIu64, + (*txn_req)->req_id, (*txn_req)->txn->txn_id); XFREE(MTYPE_MGMTD_TXN_SETCFG_REQ, (*txn_req)->req.set_cfg); break; case MGMTD_TXN_PROC_COMMITCFG: - MGMTD_TXN_DBG("Deleting COMMITCFG req-id: %" PRIu64 - " txn-id: %" PRIu64, - (*txn_req)->req_id, (*txn_req)->txn->txn_id); + __dbg("Deleting COMMITCFG req-id: %" PRIu64 " txn-id: %" PRIu64, + (*txn_req)->req_id, (*txn_req)->txn->txn_id); ccreq = &(*txn_req)->req.commit_cfg; - cleanup = (ccreq->curr_phase >= MGMTD_COMMIT_PHASE_TXN_CREATE && - ccreq->curr_phase < MGMTD_COMMIT_PHASE_TXN_DELETE); + cleanup = (ccreq->phase >= MGMTD_COMMIT_PHASE_TXN_CREATE && + ccreq->phase < MGMTD_COMMIT_PHASE_TXN_DELETE); FOREACH_MGMTD_BE_CLIENT_ID (id) { /* @@ -534,20 +460,13 @@ static void mgmt_txn_req_free(struct mgmt_txn_req **txn_req) * anything more with them */ mgmt_txn_cleanup_be_cfg_batches((*txn_req)->txn, id); - if (ccreq->batches) { - hash_clean(ccreq->batches, - mgmt_txn_cfgbatch_hash_free); - hash_free(ccreq->batches); - ccreq->batches = NULL; - } /* * If we were in the middle of the state machine then * send a txn delete message */ adapter = mgmt_be_get_adapter_by_id(id); - if (adapter && cleanup && - ccreq->subscr_info.xpath_subscr[id]) + if (adapter && cleanup && IS_IDBIT_SET(ccreq->clients, id)) mgmt_txn_send_be_txn_delete((*txn_req)->txn, adapter); } @@ -560,9 +479,8 @@ static void mgmt_txn_req_free(struct mgmt_txn_req **txn_req) ->req.get_data->xpaths[indx]); } req_list = &(*txn_req)->txn->get_cfg_reqs; - MGMTD_TXN_DBG("Deleting GETCFG req-id: %" PRIu64 - " txn-id: %" PRIu64, - (*txn_req)->req_id, (*txn_req)->txn->txn_id); + __dbg("Deleting GETCFG req-id: %" PRIu64 " txn-id: %" PRIu64, + (*txn_req)->req_id, (*txn_req)->txn->txn_id); if ((*txn_req)->req.get_data->reply) XFREE(MTYPE_MGMTD_TXN_GETDATA_REPLY, (*txn_req)->req.get_data->reply); @@ -572,42 +490,25 @@ static void mgmt_txn_req_free(struct mgmt_txn_req **txn_req) XFREE(MTYPE_MGMTD_TXN_GETDATA_REQ, (*txn_req)->req.get_data); break; - case MGMTD_TXN_PROC_GETDATA: - for (indx = 0; indx < (*txn_req)->req.get_data->num_xpaths; - indx++) { - if ((*txn_req)->req.get_data->xpaths[indx]) - free((void *)(*txn_req) - ->req.get_data->xpaths[indx]); - } - pending_list = &(*txn_req)->txn->pending_get_datas; - req_list = &(*txn_req)->txn->get_data_reqs; - MGMTD_TXN_DBG("Deleting GETDATA req-id: %" PRIu64 - " txn-id: %" PRIu64, - (*txn_req)->req_id, (*txn_req)->txn->txn_id); - if ((*txn_req)->req.get_data->reply) - XFREE(MTYPE_MGMTD_TXN_GETDATA_REPLY, - (*txn_req)->req.get_data->reply); - XFREE(MTYPE_MGMTD_TXN_GETDATA_REQ, (*txn_req)->req.get_data); + case MGMTD_TXN_PROC_GETTREE: + __dbg("Deleting GETTREE req-id: %" PRIu64 " of txn-id: %" PRIu64, + (*txn_req)->req_id, (*txn_req)->txn->txn_id); + req_list = &(*txn_req)->txn->get_tree_reqs; + lyd_free_all((*txn_req)->req.get_tree->client_results); + 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_COMMITCFG_TIMEOUT: - case MGMTD_TXN_CLEANUP: + case MGMTD_TXN_GETTREE_TIMEOUT: break; } - if ((*txn_req)->pending_be_proc && pending_list) { - mgmt_txn_reqs_del(pending_list, *txn_req); - MGMTD_TXN_DBG("Removed req-id: %" PRIu64 - " from pending-list (left:%zu)", - (*txn_req)->req_id, - mgmt_txn_reqs_count(pending_list)); - } else if (req_list) { + if (req_list) { mgmt_txn_reqs_del(req_list, *txn_req); - MGMTD_TXN_DBG("Removed req-id: %" PRIu64 - " from request-list (left:%zu)", - (*txn_req)->req_id, mgmt_txn_reqs_count(req_list)); + __dbg("Removed req-id: %" PRIu64 " from request-list (left:%zu)", + (*txn_req)->req_id, mgmt_txn_reqs_count(req_list)); } - (*txn_req)->pending_be_proc = false; MGMTD_TXN_UNLOCK(&(*txn_req)->txn); XFREE(MTYPE_MGMTD_TXN_REQ, (*txn_req)); *txn_req = NULL; @@ -630,10 +531,10 @@ static void mgmt_txn_process_set_cfg(struct event *thread) assert(txn); cmt_stats = mgmt_fe_get_session_commit_stats(txn->session_id); - MGMTD_TXN_DBG("Processing %zu SET_CONFIG requests txn-id:%" PRIu64 - " session-id: %" PRIu64, - mgmt_txn_reqs_count(&txn->set_cfg_reqs), txn->txn_id, - txn->session_id); + __dbg("Processing %zu SET_CONFIG requests txn-id:%" PRIu64 + " session-id: %" PRIu64, + mgmt_txn_reqs_count(&txn->set_cfg_reqs), txn->txn_id, + txn->session_id); FOREACH_TXN_REQ_IN_LIST (&txn->set_cfg_reqs, txn_req) { assert(txn_req->req_event == MGMTD_TXN_PROC_SETCFG); @@ -666,7 +567,7 @@ static void mgmt_txn_process_set_cfg(struct event *thread) txn_req->req.set_cfg->cfg_changes, (size_t)txn_req->req.set_cfg ->num_cfg_changes, - NULL, NULL, 0, err_buf, + NULL, false, err_buf, sizeof(err_buf), &error); if (error) { mgmt_fe_send_set_cfg_reply(txn->session_id, txn->txn_id, @@ -685,14 +586,17 @@ static void mgmt_txn_process_set_cfg(struct event *thread) /* We expect the user to have locked the DST DS */ if (!mgmt_ds_is_locked(txn_req->req.set_cfg->dst_ds_ctx, txn->session_id)) { - MGMTD_TXN_ERR("DS %u not locked for implicit commit txn-id: %" PRIu64 - " session-id: %" PRIu64 " err: %s", - txn_req->req.set_cfg->dst_ds_id, - txn->txn_id, txn->session_id, - strerror(ret)); - mgmt_txn_send_commit_cfg_reply( - txn, MGMTD_DS_LOCK_FAILED, - "running DS not locked for implicit commit"); + __log_err("DS %u not locked for implicit commit txn-id: %" PRIu64 + " session-id: %" PRIu64 " err: %s", + txn_req->req.set_cfg->dst_ds_id, + txn->txn_id, txn->session_id, + strerror(ret)); + mgmt_fe_send_set_cfg_reply( + txn->session_id, txn->txn_id, + txn_req->req.set_cfg->ds_id, + txn_req->req_id, MGMTD_DS_LOCK_FAILED, + "running DS not locked for implicit commit", + txn_req->req.set_cfg->implicit_commit); goto mgmt_txn_process_set_cfg_done; } @@ -717,9 +621,9 @@ static void mgmt_txn_process_set_cfg(struct event *thread) txn_req->req_id, MGMTD_SUCCESS, NULL, false) != 0) { - MGMTD_TXN_ERR("Failed to send SET_CONFIG_REPLY txn-id %" PRIu64 - " session-id: %" PRIu64, - txn->txn_id, txn->session_id); + __log_err("Failed to send SET_CONFIG_REPLY txn-id %" PRIu64 + " session-id: %" PRIu64, + txn->txn_id, txn->session_id); } mgmt_txn_process_set_cfg_done: @@ -736,9 +640,8 @@ mgmt_txn_process_set_cfg_done: left = mgmt_txn_reqs_count(&txn->set_cfg_reqs); if (left) { - MGMTD_TXN_DBG("Processed maximum number of Set-Config requests (%d/%d/%d). Rescheduling for rest.", - num_processed, MGMTD_TXN_MAX_NUM_SETCFG_PROC, - (int)left); + __dbg("Processed maximum number of Set-Config requests (%d/%d/%d). Rescheduling for rest.", + num_processed, MGMTD_TXN_MAX_NUM_SETCFG_PROC, (int)left); mgmt_txn_register_event(txn, MGMTD_TXN_PROC_SETCFG); } } @@ -769,9 +672,9 @@ static int mgmt_txn_send_commit_cfg_reply(struct mgmt_txn_ctx *txn, txn->commit_cfg_req->req.commit_cfg .validate_only, result, error_if_any) != 0) { - MGMTD_TXN_ERR("Failed to send COMMIT-CONFIG-REPLY txn-id: %" PRIu64 - " session-id: %" PRIu64, - txn->txn_id, txn->session_id); + __log_err("Failed to send COMMIT-CONFIG-REPLY txn-id: %" PRIu64 + " session-id: %" PRIu64, + txn->txn_id, txn->session_id); } if (txn->commit_cfg_req->req.commit_cfg.implicit && txn->session_id && @@ -783,9 +686,9 @@ static int mgmt_txn_send_commit_cfg_reply(struct mgmt_txn_ctx *txn, success ? MGMTD_SUCCESS : MGMTD_INTERNAL_ERROR, error_if_any, true) != 0) { - MGMTD_TXN_ERR("Failed to send SET-CONFIG-REPLY txn-id: %" PRIu64 - " session-id: %" PRIu64, - txn->txn_id, txn->session_id); + __log_err("Failed to send SET-CONFIG-REPLY txn-id: %" PRIu64 + " session-id: %" PRIu64, + txn->txn_id, txn->session_id); } if (success) { @@ -848,6 +751,14 @@ static int mgmt_txn_send_commit_cfg_reply(struct mgmt_txn_ctx *txn, mgmt_history_rollback_complete(success); } + if (txn->commit_cfg_req->req.commit_cfg.init) { + /* + * This is the backend init request. + * We need to unlock the running datastore. + */ + mgmt_ds_unlock(txn->commit_cfg_req->req.commit_cfg.dst_ds_ctx); + } + txn->commit_cfg_req->req.commit_cfg.cmt_stats = NULL; mgmt_txn_req_free(&txn->commit_cfg_req); @@ -857,66 +768,26 @@ static int mgmt_txn_send_commit_cfg_reply(struct mgmt_txn_ctx *txn, * we need to cleanup by itself. */ if (!txn->session_id) - mgmt_txn_register_event(txn, MGMTD_TXN_CLEANUP); + mgmt_txn_cleanup_txn(&txn); return 0; } -static void -mgmt_move_txn_cfg_batch_to_next(struct mgmt_commit_cfg_req *cmtcfg_req, - struct mgmt_txn_be_cfg_batch *batch, - struct mgmt_txn_batches_head *src_list, - struct mgmt_txn_batches_head *dst_list, - bool update_commit_phase, - enum mgmt_commit_phase to_phase) -{ - mgmt_txn_batches_del(src_list, batch); - - if (update_commit_phase) { - MGMTD_TXN_DBG("Move txn-id %" PRIu64 " batch-id: %" PRIu64 - " from '%s' --> '%s'", - batch->txn->txn_id, batch->batch_id, - mgmt_commit_phase2str(batch->comm_phase), - mgmt_txn_commit_phase_str(batch->txn, false)); - batch->comm_phase = to_phase; - } - - mgmt_txn_batches_add_tail(dst_list, batch); -} - -static void mgmt_move_txn_cfg_batches(struct mgmt_txn_ctx *txn, - struct mgmt_commit_cfg_req *cmtcfg_req, - struct mgmt_txn_batches_head *src_list, - struct mgmt_txn_batches_head *dst_list, - bool update_commit_phase, - enum mgmt_commit_phase to_phase) -{ - struct mgmt_txn_be_cfg_batch *batch; - - FOREACH_TXN_CFG_BATCH_IN_LIST (src_list, batch) { - mgmt_move_txn_cfg_batch_to_next(cmtcfg_req, batch, src_list, - dst_list, update_commit_phase, - to_phase); - } -} - static int mgmt_try_move_commit_to_next_phase(struct mgmt_txn_ctx *txn, struct mgmt_commit_cfg_req *cmtcfg_req) { - struct mgmt_txn_batches_head *curr_list, *next_list; enum mgmt_be_client_id id; - MGMTD_TXN_DBG("txn-id: %" PRIu64 ", Phase(current:'%s' next:'%s')", - txn->txn_id, mgmt_txn_commit_phase_str(txn, true), - mgmt_txn_commit_phase_str(txn, false)); + __dbg("txn-id: %" PRIu64 ", Phase '%s'", txn->txn_id, + mgmt_txn_commit_phase_str(txn)); /* * Check if all clients has moved to next phase or not. */ FOREACH_MGMTD_BE_CLIENT_ID (id) { - if (cmtcfg_req->subscr_info.xpath_subscr[id] && - mgmt_txn_batches_count(&cmtcfg_req->curr_batches[id])) { + if (IS_IDBIT_SET(cmtcfg_req->clients, id) && + cmtcfg_req->be_phase[id] == cmtcfg_req->phase) { /* * There's atleast once client who hasn't moved to * next phase. @@ -929,83 +800,36 @@ mgmt_try_move_commit_to_next_phase(struct mgmt_txn_ctx *txn, } } - MGMTD_TXN_DBG("Move entire txn-id: %" PRIu64 " from '%s' to '%s'", - txn->txn_id, mgmt_txn_commit_phase_str(txn, true), - mgmt_txn_commit_phase_str(txn, false)); - /* * If we are here, it means all the clients has moved to next phase. * So we can move the whole commit to next phase. */ - cmtcfg_req->curr_phase = cmtcfg_req->next_phase; - cmtcfg_req->next_phase++; - MGMTD_TXN_DBG("Move back all config batches for txn-id: %" PRIu64 - " from next to current branch", - txn->txn_id); - FOREACH_MGMTD_BE_CLIENT_ID (id) { - curr_list = &cmtcfg_req->curr_batches[id]; - next_list = &cmtcfg_req->next_batches[id]; - mgmt_move_txn_cfg_batches(txn, cmtcfg_req, next_list, curr_list, - false, 0); - } - - mgmt_txn_register_event(txn, MGMTD_TXN_PROC_COMMITCFG); - - return 0; -} - -static int -mgmt_move_be_commit_to_next_phase(struct mgmt_txn_ctx *txn, - struct mgmt_be_client_adapter *adapter) -{ - struct mgmt_commit_cfg_req *cmtcfg_req; - struct mgmt_txn_batches_head *curr_list, *next_list; - - if (txn->type != MGMTD_TXN_TYPE_CONFIG || !txn->commit_cfg_req) - return -1; - - cmtcfg_req = &txn->commit_cfg_req->req.commit_cfg; - - MGMTD_TXN_DBG("Move txn-id: %" PRIu64 - " for '%s' Phase(current: '%s' next:'%s')", - txn->txn_id, adapter->name, - mgmt_txn_commit_phase_str(txn, true), - mgmt_txn_commit_phase_str(txn, false)); - - MGMTD_TXN_DBG("Move all config batches for '%s' from current to next list", - adapter->name); - curr_list = &cmtcfg_req->curr_batches[adapter->id]; - next_list = &cmtcfg_req->next_batches[adapter->id]; - mgmt_move_txn_cfg_batches(txn, cmtcfg_req, curr_list, next_list, true, - cmtcfg_req->next_phase); + cmtcfg_req->phase++; - MGMTD_TXN_DBG("txn-id: %" PRIu64 ", Phase(current:'%s' next:'%s')", - txn->txn_id, mgmt_txn_commit_phase_str(txn, true), - mgmt_txn_commit_phase_str(txn, false)); + __dbg("Move entire txn-id: %" PRIu64 " to phase '%s'", txn->txn_id, + mgmt_txn_commit_phase_str(txn)); - /* - * Check if all clients has moved to next phase or not. - */ - mgmt_try_move_commit_to_next_phase(txn, cmtcfg_req); + mgmt_txn_register_event(txn, MGMTD_TXN_PROC_COMMITCFG); return 0; } +/* + * This is the real workhorse + */ static int mgmt_txn_create_config_batches(struct mgmt_txn_req *txn_req, struct nb_config_cbs *changes) { struct nb_config_cb *cb, *nxt; struct nb_config_change *chg; struct mgmt_txn_be_cfg_batch *batch; - struct mgmt_be_client_subscr_info subscr_info; char *xpath = NULL, *value = NULL; - char err_buf[1024]; enum mgmt_be_client_id id; struct mgmt_be_client_adapter *adapter; struct mgmt_commit_cfg_req *cmtcfg_req; - bool found_validator; int num_chgs = 0; int xpath_len, value_len; + uint64_t clients, chg_clients; cmtcfg_req = &txn_req->req.commit_cfg; @@ -1029,24 +853,21 @@ static int mgmt_txn_create_config_batches(struct mgmt_txn_req *txn_req, if (!value) value = (char *)MGMTD_BE_CONTAINER_NODE_VAL; - MGMTD_TXN_DBG("XPATH: %s, Value: '%s'", xpath, - value ? value : "NIL"); + __dbg("XPATH: %s, Value: '%s'", xpath, value ? value : "NIL"); + + clients = mgmt_be_interested_clients(xpath, true); - mgmt_be_get_subscr_info_for_xpath(xpath, &subscr_info); + chg_clients = 0; xpath_len = strlen(xpath) + 1; value_len = strlen(value) + 1; - found_validator = false; - FOREACH_MGMTD_BE_CLIENT_ID (id) { - if (!(subscr_info.xpath_subscr[id] & - (MGMT_SUBSCR_VALIDATE_CFG | - MGMT_SUBSCR_NOTIFY_CFG))) - continue; - + FOREACH_BE_CLIENT_BITS (id, clients) { adapter = mgmt_be_get_adapter_by_id(id); if (!adapter) continue; + chg_clients |= (1ull << id); + batch = cmtcfg_req->last_be_cfg_batch[id]; if (!batch || (batch->num_cfg_data == @@ -1058,18 +879,20 @@ static int mgmt_txn_create_config_batches(struct mgmt_txn_req *txn_req, } batch->buf_space_left -= (xpath_len + value_len); - memcpy(&batch->xp_subscr[batch->num_cfg_data], - &subscr_info.xpath_subscr[id], - sizeof(batch->xp_subscr[0])); mgmt_yang_cfg_data_req_init( &batch->cfg_data[batch->num_cfg_data]); batch->cfg_datap[batch->num_cfg_data] = &batch->cfg_data[batch->num_cfg_data]; - if (chg->cb.operation == NB_OP_DESTROY) + /* + * On the backend, we don't really care if it's CREATE + * or MODIFY, because the existence was already checked + * on the frontend. Therefore we use SET for both. + */ + if (chg->cb.operation == NB_CB_DESTROY) batch->cfg_data[batch->num_cfg_data].req_type = - MGMTD__CFG_DATA_REQ_TYPE__DELETE_DATA; + MGMTD__CFG_DATA_REQ_TYPE__REMOVE_DATA; else batch->cfg_data[batch->num_cfg_data].req_type = MGMTD__CFG_DATA_REQ_TYPE__SET_DATA; @@ -1087,28 +910,19 @@ static int mgmt_txn_create_config_batches(struct mgmt_txn_req *txn_req, MGMTD__YANG_DATA_VALUE__VALUE_ENCODED_STR_VAL; batch->value[batch->num_cfg_data].encoded_str_val = value; - value = NULL; - if (subscr_info.xpath_subscr[id] & - MGMT_SUBSCR_VALIDATE_CFG) - found_validator = true; - - cmtcfg_req->subscr_info.xpath_subscr[id] |= - subscr_info.xpath_subscr[id]; - MGMTD_TXN_DBG(" -- %s, batch-id: %" PRIu64 " item:%d", - adapter->name, batch->batch_id, - (int)batch->num_cfg_data); + __dbg(" -- %s, batch item:%d", adapter->name, + (int)batch->num_cfg_data); batch->num_cfg_data++; num_chgs++; } - if (!found_validator) { - snprintf(err_buf, sizeof(err_buf), - "No validator module found for XPATH: '%s", - xpath); - MGMTD_TXN_ERR("***** %s", err_buf); - } + if (!chg_clients) + __log_err("No connected daemon is interested in XPATH %s", + xpath); + + cmtcfg_req->clients |= chg_clients; free(xpath); } @@ -1121,7 +935,13 @@ static int mgmt_txn_create_config_batches(struct mgmt_txn_req *txn_req, return -1; } - cmtcfg_req->next_phase = MGMTD_COMMIT_PHASE_TXN_CREATE; + /* Move all BE clients to create phase */ + FOREACH_MGMTD_BE_CLIENT_ID(id) { + if (IS_IDBIT_SET(cmtcfg_req->clients, id)) + cmtcfg_req->be_phase[id] = + MGMTD_COMMIT_PHASE_TXN_CREATE; + } + return 0; } @@ -1194,25 +1014,28 @@ static int mgmt_txn_prepare_config(struct mgmt_txn_ctx *txn) } /* - * Check for diffs from scratch buffer. If found empty - * get the diff from Candidate DS itself. + * Validate YANG contents of the source DS and get the diff + * between source and destination DS contents. */ - cfg_chgs = &nb_config->cfg_chgs; - if (RB_EMPTY(nb_config_cbs, cfg_chgs)) { - /* - * This could be the case when the config is directly - * loaded onto the candidate DS from a file. Get the - * diff from a full comparison of the candidate and - * running DSs. - */ - nb_config_diff(mgmt_ds_get_nb_config( - txn->commit_cfg_req->req.commit_cfg - .dst_ds_ctx), - nb_config, &changes); - cfg_chgs = &changes; - del_cfg_chgs = true; + char err_buf[BUFSIZ] = { 0 }; + + ret = nb_candidate_validate_yang(nb_config, true, err_buf, + sizeof(err_buf) - 1); + if (ret != NB_OK) { + if (strncmp(err_buf, " ", strlen(err_buf)) == 0) + strlcpy(err_buf, "Validation failed", sizeof(err_buf)); + (void)mgmt_txn_send_commit_cfg_reply(txn, MGMTD_INVALID_PARAM, + err_buf); + ret = -1; + goto mgmt_txn_prepare_config_done; } + nb_config_diff(mgmt_ds_get_nb_config(txn->commit_cfg_req->req.commit_cfg + .dst_ds_ctx), + nb_config, &changes); + cfg_chgs = &changes; + del_cfg_chgs = true; + if (RB_EMPTY(nb_config_cbs, cfg_chgs)) { /* * This means there's no changes to commit whatsoever @@ -1230,28 +1053,12 @@ static int mgmt_txn_prepare_config(struct mgmt_txn_ctx *txn) ->validate_start, NULL); /* - * Validate YANG contents of the source DS and get the diff - * between source and destination DS contents. - */ - char err_buf[1024] = { 0 }; - nb_ctx.client = NB_CLIENT_MGMTD_SERVER; - nb_ctx.user = (void *)txn; - - ret = nb_candidate_validate_yang(nb_config, true, err_buf, - sizeof(err_buf) - 1); - if (ret != NB_OK) { - if (strncmp(err_buf, " ", strlen(err_buf)) == 0) - strlcpy(err_buf, "Validation failed", sizeof(err_buf)); - (void)mgmt_txn_send_commit_cfg_reply(txn, MGMTD_INVALID_PARAM, - err_buf); - ret = -1; - goto mgmt_txn_prepare_config_done; - } - /* * Perform application level validations locally on the MGMTD * process by calling application specific validation routines * loaded onto MGMTD process using libraries. */ + nb_ctx.client = NB_CLIENT_MGMTD_SERVER; + nb_ctx.user = (void *)txn; ret = nb_candidate_validate_code(&nb_ctx, nb_config, &changes, err_buf, sizeof(err_buf) - 1); if (ret != NB_OK) { @@ -1290,7 +1097,7 @@ mgmt_txn_prep_config_validation_done: } /* Move to the Transaction Create Phase */ - txn->commit_cfg_req->req.commit_cfg.curr_phase = + txn->commit_cfg_req->req.commit_cfg.phase = MGMTD_COMMIT_PHASE_TXN_CREATE; mgmt_txn_register_event(txn, MGMTD_TXN_PROC_COMMITCFG); @@ -1312,13 +1119,12 @@ static int mgmt_txn_send_be_txn_create(struct mgmt_txn_ctx *txn) enum mgmt_be_client_id id; struct mgmt_be_client_adapter *adapter; struct mgmt_commit_cfg_req *cmtcfg_req; - struct mgmt_txn_be_cfg_batch *batch; assert(txn->type == MGMTD_TXN_TYPE_CONFIG && txn->commit_cfg_req); cmtcfg_req = &txn->commit_cfg_req->req.commit_cfg; FOREACH_MGMTD_BE_CLIENT_ID (id) { - if (cmtcfg_req->subscr_info.xpath_subscr[id]) { + if (IS_IDBIT_SET(cmtcfg_req->clients, id)) { adapter = mgmt_be_get_adapter_by_id(id); if (mgmt_be_send_txn_req(adapter, txn->txn_id, true)) { (void)mgmt_txn_send_commit_cfg_reply( @@ -1326,29 +1132,16 @@ static int mgmt_txn_send_be_txn_create(struct mgmt_txn_ctx *txn) "Could not send TXN_CREATE to backend adapter"); return -1; } - - FOREACH_TXN_CFG_BATCH_IN_LIST (&txn->commit_cfg_req->req - .commit_cfg - .curr_batches[id], - batch) - batch->comm_phase = - MGMTD_COMMIT_PHASE_TXN_CREATE; } } - txn->commit_cfg_req->req.commit_cfg.next_phase = - MGMTD_COMMIT_PHASE_SEND_CFG; - /* * Dont move the commit to next phase yet. Wait for the TXN_REPLY to * come back. */ - MGMTD_TXN_DBG("txn-id: %" PRIu64 " session-id: %" PRIu64 - " Phase(Current:'%s', Next: '%s')", - txn->txn_id, txn->session_id, - mgmt_txn_commit_phase_str(txn, true), - mgmt_txn_commit_phase_str(txn, false)); + __dbg("txn-id: %" PRIu64 " session-id: %" PRIu64 " Phase '%s'", + txn->txn_id, txn->session_id, mgmt_txn_commit_phase_str(txn)); return 0; } @@ -1364,41 +1157,34 @@ static int mgmt_txn_send_be_cfg_data(struct mgmt_txn_ctx *txn, assert(txn->type == MGMTD_TXN_TYPE_CONFIG && txn->commit_cfg_req); cmtcfg_req = &txn->commit_cfg_req->req.commit_cfg; - assert(cmtcfg_req->subscr_info.xpath_subscr[adapter->id]); + assert(IS_IDBIT_SET(cmtcfg_req->clients, adapter->id)); indx = 0; - num_batches = - mgmt_txn_batches_count(&cmtcfg_req->curr_batches[adapter->id]); - FOREACH_TXN_CFG_BATCH_IN_LIST (&cmtcfg_req->curr_batches[adapter->id], + num_batches = mgmt_txn_batches_count(&cmtcfg_req->batches[adapter->id]); + FOREACH_TXN_CFG_BATCH_IN_LIST (&cmtcfg_req->batches[adapter->id], batch) { - assert(cmtcfg_req->next_phase == MGMTD_COMMIT_PHASE_SEND_CFG); cfg_req.cfgdata_reqs = batch->cfg_datap; cfg_req.num_reqs = batch->num_cfg_data; indx++; if (mgmt_be_send_cfgdata_req(adapter, txn->txn_id, - batch->batch_id, cfg_req.cfgdata_reqs, cfg_req.num_reqs, indx == num_batches)) { (void)mgmt_txn_send_commit_cfg_reply( txn, MGMTD_INTERNAL_ERROR, "Internal Error! Could not send config data to backend!"); - MGMTD_TXN_ERR("Could not send CFGDATA_CREATE txn-id: %" PRIu64 - " batch-id: %" PRIu64 " to client '%s", - txn->txn_id, batch->batch_id, - adapter->name); + __log_err("Could not send CFGDATA_CREATE txn-id: %" PRIu64 + " to client '%s", + txn->txn_id, adapter->name); return -1; } cmtcfg_req->cmt_stats->last_num_cfgdata_reqs++; - mgmt_move_txn_cfg_batch_to_next( - cmtcfg_req, batch, - &cmtcfg_req->curr_batches[adapter->id], - &cmtcfg_req->next_batches[adapter->id], true, - MGMTD_COMMIT_PHASE_SEND_CFG); } + 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. @@ -1415,9 +1201,8 @@ static int mgmt_txn_send_be_txn_delete(struct mgmt_txn_ctx *txn, &txn->commit_cfg_req->req.commit_cfg; assert(txn->type == MGMTD_TXN_TYPE_CONFIG); - assert(!mgmt_txn_batches_count(&cmtcfg_req->curr_batches[adapter->id])); - if (!cmtcfg_req->subscr_info.xpath_subscr[adapter->id]) + if (IS_IDBIT_UNSET(cmtcfg_req->clients, adapter->id)) return 0; return mgmt_be_send_txn_req(adapter, txn->txn_id, false); @@ -1435,8 +1220,8 @@ static void mgmt_txn_cfg_commit_timedout(struct event *thread) if (!txn->commit_cfg_req) return; - MGMTD_TXN_ERR("Backend timeout txn-id: %" PRIu64 " aborting commit", - txn->txn_id); + __log_err("Backend timeout txn-id: %" PRIu64 " aborting commit", + txn->txn_id); /* * Send a COMMIT_CONFIG_REPLY with failure. @@ -1448,6 +1233,84 @@ static void mgmt_txn_cfg_commit_timedout(struct event *thread) "Operation on the backend timed-out. Aborting commit!"); } + +static int txn_get_tree_data_done(struct mgmt_txn_ctx *txn, + struct mgmt_txn_req *txn_req) +{ + struct txn_req_get_tree *get_tree = txn_req->req.get_tree; + uint64_t req_id = txn_req->req_id; + struct lyd_node *result; + int ret = NB_OK; + + /* cancel timer and send reply onward */ + EVENT_OFF(txn->get_tree_timeout); + + if (!get_tree->simple_xpath && get_tree->client_results) { + /* + * We have a complex query so Filter results by the xpath query. + */ + if (yang_lyd_trim_xpath(&get_tree->client_results, + txn_req->req.get_tree->xpath)) + ret = NB_ERR; + } + + result = get_tree->client_results; + + if (ret == NB_OK && result && get_tree->exact) + result = yang_dnode_get(result, get_tree->xpath); + + if (ret == NB_OK) + ret = mgmt_fe_adapter_send_tree_data(txn->session_id, + txn->txn_id, + txn_req->req_id, + get_tree->result_type, + get_tree->wd_options, + result, + get_tree->partial_error, + false); + + /* we're done with the request */ + mgmt_txn_req_free(&txn_req); + + if (ret) { + __log_err("Error sending the results of GETTREE for txn-id %" PRIu64 + " req_id %" PRIu64 " to requested type %u", + txn->txn_id, req_id, get_tree->result_type); + + (void)mgmt_fe_adapter_txn_error(txn->txn_id, req_id, false, ret, + "Error converting results of GETTREE"); + } + + return ret; +} + + +static void txn_get_tree_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_SHOW); + + + __log_err("Backend timeout txn-id: %" PRIu64 " ending get-tree", + txn->txn_id); + + /* + * Send a get-tree data reply. + * + * NOTE: The transaction cleanup will be triggered from Front-end + * adapter. + */ + + txn_req->req.get_tree->partial_error = -ETIMEDOUT; + txn_get_tree_data_done(txn, txn_req); +} + /* * Send CFG_APPLY_REQs to all the backend client. * @@ -1461,8 +1324,6 @@ static int mgmt_txn_send_be_cfg_apply(struct mgmt_txn_ctx *txn) enum mgmt_be_client_id id; struct mgmt_be_client_adapter *adapter; struct mgmt_commit_cfg_req *cmtcfg_req; - struct mgmt_txn_batches_head *batch_list; - struct mgmt_txn_be_cfg_batch *batch; assert(txn->type == MGMTD_TXN_TYPE_CONFIG && txn->commit_cfg_req); @@ -1476,13 +1337,11 @@ static int mgmt_txn_send_be_cfg_apply(struct mgmt_txn_ctx *txn) } FOREACH_MGMTD_BE_CLIENT_ID (id) { - if (cmtcfg_req->subscr_info.xpath_subscr[id] & - MGMT_SUBSCR_NOTIFY_CFG) { + if (IS_IDBIT_SET(cmtcfg_req->clients, id)) { adapter = mgmt_be_get_adapter_by_id(id); if (!adapter) return -1; - batch_list = &cmtcfg_req->curr_batches[id]; if (mgmt_be_send_cfgapply_req(adapter, txn->txn_id)) { (void)mgmt_txn_send_commit_cfg_reply( txn, MGMTD_INTERNAL_ERROR, @@ -1493,15 +1352,9 @@ static int mgmt_txn_send_be_cfg_apply(struct mgmt_txn_ctx *txn) UNSET_FLAG(adapter->flags, MGMTD_BE_ADAPTER_FLAGS_CFG_SYNCED); - - FOREACH_TXN_CFG_BATCH_IN_LIST (batch_list, batch) - batch->comm_phase = MGMTD_COMMIT_PHASE_APPLY_CFG; } } - txn->commit_cfg_req->req.commit_cfg.next_phase = - MGMTD_COMMIT_PHASE_TXN_DELETE; - /* * Dont move the commit to next phase yet. Wait for all VALIDATE_REPLIES * to come back. @@ -1518,15 +1371,13 @@ static void mgmt_txn_process_commit_cfg(struct event *thread) txn = (struct mgmt_txn_ctx *)EVENT_ARG(thread); assert(txn); - MGMTD_TXN_DBG("Processing COMMIT_CONFIG for txn-id: %" PRIu64 - " session-id: %" PRIu64 " Phase(Current:'%s', Next: '%s')", - txn->txn_id, txn->session_id, - mgmt_txn_commit_phase_str(txn, true), - mgmt_txn_commit_phase_str(txn, false)); + __dbg("Processing COMMIT_CONFIG for txn-id: %" PRIu64 + " session-id: %" PRIu64 " Phase '%s'", + txn->txn_id, txn->session_id, mgmt_txn_commit_phase_str(txn)); assert(txn->commit_cfg_req); cmtcfg_req = &txn->commit_cfg_req->req.commit_cfg; - switch (cmtcfg_req->curr_phase) { + switch (cmtcfg_req->phase) { case MGMTD_COMMIT_PHASE_PREPARE_CFG: mgmt_txn_prepare_config(txn); break; @@ -1548,15 +1399,13 @@ static void mgmt_txn_process_commit_cfg(struct event *thread) * Backend by now. */ #ifndef MGMTD_LOCAL_VALIDATIONS_ENABLED - assert(cmtcfg_req->next_phase == MGMTD_COMMIT_PHASE_APPLY_CFG); - MGMTD_TXN_DBG("txn-id: %" PRIu64 " session-id: %" PRIu64 - " trigger sending CFG_VALIDATE_REQ to all backend clients", - txn->txn_id, txn->session_id); + __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 */ - assert(cmtcfg_req->next_phase == MGMTD_COMMIT_PHASE_APPLY_CFG); - MGMTD_TXN_DBG("txn-id: %" PRIu64 " session-id: %" PRIu64 - " trigger sending CFG_APPLY_REQ to all backend clients", - txn->txn_id, txn->session_id); + __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: @@ -1587,12 +1436,6 @@ static void mgmt_txn_process_commit_cfg(struct event *thread) case MGMTD_COMMIT_PHASE_MAX: break; } - - MGMTD_TXN_DBG("txn-id:%" PRIu64 " session-id: %" PRIu64 - " phase updated to (current:'%s', next: '%s')", - txn->txn_id, txn->session_id, - mgmt_txn_commit_phase_str(txn, true), - mgmt_txn_commit_phase_str(txn, false)); } static void mgmt_init_get_data_reply(struct mgmt_get_data_reply *get_reply) @@ -1613,8 +1456,6 @@ static void mgmt_reset_get_data_reply(struct mgmt_get_data_reply *get_reply) get_reply->reply_xpathp[indx] = 0; } if (get_reply->reply_data[indx].xpath) { - zlog_debug("%s free xpath %p", __func__, - get_reply->reply_data[indx].xpath); free(get_reply->reply_data[indx].xpath); get_reply->reply_data[indx].xpath = 0; } @@ -1653,8 +1494,8 @@ static void mgmt_txn_send_getcfg_reply_data(struct mgmt_txn_req *txn_req, data_reply->next_indx = (!get_reply->last_batch ? get_req->total_reply : -1); - MGMTD_TXN_DBG("Sending %zu Get-Config/Data replies next-index:%" PRId64, - data_reply->n_data, data_reply->next_indx); + __dbg("Sending %zu Get-Config/Data replies next-index:%" PRId64, + data_reply->n_data, data_reply->next_indx); switch (txn_req->req_event) { case MGMTD_TXN_PROC_GETCFG: @@ -1662,30 +1503,18 @@ static void mgmt_txn_send_getcfg_reply_data(struct mgmt_txn_req *txn_req, txn_req->txn->txn_id, get_req->ds_id, txn_req->req_id, MGMTD_SUCCESS, data_reply, NULL) != 0) { - MGMTD_TXN_ERR("Failed to send GET-CONFIG-REPLY txn-id: %" PRIu64 - " session-id: %" PRIu64 - " req-id: %" PRIu64, - txn_req->txn->txn_id, - txn_req->txn->session_id, txn_req->req_id); - } - break; - case MGMTD_TXN_PROC_GETDATA: - if (mgmt_fe_send_get_reply(txn_req->txn->session_id, - txn_req->txn->txn_id, get_req->ds_id, - txn_req->req_id, MGMTD_SUCCESS, - data_reply, NULL) != 0) { - MGMTD_TXN_ERR("Failed to send GET-DATA-REPLY txn-id: %" PRIu64 - " session-id: %" PRIu64 - " req-id: %" PRIu64, - txn_req->txn->txn_id, - txn_req->txn->session_id, txn_req->req_id); + __log_err("Failed to send GET-CONFIG-REPLY txn-id: %" PRIu64 + " session-id: %" PRIu64 " req-id: %" PRIu64, + txn_req->txn->txn_id, + txn_req->txn->session_id, txn_req->req_id); } break; case MGMTD_TXN_PROC_SETCFG: case MGMTD_TXN_PROC_COMMITCFG: + case MGMTD_TXN_PROC_GETTREE: + case MGMTD_TXN_GETTREE_TIMEOUT: case MGMTD_TXN_COMMITCFG_TIMEOUT: - case MGMTD_TXN_CLEANUP: - MGMTD_TXN_ERR("Invalid Txn-Req-Event %u", txn_req->req_event); + __log_err("Invalid Txn-Req-Event %u", txn_req->req_event); break; } @@ -1695,10 +1524,8 @@ static void mgmt_txn_send_getcfg_reply_data(struct mgmt_txn_req *txn_req, mgmt_reset_get_data_reply_buf(get_req); } -static void mgmt_txn_iter_and_send_get_cfg_reply(const char *xpath, - struct lyd_node *node, - struct nb_node *nb_node, - void *ctx) +static void txn_iter_get_config_data_cb(const char *xpath, struct lyd_node *node, + struct nb_node *nb_node, void *ctx) { struct mgmt_txn_req *txn_req; struct mgmt_get_data_req *get_req; @@ -1713,8 +1540,7 @@ static void mgmt_txn_iter_and_send_get_cfg_reply(const char *xpath, if (!(node->schema->nodetype & LYD_NODE_TERM)) return; - assert(txn_req->req_event == MGMTD_TXN_PROC_GETCFG || - txn_req->req_event == MGMTD_TXN_PROC_GETDATA); + assert(txn_req->req_event == MGMTD_TXN_PROC_GETCFG); get_req = txn_req->req.get_data; assert(get_req); @@ -1731,8 +1557,8 @@ static void mgmt_txn_iter_and_send_get_cfg_reply(const char *xpath, get_reply->num_reply++; get_req->total_reply++; - MGMTD_TXN_DBG(" [%d] XPATH: '%s', Value: '%s'", get_req->total_reply, - data->xpath, data_value->encoded_str_val); + __dbg(" [%d] XPATH: '%s', Value: '%s'", get_req->total_reply, + data->xpath, data_value->encoded_str_val); if (get_reply->num_reply == MGMTD_MAX_NUM_DATA_REPLY_IN_BATCH) mgmt_txn_send_getcfg_reply_data(txn_req, get_req); @@ -1766,8 +1592,8 @@ static int mgmt_txn_get_config(struct mgmt_txn_ctx *txn, */ get_reply = get_data->reply; for (indx = 0; indx < get_data->num_xpaths; indx++) { - MGMTD_TXN_DBG("Trying to get all data under '%s'", - get_data->xpaths[indx]); + __dbg("Trying to get all data under '%s'", + get_data->xpaths[indx]); mgmt_init_get_data_reply(get_reply); /* * mgmt_ds_iter_data works on path prefixes, but the user may @@ -1776,18 +1602,17 @@ static int mgmt_txn_get_config(struct mgmt_txn_ctx *txn, */ if (mgmt_ds_iter_data(get_data->ds_id, root, get_data->xpaths[indx], - mgmt_txn_iter_and_send_get_cfg_reply, + txn_iter_get_config_data_cb, (void *)txn_req) == -1) { - MGMTD_TXN_DBG("Invalid Xpath '%s", - get_data->xpaths[indx]); + __dbg("Invalid Xpath '%s", get_data->xpaths[indx]); mgmt_fe_send_get_reply(txn->session_id, txn->txn_id, get_data->ds_id, txn_req->req_id, MGMTD_INTERNAL_ERROR, NULL, "Invalid xpath"); goto mgmt_txn_get_config_failed; } - MGMTD_TXN_DBG("Got %d remaining data-replies for xpath '%s'", - get_reply->num_reply, get_data->xpaths[indx]); + __dbg("Got %d remaining data-replies for xpath '%s'", + get_reply->num_reply, get_data->xpaths[indx]); get_reply->last_batch = true; mgmt_txn_send_getcfg_reply_data(txn_req, get_data); } @@ -1814,10 +1639,10 @@ static void mgmt_txn_process_get_cfg(struct event *thread) txn = (struct mgmt_txn_ctx *)EVENT_ARG(thread); assert(txn); - MGMTD_TXN_DBG("Processing %zu GET_CONFIG requests txn-id: %" PRIu64 - " session-id: %" PRIu64, - mgmt_txn_reqs_count(&txn->get_cfg_reqs), txn->txn_id, - txn->session_id); + __dbg("Processing %zu GET_CONFIG requests txn-id: %" PRIu64 + " session-id: %" PRIu64, + mgmt_txn_reqs_count(&txn->get_cfg_reqs), txn->txn_id, + txn->session_id); FOREACH_TXN_REQ_IN_LIST (&txn->get_cfg_reqs, txn_req) { error = false; @@ -1826,11 +1651,10 @@ static void mgmt_txn_process_get_cfg(struct event *thread) assert(cfg_root); if (mgmt_txn_get_config(txn, txn_req, cfg_root) != 0) { - MGMTD_TXN_ERR("Unable to retrieve config from DS %d txn-id: %" PRIu64 - " session-id: %" PRIu64 - " req-id: %" PRIu64, - txn_req->req.get_data->ds_id, txn->txn_id, - txn->session_id, txn_req->req_id); + __log_err("Unable to retrieve config from DS %d txn-id: %" PRIu64 + " session-id: %" PRIu64 " req-id: %" PRIu64, + txn_req->req.get_data->ds_id, txn->txn_id, + txn->session_id, txn_req->req_id); error = true; } @@ -1853,60 +1677,12 @@ static void mgmt_txn_process_get_cfg(struct event *thread) } if (mgmt_txn_reqs_count(&txn->get_cfg_reqs)) { - MGMTD_TXN_DBG("Processed maximum number of Get-Config requests (%d/%d). Rescheduling for rest.", - num_processed, MGMTD_TXN_MAX_NUM_GETCFG_PROC); + __dbg("Processed maximum number of Get-Config requests (%d/%d). Rescheduling for rest.", + num_processed, MGMTD_TXN_MAX_NUM_GETCFG_PROC); mgmt_txn_register_event(txn, MGMTD_TXN_PROC_GETCFG); } } -static void mgmt_txn_process_get_data(struct event *thread) -{ - struct mgmt_txn_ctx *txn; - struct mgmt_txn_req *txn_req; - int num_processed = 0; - - txn = (struct mgmt_txn_ctx *)EVENT_ARG(thread); - assert(txn); - - MGMTD_TXN_DBG("Processing %zu GET_DATA requests txn-id: %" PRIu64 - " session-id: %" PRIu64, - mgmt_txn_reqs_count(&txn->get_data_reqs), txn->txn_id, - txn->session_id); - - FOREACH_TXN_REQ_IN_LIST (&txn->get_data_reqs, txn_req) { - assert(txn_req->req_event == MGMTD_TXN_PROC_GETDATA); - - /* - * TODO: Trigger GET procedures for Backend - * For now return back error. - */ - mgmt_fe_send_get_reply(txn->session_id, txn->txn_id, - txn_req->req.get_data->ds_id, - txn_req->req_id, MGMTD_INTERNAL_ERROR, - NULL, "GET-DATA is not supported yet!"); - /* - * Delete the txn request. - * Note: The following will remove it from the list - * as well. - */ - mgmt_txn_req_free(&txn_req); - - /* - * Else the transaction would have been already deleted or - * moved to corresponding pending list. No need to delete it. - */ - num_processed++; - if (num_processed == MGMTD_TXN_MAX_NUM_GETDATA_PROC) - break; - } - - if (mgmt_txn_reqs_count(&txn->get_data_reqs)) { - MGMTD_TXN_DBG("Processed maximum number of Get-Data requests (%d/%d). Rescheduling for rest.", - num_processed, MGMTD_TXN_MAX_NUM_GETDATA_PROC); - mgmt_txn_register_event(txn, MGMTD_TXN_PROC_GETDATA); - } -} - static struct mgmt_txn_ctx * mgmt_fe_find_txn_by_session_id(struct mgmt_master *cm, uint64_t session_id, enum mgmt_txn_type type) @@ -1926,15 +1702,9 @@ static struct mgmt_txn_ctx *mgmt_txn_create_new(uint64_t session_id, { struct mgmt_txn_ctx *txn = NULL; - /* - * For 'CONFIG' transaction check if one is already created - * or not. - */ - if (type == MGMTD_TXN_TYPE_CONFIG && mgmt_txn_mm->cfg_txn) { - if (mgmt_config_txn_in_progress() == session_id) - txn = mgmt_txn_mm->cfg_txn; - goto mgmt_create_txn_done; - } + /* Do not allow multiple config transactions */ + if (type == MGMTD_TXN_TYPE_CONFIG && mgmt_config_txn_in_progress()) + return NULL; txn = mgmt_fe_find_txn_by_session_id(mgmt_txn_mm, session_id, type); if (!txn) { @@ -1944,10 +1714,10 @@ static struct mgmt_txn_ctx *mgmt_txn_create_new(uint64_t session_id, txn->session_id = session_id; txn->type = type; mgmt_txns_add_tail(&mgmt_txn_mm->txn_list, txn); + /* TODO: why do we need N lists for one transaction */ mgmt_txn_reqs_init(&txn->set_cfg_reqs); mgmt_txn_reqs_init(&txn->get_cfg_reqs); - mgmt_txn_reqs_init(&txn->get_data_reqs); - mgmt_txn_reqs_init(&txn->pending_get_datas); + mgmt_txn_reqs_init(&txn->get_tree_reqs); txn->commit_cfg_req = NULL; txn->refcount = 0; if (!mgmt_txn_mm->next_txn_id) @@ -1955,8 +1725,8 @@ static struct mgmt_txn_ctx *mgmt_txn_create_new(uint64_t session_id, txn->txn_id = mgmt_txn_mm->next_txn_id++; hash_get(mgmt_txn_mm->txn_hash, txn, hash_alloc_intern); - MGMTD_TXN_DBG("Added new '%s' txn-id: %" PRIu64, - mgmt_txn_type2str(type), txn->txn_id); + __dbg("Added new '%s' txn-id: %" PRIu64, + mgmt_txn_type2str(type), txn->txn_id); if (type == MGMTD_TXN_TYPE_CONFIG) mgmt_txn_mm->cfg_txn = txn; @@ -1964,7 +1734,6 @@ static struct mgmt_txn_ctx *mgmt_txn_create_new(uint64_t session_id, MGMTD_TXN_LOCK(txn); } -mgmt_create_txn_done: return txn; } @@ -2029,12 +1798,18 @@ static inline struct mgmt_txn_ctx *mgmt_txn_id2ctx(uint64_t txn_id) return txn; } +uint64_t mgmt_txn_get_session_id(uint64_t txn_id) +{ + struct mgmt_txn_ctx *txn = mgmt_txn_id2ctx(txn_id); + + return txn ? txn->session_id : MGMTD_SESSION_ID_NONE; +} + static void mgmt_txn_lock(struct mgmt_txn_ctx *txn, const char *file, int line) { txn->refcount++; - MGMTD_TXN_DBG("%s:%d --> Lock %s txn-id: %" PRIu64 " refcnt: %d", file, - line, mgmt_txn_type2str(txn->type), txn->txn_id, - txn->refcount); + __dbg("%s:%d --> Lock %s txn-id: %" PRIu64 " refcnt: %d", file, line, + mgmt_txn_type2str(txn->type), txn->txn_id, txn->refcount); } static void mgmt_txn_unlock(struct mgmt_txn_ctx **txn, const char *file, @@ -2043,9 +1818,8 @@ static void mgmt_txn_unlock(struct mgmt_txn_ctx **txn, const char *file, assert(*txn && (*txn)->refcount); (*txn)->refcount--; - MGMTD_TXN_DBG("%s:%d --> Unlock %s txn-id: %" PRIu64 " refcnt: %d", - file, line, mgmt_txn_type2str((*txn)->type), - (*txn)->txn_id, (*txn)->refcount); + __dbg("%s:%d --> Unlock %s txn-id: %" PRIu64 " refcnt: %d", file, line, + mgmt_txn_type2str((*txn)->type), (*txn)->txn_id, (*txn)->refcount); if (!(*txn)->refcount) { if ((*txn)->type == MGMTD_TXN_TYPE_CONFIG) if (mgmt_txn_mm->cfg_txn == *txn) @@ -2054,13 +1828,13 @@ static void mgmt_txn_unlock(struct mgmt_txn_ctx **txn, const char *file, EVENT_OFF((*txn)->proc_get_data); EVENT_OFF((*txn)->proc_comm_cfg); EVENT_OFF((*txn)->comm_cfg_timeout); + EVENT_OFF((*txn)->get_tree_timeout); hash_release(mgmt_txn_mm->txn_hash, *txn); mgmt_txns_del(&mgmt_txn_mm->txn_list, *txn); - MGMTD_TXN_DBG("Deleted %s txn-id: %" PRIu64 - " session-id: %" PRIu64, - mgmt_txn_type2str((*txn)->type), (*txn)->txn_id, - (*txn)->session_id); + __dbg("Deleted %s txn-id: %" PRIu64 " session-id: %" PRIu64, + mgmt_txn_type2str((*txn)->type), (*txn)->txn_id, + (*txn)->session_id); XFREE(MTYPE_MGMTD_TXN, *txn); } @@ -2086,16 +1860,6 @@ static void mgmt_txn_cleanup_all_txns(void) mgmt_txn_cleanup_txn(&txn); } -static void mgmt_txn_cleanup(struct event *thread) -{ - struct mgmt_txn_ctx *txn; - - txn = (struct mgmt_txn_ctx *)EVENT_ARG(thread); - assert(txn); - - mgmt_txn_cleanup_txn(&txn); -} - static void mgmt_txn_register_event(struct mgmt_txn_ctx *txn, enum mgmt_txn_event event) { @@ -2117,19 +1881,19 @@ static void mgmt_txn_register_event(struct mgmt_txn_ctx *txn, event_add_timer_tv(mgmt_txn_tm, mgmt_txn_process_get_cfg, txn, &tv, &txn->proc_get_cfg); break; - case MGMTD_TXN_PROC_GETDATA: - event_add_timer_tv(mgmt_txn_tm, mgmt_txn_process_get_data, txn, - &tv, &txn->proc_get_data); - break; case MGMTD_TXN_COMMITCFG_TIMEOUT: - event_add_timer_msec(mgmt_txn_tm, mgmt_txn_cfg_commit_timedout, - txn, MGMTD_TXN_CFG_COMMIT_MAX_DELAY_MSEC, - &txn->comm_cfg_timeout); + event_add_timer(mgmt_txn_tm, mgmt_txn_cfg_commit_timedout, 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: + assert(!"code bug do not register this event"); break; - case MGMTD_TXN_CLEANUP: - tv.tv_usec = MGMTD_TXN_CLEANUP_DELAY_USEC; - event_add_timer_tv(mgmt_txn_tm, mgmt_txn_cleanup, txn, &tv, - &txn->clnup); } } @@ -2154,12 +1918,12 @@ void mgmt_txn_destroy(void) mgmt_txn_hash_destroy(); } -uint64_t mgmt_config_txn_in_progress(void) +bool mgmt_config_txn_in_progress(void) { if (mgmt_txn_mm && mgmt_txn_mm->cfg_txn) - return mgmt_txn_mm->cfg_txn->session_id; + return true; - return MGMTD_SESSION_ID_NONE; + return false; } uint64_t mgmt_create_txn(uint64_t session_id, enum mgmt_txn_type type) @@ -2195,13 +1959,14 @@ int mgmt_txn_send_set_config_req(uint64_t txn_id, uint64_t req_id, size_t indx; uint16_t *num_chgs; struct nb_cfg_change *cfg_chg; + struct nb_node *node; txn = mgmt_txn_id2ctx(txn_id); if (!txn) return -1; if (implicit_commit && mgmt_txn_reqs_count(&txn->set_cfg_reqs)) { - MGMTD_TXN_ERR( + __log_err( "For implicit commit config only one SETCFG-REQ can be allowed!"); return -1; } @@ -2213,27 +1978,42 @@ int mgmt_txn_send_set_config_req(uint64_t txn_id, uint64_t req_id, for (indx = 0; indx < num_req; indx++) { cfg_chg = &txn_req->req.set_cfg->cfg_changes[*num_chgs]; - if (cfg_req[indx]->req_type == - MGMTD__CFG_DATA_REQ_TYPE__DELETE_DATA) + switch (cfg_req[indx]->req_type) { + case MGMTD__CFG_DATA_REQ_TYPE__DELETE_DATA: + cfg_chg->operation = NB_OP_DELETE; + break; + case MGMTD__CFG_DATA_REQ_TYPE__REMOVE_DATA: cfg_chg->operation = NB_OP_DESTROY; - else if (cfg_req[indx]->req_type == - MGMTD__CFG_DATA_REQ_TYPE__SET_DATA) - cfg_chg->operation = - mgmt_ds_find_data_node_by_xpath(ds_ctx, - cfg_req[indx] - ->data - ->xpath) - ? NB_OP_MODIFY - : NB_OP_CREATE; - else + break; + case MGMTD__CFG_DATA_REQ_TYPE__SET_DATA: + /* + * For backward compatibility, we need to allow creating + * *new* list keys with SET_DATA operation. NB_OP_MODIFY + * is not allowed for keys, so use NB_OP_CREATE_EXCL. + */ + node = nb_node_find(cfg_req[indx]->data->xpath); + if (node && lysc_is_key(node->snode)) + cfg_chg->operation = NB_OP_CREATE_EXCL; + else + cfg_chg->operation = NB_OP_MODIFY; + break; + case MGMTD__CFG_DATA_REQ_TYPE__CREATE_DATA: + cfg_chg->operation = NB_OP_CREATE_EXCL; + break; + case MGMTD__CFG_DATA_REQ_TYPE__REPLACE_DATA: + cfg_chg->operation = NB_OP_REPLACE; + break; + case MGMTD__CFG_DATA_REQ_TYPE__REQ_TYPE_NONE: + case _MGMTD__CFG_DATA_REQ_TYPE_IS_INT_SIZE: + default: continue; + } - MGMTD_TXN_DBG("XPath: '%s', Value: '%s'", - cfg_req[indx]->data->xpath, - (cfg_req[indx]->data->value && - cfg_req[indx]->data->value->encoded_str_val - ? cfg_req[indx]->data->value->encoded_str_val - : "NULL")); + __dbg("XPath: '%s', Value: '%s'", cfg_req[indx]->data->xpath, + (cfg_req[indx]->data->value && + cfg_req[indx]->data->value->encoded_str_val + ? cfg_req[indx]->data->value->encoded_str_val + : "NULL")); strlcpy(cfg_chg->xpath, cfg_req[indx]->data->xpath, sizeof(cfg_chg->xpath)); cfg_chg->value = @@ -2243,8 +2023,8 @@ int mgmt_txn_send_set_config_req(uint64_t txn_id, uint64_t req_id, ->data->value->encoded_str_val) : NULL); if (cfg_chg->value) - MGMTD_TXN_DBG("Allocated value at %p ==> '%s'", - cfg_chg->value, cfg_chg->value); + __dbg("Allocated value at %p ==> '%s'", cfg_chg->value, + cfg_chg->value); (*num_chgs)++; } @@ -2274,9 +2054,9 @@ int mgmt_txn_send_commit_config_req(uint64_t txn_id, uint64_t req_id, return -1; if (txn->commit_cfg_req) { - MGMTD_TXN_ERR("Commit already in-progress txn-id: %" PRIu64 - " session-id: %" PRIu64 ". Cannot start another", - txn->txn_id, txn->session_id); + __log_err("Commit already in-progress txn-id: %" PRIu64 + " session-id: %" PRIu64 ". Cannot start another", + txn->txn_id, txn->session_id); return -1; } @@ -2306,15 +2086,26 @@ int mgmt_txn_notify_be_adapter_conn(struct mgmt_be_client_adapter *adapter, struct mgmt_commit_cfg_req *cmtcfg_req; static struct mgmt_commit_stats dummy_stats; struct nb_config_cbs *adapter_cfgs = NULL; + struct mgmt_ds_ctx *ds_ctx; memset(&dummy_stats, 0, sizeof(dummy_stats)); if (connect) { - /* Get config for this single backend client */ + ds_ctx = mgmt_ds_get_ctx_by_id(mm, MGMTD_DS_RUNNING); + assert(ds_ctx); + + /* + * Lock the running datastore to prevent any changes while we + * are initializing the backend. + */ + if (mgmt_ds_lock(ds_ctx, 0) != 0) + return -1; + /* Get config for this single backend client */ mgmt_be_get_adapter_config(adapter, &adapter_cfgs); if (!adapter_cfgs || RB_EMPTY(nb_config_cbs, adapter_cfgs)) { SET_FLAG(adapter->flags, MGMTD_BE_ADAPTER_FLAGS_CFG_SYNCED); + mgmt_ds_unlock(ds_ctx); return 0; } @@ -2324,14 +2115,15 @@ int mgmt_txn_notify_be_adapter_conn(struct mgmt_be_client_adapter *adapter, */ txn = mgmt_txn_create_new(0, MGMTD_TXN_TYPE_CONFIG); if (!txn) { - MGMTD_TXN_ERR("Failed to create CONFIG Transaction for downloading CONFIGs for client '%s'", - adapter->name); + __log_err("Failed to create CONFIG Transaction for downloading CONFIGs for client '%s'", + adapter->name); + mgmt_ds_unlock(ds_ctx); + nb_config_diff_del_changes(adapter_cfgs); return -1; } - MGMTD_TXN_DBG("Created initial txn-id: %" PRIu64 - " for BE client '%s'", - txn->txn_id, adapter->name); + __dbg("Created initial txn-id: %" PRIu64 " for BE client '%s'", + txn->txn_id, adapter->name); /* * Set the changeset for transaction to commit and trigger the * commit request. @@ -2339,10 +2131,11 @@ int mgmt_txn_notify_be_adapter_conn(struct mgmt_be_client_adapter *adapter, txn_req = mgmt_txn_req_alloc(txn, 0, MGMTD_TXN_PROC_COMMITCFG); txn_req->req.commit_cfg.src_ds_id = MGMTD_DS_NONE; txn_req->req.commit_cfg.src_ds_ctx = 0; - txn_req->req.commit_cfg.dst_ds_id = MGMTD_DS_NONE; - txn_req->req.commit_cfg.dst_ds_ctx = 0; + txn_req->req.commit_cfg.dst_ds_id = MGMTD_DS_RUNNING; + txn_req->req.commit_cfg.dst_ds_ctx = ds_ctx; txn_req->req.commit_cfg.validate_only = false; txn_req->req.commit_cfg.abort = false; + txn_req->req.commit_cfg.init = true; txn_req->req.commit_cfg.cmt_stats = &dummy_stats; txn_req->req.commit_cfg.cfg_chgs = adapter_cfgs; @@ -2365,9 +2158,8 @@ int mgmt_txn_notify_be_adapter_conn(struct mgmt_be_client_adapter *adapter, ? &txn->commit_cfg_req->req .commit_cfg : NULL; - if (cmtcfg_req && - cmtcfg_req->subscr_info - .xpath_subscr[adapter->id]) { + if (cmtcfg_req && IS_IDBIT_SET(cmtcfg_req->clients, + adapter->id)) { mgmt_txn_send_commit_cfg_reply( txn, MGMTD_INTERNAL_ERROR, "Backend daemon disconnected while processing commit!"); @@ -2400,7 +2192,7 @@ int mgmt_txn_notify_be_txn_reply(uint64_t txn_id, bool create, bool success, * Done with TXN_CREATE. Move the backend client to * next phase. */ - assert(cmtcfg_req->curr_phase == + assert(cmtcfg_req->phase == MGMTD_COMMIT_PHASE_TXN_CREATE); /* @@ -2412,23 +2204,16 @@ int mgmt_txn_notify_be_txn_reply(uint64_t txn_id, bool create, bool success, txn, MGMTD_INTERNAL_ERROR, "Internal error! Failed to initiate transaction at backend!"); } - } else { - /* - * Done with TXN_DELETE. Move the backend client to next phase. - */ - if (false) - mgmt_move_be_commit_to_next_phase(txn, adapter); } return 0; } -int mgmt_txn_notify_be_cfgdata_reply(uint64_t txn_id, uint64_t batch_id, - bool success, char *error_if_any, +int mgmt_txn_notify_be_cfgdata_reply(uint64_t txn_id, bool success, + char *error_if_any, struct mgmt_be_client_adapter *adapter) { struct mgmt_txn_ctx *txn; - struct mgmt_txn_be_cfg_batch *batch; struct mgmt_commit_cfg_req *cmtcfg_req; txn = mgmt_txn_id2ctx(txn_id); @@ -2439,15 +2224,11 @@ int mgmt_txn_notify_be_cfgdata_reply(uint64_t txn_id, uint64_t batch_id, return -1; cmtcfg_req = &txn->commit_cfg_req->req.commit_cfg; - batch = mgmt_txn_cfgbatch_id2ctx(txn, batch_id); - if (!batch || batch->txn != txn) - return -1; - if (!success) { - MGMTD_TXN_ERR("CFGDATA_CREATE_REQ sent to '%s' failed txn-id: %" PRIu64 - " batch-id %" PRIu64 " err: %s", - adapter->name, txn->txn_id, batch->batch_id, - error_if_any ? error_if_any : "None"); + __log_err("CFGDATA_CREATE_REQ sent to '%s' failed txn-id: %" PRIu64 + " err: %s", + adapter->name, txn->txn_id, + error_if_any ? error_if_any : "None"); mgmt_txn_send_commit_cfg_reply( txn, MGMTD_INTERNAL_ERROR, error_if_any @@ -2456,14 +2237,11 @@ int mgmt_txn_notify_be_cfgdata_reply(uint64_t txn_id, uint64_t batch_id, return 0; } - MGMTD_TXN_DBG("CFGDATA_CREATE_REQ sent to '%s' was successful txn-id: %" PRIu64 - " batch-id %" PRIu64 " err: %s", - adapter->name, txn->txn_id, batch->batch_id, - error_if_any ? error_if_any : "None"); - mgmt_move_txn_cfg_batch_to_next(cmtcfg_req, batch, - &cmtcfg_req->curr_batches[adapter->id], - &cmtcfg_req->next_batches[adapter->id], - true, MGMTD_COMMIT_PHASE_APPLY_CFG); + __dbg("CFGDATA_CREATE_REQ sent to '%s' was successful txn-id: %" PRIu64 + " err: %s", + adapter->name, txn->txn_id, error_if_any ? error_if_any : "None"); + + cmtcfg_req->be_phase[adapter->id] = MGMTD_COMMIT_PHASE_APPLY_CFG; mgmt_try_move_commit_to_next_phase(txn, cmtcfg_req); @@ -2471,14 +2249,11 @@ int mgmt_txn_notify_be_cfgdata_reply(uint64_t txn_id, uint64_t batch_id, } int mgmt_txn_notify_be_cfg_apply_reply(uint64_t txn_id, bool success, - uint64_t batch_ids[], - size_t num_batch_ids, char *error_if_any, + char *error_if_any, struct mgmt_be_client_adapter *adapter) { struct mgmt_txn_ctx *txn; - struct mgmt_txn_be_cfg_batch *batch; struct mgmt_commit_cfg_req *cmtcfg_req = NULL; - size_t indx; txn = mgmt_txn_id2ctx(txn_id); if (!txn || txn->type != MGMTD_TXN_TYPE_CONFIG || !txn->commit_cfg_req) @@ -2487,11 +2262,10 @@ int mgmt_txn_notify_be_cfg_apply_reply(uint64_t txn_id, bool success, cmtcfg_req = &txn->commit_cfg_req->req.commit_cfg; if (!success) { - MGMTD_TXN_ERR("CFGDATA_APPLY_REQ sent to '%s' failed txn-id: %" PRIu64 - " batch ids %" PRIu64 " - %" PRIu64 " err: %s", - adapter->name, txn->txn_id, batch_ids[0], - batch_ids[num_batch_ids - 1], - error_if_any ? error_if_any : "None"); + __log_err("CFGDATA_APPLY_REQ sent to '%s' failed txn-id: %" PRIu64 + " err: %s", + adapter->name, txn->txn_id, + error_if_any ? error_if_any : "None"); mgmt_txn_send_commit_cfg_reply( txn, MGMTD_INTERNAL_ERROR, error_if_any @@ -2500,25 +2274,14 @@ int mgmt_txn_notify_be_cfg_apply_reply(uint64_t txn_id, bool success, return 0; } - for (indx = 0; indx < num_batch_ids; indx++) { - batch = mgmt_txn_cfgbatch_id2ctx(txn, batch_ids[indx]); - if (batch->txn != txn) - return -1; - mgmt_move_txn_cfg_batch_to_next( - cmtcfg_req, batch, - &cmtcfg_req->curr_batches[adapter->id], - &cmtcfg_req->next_batches[adapter->id], true, - MGMTD_COMMIT_PHASE_TXN_DELETE); - } + cmtcfg_req->be_phase[adapter->id] = MGMTD_COMMIT_PHASE_TXN_DELETE; - if (!mgmt_txn_batches_count(&cmtcfg_req->curr_batches[adapter->id])) { - /* - * All configuration for the specific backend has been applied. - * Send TXN-DELETE to wrap up the transaction for this backend. - */ - SET_FLAG(adapter->flags, MGMTD_BE_ADAPTER_FLAGS_CFG_SYNCED); - mgmt_txn_send_be_txn_delete(txn, adapter); - } + /* + * All configuration for the specific backend has been applied. + * Send TXN-DELETE to wrap up the transaction for this backend. + */ + SET_FLAG(adapter->flags, MGMTD_BE_ADAPTER_FLAGS_CFG_SYNCED); + mgmt_txn_send_be_txn_delete(txn, adapter); mgmt_try_move_commit_to_next_phase(txn, cmtcfg_req); if (mm->perf_stats_en) @@ -2540,15 +2303,14 @@ int mgmt_txn_send_get_req(uint64_t txn_id, uint64_t req_id, if (!txn) return -1; - req_event = cfg_root ? MGMTD_TXN_PROC_GETCFG : MGMTD_TXN_PROC_GETDATA; - + req_event = MGMTD_TXN_PROC_GETCFG; txn_req = mgmt_txn_req_alloc(txn, req_id, req_event); txn_req->req.get_data->ds_id = ds_id; txn_req->req.get_data->cfg_root = cfg_root; for (indx = 0; indx < num_reqs && indx < MGMTD_MAX_NUM_DATA_REPLY_IN_BATCH; indx++) { - MGMTD_TXN_DBG("XPath: '%s'", data_req[indx]->data->xpath); + __dbg("XPath: '%s'", data_req[indx]->data->xpath); txn_req->req.get_data->xpaths[indx] = strdup(data_req[indx]->data->xpath); txn_req->req.get_data->num_xpaths++; @@ -2559,6 +2321,266 @@ int mgmt_txn_send_get_req(uint64_t txn_id, uint64_t req_id, return 0; } + +/** + * Send get-tree requests to each client indicated in `clients` bitmask, which + * has registered operational state that matches the given `xpath` + */ +int mgmt_txn_send_get_tree_oper(uint64_t txn_id, uint64_t req_id, + uint64_t clients, Mgmtd__DatastoreId ds_id, + LYD_FORMAT result_type, uint8_t flags, + uint32_t wd_options, bool simple_xpath, + const char *xpath) +{ + struct mgmt_msg_get_tree *msg; + struct mgmt_txn_ctx *txn; + struct mgmt_txn_req *txn_req; + struct txn_req_get_tree *get_tree; + enum mgmt_be_client_id id; + ssize_t slen = strlen(xpath); + int ret; + + txn = mgmt_txn_id2ctx(txn_id); + if (!txn) + return -1; + + /* If error in this function below here, be sure to free the req */ + txn_req = mgmt_txn_req_alloc(txn, req_id, MGMTD_TXN_PROC_GETTREE); + get_tree = txn_req->req.get_tree; + get_tree->result_type = result_type; + get_tree->wd_options = wd_options; + get_tree->exact = CHECK_FLAG(flags, GET_DATA_FLAG_EXACT); + get_tree->simple_xpath = simple_xpath; + get_tree->xpath = XSTRDUP(MTYPE_MGMTD_XPATH, xpath); + + if (CHECK_FLAG(flags, GET_DATA_FLAG_CONFIG)) { + /* + * If the requested datastore is operational, get the config + * from running. + */ + struct mgmt_ds_ctx *ds = + mgmt_ds_get_ctx_by_id(mm, ds_id == MGMTD_DS_OPERATIONAL + ? MGMTD_DS_RUNNING + : ds_id); + struct nb_config *config = mgmt_ds_get_nb_config(ds); + + if (config) { + struct ly_set *set = NULL; + LY_ERR err; + + err = lyd_find_xpath(config->dnode, xpath, &set); + if (err) { + get_tree->partial_error = err; + goto state; + } + + /* + * If there's a single result, duplicate the returned + * node. If there are multiple results, duplicate the + * whole config and mark simple_xpath as false so the + * result is trimmed later in txn_get_tree_data_done. + */ + if (set->count == 1) { + err = lyd_dup_single(set->dnodes[0], NULL, + LYD_DUP_WITH_PARENTS | + LYD_DUP_WITH_FLAGS | + LYD_DUP_RECURSIVE, + &get_tree->client_results); + if (!err) + while (get_tree->client_results->parent) + get_tree->client_results = lyd_parent( + get_tree->client_results); + } else if (set->count > 1) { + err = lyd_dup_siblings(config->dnode, NULL, + LYD_DUP_RECURSIVE | + LYD_DUP_WITH_FLAGS, + &get_tree->client_results); + if (!err) + get_tree->simple_xpath = false; + } + + if (err) + get_tree->partial_error = err; + + ly_set_free(set, NULL); + } + } +state: + /* If we are only getting config, we are done */ + if (!CHECK_FLAG(flags, GET_DATA_FLAG_STATE) || + ds_id != MGMTD_DS_OPERATIONAL || !clients) + return txn_get_tree_data_done(txn, txn_req); + + msg = mgmt_msg_native_alloc_msg(struct mgmt_msg_get_tree, slen + 1, + MTYPE_MSG_NATIVE_GET_TREE); + msg->refer_id = txn_id; + msg->req_id = req_id; + msg->code = MGMT_MSG_CODE_GET_TREE; + /* Always operate with the binary format in the backend */ + msg->result_type = LYD_LYB; + strlcpy(msg->xpath, xpath, slen + 1); + + assert(clients); + FOREACH_BE_CLIENT_BITS (id, clients) { + ret = mgmt_be_send_native(id, msg); + if (ret) { + __log_err("Could not send get-tree message to backend client %s", + mgmt_be_client_id2name(id)); + continue; + } + + __dbg("Sent get-tree req to backend client %s", + mgmt_be_client_id2name(id)); + + /* record that we sent the request to the client */ + get_tree->sent_clients |= (1u << id); + } + + mgmt_msg_native_free_msg(msg); + + /* Return if we didn't send any messages to backends */ + if (!get_tree->sent_clients) + return txn_get_tree_data_done(txn, txn_req); + + /* Start timeout timer - pulled out of register event code so we can + * pass a different arg + */ + event_add_timer(mgmt_txn_tm, txn_get_tree_timeout, txn_req, + MGMTD_TXN_GET_TREE_MAX_DELAY_SEC, + &txn->get_tree_timeout); + return 0; +} + +/* + * Error reply from the backend client. + */ +int mgmt_txn_notify_error(struct mgmt_be_client_adapter *adapter, + uint64_t txn_id, uint64_t req_id, int error, + const char *errstr) +{ + 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 mgmt_txn_req *txn_req; + + if (!txn) { + __log_err("Error reply from %s cannot find txn-id %" PRIu64, + adapter->name, txn_id); + return -1; + } + + /* Find the request. */ + FOREACH_TXN_REQ_IN_LIST (&txn->get_tree_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, + adapter->name, txn_id, req_id); + return -1; + } + + __log_err("Error reply from %s for txn-id %" PRIu64 " req_id %" PRIu64, + adapter->name, txn_id, req_id); + + switch (txn_req->req_event) { + case MGMTD_TXN_PROC_GETTREE: + get_tree = txn_req->req.get_tree; + get_tree->recv_clients |= (1u << id); + get_tree->partial_error = error; + + /* check if done yet */ + if (get_tree->recv_clients != get_tree->sent_clients) + return 0; + return txn_get_tree_data_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; + } +} + +/* + * Get-tree data from the backend client. + */ +int mgmt_txn_notify_tree_data_reply(struct mgmt_be_client_adapter *adapter, + struct mgmt_msg_tree_data *data_msg, + size_t msg_len) +{ + uint64_t txn_id = data_msg->refer_id; + uint64_t req_id = data_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_get_tree *get_tree; + struct lyd_node *tree = NULL; + LY_ERR err; + + if (!txn) { + __log_err("GETTREE reply from %s for a missing txn-id %" PRIu64, + adapter->name, txn_id); + return -1; + } + + /* Find the request. */ + FOREACH_TXN_REQ_IN_LIST (&txn->get_tree_reqs, txn_req) + if (txn_req->req_id == req_id) + break; + if (!txn_req) { + __log_err("GETTREE reply from %s for txn-id %" PRIu64 + " missing req_id %" PRIu64, + adapter->name, txn_id, req_id); + return -1; + } + + get_tree = txn_req->req.get_tree; + + /* store the result */ + err = lyd_parse_data_mem(ly_native_ctx, (const char *)data_msg->result, + data_msg->result_type, + LYD_PARSE_STRICT | LYD_PARSE_ONLY, + 0 /*LYD_VALIDATE_OPERATIONAL*/, &tree); + if (err) { + __log_err("GETTREE reply from %s for txn-id %" PRIu64 + " req_id %" PRIu64 " error parsing result of type %u", + adapter->name, txn_id, req_id, data_msg->result_type); + } + if (!err) { + /* TODO: we could merge ly_errs here if it's not binary */ + + if (!get_tree->client_results) + get_tree->client_results = tree; + else + err = lyd_merge_siblings(&get_tree->client_results, + tree, LYD_MERGE_DESTRUCT); + if (err) { + __log_err("GETTREE reply from %s for txn-id %" PRIu64 + " req_id %" PRIu64 " error merging result", + adapter->name, txn_id, req_id); + } + } + if (!get_tree->partial_error) + get_tree->partial_error = (data_msg->partial_error + ? data_msg->partial_error + : (int)err); + + if (!data_msg->more) + get_tree->recv_clients |= (1u << id); + + /* check if done yet */ + if (get_tree->recv_clients != get_tree->sent_clients) + return 0; + + return txn_get_tree_data_done(txn, txn_req); +} + void mgmt_txn_status_write(struct vty *vty) { struct mgmt_txn_ctx *txn; @@ -2614,12 +2636,12 @@ int mgmt_txn_rollback_trigger_cfg_apply(struct mgmt_ds_ctx *src_ds_ctx, */ txn = mgmt_txn_create_new(0, MGMTD_TXN_TYPE_CONFIG); if (!txn) { - MGMTD_TXN_ERR( + __log_err( "Failed to create CONFIG Transaction for downloading CONFIGs"); return -1; } - MGMTD_TXN_DBG("Created rollback txn-id: %" PRIu64, txn->txn_id); + __dbg("Created rollback txn-id: %" PRIu64, txn->txn_id); /* * Set the changeset for transaction to commit and trigger the commit diff --git a/mgmtd/mgmt_txn.h b/mgmtd/mgmt_txn.h index 068f07a..b719832 100644 --- a/mgmtd/mgmt_txn.h +++ b/mgmtd/mgmt_txn.h @@ -9,23 +9,23 @@ #ifndef _FRR_MGMTD_TXN_H_ #define _FRR_MGMTD_TXN_H_ +#include "lib/mgmt_msg_native.h" #include "mgmtd/mgmt_be_adapter.h" #include "mgmtd/mgmt.h" #include "mgmtd/mgmt_ds.h" -#define MGMTD_TXN_PROC_DELAY_MSEC 5 #define MGMTD_TXN_PROC_DELAY_USEC 10 #define MGMTD_TXN_MAX_NUM_SETCFG_PROC 128 #define MGMTD_TXN_MAX_NUM_GETCFG_PROC 128 #define MGMTD_TXN_MAX_NUM_GETDATA_PROC 128 -#define MGMTD_TXN_SEND_CFGVALIDATE_DELAY_MSEC 100 -#define MGMTD_TXN_SEND_CFGAPPLY_DELAY_MSEC 100 -#define MGMTD_TXN_CFG_COMMIT_MAX_DELAY_MSEC 30000 /* 30 seconds */ +#define MGMTD_TXN_CFG_COMMIT_MAX_DELAY_SEC 600 +#define MGMTD_TXN_GET_TREE_MAX_DELAY_SEC 600 -#define MGMTD_TXN_CLEANUP_DELAY_MSEC 100 #define MGMTD_TXN_CLEANUP_DELAY_USEC 10 +#define MGMTD_TXN_ID_NONE 0 + /* * The following definition enables local validation of config * on the MGMTD process by loading client-defined NB callbacks @@ -71,12 +71,18 @@ extern int mgmt_txn_init(struct mgmt_master *cm, struct event_loop *tm); extern void mgmt_txn_destroy(void); /* - * Check if transaction is in progress. + * Check if configuration transaction is in progress. * * Returns: - * session ID if in-progress, MGMTD_SESSION_ID_NONE otherwise. + * true if in-progress, false otherwise. */ -extern uint64_t mgmt_config_txn_in_progress(void); +extern bool mgmt_config_txn_in_progress(void); + +/** + * Get the session ID associated with the given ``txn-id``. + * + */ +extern uint64_t mgmt_txn_get_session_id(uint64_t txn_id); /* * Create transaction. @@ -188,6 +194,31 @@ extern int mgmt_txn_send_get_req(uint64_t txn_id, uint64_t req_id, Mgmtd__YangGetDataReq **data_req, size_t num_reqs); + +/** + * Send get-tree to the backend `clients`. + * + * Args: + * txn_id: Transaction identifier. + * req_id: FE client request identifier. + * clients: Bitmask of clients to send get-tree to. + * ds_id: datastore ID. + * result_type: LYD_FORMAT result format. + * flags: option flags for the request. + * wd_options: LYD_PRINT_WD_* flags for the result. + * simple_xpath: true if xpath is simple (only key predicates). + * xpath: The xpath to get the tree from. + * + * Return: + * 0 on success. + */ +extern int mgmt_txn_send_get_tree_oper(uint64_t txn_id, uint64_t req_id, + uint64_t clients, + Mgmtd__DatastoreId ds_id, + LYD_FORMAT result_type, uint8_t flags, + uint32_t wd_options, bool simple_xpath, + const char *xpath); + /* * Notifiy backend adapter on connection. */ @@ -206,8 +237,8 @@ mgmt_txn_notify_be_txn_reply(uint64_t txn_id, bool create, bool success, * Reply to backend adapater with config data create request. */ extern int -mgmt_txn_notify_be_cfgdata_reply(uint64_t txn_id, uint64_t batch_id, - bool success, char *error_if_any, +mgmt_txn_notify_be_cfgdata_reply(uint64_t txn_id, bool success, + char *error_if_any, struct mgmt_be_client_adapter *adapter); /* @@ -223,10 +254,37 @@ extern int mgmt_txn_notify_be_cfg_validate_reply( */ extern int mgmt_txn_notify_be_cfg_apply_reply(uint64_t txn_id, bool success, - uint64_t batch_ids[], - size_t num_batch_ids, char *error_if_any, + char *error_if_any, struct mgmt_be_client_adapter *adapter); + +/** + * Process a reply from a backend client to our get-tree request + * + * Args: + * adapter: The adapter that received the result. + * txn_id: The transaction for this get-tree request. + * req_id: The request ID for this transaction. + * error: the integer error value (negative) + * errstr: the string description of the error. + */ +int mgmt_txn_notify_error(struct mgmt_be_client_adapter *adapter, + uint64_t txn_id, uint64_t req_id, int error, + const char *errstr); + +/** + * Process a reply from a backend client to our get-tree request + * + * Args: + * adapter: The adapter that received the result. + * data_msg: The message from the backend. + * msg_len: Total length of the message. + */ + +extern int mgmt_txn_notify_tree_data_reply(struct mgmt_be_client_adapter *adapter, + struct mgmt_msg_tree_data *data_msg, + size_t msg_len); + /* * Dump transaction status to vty. */ diff --git a/mgmtd/mgmt_vty.c b/mgmtd/mgmt_vty.c index b49bf80..bbc1077 100644 --- a/mgmtd/mgmt_vty.c +++ b/mgmtd/mgmt_vty.c @@ -8,10 +8,13 @@ #include <zebra.h> +#include "affinitymap.h" #include "command.h" +#include "filter.h" #include "json.h" #include "network.h" #include "northbound_cli.h" +#include "routemap.h" #include "mgmtd/mgmt.h" #include "mgmtd/mgmt_be_adapter.h" @@ -20,6 +23,10 @@ #include "mgmtd/mgmt_history.h" #include "mgmtd/mgmt_vty_clippy.c" +#include "ripd/rip_nb.h" +#include "ripngd/ripng_nb.h" +#include "staticd/static_vty.h" +#include "zebra/zebra_cli.h" extern struct frr_daemon_info *mgmt_daemon_info; @@ -144,6 +151,23 @@ DEFPY(mgmt_commit, return CMD_SUCCESS; } +DEFPY(mgmt_create_config_data, mgmt_create_config_data_cmd, + "mgmt create-config WORD$path VALUE", + MGMTD_STR + "Create configuration data\n" + "XPath expression specifying the YANG data path\n" + "Value of the data to create\n") +{ + strlcpy(vty->cfg_changes[0].xpath, path, + sizeof(vty->cfg_changes[0].xpath)); + vty->cfg_changes[0].value = value; + vty->cfg_changes[0].operation = NB_OP_CREATE_EXCL; + vty->num_cfg_changes = 1; + + vty_mgmt_send_config_data(vty, NULL, false); + return CMD_SUCCESS; +} + DEFPY(mgmt_set_config_data, mgmt_set_config_data_cmd, "mgmt set-config WORD$path VALUE", MGMTD_STR @@ -154,10 +178,10 @@ DEFPY(mgmt_set_config_data, mgmt_set_config_data_cmd, strlcpy(vty->cfg_changes[0].xpath, path, sizeof(vty->cfg_changes[0].xpath)); vty->cfg_changes[0].value = value; - vty->cfg_changes[0].operation = NB_OP_CREATE; + vty->cfg_changes[0].operation = NB_OP_MODIFY; vty->num_cfg_changes = 1; - vty_mgmt_send_config_data(vty, false); + vty_mgmt_send_config_data(vty, NULL, false); return CMD_SUCCESS; } @@ -171,10 +195,45 @@ DEFPY(mgmt_delete_config_data, mgmt_delete_config_data_cmd, strlcpy(vty->cfg_changes[0].xpath, path, sizeof(vty->cfg_changes[0].xpath)); vty->cfg_changes[0].value = NULL; + vty->cfg_changes[0].operation = NB_OP_DELETE; + vty->num_cfg_changes = 1; + + vty_mgmt_send_config_data(vty, NULL, false); + return CMD_SUCCESS; +} + +DEFPY(mgmt_remove_config_data, mgmt_remove_config_data_cmd, + "mgmt remove-config WORD$path", + MGMTD_STR + "Remove configuration data\n" + "XPath expression specifying the YANG data path\n") +{ + + strlcpy(vty->cfg_changes[0].xpath, path, + sizeof(vty->cfg_changes[0].xpath)); + vty->cfg_changes[0].value = NULL; vty->cfg_changes[0].operation = NB_OP_DESTROY; vty->num_cfg_changes = 1; - vty_mgmt_send_config_data(vty, false); + vty_mgmt_send_config_data(vty, NULL, false); + return CMD_SUCCESS; +} + +DEFPY(mgmt_replace_config_data, mgmt_replace_config_data_cmd, + "mgmt replace-config WORD$path VALUE", + MGMTD_STR + "Replace configuration data\n" + "XPath expression specifying the YANG data path\n" + "Value of the data to set\n") +{ + + strlcpy(vty->cfg_changes[0].xpath, path, + sizeof(vty->cfg_changes[0].xpath)); + vty->cfg_changes[0].value = value; + vty->cfg_changes[0].operation = NB_OP_REPLACE; + vty->num_cfg_changes = 1; + + vty_mgmt_send_config_data(vty, NULL, false); return CMD_SUCCESS; } @@ -199,22 +258,69 @@ DEFPY(show_mgmt_get_config, show_mgmt_get_config_cmd, } DEFPY(show_mgmt_get_data, show_mgmt_get_data_cmd, - "show mgmt get-data [candidate|operational|running]$dsname WORD$path", - SHOW_STR MGMTD_STR - "Get data from a specific datastore\n" + "show mgmt get-data WORD$path [datastore <candidate|running|operational>$ds] [with-config|only-config]$content [exact]$exact [with-defaults <trim|all-tag|all>$wd] [json|xml]$fmt", + SHOW_STR + MGMTD_STR + "Get a data from the operational datastore\n" + "XPath expression specifying the YANG data root\n" + "Specify datastore to get data from (operational by default)\n" "Candidate datastore\n" - "Operational datastore (default)\n" "Running datastore\n" - "XPath expression specifying the YANG data path\n") + "Operational datastore\n" + "Include \"config true\" data\n" + "Get only \"config true\" data\n" + "Get exact node instead of the whole data tree\n" + "Configure 'with-defaults' mode per RFC 6243 (\"explicit\" mode by default)\n" + "Use \"trim\" mode\n" + "Use \"report-all-tagged\" mode\n" + "Use \"report-all\" mode\n" + "JSON output format\n" + "XML output format\n") { - const char *xpath_list[VTY_MAXCFGCHANGES] = {0}; - Mgmtd__DatastoreId datastore = MGMTD_DS_OPERATIONAL; + LYD_FORMAT format = (fmt && fmt[0] == 'x') ? LYD_XML : LYD_JSON; + int plen = strlen(path); + char *xpath = NULL; + uint8_t flags = content ? GET_DATA_FLAG_CONFIG : GET_DATA_FLAG_STATE; + uint8_t defaults = GET_DATA_DEFAULTS_EXPLICIT; + uint8_t datastore = MGMT_MSG_DATASTORE_OPERATIONAL; + + if (content && content[0] == 'w') + flags |= GET_DATA_FLAG_STATE; + + if (exact) + flags |= GET_DATA_FLAG_EXACT; + + if (wd) { + if (wd[0] == 't') + defaults = GET_DATA_DEFAULTS_TRIM; + else if (wd[3] == '-') + defaults = GET_DATA_DEFAULTS_ALL_ADD_TAG; + else + defaults = GET_DATA_DEFAULTS_ALL; + } - if (dsname) - datastore = mgmt_ds_name2id(dsname); + if (ds) { + if (ds[0] == 'c') + datastore = MGMT_MSG_DATASTORE_CANDIDATE; + else if (ds[0] == 'r') + datastore = MGMT_MSG_DATASTORE_RUNNING; + } + + /* get rid of extraneous trailing slash-* or single '/' unless root */ + if (plen > 2 && ((path[plen - 2] == '/' && path[plen - 1] == '*') || + (path[plen - 2] != '/' && path[plen - 1] == '/'))) { + plen = path[plen - 1] == '/' ? plen - 1 : plen - 2; + xpath = XSTRDUP(MTYPE_TMP, path); + xpath[plen] = 0; + path = xpath; + } + + vty_mgmt_send_get_data_req(vty, datastore, format, flags, defaults, + path); + + if (xpath) + XFREE(MTYPE_TMP, xpath); - xpath_list[0] = path; - vty_mgmt_send_get_req(vty, false, datastore, xpath_list, 1); return CMD_SUCCESS; } @@ -273,7 +379,7 @@ DEFPY(show_mgmt_map_xpath, "Get YANG Backend Subscription\n" "XPath expression specifying the YANG data path\n") { - mgmt_be_xpath_subscr_info_write(vty, path); + mgmt_be_show_xpath_registries(vty, path); return CMD_SUCCESS; } @@ -379,7 +485,7 @@ DEFPY(mgmt_rollback, int config_write_mgmt_debug(struct vty *vty); static struct cmd_node debug_node = { - .name = "debug", + .name = "mgmt debug", .node = DEBUG_NODE, .prompt = "", .config_write = config_write_mgmt_debug, @@ -438,12 +544,18 @@ DEFPY(debug_mgmt, debug_mgmt_cmd, { uint32_t mode = DEBUG_NODE2MODE(vty->node); - if (be) + if (be) { DEBUG_MODE_SET(&mgmt_debug_be, mode, !no); + mgmt_be_adapter_toggle_client_debug( + DEBUG_MODE_CHECK(&mgmt_debug_be, DEBUG_MODE_ALL)); + } if (ds) DEBUG_MODE_SET(&mgmt_debug_ds, mode, !no); - if (fe) + if (fe) { DEBUG_MODE_SET(&mgmt_debug_fe, mode, !no); + mgmt_fe_adapter_toggle_client_debug( + DEBUG_MODE_CHECK(&mgmt_debug_fe, DEBUG_MODE_ALL)); + } if (txn) DEBUG_MODE_SET(&mgmt_debug_txn, mode, !no); @@ -452,19 +564,57 @@ DEFPY(debug_mgmt, debug_mgmt_cmd, static void mgmt_config_read_in(struct event *event) { - mgmt_vty_read_configs(); + if (vty_mgmt_fe_enabled()) + mgmt_vty_read_configs(); + else { + zlog_warn("%s: no connection to front-end server, retry in 1s", + __func__); + event_add_timer(mm->master, mgmt_config_read_in, NULL, 1, + &mgmt_daemon_info->read_in); + } +} + +static int mgmtd_config_write(struct vty *vty) +{ + struct lyd_node *root; + + LY_LIST_FOR (running_config->dnode, root) { + nb_cli_show_dnode_cmds(vty, root, false); + } + + return 1; } +static struct cmd_node mgmtd_node = { + .name = "mgmtd", + .node = MGMTD_NODE, + .prompt = "", + .config_write = mgmtd_config_write, +}; + void mgmt_vty_init(void) { /* + * Library based CLI handlers + */ + filter_cli_init(); + route_map_cli_init(); + affinity_map_init(); + + /* * Initialize command handling from VTYSH connection. * Call command initialization routines defined by * backend components that are moved to new MGMTD infra * here one by one. */ -#if HAVE_STATICD - extern void static_vty_init(void); + zebra_cli_init(); +#ifdef HAVE_RIPD + rip_cli_init(); +#endif +#ifdef HAVE_RIPNGD + ripng_cli_init(); +#endif +#ifdef HAVE_STATICD static_vty_init(); #endif @@ -472,6 +622,7 @@ void mgmt_vty_init(void) &mgmt_daemon_info->read_in); install_node(&debug_node); + install_node(&mgmtd_node); install_element(VIEW_NODE, &show_mgmt_be_adapter_cmd); install_element(VIEW_NODE, &show_mgmt_be_xpath_reg_cmd); @@ -485,8 +636,11 @@ void mgmt_vty_init(void) install_element(VIEW_NODE, &show_mgmt_cmt_hist_cmd); install_element(CONFIG_NODE, &mgmt_commit_cmd); + install_element(CONFIG_NODE, &mgmt_create_config_data_cmd); install_element(CONFIG_NODE, &mgmt_set_config_data_cmd); 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_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 67b45d5..1624c6e 100644 --- a/mgmtd/subdir.am +++ b/mgmtd/subdir.am @@ -15,6 +15,9 @@ clippy_scan += \ # end lib_LTLIBRARIES += mgmtd/libmgmt_be_nb.la +mgmtd_libmgmt_be_nb_la_SOURCES = \ + zebra/zebra_cli.c \ + # end nodist_mgmtd_libmgmt_be_nb_la_SOURCES = \ # end mgmtd_libmgmt_be_nb_la_CFLAGS = $(AM_CFLAGS) -DINCLUDE_MGMTD_CMDDEFS_ONLY @@ -33,11 +36,6 @@ mgmtd_libmgmtd_a_SOURCES = \ mgmtd/mgmt_vty.c \ # end -mgmtdheaderdir = $(pkgincludedir)/mgmtd -mgmtdheader_HEADERS = \ - mgmtd/mgmt_defines.h \ - # end - noinst_HEADERS += \ mgmtd/mgmt.h \ mgmtd/mgmt_be_adapter.h \ @@ -46,23 +44,64 @@ noinst_HEADERS += \ mgmtd/mgmt_history.h \ mgmtd/mgmt_memory.h \ mgmtd/mgmt_txn.h \ + zebra/zebra_cli.h \ # end sbin_PROGRAMS += mgmtd/mgmtd +if MGMTD_TESTC +sbin_PROGRAMS += mgmtd/mgmtd_testc +mgmtd_mgmtd_testc_SOURCES = mgmtd/mgmt_testc.c +mgmtd_mgmtd_testc_LDADD = lib/libfrr.la +endif + mgmtd_mgmtd_SOURCES = \ mgmtd/mgmt_main.c \ # end 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 mgmtd_mgmtd_CFLAGS = $(AM_CFLAGS) -I ./ mgmtd_mgmtd_LDADD = mgmtd/libmgmtd.a lib/libfrr.la $(LIBCAP) $(LIBM) $(LIBYANG_LIBS) $(UST_LIBS) mgmtd_mgmtd_LDADD += mgmtd/libmgmt_be_nb.la + +if STATICD +nodist_mgmtd_mgmtd_SOURCES += yang/frr-bfdd.yang.c +else +if RIPD +nodist_mgmtd_mgmtd_SOURCES += yang/frr-bfdd.yang.c +endif +endif + +if RIPD +nodist_mgmtd_mgmtd_SOURCES += \ + yang/frr-ripd.yang.c \ + # end +mgmtd_libmgmt_be_nb_la_SOURCES += \ + ripd/rip_cli.c \ + # end +endif + +if RIPNGD +nodist_mgmtd_mgmtd_SOURCES += \ + yang/frr-ripngd.yang.c \ + # end +mgmtd_libmgmt_be_nb_la_SOURCES += \ + ripngd/ripng_cli.c \ + # end +endif + if STATICD nodist_mgmtd_mgmtd_SOURCES += \ yang/frr-staticd.yang.c \ - yang/frr-bfdd.yang.c \ # end -nodist_mgmtd_libmgmt_be_nb_la_SOURCES += staticd/static_vty.c +nodist_mgmtd_libmgmt_be_nb_la_SOURCES += \ + staticd/static_vty.c \ + # end endif + |