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.'''
41 noprint_fields = {'_vl_msg_id': None,
44 is_number = {'u8': None,
55 def __init__(self, module, types, defines, imported_types, stream):
58 self.defines = defines
60 self.types_hash = {'vl_api_'+d.name+'_t':
61 d for d in types + imported_types}
62 self.defines_hash = {d.name: d for d in defines}
65 '''Output the top boilerplate.'''
66 write = self.stream.write
67 write('#ifndef included_{}_api_tojson_h\n'.format(self.module))
68 write('#define included_{}_api_tojson_h\n'.format(self.module))
69 write('#include <vppinfra/cJSON.h>\n\n')
70 write('#include <vppinfra/jsonformat.h>\n\n')
71 if self.module == 'interface_types':
72 write('#define vl_printfun\n')
73 write('#include <vnet/interface_types.api.h>\n\n')
76 '''Output the bottom boilerplate.'''
77 write = self.stream.write
80 def get_base_type(self, t):
83 vt = self.types_hash[t]
84 if vt.type == 'Using' and 'length' not in vt.alias:
85 vt_type = vt.alias['type']
90 def get_json_func(self, t):
91 '''Given the type, returns the function to use to create a
93 vt, vt_type = self.get_base_type(t)
95 if t in self.is_number or vt_type in self.is_number:
96 return 'cJSON_AddNumberToObject', '', False
98 return 'cJSON_AddBoolToObject', '', False
100 # Lookup type name check if it's enum
101 if vt.type == 'Enum' or vt.type == 'EnumFlag':
102 return '{t}_tojson'.format(t=t), '', True
103 return '{t}_tojson'.format(t=t), '&', True
105 def get_json_array_func(self, t):
106 '''Given a type returns the function to create a cJSON object
108 if t in self.is_number:
109 return 'cJSON_CreateNumber', ''
111 return 'cJSON_CreateBool', ''
112 vt, vt_type = self.get_base_type(t)
113 if vt.type == 'Enum' or vt.type == 'EnumFlag':
114 return '{t}_tojson'.format(t=t), ''
115 return '{t}_tojson'.format(t=t), '&'
117 def print_string(self, o):
118 '''Create cJSON object from vl_api_string_t'''
119 write = self.stream.write
121 write(' vl_api_string_cJSON_AddToObject(o, "{n}", &a->{n});\n'
122 .format(n=o.fieldname))
125 write(' cJSON_AddStringToObject(o, "{n}", (char *)a->{n});\n'
126 .format(n=o.fieldname))
128 def print_field(self, o):
129 '''Called for every field in a typedef or define.'''
130 write = self.stream.write
131 if o.fieldname in self.noprint_fields:
134 f, p, newobj = self.get_json_func(o.fieldtype)
137 write(' cJSON_AddItemToObject(o, "{n}", {f}({p}a->{n}));\n'
138 .format(f=f, p=p, n=o.fieldname))
140 write(' {f}(o, "{n}", {p}a->{n});\n'
141 .format(f=f, p=p, n=o.fieldname))
143 _dispatch['Field'] = print_field
145 def print_array(self, o):
146 '''Converts a VPP API array to cJSON array.'''
147 write = self.stream.write
152 cJSON *array = cJSON_AddArrayToObject(o, "{n}");
153 for (i = 0; i < {lfield}; i++) {{
154 cJSON_AddItemToArray(array, {f}({p}a->{n}[i]));
159 if o.fieldtype == 'string':
163 lfield = 'a->' + o.lengthfield if o.lengthfield else o.length
164 if o.fieldtype == 'u8':
166 # What is length field doing here?
167 write(' u8 *s = format(0, "0x%U", format_hex_bytes, '
168 '&a->{n}, {lfield});\n'
169 .format(n=o.fieldname, lfield=lfield))
170 write(' cJSON_AddStringToObject(o, "{n}", (char *)s);\n'
171 .format(n=o.fieldname))
172 write(' vec_free(s);\n')
176 f, p = self.get_json_array_func(o.fieldtype)
177 write(forloop.format(lfield=lfield,
183 _dispatch['Array'] = print_array
185 def print_enum(self, o):
186 '''Create cJSON object (string) for VPP API enum'''
187 write = self.stream.write
188 write('static inline cJSON *vl_api_{name}_t_tojson '
189 '(vl_api_{name}_t a) {{\n'.format(name=o.name))
191 write(" switch(a) {\n")
193 write(" case %s:\n" % b[1])
194 write(' return cJSON_CreateString("{}");\n'.format(b[0]))
195 write(' default: return cJSON_CreateString("Invalid ENUM");\n')
197 write(' return 0;\n')
200 _dispatch['Enum'] = print_enum
202 def print_enum_flag(self, o):
203 '''Create cJSON object (string) for VPP API enum'''
204 write = self.stream.write
205 write('static inline cJSON *vl_api_{name}_t_tojson '
206 '(vl_api_{name}_t a) {{\n'.format(name=o.name))
207 write(' cJSON *array = cJSON_CreateArray();\n')
212 write(' if (a & {})\n'.format(b[0]))
214 ' cJSON_AddItemToArray(array, cJSON_CreateString("{}"));\n'.format(b[0]))
215 write(' return array;\n')
218 _dispatch['EnumFlag'] = print_enum_flag
220 def print_typedef(self, o):
221 '''Create cJSON (dictionary) object from VPP API typedef'''
222 write = self.stream.write
223 write('static inline cJSON *vl_api_{name}_t_tojson '
224 '(vl_api_{name}_t *a) {{\n'.format(name=o.name))
225 write(' cJSON *o = cJSON_CreateObject();\n')
228 self._dispatch[t.type](self, t)
230 write(' return o;\n')
233 def print_define(self, o):
234 '''Create cJSON (dictionary) object from VPP API define'''
235 write = self.stream.write
236 write('static inline cJSON *vl_api_{name}_t_tojson '
237 '(vl_api_{name}_t *a) {{\n'.format(name=o.name))
238 write(' cJSON *o = cJSON_CreateObject();\n')
239 write(' cJSON_AddStringToObject(o, "_msgname", "{}");\n'
241 write(' cJSON_AddStringToObject(o, "_crc", "{crc:08x}");\n'
245 self._dispatch[t.type](self, t)
247 write(' return o;\n')
250 def print_using(self, o):
251 '''Create cJSON (dictionary) object from VPP API aliased type'''
255 write = self.stream.write
256 write('static inline cJSON *vl_api_{name}_t_tojson '
257 '(vl_api_{name}_t *a) {{\n'.format(name=o.name))
259 write(' u8 *s = format(0, "%U", format_vl_api_{}_t, a);\n'
261 write(' cJSON *o = cJSON_CreateString((char *)s);\n')
262 write(' vec_free(s);\n')
263 write(' return o;\n')
266 _dispatch['Typedef'] = print_typedef
267 _dispatch['Define'] = print_define
268 _dispatch['Using'] = print_using
269 _dispatch['Union'] = print_typedef
271 def generate_function(self, t):
272 '''Main entry point'''
273 write = self.stream.write
275 write('/* Manual print {} */\n'.format(t.name))
277 self._dispatch[t.type](self, t)
279 def generate_types(self):
280 '''Main entry point'''
282 self.generate_function(t)
284 def generate_defines(self):
285 '''Main entry point'''
286 for t in self.defines:
287 self.generate_function(t)
292 Parse JSON objects into VPP API binary message structures.
295 noprint_fields = {'_vl_msg_id': None,
296 'client_index': None,
298 is_number = {'u8': None,
309 def __init__(self, module, types, defines, imported_types, stream):
312 self.defines = defines
314 self.types_hash = {'vl_api_'+d.name+'_t':
315 d for d in types + imported_types}
316 self.defines_hash = {d.name: d for d in defines}
319 '''Output the top boilerplate.'''
320 write = self.stream.write
321 write('#ifndef included_{}_api_fromjson_h\n'.format(self.module))
322 write('#define included_{}_api_fromjson_h\n'.format(self.module))
323 write('#include <vppinfra/cJSON.h>\n\n')
324 write('#include <vppinfra/jsonformat.h>\n\n')
325 write('#pragma GCC diagnostic ignored "-Wunused-label"\n')
327 def is_base_type(self, t):
328 '''Check if a type is one of the VPP API base types'''
329 if t in self.is_number:
336 '''Output the bottom boilerplate.'''
337 write = self.stream.write
340 def print_string(self, o, toplevel=False):
341 '''Convert JSON string to vl_api_string_t'''
342 write = self.stream.write
344 msgvar = "a" if toplevel else "*mp"
345 msgsize = "l" if toplevel else "*len"
348 write(' char *p = cJSON_GetStringValue(item);\n')
349 write(' size_t plen = strlen(p);\n')
350 write(' {msgvar} = cJSON_realloc({msgvar}, {msgsize} + plen, {msgsize});\n'
351 .format(msgvar=msgvar, msgsize=msgsize))
352 write(' if ({msgvar} == 0) goto error;\n'.format(msgvar=msgvar))
353 write(' vl_api_c_string_to_api_string(p, (void *){msgvar} + '
354 '{msgsize} - sizeof(vl_api_string_t));\n'
355 .format(msgvar=msgvar, msgsize=msgsize))
356 write(' {msgsize} += plen;\n'.format(msgsize=msgsize))
358 write(' strncpy_s((char *)a->{n}, sizeof(a->{n}), '
359 'cJSON_GetStringValue(item), sizeof(a->{n}) - 1);\n'
360 .format(n=o.fieldname))
362 def print_field(self, o, toplevel=False):
363 '''Called for every field in a typedef or define.'''
364 write = self.stream.write
365 if o.fieldname in self.noprint_fields:
367 is_bt = self.is_base_type(o.fieldtype)
368 t = 'vl_api_{}'.format(o.fieldtype) if is_bt else o.fieldtype
370 msgvar = "(void **)&a" if toplevel else "mp"
371 msgsize = "&l" if toplevel else "len"
374 write(' vl_api_{t}_fromjson(item, &a->{n});\n'
375 .format(t=o.fieldtype, n=o.fieldname))
377 write(' if ({t}_fromjson({msgvar}, '
378 '{msgsize}, item, &a->{n}) < 0) goto error;\n'
379 .format(t=t, n=o.fieldname, msgvar=msgvar, msgsize=msgsize))
381 _dispatch['Field'] = print_field
383 def print_array(self, o, toplevel=False):
384 '''Convert JSON array to VPP API array'''
385 write = self.stream.write
390 cJSON *array = cJSON_GetObjectItem(o, "{n}");
391 int size = cJSON_GetArraySize(array);
392 if (size != {lfield}) goto error;
393 for (i = 0; i < size; i++) {{
394 cJSON *e = cJSON_GetArrayItem(array, i);
402 cJSON *array = cJSON_GetObjectItem(o, "{n}");
403 int size = cJSON_GetArraySize(array);
405 {realloc} = cJSON_realloc({realloc}, {msgsize} + sizeof({t}) * size, {msgsize});
406 {t} *d = (void *){realloc} + {msgsize};
407 {msgsize} += sizeof({t}) * size;
408 for (i = 0; i < size; i++) {{
409 cJSON *e = cJSON_GetArrayItem(array, i);
415 if o.fieldtype == 'string':
416 self.print_string(o, toplevel)
419 lfield = 'a->' + o.lengthfield if o.lengthfield else o.length
420 msgvar = "(void **)&a" if toplevel else "mp"
421 realloc = "a" if toplevel else "*mp"
422 msgsize = "l" if toplevel else "*len"
424 if o.fieldtype == 'u8':
426 write(' s = u8string_fromjson(o, "{}");\n'
427 .format(o.fieldname))
428 write(' if (!s) goto error;\n')
429 write(' {} = vec_len(s);\n'.format(lfield))
431 write(' {realloc} = cJSON_realloc({realloc}, {msgsize} + '
432 'vec_len(s), {msgsize});\n'.format(msgvar=msgvar, msgsize=msgsize, realloc=realloc))
433 write(' memcpy((void *){realloc} + {msgsize}, s, '
434 'vec_len(s));\n'.format(realloc=realloc, msgsize=msgsize))
435 write(' {msgsize} += vec_len(s);\n'.format(msgsize=msgsize))
437 write(' vec_free(s);\n')
439 write(' if (u8string_fromjson2(o, "{n}", a->{n}) < 0) goto error;\n'
440 .format(n=o.fieldname))
443 is_bt = self.is_base_type(o.fieldtype)
447 call = ('vl_api_{t}_fromjson(e, &d[i]);'
448 .format(t=o.fieldtype))
450 call = ('if ({t}_fromjson({msgvar}, len, e, &d[i]) < 0) goto error; '
451 .format(t=o.fieldtype, msgvar=msgvar))
452 write(forloop_vla.format(lfield=lfield,
460 call = ('vl_api_{t}_fromjson(e, &a->{n}[i]);'
461 .format(t=t, n=o.fieldname))
463 call = ('if ({}_fromjson({}, len, e, &a->{}[i]) < 0) goto error;'
464 .format(t, msgvar, o.fieldname))
465 write(forloop.format(lfield=lfield,
473 _dispatch['Array'] = print_array
475 def print_enum(self, o):
476 '''Convert to JSON enum(string) to VPP API enum (int)'''
477 write = self.stream.write
478 write('static inline int vl_api_{n}_t_fromjson'
479 '(void **mp, int *len, cJSON *o, vl_api_{n}_t *a) {{\n'
481 write(' char *p = cJSON_GetStringValue(o);\n')
483 write(' if (strcmp(p, "{}") == 0) {{*a = {}; return 0;}}\n'
486 write(' return -1;\n')
489 _dispatch['Enum'] = print_enum
491 def print_enum_flag(self, o):
492 '''Convert to JSON enum(string) to VPP API enum (int)'''
493 write = self.stream.write
494 write('static inline int vl_api_{n}_t_fromjson '
495 '(void **mp, int *len, cJSON *o, vl_api_{n}_t *a) {{\n'
499 write(' for (i = 0; i < cJSON_GetArraySize(o); i++) {\n')
500 write(' cJSON *e = cJSON_GetArrayItem(o, i);\n')
501 write(' char *p = cJSON_GetStringValue(e);\n')
502 write(' if (!p) return -1;\n')
504 write(' if (strcmp(p, "{}") == 0) *a |= {};\n'
507 write(' return 0;\n')
510 _dispatch['EnumFlag'] = print_enum_flag
512 def print_typedef(self, o):
513 '''Convert from JSON object to VPP API binary representation'''
514 write = self.stream.write
516 write('static inline int vl_api_{name}_t_fromjson (void **mp, '
517 'int *len, cJSON *o, vl_api_{name}_t *a) {{\n'
518 .format(name=o.name))
519 write(' cJSON *item __attribute__ ((unused));\n')
520 write(' u8 *s __attribute__ ((unused));\n')
522 if t.type == 'Field' and t.is_lengthfield:
524 write('\n item = cJSON_GetObjectItem(o, "{}");\n'
525 .format(t.fieldname))
526 write(' if (!item) goto error;\n')
527 self._dispatch[t.type](self, t)
529 write('\n return 0;\n')
531 write(' return -1;\n')
534 def print_union(self, o):
535 '''Convert JSON object to VPP API binary union'''
536 write = self.stream.write
538 write('static inline int vl_api_{name}_t_fromjson (void **mp, '
539 'int *len, cJSON *o, vl_api_{name}_t *a) {{\n'
540 .format(name=o.name))
541 write(' cJSON *item __attribute__ ((unused));\n')
542 write(' u8 *s __attribute__ ((unused));\n')
544 if t.type == 'Field' and t.is_lengthfield:
546 write(' item = cJSON_GetObjectItem(o, "{}");\n'
547 .format(t.fieldname))
548 write(' if (item) {\n')
549 self._dispatch[t.type](self, t)
551 write('\n return 0;\n')
553 write(' return -1;\n')
556 def print_define(self, o):
557 '''Convert JSON object to VPP API message'''
558 write = self.stream.write
560 write('static inline vl_api_{name}_t *vl_api_{name}_t_fromjson '
561 '(cJSON *o, int *len) {{\n'.format(name=o.name))
562 write(' cJSON *item __attribute__ ((unused));\n')
563 write(' u8 *s __attribute__ ((unused));\n')
564 write(' int l = sizeof(vl_api_{}_t);\n'.format(o.name))
565 write(' vl_api_{}_t *a = cJSON_malloc(l);\n'.format(o.name))
569 if t.fieldname in self.noprint_fields:
571 if t.type == 'Field' and t.is_lengthfield:
573 write(' item = cJSON_GetObjectItem(o, "{}");\n'
574 .format(t.fieldname))
575 write(' if (!item) goto error;\n')
577 self._dispatch[t.type](self, t, toplevel=True)
580 write(' *len = l;\n')
581 write(' return a;\n')
585 write(' cJSON_free(a);\n')
586 write(' return 0;\n')
589 def print_using(self, o):
590 '''Convert JSON field to VPP type alias'''
591 write = self.stream.write
597 write('static inline int vl_api_{name}_t_fromjson (void **mp, '
598 'int *len, cJSON *o, vl_api_{name}_t *a) {{\n'
599 .format(name=o.name))
600 if 'length' in o.alias:
601 if t.fieldtype != 'u8':
602 raise ValueError("Error in processing type {} for {}"
603 .format(t.fieldtype, o.name))
604 write(' vl_api_u8_string_fromjson(o, (u8 *)a, {});\n'
605 .format(o.alias['length']))
607 write(' vl_api_{t}_fromjson(o, ({t} *)a);\n'
608 .format(t=t.fieldtype))
610 write(' return 0;\n')
613 _dispatch['Typedef'] = print_typedef
614 _dispatch['Define'] = print_define
615 _dispatch['Using'] = print_using
616 _dispatch['Union'] = print_union
618 def generate_function(self, t):
619 '''Main entry point'''
620 write = self.stream.write
622 write('/* Manual print {} */\n'.format(t.name))
624 self._dispatch[t.type](self, t)
626 def generate_types(self):
627 '''Main entry point'''
629 self.generate_function(t)
631 def generate_defines(self):
632 '''Main entry point'''
633 for t in self.defines:
634 self.generate_function(t)
637 def generate_tojson(s, modulename, stream):
638 '''Generate all functions to convert from API to JSON'''
641 write('/* Imported API files */\n')
642 for i in s['Import']:
643 f = i.filename.replace('plugins/', '')
644 write('#include <{}_tojson.h>\n'.format(f))
646 pp = ToJSON(modulename, s['types'], s['Define'], s['imported']['types'],
650 pp.generate_defines()
655 def generate_fromjson(s, modulename, stream):
656 '''Generate all functions to convert from JSON to API'''
658 write('/* Imported API files */\n')
659 for i in s['Import']:
660 f = i.filename.replace('plugins/', '')
661 write('#include <{}_fromjson.h>\n'.format(f))
663 pp = FromJSON(modulename, s['types'], s['Define'], s['imported']['types'],
667 pp.generate_defines()
672 ###############################################################################
675 DATESTRING = datetime.datetime.utcfromtimestamp(
676 int(os.environ.get('SOURCE_DATE_EPOCH', time.time())))
677 TOP_BOILERPLATE = '''\
679 * VLIB API definitions {datestring}
680 * Input file: {input_filename}
681 * Automatically generated: please edit the input file NOT this file!
685 #if defined(vl_msg_id)||defined(vl_union_id) \\
686 || defined(vl_printfun) ||defined(vl_endianfun) \\
687 || defined(vl_api_version)||defined(vl_typedefs) \\
688 || defined(vl_msg_name)||defined(vl_msg_name_crc_list) \\
689 || defined(vl_api_version_tuple) || defined(vl_calcsizefun)
690 /* ok, something was selected */
692 #warning no content included from {input_filename}
695 #define VL_API_PACKED(x) x __attribute__ ((packed))
698 BOTTOM_BOILERPLATE = '''\
699 /****** API CRC (whole file) *****/
701 #ifdef vl_api_version
702 vl_api_version({input_filename}, {file_crc:#08x})
709 '''Generate macro to map API message id to handler'''
712 /****** Message ID / handler enum ******/
717 for t in s['Define']:
718 output += "vl_msg_id(VL_API_%s, vl_api_%s_t_handler)\n" % \
719 (t.name.upper(), t.name)
726 '''Generate calls to name mapping macro'''
729 /****** Message names ******/
734 for t in s['Define']:
735 dont_trace = 0 if t.dont_trace else 1
736 output += "vl_msg_name(vl_api_%s_t, %d)\n" % (t.name, dont_trace)
742 def msg_name_crc_list(s, suffix):
743 '''Generate list of names to CRC mappings'''
746 /****** Message name, crc list ******/
748 #ifdef vl_msg_name_crc_list
750 output += "#define foreach_vl_msg_name_crc_%s " % suffix
752 for t in s['Define']:
753 output += "\\\n_(VL_API_%s, %s, %08x) " % \
754 (t.name.upper(), t.name, t.crc)
760 def api2c(fieldtype):
761 '''Map between API type names and internal VPP type names'''
762 mappingtable = {'string': 'vl_api_string_t', }
763 if fieldtype in mappingtable:
764 return mappingtable[fieldtype]
768 def typedefs(filename):
769 '''Include in the main files to the types file'''
772 /****** Typedefs ******/
775 #include "{include}.api_types.h"
777 '''.format(include=filename)
781 FORMAT_STRINGS = {'u8': '%u',
794 '''Functions for pretty printing VPP API messages'''
796 noprint_fields = {'_vl_msg_id': None,
797 'client_index': None,
800 def __init__(self, stream):
804 def print_string(o, stream):
805 '''Pretty print a vl_api_string_t'''
808 write(' if (vl_api_string_len(&a->{f}) > 0) {{\n'
809 .format(f=o.fieldname))
810 write(' s = format(s, "\\n%U{f}: %U", '
811 'format_white_space, indent, '
812 'vl_api_format_string, (&a->{f}));\n'.format(f=o.fieldname))
814 write(' s = format(s, "\\n%U{f}:", '
815 'format_white_space, indent);\n'.format(f=o.fieldname))
818 write(' s = format(s, "\\n%U{f}: %s", '
819 'format_white_space, indent, a->{f});\n'
820 .format(f=o.fieldname))
822 def print_field(self, o, stream):
823 '''Pretty print API field'''
825 if o.fieldname in self.noprint_fields:
827 if o.fieldtype in FORMAT_STRINGS:
828 f = FORMAT_STRINGS[o.fieldtype]
829 write(' s = format(s, "\\n%U{n}: {f}", '
830 'format_white_space, indent, a->{n});\n'
831 .format(n=o.fieldname, f=f))
833 write(' s = format(s, "\\n%U{n}: %U", '
834 'format_white_space, indent, '
835 'format_{t}, &a->{n}, indent);\n'
836 .format(n=o.fieldname, t=o.fieldtype))
838 _dispatch['Field'] = print_field
840 def print_array(self, o, stream):
841 '''Pretty print API array'''
845 for (i = 0; i < {lfield}; i++) {{
846 s = format(s, "\\n%U{n}: %U",
847 format_white_space, indent, format_{t}, &a->{n}[i], indent);
851 forloop_format = '''\
852 for (i = 0; i < {lfield}; i++) {{
853 s = format(s, "\\n%U{n}: {t}",
854 format_white_space, indent, a->{n}[i]);
858 if o.fieldtype == 'string':
859 self.print_string(o, stream)
862 if o.fieldtype == 'u8':
864 write(' s = format(s, "\\n%U{n}: %U", format_white_space, '
865 'indent, format_hex_bytes, a->{n}, a->{lfield});\n'
866 .format(n=o.fieldname, lfield=o.lengthfield))
868 write(' s = format(s, "\\n%U{n}: %U", format_white_space, '
869 'indent, format_hex_bytes, a, {lfield});\n'
870 .format(n=o.fieldname, lfield=o.length))
873 lfield = 'a->' + o.lengthfield if o.lengthfield else o.length
874 if o.fieldtype in FORMAT_STRINGS:
875 write(forloop_format.format(lfield=lfield,
876 t=FORMAT_STRINGS[o.fieldtype],
879 write(forloop.format(lfield=lfield, t=o.fieldtype, n=o.fieldname))
881 _dispatch['Array'] = print_array
884 def print_alias(k, v, stream):
885 '''Pretty print type alias'''
887 if ('length' in v.alias and v.alias['length'] and
888 v.alias['type'] == 'u8'):
889 write(' return format(s, "%U", format_hex_bytes, a, {});\n'
890 .format(v.alias['length']))
891 elif v.alias['type'] in FORMAT_STRINGS:
892 write(' return format(s, "{}", *a);\n'
893 .format(FORMAT_STRINGS[v.alias['type']]))
895 write(' return format(s, "{} (print not implemented)");\n'
899 def print_enum(o, stream):
900 '''Pretty print API enum'''
902 write(" switch(*a) {\n")
904 write(" case %s:\n" % b[1])
905 write(' return format(s, "{}");\n'.format(b[0]))
908 _dispatch['Enum'] = print_enum
909 _dispatch['EnumFlag'] = print_enum
911 def print_obj(self, o, stream):
915 if o.type in self._dispatch:
916 self._dispatch[o.type](self, o, stream)
918 write(' s = format(s, "\\n{} {} {} (print not implemented");\n'
919 .format(o.type, o.fieldtype, o.fieldname))
922 def printfun(objs, stream, modulename):
923 '''Main entry point for pretty print function generation'''
927 /****** Print functions *****/
929 #ifndef included_{module}_printfun
930 #define included_{module}_printfun
933 #define _uword_fmt \"%lld\"
934 #define _uword_cast (long long)
936 #define _uword_fmt \"%ld\"
937 #define _uword_cast long
940 #include "{module}.api_tojson.h"
941 #include "{module}.api_fromjson.h"
946 static inline void *vl_api_{name}_t_print{suffix} (vl_api_{name}_t *a, void *handle)
949 u32 indent __attribute__((unused)) = 2;
950 int i __attribute__((unused));
953 h = h.format(module=modulename)
956 pp = Printfun(stream)
959 write("/***** manual: vl_api_%s_t_print *****/\n\n" % t.name)
961 write(signature.format(name=t.name, suffix=''))
962 write(' /* Message definition: vl_api_{}_t: */\n'.format(t.name))
963 write(" s = format(s, \"vl_api_%s_t:\");\n" % t.name)
965 pp.print_obj(o, stream)
966 write(' vec_add1(s, 0);\n')
967 write(' vl_print (handle, (char *)s);\n')
968 write(' vec_free (s);\n')
969 write(' return handle;\n')
972 write(signature.format(name=t.name, suffix='_json'))
973 write(' cJSON * o = vl_api_{}_t_tojson(a);\n'.format(t.name))
975 write(' char *out = cJSON_Print(o);\n')
976 write(' vl_print(handle, out);\n')
977 write(' cJSON_Delete(o);\n')
978 write(' cJSON_free(out);\n')
979 write(' return handle;\n')
983 write("\n#endif /* vl_printfun */\n")
988 def printfun_types(objs, stream, modulename):
989 '''Pretty print API types'''
991 pp = Printfun(stream)
994 /****** Print functions *****/
996 #ifndef included_{module}_printfun_types
997 #define included_{module}_printfun_types
1000 h = h.format(module=modulename)
1004 static inline u8 *format_vl_api_{name}_t (u8 *s, va_list * args)
1006 vl_api_{name}_t *a = va_arg (*args, vl_api_{name}_t *);
1007 u32 indent __attribute__((unused)) = va_arg (*args, u32);
1008 int i __attribute__((unused));
1013 if t.__class__.__name__ == 'Enum' or t.__class__.__name__ == 'EnumFlag':
1014 write(signature.format(name=t.name))
1015 pp.print_enum(t.block, stream)
1016 write(' return s;\n')
1021 write("/***** manual: vl_api_%s_t_print *****/\n\n" % t.name)
1024 if t.__class__.__name__ == 'Using':
1025 write(signature.format(name=t.name))
1026 pp.print_alias(t.name, t, stream)
1030 write(signature.format(name=t.name))
1032 pp.print_obj(o, stream)
1034 write(' return s;\n')
1038 write("\n#endif /* vl_printfun_types */\n")
1041 def generate_imports(imports):
1042 '''Add #include matching the API import statements'''
1043 output = '/* Imported API files */\n'
1044 output += '#ifndef vl_api_version\n'
1047 s = i.filename.replace('plugins/', '')
1048 output += '#include <{}.h>\n'.format(s)
1049 output += '#endif\n'
1054 'u16': 'clib_net_to_host_u16',
1055 'u32': 'clib_net_to_host_u32',
1056 'u64': 'clib_net_to_host_u64',
1057 'i16': 'clib_net_to_host_i16',
1058 'i32': 'clib_net_to_host_i32',
1059 'i64': 'clib_net_to_host_i64',
1060 'f64': 'clib_net_to_host_f64',
1064 def endianfun_array(o):
1065 '''Generate endian functions for arrays'''
1067 for (i = 0; i < {length}; i++) {{
1068 a->{name}[i] = {format}(a->{name}[i]);
1072 forloop_format = '''\
1073 for (i = 0; i < {length}; i++) {{
1074 {type}_endian(&a->{name}[i]);
1079 if o.fieldtype == 'u8' or o.fieldtype == 'string' or o.fieldtype == 'bool':
1080 output += ' /* a->{n} = a->{n} (no-op) */\n'.format(n=o.fieldname)
1082 lfield = 'a->' + o.lengthfield if o.lengthfield else o.length
1083 if o.fieldtype in ENDIAN_STRINGS:
1085 .format(length=lfield,
1086 format=ENDIAN_STRINGS[o.fieldtype],
1089 output += (forloop_format
1090 .format(length=lfield, type=o.fieldtype,
1095 NO_ENDIAN_CONVERSION = {'client_index': None}
1098 def endianfun_obj(o):
1099 '''Generate endian conversion function for type'''
1101 if o.type == 'Array':
1102 return endianfun_array(o)
1103 if o.type != 'Field':
1104 output += (' s = format(s, "\\n{} {} {} (print not implemented");\n'
1105 .format(o.type, o.fieldtype, o.fieldname))
1107 if o.fieldname in NO_ENDIAN_CONVERSION:
1108 output += ' /* a->{n} = a->{n} (no-op) */\n'.format(n=o.fieldname)
1110 if o.fieldtype in ENDIAN_STRINGS:
1111 output += (' a->{name} = {format}(a->{name});\n'
1112 .format(name=o.fieldname,
1113 format=ENDIAN_STRINGS[o.fieldtype]))
1114 elif o.fieldtype.startswith('vl_api_'):
1115 output += (' {type}_endian(&a->{name});\n'
1116 .format(type=o.fieldtype, name=o.fieldname))
1118 output += ' /* a->{n} = a->{n} (no-op) */\n'.format(n=o.fieldname)
1123 def endianfun(objs, modulename):
1124 '''Main entry point for endian function generation'''
1127 /****** Endian swap functions *****/\n\
1129 #ifndef included_{module}_endianfun
1130 #define included_{module}_endianfun
1132 #undef clib_net_to_host_uword
1134 #define clib_net_to_host_uword clib_net_to_host_u64
1136 #define clib_net_to_host_uword clib_net_to_host_u32
1140 output = output.format(module=modulename)
1143 static inline void vl_api_{name}_t_endian (vl_api_{name}_t *a)
1145 int i __attribute__((unused));
1149 if t.__class__.__name__ == 'Enum' or t.__class__.__name__ == 'EnumFlag':
1150 output += signature.format(name=t.name)
1151 if t.enumtype in ENDIAN_STRINGS:
1152 output += (' *a = {}(*a);\n'
1153 .format(ENDIAN_STRINGS[t.enumtype]))
1155 output += (' /* a->{name} = a->{name} (no-op) */\n'
1156 .format(name=t.name))
1162 output += "/***** manual: vl_api_%s_t_endian *****/\n\n" % t.name
1165 if t.__class__.__name__ == 'Using':
1166 output += signature.format(name=t.name)
1167 if ('length' in t.alias and t.alias['length'] and
1168 t.alias['type'] == 'u8'):
1169 output += (' /* a->{name} = a->{name} (no-op) */\n'
1170 .format(name=t.name))
1171 elif t.alias['type'] in FORMAT_STRINGS:
1172 output += (' *a = {}(*a);\n'
1173 .format(ENDIAN_STRINGS[t.alias['type']]))
1175 output += ' /* Not Implemented yet {} */'.format(t.name)
1179 output += signature.format(name=t.name)
1182 output += endianfun_obj(o)
1185 output += "\n#endif"
1186 output += "\n#endif /* vl_endianfun */\n\n"
1191 def calc_size_fun(objs, modulename):
1192 '''Main entry point for calculate size function generation'''
1195 /****** Calculate size functions *****/\n\
1196 #ifdef vl_calcsizefun
1197 #ifndef included_{module}_calcsizefun
1198 #define included_{module}_calcsizefun
1201 output = output.format(module=modulename)
1204 /* calculate message size of message in network byte order */
1205 static inline uword vl_api_{name}_t_calc_size (vl_api_{name}_t *a)
1210 tname = o.__class__.__name__
1212 output += signature.format(name=o.name)
1213 output += f" return sizeof(*a)"
1214 if tname == 'Using':
1215 if 'length' in o.alias:
1217 tmp = int(o.alias['length'])
1219 raise (f"Unexpected length '0' for alias {o}")
1221 # output += f" + vl_api_{o.alias.name}_t_calc_size({o.name})"
1227 elif tname == 'Enum' or tname == 'EnumFlag':
1231 if b.type == 'Option':
1233 elif b.type == 'Field':
1234 if b.fieldtype.startswith('vl_api_'):
1235 output += f" - sizeof(a->{b.fieldname})"
1236 output += f" + {b.fieldtype}_calc_size(&a->{b.fieldname})"
1237 elif b.type == 'Array':
1239 m = list(filter(lambda x: x.fieldname == b.lengthfield, o.block))
1241 raise Exception(f"Expected 1 match for field '{b.lengthfield}', got '{m}'")
1243 if lf.fieldtype in ENDIAN_STRINGS:
1244 output += f" + {ENDIAN_STRINGS[lf.fieldtype]}(a->{b.lengthfield}) * sizeof(a->{b.fieldname}[0])"
1245 elif lf.fieldtype == "u8":
1246 output += f" + a->{b.lengthfield} * sizeof(a->{b.fieldname}[0])"
1248 raise Exception(f"Don't know how to endian swap {lf.fieldtype}")
1250 # Fixed length strings decay to nul terminated u8
1251 if b.fieldtype == 'string':
1253 output += f" + vl_api_string_len(&a->{b.fieldname})"
1257 output += "\n#endif"
1258 output += "\n#endif /* vl_calcsizefun */\n\n"
1263 def version_tuple(s, module):
1264 '''Generate semantic version string'''
1266 /****** Version tuple *****/
1268 #ifdef vl_api_version_tuple
1271 if 'version' in s['Option']:
1272 v = s['Option']['version']
1273 (major, minor, patch) = v.split('.')
1274 output += "vl_api_version_tuple(%s, %s, %s, %s)\n" % \
1275 (module, major, minor, patch)
1277 output += "\n#endif /* vl_api_version_tuple */\n\n"
1282 def generate_include_enum(s, module, stream):
1283 '''Generate <name>.api_enum.h'''
1284 write = stream.write
1287 write('typedef enum {\n')
1288 for t in s['Define']:
1289 write(' VL_API_{},\n'.format(t.name.upper()))
1290 write(' VL_MSG_{}_LAST\n'.format(module.upper()))
1291 write('}} vl_api_{}_enum_t;\n'.format(module))
1294 def generate_include_counters(s, stream):
1295 '''Include file for the counter data model types.'''
1296 write = stream.write
1299 csetname = counters.name
1300 write('typedef enum {\n')
1301 for c in counters.block:
1302 write(' {}_ERROR_{},\n'
1303 .format(csetname.upper(), c['name'].upper()))
1304 write(' {}_N_ERROR\n'.format(csetname.upper()))
1305 write('}} vl_counter_{}_enum_t;\n'.format(csetname))
1307 write('extern vlib_error_desc_t {}_error_counters[];\n'.format(csetname))
1310 def generate_include_types(s, module, stream):
1311 '''Generate separate API _types file.'''
1312 write = stream.write
1314 write('#ifndef included_{module}_api_types_h\n'.format(module=module))
1315 write('#define included_{module}_api_types_h\n'.format(module=module))
1317 if 'version' in s['Option']:
1318 v = s['Option']['version']
1319 (major, minor, patch) = v.split('.')
1320 write('#define VL_API_{m}_API_VERSION_MAJOR {v}\n'
1321 .format(m=module.upper(), v=major))
1322 write('#define VL_API_{m}_API_VERSION_MINOR {v}\n'
1323 .format(m=module.upper(), v=minor))
1324 write('#define VL_API_{m}_API_VERSION_PATCH {v}\n'
1325 .format(m=module.upper(), v=patch))
1328 write('/* Imported API files */\n')
1329 for i in s['Import']:
1330 filename = i.filename.replace('plugins/', '')
1331 write('#include <{}_types.h>\n'.format(filename))
1333 for o in itertools.chain(s['types'], s['Define']):
1334 tname = o.__class__.__name__
1335 if tname == 'Using':
1336 if 'length' in o.alias:
1337 write('typedef %s vl_api_%s_t[%s];\n' %
1338 (o.alias['type'], o.name, o.alias['length']))
1340 write('typedef %s vl_api_%s_t;\n' % (o.alias['type'], o.name))
1341 elif tname == 'Enum' or tname == 'EnumFlag':
1342 if o.enumtype == 'u32':
1343 write("typedef enum {\n")
1345 write("typedef enum __attribute__((packed)) {\n")
1348 write(" %s = %s,\n" % (b[0], b[1]))
1349 write('} vl_api_%s_t;\n' % o.name)
1350 if o.enumtype != 'u32':
1351 size1 = 'sizeof(vl_api_%s_t)' % o.name
1352 size2 = 'sizeof(%s)' % o.enumtype
1353 err_str = 'size of API enum %s is wrong' % o.name
1354 write('STATIC_ASSERT(%s == %s, "%s");\n'
1355 % (size1, size2, err_str))
1357 if tname == 'Union':
1358 write("typedef union __attribute__ ((packed)) _vl_api_%s {\n"
1361 write(("typedef struct __attribute__ ((packed)) _vl_api_%s {\n")
1364 if b.type == 'Option':
1366 if b.type == 'Field':
1367 write(" %s %s;\n" % (api2c(b.fieldtype),
1369 elif b.type == 'Array':
1371 write(" %s %s[0];\n" % (api2c(b.fieldtype),
1374 # Fixed length strings decay to nul terminated u8
1375 if b.fieldtype == 'string':
1378 .format(api2c(b.fieldtype),
1381 write(' u8 {}[{}];\n'
1382 .format(b.fieldname, b.length))
1384 write(" %s %s[%s];\n" %
1385 (api2c(b.fieldtype), b.fieldname,
1388 raise ValueError("Error in processing type {} for {}"
1391 write('} vl_api_%s_t;\n' % o.name)
1392 write(f'#define VL_API_{o.name.upper()}_IS_CONSTANT_SIZE ({0 if o.vla else 1})\n\n')
1394 for t in s['Define']:
1395 write('#define VL_API_{ID}_CRC "{n}_{crc:08x}"\n'
1396 .format(n=t.name, ID=t.name.upper(), crc=t.crc))
1401 def generate_c_boilerplate(services, defines, counters, file_crc,
1403 '''VPP side plugin.'''
1404 write = stream.write
1405 define_hash = {d.name: d for d in defines}
1408 #define vl_endianfun /* define message structures */
1409 #include "{module}.api.h"
1412 #define vl_calcsizefun
1413 #include "{module}.api.h"
1414 #undef vl_calsizefun
1416 /* instantiate all the print functions we know about */
1417 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
1419 #include "{module}.api.h"
1424 write(hdr.format(module=module))
1425 write('static u16\n')
1426 write('setup_message_id_table (void) {\n')
1427 write(' api_main_t *am = my_api_main;\n')
1428 write(' vl_msg_api_msg_config_t c;\n')
1429 write(' u16 msg_id_base = vl_msg_api_get_msg_ids ("{}_{crc:08x}", '
1430 'VL_MSG_{m}_LAST);\n'
1431 .format(module, crc=file_crc, m=module.upper()))
1434 write(' vl_msg_api_add_msg_name_crc (am, "{n}_{crc:08x}",\n'
1435 ' VL_API_{ID} + msg_id_base);\n'
1436 .format(n=d.name, ID=d.name.upper(), crc=d.crc))
1438 d = define_hash[s.caller]
1439 write(' c = (vl_msg_api_msg_config_t) '
1440 ' {{.id = VL_API_{ID} + msg_id_base,\n'
1442 ' .handler = vl_api_{n}_t_handler,\n'
1443 ' .cleanup = vl_noop_handler,\n'
1444 ' .endian = vl_api_{n}_t_endian,\n'
1445 ' .print = vl_api_{n}_t_print,\n'
1448 ' .print_json = vl_api_{n}_t_print_json,\n'
1449 ' .tojson = vl_api_{n}_t_tojson,\n'
1450 ' .fromjson = vl_api_{n}_t_fromjson,\n'
1451 ' .calc_size = vl_api_{n}_t_calc_size,\n'
1452 ' .is_autoendian = {auto}}};\n'
1453 .format(n=s.caller, ID=s.caller.upper(),
1455 write(' vl_msg_api_config (&c);\n')
1457 d = define_hash[s.reply]
1458 write(' c = (vl_msg_api_msg_config_t) '
1459 '{{.id = VL_API_{ID} + msg_id_base,\n'
1462 ' .cleanup = vl_noop_handler,\n'
1463 ' .endian = vl_api_{n}_t_endian,\n'
1464 ' .print = vl_api_{n}_t_print,\n'
1467 ' .print_json = vl_api_{n}_t_print_json,\n'
1468 ' .tojson = vl_api_{n}_t_tojson,\n'
1469 ' .fromjson = vl_api_{n}_t_fromjson,\n'
1470 ' .calc_size = vl_api_{n}_t_calc_size,\n'
1471 ' .is_autoendian = {auto}}};\n'
1472 .format(n=s.reply, ID=s.reply.upper(),
1474 write(' vl_msg_api_config (&c);\n')
1478 write(' return msg_id_base;\n')
1481 severity = {'error': 'VL_COUNTER_SEVERITY_ERROR',
1482 'info': 'VL_COUNTER_SEVERITY_INFO',
1483 'warn': 'VL_COUNTER_SEVERITY_WARN'}
1485 for cnt in counters:
1487 write('vlib_error_desc_t {}_error_counters[] = {{\n'.format(csetname))
1490 write(' .name = "{}",\n'.format(c['name']))
1491 write(' .desc = "{}",\n'.format(c['description']))
1492 write(' .severity = {},\n'.format(severity[c['severity']]))
1497 def generate_c_test_boilerplate(services, defines, file_crc, module, plugin,
1499 '''Generate code for legacy style VAT. To be deleted.'''
1500 write = stream.write
1502 define_hash = {d.name: d for d in defines}
1505 #define vl_endianfun /* define message structures */
1506 #include "{module}.api.h"
1509 #define vl_calcsizefun
1510 #include "{module}.api.h"
1511 #undef vl_calsizefun
1513 /* instantiate all the print functions we know about */
1514 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
1516 #include "{module}.api.h"
1521 write(hdr.format(module=module))
1524 d = define_hash[s.reply]
1529 ' * Manual definition requested for: \n'
1530 ' * vl_api_{n}_t_handler()\n'
1534 if not define_hash[s.caller].autoreply:
1535 write('/* Generation not supported (vl_api_{n}_t_handler()) */\n'
1538 write('#ifndef VL_API_{n}_T_HANDLER\n'.format(n=s.reply.upper()))
1539 write('static void\n')
1540 write('vl_api_{n}_t_handler (vl_api_{n}_t * mp) {{\n'
1542 write(' vat_main_t * vam = {}_test_main.vat_main;\n'.format(module))
1543 write(' i32 retval = ntohl(mp->retval);\n')
1544 write(' if (vam->async_mode) {\n')
1545 write(' vam->async_errors += (retval < 0);\n')
1546 write(' } else {\n')
1547 write(' vam->retval = retval;\n')
1548 write(' vam->result_ready = 1;\n')
1554 if define_hash[e].manual_print:
1556 write('static void\n')
1557 write('vl_api_{n}_t_handler (vl_api_{n}_t * mp) {{\n'.format(n=e))
1558 write(' vl_print(0, "{n} event called:");\n'.format(n=e))
1559 write(' vl_api_{n}_t_print(mp, 0);\n'.format(n=e))
1562 write('static void\n')
1563 write('setup_message_id_table (vat_main_t * vam, u16 msg_id_base) {\n')
1565 write(' vl_msg_api_set_handlers(VL_API_{ID} + msg_id_base, '
1567 ' vl_api_{n}_t_handler, '
1568 ' vl_noop_handler,\n'
1569 ' vl_api_{n}_t_endian, '
1570 ' vl_api_{n}_t_print,\n'
1571 ' sizeof(vl_api_{n}_t), 1,\n'
1572 ' vl_api_{n}_t_print_json,\n'
1573 ' vl_api_{n}_t_tojson,\n'
1574 ' vl_api_{n}_t_fromjson,\n'
1575 ' vl_api_{n}_t_calc_size);\n'
1576 .format(n=s.reply, ID=s.reply.upper()))
1577 write(' hash_set_mem (vam->function_by_name, "{n}", api_{n});\n'
1578 .format(n=s.caller))
1580 write(' hash_set_mem (vam->help_by_name, "{n}", "{help}");\n'
1582 help=define_hash[s.caller].options['vat_help']))
1588 write(' vl_msg_api_set_handlers(VL_API_{ID} + msg_id_base, '
1590 ' vl_api_{n}_t_handler, '
1591 ' vl_noop_handler,\n'
1592 ' vl_api_{n}_t_endian, '
1593 ' vl_api_{n}_t_print,\n'
1594 ' sizeof(vl_api_{n}_t), 1,\n'
1595 ' vl_api_{n}_t_print_json,\n'
1596 ' vl_api_{n}_t_tojson,\n'
1597 ' vl_api_{n}_t_fromjson,\n'
1598 ' vl_api_{n}_t_calc_size);\n'
1599 .format(n=e, ID=e.upper()))
1602 write('clib_error_t * vat_plugin_register (vat_main_t *vam)\n')
1604 write(' {n}_test_main_t * mainp = &{n}_test_main;\n'.format(n=module))
1605 write(' mainp->vat_main = vam;\n')
1606 write(' mainp->msg_id_base = vl_client_get_first_plugin_msg_id '
1607 ' ("{n}_{crc:08x}");\n'
1608 .format(n=module, crc=file_crc))
1609 write(' if (mainp->msg_id_base == (u16) ~0)\n')
1610 write(' return clib_error_return (0, "{} plugin not loaded...");\n'
1612 write(' setup_message_id_table (vam, mainp->msg_id_base);\n')
1613 write('#ifdef VL_API_LOCAL_SETUP_MESSAGE_ID_TABLE\n')
1614 write(' VL_API_LOCAL_SETUP_MESSAGE_ID_TABLE(vam);\n')
1616 write(' return 0;\n')
1621 '''Check if a method is generated already.'''
1622 def _f(module, d, processed, *args):
1623 if d.name in processed:
1625 processed[d.name] = True
1626 return func(module, d, *args)
1630 def c_test_api_service(s, dump, stream):
1631 '''Generate JSON code for a service.'''
1632 write = stream.write
1634 req_reply_template = '''\
1641 mp = vl_api_{n}_t_fromjson(o, &len);
1643 fprintf(stderr, "Failed converting JSON to API\\n");
1647 mp->_vl_msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1648 vl_api_{n}_t_endian(mp);
1649 vac_write((char *)mp, len);
1655 vac_read(&p, &l, 5); // XXX: Fix timeout
1656 if (p == 0 || l == 0) return 0;
1657 // XXX Will fail in case of event received. Do loop
1658 if (ntohs(*((u16 *)p)) != vac_get_msg_index(VL_API_{R}_CRC)) {{
1659 fprintf(stderr, "Mismatched reply\\n");
1662 vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1663 vl_api_{r}_t_endian(rmp);
1664 return vl_api_{r}_t_tojson(rmp);
1668 dump_details_template = '''\
1672 u16 msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1675 vl_api_{n}_t *mp = vl_api_{n}_t_fromjson(o, &len);
1677 fprintf(stderr, "Failed converting JSON to API\\n");
1680 mp->_vl_msg_id = msg_id;
1681 vl_api_{n}_t_endian(mp);
1682 vac_write((char *)mp, len);
1685 vat2_control_ping(123); // FIX CONTEXT
1686 cJSON *reply = cJSON_CreateArray();
1688 u16 ping_reply_msg_id = vac_get_msg_index(VL_API_CONTROL_PING_REPLY_CRC);
1689 u16 details_msg_id = vac_get_msg_index(VL_API_{R}_CRC);
1695 vac_read(&p, &l, 5); // XXX: Fix timeout
1696 if (p == 0 || l == 0) {{
1701 /* Message can be one of [_details, control_ping_reply
1702 * or unrelated event]
1704 u16 reply_msg_id = ntohs(*((u16 *)p));
1705 if (reply_msg_id == ping_reply_msg_id) {{
1709 if (reply_msg_id == details_msg_id) {{
1710 if (l < sizeof(vl_api_{r}_t)) {{
1714 vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1715 vl_api_{r}_t_endian(rmp);
1716 cJSON_AddItemToArray(reply, vl_api_{r}_t_tojson(rmp));
1723 gets_details_reply_template = '''\
1727 u16 msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1730 vl_api_{n}_t *mp = vl_api_{n}_t_fromjson(o, &len);
1732 fprintf(stderr, "Failed converting JSON to API\\n");
1735 mp->_vl_msg_id = msg_id;
1737 vl_api_{n}_t_endian(mp);
1738 vac_write((char *)mp, len);
1741 cJSON *reply = cJSON_CreateArray();
1743 u16 reply_msg_id = vac_get_msg_index(VL_API_{R}_CRC);
1744 u16 details_msg_id = vac_get_msg_index(VL_API_{D}_CRC);
1750 vac_read(&p, &l, 5); // XXX: Fix timeout
1752 /* Message can be one of [_details, control_ping_reply
1753 * or unrelated event]
1755 u16 msg_id = ntohs(*((u16 *)p));
1756 if (msg_id == reply_msg_id) {{
1757 vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1758 vl_api_{r}_t_endian(rmp);
1759 cJSON_AddItemToArray(reply, vl_api_{r}_t_tojson(rmp));
1763 if (msg_id == details_msg_id) {{
1764 vl_api_{d}_t *rmp = (vl_api_{d}_t *)p;
1765 vl_api_{d}_t_endian(rmp);
1766 cJSON_AddItemToArray(reply, vl_api_{d}_t_tojson(rmp));
1775 if s.stream_message:
1776 write(gets_details_reply_template
1777 .format(n=s.caller, r=s.reply, N=s.caller.upper(),
1778 R=s.reply.upper(), d=s.stream_message,
1779 D=s.stream_message.upper()))
1781 write(dump_details_template.format(n=s.caller, r=s.reply,
1785 write(req_reply_template.format(n=s.caller, r=s.reply,
1790 def generate_c_test2_boilerplate(services, defines, module, stream):
1791 '''Generate code for VAT2 plugin.'''
1792 write = stream.write
1794 define_hash = {d.name: d for d in defines}
1798 #include <vlibapi/api.h>
1799 #include <vlibmemory/api.h>
1800 #include <vppinfra/error.h>
1801 #include <vnet/ip/ip_format_fns.h>
1802 #include <vnet/ethernet/ethernet_format_fns.h>
1804 #define vl_typedefs /* define message structures */
1805 #include <vlibmemory/vl_memory_api_h.h>
1806 #include <vlibmemory/vlib.api_types.h>
1807 #include <vlibmemory/vlib.api.h>
1810 #include "{module}.api_enum.h"
1811 #include "{module}.api_types.h"
1813 #define vl_endianfun /* define message structures */
1814 #include "{module}.api.h"
1817 #define vl_calcsizefun
1818 #include "{module}.api.h"
1819 #undef vl_calsizefun
1821 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
1823 #include "{module}.api.h"
1826 #include "{module}.api_tojson.h"
1827 #include "{module}.api_fromjson.h"
1828 #include <vpp-api/client/vppapiclient.h>
1830 #include <vat2/vat2_helpers.h>
1834 write(hdr.format(module=module))
1837 if s.reply not in define_hash:
1839 c_test_api_service(s, s.stream, stream)
1841 write('void vat2_register_function(char *, cJSON * (*)(cJSON *), cJSON * (*)(void *), u32);\n')
1842 # write('__attribute__((constructor))')
1843 write('clib_error_t *\n')
1844 write('vat2_register_plugin (void) {\n')
1846 if s.reply not in define_hash:
1848 crc = define_hash[s.caller].crc
1849 write(' vat2_register_function("{n}", api_{n}, (cJSON * (*)(void *))vl_api_{n}_t_tojson, 0x{crc:08x});\n'
1850 .format(n=s.caller, crc=crc))
1851 write(' return 0;\n')
1856 # Plugin entry point
1858 def run(args, apifilename, s):
1859 '''Main plugin entry point.'''
1862 if not args.outputdir:
1863 sys.stderr.write('Missing --outputdir argument')
1866 basename = os.path.basename(apifilename)
1867 filename, _ = os.path.splitext(basename)
1868 modulename = filename.replace('.', '_')
1869 filename_enum = os.path.join(args.outputdir + '/' + basename + '_enum.h')
1870 filename_types = os.path.join(args.outputdir + '/' + basename + '_types.h')
1871 filename_c = os.path.join(args.outputdir + '/' + basename + '.c')
1872 filename_c_test = os.path.join(args.outputdir + '/' + basename + '_test.c')
1873 filename_c_test2 = (os.path.join(args.outputdir + '/' + basename +
1875 filename_c_tojson = (os.path.join(args.outputdir +
1876 '/' + basename + '_tojson.h'))
1877 filename_c_fromjson = (os.path.join(args.outputdir + '/' +
1878 basename + '_fromjson.h'))
1880 # Generate separate types file
1882 generate_include_types(s, modulename, st)
1883 with open(filename_types, 'w') as fd:
1885 shutil.copyfileobj(st, fd)
1888 # Generate separate enum file
1890 st.write('#ifndef included_{}_api_enum_h\n'.format(modulename))
1891 st.write('#define included_{}_api_enum_h\n'.format(modulename))
1892 generate_include_enum(s, modulename, st)
1893 generate_include_counters(s['Counters'], st)
1894 st.write('#endif\n')
1895 with open(filename_enum, 'w') as fd:
1897 shutil.copyfileobj(st, fd)
1900 # Generate separate C file
1902 generate_c_boilerplate(s['Service'], s['Define'], s['Counters'],
1903 s['file_crc'], modulename, st)
1904 with open(filename_c, 'w') as fd:
1906 shutil.copyfileobj(st, fd)
1909 # Generate separate C test file
1911 plugin = bool('plugin' in apifilename)
1912 generate_c_test_boilerplate(s['Service'], s['Define'],
1914 modulename, plugin, st)
1915 with open(filename_c_test, 'w') as fd:
1917 shutil.copyfileobj(st, fd)
1920 # Fully autogenerated VATv2 C test file
1922 generate_c_test2_boilerplate(s['Service'], s['Define'],
1924 with open(filename_c_test2, 'w') as fd:
1926 shutil.copyfileobj(st, fd)
1929 # Generate separate JSON file
1931 generate_tojson(s, modulename, st)
1932 with open(filename_c_tojson, 'w') as fd:
1934 shutil.copyfileobj(st, fd)
1937 generate_fromjson(s, modulename, st)
1938 with open(filename_c_fromjson, 'w') as fd:
1940 shutil.copyfileobj(st, fd)
1943 output = TOP_BOILERPLATE.format(datestring=DATESTRING,
1944 input_filename=basename)
1945 output += generate_imports(s['Import'])
1946 output += msg_ids(s)
1947 output += msg_names(s)
1948 output += msg_name_crc_list(s, filename)
1949 output += typedefs(modulename)
1950 printfun_types(s['types'], stream, modulename)
1951 printfun(s['Define'], stream, modulename)
1952 output += stream.getvalue()
1954 output += endianfun(s['types'] + s['Define'], modulename)
1955 output += calc_size_fun(s['types'] + s['Define'], modulename)
1956 output += version_tuple(s, basename)
1957 output += BOTTOM_BOILERPLATE.format(input_filename=basename,
1958 file_crc=s['file_crc'])