API: Add service definitions for events and singleton messages (second attempt)

Based on https://gerrit.fd.io/r/#/c/10920/

Updates service definition in stats.api with correct reply message names.

Change-Id: I3282bee5304e667e23bc1fab3f43d967a50d880d
Signed-off-by: Marek Gradzki <mgradzki@cisco.com>
diff --git a/src/tools/vppapigen/vppapigen.py b/src/tools/vppapigen/vppapigen.py
index a230e22..620b2ef 100755
--- a/src/tools/vppapigen/vppapigen.py
+++ b/src/tools/vppapigen/vppapigen.py
@@ -41,6 +41,7 @@
         'service': 'SERVICE',
         'rpc': 'RPC',
         'returns': 'RETURNS',
+        'null': 'NULL',
         'stream': 'STREAM',
         'events': 'EVENTS',
         'define': 'DEFINE',
@@ -115,12 +116,6 @@
     # A string containing ignored characters (spaces and tabs)
     t_ignore = ' \t'
 
-
-class Iterator(type):
-    def __iter__(self):
-        return self.iter()
-
-
 class Service():
     def __init__(self, caller, reply, events=[], stream=False):
         self.caller = caller
@@ -346,7 +341,8 @@
             p[0] = p[1] + [p[2]]
 
     def p_service_statement(self, p):
-        '''service_statement : RPC ID RETURNS ID ';'
+        '''service_statement : RPC ID RETURNS NULL ';'
+                             | RPC ID RETURNS ID ';'
                              | RPC ID RETURNS STREAM ID ';'
                              | RPC ID RETURNS ID EVENTS event_list ';' '''
         if len(p) == 8:
@@ -571,15 +567,16 @@
                     if isinstance(o2, Service):
                         s['services'].append(o2)
 
-        # Create services implicitly
+
         msgs = {d.name: d for d in s['defines']}
         svcs = {s.caller: s for s in s['services']}
+        seen_services = {}
 
         for service in svcs:
             if service not in msgs:
                 raise ValueError('Service definition refers to unknown message'
                                  ' definition: {}'.format(service))
-            if svcs[service].reply not in msgs:
+            if svcs[service].reply != 'null' and svcs[service].reply not in msgs:
                 raise ValueError('Service definition refers to unknown message'
                                  ' definition in reply: {}'
                                  .format(svcs[service].reply))
@@ -588,12 +585,18 @@
                     raise ValueError('Service definition refers to unknown '
                                      'event: {} in message: {}'
                                      .format(event, service))
+                seen_services[event] = True
 
+        # Create services implicitly
         for d in msgs:
+            if d in seen_services:
+                continue
             if msgs[d].singular is True:
                 continue
-            if d.endswith('_counters'):
-                continue
+            #if d.endswith('_counters'):
+            #    continue
+            #if d.endswith('_event'):
+            #    continue
             if d.endswith('_reply'):
                 if d[:-6] in svcs:
                     continue
@@ -629,10 +632,16 @@
 
         return s
 
-    def process_imports(self, objs):
+    def process_imports(self, objs, in_import):
+        imported_objs = []
         for o in objs:
             if isinstance(o, Import):
-                return objs + self.process_imports(o.result)
+                return objs + self.process_imports(o.result, True)
+            if in_import:
+                if isinstance(o, Define) and o.typeonly:
+                    imported_objs.append(o)
+        if in_import:
+            return imported_objs
         return objs
 
 
@@ -664,9 +673,6 @@
 # Main
 #
 def main():
-    logging.basicConfig()
-    log = logging.getLogger('vppapigen')
-
     cliparser = argparse.ArgumentParser(description='VPP API generator')
     cliparser.add_argument('--pluginpath', default=""),
     cliparser.add_argument('--includedir', action='append'),
@@ -692,11 +698,18 @@
     else:
         filename = ''
 
+    if args.debug:
+        logging.basicConfig(stream=sys.stdout, level=logging.WARNING)
+    else:
+        logging.basicConfig()
+    log = logging.getLogger('vppapigen')
+
+
     parser = VPPAPI(debug=args.debug, filename=filename, logger=log)
     result = parser.parse_file(args.input, log)
 
     # Build a list of objects. Hash of lists.
-    result = parser.process_imports(result)
+    result = parser.process_imports(result, False)
     s = parser.process(result)
 
     # Add msg_id field
diff --git a/src/vlibmemory/memclnt.api b/src/vlibmemory/memclnt.api
index fb08f51..d03c105 100644
--- a/src/vlibmemory/memclnt.api
+++ b/src/vlibmemory/memclnt.api
@@ -17,6 +17,16 @@
 option version = "1.0.0";
 
 /*
+ * Define services not following the normal convetions here
+ */
+service {
+  rpc memclnt_rx_thread_suspend returns null;
+  rpc memclnt_read_timeout returns null;
+  rpc rx_thread_exit returns null;
+  rpc trace_plugin_msg_ids returns null;
+};
+
+/*
  * Create a client registration 
  */
 manual_print 
diff --git a/src/vnet/dhcp/dhcp.api b/src/vnet/dhcp/dhcp.api
index 721a1be..975aa6e 100644
--- a/src/vnet/dhcp/dhcp.api
+++ b/src/vnet/dhcp/dhcp.api
@@ -109,6 +109,10 @@
   u8 host_mac[6];
 };
 
+service {
+  rpc dhcp_client_config returns dhcp_client_config_reply events dhcp_compl_event;
+};
+
 /** \brief Dump DHCP proxy table
     @param client_index - opaque cookie to identify the sender
     @param True for IPv6 proxy table
diff --git a/src/vnet/interface.api b/src/vnet/interface.api
index a8733d1..a5a9184 100644
--- a/src/vnet/interface.api
+++ b/src/vnet/interface.api
@@ -1,5 +1,10 @@
 option version = "1.0.0";
 
+service {
+  rpc want_interface_events returns want_interface_events_reply
+    events sw_interface_event;
+};
+
 /** \brief Set flags on the interface
     @param client_index - opaque cookie to identify the sender
     @param context - sender context, to match reply w/ request
@@ -273,66 +278,6 @@
   u64 rx_mpls;
 };
 
-/** \brief Simple stats counters structure
-    @param vnet_counter_type- such as ip4, ip6, punts, etc
-    @param first_sw_if_index - first sw index in block of index, counts
-    @param count - number of counters, equal to the number of interfaces in
-      this stats block
-    @param data - contiguous block of u64 counters
-
-    vnet_counter_type defined in enums - plural - in vnet/interface.h
-*/
-manual_print manual_endian define vnet_interface_simple_counters
-{
-  u8 vnet_counter_type;
-  u32 first_sw_if_index;
-  u32 count;
-  u64 data[count];
-};
-
-/** \brief Combined stats counters structure
-    @param vnet_counter_type- such as ip4, ip6, punts, etc
-    @param first_sw_if_index - first sw index in block of index, counts
-    @param count - number of counters, equal to the number of interfaces in
-      this stats block
-    @param data - contiguous block of vlib_counter_t structures
-
-    vnet_counter_type defined in enums - plural - in vnet/interface.h
-*/
-manual_print manual_endian define vnet_interface_combined_counters
-{
-  u8 vnet_counter_type;
-  u32 first_sw_if_index;
-  u32 count;
-  vl_api_vlib_counter_t data[count];
-};
-
-
-/** \brief Simple per interface stats counters structure
-    @param count - number of elements in message
-    @param timestamp - u32 vlib timestamp for control plane
-    @param data[count] - vl_api_vnet_simple_counter_t 
-
-*/
-manual_print manual_endian define vnet_per_interface_simple_counters
-{
-  u32 count;
-  u32 timestamp;
-  vl_api_vnet_simple_counter_t data[count];
-};
-
-/** \brief Combined stats counters structure per interface
-    @param count - number of elements in message
-    @param timestamp - u32 vlib timestamp for control plane
-    @param data[count] - vl_api_vnet_combined_counter_t
-*/
-manual_print manual_endian define vnet_per_interface_combined_counters
-{
-  u32 count;
-  u32 timestamp;
-  vl_api_vnet_combined_counter_t data[count];
-};
-
 /** \brief Set unnumbered interface add / del request
     @param client_index - opaque cookie to identify the sender
     @param context - sender context, to match reply w/ request
diff --git a/src/vnet/ip/ip.api b/src/vnet/ip/ip.api
index 6ed5a9d..b94d6d7 100644
--- a/src/vnet/ip/ip.api
+++ b/src/vnet/ip/ip.api
@@ -686,6 +686,11 @@
   u8 mac_ip;
 };
 
+service {
+  rpc want_ip4_arp_events returns want_ip4_arp_events_reply
+    events ip4_arp_event;
+};
+
 /** \brief Register for ip6 nd resolution events
     @param client_index - opaque cookie to identify the sender
     @param context - sender context, to match reply w/ request
@@ -720,6 +725,11 @@
   u8 mac_ip;
 };
 
+service {
+  rpc want_ip6_nd_events returns want_ip6_nd_events_reply
+    events ip6_nd_event;
+};
+
 /** \brief Proxy ARP add / del request
     @param client_index - opaque cookie to identify the sender
     @param context - sender context, to match reply w/ request
diff --git a/src/vnet/l2/l2.api b/src/vnet/l2/l2.api
index 5102783..3fb6de0 100644
--- a/src/vnet/l2/l2.api
+++ b/src/vnet/l2/l2.api
@@ -182,6 +182,11 @@
   vl_api_mac_entry_t mac[n_macs];
 };
 
+service {
+  rpc want_l2_macs_events returns want_l2_macs_events_reply
+    events l2_macs_event;
+};
+
 /** \brief Set interface L2 flags (such as L2_LEARN, L2_FWD, 
     L2_FLOOD, L2_UU_FLOOD, or L2_ARP_TERM bits). This can be used
     to disable one or more of the features represented by the
diff --git a/src/vpp-api/vapi/vapi_c_gen.py b/src/vpp-api/vapi/vapi_c_gen.py
index 4b7da6a..60c54e4 100755
--- a/src/vpp-api/vapi/vapi_c_gen.py
+++ b/src/vpp-api/vapi/vapi_c_gen.py
@@ -70,6 +70,13 @@
     def __init__(self, name, fields):
         super(CStruct, self).__init__(name, fields)
 
+    def duplicate_barrier(func):
+        def func_wrapper(self):
+            name = self.get_c_name()
+            return "#ifndef defined_{}\n#define defined_{}\n{}\n#endif".format(name, name, func(self))
+        return func_wrapper
+
+    @duplicate_barrier
     def get_c_def(self):
         return "\n".join([
             "typedef struct __attribute__((__packed__)) {",
@@ -271,6 +278,13 @@
             "}",
         ])
 
+    def duplicate_barrier(func):
+        def func_wrapper(self):
+            name = self.get_payload_struct_name()
+            return "#ifndef defined_{}\n#define defined_{}\n{}\n#endif".format(name, name, func(self))
+        return func_wrapper
+
+    @duplicate_barrier
     def get_c_def(self):
         if self.has_payload():
             return "\n".join([
@@ -585,9 +599,12 @@
     print("")
     function_attrs = "static inline "
     for t in parser.types_by_json[j].values():
+        print("#ifndef defined_inline_%s" % t.get_c_name())
+        print("#define defined_inline_%s" % t.get_c_name())
         print("%s%s" % (function_attrs, t.get_swap_to_be_func_def()))
         print("")
         print("%s%s" % (function_attrs, t.get_swap_to_host_func_def()))
+        print("#endif")
         print("")
     for m in parser.messages_by_json[j].values():
         if m.has_payload():
diff --git a/src/vpp/oam/oam.api b/src/vpp/oam/oam.api
index d03902e..3823d8e 100644
--- a/src/vpp/oam/oam.api
+++ b/src/vpp/oam/oam.api
@@ -45,6 +45,11 @@
   u32 pid;
 };
 
+service {
+  rpc want_oam_events returns want_oam_events_reply
+    events oam_event;
+};
+
 /** \brief OAM add / del target request
     @param client_index - opaque cookie to identify the sender
     @param context - sender context, to match reply w/ request
diff --git a/src/vpp/stats/stats.api b/src/vpp/stats/stats.api
index b429faa..2913d24 100644
--- a/src/vpp/stats/stats.api
+++ b/src/vpp/stats/stats.api
@@ -20,6 +20,43 @@
 
 option version = "1.0.0";
 
+import "vnet/interface.api";
+
+service {
+  rpc want_stats
+    returns want_stats_reply;
+  rpc want_interface_simple_stats
+    returns want_interface_simple_stats_reply
+    events vnet_interface_simple_counters;
+  rpc want_per_interface_simple_stats
+    returns want_per_interface_simple_stats_reply
+    events vnet_per_interface_simple_counters;
+  rpc want_interface_combined_stats
+    returns want_interface_combined_stats_reply
+    events vnet_interface_combined_counters;
+  rpc want_per_interface_combined_stats
+    returns want_per_interface_combined_stats_reply
+    events vnet_per_interface_combined_counters;
+  rpc want_ip4_fib_stats
+    returns want_ip4_fib_stats_reply
+    events vnet_ip4_fib_counters;
+  rpc want_ip6_fib_stats
+    returns want_ip6_fib_stats_reply
+    events vnet_ip6_fib_counters;
+  rpc want_ip4_mfib_stats
+    returns want_ip4_mfib_stats_reply
+    events vnet_ip4_mfib_counters;
+  rpc want_ip6_mfib_stats
+    returns want_ip6_mfib_stats_reply
+    events vnet_ip6_mfib_counters;
+  rpc want_ip4_nbr_stats
+    returns want_ip4_nbr_stats_reply
+    events vnet_ip4_nbr_counters;
+  rpc want_ip6_nbr_stats
+    returns want_ip6_nbr_stats_reply
+    events vnet_ip6_nbr_counters;
+};
+
 /** \brief Want Stats, enable/disable ALL stats updates
     @param client_index - opaque cookie to identify the sender
     @param context - sender context, to match reply w/ request
@@ -292,6 +329,64 @@
   vl_api_ip6_nbr_counter_t c[count];
 };
 
+/** \brief Simple stats counters structure
+    @param vnet_counter_type- such as ip4, ip6, punts, etc
+    @param first_sw_if_index - first sw index in block of index, counts
+    @param count - number of counters, equal to the number of interfaces in
+      this stats block
+    @param data - contiguous block of u64 counters
+
+    vnet_counter_type defined in enums - plural - in vnet/interface.h
+*/
+manual_print manual_endian define vnet_interface_simple_counters
+{
+  u8 vnet_counter_type;
+  u32 first_sw_if_index;
+  u32 count;
+  u64 data[count];
+};
+
+/** \brief Combined stats counters structure
+    @param vnet_counter_type- such as ip4, ip6, punts, etc
+    @param first_sw_if_index - first sw index in block of index, counts
+    @param count - number of counters, equal to the number of interfaces in
+      this stats block
+    @param data - contiguous block of vlib_counter_t structures
+
+    vnet_counter_type defined in enums - plural - in vnet/interface.h
+*/
+manual_print manual_endian define vnet_interface_combined_counters
+{
+  u8 vnet_counter_type;
+  u32 first_sw_if_index;
+  u32 count;
+  vl_api_vlib_counter_t data[count];
+};
+
+/** \brief Simple per interface stats counters structure
+    @param count - number of elements in message
+    @param timestamp - u32 vlib timestamp for control plane
+    @param data[count] - vl_api_vnet_simple_counter_t 
+
+*/
+manual_print manual_endian define vnet_per_interface_simple_counters
+{
+  u32 count;
+  u32 timestamp;
+  vl_api_vnet_simple_counter_t data[count];
+};
+
+/** \brief Combined stats counters structure per interface
+    @param count - number of elements in message
+    @param timestamp - u32 vlib timestamp for control plane
+    @param data[count] - vl_api_vnet_combined_counter_t
+*/
+manual_print manual_endian define vnet_per_interface_combined_counters
+{
+  u32 count;
+  u32 timestamp;
+  vl_api_vnet_combined_counter_t data[count];
+};
 
 /** \brief Request for a single block of summary stats
     @param client_index - opaque cookie to identify the sender