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