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 "mgmtd/mgmt_history.h"
15 #include "mgmtd/mgmt_txn.h"
16 #include "libyang/libyang.h"
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__)
24 Mgmtd__DatastoreId ds_id
;
25 int lock
; /* 0 unlocked, >0 read locked < write locked */
30 struct nb_config
*cfg_root
;
31 struct lyd_node
*dnode_root
;
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 */
43 static struct mgmt_master
*mgmt_ds_mm
;
44 static struct mgmt_ds_ctx running
, candidate
, oper
;
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
,
51 struct lyd_node
*root
;
54 if (base_xpath
[0] == '\0')
55 root
= ds_ctx
->config_ds
? ds_ctx
->root
.cfg_root
->dnode
56 : ds_ctx
->root
.dnode_root
;
58 root
= yang_dnode_get(ds_ctx
->config_ds
59 ? ds_ctx
->root
.cfg_root
->dnode
60 : ds_ctx
->root
.dnode_root
,
65 options
= ds_ctx
->config_ds
? LYD_PRINT_WD_TRIM
:
66 LYD_PRINT_WD_EXPLICIT
;
68 if (base_xpath
[0] == '\0')
69 lyd_print_all(out
, root
, format
, options
);
71 lyd_print_tree(out
, root
, format
, options
);
76 static int mgmt_ds_replace_dst_with_src_ds(struct mgmt_ds_ctx
*src
,
77 struct mgmt_ds_ctx
*dst
)
79 struct lyd_node
*dst_dnode
, *src_dnode
;
83 MGMTD_DS_DBG("Replacing %d with %d", dst
->ds_id
, src
->ds_id
);
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
;
91 yang_dnode_free(dst_dnode
);
93 /* Not using nb_config_replace as the oper ds does not contain nb_config
95 dst_dnode
= yang_dnode_dup(src_dnode
);
97 dst
->root
.cfg_root
->dnode
= dst_dnode
;
99 dst
->root
.dnode_root
= dst_dnode
;
101 if (src
->ds_id
== MGMTD_DS_CANDIDATE
) {
103 * Drop the changes in scratch-buffer.
105 MGMTD_DS_DBG("Emptying Candidate Scratch buffer!");
106 nb_config_diff_del_changes(&src
->root
.cfg_root
->cfg_chgs
);
109 /* TODO: Update the versions if nb_config present */
114 static int mgmt_ds_merge_src_with_dst_ds(struct mgmt_ds_ctx
*src
,
115 struct mgmt_ds_ctx
*dst
)
118 struct lyd_node
**dst_dnode
, *src_dnode
;
123 MGMTD_DS_DBG("Merging DS %d with %d", dst
->ds_id
, src
->ds_id
);
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);
131 MGMTD_DS_ERR("lyd_merge() failed with err %d", ret
);
135 if (src
->ds_id
== MGMTD_DS_CANDIDATE
) {
137 * Drop the changes in scratch-buffer.
139 MGMTD_DS_DBG("Emptying Candidate Scratch buffer!");
140 nb_config_diff_del_changes(&src
->root
.cfg_root
->cfg_chgs
);
146 static int mgmt_ds_load_cfg_from_file(const char *filepath
,
147 struct lyd_node
**dnode
)
152 ret
= lyd_parse_data_path(ly_native_ctx
, filepath
, LYD_JSON
,
153 LYD_PARSE_STRICT
, 0, dnode
);
155 if (ret
!= LY_SUCCESS
) {
157 yang_dnode_free(*dnode
);
164 void mgmt_ds_reset_candidate(void)
166 struct lyd_node
*dnode
= mm
->candidate_ds
->root
.cfg_root
->dnode
;
169 yang_dnode_free(dnode
);
171 dnode
= yang_dnode_new(ly_native_ctx
, true);
172 mm
->candidate_ds
->root
.cfg_root
->dnode
= dnode
;
176 int mgmt_ds_init(struct mgmt_master
*mm
)
178 if (mgmt_ds_mm
|| mm
->running_ds
|| mm
->candidate_ds
|| mm
->oper_ds
)
179 assert(!"MGMTD: Call ds_init only once!");
181 /* Use Running DS from NB module??? */
183 assert(!"MGMTD: Call ds_init after frr_init only!");
185 running
.root
.cfg_root
= running_config
;
186 running
.config_ds
= true;
187 running
.ds_id
= MGMTD_DS_RUNNING
;
189 candidate
.root
.cfg_root
= nb_config_dup(running
.root
.cfg_root
);
190 candidate
.config_ds
= true;
191 candidate
.ds_id
= MGMTD_DS_CANDIDATE
;
194 * Redirect lib/vty candidate-config datastore to the global candidate
195 * config Ds on the MGMTD process.
197 vty_mgmt_candidate_config
= candidate
.root
.cfg_root
;
199 oper
.root
.dnode_root
= yang_dnode_new(ly_native_ctx
, true);
200 oper
.config_ds
= false;
201 oper
.ds_id
= MGMTD_DS_OPERATIONAL
;
203 mm
->running_ds
= &running
;
204 mm
->candidate_ds
= &candidate
;
211 void mgmt_ds_destroy(void)
214 * TODO: Free the datastores.
218 struct mgmt_ds_ctx
*mgmt_ds_get_ctx_by_id(struct mgmt_master
*mm
,
219 Mgmtd__DatastoreId 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__DATASTORE_ID__STARTUP_DS
:
230 case _MGMTD__DATASTORE_ID_IS_INT_SIZE
:
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_copy_dss(struct mgmt_ds_ctx
*src_ds_ctx
,
282 struct mgmt_ds_ctx
*dst_ds_ctx
, bool updt_cmt_rec
)
284 if (mgmt_ds_replace_dst_with_src_ds(src_ds_ctx
, dst_ds_ctx
) != 0)
287 if (updt_cmt_rec
&& dst_ds_ctx
->ds_id
== MGMTD_DS_RUNNING
)
288 mgmt_history_new_record(dst_ds_ctx
);
293 int mgmt_ds_dump_ds_to_file(char *file_name
, struct mgmt_ds_ctx
*ds_ctx
)
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);
306 struct nb_config
*mgmt_ds_get_nb_config(struct mgmt_ds_ctx
*ds_ctx
)
311 return ds_ctx
->config_ds
? ds_ctx
->root
.cfg_root
: NULL
;
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
,
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;
333 if (num_nodes
&& !*num_nodes
)
337 num_left
= *num_nodes
;
338 MGMTD_DS_DBG(" -- START: num_left:%d", num_left
);
342 MGMTD_DS_DBG(" -- START: Base: %s", base_xpath
);
345 base_dnode
= yang_dnode_get(
346 ds_ctx
->config_ds
? ds_ctx
->root
.cfg_root
->dnode
347 : ds_ctx
->root
.dnode_root
,
352 if (mgmt_ds_node_iter_fn
) {
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
360 * NOTE: In such case caller will have to take care of
363 iter_xp
= alloc_xp_copy
? strdup(base_xpath
) : base_xpath
;
365 nbnode
= (struct nb_node
*)base_dnode
->schema
->priv
;
366 (*mgmt_ds_node_iter_fn
)(ds_ctx
, iter_xp
, base_dnode
, nbnode
,
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.
379 if (!childs_as_well
|| base_dnode
->schema
->nodetype
& LYD_NODE_TERM
)
383 LY_LIST_FOR (lyd_child(base_dnode
), dnode
) {
384 assert(dnode
->schema
&& dnode
->schema
->priv
);
388 if (!xpaths
[*num_nodes
]) {
391 (char *)calloc(1, MGMTD_MAX_XPATH_LEN
);
393 xpath
= lyd_path(dnode
, LYD_PATH_STD
,
395 MGMTD_MAX_XPATH_LEN
);
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
);
405 MGMTD_DS_DBG(" -- XPATH: %s", xpath
);
408 num_found
= num_left
;
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
);
417 num_left
-= num_found
;
418 (*num_nodes
) += num_found
;
432 MGMTD_DS_DBG(" -- END: *num_nodes:%d, num_left:%d", *num_nodes
,
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
)
443 char base_xpath
[MGMTD_MAX_XPATH_LEN
];
445 if (!ds_ctx
|| !num_nodes
)
448 if (xpath
[0] == '.' && xpath
[1] == '/')
451 strlcpy(base_xpath
, xpath
, sizeof(base_xpath
));
452 mgmt_remove_trailing_separator(base_xpath
, '/');
454 return (mgmt_walk_ds_nodes(ds_ctx
, base_xpath
, NULL
, NULL
, NULL
,
455 dxpaths
, num_nodes
, get_childs_as_well
,
459 struct lyd_node
*mgmt_ds_find_data_node_by_xpath(struct mgmt_ds_ctx
*ds_ctx
,
465 return yang_dnode_get(ds_ctx
->config_ds
? ds_ctx
->root
.cfg_root
->dnode
466 : ds_ctx
->root
.dnode_root
,
470 int mgmt_ds_delete_data_nodes(struct mgmt_ds_ctx
*ds_ctx
, const char *xpath
)
472 struct nb_node
*nb_node
;
473 struct lyd_node
*dnode
, *dep_dnode
;
474 char dep_xpath
[XPATH_MAXLEN
];
479 nb_node
= nb_node_find(xpath
);
481 dnode
= yang_dnode_get(ds_ctx
->config_ds
482 ? ds_ctx
->root
.cfg_root
->dnode
483 : ds_ctx
->root
.dnode_root
,
488 * Return a special error code so the caller can choose
489 * whether to ignore it or not.
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
);
496 dep_dnode
= yang_dnode_get(
497 ds_ctx
->config_ds
? ds_ctx
->root
.cfg_root
->dnode
498 : ds_ctx
->root
.dnode_root
,
501 lyd_free_tree(dep_dnode
);
503 lyd_free_tree(dnode
);
508 int mgmt_ds_load_config_from_file(struct mgmt_ds_ctx
*dst
,
509 const char *file_path
, bool merge
)
511 struct lyd_node
*iter
;
512 struct mgmt_ds_ctx parsed
;
517 if (mgmt_ds_load_cfg_from_file(file_path
, &iter
) != 0) {
518 MGMTD_DS_ERR("Failed to load config from the file %s",
523 parsed
.root
.cfg_root
= nb_config_new(iter
);
524 parsed
.config_ds
= true;
525 parsed
.ds_id
= dst
->ds_id
;
528 mgmt_ds_merge_src_with_dst_ds(&parsed
, dst
);
530 mgmt_ds_replace_dst_with_src_ds(&parsed
, dst
);
532 nb_config_free(parsed
.root
.cfg_root
);
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
,
540 struct lyd_node
*node
,
541 struct nb_node
*nb_node
,
543 void *ctx
, bool alloc_xp_copy
)
546 char xpath
[MGMTD_MAX_XPATH_LEN
];
547 struct lyd_node
*base_dnode
= NULL
;
548 struct lyd_node
*node
;
553 mgmt_remove_trailing_separator(base_xpath
, '/');
555 strlcpy(xpath
, base_xpath
, sizeof(xpath
));
557 MGMTD_DS_DBG(" -- START DS walk for DSid: %d", ds_ctx
->ds_id
);
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
;
564 /* get first top-level sibling */
565 while (base_dnode
->parent
)
566 base_dnode
= lyd_parent(base_dnode
);
568 while (base_dnode
->prev
->next
)
569 base_dnode
= base_dnode
->prev
;
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
);
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
);
584 void mgmt_ds_dump_tree(struct vty
*vty
, struct mgmt_ds_ctx
*ds_ctx
,
585 const char *xpath
, FILE *f
, LYD_FORMAT format
)
589 char base_xpath
[MGMTD_MAX_XPATH_LEN
] = {0};
592 vty_out(vty
, " >>>>> Datastore Not Initialized!\n");
597 strlcpy(base_xpath
, xpath
, MGMTD_MAX_XPATH_LEN
);
598 mgmt_remove_trailing_separator(base_xpath
, '/');
602 ly_out_new_file(f
, &out
);
604 ly_out_new_memory(&str
, 0, &out
);
606 mgmt_ds_dump_in_memory(ds_ctx
, base_xpath
, format
, out
);
609 vty_out(vty
, "%s\n", str
);
611 ly_out_free(out
, NULL
, 0);
614 void mgmt_ds_status_write_one(struct vty
*vty
, struct mgmt_ds_ctx
*ds_ctx
)
617 vty_out(vty
, " >>>>> Datastore Not Initialized!\n");
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");
627 void mgmt_ds_status_write(struct vty
*vty
)
629 vty_out(vty
, "MGMTD Datastores\n");
631 mgmt_ds_status_write_one(vty
, mgmt_ds_mm
->running_ds
);
633 mgmt_ds_status_write_one(vty
, mgmt_ds_mm
->candidate_ds
);
635 mgmt_ds_status_write_one(vty
, mgmt_ds_mm
->oper_ds
);