]> git.proxmox.com Git - mirror_frr.git/blob - mgmtd/mgmt_ds.c
Merge pull request #13020 from SaiGomathiN/2462808-3
[mirror_frr.git] / mgmtd / mgmt_ds.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * MGMTD Datastores
4 *
5 * Copyright (C) 2021 Vmware, Inc.
6 * Pushpasis Sarkar <spushpasis@vmware.com>
7 */
8
9 #include <zebra.h>
10 #include "md5.h"
11 #include "mgmtd/mgmt.h"
12 #include "mgmtd/mgmt_memory.h"
13 #include "mgmtd/mgmt_ds.h"
14 #include "mgmtd/mgmt_history.h"
15 #include "mgmtd/mgmt_txn.h"
16 #include "libyang/libyang.h"
17
18 #define MGMTD_DS_DBG(fmt, ...) \
19 DEBUGD(&mgmt_debug_ds, "%s:" fmt, __func__, ##__VA_ARGS__)
20 #define MGMTD_DS_ERR(fmt, ...) \
21 zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__)
22
23 struct mgmt_ds_ctx {
24 Mgmtd__DatastoreId ds_id;
25 int lock; /* 0 unlocked, >0 read locked < write locked */
26
27 bool config_ds;
28
29 union {
30 struct nb_config *cfg_root;
31 struct lyd_node *dnode_root;
32 } root;
33 };
34
35 const char *mgmt_ds_names[MGMTD_DS_MAX_ID + 1] = {
36 MGMTD_DS_NAME_NONE, /* MGMTD_DS_NONE */
37 MGMTD_DS_NAME_RUNNING, /* MGMTD_DS_RUNNING */
38 MGMTD_DS_NAME_CANDIDATE, /* MGMTD_DS_CANDIDATE */
39 MGMTD_DS_NAME_OPERATIONAL, /* MGMTD_DS_OPERATIONAL */
40 "Unknown/Invalid", /* MGMTD_DS_ID_MAX */
41 };
42
43 static struct mgmt_master *mgmt_ds_mm;
44 static struct mgmt_ds_ctx running, candidate, oper;
45
46 /* Dump the data tree of the specified format in the file pointed by the path */
47 static int mgmt_ds_dump_in_memory(struct mgmt_ds_ctx *ds_ctx,
48 const char *base_xpath, LYD_FORMAT format,
49 struct ly_out *out)
50 {
51 struct lyd_node *root;
52 uint32_t options = 0;
53
54 if (base_xpath[0] == '\0')
55 root = ds_ctx->config_ds ? ds_ctx->root.cfg_root->dnode
56 : ds_ctx->root.dnode_root;
57 else
58 root = yang_dnode_get(ds_ctx->config_ds
59 ? ds_ctx->root.cfg_root->dnode
60 : ds_ctx->root.dnode_root,
61 base_xpath);
62 if (!root)
63 return -1;
64
65 options = ds_ctx->config_ds ? LYD_PRINT_WD_TRIM :
66 LYD_PRINT_WD_EXPLICIT;
67
68 if (base_xpath[0] == '\0')
69 lyd_print_all(out, root, format, options);
70 else
71 lyd_print_tree(out, root, format, options);
72
73 return 0;
74 }
75
76 static int mgmt_ds_replace_dst_with_src_ds(struct mgmt_ds_ctx *src,
77 struct mgmt_ds_ctx *dst)
78 {
79 struct lyd_node *dst_dnode, *src_dnode;
80
81 if (!src || !dst)
82 return -1;
83 MGMTD_DS_DBG("Replacing %d with %d", dst->ds_id, src->ds_id);
84
85 src_dnode = src->config_ds ? src->root.cfg_root->dnode
86 : dst->root.dnode_root;
87 dst_dnode = dst->config_ds ? dst->root.cfg_root->dnode
88 : dst->root.dnode_root;
89
90 if (dst_dnode)
91 yang_dnode_free(dst_dnode);
92
93 /* Not using nb_config_replace as the oper ds does not contain nb_config
94 */
95 dst_dnode = yang_dnode_dup(src_dnode);
96 if (dst->config_ds)
97 dst->root.cfg_root->dnode = dst_dnode;
98 else
99 dst->root.dnode_root = dst_dnode;
100
101 if (src->ds_id == MGMTD_DS_CANDIDATE) {
102 /*
103 * Drop the changes in scratch-buffer.
104 */
105 MGMTD_DS_DBG("Emptying Candidate Scratch buffer!");
106 nb_config_diff_del_changes(&src->root.cfg_root->cfg_chgs);
107 }
108
109 /* TODO: Update the versions if nb_config present */
110
111 return 0;
112 }
113
114 static int mgmt_ds_merge_src_with_dst_ds(struct mgmt_ds_ctx *src,
115 struct mgmt_ds_ctx *dst)
116 {
117 int ret;
118 struct lyd_node **dst_dnode, *src_dnode;
119
120 if (!src || !dst)
121 return -1;
122
123 MGMTD_DS_DBG("Merging DS %d with %d", dst->ds_id, src->ds_id);
124
125 src_dnode = src->config_ds ? src->root.cfg_root->dnode
126 : dst->root.dnode_root;
127 dst_dnode = dst->config_ds ? &dst->root.cfg_root->dnode
128 : &dst->root.dnode_root;
129 ret = lyd_merge_siblings(dst_dnode, src_dnode, 0);
130 if (ret != 0) {
131 MGMTD_DS_ERR("lyd_merge() failed with err %d", ret);
132 return ret;
133 }
134
135 if (src->ds_id == MGMTD_DS_CANDIDATE) {
136 /*
137 * Drop the changes in scratch-buffer.
138 */
139 MGMTD_DS_DBG("Emptying Candidate Scratch buffer!");
140 nb_config_diff_del_changes(&src->root.cfg_root->cfg_chgs);
141 }
142
143 return 0;
144 }
145
146 static int mgmt_ds_load_cfg_from_file(const char *filepath,
147 struct lyd_node **dnode)
148 {
149 LY_ERR ret;
150
151 *dnode = NULL;
152 ret = lyd_parse_data_path(ly_native_ctx, filepath, LYD_JSON,
153 LYD_PARSE_STRICT, 0, dnode);
154
155 if (ret != LY_SUCCESS) {
156 if (*dnode)
157 yang_dnode_free(*dnode);
158 return -1;
159 }
160
161 return 0;
162 }
163
164 void mgmt_ds_reset_candidate(void)
165 {
166 struct lyd_node *dnode = mm->candidate_ds->root.cfg_root->dnode;
167
168 if (dnode)
169 yang_dnode_free(dnode);
170
171 dnode = yang_dnode_new(ly_native_ctx, true);
172 mm->candidate_ds->root.cfg_root->dnode = dnode;
173 }
174
175
176 int mgmt_ds_init(struct mgmt_master *mm)
177 {
178 if (mgmt_ds_mm || mm->running_ds || mm->candidate_ds || mm->oper_ds)
179 assert(!"MGMTD: Call ds_init only once!");
180
181 /* Use Running DS from NB module??? */
182 if (!running_config)
183 assert(!"MGMTD: Call ds_init after frr_init only!");
184
185 running.root.cfg_root = running_config;
186 running.config_ds = true;
187 running.ds_id = MGMTD_DS_RUNNING;
188
189 candidate.root.cfg_root = nb_config_dup(running.root.cfg_root);
190 candidate.config_ds = true;
191 candidate.ds_id = MGMTD_DS_CANDIDATE;
192
193 /*
194 * Redirect lib/vty candidate-config datastore to the global candidate
195 * config Ds on the MGMTD process.
196 */
197 vty_mgmt_candidate_config = candidate.root.cfg_root;
198
199 oper.root.dnode_root = yang_dnode_new(ly_native_ctx, true);
200 oper.config_ds = false;
201 oper.ds_id = MGMTD_DS_OPERATIONAL;
202
203 mm->running_ds = &running;
204 mm->candidate_ds = &candidate;
205 mm->oper_ds = &oper;
206 mgmt_ds_mm = mm;
207
208 return 0;
209 }
210
211 void mgmt_ds_destroy(void)
212 {
213 /*
214 * TODO: Free the datastores.
215 */
216 }
217
218 struct mgmt_ds_ctx *mgmt_ds_get_ctx_by_id(struct mgmt_master *mm,
219 Mgmtd__DatastoreId ds_id)
220 {
221 switch (ds_id) {
222 case MGMTD_DS_CANDIDATE:
223 return (mm->candidate_ds);
224 case MGMTD_DS_RUNNING:
225 return (mm->running_ds);
226 case MGMTD_DS_OPERATIONAL:
227 return (mm->oper_ds);
228 case MGMTD_DS_NONE:
229 case MGMTD__DATASTORE_ID__STARTUP_DS:
230 case _MGMTD__DATASTORE_ID_IS_INT_SIZE:
231 return 0;
232 }
233
234 return 0;
235 }
236
237 bool mgmt_ds_is_config(struct mgmt_ds_ctx *ds_ctx)
238 {
239 if (!ds_ctx)
240 return false;
241
242 return ds_ctx->config_ds;
243 }
244
245 int mgmt_ds_read_lock(struct mgmt_ds_ctx *ds_ctx)
246 {
247 if (!ds_ctx)
248 return EINVAL;
249 if (ds_ctx->lock < 0)
250 return EBUSY;
251 ++ds_ctx->lock;
252 return 0;
253 }
254
255 int mgmt_ds_write_lock(struct mgmt_ds_ctx *ds_ctx)
256 {
257 if (!ds_ctx)
258 return EINVAL;
259 if (ds_ctx->lock != 0)
260 return EBUSY;
261 ds_ctx->lock = -1;
262 return 0;
263 }
264
265 int mgmt_ds_unlock(struct mgmt_ds_ctx *ds_ctx)
266 {
267 if (!ds_ctx)
268 return EINVAL;
269 if (ds_ctx->lock > 0)
270 --ds_ctx->lock;
271 else if (ds_ctx->lock < 0) {
272 assert(ds_ctx->lock == -1);
273 ds_ctx->lock = 0;
274 } else {
275 assert(ds_ctx->lock != 0);
276 return EINVAL;
277 }
278 return 0;
279 }
280
281 int mgmt_ds_copy_dss(struct mgmt_ds_ctx *src_ds_ctx,
282 struct mgmt_ds_ctx *dst_ds_ctx, bool updt_cmt_rec)
283 {
284 if (mgmt_ds_replace_dst_with_src_ds(src_ds_ctx, dst_ds_ctx) != 0)
285 return -1;
286
287 if (updt_cmt_rec && dst_ds_ctx->ds_id == MGMTD_DS_RUNNING)
288 mgmt_history_new_record(dst_ds_ctx);
289
290 return 0;
291 }
292
293 int mgmt_ds_dump_ds_to_file(char *file_name, struct mgmt_ds_ctx *ds_ctx)
294 {
295 struct ly_out *out;
296 int ret = 0;
297
298 if (ly_out_new_filepath(file_name, &out) == LY_SUCCESS) {
299 ret = mgmt_ds_dump_in_memory(ds_ctx, "", LYD_JSON, out);
300 ly_out_free(out, NULL, 0);
301 }
302
303 return ret;
304 }
305
306 struct nb_config *mgmt_ds_get_nb_config(struct mgmt_ds_ctx *ds_ctx)
307 {
308 if (!ds_ctx)
309 return NULL;
310
311 return ds_ctx->config_ds ? ds_ctx->root.cfg_root : NULL;
312 }
313
314 static int mgmt_walk_ds_nodes(
315 struct mgmt_ds_ctx *ds_ctx, char *base_xpath,
316 struct lyd_node *base_dnode,
317 void (*mgmt_ds_node_iter_fn)(struct mgmt_ds_ctx *ds_ctx, char *xpath,
318 struct lyd_node *node,
319 struct nb_node *nb_node, void *ctx),
320 void *ctx, char *xpaths[], int *num_nodes, bool childs_as_well,
321 bool alloc_xp_copy)
322 {
323 uint32_t indx;
324 char *xpath, *xpath_buf, *iter_xp;
325 int ret, num_left = 0, num_found = 0;
326 struct lyd_node *dnode;
327 struct nb_node *nbnode;
328 bool alloc_xp = false;
329
330 if (xpaths)
331 assert(num_nodes);
332
333 if (num_nodes && !*num_nodes)
334 return 0;
335
336 if (num_nodes) {
337 num_left = *num_nodes;
338 MGMTD_DS_DBG(" -- START: num_left:%d", num_left);
339 *num_nodes = 0;
340 }
341
342 MGMTD_DS_DBG(" -- START: Base: %s", base_xpath);
343
344 if (!base_dnode)
345 base_dnode = yang_dnode_get(
346 ds_ctx->config_ds ? ds_ctx->root.cfg_root->dnode
347 : ds_ctx->root.dnode_root,
348 base_xpath);
349 if (!base_dnode)
350 return -1;
351
352 if (mgmt_ds_node_iter_fn) {
353 /*
354 * In case the caller is interested in getting a copy
355 * of the xpath for themselves (by setting
356 * 'alloc_xp_copy' to 'true') we make a copy for the
357 * caller and pass it. Else we pass the original xpath
358 * buffer.
359 *
360 * NOTE: In such case caller will have to take care of
361 * the copy later.
362 */
363 iter_xp = alloc_xp_copy ? strdup(base_xpath) : base_xpath;
364
365 nbnode = (struct nb_node *)base_dnode->schema->priv;
366 (*mgmt_ds_node_iter_fn)(ds_ctx, iter_xp, base_dnode, nbnode,
367 ctx);
368 }
369
370 if (num_nodes) {
371 (*num_nodes)++;
372 num_left--;
373 }
374
375 /*
376 * If the base_xpath points to a leaf node, or we don't need to
377 * visit any children we can skip the tree walk.
378 */
379 if (!childs_as_well || base_dnode->schema->nodetype & LYD_NODE_TERM)
380 return 0;
381
382 indx = 0;
383 LY_LIST_FOR (lyd_child(base_dnode), dnode) {
384 assert(dnode->schema && dnode->schema->priv);
385
386 xpath = NULL;
387 if (xpaths) {
388 if (!xpaths[*num_nodes]) {
389 alloc_xp = true;
390 xpaths[*num_nodes] =
391 (char *)calloc(1, MGMTD_MAX_XPATH_LEN);
392 }
393 xpath = lyd_path(dnode, LYD_PATH_STD,
394 xpaths[*num_nodes],
395 MGMTD_MAX_XPATH_LEN);
396 } else {
397 alloc_xp = true;
398 xpath_buf = (char *)calloc(1, MGMTD_MAX_XPATH_LEN);
399 (void) lyd_path(dnode, LYD_PATH_STD, xpath_buf,
400 MGMTD_MAX_XPATH_LEN);
401 xpath = xpath_buf;
402 }
403
404 assert(xpath);
405 MGMTD_DS_DBG(" -- XPATH: %s", xpath);
406
407 if (num_nodes)
408 num_found = num_left;
409
410 ret = mgmt_walk_ds_nodes(ds_ctx, xpath, dnode,
411 mgmt_ds_node_iter_fn, ctx,
412 xpaths ? &xpaths[*num_nodes] : NULL,
413 num_nodes ? &num_found : NULL,
414 childs_as_well, alloc_xp_copy);
415
416 if (num_nodes) {
417 num_left -= num_found;
418 (*num_nodes) += num_found;
419 }
420
421 if (alloc_xp)
422 free(xpath);
423
424 if (ret != 0)
425 break;
426
427 indx++;
428 }
429
430
431 if (num_nodes) {
432 MGMTD_DS_DBG(" -- END: *num_nodes:%d, num_left:%d", *num_nodes,
433 num_left);
434 }
435
436 return 0;
437 }
438
439 int mgmt_ds_lookup_data_nodes(struct mgmt_ds_ctx *ds_ctx, const char *xpath,
440 char *dxpaths[], int *num_nodes,
441 bool get_childs_as_well, bool alloc_xp_copy)
442 {
443 char base_xpath[MGMTD_MAX_XPATH_LEN];
444
445 if (!ds_ctx || !num_nodes)
446 return -1;
447
448 if (xpath[0] == '.' && xpath[1] == '/')
449 xpath += 2;
450
451 strlcpy(base_xpath, xpath, sizeof(base_xpath));
452 mgmt_remove_trailing_separator(base_xpath, '/');
453
454 return (mgmt_walk_ds_nodes(ds_ctx, base_xpath, NULL, NULL, NULL,
455 dxpaths, num_nodes, get_childs_as_well,
456 alloc_xp_copy));
457 }
458
459 struct lyd_node *mgmt_ds_find_data_node_by_xpath(struct mgmt_ds_ctx *ds_ctx,
460 const char *xpath)
461 {
462 if (!ds_ctx)
463 return NULL;
464
465 return yang_dnode_get(ds_ctx->config_ds ? ds_ctx->root.cfg_root->dnode
466 : ds_ctx->root.dnode_root,
467 xpath);
468 }
469
470 int mgmt_ds_delete_data_nodes(struct mgmt_ds_ctx *ds_ctx, const char *xpath)
471 {
472 struct nb_node *nb_node;
473 struct lyd_node *dnode, *dep_dnode;
474 char dep_xpath[XPATH_MAXLEN];
475
476 if (!ds_ctx)
477 return -1;
478
479 nb_node = nb_node_find(xpath);
480
481 dnode = yang_dnode_get(ds_ctx->config_ds
482 ? ds_ctx->root.cfg_root->dnode
483 : ds_ctx->root.dnode_root,
484 xpath);
485
486 if (!dnode)
487 /*
488 * Return a special error code so the caller can choose
489 * whether to ignore it or not.
490 */
491 return NB_ERR_NOT_FOUND;
492 /* destroy dependant */
493 if (nb_node && nb_node->dep_cbs.get_dependant_xpath) {
494 nb_node->dep_cbs.get_dependant_xpath(dnode, dep_xpath);
495
496 dep_dnode = yang_dnode_get(
497 ds_ctx->config_ds ? ds_ctx->root.cfg_root->dnode
498 : ds_ctx->root.dnode_root,
499 dep_xpath);
500 if (dep_dnode)
501 lyd_free_tree(dep_dnode);
502 }
503 lyd_free_tree(dnode);
504
505 return 0;
506 }
507
508 int mgmt_ds_load_config_from_file(struct mgmt_ds_ctx *dst,
509 const char *file_path, bool merge)
510 {
511 struct lyd_node *iter;
512 struct mgmt_ds_ctx parsed;
513
514 if (!dst)
515 return -1;
516
517 if (mgmt_ds_load_cfg_from_file(file_path, &iter) != 0) {
518 MGMTD_DS_ERR("Failed to load config from the file %s",
519 file_path);
520 return -1;
521 }
522
523 parsed.root.cfg_root = nb_config_new(iter);
524 parsed.config_ds = true;
525 parsed.ds_id = dst->ds_id;
526
527 if (merge)
528 mgmt_ds_merge_src_with_dst_ds(&parsed, dst);
529 else
530 mgmt_ds_replace_dst_with_src_ds(&parsed, dst);
531
532 nb_config_free(parsed.root.cfg_root);
533
534 return 0;
535 }
536
537 int mgmt_ds_iter_data(struct mgmt_ds_ctx *ds_ctx, char *base_xpath,
538 void (*mgmt_ds_node_iter_fn)(struct mgmt_ds_ctx *ds_ctx,
539 char *xpath,
540 struct lyd_node *node,
541 struct nb_node *nb_node,
542 void *ctx),
543 void *ctx, bool alloc_xp_copy)
544 {
545 int ret;
546 char xpath[MGMTD_MAX_XPATH_LEN];
547 struct lyd_node *base_dnode = NULL;
548 struct lyd_node *node;
549
550 if (!ds_ctx)
551 return -1;
552
553 mgmt_remove_trailing_separator(base_xpath, '/');
554
555 strlcpy(xpath, base_xpath, sizeof(xpath));
556
557 MGMTD_DS_DBG(" -- START DS walk for DSid: %d", ds_ctx->ds_id);
558
559 /* If the base_xpath is empty then crawl the sibblings */
560 if (xpath[0] == '\0') {
561 base_dnode = ds_ctx->config_ds ? ds_ctx->root.cfg_root->dnode
562 : ds_ctx->root.dnode_root;
563
564 /* get first top-level sibling */
565 while (base_dnode->parent)
566 base_dnode = lyd_parent(base_dnode);
567
568 while (base_dnode->prev->next)
569 base_dnode = base_dnode->prev;
570
571 LY_LIST_FOR (base_dnode, node) {
572 ret = mgmt_walk_ds_nodes(
573 ds_ctx, xpath, node, mgmt_ds_node_iter_fn,
574 ctx, NULL, NULL, true, alloc_xp_copy);
575 }
576 } else
577 ret = mgmt_walk_ds_nodes(ds_ctx, xpath, base_dnode,
578 mgmt_ds_node_iter_fn, ctx, NULL, NULL,
579 true, alloc_xp_copy);
580
581 return ret;
582 }
583
584 void mgmt_ds_dump_tree(struct vty *vty, struct mgmt_ds_ctx *ds_ctx,
585 const char *xpath, FILE *f, LYD_FORMAT format)
586 {
587 struct ly_out *out;
588 char *str;
589 char base_xpath[MGMTD_MAX_XPATH_LEN] = {0};
590
591 if (!ds_ctx) {
592 vty_out(vty, " >>>>> Datastore Not Initialized!\n");
593 return;
594 }
595
596 if (xpath) {
597 strlcpy(base_xpath, xpath, MGMTD_MAX_XPATH_LEN);
598 mgmt_remove_trailing_separator(base_xpath, '/');
599 }
600
601 if (f)
602 ly_out_new_file(f, &out);
603 else
604 ly_out_new_memory(&str, 0, &out);
605
606 mgmt_ds_dump_in_memory(ds_ctx, base_xpath, format, out);
607
608 if (!f)
609 vty_out(vty, "%s\n", str);
610
611 ly_out_free(out, NULL, 0);
612 }
613
614 void mgmt_ds_status_write_one(struct vty *vty, struct mgmt_ds_ctx *ds_ctx)
615 {
616 if (!ds_ctx) {
617 vty_out(vty, " >>>>> Datastore Not Initialized!\n");
618 return;
619 }
620
621 vty_out(vty, " DS: %s\n", mgmt_ds_id2name(ds_ctx->ds_id));
622 vty_out(vty, " DS-Hndl: \t\t\t%p\n", ds_ctx);
623 vty_out(vty, " Config: \t\t\t%s\n",
624 ds_ctx->config_ds ? "True" : "False");
625 }
626
627 void mgmt_ds_status_write(struct vty *vty)
628 {
629 vty_out(vty, "MGMTD Datastores\n");
630
631 mgmt_ds_status_write_one(vty, mgmt_ds_mm->running_ds);
632
633 mgmt_ds_status_write_one(vty, mgmt_ds_mm->candidate_ds);
634
635 mgmt_ds_status_write_one(vty, mgmt_ds_mm->oper_ds);
636 }