PAPI: Add MACAddress object wrapper for vl_api_mac_address_t

Change the definition of vl_api_mac_address_t to an aliased type.

Change-Id: I1434f316d0fad6a099592f39bceeb8faeaf1d134
Signed-off-by: Ole Troan <ot@cisco.com>
diff --git a/src/plugins/gbp/gbp_api.c b/src/plugins/gbp/gbp_api.c
index 5f87db8..74355d1 100644
--- a/src/plugins/gbp/gbp_api.c
+++ b/src/plugins/gbp/gbp_api.c
@@ -154,7 +154,7 @@
 	ip_address_decode (&mp->endpoint.ips[ii], &ips[ii]);
       }
     }
-  mac_address_decode (&mp->endpoint.mac, &mac);
+  mac_address_decode (mp->endpoint.mac, &mac);
 
   if (GBP_ENDPOINT_FLAG_REMOTE & gef)
     {
@@ -260,7 +260,7 @@
   mp->endpoint.flags = gbp_endpoint_flags_encode (gef->gef_flags);
   mp->handle = htonl (gei);
   mp->age = vlib_time_now (vlib_get_main ()) - ge->ge_last_time;
-  mac_address_encode (&ge->ge_key.gek_mac, &mp->endpoint.mac);
+  mac_address_encode (&ge->ge_key.gek_mac, mp->endpoint.mac);
 
   vec_foreach_index (ii, ge->ge_key.gek_ips)
   {
@@ -821,7 +821,7 @@
     return (VNET_API_ERROR_NO_SUCH_FIB);
 
   ip_address_decode (&in->ip, &ip);
-  mac_address_decode (&in->mac, &mac);
+  mac_address_decode (in->mac, &mac);
 
   *gnhi = gbp_next_hop_alloc (&ip, grd, &mac, gbd);
 
diff --git a/src/vat/api_format.c b/src/vat/api_format.c
index fc4c38b..55f5197 100644
--- a/src/vat/api_format.c
+++ b/src/vat/api_format.c
@@ -82,14 +82,6 @@
 #if VPP_API_TEST_BUILTIN == 0
 #include <netdb.h>
 
-/* *INDENT-OFF* */
-const mac_address_t ZERO_MAC_ADDRESS = {
-  .bytes = {
-    0, 0, 0, 0, 0, 0,
-  },
-};
-/* *INDENT-ON* */
-
 u32
 vl (void *p)
 {
@@ -7323,7 +7315,7 @@
 api_bd_ip_mac_add_del (vat_main_t * vam)
 {
   vl_api_address_t ip = VL_API_ZERO_ADDRESS;
-  vl_api_mac_address_t mac = VL_API_ZERO_MAC_ADDRESS;
+  vl_api_mac_address_t mac = { 0 };
   unformat_input_t *i = vam->input;
   vl_api_bd_ip_mac_add_del_t *mp;
   ip46_type_t type;
diff --git a/src/vnet/ethernet/ethernet_types.api b/src/vnet/ethernet/ethernet_types.api
index c33a02c..f945f20 100644
--- a/src/vnet/ethernet/ethernet_types.api
+++ b/src/vnet/ethernet/ethernet_types.api
@@ -14,6 +14,4 @@
  * limitations under the License.
  */
 
-typedef mac_address {
-  u8 bytes[6];
-};
+typedef u8 mac_address[6];
diff --git a/src/vnet/ethernet/ethernet_types_api.c b/src/vnet/ethernet/ethernet_types_api.c
index 4b84d38..90b630d 100644
--- a/src/vnet/ethernet/ethernet_types_api.c
+++ b/src/vnet/ethernet/ethernet_types_api.c
@@ -30,15 +30,15 @@
 #undef vl_printfun
 
 void
-mac_address_decode (const vl_api_mac_address_t * in, mac_address_t * out)
+mac_address_decode (const u8 * in, mac_address_t * out)
 {
-  mac_address_from_bytes (out, in->bytes);
+  mac_address_from_bytes (out, in);
 }
 
 void
-mac_address_encode (const mac_address_t * in, vl_api_mac_address_t * out)
+mac_address_encode (const mac_address_t * in, u8 * out)
 {
-  clib_memcpy_fast (out->bytes, in->bytes, 6);
+  clib_memcpy_fast (out, in->bytes, 6);
 }
 
 /*
diff --git a/src/vnet/ethernet/ethernet_types_api.h b/src/vnet/ethernet/ethernet_types_api.h
index b65d9d4..e2c638d 100644
--- a/src/vnet/ethernet/ethernet_types_api.h
+++ b/src/vnet/ethernet/ethernet_types_api.h
@@ -22,15 +22,8 @@
 
 #include <vnet/ethernet/mac_address.h>
 
-/**
- * Forward declarations so we need not #include the API definitions here
- */
-struct _vl_api_mac_address;
-
-extern void mac_address_decode (const struct _vl_api_mac_address *in,
-				mac_address_t * out);
-extern void mac_address_encode (const mac_address_t * in,
-				struct _vl_api_mac_address *out);
+extern void mac_address_decode (const u8 * in, mac_address_t * out);
+extern void mac_address_encode (const mac_address_t * in, u8 * out);
 
 #endif
 
diff --git a/src/vnet/l2/l2.api b/src/vnet/l2/l2.api
index 7c71ea6..ea24a71 100644
--- a/src/vnet/l2/l2.api
+++ b/src/vnet/l2/l2.api
@@ -499,7 +499,7 @@
   u32 bd_id;
   u8 is_ipv6;
   u8 ip_address[16];
-  u8 mac_address[6];
+  vl_api_mac_address_t mac_address;
 };
 
 /** \brief Dump bridge domain IP to MAC entries
diff --git a/src/vnet/l2/l2_api.c b/src/vnet/l2/l2_api.c
index 2fa238e..9e3a47f 100644
--- a/src/vnet/l2/l2_api.c
+++ b/src/vnet/l2/l2_api.c
@@ -883,7 +883,7 @@
   bd_index = p[0];
 
   type = ip_address_decode (&mp->ip, &ip_addr);
-  mac_address_decode (&mp->mac, &mac);
+  mac_address_decode (mp->mac, &mac);
 
   if (bd_add_del_ip_mac (bd_index, type, &ip_addr, &mac, mp->is_add))
     rv = VNET_API_ERROR_UNSPECIFIED;
diff --git a/src/vpp-api/python/vpp_papi/macaddress.py b/src/vpp-api/python/vpp_papi/macaddress.py
new file mode 100644
index 0000000..a100381
--- /dev/null
+++ b/src/vpp-api/python/vpp_papi/macaddress.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2016 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import binascii
+
+
+def mac_pton(s):
+    '''Convert MAC address as text to binary'''
+    return binascii.unhexlify(s.replace(':', ''))
+
+
+def mac_ntop(binary):
+    '''Convert MAC address as binary to text'''
+    x = b':'.join(binascii.hexlify(binary)[i:i + 2]
+                  for i in range(0, 12, 2))
+    return str(x.decode('ascii'))
+
+
+class MACAddress():
+    def __init__(self, mac):
+        '''MAC Address as a text-string (aa:bb:cc:dd:ee:ff) or 6 bytes'''
+        # Of course Python 2 doesn't distinguish str from bytes
+        if type(mac) is bytes and len(mac) == 6:
+            self.mac_binary = mac
+            self.mac_string = mac_ntop(mac)
+        else:
+            self.mac_binary = mac_pton(mac)
+            self.mac_string = mac
+
+    @property
+    def packed(self):
+        return self.mac_binary
+
+    def __len__(self):
+        return 6
+
+    def __str__(self):
+        return self.mac_string
+
+    def __repr__(self):
+        return '%s(%s)' % (self.__class__.__name__, self.mac_string)
diff --git a/src/vpp-api/python/vpp_papi/vpp_format.py b/src/vpp-api/python/vpp_papi/vpp_format.py
index 1b880ec..fec0667 100644
--- a/src/vpp-api/python/vpp_papi/vpp_format.py
+++ b/src/vpp-api/python/vpp_papi/vpp_format.py
@@ -16,6 +16,7 @@
 from socket import inet_pton, inet_ntop, AF_INET6, AF_INET
 import socket
 import ipaddress
+from . import macaddress
 
 # Copies from vl_api_address_t definition
 ADDRESS_IP4 = 0
@@ -94,6 +95,11 @@
                                   'len': o.prefixlen},
         'str': lambda s: format_vl_api_prefix_t(s)
     },
+    'vl_api_mac_address_t':
+    {
+        'MACAddress': lambda o: o.packed,
+        'str': lambda s: macaddress.mac_pton(s)
+    },
 }
 
 
@@ -118,4 +124,5 @@
     'vl_api_ip4_prefix_t': lambda o: ipaddress.IPv4Network((o.prefix, o.len)),
     'vl_api_address_t': lambda o: unformat_api_address_t(o),
     'vl_api_prefix_t': lambda o: unformat_api_prefix_t(o),
+    'vl_api_mac_address_t': lambda o: macaddress.MACAddress(o),
 }
diff --git a/src/vpp-api/python/vpp_papi/vpp_papi.py b/src/vpp-api/python/vpp_papi/vpp_papi.py
index 4de257c..9c4ede9 100644
--- a/src/vpp-api/python/vpp_papi/vpp_papi.py
+++ b/src/vpp-api/python/vpp_papi/vpp_papi.py
@@ -28,6 +28,7 @@
 import atexit
 from . vpp_serializer import VPPType, VPPEnumType, VPPUnionType, BaseTypes
 from . vpp_serializer import VPPMessage, vpp_get_type, VPPTypeAlias
+from . macaddress import MACAddress, mac_pton, mac_ntop
 
 logger = logging.getLogger(__name__)
 
@@ -57,6 +58,7 @@
         vpp_instance.logger.debug('Cleaning up VPP on exit')
         vpp_instance.disconnect()
 
+
 if sys.version[0] == '2':
     def vpp_iterator(d):
         return d.iteritems()
diff --git a/src/vpp-api/python/vpp_papi/vpp_serializer.py b/src/vpp-api/python/vpp_papi/vpp_serializer.py
index 418c024..d62e3a4 100644
--- a/src/vpp-api/python/vpp_papi/vpp_serializer.py
+++ b/src/vpp-api/python/vpp_papi/vpp_serializer.py
@@ -30,9 +30,10 @@
 logger = logging.getLogger(__name__)
 
 if sys.version[0] == '2':
-    check = lambda d: type(d) is dict
+    def check(d): type(d) is dict
 else:
-    check = lambda d: type(d) is dict or type(d) is bytes
+    def check(d): type(d) is dict or type(d) is bytes
+
 
 def conversion_required(data, field_type):
     if check(data):
@@ -101,7 +102,7 @@
             return b'', 0
         p = BaseTypes('u8', length)
         x, size = p.unpack(data, offset + length_field_size)
-        x2 = x.split(b'\0',1)[0]
+        x2 = x.split(b'\0', 1)[0]
         return (x2.decode('utf8'), size + length_field_size)
 
 
diff --git a/src/vpp/api/types.c b/src/vpp/api/types.c
index 1e36bf5..0a48711 100644
--- a/src/vpp/api/types.c
+++ b/src/vpp/api/types.c
@@ -63,7 +63,7 @@
 {
   vl_api_mac_address_t *mac = va_arg (*args, vl_api_mac_address_t *);
 
-  return (unformat (input, "%U",unformat_ethernet_address, mac->bytes));
+  return (unformat (input, "%U",unformat_ethernet_address, mac));
 }
 
 uword
@@ -86,6 +86,6 @@
 {
   vl_api_mac_address_t *mac = va_arg (*args, vl_api_mac_address_t *);
 
-  return (format (s, "%U", format_ethernet_address, mac->bytes));
+  return (format (s, "%U", format_ethernet_address, mac));
 }