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