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