+def generate_include_enum(s, module, stream):
+ """Generate <name>.api_enum.h"""
+ write = stream.write
+
+ if "Define" in s:
+ write("typedef enum {\n")
+ for t in s["Define"]:
+ write(" VL_API_{},\n".format(t.name.upper()))
+ write(" VL_MSG_{}_LAST\n".format(module.upper()))
+ write("}} vl_api_{}_enum_t;\n".format(module))
+
+
+def generate_include_counters(s, stream):
+ """Include file for the counter data model types."""
+ write = stream.write
+
+ for counters in s:
+ csetname = counters.name
+ write("typedef enum {\n")
+ for c in counters.block:
+ write(" {}_ERROR_{},\n".format(csetname.upper(), c["name"].upper()))
+ write(" {}_N_ERROR\n".format(csetname.upper()))
+ write("}} vl_counter_{}_enum_t;\n".format(csetname))
+
+ write("extern vlib_error_desc_t {}_error_counters[];\n".format(csetname))
+
+
+def generate_include_types(s, module, stream):
+ """Generate separate API _types file."""
+ write = stream.write
+
+ write("#ifndef included_{module}_api_types_h\n".format(module=module))
+ write("#define included_{module}_api_types_h\n".format(module=module))
+
+ if "version" in s["Option"]:
+ v = s["Option"]["version"]
+ (major, minor, patch) = v.split(".")
+ write(
+ "#define VL_API_{m}_API_VERSION_MAJOR {v}\n".format(
+ m=module.upper(), v=major
+ )
+ )
+ write(
+ "#define VL_API_{m}_API_VERSION_MINOR {v}\n".format(
+ m=module.upper(), v=minor
+ )
+ )
+ write(
+ "#define VL_API_{m}_API_VERSION_PATCH {v}\n".format(
+ m=module.upper(), v=patch
+ )
+ )
+
+ if "Import" in s:
+ write("/* Imported API files */\n")
+ for i in s["Import"]:
+ filename = i.filename.replace("plugins/", "")
+ write("#include <{}_types.h>\n".format(filename))
+
+ for o in itertools.chain(s["types"], s["Define"]):
+ tname = o.__class__.__name__
+ if tname == "Using":
+ if "length" in o.alias:
+ write(
+ "typedef %s vl_api_%s_t[%s];\n"
+ % (o.alias["type"], o.name, o.alias["length"])
+ )
+ else:
+ write("typedef %s vl_api_%s_t;\n" % (o.alias["type"], o.name))
+ elif tname == "Enum" or tname == "EnumFlag":
+ if o.enumtype == "u32":
+ write("typedef enum {\n")
+ else:
+ write("typedef enum __attribute__((packed)) {\n")
+
+ for b in o.block:
+ write(" %s = %s,\n" % (b[0], b[1]))
+ write("} vl_api_%s_t;\n" % o.name)
+ if o.enumtype != "u32":
+ size1 = "sizeof(vl_api_%s_t)" % o.name
+ size2 = "sizeof(%s)" % o.enumtype
+ err_str = "size of API enum %s is wrong" % o.name
+ write('STATIC_ASSERT(%s == %s, "%s");\n' % (size1, size2, err_str))
+ else:
+ if tname == "Union":
+ write("typedef union __attribute__ ((packed)) _vl_api_%s {\n" % o.name)
+ else:
+ write(
+ ("typedef struct __attribute__ ((packed)) _vl_api_%s {\n") % o.name
+ )
+ for b in o.block:
+ if b.type == "Option":
+ continue
+ if b.type == "Field":
+ write(" %s %s;\n" % (api2c(b.fieldtype), b.fieldname))
+ elif b.type == "Array":
+ if b.lengthfield:
+ write(" %s %s[0];\n" % (api2c(b.fieldtype), b.fieldname))
+ else:
+ # Fixed length strings decay to nul terminated u8
+ if b.fieldtype == "string":
+ if b.modern_vla:
+ write(
+ " {} {};\n".format(
+ api2c(b.fieldtype), b.fieldname
+ )
+ )
+ else:
+ write(" u8 {}[{}];\n".format(b.fieldname, b.length))
+ else:
+ write(
+ " %s %s[%s];\n"
+ % (api2c(b.fieldtype), b.fieldname, b.length)
+ )
+ else:
+ raise ValueError(
+ "Error in processing type {} for {}".format(b, o.name)
+ )
+
+ write("} vl_api_%s_t;\n" % o.name)
+ write(
+ f"#define VL_API_{o.name.upper()}_IS_CONSTANT_SIZE ({0 if o.vla else 1})\n\n"
+ )
+
+ for t in s["Define"]:
+ write(
+ '#define VL_API_{ID}_CRC "{n}_{crc:08x}"\n'.format(
+ n=t.name, ID=t.name.upper(), crc=t.crc
+ )
+ )
+
+ write("\n#endif\n")
+
+
+def generate_c_boilerplate(services, defines, counters, file_crc, module, stream):
+ """VPP side plugin."""
+ write = stream.write
+ define_hash = {d.name: d for d in defines}
+
+ hdr = """\
+#define vl_endianfun /* define message structures */
+#include "{module}.api.h"
+#undef vl_endianfun
+
+#define vl_calcsizefun
+#include "{module}.api.h"
+#undef vl_calsizefun
+
+/* instantiate all the print functions we know about */
+#define vl_printfun
+#include "{module}.api.h"
+#undef vl_printfun
+
+"""
+
+ write(hdr.format(module=module))
+ if len(defines) > 0:
+ write("static u16\n")
+ write("setup_message_id_table (void) {\n")
+ write(" api_main_t *am = my_api_main;\n")
+ write(" vl_msg_api_msg_config_t c;\n")
+ write(
+ ' u16 msg_id_base = vl_msg_api_get_msg_ids ("{}_{crc:08x}", '
+ "VL_MSG_{m}_LAST);\n".format(module, crc=file_crc, m=module.upper())
+ )
+
+ for d in defines:
+ write(
+ ' vl_msg_api_add_msg_name_crc (am, "{n}_{crc:08x}",\n'
+ " VL_API_{ID} + msg_id_base);\n".format(
+ n=d.name, ID=d.name.upper(), crc=d.crc
+ )
+ )
+ for s in services:
+ d = define_hash[s.caller]
+ write(
+ " c = (vl_msg_api_msg_config_t) "
+ " {{.id = VL_API_{ID} + msg_id_base,\n"
+ ' .name = "{n}",\n'
+ " .handler = vl_api_{n}_t_handler,\n"
+ " .endian = vl_api_{n}_t_endian,\n"
+ " .format_fn = vl_api_{n}_t_format,\n"
+ " .traced = 1,\n"
+ " .replay = 1,\n"
+ " .tojson = vl_api_{n}_t_tojson,\n"
+ " .fromjson = vl_api_{n}_t_fromjson,\n"
+ " .calc_size = vl_api_{n}_t_calc_size,\n"
+ " .is_autoendian = {auto}}};\n".format(
+ n=s.caller, ID=s.caller.upper(), auto=d.autoendian
+ )
+ )
+ write(" vl_msg_api_config (&c);\n")
+ try:
+ d = define_hash[s.reply]
+ write(
+ " c = (vl_msg_api_msg_config_t) "
+ "{{.id = VL_API_{ID} + msg_id_base,\n"
+ ' .name = "{n}",\n'
+ " .handler = 0,\n"
+ " .endian = vl_api_{n}_t_endian,\n"
+ " .format_fn = vl_api_{n}_t_format,\n"
+ " .traced = 1,\n"
+ " .replay = 1,\n"
+ " .tojson = vl_api_{n}_t_tojson,\n"
+ " .fromjson = vl_api_{n}_t_fromjson,\n"
+ " .calc_size = vl_api_{n}_t_calc_size,\n"
+ " .is_autoendian = {auto}}};\n".format(
+ n=s.reply, ID=s.reply.upper(), auto=d.autoendian
+ )
+ )
+ write(" vl_msg_api_config (&c);\n")
+ except KeyError:
+ pass
+
+ try:
+ if s.stream:
+ d = define_hash[s.stream_message]
+ write(
+ " c = (vl_msg_api_msg_config_t) "
+ "{{.id = VL_API_{ID} + msg_id_base,\n"
+ ' .name = "{n}",\n'
+ " .handler = 0,\n"
+ " .endian = vl_api_{n}_t_endian,\n"
+ " .format_fn = vl_api_{n}_t_format,\n"
+ " .traced = 1,\n"
+ " .replay = 1,\n"
+ " .tojson = vl_api_{n}_t_tojson,\n"
+ " .fromjson = vl_api_{n}_t_fromjson,\n"
+ " .calc_size = vl_api_{n}_t_calc_size,\n"
+ " .is_autoendian = {auto}}};\n".format(
+ n=s.stream_message,
+ ID=s.stream_message.upper(),
+ auto=d.autoendian,
+ )
+ )
+ write(" vl_msg_api_config (&c);\n")
+ except KeyError:
+ pass
+ if len(defines) > 0:
+ write(" return msg_id_base;\n")
+ write("}\n")
+
+ severity = {
+ "error": "VL_COUNTER_SEVERITY_ERROR",
+ "info": "VL_COUNTER_SEVERITY_INFO",
+ "warn": "VL_COUNTER_SEVERITY_WARN",
+ }
+
+ for cnt in counters:
+ csetname = cnt.name
+ write("vlib_error_desc_t {}_error_counters[] = {{\n".format(csetname))
+ for c in cnt.block:
+ write(" {\n")
+ write(' .name = "{}",\n'.format(c["name"]))
+ write(' .desc = "{}",\n'.format(c["description"]))
+ write(" .severity = {},\n".format(severity[c["severity"]]))
+ write(" },\n")
+ write("};\n")
+
+
+def generate_c_test_boilerplate(services, defines, file_crc, module, plugin, stream):
+ """Generate code for legacy style VAT. To be deleted."""
+ write = stream.write
+
+ define_hash = {d.name: d for d in defines}
+
+ hdr = """\
+#define vl_endianfun /* define message structures */
+#include "{module}.api.h"
+#undef vl_endianfun
+
+#define vl_calcsizefun
+#include "{module}.api.h"
+#undef vl_calsizefun
+
+/* instantiate all the print functions we know about */
+#define vl_printfun
+#include "{module}.api.h"
+#undef vl_printfun
+
+"""
+
+ write(hdr.format(module=module))
+ for s in services:
+ try:
+ d = define_hash[s.reply]
+ except KeyError:
+ continue
+ if d.manual_print:
+ write(
+ "/*\n"
+ " * Manual definition requested for: \n"
+ " * vl_api_{n}_t_handler()\n"
+ " */\n".format(n=s.reply)
+ )
+ continue
+ if not define_hash[s.caller].autoreply:
+ write(
+ "/* Generation not supported (vl_api_{n}_t_handler()) */\n".format(
+ n=s.reply
+ )
+ )
+ continue
+ write("#ifndef VL_API_{n}_T_HANDLER\n".format(n=s.reply.upper()))
+ write("static void\n")
+ write("vl_api_{n}_t_handler (vl_api_{n}_t * mp) {{\n".format(n=s.reply))
+ write(" vat_main_t * vam = {}_test_main.vat_main;\n".format(module))
+ write(" i32 retval = ntohl(mp->retval);\n")
+ write(" if (vam->async_mode) {\n")
+ write(" vam->async_errors += (retval < 0);\n")
+ write(" } else {\n")
+ write(" vam->retval = retval;\n")
+ write(" vam->result_ready = 1;\n")
+ write(" }\n")
+ write("}\n")
+ write("#endif\n")
+
+ for e in s.events:
+ if define_hash[e].manual_print:
+ continue
+ write("static void\n")
+ write("vl_api_{n}_t_handler (vl_api_{n}_t * mp) {{\n".format(n=e))
+ write(' vlib_cli_output(0, "{n} event called:");\n'.format(n=e))
+ write(
+ ' vlib_cli_output(0, "%U", vl_api_{n}_t_format, mp);\n'.format(n=e)
+ )
+ write("}\n")
+
+ write("static void\n")
+ write("setup_message_id_table (vat_main_t * vam, u16 msg_id_base) {\n")
+ for s in services:
+ write(
+ " vl_msg_api_config (&(vl_msg_api_msg_config_t){{\n"
+ " .id = VL_API_{ID} + msg_id_base,\n"
+ ' .name = "{n}",\n'
+ " .handler = vl_api_{n}_t_handler,\n"
+ " .endian = vl_api_{n}_t_endian,\n"
+ " .format_fn = vl_api_{n}_t_format,\n"
+ " .size = sizeof(vl_api_{n}_t),\n"
+ " .traced = 1,\n"
+ " .tojson = vl_api_{n}_t_tojson,\n"
+ " .fromjson = vl_api_{n}_t_fromjson,\n"
+ " .calc_size = vl_api_{n}_t_calc_size,\n"
+ " }});".format(n=s.reply, ID=s.reply.upper())
+ )
+ write(
+ ' hash_set_mem (vam->function_by_name, "{n}", api_{n});\n'.format(
+ n=s.caller
+ )
+ )
+ try:
+ write(
+ ' hash_set_mem (vam->help_by_name, "{n}", "{help}");\n'.format(
+ n=s.caller, help=define_hash[s.caller].options["vat_help"]
+ )
+ )
+ except KeyError:
+ pass
+
+ # Events
+ for e in s.events:
+ write(
+ " vl_msg_api_config (&(vl_msg_api_msg_config_t){{\n"
+ " .id = VL_API_{ID} + msg_id_base,\n"
+ ' .name = "{n}",\n'
+ " .handler = vl_api_{n}_t_handler,\n"
+ " .endian = vl_api_{n}_t_endian,\n"
+ " .format_fn = vl_api_{n}_t_format,\n"
+ " .size = sizeof(vl_api_{n}_t),\n"
+ " .traced = 1,\n"
+ " .tojson = vl_api_{n}_t_tojson,\n"
+ " .fromjson = vl_api_{n}_t_fromjson,\n"
+ " .calc_size = vl_api_{n}_t_calc_size,\n"
+ " }});".format(n=e, ID=e.upper())
+ )
+
+ write("}\n")
+ write("clib_error_t * vat_plugin_register (vat_main_t *vam)\n")
+ write("{\n")
+ write(" {n}_test_main_t * mainp = &{n}_test_main;\n".format(n=module))
+ write(" mainp->vat_main = vam;\n")
+ write(
+ " mainp->msg_id_base = vl_client_get_first_plugin_msg_id "
+ ' ("{n}_{crc:08x}");\n'.format(n=module, crc=file_crc)
+ )
+ write(" if (mainp->msg_id_base == (u16) ~0)\n")
+ write(
+ ' return clib_error_return (0, "{} plugin not loaded...");\n'.format(
+ module
+ )
+ )
+ write(" setup_message_id_table (vam, mainp->msg_id_base);\n")
+ write("#ifdef VL_API_LOCAL_SETUP_MESSAGE_ID_TABLE\n")
+ write(" VL_API_LOCAL_SETUP_MESSAGE_ID_TABLE(vam);\n")
+ write("#endif\n")
+ write(" return 0;\n")
+ write("}\n")
+
+
+def apifunc(func):
+ """Check if a method is generated already."""
+
+ def _f(module, d, processed, *args):
+ if d.name in processed:
+ return None
+ processed[d.name] = True
+ return func(module, d, *args)
+
+ return _f
+
+
+def c_test_api_service(s, dump, stream):
+ """Generate JSON code for a service."""
+ write = stream.write
+
+ req_reply_template = """\
+static cJSON *
+api_{n} (cJSON *o)
+{{
+ vl_api_{n}_t *mp;
+ int len;
+ if (!o) return 0;
+ mp = vl_api_{n}_t_fromjson(o, &len);
+ if (!mp) {{
+ fprintf(stderr, "Failed converting JSON to API\\n");
+ return 0;
+ }}
+
+ mp->_vl_msg_id = vac_get_msg_index(VL_API_{N}_CRC);
+ vl_api_{n}_t_endian(mp);
+ vac_write((char *)mp, len);
+ cJSON_free(mp);
+
+ /* Read reply */
+ char *p;
+ int l;
+ vac_read(&p, &l, 5); // XXX: Fix timeout
+ if (p == 0 || l == 0) return 0;
+ // XXX Will fail in case of event received. Do loop
+ if (ntohs(*((u16 *)p)) != vac_get_msg_index(VL_API_{R}_CRC)) {{
+ fprintf(stderr, "Mismatched reply\\n");
+ return 0;
+ }}
+ vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
+ vl_api_{r}_t_endian(rmp);
+ return vl_api_{r}_t_tojson(rmp);
+}}
+
+"""
+ dump_details_template = """\
+static cJSON *
+api_{n} (cJSON *o)
+{{
+ u16 msg_id = vac_get_msg_index(VL_API_{N}_CRC);
+ int len;
+ if (!o) return 0;
+ vl_api_{n}_t *mp = vl_api_{n}_t_fromjson(o, &len);
+ if (!mp) {{
+ fprintf(stderr, "Failed converting JSON to API\\n");
+ return 0;
+ }}
+ mp->_vl_msg_id = msg_id;
+ vl_api_{n}_t_endian(mp);
+ vac_write((char *)mp, len);
+ cJSON_free(mp);
+
+ vat2_control_ping(123); // FIX CONTEXT
+ cJSON *reply = cJSON_CreateArray();
+
+ u16 ping_reply_msg_id = vac_get_msg_index(VL_API_CONTROL_PING_REPLY_CRC);
+ u16 details_msg_id = vac_get_msg_index(VL_API_{R}_CRC);
+
+ while (1) {{
+ /* Read reply */
+ char *p;
+ int l;
+ vac_read(&p, &l, 5); // XXX: Fix timeout
+ if (p == 0 || l == 0) {{
+ cJSON_free(reply);
+ return 0;
+ }}
+
+ /* Message can be one of [_details, control_ping_reply
+ * or unrelated event]
+ */
+ u16 reply_msg_id = ntohs(*((u16 *)p));
+ if (reply_msg_id == ping_reply_msg_id) {{
+ break;
+ }}
+
+ if (reply_msg_id == details_msg_id) {{
+ if (l < sizeof(vl_api_{r}_t)) {{
+ cJSON_free(reply);
+ return 0;
+ }}
+ vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
+ vl_api_{r}_t_endian(rmp);
+ cJSON_AddItemToArray(reply, vl_api_{r}_t_tojson(rmp));
+ }}
+ }}
+ return reply;
+}}
+
+"""
+ gets_details_reply_template = """\
+static cJSON *
+api_{n} (cJSON *o)
+{{
+ u16 msg_id = vac_get_msg_index(VL_API_{N}_CRC);
+ int len = 0;
+ if (!o) return 0;
+ vl_api_{n}_t *mp = vl_api_{n}_t_fromjson(o, &len);
+ if (!mp) {{
+ fprintf(stderr, "Failed converting JSON to API\\n");
+ return 0;
+ }}
+ mp->_vl_msg_id = msg_id;
+
+ vl_api_{n}_t_endian(mp);
+ vac_write((char *)mp, len);
+ cJSON_free(mp);
+
+ cJSON *reply = cJSON_CreateArray();
+
+ u16 reply_msg_id = vac_get_msg_index(VL_API_{R}_CRC);
+ u16 details_msg_id = vac_get_msg_index(VL_API_{D}_CRC);
+
+ while (1) {{
+ /* Read reply */
+ char *p;
+ int l;
+ vac_read(&p, &l, 5); // XXX: Fix timeout
+
+ /* Message can be one of [_details, control_ping_reply
+ * or unrelated event]
+ */
+ u16 msg_id = ntohs(*((u16 *)p));
+ if (msg_id == reply_msg_id) {{
+ vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
+ vl_api_{r}_t_endian(rmp);
+ cJSON_AddItemToArray(reply, vl_api_{r}_t_tojson(rmp));
+ break;
+ }}
+
+ if (msg_id == details_msg_id) {{
+ vl_api_{d}_t *rmp = (vl_api_{d}_t *)p;
+ vl_api_{d}_t_endian(rmp);
+ cJSON_AddItemToArray(reply, vl_api_{d}_t_tojson(rmp));
+ }}
+ }}
+ return reply;
+}}
+
+"""
+
+ if dump:
+ if s.stream_message:
+ write(
+ gets_details_reply_template.format(
+ n=s.caller,
+ r=s.reply,
+ N=s.caller.upper(),
+ R=s.reply.upper(),
+ d=s.stream_message,
+ D=s.stream_message.upper(),
+ )
+ )
+ else:
+ write(
+ dump_details_template.format(
+ n=s.caller, r=s.reply, N=s.caller.upper(), R=s.reply.upper()
+ )
+ )
+ else:
+ write(
+ req_reply_template.format(
+ n=s.caller, r=s.reply, N=s.caller.upper(), R=s.reply.upper()
+ )
+ )
+
+
+def generate_c_test2_boilerplate(services, defines, module, stream):
+ """Generate code for VAT2 plugin."""
+ write = stream.write
+
+ define_hash = {d.name: d for d in defines}
+ # replies = {}
+
+ hdr = """\
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <vppinfra/error.h>
+#include <vnet/ip/ip_format_fns.h>
+#include <vnet/ethernet/ethernet_format_fns.h>
+
+#define vl_typedefs /* define message structures */
+#include <vlibmemory/vl_memory_api_h.h>
+#include <vlibmemory/vlib.api_types.h>
+#include <vlibmemory/vlib.api.h>
+#undef vl_typedefs
+
+#include "{module}.api_enum.h"
+#include "{module}.api_types.h"
+
+#define vl_endianfun /* define message structures */
+#include "{module}.api.h"
+#undef vl_endianfun
+
+#define vl_calcsizefun
+#include "{module}.api.h"
+#undef vl_calsizefun
+
+#define vl_printfun
+#include "{module}.api.h"
+#undef vl_printfun
+
+#include "{module}.api_tojson.h"
+#include "{module}.api_fromjson.h"
+#include <vpp-api/client/vppapiclient.h>
+
+#include <vat2/vat2_helpers.h>
+
+"""
+
+ write(hdr.format(module=module))
+
+ for s in services:
+ if s.reply not in define_hash:
+ continue
+ c_test_api_service(s, s.stream, stream)
+
+ write(
+ "void vat2_register_function(char *, cJSON * (*)(cJSON *), cJSON * (*)(void *), u32);\n"
+ )
+ # write('__attribute__((constructor))')
+ write("clib_error_t *\n")
+ write("vat2_register_plugin (void) {\n")
+ for s in services:
+ if s.reply not in define_hash:
+ continue
+ crc = define_hash[s.caller].crc
+ write(
+ ' vat2_register_function("{n}", api_{n}, (cJSON * (*)(void *))vl_api_{n}_t_tojson, 0x{crc:08x});\n'.format(
+ n=s.caller, crc=crc
+ )
+ )
+ write(" return 0;\n")
+ write("}\n")
+
+