]> git.proxmox.com Git - mirror_frr.git/blame - lib/yang.c
lib: add frr-isisd to the native models
[mirror_frr.git] / lib / yang.c
CommitLineData
1c2facd1
RW
1/*
2 * Copyright (C) 2018 NetDEF, Inc.
3 * Renato Westphal
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#include <zebra.h>
21
22#include "log.h"
23#include "log_int.h"
24#include "lib_errors.h"
25#include "yang.h"
26#include "yang_translator.h"
27#include "northbound.h"
28
29DEFINE_MTYPE(LIB, YANG_MODULE, "YANG module")
30DEFINE_MTYPE(LIB, YANG_DATA, "YANG data structure")
31
32/* libyang container. */
33struct ly_ctx *ly_native_ctx;
34
3a11599c
DL
35static struct yang_module_embed *embeds, **embedupd = &embeds;
36
37void yang_module_embed(struct yang_module_embed *embed)
38{
39 embed->next = NULL;
40 *embedupd = embed;
41 embedupd = &embed->next;
42}
43
44static const char *yang_module_imp_clb(const char *mod_name,
45 const char *mod_rev,
46 const char *submod_name,
47 const char *submod_rev,
48 void *user_data,
49 LYS_INFORMAT *format,
50 void (**free_module_data)
51 (void *, void*))
52{
53 struct yang_module_embed *e;
54
55 if (submod_name || submod_rev)
56 return NULL;
57
58 for (e = embeds; e; e = e->next) {
59 if (strcmp(e->mod_name, mod_name))
60 continue;
61 if (mod_rev && strcmp(e->mod_rev, mod_rev))
62 continue;
63
64 *format = e->format;
65 return e->data;
66 }
67
68 flog_warn(EC_LIB_YANG_MODULE_LOAD,
69 "YANG model \"%s@%s\" not embedded, trying external file",
70 mod_name, mod_rev ? mod_rev : "*");
71 return NULL;
72}
73
96f2c009 74static const char *const frr_native_modules[] = {
a1b5f469
RW
75 "frr-interface",
76 "frr-ripd",
e9ce224b 77 "frr-ripngd",
96f2c009 78 "frr-isisd",
a1b5f469
RW
79};
80
1c2facd1
RW
81/* Generate the yang_modules tree. */
82static inline int yang_module_compare(const struct yang_module *a,
83 const struct yang_module *b)
84{
85 return strcmp(a->name, b->name);
86}
87RB_GENERATE(yang_modules, yang_module, entry, yang_module_compare)
88
89struct yang_modules yang_modules = RB_INITIALIZER(&yang_modules);
90
91struct yang_module *yang_module_load(const char *module_name)
92{
93 struct yang_module *module;
94 const struct lys_module *module_info;
95
96 module_info = ly_ctx_load_module(ly_native_ctx, module_name, NULL);
97 if (!module_info) {
98 flog_err(EC_LIB_YANG_MODULE_LOAD,
99 "%s: failed to load data model: %s", __func__,
100 module_name);
101 exit(1);
102 }
103
104 module = XCALLOC(MTYPE_YANG_MODULE, sizeof(*module));
105 module->name = module_name;
106 module->info = module_info;
107
108 if (RB_INSERT(yang_modules, &yang_modules, module) != NULL) {
109 flog_err(EC_LIB_YANG_MODULE_LOADED_ALREADY,
110 "%s: YANG module is loaded already: %s", __func__,
111 module_name);
112 exit(1);
113 }
114
115 return module;
116}
117
a1b5f469
RW
118void yang_module_load_all(void)
119{
120 for (size_t i = 0; i < array_size(frr_native_modules); i++)
121 yang_module_load(frr_native_modules[i]);
122}
123
1c2facd1
RW
124struct yang_module *yang_module_find(const char *module_name)
125{
126 struct yang_module s;
127
128 s.name = module_name;
129 return RB_FIND(yang_modules, &yang_modules, &s);
130}
131
e0ccfad2
RW
132int yang_snodes_iterate_subtree(const struct lys_node *snode,
133 yang_iterate_cb cb, uint16_t flags, void *arg)
1c2facd1
RW
134{
135 struct lys_node *child;
e0ccfad2 136 int ret = YANG_ITER_CONTINUE;
1c2facd1
RW
137
138 if (CHECK_FLAG(flags, YANG_ITER_FILTER_IMPLICIT)) {
139 switch (snode->nodetype) {
140 case LYS_CASE:
141 case LYS_INPUT:
142 case LYS_OUTPUT:
db452508 143 if (CHECK_FLAG(snode->flags, LYS_IMPLICIT))
1c2facd1
RW
144 goto next;
145 break;
146 default:
147 break;
148 }
149 }
150
151 switch (snode->nodetype) {
152 case LYS_CONTAINER:
153 if (CHECK_FLAG(flags, YANG_ITER_FILTER_NPCONTAINERS)) {
154 struct lys_node_container *scontainer;
155
156 scontainer = (struct lys_node_container *)snode;
157 if (!scontainer->presence)
158 goto next;
159 }
160 break;
161 case LYS_LEAF:
162 if (CHECK_FLAG(flags, YANG_ITER_FILTER_LIST_KEYS)) {
163 struct lys_node_leaf *sleaf;
164
165 /* Ignore list keys. */
166 sleaf = (struct lys_node_leaf *)snode;
167 if (lys_is_key(sleaf, NULL))
168 goto next;
169 }
170 break;
171 case LYS_GROUPING:
172 /* Return since we're not interested in the grouping subtree. */
e0ccfad2 173 return YANG_ITER_CONTINUE;
1c2facd1
RW
174 case LYS_USES:
175 case LYS_AUGMENT:
176 /* Always ignore nodes of these types. */
177 goto next;
178 case LYS_INPUT:
179 case LYS_OUTPUT:
180 if (CHECK_FLAG(flags, YANG_ITER_FILTER_INPUT_OUTPUT))
181 goto next;
182 break;
183 default:
184 break;
185 }
186
e0ccfad2
RW
187 ret = (*cb)(snode, arg);
188 if (ret == YANG_ITER_STOP)
189 return ret;
1c2facd1
RW
190
191next:
192 /*
193 * YANG leafs and leaf-lists can't have child nodes, and trying to
194 * access snode->child is undefined behavior.
195 */
db452508 196 if (CHECK_FLAG(snode->nodetype, LYS_LEAF | LYS_LEAFLIST))
e0ccfad2 197 return YANG_ITER_CONTINUE;
1c2facd1
RW
198
199 LY_TREE_FOR (snode->child, child) {
544ca69a
RW
200 if (!CHECK_FLAG(flags, YANG_ITER_ALLOW_AUGMENTATIONS)
201 && child->parent != snode)
1c2facd1 202 continue;
e0ccfad2
RW
203
204 ret = yang_snodes_iterate_subtree(child, cb, flags, arg);
205 if (ret == YANG_ITER_STOP)
206 return ret;
1c2facd1 207 }
e0ccfad2
RW
208
209 return ret;
1c2facd1
RW
210}
211
e0ccfad2
RW
212int yang_snodes_iterate_module(const struct lys_module *module,
213 yang_iterate_cb cb, uint16_t flags, void *arg)
1c2facd1
RW
214{
215 struct lys_node *snode;
e0ccfad2 216 int ret = YANG_ITER_CONTINUE;
1c2facd1
RW
217
218 LY_TREE_FOR (module->data, snode) {
e0ccfad2
RW
219 ret = yang_snodes_iterate_subtree(snode, cb, flags, arg);
220 if (ret == YANG_ITER_STOP)
221 return ret;
1c2facd1
RW
222 }
223
224 for (uint8_t i = 0; i < module->augment_size; i++) {
e0ccfad2
RW
225 ret = yang_snodes_iterate_subtree(
226 (const struct lys_node *)&module->augment[i], cb, flags,
227 arg);
228 if (ret == YANG_ITER_STOP)
229 return ret;
1c2facd1 230 }
e0ccfad2
RW
231
232 return ret;
1c2facd1
RW
233}
234
e0ccfad2 235int yang_snodes_iterate_all(yang_iterate_cb cb, uint16_t flags, void *arg)
1c2facd1
RW
236{
237 struct yang_module *module;
e0ccfad2
RW
238 int ret = YANG_ITER_CONTINUE;
239
240 RB_FOREACH (module, yang_modules, &yang_modules) {
241 ret = yang_snodes_iterate_module(module->info, cb, flags, arg);
242 if (ret == YANG_ITER_STOP)
243 return ret;
244 }
1c2facd1 245
e0ccfad2 246 return ret;
1c2facd1
RW
247}
248
249void yang_snode_get_path(const struct lys_node *snode, enum yang_path_type type,
250 char *xpath, size_t xpath_len)
251{
252 char *xpath_ptr;
253
254 switch (type) {
255 case YANG_PATH_SCHEMA:
256 xpath_ptr = lys_path(snode, 0);
257 break;
258 case YANG_PATH_DATA:
259 xpath_ptr = lys_data_path(snode);
260 break;
261 default:
262 flog_err(EC_LIB_DEVELOPMENT, "%s: unknown yang path type: %u",
263 __func__, type);
264 exit(1);
265 }
266 strlcpy(xpath, xpath_ptr, xpath_len);
267 free(xpath_ptr);
268}
269
270struct lys_node *yang_snode_real_parent(const struct lys_node *snode)
271{
272 struct lys_node *parent = snode->parent;
273
274 while (parent) {
275 struct lys_node_container *scontainer;
276
277 switch (parent->nodetype) {
278 case LYS_CONTAINER:
279 scontainer = (struct lys_node_container *)parent;
280 if (scontainer->presence)
281 return parent;
282 break;
283 case LYS_LIST:
284 return parent;
285 default:
286 break;
287 }
288 parent = parent->parent;
289 }
290
291 return NULL;
292}
293
294struct lys_node *yang_snode_parent_list(const struct lys_node *snode)
295{
296 struct lys_node *parent = snode->parent;
297
298 while (parent) {
299 switch (parent->nodetype) {
300 case LYS_LIST:
301 return parent;
302 default:
303 break;
304 }
305 parent = parent->parent;
306 }
307
308 return NULL;
309}
310
311bool yang_snode_is_typeless_data(const struct lys_node *snode)
312{
313 struct lys_node_leaf *sleaf;
314
315 switch (snode->nodetype) {
316 case LYS_LEAF:
317 sleaf = (struct lys_node_leaf *)snode;
318 if (sleaf->type.base == LY_TYPE_EMPTY)
319 return true;
320 return false;
321 case LYS_LEAFLIST:
322 return false;
323 default:
324 return true;
325 }
326}
327
328const char *yang_snode_get_default(const struct lys_node *snode)
329{
330 struct lys_node_leaf *sleaf;
331
332 switch (snode->nodetype) {
333 case LYS_LEAF:
334 sleaf = (struct lys_node_leaf *)snode;
335
336 /* NOTE: this might be null. */
337 return sleaf->dflt;
338 case LYS_LEAFLIST:
339 /* TODO: check leaf-list default values */
340 return NULL;
341 default:
342 return NULL;
343 }
344}
345
346const struct lys_type *yang_snode_get_type(const struct lys_node *snode)
347{
348 struct lys_node_leaf *sleaf = (struct lys_node_leaf *)snode;
349 struct lys_type *type;
350
db452508 351 if (!CHECK_FLAG(sleaf->nodetype, LYS_LEAF | LYS_LEAFLIST))
1c2facd1
RW
352 return NULL;
353
354 type = &sleaf->type;
355 while (type->base == LY_TYPE_LEAFREF)
356 type = &type->info.lref.target->type;
357
358 return type;
359}
360
361void yang_dnode_get_path(const struct lyd_node *dnode, char *xpath,
362 size_t xpath_len)
363{
364 char *xpath_ptr;
365
366 xpath_ptr = lyd_path(dnode);
367 strlcpy(xpath, xpath_ptr, xpath_len);
368 free(xpath_ptr);
369}
370
3f662078
RW
371const char *yang_dnode_get_schema_name(const struct lyd_node *dnode,
372 const char *xpath_fmt, ...)
373{
374 if (xpath_fmt) {
375 va_list ap;
376 char xpath[XPATH_MAXLEN];
377
378 va_start(ap, xpath_fmt);
379 vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
380 va_end(ap);
381
382 dnode = yang_dnode_get(dnode, xpath);
383 if (!dnode) {
384 flog_err(EC_LIB_YANG_DNODE_NOT_FOUND,
385 "%s: couldn't find %s", __func__, xpath);
386 zlog_backtrace(LOG_ERR);
387 abort();
388 }
389 }
390
391 return dnode->schema->name;
392}
393
1c2facd1
RW
394struct lyd_node *yang_dnode_get(const struct lyd_node *dnode,
395 const char *xpath_fmt, ...)
396{
397 va_list ap;
398 char xpath[XPATH_MAXLEN];
399 struct ly_set *set;
400 struct lyd_node *dnode_ret = NULL;
401
402 va_start(ap, xpath_fmt);
403 vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
404 va_end(ap);
405
406 set = lyd_find_path(dnode, xpath);
407 assert(set);
408 if (set->number == 0)
409 goto exit;
410
411 if (set->number > 1) {
412 flog_warn(EC_LIB_YANG_DNODE_NOT_FOUND,
413 "%s: found %u elements (expected 0 or 1) [xpath %s]",
414 __func__, set->number, xpath);
415 goto exit;
416 }
417
418 dnode_ret = set->set.d[0];
419
420exit:
421 ly_set_free(set);
422
423 return dnode_ret;
424}
425
426bool yang_dnode_exists(const struct lyd_node *dnode, const char *xpath_fmt, ...)
427{
428 va_list ap;
429 char xpath[XPATH_MAXLEN];
430 struct ly_set *set;
431 bool found;
432
433 va_start(ap, xpath_fmt);
434 vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
435 va_end(ap);
436
437 set = lyd_find_path(dnode, xpath);
438 assert(set);
439 found = (set->number > 0);
440 ly_set_free(set);
441
442 return found;
443}
444
445bool yang_dnode_is_default(const struct lyd_node *dnode, const char *xpath_fmt,
446 ...)
447{
448 struct lys_node *snode;
449 struct lys_node_leaf *sleaf;
450 struct lys_node_container *scontainer;
451
452 if (xpath_fmt) {
453 va_list ap;
454 char xpath[XPATH_MAXLEN];
455
456 va_start(ap, xpath_fmt);
457 vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
458 va_end(ap);
459
460 dnode = yang_dnode_get(dnode, xpath);
461 }
462
463 assert(dnode);
464 snode = dnode->schema;
465 switch (snode->nodetype) {
466 case LYS_LEAF:
467 sleaf = (struct lys_node_leaf *)snode;
468 if (sleaf->type.base == LY_TYPE_EMPTY)
469 return false;
470 return lyd_wd_default((struct lyd_node_leaf_list *)dnode);
471 case LYS_LEAFLIST:
472 /* TODO: check leaf-list default values */
473 return false;
474 case LYS_CONTAINER:
475 scontainer = (struct lys_node_container *)snode;
476 if (scontainer->presence)
477 return false;
478 return true;
479 default:
480 return false;
481 }
482}
483
484bool yang_dnode_is_default_recursive(const struct lyd_node *dnode)
485{
486 struct lys_node *snode;
487 struct lyd_node *root, *next, *dnode_iter;
488
489 snode = dnode->schema;
db452508 490 if (CHECK_FLAG(snode->nodetype, LYS_LEAF | LYS_LEAFLIST))
1c2facd1
RW
491 return yang_dnode_is_default(dnode, NULL);
492
493 if (!yang_dnode_is_default(dnode, NULL))
494 return false;
495
496 LY_TREE_FOR (dnode->child, root) {
497 LY_TREE_DFS_BEGIN (root, next, dnode_iter) {
498 if (!yang_dnode_is_default(dnode_iter, NULL))
499 return false;
500
501 LY_TREE_DFS_END(root, next, dnode_iter);
502 }
503 }
504
505 return true;
506}
507
508void yang_dnode_change_leaf(struct lyd_node *dnode, const char *value)
509{
510 assert(dnode->schema->nodetype == LYS_LEAF);
511 lyd_change_leaf((struct lyd_node_leaf_list *)dnode, value);
512}
513
514void yang_dnode_set_entry(const struct lyd_node *dnode, void *entry)
515{
db452508 516 assert(CHECK_FLAG(dnode->schema->nodetype, LYS_LIST | LYS_CONTAINER));
1c2facd1
RW
517 lyd_set_private(dnode, entry);
518}
519
25c780a3
RW
520void *yang_dnode_get_entry(const struct lyd_node *dnode,
521 bool abort_if_not_found)
1c2facd1
RW
522{
523 const struct lyd_node *orig_dnode = dnode;
524 char xpath[XPATH_MAXLEN];
525
526 while (dnode) {
527 switch (dnode->schema->nodetype) {
528 case LYS_CONTAINER:
529 case LYS_LIST:
530 if (dnode->priv)
531 return dnode->priv;
532 break;
533 default:
534 break;
535 }
536
537 dnode = dnode->parent;
538 }
539
25c780a3
RW
540 if (!abort_if_not_found)
541 return NULL;
542
1c2facd1
RW
543 yang_dnode_get_path(orig_dnode, xpath, sizeof(xpath));
544 flog_err(EC_LIB_YANG_DNODE_NOT_FOUND,
545 "%s: failed to find entry [xpath %s]", __func__, xpath);
546 zlog_backtrace(LOG_ERR);
547 abort();
548}
549
5e02643a 550struct lyd_node *yang_dnode_new(struct ly_ctx *ly_ctx, bool config_only)
1c2facd1
RW
551{
552 struct lyd_node *dnode;
5e02643a
RW
553 int options;
554
555 if (config_only)
556 options = LYD_OPT_CONFIG;
557 else
558 options = LYD_OPT_DATA | LYD_OPT_DATA_NO_YANGLIB;
1c2facd1
RW
559
560 dnode = NULL;
5e02643a 561 if (lyd_validate(&dnode, options, ly_ctx) != 0) {
1c2facd1
RW
562 /* Should never happen. */
563 flog_err(EC_LIB_LIBYANG, "%s: lyd_validate() failed", __func__);
564 exit(1);
565 }
566
567 return dnode;
568}
569
570struct lyd_node *yang_dnode_dup(const struct lyd_node *dnode)
571{
572 return lyd_dup_withsiblings(dnode, 1);
573}
574
575void yang_dnode_free(struct lyd_node *dnode)
576{
e5dc8a44
RW
577 while (dnode->parent)
578 dnode = dnode->parent;
1c2facd1
RW
579 lyd_free_withsiblings(dnode);
580}
581
582struct yang_data *yang_data_new(const char *xpath, const char *value)
583{
1c2facd1
RW
584 struct yang_data *data;
585
1c2facd1
RW
586 data = XCALLOC(MTYPE_YANG_DATA, sizeof(*data));
587 strlcpy(data->xpath, xpath, sizeof(data->xpath));
1c2facd1
RW
588 if (value)
589 data->value = strdup(value);
590
591 return data;
592}
593
594void yang_data_free(struct yang_data *data)
595{
596 if (data->value)
597 free(data->value);
598 XFREE(MTYPE_YANG_DATA, data);
599}
600
601struct list *yang_data_list_new(void)
602{
603 struct list *list;
604
605 list = list_new();
606 list->del = (void (*)(void *))yang_data_free;
607
608 return list;
609}
610
611static void *ly_dup_cb(const void *priv)
612{
613 /* Make a shallow copy of the priv pointer. */
614 return (void *)priv;
615}
616
617/* Make libyang log its errors using FRR logging infrastructure. */
618static void ly_log_cb(LY_LOG_LEVEL level, const char *msg, const char *path)
619{
620 int priority;
621
622 switch (level) {
623 case LY_LLERR:
624 priority = LOG_ERR;
625 break;
626 case LY_LLWRN:
627 priority = LOG_WARNING;
628 break;
629 case LY_LLVRB:
630 priority = LOG_DEBUG;
631 break;
632 default:
633 return;
634 }
635
636 if (path)
637 zlog(priority, "libyang: %s (%s)", msg, path);
638 else
639 zlog(priority, "libyang: %s", msg);
640}
641
642void yang_init(void)
643{
644 static char ly_plugin_dir[PATH_MAX];
645 const char *const *ly_loaded_plugins;
646 const char *ly_plugin;
647 bool found_ly_frr_types = false;
648
649 /* Tell libyang where to find its plugins. */
650 snprintf(ly_plugin_dir, sizeof(ly_plugin_dir), "%s=%s",
651 "LIBYANG_USER_TYPES_PLUGINS_DIR", LIBYANG_PLUGINS_PATH);
652 putenv(ly_plugin_dir);
653
654 /* Initialize libyang global parameters that affect all containers. */
655 ly_set_log_clb(ly_log_cb, 1);
656 ly_log_options(LY_LOLOG | LY_LOSTORE);
657
658 /* Initialize libyang container for native models. */
f3e5b71c
RW
659 ly_native_ctx =
660 ly_ctx_new(YANG_MODELS_PATH, LY_CTX_DISABLE_SEARCHDIR_CWD);
1c2facd1
RW
661 if (!ly_native_ctx) {
662 flog_err(EC_LIB_LIBYANG, "%s: ly_ctx_new() failed", __func__);
663 exit(1);
664 }
3a11599c 665 ly_ctx_set_module_imp_clb(ly_native_ctx, yang_module_imp_clb, NULL);
1c2facd1
RW
666 ly_ctx_set_priv_dup_clb(ly_native_ctx, ly_dup_cb);
667
668 /* Detect if the required libyang plugin(s) were loaded successfully. */
669 ly_loaded_plugins = ly_get_loaded_plugins();
670 for (size_t i = 0; (ly_plugin = ly_loaded_plugins[i]); i++) {
671 if (strmatch(ly_plugin, "frr_user_types")) {
672 found_ly_frr_types = true;
673 break;
674 }
675 }
676 if (!found_ly_frr_types) {
677 flog_err(EC_LIB_LIBYANG_PLUGIN_LOAD,
678 "%s: failed to load frr_user_types.so", __func__);
679 exit(1);
680 }
681
682 yang_translator_init();
683}
684
685void yang_terminate(void)
686{
687 struct yang_module *module;
688
689 yang_translator_terminate();
690
691 while (!RB_EMPTY(yang_modules, &yang_modules)) {
692 module = RB_ROOT(yang_modules, &yang_modules);
693
694 /*
695 * We shouldn't call ly_ctx_remove_module() here because this
696 * function also removes other modules that depend on it.
697 *
698 * ly_ctx_destroy() will release all memory for us.
699 */
700 RB_REMOVE(yang_modules, &yang_modules, module);
701 XFREE(MTYPE_YANG_MODULE, module);
702 }
703
704 ly_ctx_destroy(ly_native_ctx, NULL);
705}