]> git.proxmox.com Git - mirror_frr.git/blame - lib/yang.c
lib: introduce new northbound API
[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
35/* Generate the yang_modules tree. */
36static inline int yang_module_compare(const struct yang_module *a,
37 const struct yang_module *b)
38{
39 return strcmp(a->name, b->name);
40}
41RB_GENERATE(yang_modules, yang_module, entry, yang_module_compare)
42
43struct yang_modules yang_modules = RB_INITIALIZER(&yang_modules);
44
45struct yang_module *yang_module_load(const char *module_name)
46{
47 struct yang_module *module;
48 const struct lys_module *module_info;
49
50 module_info = ly_ctx_load_module(ly_native_ctx, module_name, NULL);
51 if (!module_info) {
52 flog_err(EC_LIB_YANG_MODULE_LOAD,
53 "%s: failed to load data model: %s", __func__,
54 module_name);
55 exit(1);
56 }
57
58 module = XCALLOC(MTYPE_YANG_MODULE, sizeof(*module));
59 module->name = module_name;
60 module->info = module_info;
61
62 if (RB_INSERT(yang_modules, &yang_modules, module) != NULL) {
63 flog_err(EC_LIB_YANG_MODULE_LOADED_ALREADY,
64 "%s: YANG module is loaded already: %s", __func__,
65 module_name);
66 exit(1);
67 }
68
69 return module;
70}
71
72struct yang_module *yang_module_find(const char *module_name)
73{
74 struct yang_module s;
75
76 s.name = module_name;
77 return RB_FIND(yang_modules, &yang_modules, &s);
78}
79
80/*
81 * Helper function for yang_module_snodes_iterate() and
82 * yang_all_snodes_iterate(). This is a recursive function.
83 */
84static void yang_snodes_iterate(const struct lys_node *snode,
85 void (*func)(const struct lys_node *, void *,
86 void *),
87 uint16_t flags, void *arg1, void *arg2)
88{
89 struct lys_node *child;
90
91 if (CHECK_FLAG(flags, YANG_ITER_FILTER_IMPLICIT)) {
92 switch (snode->nodetype) {
93 case LYS_CASE:
94 case LYS_INPUT:
95 case LYS_OUTPUT:
96 if (snode->flags & LYS_IMPLICIT)
97 goto next;
98 break;
99 default:
100 break;
101 }
102 }
103
104 switch (snode->nodetype) {
105 case LYS_CONTAINER:
106 if (CHECK_FLAG(flags, YANG_ITER_FILTER_NPCONTAINERS)) {
107 struct lys_node_container *scontainer;
108
109 scontainer = (struct lys_node_container *)snode;
110 if (!scontainer->presence)
111 goto next;
112 }
113 break;
114 case LYS_LEAF:
115 if (CHECK_FLAG(flags, YANG_ITER_FILTER_LIST_KEYS)) {
116 struct lys_node_leaf *sleaf;
117
118 /* Ignore list keys. */
119 sleaf = (struct lys_node_leaf *)snode;
120 if (lys_is_key(sleaf, NULL))
121 goto next;
122 }
123 break;
124 case LYS_GROUPING:
125 /* Return since we're not interested in the grouping subtree. */
126 return;
127 case LYS_USES:
128 case LYS_AUGMENT:
129 /* Always ignore nodes of these types. */
130 goto next;
131 case LYS_INPUT:
132 case LYS_OUTPUT:
133 if (CHECK_FLAG(flags, YANG_ITER_FILTER_INPUT_OUTPUT))
134 goto next;
135 break;
136 default:
137 break;
138 }
139
140 (*func)(snode, arg1, arg2);
141
142next:
143 /*
144 * YANG leafs and leaf-lists can't have child nodes, and trying to
145 * access snode->child is undefined behavior.
146 */
147 if (snode->nodetype & (LYS_LEAF | LYS_LEAFLIST))
148 return;
149
150 LY_TREE_FOR (snode->child, child) {
151 if (child->parent != snode)
152 continue;
153 yang_snodes_iterate(child, func, flags, arg1, arg2);
154 }
155}
156
157void yang_module_snodes_iterate(const struct lys_module *module,
158 void (*func)(const struct lys_node *, void *,
159 void *),
160 uint16_t flags, void *arg1, void *arg2)
161{
162 struct lys_node *snode;
163
164 LY_TREE_FOR (module->data, snode) {
165 yang_snodes_iterate(snode, func, flags, arg1, arg2);
166 }
167
168 for (uint8_t i = 0; i < module->augment_size; i++) {
169 yang_snodes_iterate(
170 (const struct lys_node *)&module->augment[i], func,
171 flags, arg1, arg2);
172 }
173}
174
175void yang_all_snodes_iterate(void (*func)(const struct lys_node *, void *,
176 void *),
177 uint16_t flags, void *arg1, void *arg2)
178{
179 struct yang_module *module;
180
181 RB_FOREACH (module, yang_modules, &yang_modules)
182 yang_module_snodes_iterate(module->info, func, flags, arg1,
183 arg2);
184}
185
186void yang_snode_get_path(const struct lys_node *snode, enum yang_path_type type,
187 char *xpath, size_t xpath_len)
188{
189 char *xpath_ptr;
190
191 switch (type) {
192 case YANG_PATH_SCHEMA:
193 xpath_ptr = lys_path(snode, 0);
194 break;
195 case YANG_PATH_DATA:
196 xpath_ptr = lys_data_path(snode);
197 break;
198 default:
199 flog_err(EC_LIB_DEVELOPMENT, "%s: unknown yang path type: %u",
200 __func__, type);
201 exit(1);
202 }
203 strlcpy(xpath, xpath_ptr, xpath_len);
204 free(xpath_ptr);
205}
206
207struct lys_node *yang_snode_real_parent(const struct lys_node *snode)
208{
209 struct lys_node *parent = snode->parent;
210
211 while (parent) {
212 struct lys_node_container *scontainer;
213
214 switch (parent->nodetype) {
215 case LYS_CONTAINER:
216 scontainer = (struct lys_node_container *)parent;
217 if (scontainer->presence)
218 return parent;
219 break;
220 case LYS_LIST:
221 return parent;
222 default:
223 break;
224 }
225 parent = parent->parent;
226 }
227
228 return NULL;
229}
230
231struct lys_node *yang_snode_parent_list(const struct lys_node *snode)
232{
233 struct lys_node *parent = snode->parent;
234
235 while (parent) {
236 switch (parent->nodetype) {
237 case LYS_LIST:
238 return parent;
239 default:
240 break;
241 }
242 parent = parent->parent;
243 }
244
245 return NULL;
246}
247
248bool yang_snode_is_typeless_data(const struct lys_node *snode)
249{
250 struct lys_node_leaf *sleaf;
251
252 switch (snode->nodetype) {
253 case LYS_LEAF:
254 sleaf = (struct lys_node_leaf *)snode;
255 if (sleaf->type.base == LY_TYPE_EMPTY)
256 return true;
257 return false;
258 case LYS_LEAFLIST:
259 return false;
260 default:
261 return true;
262 }
263}
264
265const char *yang_snode_get_default(const struct lys_node *snode)
266{
267 struct lys_node_leaf *sleaf;
268
269 switch (snode->nodetype) {
270 case LYS_LEAF:
271 sleaf = (struct lys_node_leaf *)snode;
272
273 /* NOTE: this might be null. */
274 return sleaf->dflt;
275 case LYS_LEAFLIST:
276 /* TODO: check leaf-list default values */
277 return NULL;
278 default:
279 return NULL;
280 }
281}
282
283const struct lys_type *yang_snode_get_type(const struct lys_node *snode)
284{
285 struct lys_node_leaf *sleaf = (struct lys_node_leaf *)snode;
286 struct lys_type *type;
287
288 if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST)))
289 return NULL;
290
291 type = &sleaf->type;
292 while (type->base == LY_TYPE_LEAFREF)
293 type = &type->info.lref.target->type;
294
295 return type;
296}
297
298void yang_dnode_get_path(const struct lyd_node *dnode, char *xpath,
299 size_t xpath_len)
300{
301 char *xpath_ptr;
302
303 xpath_ptr = lyd_path(dnode);
304 strlcpy(xpath, xpath_ptr, xpath_len);
305 free(xpath_ptr);
306}
307
308struct lyd_node *yang_dnode_get(const struct lyd_node *dnode,
309 const char *xpath_fmt, ...)
310{
311 va_list ap;
312 char xpath[XPATH_MAXLEN];
313 struct ly_set *set;
314 struct lyd_node *dnode_ret = NULL;
315
316 va_start(ap, xpath_fmt);
317 vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
318 va_end(ap);
319
320 set = lyd_find_path(dnode, xpath);
321 assert(set);
322 if (set->number == 0)
323 goto exit;
324
325 if (set->number > 1) {
326 flog_warn(EC_LIB_YANG_DNODE_NOT_FOUND,
327 "%s: found %u elements (expected 0 or 1) [xpath %s]",
328 __func__, set->number, xpath);
329 goto exit;
330 }
331
332 dnode_ret = set->set.d[0];
333
334exit:
335 ly_set_free(set);
336
337 return dnode_ret;
338}
339
340bool yang_dnode_exists(const struct lyd_node *dnode, const char *xpath_fmt, ...)
341{
342 va_list ap;
343 char xpath[XPATH_MAXLEN];
344 struct ly_set *set;
345 bool found;
346
347 va_start(ap, xpath_fmt);
348 vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
349 va_end(ap);
350
351 set = lyd_find_path(dnode, xpath);
352 assert(set);
353 found = (set->number > 0);
354 ly_set_free(set);
355
356 return found;
357}
358
359bool yang_dnode_is_default(const struct lyd_node *dnode, const char *xpath_fmt,
360 ...)
361{
362 struct lys_node *snode;
363 struct lys_node_leaf *sleaf;
364 struct lys_node_container *scontainer;
365
366 if (xpath_fmt) {
367 va_list ap;
368 char xpath[XPATH_MAXLEN];
369
370 va_start(ap, xpath_fmt);
371 vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
372 va_end(ap);
373
374 dnode = yang_dnode_get(dnode, xpath);
375 }
376
377 assert(dnode);
378 snode = dnode->schema;
379 switch (snode->nodetype) {
380 case LYS_LEAF:
381 sleaf = (struct lys_node_leaf *)snode;
382 if (sleaf->type.base == LY_TYPE_EMPTY)
383 return false;
384 return lyd_wd_default((struct lyd_node_leaf_list *)dnode);
385 case LYS_LEAFLIST:
386 /* TODO: check leaf-list default values */
387 return false;
388 case LYS_CONTAINER:
389 scontainer = (struct lys_node_container *)snode;
390 if (scontainer->presence)
391 return false;
392 return true;
393 default:
394 return false;
395 }
396}
397
398bool yang_dnode_is_default_recursive(const struct lyd_node *dnode)
399{
400 struct lys_node *snode;
401 struct lyd_node *root, *next, *dnode_iter;
402
403 snode = dnode->schema;
404 if (snode->nodetype & (LYS_LEAF | LYS_LEAFLIST))
405 return yang_dnode_is_default(dnode, NULL);
406
407 if (!yang_dnode_is_default(dnode, NULL))
408 return false;
409
410 LY_TREE_FOR (dnode->child, root) {
411 LY_TREE_DFS_BEGIN (root, next, dnode_iter) {
412 if (!yang_dnode_is_default(dnode_iter, NULL))
413 return false;
414
415 LY_TREE_DFS_END(root, next, dnode_iter);
416 }
417 }
418
419 return true;
420}
421
422void yang_dnode_change_leaf(struct lyd_node *dnode, const char *value)
423{
424 assert(dnode->schema->nodetype == LYS_LEAF);
425 lyd_change_leaf((struct lyd_node_leaf_list *)dnode, value);
426}
427
428void yang_dnode_set_entry(const struct lyd_node *dnode, void *entry)
429{
430 assert(dnode->schema->nodetype & (LYS_LIST | LYS_CONTAINER));
431 lyd_set_private(dnode, entry);
432}
433
434void *yang_dnode_get_entry(const struct lyd_node *dnode)
435{
436 const struct lyd_node *orig_dnode = dnode;
437 char xpath[XPATH_MAXLEN];
438
439 while (dnode) {
440 switch (dnode->schema->nodetype) {
441 case LYS_CONTAINER:
442 case LYS_LIST:
443 if (dnode->priv)
444 return dnode->priv;
445 break;
446 default:
447 break;
448 }
449
450 dnode = dnode->parent;
451 }
452
453 yang_dnode_get_path(orig_dnode, xpath, sizeof(xpath));
454 flog_err(EC_LIB_YANG_DNODE_NOT_FOUND,
455 "%s: failed to find entry [xpath %s]", __func__, xpath);
456 zlog_backtrace(LOG_ERR);
457 abort();
458}
459
460struct lyd_node *yang_dnode_new(struct ly_ctx *ly_ctx)
461{
462 struct lyd_node *dnode;
463
464 dnode = NULL;
465 if (lyd_validate(&dnode, LYD_OPT_CONFIG, ly_ctx) != 0) {
466 /* Should never happen. */
467 flog_err(EC_LIB_LIBYANG, "%s: lyd_validate() failed", __func__);
468 exit(1);
469 }
470
471 return dnode;
472}
473
474struct lyd_node *yang_dnode_dup(const struct lyd_node *dnode)
475{
476 return lyd_dup_withsiblings(dnode, 1);
477}
478
479void yang_dnode_free(struct lyd_node *dnode)
480{
481 lyd_free_withsiblings(dnode);
482}
483
484struct yang_data *yang_data_new(const char *xpath, const char *value)
485{
486 const struct lys_node *snode;
487 struct yang_data *data;
488
489 snode = ly_ctx_get_node(ly_native_ctx, NULL, xpath, 0);
490 if (!snode)
491 snode = ly_ctx_get_node(ly_native_ctx, NULL, xpath, 1);
492 if (!snode) {
493 flog_err(EC_LIB_YANG_UNKNOWN_DATA_PATH,
494 "%s: unknown data path: %s", __func__, xpath);
495 zlog_backtrace(LOG_ERR);
496 abort();
497 }
498
499 data = XCALLOC(MTYPE_YANG_DATA, sizeof(*data));
500 strlcpy(data->xpath, xpath, sizeof(data->xpath));
501 data->snode = snode;
502 if (value)
503 data->value = strdup(value);
504
505 return data;
506}
507
508void yang_data_free(struct yang_data *data)
509{
510 if (data->value)
511 free(data->value);
512 XFREE(MTYPE_YANG_DATA, data);
513}
514
515struct list *yang_data_list_new(void)
516{
517 struct list *list;
518
519 list = list_new();
520 list->del = (void (*)(void *))yang_data_free;
521
522 return list;
523}
524
525static void *ly_dup_cb(const void *priv)
526{
527 /* Make a shallow copy of the priv pointer. */
528 return (void *)priv;
529}
530
531/* Make libyang log its errors using FRR logging infrastructure. */
532static void ly_log_cb(LY_LOG_LEVEL level, const char *msg, const char *path)
533{
534 int priority;
535
536 switch (level) {
537 case LY_LLERR:
538 priority = LOG_ERR;
539 break;
540 case LY_LLWRN:
541 priority = LOG_WARNING;
542 break;
543 case LY_LLVRB:
544 priority = LOG_DEBUG;
545 break;
546 default:
547 return;
548 }
549
550 if (path)
551 zlog(priority, "libyang: %s (%s)", msg, path);
552 else
553 zlog(priority, "libyang: %s", msg);
554}
555
556void yang_init(void)
557{
558 static char ly_plugin_dir[PATH_MAX];
559 const char *const *ly_loaded_plugins;
560 const char *ly_plugin;
561 bool found_ly_frr_types = false;
562
563 /* Tell libyang where to find its plugins. */
564 snprintf(ly_plugin_dir, sizeof(ly_plugin_dir), "%s=%s",
565 "LIBYANG_USER_TYPES_PLUGINS_DIR", LIBYANG_PLUGINS_PATH);
566 putenv(ly_plugin_dir);
567
568 /* Initialize libyang global parameters that affect all containers. */
569 ly_set_log_clb(ly_log_cb, 1);
570 ly_log_options(LY_LOLOG | LY_LOSTORE);
571
572 /* Initialize libyang container for native models. */
573 ly_native_ctx = ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIR_CWD);
574 if (!ly_native_ctx) {
575 flog_err(EC_LIB_LIBYANG, "%s: ly_ctx_new() failed", __func__);
576 exit(1);
577 }
578 ly_ctx_set_searchdir(ly_native_ctx, YANG_MODELS_PATH);
579 ly_ctx_set_priv_dup_clb(ly_native_ctx, ly_dup_cb);
580
581 /* Detect if the required libyang plugin(s) were loaded successfully. */
582 ly_loaded_plugins = ly_get_loaded_plugins();
583 for (size_t i = 0; (ly_plugin = ly_loaded_plugins[i]); i++) {
584 if (strmatch(ly_plugin, "frr_user_types")) {
585 found_ly_frr_types = true;
586 break;
587 }
588 }
589 if (!found_ly_frr_types) {
590 flog_err(EC_LIB_LIBYANG_PLUGIN_LOAD,
591 "%s: failed to load frr_user_types.so", __func__);
592 exit(1);
593 }
594
595 yang_translator_init();
596}
597
598void yang_terminate(void)
599{
600 struct yang_module *module;
601
602 yang_translator_terminate();
603
604 while (!RB_EMPTY(yang_modules, &yang_modules)) {
605 module = RB_ROOT(yang_modules, &yang_modules);
606
607 /*
608 * We shouldn't call ly_ctx_remove_module() here because this
609 * function also removes other modules that depend on it.
610 *
611 * ly_ctx_destroy() will release all memory for us.
612 */
613 RB_REMOVE(yang_modules, &yang_modules, module);
614 XFREE(MTYPE_YANG_MODULE, module);
615 }
616
617 ly_ctx_destroy(ly_native_ctx, NULL);
618}