1 // SPDX-License-Identifier: GPL-2.0-or-later
5 * Copyright (C) 2021 Vmware, Inc.
6 * Pushpasis Sarkar <spushpasis@vmware.com>
11 #include "mgmtd/mgmt.h"
12 #include "mgmtd/mgmt_memory.h"
13 #include "mgmtd/mgmt_ds.h"
14 #include "libyang/libyang.h"
16 #ifdef REDIRECT_DEBUG_TO_STDERR
17 #define MGMTD_DS_DBG(fmt, ...) \
18 fprintf(stderr, "%s: " fmt "\n", __func__, ##__VA_ARGS__)
19 #define MGMTD_DS_ERR(fmt, ...) \
20 fprintf(stderr, "%s: ERROR, " fmt "\n", __func__, ##__VA_ARGS__)
21 #else /* REDIRECT_DEBUG_TO_STDERR */
22 #define MGMTD_DS_DBG(fmt, ...) \
25 zlog_debug("%s: " fmt, __func__, ##__VA_ARGS__); \
27 #define MGMTD_DS_ERR(fmt, ...) \
28 zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__)
29 #endif /* REDIRECT_DEBUG_TO_STDERR */
32 enum mgmt_datastore_id ds_id
;
33 int lock
; /* 0 unlocked, >0 read locked < write locked */
38 struct nb_config
*cfg_root
;
39 struct lyd_node
*dnode_root
;
43 const char *mgmt_ds_names
[MGMTD_DS_MAX_ID
+ 1] = {
44 MGMTD_DS_NAME_NONE
, /* MGMTD_DS_NONE */
45 MGMTD_DS_NAME_RUNNING
, /* MGMTD_DS_RUNNING */
46 MGMTD_DS_NAME_CANDIDATE
, /* MGMTD_DS_CANDIDATE */
47 MGMTD_DS_NAME_OPERATIONAL
, /* MGMTD_DS_OPERATIONAL */
48 "Unknown/Invalid", /* MGMTD_DS_ID_MAX */
51 static struct mgmt_master
*mgmt_ds_mm
;
52 static struct mgmt_ds_ctx running
, candidate
, oper
;
54 /* Dump the data tree of the specified format in the file pointed by the path */
55 static int mgmt_ds_dump_in_memory(struct mgmt_ds_ctx
*ds_ctx
,
56 const char *base_xpath
, LYD_FORMAT format
,
59 struct lyd_node
*root
;
62 if (base_xpath
[0] == '\0')
63 root
= ds_ctx
->config_ds
? ds_ctx
->root
.cfg_root
->dnode
64 : ds_ctx
->root
.dnode_root
;
66 root
= yang_dnode_get(ds_ctx
->config_ds
67 ? ds_ctx
->root
.cfg_root
->dnode
68 : ds_ctx
->root
.dnode_root
,
73 options
= ds_ctx
->config_ds
? LYD_PRINT_WD_TRIM
:
74 LYD_PRINT_WD_EXPLICIT
;
76 if (base_xpath
[0] == '\0')
77 lyd_print_all(out
, root
, format
, options
);
79 lyd_print_tree(out
, root
, format
, options
);
84 static int mgmt_ds_replace_dst_with_src_ds(struct mgmt_ds_ctx
*src
,
85 struct mgmt_ds_ctx
*dst
)
87 struct lyd_node
*dst_dnode
, *src_dnode
;
92 MGMTD_DS_DBG("Replacing %d with %d", dst
->ds_id
, src
->ds_id
);
94 src_dnode
= src
->config_ds
? src
->root
.cfg_root
->dnode
95 : dst
->root
.dnode_root
;
96 dst_dnode
= dst
->config_ds
? dst
->root
.cfg_root
->dnode
97 : dst
->root
.dnode_root
;
100 yang_dnode_free(dst_dnode
);
102 /* Not using nb_config_replace as the oper ds does not contain nb_config
104 dst_dnode
= yang_dnode_dup(src_dnode
);
106 dst
->root
.cfg_root
->dnode
= dst_dnode
;
108 dst
->root
.dnode_root
= dst_dnode
;
110 if (dst
->ds_id
== MGMTD_DS_RUNNING
) {
111 if (ly_out_new_filepath(MGMTD_STARTUP_DS_FILE_PATH
, &out
)
113 mgmt_ds_dump_in_memory(dst
, "", LYD_JSON
, out
);
114 ly_out_free(out
, NULL
, 0);
117 /* TODO: Update the versions if nb_config present */
122 static int mgmt_ds_merge_src_with_dst_ds(struct mgmt_ds_ctx
*src
,
123 struct mgmt_ds_ctx
*dst
)
126 struct lyd_node
**dst_dnode
, *src_dnode
;
132 MGMTD_DS_DBG("Merging DS %d with %d", dst
->ds_id
, src
->ds_id
);
134 src_dnode
= src
->config_ds
? src
->root
.cfg_root
->dnode
135 : dst
->root
.dnode_root
;
136 dst_dnode
= dst
->config_ds
? &dst
->root
.cfg_root
->dnode
137 : &dst
->root
.dnode_root
;
138 ret
= lyd_merge_siblings(dst_dnode
, src_dnode
, 0);
140 MGMTD_DS_ERR("lyd_merge() failed with err %d", ret
);
144 if (dst
->ds_id
== MGMTD_DS_RUNNING
) {
145 if (ly_out_new_filepath(MGMTD_STARTUP_DS_FILE_PATH
, &out
)
147 mgmt_ds_dump_in_memory(dst
, "", LYD_JSON
, out
);
148 ly_out_free(out
, NULL
, 0);
154 static int mgmt_ds_load_cfg_from_file(const char *filepath
,
155 struct lyd_node
**dnode
)
160 ret
= lyd_parse_data_path(ly_native_ctx
, filepath
, LYD_JSON
,
161 LYD_PARSE_STRICT
, 0, dnode
);
163 if (ret
!= LY_SUCCESS
) {
165 yang_dnode_free(*dnode
);
172 int mgmt_ds_init(struct mgmt_master
*mm
)
174 struct lyd_node
*root
;
176 if (mgmt_ds_mm
|| mm
->running_ds
|| mm
->candidate_ds
|| mm
->oper_ds
)
177 assert(!"MGMTD: Call ds_init only once!");
179 /* Use Running DS from NB module??? */
181 assert(!"MGMTD: Call ds_init after frr_init only!");
183 if (mgmt_ds_load_cfg_from_file(MGMTD_STARTUP_DS_FILE_PATH
, &root
)
185 nb_config_free(running_config
);
186 running_config
= nb_config_new(root
);
189 running
.root
.cfg_root
= running_config
;
190 running
.config_ds
= true;
191 running
.ds_id
= MGMTD_DS_RUNNING
;
193 candidate
.root
.cfg_root
= nb_config_dup(running
.root
.cfg_root
);
194 candidate
.config_ds
= true;
195 candidate
.ds_id
= MGMTD_DS_CANDIDATE
;
197 oper
.root
.dnode_root
= yang_dnode_new(ly_native_ctx
, true);
198 oper
.config_ds
= false;
199 oper
.ds_id
= MGMTD_DS_OPERATIONAL
;
201 mm
->running_ds
= &running
;
202 mm
->candidate_ds
= &candidate
;
209 void mgmt_ds_destroy(void)
213 * TODO: Free the datastores.
218 struct mgmt_ds_ctx
*mgmt_ds_get_ctx_by_id(struct mgmt_master
*mm
,
219 enum mgmt_datastore_id 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
);
229 case MGMTD_DS_MAX_ID
:
237 bool mgmt_ds_is_config(struct mgmt_ds_ctx
*ds_ctx
)
242 return ds_ctx
->config_ds
;
245 int mgmt_ds_read_lock(struct mgmt_ds_ctx
*ds_ctx
)
249 if (ds_ctx
->lock
< 0)
255 int mgmt_ds_write_lock(struct mgmt_ds_ctx
*ds_ctx
)
259 if (ds_ctx
->lock
!= 0)
265 int mgmt_ds_unlock(struct mgmt_ds_ctx
*ds_ctx
)
269 if (ds_ctx
->lock
> 0)
271 else if (ds_ctx
->lock
< 0) {
272 assert(ds_ctx
->lock
== -1);
275 assert(ds_ctx
->lock
!= 0);
281 int mgmt_ds_merge_dss(struct mgmt_ds_ctx
*src_ds_ctx
,
282 struct mgmt_ds_ctx
*dst_ds_ctx
, bool updt_cmt_rec
)
284 if (mgmt_ds_merge_src_with_dst_ds(src_ds_ctx
, dst_ds_ctx
) != 0)
290 int mgmt_ds_copy_dss(struct mgmt_ds_ctx
*src_ds_ctx
,
291 struct mgmt_ds_ctx
*dst_ds_ctx
, bool updt_cmt_rec
)
293 if (mgmt_ds_replace_dst_with_src_ds(src_ds_ctx
, dst_ds_ctx
) != 0)
299 int mgmt_ds_dump_ds_to_file(char *file_name
, struct mgmt_ds_ctx
*ds_ctx
)
304 if (ly_out_new_filepath(file_name
, &out
) == LY_SUCCESS
) {
305 ret
= mgmt_ds_dump_in_memory(ds_ctx
, "", LYD_JSON
, out
);
306 ly_out_free(out
, NULL
, 0);
312 struct nb_config
*mgmt_ds_get_nb_config(struct mgmt_ds_ctx
*ds_ctx
)
317 return ds_ctx
->config_ds
? ds_ctx
->root
.cfg_root
: NULL
;
320 static int mgmt_walk_ds_nodes(
321 struct mgmt_ds_ctx
*ds_ctx
, char *base_xpath
,
322 struct lyd_node
*base_dnode
,
323 void (*mgmt_ds_node_iter_fn
)(struct mgmt_ds_ctx
*ds_ctx
, char *xpath
,
324 struct lyd_node
*node
,
325 struct nb_node
*nb_node
, void *ctx
),
326 void *ctx
, char *xpaths
[], int *num_nodes
, bool childs_as_well
,
330 char *xpath
, *xpath_buf
, *iter_xp
;
331 int ret
, num_left
= 0, num_found
= 0;
332 struct lyd_node
*dnode
;
333 struct nb_node
*nbnode
;
334 bool alloc_xp
= false;
339 if (num_nodes
&& !*num_nodes
)
343 num_left
= *num_nodes
;
344 MGMTD_DS_DBG(" -- START: num_left:%d", num_left
);
348 MGMTD_DS_DBG(" -- START: Base: %s", base_xpath
);
351 base_dnode
= yang_dnode_get(
352 ds_ctx
->config_ds
? ds_ctx
->root
.cfg_root
->dnode
353 : ds_ctx
->root
.dnode_root
,
358 if (mgmt_ds_node_iter_fn
) {
360 * In case the caller is interested in getting a copy
361 * of the xpath for themselves (by setting
362 * 'alloc_xp_copy' to 'true') we make a copy for the
363 * caller and pass it. Else we pass the original xpath
366 * NOTE: In such case caller will have to take care of
369 iter_xp
= alloc_xp_copy
? strdup(base_xpath
) : base_xpath
;
371 nbnode
= (struct nb_node
*)base_dnode
->schema
->priv
;
372 (*mgmt_ds_node_iter_fn
)(ds_ctx
, iter_xp
, base_dnode
, nbnode
,
381 /* If the base_xpath points to leaf node, we can skip the tree walk */
382 if (base_dnode
->schema
->nodetype
& LYD_NODE_TERM
)
386 LY_LIST_FOR (lyd_child(base_dnode
), dnode
) {
387 assert(dnode
->schema
&& dnode
->schema
->priv
);
388 nbnode
= (struct nb_node
*)dnode
->schema
->priv
;
392 if (!xpaths
[*num_nodes
]) {
395 (char *)calloc(1, MGMTD_MAX_XPATH_LEN
);
397 xpath
= lyd_path(dnode
, LYD_PATH_STD
,
399 MGMTD_MAX_XPATH_LEN
);
402 xpath_buf
= (char *)calloc(1, MGMTD_MAX_XPATH_LEN
);
403 (void) lyd_path(dnode
, LYD_PATH_STD
, xpath_buf
,
404 MGMTD_MAX_XPATH_LEN
);
409 MGMTD_DS_DBG(" -- XPATH: %s", xpath
);
415 num_found
= num_left
;
417 ret
= mgmt_walk_ds_nodes(ds_ctx
, xpath
, dnode
,
418 mgmt_ds_node_iter_fn
, ctx
,
419 xpaths
? &xpaths
[*num_nodes
] : NULL
,
420 num_nodes
? &num_found
: NULL
,
421 childs_as_well
, alloc_xp_copy
);
424 num_left
-= num_found
;
425 (*num_nodes
) += num_found
;
439 MGMTD_DS_DBG(" -- END: *num_nodes:%d, num_left:%d", *num_nodes
,
446 int mgmt_ds_lookup_data_nodes(struct mgmt_ds_ctx
*ds_ctx
, const char *xpath
,
447 char *dxpaths
[], int *num_nodes
,
448 bool get_childs_as_well
, bool alloc_xp_copy
)
450 char base_xpath
[MGMTD_MAX_XPATH_LEN
];
452 if (!ds_ctx
|| !num_nodes
)
455 if (xpath
[0] == '.' && xpath
[1] == '/')
458 strlcpy(base_xpath
, xpath
, sizeof(base_xpath
));
459 mgmt_remove_trailing_separator(base_xpath
, '/');
461 return (mgmt_walk_ds_nodes(ds_ctx
, base_xpath
, NULL
, NULL
, NULL
,
462 dxpaths
, num_nodes
, get_childs_as_well
,
466 struct lyd_node
*mgmt_ds_find_data_node_by_xpath(struct mgmt_ds_ctx
*ds_ctx
,
472 return yang_dnode_get(ds_ctx
->config_ds
? ds_ctx
->root
.cfg_root
->dnode
473 : ds_ctx
->root
.dnode_root
,
477 int mgmt_ds_delete_data_nodes(struct mgmt_ds_ctx
*ds_ctx
, const char *xpath
)
479 struct nb_node
*nb_node
;
480 struct lyd_node
*dnode
, *dep_dnode
;
481 char dep_xpath
[XPATH_MAXLEN
];
486 nb_node
= nb_node_find(xpath
);
488 dnode
= yang_dnode_get(ds_ctx
->config_ds
489 ? ds_ctx
->root
.cfg_root
->dnode
490 : ds_ctx
->root
.dnode_root
,
495 * Return a special error code so the caller can choose
496 * whether to ignore it or not.
498 return NB_ERR_NOT_FOUND
;
499 /* destroy dependant */
500 if (nb_node
->dep_cbs
.get_dependant_xpath
) {
501 nb_node
->dep_cbs
.get_dependant_xpath(dnode
, dep_xpath
);
503 dep_dnode
= yang_dnode_get(
504 ds_ctx
->config_ds
? ds_ctx
->root
.cfg_root
->dnode
505 : ds_ctx
->root
.dnode_root
,
508 lyd_free_tree(dep_dnode
);
510 lyd_free_tree(dnode
);
515 int mgmt_ds_load_config_from_file(struct mgmt_ds_ctx
*dst
,
516 const char *file_path
, bool merge
)
518 struct lyd_node
*iter
;
519 struct mgmt_ds_ctx parsed
;
524 if (mgmt_ds_load_cfg_from_file(file_path
, &iter
) != 0) {
525 MGMTD_DS_ERR("Failed to load config from the file %s",
530 parsed
.root
.cfg_root
= nb_config_new(iter
);
531 parsed
.config_ds
= true;
532 parsed
.ds_id
= dst
->ds_id
;
535 mgmt_ds_merge_src_with_dst_ds(&parsed
, dst
);
537 mgmt_ds_replace_dst_with_src_ds(&parsed
, dst
);
539 nb_config_free(parsed
.root
.cfg_root
);
544 int mgmt_ds_iter_data(struct mgmt_ds_ctx
*ds_ctx
, char *base_xpath
,
545 void (*mgmt_ds_node_iter_fn
)(struct mgmt_ds_ctx
*ds_ctx
,
547 struct lyd_node
*node
,
548 struct nb_node
*nb_node
,
550 void *ctx
, bool alloc_xp_copy
)
553 char xpath
[MGMTD_MAX_XPATH_LEN
];
554 struct lyd_node
*base_dnode
= NULL
;
555 struct lyd_node
*node
;
560 mgmt_remove_trailing_separator(base_xpath
, '/');
562 strlcpy(xpath
, base_xpath
, sizeof(xpath
));
564 MGMTD_DS_DBG(" -- START DS walk for DSid: %d", ds_ctx
->ds_id
);
566 /* If the base_xpath is empty then crawl the sibblings */
567 if (xpath
[0] == '\0') {
568 base_dnode
= ds_ctx
->config_ds
? ds_ctx
->root
.cfg_root
->dnode
569 : ds_ctx
->root
.dnode_root
;
571 /* get first top-level sibling */
572 while (base_dnode
->parent
)
573 base_dnode
= lyd_parent(base_dnode
);
575 while (base_dnode
->prev
->next
)
576 base_dnode
= base_dnode
->prev
;
578 LY_LIST_FOR (base_dnode
, node
) {
579 ret
= mgmt_walk_ds_nodes(
580 ds_ctx
, xpath
, node
, mgmt_ds_node_iter_fn
,
581 ctx
, NULL
, NULL
, true, alloc_xp_copy
);
584 ret
= mgmt_walk_ds_nodes(ds_ctx
, xpath
, base_dnode
,
585 mgmt_ds_node_iter_fn
, ctx
, NULL
, NULL
,
586 true, alloc_xp_copy
);
591 void mgmt_ds_dump_tree(struct vty
*vty
, struct mgmt_ds_ctx
*ds_ctx
,
592 const char *xpath
, FILE *f
, LYD_FORMAT format
)
596 char base_xpath
[MGMTD_MAX_XPATH_LEN
] = {0};
599 vty_out(vty
, " >>>>> Datastore Not Initialized!\n");
604 strlcpy(base_xpath
, xpath
, MGMTD_MAX_XPATH_LEN
);
605 mgmt_remove_trailing_separator(base_xpath
, '/');
609 ly_out_new_file(f
, &out
);
611 ly_out_new_memory(&str
, 0, &out
);
613 mgmt_ds_dump_in_memory(ds_ctx
, base_xpath
, format
, out
);
616 vty_out(vty
, "%s\n", str
);
618 ly_out_free(out
, NULL
, 0);
621 void mgmt_ds_status_write_one(struct vty
*vty
, struct mgmt_ds_ctx
*ds_ctx
)
624 vty_out(vty
, " >>>>> Datastore Not Initialized!\n");
628 vty_out(vty
, " DS: %s\n", mgmt_ds_id2name(ds_ctx
->ds_id
));
629 vty_out(vty
, " DS-Hndl: \t\t\t%p\n", ds_ctx
);
630 vty_out(vty
, " Config: \t\t\t%s\n",
631 ds_ctx
->config_ds
? "True" : "False");
634 void mgmt_ds_status_write(struct vty
*vty
)
636 vty_out(vty
, "MGMTD Datastores\n");
638 mgmt_ds_status_write_one(vty
, mgmt_ds_mm
->running_ds
);
640 mgmt_ds_status_write_one(vty
, mgmt_ds_mm
->candidate_ds
);
642 mgmt_ds_status_write_one(vty
, mgmt_ds_mm
->oper_ds
);