]> git.puffer.fish Git - mirror/frr.git/commitdiff
mgmtd: step 4: FE adapter get-tree functionality
authorChristian Hopps <chopps@labn.net>
Fri, 7 Jul 2023 05:01:57 +0000 (01:01 -0400)
committerChristian Hopps <chopps@labn.net>
Thu, 28 Dec 2023 10:41:54 +0000 (10:41 +0000)
Signed-off-by: Christian Hopps <chopps@labn.net>
lib/mgmt_fe_client.h
lib/vty.c
mgmtd/mgmt_fe_adapter.c
mgmtd/mgmt_fe_adapter.h

index 32d2920ff944eca8372deaa975e72c0ab15be91e..3a1a1e5705784df9263259b41c4ee2a35fba788e 100644 (file)
@@ -119,7 +119,7 @@ struct mgmt_fe_client_cbs {
        /* Called when get-tree result is returned */
        int (*get_tree_notify)(struct mgmt_fe_client *client,
                               uintptr_t user_data, uint64_t client_id,
-                              uint64_t session_id, uint64_t session_ctx,
+                              uint64_t session_id, uintptr_t session_ctx,
                               uint64_t req_id, Mgmtd__DatastoreId ds_id,
                               LYD_FORMAT result_type, void *result, size_t len,
                               int partial_error);
index afd00d1da5473bf94f08f4504ce9deeba58c61c9..3c80403cce7bb5d41d0b1e634491ab8b200c51ef 100644 (file)
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -3859,7 +3859,7 @@ static uint vty_out_yang_errors(struct vty *vty, LYD_FORMAT format)
 
 static int vty_mgmt_get_tree_result_notified(
        struct mgmt_fe_client *client, uintptr_t user_data, uint64_t client_id,
-       uint64_t session_id, uint64_t session_ctx, uint64_t req_id,
+       uint64_t session_id, uintptr_t session_ctx, uint64_t req_id,
        Mgmtd__DatastoreId ds_id, LYD_FORMAT result_type, void *result,
        size_t len, int partial_error)
 {
index d613b7467a7ede8ef6e20958911efc083435064b..9c7f0706682d0a7fc6eeddcb16cffa50b300018b 100644 (file)
@@ -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"
@@ -254,6 +256,19 @@ void mgmt_fe_adapter_toggle_client_debug(bool set)
                adapter->conn->debug = set;
 }
 
+static struct mgmt_fe_session_ctx *fe_adapter_session_by_txn_id(uint64_t txn_id)
+{
+#if 0 /* allow commit to compile */
+       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);
+#else
+       return NULL;
+#endif
+}
+
 static struct mgmt_fe_session_ctx *
 mgmt_fe_create_session(struct mgmt_fe_client_adapter *adapter,
                           uint64_t client_id)
@@ -281,6 +296,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)
 {
@@ -478,6 +501,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;
@@ -1028,12 +1073,190 @@ 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,
+                                    const struct lyd_node *tree,
+                                    int partial_error)
+
+{
+       struct mgmt_msg_tree_data *msg;
+       struct lyd_node *empty = NULL;
+       uint8_t *buf = NULL;
+       int ret = 0;
+
+       darr_append_n(buf, offsetof(typeof(*msg), result));
+       msg = (typeof(msg))buf;
+       msg->session_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;
+
+       if (!tree) {
+               empty = yang_dnode_new(ly_native_ctx, false);
+               tree = empty;
+       }
+
+       ret = yang_print_tree_append(&buf, tree, result_type,
+                                    (LYD_PRINT_WD_EXPLICIT |
+                                     LYD_PRINT_WITHSIBLINGS));
+       /* buf may have been reallocated and moved */
+       msg = (typeof(msg))buf;
+
+
+       if (ret != LY_SUCCESS) {
+               MGMTD_FE_ADAPTER_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;
+       }
+
+       MGMTD_FE_ADAPTER_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, darr_len(buf));
+
+       ret = fe_adapter_send_native_msg(session->adapter, buf, darr_len(buf),
+                                        short_circuit_ok);
+done:
+       if (empty)
+               yang_dnode_free(empty);
+       darr_free(buf);
+
+       return ret;
+}
+
+/**
+ * Handle a get-tree message from the client.
+ */
+static void fe_adapter_handle_get_tree(struct mgmt_fe_session_ctx *session,
+                                      void *data, size_t len)
+{
+       struct mgmt_msg_get_tree *msg = data;
+       uint64_t req_id = msg->req_id;
+       uint64_t clients;
+       int ret;
+
+       MGMTD_FE_ADAPTER_DBG("Received get-tree request from client %s for session-id %" PRIu64
+                            " req-id %" PRIu64,
+                            session->adapter->name, session->session_id,
+                            msg->req_id);
+
+       if (session->txn_id != MGMTD_TXN_ID_NONE) {
+               fe_adapter_send_error(session, req_id, false, -EINPROGRESS,
+                                     "Transaction in progress txn-id: %" PRIu64
+                                     " for session-id: %" PRIu64,
+                                     session->txn_id, session->session_id);
+               return;
+       }
+
+       clients = mgmt_be_interested_clients(msg->xpath, false);
+       if (!clients) {
+               MGMTD_FE_ADAPTER_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, NULL, 0);
+               return;
+       }
+
+       /* 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");
+               return;
+       }
+
+       MGMTD_FE_ADAPTER_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 */
+#if 0 /* allow commit to compile */
+       ret = mgmt_txn_send_get_tree_oper(session->txn_id, req_id, clients,
+                                         msg->result_type, msg->xpath);
+#else
+       ret = NB_OK;
+#endif
+       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");
+       }
+}
+
+/**
+ * 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->session_id);
+       if (!session) {
+               MGMTD_FE_ADAPTER_ERR("adapter %s: recv msg unknown session-id %" PRIu64,
+                                    adapter->name, msg->session_id);
+               return;
+       }
+       assert(session->adapter == adapter);
+
+       switch (msg->code) {
+       case MGMT_MSG_CODE_GET_TREE:
+               fe_adapter_handle_get_tree(session, msg, msg_len);
+               break;
+       default:
+               MGMTD_FE_ADAPTER_ERR("unknown native message session-id %" PRIu64
+                                    " req-id %" PRIu64
+                                    " code %u to FE adapter %s",
+                                    msg->session_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
+                       MGMTD_FE_ADAPTER_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,
@@ -1209,8 +1432,54 @@ 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,
+                                  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, 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) {
+               MGMTD_FE_ADAPTER_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;
 
index 1172262a45ae9e61a8703bba118b3ac6ced43090..09d64415bcbb0f6e0595033ce4707681ddad3dd5 100644 (file)
@@ -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.
+ *     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,
+                                         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);