2 * Copyright (C) 2018 NetDEF, Inc.
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)
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
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
23 #include "lib_errors.h"
26 #include "yang_translator.h"
28 DEFINE_MTYPE_STATIC(LIB
, YANG_TRANSLATOR
, "YANG Translator")
29 DEFINE_MTYPE_STATIC(LIB
, YANG_TRANSLATOR_MODULE
, "YANG Translator Module")
30 DEFINE_MTYPE_STATIC(LIB
, YANG_TRANSLATOR_MAPPING
, "YANG Translator Mapping")
32 /* Generate the yang_translators tree. */
33 static inline int yang_translator_compare(const struct yang_translator
*a
,
34 const struct yang_translator
*b
)
36 return strcmp(a
->family
, b
->family
);
38 RB_GENERATE(yang_translators
, yang_translator
, entry
, yang_translator_compare
)
40 struct yang_translators yang_translators
= RB_INITIALIZER(&yang_translators
);
42 /* Separate libyang context for the translator module. */
43 static struct ly_ctx
*ly_translator_ctx
;
46 yang_translator_validate(struct yang_translator
*translator
);
47 static unsigned int yang_module_nodes_count(const struct lys_module
*module
);
48 static void str_replace(char *o_string
, const char *s_string
,
49 const char *r_string
);
51 struct yang_mapping_node
{
52 char xpath_from_canonical
[XPATH_MAXLEN
];
53 char xpath_from_fmt
[XPATH_MAXLEN
];
54 char xpath_to_fmt
[XPATH_MAXLEN
];
57 static bool yang_mapping_hash_cmp(const void *value1
, const void *value2
)
59 const struct yang_mapping_node
*c1
= value1
;
60 const struct yang_mapping_node
*c2
= value2
;
62 return strmatch(c1
->xpath_from_canonical
, c2
->xpath_from_canonical
);
65 static unsigned int yang_mapping_hash_key(void *value
)
67 return string_hash_make(value
);
70 static void *yang_mapping_hash_alloc(void *p
)
72 struct yang_mapping_node
*new, *key
= p
;
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
));
81 static void yang_mapping_hash_free(void *arg
)
83 XFREE(MTYPE_YANG_TRANSLATOR_MAPPING
, arg
);
86 static struct yang_mapping_node
*
87 yang_mapping_lookup(const struct yang_translator
*translator
, int dir
,
90 struct yang_mapping_node s
;
92 strlcpy(s
.xpath_from_canonical
, xpath
, sizeof(s
.xpath_from_canonical
));
93 return hash_lookup(translator
->mappings
[dir
], &s
);
96 static 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
)
101 struct yang_mapping_node
*mapping
, s
;
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");
121 struct yang_translator
*yang_translator_load(const char *path
)
123 struct yang_translator
*translator
;
124 struct yang_tmodule
*tmodule
;
126 struct lyd_node
*dnode
;
130 /* Load module translator (JSON file). */
131 dnode
= lyd_parse_path(ly_translator_ctx
, path
, LYD_JSON
,
134 flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD
,
135 "%s: lyd_parse_path() failed", __func__
);
138 dnode
= yang_dnode_get(dnode
,
139 "/frr-module-translator:frr-module-translator");
141 * libyang guarantees the "frr-module-translator" top-level container is
142 * always present since it contains mandatory child nodes.
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",
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
);
164 /* Initialize the translator libyang context. */
166 ly_ctx_new(YANG_MODELS_PATH
, LY_CTX_DISABLE_SEARCHDIR_CWD
);
167 if (!translator
->ly_ctx
) {
168 flog_warn(EC_LIB_LIBYANG
, "%s: ly_ctx_new() failed", __func__
);
172 /* Load modules and deviations. */
173 set
= lyd_find_path(dnode
, "./module");
175 for (size_t i
= 0; i
< set
->number
; i
++) {
176 const char *module_name
;
179 XCALLOC(MTYPE_YANG_TRANSLATOR_MODULE
, sizeof(*tmodule
));
181 module_name
= yang_dnode_get_string(set
->set
.d
[i
], "./name");
182 tmodule
->module
= ly_ctx_load_module(translator
->ly_ctx
,
184 if (!tmodule
->module
) {
185 flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD
,
186 "%s: failed to load module: %s", __func__
,
193 yang_dnode_get_string(set
->set
.d
[i
], "./deviations");
194 tmodule
->deviations
= ly_ctx_load_module(translator
->ly_ctx
,
196 if (!tmodule
->deviations
) {
197 flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD
,
198 "%s: failed to load module: %s", __func__
,
203 lys_set_disabled(tmodule
->deviations
);
205 listnode_add(translator
->modules
, tmodule
);
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
);
214 lys_set_enabled(tmodule
->deviations
);
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
)
224 set
= lyd_find_path(dnode
, "./module/mappings");
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
;
230 xpath_custom
= yang_dnode_get_string(set
->set
.d
[i
], "./custom");
231 snode_custom
= ly_ctx_get_node(translator
->ly_ctx
, NULL
,
234 flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD
,
235 "%s: unknown data path: %s", __func__
,
241 xpath_native
= yang_dnode_get_string(set
->set
.d
[i
], "./native");
243 ly_ctx_get_node(ly_native_ctx
, NULL
, xpath_native
, 0);
245 flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD
,
246 "%s: unknown data path: %s", __func__
,
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
);
259 /* Validate mappings. */
260 if (yang_translator_validate(translator
) != 0)
263 yang_dnode_free(dnode
);
268 yang_dnode_free(dnode
);
269 yang_translator_unload(translator
);
274 static void yang_tmodule_delete(struct yang_tmodule
*tmodule
)
276 XFREE(MTYPE_YANG_TRANSLATOR_MODULE
, tmodule
);
279 void yang_translator_unload(struct yang_translator
*translator
)
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
);
290 struct yang_translator
*yang_translator_find(const char *family
)
292 struct yang_translator s
;
294 strlcpy(s
.family
, family
, sizeof(s
.family
));
295 return RB_FIND(yang_translators
, &yang_translators
, &s
);
298 enum yang_translate_result
299 yang_translate_xpath(const struct yang_translator
*translator
, int dir
,
300 char *xpath
, size_t xpath_len
)
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
];
309 if (dir
== YANG_TRANSLATE_TO_NATIVE
)
310 ly_ctx
= translator
->ly_ctx
;
312 ly_ctx
= ly_native_ctx
;
314 snode
= ly_ctx_get_node(ly_ctx
, NULL
, xpath
, 0);
316 flog_warn(EC_LIB_YANG_TRANSLATION_ERROR
,
317 "%s: unknown data path: %s", __func__
, xpath
);
318 return YANG_TRANSLATE_FAILURE
;
321 yang_snode_get_path(snode
, YANG_PATH_DATA
, xpath_canonical
,
322 sizeof(xpath_canonical
));
323 mapping
= yang_mapping_lookup(translator
, dir
, xpath_canonical
);
325 return YANG_TRANSLATE_NOTFOUND
;
327 n
= sscanf(xpath
, mapping
->xpath_from_fmt
, keys
[0], keys
[1], keys
[2],
330 flog_warn(EC_LIB_YANG_TRANSLATION_ERROR
,
331 "%s: sscanf() failed: %s", __func__
,
332 safe_strerror(errno
));
333 return YANG_TRANSLATE_FAILURE
;
336 snprintf(xpath
, xpath_len
, mapping
->xpath_to_fmt
, keys
[0], keys
[1],
339 return YANG_TRANSLATE_SUCCESS
;
342 int yang_translate_dnode(const struct yang_translator
*translator
, int dir
,
343 struct lyd_node
**dnode
)
345 struct ly_ctx
*ly_ctx
;
346 struct lyd_node
*new;
347 struct lyd_node
*root
, *next
, *dnode_iter
;
349 /* Create new libyang data node to hold the translated data. */
350 if (dir
== YANG_TRANSLATE_TO_NATIVE
)
351 ly_ctx
= ly_native_ctx
;
353 ly_ctx
= translator
->ly_ctx
;
354 new = yang_dnode_new(ly_ctx
, false);
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
;
362 yang_dnode_get_path(dnode_iter
, xpath
, sizeof(xpath
));
363 ret
= yang_translate_xpath(translator
, dir
, xpath
,
366 case YANG_TRANSLATE_SUCCESS
:
368 case YANG_TRANSLATE_NOTFOUND
:
370 case YANG_TRANSLATE_FAILURE
:
374 /* Create new node in the tree of translated data. */
376 if (!lyd_new_path(new, ly_ctx
, xpath
,
377 (void *)yang_dnode_get_string(
379 0, LYD_PATH_OPT_UPDATE
)
381 flog_err(EC_LIB_LIBYANG
,
382 "%s: lyd_new_path() failed", __func__
);
387 LY_TREE_DFS_END(root
, next
, dnode_iter
);
391 /* Replace dnode by the new translated dnode. */
392 yang_dnode_free(*dnode
);
395 return YANG_TRANSLATE_SUCCESS
;
398 yang_dnode_free(new);
400 return YANG_TRANSLATE_FAILURE
;
403 struct translator_validate_args
{
404 struct yang_translator
*translator
;
408 static int yang_translator_validate_cb(const struct lys_node
*snode_custom
,
411 struct translator_validate_args
*args
= arg
;
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
];
417 yang_snode_get_path(snode_custom
, YANG_PATH_DATA
, xpath
, sizeof(xpath
));
418 mapping
= yang_mapping_lookup(args
->translator
,
419 YANG_TRANSLATE_TO_NATIVE
, xpath
);
421 flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD
,
422 "%s: missing mapping for \"%s\"", __func__
, xpath
);
424 return YANG_ITER_CONTINUE
;
428 ly_ctx_get_node(ly_native_ctx
, NULL
, mapping
->xpath_to_fmt
, 0);
429 assert(snode_native
);
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
) {
437 EC_LIB_YANG_TRANSLATOR_LOAD
,
438 "%s: YANG types are incompatible (xpath: \"%s\")",
441 return YANG_ITER_CONTINUE
;
444 /* TODO: check if the value spaces are identical. */
447 return YANG_ITER_CONTINUE
;
451 * Check if the modules from the translator have a mapping for all of their
452 * schema nodes (after loading the deviations).
454 static unsigned int yang_translator_validate(struct yang_translator
*translator
)
456 struct yang_tmodule
*tmodule
;
458 struct translator_validate_args args
;
460 args
.translator
= translator
;
463 for (ALL_LIST_ELEMENTS_RO(translator
->modules
, ln
, tmodule
)) {
464 yang_snodes_iterate_module(
465 tmodule
->module
, yang_translator_validate_cb
,
466 YANG_ITER_FILTER_NPCONTAINERS
467 | YANG_ITER_FILTER_LIST_KEYS
468 | YANG_ITER_FILTER_INPUT_OUTPUT
,
474 EC_LIB_YANG_TRANSLATOR_LOAD
,
475 "%s: failed to validate \"%s\" module translator: %u error(s)",
476 __func__
, translator
->family
, args
.errors
);
481 static int yang_module_nodes_count_cb(const struct lys_node
*snode
, void *arg
)
483 unsigned int *total
= arg
;
487 return YANG_ITER_CONTINUE
;
490 /* Calculate the number of nodes for the given module. */
491 static unsigned int yang_module_nodes_count(const struct lys_module
*module
)
493 unsigned int total
= 0;
495 yang_snodes_iterate_module(module
, yang_module_nodes_count_cb
,
496 YANG_ITER_FILTER_NPCONTAINERS
497 | YANG_ITER_FILTER_LIST_KEYS
498 | YANG_ITER_FILTER_INPUT_OUTPUT
,
504 /* TODO: rewrite this function. */
505 static void str_replace(char *o_string
, const char *s_string
,
506 const char *r_string
)
511 ch
= strstr(o_string
, s_string
);
515 strncpy(buffer
, o_string
, ch
- o_string
);
516 buffer
[ch
- o_string
] = 0;
518 sprintf(buffer
+ (ch
- o_string
), "%s%s", r_string
,
519 ch
+ strlen(s_string
));
522 strcpy(o_string
, buffer
);
523 return str_replace(o_string
, s_string
, r_string
);
526 void yang_translator_init(void)
529 ly_ctx_new(YANG_MODELS_PATH
, LY_CTX_DISABLE_SEARCHDIR_CWD
);
530 if (!ly_translator_ctx
) {
531 flog_err(EC_LIB_LIBYANG
, "%s: ly_ctx_new() failed", __func__
);
535 if (!ly_ctx_load_module(ly_translator_ctx
, "frr-module-translator",
538 EC_LIB_YANG_MODULE_LOAD
,
539 "%s: failed to load the \"frr-module-translator\" module",
545 void yang_translator_terminate(void)
547 while (!RB_EMPTY(yang_translators
, &yang_translators
)) {
548 struct yang_translator
*translator
;
550 translator
= RB_ROOT(yang_translators
, &yang_translators
);
551 yang_translator_unload(translator
);
554 ly_ctx_destroy(ly_translator_ctx
, NULL
);