*/
#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"
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
};
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;
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;
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 */
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
*/
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.
" 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;
}
(*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;
}
"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.
*
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);
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;
*/
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]);
/*
* 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)
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)
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++;
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);
&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;
}
}
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;