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"
29 DEFINE_MTYPE_STATIC(LIB
, YANG_TRANSLATOR
, "YANG Translator");
30 DEFINE_MTYPE_STATIC(LIB
, YANG_TRANSLATOR_MODULE
, "YANG Translator Module");
31 DEFINE_MTYPE_STATIC(LIB
, YANG_TRANSLATOR_MAPPING
, "YANG Translator Mapping");
33 /* Generate the yang_translators tree. */
34 static inline int yang_translator_compare(const struct yang_translator
*a
,
35 const struct yang_translator
*b
)
37 return strcmp(a
->family
, b
->family
);
39 RB_GENERATE(yang_translators
, yang_translator
, entry
, yang_translator_compare
)
41 struct yang_translators yang_translators
= RB_INITIALIZER(&yang_translators
);
43 /* Separate libyang context for the translator module. */
44 static struct ly_ctx
*ly_translator_ctx
;
47 yang_translator_validate(struct yang_translator
*translator
);
48 static unsigned int yang_module_nodes_count(const struct lys_module
*module
);
50 struct yang_mapping_node
{
51 char xpath_from_canonical
[XPATH_MAXLEN
];
52 char xpath_from_fmt
[XPATH_MAXLEN
];
53 char xpath_to_fmt
[XPATH_MAXLEN
];
56 static bool yang_mapping_hash_cmp(const void *value1
, const void *value2
)
58 const struct yang_mapping_node
*c1
= value1
;
59 const struct yang_mapping_node
*c2
= value2
;
61 return strmatch(c1
->xpath_from_canonical
, c2
->xpath_from_canonical
);
64 static unsigned int yang_mapping_hash_key(const void *value
)
66 return string_hash_make(value
);
69 static void *yang_mapping_hash_alloc(void *p
)
71 struct yang_mapping_node
*new, *key
= p
;
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
));
80 static void yang_mapping_hash_free(void *arg
)
82 XFREE(MTYPE_YANG_TRANSLATOR_MAPPING
, arg
);
85 static struct yang_mapping_node
*
86 yang_mapping_lookup(const struct yang_translator
*translator
, int dir
,
89 struct yang_mapping_node s
;
91 strlcpy(s
.xpath_from_canonical
, xpath
, sizeof(s
.xpath_from_canonical
));
92 return hash_lookup(translator
->mappings
[dir
], &s
);
95 static void yang_mapping_add(struct yang_translator
*translator
, int dir
,
96 const struct lysc_node
*snode
,
97 const char *xpath_from_fmt
,
98 const char *xpath_to_fmt
)
100 struct yang_mapping_node
*mapping
, s
;
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
));
111 const char *keys
[] = {"KEY1", "KEY2", "KEY3", "KEY4"};
114 for (unsigned int i
= 0; i
< array_size(keys
); i
++) {
115 xpfmt
= frrstr_replace(mapping
->xpath_from_fmt
, keys
[i
],
117 strlcpy(mapping
->xpath_from_fmt
, xpfmt
,
118 sizeof(mapping
->xpath_from_fmt
));
119 XFREE(MTYPE_TMP
, xpfmt
);
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
);
130 static void yang_tmodule_delete(struct yang_tmodule
*tmodule
)
132 XFREE(MTYPE_YANG_TRANSLATOR_MODULE
, tmodule
);
135 struct yang_translator
*yang_translator_load(const char *path
)
137 struct yang_translator
*translator
;
138 struct yang_tmodule
*tmodule
= NULL
;
140 struct lyd_node
*dnode
;
145 /* Load module translator (JSON file). */
146 err
= lyd_parse_data_path(ly_translator_ctx
, path
, LYD_JSON
,
147 LYD_PARSE_NO_STATE
, LYD_VALIDATE_NO_STATE
,
150 flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD
,
151 "%s: lyd_parse_path() failed: %d", __func__
, err
);
154 dnode
= yang_dnode_get(dnode
,
155 "/frr-module-translator:frr-module-translator");
157 * libyang guarantees the "frr-module-translator" top-level container is
158 * always present since it contains mandatory child nodes.
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",
168 yang_dnode_free(dnode
);
172 translator
= XCALLOC(MTYPE_YANG_TRANSLATOR
, sizeof(*translator
));
173 strlcpy(translator
->family
, family
, sizeof(translator
->family
));
174 translator
->modules
= list_new();
175 for (size_t i
= 0; i
< YANG_TRANSLATE_MAX
; i
++)
176 translator
->mappings
[i
] = hash_create(yang_mapping_hash_key
,
177 yang_mapping_hash_cmp
,
178 "YANG translation table");
179 RB_INSERT(yang_translators
, &yang_translators
, translator
);
181 /* Initialize the translator libyang context. */
182 translator
->ly_ctx
= yang_ctx_new_setup(false, false);
183 if (!translator
->ly_ctx
) {
184 flog_warn(EC_LIB_LIBYANG
, "%s: ly_ctx_new() failed", __func__
);
189 if (lyd_find_xpath(dnode
, "./module", &set
) != LY_SUCCESS
)
190 assert(0); /* XXX libyang2: old ly1 code asserted success */
192 for (size_t i
= 0; i
< set
->count
; i
++) {
193 const char *module_name
;
196 XCALLOC(MTYPE_YANG_TRANSLATOR_MODULE
, sizeof(*tmodule
));
198 module_name
= yang_dnode_get_string(set
->dnodes
[i
], "./name");
199 tmodule
->module
= ly_ctx_load_module(translator
->ly_ctx
,
200 module_name
, NULL
, NULL
);
201 if (!tmodule
->module
) {
202 flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD
,
203 "%s: failed to load module: %s", __func__
,
205 ly_set_free(set
, NULL
);
210 /* Count nodes in modules. */
211 for (ALL_LIST_ELEMENTS_RO(translator
->modules
, ln
, tmodule
)) {
212 tmodule
->nodes_before_deviations
=
213 yang_module_nodes_count(tmodule
->module
);
216 /* Load the deviations and count nodes again */
217 for (ALL_LIST_ELEMENTS_RO(translator
->modules
, ln
, tmodule
)) {
218 const char *module_name
= tmodule
->module
->name
;
219 tmodule
->deviations
= ly_ctx_load_module(
220 translator
->ly_ctx
, module_name
, NULL
, NULL
);
221 if (!tmodule
->deviations
) {
222 flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD
,
223 "%s: failed to load module: %s", __func__
,
225 ly_set_free(set
, NULL
);
229 tmodule
->nodes_after_deviations
=
230 yang_module_nodes_count(tmodule
->module
);
232 ly_set_free(set
, NULL
);
234 /* Calculate the coverage. */
235 for (ALL_LIST_ELEMENTS_RO(translator
->modules
, ln
, tmodule
)) {
236 tmodule
->coverage
= ((double)tmodule
->nodes_after_deviations
237 / (double)tmodule
->nodes_before_deviations
)
242 if (lyd_find_xpath(dnode
, "./module/mappings", &set
) != LY_SUCCESS
)
243 assert(0); /* XXX libyang2: old ly1 code asserted success */
244 for (size_t i
= 0; i
< set
->count
; i
++) {
245 const char *xpath_custom
, *xpath_native
;
246 const struct lysc_node
*snode_custom
, *snode_native
;
249 yang_dnode_get_string(set
->dnodes
[i
], "./custom");
251 snode_custom
= lys_find_path(translator
->ly_ctx
, NULL
,
254 flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD
,
255 "%s: unknown data path: %s", __func__
,
257 ly_set_free(set
, NULL
);
262 yang_dnode_get_string(set
->dnodes
[i
], "./native");
264 lys_find_path(ly_native_ctx
, NULL
, xpath_native
, 0);
266 flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD
,
267 "%s: unknown data path: %s", __func__
,
269 ly_set_free(set
, NULL
);
273 yang_mapping_add(translator
, YANG_TRANSLATE_TO_NATIVE
,
274 snode_custom
, xpath_custom
, xpath_native
);
275 yang_mapping_add(translator
, YANG_TRANSLATE_FROM_NATIVE
,
276 snode_native
, xpath_native
, xpath_custom
);
278 ly_set_free(set
, NULL
);
280 /* Validate mappings. */
281 if (yang_translator_validate(translator
) != 0)
284 yang_dnode_free(dnode
);
289 yang_dnode_free(dnode
);
290 yang_translator_unload(translator
);
291 yang_tmodule_delete(tmodule
);
296 void yang_translator_unload(struct yang_translator
*translator
)
298 for (size_t i
= 0; i
< YANG_TRANSLATE_MAX
; i
++)
299 hash_clean(translator
->mappings
[i
], yang_mapping_hash_free
);
300 translator
->modules
->del
= (void (*)(void *))yang_tmodule_delete
;
301 list_delete(&translator
->modules
);
302 ly_ctx_destroy(translator
->ly_ctx
);
303 RB_REMOVE(yang_translators
, &yang_translators
, translator
);
304 XFREE(MTYPE_YANG_TRANSLATOR
, translator
);
307 struct yang_translator
*yang_translator_find(const char *family
)
309 struct yang_translator s
;
311 strlcpy(s
.family
, family
, sizeof(s
.family
));
312 return RB_FIND(yang_translators
, &yang_translators
, &s
);
315 enum yang_translate_result
316 yang_translate_xpath(const struct yang_translator
*translator
, int dir
,
317 char *xpath
, size_t xpath_len
)
319 struct ly_ctx
*ly_ctx
;
320 const struct lysc_node
*snode
;
321 struct yang_mapping_node
*mapping
;
322 char xpath_canonical
[XPATH_MAXLEN
];
323 char keys
[4][LIST_MAXKEYLEN
];
326 if (dir
== YANG_TRANSLATE_TO_NATIVE
)
327 ly_ctx
= translator
->ly_ctx
;
329 ly_ctx
= ly_native_ctx
;
331 snode
= lys_find_path(ly_ctx
, NULL
, xpath
, 0);
333 flog_warn(EC_LIB_YANG_TRANSLATION_ERROR
,
334 "%s: unknown data path: %s", __func__
, xpath
);
335 return YANG_TRANSLATE_FAILURE
;
338 yang_snode_get_path(snode
, YANG_PATH_DATA
, xpath_canonical
,
339 sizeof(xpath_canonical
));
340 mapping
= yang_mapping_lookup(translator
, dir
, xpath_canonical
);
342 return YANG_TRANSLATE_NOTFOUND
;
344 #pragma GCC diagnostic push
345 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
346 /* processing format strings from mapping node... */
347 n
= sscanf(xpath
, mapping
->xpath_from_fmt
, keys
[0], keys
[1], keys
[2],
349 #pragma GCC diagnostic pop
351 flog_warn(EC_LIB_YANG_TRANSLATION_ERROR
,
352 "%s: sscanf() failed: %s", __func__
,
353 safe_strerror(errno
));
354 return YANG_TRANSLATE_FAILURE
;
357 #pragma GCC diagnostic push
358 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
359 /* processing format strings from mapping node... */
360 snprintf(xpath
, xpath_len
, mapping
->xpath_to_fmt
, keys
[0], keys
[1],
362 #pragma GCC diagnostic pop
364 return YANG_TRANSLATE_SUCCESS
;
367 int yang_translate_dnode(const struct yang_translator
*translator
, int dir
,
368 struct lyd_node
**dnode
)
370 struct ly_ctx
*ly_ctx
;
371 struct lyd_node
*new;
372 struct lyd_node
*root
, *dnode_iter
;
374 /* Create new libyang data node to hold the translated data. */
375 if (dir
== YANG_TRANSLATE_TO_NATIVE
)
376 ly_ctx
= ly_native_ctx
;
378 ly_ctx
= translator
->ly_ctx
;
379 new = yang_dnode_new(ly_ctx
, false);
381 /* Iterate over all nodes from the data tree. */
382 LY_LIST_FOR (*dnode
, root
) {
383 LYD_TREE_DFS_BEGIN (root
, dnode_iter
) {
384 char xpath
[XPATH_MAXLEN
];
385 enum yang_translate_result ret
;
387 yang_dnode_get_path(dnode_iter
, xpath
, sizeof(xpath
));
388 ret
= yang_translate_xpath(translator
, dir
, xpath
,
391 case YANG_TRANSLATE_SUCCESS
:
393 case YANG_TRANSLATE_NOTFOUND
:
395 case YANG_TRANSLATE_FAILURE
:
399 /* Create new node in the tree of translated data. */
400 if (lyd_new_path(new, ly_ctx
, xpath
,
401 (void *)yang_dnode_get_string(
403 LYD_NEW_PATH_UPDATE
, NULL
)) {
404 flog_err(EC_LIB_LIBYANG
,
405 "%s: lyd_new_path() failed", __func__
);
410 LYD_TREE_DFS_END(root
, dnode_iter
);
414 /* Replace dnode by the new translated dnode. */
415 yang_dnode_free(*dnode
);
418 return YANG_TRANSLATE_SUCCESS
;
421 yang_dnode_free(new);
423 return YANG_TRANSLATE_FAILURE
;
426 struct translator_validate_args
{
427 struct yang_translator
*translator
;
431 static int yang_translator_validate_cb(const struct lysc_node
*snode_custom
,
434 struct translator_validate_args
*args
= arg
;
435 struct yang_mapping_node
*mapping
;
436 const struct lysc_node
*snode_native
;
437 const struct lysc_type
*stype_custom
, *stype_native
;
438 char xpath
[XPATH_MAXLEN
];
440 yang_snode_get_path(snode_custom
, YANG_PATH_DATA
, xpath
, sizeof(xpath
));
441 mapping
= yang_mapping_lookup(args
->translator
,
442 YANG_TRANSLATE_TO_NATIVE
, xpath
);
444 flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD
,
445 "%s: missing mapping for \"%s\"", __func__
, xpath
);
447 return YANG_ITER_CONTINUE
;
451 lys_find_path(ly_native_ctx
, NULL
, mapping
->xpath_to_fmt
, 0);
452 assert(snode_native
);
454 /* Check if the YANG types are compatible. */
455 stype_custom
= yang_snode_get_type(snode_custom
);
456 stype_native
= yang_snode_get_type(snode_native
);
457 if (stype_custom
&& stype_native
) {
458 if (stype_custom
->basetype
!= stype_native
->basetype
) {
460 EC_LIB_YANG_TRANSLATOR_LOAD
,
461 "%s: YANG types are incompatible (xpath: \"%s\")",
464 return YANG_ITER_CONTINUE
;
467 /* TODO: check if the value spaces are identical. */
470 return YANG_ITER_CONTINUE
;
474 * Check if the modules from the translator have a mapping for all of their
475 * schema nodes (after loading the deviations).
477 static unsigned int yang_translator_validate(struct yang_translator
*translator
)
479 struct yang_tmodule
*tmodule
;
481 struct translator_validate_args args
;
483 args
.translator
= translator
;
486 for (ALL_LIST_ELEMENTS_RO(translator
->modules
, ln
, tmodule
)) {
487 yang_snodes_iterate(tmodule
->module
,
488 yang_translator_validate_cb
,
489 YANG_ITER_FILTER_NPCONTAINERS
490 | YANG_ITER_FILTER_LIST_KEYS
491 | YANG_ITER_FILTER_INPUT_OUTPUT
,
497 EC_LIB_YANG_TRANSLATOR_LOAD
,
498 "%s: failed to validate \"%s\" module translator: %u error(s)",
499 __func__
, translator
->family
, args
.errors
);
504 static int yang_module_nodes_count_cb(const struct lysc_node
*snode
, void *arg
)
506 unsigned int *total
= arg
;
510 return YANG_ITER_CONTINUE
;
513 /* Calculate the number of nodes for the given module. */
514 static unsigned int yang_module_nodes_count(const struct lys_module
*module
)
516 unsigned int total
= 0;
518 yang_snodes_iterate(module
, yang_module_nodes_count_cb
,
519 YANG_ITER_FILTER_NPCONTAINERS
520 | YANG_ITER_FILTER_LIST_KEYS
521 | YANG_ITER_FILTER_INPUT_OUTPUT
,
527 void yang_translator_init(void)
529 ly_translator_ctx
= yang_ctx_new_setup(true, false);
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
);