python API: work towards python/vpp api separation 70/5570/4
authorKlement Sekera <ksekera@cisco.com>
Wed, 1 Mar 2017 08:53:19 +0000 (09:53 +0100)
committerOle Trøan <otroan@employees.org>
Fri, 3 Mar 2017 07:58:23 +0000 (07:58 +0000)
This change improves vpp_papi behaviour by introducing alternate way of
calling vpp APIs.

The common code is the same:

vpp = VPP(...)
vpp.connect(...)

Calling VPP API is different, instead of deprecated:

vpp.show_version() # deprecated

one should write

vpp.api.show_version()

this allows VPP messages like "connect" and "disconnect" to be used,
once the old API is dropped (in 17.07). Also part of this patch is a
check for name conflict, to prevent VPP object overwriting its own
functionality with generated code based on json files.

Change-Id: I22e573b6a45f8b2a1f0340c5c2597c194fe42ca4
Signed-off-by: Klement Sekera <ksekera@cisco.com>
src/vpp-api/python/vpp_papi/vpp_papi.py
test/vpp_papi_provider.py

index 110f4b1..83247ff 100644 (file)
@@ -31,6 +31,20 @@ def vpp_atexit(self):
         eprint ('Cleaning up VPP on exit')
         self.disconnect()
 
+
+class Empty(object):
+    pass
+
+
+class FuncWrapper(object):
+    def __init__(self, func):
+        self._func = func
+        self.__name__ = func.__name__
+
+    def __call__(self, **kwargs):
+        return self._func(**kwargs)
+
+
 class VPP():
     """VPP interface.
 
@@ -309,9 +323,16 @@ class VPP():
         f.__doc__ = ", ".join(["%s %s" % (argtypes[k], k) for k in args.keys()])
         return f
 
+    @property
+    def api(self):
+        if not hasattr(self, "_api"):
+            raise Exception("Not connected, api definitions not available")
+        return self._api
+
     def _register_functions(self, async=False):
         self.id_names = [None] * (self.vpp_dictionary_maxid + 1)
         self.id_msgdef = [None] * (self.vpp_dictionary_maxid + 1)
+        self._api = Empty()
         for name, msgdef in self.messages.iteritems():
             if name in self.vpp_dictionary:
                 if self.messages[name]['crc'] != self.vpp_dictionary[name]['crc']:
@@ -322,7 +343,15 @@ class VPP():
                 self.id_msgdef[i] = msgdef
                 self.id_names[i] = name
                 multipart = True if name.find('_dump') > 0 else False
-                setattr(self, name, self.make_function(name, i, msgdef, multipart, async))
+                f = self.make_function(name, i, msgdef, multipart, async)
+                setattr(self._api, name, FuncWrapper(f))
+
+                # olf API stuff starts here - will be removed in 17.07
+                if hasattr(self, name):
+                    raise NameError(
+                        3, "Conflicting name in JSON definition: `%s'" % name)
+                setattr(self, name, f)
+                # old API stuff ends here
 
     def _write (self, buf):
         """Send a binary-packed message to VPP."""
index de189c3..5d4d6b7 100644 (file)
@@ -1,5 +1,4 @@
 import os
-import socket
 import fnmatch
 import time
 from hook import Hook
@@ -56,7 +55,7 @@ class VppPapiProvider(object):
             for filename in fnmatch.filter(filenames, '*.api.json'):
                 jsonfiles.append(os.path.join(root, filename))
 
-        self.papi = VPP(jsonfiles)
+        self.vpp = VPP(jsonfiles)
         self._events = deque()
 
     def __enter__(self):
@@ -124,12 +123,13 @@ class VppPapiProvider(object):
 
     def connect(self):
         """Connect the API to VPP"""
-        self.papi.connect(self.name, self.shm_prefix)
-        self.papi.register_event_callback(self)
+        self.vpp.connect(self.name, self.shm_prefix)
+        self.papi = self.vpp.api
+        self.vpp.register_event_callback(self)
 
     def disconnect(self):
         """Disconnect the API from VPP"""
-        self.papi.disconnect()
+        self.vpp.disconnect()
 
     def api(self, api_fn, api_args, expected_retval=0):
         """ Call API function and check it's return value.
@@ -190,7 +190,7 @@ class VppPapiProvider(object):
 
     def show_version(self):
         """ """
-        return self.papi.show_version()
+        return self.api(self.papi.show_version, {})
 
     def pg_create_interface(self, pg_index):
         """