diff options
Diffstat (limited to 'mgmtd/mgmt_fe_adapter.c')
-rw-r--r-- | mgmtd/mgmt_fe_adapter.c | 235 |
1 files changed, 198 insertions, 37 deletions
diff --git a/mgmtd/mgmt_fe_adapter.c b/mgmtd/mgmt_fe_adapter.c index 22656f189..96b7cbd59 100644 --- a/mgmtd/mgmt_fe_adapter.c +++ b/mgmtd/mgmt_fe_adapter.c @@ -9,6 +9,7 @@ #include <zebra.h> #include "darr.h" +#include "frrstr.h" #include "sockopt.h" #include "network.h" #include "libfrr.h" @@ -31,6 +32,7 @@ #define FOREACH_ADAPTER_IN_LIST(adapter) \ frr_each_safe (mgmt_fe_adapters, &mgmt_fe_adapters, (adapter)) + enum mgmt_session_event { MGMTD_FE_SESSION_CFG_TXN_CLNUP = 1, MGMTD_FE_SESSION_SHOW_TXN_CLNUP, @@ -55,6 +57,22 @@ DECLARE_LIST(mgmt_fe_sessions, struct mgmt_fe_session_ctx, list_linkage); #define FOREACH_SESSION_IN_LIST(adapter, session) \ frr_each_safe (mgmt_fe_sessions, &(adapter)->fe_sessions, (session)) +/* + * A tree for storing unique notify-select strings. + */ +PREDECL_RBTREE_UNIQ(ns_string); +struct ns_string { + struct ns_string_item link; + struct list *sessions; + char s[]; +}; +static uint32_t ns_string_compare(const struct ns_string *ns1, const struct ns_string *ns2); +DECLARE_RBTREE_UNIQ(ns_string, struct ns_string, link, ns_string_compare); + +/* ---------------- */ +/* Global variables */ +/* ---------------- */ + static struct event_loop *mgmt_loop; static struct msg_server mgmt_fe_server = {.fd = -1}; @@ -63,6 +81,72 @@ static struct mgmt_fe_adapters_head mgmt_fe_adapters; static struct hash *mgmt_fe_sessions; static uint64_t mgmt_fe_next_session_id; +static struct ns_string_head mgmt_fe_ns_strings; + +/* ------------------------------ */ +/* Notify select string functions */ +/* ------------------------------ */ + +static uint32_t ns_string_compare(const struct ns_string *ns1, const struct ns_string *ns2) +{ + return strcmp(ns1->s, ns2->s); +} + +static void mgmt_fe_free_ns_string(struct ns_string *ns) +{ + list_delete(&ns->sessions); + XFREE(MTYPE_MGMTD_XPATH, ns); +} + +static void mgmt_fe_free_ns_strings(struct ns_string_head *head) +{ + struct ns_string *ns; + + while ((ns = ns_string_pop(head))) + mgmt_fe_free_ns_string(ns); + ns_string_fini(head); +} + +static void mgmt_fe_ns_string_remove_session(struct ns_string_head *head, + struct mgmt_fe_session_ctx *session) +{ + struct ns_string *ns; + + frr_each_safe (ns_string, head, ns) { + listnode_delete(ns->sessions, session); + if (list_isempty(ns->sessions)) { + ns_string_del(head, ns); + mgmt_fe_free_ns_string(ns); + } + } +} + +static void mgmt_fe_add_ns_string(struct ns_string_head *head, const char *path, size_t plen, + struct mgmt_fe_session_ctx *session) +{ + struct ns_string *e, *ns; + + ns = XCALLOC(MTYPE_MGMTD_XPATH, sizeof(*ns) + plen + 1); + strlcpy(ns->s, path, plen + 1); + e = ns_string_add(head, ns); + if (!e) + ns->sessions = list_new(); + if (!listnode_lookup(ns->sessions, session)) + listnode_add(ns->sessions, session); +} + +char **mgmt_fe_get_all_selectors(void) +{ + char **selectors = NULL; + struct ns_string *ns; + + frr_each (ns_string, &mgmt_fe_ns_strings, ns) + *darr_append(selectors) = darr_strdup(ns->s); + + return selectors; +} + + /* Forward declarations */ static void mgmt_fe_session_register_event(struct mgmt_fe_session_ctx *session, @@ -190,6 +274,7 @@ static void mgmt_fe_cleanup_session(struct mgmt_fe_session_ctx **sessionp) assert(session->adapter->refcount > 1); mgmt_fe_adapter_unlock(&session->adapter); } + mgmt_fe_ns_string_remove_session(&mgmt_fe_ns_strings, session); darr_free_free(session->notify_xpaths); hash_release(mgmt_fe_sessions, session); XFREE(MTYPE_MGMTD_FE_SESSION, session); @@ -1542,32 +1627,90 @@ static void fe_adapter_handle_edit(struct mgmt_fe_session_ctx *session, * @__msg: the message data. * @msg_len: the length of the message data. */ -static void fe_adapter_handle_notify_select(struct mgmt_fe_session_ctx *session, - void *__msg, size_t msg_len) +static void fe_adapter_handle_notify_select(struct mgmt_fe_session_ctx *session, void *__msg, + size_t msg_len) { struct mgmt_msg_notify_select *msg = __msg; uint64_t req_id = msg->req_id; const char **selectors = NULL; const char **new; + const char **sp; + char *selstr = NULL; + uint64_t clients = 0; + uint ret; if (msg_len >= sizeof(*msg)) { - selectors = mgmt_msg_native_strings_decode(msg, msg_len, - msg->selectors); + selectors = mgmt_msg_native_strings_decode(msg, msg_len, msg->selectors); if (!selectors) { - fe_adapter_send_error(session, req_id, false, -EINVAL, - "Invalid message"); + fe_adapter_send_error(session, req_id, false, -EINVAL, "Invalid message"); return; } } + if (DEBUG_MODE_CHECK(&mgmt_debug_fe, DEBUG_MODE_ALL)) { + selstr = frrstr_join(selectors, darr_len(selectors), ", "); + if (!selstr) + selstr = XSTRDUP(MTYPE_TMP, ""); + } + if (msg->replace) { + mgmt_fe_ns_string_remove_session(&mgmt_fe_ns_strings, session); + // [ ] Keep a local tree to optimize sending selectors to BE? + // [*] Or just KISS and fanout the original message to BEs? + // mgmt_remove_add_notify_selectors(session->notify_xpaths, selectors); darr_free_free(session->notify_xpaths); session->notify_xpaths = selectors; } else if (selectors) { - new = darr_append_nz(session->notify_xpaths, - darr_len(selectors)); + // [ ] Keep a local tree to optimize sending selectors to BE? + // [*] Or just KISS and fanout the original message to BEs? + // mgmt_remove_add_notify_selectors(session->notify_xpaths, selectors); + new = darr_append_nz(session->notify_xpaths, darr_len(selectors)); memcpy(new, selectors, darr_len(selectors) * sizeof(*selectors)); - darr_free(selectors); + } else { + __log_err("Invalid msg from session-id: %Lu: no selectors present in non-replace msg", + session->session_id); + darr_free_free(selectors); + selectors = NULL; + goto done; } + + + if (session->notify_xpaths && DEBUG_MODE_CHECK(&mgmt_debug_fe, DEBUG_MODE_ALL)) { + const char **sel = session->notify_xpaths; + char *s = frrstr_join(sel, darr_len(sel), ", "); + __dbg("New NOTIF %d selectors '%s' (replace: %d) txn-id: %Lu for session-id: %Lu", + darr_len(sel), s, msg->replace, session->cfg_txn_id, session->session_id); + XFREE(MTYPE_TMP, s); + } + + /* Add the new selectors to the global tree */ + darr_foreach_p (selectors, sp) + mgmt_fe_add_ns_string(&mgmt_fe_ns_strings, *sp, darr_strlen(*sp), session); + + /* Check if any backends are interested in the new selectors. */ + if (msg->replace) { + /* If we are replacing we'll send all the selectors again with replace flag */ + clients = mgmt_be_interested_clients("/", MGMT_BE_XPATH_SUBSCR_TYPE_OPER); + } else { + darr_foreach_p (selectors, sp) + clients |= mgmt_be_interested_clients(*sp, MGMT_BE_XPATH_SUBSCR_TYPE_OPER); + } + if (!clients) { + __dbg("No backends provide oper for notify selectors: '%s' txn-id %Lu session-id: %Lu", + selstr, session->txn_id, session->session_id); + goto done; + } + + /* We don't use a transaction for this, just send the message */ + ret = mgmt_txn_send_notify_selectors(req_id, clients, msg->replace ? NULL : selectors); + if (ret) { + fe_adapter_send_error(session, req_id, false, -EINPROGRESS, + "Failed to create a NOTIFY_SELECT transaction"); + } +done: + if (session->notify_xpaths != selectors) + darr_free(selectors); + if (selstr) + XFREE(MTYPE_TMP, selstr); } /** @@ -1758,10 +1901,11 @@ 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; - struct nb_node *nb_node; - const char **xpath_prefix; + struct nb_node *nb_node = NULL; + struct listnode *node; + struct ns_string *ns; const char *notif; - bool sendit; + bool is_root; uint len; assert(msg->refer_id == 0); @@ -1772,36 +1916,48 @@ void mgmt_fe_adapter_send_notify(struct mgmt_msg_notify_data *msg, size_t msglen return; } - /* - * We need the nb_node to obtain a path which does not include any - * specific list entry selectors - */ - nb_node = nb_node_find(notif); - if (!nb_node) { - __log_err("No schema found for notification: %s", notif); - return; + is_root = !strcmp(notif, "/"); + if (!is_root) { + /* + * We need the nb_node to obtain a path which does not include any + * specific list entry selectors + */ + nb_node = nb_node_find(notif); + if (!nb_node) { + __log_err("No schema found for notification: %s", notif); + return; + } } - FOREACH_ADAPTER_IN_LIST (adapter) { - FOREACH_SESSION_IN_LIST (adapter, session) { - /* If no selectors then always send */ - sendit = !session->notify_xpaths; - darr_foreach_p (session->notify_xpaths, xpath_prefix) { - len = strlen(*xpath_prefix); - if (!strncmp(*xpath_prefix, notif, len) || - !strncmp(*xpath_prefix, nb_node->xpath, - len)) { - sendit = true; - break; - } - } - if (sendit) { + frr_each (ns_string, &mgmt_fe_ns_strings, ns) { + if (!is_root) { + len = strlen(ns->s); + if (strncmp(ns->s, notif, len) && strncmp(ns->s, nb_node->xpath, len)) + continue; + } + for (ALL_LIST_ELEMENTS_RO(ns->sessions, node, session)) { + msg->refer_id = session->session_id; + (void)fe_adapter_send_native_msg(session->adapter, msg, msglen, false); + } + } + + /* + * Send all YANG defined notifications to all sesisons with *no* + * selectors as well (i.e., original NETCONF/RESTCONF notification + * scheme). + */ + if (!is_root && CHECK_FLAG(nb_node->snode->nodetype, LYS_NOTIF)) { + FOREACH_ADAPTER_IN_LIST (adapter) { + FOREACH_SESSION_IN_LIST (adapter, session) { + if (session->notify_xpaths) + continue; msg->refer_id = session->session_id; (void)fe_adapter_send_native_msg(adapter, msg, msglen, false); } } } + msg->refer_id = 0; } @@ -1810,9 +1966,10 @@ void mgmt_fe_adapter_lock(struct mgmt_fe_client_adapter *adapter) adapter->refcount++; } -extern void mgmt_fe_adapter_unlock(struct mgmt_fe_client_adapter **adapter) +void mgmt_fe_adapter_unlock(struct mgmt_fe_client_adapter **adapter) { struct mgmt_fe_client_adapter *a = *adapter; + assert(a && a->refcount); if (!--a->refcount) { @@ -1840,6 +1997,8 @@ void mgmt_fe_adapter_init(struct event_loop *tm) hash_create(mgmt_fe_session_hash_key, mgmt_fe_session_hash_cmp, "MGMT Frontend Sessions"); + ns_string_init(&mgmt_fe_ns_strings); + snprintf(server_path, sizeof(server_path), MGMTD_FE_SOCK_NAME); if (msg_server_init(&mgmt_fe_server, server_path, tm, @@ -1869,10 +2028,13 @@ void mgmt_fe_adapter_destroy(void) msg_server_cleanup(&mgmt_fe_server); + /* Deleting the adapters will delete all the sessions */ FOREACH_ADAPTER_IN_LIST (adapter) mgmt_fe_adapter_delete(adapter); + mgmt_fe_free_ns_strings(&mgmt_fe_ns_strings); + hash_clean_and_free(&mgmt_fe_sessions, mgmt_fe_abort_if_session); } @@ -1885,8 +2047,7 @@ struct msg_conn *mgmt_fe_create_adapter(int conn_fd, union sockunion *from) adapter = mgmt_fe_find_adapter_by_fd(conn_fd); if (!adapter) { - adapter = XCALLOC(MTYPE_MGMTD_FE_ADPATER, - sizeof(struct mgmt_fe_client_adapter)); + adapter = XCALLOC(MTYPE_MGMTD_FE_ADPATER, sizeof(struct mgmt_fe_client_adapter)); snprintf(adapter->name, sizeof(adapter->name), "Unknown-FD-%d", conn_fd); |