API: Add support for "defaults" 46/19246/4
authorOle Troan <ot@cisco.com>
Tue, 30 Apr 2019 08:04:36 +0000 (10:04 +0200)
committerPaul Vinciguerra <pvinci@vinciconsulting.com>
Fri, 7 Jun 2019 10:38:35 +0000 (10:38 +0000)
Add support in the API language for specifying a field default.
Add default support in Python binding.

define foo {
  u16 mtu [default = 1500];
};

This is client side only. I.e. if the mtu argument is not passed to the foo function,
the client language binding will set it default to 1500.

Change-Id: I5df43f3cd87cb300b40ca38e15dcab25b40e424a
Signed-off-by: Ole Troan <ot@cisco.com>
src/tools/vppapigen/vppapigen.py
src/vpp-api/python/vpp_papi/tests/test_vpp_serializer.py
src/vpp-api/python/vpp_papi/vpp_serializer.py
src/vpp-api/python/vpp_papi/vpp_stats.py
src/vpp-api/python/vpp_papi/vpp_transport_socket.py

index c60e43e..fd87b18 100755 (executable)
@@ -509,18 +509,31 @@ class VPPAPIParser(object):
         else:
             p[0] = p[1]
 
+    def p_field_options(self, p):
+        '''field_options : field_option
+                           | field_options field_option'''
+        if len(p) == 2:
+            p[0] = p[1]
+        else:
+            p[0] = { **p[1], **p[2] }
+
+    def p_field_option(self, p):
+        '''field_option : ID '=' assignee ','
+                        | ID '=' assignee
+        '''
+        p[0] = { p[1]: p[3] }
+
     def p_declaration(self, p):
         '''declaration : type_specifier ID ';'
-                       | type_specifier ID '[' ID '=' assignee ']' ';' '''
-        if len(p) == 9:
-            p[0] = Field(p[1], p[2], {p[4]: p[6]})
+                       | type_specifier ID '[' field_options ']' ';' '''
+        if len(p) == 7:
+            p[0] = Field(p[1], p[2], p[4])
         elif len(p) == 4:
             p[0] = Field(p[1], p[2])
         else:
             self._parse_error('ERROR')
         self.fields.append(p[2])
 
-
     def p_declaration_array(self, p):
         '''declaration : type_specifier ID '[' NUM ']' ';'
                        | type_specifier ID '[' ID ']' ';' '''
index ec73347..74a0a62 100755 (executable)
@@ -9,6 +9,7 @@ import logging
 import sys
 from ipaddress import *
 
+
 class TestLimits(unittest.TestCase):
     def test_limit(self):
         limited_type = VPPType('limited_type_t',
@@ -16,14 +17,27 @@ class TestLimits(unittest.TestCase):
         unlimited_type = VPPType('limited_type_t',
                                  [['string', 'name']])
 
-
-        b = limited_type.pack({'name':'foobar'})
+        b = limited_type.pack({'name': 'foobar'})
         self.assertEqual(len(b), 10)
-        b = unlimited_type.pack({'name':'foobar'})
+        b = unlimited_type.pack({'name': 'foobar'})
         self.assertEqual(len(b), 10)
 
         with self.assertRaises(VPPSerializerValueError):
-            b = limited_type.pack({'name':'foobar'*3})
+            b = limited_type.pack({'name': 'foobar'*3})
+
+
+class TestDefaults(unittest.TestCase):
+    def test_defaults(self):
+        default_type = VPPType('default_type_t',
+                               [['u16', 'mtu', {'default': 1500, 'limit': 0}]])
+
+        b = default_type.pack({})
+        self.assertEqual(len(b), 2)
+
+        nt, size = default_type.unpack(b)
+        self.assertEqual(len(b), size)
+        self.assertEqual(nt.mtu, 1500)
+
 
 class TestAddType(unittest.TestCase):
 
index d109331..2d775d5 100644 (file)
 import struct
 import collections
 import sys
+import logging
+from . import vpp_format
+import ipaddress
+import socket
 
 if sys.version_info <= (3, 4):
     from aenum import IntEnum
@@ -27,11 +31,6 @@ if sys.version_info <= (3, 6):
 else:
     from enum import IntFlag
 
-import logging
-from . import vpp_format
-import ipaddress
-
-import socket
 
 #
 # Set log-level in application by doing e.g.:
@@ -69,7 +68,7 @@ def conversion_unpacker(data, field_type):
 
 
 class BaseTypes(object):
-    def __init__(self, type, elements=0):
+    def __init__(self, type, elements=0, options=None):
         base_types = {'u8': '>B',
                       'string': '>s',
                       'u16': '>H',
@@ -85,10 +84,18 @@ class BaseTypes(object):
         else:
             self.packer = struct.Struct(base_types[type])
         self.size = self.packer.size
+        self.options = options
+
+    def __call__(self, args):
+        self.options = args
+        return self
 
     def pack(self, data, kwargs=None):
         if not data:  # Default to zero if not specified
-            data = 0
+            if self.options and 'default' in self.options:
+                data = self.options['default']
+            else:
+                data = 0
         return self.packer.pack(data)
 
     def unpack(self, data, offset, result=None, ntc=False):
@@ -148,6 +155,10 @@ class FixedList_u8(object):
         self.size = self.packer.size
         self.field_type = field_type
 
+    def __call__(self, args):
+        self.options = args
+        return self
+
     def pack(self, data, kwargs=None):
         """Packs a fixed length bytestring. Left-pads with zeros
         if input data is too short."""
@@ -183,6 +194,10 @@ class FixedList(object):
         self.name = name
         self.field_type = field_type
 
+    def __call__(self, args):
+        self.options = args
+        return self
+
     def pack(self, list, kwargs):
         if len(list) != self.num:
             raise VPPSerializerValueError(
@@ -214,6 +229,10 @@ class VLAList(object):
         self.size = self.packer.size
         self.length_field = len_field_name
 
+    def __call__(self, args):
+        self.options = args
+        return self
+
     def pack(self, list, kwargs=None):
         if not list:
             return b""
@@ -257,6 +276,10 @@ class VLAList_legacy():
         self.packer = types[field_type]
         self.size = self.packer.size
 
+    def __call__(self, args):
+        self.options = args
+        return self
+
     def pack(self, list, kwargs=None):
         if self.packer.size == 1:
             return bytes(list)
@@ -298,6 +321,10 @@ class VPPEnumType(object):
         self.enum = IntFlag(name, e_hash)
         types[name] = self
 
+    def __call__(self, args):
+        self.options = args
+        return self
+
     def __getattr__(self, name):
         return self.enum[name]
 
@@ -338,6 +365,10 @@ class VPPUnionType(object):
         types[name] = self
         self.tuple = collections.namedtuple(name, fields, rename=True)
 
+    def __call__(self, args):
+        self.options = args
+        return self
+
     # Union of variable length?
     def pack(self, data, kwargs=None):
         if not data:
@@ -383,6 +414,10 @@ class VPPTypeAlias(object):
 
         types[name] = self
 
+    def __call__(self, args):
+        self.options = args
+        return self
+
     def pack(self, data, kwargs=None):
         if data and conversion_required(data, self.name):
             try:
@@ -423,14 +458,14 @@ class VPPType(object):
                 raise VPPSerializerValueError(
                     'Unknown message type {}'.format(f_type))
 
-            l = len(f)
+            fieldlen = len(f)
             options = [x for x in f if type(x) is dict]
             if len(options):
                 self.options = options[0]
-                l -= 1
+                fieldlen -= 1
             else:
                 self.options = {}
-            if l == 3:  # list
+            if fieldlen == 3:  # list
                 list_elements = f[2]
                 if list_elements == 0:
                     p = VLAList_legacy(f_name, f_type)
@@ -443,15 +478,12 @@ class VPPType(object):
                     p = FixedList(f_name, f_type, list_elements)
                     self.packers.append(p)
                     size += p.size
-            elif l == 4:  # Variable length list
+            elif fieldlen == 4:  # Variable length list
                 length_index = self.fields.index(f[3])
                 p = VLAList(f_name, f_type, f[3], length_index)
                 self.packers.append(p)
             else:
-                if f_type == 'string':
-                    p = types[f_type](self.options)
-                else:
-                    p = types[f_type]
+                p = types[f_type](self.options)
                 self.packers.append(p)
                 size += p.size
 
@@ -459,6 +491,10 @@ class VPPType(object):
         self.tuple = collections.namedtuple(name, self.fields, rename=True)
         types[name] = self
 
+    def __call__(self, args):
+        self.options = args
+        return self
+
     def pack(self, data, kwargs=None):
         if not kwargs:
             kwargs = data
index 76ccf10..de72249 100644 (file)
@@ -73,7 +73,8 @@ void stat_segment_disconnect (void);
 
 uint32_t *stat_segment_ls_r (uint8_t ** patterns, stat_client_main_t * sm);
 uint32_t *stat_segment_ls (uint8_t ** pattern);
-stat_segment_data_t *stat_segment_dump_r (uint32_t * stats, stat_client_main_t * sm);
+stat_segment_data_t *stat_segment_dump_r (uint32_t * stats,
+                                          stat_client_main_t * sm);
 stat_segment_data_t *stat_segment_dump (uint32_t * counter_vec);
 void stat_segment_data_free (stat_segment_data_t * res);
 
@@ -133,7 +134,9 @@ def error_vec_list(api, e):
     return vec
 
 def name_vec_list(api, e):
-    return [ffi.string(e[i]).decode('utf-8') for i in range(api.stat_segment_vec_len(e)) if e[i] != ffi.NULL]
+    return [ffi.string(e[i]).decode('utf-8') for i in
+            range(api.stat_segment_vec_len(e)) if e[i] != ffi.NULL]
+
 
 def stat_entry_to_python(api, e):
     # Scalar index
@@ -222,7 +225,8 @@ class VPPStats(object):
 
         if rv == ffi.NULL:
             raise VPPStatsIOError()
-        return [ffi.string(self.api.stat_segment_index_to_name_r(rv[i], self.client)).decode('utf-8')
+        return [ffi.string(self.api.stat_segment_index_to_name_r(
+            rv[i], self.client)).decode('utf-8')
                 for i in range(self.api.stat_segment_vec_len(rv))]
 
     def dump(self, counters):
index 6989e9a..6e228e4 100644 (file)
@@ -193,23 +193,23 @@ class VppTransport(object):
         hdr = self.socket.recv(16)
         if not hdr:
             return
-        (_, l, _) = self.header.unpack(hdr) # If at head of message
+        (_, hdrlen, _) = self.header.unpack(hdr)  # If at head of message
 
         # Read rest of message
-        msg = self.socket.recv(l)
-        if l > len(msg):
+        msg = self.socket.recv(hdrlen)
+        if hdrlen > len(msg):
             nbytes = len(msg)
-            buf = bytearray(l)
+            buf = bytearray(hdrlen)
             view = memoryview(buf)
             view[:nbytes] = msg
             view = view[nbytes:]
-            left = l - nbytes
+            left = hdrlen - nbytes
             while left:
                 nbytes = self.socket.recv_into(view, left)
                 view = view[nbytes:]
                 left -= nbytes
             return buf
-        if l == len(msg):
+        if hdrlen == len(msg):
             return msg
         raise VppTransportSocketIOError(1, 'Unknown socket read error')