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