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