diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:16:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:16:35 +0000 |
commit | e2bbf175a2184bd76f6c54ccf8456babeb1a46fc (patch) | |
tree | f0b76550d6e6f500ada964a3a4ee933a45e5a6f1 /mgmtd/mgmt_be_adapter.c | |
parent | Initial commit. (diff) | |
download | frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.tar.xz frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.zip |
Adding upstream version 9.1.upstream/9.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'mgmtd/mgmt_be_adapter.c')
-rw-r--r-- | mgmtd/mgmt_be_adapter.c | 935 |
1 files changed, 935 insertions, 0 deletions
diff --git a/mgmtd/mgmt_be_adapter.c b/mgmtd/mgmt_be_adapter.c new file mode 100644 index 0000000..399fdaf --- /dev/null +++ b/mgmtd/mgmt_be_adapter.c @@ -0,0 +1,935 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * MGMTD Backend Client Connection Adapter + * + * Copyright (C) 2021 Vmware, Inc. + * Pushpasis Sarkar <spushpasis@vmware.com> + * Copyright (c) 2023, LabN Consulting, L.L.C. + */ + +#include <zebra.h> +#include "darr.h" +#include "frrevent.h" +#include "sockopt.h" +#include "network.h" +#include "libfrr.h" +#include "mgmt_msg.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, ...) \ + DEBUGD(&mgmt_debug_be, "BE-ADAPTER: %s: " fmt, __func__, ##__VA_ARGS__) +#define MGMTD_BE_ADAPTER_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)) + +/* + * Mapping of YANG XPath regular expressions to + * their corresponding backend clients. + */ +struct mgmt_be_xpath_map { + char *xpath_regexp; + uint subscr_info[MGMTD_BE_CLIENT_ID_MAX]; +}; + +struct mgmt_be_client_xpath { + const char *xpath; + uint subscribed; +}; + +struct mgmt_be_client_xpath_map { + struct mgmt_be_client_xpath *xpaths; + uint nxpaths; +}; + +struct mgmt_be_get_adapter_config_params { + struct mgmt_be_client_adapter *adapter; + struct nb_config_cbs *cfg_chgs; + uint32_t seq; +}; + +/* + * 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, + }, +}; +#endif + +static struct mgmt_be_client_xpath_map + mgmt_client_xpaths[MGMTD_BE_CLIENT_ID_MAX] = { +#ifdef HAVE_STATICD + [MGMTD_BE_CLIENT_ID_STATICD] = {staticd_xpaths, + array_size(staticd_xpaths)}, +#endif +}; + +/* + * We would like to have a better ADT than one with O(n) comparisons + * + * Perhaps it's possible to sort this array in a way that allows binary search + * to find the start, then walk until no possible match can follow? Intuition + * 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 event_loop *mgmt_loop; +static struct msg_server mgmt_be_server = {.fd = -1}; + +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 struct mgmt_be_client_adapter * +mgmt_be_find_adapter_by_fd(int conn_fd) +{ + struct mgmt_be_client_adapter *adapter; + + FOREACH_ADAPTER_IN_LIST (adapter) { + if (adapter->conn->fd == conn_fd) + return adapter; + } + + return NULL; +} + +static struct mgmt_be_client_adapter * +mgmt_be_find_adapter_by_name(const char *name) +{ + struct mgmt_be_client_adapter *adapter; + + FOREACH_ADAPTER_IN_LIST (adapter) { + if (!strncmp(adapter->name, name, sizeof(adapter->name))) + return adapter; + } + + return NULL; +} + +static void mgmt_register_client_xpath(enum mgmt_be_client_id id, + const char *xpath, uint subscribed) +{ + struct mgmt_be_xpath_map *map; + + darr_foreach_p (mgmt_xpath_map, map) + if (!strcmp(xpath, map->xpath_regexp)) { + map->subscr_info[id] = subscribed; + 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; +} + +/* + * Load the initial mapping from static init map + */ +static void mgmt_be_xpath_map_init(void) +{ + struct mgmt_be_client_xpath *init, *end; + enum mgmt_be_client_id id; + + MGMTD_BE_ADAPTER_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); + } + } + + MGMTD_BE_ADAPTER_DBG("Total XPath Maps: %u", darr_len(mgmt_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; + + 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--; + + 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]; + } + } + } + + /* + * 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 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; + } + + /* + * Proceed to the next character in the RE/XP string as + * necessary. + */ + if (!re_wild) + re_indx++; + if (!xp_wild) + xp_indx++; + } + + /* + * 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; +} + +static void mgmt_be_adapter_delete(struct mgmt_be_client_adapter *adapter) +{ + MGMTD_BE_ADAPTER_DBG("deleting client adapter '%s'", adapter->name); + + /* + * Notify about disconnect for appropriate cleanup + */ + mgmt_txn_notify_be_adapter_conn(adapter, false); + if (adapter->id < MGMTD_BE_CLIENT_ID_MAX) { + mgmt_be_adapters_by_id[adapter->id] = NULL; + adapter->id = MGMTD_BE_CLIENT_ID_MAX; + } + + assert(adapter->refcount == 1); + mgmt_be_adapter_unlock(&adapter); +} + +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); + + mgmt_be_adapter_delete(adapter); + + return 0; +} + +static void +mgmt_be_adapter_cleanup_old_conn(struct mgmt_be_client_adapter *adapter) +{ + struct mgmt_be_client_adapter *old; + + FOREACH_ADAPTER_IN_LIST (old) { + if (old != adapter && + !strncmp(adapter->name, old->name, sizeof(adapter->name))) { + /* + * 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); + /* 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) +{ + return msg_conn_send_msg( + adapter->conn, MGMT_MSG_VERSION_PROTOBUF, be_msg, + mgmtd__be_message__get_packed_size(be_msg), + (size_t(*)(void *, void *))mgmtd__be_message__pack, false); +} + +static int mgmt_be_send_subscr_reply(struct mgmt_be_client_adapter *adapter, + bool success) +{ + Mgmtd__BeMessage be_msg; + Mgmtd__BeSubscribeReply reply; + + mgmtd__be_subscribe_reply__init(&reply); + reply.success = success; + + mgmtd__be_message__init(&be_msg); + 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); + + return mgmt_be_adapter_send_msg(adapter, &be_msg); +} + +static int +mgmt_be_adapter_handle_msg(struct mgmt_be_client_adapter *adapter, + Mgmtd__BeMessage *be_msg) +{ + /* + * 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); + + 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); + /* 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; + mgmt_be_adapter_cleanup_old_conn(adapter); + + /* schedule INIT sequence now that it is registered */ + 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); + 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"); + /* + * Forward the TXN_REPLY to txn module. + */ + mgmt_txn_notify_be_txn_reply( + be_msg->txn_reply->txn_id, + be_msg->txn_reply->create, + 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"); + /* + * 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"); + /* + * 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: + case MGMTD__BE_MESSAGE__MESSAGE__NOT_SET: + default: + /* + * A 'default' case is being added contrary to the + * FRR code guidelines to take care of build + * failures on certain build systems (courtesy of + * the proto-c package). + */ + break; + } + + return 0; +} + +int mgmt_be_send_txn_req(struct mgmt_be_client_adapter *adapter, + uint64_t txn_id, bool create) +{ + Mgmtd__BeMessage be_msg; + Mgmtd__BeTxnReq txn_req; + + mgmtd__be_txn_req__init(&txn_req); + txn_req.create = create; + txn_req.txn_id = txn_id; + + mgmtd__be_message__init(&be_msg); + 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); + + 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, + Mgmtd__YangCfgDataReq **cfgdata_reqs, + size_t num_reqs, bool end_of_data) +{ + Mgmtd__BeMessage be_msg; + 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; + cfgdata_req.end_of_data = end_of_data; + + mgmtd__be_message__init(&be_msg); + 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); + + return mgmt_be_adapter_send_msg(adapter, &be_msg); +} + +int mgmt_be_send_cfgapply_req(struct mgmt_be_client_adapter *adapter, + uint64_t txn_id) +{ + Mgmtd__BeMessage be_msg; + Mgmtd__BeCfgDataApplyReq apply_req; + + mgmtd__be_cfg_data_apply_req__init(&apply_req); + apply_req.txn_id = txn_id; + + mgmtd__be_message__init(&be_msg); + 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); + + return mgmt_be_adapter_send_msg(adapter, &be_msg); +} + +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); + + if (!be_msg) { + MGMTD_BE_ADAPTER_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); + (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); +} + +/* + * Initialize a BE client over a new connection + */ +static void mgmt_be_adapter_conn_init(struct event *thread) +{ + struct mgmt_be_client_adapter *adapter; + + adapter = (struct mgmt_be_client_adapter *)EVENT_ARG(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. + */ + 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; + } +} + +/* + * Schedule the initialization of the BE client connection. + */ +static void +mgmt_be_adapter_sched_init_event(struct mgmt_be_client_adapter *adapter) +{ + event_add_timer_msec(mgmt_loop, mgmt_be_adapter_conn_init, adapter, + MGMTD_BE_CONN_INIT_DELAY_MSEC, + &adapter->conn_init_ev); +} + +void mgmt_be_adapter_lock(struct mgmt_be_client_adapter *adapter) +{ + adapter->refcount++; +} + +extern void mgmt_be_adapter_unlock(struct mgmt_be_client_adapter **adapter) +{ + struct mgmt_be_client_adapter *a = *adapter; + assert(a && a->refcount); + + if (!--a->refcount) { + mgmt_be_adapters_del(&mgmt_be_adapters, a); + EVENT_OFF(a->conn_init_ev); + msg_server_conn_delete(a->conn); + XFREE(MTYPE_MGMTD_BE_ADPATER, a); + } + + *adapter = NULL; +} + +/* + * Initialize the BE adapter module + */ +void mgmt_be_adapter_init(struct event_loop *tm) +{ + 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)) { + zlog_err("cannot initialize backend server"); + exit(1); + } +} + +/* + * Destroy the BE adapter module + */ +void mgmt_be_adapter_destroy(void) +{ + struct mgmt_be_client_adapter *adapter; + + msg_server_cleanup(&mgmt_be_server); + FOREACH_ADAPTER_IN_LIST (adapter) { + mgmt_be_adapter_delete(adapter); + } + mgmt_be_xpath_map_cleanup(); +} + +/* + * The server accepted a new connection + */ +struct msg_conn *mgmt_be_create_adapter(int conn_fd, union sockunion *from) +{ + struct mgmt_be_client_adapter *adapter = NULL; + + assert(!mgmt_be_find_adapter_by_fd(conn_fd)); + + adapter = XCALLOC(MTYPE_MGMTD_BE_ADPATER, + sizeof(struct mgmt_be_client_adapter)); + adapter->id = MGMTD_BE_CLIENT_ID_MAX; + snprintf(adapter->name, sizeof(adapter->name), "Unknown-FD-%d", + conn_fd); + + mgmt_be_adapter_lock(adapter); + 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"); + + MGMTD_BE_ADAPTER_DBG("Added new MGMTD Backend adapter '%s'", + adapter->name); + + return adapter->conn; +} + +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); +} + +struct mgmt_be_client_adapter * +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) +{ + struct mgmt_be_get_adapter_config_params parms; + struct nb_config *cfg_root = mgmt_ds_get_nb_config(mm->running_ds); + + assert(cfg_chgs); + + /* + * 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; + + mgmt_ds_iter_data(MGMTD_DS_RUNNING, cfg_root, "", + mgmt_be_iter_and_get_cfg, (void *)&parms); + } + + *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) +{ + struct mgmt_be_xpath_map *map; + enum mgmt_be_client_id id; + + memset(subscr_info, 0, sizeof(*subscr_info)); + + 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]; + } + } + + 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]); + } + } +} + +/** + * Return the subscription info bits for a given `xpath` for a given + * `client_id`. + * + * Args: + * xpath - the xpath to check for subscription information. + * client_id - the BE client being checked for. + * subscr_mask - The subscr bits the caller is interested in seeing + * if set. + * + * Returns: + * The subscription info bits. + */ +static uint mgmt_be_get_subscr_for_xpath_and_client( + const char *xpath, enum mgmt_be_client_id client_id, uint subscr_mask) +{ + struct mgmt_be_client_xpath_map *map; + uint subscr = 0; + uint i; + + assert(client_id < MGMTD_BE_CLIENT_ID_MAX); + + MGMTD_BE_ADAPTER_DBG("Checking client: %s for xpath: '%s'", + mgmt_be_client_id2name(client_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; + } + MGMTD_BE_ADAPTER_DBG("client: %s: subscribed: 0x%x", + mgmt_be_client_id2name(client_id), subscr); + return subscr; +} + +void mgmt_be_adapter_status_write(struct vty *vty) +{ + struct mgmt_be_client_adapter *adapter; + + vty_out(vty, "MGMTD Backend Adapters\n"); + + FOREACH_ADAPTER_IN_LIST (adapter) { + vty_out(vty, " Client: \t\t\t%s\n", adapter->name); + vty_out(vty, " Conn-FD: \t\t\t%d\n", adapter->conn->fd); + vty_out(vty, " Client-Id: \t\t\t%d\n", adapter->id); + vty_out(vty, " Ref-Count: \t\t\t%u\n", adapter->refcount); + vty_out(vty, " Msg-Recvd: \t\t\t%" PRIu64 "\n", + adapter->conn->mstate.nrxm); + vty_out(vty, " Bytes-Recvd: \t\t%" PRIu64 "\n", + adapter->conn->mstate.nrxb); + vty_out(vty, " Msg-Sent: \t\t\t%" PRIu64 "\n", + adapter->conn->mstate.ntxm); + vty_out(vty, " Bytes-Sent: \t\t%" PRIu64 "\n", + adapter->conn->mstate.ntxb); + } + vty_out(vty, " Total: %d\n", + (int)mgmt_be_adapters_count(&mgmt_be_adapters)); +} + +void mgmt_be_xpath_register_write(struct vty *vty) +{ + 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"); + + 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, "Total XPath Registries: %u\n", darr_len(mgmt_xpath_map)); +} + +void mgmt_be_xpath_subscr_info_write(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; + + mgmt_be_get_subscr_info_for_xpath(xpath, &subscr); + + 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); + adapter = mgmt_be_get_adapter_by_id(id); + if (adapter) + vty_out(vty, " -- Adapter: %p\n", adapter); + } +} |