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