]> git.proxmox.com Git - mirror_frr.git/blame - mgmtd/mgmt_txn.c
mgmtd: address review comments
[mirror_frr.git] / mgmtd / mgmt_txn.c
CommitLineData
74335ceb
YR
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * MGMTD Transactions
4 *
5 * Copyright (C) 2021 Vmware, Inc.
6 * Pushpasis Sarkar <spushpasis@vmware.com>
7 */
8
9#include <zebra.h>
10#include "hash.h"
11#include "jhash.h"
12#include "libfrr.h"
13#include "mgmtd/mgmt.h"
14#include "mgmtd/mgmt_memory.h"
15#include "mgmtd/mgmt_txn.h"
16
048e1e7b 17#define MGMTD_TXN_DBG(fmt, ...) \
cfa0facb
CH
18 DEBUGD(&mgmt_debug_txn, "%s:" fmt, __func__, ##__VA_ARGS__)
19#define MGMTD_TXN_ERR(fmt, ...) \
74335ceb 20 zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__)
74335ceb
YR
21
22#define MGMTD_TXN_LOCK(txn) mgmt_txn_lock(txn, __FILE__, __LINE__)
23#define MGMTD_TXN_UNLOCK(txn) mgmt_txn_unlock(txn, __FILE__, __LINE__)
24
25enum mgmt_txn_event {
26 MGMTD_TXN_PROC_SETCFG = 1,
27 MGMTD_TXN_PROC_COMMITCFG,
28 MGMTD_TXN_PROC_GETCFG,
29 MGMTD_TXN_PROC_GETDATA,
30 MGMTD_TXN_COMMITCFG_TIMEOUT,
31 MGMTD_TXN_CLEANUP
32};
33
34PREDECL_LIST(mgmt_txn_reqs);
35
36struct mgmt_set_cfg_req {
37 Mgmtd__DatastoreId ds_id;
38 struct mgmt_ds_ctx *ds_ctx;
39 struct nb_cfg_change cfg_changes[MGMTD_MAX_CFG_CHANGES_IN_BATCH];
40 uint16_t num_cfg_changes;
41 bool implicit_commit;
42 Mgmtd__DatastoreId dst_ds_id;
43 struct mgmt_ds_ctx *dst_ds_ctx;
44 struct mgmt_setcfg_stats *setcfg_stats;
45};
46
47enum mgmt_commit_phase {
48 MGMTD_COMMIT_PHASE_PREPARE_CFG = 0,
49 MGMTD_COMMIT_PHASE_TXN_CREATE,
50 MGMTD_COMMIT_PHASE_SEND_CFG,
51 MGMTD_COMMIT_PHASE_APPLY_CFG,
52 MGMTD_COMMIT_PHASE_TXN_DELETE,
53 MGMTD_COMMIT_PHASE_MAX
54};
55
56static inline const char *
57mgmt_commit_phase2str(enum mgmt_commit_phase cmt_phase)
58{
59 switch (cmt_phase) {
60 case MGMTD_COMMIT_PHASE_PREPARE_CFG:
61 return "PREP-CFG";
62 case MGMTD_COMMIT_PHASE_TXN_CREATE:
63 return "CREATE-TXN";
64 case MGMTD_COMMIT_PHASE_SEND_CFG:
65 return "SEND-CFG";
66 case MGMTD_COMMIT_PHASE_APPLY_CFG:
67 return "APPLY-CFG";
68 case MGMTD_COMMIT_PHASE_TXN_DELETE:
69 return "DELETE-TXN";
70 case MGMTD_COMMIT_PHASE_MAX:
71 return "Invalid/Unknown";
72 }
73
74 return "Invalid/Unknown";
75}
76
77PREDECL_LIST(mgmt_txn_batches);
78
79struct mgmt_txn_be_cfg_batch {
80 struct mgmt_txn_ctx *txn;
81 uint64_t batch_id;
82 enum mgmt_be_client_id be_id;
83 struct mgmt_be_client_adapter *be_adapter;
0327be91 84 uint xp_subscr[MGMTD_MAX_CFG_CHANGES_IN_BATCH];
74335ceb
YR
85 Mgmtd__YangCfgDataReq cfg_data[MGMTD_MAX_CFG_CHANGES_IN_BATCH];
86 Mgmtd__YangCfgDataReq * cfg_datap[MGMTD_MAX_CFG_CHANGES_IN_BATCH];
87 Mgmtd__YangData data[MGMTD_MAX_CFG_CHANGES_IN_BATCH];
88 Mgmtd__YangDataValue value[MGMTD_MAX_CFG_CHANGES_IN_BATCH];
89 size_t num_cfg_data;
90 int buf_space_left;
91 enum mgmt_commit_phase comm_phase;
92 struct mgmt_txn_batches_item list_linkage;
93};
94
95DECLARE_LIST(mgmt_txn_batches, struct mgmt_txn_be_cfg_batch, list_linkage);
96
97#define FOREACH_TXN_CFG_BATCH_IN_LIST(list, batch) \
98 frr_each_safe (mgmt_txn_batches, list, batch)
99
100struct mgmt_commit_cfg_req {
101 Mgmtd__DatastoreId src_ds_id;
102 struct mgmt_ds_ctx *src_ds_ctx;
103 Mgmtd__DatastoreId dst_ds_id;
104 struct mgmt_ds_ctx *dst_ds_ctx;
105 uint32_t nb_txn_id;
106 uint8_t validate_only : 1;
107 uint8_t abort : 1;
108 uint8_t implicit : 1;
109 uint8_t rollback : 1;
110
111 /* Track commit phases */
112 enum mgmt_commit_phase curr_phase;
113 enum mgmt_commit_phase next_phase;
114
115 /*
116 * Set of config changes to commit. This is used only
117 * when changes are NOT to be determined by comparing
118 * candidate and running DSs. This is typically used
119 * for downloading all relevant configs for a new backend
120 * client that has recently come up and connected with
121 * MGMTD.
122 */
123 struct nb_config_cbs *cfg_chgs;
124
125 /*
126 * Details on all the Backend Clients associated with
127 * this commit.
128 */
129 struct mgmt_be_client_subscr_info subscr_info;
130
131 /*
132 * List of backend batches for this commit to be validated
133 * and applied at the backend.
134 *
135 * FIXME: Need to re-think this design for the case set of
136 * validators for a given YANG data item is different from
137 * the set of notifiers for the same. We may need to have
138 * separate list of batches for VALIDATE and APPLY.
139 */
140 struct mgmt_txn_batches_head curr_batches[MGMTD_BE_CLIENT_ID_MAX];
141 struct mgmt_txn_batches_head next_batches[MGMTD_BE_CLIENT_ID_MAX];
142 /*
143 * The last batch added for any backend client. This is always on
144 * 'curr_batches'
145 */
146 struct mgmt_txn_be_cfg_batch
147 *last_be_cfg_batch[MGMTD_BE_CLIENT_ID_MAX];
148 struct hash *batches;
149 uint64_t next_batch_id;
150
151 struct mgmt_commit_stats *cmt_stats;
152};
153
154struct mgmt_get_data_reply {
155 /* Buffer space for preparing data reply */
156 int num_reply;
157 int last_batch;
158 Mgmtd__YangDataReply data_reply;
159 Mgmtd__YangData reply_data[MGMTD_MAX_NUM_DATA_REPLY_IN_BATCH];
160 Mgmtd__YangData * reply_datap[MGMTD_MAX_NUM_DATA_REPLY_IN_BATCH];
161 Mgmtd__YangDataValue reply_value[MGMTD_MAX_NUM_DATA_REPLY_IN_BATCH];
162 char *reply_xpathp[MGMTD_MAX_NUM_DATA_REPLY_IN_BATCH];
163};
164
165struct mgmt_get_data_req {
166 Mgmtd__DatastoreId ds_id;
167 struct mgmt_ds_ctx *ds_ctx;
168 char *xpaths[MGMTD_MAX_NUM_DATA_REQ_IN_BATCH];
169 int num_xpaths;
170
171 /*
172 * Buffer space for preparing reply.
173 * NOTE: Should only be malloc-ed on demand to reduce
174 * memory footprint. Freed up via mgmt_trx_req_free()
175 */
176 struct mgmt_get_data_reply *reply;
177
178 int total_reply;
179};
180
181struct mgmt_txn_req {
182 struct mgmt_txn_ctx *txn;
183 enum mgmt_txn_event req_event;
184 uint64_t req_id;
185 union {
186 struct mgmt_set_cfg_req *set_cfg;
187 struct mgmt_get_data_req *get_data;
188 struct mgmt_commit_cfg_req commit_cfg;
189 } req;
190
191 bool pending_be_proc;
192 struct mgmt_txn_reqs_item list_linkage;
193};
194
195DECLARE_LIST(mgmt_txn_reqs, struct mgmt_txn_req, list_linkage);
196
197#define FOREACH_TXN_REQ_IN_LIST(list, req) \
198 frr_each_safe (mgmt_txn_reqs, list, req)
199
200struct mgmt_txn_ctx {
201 uint64_t session_id; /* One transaction per client session */
202 uint64_t txn_id;
203 enum mgmt_txn_type type;
204
205 /* struct mgmt_master *mm; */
206
e6685141
DS
207 struct event *proc_set_cfg;
208 struct event *proc_comm_cfg;
209 struct event *proc_get_cfg;
210 struct event *proc_get_data;
211 struct event *comm_cfg_timeout;
212 struct event *clnup;
74335ceb
YR
213
214 /* List of backend adapters involved in this transaction */
215 struct mgmt_txn_badapters_head be_adapters;
216
217 int refcount;
218
219 struct mgmt_txns_item list_linkage;
220
221 /*
222 * List of pending set-config requests for a given
223 * transaction/session. Just one list for requests
224 * not processed at all. There's no backend interaction
225 * involved.
226 */
227 struct mgmt_txn_reqs_head set_cfg_reqs;
228 /*
229 * List of pending get-config requests for a given
230 * transaction/session. Just one list for requests
231 * not processed at all. There's no backend interaction
232 * involved.
233 */
234 struct mgmt_txn_reqs_head get_cfg_reqs;
235 /*
236 * List of pending get-data requests for a given
237 * transaction/session Two lists, one for requests
238 * not processed at all, and one for requests that
239 * has been sent to backend for processing.
240 */
241 struct mgmt_txn_reqs_head get_data_reqs;
242 struct mgmt_txn_reqs_head pending_get_datas;
243 /*
244 * There will always be one commit-config allowed for a given
245 * transaction/session. No need to maintain lists for it.
246 */
247 struct mgmt_txn_req *commit_cfg_req;
248};
249
250DECLARE_LIST(mgmt_txns, struct mgmt_txn_ctx, list_linkage);
251
252#define FOREACH_TXN_IN_LIST(mm, txn) \
253 frr_each_safe (mgmt_txns, &(mm)->txn_list, (txn))
254
255static int mgmt_txn_send_commit_cfg_reply(struct mgmt_txn_ctx *txn,
256 enum mgmt_result result,
257 const char *error_if_any);
258
259static inline const char *
260mgmt_txn_commit_phase_str(struct mgmt_txn_ctx *txn, bool curr)
261{
262 if (!txn->commit_cfg_req)
263 return "None";
264
265 return (mgmt_commit_phase2str(
266 curr ? txn->commit_cfg_req->req.commit_cfg.curr_phase
267 : txn->commit_cfg_req->req.commit_cfg.next_phase));
268}
269
270static void mgmt_txn_lock(struct mgmt_txn_ctx *txn, const char *file,
271 int line);
272static void mgmt_txn_unlock(struct mgmt_txn_ctx **txn, const char *file,
273 int line);
274static int
275mgmt_txn_send_be_txn_delete(struct mgmt_txn_ctx *txn,
276 struct mgmt_be_client_adapter *adapter);
277
cd9d0537 278static struct event_loop *mgmt_txn_tm;
74335ceb
YR
279static struct mgmt_master *mgmt_txn_mm;
280
281static void mgmt_txn_register_event(struct mgmt_txn_ctx *txn,
282 enum mgmt_txn_event event);
283
284static int
285mgmt_move_be_commit_to_next_phase(struct mgmt_txn_ctx *txn,
286 struct mgmt_be_client_adapter *adapter);
287
288static struct mgmt_txn_be_cfg_batch *
289mgmt_txn_cfg_batch_alloc(struct mgmt_txn_ctx *txn,
290 enum mgmt_be_client_id id,
291 struct mgmt_be_client_adapter *be_adapter)
292{
293 struct mgmt_txn_be_cfg_batch *cfg_btch;
294
295 cfg_btch = XCALLOC(MTYPE_MGMTD_TXN_CFG_BATCH,
296 sizeof(struct mgmt_txn_be_cfg_batch));
297 assert(cfg_btch);
298 cfg_btch->be_id = id;
299
300 cfg_btch->txn = txn;
301 MGMTD_TXN_LOCK(txn);
302 assert(txn->commit_cfg_req);
303 mgmt_txn_batches_add_tail(
304 &txn->commit_cfg_req->req.commit_cfg.curr_batches[id],
305 cfg_btch);
306 cfg_btch->be_adapter = be_adapter;
307 cfg_btch->buf_space_left = MGMTD_BE_CFGDATA_MAX_MSG_LEN;
308 if (be_adapter)
309 mgmt_be_adapter_lock(be_adapter);
310
311 txn->commit_cfg_req->req.commit_cfg.last_be_cfg_batch[id] =
312 cfg_btch;
313 if (!txn->commit_cfg_req->req.commit_cfg.next_batch_id)
314 txn->commit_cfg_req->req.commit_cfg.next_batch_id++;
315 cfg_btch->batch_id =
316 txn->commit_cfg_req->req.commit_cfg.next_batch_id++;
317 hash_get(txn->commit_cfg_req->req.commit_cfg.batches, cfg_btch,
318 hash_alloc_intern);
319
320 return cfg_btch;
321}
322
323static void
324mgmt_txn_cfg_batch_free(struct mgmt_txn_be_cfg_batch **cfg_btch)
325{
326 size_t indx;
327 struct mgmt_commit_cfg_req *cmtcfg_req;
328
218625aa
CH
329 MGMTD_TXN_DBG(" freeing batch-id: %" PRIu64 " txn-id %" PRIu64,
330 (*cfg_btch)->batch_id, (*cfg_btch)->txn->txn_id);
74335ceb
YR
331
332 assert((*cfg_btch)->txn
333 && (*cfg_btch)->txn->type == MGMTD_TXN_TYPE_CONFIG);
334
335 cmtcfg_req = &(*cfg_btch)->txn->commit_cfg_req->req.commit_cfg;
336 hash_release(cmtcfg_req->batches, *cfg_btch);
337 mgmt_txn_batches_del(&cmtcfg_req->curr_batches[(*cfg_btch)->be_id],
338 *cfg_btch);
339 mgmt_txn_batches_del(&cmtcfg_req->next_batches[(*cfg_btch)->be_id],
340 *cfg_btch);
341
342 if ((*cfg_btch)->be_adapter)
343 mgmt_be_adapter_unlock(&(*cfg_btch)->be_adapter);
344
345 for (indx = 0; indx < (*cfg_btch)->num_cfg_data; indx++) {
346 if ((*cfg_btch)->data[indx].xpath) {
347 free((*cfg_btch)->data[indx].xpath);
348 (*cfg_btch)->data[indx].xpath = NULL;
349 }
350 }
351
352 MGMTD_TXN_UNLOCK(&(*cfg_btch)->txn);
353
354 XFREE(MTYPE_MGMTD_TXN_CFG_BATCH, *cfg_btch);
355 *cfg_btch = NULL;
356}
357
358static unsigned int mgmt_txn_cfgbatch_hash_key(const void *data)
359{
360 const struct mgmt_txn_be_cfg_batch *batch = data;
361
362 return jhash2((uint32_t *) &batch->batch_id,
363 sizeof(batch->batch_id) / sizeof(uint32_t), 0);
364}
365
366static bool mgmt_txn_cfgbatch_hash_cmp(const void *d1, const void *d2)
367{
368 const struct mgmt_txn_be_cfg_batch *batch1 = d1;
369 const struct mgmt_txn_be_cfg_batch *batch2 = d2;
370
371 return (batch1->batch_id == batch2->batch_id);
372}
373
374static void mgmt_txn_cfgbatch_hash_free(void *data)
375{
376 struct mgmt_txn_be_cfg_batch *batch = data;
377
378 mgmt_txn_cfg_batch_free(&batch);
379}
380
381static inline struct mgmt_txn_be_cfg_batch *
382mgmt_txn_cfgbatch_id2ctx(struct mgmt_txn_ctx *txn, uint64_t batch_id)
383{
384 struct mgmt_txn_be_cfg_batch key = {0};
385 struct mgmt_txn_be_cfg_batch *batch;
386
387 if (!txn->commit_cfg_req)
388 return NULL;
389
390 key.batch_id = batch_id;
391 batch = hash_lookup(txn->commit_cfg_req->req.commit_cfg.batches,
392 &key);
393
394 return batch;
395}
396
397static void mgmt_txn_cleanup_be_cfg_batches(struct mgmt_txn_ctx *txn,
398 enum mgmt_be_client_id id)
399{
400 struct mgmt_txn_be_cfg_batch *cfg_btch;
401 struct mgmt_txn_batches_head *list;
402
403 list = &txn->commit_cfg_req->req.commit_cfg.curr_batches[id];
404 FOREACH_TXN_CFG_BATCH_IN_LIST (list, cfg_btch)
405 mgmt_txn_cfg_batch_free(&cfg_btch);
406
407 mgmt_txn_batches_fini(list);
408
409 list = &txn->commit_cfg_req->req.commit_cfg.next_batches[id];
410 FOREACH_TXN_CFG_BATCH_IN_LIST (list, cfg_btch)
411 mgmt_txn_cfg_batch_free(&cfg_btch);
412
413 mgmt_txn_batches_fini(list);
414
415 txn->commit_cfg_req->req.commit_cfg.last_be_cfg_batch[id] = NULL;
416}
417
418static struct mgmt_txn_req *mgmt_txn_req_alloc(struct mgmt_txn_ctx *txn,
419 uint64_t req_id,
420 enum mgmt_txn_event req_event)
421{
422 struct mgmt_txn_req *txn_req;
423 enum mgmt_be_client_id id;
424
425 txn_req = XCALLOC(MTYPE_MGMTD_TXN_REQ, sizeof(struct mgmt_txn_req));
426 assert(txn_req);
427 txn_req->txn = txn;
428 txn_req->req_id = req_id;
429 txn_req->req_event = req_event;
430 txn_req->pending_be_proc = false;
431
432 switch (txn_req->req_event) {
433 case MGMTD_TXN_PROC_SETCFG:
434 txn_req->req.set_cfg = XCALLOC(MTYPE_MGMTD_TXN_SETCFG_REQ,
435 sizeof(struct mgmt_set_cfg_req));
436 assert(txn_req->req.set_cfg);
437 mgmt_txn_reqs_add_tail(&txn->set_cfg_reqs, txn_req);
218625aa
CH
438 MGMTD_TXN_DBG("Added a new SETCFG req-id: %" PRIu64
439 " txn-id: %" PRIu64 ", session-id: %" PRIu64,
440 txn_req->req_id, txn->txn_id, txn->session_id);
74335ceb
YR
441 break;
442 case MGMTD_TXN_PROC_COMMITCFG:
443 txn->commit_cfg_req = txn_req;
218625aa
CH
444 MGMTD_TXN_DBG("Added a new COMMITCFG req-id: %" PRIu64
445 " txn-id: %" PRIu64 " session-id: %" PRIu64,
446 txn_req->req_id, txn->txn_id, txn->session_id);
74335ceb
YR
447
448 FOREACH_MGMTD_BE_CLIENT_ID (id) {
449 mgmt_txn_batches_init(
450 &txn_req->req.commit_cfg.curr_batches[id]);
451 mgmt_txn_batches_init(
452 &txn_req->req.commit_cfg.next_batches[id]);
453 }
454
455 txn_req->req.commit_cfg.batches =
456 hash_create(mgmt_txn_cfgbatch_hash_key,
457 mgmt_txn_cfgbatch_hash_cmp,
458 "MGMT Config Batches");
459 break;
460 case MGMTD_TXN_PROC_GETCFG:
461 txn_req->req.get_data =
462 XCALLOC(MTYPE_MGMTD_TXN_GETDATA_REQ,
463 sizeof(struct mgmt_get_data_req));
464 assert(txn_req->req.get_data);
465 mgmt_txn_reqs_add_tail(&txn->get_cfg_reqs, txn_req);
218625aa
CH
466 MGMTD_TXN_DBG("Added a new GETCFG req-id: %" PRIu64
467 " txn-id: %" PRIu64 " session-id: %" PRIu64,
468 txn_req->req_id, txn->txn_id, txn->session_id);
74335ceb
YR
469 break;
470 case MGMTD_TXN_PROC_GETDATA:
471 txn_req->req.get_data =
472 XCALLOC(MTYPE_MGMTD_TXN_GETDATA_REQ,
473 sizeof(struct mgmt_get_data_req));
474 assert(txn_req->req.get_data);
475 mgmt_txn_reqs_add_tail(&txn->get_data_reqs, txn_req);
218625aa
CH
476 MGMTD_TXN_DBG("Added a new GETDATA req-id: %" PRIu64
477 " txn-id: %" PRIu64 " session-id: %" PRIu64,
478 txn_req->req_id, txn->txn_id, txn->session_id);
74335ceb
YR
479 break;
480 case MGMTD_TXN_COMMITCFG_TIMEOUT:
481 case MGMTD_TXN_CLEANUP:
482 break;
483 }
484
485 MGMTD_TXN_LOCK(txn);
486
487 return txn_req;
488}
489
490static void mgmt_txn_req_free(struct mgmt_txn_req **txn_req)
491{
492 int indx;
493 struct mgmt_txn_reqs_head *req_list = NULL;
494 struct mgmt_txn_reqs_head *pending_list = NULL;
495 enum mgmt_be_client_id id;
496 struct mgmt_be_client_adapter *adapter;
497
498 switch ((*txn_req)->req_event) {
499 case MGMTD_TXN_PROC_SETCFG:
500 for (indx = 0; indx < (*txn_req)->req.set_cfg->num_cfg_changes;
501 indx++) {
502 if ((*txn_req)->req.set_cfg->cfg_changes[indx].value) {
503 MGMTD_TXN_DBG(
504 "Freeing value for %s at %p ==> '%s'",
505 (*txn_req)
506 ->req.set_cfg->cfg_changes[indx]
507 .xpath,
508 (*txn_req)
509 ->req.set_cfg->cfg_changes[indx]
510 .value,
511 (*txn_req)
512 ->req.set_cfg->cfg_changes[indx]
513 .value);
514 free((void *)(*txn_req)
515 ->req.set_cfg->cfg_changes[indx]
516 .value);
517 }
518 }
519 req_list = &(*txn_req)->txn->set_cfg_reqs;
218625aa
CH
520 MGMTD_TXN_DBG("Deleting SETCFG req-id: %" PRIu64
521 " txn-id: %" PRIu64,
522 (*txn_req)->req_id, (*txn_req)->txn->txn_id);
74335ceb
YR
523 XFREE(MTYPE_MGMTD_TXN_SETCFG_REQ, (*txn_req)->req.set_cfg);
524 break;
525 case MGMTD_TXN_PROC_COMMITCFG:
218625aa
CH
526 MGMTD_TXN_DBG("Deleting COMMITCFG req-id: %" PRIu64
527 " txn-id: %" PRIu64,
528 (*txn_req)->req_id, (*txn_req)->txn->txn_id);
74335ceb
YR
529 FOREACH_MGMTD_BE_CLIENT_ID (id) {
530 /*
531 * Send TXN_DELETE to cleanup state for this
532 * transaction on backend
533 */
0327be91
CH
534 if ((*txn_req)->req.commit_cfg.curr_phase >=
535 MGMTD_COMMIT_PHASE_TXN_CREATE &&
536 (*txn_req)->req.commit_cfg.curr_phase <
537 MGMTD_COMMIT_PHASE_TXN_DELETE &&
538 (*txn_req)
539 ->req.commit_cfg.subscr_info
540 .xpath_subscr[id]) {
74335ceb
YR
541 adapter = mgmt_be_get_adapter_by_id(id);
542 if (adapter)
543 mgmt_txn_send_be_txn_delete(
544 (*txn_req)->txn, adapter);
545 }
546
547 mgmt_txn_cleanup_be_cfg_batches((*txn_req)->txn,
548 id);
549 if ((*txn_req)->req.commit_cfg.batches) {
550 hash_clean((*txn_req)->req.commit_cfg.batches,
551 mgmt_txn_cfgbatch_hash_free);
552 hash_free((*txn_req)->req.commit_cfg.batches);
553 (*txn_req)->req.commit_cfg.batches = NULL;
554 }
555 }
556 break;
557 case MGMTD_TXN_PROC_GETCFG:
558 for (indx = 0; indx < (*txn_req)->req.get_data->num_xpaths;
559 indx++) {
560 if ((*txn_req)->req.get_data->xpaths[indx])
561 free((void *)(*txn_req)
562 ->req.get_data->xpaths[indx]);
563 }
564 req_list = &(*txn_req)->txn->get_cfg_reqs;
218625aa
CH
565 MGMTD_TXN_DBG("Deleting GETCFG req-id: %" PRIu64
566 " txn-id: %" PRIu64,
567 (*txn_req)->req_id, (*txn_req)->txn->txn_id);
74335ceb
YR
568 if ((*txn_req)->req.get_data->reply)
569 XFREE(MTYPE_MGMTD_TXN_GETDATA_REPLY,
570 (*txn_req)->req.get_data->reply);
571 XFREE(MTYPE_MGMTD_TXN_GETDATA_REQ, (*txn_req)->req.get_data);
572 break;
573 case MGMTD_TXN_PROC_GETDATA:
574 for (indx = 0; indx < (*txn_req)->req.get_data->num_xpaths;
575 indx++) {
576 if ((*txn_req)->req.get_data->xpaths[indx])
577 free((void *)(*txn_req)
578 ->req.get_data->xpaths[indx]);
579 }
580 pending_list = &(*txn_req)->txn->pending_get_datas;
581 req_list = &(*txn_req)->txn->get_data_reqs;
218625aa
CH
582 MGMTD_TXN_DBG("Deleting GETDATA req-id: %" PRIu64
583 " txn-id: %" PRIu64,
584 (*txn_req)->req_id, (*txn_req)->txn->txn_id);
74335ceb
YR
585 if ((*txn_req)->req.get_data->reply)
586 XFREE(MTYPE_MGMTD_TXN_GETDATA_REPLY,
587 (*txn_req)->req.get_data->reply);
588 XFREE(MTYPE_MGMTD_TXN_GETDATA_REQ, (*txn_req)->req.get_data);
589 break;
590 case MGMTD_TXN_COMMITCFG_TIMEOUT:
591 case MGMTD_TXN_CLEANUP:
592 break;
593 }
594
595 if ((*txn_req)->pending_be_proc && pending_list) {
596 mgmt_txn_reqs_del(pending_list, *txn_req);
218625aa
CH
597 MGMTD_TXN_DBG("Removed req-id: %" PRIu64
598 " from pending-list (left:%zu)",
599 (*txn_req)->req_id,
600 mgmt_txn_reqs_count(pending_list));
74335ceb
YR
601 } else if (req_list) {
602 mgmt_txn_reqs_del(req_list, *txn_req);
218625aa
CH
603 MGMTD_TXN_DBG("Removed req-id: %" PRIu64
604 " from request-list (left:%zu)",
605 (*txn_req)->req_id,
606 mgmt_txn_reqs_count(req_list));
74335ceb
YR
607 }
608
609 (*txn_req)->pending_be_proc = false;
610 MGMTD_TXN_UNLOCK(&(*txn_req)->txn);
611 XFREE(MTYPE_MGMTD_TXN_REQ, (*txn_req));
612 *txn_req = NULL;
613}
614
e6685141 615static void mgmt_txn_process_set_cfg(struct event *thread)
74335ceb
YR
616{
617 struct mgmt_txn_ctx *txn;
618 struct mgmt_txn_req *txn_req;
619 struct mgmt_ds_ctx *ds_ctx;
620 struct nb_config *nb_config;
621 char err_buf[1024];
622 bool error;
623 int num_processed = 0;
624 size_t left;
625 struct mgmt_commit_stats *cmt_stats;
626 int ret = 0;
627
e16d030c 628 txn = (struct mgmt_txn_ctx *)EVENT_ARG(thread);
74335ceb
YR
629 assert(txn);
630 cmt_stats = mgmt_fe_get_session_commit_stats(txn->session_id);
631
218625aa
CH
632 MGMTD_TXN_DBG("Processing %zu SET_CONFIG requests txn-id:%" PRIu64
633 " session-id: %" PRIu64,
634 mgmt_txn_reqs_count(&txn->set_cfg_reqs), txn->txn_id,
635 txn->session_id);
74335ceb
YR
636
637 FOREACH_TXN_REQ_IN_LIST (&txn->set_cfg_reqs, txn_req) {
638 error = false;
639 assert(txn_req->req_event == MGMTD_TXN_PROC_SETCFG);
640 ds_ctx = txn_req->req.set_cfg->ds_ctx;
641 if (!ds_ctx) {
642 mgmt_fe_send_set_cfg_reply(
643 txn->session_id, txn->txn_id,
644 txn_req->req.set_cfg->ds_id, txn_req->req_id,
645 MGMTD_INTERNAL_ERROR, "No such datastore!",
646 txn_req->req.set_cfg->implicit_commit);
647 error = true;
648 goto mgmt_txn_process_set_cfg_done;
649 }
650
651 nb_config = mgmt_ds_get_nb_config(ds_ctx);
652 if (!nb_config) {
653 mgmt_fe_send_set_cfg_reply(
654 txn->session_id, txn->txn_id,
655 txn_req->req.set_cfg->ds_id, txn_req->req_id,
656 MGMTD_INTERNAL_ERROR,
657 "Unable to retrieve DS Config Tree!",
658 txn_req->req.set_cfg->implicit_commit);
659 error = true;
660 goto mgmt_txn_process_set_cfg_done;
661 }
662
663 error = false;
664 nb_candidate_edit_config_changes(
665 nb_config, txn_req->req.set_cfg->cfg_changes,
666 (size_t)txn_req->req.set_cfg->num_cfg_changes, NULL,
667 NULL, 0, err_buf, sizeof(err_buf), &error);
668 if (error) {
669 mgmt_fe_send_set_cfg_reply(
670 txn->session_id, txn->txn_id,
671 txn_req->req.set_cfg->ds_id, txn_req->req_id,
672 MGMTD_INTERNAL_ERROR, err_buf,
673 txn_req->req.set_cfg->implicit_commit);
674 goto mgmt_txn_process_set_cfg_done;
675 }
676
677 if (txn_req->req.set_cfg->implicit_commit) {
678 assert(mgmt_txn_reqs_count(&txn->set_cfg_reqs) == 1);
679 assert(txn_req->req.set_cfg->dst_ds_ctx);
680
681 ret = mgmt_ds_write_lock(
682 txn_req->req.set_cfg->dst_ds_ctx);
683 if (ret != 0) {
684 MGMTD_TXN_ERR(
218625aa
CH
685 "Failed to lock DS %u txn-id: %" PRIu64
686 " session-id: %" PRIu64 " err: %s",
687 txn_req->req.set_cfg->dst_ds_id,
688 txn->txn_id, txn->session_id,
74335ceb
YR
689 strerror(ret));
690 mgmt_txn_send_commit_cfg_reply(
691 txn, MGMTD_DS_LOCK_FAILED,
692 "Lock running DS before implicit commit failed!");
693 goto mgmt_txn_process_set_cfg_done;
694 }
695
696 mgmt_txn_send_commit_config_req(
697 txn->txn_id, txn_req->req_id,
698 txn_req->req.set_cfg->ds_id,
699 txn_req->req.set_cfg->ds_ctx,
700 txn_req->req.set_cfg->dst_ds_id,
701 txn_req->req.set_cfg->dst_ds_ctx, false,
702 false, true);
703
704 if (mm->perf_stats_en)
705 gettimeofday(&cmt_stats->last_start, NULL);
706 cmt_stats->commit_cnt++;
707 } else if (mgmt_fe_send_set_cfg_reply(
708 txn->session_id, txn->txn_id,
709 txn_req->req.set_cfg->ds_id,
710 txn_req->req_id, MGMTD_SUCCESS, NULL, false)
711 != 0) {
712 MGMTD_TXN_ERR(
218625aa
CH
713 "Failed to send SET_CONFIG_REPLY txn-id %" PRIu64
714 " session-id: %" PRIu64,
715 txn->txn_id, txn->session_id);
74335ceb
YR
716 error = true;
717 }
718
719 mgmt_txn_process_set_cfg_done:
720
721 /*
722 * Note: The following will remove it from the list as well.
723 */
724 mgmt_txn_req_free(&txn_req);
725
726 num_processed++;
727 if (num_processed == MGMTD_TXN_MAX_NUM_SETCFG_PROC)
728 break;
729 }
730
731 left = mgmt_txn_reqs_count(&txn->set_cfg_reqs);
732 if (left) {
733 MGMTD_TXN_DBG(
734 "Processed maximum number of Set-Config requests (%d/%d/%d). Rescheduling for rest.",
735 num_processed, MGMTD_TXN_MAX_NUM_SETCFG_PROC,
736 (int)left);
737 mgmt_txn_register_event(txn, MGMTD_TXN_PROC_SETCFG);
738 }
739}
740
741static int mgmt_txn_send_commit_cfg_reply(struct mgmt_txn_ctx *txn,
742 enum mgmt_result result,
743 const char *error_if_any)
744{
745 int ret = 0;
746 bool success, create_cmt_info_rec;
747
748 if (!txn->commit_cfg_req)
749 return -1;
750
751 success = (result == MGMTD_SUCCESS || result == MGMTD_NO_CFG_CHANGES);
752
753 if (!txn->commit_cfg_req->req.commit_cfg.implicit && txn->session_id
754 && mgmt_fe_send_commit_cfg_reply(
755 txn->session_id, txn->txn_id,
756 txn->commit_cfg_req->req.commit_cfg.src_ds_id,
757 txn->commit_cfg_req->req.commit_cfg.dst_ds_id,
758 txn->commit_cfg_req->req_id,
759 txn->commit_cfg_req->req.commit_cfg.validate_only,
760 result, error_if_any)
761 != 0) {
762 MGMTD_TXN_ERR(
218625aa
CH
763 "Failed to send COMMIT-CONFIG-REPLY txn-id: %" PRIu64
764 " session-id: %" PRIu64,
765 txn->txn_id, txn->session_id);
74335ceb
YR
766 }
767
768 if (txn->commit_cfg_req->req.commit_cfg.implicit && txn->session_id
769 && mgmt_fe_send_set_cfg_reply(
770 txn->session_id, txn->txn_id,
771 txn->commit_cfg_req->req.commit_cfg.src_ds_id,
772 txn->commit_cfg_req->req_id,
773 success ? MGMTD_SUCCESS : MGMTD_INTERNAL_ERROR,
774 error_if_any, true)
775 != 0) {
218625aa
CH
776 MGMTD_TXN_ERR("Failed to send SET-CONFIG-REPLY txn-id: %" PRIu64
777 " session-id: %" PRIu64,
778 txn->txn_id, txn->session_id);
74335ceb
YR
779 }
780
781 if (success) {
782 /* Stop the commit-timeout timer */
e16d030c 783 EVENT_OFF(txn->comm_cfg_timeout);
74335ceb
YR
784
785 create_cmt_info_rec =
786 (result != MGMTD_NO_CFG_CHANGES &&
787 !txn->commit_cfg_req->req.commit_cfg.rollback);
788
789 /*
790 * Successful commit: Merge Src DS into Dst DS if and only if
791 * this was not a validate-only or abort request.
792 */
793 if ((txn->session_id
794 && !txn->commit_cfg_req->req.commit_cfg.validate_only
795 && !txn->commit_cfg_req->req.commit_cfg.abort)
796 || txn->commit_cfg_req->req.commit_cfg.rollback) {
797 mgmt_ds_copy_dss(txn->commit_cfg_req->req.commit_cfg
798 .src_ds_ctx,
799 txn->commit_cfg_req->req.commit_cfg
800 .dst_ds_ctx,
801 create_cmt_info_rec);
802 }
803
804 /*
805 * Restore Src DS back to Dest DS only through a commit abort
806 * request.
807 */
808 if (txn->session_id
809 && txn->commit_cfg_req->req.commit_cfg.abort)
810 mgmt_ds_copy_dss(txn->commit_cfg_req->req.commit_cfg
811 .dst_ds_ctx,
812 txn->commit_cfg_req->req.commit_cfg
813 .src_ds_ctx,
814 false);
815 } else {
816 /*
817 * The commit has failied. For implicit commit requests restore
818 * back the contents of the candidate DS.
819 */
820 if (txn->commit_cfg_req->req.commit_cfg.implicit)
821 mgmt_ds_copy_dss(txn->commit_cfg_req->req.commit_cfg
822 .dst_ds_ctx,
823 txn->commit_cfg_req->req.commit_cfg
824 .src_ds_ctx,
825 false);
826 }
827
828 if (txn->commit_cfg_req->req.commit_cfg.rollback) {
829 ret = mgmt_ds_unlock(
830 txn->commit_cfg_req->req.commit_cfg.dst_ds_ctx);
831 if (ret != 0)
832 MGMTD_TXN_ERR(
833 "Failed to unlock the dst DS during rollback : %s",
834 strerror(ret));
1401ee8b
PS
835
836 /*
837 * Resume processing the rollback command.
838 */
839 mgmt_history_rollback_complete(success);
74335ceb
YR
840 }
841
842 if (txn->commit_cfg_req->req.commit_cfg.implicit)
843 if (mgmt_ds_unlock(
844 txn->commit_cfg_req->req.commit_cfg.dst_ds_ctx)
845 != 0)
846 MGMTD_TXN_ERR(
847 "Failed to unlock the dst DS during implicit : %s",
848 strerror(ret));
849
850 txn->commit_cfg_req->req.commit_cfg.cmt_stats = NULL;
851 mgmt_txn_req_free(&txn->commit_cfg_req);
852
853 /*
854 * The CONFIG Transaction should be destroyed from Frontend-adapter.
855 * But in case the transaction is not triggered from a front-end session
856 * we need to cleanup by itself.
857 */
858 if (!txn->session_id)
859 mgmt_txn_register_event(txn, MGMTD_TXN_CLEANUP);
860
861 return 0;
862}
863
864static void
865mgmt_move_txn_cfg_batch_to_next(struct mgmt_commit_cfg_req *cmtcfg_req,
866 struct mgmt_txn_be_cfg_batch *cfg_btch,
867 struct mgmt_txn_batches_head *src_list,
868 struct mgmt_txn_batches_head *dst_list,
869 bool update_commit_phase,
870 enum mgmt_commit_phase to_phase)
871{
872 mgmt_txn_batches_del(src_list, cfg_btch);
873
874 if (update_commit_phase) {
218625aa
CH
875 MGMTD_TXN_DBG("Move txn-id %" PRIu64 " batch-id: %" PRIu64
876 " from '%s' --> '%s'",
877 cfg_btch->txn->txn_id, cfg_btch->batch_id,
74335ceb
YR
878 mgmt_commit_phase2str(cfg_btch->comm_phase),
879 mgmt_txn_commit_phase_str(cfg_btch->txn, false));
880 cfg_btch->comm_phase = to_phase;
881 }
882
883 mgmt_txn_batches_add_tail(dst_list, cfg_btch);
884}
885
886static void mgmt_move_txn_cfg_batches(struct mgmt_txn_ctx *txn,
887 struct mgmt_commit_cfg_req *cmtcfg_req,
888 struct mgmt_txn_batches_head *src_list,
889 struct mgmt_txn_batches_head *dst_list,
890 bool update_commit_phase,
891 enum mgmt_commit_phase to_phase)
892{
893 struct mgmt_txn_be_cfg_batch *cfg_btch;
894
895 FOREACH_TXN_CFG_BATCH_IN_LIST (src_list, cfg_btch) {
896 mgmt_move_txn_cfg_batch_to_next(cmtcfg_req, cfg_btch, src_list,
897 dst_list, update_commit_phase,
898 to_phase);
899 }
900}
901
902static int
903mgmt_try_move_commit_to_next_phase(struct mgmt_txn_ctx *txn,
904 struct mgmt_commit_cfg_req *cmtcfg_req)
905{
906 struct mgmt_txn_batches_head *curr_list, *next_list;
907 enum mgmt_be_client_id id;
908
218625aa
CH
909 MGMTD_TXN_DBG("txn-id: %" PRIu64 ", Phase(current:'%s' next:'%s')",
910 txn->txn_id, mgmt_txn_commit_phase_str(txn, true),
74335ceb
YR
911 mgmt_txn_commit_phase_str(txn, false));
912
913 /*
914 * Check if all clients has moved to next phase or not.
915 */
916 FOREACH_MGMTD_BE_CLIENT_ID (id) {
0327be91 917 if (cmtcfg_req->subscr_info.xpath_subscr[id] &&
74335ceb
YR
918 mgmt_txn_batches_count(&cmtcfg_req->curr_batches[id])) {
919 /*
920 * There's atleast once client who hasn't moved to
921 * next phase.
922 *
923 * TODO: Need to re-think this design for the case
924 * set of validators for a given YANG data item is
925 * different from the set of notifiers for the same.
926 */
927 return -1;
928 }
929 }
930
218625aa
CH
931 MGMTD_TXN_DBG("Move entire txn-id: %" PRIu64 " from '%s' to '%s'",
932 txn->txn_id, mgmt_txn_commit_phase_str(txn, true),
933 mgmt_txn_commit_phase_str(txn, false));
74335ceb
YR
934
935 /*
936 * If we are here, it means all the clients has moved to next phase.
937 * So we can move the whole commit to next phase.
938 */
939 cmtcfg_req->curr_phase = cmtcfg_req->next_phase;
940 cmtcfg_req->next_phase++;
218625aa
CH
941 MGMTD_TXN_DBG("Move back all config batches for txn-id: %" PRIu64
942 " from next to current branch",
943 txn->txn_id);
74335ceb
YR
944 FOREACH_MGMTD_BE_CLIENT_ID (id) {
945 curr_list = &cmtcfg_req->curr_batches[id];
946 next_list = &cmtcfg_req->next_batches[id];
947 mgmt_move_txn_cfg_batches(txn, cmtcfg_req, next_list,
948 curr_list, false, 0);
949 }
950
951 mgmt_txn_register_event(txn, MGMTD_TXN_PROC_COMMITCFG);
952
953 return 0;
954}
955
956static int
957mgmt_move_be_commit_to_next_phase(struct mgmt_txn_ctx *txn,
958 struct mgmt_be_client_adapter *adapter)
959{
960 struct mgmt_commit_cfg_req *cmtcfg_req;
961 struct mgmt_txn_batches_head *curr_list, *next_list;
962
963 if (txn->type != MGMTD_TXN_TYPE_CONFIG || !txn->commit_cfg_req)
964 return -1;
965
966 cmtcfg_req = &txn->commit_cfg_req->req.commit_cfg;
967
218625aa
CH
968 MGMTD_TXN_DBG("Move txn-id: %" PRIu64
969 " for '%s' Phase(current: '%s' next:'%s')",
970 txn->txn_id, adapter->name,
971 mgmt_txn_commit_phase_str(txn, true),
972 mgmt_txn_commit_phase_str(txn, false));
74335ceb
YR
973
974 MGMTD_TXN_DBG(
975 "Move all config batches for '%s' from current to next list",
976 adapter->name);
977 curr_list = &cmtcfg_req->curr_batches[adapter->id];
978 next_list = &cmtcfg_req->next_batches[adapter->id];
979 mgmt_move_txn_cfg_batches(txn, cmtcfg_req, curr_list, next_list, true,
980 cmtcfg_req->next_phase);
981
218625aa
CH
982 MGMTD_TXN_DBG("txn-id: %" PRIu64 ", Phase(current:'%s' next:'%s')",
983 txn->txn_id, mgmt_txn_commit_phase_str(txn, true),
984 mgmt_txn_commit_phase_str(txn, false));
74335ceb
YR
985
986 /*
987 * Check if all clients has moved to next phase or not.
988 */
989 mgmt_try_move_commit_to_next_phase(txn, cmtcfg_req);
990
991 return 0;
992}
993
994static int mgmt_txn_create_config_batches(struct mgmt_txn_req *txn_req,
995 struct nb_config_cbs *changes)
996{
997 struct nb_config_cb *cb, *nxt;
998 struct nb_config_change *chg;
999 struct mgmt_txn_be_cfg_batch *cfg_btch;
1000 struct mgmt_be_client_subscr_info subscr_info;
1001 char *xpath = NULL, *value = NULL;
1002 char err_buf[1024];
1003 enum mgmt_be_client_id id;
1004 struct mgmt_be_client_adapter *adapter;
1005 struct mgmt_commit_cfg_req *cmtcfg_req;
1006 bool found_validator;
1007 int num_chgs = 0;
1008 int xpath_len, value_len;
1009
1010 cmtcfg_req = &txn_req->req.commit_cfg;
1011
1012 RB_FOREACH_SAFE (cb, nb_config_cbs, changes, nxt) {
1013 chg = (struct nb_config_change *)cb;
1014
1015 /*
1016 * Could have directly pointed to xpath in nb_node.
1017 * But dont want to mess with it now.
1018 * xpath = chg->cb.nb_node->xpath;
1019 */
1020 xpath = lyd_path(chg->cb.dnode, LYD_PATH_STD, NULL, 0);
1021 if (!xpath) {
1022 (void)mgmt_txn_send_commit_cfg_reply(
1023 txn_req->txn, MGMTD_INTERNAL_ERROR,
1024 "Internal error! Could not get Xpath from Ds node!");
1025 goto mgmt_txn_create_config_batches_failed;
1026 }
1027
1028 value = (char *)lyd_get_value(chg->cb.dnode);
1029 if (!value)
1030 value = (char *)MGMTD_BE_CONTAINER_NODE_VAL;
1031
1032 MGMTD_TXN_DBG("XPATH: %s, Value: '%s'", xpath,
1033 value ? value : "NIL");
1034
0327be91 1035 mgmt_be_get_subscr_info_for_xpath(xpath, &subscr_info);
74335ceb
YR
1036
1037 xpath_len = strlen(xpath) + 1;
1038 value_len = strlen(value) + 1;
1039 found_validator = false;
1040 FOREACH_MGMTD_BE_CLIENT_ID (id) {
0327be91
CH
1041 if (!(subscr_info.xpath_subscr[id] &
1042 (MGMT_SUBSCR_VALIDATE_CFG |
1043 MGMT_SUBSCR_NOTIFY_CFG)))
74335ceb
YR
1044 continue;
1045
1046 adapter = mgmt_be_get_adapter_by_id(id);
1047 if (!adapter)
1048 continue;
1049
1050 cfg_btch = cmtcfg_req->last_be_cfg_batch[id];
1051 if (!cfg_btch
1052 || (cfg_btch->num_cfg_data
1053 == MGMTD_MAX_CFG_CHANGES_IN_BATCH)
1054 || (cfg_btch->buf_space_left
1055 < (xpath_len + value_len))) {
1056 /* Allocate a new config batch */
1057 cfg_btch = mgmt_txn_cfg_batch_alloc(
1058 txn_req->txn, id, adapter);
1059 }
1060
1061 cfg_btch->buf_space_left -= (xpath_len + value_len);
1062 memcpy(&cfg_btch->xp_subscr[cfg_btch->num_cfg_data],
1063 &subscr_info.xpath_subscr[id],
1064 sizeof(cfg_btch->xp_subscr[0]));
1065
1066 mgmt_yang_cfg_data_req_init(
1067 &cfg_btch->cfg_data[cfg_btch->num_cfg_data]);
1068 cfg_btch->cfg_datap[cfg_btch->num_cfg_data] =
1069 &cfg_btch->cfg_data[cfg_btch->num_cfg_data];
1070
1071 if (chg->cb.operation == NB_OP_DESTROY)
1072 cfg_btch->cfg_data[cfg_btch->num_cfg_data]
1073 .req_type =
1074 MGMTD__CFG_DATA_REQ_TYPE__DELETE_DATA;
1075 else
1076 cfg_btch->cfg_data[cfg_btch->num_cfg_data]
1077 .req_type =
1078 MGMTD__CFG_DATA_REQ_TYPE__SET_DATA;
1079
1080 mgmt_yang_data_init(
1081 &cfg_btch->data[cfg_btch->num_cfg_data]);
1082 cfg_btch->cfg_data[cfg_btch->num_cfg_data].data =
1083 &cfg_btch->data[cfg_btch->num_cfg_data];
52a50ca1
CH
1084 cfg_btch->data[cfg_btch->num_cfg_data].xpath =
1085 strdup(xpath);
74335ceb
YR
1086
1087 mgmt_yang_data_value_init(
1088 &cfg_btch->value[cfg_btch->num_cfg_data]);
1089 cfg_btch->data[cfg_btch->num_cfg_data].value =
1090 &cfg_btch->value[cfg_btch->num_cfg_data];
1091 cfg_btch->value[cfg_btch->num_cfg_data].value_case =
1092 MGMTD__YANG_DATA_VALUE__VALUE_ENCODED_STR_VAL;
1093 cfg_btch->value[cfg_btch->num_cfg_data]
1094 .encoded_str_val = value;
1095 value = NULL;
1096
0327be91
CH
1097 if (subscr_info.xpath_subscr[id] &
1098 MGMT_SUBSCR_VALIDATE_CFG)
74335ceb
YR
1099 found_validator = true;
1100
0327be91
CH
1101 cmtcfg_req->subscr_info.xpath_subscr[id] |=
1102 subscr_info.xpath_subscr[id];
1103 MGMTD_TXN_DBG(" -- %s, {V:%d, N:%d}, batch-id: %" PRIu64
1104 " item:%d",
1105 adapter->name,
1106 (subscr_info.xpath_subscr[id] &
1107 MGMT_SUBSCR_VALIDATE_CFG) != 0,
1108 (subscr_info.xpath_subscr[id] &
1109 MGMT_SUBSCR_NOTIFY_CFG) != 0,
1110 cfg_btch->batch_id,
1111 (int)cfg_btch->num_cfg_data);
74335ceb
YR
1112
1113 cfg_btch->num_cfg_data++;
1114 num_chgs++;
1115 }
1116
1117 if (!found_validator) {
1118 snprintf(err_buf, sizeof(err_buf),
1119 "No validator module found for XPATH: '%s",
1120 xpath);
1121 MGMTD_TXN_ERR("***** %s", err_buf);
1122 }
52a50ca1
CH
1123
1124 free(xpath);
1125 xpath = NULL;
74335ceb
YR
1126 }
1127
1128 cmtcfg_req->cmt_stats->last_batch_cnt = num_chgs;
1129 if (!num_chgs) {
1130 (void)mgmt_txn_send_commit_cfg_reply(
1131 txn_req->txn, MGMTD_NO_CFG_CHANGES,
1132 "No changes found to commit!");
1133 goto mgmt_txn_create_config_batches_failed;
1134 }
1135
1136 cmtcfg_req->next_phase = MGMTD_COMMIT_PHASE_TXN_CREATE;
1137 return 0;
1138
1139mgmt_txn_create_config_batches_failed:
1140
1141 if (xpath)
1142 free(xpath);
1143
1144 return -1;
1145}
1146
1147static int mgmt_txn_prepare_config(struct mgmt_txn_ctx *txn)
1148{
1149 struct nb_context nb_ctx;
1150 struct nb_config *nb_config;
1151 struct nb_config_cbs changes;
1152 struct nb_config_cbs *cfg_chgs = NULL;
1153 int ret;
1154 bool del_cfg_chgs = false;
1155
1156 ret = 0;
1157 memset(&nb_ctx, 0, sizeof(nb_ctx));
1158 memset(&changes, 0, sizeof(changes));
1159 if (txn->commit_cfg_req->req.commit_cfg.cfg_chgs) {
1160 cfg_chgs = txn->commit_cfg_req->req.commit_cfg.cfg_chgs;
1161 del_cfg_chgs = true;
1162 goto mgmt_txn_prep_config_validation_done;
1163 }
1164
1165 if (txn->commit_cfg_req->req.commit_cfg.src_ds_id
1166 != MGMTD_DS_CANDIDATE) {
1167 (void)mgmt_txn_send_commit_cfg_reply(
1168 txn, MGMTD_INVALID_PARAM,
1169 "Source DS cannot be any other than CANDIDATE!");
1170 ret = -1;
1171 goto mgmt_txn_prepare_config_done;
1172 }
1173
1174 if (txn->commit_cfg_req->req.commit_cfg.dst_ds_id
1175 != MGMTD_DS_RUNNING) {
1176 (void)mgmt_txn_send_commit_cfg_reply(
1177 txn, MGMTD_INVALID_PARAM,
1178 "Destination DS cannot be any other than RUNNING!");
1179 ret = -1;
1180 goto mgmt_txn_prepare_config_done;
1181 }
1182
1183 if (!txn->commit_cfg_req->req.commit_cfg.src_ds_ctx) {
1184 (void)mgmt_txn_send_commit_cfg_reply(
1185 txn, MGMTD_INVALID_PARAM, "No such source datastore!");
1186 ret = -1;
1187 goto mgmt_txn_prepare_config_done;
1188 }
1189
1190 if (!txn->commit_cfg_req->req.commit_cfg.dst_ds_ctx) {
1191 (void)mgmt_txn_send_commit_cfg_reply(
1192 txn, MGMTD_INVALID_PARAM,
1193 "No such destination datastore!");
1194 ret = -1;
1195 goto mgmt_txn_prepare_config_done;
1196 }
1197
1198 if (txn->commit_cfg_req->req.commit_cfg.abort) {
1199 /*
1200 * This is a commit abort request. Return back success.
1201 * That should trigger a restore of Candidate datastore to
1202 * Running.
1203 */
1204 (void)mgmt_txn_send_commit_cfg_reply(txn, MGMTD_SUCCESS,
1205 NULL);
1206 goto mgmt_txn_prepare_config_done;
1207 }
1208
1209 nb_config = mgmt_ds_get_nb_config(
1210 txn->commit_cfg_req->req.commit_cfg.src_ds_ctx);
1211 if (!nb_config) {
1212 (void)mgmt_txn_send_commit_cfg_reply(
1213 txn, MGMTD_INTERNAL_ERROR,
1214 "Unable to retrieve Commit DS Config Tree!");
1215 ret = -1;
1216 goto mgmt_txn_prepare_config_done;
1217 }
1218
1219 /*
1220 * Check for diffs from scratch buffer. If found empty
1221 * get the diff from Candidate DS itself.
1222 */
1223 cfg_chgs = &nb_config->cfg_chgs;
1224 if (RB_EMPTY(nb_config_cbs, cfg_chgs)) {
1225 /*
1226 * This could be the case when the config is directly
1227 * loaded onto the candidate DS from a file. Get the
1228 * diff from a full comparison of the candidate and
1229 * running DSs.
1230 */
1231 nb_config_diff(
1232 mgmt_ds_get_nb_config(txn->commit_cfg_req->req
1233 .commit_cfg.dst_ds_ctx),
1234 nb_config, &changes);
1235 cfg_chgs = &changes;
1236 del_cfg_chgs = true;
1237 }
1238
1239 if (RB_EMPTY(nb_config_cbs, cfg_chgs)) {
1240 /*
1241 * This means there's no changes to commit whatsoever
1242 * is the source of the changes in config.
1243 */
1244 (void)mgmt_txn_send_commit_cfg_reply(
1245 txn, MGMTD_NO_CFG_CHANGES,
1246 "No changes found to be committed!");
1247 ret = -1;
1248 goto mgmt_txn_prepare_config_done;
1249 }
1250
1251#ifdef MGMTD_LOCAL_VALIDATIONS_ENABLED
1252 if (mm->perf_stats_en)
1253 gettimeofday(&txn->commit_cfg_req->req.commit_cfg.cmt_stats
1254 ->validate_start,
1255 NULL);
1256 /*
1257 * Validate YANG contents of the source DS and get the diff
1258 * between source and destination DS contents.
1259 */
1260 char err_buf[1024] = {0};
1261 nb_ctx.client = NB_CLIENT_MGMTD_SERVER;
1262 nb_ctx.user = (void *)txn;
83b78f43 1263
74335ceb
YR
1264 ret = nb_candidate_validate_yang(nb_config, false, err_buf,
1265 sizeof(err_buf) - 1);
1266 if (ret != NB_OK) {
1267 if (strncmp(err_buf, " ", strlen(err_buf)) == 0)
1268 strlcpy(err_buf, "Validation failed", sizeof(err_buf));
1269 (void)mgmt_txn_send_commit_cfg_reply(txn, MGMTD_INVALID_PARAM,
1270 err_buf);
1271 ret = -1;
1272 goto mgmt_txn_prepare_config_done;
1273 }
1274 /*
1275 * Perform application level validations locally on the MGMTD
1276 * process by calling application specific validation routines
1277 * loaded onto MGMTD process using libraries.
1278 */
1279 ret = nb_candidate_validate_code(&nb_ctx, nb_config, &changes, err_buf,
1280 sizeof(err_buf) - 1);
1281 if (ret != NB_OK) {
1282 if (strncmp(err_buf, " ", strlen(err_buf)) == 0)
1283 strlcpy(err_buf, "Validation failed", sizeof(err_buf));
1284 (void)mgmt_txn_send_commit_cfg_reply(txn, MGMTD_INVALID_PARAM,
1285 err_buf);
1286 ret = -1;
1287 goto mgmt_txn_prepare_config_done;
1288 }
1289
1290 if (txn->commit_cfg_req->req.commit_cfg.validate_only) {
1291 /*
1292 * This was a validate-only COMMIT request return success.
1293 */
1294 (void)mgmt_txn_send_commit_cfg_reply(txn, MGMTD_SUCCESS,
1295 NULL);
1296 goto mgmt_txn_prepare_config_done;
1297 }
1298#endif /* ifdef MGMTD_LOCAL_VALIDATIONS_ENABLED */
1299
1300mgmt_txn_prep_config_validation_done:
1301
1302 if (mm->perf_stats_en)
1303 gettimeofday(&txn->commit_cfg_req->req.commit_cfg.cmt_stats
1304 ->prep_cfg_start,
1305 NULL);
1306
1307 /*
1308 * Iterate over the diffs and create ordered batches of config
1309 * commands to be validated.
1310 */
1311 ret = mgmt_txn_create_config_batches(txn->commit_cfg_req, cfg_chgs);
1312 if (ret != 0) {
1313 ret = -1;
1314 goto mgmt_txn_prepare_config_done;
1315 }
1316
1317 /* Move to the Transaction Create Phase */
1318 txn->commit_cfg_req->req.commit_cfg.curr_phase =
1319 MGMTD_COMMIT_PHASE_TXN_CREATE;
1320 mgmt_txn_register_event(txn, MGMTD_TXN_PROC_COMMITCFG);
1321
1322 /*
1323 * Start the COMMIT Timeout Timer to abort Txn if things get stuck at
1324 * backend.
1325 */
1326 mgmt_txn_register_event(txn, MGMTD_TXN_COMMITCFG_TIMEOUT);
1327mgmt_txn_prepare_config_done:
1328
1329 if (cfg_chgs && del_cfg_chgs)
1330 nb_config_diff_del_changes(cfg_chgs);
1331
1332 return ret;
1333}
1334
1335static int mgmt_txn_send_be_txn_create(struct mgmt_txn_ctx *txn)
1336{
1337 enum mgmt_be_client_id id;
1338 struct mgmt_be_client_adapter *adapter;
1339 struct mgmt_commit_cfg_req *cmtcfg_req;
1340 struct mgmt_txn_be_cfg_batch *cfg_btch;
1341
1342 assert(txn->type == MGMTD_TXN_TYPE_CONFIG && txn->commit_cfg_req);
1343
1344 cmtcfg_req = &txn->commit_cfg_req->req.commit_cfg;
1345 FOREACH_MGMTD_BE_CLIENT_ID (id) {
0327be91 1346 if (cmtcfg_req->subscr_info.xpath_subscr[id]) {
74335ceb
YR
1347 adapter = mgmt_be_get_adapter_by_id(id);
1348 if (mgmt_be_create_txn(adapter, txn->txn_id)
1349 != 0) {
1350 (void)mgmt_txn_send_commit_cfg_reply(
1351 txn, MGMTD_INTERNAL_ERROR,
1352 "Could not send TXN_CREATE to backend adapter");
1353 return -1;
1354 }
1355
1356 FOREACH_TXN_CFG_BATCH_IN_LIST (
1357 &txn->commit_cfg_req->req.commit_cfg
1358 .curr_batches[id],
1359 cfg_btch)
1360 cfg_btch->comm_phase =
1361 MGMTD_COMMIT_PHASE_TXN_CREATE;
1362 }
1363 }
1364
1365 txn->commit_cfg_req->req.commit_cfg.next_phase =
1366 MGMTD_COMMIT_PHASE_SEND_CFG;
1367
1368 /*
1369 * Dont move the commit to next phase yet. Wait for the TXN_REPLY to
1370 * come back.
1371 */
1372
218625aa
CH
1373 MGMTD_TXN_DBG("txn-id: %" PRIu64 " session-id: %" PRIu64
1374 " Phase(Current:'%s', Next: '%s')",
1375 txn->txn_id, txn->session_id,
1376 mgmt_txn_commit_phase_str(txn, true),
1377 mgmt_txn_commit_phase_str(txn, false));
74335ceb
YR
1378
1379 return 0;
1380}
1381
1382static int
1383mgmt_txn_send_be_cfg_data(struct mgmt_txn_ctx *txn,
1384 struct mgmt_be_client_adapter *adapter)
1385{
1386 struct mgmt_commit_cfg_req *cmtcfg_req;
1387 struct mgmt_txn_be_cfg_batch *cfg_btch;
1388 struct mgmt_be_cfgreq cfg_req = {0};
1389 size_t num_batches, indx;
1390
1391 assert(txn->type == MGMTD_TXN_TYPE_CONFIG && txn->commit_cfg_req);
1392
1393 cmtcfg_req = &txn->commit_cfg_req->req.commit_cfg;
0327be91 1394 assert(cmtcfg_req->subscr_info.xpath_subscr[adapter->id]);
74335ceb
YR
1395
1396 indx = 0;
1397 num_batches =
1398 mgmt_txn_batches_count(&cmtcfg_req->curr_batches[adapter->id]);
1399 FOREACH_TXN_CFG_BATCH_IN_LIST (&cmtcfg_req->curr_batches[adapter->id],
1400 cfg_btch) {
1401 assert(cmtcfg_req->next_phase == MGMTD_COMMIT_PHASE_SEND_CFG);
1402
1403 cfg_req.cfgdata_reqs = cfg_btch->cfg_datap;
1404 cfg_req.num_reqs = cfg_btch->num_cfg_data;
1405 indx++;
1406 if (mgmt_be_send_cfg_data_create_req(
1407 adapter, txn->txn_id, cfg_btch->batch_id, &cfg_req,
1408 indx == num_batches ? true : false)
1409 != 0) {
1410 (void)mgmt_txn_send_commit_cfg_reply(
1411 txn, MGMTD_INTERNAL_ERROR,
1412 "Internal Error! Could not send config data to backend!");
1413 MGMTD_TXN_ERR(
218625aa
CH
1414 "Could not send CFGDATA_CREATE txn-id: %" PRIu64
1415 " batch-id: %" PRIu64 " to client '%s",
1416 txn->txn_id, cfg_btch->batch_id, adapter->name);
74335ceb
YR
1417 return -1;
1418 }
1419
1420 cmtcfg_req->cmt_stats->last_num_cfgdata_reqs++;
1421 mgmt_move_txn_cfg_batch_to_next(
1422 cmtcfg_req, cfg_btch,
1423 &cmtcfg_req->curr_batches[adapter->id],
1424 &cmtcfg_req->next_batches[adapter->id], true,
1425 MGMTD_COMMIT_PHASE_SEND_CFG);
1426 }
1427
1428 /*
1429 * This could ne the last Backend Client to send CFGDATA_CREATE_REQ to.
1430 * Try moving the commit to next phase.
1431 */
1432 mgmt_try_move_commit_to_next_phase(txn, cmtcfg_req);
1433
1434 return 0;
1435}
1436
1437static int
1438mgmt_txn_send_be_txn_delete(struct mgmt_txn_ctx *txn,
1439 struct mgmt_be_client_adapter *adapter)
1440{
1441 struct mgmt_commit_cfg_req *cmtcfg_req;
1442 struct mgmt_txn_be_cfg_batch *cfg_btch;
1443
1444 assert(txn->type == MGMTD_TXN_TYPE_CONFIG && txn->commit_cfg_req);
1445
1446 cmtcfg_req = &txn->commit_cfg_req->req.commit_cfg;
0327be91 1447 if (cmtcfg_req->subscr_info.xpath_subscr[adapter->id]) {
74335ceb
YR
1448 adapter = mgmt_be_get_adapter_by_id(adapter->id);
1449 (void)mgmt_be_destroy_txn(adapter, txn->txn_id);
1450
1451 FOREACH_TXN_CFG_BATCH_IN_LIST (
1452 &txn->commit_cfg_req->req.commit_cfg
1453 .curr_batches[adapter->id],
1454 cfg_btch)
1455 cfg_btch->comm_phase = MGMTD_COMMIT_PHASE_TXN_DELETE;
1456 }
1457
1458 return 0;
1459}
1460
e6685141 1461static void mgmt_txn_cfg_commit_timedout(struct event *thread)
74335ceb
YR
1462{
1463 struct mgmt_txn_ctx *txn;
1464
e16d030c 1465 txn = (struct mgmt_txn_ctx *)EVENT_ARG(thread);
74335ceb
YR
1466 assert(txn);
1467
1468 assert(txn->type == MGMTD_TXN_TYPE_CONFIG);
1469
1470 if (!txn->commit_cfg_req)
1471 return;
1472
218625aa
CH
1473 MGMTD_TXN_ERR("Backend timeout txn-id: %" PRIu64 " aborting commit",
1474 txn->txn_id);
74335ceb
YR
1475
1476 /*
1477 * Send a COMMIT_CONFIG_REPLY with failure.
1478 * NOTE: The transaction cleanup will be triggered from Front-end
1479 * adapter.
1480 */
1481 mgmt_txn_send_commit_cfg_reply(
1482 txn, MGMTD_INTERNAL_ERROR,
1483 "Operation on the backend timed-out. Aborting commit!");
1484}
1485
1486/*
1487 * Send CFG_APPLY_REQs to all the backend client.
1488 *
1489 * NOTE: This is always dispatched when all CFGDATA_CREATE_REQs
1490 * for all backend clients has been generated. Please see
1491 * mgmt_txn_register_event() and mgmt_txn_process_commit_cfg()
1492 * for details.
1493 */
1494static int mgmt_txn_send_be_cfg_apply(struct mgmt_txn_ctx *txn)
1495{
1496 enum mgmt_be_client_id id;
1497 struct mgmt_be_client_adapter *adapter;
1498 struct mgmt_commit_cfg_req *cmtcfg_req;
1499 struct mgmt_txn_batches_head *btch_list;
1500 struct mgmt_txn_be_cfg_batch *cfg_btch;
1501
1502 assert(txn->type == MGMTD_TXN_TYPE_CONFIG && txn->commit_cfg_req);
1503
1504 cmtcfg_req = &txn->commit_cfg_req->req.commit_cfg;
1505 if (cmtcfg_req->validate_only) {
1506 /*
1507 * If this was a validate-only COMMIT request return success.
1508 */
1509 (void)mgmt_txn_send_commit_cfg_reply(txn, MGMTD_SUCCESS,
1510 NULL);
1511 return 0;
1512 }
1513
1514 FOREACH_MGMTD_BE_CLIENT_ID (id) {
0327be91
CH
1515 if (cmtcfg_req->subscr_info.xpath_subscr[id] &
1516 MGMT_SUBSCR_NOTIFY_CFG) {
74335ceb
YR
1517 adapter = mgmt_be_get_adapter_by_id(id);
1518 if (!adapter)
1519 return -1;
1520
1521 btch_list = &cmtcfg_req->curr_batches[id];
1522 if (mgmt_be_send_cfg_apply_req(adapter, txn->txn_id)
1523 != 0) {
1524 (void)mgmt_txn_send_commit_cfg_reply(
1525 txn, MGMTD_INTERNAL_ERROR,
1526 "Could not send CFG_APPLY_REQ to backend adapter");
1527 return -1;
1528 }
1529 cmtcfg_req->cmt_stats->last_num_apply_reqs++;
1530
1531 UNSET_FLAG(adapter->flags,
1532 MGMTD_BE_ADAPTER_FLAGS_CFG_SYNCED);
1533
1534 FOREACH_TXN_CFG_BATCH_IN_LIST (btch_list, cfg_btch)
1535 cfg_btch->comm_phase =
1536 MGMTD_COMMIT_PHASE_APPLY_CFG;
1537 }
1538 }
1539
1540 txn->commit_cfg_req->req.commit_cfg.next_phase =
1541 MGMTD_COMMIT_PHASE_TXN_DELETE;
1542
1543 /*
1544 * Dont move the commit to next phase yet. Wait for all VALIDATE_REPLIES
1545 * to come back.
1546 */
1547
1548 return 0;
1549}
1550
e6685141 1551static void mgmt_txn_process_commit_cfg(struct event *thread)
74335ceb
YR
1552{
1553 struct mgmt_txn_ctx *txn;
1554 struct mgmt_commit_cfg_req *cmtcfg_req;
1555
e16d030c 1556 txn = (struct mgmt_txn_ctx *)EVENT_ARG(thread);
74335ceb
YR
1557 assert(txn);
1558
218625aa
CH
1559 MGMTD_TXN_DBG("Processing COMMIT_CONFIG for txn-id: %" PRIu64
1560 " session-id: %" PRIu64
1561 " Phase(Current:'%s', Next: '%s')",
1562 txn->txn_id, txn->session_id,
1563 mgmt_txn_commit_phase_str(txn, true),
1564 mgmt_txn_commit_phase_str(txn, false));
74335ceb
YR
1565
1566 assert(txn->commit_cfg_req);
1567 cmtcfg_req = &txn->commit_cfg_req->req.commit_cfg;
1568 switch (cmtcfg_req->curr_phase) {
1569 case MGMTD_COMMIT_PHASE_PREPARE_CFG:
1570 mgmt_txn_prepare_config(txn);
1571 break;
1572 case MGMTD_COMMIT_PHASE_TXN_CREATE:
1573 if (mm->perf_stats_en)
1574 gettimeofday(&cmtcfg_req->cmt_stats->txn_create_start,
1575 NULL);
1576 /*
1577 * Send TXN_CREATE_REQ to all Backend now.
1578 */
1579 mgmt_txn_send_be_txn_create(txn);
1580 break;
1581 case MGMTD_COMMIT_PHASE_SEND_CFG:
1582 if (mm->perf_stats_en)
1583 gettimeofday(&cmtcfg_req->cmt_stats->send_cfg_start,
1584 NULL);
1585 /*
1586 * All CFGDATA_CREATE_REQ should have been sent to
1587 * Backend by now.
1588 */
1589#ifndef MGMTD_LOCAL_VALIDATIONS_ENABLED
1590 assert(cmtcfg_req->next_phase == MGMTD_COMMIT_PHASE_APPLY_CFG);
1591 MGMTD_TXN_DBG(
218625aa
CH
1592 "txn-id: %" PRIu64 " session-id: %" PRIu64
1593 " trigger sending CFG_VALIDATE_REQ to all backend clients",
1594 txn->txn_id, txn->session_id);
74335ceb
YR
1595#else /* ifndef MGMTD_LOCAL_VALIDATIONS_ENABLED */
1596 assert(cmtcfg_req->next_phase == MGMTD_COMMIT_PHASE_APPLY_CFG);
1597 MGMTD_TXN_DBG(
218625aa
CH
1598 "txn-id: %" PRIu64 " session-id: %" PRIu64
1599 " trigger sending CFG_APPLY_REQ to all backend clients",
1600 txn->txn_id, txn->session_id);
74335ceb
YR
1601#endif /* ifndef MGMTD_LOCAL_VALIDATIONS_ENABLED */
1602 break;
1603 case MGMTD_COMMIT_PHASE_APPLY_CFG:
1604 if (mm->perf_stats_en)
1605 gettimeofday(&cmtcfg_req->cmt_stats->apply_cfg_start,
1606 NULL);
1607 /*
1608 * We should have received successful CFG_VALIDATE_REPLY from
1609 * all concerned Backend Clients by now. Send out the
1610 * CFG_APPLY_REQs now.
1611 */
1612 mgmt_txn_send_be_cfg_apply(txn);
1613 break;
1614 case MGMTD_COMMIT_PHASE_TXN_DELETE:
1615 if (mm->perf_stats_en)
1616 gettimeofday(&cmtcfg_req->cmt_stats->txn_del_start,
1617 NULL);
1618 /*
1619 * We would have sent TXN_DELETE_REQ to all backend by now.
1620 * Send a successful CONFIG_COMMIT_REPLY back to front-end.
1621 * NOTE: This should also trigger DS merge/unlock and Txn
1622 * cleanup. Please see mgmt_fe_send_commit_cfg_reply() for
1623 * more details.
1624 */
e16d030c 1625 EVENT_OFF(txn->comm_cfg_timeout);
74335ceb
YR
1626 mgmt_txn_send_commit_cfg_reply(txn, MGMTD_SUCCESS, NULL);
1627 break;
1628 case MGMTD_COMMIT_PHASE_MAX:
1629 break;
1630 }
1631
218625aa
CH
1632 MGMTD_TXN_DBG("txn-id:%" PRIu64 " session-id: %" PRIu64
1633 " phase updated to (current:'%s', next: '%s')",
1634 txn->txn_id, txn->session_id,
1635 mgmt_txn_commit_phase_str(txn, true),
1636 mgmt_txn_commit_phase_str(txn, false));
74335ceb
YR
1637}
1638
1639static void mgmt_init_get_data_reply(struct mgmt_get_data_reply *get_reply)
1640{
1641 size_t indx;
1642
1643 for (indx = 0; indx < array_size(get_reply->reply_data); indx++)
1644 get_reply->reply_datap[indx] = &get_reply->reply_data[indx];
1645}
1646
1647static void mgmt_reset_get_data_reply(struct mgmt_get_data_reply *get_reply)
1648{
1649 int indx;
1650
1651 for (indx = 0; indx < get_reply->num_reply; indx++) {
1652 if (get_reply->reply_xpathp[indx]) {
1653 free(get_reply->reply_xpathp[indx]);
1654 get_reply->reply_xpathp[indx] = 0;
1655 }
1656 if (get_reply->reply_data[indx].xpath) {
1657 zlog_debug("%s free xpath %p", __func__,
1658 get_reply->reply_data[indx].xpath);
1659 free(get_reply->reply_data[indx].xpath);
1660 get_reply->reply_data[indx].xpath = 0;
1661 }
1662 }
1663
1664 get_reply->num_reply = 0;
1665 memset(&get_reply->data_reply, 0, sizeof(get_reply->data_reply));
1666 memset(&get_reply->reply_data, 0, sizeof(get_reply->reply_data));
1667 memset(&get_reply->reply_datap, 0, sizeof(get_reply->reply_datap));
1668
1669 memset(&get_reply->reply_value, 0, sizeof(get_reply->reply_value));
1670
1671 mgmt_init_get_data_reply(get_reply);
1672}
1673
1674static void mgmt_reset_get_data_reply_buf(struct mgmt_get_data_req *get_data)
1675{
1676 if (get_data->reply)
1677 mgmt_reset_get_data_reply(get_data->reply);
1678}
1679
1680static void mgmt_txn_send_getcfg_reply_data(struct mgmt_txn_req *txn_req,
1681 struct mgmt_get_data_req *get_req)
1682{
1683 struct mgmt_get_data_reply *get_reply;
1684 Mgmtd__YangDataReply *data_reply;
1685
1686 get_reply = get_req->reply;
1687 if (!get_reply)
1688 return;
1689
1690 data_reply = &get_reply->data_reply;
1691 mgmt_yang_data_reply_init(data_reply);
1692 data_reply->n_data = get_reply->num_reply;
1693 data_reply->data = get_reply->reply_datap;
1694 data_reply->next_indx =
1695 (!get_reply->last_batch ? get_req->total_reply : -1);
1696
218625aa
CH
1697 MGMTD_TXN_DBG("Sending %zu Get-Config/Data replies next-index:%" PRId64,
1698 data_reply->n_data, data_reply->next_indx);
74335ceb
YR
1699
1700 switch (txn_req->req_event) {
1701 case MGMTD_TXN_PROC_GETCFG:
1702 if (mgmt_fe_send_get_cfg_reply(
1703 txn_req->txn->session_id, txn_req->txn->txn_id,
1704 get_req->ds_id, txn_req->req_id, MGMTD_SUCCESS,
1705 data_reply, NULL)
1706 != 0) {
1707 MGMTD_TXN_ERR(
218625aa
CH
1708 "Failed to send GET-CONFIG-REPLY txn-id: %" PRIu64
1709 " session-id: %" PRIu64 " req-id: %" PRIu64,
1710 txn_req->txn->txn_id, txn_req->txn->session_id,
1711 txn_req->req_id);
74335ceb
YR
1712 }
1713 break;
1714 case MGMTD_TXN_PROC_GETDATA:
1715 if (mgmt_fe_send_get_data_reply(
1716 txn_req->txn->session_id, txn_req->txn->txn_id,
1717 get_req->ds_id, txn_req->req_id, MGMTD_SUCCESS,
1718 data_reply, NULL)
1719 != 0) {
1720 MGMTD_TXN_ERR(
218625aa
CH
1721 "Failed to send GET-DATA-REPLY txn-id: %" PRIu64
1722 " session-id: %" PRIu64 " req-id: %" PRIu64,
1723 txn_req->txn->txn_id, txn_req->txn->session_id,
1724 txn_req->req_id);
74335ceb
YR
1725 }
1726 break;
1727 case MGMTD_TXN_PROC_SETCFG:
1728 case MGMTD_TXN_PROC_COMMITCFG:
1729 case MGMTD_TXN_COMMITCFG_TIMEOUT:
1730 case MGMTD_TXN_CLEANUP:
1731 MGMTD_TXN_ERR("Invalid Txn-Req-Event %u",
1732 txn_req->req_event);
1733 break;
1734 }
1735
1736 /*
1737 * Reset reply buffer for next reply.
1738 */
1739 mgmt_reset_get_data_reply_buf(get_req);
1740}
1741
1742static void mgmt_txn_iter_and_send_get_cfg_reply(struct mgmt_ds_ctx *ds_ctx,
acd7aea0
CH
1743 const char *xpath,
1744 struct lyd_node *node,
1745 struct nb_node *nb_node,
1746 void *ctx)
74335ceb
YR
1747{
1748 struct mgmt_txn_req *txn_req;
1749 struct mgmt_get_data_req *get_req;
1750 struct mgmt_get_data_reply *get_reply;
1751 Mgmtd__YangData *data;
1752 Mgmtd__YangDataValue *data_value;
1753
1754 txn_req = (struct mgmt_txn_req *)ctx;
1755 if (!txn_req)
acd7aea0 1756 return;
74335ceb
YR
1757
1758 if (!(node->schema->nodetype & LYD_NODE_TERM))
acd7aea0 1759 return;
74335ceb
YR
1760
1761 assert(txn_req->req_event == MGMTD_TXN_PROC_GETCFG
1762 || txn_req->req_event == MGMTD_TXN_PROC_GETDATA);
1763
1764 get_req = txn_req->req.get_data;
1765 assert(get_req);
1766 get_reply = get_req->reply;
1767 data = &get_reply->reply_data[get_reply->num_reply];
1768 data_value = &get_reply->reply_value[get_reply->num_reply];
1769
1770 mgmt_yang_data_init(data);
acd7aea0 1771 data->xpath = strdup(xpath);
74335ceb
YR
1772 mgmt_yang_data_value_init(data_value);
1773 data_value->value_case = MGMTD__YANG_DATA_VALUE__VALUE_ENCODED_STR_VAL;
1774 data_value->encoded_str_val = (char *)lyd_get_value(node);
1775 data->value = data_value;
1776
1777 get_reply->num_reply++;
1778 get_req->total_reply++;
1779 MGMTD_TXN_DBG(" [%d] XPATH: '%s', Value: '%s'", get_req->total_reply,
1780 data->xpath, data_value->encoded_str_val);
1781
1782 if (get_reply->num_reply == MGMTD_MAX_NUM_DATA_REPLY_IN_BATCH)
1783 mgmt_txn_send_getcfg_reply_data(txn_req, get_req);
74335ceb
YR
1784}
1785
1786static int mgmt_txn_get_config(struct mgmt_txn_ctx *txn,
1787 struct mgmt_txn_req *txn_req,
1788 struct mgmt_ds_ctx *ds_ctx)
1789{
74335ceb
YR
1790 int indx;
1791 struct mgmt_get_data_req *get_data;
1792 struct mgmt_get_data_reply *get_reply;
1793
74335ceb
YR
1794 get_data = txn_req->req.get_data;
1795
1796 if (!get_data->reply) {
1797 get_data->reply = XCALLOC(MTYPE_MGMTD_TXN_GETDATA_REPLY,
1798 sizeof(struct mgmt_get_data_reply));
1799 if (!get_data->reply) {
1800 mgmt_fe_send_get_cfg_reply(
1801 txn->session_id, txn->txn_id,
1802 get_data->ds_id, txn_req->req_id,
1803 MGMTD_INTERNAL_ERROR, NULL,
1804 "Internal error: Unable to allocate reply buffers!");
1805 goto mgmt_txn_get_config_failed;
1806 }
1807 }
1808
1809 /*
1810 * Read data contents from the DS and respond back directly.
1811 * No need to go to backend for getting data.
1812 */
1813 get_reply = get_data->reply;
1814 for (indx = 0; indx < get_data->num_xpaths; indx++) {
1815 MGMTD_TXN_DBG("Trying to get all data under '%s'",
1816 get_data->xpaths[indx]);
1817 mgmt_init_get_data_reply(get_reply);
acd7aea0
CH
1818 /*
1819 * mgmt_ds_iter_data works on path prefixes, but the user may
1820 * want to also use an xpath regexp we need to add this
1821 * functionality.
1822 */
74335ceb
YR
1823 if (mgmt_ds_iter_data(get_data->ds_ctx, get_data->xpaths[indx],
1824 mgmt_txn_iter_and_send_get_cfg_reply,
acd7aea0 1825 (void *)txn_req) == -1) {
74335ceb
YR
1826 MGMTD_TXN_DBG("Invalid Xpath '%s",
1827 get_data->xpaths[indx]);
1828 mgmt_fe_send_get_cfg_reply(
1829 txn->session_id, txn->txn_id,
1830 get_data->ds_id, txn_req->req_id,
1831 MGMTD_INTERNAL_ERROR, NULL, "Invalid xpath");
1832 goto mgmt_txn_get_config_failed;
1833 }
1834 MGMTD_TXN_DBG("Got %d remaining data-replies for xpath '%s'",
1835 get_reply->num_reply, get_data->xpaths[indx]);
1836 get_reply->last_batch = true;
1837 mgmt_txn_send_getcfg_reply_data(txn_req, get_data);
1838 }
1839
1840mgmt_txn_get_config_failed:
1841
93d4e355 1842 /*
1843 * Delete the txn request. It will also remove it from request
1844 * list.
1845 */
1846 mgmt_txn_req_free(&txn_req);
74335ceb
YR
1847
1848 return 0;
1849}
1850
e6685141 1851static void mgmt_txn_process_get_cfg(struct event *thread)
74335ceb
YR
1852{
1853 struct mgmt_txn_ctx *txn;
1854 struct mgmt_txn_req *txn_req;
1855 struct mgmt_ds_ctx *ds_ctx;
1856 int num_processed = 0;
1857 bool error;
1858
e16d030c 1859 txn = (struct mgmt_txn_ctx *)EVENT_ARG(thread);
74335ceb
YR
1860 assert(txn);
1861
218625aa
CH
1862 MGMTD_TXN_DBG("Processing %zu GET_CONFIG requests txn-id: %" PRIu64
1863 " session-id: %" PRIu64,
1864 mgmt_txn_reqs_count(&txn->get_cfg_reqs), txn->txn_id,
1865 txn->session_id);
74335ceb
YR
1866
1867 FOREACH_TXN_REQ_IN_LIST (&txn->get_cfg_reqs, txn_req) {
1868 error = false;
1869 assert(txn_req->req_event == MGMTD_TXN_PROC_GETCFG);
1870 ds_ctx = txn_req->req.get_data->ds_ctx;
1871 if (!ds_ctx) {
1872 mgmt_fe_send_get_cfg_reply(
1873 txn->session_id, txn->txn_id,
1874 txn_req->req.get_data->ds_id, txn_req->req_id,
1875 MGMTD_INTERNAL_ERROR, NULL,
1876 "No such datastore!");
1877 error = true;
1878 goto mgmt_txn_process_get_cfg_done;
1879 }
1880
1881 if (mgmt_txn_get_config(txn, txn_req, ds_ctx) != 0) {
1882 MGMTD_TXN_ERR(
218625aa
CH
1883 "Unable to retrieve config from DS %d txn-id: %" PRIu64
1884 " session-id: %" PRIu64 " req-id: %" PRIu64,
1885 txn_req->req.get_data->ds_id, txn->txn_id,
1886 txn->session_id, txn_req->req_id);
74335ceb
YR
1887 error = true;
1888 }
1889
1890 mgmt_txn_process_get_cfg_done:
1891
1892 if (error) {
1893 /*
1894 * Delete the txn request.
1895 * Note: The following will remove it from the list
1896 * as well.
1897 */
1898 mgmt_txn_req_free(&txn_req);
1899 }
1900
1901 /*
1902 * Else the transaction would have been already deleted or
1903 * moved to corresponding pending list. No need to delete it.
1904 */
1905 num_processed++;
1906 if (num_processed == MGMTD_TXN_MAX_NUM_GETCFG_PROC)
1907 break;
1908 }
1909
1910 if (mgmt_txn_reqs_count(&txn->get_cfg_reqs)) {
1911 MGMTD_TXN_DBG(
1912 "Processed maximum number of Get-Config requests (%d/%d). Rescheduling for rest.",
1913 num_processed, MGMTD_TXN_MAX_NUM_GETCFG_PROC);
1914 mgmt_txn_register_event(txn, MGMTD_TXN_PROC_GETCFG);
1915 }
1916}
1917
e6685141 1918static void mgmt_txn_process_get_data(struct event *thread)
74335ceb
YR
1919{
1920 struct mgmt_txn_ctx *txn;
1921 struct mgmt_txn_req *txn_req;
1922 struct mgmt_ds_ctx *ds_ctx;
1923 int num_processed = 0;
1924 bool error;
1925
e16d030c 1926 txn = (struct mgmt_txn_ctx *)EVENT_ARG(thread);
74335ceb
YR
1927 assert(txn);
1928
218625aa
CH
1929 MGMTD_TXN_DBG("Processing %zu GET_DATA requests txn-id: %" PRIu64
1930 " session-id: %" PRIu64,
1931 mgmt_txn_reqs_count(&txn->get_data_reqs), txn->txn_id,
1932 txn->session_id);
74335ceb
YR
1933
1934 FOREACH_TXN_REQ_IN_LIST (&txn->get_data_reqs, txn_req) {
1935 error = false;
1936 assert(txn_req->req_event == MGMTD_TXN_PROC_GETDATA);
1937 ds_ctx = txn_req->req.get_data->ds_ctx;
1938 if (!ds_ctx) {
1939 mgmt_fe_send_get_data_reply(
1940 txn->session_id, txn->txn_id,
1941 txn_req->req.get_data->ds_id, txn_req->req_id,
1942 MGMTD_INTERNAL_ERROR, NULL,
1943 "No such datastore!");
1944 error = true;
1945 goto mgmt_txn_process_get_data_done;
1946 }
1947
1948 if (mgmt_ds_is_config(ds_ctx)) {
1949 if (mgmt_txn_get_config(txn, txn_req, ds_ctx)
1950 != 0) {
1951 MGMTD_TXN_ERR(
218625aa
CH
1952 "Unable to retrieve config from DS %d txn-id %" PRIu64
1953 " session-id: %" PRIu64
1954 " req-id: %" PRIu64,
1955 txn_req->req.get_data->ds_id,
1956 txn->txn_id, txn->session_id,
1957 txn_req->req_id);
74335ceb
YR
1958 error = true;
1959 }
1960 } else {
1961 /*
1962 * TODO: Trigger GET procedures for Backend
1963 * For now return back error.
1964 */
1965 mgmt_fe_send_get_data_reply(
1966 txn->session_id, txn->txn_id,
1967 txn_req->req.get_data->ds_id, txn_req->req_id,
1968 MGMTD_INTERNAL_ERROR, NULL,
1969 "GET-DATA on Oper DS is not supported yet!");
1970 error = true;
1971 }
1972
1973 mgmt_txn_process_get_data_done:
1974
1975 if (error) {
1976 /*
1977 * Delete the txn request.
1978 * Note: The following will remove it from the list
1979 * as well.
1980 */
1981 mgmt_txn_req_free(&txn_req);
1982 }
1983
1984 /*
1985 * Else the transaction would have been already deleted or
1986 * moved to corresponding pending list. No need to delete it.
1987 */
1988 num_processed++;
1989 if (num_processed == MGMTD_TXN_MAX_NUM_GETDATA_PROC)
1990 break;
1991 }
1992
1993 if (mgmt_txn_reqs_count(&txn->get_data_reqs)) {
1994 MGMTD_TXN_DBG(
1995 "Processed maximum number of Get-Data requests (%d/%d). Rescheduling for rest.",
1996 num_processed, MGMTD_TXN_MAX_NUM_GETDATA_PROC);
1997 mgmt_txn_register_event(txn, MGMTD_TXN_PROC_GETDATA);
1998 }
1999}
2000
2001static struct mgmt_txn_ctx *
2002mgmt_fe_find_txn_by_session_id(struct mgmt_master *cm, uint64_t session_id,
2003 enum mgmt_txn_type type)
2004{
2005 struct mgmt_txn_ctx *txn;
2006
2007 FOREACH_TXN_IN_LIST (cm, txn) {
2008 if (txn->session_id == session_id && txn->type == type)
2009 return txn;
2010 }
2011
2012 return NULL;
2013}
2014
2015static struct mgmt_txn_ctx *mgmt_txn_create_new(uint64_t session_id,
2016 enum mgmt_txn_type type)
2017{
2018 struct mgmt_txn_ctx *txn = NULL;
2019
2020 /*
2021 * For 'CONFIG' transaction check if one is already created
2022 * or not.
2023 */
2024 if (type == MGMTD_TXN_TYPE_CONFIG && mgmt_txn_mm->cfg_txn) {
2025 if (mgmt_config_txn_in_progress() == session_id)
2026 txn = mgmt_txn_mm->cfg_txn;
2027 goto mgmt_create_txn_done;
2028 }
2029
2030 txn = mgmt_fe_find_txn_by_session_id(mgmt_txn_mm, session_id,
2031 type);
2032 if (!txn) {
2033 txn = XCALLOC(MTYPE_MGMTD_TXN, sizeof(struct mgmt_txn_ctx));
2034 assert(txn);
2035
2036 txn->session_id = session_id;
2037 txn->type = type;
74335ceb
YR
2038 mgmt_txns_add_tail(&mgmt_txn_mm->txn_list, txn);
2039 mgmt_txn_reqs_init(&txn->set_cfg_reqs);
2040 mgmt_txn_reqs_init(&txn->get_cfg_reqs);
2041 mgmt_txn_reqs_init(&txn->get_data_reqs);
2042 mgmt_txn_reqs_init(&txn->pending_get_datas);
2043 txn->commit_cfg_req = NULL;
2044 txn->refcount = 0;
2045 if (!mgmt_txn_mm->next_txn_id)
2046 mgmt_txn_mm->next_txn_id++;
2047 txn->txn_id = mgmt_txn_mm->next_txn_id++;
2048 hash_get(mgmt_txn_mm->txn_hash, txn, hash_alloc_intern);
2049
218625aa
CH
2050 MGMTD_TXN_DBG("Added new '%s' txn-id: %" PRIu64,
2051 mgmt_txn_type2str(type), txn->txn_id);
74335ceb
YR
2052
2053 if (type == MGMTD_TXN_TYPE_CONFIG)
2054 mgmt_txn_mm->cfg_txn = txn;
2055
2056 MGMTD_TXN_LOCK(txn);
2057 }
2058
2059mgmt_create_txn_done:
2060 return txn;
2061}
2062
2063static void mgmt_txn_delete(struct mgmt_txn_ctx **txn)
2064{
2065 MGMTD_TXN_UNLOCK(txn);
2066}
2067
2068static unsigned int mgmt_txn_hash_key(const void *data)
2069{
2070 const struct mgmt_txn_ctx *txn = data;
2071
2072 return jhash2((uint32_t *) &txn->txn_id,
2073 sizeof(txn->txn_id) / sizeof(uint32_t), 0);
2074}
2075
2076static bool mgmt_txn_hash_cmp(const void *d1, const void *d2)
2077{
2078 const struct mgmt_txn_ctx *txn1 = d1;
2079 const struct mgmt_txn_ctx *txn2 = d2;
2080
2081 return (txn1->txn_id == txn2->txn_id);
2082}
2083
2084static void mgmt_txn_hash_free(void *data)
2085{
2086 struct mgmt_txn_ctx *txn = data;
2087
2088 mgmt_txn_delete(&txn);
2089}
2090
2091static void mgmt_txn_hash_init(void)
2092{
2093 if (!mgmt_txn_mm || mgmt_txn_mm->txn_hash)
2094 return;
2095
2096 mgmt_txn_mm->txn_hash = hash_create(mgmt_txn_hash_key,
2097 mgmt_txn_hash_cmp,
2098 "MGMT Transactions");
2099}
2100
2101static void mgmt_txn_hash_destroy(void)
2102{
2103 if (!mgmt_txn_mm || !mgmt_txn_mm->txn_hash)
2104 return;
2105
2106 hash_clean(mgmt_txn_mm->txn_hash,
2107 mgmt_txn_hash_free);
2108 hash_free(mgmt_txn_mm->txn_hash);
2109 mgmt_txn_mm->txn_hash = NULL;
2110}
2111
2112static inline struct mgmt_txn_ctx *
2113mgmt_txn_id2ctx(uint64_t txn_id)
2114{
2115 struct mgmt_txn_ctx key = {0};
2116 struct mgmt_txn_ctx *txn;
2117
2118 if (!mgmt_txn_mm || !mgmt_txn_mm->txn_hash)
2119 return NULL;
2120
2121 key.txn_id = txn_id;
2122 txn = hash_lookup(mgmt_txn_mm->txn_hash, &key);
2123
2124 return txn;
2125}
2126
2127static void mgmt_txn_lock(struct mgmt_txn_ctx *txn, const char *file,
2128 int line)
2129{
2130 txn->refcount++;
218625aa
CH
2131 MGMTD_TXN_DBG("%s:%d --> Lock %s txn-id: %" PRIu64 " refcnt: %d", file,
2132 line, mgmt_txn_type2str(txn->type), txn->txn_id,
2133 txn->refcount);
74335ceb
YR
2134}
2135
2136static void mgmt_txn_unlock(struct mgmt_txn_ctx **txn, const char *file,
2137 int line)
2138{
2139 assert(*txn && (*txn)->refcount);
2140
2141 (*txn)->refcount--;
218625aa
CH
2142 MGMTD_TXN_DBG("%s:%d --> Unlock %s txn-id: %" PRIu64 " refcnt: %d",
2143 file, line, mgmt_txn_type2str((*txn)->type),
2144 (*txn)->txn_id, (*txn)->refcount);
74335ceb
YR
2145 if (!(*txn)->refcount) {
2146 if ((*txn)->type == MGMTD_TXN_TYPE_CONFIG)
2147 if (mgmt_txn_mm->cfg_txn == *txn)
2148 mgmt_txn_mm->cfg_txn = NULL;
e16d030c
DS
2149 EVENT_OFF((*txn)->proc_get_cfg);
2150 EVENT_OFF((*txn)->proc_get_data);
2151 EVENT_OFF((*txn)->proc_comm_cfg);
2152 EVENT_OFF((*txn)->comm_cfg_timeout);
74335ceb
YR
2153 hash_release(mgmt_txn_mm->txn_hash, *txn);
2154 mgmt_txns_del(&mgmt_txn_mm->txn_list, *txn);
2155
218625aa
CH
2156 MGMTD_TXN_DBG("Deleted %s txn-id: %" PRIu64
2157 " session-id: %" PRIu64,
2158 mgmt_txn_type2str((*txn)->type), (*txn)->txn_id,
2159 (*txn)->session_id);
74335ceb
YR
2160
2161 XFREE(MTYPE_MGMTD_TXN, *txn);
2162 }
2163
2164 *txn = NULL;
2165}
2166
2167static void mgmt_txn_cleanup_txn(struct mgmt_txn_ctx **txn)
2168{
2169 /* TODO: Any other cleanup applicable */
2170
2171 mgmt_txn_delete(txn);
2172}
2173
2174static void
2175mgmt_txn_cleanup_all_txns(void)
2176{
2177 struct mgmt_txn_ctx *txn;
2178
2179 if (!mgmt_txn_mm || !mgmt_txn_mm->txn_hash)
2180 return;
2181
2182 FOREACH_TXN_IN_LIST (mgmt_txn_mm, txn)
2183 mgmt_txn_cleanup_txn(&txn);
2184}
2185
e6685141 2186static void mgmt_txn_cleanup(struct event *thread)
74335ceb
YR
2187{
2188 struct mgmt_txn_ctx *txn;
2189
e16d030c 2190 txn = (struct mgmt_txn_ctx *)EVENT_ARG(thread);
74335ceb
YR
2191 assert(txn);
2192
2193 mgmt_txn_cleanup_txn(&txn);
2194}
2195
2196static void mgmt_txn_register_event(struct mgmt_txn_ctx *txn,
2197 enum mgmt_txn_event event)
2198{
2199 struct timeval tv = {.tv_sec = 0,
2200 .tv_usec = MGMTD_TXN_PROC_DELAY_USEC};
2201
2202 assert(mgmt_txn_mm && mgmt_txn_tm);
2203
2204 switch (event) {
2205 case MGMTD_TXN_PROC_SETCFG:
907a2395 2206 event_add_timer_tv(mgmt_txn_tm, mgmt_txn_process_set_cfg,
74335ceb 2207 txn, &tv, &txn->proc_set_cfg);
74335ceb
YR
2208 break;
2209 case MGMTD_TXN_PROC_COMMITCFG:
907a2395 2210 event_add_timer_tv(mgmt_txn_tm, mgmt_txn_process_commit_cfg,
74335ceb 2211 txn, &tv, &txn->proc_comm_cfg);
74335ceb
YR
2212 break;
2213 case MGMTD_TXN_PROC_GETCFG:
907a2395 2214 event_add_timer_tv(mgmt_txn_tm, mgmt_txn_process_get_cfg,
74335ceb 2215 txn, &tv, &txn->proc_get_cfg);
74335ceb
YR
2216 break;
2217 case MGMTD_TXN_PROC_GETDATA:
907a2395 2218 event_add_timer_tv(mgmt_txn_tm, mgmt_txn_process_get_data,
74335ceb 2219 txn, &tv, &txn->proc_get_data);
74335ceb
YR
2220 break;
2221 case MGMTD_TXN_COMMITCFG_TIMEOUT:
907a2395 2222 event_add_timer_msec(mgmt_txn_tm,
74335ceb
YR
2223 mgmt_txn_cfg_commit_timedout, txn,
2224 MGMTD_TXN_CFG_COMMIT_MAX_DELAY_MSEC,
2225 &txn->comm_cfg_timeout);
74335ceb
YR
2226 break;
2227 case MGMTD_TXN_CLEANUP:
2228 tv.tv_usec = MGMTD_TXN_CLEANUP_DELAY_USEC;
907a2395 2229 event_add_timer_tv(mgmt_txn_tm, mgmt_txn_cleanup, txn, &tv,
74335ceb 2230 &txn->clnup);
74335ceb
YR
2231 }
2232}
2233
cd9d0537 2234int mgmt_txn_init(struct mgmt_master *mm, struct event_loop *tm)
74335ceb
YR
2235{
2236 if (mgmt_txn_mm || mgmt_txn_tm)
2237 assert(!"MGMTD TXN: Call txn_init() only once");
2238
2239 mgmt_txn_mm = mm;
2240 mgmt_txn_tm = tm;
2241 mgmt_txns_init(&mm->txn_list);
2242 mgmt_txn_hash_init();
2243 assert(!mm->cfg_txn);
2244 mm->cfg_txn = NULL;
2245
2246 return 0;
2247}
2248
2249void mgmt_txn_destroy(void)
2250{
2251 mgmt_txn_cleanup_all_txns();
2252 mgmt_txn_hash_destroy();
2253}
2254
2255uint64_t mgmt_config_txn_in_progress(void)
2256{
2257 if (mgmt_txn_mm && mgmt_txn_mm->cfg_txn)
2258 return mgmt_txn_mm->cfg_txn->session_id;
2259
2260 return MGMTD_SESSION_ID_NONE;
2261}
2262
2263uint64_t mgmt_create_txn(uint64_t session_id, enum mgmt_txn_type type)
2264{
2265 struct mgmt_txn_ctx *txn;
2266
2267 txn = mgmt_txn_create_new(session_id, type);
2268 return txn ? txn->txn_id : MGMTD_TXN_ID_NONE;
2269}
2270
2271bool mgmt_txn_id_is_valid(uint64_t txn_id)
2272{
2273 return mgmt_txn_id2ctx(txn_id) ? true : false;
2274}
2275
2276void mgmt_destroy_txn(uint64_t *txn_id)
2277{
2278 struct mgmt_txn_ctx *txn;
2279
2280 txn = mgmt_txn_id2ctx(*txn_id);
2281 if (!txn)
2282 return;
2283
2284 mgmt_txn_delete(&txn);
2285 *txn_id = MGMTD_TXN_ID_NONE;
2286}
2287
2288enum mgmt_txn_type mgmt_get_txn_type(uint64_t txn_id)
2289{
2290 struct mgmt_txn_ctx *txn;
2291
2292 txn = mgmt_txn_id2ctx(txn_id);
2293 if (!txn)
2294 return MGMTD_TXN_TYPE_NONE;
2295
2296 return txn->type;
2297}
2298
2299int mgmt_txn_send_set_config_req(uint64_t txn_id, uint64_t req_id,
2300 Mgmtd__DatastoreId ds_id,
2301 struct mgmt_ds_ctx *ds_ctx,
2302 Mgmtd__YangCfgDataReq **cfg_req,
2303 size_t num_req, bool implicit_commit,
2304 Mgmtd__DatastoreId dst_ds_id,
2305 struct mgmt_ds_ctx *dst_ds_ctx)
2306{
2307 struct mgmt_txn_ctx *txn;
2308 struct mgmt_txn_req *txn_req;
2309 size_t indx;
2310 uint16_t *num_chgs;
2311 struct nb_cfg_change *cfg_chg;
2312
2313 txn = mgmt_txn_id2ctx(txn_id);
2314 if (!txn)
2315 return -1;
2316
2317 if (implicit_commit && mgmt_txn_reqs_count(&txn->set_cfg_reqs)) {
2318 MGMTD_TXN_ERR(
2319 "For implicit commit config only one SETCFG-REQ can be allowed!");
2320 return -1;
2321 }
2322
2323 txn_req = mgmt_txn_req_alloc(txn, req_id, MGMTD_TXN_PROC_SETCFG);
2324 txn_req->req.set_cfg->ds_id = ds_id;
2325 txn_req->req.set_cfg->ds_ctx = ds_ctx;
2326 num_chgs = &txn_req->req.set_cfg->num_cfg_changes;
2327 for (indx = 0; indx < num_req; indx++) {
2328 cfg_chg = &txn_req->req.set_cfg->cfg_changes[*num_chgs];
2329
2330 if (cfg_req[indx]->req_type
2331 == MGMTD__CFG_DATA_REQ_TYPE__DELETE_DATA)
2332 cfg_chg->operation = NB_OP_DESTROY;
2333 else if (cfg_req[indx]->req_type
2334 == MGMTD__CFG_DATA_REQ_TYPE__SET_DATA)
2335 cfg_chg->operation =
2336 mgmt_ds_find_data_node_by_xpath(
2337 ds_ctx, cfg_req[indx]->data->xpath)
2338 ? NB_OP_MODIFY
2339 : NB_OP_CREATE;
2340 else
2341 continue;
2342
2343 MGMTD_TXN_DBG(
2344 "XPath: '%s', Value: '%s'", cfg_req[indx]->data->xpath,
2345 (cfg_req[indx]->data->value
2346 && cfg_req[indx]
2347 ->data->value
2348 ->encoded_str_val
2349 ? cfg_req[indx]->data->value->encoded_str_val
2350 : "NULL"));
2351 strlcpy(cfg_chg->xpath, cfg_req[indx]->data->xpath,
2352 sizeof(cfg_chg->xpath));
2353 cfg_chg->value = (cfg_req[indx]->data->value
2354 && cfg_req[indx]
2355 ->data->value
2356 ->encoded_str_val
2357 ? strdup(cfg_req[indx]
2358 ->data->value
2359 ->encoded_str_val)
2360 : NULL);
2361 if (cfg_chg->value)
2362 MGMTD_TXN_DBG("Allocated value at %p ==> '%s'",
2363 cfg_chg->value, cfg_chg->value);
2364
2365 (*num_chgs)++;
2366 }
2367 txn_req->req.set_cfg->implicit_commit = implicit_commit;
2368 txn_req->req.set_cfg->dst_ds_id = dst_ds_id;
2369 txn_req->req.set_cfg->dst_ds_ctx = dst_ds_ctx;
2370 txn_req->req.set_cfg->setcfg_stats =
2371 mgmt_fe_get_session_setcfg_stats(txn->session_id);
2372 mgmt_txn_register_event(txn, MGMTD_TXN_PROC_SETCFG);
2373
2374 return 0;
2375}
2376
2377int mgmt_txn_send_commit_config_req(uint64_t txn_id, uint64_t req_id,
2378 Mgmtd__DatastoreId src_ds_id,
2379 struct mgmt_ds_ctx *src_ds_ctx,
2380 Mgmtd__DatastoreId dst_ds_id,
2381 struct mgmt_ds_ctx *dst_ds_ctx,
2382 bool validate_only, bool abort,
2383 bool implicit)
2384{
2385 struct mgmt_txn_ctx *txn;
2386 struct mgmt_txn_req *txn_req;
2387
2388 txn = mgmt_txn_id2ctx(txn_id);
2389 if (!txn)
2390 return -1;
2391
2392 if (txn->commit_cfg_req) {
218625aa
CH
2393 MGMTD_TXN_ERR("Commit already in-progress txn-id: %" PRIu64
2394 " session-id: %" PRIu64 ". Cannot start another",
2395 txn->txn_id, txn->session_id);
74335ceb
YR
2396 return -1;
2397 }
2398
2399 txn_req = mgmt_txn_req_alloc(txn, req_id, MGMTD_TXN_PROC_COMMITCFG);
2400 txn_req->req.commit_cfg.src_ds_id = src_ds_id;
2401 txn_req->req.commit_cfg.src_ds_ctx = src_ds_ctx;
2402 txn_req->req.commit_cfg.dst_ds_id = dst_ds_id;
2403 txn_req->req.commit_cfg.dst_ds_ctx = dst_ds_ctx;
2404 txn_req->req.commit_cfg.validate_only = validate_only;
2405 txn_req->req.commit_cfg.abort = abort;
2406 txn_req->req.commit_cfg.implicit = implicit;
2407 txn_req->req.commit_cfg.cmt_stats =
2408 mgmt_fe_get_session_commit_stats(txn->session_id);
2409
2410 /*
2411 * Trigger a COMMIT-CONFIG process.
2412 */
2413 mgmt_txn_register_event(txn, MGMTD_TXN_PROC_COMMITCFG);
2414 return 0;
2415}
2416
2417int mgmt_txn_notify_be_adapter_conn(struct mgmt_be_client_adapter *adapter,
2418 bool connect)
2419{
2420 struct mgmt_txn_ctx *txn;
2421 struct mgmt_txn_req *txn_req;
2422 struct mgmt_commit_cfg_req *cmtcfg_req;
2423 static struct mgmt_commit_stats dummy_stats;
2424 struct nb_config_cbs *adapter_cfgs = NULL;
2425
2426 memset(&dummy_stats, 0, sizeof(dummy_stats));
2427 if (connect) {
2428 /* Get config for this single backend client */
2429 mgmt_be_get_adapter_config(adapter, mm->running_ds,
2430 &adapter_cfgs);
2431
2432 if (!adapter_cfgs || RB_EMPTY(nb_config_cbs, adapter_cfgs)) {
2433 SET_FLAG(adapter->flags,
2434 MGMTD_BE_ADAPTER_FLAGS_CFG_SYNCED);
2435 return 0;
2436 }
2437
2438 /*
2439 * Create a CONFIG transaction to push the config changes
2440 * provided to the backend client.
2441 */
2442 txn = mgmt_txn_create_new(0, MGMTD_TXN_TYPE_CONFIG);
2443 if (!txn) {
2444 MGMTD_TXN_ERR(
2445 "Failed to create CONFIG Transaction for downloading CONFIGs for client '%s'",
2446 adapter->name);
2447 return -1;
2448 }
2449
218625aa
CH
2450 MGMTD_TXN_DBG("Created initial txn-id: %" PRIu64
2451 " for BE client '%s'",
f82370b4 2452 txn->txn_id, adapter->name);
74335ceb
YR
2453 /*
2454 * Set the changeset for transaction to commit and trigger the
2455 * commit request.
2456 */
2457 txn_req =
2458 mgmt_txn_req_alloc(txn, 0, MGMTD_TXN_PROC_COMMITCFG);
2459 txn_req->req.commit_cfg.src_ds_id = MGMTD_DS_NONE;
2460 txn_req->req.commit_cfg.src_ds_ctx = 0;
2461 txn_req->req.commit_cfg.dst_ds_id = MGMTD_DS_NONE;
2462 txn_req->req.commit_cfg.dst_ds_ctx = 0;
2463 txn_req->req.commit_cfg.validate_only = false;
2464 txn_req->req.commit_cfg.abort = false;
2465 txn_req->req.commit_cfg.cmt_stats = &dummy_stats;
2466 txn_req->req.commit_cfg.cfg_chgs = adapter_cfgs;
2467
2468 /*
2469 * Trigger a COMMIT-CONFIG process.
2470 */
2471 mgmt_txn_register_event(txn, MGMTD_TXN_PROC_COMMITCFG);
2472
2473 } else {
2474 /*
2475 * Check if any transaction is currently on-going that
2476 * involves this backend client. If so, report the transaction
2477 * has failed.
2478 */
2479 FOREACH_TXN_IN_LIST (mgmt_txn_mm, txn) {
51941c19
CH
2480 /* TODO: update with operational state when that is
2481 * completed */
74335ceb
YR
2482 if (txn->type == MGMTD_TXN_TYPE_CONFIG) {
2483 cmtcfg_req = txn->commit_cfg_req
2484 ? &txn->commit_cfg_req
2485 ->req.commit_cfg
2486 : NULL;
0327be91
CH
2487 if (cmtcfg_req &&
2488 cmtcfg_req->subscr_info
2489 .xpath_subscr[adapter->id]) {
74335ceb
YR
2490 mgmt_txn_send_commit_cfg_reply(
2491 txn, MGMTD_INTERNAL_ERROR,
2492 "Backend daemon disconnected while processing commit!");
2493 }
2494 }
2495 }
2496 }
2497
2498 return 0;
2499}
2500
2501int mgmt_txn_notify_be_txn_reply(uint64_t txn_id, bool create,
2502 bool success,
2503 struct mgmt_be_client_adapter *adapter)
2504{
2505 struct mgmt_txn_ctx *txn;
2506 struct mgmt_commit_cfg_req *cmtcfg_req = NULL;
2507
2508 txn = mgmt_txn_id2ctx(txn_id);
2509 if (!txn || txn->type != MGMTD_TXN_TYPE_CONFIG)
2510 return -1;
2511
2512 if (!create && !txn->commit_cfg_req)
2513 return 0;
2514
2515 assert(txn->commit_cfg_req);
2516 cmtcfg_req = &txn->commit_cfg_req->req.commit_cfg;
2517 if (create) {
2518 if (success) {
2519 /*
2520 * Done with TXN_CREATE. Move the backend client to
2521 * next phase.
2522 */
2523 assert(cmtcfg_req->curr_phase
2524 == MGMTD_COMMIT_PHASE_TXN_CREATE);
2525
2526 /*
2527 * Send CFGDATA_CREATE-REQs to the backend immediately.
2528 */
2529 mgmt_txn_send_be_cfg_data(txn, adapter);
2530 } else {
2531 mgmt_txn_send_commit_cfg_reply(
2532 txn, MGMTD_INTERNAL_ERROR,
2533 "Internal error! Failed to initiate transaction at backend!");
2534 }
2535 } else {
2536 /*
2537 * Done with TXN_DELETE. Move the backend client to next phase.
2538 */
2539 if (false)
2540 mgmt_move_be_commit_to_next_phase(txn, adapter);
2541 }
2542
2543 return 0;
2544}
2545
2546int mgmt_txn_notify_be_cfgdata_reply(
2547 uint64_t txn_id, uint64_t batch_id, bool success, char *error_if_any,
2548 struct mgmt_be_client_adapter *adapter)
2549{
2550 struct mgmt_txn_ctx *txn;
2551 struct mgmt_txn_be_cfg_batch *cfg_btch;
2552 struct mgmt_commit_cfg_req *cmtcfg_req = NULL;
2553
2554 txn = mgmt_txn_id2ctx(txn_id);
2555 if (!txn || txn->type != MGMTD_TXN_TYPE_CONFIG)
2556 return -1;
2557
2558 if (!txn->commit_cfg_req)
2559 return -1;
2560 cmtcfg_req = &txn->commit_cfg_req->req.commit_cfg;
2561
2562 cfg_btch = mgmt_txn_cfgbatch_id2ctx(txn, batch_id);
2563 if (!cfg_btch || cfg_btch->txn != txn)
2564 return -1;
2565
2566 if (!success) {
2567 MGMTD_TXN_ERR(
218625aa
CH
2568 "CFGDATA_CREATE_REQ sent to '%s' failed txn-id: %" PRIu64
2569 " batch-id %" PRIu64 " err: %s",
2570 adapter->name, txn->txn_id, cfg_btch->batch_id,
74335ceb
YR
2571 error_if_any ? error_if_any : "None");
2572 mgmt_txn_send_commit_cfg_reply(
2573 txn, MGMTD_INTERNAL_ERROR,
1401ee8b 2574 error_if_any ? error_if_any :
74335ceb
YR
2575 "Internal error! Failed to download config data to backend!");
2576 return 0;
2577 }
2578
2579 MGMTD_TXN_DBG(
218625aa
CH
2580 "CFGDATA_CREATE_REQ sent to '%s' was successful txn-id: %" PRIu64
2581 " batch-id %" PRIu64 " err: %s",
2582 adapter->name, txn->txn_id, cfg_btch->batch_id,
74335ceb
YR
2583 error_if_any ? error_if_any : "None");
2584 mgmt_move_txn_cfg_batch_to_next(
2585 cmtcfg_req, cfg_btch, &cmtcfg_req->curr_batches[adapter->id],
2586 &cmtcfg_req->next_batches[adapter->id], true,
2587 MGMTD_COMMIT_PHASE_APPLY_CFG);
2588
2589 mgmt_try_move_commit_to_next_phase(txn, cmtcfg_req);
2590
2591 return 0;
2592}
2593
f82370b4 2594int mgmt_txn_notify_be_cfg_apply_reply(uint64_t txn_id, bool success,
74335ceb
YR
2595 uint64_t batch_ids[],
2596 size_t num_batch_ids, char *error_if_any,
2597 struct mgmt_be_client_adapter *adapter)
2598{
2599 struct mgmt_txn_ctx *txn;
2600 struct mgmt_txn_be_cfg_batch *cfg_btch;
2601 struct mgmt_commit_cfg_req *cmtcfg_req = NULL;
2602 size_t indx;
2603
2604 txn = mgmt_txn_id2ctx(txn_id);
2605 if (!txn || txn->type != MGMTD_TXN_TYPE_CONFIG
2606 || !txn->commit_cfg_req)
2607 return -1;
2608
2609 cmtcfg_req = &txn->commit_cfg_req->req.commit_cfg;
2610
2611 if (!success) {
2612 MGMTD_TXN_ERR(
218625aa
CH
2613 "CFGDATA_APPLY_REQ sent to '%s' failed txn-id: %" PRIu64
2614 " batch ids %" PRIu64 " - %" PRIu64 " err: %s",
2615 adapter->name, txn->txn_id, batch_ids[0],
2616 batch_ids[num_batch_ids - 1],
74335ceb
YR
2617 error_if_any ? error_if_any : "None");
2618 mgmt_txn_send_commit_cfg_reply(
2619 txn, MGMTD_INTERNAL_ERROR,
1401ee8b 2620 error_if_any ? error_if_any :
74335ceb
YR
2621 "Internal error! Failed to apply config data on backend!");
2622 return 0;
2623 }
2624
2625 for (indx = 0; indx < num_batch_ids; indx++) {
2626 cfg_btch = mgmt_txn_cfgbatch_id2ctx(txn, batch_ids[indx]);
2627 if (cfg_btch->txn != txn)
2628 return -1;
2629 mgmt_move_txn_cfg_batch_to_next(
2630 cmtcfg_req, cfg_btch,
2631 &cmtcfg_req->curr_batches[adapter->id],
2632 &cmtcfg_req->next_batches[adapter->id], true,
2633 MGMTD_COMMIT_PHASE_TXN_DELETE);
2634 }
2635
2636 if (!mgmt_txn_batches_count(&cmtcfg_req->curr_batches[adapter->id])) {
2637 /*
2638 * All configuration for the specific backend has been applied.
2639 * Send TXN-DELETE to wrap up the transaction for this backend.
2640 */
2641 SET_FLAG(adapter->flags, MGMTD_BE_ADAPTER_FLAGS_CFG_SYNCED);
2642 mgmt_txn_send_be_txn_delete(txn, adapter);
2643 }
2644
2645 mgmt_try_move_commit_to_next_phase(txn, cmtcfg_req);
2646 if (mm->perf_stats_en)
2647 gettimeofday(&cmtcfg_req->cmt_stats->apply_cfg_end, NULL);
2648
2649 return 0;
2650}
2651
2652int mgmt_txn_send_commit_config_reply(uint64_t txn_id,
2653 enum mgmt_result result,
2654 const char *error_if_any)
2655{
2656 struct mgmt_txn_ctx *txn;
2657
2658 txn = mgmt_txn_id2ctx(txn_id);
2659 if (!txn)
2660 return -1;
2661
2662 if (!txn->commit_cfg_req) {
218625aa
CH
2663 MGMTD_TXN_ERR("NO commit in-progress txn-id: %" PRIu64
2664 " session-id: %" PRIu64,
2665 txn->txn_id, txn->session_id);
74335ceb
YR
2666 return -1;
2667 }
2668
2669 return mgmt_txn_send_commit_cfg_reply(txn, result, error_if_any);
2670}
2671
2672int mgmt_txn_send_get_config_req(uint64_t txn_id, uint64_t req_id,
2673 Mgmtd__DatastoreId ds_id,
2674 struct mgmt_ds_ctx *ds_ctx,
2675 Mgmtd__YangGetDataReq **data_req,
2676 size_t num_reqs)
2677{
2678 struct mgmt_txn_ctx *txn;
2679 struct mgmt_txn_req *txn_req;
2680 size_t indx;
2681
2682 txn = mgmt_txn_id2ctx(txn_id);
2683 if (!txn)
2684 return -1;
2685
2686 txn_req = mgmt_txn_req_alloc(txn, req_id, MGMTD_TXN_PROC_GETCFG);
2687 txn_req->req.get_data->ds_id = ds_id;
2688 txn_req->req.get_data->ds_ctx = ds_ctx;
2689 for (indx = 0;
2690 indx < num_reqs && indx < MGMTD_MAX_NUM_DATA_REPLY_IN_BATCH;
2691 indx++) {
2692 MGMTD_TXN_DBG("XPath: '%s'", data_req[indx]->data->xpath);
2693 txn_req->req.get_data->xpaths[indx] =
2694 strdup(data_req[indx]->data->xpath);
2695 txn_req->req.get_data->num_xpaths++;
2696 }
2697
2698 mgmt_txn_register_event(txn, MGMTD_TXN_PROC_GETCFG);
2699
2700 return 0;
2701}
2702
2703int mgmt_txn_send_get_data_req(uint64_t txn_id, uint64_t req_id,
2704 Mgmtd__DatastoreId ds_id,
2705 struct mgmt_ds_ctx *ds_ctx,
2706 Mgmtd__YangGetDataReq **data_req,
2707 size_t num_reqs)
2708{
2709 struct mgmt_txn_ctx *txn;
2710 struct mgmt_txn_req *txn_req;
2711 size_t indx;
2712
2713 txn = mgmt_txn_id2ctx(txn_id);
2714 if (!txn)
2715 return -1;
2716
2717 txn_req = mgmt_txn_req_alloc(txn, req_id, MGMTD_TXN_PROC_GETDATA);
2718 txn_req->req.get_data->ds_id = ds_id;
2719 txn_req->req.get_data->ds_ctx = ds_ctx;
2720 for (indx = 0;
2721 indx < num_reqs && indx < MGMTD_MAX_NUM_DATA_REPLY_IN_BATCH;
2722 indx++) {
2723 MGMTD_TXN_DBG("XPath: '%s'", data_req[indx]->data->xpath);
2724 txn_req->req.get_data->xpaths[indx] =
2725 strdup(data_req[indx]->data->xpath);
2726 txn_req->req.get_data->num_xpaths++;
2727 }
2728
2729 mgmt_txn_register_event(txn, MGMTD_TXN_PROC_GETDATA);
2730
2731 return 0;
2732}
2733
2734void mgmt_txn_status_write(struct vty *vty)
2735{
2736 struct mgmt_txn_ctx *txn;
2737
2738 vty_out(vty, "MGMTD Transactions\n");
2739
2740 FOREACH_TXN_IN_LIST (mgmt_txn_mm, txn) {
218625aa
CH
2741 vty_out(vty, " Txn: \t\t\t0x%p\n", txn);
2742 vty_out(vty, " Txn-Id: \t\t\t%" PRIu64 "\n", txn->txn_id);
2743 vty_out(vty, " Session-Id: \t\t%" PRIu64 "\n",
2744 txn->session_id);
74335ceb
YR
2745 vty_out(vty, " Type: \t\t\t%s\n",
2746 mgmt_txn_type2str(txn->type));
2747 vty_out(vty, " Ref-Count: \t\t\t%d\n", txn->refcount);
2748 }
2749 vty_out(vty, " Total: %d\n",
2750 (int)mgmt_txns_count(&mgmt_txn_mm->txn_list));
2751}
2752
2753int mgmt_txn_rollback_trigger_cfg_apply(struct mgmt_ds_ctx *src_ds_ctx,
2754 struct mgmt_ds_ctx *dst_ds_ctx)
2755{
2756 static struct nb_config_cbs changes;
2757 struct nb_config_cbs *cfg_chgs = NULL;
2758 struct mgmt_txn_ctx *txn;
2759 struct mgmt_txn_req *txn_req;
2760 static struct mgmt_commit_stats dummy_stats;
2761
2762 memset(&changes, 0, sizeof(changes));
2763 memset(&dummy_stats, 0, sizeof(dummy_stats));
2764 /*
2765 * This could be the case when the config is directly
2766 * loaded onto the candidate DS from a file. Get the
2767 * diff from a full comparison of the candidate and
2768 * running DSs.
2769 */
2770 nb_config_diff(mgmt_ds_get_nb_config(dst_ds_ctx),
2771 mgmt_ds_get_nb_config(src_ds_ctx), &changes);
2772 cfg_chgs = &changes;
2773
2774 if (RB_EMPTY(nb_config_cbs, cfg_chgs)) {
2775 /*
2776 * This means there's no changes to commit whatsoever
2777 * is the source of the changes in config.
2778 */
2779 return -1;
2780 }
2781
2782 /*
2783 * Create a CONFIG transaction to push the config changes
2784 * provided to the backend client.
2785 */
2786 txn = mgmt_txn_create_new(0, MGMTD_TXN_TYPE_CONFIG);
2787 if (!txn) {
2788 MGMTD_TXN_ERR(
2789 "Failed to create CONFIG Transaction for downloading CONFIGs");
2790 return -1;
2791 }
2792
218625aa 2793 MGMTD_TXN_DBG("Created rollback txn-id: %" PRIu64, txn->txn_id);
f82370b4 2794
74335ceb
YR
2795 /*
2796 * Set the changeset for transaction to commit and trigger the commit
2797 * request.
2798 */
2799 txn_req = mgmt_txn_req_alloc(txn, 0, MGMTD_TXN_PROC_COMMITCFG);
2800 txn_req->req.commit_cfg.src_ds_id = MGMTD_DS_CANDIDATE;
2801 txn_req->req.commit_cfg.src_ds_ctx = src_ds_ctx;
2802 txn_req->req.commit_cfg.dst_ds_id = MGMTD_DS_RUNNING;
2803 txn_req->req.commit_cfg.dst_ds_ctx = dst_ds_ctx;
2804 txn_req->req.commit_cfg.validate_only = false;
2805 txn_req->req.commit_cfg.abort = false;
2806 txn_req->req.commit_cfg.rollback = true;
2807 txn_req->req.commit_cfg.cmt_stats = &dummy_stats;
2808 txn_req->req.commit_cfg.cfg_chgs = cfg_chgs;
2809
2810 /*
2811 * Trigger a COMMIT-CONFIG process.
2812 */
2813 mgmt_txn_register_event(txn, MGMTD_TXN_PROC_COMMITCFG);
2814 return 0;
2815}