]>
Commit | Line | Data |
---|---|---|
acddc0ed | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
1c2facd1 RW |
2 | /* |
3 | * Copyright (C) 2018 NetDEF, Inc. | |
4 | * Renato Westphal | |
1c2facd1 RW |
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" | |
f9ce1142 | 14 | #include "frrstr.h" |
1c2facd1 | 15 | |
bf8d3d6a DL |
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"); | |
1c2facd1 RW |
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 | { | |
24 | return strcmp(a->family, b->family); | |
25 | } | |
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); | |
1c2facd1 RW |
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 | { | |
45 | const struct yang_mapping_node *c1 = value1; | |
46 | const struct yang_mapping_node *c2 = value2; | |
47 | ||
48 | return strmatch(c1->xpath_from_canonical, c2->xpath_from_canonical); | |
49 | } | |
50 | ||
d8b87afe | 51 | static unsigned int yang_mapping_hash_key(const void *value) |
1c2facd1 RW |
52 | { |
53 | return string_hash_make(value); | |
54 | } | |
55 | ||
56 | static void *yang_mapping_hash_alloc(void *p) | |
57 | { | |
58 | struct yang_mapping_node *new, *key = p; | |
59 | ||
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)); | |
63 | ||
64 | return new; | |
65 | } | |
66 | ||
67 | static void yang_mapping_hash_free(void *arg) | |
68 | { | |
69 | XFREE(MTYPE_YANG_TRANSLATOR_MAPPING, arg); | |
70 | } | |
71 | ||
72 | static struct yang_mapping_node * | |
73 | yang_mapping_lookup(const struct yang_translator *translator, int dir, | |
74 | const char *xpath) | |
75 | { | |
76 | struct yang_mapping_node s; | |
77 | ||
78 | strlcpy(s.xpath_from_canonical, xpath, sizeof(s.xpath_from_canonical)); | |
79 | return hash_lookup(translator->mappings[dir], &s); | |
80 | } | |
81 | ||
82 | static void yang_mapping_add(struct yang_translator *translator, int dir, | |
3bb513c3 | 83 | const struct lysc_node *snode, |
1c2facd1 RW |
84 | const char *xpath_from_fmt, |
85 | const char *xpath_to_fmt) | |
86 | { | |
87 | struct yang_mapping_node *mapping, s; | |
88 | ||
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)); | |
f9ce1142 QY |
97 | |
98 | const char *keys[] = {"KEY1", "KEY2", "KEY3", "KEY4"}; | |
99 | char *xpfmt; | |
100 | ||
101 | for (unsigned int i = 0; i < array_size(keys); i++) { | |
102 | xpfmt = frrstr_replace(mapping->xpath_from_fmt, keys[i], | |
103 | "%[^']"); | |
104 | strlcpy(mapping->xpath_from_fmt, xpfmt, | |
105 | sizeof(mapping->xpath_from_fmt)); | |
106 | XFREE(MTYPE_TMP, xpfmt); | |
107 | } | |
108 | ||
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); | |
114 | } | |
1c2facd1 RW |
115 | } |
116 | ||
b9941b3f DA |
117 | static void yang_tmodule_delete(struct yang_tmodule *tmodule) |
118 | { | |
119 | XFREE(MTYPE_YANG_TRANSLATOR_MODULE, tmodule); | |
120 | } | |
121 | ||
1c2facd1 RW |
122 | struct yang_translator *yang_translator_load(const char *path) |
123 | { | |
124 | struct yang_translator *translator; | |
b9941b3f | 125 | struct yang_tmodule *tmodule = NULL; |
1c2facd1 RW |
126 | const char *family; |
127 | struct lyd_node *dnode; | |
128 | struct ly_set *set; | |
129 | struct listnode *ln; | |
3bb513c3 | 130 | LY_ERR err; |
1c2facd1 RW |
131 | |
132 | /* Load module translator (JSON file). */ | |
3bb513c3 CH |
133 | err = lyd_parse_data_path(ly_translator_ctx, path, LYD_JSON, |
134 | LYD_PARSE_NO_STATE, LYD_VALIDATE_NO_STATE, | |
135 | &dnode); | |
136 | if (err) { | |
1c2facd1 | 137 | flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD, |
3bb513c3 | 138 | "%s: lyd_parse_path() failed: %d", __func__, err); |
1c2facd1 RW |
139 | return NULL; |
140 | } | |
141 | dnode = yang_dnode_get(dnode, | |
142 | "/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 | assert(dnode); | |
148 | ||
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", | |
154 | __func__, family); | |
ecf82aa4 | 155 | yang_dnode_free(dnode); |
1c2facd1 RW |
156 | return NULL; |
157 | } | |
158 | ||
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); | |
167 | ||
168 | /* Initialize the translator libyang context. */ | |
3bb513c3 | 169 | translator->ly_ctx = yang_ctx_new_setup(false, false); |
1c2facd1 RW |
170 | if (!translator->ly_ctx) { |
171 | flog_warn(EC_LIB_LIBYANG, "%s: ly_ctx_new() failed", __func__); | |
172 | goto error; | |
173 | } | |
1c2facd1 | 174 | |
3bb513c3 CH |
175 | /* Load modules */ |
176 | if (lyd_find_xpath(dnode, "./module", &set) != LY_SUCCESS) | |
177 | assert(0); /* XXX libyang2: old ly1 code asserted success */ | |
178 | ||
179 | for (size_t i = 0; i < set->count; i++) { | |
1c2facd1 RW |
180 | const char *module_name; |
181 | ||
182 | tmodule = | |
183 | XCALLOC(MTYPE_YANG_TRANSLATOR_MODULE, sizeof(*tmodule)); | |
184 | ||
3bb513c3 | 185 | module_name = yang_dnode_get_string(set->dnodes[i], "./name"); |
1c2facd1 | 186 | tmodule->module = ly_ctx_load_module(translator->ly_ctx, |
3bb513c3 | 187 | module_name, NULL, NULL); |
1c2facd1 RW |
188 | if (!tmodule->module) { |
189 | flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD, | |
190 | "%s: failed to load module: %s", __func__, | |
191 | module_name); | |
3bb513c3 | 192 | ly_set_free(set, NULL); |
1c2facd1 RW |
193 | goto error; |
194 | } | |
3bb513c3 | 195 | } |
1c2facd1 | 196 | |
3bb513c3 CH |
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); | |
201 | } | |
202 | ||
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); | |
1c2facd1 RW |
208 | if (!tmodule->deviations) { |
209 | flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD, | |
210 | "%s: failed to load module: %s", __func__, | |
211 | module_name); | |
3bb513c3 | 212 | ly_set_free(set, NULL); |
1c2facd1 RW |
213 | goto error; |
214 | } | |
1c2facd1 | 215 | |
3bb513c3 CH |
216 | tmodule->nodes_after_deviations = |
217 | yang_module_nodes_count(tmodule->module); | |
1c2facd1 | 218 | } |
3bb513c3 | 219 | ly_set_free(set, NULL); |
1c2facd1 RW |
220 | |
221 | /* Calculate the coverage. */ | |
222 | for (ALL_LIST_ELEMENTS_RO(translator->modules, ln, tmodule)) { | |
1c2facd1 RW |
223 | tmodule->coverage = ((double)tmodule->nodes_after_deviations |
224 | / (double)tmodule->nodes_before_deviations) | |
225 | * 100; | |
226 | } | |
227 | ||
228 | /* Load mappings. */ | |
3bb513c3 CH |
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++) { | |
1c2facd1 | 232 | const char *xpath_custom, *xpath_native; |
3bb513c3 CH |
233 | const struct lysc_node *snode_custom, *snode_native; |
234 | ||
235 | xpath_custom = | |
236 | yang_dnode_get_string(set->dnodes[i], "./custom"); | |
1c2facd1 | 237 | |
9e0241c8 CH |
238 | snode_custom = |
239 | yang_find_snode(translator->ly_ctx, xpath_custom, 0); | |
1c2facd1 RW |
240 | if (!snode_custom) { |
241 | flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD, | |
242 | "%s: unknown data path: %s", __func__, | |
243 | xpath_custom); | |
3bb513c3 | 244 | ly_set_free(set, NULL); |
1c2facd1 RW |
245 | goto error; |
246 | } | |
247 | ||
3bb513c3 CH |
248 | xpath_native = |
249 | yang_dnode_get_string(set->dnodes[i], "./native"); | |
9e0241c8 | 250 | snode_native = yang_find_snode(ly_native_ctx, xpath_native, 0); |
1c2facd1 RW |
251 | if (!snode_native) { |
252 | flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD, | |
253 | "%s: unknown data path: %s", __func__, | |
254 | xpath_native); | |
3bb513c3 | 255 | ly_set_free(set, NULL); |
1c2facd1 RW |
256 | goto error; |
257 | } | |
258 | ||
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); | |
263 | } | |
3bb513c3 | 264 | ly_set_free(set, NULL); |
1c2facd1 RW |
265 | |
266 | /* Validate mappings. */ | |
267 | if (yang_translator_validate(translator) != 0) | |
268 | goto error; | |
269 | ||
270 | yang_dnode_free(dnode); | |
271 | ||
272 | return translator; | |
273 | ||
274 | error: | |
275 | yang_dnode_free(dnode); | |
276 | yang_translator_unload(translator); | |
b9941b3f | 277 | yang_tmodule_delete(tmodule); |
1c2facd1 RW |
278 | |
279 | return NULL; | |
280 | } | |
281 | ||
1c2facd1 RW |
282 | void yang_translator_unload(struct yang_translator *translator) |
283 | { | |
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); | |
3bb513c3 | 288 | ly_ctx_destroy(translator->ly_ctx); |
1c2facd1 RW |
289 | RB_REMOVE(yang_translators, &yang_translators, translator); |
290 | XFREE(MTYPE_YANG_TRANSLATOR, translator); | |
291 | } | |
292 | ||
293 | struct yang_translator *yang_translator_find(const char *family) | |
294 | { | |
295 | struct yang_translator s; | |
296 | ||
297 | strlcpy(s.family, family, sizeof(s.family)); | |
298 | return RB_FIND(yang_translators, &yang_translators, &s); | |
299 | } | |
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 | { | |
305 | struct ly_ctx *ly_ctx; | |
3bb513c3 | 306 | const struct lysc_node *snode; |
1c2facd1 RW |
307 | struct yang_mapping_node *mapping; |
308 | char xpath_canonical[XPATH_MAXLEN]; | |
309 | char keys[4][LIST_MAXKEYLEN]; | |
310 | int n; | |
311 | ||
312 | if (dir == YANG_TRANSLATE_TO_NATIVE) | |
313 | ly_ctx = translator->ly_ctx; | |
314 | else | |
315 | ly_ctx = ly_native_ctx; | |
316 | ||
9e0241c8 | 317 | snode = yang_find_snode(ly_ctx, xpath, 0); |
1c2facd1 RW |
318 | if (!snode) { |
319 | flog_warn(EC_LIB_YANG_TRANSLATION_ERROR, | |
320 | "%s: unknown data path: %s", __func__, xpath); | |
321 | return YANG_TRANSLATE_FAILURE; | |
322 | } | |
323 | ||
324 | yang_snode_get_path(snode, YANG_PATH_DATA, xpath_canonical, | |
325 | sizeof(xpath_canonical)); | |
326 | mapping = yang_mapping_lookup(translator, dir, xpath_canonical); | |
327 | if (!mapping) | |
328 | return YANG_TRANSLATE_NOTFOUND; | |
329 | ||
c84e5187 DL |
330 | #pragma GCC diagnostic push |
331 | #pragma GCC diagnostic ignored "-Wformat-nonliteral" | |
332 | /* processing format strings from mapping node... */ | |
1c2facd1 RW |
333 | n = sscanf(xpath, mapping->xpath_from_fmt, keys[0], keys[1], keys[2], |
334 | keys[3]); | |
c84e5187 | 335 | #pragma GCC diagnostic pop |
1c2facd1 RW |
336 | if (n < 0) { |
337 | flog_warn(EC_LIB_YANG_TRANSLATION_ERROR, | |
338 | "%s: sscanf() failed: %s", __func__, | |
339 | safe_strerror(errno)); | |
340 | return YANG_TRANSLATE_FAILURE; | |
341 | } | |
342 | ||
c84e5187 DL |
343 | #pragma GCC diagnostic push |
344 | #pragma GCC diagnostic ignored "-Wformat-nonliteral" | |
345 | /* processing format strings from mapping node... */ | |
1c2facd1 RW |
346 | snprintf(xpath, xpath_len, mapping->xpath_to_fmt, keys[0], keys[1], |
347 | keys[2], keys[3]); | |
c84e5187 | 348 | #pragma GCC diagnostic pop |
1c2facd1 RW |
349 | |
350 | return YANG_TRANSLATE_SUCCESS; | |
351 | } | |
352 | ||
353 | int yang_translate_dnode(const struct yang_translator *translator, int dir, | |
354 | struct lyd_node **dnode) | |
355 | { | |
356 | struct ly_ctx *ly_ctx; | |
357 | struct lyd_node *new; | |
3bb513c3 | 358 | struct lyd_node *root, *dnode_iter; |
1c2facd1 RW |
359 | |
360 | /* Create new libyang data node to hold the translated data. */ | |
361 | if (dir == YANG_TRANSLATE_TO_NATIVE) | |
362 | ly_ctx = ly_native_ctx; | |
363 | else | |
364 | ly_ctx = translator->ly_ctx; | |
5e02643a | 365 | new = yang_dnode_new(ly_ctx, false); |
1c2facd1 RW |
366 | |
367 | /* Iterate over all nodes from the data tree. */ | |
3bb513c3 CH |
368 | LY_LIST_FOR (*dnode, root) { |
369 | LYD_TREE_DFS_BEGIN (root, dnode_iter) { | |
1c2facd1 RW |
370 | char xpath[XPATH_MAXLEN]; |
371 | enum yang_translate_result ret; | |
372 | ||
373 | yang_dnode_get_path(dnode_iter, xpath, sizeof(xpath)); | |
374 | ret = yang_translate_xpath(translator, dir, xpath, | |
375 | sizeof(xpath)); | |
376 | switch (ret) { | |
377 | case YANG_TRANSLATE_SUCCESS: | |
378 | break; | |
379 | case YANG_TRANSLATE_NOTFOUND: | |
380 | goto next; | |
381 | case YANG_TRANSLATE_FAILURE: | |
382 | goto error; | |
383 | } | |
384 | ||
385 | /* Create new node in the tree of translated data. */ | |
3bb513c3 CH |
386 | if (lyd_new_path(new, ly_ctx, xpath, |
387 | (void *)yang_dnode_get_string( | |
388 | dnode_iter, NULL), | |
389 | LYD_NEW_PATH_UPDATE, NULL)) { | |
1c2facd1 RW |
390 | flog_err(EC_LIB_LIBYANG, |
391 | "%s: lyd_new_path() failed", __func__); | |
392 | goto error; | |
393 | } | |
394 | ||
395 | next: | |
3bb513c3 | 396 | LYD_TREE_DFS_END(root, dnode_iter); |
1c2facd1 RW |
397 | } |
398 | } | |
399 | ||
400 | /* Replace dnode by the new translated dnode. */ | |
401 | yang_dnode_free(*dnode); | |
402 | *dnode = new; | |
403 | ||
404 | return YANG_TRANSLATE_SUCCESS; | |
405 | ||
406 | error: | |
407 | yang_dnode_free(new); | |
408 | ||
409 | return YANG_TRANSLATE_FAILURE; | |
410 | } | |
411 | ||
e0ccfad2 RW |
412 | struct translator_validate_args { |
413 | struct yang_translator *translator; | |
414 | unsigned int errors; | |
415 | }; | |
416 | ||
3bb513c3 | 417 | static int yang_translator_validate_cb(const struct lysc_node *snode_custom, |
e0ccfad2 | 418 | void *arg) |
1c2facd1 | 419 | { |
e0ccfad2 | 420 | struct translator_validate_args *args = arg; |
1c2facd1 | 421 | struct yang_mapping_node *mapping; |
3bb513c3 CH |
422 | const struct lysc_node *snode_native; |
423 | const struct lysc_type *stype_custom, *stype_native; | |
1c2facd1 RW |
424 | char xpath[XPATH_MAXLEN]; |
425 | ||
426 | yang_snode_get_path(snode_custom, YANG_PATH_DATA, xpath, sizeof(xpath)); | |
e0ccfad2 RW |
427 | mapping = yang_mapping_lookup(args->translator, |
428 | YANG_TRANSLATE_TO_NATIVE, xpath); | |
1c2facd1 RW |
429 | if (!mapping) { |
430 | flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD, | |
431 | "%s: missing mapping for \"%s\"", __func__, xpath); | |
e0ccfad2 RW |
432 | args->errors += 1; |
433 | return YANG_ITER_CONTINUE; | |
1c2facd1 RW |
434 | } |
435 | ||
436 | snode_native = | |
3bb513c3 | 437 | lys_find_path(ly_native_ctx, NULL, mapping->xpath_to_fmt, 0); |
1c2facd1 RW |
438 | assert(snode_native); |
439 | ||
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) { | |
3bb513c3 | 444 | if (stype_custom->basetype != stype_native->basetype) { |
1c2facd1 RW |
445 | flog_warn( |
446 | EC_LIB_YANG_TRANSLATOR_LOAD, | |
447 | "%s: YANG types are incompatible (xpath: \"%s\")", | |
448 | __func__, xpath); | |
e0ccfad2 RW |
449 | args->errors += 1; |
450 | return YANG_ITER_CONTINUE; | |
1c2facd1 RW |
451 | } |
452 | ||
453 | /* TODO: check if the value spaces are identical. */ | |
454 | } | |
e0ccfad2 RW |
455 | |
456 | return YANG_ITER_CONTINUE; | |
1c2facd1 RW |
457 | } |
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 | { | |
465 | struct yang_tmodule *tmodule; | |
466 | struct listnode *ln; | |
e0ccfad2 RW |
467 | struct translator_validate_args args; |
468 | ||
469 | args.translator = translator; | |
470 | args.errors = 0; | |
1c2facd1 RW |
471 | |
472 | for (ALL_LIST_ELEMENTS_RO(translator->modules, ln, tmodule)) { | |
8d869d37 RW |
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, | |
478 | &args); | |
1c2facd1 RW |
479 | } |
480 | ||
e0ccfad2 | 481 | if (args.errors) |
1c2facd1 RW |
482 | flog_warn( |
483 | EC_LIB_YANG_TRANSLATOR_LOAD, | |
484 | "%s: failed to validate \"%s\" module translator: %u error(s)", | |
e0ccfad2 | 485 | __func__, translator->family, args.errors); |
1c2facd1 | 486 | |
e0ccfad2 | 487 | return args.errors; |
1c2facd1 RW |
488 | } |
489 | ||
3bb513c3 | 490 | static int yang_module_nodes_count_cb(const struct lysc_node *snode, void *arg) |
1c2facd1 | 491 | { |
e0ccfad2 | 492 | unsigned int *total = arg; |
1c2facd1 RW |
493 | |
494 | *total += 1; | |
e0ccfad2 RW |
495 | |
496 | return YANG_ITER_CONTINUE; | |
1c2facd1 RW |
497 | } |
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 | { | |
502 | unsigned int total = 0; | |
503 | ||
8d869d37 RW |
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, | |
508 | &total); | |
1c2facd1 RW |
509 | |
510 | return total; | |
511 | } | |
512 | ||
1c2facd1 RW |
513 | void yang_translator_init(void) |
514 | { | |
3bb513c3 | 515 | ly_translator_ctx = yang_ctx_new_setup(true, false); |
1c2facd1 RW |
516 | if (!ly_translator_ctx) { |
517 | flog_err(EC_LIB_LIBYANG, "%s: ly_ctx_new() failed", __func__); | |
518 | exit(1); | |
519 | } | |
1c2facd1 RW |
520 | |
521 | if (!ly_ctx_load_module(ly_translator_ctx, "frr-module-translator", | |
3bb513c3 | 522 | NULL, NULL)) { |
1c2facd1 RW |
523 | flog_err( |
524 | EC_LIB_YANG_MODULE_LOAD, | |
525 | "%s: failed to load the \"frr-module-translator\" module", | |
526 | __func__); | |
527 | exit(1); | |
528 | } | |
529 | } | |
530 | ||
531 | void yang_translator_terminate(void) | |
532 | { | |
533 | while (!RB_EMPTY(yang_translators, &yang_translators)) { | |
534 | struct yang_translator *translator; | |
535 | ||
536 | translator = RB_ROOT(yang_translators, &yang_translators); | |
537 | yang_translator_unload(translator); | |
538 | } | |
539 | ||
3bb513c3 | 540 | ly_ctx_destroy(ly_translator_ctx); |
1c2facd1 | 541 | } |