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