]> git.proxmox.com Git - mirror_frr.git/blob - mgmtd/mgmt_ds.c
mgmtd: Bringup MGMTD daemon and datastore module support
[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 "libyang/libyang.h"
15
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, ...) \
23 do { \
24 if (mgmt_debug_ds) \
25 zlog_debug("%s: " fmt, __func__, ##__VA_ARGS__); \
26 } while (0)
27 #define MGMTD_DS_ERR(fmt, ...) \
28 zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__)
29 #endif /* REDIRECT_DEBUG_TO_STDERR */
30
31 struct mgmt_ds_ctx {
32 enum mgmt_datastore_id ds_id;
33 int lock; /* 0 unlocked, >0 read locked < write locked */
34
35 bool config_ds;
36
37 union {
38 struct nb_config *cfg_root;
39 struct lyd_node *dnode_root;
40 } root;
41 };
42
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 */
49 };
50
51 static struct mgmt_master *mgmt_ds_mm;
52 static struct mgmt_ds_ctx running, candidate, oper;
53
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,
57 struct ly_out *out)
58 {
59 struct lyd_node *root;
60 uint32_t options = 0;
61
62 if (base_xpath[0] == '\0')
63 root = ds_ctx->config_ds ? ds_ctx->root.cfg_root->dnode
64 : ds_ctx->root.dnode_root;
65 else
66 root = yang_dnode_get(ds_ctx->config_ds
67 ? ds_ctx->root.cfg_root->dnode
68 : ds_ctx->root.dnode_root,
69 base_xpath);
70 if (!root)
71 return -1;
72
73 options = ds_ctx->config_ds ? LYD_PRINT_WD_TRIM :
74 LYD_PRINT_WD_EXPLICIT;
75
76 if (base_xpath[0] == '\0')
77 lyd_print_all(out, root, format, options);
78 else
79 lyd_print_tree(out, root, format, options);
80
81 return 0;
82 }
83
84 static int mgmt_ds_replace_dst_with_src_ds(struct mgmt_ds_ctx *src,
85 struct mgmt_ds_ctx *dst)
86 {
87 struct lyd_node *dst_dnode, *src_dnode;
88 struct ly_out *out;
89
90 if (!src || !dst)
91 return -1;
92 MGMTD_DS_DBG("Replacing %d with %d", dst->ds_id, src->ds_id);
93
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;
98
99 if (dst_dnode)
100 yang_dnode_free(dst_dnode);
101
102 /* Not using nb_config_replace as the oper ds does not contain nb_config
103 */
104 dst_dnode = yang_dnode_dup(src_dnode);
105 if (dst->config_ds)
106 dst->root.cfg_root->dnode = dst_dnode;
107 else
108 dst->root.dnode_root = dst_dnode;
109
110 if (dst->ds_id == MGMTD_DS_RUNNING) {
111 if (ly_out_new_filepath(MGMTD_STARTUP_DS_FILE_PATH, &out)
112 == LY_SUCCESS)
113 mgmt_ds_dump_in_memory(dst, "", LYD_JSON, out);
114 ly_out_free(out, NULL, 0);
115 }
116
117 /* TODO: Update the versions if nb_config present */
118
119 return 0;
120 }
121
122 static int mgmt_ds_merge_src_with_dst_ds(struct mgmt_ds_ctx *src,
123 struct mgmt_ds_ctx *dst)
124 {
125 int ret;
126 struct lyd_node **dst_dnode, *src_dnode;
127 struct ly_out *out;
128
129 if (!src || !dst)
130 return -1;
131
132 MGMTD_DS_DBG("Merging DS %d with %d", dst->ds_id, src->ds_id);
133
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);
139 if (ret != 0) {
140 MGMTD_DS_ERR("lyd_merge() failed with err %d", ret);
141 return ret;
142 }
143
144 if (dst->ds_id == MGMTD_DS_RUNNING) {
145 if (ly_out_new_filepath(MGMTD_STARTUP_DS_FILE_PATH, &out)
146 == LY_SUCCESS)
147 mgmt_ds_dump_in_memory(dst, "", LYD_JSON, out);
148 ly_out_free(out, NULL, 0);
149 }
150
151 return 0;
152 }
153
154 static int mgmt_ds_load_cfg_from_file(const char *filepath,
155 struct lyd_node **dnode)
156 {
157 LY_ERR ret;
158
159 *dnode = NULL;
160 ret = lyd_parse_data_path(ly_native_ctx, filepath, LYD_JSON,
161 LYD_PARSE_STRICT, 0, dnode);
162
163 if (ret != LY_SUCCESS) {
164 if (*dnode)
165 yang_dnode_free(*dnode);
166 return -1;
167 }
168
169 return 0;
170 }
171
172 int mgmt_ds_init(struct mgmt_master *mm)
173 {
174 struct lyd_node *root;
175
176 if (mgmt_ds_mm || mm->running_ds || mm->candidate_ds || mm->oper_ds)
177 assert(!"MGMTD: Call ds_init only once!");
178
179 /* Use Running DS from NB module??? */
180 if (!running_config)
181 assert(!"MGMTD: Call ds_init after frr_init only!");
182
183 if (mgmt_ds_load_cfg_from_file(MGMTD_STARTUP_DS_FILE_PATH, &root)
184 == 0) {
185 nb_config_free(running_config);
186 running_config = nb_config_new(root);
187 }
188
189 running.root.cfg_root = running_config;
190 running.config_ds = true;
191 running.ds_id = MGMTD_DS_RUNNING;
192
193 candidate.root.cfg_root = nb_config_dup(running.root.cfg_root);
194 candidate.config_ds = true;
195 candidate.ds_id = MGMTD_DS_CANDIDATE;
196
197 oper.root.dnode_root = yang_dnode_new(ly_native_ctx, true);
198 oper.config_ds = false;
199 oper.ds_id = MGMTD_DS_OPERATIONAL;
200
201 mm->running_ds = &running;
202 mm->candidate_ds = &candidate;
203 mm->oper_ds = &oper;
204 mgmt_ds_mm = mm;
205
206 return 0;
207 }
208
209 void mgmt_ds_destroy(void)
210 {
211
212 /*
213 * TODO: Free the datastores.
214 */
215
216 }
217
218 struct mgmt_ds_ctx *mgmt_ds_get_ctx_by_id(struct mgmt_master *mm,
219 enum mgmt_datastore_id 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_DS_MAX_ID:
230 default:
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_merge_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_merge_src_with_dst_ds(src_ds_ctx, dst_ds_ctx) != 0)
285 return -1;
286
287 return 0;
288 }
289
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)
292 {
293 if (mgmt_ds_replace_dst_with_src_ds(src_ds_ctx, dst_ds_ctx) != 0)
294 return -1;
295
296 return 0;
297 }
298
299 int mgmt_ds_dump_ds_to_file(char *file_name, struct mgmt_ds_ctx *ds_ctx)
300 {
301 struct ly_out *out;
302 int ret = 0;
303
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);
307 }
308
309 return ret;
310 }
311
312 struct nb_config *mgmt_ds_get_nb_config(struct mgmt_ds_ctx *ds_ctx)
313 {
314 if (!ds_ctx)
315 return NULL;
316
317 return ds_ctx->config_ds ? ds_ctx->root.cfg_root : NULL;
318 }
319
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,
327 bool alloc_xp_copy)
328 {
329 uint32_t indx;
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;
335
336 if (xpaths)
337 assert(num_nodes);
338
339 if (num_nodes && !*num_nodes)
340 return 0;
341
342 if (num_nodes) {
343 num_left = *num_nodes;
344 MGMTD_DS_DBG(" -- START: num_left:%d", num_left);
345 *num_nodes = 0;
346 }
347
348 MGMTD_DS_DBG(" -- START: Base: %s", base_xpath);
349
350 if (!base_dnode)
351 base_dnode = yang_dnode_get(
352 ds_ctx->config_ds ? ds_ctx->root.cfg_root->dnode
353 : ds_ctx->root.dnode_root,
354 base_xpath);
355 if (!base_dnode)
356 return -1;
357
358 if (mgmt_ds_node_iter_fn) {
359 /*
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
364 * buffer.
365 *
366 * NOTE: In such case caller will have to take care of
367 * the copy later.
368 */
369 iter_xp = alloc_xp_copy ? strdup(base_xpath) : base_xpath;
370
371 nbnode = (struct nb_node *)base_dnode->schema->priv;
372 (*mgmt_ds_node_iter_fn)(ds_ctx, iter_xp, base_dnode, nbnode,
373 ctx);
374 }
375
376 if (num_nodes) {
377 (*num_nodes)++;
378 num_left--;
379 }
380
381 /* If the base_xpath points to leaf node, we can skip the tree walk */
382 if (base_dnode->schema->nodetype & LYD_NODE_TERM)
383 return 0;
384
385 indx = 0;
386 LY_LIST_FOR (lyd_child(base_dnode), dnode) {
387 assert(dnode->schema && dnode->schema->priv);
388 nbnode = (struct nb_node *)dnode->schema->priv;
389
390 xpath = NULL;
391 if (xpaths) {
392 if (!xpaths[*num_nodes]) {
393 alloc_xp = true;
394 xpaths[*num_nodes] =
395 (char *)calloc(1, MGMTD_MAX_XPATH_LEN);
396 }
397 xpath = lyd_path(dnode, LYD_PATH_STD,
398 xpaths[*num_nodes],
399 MGMTD_MAX_XPATH_LEN);
400 } else {
401 alloc_xp = true;
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);
405 xpath = xpath_buf;
406 }
407
408 assert(xpath);
409 MGMTD_DS_DBG(" -- XPATH: %s", xpath);
410
411 if (!childs_as_well)
412 continue;
413
414 if (num_nodes)
415 num_found = num_left;
416
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);
422
423 if (num_nodes) {
424 num_left -= num_found;
425 (*num_nodes) += num_found;
426 }
427
428 if (alloc_xp)
429 free(xpath);
430
431 if (ret != 0)
432 break;
433
434 indx++;
435 }
436
437
438 if (num_nodes) {
439 MGMTD_DS_DBG(" -- END: *num_nodes:%d, num_left:%d", *num_nodes,
440 num_left);
441 }
442
443 return 0;
444 }
445
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)
449 {
450 char base_xpath[MGMTD_MAX_XPATH_LEN];
451
452 if (!ds_ctx || !num_nodes)
453 return -1;
454
455 if (xpath[0] == '.' && xpath[1] == '/')
456 xpath += 2;
457
458 strlcpy(base_xpath, xpath, sizeof(base_xpath));
459 mgmt_remove_trailing_separator(base_xpath, '/');
460
461 return (mgmt_walk_ds_nodes(ds_ctx, base_xpath, NULL, NULL, NULL,
462 dxpaths, num_nodes, get_childs_as_well,
463 alloc_xp_copy));
464 }
465
466 struct lyd_node *mgmt_ds_find_data_node_by_xpath(struct mgmt_ds_ctx *ds_ctx,
467 const char *xpath)
468 {
469 if (!ds_ctx)
470 return NULL;
471
472 return yang_dnode_get(ds_ctx->config_ds ? ds_ctx->root.cfg_root->dnode
473 : ds_ctx->root.dnode_root,
474 xpath);
475 }
476
477 int mgmt_ds_delete_data_nodes(struct mgmt_ds_ctx *ds_ctx, const char *xpath)
478 {
479 struct nb_node *nb_node;
480 struct lyd_node *dnode, *dep_dnode;
481 char dep_xpath[XPATH_MAXLEN];
482
483 if (!ds_ctx)
484 return -1;
485
486 nb_node = nb_node_find(xpath);
487
488 dnode = yang_dnode_get(ds_ctx->config_ds
489 ? ds_ctx->root.cfg_root->dnode
490 : ds_ctx->root.dnode_root,
491 xpath);
492
493 if (!dnode)
494 /*
495 * Return a special error code so the caller can choose
496 * whether to ignore it or not.
497 */
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);
502
503 dep_dnode = yang_dnode_get(
504 ds_ctx->config_ds ? ds_ctx->root.cfg_root->dnode
505 : ds_ctx->root.dnode_root,
506 dep_xpath);
507 if (dep_dnode)
508 lyd_free_tree(dep_dnode);
509 }
510 lyd_free_tree(dnode);
511
512 return 0;
513 }
514
515 int mgmt_ds_load_config_from_file(struct mgmt_ds_ctx *dst,
516 const char *file_path, bool merge)
517 {
518 struct lyd_node *iter;
519 struct mgmt_ds_ctx parsed;
520
521 if (!dst)
522 return -1;
523
524 if (mgmt_ds_load_cfg_from_file(file_path, &iter) != 0) {
525 MGMTD_DS_ERR("Failed to load config from the file %s",
526 file_path);
527 return -1;
528 }
529
530 parsed.root.cfg_root = nb_config_new(iter);
531 parsed.config_ds = true;
532 parsed.ds_id = dst->ds_id;
533
534 if (merge)
535 mgmt_ds_merge_src_with_dst_ds(&parsed, dst);
536 else
537 mgmt_ds_replace_dst_with_src_ds(&parsed, dst);
538
539 nb_config_free(parsed.root.cfg_root);
540
541 return 0;
542 }
543
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,
546 char *xpath,
547 struct lyd_node *node,
548 struct nb_node *nb_node,
549 void *ctx),
550 void *ctx, bool alloc_xp_copy)
551 {
552 int ret;
553 char xpath[MGMTD_MAX_XPATH_LEN];
554 struct lyd_node *base_dnode = NULL;
555 struct lyd_node *node;
556
557 if (!ds_ctx)
558 return -1;
559
560 mgmt_remove_trailing_separator(base_xpath, '/');
561
562 strlcpy(xpath, base_xpath, sizeof(xpath));
563
564 MGMTD_DS_DBG(" -- START DS walk for DSid: %d", ds_ctx->ds_id);
565
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;
570
571 /* get first top-level sibling */
572 while (base_dnode->parent)
573 base_dnode = lyd_parent(base_dnode);
574
575 while (base_dnode->prev->next)
576 base_dnode = base_dnode->prev;
577
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);
582 }
583 } else
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);
587
588 return ret;
589 }
590
591 void mgmt_ds_dump_tree(struct vty *vty, struct mgmt_ds_ctx *ds_ctx,
592 const char *xpath, FILE *f, LYD_FORMAT format)
593 {
594 struct ly_out *out;
595 char *str;
596 char base_xpath[MGMTD_MAX_XPATH_LEN] = {0};
597
598 if (!ds_ctx) {
599 vty_out(vty, " >>>>> Datastore Not Initialized!\n");
600 return;
601 }
602
603 if (xpath) {
604 strlcpy(base_xpath, xpath, MGMTD_MAX_XPATH_LEN);
605 mgmt_remove_trailing_separator(base_xpath, '/');
606 }
607
608 if (f)
609 ly_out_new_file(f, &out);
610 else
611 ly_out_new_memory(&str, 0, &out);
612
613 mgmt_ds_dump_in_memory(ds_ctx, base_xpath, format, out);
614
615 if (!f)
616 vty_out(vty, "%s\n", str);
617
618 ly_out_free(out, NULL, 0);
619 }
620
621 void mgmt_ds_status_write_one(struct vty *vty, struct mgmt_ds_ctx *ds_ctx)
622 {
623 if (!ds_ctx) {
624 vty_out(vty, " >>>>> Datastore Not Initialized!\n");
625 return;
626 }
627
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");
632 }
633
634 void mgmt_ds_status_write(struct vty *vty)
635 {
636 vty_out(vty, "MGMTD Datastores\n");
637
638 mgmt_ds_status_write_one(vty, mgmt_ds_mm->running_ds);
639
640 mgmt_ds_status_write_one(vty, mgmt_ds_mm->candidate_ds);
641
642 mgmt_ds_status_write_one(vty, mgmt_ds_mm->oper_ds);
643 }