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