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