From b67e048c6b17c46a815a82ffdd3276eae89283d0 Mon Sep 17 00:00:00 2001 From: Christian Hopps Date: Mon, 30 Jun 2025 15:48:03 -0400 Subject: [PATCH 1/2] tools: generate python callbacks --- tools/gen_northbound_callbacks.c | 224 +++++++++++++------------------ 1 file changed, 94 insertions(+), 130 deletions(-) diff --git a/tools/gen_northbound_callbacks.c b/tools/gen_northbound_callbacks.c index 208f19406d23..a88921bbe0b7 100644 --- a/tools/gen_northbound_callbacks.c +++ b/tools/gen_northbound_callbacks.c @@ -199,15 +199,15 @@ static void generate_config_write_cb_name(const struct lysc_node *snode, } static void generate_prototype(const struct nb_callback_info *ncinfo, - const char *cb_name) + const char *cb_name) { - printf("%s%s(%s);\n", ncinfo->return_type, cb_name, ncinfo->arguments); + /* prototypes are unnecessary in Python */ } static void generate_config_write_prototype(const struct nb_callback_info *ncinfo, - const char *cb_name) + const char *cb_name) { - printf("%s%s(%s);\n", ncinfo->return_type, cb_name, ncinfo->arguments); + /* prototypes are unnecessary in Python */ } static int generate_prototypes(const struct lysc_node *snode, void *arg) @@ -257,46 +257,41 @@ static int generate_prototypes(const struct lysc_node *snode, void *arg) return YANG_ITER_CONTINUE; } -static void generate_callback(const struct nb_callback_info *ncinfo, - const char *cb_name) +static const char *python_return_value(const char *ret) { - printf("%s%s%s(%s)\n{\n", f_static_cbs ? "static " : "", ncinfo->return_type, cb_name, - ncinfo->arguments); - - switch (ncinfo->operation) { - case NB_CB_CREATE: - case NB_CB_MODIFY: - case NB_CB_DESTROY: - case NB_CB_MOVE: - printf("\tswitch (args->event) {\n" - "\tcase NB_EV_VALIDATE:\n" - "\tcase NB_EV_PREPARE:\n" - "\tcase NB_EV_ABORT:\n" - "\tcase NB_EV_APPLY:\n" - "\t\t/* TODO: implement me. */\n" - "\t\tbreak;\n" - "\t}\n\n" - ); - break; - - default: - printf("\t/* TODO: implement me. */\n"); - break; - } + if (strcmp(ret, "NULL") == 0) + return "None"; + return ret; +} - printf("\treturn %s;\n}\n\n", ncinfo->return_value); +static void generate_callback(const struct nb_callback_info *ncinfo, + const char *cb_name) +{ + printf("def %s(args):\n", cb_name); + printf("\t\"\"\"TODO: implement me.\"\"\"\n"); + + switch (ncinfo->operation) { + case NB_CB_CREATE: + case NB_CB_MODIFY: + case NB_CB_DESTROY: + case NB_CB_MOVE: + printf("\t# handle args.event here\n"); + break; + default: + break; + } + + if (ncinfo->return_value[0]) + printf("\treturn %s\n\n", python_return_value(ncinfo->return_value)); + else + printf("\n"); } static void generate_config_write_callback(const struct nb_callback_info *ncinfo, - const char *cb_name) + const char *cb_name) { - printf("%s%s%s(%s)\n{\n", f_static_cbs ? "static " : "", ncinfo->return_type, cb_name, - ncinfo->arguments); - - /* Add a comment, since these callbacks may not all be needed. */ - printf("\t/* TODO: this cli callback is optional; the cli output may not need to be done at each node. */\n"); - - printf("}\n\n"); + printf("def %s(vty, dnode, show_defaults):\n", cb_name); + printf("\t# TODO: this cli callback is optional; the cli output may not need to be done at each node.\n\n"); } static int generate_callbacks(const struct lysc_node *snode, void *arg) @@ -330,12 +325,9 @@ static int generate_callbacks(const struct lysc_node *snode, void *arg) yang_snode_get_path(snode, YANG_PATH_DATA, xpath, sizeof(xpath)); - printf("/*\n" - " * XPath: %s\n" - " */\n", - xpath); - first = false; - } + printf("# XPath: %s\n", xpath); + first = false; + } if (f_new_cbs && cb->operation == NB_CB_GET_NEXT && snode->nodetype == LYS_LEAFLIST) continue; @@ -363,11 +355,11 @@ static int generate_callbacks(const struct lysc_node *snode, void *arg) static int generate_nb_nodes(const struct lysc_node *snode, void *arg) { - bool first = true; - char cb_name[BUFSIZ]; - char xpath[XPATH_MAXLEN]; - bool config_pass = *(bool *)arg; - bool need_config_write = true; + bool first = true; + char cb_name[BUFSIZ]; + char xpath[XPATH_MAXLEN]; + bool config_pass = *(bool *)arg; + bool need_config_write = true; switch (snode->nodetype) { case LYS_CONTAINER: @@ -391,50 +383,45 @@ static int generate_nb_nodes(const struct lysc_node *snode, void *arg) || !nb_cb_operation_is_valid(cb->operation, snode)) continue; - if (config_pass) { - if (first) { - yang_snode_get_path(snode, YANG_PATH_DATA, xpath, - sizeof(xpath)); - - printf("\t\t{\n" - "\t\t\t.xpath = \"%s\",\n", - xpath); - printf("\t\t\t.cbs = {\n"); - first = false; - } - if (f_new_cbs && cb->operation == NB_CB_GET_NEXT && - snode->nodetype == LYS_LEAFLIST) - continue; - - generate_callback_name(snode, cb->operation, cb_name, - sizeof(cb_name)); - printf("\t\t\t\t.%s = %s,\n", __operation_name(cb->operation), cb_name); - } else if (cb->need_config_write && need_config_write) { - if (first) { - yang_snode_get_path(snode, - YANG_PATH_DATA, - xpath, - sizeof(xpath)); - - printf("\t\t{\n" - "\t\t\t.xpath = \"%s\",\n", - xpath); - printf("\t\t\t.cbs = {\n"); - first = false; - } - - generate_config_write_cb_name(snode, cb_name, - sizeof(cb_name)); - printf("\t\t\t\t.cli_show = %s,\n", cb_name); - - need_config_write = false; - } - } - - if (!first) { - printf("\t\t\t}\n"); - printf("\t\t},\n"); - } + if (config_pass) { + if (first) { + yang_snode_get_path(snode, YANG_PATH_DATA, xpath, + sizeof(xpath)); + + printf(" {\n \"xpath\": \"%s\",\n \"cbs\": {\n", + xpath); + first = false; + } + if (f_new_cbs && cb->operation == NB_CB_GET_NEXT && + snode->nodetype == LYS_LEAFLIST) + continue; + + generate_callback_name(snode, cb->operation, cb_name, + sizeof(cb_name)); + printf(" \"%s\": %s,\n", __operation_name(cb->operation), cb_name); + } else if (cb->need_config_write && need_config_write) { + if (first) { + yang_snode_get_path(snode, + YANG_PATH_DATA, + xpath, + sizeof(xpath)); + + printf(" {\n \"xpath\": \"%s\",\n \"cbs\": {\n", + xpath); + first = false; + } + + generate_config_write_cb_name(snode, cb_name, + sizeof(cb_name)); + printf(" \"cli_show\": %s,\n", cb_name); + + need_config_write = false; + } + } + + if (!first) { + printf(" }\n },\n"); + } return YANG_ITER_CONTINUE; } @@ -515,12 +502,7 @@ int main(int argc, char *argv[]) */ printf("// SPDX-" "License-Identifier: GPL-2.0-or-later\n\n"); - /* Generate callback prototypes. */ - if (!f_static_cbs) { - printf("/* prototypes */\n"); - yang_snodes_iterate(module->info, generate_prototypes, 0, NULL); - printf("\n"); - } + /* Prototypes are not required for Python output. */ /* Generate callback functions. */ yang_snodes_iterate(module->info, generate_callbacks, 0, NULL); @@ -535,37 +517,19 @@ int main(int argc, char *argv[]) * config-output-oriented callbacks. */ - /* Generate frr_yang_module_info array, with config-handling callbacks */ - config_pass = true; - printf("/* clang-format off */\n" - "const struct frr_yang_module_info %s_nb_info = {\n" - "\t.name = \"%s\",\n" - "\t.nodes = {\n", - module_name_underscores, module->name); - yang_snodes_iterate(module->info, generate_nb_nodes, 0, &config_pass); - - /* Emit terminator element */ - printf("\t\t{\n" - "\t\t\t.xpath = NULL,\n" - "\t\t},\n"); - printf("\t}\n" - "};\n"); - - /* Generate second array, with output-oriented callbacks. */ - config_pass = false; - printf("\n/* clang-format off */\n" - "const struct frr_yang_module_info %s_cli_info = {\n" - "\t.name = \"%s\",\n" - "\t.nodes = {\n", - module_name_underscores, module->name); - yang_snodes_iterate(module->info, generate_nb_nodes, 0, &config_pass); - - /* Emit terminator element */ - printf("\t\t{\n" - "\t\t\t.xpath = NULL,\n" - "\t\t},\n"); - printf("\t}\n" - "};\n"); + /* Generate Python structures with callbacks */ + config_pass = true; + printf("%s_nb_info = {\n \"name\": \"%s\",\n \"nodes\": [\n", + module_name_underscores, module->name); + yang_snodes_iterate(module->info, generate_nb_nodes, 0, &config_pass); + printf(" ]\n}\n"); + + /* Generate second array, with output-oriented callbacks. */ + config_pass = false; + printf("\n%s_cli_info = {\n \"name\": \"%s\",\n \"nodes\": [\n", + module_name_underscores, module->name); + yang_snodes_iterate(module->info, generate_nb_nodes, 0, &config_pass); + printf(" ]\n}\n"); /* Cleanup and exit. */ nb_nodes_delete(); From 7254def88b1707bb2978ca5d138ea1416168df02 Mon Sep 17 00:00:00 2001 From: Christian Hopps Date: Mon, 30 Jun 2025 21:40:34 -0400 Subject: [PATCH 2/2] Restore C northbound callback generator and add Python option --- tools/gen_northbound_callbacks.c | 226 +++++++++++++++++++++++-------- 1 file changed, 172 insertions(+), 54 deletions(-) diff --git a/tools/gen_northbound_callbacks.c b/tools/gen_northbound_callbacks.c index a88921bbe0b7..9b0037c00c9a 100644 --- a/tools/gen_northbound_callbacks.c +++ b/tools/gen_northbound_callbacks.c @@ -17,12 +17,14 @@ static bool f_static_cbs; static bool f_new_cbs; +static bool f_python_output; static void __attribute__((noreturn)) usage(int status) { extern const char *__progname; - fprintf(stderr, "usage: %s [-h] [-n] [-s] [-p path]* [LOAD-MODULE ...] MODULE\n", - __progname); + fprintf(stderr, + "usage: %s [-h] [-n] [-s] [-y] [-p path]* [LOAD-MODULE ...] MODULE\n", + __progname); exit(status); } @@ -201,13 +203,19 @@ static void generate_config_write_cb_name(const struct lysc_node *snode, static void generate_prototype(const struct nb_callback_info *ncinfo, const char *cb_name) { - /* prototypes are unnecessary in Python */ + if (f_python_output) + return; + + printf("%s%s(%s);\n", ncinfo->return_type, cb_name, ncinfo->arguments); } static void generate_config_write_prototype(const struct nb_callback_info *ncinfo, const char *cb_name) { - /* prototypes are unnecessary in Python */ + if (f_python_output) + return; + + printf("%s%s(%s);\n", ncinfo->return_type, cb_name, ncinfo->arguments); } static int generate_prototypes(const struct lysc_node *snode, void *arg) @@ -259,39 +267,76 @@ static int generate_prototypes(const struct lysc_node *snode, void *arg) static const char *python_return_value(const char *ret) { - if (strcmp(ret, "NULL") == 0) - return "None"; - return ret; + return strcmp(ret, "NULL") == 0 ? "None" : ret; } static void generate_callback(const struct nb_callback_info *ncinfo, const char *cb_name) { - printf("def %s(args):\n", cb_name); - printf("\t\"\"\"TODO: implement me.\"\"\"\n"); + if (f_python_output) { + printf("def %s(args):\n", cb_name); + printf("\t\"\"\"TODO: implement me.\"\"\"\n"); + + switch (ncinfo->operation) { + case NB_CB_CREATE: + case NB_CB_MODIFY: + case NB_CB_DESTROY: + case NB_CB_MOVE: + printf("\t# handle args.event here\n"); + break; + default: + break; + } + + if (ncinfo->return_value[0]) + printf("\treturn %s\n\n", python_return_value(ncinfo->return_value)); + else + printf("\n"); + return; + } + + printf("%s%s%s(%s)\n{\n", f_static_cbs ? "static " : "", ncinfo->return_type, cb_name, + ncinfo->arguments); switch (ncinfo->operation) { case NB_CB_CREATE: case NB_CB_MODIFY: case NB_CB_DESTROY: case NB_CB_MOVE: - printf("\t# handle args.event here\n"); + printf("\tswitch (args->event) {\n" + "\tcase NB_EV_VALIDATE:\n" + "\tcase NB_EV_PREPARE:\n" + "\tcase NB_EV_ABORT:\n" + "\tcase NB_EV_APPLY:\n" + "\t\t/* TODO: implement me. */\n" + "\t\tbreak;\n" + "\t}\n\n"); break; + default: + printf("\t/* TODO: implement me. */\n"); break; } - if (ncinfo->return_value[0]) - printf("\treturn %s\n\n", python_return_value(ncinfo->return_value)); - else - printf("\n"); + printf("\treturn %s;\n}\n\n", ncinfo->return_value); } static void generate_config_write_callback(const struct nb_callback_info *ncinfo, const char *cb_name) { - printf("def %s(vty, dnode, show_defaults):\n", cb_name); - printf("\t# TODO: this cli callback is optional; the cli output may not need to be done at each node.\n\n"); + if (f_python_output) { + printf("def %s(vty, dnode, show_defaults):\n", cb_name); + printf("\t# TODO: this cli callback is optional; the cli output may not need to be done at each node.\n\n"); + return; + } + + printf("%s%s%s(%s)\n{\n", f_static_cbs ? "static " : "", ncinfo->return_type, cb_name, + ncinfo->arguments); + + /* Add a comment, since these callbacks may not all be needed. */ + printf("\t/* TODO: this cli callback is optional; the cli output may not need to be done at each node. */\n"); + + printf("}\n\n"); } static int generate_callbacks(const struct lysc_node *snode, void *arg) @@ -319,13 +364,19 @@ static int generate_callbacks(const struct lysc_node *snode, void *arg) || !nb_cb_operation_is_valid(cb->operation, snode)) continue; - if (first) { - char xpath[XPATH_MAXLEN]; + if (first) { + char xpath[XPATH_MAXLEN]; - yang_snode_get_path(snode, YANG_PATH_DATA, xpath, - sizeof(xpath)); + yang_snode_get_path(snode, YANG_PATH_DATA, xpath, + sizeof(xpath)); - printf("# XPath: %s\n", xpath); + if (f_python_output) + printf("# XPath: %s\n", xpath); + else + printf("/*\n" + " * XPath: %s\n" + " */\n", + xpath); first = false; } @@ -355,11 +406,11 @@ static int generate_callbacks(const struct lysc_node *snode, void *arg) static int generate_nb_nodes(const struct lysc_node *snode, void *arg) { - bool first = true; - char cb_name[BUFSIZ]; - char xpath[XPATH_MAXLEN]; - bool config_pass = *(bool *)arg; - bool need_config_write = true; + bool first = true; + char cb_name[BUFSIZ]; + char xpath[XPATH_MAXLEN]; + bool config_pass = *(bool *)arg; + bool need_config_write = true; switch (snode->nodetype) { case LYS_CONTAINER: @@ -388,8 +439,15 @@ static int generate_nb_nodes(const struct lysc_node *snode, void *arg) yang_snode_get_path(snode, YANG_PATH_DATA, xpath, sizeof(xpath)); - printf(" {\n \"xpath\": \"%s\",\n \"cbs\": {\n", - xpath); + if (f_python_output) { + printf(" {\n \"xpath\": \"%s\",\n \"cbs\": {\n", + xpath); + } else { + printf("\t\t{\n" + "\t\t\t.xpath = \"%s\",\n", + xpath); + printf("\t\t\t.cbs = {\n"); + } first = false; } if (f_new_cbs && cb->operation == NB_CB_GET_NEXT && @@ -398,7 +456,10 @@ static int generate_nb_nodes(const struct lysc_node *snode, void *arg) generate_callback_name(snode, cb->operation, cb_name, sizeof(cb_name)); - printf(" \"%s\": %s,\n", __operation_name(cb->operation), cb_name); + if (f_python_output) + printf(" \"%s\": %s,\n", __operation_name(cb->operation), cb_name); + else + printf("\t\t\t\t.%s = %s,\n", __operation_name(cb->operation), cb_name); } else if (cb->need_config_write && need_config_write) { if (first) { yang_snode_get_path(snode, @@ -406,21 +467,36 @@ static int generate_nb_nodes(const struct lysc_node *snode, void *arg) xpath, sizeof(xpath)); - printf(" {\n \"xpath\": \"%s\",\n \"cbs\": {\n", - xpath); + if (f_python_output) { + printf(" {\n \"xpath\": \"%s\",\n \"cbs\": {\n", + xpath); + } else { + printf("\t\t{\n" + "\t\t\t.xpath = \"%s\",\n", + xpath); + printf("\t\t\t.cbs = {\n"); + } first = false; } generate_config_write_cb_name(snode, cb_name, sizeof(cb_name)); - printf(" \"cli_show\": %s,\n", cb_name); + if (f_python_output) + printf(" \"cli_show\": %s,\n", cb_name); + else + printf("\t\t\t\t.cli_show = %s,\n", cb_name); need_config_write = false; } - } + } if (!first) { - printf(" }\n },\n"); + if (f_python_output) { + printf(" }\n },\n"); + } else { + printf("\t\t\t}\n"); + printf("\t\t},\n"); + } } return YANG_ITER_CONTINUE; @@ -436,7 +512,7 @@ int main(int argc, char *argv[]) int opt; bool config_pass; - while ((opt = getopt(argc, argv, "hnp:s")) != -1) { + while ((opt = getopt(argc, argv, "hnp:sy")) != -1) { switch (opt) { case 'h': usage(EXIT_SUCCESS); @@ -459,11 +535,14 @@ int main(int argc, char *argv[]) *darr_append(search_paths) = darr_strdup(optarg); break; - case 's': - f_static_cbs = true; - break; - default: - usage(EXIT_FAILURE); + case 's': + f_static_cbs = true; + break; + case 'y': + f_python_output = true; + break; + default: + usage(EXIT_FAILURE); /* NOTREACHED */ } } @@ -502,7 +581,12 @@ int main(int argc, char *argv[]) */ printf("// SPDX-" "License-Identifier: GPL-2.0-or-later\n\n"); - /* Prototypes are not required for Python output. */ + /* Generate callback prototypes. */ + if (!f_static_cbs && !f_python_output) { + printf("/* prototypes */\n"); + yang_snodes_iterate(module->info, generate_prototypes, 0, NULL); + printf("\n"); + } /* Generate callback functions. */ yang_snodes_iterate(module->info, generate_callbacks, 0, NULL); @@ -517,19 +601,53 @@ int main(int argc, char *argv[]) * config-output-oriented callbacks. */ - /* Generate Python structures with callbacks */ - config_pass = true; - printf("%s_nb_info = {\n \"name\": \"%s\",\n \"nodes\": [\n", - module_name_underscores, module->name); - yang_snodes_iterate(module->info, generate_nb_nodes, 0, &config_pass); - printf(" ]\n}\n"); - - /* Generate second array, with output-oriented callbacks. */ - config_pass = false; - printf("\n%s_cli_info = {\n \"name\": \"%s\",\n \"nodes\": [\n", - module_name_underscores, module->name); - yang_snodes_iterate(module->info, generate_nb_nodes, 0, &config_pass); - printf(" ]\n}\n"); + if (f_python_output) { + /* Generate Python structures with callbacks */ + config_pass = true; + printf("%s_nb_info = {\n \"name\": \"%s\",\n \"nodes\": [\n", + module_name_underscores, module->name); + yang_snodes_iterate(module->info, generate_nb_nodes, 0, &config_pass); + printf(" ]\n}\n"); + + /* Generate second array, with output-oriented callbacks. */ + config_pass = false; + printf("\n%s_cli_info = {\n \"name\": \"%s\",\n \"nodes\": [\n", + module_name_underscores, module->name); + yang_snodes_iterate(module->info, generate_nb_nodes, 0, &config_pass); + printf(" ]\n}\n"); + } else { + /* Generate frr_yang_module_info array, with config-handling callbacks */ + config_pass = true; + printf("/* clang-format off */\n" + "const struct frr_yang_module_info %s_nb_info = {\n" + "\t.name = \"%s\",\n" + "\t.nodes = {\n", + module_name_underscores, module->name); + yang_snodes_iterate(module->info, generate_nb_nodes, 0, &config_pass); + + /* Emit terminator element */ + printf("\t\t{\n" + "\t\t\t.xpath = NULL,\n" + "\t\t},\n"); + printf("\t}\n" + "};\n"); + + /* Generate second array, with output-oriented callbacks. */ + config_pass = false; + printf("\n/* clang-format off */\n" + "const struct frr_yang_module_info %s_cli_info = {\n" + "\t.name = \"%s\",\n" + "\t.nodes = {\n", + module_name_underscores, module->name); + yang_snodes_iterate(module->info, generate_nb_nodes, 0, &config_pass); + + /* Emit terminator element */ + printf("\t\t{\n" + "\t\t\t.xpath = NULL,\n" + "\t\t},\n"); + printf("\t}\n" + "};\n"); + } /* Cleanup and exit. */ nb_nodes_delete();