]> git.puffer.fish Git - mirror/frr.git/commitdiff
mgmtd: step 5: add get-tree txn functionality
authorChristian Hopps <chopps@labn.net>
Fri, 7 Jul 2023 03:23:24 +0000 (23:23 -0400)
committerChristian Hopps <chopps@labn.net>
Thu, 28 Dec 2023 10:41:54 +0000 (10:41 +0000)
Adds the guts of the get-tree functionality that is called by or calls
the FE and BE code for get-tree processing.

Signed-off-by: Christian Hopps <chopps@labn.net>
mgmtd/mgmt_be_adapter.c
mgmtd/mgmt_be_adapter.h
mgmtd/mgmt_fe_adapter.c
mgmtd/mgmt_memory.c
mgmtd/mgmt_memory.h
mgmtd/mgmt_txn.c
mgmtd/mgmt_txn.h

index cf6c84879363a7df8177892f9368e7a224c3b975..6b2f9026db2b81790d438aa10b63abadbc40e281 100644 (file)
@@ -103,8 +103,7 @@ mgmt_be_adapter_sched_init_event(struct mgmt_be_client_adapter *adapter);
 static bool be_is_client_interested(const char *xpath,
                                    enum mgmt_be_client_id id, bool config);
 
-
-static const char *mgmt_be_client_id2name(enum mgmt_be_client_id id)
+const char *mgmt_be_client_id2name(enum mgmt_be_client_id id)
 {
        if (id > MGMTD_BE_CLIENT_ID_MAX)
                return "invalid client id";
@@ -528,10 +527,37 @@ 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_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;
+               MGMTD_BE_ADAPTER_DBG("Got ERROR from '%s' txn-id %" PRIx64,
+                                    adapter->name, msg->txn_id);
+
+               /* Forward the reply to the txn module */
+               mgmt_txn_notify_error(adapter, msg->txn_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;
+               MGMTD_BE_ADAPTER_DBG("Got TREE_DATA from '%s' txn-id %" PRIx64,
+                                    adapter->name, msg->txn_id);
+
+               /* Forward the reply to the txn module */
+               mgmt_txn_notify_tree_data_reply(adapter, tree_msg, msg_len);
+               break;
        default:
-               MGMTD_BE_ADAPTER_ERR("unknown native message code %u to BE adapter %s",
-                                    msg->code, adapter->name);
+               MGMTD_BE_ADAPTER_ERR("unknown native message txn-id %" PRIu64
+                                    " req-id %" PRIu64
+                                    " code %u from BE client for adapter %s",
+                                    msg->txn_id, msg->req_id, msg->code,
+                                    adapter->name);
                break;
        }
 }
index 9f62f87d6da460dcb7592da3c306af93a8188053..b8abd016e6a28cf53d81932780a7820a386d7054 100644 (file)
@@ -149,6 +149,9 @@ 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);
 
index 9c7f0706682d0a7fc6eeddcb16cffa50b300018b..11262df863706aa3b186af062959355730dccc7d 100644 (file)
@@ -258,15 +258,11 @@ void mgmt_fe_adapter_toggle_client_debug(bool 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 *
@@ -1193,12 +1189,8 @@ static void fe_adapter_handle_get_tree(struct mgmt_fe_session_ctx *session,
                             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);
index b2a0f0e848c092721fbadb160121f44738ba0155..0fce61aa97923c35e1b471e1fbd19b521ec2b4b2 100644 (file)
@@ -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");
index 06518e38384f1c949ae0df9295f8d117113e420a..d5b6aa632ea3a31e6c5a980d7dea6d7b6d96d345 100644 (file)
@@ -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);
index c2dca2aea18d3101e32ad42129cb39a866c4f1d3..af782f1dc30342663d30e06580ca9b2316d69018 100644 (file)
@@ -7,9 +7,12 @@
  */
 
 #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"
@@ -27,7 +30,9 @@ enum mgmt_txn_event {
        MGMTD_TXN_PROC_COMMITCFG,
        MGMTD_TXN_PROC_GETCFG,
        MGMTD_TXN_PROC_GETDATA,
+       MGMTD_TXN_PROC_GETTREE,
        MGMTD_TXN_COMMITCFG_TIMEOUT,
+       MGMTD_TXN_GETTREE_TIMEOUT,
        MGMTD_TXN_CLEANUP
 };
 
@@ -166,6 +171,16 @@ struct mgmt_get_data_req {
        int total_reply;
 };
 
+
+struct txn_req_get_tree {
+       char *xpath;           /* xpath of tree to get */
+       uint8_t result_type;   /* LYD_FORMAT for results */
+       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 */
+       struct lyd_node *client_results; /* result tree from clients */
+};
+
 struct mgmt_txn_req {
        struct mgmt_txn_ctx *txn;
        enum mgmt_txn_event req_event;
@@ -173,6 +188,7 @@ 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;
 
@@ -196,7 +212,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 */
@@ -206,6 +224,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
@@ -228,6 +250,10 @@ struct mgmt_txn_ctx {
         */
        struct mgmt_txn_reqs_head get_data_reqs;
        struct mgmt_txn_reqs_head pending_get_datas;
+       /*
+        * List of pending get-tree requests.
+        */
+       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.
@@ -396,7 +422,16 @@ static struct mgmt_txn_req *mgmt_txn_req_alloc(struct mgmt_txn_ctx *txn,
                              " txn-id: %" PRIu64 " session-id: %" PRIu64,
                              txn_req->req_id, txn->txn_id, txn->session_id);
                break;
+       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);
+               MGMTD_TXN_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_GETTREE_TIMEOUT:
        case MGMTD_TXN_CLEANUP:
                break;
        }
@@ -513,7 +548,17 @@ static void mgmt_txn_req_free(struct mgmt_txn_req **txn_req)
                              (*txn_req)->req.get_data->reply);
                XFREE(MTYPE_MGMTD_TXN_GETDATA_REQ, (*txn_req)->req.get_data);
                break;
+       case MGMTD_TXN_PROC_GETTREE:
+               MGMTD_TXN_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_GETTREE_TIMEOUT:
        case MGMTD_TXN_CLEANUP:
                break;
        }
@@ -1260,6 +1305,66 @@ 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;
+       int ret = 0;
+
+       /* cancel timer and send reply onward */
+       EVENT_OFF(txn->get_tree_timeout);
+
+       ret = mgmt_fe_adapter_send_tree_data(txn->session_id, txn->txn_id,
+                                            txn_req->req_id,
+                                            get_tree->result_type,
+                                            get_tree->client_results,
+                                            get_tree->partial_error, false);
+
+       /* we're done with the request */
+       mgmt_txn_req_free(&txn_req);
+
+       if (ret) {
+               MGMTD_TXN_ERR("Error saving the results of GETTREE for txn-id %" PRIu64
+                             " req_id %" PRIu64 " to requested type %u",
+                             txn->txn_id, txn_req->req_id,
+                             get_tree->result_type);
+
+               (void)mgmt_fe_adapter_txn_error(txn->txn_id, txn_req->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);
+
+
+       MGMTD_TXN_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.
  *
@@ -1488,6 +1593,8 @@ static void mgmt_txn_send_getcfg_reply_data(struct mgmt_txn_req *txn_req,
                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);
@@ -1500,10 +1607,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;
@@ -1581,7 +1686,7 @@ 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]);
@@ -1733,7 +1838,7 @@ static struct mgmt_txn_ctx *mgmt_txn_create_new(uint64_t session_id,
 
        /*
         * For 'CONFIG' transaction check if one is already created
-        * or not.
+        * or not. TODO: figure out what code counts on this and fix it.
         */
        if (type == MGMTD_TXN_TYPE_CONFIG && mgmt_txn_mm->cfg_txn) {
                if (mgmt_config_txn_in_progress() == session_id)
@@ -1749,10 +1854,12 @@ 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)
@@ -1834,6 +1941,13 @@ 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++;
@@ -1859,6 +1973,7 @@ 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);
 
@@ -1927,14 +2042,23 @@ static void mgmt_txn_register_event(struct mgmt_txn_ctx *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_CLEANUP:
                tv.tv_usec = MGMTD_TXN_CLEANUP_DELAY_USEC;
                event_add_timer_tv(mgmt_txn_tm, mgmt_txn_cleanup, txn, &tv,
                                   &txn->clnup);
+               break;
+       case MGMTD_TXN_PROC_GETTREE:
+               assert(!"code bug do not register this event");
+               break;
        }
 }
 
@@ -2333,6 +2457,202 @@ 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, LYD_FORMAT result_type,
+                               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;
+       size_t mlen = sizeof(*msg) + strlen(xpath) + 1;
+       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->xpath = XSTRDUP(MTYPE_MGMTD_XPATH, xpath);
+
+       msg = XCALLOC(MTYPE_MSG_NATIVE_MSG, mlen);
+       msg->txn_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, mlen - sizeof(*msg));
+
+       assert(clients);
+       FOREACH_BE_CLIENT_BITS (id, clients) {
+               ret = mgmt_be_send_native(id, msg, mlen);
+               if (ret) {
+                       MGMTD_TXN_ERR("Could not send get-tree message to backend client %s",
+                                     mgmt_be_client_id2name(id));
+                       continue;
+               }
+
+               MGMTD_TXN_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);
+       }
+
+       XFREE(MTYPE_MSG_NATIVE_MSG, msg);
+
+       /* 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) {
+               MGMTD_TXN_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) {
+               MGMTD_TXN_ERR("Error reply from %s for txn-id %" PRIu64
+                             " cannot find req_id %" PRIu64,
+                             adapter->name, txn_id, req_id);
+               return -1;
+       }
+
+       MGMTD_TXN_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_PROC_GETDATA:
+       case MGMTD_TXN_COMMITCFG_TIMEOUT:
+       case MGMTD_TXN_GETTREE_TIMEOUT:
+       case MGMTD_TXN_CLEANUP:
+       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->txn_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) {
+               MGMTD_TXN_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) {
+               MGMTD_TXN_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) {
+               MGMTD_TXN_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) {
+                       MGMTD_TXN_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);
+
+       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;
index a89d5fb9393fd45be9fca3e0700f8ce80e768678..4aa067775558163a4db5edfd8f1794068aa41be4 100644 (file)
@@ -9,21 +9,19 @@
 #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
@@ -80,6 +78,12 @@ extern void mgmt_txn_destroy(void);
  */
 extern uint64_t 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.
  *
@@ -190,6 +194,23 @@ 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.
+ *     result_type: LYD_FORMAT result format.
+ *     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, LYD_FORMAT result_type,
+                                      const char *xpath);
+
 /*
  * Notifiy backend adapter on connection.
  */
@@ -228,6 +249,34 @@ mgmt_txn_notify_be_cfg_apply_reply(uint64_t txn_id, bool success,
                                       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.
  */