]> git.proxmox.com Git - mirror_frr.git/blob - lib/yang_translator.c
doc: Add `show ipv6 rpf X:X::X:X` command to docs
[mirror_frr.git] / lib / yang_translator.c
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 "lib_errors.h"
24 #include "hash.h"
25 #include "yang.h"
26 #include "yang_translator.h"
27 #include "frrstr.h"
28
29 DEFINE_MTYPE_STATIC(LIB, YANG_TRANSLATOR, "YANG Translator");
30 DEFINE_MTYPE_STATIC(LIB, YANG_TRANSLATOR_MODULE, "YANG Translator Module");
31 DEFINE_MTYPE_STATIC(LIB, YANG_TRANSLATOR_MAPPING, "YANG Translator Mapping");
32
33 /* Generate the yang_translators tree. */
34 static inline int yang_translator_compare(const struct yang_translator *a,
35 const struct yang_translator *b)
36 {
37 return strcmp(a->family, b->family);
38 }
39 RB_GENERATE(yang_translators, yang_translator, entry, yang_translator_compare)
40
41 struct yang_translators yang_translators = RB_INITIALIZER(&yang_translators);
42
43 /* Separate libyang context for the translator module. */
44 static struct ly_ctx *ly_translator_ctx;
45
46 static unsigned int
47 yang_translator_validate(struct yang_translator *translator);
48 static unsigned int yang_module_nodes_count(const struct lys_module *module);
49
50 struct yang_mapping_node {
51 char xpath_from_canonical[XPATH_MAXLEN];
52 char xpath_from_fmt[XPATH_MAXLEN];
53 char xpath_to_fmt[XPATH_MAXLEN];
54 };
55
56 static bool yang_mapping_hash_cmp(const void *value1, const void *value2)
57 {
58 const struct yang_mapping_node *c1 = value1;
59 const struct yang_mapping_node *c2 = value2;
60
61 return strmatch(c1->xpath_from_canonical, c2->xpath_from_canonical);
62 }
63
64 static unsigned int yang_mapping_hash_key(const void *value)
65 {
66 return string_hash_make(value);
67 }
68
69 static void *yang_mapping_hash_alloc(void *p)
70 {
71 struct yang_mapping_node *new, *key = p;
72
73 new = XCALLOC(MTYPE_YANG_TRANSLATOR_MAPPING, sizeof(*new));
74 strlcpy(new->xpath_from_canonical, key->xpath_from_canonical,
75 sizeof(new->xpath_from_canonical));
76
77 return new;
78 }
79
80 static void yang_mapping_hash_free(void *arg)
81 {
82 XFREE(MTYPE_YANG_TRANSLATOR_MAPPING, arg);
83 }
84
85 static struct yang_mapping_node *
86 yang_mapping_lookup(const struct yang_translator *translator, int dir,
87 const char *xpath)
88 {
89 struct yang_mapping_node s;
90
91 strlcpy(s.xpath_from_canonical, xpath, sizeof(s.xpath_from_canonical));
92 return hash_lookup(translator->mappings[dir], &s);
93 }
94
95 static void yang_mapping_add(struct yang_translator *translator, int dir,
96 const struct lysc_node *snode,
97 const char *xpath_from_fmt,
98 const char *xpath_to_fmt)
99 {
100 struct yang_mapping_node *mapping, s;
101
102 yang_snode_get_path(snode, YANG_PATH_DATA, s.xpath_from_canonical,
103 sizeof(s.xpath_from_canonical));
104 mapping = hash_get(translator->mappings[dir], &s,
105 yang_mapping_hash_alloc);
106 strlcpy(mapping->xpath_from_fmt, xpath_from_fmt,
107 sizeof(mapping->xpath_from_fmt));
108 strlcpy(mapping->xpath_to_fmt, xpath_to_fmt,
109 sizeof(mapping->xpath_to_fmt));
110
111 const char *keys[] = {"KEY1", "KEY2", "KEY3", "KEY4"};
112 char *xpfmt;
113
114 for (unsigned int i = 0; i < array_size(keys); i++) {
115 xpfmt = frrstr_replace(mapping->xpath_from_fmt, keys[i],
116 "%[^']");
117 strlcpy(mapping->xpath_from_fmt, xpfmt,
118 sizeof(mapping->xpath_from_fmt));
119 XFREE(MTYPE_TMP, xpfmt);
120 }
121
122 for (unsigned int i = 0; i < array_size(keys); i++) {
123 xpfmt = frrstr_replace(mapping->xpath_to_fmt, keys[i], "%s");
124 strlcpy(mapping->xpath_to_fmt, xpfmt,
125 sizeof(mapping->xpath_to_fmt));
126 XFREE(MTYPE_TMP, xpfmt);
127 }
128 }
129
130 static void yang_tmodule_delete(struct yang_tmodule *tmodule)
131 {
132 XFREE(MTYPE_YANG_TRANSLATOR_MODULE, tmodule);
133 }
134
135 struct yang_translator *yang_translator_load(const char *path)
136 {
137 struct yang_translator *translator;
138 struct yang_tmodule *tmodule = NULL;
139 const char *family;
140 struct lyd_node *dnode;
141 struct ly_set *set;
142 struct listnode *ln;
143 LY_ERR err;
144
145 /* Load module translator (JSON file). */
146 err = lyd_parse_data_path(ly_translator_ctx, path, LYD_JSON,
147 LYD_PARSE_NO_STATE, LYD_VALIDATE_NO_STATE,
148 &dnode);
149 if (err) {
150 flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD,
151 "%s: lyd_parse_path() failed: %d", __func__, err);
152 return NULL;
153 }
154 dnode = yang_dnode_get(dnode,
155 "/frr-module-translator:frr-module-translator");
156 /*
157 * libyang guarantees the "frr-module-translator" top-level container is
158 * always present since it contains mandatory child nodes.
159 */
160 assert(dnode);
161
162 family = yang_dnode_get_string(dnode, "./family");
163 translator = yang_translator_find(family);
164 if (translator != NULL) {
165 flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD,
166 "%s: module translator \"%s\" is loaded already",
167 __func__, family);
168 yang_dnode_free(dnode);
169 return NULL;
170 }
171
172 translator = XCALLOC(MTYPE_YANG_TRANSLATOR, sizeof(*translator));
173 strlcpy(translator->family, family, sizeof(translator->family));
174 translator->modules = list_new();
175 for (size_t i = 0; i < YANG_TRANSLATE_MAX; i++)
176 translator->mappings[i] = hash_create(yang_mapping_hash_key,
177 yang_mapping_hash_cmp,
178 "YANG translation table");
179 RB_INSERT(yang_translators, &yang_translators, translator);
180
181 /* Initialize the translator libyang context. */
182 translator->ly_ctx = yang_ctx_new_setup(false, false);
183 if (!translator->ly_ctx) {
184 flog_warn(EC_LIB_LIBYANG, "%s: ly_ctx_new() failed", __func__);
185 goto error;
186 }
187
188 /* Load modules */
189 if (lyd_find_xpath(dnode, "./module", &set) != LY_SUCCESS)
190 assert(0); /* XXX libyang2: old ly1 code asserted success */
191
192 for (size_t i = 0; i < set->count; i++) {
193 const char *module_name;
194
195 tmodule =
196 XCALLOC(MTYPE_YANG_TRANSLATOR_MODULE, sizeof(*tmodule));
197
198 module_name = yang_dnode_get_string(set->dnodes[i], "./name");
199 tmodule->module = ly_ctx_load_module(translator->ly_ctx,
200 module_name, NULL, NULL);
201 if (!tmodule->module) {
202 flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD,
203 "%s: failed to load module: %s", __func__,
204 module_name);
205 ly_set_free(set, NULL);
206 goto error;
207 }
208 }
209
210 /* Count nodes in modules. */
211 for (ALL_LIST_ELEMENTS_RO(translator->modules, ln, tmodule)) {
212 tmodule->nodes_before_deviations =
213 yang_module_nodes_count(tmodule->module);
214 }
215
216 /* Load the deviations and count nodes again */
217 for (ALL_LIST_ELEMENTS_RO(translator->modules, ln, tmodule)) {
218 const char *module_name = tmodule->module->name;
219 tmodule->deviations = ly_ctx_load_module(
220 translator->ly_ctx, module_name, NULL, NULL);
221 if (!tmodule->deviations) {
222 flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD,
223 "%s: failed to load module: %s", __func__,
224 module_name);
225 ly_set_free(set, NULL);
226 goto error;
227 }
228
229 tmodule->nodes_after_deviations =
230 yang_module_nodes_count(tmodule->module);
231 }
232 ly_set_free(set, NULL);
233
234 /* Calculate the coverage. */
235 for (ALL_LIST_ELEMENTS_RO(translator->modules, ln, tmodule)) {
236 tmodule->coverage = ((double)tmodule->nodes_after_deviations
237 / (double)tmodule->nodes_before_deviations)
238 * 100;
239 }
240
241 /* Load mappings. */
242 if (lyd_find_xpath(dnode, "./module/mappings", &set) != LY_SUCCESS)
243 assert(0); /* XXX libyang2: old ly1 code asserted success */
244 for (size_t i = 0; i < set->count; i++) {
245 const char *xpath_custom, *xpath_native;
246 const struct lysc_node *snode_custom, *snode_native;
247
248 xpath_custom =
249 yang_dnode_get_string(set->dnodes[i], "./custom");
250
251 snode_custom = lys_find_path(translator->ly_ctx, NULL,
252 xpath_custom, 0);
253 if (!snode_custom) {
254 flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD,
255 "%s: unknown data path: %s", __func__,
256 xpath_custom);
257 ly_set_free(set, NULL);
258 goto error;
259 }
260
261 xpath_native =
262 yang_dnode_get_string(set->dnodes[i], "./native");
263 snode_native =
264 lys_find_path(ly_native_ctx, NULL, xpath_native, 0);
265 if (!snode_native) {
266 flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD,
267 "%s: unknown data path: %s", __func__,
268 xpath_native);
269 ly_set_free(set, NULL);
270 goto error;
271 }
272
273 yang_mapping_add(translator, YANG_TRANSLATE_TO_NATIVE,
274 snode_custom, xpath_custom, xpath_native);
275 yang_mapping_add(translator, YANG_TRANSLATE_FROM_NATIVE,
276 snode_native, xpath_native, xpath_custom);
277 }
278 ly_set_free(set, NULL);
279
280 /* Validate mappings. */
281 if (yang_translator_validate(translator) != 0)
282 goto error;
283
284 yang_dnode_free(dnode);
285
286 return translator;
287
288 error:
289 yang_dnode_free(dnode);
290 yang_translator_unload(translator);
291 yang_tmodule_delete(tmodule);
292
293 return NULL;
294 }
295
296 void yang_translator_unload(struct yang_translator *translator)
297 {
298 for (size_t i = 0; i < YANG_TRANSLATE_MAX; i++)
299 hash_clean(translator->mappings[i], yang_mapping_hash_free);
300 translator->modules->del = (void (*)(void *))yang_tmodule_delete;
301 list_delete(&translator->modules);
302 ly_ctx_destroy(translator->ly_ctx);
303 RB_REMOVE(yang_translators, &yang_translators, translator);
304 XFREE(MTYPE_YANG_TRANSLATOR, translator);
305 }
306
307 struct yang_translator *yang_translator_find(const char *family)
308 {
309 struct yang_translator s;
310
311 strlcpy(s.family, family, sizeof(s.family));
312 return RB_FIND(yang_translators, &yang_translators, &s);
313 }
314
315 enum yang_translate_result
316 yang_translate_xpath(const struct yang_translator *translator, int dir,
317 char *xpath, size_t xpath_len)
318 {
319 struct ly_ctx *ly_ctx;
320 const struct lysc_node *snode;
321 struct yang_mapping_node *mapping;
322 char xpath_canonical[XPATH_MAXLEN];
323 char keys[4][LIST_MAXKEYLEN];
324 int n;
325
326 if (dir == YANG_TRANSLATE_TO_NATIVE)
327 ly_ctx = translator->ly_ctx;
328 else
329 ly_ctx = ly_native_ctx;
330
331 snode = lys_find_path(ly_ctx, NULL, xpath, 0);
332 if (!snode) {
333 flog_warn(EC_LIB_YANG_TRANSLATION_ERROR,
334 "%s: unknown data path: %s", __func__, xpath);
335 return YANG_TRANSLATE_FAILURE;
336 }
337
338 yang_snode_get_path(snode, YANG_PATH_DATA, xpath_canonical,
339 sizeof(xpath_canonical));
340 mapping = yang_mapping_lookup(translator, dir, xpath_canonical);
341 if (!mapping)
342 return YANG_TRANSLATE_NOTFOUND;
343
344 #pragma GCC diagnostic push
345 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
346 /* processing format strings from mapping node... */
347 n = sscanf(xpath, mapping->xpath_from_fmt, keys[0], keys[1], keys[2],
348 keys[3]);
349 #pragma GCC diagnostic pop
350 if (n < 0) {
351 flog_warn(EC_LIB_YANG_TRANSLATION_ERROR,
352 "%s: sscanf() failed: %s", __func__,
353 safe_strerror(errno));
354 return YANG_TRANSLATE_FAILURE;
355 }
356
357 #pragma GCC diagnostic push
358 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
359 /* processing format strings from mapping node... */
360 snprintf(xpath, xpath_len, mapping->xpath_to_fmt, keys[0], keys[1],
361 keys[2], keys[3]);
362 #pragma GCC diagnostic pop
363
364 return YANG_TRANSLATE_SUCCESS;
365 }
366
367 int yang_translate_dnode(const struct yang_translator *translator, int dir,
368 struct lyd_node **dnode)
369 {
370 struct ly_ctx *ly_ctx;
371 struct lyd_node *new;
372 struct lyd_node *root, *dnode_iter;
373
374 /* Create new libyang data node to hold the translated data. */
375 if (dir == YANG_TRANSLATE_TO_NATIVE)
376 ly_ctx = ly_native_ctx;
377 else
378 ly_ctx = translator->ly_ctx;
379 new = yang_dnode_new(ly_ctx, false);
380
381 /* Iterate over all nodes from the data tree. */
382 LY_LIST_FOR (*dnode, root) {
383 LYD_TREE_DFS_BEGIN (root, dnode_iter) {
384 char xpath[XPATH_MAXLEN];
385 enum yang_translate_result ret;
386
387 yang_dnode_get_path(dnode_iter, xpath, sizeof(xpath));
388 ret = yang_translate_xpath(translator, dir, xpath,
389 sizeof(xpath));
390 switch (ret) {
391 case YANG_TRANSLATE_SUCCESS:
392 break;
393 case YANG_TRANSLATE_NOTFOUND:
394 goto next;
395 case YANG_TRANSLATE_FAILURE:
396 goto error;
397 }
398
399 /* Create new node in the tree of translated data. */
400 if (lyd_new_path(new, ly_ctx, xpath,
401 (void *)yang_dnode_get_string(
402 dnode_iter, NULL),
403 LYD_NEW_PATH_UPDATE, NULL)) {
404 flog_err(EC_LIB_LIBYANG,
405 "%s: lyd_new_path() failed", __func__);
406 goto error;
407 }
408
409 next:
410 LYD_TREE_DFS_END(root, dnode_iter);
411 }
412 }
413
414 /* Replace dnode by the new translated dnode. */
415 yang_dnode_free(*dnode);
416 *dnode = new;
417
418 return YANG_TRANSLATE_SUCCESS;
419
420 error:
421 yang_dnode_free(new);
422
423 return YANG_TRANSLATE_FAILURE;
424 }
425
426 struct translator_validate_args {
427 struct yang_translator *translator;
428 unsigned int errors;
429 };
430
431 static int yang_translator_validate_cb(const struct lysc_node *snode_custom,
432 void *arg)
433 {
434 struct translator_validate_args *args = arg;
435 struct yang_mapping_node *mapping;
436 const struct lysc_node *snode_native;
437 const struct lysc_type *stype_custom, *stype_native;
438 char xpath[XPATH_MAXLEN];
439
440 yang_snode_get_path(snode_custom, YANG_PATH_DATA, xpath, sizeof(xpath));
441 mapping = yang_mapping_lookup(args->translator,
442 YANG_TRANSLATE_TO_NATIVE, xpath);
443 if (!mapping) {
444 flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD,
445 "%s: missing mapping for \"%s\"", __func__, xpath);
446 args->errors += 1;
447 return YANG_ITER_CONTINUE;
448 }
449
450 snode_native =
451 lys_find_path(ly_native_ctx, NULL, mapping->xpath_to_fmt, 0);
452 assert(snode_native);
453
454 /* Check if the YANG types are compatible. */
455 stype_custom = yang_snode_get_type(snode_custom);
456 stype_native = yang_snode_get_type(snode_native);
457 if (stype_custom && stype_native) {
458 if (stype_custom->basetype != stype_native->basetype) {
459 flog_warn(
460 EC_LIB_YANG_TRANSLATOR_LOAD,
461 "%s: YANG types are incompatible (xpath: \"%s\")",
462 __func__, xpath);
463 args->errors += 1;
464 return YANG_ITER_CONTINUE;
465 }
466
467 /* TODO: check if the value spaces are identical. */
468 }
469
470 return YANG_ITER_CONTINUE;
471 }
472
473 /*
474 * Check if the modules from the translator have a mapping for all of their
475 * schema nodes (after loading the deviations).
476 */
477 static unsigned int yang_translator_validate(struct yang_translator *translator)
478 {
479 struct yang_tmodule *tmodule;
480 struct listnode *ln;
481 struct translator_validate_args args;
482
483 args.translator = translator;
484 args.errors = 0;
485
486 for (ALL_LIST_ELEMENTS_RO(translator->modules, ln, tmodule)) {
487 yang_snodes_iterate(tmodule->module,
488 yang_translator_validate_cb,
489 YANG_ITER_FILTER_NPCONTAINERS
490 | YANG_ITER_FILTER_LIST_KEYS
491 | YANG_ITER_FILTER_INPUT_OUTPUT,
492 &args);
493 }
494
495 if (args.errors)
496 flog_warn(
497 EC_LIB_YANG_TRANSLATOR_LOAD,
498 "%s: failed to validate \"%s\" module translator: %u error(s)",
499 __func__, translator->family, args.errors);
500
501 return args.errors;
502 }
503
504 static int yang_module_nodes_count_cb(const struct lysc_node *snode, void *arg)
505 {
506 unsigned int *total = arg;
507
508 *total += 1;
509
510 return YANG_ITER_CONTINUE;
511 }
512
513 /* Calculate the number of nodes for the given module. */
514 static unsigned int yang_module_nodes_count(const struct lys_module *module)
515 {
516 unsigned int total = 0;
517
518 yang_snodes_iterate(module, yang_module_nodes_count_cb,
519 YANG_ITER_FILTER_NPCONTAINERS
520 | YANG_ITER_FILTER_LIST_KEYS
521 | YANG_ITER_FILTER_INPUT_OUTPUT,
522 &total);
523
524 return total;
525 }
526
527 void yang_translator_init(void)
528 {
529 ly_translator_ctx = yang_ctx_new_setup(true, false);
530 if (!ly_translator_ctx) {
531 flog_err(EC_LIB_LIBYANG, "%s: ly_ctx_new() failed", __func__);
532 exit(1);
533 }
534
535 if (!ly_ctx_load_module(ly_translator_ctx, "frr-module-translator",
536 NULL, NULL)) {
537 flog_err(
538 EC_LIB_YANG_MODULE_LOAD,
539 "%s: failed to load the \"frr-module-translator\" module",
540 __func__);
541 exit(1);
542 }
543 }
544
545 void yang_translator_terminate(void)
546 {
547 while (!RB_EMPTY(yang_translators, &yang_translators)) {
548 struct yang_translator *translator;
549
550 translator = RB_ROOT(yang_translators, &yang_translators);
551 yang_translator_unload(translator);
552 }
553
554 ly_ctx_destroy(ly_translator_ctx);
555 }