2 # Copyright (c) 2020 Cisco and/or its affiliates.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at:
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
17 # Provide two classes FromJSON and TOJSON that converts between JSON and VPP's
22 This module creates C code for core VPP, VPP plugins and client side VAT and
31 from io import StringIO
34 process_imports = False
37 ###############################################################################
39 """Class to generate functions converting from VPP binary API to JSON."""
42 noprint_fields = {"_vl_msg_id": None, "client_index": None, "context": None}
55 def __init__(self, module, types, defines, imported_types, stream):
58 self.defines = defines
60 self.types_hash = {"vl_api_" + d.name + "_t": d for d in types + imported_types}
61 self.defines_hash = {d.name: d for d in defines}
64 """Output the top boilerplate."""
65 write = self.stream.write
66 write("#ifndef included_{}_api_tojson_h\n".format(self.module))
67 write("#define included_{}_api_tojson_h\n".format(self.module))
68 write("#include <vppinfra/cJSON.h>\n\n")
69 write("#include <vppinfra/jsonformat.h>\n\n")
70 if self.module == "interface_types":
71 write("#define vl_printfun\n")
72 write("#include <vnet/interface_types.api.h>\n\n")
75 """Output the bottom boilerplate."""
76 write = self.stream.write
79 def get_base_type(self, t):
82 vt = self.types_hash[t]
83 if vt.type == "Using" and "length" not in vt.alias:
84 vt_type = vt.alias["type"]
89 def get_json_func(self, t):
90 """Given the type, returns the function to use to create a
92 vt, vt_type = self.get_base_type(t)
94 if t in self.is_number or vt_type in self.is_number:
95 return "cJSON_AddNumberToObject", "", False
97 return "cJSON_AddBoolToObject", "", False
99 # Lookup type name check if it's enum
100 if vt.type == "Enum" or vt.type == "EnumFlag":
101 return "{t}_tojson".format(t=t), "", True
102 return "{t}_tojson".format(t=t), "&", True
104 def get_json_array_func(self, t):
105 """Given a type returns the function to create a cJSON object
107 if t in self.is_number:
108 return "cJSON_CreateNumber", ""
110 return "cJSON_CreateBool", ""
111 vt, vt_type = self.get_base_type(t)
112 if vt.type == "Enum" or vt.type == "EnumFlag":
113 return "{t}_tojson".format(t=t), ""
114 return "{t}_tojson".format(t=t), "&"
116 def print_string(self, o):
117 """Create cJSON object from vl_api_string_t"""
118 write = self.stream.write
121 ' vl_api_string_cJSON_AddToObject(o, "{n}", &a->{n});\n'.format(
127 ' cJSON_AddStringToObject(o, "{n}", (char *)a->{n});\n'.format(
132 def print_field(self, o):
133 """Called for every field in a typedef or define."""
134 write = self.stream.write
135 if o.fieldname in self.noprint_fields:
138 f, p, newobj = self.get_json_func(o.fieldtype)
142 ' cJSON_AddItemToObject(o, "{n}", {f}({p}a->{n}));\n'.format(
143 f=f, p=p, n=o.fieldname
147 write(' {f}(o, "{n}", {p}a->{n});\n'.format(f=f, p=p, n=o.fieldname))
149 _dispatch["Field"] = print_field
151 def print_array(self, o):
152 """Converts a VPP API array to cJSON array."""
153 write = self.stream.write
158 cJSON *array = cJSON_AddArrayToObject(o, "{n}");
159 for (i = 0; i < {lfield}; i++) {{
160 cJSON_AddItemToArray(array, {f}({p}a->{n}[i]));
165 if o.fieldtype == "string":
169 lfield = "a->" + o.lengthfield if o.lengthfield else o.length
170 if o.fieldtype == "u8":
172 # What is length field doing here?
174 ' u8 *s = format(0, "0x%U", format_hex_bytes, '
175 "&a->{n}, {lfield});\n".format(n=o.fieldname, lfield=lfield)
178 ' cJSON_AddStringToObject(o, "{n}", (char *)s);\n'.format(
182 write(" vec_free(s);\n")
186 f, p = self.get_json_array_func(o.fieldtype)
187 write(forloop.format(lfield=lfield, t=o.fieldtype, n=o.fieldname, f=f, p=p))
189 _dispatch["Array"] = print_array
191 def print_enum(self, o):
192 """Create cJSON object (string) for VPP API enum"""
193 write = self.stream.write
195 "static inline cJSON *vl_api_{name}_t_tojson "
196 "(vl_api_{name}_t a) {{\n".format(name=o.name)
199 write(" switch(a) {\n")
201 write(" case %s:\n" % b[1])
202 write(' return cJSON_CreateString("{}");\n'.format(b[0]))
203 write(' default: return cJSON_CreateString("Invalid ENUM");\n')
205 write(" return 0;\n")
208 _dispatch["Enum"] = print_enum
210 def print_enum_flag(self, o):
211 """Create cJSON object (string) for VPP API enum"""
212 write = self.stream.write
214 "static inline cJSON *vl_api_{name}_t_tojson "
215 "(vl_api_{name}_t a) {{\n".format(name=o.name)
217 write(" cJSON *array = cJSON_CreateArray();\n")
222 write(" if (a & {})\n".format(b[0]))
224 ' cJSON_AddItemToArray(array, cJSON_CreateString("{}"));\n'.format(
228 write(" return array;\n")
231 _dispatch["EnumFlag"] = print_enum_flag
233 def print_typedef(self, o):
234 """Create cJSON (dictionary) object from VPP API typedef"""
235 write = self.stream.write
237 "static inline cJSON *vl_api_{name}_t_tojson "
238 "(vl_api_{name}_t *a) {{\n".format(name=o.name)
240 write(" cJSON *o = cJSON_CreateObject();\n")
243 self._dispatch[t.type](self, t)
245 write(" return o;\n")
248 def print_define(self, o):
249 """Create cJSON (dictionary) object from VPP API define"""
250 write = self.stream.write
252 "static inline cJSON *vl_api_{name}_t_tojson "
253 "(vl_api_{name}_t *a) {{\n".format(name=o.name)
255 write(" cJSON *o = cJSON_CreateObject();\n")
256 write(' cJSON_AddStringToObject(o, "_msgname", "{}");\n'.format(o.name))
258 ' cJSON_AddStringToObject(o, "_crc", "{crc:08x}");\n'.format(crc=o.crc)
262 self._dispatch[t.type](self, t)
264 write(" return o;\n")
267 def print_using(self, o):
268 """Create cJSON (dictionary) object from VPP API aliased type"""
272 write = self.stream.write
274 "static inline cJSON *vl_api_{name}_t_tojson "
275 "(vl_api_{name}_t *a) {{\n".format(name=o.name)
278 write(' u8 *s = format(0, "%U", format_vl_api_{}_t, a);\n'.format(o.name))
279 write(" cJSON *o = cJSON_CreateString((char *)s);\n")
280 write(" vec_free(s);\n")
281 write(" return o;\n")
284 _dispatch["Typedef"] = print_typedef
285 _dispatch["Define"] = print_define
286 _dispatch["Using"] = print_using
287 _dispatch["Union"] = print_typedef
289 def generate_function(self, t):
290 """Main entry point"""
291 write = self.stream.write
293 write("/* Manual print {} */\n".format(t.name))
295 self._dispatch[t.type](self, t)
297 def generate_types(self):
298 """Main entry point"""
300 self.generate_function(t)
302 def generate_defines(self):
303 """Main entry point"""
304 for t in self.defines:
305 self.generate_function(t)
310 Parse JSON objects into VPP API binary message structures.
314 noprint_fields = {"_vl_msg_id": None, "client_index": None, "context": None}
327 def __init__(self, module, types, defines, imported_types, stream):
330 self.defines = defines
332 self.types_hash = {"vl_api_" + d.name + "_t": d for d in types + imported_types}
333 self.defines_hash = {d.name: d for d in defines}
336 """Output the top boilerplate."""
337 write = self.stream.write
338 write("#ifndef included_{}_api_fromjson_h\n".format(self.module))
339 write("#define included_{}_api_fromjson_h\n".format(self.module))
340 write("#include <vppinfra/cJSON.h>\n\n")
341 write("#include <vppinfra/jsonformat.h>\n\n")
342 write('#pragma GCC diagnostic ignored "-Wunused-label"\n')
344 def is_base_type(self, t):
345 """Check if a type is one of the VPP API base types"""
346 if t in self.is_number:
353 """Output the bottom boilerplate."""
354 write = self.stream.write
357 def print_string(self, o, toplevel=False):
358 """Convert JSON string to vl_api_string_t"""
359 write = self.stream.write
361 msgvar = "a" if toplevel else "*mp"
362 msgsize = "l" if toplevel else "*len"
365 write(" char *p = cJSON_GetStringValue(item);\n")
366 write(" size_t plen = strlen(p);\n")
368 " {msgvar} = cJSON_realloc({msgvar}, {msgsize} + plen, {msgsize});\n".format(
369 msgvar=msgvar, msgsize=msgsize
372 write(" if ({msgvar} == 0) goto error;\n".format(msgvar=msgvar))
374 " vl_api_c_string_to_api_string(p, (void *){msgvar} + "
375 "{msgsize} - sizeof(vl_api_string_t));\n".format(
376 msgvar=msgvar, msgsize=msgsize
379 write(" {msgsize} += plen;\n".format(msgsize=msgsize))
382 " strncpy_s((char *)a->{n}, sizeof(a->{n}), "
383 "cJSON_GetStringValue(item), sizeof(a->{n}) - 1);\n".format(
388 def print_field(self, o, toplevel=False):
389 """Called for every field in a typedef or define."""
390 write = self.stream.write
391 if o.fieldname in self.noprint_fields:
393 is_bt = self.is_base_type(o.fieldtype)
394 t = "vl_api_{}".format(o.fieldtype) if is_bt else o.fieldtype
396 msgvar = "(void **)&a" if toplevel else "mp"
397 msgsize = "&l" if toplevel else "len"
401 " vl_api_{t}_fromjson(item, &a->{n});\n".format(
402 t=o.fieldtype, n=o.fieldname
407 " if ({t}_fromjson({msgvar}, "
408 "{msgsize}, item, &a->{n}) < 0) goto error;\n".format(
409 t=t, n=o.fieldname, msgvar=msgvar, msgsize=msgsize
413 _dispatch["Field"] = print_field
415 def print_array(self, o, toplevel=False):
416 """Convert JSON array to VPP API array"""
417 write = self.stream.write
422 cJSON *array = cJSON_GetObjectItem(o, "{n}");
423 int size = cJSON_GetArraySize(array);
424 if (size != {lfield}) goto error;
425 for (i = 0; i < size; i++) {{
426 cJSON *e = cJSON_GetArrayItem(array, i);
434 cJSON *array = cJSON_GetObjectItem(o, "{n}");
435 int size = cJSON_GetArraySize(array);
437 {realloc} = cJSON_realloc({realloc}, {msgsize} + sizeof({t}) * size, {msgsize});
438 {t} *d = (void *){realloc} + {msgsize};
439 {msgsize} += sizeof({t}) * size;
440 for (i = 0; i < size; i++) {{
441 cJSON *e = cJSON_GetArrayItem(array, i);
447 if o.fieldtype == "string":
448 self.print_string(o, toplevel)
451 lfield = "a->" + o.lengthfield if o.lengthfield else o.length
452 msgvar = "(void **)&a" if toplevel else "mp"
453 realloc = "a" if toplevel else "*mp"
454 msgsize = "l" if toplevel else "*len"
456 if o.fieldtype == "u8":
458 write(' s = u8string_fromjson(o, "{}");\n'.format(o.fieldname))
459 write(" if (!s) goto error;\n")
460 write(" {} = vec_len(s);\n".format(lfield))
463 " {realloc} = cJSON_realloc({realloc}, {msgsize} + "
464 "vec_len(s), {msgsize});\n".format(
465 msgvar=msgvar, msgsize=msgsize, realloc=realloc
469 " memcpy((void *){realloc} + {msgsize}, s, "
470 "vec_len(s));\n".format(realloc=realloc, msgsize=msgsize)
472 write(" {msgsize} += vec_len(s);\n".format(msgsize=msgsize))
474 write(" vec_free(s);\n")
477 ' if (u8string_fromjson2(o, "{n}", a->{n}) < 0) goto error;\n'.format(
483 is_bt = self.is_base_type(o.fieldtype)
487 call = "vl_api_{t}_fromjson(e, &d[i]);".format(t=o.fieldtype)
489 call = "if ({t}_fromjson({msgvar}, len, e, &d[i]) < 0) goto error; ".format(
490 t=o.fieldtype, msgvar=msgvar
504 call = "vl_api_{t}_fromjson(e, &a->{n}[i]);".format(t=t, n=o.fieldname)
506 call = "if ({}_fromjson({}, len, e, &a->{}[i]) < 0) goto error;".format(
507 t, msgvar, o.fieldname
521 _dispatch["Array"] = print_array
523 def print_enum(self, o):
524 """Convert to JSON enum(string) to VPP API enum (int)"""
525 write = self.stream.write
527 "static inline int vl_api_{n}_t_fromjson"
528 "(void **mp, int *len, cJSON *o, vl_api_{n}_t *a) {{\n".format(n=o.name)
530 write(" char *p = cJSON_GetStringValue(o);\n")
533 ' if (strcmp(p, "{}") == 0) {{*a = {}; return 0;}}\n'.format(
538 write(" return -1;\n")
541 _dispatch["Enum"] = print_enum
543 def print_enum_flag(self, o):
544 """Convert to JSON enum(string) to VPP API enum (int)"""
545 write = self.stream.write
547 "static inline int vl_api_{n}_t_fromjson "
548 "(void **mp, int *len, cJSON *o, vl_api_{n}_t *a) {{\n".format(n=o.name)
552 write(" for (i = 0; i < cJSON_GetArraySize(o); i++) {\n")
553 write(" cJSON *e = cJSON_GetArrayItem(o, i);\n")
554 write(" char *p = cJSON_GetStringValue(e);\n")
555 write(" if (!p) return -1;\n")
557 write(' if (strcmp(p, "{}") == 0) *a |= {};\n'.format(b[0], b[1]))
559 write(" return 0;\n")
562 _dispatch["EnumFlag"] = print_enum_flag
564 def print_typedef(self, o):
565 """Convert from JSON object to VPP API binary representation"""
566 write = self.stream.write
569 "static inline int vl_api_{name}_t_fromjson (void **mp, "
570 "int *len, cJSON *o, vl_api_{name}_t *a) {{\n".format(name=o.name)
572 write(" cJSON *item __attribute__ ((unused));\n")
573 write(" u8 *s __attribute__ ((unused));\n")
575 if t.type == "Field" and t.is_lengthfield:
577 write('\n item = cJSON_GetObjectItem(o, "{}");\n'.format(t.fieldname))
578 write(" if (!item) goto error;\n")
579 self._dispatch[t.type](self, t)
581 write("\n return 0;\n")
583 write(" return -1;\n")
586 def print_union(self, o):
587 """Convert JSON object to VPP API binary union"""
588 write = self.stream.write
591 "static inline int vl_api_{name}_t_fromjson (void **mp, "
592 "int *len, cJSON *o, vl_api_{name}_t *a) {{\n".format(name=o.name)
594 write(" cJSON *item __attribute__ ((unused));\n")
595 write(" u8 *s __attribute__ ((unused));\n")
597 if t.type == "Field" and t.is_lengthfield:
599 write(' item = cJSON_GetObjectItem(o, "{}");\n'.format(t.fieldname))
600 write(" if (item) {\n")
601 self._dispatch[t.type](self, t)
603 write("\n return 0;\n")
605 write(" return -1;\n")
608 def print_define(self, o):
609 """Convert JSON object to VPP API message"""
610 write = self.stream.write
613 "static inline vl_api_{name}_t *vl_api_{name}_t_fromjson "
614 "(cJSON *o, int *len) {{\n".format(name=o.name)
616 write(" cJSON *item __attribute__ ((unused));\n")
617 write(" u8 *s __attribute__ ((unused));\n")
618 write(" int l = sizeof(vl_api_{}_t);\n".format(o.name))
619 write(" vl_api_{}_t *a = cJSON_malloc(l);\n".format(o.name))
623 if t.fieldname in self.noprint_fields:
625 if t.type == "Field" and t.is_lengthfield:
627 write(' item = cJSON_GetObjectItem(o, "{}");\n'.format(t.fieldname))
628 write(" if (!item) goto error;\n")
630 self._dispatch[t.type](self, t, toplevel=True)
633 write(" *len = l;\n")
634 write(" return a;\n")
638 write(" cJSON_free(a);\n")
639 write(" return 0;\n")
642 def print_using(self, o):
643 """Convert JSON field to VPP type alias"""
644 write = self.stream.write
651 "static inline int vl_api_{name}_t_fromjson (void **mp, "
652 "int *len, cJSON *o, vl_api_{name}_t *a) {{\n".format(name=o.name)
654 if "length" in o.alias:
655 if t.fieldtype != "u8":
657 "Error in processing type {} for {}".format(t.fieldtype, o.name)
660 " vl_api_u8_string_fromjson(o, (u8 *)a, {});\n".format(
665 write(" vl_api_{t}_fromjson(o, ({t} *)a);\n".format(t=t.fieldtype))
667 write(" return 0;\n")
670 _dispatch["Typedef"] = print_typedef
671 _dispatch["Define"] = print_define
672 _dispatch["Using"] = print_using
673 _dispatch["Union"] = print_union
675 def generate_function(self, t):
676 """Main entry point"""
677 write = self.stream.write
679 write("/* Manual print {} */\n".format(t.name))
681 self._dispatch[t.type](self, t)
683 def generate_types(self):
684 """Main entry point"""
686 self.generate_function(t)
688 def generate_defines(self):
689 """Main entry point"""
690 for t in self.defines:
691 self.generate_function(t)
694 def generate_tojson(s, modulename, stream):
695 """Generate all functions to convert from API to JSON"""
698 write("/* Imported API files */\n")
699 for i in s["Import"]:
700 f = i.filename.replace("plugins/", "")
701 write("#include <{}_tojson.h>\n".format(f))
703 pp = ToJSON(modulename, s["types"], s["Define"], s["imported"]["types"], stream)
706 pp.generate_defines()
711 def generate_fromjson(s, modulename, stream):
712 """Generate all functions to convert from JSON to API"""
714 write("/* Imported API files */\n")
715 for i in s["Import"]:
716 f = i.filename.replace("plugins/", "")
717 write("#include <{}_fromjson.h>\n".format(f))
719 pp = FromJSON(modulename, s["types"], s["Define"], s["imported"]["types"], stream)
722 pp.generate_defines()
728 ###############################################################################
731 DATESTRING = datetime.datetime.utcfromtimestamp(
732 int(os.environ.get("SOURCE_DATE_EPOCH", time.time()))
734 TOP_BOILERPLATE = """\
736 * VLIB API definitions {datestring}
737 * Input file: {input_filename}
738 * Automatically generated: please edit the input file NOT this file!
742 #if defined(vl_msg_id)||defined(vl_union_id) \\
743 || defined(vl_printfun) ||defined(vl_endianfun) \\
744 || defined(vl_api_version)||defined(vl_typedefs) \\
745 || defined(vl_msg_name)||defined(vl_msg_name_crc_list) \\
746 || defined(vl_api_version_tuple) || defined(vl_calcsizefun)
747 /* ok, something was selected */
749 #warning no content included from {input_filename}
752 #define VL_API_PACKED(x) x __attribute__ ((packed))
755 * Note: VL_API_MAX_ARRAY_SIZE is set to an arbitrarily large limit.
757 * However, any message with a ~2 billion element array is likely to break the
758 * api handling long before this limit causes array element endian issues.
760 * Applications should be written to create reasonable api messages.
762 #define VL_API_MAX_ARRAY_SIZE 0x7fffffff
766 BOTTOM_BOILERPLATE = """\
767 /****** API CRC (whole file) *****/
769 #ifdef vl_api_version
770 vl_api_version({input_filename}, {file_crc:#08x})
777 """Generate macro to map API message id to handler"""
780 /****** Message ID / handler enum ******/
785 for t in s["Define"]:
786 output += "vl_msg_id(VL_API_%s, vl_api_%s_t_handler)\n" % (
796 """Generate calls to name mapping macro"""
799 /****** Message names ******/
804 for t in s["Define"]:
805 dont_trace = 0 if t.dont_trace else 1
806 output += "vl_msg_name(vl_api_%s_t, %d)\n" % (t.name, dont_trace)
812 def msg_name_crc_list(s, suffix):
813 """Generate list of names to CRC mappings"""
816 /****** Message name, crc list ******/
818 #ifdef vl_msg_name_crc_list
820 output += "#define foreach_vl_msg_name_crc_%s " % suffix
822 for t in s["Define"]:
823 output += "\\\n_(VL_API_%s, %s, %08x) " % (t.name.upper(), t.name, t.crc)
829 def api2c(fieldtype):
830 """Map between API type names and internal VPP type names"""
832 "string": "vl_api_string_t",
834 if fieldtype in mappingtable:
835 return mappingtable[fieldtype]
839 def typedefs(filename):
840 """Include in the main files to the types file"""
843 /****** Typedefs ******/
846 #include "{include}.api_types.h"
869 """Functions for pretty printing VPP API messages"""
872 noprint_fields = {"_vl_msg_id": None, "client_index": None, "context": None}
874 def __init__(self, stream):
878 def print_string(o, stream):
879 """Pretty print a vl_api_string_t"""
882 write(" if (vl_api_string_len(&a->{f}) > 0) {{\n".format(f=o.fieldname))
884 ' s = format(s, "\\n%U{f}: %U", '
885 "format_white_space, indent, "
886 "vl_api_format_string, (&a->{f}));\n".format(f=o.fieldname)
890 ' s = format(s, "\\n%U{f}:", '
891 "format_white_space, indent);\n".format(f=o.fieldname)
896 ' s = format(s, "\\n%U{f}: %s", '
897 "format_white_space, indent, a->{f});\n".format(f=o.fieldname)
900 def print_field(self, o, stream):
901 """Pretty print API field"""
903 if o.fieldname in self.noprint_fields:
905 if o.fieldtype in FORMAT_STRINGS:
906 f = FORMAT_STRINGS[o.fieldtype]
908 ' s = format(s, "\\n%U{n}: {f}", '
909 "format_white_space, indent, a->{n});\n".format(n=o.fieldname, f=f)
913 ' s = format(s, "\\n%U{n}: %U", '
914 "format_white_space, indent, "
915 "format_{t}, &a->{n}, indent);\n".format(n=o.fieldname, t=o.fieldtype)
918 _dispatch["Field"] = print_field
920 def print_array(self, o, stream):
921 """Pretty print API array"""
925 for (i = 0; i < {lfield}; i++) {{
926 s = format(s, "\\n%U{n}: %U",
927 format_white_space, indent, format_{t}, &a->{n}[i], indent);
931 forloop_format = """\
932 for (i = 0; i < {lfield}; i++) {{
933 s = format(s, "\\n%U{n}: {t}",
934 format_white_space, indent, a->{n}[i]);
938 if o.fieldtype == "string":
939 self.print_string(o, stream)
942 if o.fieldtype == "u8":
945 ' s = format(s, "\\n%U{n}: %U", format_white_space, '
946 "indent, format_hex_bytes, a->{n}, a->{lfield});\n".format(
947 n=o.fieldname, lfield=o.lengthfield
952 ' s = format(s, "\\n%U{n}: %U", format_white_space, '
953 "indent, format_hex_bytes, a, {lfield});\n".format(
954 n=o.fieldname, lfield=o.length
959 lfield = "a->" + o.lengthfield if o.lengthfield else o.length
960 if o.fieldtype in FORMAT_STRINGS:
962 forloop_format.format(
963 lfield=lfield, t=FORMAT_STRINGS[o.fieldtype], n=o.fieldname
967 write(forloop.format(lfield=lfield, t=o.fieldtype, n=o.fieldname))
969 _dispatch["Array"] = print_array
972 def print_alias(k, v, stream):
973 """Pretty print type alias"""
975 if "length" in v.alias and v.alias["length"] and v.alias["type"] == "u8":
977 ' return format(s, "%U", format_hex_bytes, a, {});\n'.format(
981 elif v.alias["type"] in FORMAT_STRINGS:
983 ' return format(s, "{}", *a);\n'.format(
984 FORMAT_STRINGS[v.alias["type"]]
988 write(' return format(s, "{} (print not implemented)");\n'.format(k))
991 def print_enum(o, stream):
992 """Pretty print API enum"""
994 write(" switch(*a) {\n")
996 write(" case %s:\n" % b[1])
997 write(' return format(s, "{}");\n'.format(b[0]))
1000 _dispatch["Enum"] = print_enum
1001 _dispatch["EnumFlag"] = print_enum
1003 def print_obj(self, o, stream):
1005 write = stream.write
1007 if o.type in self._dispatch:
1008 self._dispatch[o.type](self, o, stream)
1011 ' s = format(s, "\\n{} {} {} (print not implemented");\n'.format(
1012 o.type, o.fieldtype, o.fieldname
1017 def printfun(objs, stream, modulename):
1018 """Main entry point for pretty print function generation"""
1019 write = stream.write
1022 /****** Print functions *****/
1024 #ifndef included_{module}_printfun
1025 #define included_{module}_printfun
1028 #define _uword_fmt \"%lld\"
1029 #define _uword_cast (long long)
1031 #define _uword_fmt \"%ld\"
1032 #define _uword_cast long
1035 #include "{module}.api_tojson.h"
1036 #include "{module}.api_fromjson.h"
1041 static inline u8 *vl_api_{name}_t_format (u8 *s, va_list *args)
1043 __attribute__((unused)) vl_api_{name}_t *a = va_arg (*args, vl_api_{name}_t *);
1044 u32 indent __attribute__((unused)) = 2;
1045 int i __attribute__((unused));
1048 h = h.format(module=modulename)
1051 pp = Printfun(stream)
1054 write("/***** manual: vl_api_%s_t_format *****/\n\n" % t.name)
1056 write(signature.format(name=t.name, suffix=""))
1057 write(" /* Message definition: vl_api_{}_t: */\n".format(t.name))
1058 write(' s = format(s, "vl_api_%s_t:");\n' % t.name)
1060 pp.print_obj(o, stream)
1061 write(" return s;\n")
1065 write("\n#endif /* vl_printfun */\n")
1070 def printfun_types(objs, stream, modulename):
1071 """Pretty print API types"""
1072 write = stream.write
1073 pp = Printfun(stream)
1076 /****** Print functions *****/
1078 #ifndef included_{module}_printfun_types
1079 #define included_{module}_printfun_types
1082 h = h.format(module=modulename)
1086 static inline u8 *format_vl_api_{name}_t (u8 *s, va_list * args)
1088 vl_api_{name}_t *a = va_arg (*args, vl_api_{name}_t *);
1089 u32 indent __attribute__((unused)) = va_arg (*args, u32);
1090 int i __attribute__((unused));
1095 if t.__class__.__name__ == "Enum" or t.__class__.__name__ == "EnumFlag":
1096 write(signature.format(name=t.name))
1097 pp.print_enum(t.block, stream)
1098 write(" return s;\n")
1103 write("/***** manual: vl_api_%s_t_format *****/\n\n" % t.name)
1106 if t.__class__.__name__ == "Using":
1107 write(signature.format(name=t.name))
1108 pp.print_alias(t.name, t, stream)
1112 write(signature.format(name=t.name))
1114 pp.print_obj(o, stream)
1116 write(" return s;\n")
1120 write("\n#endif /* vl_printfun_types */\n")
1123 def generate_imports(imports):
1124 """Add #include matching the API import statements"""
1125 output = "/* Imported API files */\n"
1126 output += "#ifndef vl_api_version\n"
1129 s = i.filename.replace("plugins/", "")
1130 output += "#include <{}.h>\n".format(s)
1131 output += "#endif\n"
1136 "u16": "clib_net_to_host_u16",
1137 "u32": "clib_net_to_host_u32",
1138 "u64": "clib_net_to_host_u64",
1139 "i16": "clib_net_to_host_i16",
1140 "i32": "clib_net_to_host_i32",
1141 "i64": "clib_net_to_host_i64",
1142 "f64": "clib_net_to_host_f64",
1146 def get_endian_string(o, type):
1147 """Return proper endian string conversion function"""
1150 return ENDIAN_STRINGS[type].replace("net_to_host", "host_to_net")
1153 return ENDIAN_STRINGS[type]
1156 def endianfun_array(o):
1157 """Generate endian functions for arrays"""
1160 ASSERT((u32){length} <= (u32)VL_API_MAX_ARRAY_SIZE);
1161 for (i = 0; i < {length}; i++) {{
1162 a->{name}[i] = {format}(a->{name}[i]);
1166 forloop_format = """\
1167 for (i = 0; i < {length}; i++) {{
1168 {type}_endian(&a->{name}[i]);
1172 to_network_comment = ""
1175 to_network_comment = """/*
1176 * Array fields processed first to handle variable length arrays and size
1177 * field endian conversion in the proper order for to-network messages.
1178 * Message fields have been sorted by type in the code generator, thus fields
1179 * in this generated code may be converted in a different order than specified
1180 * in the *.api file.
1186 if o.fieldtype == "u8" or o.fieldtype == "string" or o.fieldtype == "bool":
1187 output += " /* a->{n} = a->{n} (no-op) */\n".format(n=o.fieldname)
1189 lfield = "a->" + o.lengthfield if o.lengthfield else o.length
1190 if o.fieldtype in ENDIAN_STRINGS:
1191 output += forloop.format(
1192 comment=to_network_comment,
1194 format=get_endian_string(o, o.fieldtype),
1198 output += forloop_format.format(
1199 length=lfield, type=o.fieldtype, name=o.fieldname
1204 NO_ENDIAN_CONVERSION = {"client_index": None}
1207 def endianfun_obj(o):
1208 """Generate endian conversion function for type"""
1210 if o.type == "Array":
1211 return endianfun_array(o)
1212 if o.type != "Field":
1213 output += ' s = format(s, "\\n{} {} {} (print not implemented");\n'.format(
1214 o.type, o.fieldtype, o.fieldname
1217 if o.fieldname in NO_ENDIAN_CONVERSION:
1218 output += " /* a->{n} = a->{n} (no-op) */\n".format(n=o.fieldname)
1220 if o.fieldtype in ENDIAN_STRINGS:
1221 output += " a->{name} = {format}(a->{name});\n".format(
1222 name=o.fieldname, format=get_endian_string(o, o.fieldtype)
1224 elif o.fieldtype.startswith("vl_api_"):
1225 output += " {type}_endian(&a->{name});\n".format(
1226 type=o.fieldtype, name=o.fieldname
1229 output += " /* a->{n} = a->{n} (no-op) */\n".format(n=o.fieldname)
1234 def endianfun(objs, modulename):
1235 """Main entry point for endian function generation"""
1238 /****** Endian swap functions *****/\n\
1240 #ifndef included_{module}_endianfun
1241 #define included_{module}_endianfun
1243 #undef clib_net_to_host_uword
1244 #undef clib_host_to_net_uword
1246 #define clib_net_to_host_uword clib_net_to_host_u64
1247 #define clib_host_to_net_uword clib_host_to_net_u64
1249 #define clib_net_to_host_uword clib_net_to_host_u32
1250 #define clib_host_to_net_uword clib_host_to_net_u32
1254 output = output.format(module=modulename)
1257 static inline void vl_api_{name}_t_endian (vl_api_{name}_t *a)
1259 int i __attribute__((unused));
1263 # Outbound (to network) messages are identified by message nomenclature
1264 # i.e. message names ending with these suffixes are 'to network'
1265 if t.name.endswith("_reply") or t.name.endswith("_details"):
1268 t.to_network = False
1270 if t.__class__.__name__ == "Enum" or t.__class__.__name__ == "EnumFlag":
1271 output += signature.format(name=t.name)
1272 if t.enumtype in ENDIAN_STRINGS:
1273 output += " *a = {}(*a);\n".format(get_endian_string(t, t.enumtype))
1275 output += " /* a->{name} = a->{name} (no-op) */\n".format(
1283 output += "/***** manual: vl_api_%s_t_endian *****/\n\n" % t.name
1286 if t.__class__.__name__ == "Using":
1287 output += signature.format(name=t.name)
1288 if "length" in t.alias and t.alias["length"] and t.alias["type"] == "u8":
1289 output += " /* a->{name} = a->{name} (no-op) */\n".format(
1292 elif t.alias["type"] in FORMAT_STRINGS:
1293 output += " *a = {}(*a);\n".format(
1294 get_endian_string(t, t.alias["type"])
1297 output += " /* Not Implemented yet {} */".format(t.name)
1301 output += signature.format(name=t.name)
1303 # For outbound (to network) messages:
1304 # some arrays have dynamic length -- iterate over
1305 # them before changing endianness for the length field
1306 # by making the Array types show up first
1308 t.block.sort(key=lambda x: x.type)
1311 o.to_network = t.to_network
1312 output += endianfun_obj(o)
1315 output += "\n#endif"
1316 output += "\n#endif /* vl_endianfun */\n\n"
1321 def calc_size_fun(objs, modulename):
1322 """Main entry point for calculate size function generation"""
1325 /****** Calculate size functions *****/\n\
1326 #ifdef vl_calcsizefun
1327 #ifndef included_{module}_calcsizefun
1328 #define included_{module}_calcsizefun
1331 output = output.format(module=modulename)
1334 /* calculate message size of message in network byte order */
1335 static inline uword vl_api_{name}_t_calc_size (vl_api_{name}_t *a)
1340 tname = o.__class__.__name__
1342 output += signature.format(name=o.name)
1343 output += f" return sizeof(*a)"
1344 if tname == "Using":
1345 if "length" in o.alias:
1347 tmp = int(o.alias["length"])
1349 raise (f"Unexpected length '0' for alias {o}")
1351 # output += f" + vl_api_{o.alias.name}_t_calc_size({o.name})"
1357 elif tname == "Enum" or tname == "EnumFlag":
1361 if b.type == "Option":
1363 elif b.type == "Field":
1364 if b.fieldtype.startswith("vl_api_"):
1365 output += f" - sizeof(a->{b.fieldname})"
1366 output += f" + {b.fieldtype}_calc_size(&a->{b.fieldname})"
1367 elif b.type == "Array":
1370 filter(lambda x: x.fieldname == b.lengthfield, o.block)
1374 f"Expected 1 match for field '{b.lengthfield}', got '{m}'"
1377 if lf.fieldtype in ENDIAN_STRINGS:
1378 output += f" + {get_endian_string(b, lf.fieldtype)}(a->{b.lengthfield}) * sizeof(a->{b.fieldname}[0])"
1379 elif lf.fieldtype == "u8":
1381 f" + a->{b.lengthfield} * sizeof(a->{b.fieldname}[0])"
1385 f"Don't know how to endian swap {lf.fieldtype}"
1388 # Fixed length strings decay to nul terminated u8
1389 if b.fieldtype == "string":
1391 output += f" + vl_api_string_len(&a->{b.fieldname})"
1395 output += "\n#endif"
1396 output += "\n#endif /* vl_calcsizefun */\n\n"
1401 def version_tuple(s, module):
1402 """Generate semantic version string"""
1404 /****** Version tuple *****/
1406 #ifdef vl_api_version_tuple
1409 if "version" in s["Option"]:
1410 v = s["Option"]["version"]
1411 (major, minor, patch) = v.split(".")
1412 output += "vl_api_version_tuple(%s, %s, %s, %s)\n" % (
1419 output += "\n#endif /* vl_api_version_tuple */\n\n"
1424 def generate_include_enum(s, module, stream):
1425 """Generate <name>.api_enum.h"""
1426 write = stream.write
1429 write("typedef enum {\n")
1430 for t in s["Define"]:
1431 write(" VL_API_{},\n".format(t.name.upper()))
1432 write(" VL_MSG_{}_LAST\n".format(module.upper()))
1433 write("}} vl_api_{}_enum_t;\n".format(module))
1436 def generate_include_counters(s, stream):
1437 """Include file for the counter data model types."""
1438 write = stream.write
1441 csetname = counters.name
1442 write("typedef enum {\n")
1443 for c in counters.block:
1444 write(" {}_ERROR_{},\n".format(csetname.upper(), c["name"].upper()))
1445 write(" {}_N_ERROR\n".format(csetname.upper()))
1446 write("}} vl_counter_{}_enum_t;\n".format(csetname))
1448 write("extern vlib_error_desc_t {}_error_counters[];\n".format(csetname))
1451 def generate_include_types(s, module, stream):
1452 """Generate separate API _types file."""
1453 write = stream.write
1455 write("#ifndef included_{module}_api_types_h\n".format(module=module))
1456 write("#define included_{module}_api_types_h\n".format(module=module))
1458 if "version" in s["Option"]:
1459 v = s["Option"]["version"]
1460 (major, minor, patch) = v.split(".")
1462 "#define VL_API_{m}_API_VERSION_MAJOR {v}\n".format(
1463 m=module.upper(), v=major
1467 "#define VL_API_{m}_API_VERSION_MINOR {v}\n".format(
1468 m=module.upper(), v=minor
1472 "#define VL_API_{m}_API_VERSION_PATCH {v}\n".format(
1473 m=module.upper(), v=patch
1478 write("/* Imported API files */\n")
1479 for i in s["Import"]:
1480 filename = i.filename.replace("plugins/", "")
1481 write("#include <{}_types.h>\n".format(filename))
1483 for o in itertools.chain(s["types"], s["Define"]):
1484 tname = o.__class__.__name__
1485 if tname == "Using":
1486 if "length" in o.alias:
1488 "typedef %s vl_api_%s_t[%s];\n"
1489 % (o.alias["type"], o.name, o.alias["length"])
1492 write("typedef %s vl_api_%s_t;\n" % (o.alias["type"], o.name))
1493 elif tname == "Enum" or tname == "EnumFlag":
1494 if o.enumtype == "u32":
1495 write("typedef enum {\n")
1497 write("typedef enum __attribute__((packed)) {\n")
1500 write(" %s = %s,\n" % (b[0], b[1]))
1501 write("} vl_api_%s_t;\n" % o.name)
1502 if o.enumtype != "u32":
1503 size1 = "sizeof(vl_api_%s_t)" % o.name
1504 size2 = "sizeof(%s)" % o.enumtype
1505 err_str = "size of API enum %s is wrong" % o.name
1506 write('STATIC_ASSERT(%s == %s, "%s");\n' % (size1, size2, err_str))
1508 if tname == "Union":
1509 write("typedef union __attribute__ ((packed)) _vl_api_%s {\n" % o.name)
1512 ("typedef struct __attribute__ ((packed)) _vl_api_%s {\n") % o.name
1515 if b.type == "Option":
1517 if b.type == "Field":
1518 write(" %s %s;\n" % (api2c(b.fieldtype), b.fieldname))
1519 elif b.type == "Array":
1521 write(" %s %s[0];\n" % (api2c(b.fieldtype), b.fieldname))
1523 # Fixed length strings decay to nul terminated u8
1524 if b.fieldtype == "string":
1528 api2c(b.fieldtype), b.fieldname
1532 write(" u8 {}[{}];\n".format(b.fieldname, b.length))
1536 % (api2c(b.fieldtype), b.fieldname, b.length)
1540 "Error in processing type {} for {}".format(b, o.name)
1543 write("} vl_api_%s_t;\n" % o.name)
1545 f"#define VL_API_{o.name.upper()}_IS_CONSTANT_SIZE ({0 if o.vla else 1})\n\n"
1548 for t in s["Define"]:
1550 '#define VL_API_{ID}_CRC "{n}_{crc:08x}"\n'.format(
1551 n=t.name, ID=t.name.upper(), crc=t.crc
1558 def generate_c_boilerplate(services, defines, counters, file_crc, module, stream):
1559 """VPP side plugin."""
1560 write = stream.write
1561 define_hash = {d.name: d for d in defines}
1564 #define vl_endianfun /* define message structures */
1565 #include "{module}.api.h"
1568 #define vl_calcsizefun
1569 #include "{module}.api.h"
1570 #undef vl_calsizefun
1572 /* instantiate all the print functions we know about */
1574 #include "{module}.api.h"
1579 write(hdr.format(module=module))
1580 if len(defines) > 0:
1581 write("static u16\n")
1582 write("setup_message_id_table (void) {\n")
1583 write(" api_main_t *am = my_api_main;\n")
1584 write(" vl_msg_api_msg_config_t c;\n")
1586 ' u16 msg_id_base = vl_msg_api_get_msg_ids ("{}_{crc:08x}", '
1587 "VL_MSG_{m}_LAST);\n".format(module, crc=file_crc, m=module.upper())
1592 ' vl_msg_api_add_msg_name_crc (am, "{n}_{crc:08x}",\n'
1593 " VL_API_{ID} + msg_id_base);\n".format(
1594 n=d.name, ID=d.name.upper(), crc=d.crc
1598 d = define_hash[s.caller]
1600 " c = (vl_msg_api_msg_config_t) "
1601 " {{.id = VL_API_{ID} + msg_id_base,\n"
1603 " .handler = vl_api_{n}_t_handler,\n"
1604 " .endian = vl_api_{n}_t_endian,\n"
1605 " .format_fn = vl_api_{n}_t_format,\n"
1608 " .tojson = vl_api_{n}_t_tojson,\n"
1609 " .fromjson = vl_api_{n}_t_fromjson,\n"
1610 " .calc_size = vl_api_{n}_t_calc_size,\n"
1611 " .is_autoendian = {auto}}};\n".format(
1612 n=s.caller, ID=s.caller.upper(), auto=d.autoendian
1615 write(" vl_msg_api_config (&c);\n")
1617 d = define_hash[s.reply]
1619 " c = (vl_msg_api_msg_config_t) "
1620 "{{.id = VL_API_{ID} + msg_id_base,\n"
1623 " .endian = vl_api_{n}_t_endian,\n"
1624 " .format_fn = vl_api_{n}_t_format,\n"
1627 " .tojson = vl_api_{n}_t_tojson,\n"
1628 " .fromjson = vl_api_{n}_t_fromjson,\n"
1629 " .calc_size = vl_api_{n}_t_calc_size,\n"
1630 " .is_autoendian = {auto}}};\n".format(
1631 n=s.reply, ID=s.reply.upper(), auto=d.autoendian
1634 write(" vl_msg_api_config (&c);\n")
1640 d = define_hash[s.stream_message]
1642 " c = (vl_msg_api_msg_config_t) "
1643 "{{.id = VL_API_{ID} + msg_id_base,\n"
1646 " .endian = vl_api_{n}_t_endian,\n"
1647 " .format_fn = vl_api_{n}_t_format,\n"
1650 " .tojson = vl_api_{n}_t_tojson,\n"
1651 " .fromjson = vl_api_{n}_t_fromjson,\n"
1652 " .calc_size = vl_api_{n}_t_calc_size,\n"
1653 " .is_autoendian = {auto}}};\n".format(
1655 ID=s.stream_message.upper(),
1659 write(" vl_msg_api_config (&c);\n")
1662 if len(defines) > 0:
1663 write(" return msg_id_base;\n")
1667 "error": "VL_COUNTER_SEVERITY_ERROR",
1668 "info": "VL_COUNTER_SEVERITY_INFO",
1669 "warn": "VL_COUNTER_SEVERITY_WARN",
1672 for cnt in counters:
1674 write("vlib_error_desc_t {}_error_counters[] = {{\n".format(csetname))
1677 write(' .name = "{}",\n'.format(c["name"]))
1678 write(' .desc = "{}",\n'.format(c["description"]))
1679 write(" .severity = {},\n".format(severity[c["severity"]]))
1684 def generate_c_test_boilerplate(services, defines, file_crc, module, plugin, stream):
1685 """Generate code for legacy style VAT. To be deleted."""
1686 write = stream.write
1688 define_hash = {d.name: d for d in defines}
1691 #define vl_endianfun /* define message structures */
1692 #include "{module}.api.h"
1695 #define vl_calcsizefun
1696 #include "{module}.api.h"
1697 #undef vl_calsizefun
1699 /* instantiate all the print functions we know about */
1701 #include "{module}.api.h"
1706 write(hdr.format(module=module))
1709 d = define_hash[s.reply]
1715 " * Manual definition requested for: \n"
1716 " * vl_api_{n}_t_handler()\n"
1717 " */\n".format(n=s.reply)
1720 if not define_hash[s.caller].autoreply:
1722 "/* Generation not supported (vl_api_{n}_t_handler()) */\n".format(
1727 write("#ifndef VL_API_{n}_T_HANDLER\n".format(n=s.reply.upper()))
1728 write("static void\n")
1729 write("vl_api_{n}_t_handler (vl_api_{n}_t * mp) {{\n".format(n=s.reply))
1730 write(" vat_main_t * vam = {}_test_main.vat_main;\n".format(module))
1731 write(" i32 retval = ntohl(mp->retval);\n")
1732 write(" if (vam->async_mode) {\n")
1733 write(" vam->async_errors += (retval < 0);\n")
1734 write(" } else {\n")
1735 write(" vam->retval = retval;\n")
1736 write(" vam->result_ready = 1;\n")
1742 if define_hash[e].manual_print:
1744 write("static void\n")
1745 write("vl_api_{n}_t_handler (vl_api_{n}_t * mp) {{\n".format(n=e))
1746 write(' vlib_cli_output(0, "{n} event called:");\n'.format(n=e))
1748 ' vlib_cli_output(0, "%U", vl_api_{n}_t_format, mp);\n'.format(n=e)
1752 write("static void\n")
1753 write("setup_message_id_table (vat_main_t * vam, u16 msg_id_base) {\n")
1756 " vl_msg_api_config (&(vl_msg_api_msg_config_t){{\n"
1757 " .id = VL_API_{ID} + msg_id_base,\n"
1759 " .handler = vl_api_{n}_t_handler,\n"
1760 " .endian = vl_api_{n}_t_endian,\n"
1761 " .format_fn = vl_api_{n}_t_format,\n"
1762 " .size = sizeof(vl_api_{n}_t),\n"
1764 " .tojson = vl_api_{n}_t_tojson,\n"
1765 " .fromjson = vl_api_{n}_t_fromjson,\n"
1766 " .calc_size = vl_api_{n}_t_calc_size,\n"
1767 " }});".format(n=s.reply, ID=s.reply.upper())
1770 ' hash_set_mem (vam->function_by_name, "{n}", api_{n});\n'.format(
1776 ' hash_set_mem (vam->help_by_name, "{n}", "{help}");\n'.format(
1777 n=s.caller, help=define_hash[s.caller].options["vat_help"]
1786 " vl_msg_api_config (&(vl_msg_api_msg_config_t){{\n"
1787 " .id = VL_API_{ID} + msg_id_base,\n"
1789 " .handler = vl_api_{n}_t_handler,\n"
1790 " .endian = vl_api_{n}_t_endian,\n"
1791 " .format_fn = vl_api_{n}_t_format,\n"
1792 " .size = sizeof(vl_api_{n}_t),\n"
1794 " .tojson = vl_api_{n}_t_tojson,\n"
1795 " .fromjson = vl_api_{n}_t_fromjson,\n"
1796 " .calc_size = vl_api_{n}_t_calc_size,\n"
1797 " }});".format(n=e, ID=e.upper())
1801 write("clib_error_t * vat_plugin_register (vat_main_t *vam)\n")
1803 write(" {n}_test_main_t * mainp = &{n}_test_main;\n".format(n=module))
1804 write(" mainp->vat_main = vam;\n")
1806 " mainp->msg_id_base = vl_client_get_first_plugin_msg_id "
1807 ' ("{n}_{crc:08x}");\n'.format(n=module, crc=file_crc)
1809 write(" if (mainp->msg_id_base == (u16) ~0)\n")
1811 ' return clib_error_return (0, "{} plugin not loaded...");\n'.format(
1815 write(" setup_message_id_table (vam, mainp->msg_id_base);\n")
1816 write("#ifdef VL_API_LOCAL_SETUP_MESSAGE_ID_TABLE\n")
1817 write(" VL_API_LOCAL_SETUP_MESSAGE_ID_TABLE(vam);\n")
1819 write(" return 0;\n")
1824 """Check if a method is generated already."""
1826 def _f(module, d, processed, *args):
1827 if d.name in processed:
1829 processed[d.name] = True
1830 return func(module, d, *args)
1835 def c_test_api_service(s, dump, stream):
1836 """Generate JSON code for a service."""
1837 write = stream.write
1839 req_reply_template = """\
1846 mp = vl_api_{n}_t_fromjson(o, &len);
1848 fprintf(stderr, "Failed converting JSON to API\\n");
1852 mp->_vl_msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1853 vl_api_{n}_t_endian(mp);
1854 vac_write((char *)mp, len);
1860 vac_read(&p, &l, 5); // XXX: Fix timeout
1861 if (p == 0 || l == 0) return 0;
1862 // XXX Will fail in case of event received. Do loop
1863 if (ntohs(*((u16 *)p)) != vac_get_msg_index(VL_API_{R}_CRC)) {{
1864 fprintf(stderr, "Mismatched reply\\n");
1867 vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1868 vl_api_{r}_t_endian(rmp);
1869 return vl_api_{r}_t_tojson(rmp);
1873 dump_details_template = """\
1877 u16 msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1880 vl_api_{n}_t *mp = vl_api_{n}_t_fromjson(o, &len);
1882 fprintf(stderr, "Failed converting JSON to API\\n");
1885 mp->_vl_msg_id = msg_id;
1886 vl_api_{n}_t_endian(mp);
1887 vac_write((char *)mp, len);
1890 vat2_control_ping(123); // FIX CONTEXT
1891 cJSON *reply = cJSON_CreateArray();
1893 u16 ping_reply_msg_id = vac_get_msg_index(VL_API_CONTROL_PING_REPLY_CRC);
1894 u16 details_msg_id = vac_get_msg_index(VL_API_{R}_CRC);
1900 vac_read(&p, &l, 5); // XXX: Fix timeout
1901 if (p == 0 || l == 0) {{
1906 /* Message can be one of [_details, control_ping_reply
1907 * or unrelated event]
1909 u16 reply_msg_id = ntohs(*((u16 *)p));
1910 if (reply_msg_id == ping_reply_msg_id) {{
1914 if (reply_msg_id == details_msg_id) {{
1915 if (l < sizeof(vl_api_{r}_t)) {{
1919 vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1920 vl_api_{r}_t_endian(rmp);
1921 cJSON_AddItemToArray(reply, vl_api_{r}_t_tojson(rmp));
1928 gets_details_reply_template = """\
1932 u16 msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1935 vl_api_{n}_t *mp = vl_api_{n}_t_fromjson(o, &len);
1937 fprintf(stderr, "Failed converting JSON to API\\n");
1940 mp->_vl_msg_id = msg_id;
1942 vl_api_{n}_t_endian(mp);
1943 vac_write((char *)mp, len);
1946 cJSON *reply = cJSON_CreateArray();
1948 u16 reply_msg_id = vac_get_msg_index(VL_API_{R}_CRC);
1949 u16 details_msg_id = vac_get_msg_index(VL_API_{D}_CRC);
1955 vac_read(&p, &l, 5); // XXX: Fix timeout
1957 /* Message can be one of [_details, control_ping_reply
1958 * or unrelated event]
1960 u16 msg_id = ntohs(*((u16 *)p));
1961 if (msg_id == reply_msg_id) {{
1962 vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1963 vl_api_{r}_t_endian(rmp);
1964 cJSON_AddItemToArray(reply, vl_api_{r}_t_tojson(rmp));
1968 if (msg_id == details_msg_id) {{
1969 vl_api_{d}_t *rmp = (vl_api_{d}_t *)p;
1970 vl_api_{d}_t_endian(rmp);
1971 cJSON_AddItemToArray(reply, vl_api_{d}_t_tojson(rmp));
1980 if s.stream_message:
1982 gets_details_reply_template.format(
1988 D=s.stream_message.upper(),
1993 dump_details_template.format(
1994 n=s.caller, r=s.reply, N=s.caller.upper(), R=s.reply.upper()
1999 req_reply_template.format(
2000 n=s.caller, r=s.reply, N=s.caller.upper(), R=s.reply.upper()
2005 def generate_c_test2_boilerplate(services, defines, module, stream):
2006 """Generate code for VAT2 plugin."""
2007 write = stream.write
2009 define_hash = {d.name: d for d in defines}
2013 #include <vlibapi/api.h>
2014 #include <vlibmemory/api.h>
2015 #include <vppinfra/error.h>
2016 #include <vnet/ip/ip_format_fns.h>
2017 #include <vnet/ethernet/ethernet_format_fns.h>
2019 #define vl_typedefs /* define message structures */
2020 #include <vlibmemory/vl_memory_api_h.h>
2021 #include <vlibmemory/vlib.api_types.h>
2022 #include <vlibmemory/vlib.api.h>
2025 #include "{module}.api_enum.h"
2026 #include "{module}.api_types.h"
2028 #define vl_endianfun /* define message structures */
2029 #include "{module}.api.h"
2032 #define vl_calcsizefun
2033 #include "{module}.api.h"
2034 #undef vl_calsizefun
2037 #include "{module}.api.h"
2040 #include "{module}.api_tojson.h"
2041 #include "{module}.api_fromjson.h"
2042 #include <vpp-api/client/vppapiclient.h>
2044 #include <vat2/vat2_helpers.h>
2048 write(hdr.format(module=module))
2051 if s.reply not in define_hash:
2053 c_test_api_service(s, s.stream, stream)
2056 "void vat2_register_function(char *, cJSON * (*)(cJSON *), cJSON * (*)(void *), u32);\n"
2058 # write('__attribute__((constructor))')
2059 write("clib_error_t *\n")
2060 write("vat2_register_plugin (void) {\n")
2062 if s.reply not in define_hash:
2064 crc = define_hash[s.caller].crc
2066 ' vat2_register_function("{n}", api_{n}, (cJSON * (*)(void *))vl_api_{n}_t_tojson, 0x{crc:08x});\n'.format(
2070 write(" return 0;\n")
2075 # Plugin entry point
2077 def run(output_dir, apifilename, s):
2078 """Main plugin entry point."""
2082 sys.stderr.write("Missing --outputdir argument")
2085 basename = os.path.basename(apifilename)
2086 filename, _ = os.path.splitext(basename)
2087 modulename = filename.replace(".", "_")
2088 filename_enum = os.path.join(output_dir + "/" + basename + "_enum.h")
2089 filename_types = os.path.join(output_dir + "/" + basename + "_types.h")
2090 filename_c = os.path.join(output_dir + "/" + basename + ".c")
2091 filename_c_test = os.path.join(output_dir + "/" + basename + "_test.c")
2092 filename_c_test2 = os.path.join(output_dir + "/" + basename + "_test2.c")
2093 filename_c_tojson = os.path.join(output_dir + "/" + basename + "_tojson.h")
2094 filename_c_fromjson = os.path.join(output_dir + "/" + basename + "_fromjson.h")
2096 # Generate separate types file
2098 generate_include_types(s, modulename, st)
2099 with open(filename_types, "w") as fd:
2101 shutil.copyfileobj(st, fd)
2104 # Generate separate enum file
2106 st.write("#ifndef included_{}_api_enum_h\n".format(modulename))
2107 st.write("#define included_{}_api_enum_h\n".format(modulename))
2108 generate_include_enum(s, modulename, st)
2109 generate_include_counters(s["Counters"], st)
2110 st.write("#endif\n")
2111 with open(filename_enum, "w") as fd:
2113 shutil.copyfileobj(st, fd)
2116 # Generate separate C file
2118 generate_c_boilerplate(
2119 s["Service"], s["Define"], s["Counters"], s["file_crc"], modulename, st
2121 with open(filename_c, "w") as fd:
2123 shutil.copyfileobj(st, fd)
2126 # Generate separate C test file
2128 plugin = bool("plugin" in apifilename)
2129 generate_c_test_boilerplate(
2130 s["Service"], s["Define"], s["file_crc"], modulename, plugin, st
2132 with open(filename_c_test, "w") as fd:
2134 shutil.copyfileobj(st, fd)
2137 # Fully autogenerated VATv2 C test file
2139 generate_c_test2_boilerplate(s["Service"], s["Define"], modulename, st)
2140 with open(filename_c_test2, "w") as fd:
2142 shutil.copyfileobj(st, fd)
2145 # Generate separate JSON file
2147 generate_tojson(s, modulename, st)
2148 with open(filename_c_tojson, "w") as fd:
2150 shutil.copyfileobj(st, fd)
2153 generate_fromjson(s, modulename, st)
2154 with open(filename_c_fromjson, "w") as fd:
2156 shutil.copyfileobj(st, fd)
2159 output = TOP_BOILERPLATE.format(datestring=DATESTRING, input_filename=basename)
2160 output += generate_imports(s["Import"])
2161 output += msg_ids(s)
2162 output += msg_names(s)
2163 output += msg_name_crc_list(s, filename)
2164 output += typedefs(modulename)
2165 printfun_types(s["types"], stream, modulename)
2166 printfun(s["Define"], stream, modulename)
2167 output += stream.getvalue()
2169 output += endianfun(s["types"] + s["Define"], modulename)
2170 output += calc_size_fun(s["types"] + s["Define"], modulename)
2171 output += version_tuple(s, basename)
2172 output += BOTTOM_BOILERPLATE.format(input_filename=basename, file_crc=s["file_crc"])