1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2018 NetDEF, Inc.
10 #include "lib_errors.h"
13 #include "yang_translator.h"
16 DEFINE_MTYPE_STATIC(LIB
, YANG_TRANSLATOR
, "YANG Translator");
17 DEFINE_MTYPE_STATIC(LIB
, YANG_TRANSLATOR_MODULE
, "YANG Translator Module");
18 DEFINE_MTYPE_STATIC(LIB
, YANG_TRANSLATOR_MAPPING
, "YANG Translator Mapping");
20 /* Generate the yang_translators tree. */
21 static inline int yang_translator_compare(const struct yang_translator
*a
,
22 const struct yang_translator
*b
)
24 return strcmp(a
->family
, b
->family
);
26 RB_GENERATE(yang_translators
, yang_translator
, entry
, yang_translator_compare
)
28 struct yang_translators yang_translators
= RB_INITIALIZER(&yang_translators
);
30 /* Separate libyang context for the translator module. */
31 static struct ly_ctx
*ly_translator_ctx
;
34 yang_translator_validate(struct yang_translator
*translator
);
35 static unsigned int yang_module_nodes_count(const struct lys_module
*module
);
37 struct yang_mapping_node
{
38 char xpath_from_canonical
[XPATH_MAXLEN
];
39 char xpath_from_fmt
[XPATH_MAXLEN
];
40 char xpath_to_fmt
[XPATH_MAXLEN
];
43 static bool yang_mapping_hash_cmp(const void *value1
, const void *value2
)
45 const struct yang_mapping_node
*c1
= value1
;
46 const struct yang_mapping_node
*c2
= value2
;
48 return strmatch(c1
->xpath_from_canonical
, c2
->xpath_from_canonical
);
51 static unsigned int yang_mapping_hash_key(const void *value
)
53 return string_hash_make(value
);
56 static void *yang_mapping_hash_alloc(void *p
)
58 struct yang_mapping_node
*new, *key
= p
;
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
));
67 static void yang_mapping_hash_free(void *arg
)
69 XFREE(MTYPE_YANG_TRANSLATOR_MAPPING
, arg
);
72 static struct yang_mapping_node
*
73 yang_mapping_lookup(const struct yang_translator
*translator
, int dir
,
76 struct yang_mapping_node s
;
78 strlcpy(s
.xpath_from_canonical
, xpath
, sizeof(s
.xpath_from_canonical
));
79 return hash_lookup(translator
->mappings
[dir
], &s
);
82 static void yang_mapping_add(struct yang_translator
*translator
, int dir
,
83 const struct lysc_node
*snode
,
84 const char *xpath_from_fmt
,
85 const char *xpath_to_fmt
)
87 struct yang_mapping_node
*mapping
, s
;
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
));
98 const char *keys
[] = {"KEY1", "KEY2", "KEY3", "KEY4"};
101 for (unsigned int i
= 0; i
< array_size(keys
); i
++) {
102 xpfmt
= frrstr_replace(mapping
->xpath_from_fmt
, keys
[i
],
104 strlcpy(mapping
->xpath_from_fmt
, xpfmt
,
105 sizeof(mapping
->xpath_from_fmt
));
106 XFREE(MTYPE_TMP
, xpfmt
);
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
);
117 static void yang_tmodule_delete(struct yang_tmodule
*tmodule
)
119 XFREE(MTYPE_YANG_TRANSLATOR_MODULE
, tmodule
);
122 struct yang_translator
*yang_translator_load(const char *path
)
124 struct yang_translator
*translator
;
125 struct yang_tmodule
*tmodule
= NULL
;
127 struct lyd_node
*dnode
;
132 /* Load module translator (JSON file). */
133 err
= lyd_parse_data_path(ly_translator_ctx
, path
, LYD_JSON
,
134 LYD_PARSE_NO_STATE
, LYD_VALIDATE_NO_STATE
,
137 flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD
,
138 "%s: lyd_parse_path() failed: %d", __func__
, err
);
141 dnode
= yang_dnode_get(dnode
,
142 "/frr-module-translator:frr-module-translator");
144 * libyang guarantees the "frr-module-translator" top-level container is
145 * always present since it contains mandatory child nodes.
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",
155 yang_dnode_free(dnode
);
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
);
168 /* Initialize the translator libyang context. */
169 translator
->ly_ctx
= yang_ctx_new_setup(false, false);
170 if (!translator
->ly_ctx
) {
171 flog_warn(EC_LIB_LIBYANG
, "%s: ly_ctx_new() failed", __func__
);
176 if (lyd_find_xpath(dnode
, "./module", &set
) != LY_SUCCESS
)
177 assert(0); /* XXX libyang2: old ly1 code asserted success */
179 for (size_t i
= 0; i
< set
->count
; i
++) {
180 const char *module_name
;
183 XCALLOC(MTYPE_YANG_TRANSLATOR_MODULE
, sizeof(*tmodule
));
185 module_name
= yang_dnode_get_string(set
->dnodes
[i
], "./name");
186 tmodule
->module
= ly_ctx_load_module(translator
->ly_ctx
,
187 module_name
, NULL
, NULL
);
188 if (!tmodule
->module
) {
189 flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD
,
190 "%s: failed to load module: %s", __func__
,
192 ly_set_free(set
, NULL
);
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
);
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
);
208 if (!tmodule
->deviations
) {
209 flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD
,
210 "%s: failed to load module: %s", __func__
,
212 ly_set_free(set
, NULL
);
216 tmodule
->nodes_after_deviations
=
217 yang_module_nodes_count(tmodule
->module
);
219 ly_set_free(set
, NULL
);
221 /* Calculate the coverage. */
222 for (ALL_LIST_ELEMENTS_RO(translator
->modules
, ln
, tmodule
)) {
223 tmodule
->coverage
= ((double)tmodule
->nodes_after_deviations
224 / (double)tmodule
->nodes_before_deviations
)
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
++) {
232 const char *xpath_custom
, *xpath_native
;
233 const struct lysc_node
*snode_custom
, *snode_native
;
236 yang_dnode_get_string(set
->dnodes
[i
], "./custom");
239 yang_find_snode(translator
->ly_ctx
, xpath_custom
, 0);
241 flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD
,
242 "%s: unknown data path: %s", __func__
,
244 ly_set_free(set
, NULL
);
249 yang_dnode_get_string(set
->dnodes
[i
], "./native");
250 snode_native
= yang_find_snode(ly_native_ctx
, xpath_native
, 0);
252 flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD
,
253 "%s: unknown data path: %s", __func__
,
255 ly_set_free(set
, NULL
);
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
);
264 ly_set_free(set
, NULL
);
266 /* Validate mappings. */
267 if (yang_translator_validate(translator
) != 0)
270 yang_dnode_free(dnode
);
275 yang_dnode_free(dnode
);
276 yang_translator_unload(translator
);
277 yang_tmodule_delete(tmodule
);
282 void yang_translator_unload(struct yang_translator
*translator
)
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
);
288 ly_ctx_destroy(translator
->ly_ctx
);
289 RB_REMOVE(yang_translators
, &yang_translators
, translator
);
290 XFREE(MTYPE_YANG_TRANSLATOR
, translator
);
293 struct yang_translator
*yang_translator_find(const char *family
)
295 struct yang_translator s
;
297 strlcpy(s
.family
, family
, sizeof(s
.family
));
298 return RB_FIND(yang_translators
, &yang_translators
, &s
);
301 enum yang_translate_result
302 yang_translate_xpath(const struct yang_translator
*translator
, int dir
,
303 char *xpath
, size_t xpath_len
)
305 struct ly_ctx
*ly_ctx
;
306 const struct lysc_node
*snode
;
307 struct yang_mapping_node
*mapping
;
308 char xpath_canonical
[XPATH_MAXLEN
];
309 char keys
[4][LIST_MAXKEYLEN
];
312 if (dir
== YANG_TRANSLATE_TO_NATIVE
)
313 ly_ctx
= translator
->ly_ctx
;
315 ly_ctx
= ly_native_ctx
;
317 snode
= yang_find_snode(ly_ctx
, xpath
, 0);
319 flog_warn(EC_LIB_YANG_TRANSLATION_ERROR
,
320 "%s: unknown data path: %s", __func__
, xpath
);
321 return YANG_TRANSLATE_FAILURE
;
324 yang_snode_get_path(snode
, YANG_PATH_DATA
, xpath_canonical
,
325 sizeof(xpath_canonical
));
326 mapping
= yang_mapping_lookup(translator
, dir
, xpath_canonical
);
328 return YANG_TRANSLATE_NOTFOUND
;
330 #pragma GCC diagnostic push
331 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
332 /* processing format strings from mapping node... */
333 n
= sscanf(xpath
, mapping
->xpath_from_fmt
, keys
[0], keys
[1], keys
[2],
335 #pragma GCC diagnostic pop
337 flog_warn(EC_LIB_YANG_TRANSLATION_ERROR
,
338 "%s: sscanf() failed: %s", __func__
,
339 safe_strerror(errno
));
340 return YANG_TRANSLATE_FAILURE
;
343 #pragma GCC diagnostic push
344 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
345 /* processing format strings from mapping node... */
346 snprintf(xpath
, xpath_len
, mapping
->xpath_to_fmt
, keys
[0], keys
[1],
348 #pragma GCC diagnostic pop
350 return YANG_TRANSLATE_SUCCESS
;
353 int yang_translate_dnode(const struct yang_translator
*translator
, int dir
,
354 struct lyd_node
**dnode
)
356 struct ly_ctx
*ly_ctx
;
357 struct lyd_node
*new;
358 struct lyd_node
*root
, *dnode_iter
;
360 /* Create new libyang data node to hold the translated data. */
361 if (dir
== YANG_TRANSLATE_TO_NATIVE
)
362 ly_ctx
= ly_native_ctx
;
364 ly_ctx
= translator
->ly_ctx
;
365 new = yang_dnode_new(ly_ctx
, false);
367 /* Iterate over all nodes from the data tree. */
368 LY_LIST_FOR (*dnode
, root
) {
369 LYD_TREE_DFS_BEGIN (root
, dnode_iter
) {
370 char xpath
[XPATH_MAXLEN
];
371 enum yang_translate_result ret
;
373 yang_dnode_get_path(dnode_iter
, xpath
, sizeof(xpath
));
374 ret
= yang_translate_xpath(translator
, dir
, xpath
,
377 case YANG_TRANSLATE_SUCCESS
:
379 case YANG_TRANSLATE_NOTFOUND
:
381 case YANG_TRANSLATE_FAILURE
:
385 /* Create new node in the tree of translated data. */
386 if (lyd_new_path(new, ly_ctx
, xpath
,
387 (void *)yang_dnode_get_string(
389 LYD_NEW_PATH_UPDATE
, NULL
)) {
390 flog_err(EC_LIB_LIBYANG
,
391 "%s: lyd_new_path() failed", __func__
);
396 LYD_TREE_DFS_END(root
, dnode_iter
);
400 /* Replace dnode by the new translated dnode. */
401 yang_dnode_free(*dnode
);
404 return YANG_TRANSLATE_SUCCESS
;
407 yang_dnode_free(new);
409 return YANG_TRANSLATE_FAILURE
;
412 struct translator_validate_args
{
413 struct yang_translator
*translator
;
417 static int yang_translator_validate_cb(const struct lysc_node
*snode_custom
,
420 struct translator_validate_args
*args
= arg
;
421 struct yang_mapping_node
*mapping
;
422 const struct lysc_node
*snode_native
;
423 const struct lysc_type
*stype_custom
, *stype_native
;
424 char xpath
[XPATH_MAXLEN
];
426 yang_snode_get_path(snode_custom
, YANG_PATH_DATA
, xpath
, sizeof(xpath
));
427 mapping
= yang_mapping_lookup(args
->translator
,
428 YANG_TRANSLATE_TO_NATIVE
, xpath
);
430 flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD
,
431 "%s: missing mapping for \"%s\"", __func__
, xpath
);
433 return YANG_ITER_CONTINUE
;
437 lys_find_path(ly_native_ctx
, NULL
, mapping
->xpath_to_fmt
, 0);
438 assert(snode_native
);
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
) {
444 if (stype_custom
->basetype
!= stype_native
->basetype
) {
446 EC_LIB_YANG_TRANSLATOR_LOAD
,
447 "%s: YANG types are incompatible (xpath: \"%s\")",
450 return YANG_ITER_CONTINUE
;
453 /* TODO: check if the value spaces are identical. */
456 return YANG_ITER_CONTINUE
;
460 * Check if the modules from the translator have a mapping for all of their
461 * schema nodes (after loading the deviations).
463 static unsigned int yang_translator_validate(struct yang_translator
*translator
)
465 struct yang_tmodule
*tmodule
;
467 struct translator_validate_args args
;
469 args
.translator
= translator
;
472 for (ALL_LIST_ELEMENTS_RO(translator
->modules
, ln
, tmodule
)) {
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
,
483 EC_LIB_YANG_TRANSLATOR_LOAD
,
484 "%s: failed to validate \"%s\" module translator: %u error(s)",
485 __func__
, translator
->family
, args
.errors
);
490 static int yang_module_nodes_count_cb(const struct lysc_node
*snode
, void *arg
)
492 unsigned int *total
= arg
;
496 return YANG_ITER_CONTINUE
;
499 /* Calculate the number of nodes for the given module. */
500 static unsigned int yang_module_nodes_count(const struct lys_module
*module
)
502 unsigned int total
= 0;
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
,
513 void yang_translator_init(void)
515 ly_translator_ctx
= yang_ctx_new_setup(true, false);
516 if (!ly_translator_ctx
) {
517 flog_err(EC_LIB_LIBYANG
, "%s: ly_ctx_new() failed", __func__
);
521 if (!ly_ctx_load_module(ly_translator_ctx
, "frr-module-translator",
524 EC_LIB_YANG_MODULE_LOAD
,
525 "%s: failed to load the \"frr-module-translator\" module",
531 void yang_translator_terminate(void)
533 while (!RB_EMPTY(yang_translators
, &yang_translators
)) {
534 struct yang_translator
*translator
;
536 translator
= RB_ROOT(yang_translators
, &yang_translators
);
537 yang_translator_unload(translator
);
540 ly_ctx_destroy(ly_translator_ctx
);