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