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(
128 ' cJSON_AddStringToObject(o, "{n}", (char *)a->{n});\n'.format(
133 def print_field(self, o):
134 """Called for every field in a typedef or define."""
135 write = self.stream.write
136 if o.fieldname in self.noprint_fields:
139 f, p, newobj = self.get_json_func(o.fieldtype)
143 ' cJSON_AddItemToObject(o, "{n}", {f}({p}a->{n}));\n'.format(
144 f=f, p=p, n=o.fieldname
148 write(' {f}(o, "{n}", {p}a->{n});\n'.format(f=f, p=p, n=o.fieldname))
150 _dispatch["Field"] = print_field
152 def print_array(self, o):
153 """Converts a VPP API array to cJSON array."""
154 write = self.stream.write
159 cJSON *array = cJSON_AddArrayToObject(o, "{n}");
160 for (i = 0; i < {lfield}; i++) {{
161 cJSON_AddItemToArray(array, {f}({p}a->{n}[i]));
166 if o.fieldtype == "string":
170 lfield = "a->" + o.lengthfield if o.lengthfield else o.length
171 if o.fieldtype == "u8":
173 # What is length field doing here?
175 ' u8 *s = format(0, "0x%U", format_hex_bytes, '
176 "&a->{n}, {lfield});\n".format(n=o.fieldname, lfield=lfield)
179 ' cJSON_AddStringToObject(o, "{n}", (char *)s);\n'.format(
183 write(" vec_free(s);\n")
187 f, p = self.get_json_array_func(o.fieldtype)
188 write(forloop.format(lfield=lfield, t=o.fieldtype, n=o.fieldname, f=f, p=p))
190 _dispatch["Array"] = print_array
192 def print_enum(self, o):
193 """Create cJSON object (string) for VPP API enum"""
194 write = self.stream.write
196 "static inline cJSON *vl_api_{name}_t_tojson "
197 "(vl_api_{name}_t a) {{\n".format(name=o.name)
200 write(" switch(a) {\n")
202 write(" case %s:\n" % b[1])
203 write(' return cJSON_CreateString("{}");\n'.format(b[0]))
204 write(' default: return cJSON_CreateString("Invalid ENUM");\n')
206 write(" return 0;\n")
209 _dispatch["Enum"] = print_enum
211 def print_enum_flag(self, o):
212 """Create cJSON object (string) for VPP API enum"""
213 write = self.stream.write
215 "static inline cJSON *vl_api_{name}_t_tojson "
216 "(vl_api_{name}_t a) {{\n".format(name=o.name)
218 write(" cJSON *array = cJSON_CreateArray();\n")
223 write(" if (a & {})\n".format(b[0]))
225 ' cJSON_AddItemToArray(array, cJSON_CreateString("{}"));\n'.format(
229 write(" return array;\n")
232 _dispatch["EnumFlag"] = print_enum_flag
234 def print_typedef(self, o):
235 """Create cJSON (dictionary) object from VPP API typedef"""
236 write = self.stream.write
238 "static inline cJSON *vl_api_{name}_t_tojson "
239 "(vl_api_{name}_t *a) {{\n".format(name=o.name)
241 write(" cJSON *o = cJSON_CreateObject();\n")
244 self._dispatch[t.type](self, t)
246 write(" return o;\n")
249 def print_define(self, o):
250 """Create cJSON (dictionary) object from VPP API define"""
251 write = self.stream.write
253 "static inline cJSON *vl_api_{name}_t_tojson "
254 "(vl_api_{name}_t *a) {{\n".format(name=o.name)
256 write(" cJSON *o = cJSON_CreateObject();\n")
257 write(' cJSON_AddStringToObject(o, "_msgname", "{}");\n'.format(o.name))
259 ' cJSON_AddStringToObject(o, "_crc", "{crc:08x}");\n'.format(crc=o.crc)
263 self._dispatch[t.type](self, t)
265 write(" return o;\n")
268 def print_using(self, o):
269 """Create cJSON (dictionary) object from VPP API aliased type"""
273 write = self.stream.write
275 "static inline cJSON *vl_api_{name}_t_tojson "
276 "(vl_api_{name}_t *a) {{\n".format(name=o.name)
279 write(' u8 *s = format(0, "%U", format_vl_api_{}_t, a);\n'.format(o.name))
280 write(" cJSON *o = cJSON_CreateString((char *)s);\n")
281 write(" vec_free(s);\n")
282 write(" return o;\n")
285 _dispatch["Typedef"] = print_typedef
286 _dispatch["Define"] = print_define
287 _dispatch["Using"] = print_using
288 _dispatch["Union"] = print_typedef
290 def generate_function(self, t):
291 """Main entry point"""
292 write = self.stream.write
294 write("/* Manual print {} */\n".format(t.name))
296 self._dispatch[t.type](self, t)
298 def generate_types(self):
299 """Main entry point"""
301 self.generate_function(t)
303 def generate_defines(self):
304 """Main entry point"""
305 for t in self.defines:
306 self.generate_function(t)
311 Parse JSON objects into VPP API binary message structures.
315 noprint_fields = {"_vl_msg_id": None, "client_index": None, "context": None}
328 def __init__(self, module, types, defines, imported_types, stream):
331 self.defines = defines
333 self.types_hash = {"vl_api_" + d.name + "_t": d for d in types + imported_types}
334 self.defines_hash = {d.name: d for d in defines}
337 """Output the top boilerplate."""
338 write = self.stream.write
339 write("#ifndef included_{}_api_fromjson_h\n".format(self.module))
340 write("#define included_{}_api_fromjson_h\n".format(self.module))
341 write("#include <vppinfra/cJSON.h>\n\n")
342 write("#include <vppinfra/jsonformat.h>\n\n")
343 write('#pragma GCC diagnostic ignored "-Wunused-label"\n')
345 def is_base_type(self, t):
346 """Check if a type is one of the VPP API base types"""
347 if t in self.is_number:
354 """Output the bottom boilerplate."""
355 write = self.stream.write
358 def print_string(self, o, toplevel=False):
359 """Convert JSON string to vl_api_string_t"""
360 write = self.stream.write
362 msgvar = "a" if toplevel else "*mp"
363 msgsize = "l" if toplevel else "*len"
366 write(" char *p = cJSON_GetStringValue(item);\n")
367 write(" size_t plen = strlen(p);\n")
369 " {msgvar} = cJSON_realloc({msgvar}, {msgsize} + plen, {msgsize});\n".format(
370 msgvar=msgvar, msgsize=msgsize
373 write(" if ({msgvar} == 0) goto error;\n".format(msgvar=msgvar))
375 " vl_api_c_string_to_api_string(p, (void *){msgvar} + "
376 "{msgsize} - sizeof(vl_api_string_t));\n".format(
377 msgvar=msgvar, msgsize=msgsize
380 write(" {msgsize} += plen;\n".format(msgsize=msgsize))
383 " strncpy_s((char *)a->{n}, sizeof(a->{n}), "
384 "cJSON_GetStringValue(item), sizeof(a->{n}) - 1);\n".format(
389 def print_field(self, o, toplevel=False):
390 """Called for every field in a typedef or define."""
391 write = self.stream.write
392 if o.fieldname in self.noprint_fields:
394 is_bt = self.is_base_type(o.fieldtype)
395 t = "vl_api_{}".format(o.fieldtype) if is_bt else o.fieldtype
397 msgvar = "(void **)&a" if toplevel else "mp"
398 msgsize = "&l" if toplevel else "len"
402 " vl_api_{t}_fromjson(item, &a->{n});\n".format(
403 t=o.fieldtype, n=o.fieldname
408 " if ({t}_fromjson({msgvar}, "
409 "{msgsize}, item, &a->{n}) < 0) goto error;\n".format(
410 t=t, n=o.fieldname, msgvar=msgvar, msgsize=msgsize
414 _dispatch["Field"] = print_field
416 def print_array(self, o, toplevel=False):
417 """Convert JSON array to VPP API array"""
418 write = self.stream.write
423 cJSON *array = cJSON_GetObjectItem(o, "{n}");
424 int size = cJSON_GetArraySize(array);
425 if (size != {lfield}) goto error;
426 for (i = 0; i < size; i++) {{
427 cJSON *e = cJSON_GetArrayItem(array, i);
435 cJSON *array = cJSON_GetObjectItem(o, "{n}");
436 int size = cJSON_GetArraySize(array);
438 {realloc} = cJSON_realloc({realloc}, {msgsize} + sizeof({t}) * size, {msgsize});
439 {t} *d = (void *){realloc} + {msgsize};
440 {msgsize} += sizeof({t}) * size;
441 for (i = 0; i < size; i++) {{
442 cJSON *e = cJSON_GetArrayItem(array, i);
448 if o.fieldtype == "string":
449 self.print_string(o, toplevel)
452 lfield = "a->" + o.lengthfield if o.lengthfield else o.length
453 msgvar = "(void **)&a" if toplevel else "mp"
454 realloc = "a" if toplevel else "*mp"
455 msgsize = "l" if toplevel else "*len"
457 if o.fieldtype == "u8":
459 write(' s = u8string_fromjson(o, "{}");\n'.format(o.fieldname))
460 write(" if (!s) goto error;\n")
461 write(" {} = vec_len(s);\n".format(lfield))
464 " {realloc} = cJSON_realloc({realloc}, {msgsize} + "
465 "vec_len(s), {msgsize});\n".format(
466 msgvar=msgvar, msgsize=msgsize, realloc=realloc
470 " memcpy((void *){realloc} + {msgsize}, s, "
471 "vec_len(s));\n".format(realloc=realloc, msgsize=msgsize)
473 write(" {msgsize} += vec_len(s);\n".format(msgsize=msgsize))
475 write(" vec_free(s);\n")
478 ' if (u8string_fromjson2(o, "{n}", a->{n}) < 0) goto error;\n'.format(
484 is_bt = self.is_base_type(o.fieldtype)
488 call = "vl_api_{t}_fromjson(e, &d[i]);".format(t=o.fieldtype)
490 call = "if ({t}_fromjson({msgvar}, len, e, &d[i]) < 0) goto error; ".format(
491 t=o.fieldtype, msgvar=msgvar
505 call = "vl_api_{t}_fromjson(e, &a->{n}[i]);".format(t=t, n=o.fieldname)
507 call = "if ({}_fromjson({}, len, e, &a->{}[i]) < 0) goto error;".format(
508 t, msgvar, o.fieldname
522 _dispatch["Array"] = print_array
524 def print_enum(self, o):
525 """Convert to JSON enum(string) to VPP API enum (int)"""
526 write = self.stream.write
528 "static inline int vl_api_{n}_t_fromjson"
529 "(void **mp, int *len, cJSON *o, vl_api_{n}_t *a) {{\n".format(n=o.name)
531 write(" char *p = cJSON_GetStringValue(o);\n")
534 ' if (strcmp(p, "{}") == 0) {{*a = {}; return 0;}}\n'.format(
539 write(" return -1;\n")
542 _dispatch["Enum"] = print_enum
544 def print_enum_flag(self, o):
545 """Convert to JSON enum(string) to VPP API enum (int)"""
546 write = self.stream.write
548 "static inline int vl_api_{n}_t_fromjson "
549 "(void **mp, int *len, cJSON *o, vl_api_{n}_t *a) {{\n".format(n=o.name)
553 write(" for (i = 0; i < cJSON_GetArraySize(o); i++) {\n")
554 write(" cJSON *e = cJSON_GetArrayItem(o, i);\n")
555 write(" char *p = cJSON_GetStringValue(e);\n")
556 write(" if (!p) return -1;\n")
558 write(' if (strcmp(p, "{}") == 0) *a |= {};\n'.format(b[0], b[1]))
560 write(" return 0;\n")
563 _dispatch["EnumFlag"] = print_enum_flag
565 def print_typedef(self, o):
566 """Convert from JSON object to VPP API binary representation"""
567 write = self.stream.write
570 "static inline int vl_api_{name}_t_fromjson (void **mp, "
571 "int *len, cJSON *o, vl_api_{name}_t *a) {{\n".format(name=o.name)
573 write(" cJSON *item __attribute__ ((unused));\n")
574 write(" u8 *s __attribute__ ((unused));\n")
576 if t.type == "Field" and t.is_lengthfield:
578 write('\n item = cJSON_GetObjectItem(o, "{}");\n'.format(t.fieldname))
579 write(" if (!item) goto error;\n")
580 self._dispatch[t.type](self, t)
582 write("\n return 0;\n")
584 write(" return -1;\n")
587 def print_union(self, o):
588 """Convert JSON object to VPP API binary union"""
589 write = self.stream.write
592 "static inline int vl_api_{name}_t_fromjson (void **mp, "
593 "int *len, cJSON *o, vl_api_{name}_t *a) {{\n".format(name=o.name)
595 write(" cJSON *item __attribute__ ((unused));\n")
596 write(" u8 *s __attribute__ ((unused));\n")
598 if t.type == "Field" and t.is_lengthfield:
600 write(' item = cJSON_GetObjectItem(o, "{}");\n'.format(t.fieldname))
601 write(" if (item) {\n")
602 self._dispatch[t.type](self, t)
604 write("\n return 0;\n")
606 write(" return -1;\n")
609 def print_define(self, o):
610 """Convert JSON object to VPP API message"""
611 write = self.stream.write
614 "static inline vl_api_{name}_t *vl_api_{name}_t_fromjson "
615 "(cJSON *o, int *len) {{\n".format(name=o.name)
617 write(" cJSON *item __attribute__ ((unused));\n")
618 write(" u8 *s __attribute__ ((unused));\n")
619 write(" int l = sizeof(vl_api_{}_t);\n".format(o.name))
620 write(" vl_api_{}_t *a = cJSON_malloc(l);\n".format(o.name))
624 if t.fieldname in self.noprint_fields:
626 if t.type == "Field" and t.is_lengthfield:
628 write(' item = cJSON_GetObjectItem(o, "{}");\n'.format(t.fieldname))
629 write(" if (!item) goto error;\n")
631 self._dispatch[t.type](self, t, toplevel=True)
634 write(" *len = l;\n")
635 write(" return a;\n")
639 write(" cJSON_free(a);\n")
640 write(" return 0;\n")
643 def print_using(self, o):
644 """Convert JSON field to VPP type alias"""
645 write = self.stream.write
652 "static inline int vl_api_{name}_t_fromjson (void **mp, "
653 "int *len, cJSON *o, vl_api_{name}_t *a) {{\n".format(name=o.name)
655 if "length" in o.alias:
656 if t.fieldtype != "u8":
658 "Error in processing type {} for {}".format(t.fieldtype, o.name)
661 " vl_api_u8_string_fromjson(o, (u8 *)a, {});\n".format(
666 write(" vl_api_{t}_fromjson(o, ({t} *)a);\n".format(t=t.fieldtype))
668 write(" return 0;\n")
671 _dispatch["Typedef"] = print_typedef
672 _dispatch["Define"] = print_define
673 _dispatch["Using"] = print_using
674 _dispatch["Union"] = print_union
676 def generate_function(self, t):
677 """Main entry point"""
678 write = self.stream.write
680 write("/* Manual print {} */\n".format(t.name))
682 self._dispatch[t.type](self, t)
684 def generate_types(self):
685 """Main entry point"""
687 self.generate_function(t)
689 def generate_defines(self):
690 """Main entry point"""
691 for t in self.defines:
692 self.generate_function(t)
695 def generate_tojson(s, modulename, stream):
696 """Generate all functions to convert from API to JSON"""
699 write("/* Imported API files */\n")
700 for i in s["Import"]:
701 f = i.filename.replace("plugins/", "")
702 write("#include <{}_tojson.h>\n".format(f))
704 pp = ToJSON(modulename, s["types"], s["Define"], s["imported"]["types"], stream)
707 pp.generate_defines()
712 def generate_fromjson(s, modulename, stream):
713 """Generate all functions to convert from JSON to API"""
715 write("/* Imported API files */\n")
716 for i in s["Import"]:
717 f = i.filename.replace("plugins/", "")
718 write("#include <{}_fromjson.h>\n".format(f))
720 pp = FromJSON(modulename, s["types"], s["Define"], s["imported"]["types"], stream)
723 pp.generate_defines()
729 ###############################################################################
732 DATESTRING = datetime.datetime.utcfromtimestamp(
733 int(os.environ.get("SOURCE_DATE_EPOCH", time.time()))
735 TOP_BOILERPLATE = """\
737 * VLIB API definitions {datestring}
738 * Input file: {input_filename}
739 * Automatically generated: please edit the input file NOT this file!
743 #if defined(vl_msg_id)||defined(vl_union_id) \\
744 || defined(vl_printfun) ||defined(vl_endianfun) \\
745 || defined(vl_api_version)||defined(vl_typedefs) \\
746 || defined(vl_msg_name)||defined(vl_msg_name_crc_list) \\
747 || defined(vl_api_version_tuple) || defined(vl_calcsizefun)
748 /* ok, something was selected */
750 #warning no content included from {input_filename}
753 #define VL_API_PACKED(x) x __attribute__ ((packed))
756 BOTTOM_BOILERPLATE = """\
757 /****** API CRC (whole file) *****/
759 #ifdef vl_api_version
760 vl_api_version({input_filename}, {file_crc:#08x})
767 """Generate macro to map API message id to handler"""
770 /****** Message ID / handler enum ******/
775 for t in s["Define"]:
776 output += "vl_msg_id(VL_API_%s, vl_api_%s_t_handler)\n" % (
786 """Generate calls to name mapping macro"""
789 /****** Message names ******/
794 for t in s["Define"]:
795 dont_trace = 0 if t.dont_trace else 1
796 output += "vl_msg_name(vl_api_%s_t, %d)\n" % (t.name, dont_trace)
802 def msg_name_crc_list(s, suffix):
803 """Generate list of names to CRC mappings"""
806 /****** Message name, crc list ******/
808 #ifdef vl_msg_name_crc_list
810 output += "#define foreach_vl_msg_name_crc_%s " % suffix
812 for t in s["Define"]:
813 output += "\\\n_(VL_API_%s, %s, %08x) " % (t.name.upper(), t.name, t.crc)
819 def api2c(fieldtype):
820 """Map between API type names and internal VPP type names"""
822 "string": "vl_api_string_t",
824 if fieldtype in mappingtable:
825 return mappingtable[fieldtype]
829 def typedefs(filename):
830 """Include in the main files to the types file"""
833 /****** Typedefs ******/
836 #include "{include}.api_types.h"
859 """Functions for pretty printing VPP API messages"""
862 noprint_fields = {"_vl_msg_id": None, "client_index": None, "context": None}
864 def __init__(self, stream):
868 def print_string(o, stream):
869 """Pretty print a vl_api_string_t"""
872 write(" if (vl_api_string_len(&a->{f}) > 0) {{\n".format(f=o.fieldname))
874 ' s = format(s, "\\n%U{f}: %U", '
875 "format_white_space, indent, "
876 "vl_api_format_string, (&a->{f}));\n".format(f=o.fieldname)
880 ' s = format(s, "\\n%U{f}:", '
881 "format_white_space, indent);\n".format(f=o.fieldname)
886 ' s = format(s, "\\n%U{f}: %s", '
887 "format_white_space, indent, a->{f});\n".format(f=o.fieldname)
890 def print_field(self, o, stream):
891 """Pretty print API field"""
893 if o.fieldname in self.noprint_fields:
895 if o.fieldtype in FORMAT_STRINGS:
896 f = FORMAT_STRINGS[o.fieldtype]
898 ' s = format(s, "\\n%U{n}: {f}", '
899 "format_white_space, indent, a->{n});\n".format(n=o.fieldname, f=f)
903 ' s = format(s, "\\n%U{n}: %U", '
904 "format_white_space, indent, "
905 "format_{t}, &a->{n}, indent);\n".format(n=o.fieldname, t=o.fieldtype)
908 _dispatch["Field"] = print_field
910 def print_array(self, o, stream):
911 """Pretty print API array"""
915 for (i = 0; i < {lfield}; i++) {{
916 s = format(s, "\\n%U{n}: %U",
917 format_white_space, indent, format_{t}, &a->{n}[i], indent);
921 forloop_format = """\
922 for (i = 0; i < {lfield}; i++) {{
923 s = format(s, "\\n%U{n}: {t}",
924 format_white_space, indent, a->{n}[i]);
928 if o.fieldtype == "string":
929 self.print_string(o, stream)
932 if o.fieldtype == "u8":
935 ' s = format(s, "\\n%U{n}: %U", format_white_space, '
936 "indent, format_hex_bytes, a->{n}, a->{lfield});\n".format(
937 n=o.fieldname, lfield=o.lengthfield
942 ' s = format(s, "\\n%U{n}: %U", format_white_space, '
943 "indent, format_hex_bytes, a, {lfield});\n".format(
944 n=o.fieldname, lfield=o.length
949 lfield = "a->" + o.lengthfield if o.lengthfield else o.length
950 if o.fieldtype in FORMAT_STRINGS:
952 forloop_format.format(
953 lfield=lfield, t=FORMAT_STRINGS[o.fieldtype], n=o.fieldname
957 write(forloop.format(lfield=lfield, t=o.fieldtype, n=o.fieldname))
959 _dispatch["Array"] = print_array
962 def print_alias(k, v, stream):
963 """Pretty print type alias"""
965 if "length" in v.alias and v.alias["length"] and v.alias["type"] == "u8":
967 ' return format(s, "%U", format_hex_bytes, a, {});\n'.format(
971 elif v.alias["type"] in FORMAT_STRINGS:
973 ' return format(s, "{}", *a);\n'.format(
974 FORMAT_STRINGS[v.alias["type"]]
978 write(' return format(s, "{} (print not implemented)");\n'.format(k))
981 def print_enum(o, stream):
982 """Pretty print API enum"""
984 write(" switch(*a) {\n")
986 write(" case %s:\n" % b[1])
987 write(' return format(s, "{}");\n'.format(b[0]))
990 _dispatch["Enum"] = print_enum
991 _dispatch["EnumFlag"] = print_enum
993 def print_obj(self, o, stream):
997 if o.type in self._dispatch:
998 self._dispatch[o.type](self, o, stream)
1001 ' s = format(s, "\\n{} {} {} (print not implemented");\n'.format(
1002 o.type, o.fieldtype, o.fieldname
1007 def printfun(objs, stream, modulename):
1008 """Main entry point for pretty print function generation"""
1009 write = stream.write
1012 /****** Print functions *****/
1014 #ifndef included_{module}_printfun
1015 #define included_{module}_printfun
1018 #define _uword_fmt \"%lld\"
1019 #define _uword_cast (long long)
1021 #define _uword_fmt \"%ld\"
1022 #define _uword_cast long
1025 #include "{module}.api_tojson.h"
1026 #include "{module}.api_fromjson.h"
1031 static inline void *vl_api_{name}_t_print{suffix} (vl_api_{name}_t *a, void *handle)
1034 u32 indent __attribute__((unused)) = 2;
1035 int i __attribute__((unused));
1038 h = h.format(module=modulename)
1041 pp = Printfun(stream)
1044 write("/***** manual: vl_api_%s_t_print *****/\n\n" % t.name)
1046 write(signature.format(name=t.name, suffix=""))
1047 write(" /* Message definition: vl_api_{}_t: */\n".format(t.name))
1048 write(' s = format(s, "vl_api_%s_t:");\n' % t.name)
1050 pp.print_obj(o, stream)
1051 write(" vec_add1(s, 0);\n")
1052 write(" vl_print (handle, (char *)s);\n")
1053 write(" vec_free (s);\n")
1054 write(" return handle;\n")
1057 write(signature.format(name=t.name, suffix="_json"))
1058 write(" cJSON * o = vl_api_{}_t_tojson(a);\n".format(t.name))
1059 write(" (void)s;\n")
1060 write(" char *out = cJSON_Print(o);\n")
1061 write(" vl_print(handle, out);\n")
1062 write(" cJSON_Delete(o);\n")
1063 write(" cJSON_free(out);\n")
1064 write(" return handle;\n")
1068 write("\n#endif /* vl_printfun */\n")
1073 def printfun_types(objs, stream, modulename):
1074 """Pretty print API types"""
1075 write = stream.write
1076 pp = Printfun(stream)
1079 /****** Print functions *****/
1081 #ifndef included_{module}_printfun_types
1082 #define included_{module}_printfun_types
1085 h = h.format(module=modulename)
1089 static inline u8 *format_vl_api_{name}_t (u8 *s, va_list * args)
1091 vl_api_{name}_t *a = va_arg (*args, vl_api_{name}_t *);
1092 u32 indent __attribute__((unused)) = va_arg (*args, u32);
1093 int i __attribute__((unused));
1098 if t.__class__.__name__ == "Enum" or t.__class__.__name__ == "EnumFlag":
1099 write(signature.format(name=t.name))
1100 pp.print_enum(t.block, stream)
1101 write(" return s;\n")
1106 write("/***** manual: vl_api_%s_t_print *****/\n\n" % t.name)
1109 if t.__class__.__name__ == "Using":
1110 write(signature.format(name=t.name))
1111 pp.print_alias(t.name, t, stream)
1115 write(signature.format(name=t.name))
1117 pp.print_obj(o, stream)
1119 write(" return s;\n")
1123 write("\n#endif /* vl_printfun_types */\n")
1126 def generate_imports(imports):
1127 """Add #include matching the API import statements"""
1128 output = "/* Imported API files */\n"
1129 output += "#ifndef vl_api_version\n"
1132 s = i.filename.replace("plugins/", "")
1133 output += "#include <{}.h>\n".format(s)
1134 output += "#endif\n"
1139 "u16": "clib_net_to_host_u16",
1140 "u32": "clib_net_to_host_u32",
1141 "u64": "clib_net_to_host_u64",
1142 "i16": "clib_net_to_host_i16",
1143 "i32": "clib_net_to_host_i32",
1144 "i64": "clib_net_to_host_i64",
1145 "f64": "clib_net_to_host_f64",
1149 def endianfun_array(o):
1150 """Generate endian functions for arrays"""
1152 for (i = 0; i < {length}; i++) {{
1153 a->{name}[i] = {format}(a->{name}[i]);
1157 forloop_format = """\
1158 for (i = 0; i < {length}; i++) {{
1159 {type}_endian(&a->{name}[i]);
1164 if o.fieldtype == "u8" or o.fieldtype == "string" or o.fieldtype == "bool":
1165 output += " /* a->{n} = a->{n} (no-op) */\n".format(n=o.fieldname)
1167 lfield = "a->" + o.lengthfield if o.lengthfield else o.length
1168 if o.fieldtype in ENDIAN_STRINGS:
1169 output += forloop.format(
1170 length=lfield, format=ENDIAN_STRINGS[o.fieldtype], name=o.fieldname
1173 output += forloop_format.format(
1174 length=lfield, type=o.fieldtype, name=o.fieldname
1179 NO_ENDIAN_CONVERSION = {"client_index": None}
1182 def endianfun_obj(o):
1183 """Generate endian conversion function for type"""
1185 if o.type == "Array":
1186 return endianfun_array(o)
1187 if o.type != "Field":
1188 output += ' s = format(s, "\\n{} {} {} (print not implemented");\n'.format(
1189 o.type, o.fieldtype, o.fieldname
1192 if o.fieldname in NO_ENDIAN_CONVERSION:
1193 output += " /* a->{n} = a->{n} (no-op) */\n".format(n=o.fieldname)
1195 if o.fieldtype in ENDIAN_STRINGS:
1196 output += " a->{name} = {format}(a->{name});\n".format(
1197 name=o.fieldname, format=ENDIAN_STRINGS[o.fieldtype]
1199 elif o.fieldtype.startswith("vl_api_"):
1200 output += " {type}_endian(&a->{name});\n".format(
1201 type=o.fieldtype, name=o.fieldname
1204 output += " /* a->{n} = a->{n} (no-op) */\n".format(n=o.fieldname)
1209 def endianfun(objs, modulename):
1210 """Main entry point for endian function generation"""
1213 /****** Endian swap functions *****/\n\
1215 #ifndef included_{module}_endianfun
1216 #define included_{module}_endianfun
1218 #undef clib_net_to_host_uword
1220 #define clib_net_to_host_uword clib_net_to_host_u64
1222 #define clib_net_to_host_uword clib_net_to_host_u32
1226 output = output.format(module=modulename)
1229 static inline void vl_api_{name}_t_endian (vl_api_{name}_t *a)
1231 int i __attribute__((unused));
1235 if t.__class__.__name__ == "Enum" or t.__class__.__name__ == "EnumFlag":
1236 output += signature.format(name=t.name)
1237 if t.enumtype in ENDIAN_STRINGS:
1238 output += " *a = {}(*a);\n".format(ENDIAN_STRINGS[t.enumtype])
1240 output += " /* a->{name} = a->{name} (no-op) */\n".format(
1248 output += "/***** manual: vl_api_%s_t_endian *****/\n\n" % t.name
1251 if t.__class__.__name__ == "Using":
1252 output += signature.format(name=t.name)
1253 if "length" in t.alias and t.alias["length"] and t.alias["type"] == "u8":
1254 output += " /* a->{name} = a->{name} (no-op) */\n".format(
1257 elif t.alias["type"] in FORMAT_STRINGS:
1258 output += " *a = {}(*a);\n".format(ENDIAN_STRINGS[t.alias["type"]])
1260 output += " /* Not Implemented yet {} */".format(t.name)
1264 output += signature.format(name=t.name)
1267 output += endianfun_obj(o)
1270 output += "\n#endif"
1271 output += "\n#endif /* vl_endianfun */\n\n"
1276 def calc_size_fun(objs, modulename):
1277 """Main entry point for calculate size function generation"""
1280 /****** Calculate size functions *****/\n\
1281 #ifdef vl_calcsizefun
1282 #ifndef included_{module}_calcsizefun
1283 #define included_{module}_calcsizefun
1286 output = output.format(module=modulename)
1289 /* calculate message size of message in network byte order */
1290 static inline uword vl_api_{name}_t_calc_size (vl_api_{name}_t *a)
1295 tname = o.__class__.__name__
1297 output += signature.format(name=o.name)
1298 output += f" return sizeof(*a)"
1299 if tname == "Using":
1300 if "length" in o.alias:
1302 tmp = int(o.alias["length"])
1304 raise (f"Unexpected length '0' for alias {o}")
1306 # output += f" + vl_api_{o.alias.name}_t_calc_size({o.name})"
1312 elif tname == "Enum" or tname == "EnumFlag":
1316 if b.type == "Option":
1318 elif b.type == "Field":
1319 if b.fieldtype.startswith("vl_api_"):
1320 output += f" - sizeof(a->{b.fieldname})"
1321 output += f" + {b.fieldtype}_calc_size(&a->{b.fieldname})"
1322 elif b.type == "Array":
1325 filter(lambda x: x.fieldname == b.lengthfield, o.block)
1329 f"Expected 1 match for field '{b.lengthfield}', got '{m}'"
1332 if lf.fieldtype in ENDIAN_STRINGS:
1333 output += f" + {ENDIAN_STRINGS[lf.fieldtype]}(a->{b.lengthfield}) * sizeof(a->{b.fieldname}[0])"
1334 elif lf.fieldtype == "u8":
1336 f" + a->{b.lengthfield} * sizeof(a->{b.fieldname}[0])"
1340 f"Don't know how to endian swap {lf.fieldtype}"
1343 # Fixed length strings decay to nul terminated u8
1344 if b.fieldtype == "string":
1346 output += f" + vl_api_string_len(&a->{b.fieldname})"
1350 output += "\n#endif"
1351 output += "\n#endif /* vl_calcsizefun */\n\n"
1356 def version_tuple(s, module):
1357 """Generate semantic version string"""
1359 /****** Version tuple *****/
1361 #ifdef vl_api_version_tuple
1364 if "version" in s["Option"]:
1365 v = s["Option"]["version"]
1366 (major, minor, patch) = v.split(".")
1367 output += "vl_api_version_tuple(%s, %s, %s, %s)\n" % (
1374 output += "\n#endif /* vl_api_version_tuple */\n\n"
1379 def generate_include_enum(s, module, stream):
1380 """Generate <name>.api_enum.h"""
1381 write = stream.write
1384 write("typedef enum {\n")
1385 for t in s["Define"]:
1386 write(" VL_API_{},\n".format(t.name.upper()))
1387 write(" VL_MSG_{}_LAST\n".format(module.upper()))
1388 write("}} vl_api_{}_enum_t;\n".format(module))
1391 def generate_include_counters(s, stream):
1392 """Include file for the counter data model types."""
1393 write = stream.write
1396 csetname = counters.name
1397 write("typedef enum {\n")
1398 for c in counters.block:
1399 write(" {}_ERROR_{},\n".format(csetname.upper(), c["name"].upper()))
1400 write(" {}_N_ERROR\n".format(csetname.upper()))
1401 write("}} vl_counter_{}_enum_t;\n".format(csetname))
1403 write("extern vlib_error_desc_t {}_error_counters[];\n".format(csetname))
1406 def generate_include_types(s, module, stream):
1407 """Generate separate API _types file."""
1408 write = stream.write
1410 write("#ifndef included_{module}_api_types_h\n".format(module=module))
1411 write("#define included_{module}_api_types_h\n".format(module=module))
1413 if "version" in s["Option"]:
1414 v = s["Option"]["version"]
1415 (major, minor, patch) = v.split(".")
1417 "#define VL_API_{m}_API_VERSION_MAJOR {v}\n".format(
1418 m=module.upper(), v=major
1422 "#define VL_API_{m}_API_VERSION_MINOR {v}\n".format(
1423 m=module.upper(), v=minor
1427 "#define VL_API_{m}_API_VERSION_PATCH {v}\n".format(
1428 m=module.upper(), v=patch
1433 write("/* Imported API files */\n")
1434 for i in s["Import"]:
1435 filename = i.filename.replace("plugins/", "")
1436 write("#include <{}_types.h>\n".format(filename))
1438 for o in itertools.chain(s["types"], s["Define"]):
1439 tname = o.__class__.__name__
1440 if tname == "Using":
1441 if "length" in o.alias:
1443 "typedef %s vl_api_%s_t[%s];\n"
1444 % (o.alias["type"], o.name, o.alias["length"])
1447 write("typedef %s vl_api_%s_t;\n" % (o.alias["type"], o.name))
1448 elif tname == "Enum" or tname == "EnumFlag":
1449 if o.enumtype == "u32":
1450 write("typedef enum {\n")
1452 write("typedef enum __attribute__((packed)) {\n")
1455 write(" %s = %s,\n" % (b[0], b[1]))
1456 write("} vl_api_%s_t;\n" % o.name)
1457 if o.enumtype != "u32":
1458 size1 = "sizeof(vl_api_%s_t)" % o.name
1459 size2 = "sizeof(%s)" % o.enumtype
1460 err_str = "size of API enum %s is wrong" % o.name
1461 write('STATIC_ASSERT(%s == %s, "%s");\n' % (size1, size2, err_str))
1463 if tname == "Union":
1464 write("typedef union __attribute__ ((packed)) _vl_api_%s {\n" % o.name)
1467 ("typedef struct __attribute__ ((packed)) _vl_api_%s {\n") % o.name
1470 if b.type == "Option":
1472 if b.type == "Field":
1473 write(" %s %s;\n" % (api2c(b.fieldtype), b.fieldname))
1474 elif b.type == "Array":
1476 write(" %s %s[0];\n" % (api2c(b.fieldtype), b.fieldname))
1478 # Fixed length strings decay to nul terminated u8
1479 if b.fieldtype == "string":
1483 api2c(b.fieldtype), b.fieldname
1487 write(" u8 {}[{}];\n".format(b.fieldname, b.length))
1491 % (api2c(b.fieldtype), b.fieldname, b.length)
1495 "Error in processing type {} for {}".format(b, o.name)
1498 write("} vl_api_%s_t;\n" % o.name)
1500 f"#define VL_API_{o.name.upper()}_IS_CONSTANT_SIZE ({0 if o.vla else 1})\n\n"
1503 for t in s["Define"]:
1505 '#define VL_API_{ID}_CRC "{n}_{crc:08x}"\n'.format(
1506 n=t.name, ID=t.name.upper(), crc=t.crc
1513 def generate_c_boilerplate(services, defines, counters, file_crc, module, stream):
1514 """VPP side plugin."""
1515 write = stream.write
1516 define_hash = {d.name: d for d in defines}
1519 #define vl_endianfun /* define message structures */
1520 #include "{module}.api.h"
1523 #define vl_calcsizefun
1524 #include "{module}.api.h"
1525 #undef vl_calsizefun
1527 /* instantiate all the print functions we know about */
1528 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
1530 #include "{module}.api.h"
1535 write(hdr.format(module=module))
1536 write("static u16\n")
1537 write("setup_message_id_table (void) {\n")
1538 write(" api_main_t *am = my_api_main;\n")
1539 write(" vl_msg_api_msg_config_t c;\n")
1541 ' u16 msg_id_base = vl_msg_api_get_msg_ids ("{}_{crc:08x}", '
1542 "VL_MSG_{m}_LAST);\n".format(module, crc=file_crc, m=module.upper())
1547 ' vl_msg_api_add_msg_name_crc (am, "{n}_{crc:08x}",\n'
1548 " VL_API_{ID} + msg_id_base);\n".format(
1549 n=d.name, ID=d.name.upper(), crc=d.crc
1553 d = define_hash[s.caller]
1555 " c = (vl_msg_api_msg_config_t) "
1556 " {{.id = VL_API_{ID} + msg_id_base,\n"
1558 " .handler = vl_api_{n}_t_handler,\n"
1559 " .cleanup = vl_noop_handler,\n"
1560 " .endian = vl_api_{n}_t_endian,\n"
1561 " .print = vl_api_{n}_t_print,\n"
1564 " .print_json = vl_api_{n}_t_print_json,\n"
1565 " .tojson = vl_api_{n}_t_tojson,\n"
1566 " .fromjson = vl_api_{n}_t_fromjson,\n"
1567 " .calc_size = vl_api_{n}_t_calc_size,\n"
1568 " .is_autoendian = {auto}}};\n".format(
1569 n=s.caller, ID=s.caller.upper(), auto=d.autoendian
1572 write(" vl_msg_api_config (&c);\n")
1574 d = define_hash[s.reply]
1576 " c = (vl_msg_api_msg_config_t) "
1577 "{{.id = VL_API_{ID} + msg_id_base,\n"
1580 " .cleanup = vl_noop_handler,\n"
1581 " .endian = vl_api_{n}_t_endian,\n"
1582 " .print = vl_api_{n}_t_print,\n"
1585 " .print_json = vl_api_{n}_t_print_json,\n"
1586 " .tojson = vl_api_{n}_t_tojson,\n"
1587 " .fromjson = vl_api_{n}_t_fromjson,\n"
1588 " .calc_size = vl_api_{n}_t_calc_size,\n"
1589 " .is_autoendian = {auto}}};\n".format(
1590 n=s.reply, ID=s.reply.upper(), auto=d.autoendian
1593 write(" vl_msg_api_config (&c);\n")
1597 write(" return msg_id_base;\n")
1601 "error": "VL_COUNTER_SEVERITY_ERROR",
1602 "info": "VL_COUNTER_SEVERITY_INFO",
1603 "warn": "VL_COUNTER_SEVERITY_WARN",
1606 for cnt in counters:
1608 write("vlib_error_desc_t {}_error_counters[] = {{\n".format(csetname))
1611 write(' .name = "{}",\n'.format(c["name"]))
1612 write(' .desc = "{}",\n'.format(c["description"]))
1613 write(" .severity = {},\n".format(severity[c["severity"]]))
1618 def generate_c_test_boilerplate(services, defines, file_crc, module, plugin, stream):
1619 """Generate code for legacy style VAT. To be deleted."""
1620 write = stream.write
1622 define_hash = {d.name: d for d in defines}
1625 #define vl_endianfun /* define message structures */
1626 #include "{module}.api.h"
1629 #define vl_calcsizefun
1630 #include "{module}.api.h"
1631 #undef vl_calsizefun
1633 /* instantiate all the print functions we know about */
1634 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
1636 #include "{module}.api.h"
1641 write(hdr.format(module=module))
1644 d = define_hash[s.reply]
1650 " * Manual definition requested for: \n"
1651 " * vl_api_{n}_t_handler()\n"
1652 " */\n".format(n=s.reply)
1655 if not define_hash[s.caller].autoreply:
1657 "/* Generation not supported (vl_api_{n}_t_handler()) */\n".format(
1662 write("#ifndef VL_API_{n}_T_HANDLER\n".format(n=s.reply.upper()))
1663 write("static void\n")
1664 write("vl_api_{n}_t_handler (vl_api_{n}_t * mp) {{\n".format(n=s.reply))
1665 write(" vat_main_t * vam = {}_test_main.vat_main;\n".format(module))
1666 write(" i32 retval = ntohl(mp->retval);\n")
1667 write(" if (vam->async_mode) {\n")
1668 write(" vam->async_errors += (retval < 0);\n")
1669 write(" } else {\n")
1670 write(" vam->retval = retval;\n")
1671 write(" vam->result_ready = 1;\n")
1677 if define_hash[e].manual_print:
1679 write("static void\n")
1680 write("vl_api_{n}_t_handler (vl_api_{n}_t * mp) {{\n".format(n=e))
1681 write(' vl_print(0, "{n} event called:");\n'.format(n=e))
1682 write(" vl_api_{n}_t_print(mp, 0);\n".format(n=e))
1685 write("static void\n")
1686 write("setup_message_id_table (vat_main_t * vam, u16 msg_id_base) {\n")
1689 " vl_msg_api_set_handlers(VL_API_{ID} + msg_id_base, "
1691 " vl_api_{n}_t_handler, "
1692 " vl_noop_handler,\n"
1693 " vl_api_{n}_t_endian, "
1694 " vl_api_{n}_t_print,\n"
1695 " sizeof(vl_api_{n}_t), 1,\n"
1696 " vl_api_{n}_t_print_json,\n"
1697 " vl_api_{n}_t_tojson,\n"
1698 " vl_api_{n}_t_fromjson,\n"
1699 " vl_api_{n}_t_calc_size);\n".format(
1700 n=s.reply, ID=s.reply.upper()
1704 ' hash_set_mem (vam->function_by_name, "{n}", api_{n});\n'.format(
1710 ' hash_set_mem (vam->help_by_name, "{n}", "{help}");\n'.format(
1711 n=s.caller, help=define_hash[s.caller].options["vat_help"]
1720 " vl_msg_api_set_handlers(VL_API_{ID} + msg_id_base, "
1722 " vl_api_{n}_t_handler, "
1723 " vl_noop_handler,\n"
1724 " vl_api_{n}_t_endian, "
1725 " vl_api_{n}_t_print,\n"
1726 " sizeof(vl_api_{n}_t), 1,\n"
1727 " vl_api_{n}_t_print_json,\n"
1728 " vl_api_{n}_t_tojson,\n"
1729 " vl_api_{n}_t_fromjson,\n"
1730 " vl_api_{n}_t_calc_size);\n".format(
1736 write("clib_error_t * vat_plugin_register (vat_main_t *vam)\n")
1738 write(" {n}_test_main_t * mainp = &{n}_test_main;\n".format(n=module))
1739 write(" mainp->vat_main = vam;\n")
1741 " mainp->msg_id_base = vl_client_get_first_plugin_msg_id "
1742 ' ("{n}_{crc:08x}");\n'.format(n=module, crc=file_crc)
1744 write(" if (mainp->msg_id_base == (u16) ~0)\n")
1746 ' return clib_error_return (0, "{} plugin not loaded...");\n'.format(
1750 write(" setup_message_id_table (vam, mainp->msg_id_base);\n")
1751 write("#ifdef VL_API_LOCAL_SETUP_MESSAGE_ID_TABLE\n")
1752 write(" VL_API_LOCAL_SETUP_MESSAGE_ID_TABLE(vam);\n")
1754 write(" return 0;\n")
1759 """Check if a method is generated already."""
1761 def _f(module, d, processed, *args):
1762 if d.name in processed:
1764 processed[d.name] = True
1765 return func(module, d, *args)
1770 def c_test_api_service(s, dump, stream):
1771 """Generate JSON code for a service."""
1772 write = stream.write
1774 req_reply_template = """\
1781 mp = vl_api_{n}_t_fromjson(o, &len);
1783 fprintf(stderr, "Failed converting JSON to API\\n");
1787 mp->_vl_msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1788 vl_api_{n}_t_endian(mp);
1789 vac_write((char *)mp, len);
1795 vac_read(&p, &l, 5); // XXX: Fix timeout
1796 if (p == 0 || l == 0) return 0;
1797 // XXX Will fail in case of event received. Do loop
1798 if (ntohs(*((u16 *)p)) != vac_get_msg_index(VL_API_{R}_CRC)) {{
1799 fprintf(stderr, "Mismatched reply\\n");
1802 vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1803 vl_api_{r}_t_endian(rmp);
1804 return vl_api_{r}_t_tojson(rmp);
1808 dump_details_template = """\
1812 u16 msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1815 vl_api_{n}_t *mp = vl_api_{n}_t_fromjson(o, &len);
1817 fprintf(stderr, "Failed converting JSON to API\\n");
1820 mp->_vl_msg_id = msg_id;
1821 vl_api_{n}_t_endian(mp);
1822 vac_write((char *)mp, len);
1825 vat2_control_ping(123); // FIX CONTEXT
1826 cJSON *reply = cJSON_CreateArray();
1828 u16 ping_reply_msg_id = vac_get_msg_index(VL_API_CONTROL_PING_REPLY_CRC);
1829 u16 details_msg_id = vac_get_msg_index(VL_API_{R}_CRC);
1835 vac_read(&p, &l, 5); // XXX: Fix timeout
1836 if (p == 0 || l == 0) {{
1841 /* Message can be one of [_details, control_ping_reply
1842 * or unrelated event]
1844 u16 reply_msg_id = ntohs(*((u16 *)p));
1845 if (reply_msg_id == ping_reply_msg_id) {{
1849 if (reply_msg_id == details_msg_id) {{
1850 if (l < sizeof(vl_api_{r}_t)) {{
1854 vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1855 vl_api_{r}_t_endian(rmp);
1856 cJSON_AddItemToArray(reply, vl_api_{r}_t_tojson(rmp));
1863 gets_details_reply_template = """\
1867 u16 msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1870 vl_api_{n}_t *mp = vl_api_{n}_t_fromjson(o, &len);
1872 fprintf(stderr, "Failed converting JSON to API\\n");
1875 mp->_vl_msg_id = msg_id;
1877 vl_api_{n}_t_endian(mp);
1878 vac_write((char *)mp, len);
1881 cJSON *reply = cJSON_CreateArray();
1883 u16 reply_msg_id = vac_get_msg_index(VL_API_{R}_CRC);
1884 u16 details_msg_id = vac_get_msg_index(VL_API_{D}_CRC);
1890 vac_read(&p, &l, 5); // XXX: Fix timeout
1892 /* Message can be one of [_details, control_ping_reply
1893 * or unrelated event]
1895 u16 msg_id = ntohs(*((u16 *)p));
1896 if (msg_id == reply_msg_id) {{
1897 vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1898 vl_api_{r}_t_endian(rmp);
1899 cJSON_AddItemToArray(reply, vl_api_{r}_t_tojson(rmp));
1903 if (msg_id == details_msg_id) {{
1904 vl_api_{d}_t *rmp = (vl_api_{d}_t *)p;
1905 vl_api_{d}_t_endian(rmp);
1906 cJSON_AddItemToArray(reply, vl_api_{d}_t_tojson(rmp));
1915 if s.stream_message:
1917 gets_details_reply_template.format(
1923 D=s.stream_message.upper(),
1928 dump_details_template.format(
1929 n=s.caller, r=s.reply, N=s.caller.upper(), R=s.reply.upper()
1934 req_reply_template.format(
1935 n=s.caller, r=s.reply, N=s.caller.upper(), R=s.reply.upper()
1940 def generate_c_test2_boilerplate(services, defines, module, stream):
1941 """Generate code for VAT2 plugin."""
1942 write = stream.write
1944 define_hash = {d.name: d for d in defines}
1948 #include <vlibapi/api.h>
1949 #include <vlibmemory/api.h>
1950 #include <vppinfra/error.h>
1951 #include <vnet/ip/ip_format_fns.h>
1952 #include <vnet/ethernet/ethernet_format_fns.h>
1954 #define vl_typedefs /* define message structures */
1955 #include <vlibmemory/vl_memory_api_h.h>
1956 #include <vlibmemory/vlib.api_types.h>
1957 #include <vlibmemory/vlib.api.h>
1960 #include "{module}.api_enum.h"
1961 #include "{module}.api_types.h"
1963 #define vl_endianfun /* define message structures */
1964 #include "{module}.api.h"
1967 #define vl_calcsizefun
1968 #include "{module}.api.h"
1969 #undef vl_calsizefun
1971 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
1973 #include "{module}.api.h"
1976 #include "{module}.api_tojson.h"
1977 #include "{module}.api_fromjson.h"
1978 #include <vpp-api/client/vppapiclient.h>
1980 #include <vat2/vat2_helpers.h>
1984 write(hdr.format(module=module))
1987 if s.reply not in define_hash:
1989 c_test_api_service(s, s.stream, stream)
1992 "void vat2_register_function(char *, cJSON * (*)(cJSON *), cJSON * (*)(void *), u32);\n"
1994 # write('__attribute__((constructor))')
1995 write("clib_error_t *\n")
1996 write("vat2_register_plugin (void) {\n")
1998 if s.reply not in define_hash:
2000 crc = define_hash[s.caller].crc
2002 ' vat2_register_function("{n}", api_{n}, (cJSON * (*)(void *))vl_api_{n}_t_tojson, 0x{crc:08x});\n'.format(
2006 write(" return 0;\n")
2011 # Plugin entry point
2013 def run(output_dir, apifilename, s):
2014 """Main plugin entry point."""
2018 sys.stderr.write("Missing --outputdir argument")
2021 basename = os.path.basename(apifilename)
2022 filename, _ = os.path.splitext(basename)
2023 modulename = filename.replace(".", "_")
2024 filename_enum = os.path.join(output_dir + "/" + basename + "_enum.h")
2025 filename_types = os.path.join(output_dir + "/" + basename + "_types.h")
2026 filename_c = os.path.join(output_dir + "/" + basename + ".c")
2027 filename_c_test = os.path.join(output_dir + "/" + basename + "_test.c")
2028 filename_c_test2 = os.path.join(output_dir + "/" + basename + "_test2.c")
2029 filename_c_tojson = os.path.join(output_dir + "/" + basename + "_tojson.h")
2030 filename_c_fromjson = os.path.join(output_dir + "/" + basename + "_fromjson.h")
2032 # Generate separate types file
2034 generate_include_types(s, modulename, st)
2035 with open(filename_types, "w") as fd:
2037 shutil.copyfileobj(st, fd)
2040 # Generate separate enum file
2042 st.write("#ifndef included_{}_api_enum_h\n".format(modulename))
2043 st.write("#define included_{}_api_enum_h\n".format(modulename))
2044 generate_include_enum(s, modulename, st)
2045 generate_include_counters(s["Counters"], st)
2046 st.write("#endif\n")
2047 with open(filename_enum, "w") as fd:
2049 shutil.copyfileobj(st, fd)
2052 # Generate separate C file
2054 generate_c_boilerplate(
2055 s["Service"], s["Define"], s["Counters"], s["file_crc"], modulename, st
2057 with open(filename_c, "w") as fd:
2059 shutil.copyfileobj(st, fd)
2062 # Generate separate C test file
2064 plugin = bool("plugin" in apifilename)
2065 generate_c_test_boilerplate(
2066 s["Service"], s["Define"], s["file_crc"], modulename, plugin, st
2068 with open(filename_c_test, "w") as fd:
2070 shutil.copyfileobj(st, fd)
2073 # Fully autogenerated VATv2 C test file
2075 generate_c_test2_boilerplate(s["Service"], s["Define"], modulename, st)
2076 with open(filename_c_test2, "w") as fd:
2078 shutil.copyfileobj(st, fd)
2081 # Generate separate JSON file
2083 generate_tojson(s, modulename, st)
2084 with open(filename_c_tojson, "w") as fd:
2086 shutil.copyfileobj(st, fd)
2089 generate_fromjson(s, modulename, st)
2090 with open(filename_c_fromjson, "w") as fd:
2092 shutil.copyfileobj(st, fd)
2095 output = TOP_BOILERPLATE.format(datestring=DATESTRING, input_filename=basename)
2096 output += generate_imports(s["Import"])
2097 output += msg_ids(s)
2098 output += msg_names(s)
2099 output += msg_name_crc_list(s, filename)
2100 output += typedefs(modulename)
2101 printfun_types(s["types"], stream, modulename)
2102 printfun(s["Define"], stream, modulename)
2103 output += stream.getvalue()
2105 output += endianfun(s["types"] + s["Define"], modulename)
2106 output += calc_size_fun(s["types"] + s["Define"], modulename)
2107 output += version_tuple(s, basename)
2108 output += BOTTOM_BOILERPLATE.format(input_filename=basename, file_crc=s["file_crc"])