]>
Commit | Line | Data |
---|---|---|
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 | ||
25 | enum 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 | ||
34 | PREDECL_LIST(mgmt_txn_reqs); | |
35 | ||
36 | struct 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 | ||
47 | enum 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 | ||
56 | static inline const char * | |
57 | mgmt_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 | ||
77 | PREDECL_LIST(mgmt_txn_batches); | |
78 | ||
79 | struct 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 | ||
95 | DECLARE_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 | ||
100 | struct 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 | ||
154 | struct 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 | ||
165 | struct 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 | ||
181 | struct 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 | ||
195 | DECLARE_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 | ||
200 | struct 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 | ||
250 | DECLARE_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 | ||
255 | static int mgmt_txn_send_commit_cfg_reply(struct mgmt_txn_ctx *txn, | |
256 | enum mgmt_result result, | |
257 | const char *error_if_any); | |
258 | ||
259 | static inline const char * | |
260 | mgmt_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 | ||
270 | static void mgmt_txn_lock(struct mgmt_txn_ctx *txn, const char *file, | |
271 | int line); | |
272 | static void mgmt_txn_unlock(struct mgmt_txn_ctx **txn, const char *file, | |
273 | int line); | |
274 | static int | |
275 | mgmt_txn_send_be_txn_delete(struct mgmt_txn_ctx *txn, | |
276 | struct mgmt_be_client_adapter *adapter); | |
277 | ||
cd9d0537 | 278 | static struct event_loop *mgmt_txn_tm; |
74335ceb YR |
279 | static struct mgmt_master *mgmt_txn_mm; |
280 | ||
281 | static void mgmt_txn_register_event(struct mgmt_txn_ctx *txn, | |
282 | enum mgmt_txn_event event); | |
283 | ||
284 | static int | |
285 | mgmt_move_be_commit_to_next_phase(struct mgmt_txn_ctx *txn, | |
286 | struct mgmt_be_client_adapter *adapter); | |
287 | ||
288 | static struct mgmt_txn_be_cfg_batch * | |
289 | mgmt_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 | ||
323 | static void | |
324 | mgmt_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 | ||
358 | static 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 | ||
366 | static 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 | ||
374 | static 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 | ||
381 | static inline struct mgmt_txn_be_cfg_batch * | |
382 | mgmt_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 | ||
397 | static 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 | ||
418 | static 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 | ||
490 | static 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 | 615 | static 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 | ||
741 | static 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 | ||
864 | static void | |
865 | mgmt_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 | ||
886 | static 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 | ||
902 | static int | |
903 | mgmt_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 | ||
956 | static int | |
957 | mgmt_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 | ||
994 | static 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 | ||
1139 | mgmt_txn_create_config_batches_failed: | |
1140 | ||
1141 | if (xpath) | |
1142 | free(xpath); | |
1143 | ||
1144 | return -1; | |
1145 | } | |
1146 | ||
1147 | static 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 | ||
1300 | mgmt_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); | |
1327 | mgmt_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 | ||
1335 | static 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 | ||
1382 | static int | |
1383 | mgmt_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 | ||
1437 | static int | |
1438 | mgmt_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 | 1461 | static 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 | */ | |
1494 | static 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 | 1551 | static 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 | ||
1639 | static 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 | ||
1647 | static 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 | ||
1674 | static 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 | ||
1680 | static 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 | ||
1742 | static 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 | ||
1786 | static 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 | ||
1840 | mgmt_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 | 1851 | static 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 | 1918 | static 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 | ||
2001 | static struct mgmt_txn_ctx * | |
2002 | mgmt_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 | ||
2015 | static 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 | ||
2059 | mgmt_create_txn_done: | |
2060 | return txn; | |
2061 | } | |
2062 | ||
2063 | static void mgmt_txn_delete(struct mgmt_txn_ctx **txn) | |
2064 | { | |
2065 | MGMTD_TXN_UNLOCK(txn); | |
2066 | } | |
2067 | ||
2068 | static 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 | ||
2076 | static 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 | ||
2084 | static void mgmt_txn_hash_free(void *data) | |
2085 | { | |
2086 | struct mgmt_txn_ctx *txn = data; | |
2087 | ||
2088 | mgmt_txn_delete(&txn); | |
2089 | } | |
2090 | ||
2091 | static 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 | ||
2101 | static 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 | ||
2112 | static inline struct mgmt_txn_ctx * | |
2113 | mgmt_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 | ||
2127 | static 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 | ||
2136 | static 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 | ||
2167 | static 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 | ||
2174 | static void | |
2175 | mgmt_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 | 2186 | static 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 | ||
2196 | static 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 | 2234 | int 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 | ||
2249 | void mgmt_txn_destroy(void) | |
2250 | { | |
2251 | mgmt_txn_cleanup_all_txns(); | |
2252 | mgmt_txn_hash_destroy(); | |
2253 | } | |
2254 | ||
2255 | uint64_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 | ||
2263 | uint64_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 | ||
2271 | bool mgmt_txn_id_is_valid(uint64_t txn_id) | |
2272 | { | |
2273 | return mgmt_txn_id2ctx(txn_id) ? true : false; | |
2274 | } | |
2275 | ||
2276 | void 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 | ||
2288 | enum 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 | ||
2299 | int 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 | ||
2377 | int 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 | ||
2417 | int 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 | ||
2501 | int 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 | ||
2546 | int 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 | 2594 | int 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 | ||
2652 | int 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 | ||
2672 | int 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 | ||
2703 | int 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 | ||
2734 | void 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 | ||
2753 | int 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 | } |