]>
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 | #define REALLY_NEED_PLAIN_GETOPT 1 | |
21 | ||
22 | #include <zebra.h> | |
23 | ||
24 | #include <unistd.h> | |
25 | ||
26 | #include "yang.h" | |
27 | #include "northbound.h" | |
28 | ||
f5f0a0e3 RW |
29 | static bool static_cbs; |
30 | ||
1c2facd1 RW |
31 | static void __attribute__((noreturn)) usage(int status) |
32 | { | |
88292d69 | 33 | extern const char *__progname; |
f5f0a0e3 | 34 | fprintf(stderr, "usage: %s [-h] [-s] [-p path] MODULE\n", __progname); |
1c2facd1 RW |
35 | exit(status); |
36 | } | |
37 | ||
38 | static struct nb_callback_info { | |
39 | int operation; | |
40 | bool optional; | |
41 | char return_type[32]; | |
42 | char return_value[32]; | |
43 | char arguments[128]; | |
44 | } nb_callbacks[] = { | |
45 | { | |
46 | .operation = NB_OP_CREATE, | |
47 | .return_type = "int ", | |
48 | .return_value = "NB_OK", | |
49 | .arguments = | |
50 | "enum nb_event event, const struct lyd_node *dnode, union nb_resource *resource", | |
51 | }, | |
52 | { | |
53 | .operation = NB_OP_MODIFY, | |
54 | .return_type = "int ", | |
55 | .return_value = "NB_OK", | |
56 | .arguments = | |
57 | "enum nb_event event, const struct lyd_node *dnode, union nb_resource *resource", | |
58 | }, | |
59 | { | |
95ce849b | 60 | .operation = NB_OP_DESTROY, |
1c2facd1 RW |
61 | .return_type = "int ", |
62 | .return_value = "NB_OK", | |
63 | .arguments = | |
64 | "enum nb_event event, const struct lyd_node *dnode", | |
65 | }, | |
66 | { | |
67 | .operation = NB_OP_MOVE, | |
68 | .return_type = "int ", | |
69 | .return_value = "NB_OK", | |
70 | .arguments = | |
71 | "enum nb_event event, const struct lyd_node *dnode", | |
72 | }, | |
73 | { | |
74 | .operation = NB_OP_APPLY_FINISH, | |
75 | .optional = true, | |
76 | .return_type = "void ", | |
77 | .return_value = "", | |
78 | .arguments = "const struct lyd_node *dnode", | |
79 | }, | |
80 | { | |
81 | .operation = NB_OP_GET_ELEM, | |
82 | .return_type = "struct yang_data *", | |
83 | .return_value = "NULL", | |
84 | .arguments = "const char *xpath, const void *list_entry", | |
85 | }, | |
86 | { | |
87 | .operation = NB_OP_GET_NEXT, | |
88 | .return_type = "const void *", | |
89 | .return_value = "NULL", | |
1a4bc045 RW |
90 | .arguments = |
91 | "const void *parent_list_entry, const void *list_entry", | |
1c2facd1 RW |
92 | }, |
93 | { | |
94 | .operation = NB_OP_GET_KEYS, | |
95 | .return_type = "int ", | |
96 | .return_value = "NB_OK", | |
1a4bc045 RW |
97 | .arguments = |
98 | "const void *list_entry, struct yang_list_keys *keys", | |
1c2facd1 RW |
99 | }, |
100 | { | |
101 | .operation = NB_OP_LOOKUP_ENTRY, | |
102 | .return_type = "const void *", | |
103 | .return_value = "NULL", | |
1a4bc045 RW |
104 | .arguments = |
105 | "const void *parent_list_entry, const struct yang_list_keys *keys", | |
1c2facd1 RW |
106 | }, |
107 | { | |
108 | .operation = NB_OP_RPC, | |
109 | .return_type = "int ", | |
110 | .return_value = "NB_OK", | |
111 | .arguments = | |
112 | "const char *xpath, const struct list *input, struct list *output", | |
113 | }, | |
114 | { | |
115 | /* sentinel */ | |
116 | .operation = -1, | |
117 | }, | |
118 | }; | |
119 | ||
120 | static void replace_hyphens_by_underscores(char *str) | |
121 | { | |
122 | char *p; | |
123 | ||
124 | p = str; | |
125 | while ((p = strchr(p, '-')) != NULL) | |
126 | *p++ = '_'; | |
127 | } | |
128 | ||
129 | static void generate_callback_name(struct lys_node *snode, | |
130 | enum nb_operation operation, char *buffer, | |
131 | size_t size) | |
132 | { | |
133 | struct list *snodes; | |
134 | struct listnode *ln; | |
135 | ||
136 | snodes = list_new(); | |
137 | for (; snode; snode = lys_parent(snode)) { | |
138 | /* Skip schema-only snodes. */ | |
db452508 RW |
139 | if (CHECK_FLAG(snode->nodetype, LYS_USES | LYS_CHOICE | LYS_CASE |
140 | | LYS_INPUT | |
141 | | LYS_OUTPUT)) | |
1c2facd1 RW |
142 | continue; |
143 | ||
144 | listnode_add_head(snodes, snode); | |
145 | } | |
146 | ||
147 | memset(buffer, 0, size); | |
148 | for (ALL_LIST_ELEMENTS_RO(snodes, ln, snode)) { | |
149 | strlcat(buffer, snode->name, size); | |
150 | strlcat(buffer, "_", size); | |
151 | } | |
152 | strlcat(buffer, nb_operation_name(operation), size); | |
153 | list_delete(&snodes); | |
154 | ||
155 | replace_hyphens_by_underscores(buffer); | |
156 | } | |
157 | ||
f5f0a0e3 RW |
158 | static void generate_prototype(const struct nb_callback_info *ncinfo, |
159 | const char *cb_name) | |
160 | { | |
161 | printf("%s%s(%s);\n", ncinfo->return_type, cb_name, ncinfo->arguments); | |
162 | } | |
163 | ||
164 | static int generate_prototypes(const struct lys_node *snode, void *arg) | |
165 | { | |
166 | switch (snode->nodetype) { | |
167 | case LYS_CONTAINER: | |
168 | case LYS_LEAF: | |
169 | case LYS_LEAFLIST: | |
170 | case LYS_LIST: | |
171 | case LYS_NOTIF: | |
172 | case LYS_RPC: | |
173 | break; | |
174 | default: | |
175 | return YANG_ITER_CONTINUE; | |
176 | } | |
177 | ||
178 | for (struct nb_callback_info *cb = &nb_callbacks[0]; | |
179 | cb->operation != -1; cb++) { | |
180 | char cb_name[BUFSIZ]; | |
181 | ||
182 | if (cb->optional | |
183 | || !nb_operation_is_valid(cb->operation, snode)) | |
184 | continue; | |
185 | ||
186 | generate_callback_name((struct lys_node *)snode, cb->operation, | |
187 | cb_name, sizeof(cb_name)); | |
188 | generate_prototype(cb, cb_name); | |
189 | } | |
190 | ||
191 | return YANG_ITER_CONTINUE; | |
192 | } | |
193 | ||
c681616f RZ |
194 | static void generate_callback(const struct nb_callback_info *ncinfo, |
195 | const char *cb_name) | |
196 | { | |
f5f0a0e3 | 197 | printf("%s%s%s(%s)\n{\n", static_cbs ? "static " : "", |
c681616f RZ |
198 | ncinfo->return_type, cb_name, ncinfo->arguments); |
199 | ||
200 | switch (ncinfo->operation) { | |
201 | case NB_OP_CREATE: | |
202 | case NB_OP_MODIFY: | |
203 | case NB_OP_DESTROY: | |
204 | case NB_OP_MOVE: | |
205 | printf("\tswitch (event) {\n" | |
206 | "\tcase NB_EV_VALIDATE:\n" | |
207 | "\tcase NB_EV_PREPARE:\n" | |
208 | "\tcase NB_EV_ABORT:\n" | |
209 | "\tcase NB_EV_APPLY:\n" | |
210 | "\t\t/* TODO: implement me. */\n" | |
211 | "\t\tbreak;\n" | |
212 | "\t}\n\n" | |
213 | ); | |
214 | break; | |
215 | ||
216 | default: | |
217 | printf("\t/* TODO: implement me. */\n"); | |
218 | break; | |
219 | } | |
220 | ||
221 | printf("\treturn %s;\n}\n\n", ncinfo->return_value); | |
222 | } | |
223 | ||
e0ccfad2 | 224 | static int generate_callbacks(const struct lys_node *snode, void *arg) |
1c2facd1 RW |
225 | { |
226 | bool first = true; | |
227 | ||
228 | switch (snode->nodetype) { | |
229 | case LYS_CONTAINER: | |
230 | case LYS_LEAF: | |
231 | case LYS_LEAFLIST: | |
232 | case LYS_LIST: | |
233 | case LYS_NOTIF: | |
234 | case LYS_RPC: | |
235 | break; | |
236 | default: | |
e0ccfad2 | 237 | return YANG_ITER_CONTINUE; |
1c2facd1 RW |
238 | } |
239 | ||
240 | for (struct nb_callback_info *cb = &nb_callbacks[0]; | |
241 | cb->operation != -1; cb++) { | |
242 | char cb_name[BUFSIZ]; | |
243 | ||
244 | if (cb->optional | |
245 | || !nb_operation_is_valid(cb->operation, snode)) | |
246 | continue; | |
247 | ||
248 | if (first) { | |
249 | char xpath[XPATH_MAXLEN]; | |
250 | ||
251 | yang_snode_get_path(snode, YANG_PATH_DATA, xpath, | |
252 | sizeof(xpath)); | |
253 | ||
254 | printf("/*\n" | |
255 | " * XPath: %s\n" | |
256 | " */\n", | |
257 | xpath); | |
258 | first = false; | |
259 | } | |
260 | ||
261 | generate_callback_name((struct lys_node *)snode, cb->operation, | |
262 | cb_name, sizeof(cb_name)); | |
c681616f | 263 | generate_callback(cb, cb_name); |
1c2facd1 | 264 | } |
e0ccfad2 RW |
265 | |
266 | return YANG_ITER_CONTINUE; | |
1c2facd1 RW |
267 | } |
268 | ||
e0ccfad2 | 269 | static int generate_nb_nodes(const struct lys_node *snode, void *arg) |
1c2facd1 RW |
270 | { |
271 | bool first = true; | |
272 | ||
273 | switch (snode->nodetype) { | |
274 | case LYS_CONTAINER: | |
275 | case LYS_LEAF: | |
276 | case LYS_LEAFLIST: | |
277 | case LYS_LIST: | |
278 | case LYS_NOTIF: | |
279 | case LYS_RPC: | |
280 | break; | |
281 | default: | |
e0ccfad2 | 282 | return YANG_ITER_CONTINUE; |
1c2facd1 RW |
283 | } |
284 | ||
285 | for (struct nb_callback_info *cb = &nb_callbacks[0]; | |
286 | cb->operation != -1; cb++) { | |
287 | char cb_name[BUFSIZ]; | |
288 | ||
289 | if (cb->optional | |
290 | || !nb_operation_is_valid(cb->operation, snode)) | |
291 | continue; | |
292 | ||
293 | if (first) { | |
294 | char xpath[XPATH_MAXLEN]; | |
295 | ||
296 | yang_snode_get_path(snode, YANG_PATH_DATA, xpath, | |
297 | sizeof(xpath)); | |
298 | ||
299 | printf("\t\t{\n" | |
300 | "\t\t\t.xpath = \"%s\",\n", | |
301 | xpath); | |
13080468 | 302 | printf("\t\t\t.cbs = {\n"); |
1c2facd1 RW |
303 | first = false; |
304 | } | |
305 | ||
306 | generate_callback_name((struct lys_node *)snode, cb->operation, | |
307 | cb_name, sizeof(cb_name)); | |
13080468 RZ |
308 | printf("\t\t\t\t.%s = %s,\n", nb_operation_name(cb->operation), |
309 | cb_name); | |
1c2facd1 RW |
310 | } |
311 | ||
13080468 RZ |
312 | if (!first) { |
313 | printf("\t\t\t}\n"); | |
1c2facd1 | 314 | printf("\t\t},\n"); |
13080468 | 315 | } |
e0ccfad2 RW |
316 | |
317 | return YANG_ITER_CONTINUE; | |
1c2facd1 RW |
318 | } |
319 | ||
320 | int main(int argc, char *argv[]) | |
321 | { | |
88292d69 | 322 | const char *search_path = NULL; |
1c2facd1 RW |
323 | struct yang_module *module; |
324 | char module_name_underscores[64]; | |
88292d69 | 325 | struct stat st; |
1c2facd1 RW |
326 | int opt; |
327 | ||
f5f0a0e3 | 328 | while ((opt = getopt(argc, argv, "hp:s")) != -1) { |
1c2facd1 RW |
329 | switch (opt) { |
330 | case 'h': | |
331 | usage(EXIT_SUCCESS); | |
332 | /* NOTREACHED */ | |
88292d69 RZ |
333 | case 'p': |
334 | if (stat(optarg, &st) == -1) { | |
335 | fprintf(stderr, | |
336 | "error: invalid search path '%s': %s\n", | |
337 | optarg, strerror(errno)); | |
338 | exit(EXIT_FAILURE); | |
339 | } | |
340 | if (S_ISDIR(st.st_mode) == 0) { | |
341 | fprintf(stderr, | |
342 | "error: search path is not directory"); | |
343 | exit(EXIT_FAILURE); | |
344 | } | |
345 | ||
346 | search_path = optarg; | |
347 | break; | |
f5f0a0e3 RW |
348 | case 's': |
349 | static_cbs = true; | |
350 | break; | |
1c2facd1 RW |
351 | default: |
352 | usage(EXIT_FAILURE); | |
353 | /* NOTREACHED */ | |
354 | } | |
355 | } | |
356 | argc -= optind; | |
357 | argv += optind; | |
358 | if (argc != 1) | |
359 | usage(EXIT_FAILURE); | |
360 | ||
b90204a8 | 361 | yang_init(false); |
1c2facd1 | 362 | |
88292d69 RZ |
363 | if (search_path) |
364 | ly_ctx_set_searchdir(ly_native_ctx, search_path); | |
365 | ||
544ca69a RW |
366 | /* Load all FRR native models to ensure all augmentations are loaded. */ |
367 | yang_module_load_all(); | |
368 | module = yang_module_find(argv[0]); | |
369 | if (!module) | |
370 | /* Non-native FRR module (e.g. modules from unit tests). */ | |
371 | module = yang_module_load(argv[0]); | |
372 | ||
373 | /* Create a nb_node for all YANG schema nodes. */ | |
374 | nb_nodes_create(); | |
1c2facd1 | 375 | |
f5f0a0e3 RW |
376 | /* Generate callback prototypes. */ |
377 | if (!static_cbs) { | |
378 | printf("/* prototypes */\n"); | |
379 | yang_snodes_iterate_module(module->info, generate_prototypes, 0, | |
380 | NULL); | |
381 | printf("\n"); | |
382 | } | |
383 | ||
1c2facd1 | 384 | /* Generate callback functions. */ |
e0ccfad2 | 385 | yang_snodes_iterate_module(module->info, generate_callbacks, 0, NULL); |
1c2facd1 RW |
386 | |
387 | strlcpy(module_name_underscores, module->name, | |
388 | sizeof(module_name_underscores)); | |
389 | replace_hyphens_by_underscores(module_name_underscores); | |
390 | ||
391 | /* Generate frr_yang_module_info array. */ | |
392 | printf("/* clang-format off */\n" | |
393 | "const struct frr_yang_module_info %s_info = {\n" | |
394 | "\t.name = \"%s\",\n" | |
395 | "\t.nodes = {\n", | |
396 | module_name_underscores, module->name); | |
e0ccfad2 | 397 | yang_snodes_iterate_module(module->info, generate_nb_nodes, 0, NULL); |
1c2facd1 RW |
398 | printf("\t\t{\n" |
399 | "\t\t\t.xpath = NULL,\n" | |
400 | "\t\t},\n"); | |
401 | printf("\t}\n" | |
402 | "};\n"); | |
403 | ||
404 | /* Cleanup and exit. */ | |
544ca69a | 405 | nb_nodes_delete(); |
1c2facd1 RW |
406 | yang_terminate(); |
407 | ||
408 | return 0; | |
409 | } |