Python API: Preparation for RPM/DEB packaging. 18/2918/10
authorOle Troan <ot@cisco.com>
Mon, 12 Sep 2016 20:00:32 +0000 (22:00 +0200)
committerDamjan Marion <dmarion.lists@gmail.com>
Fri, 23 Sep 2016 13:18:46 +0000 (13:18 +0000)
Recheck.

Repackage the Python API binding to include all
necessary modules in a single Python package.

Change-Id: I5e35141d413bfb1aad650217e1ca07d85646c349
Signed-off-by: Ole Troan <ot@cisco.com>
12 files changed:
build-root/rpm/vpp.spec
vpp-api/python/Makefile.am
vpp-api/python/pneum/pneum.c
vpp-api/python/setup.cfg
vpp-api/python/setup.py
vpp-api/python/tests/test_base.py [deleted file]
vpp-api/python/tests/test_modules.py
vpp-api/python/tests/test_papi.py
vpp-api/python/tests/test_version.py [new file with mode: 0755]
vpp-api/python/vpp_papi/__init__.py
vpp-api/python/vpp_papi/vpp_api_base.py
vpp-api/python/vpp_papi/vpp_papi.py

index 96e6f0f..35964b6 100644 (file)
@@ -6,6 +6,17 @@
 %define _version         %(../scripts/version rpm-version)
 %define _release         %(../scripts/version rpm-release)
 
+# Failsafe backport of Python2-macros for RHEL <= 6
+%{!?python_sitelib: %global python_sitelib      %(%{__python} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")}
+%{!?python_sitearch:    %global python_sitearch     %(%{__python} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib(1))")}
+%{!?python_version: %global python_version      %(%{__python} -c "import sys; sys.stdout.write(sys.version[:3])")}
+%{!?__python2:      %global __python2       %{__python}}
+%{!?python2_sitelib:    %global python2_sitelib     %{python_sitelib}}
+%{!?python2_sitearch:   %global python2_sitearch    %{python_sitearch}}
+%{!?python2_version:    %global python2_version     %{python_version}}
+
+%{!?python2_minor_version: %define python2_minor_version %(%{__python} -c "import sys ; print sys.version[2:3]")}
+
 Name: vpp
 Summary: Vector Packet Processing
 License: MIT
@@ -56,6 +67,14 @@ Requires: vpp = %{_version}-%{_release}
 %description plugins
 This package contains VPP plugins
 
+%package python-api
+Summary: VPP api python bindings
+Group: Development/Libraries
+Requires: vpp = %{_version}-%{_release}, vpp-lib = %{_version}-%{_release}, devel = %{_version}-%{_release}
+
+%description python-api
+This package contains the python bindings for the vpp api
+
 %pre
 # Add the vpp group
 groupadd -f -r vpp
@@ -68,6 +87,7 @@ mkdir -p -m755 %{buildroot}%{_bindir}
 mkdir -p -m755 %{buildroot}%{_unitdir}
 install -p -m 755 %{_vpp_install_dir}/*/bin/* %{buildroot}%{_bindir}
 install -p -m 755 %{_vpp_build_dir}/vppapigen/vppapigen %{buildroot}%{_bindir}
+install -p -m 755 ../../vppapigen/pyvppapigen.py %{buildroot}%{_bindir}
 #
 # configs
 #
@@ -93,6 +113,13 @@ do
           ln -fs $file $(echo $file | sed -e 's/\(\.so\)\.[0-9]\+.*/\1/') )
 done
 
+# Python bindings
+mkdir -p -m755 %{buildroot}%{python2_sitelib}/vpp_papi
+for file in $(find %{_vpp_install_dir}/*/lib/python2.7/site-packages/ -type f -print | grep -v pyc | grep -v pyo)
+do
+       install -p -m 666 $file %{buildroot}%{python2_sitelib}/vpp_papi/
+done
+
 #
 # devel
 #
@@ -162,10 +189,15 @@ sysctl --system
 %exclude %{_libdir}/vpp_api_test_plugins
 %{_libdir}/*
 
+%files python-api
+%defattr(644,root,root)
+%{python2_sitelib}/vpp_papi/*
+
 %files devel
 %defattr(-,bin,bin)
 /usr/bin/vppapigen
 /usr/bin/jvpp_gen.py
+/usr/bin/pyvppapigen.py
 %{_includedir}/*
 %{python2_sitelib}/jvppgen/*
 /usr/share/doc/vpp/examples/sample-plugin
index eb58933..59b1b92 100644 (file)
@@ -25,42 +25,41 @@ nobase_include_HEADERS = pneum/pneum.h
 #
 # Python / C extension
 #
-lib_LTLIBRARIES += vpp_api.la
-vpp_api_la_SOURCES = pneum/pneum.c vpp_papi/pneum_wrap.c
-vpp_api_la_LIBADD = -lvlibmemoryclient -lvlibapi -lsvm -lvppinfra -lpthread -lm -lrt
-vpp_api_la_LDFLAGS = -module $(shell python-config --ldflags)
-vpp_api_la_CPPFLAGS = $(shell python-config --includes)
-
-# Kept around for setuptools based install.
 lib_LTLIBRARIES += libpneum.la
-libpneum_la_SOURCES = pneum/pneum.c setup.py
-libpneum_la_LIBADD = -lvlibmemoryclient -lvlibapi -lsvm -lvppinfra -lpthread -lm -lrt
+libpneum_la_SOURCES = pneum/pneum.c
+libpneum_la_LIBADD = -lvlibmemoryclient -lvlibapi -lsvm -lvppinfra -lpthread \
+                     -lm -lrt
 libpneum_la_LDFLAGS = -module
 libpneum_la_CPPFLAGS =
 
 #
 # Core VPP API
 #
-BUILT_SOURCES +=                                       \
-       $(prefix)/../vpp/vpp-api/vpe.py                 \
-       $(prefix)/../vlib-api/vlibmemory/memclnt.py
+$(srcdir)/vpp_papi/vpe.py: $(prefix)/../vpp/vpp-api/vpe.api
+       $(info Creating Python binding for $@)
+       $(CC) $(CPPFLAGS) -E -P -C -x c $<      \
+       | vppapigen --input - --python -        \
+       | pyvppapigen.py --input - > $(srcdir)/vpp_papi/$(notdir $@)
 
-%.py: %.api
+$(srcdir)/vpp_papi/memclnt.py: $(prefix)/../vlib-api/vlibmemory/memclnt.api
        $(info Creating Python binding for $@)
        $(CC) $(CPPFLAGS) -E -P -C -x c $<      \
        | vppapigen --input - --python -        \
-       | pyvppapigen.py --input - > $@
+       | pyvppapigen.py --input - > $(srcdir)/vpp_papi/$(notdir $@)
 
-#
 # TODO: Support both Python 2 and 3.
-install-exec-local:
-       cd $(srcdir); \
-       mkdir -p $(prefix)/lib/python2.7/site-packages; \
-       PYTHONUSERBASE=$(prefix) python setup.py install --user
+install-exec-local: $(srcdir)/vpp_papi/vpe.py $(srcdir)/vpp_papi/memclnt.py
+       cd $(srcdir);                                                   \
+       mkdir -p $(prefix)/lib/python2.7/site-packages;                 \
+       PYTHONUSERBASE=$(prefix)                                        \
+       python setup.py build_ext -L $(prefix)/lib64 install --user
 
 #
 # Test client
 #
 noinst_PROGRAMS += test_pneum
 test_pneum_SOURCES = pneum/pneum.c pneum/test_pneum.c
-test_pneum_LDADD = -lvlibmemoryclient -lvlibapi -lsvm -lvppinfra -lpthread -lm -lrt
+test_pneum_LDADD = -lvlibmemoryclient -lvlibapi -lsvm -lvppinfra -lpthread \
+                   -lm -lrt
+
+
index 2637d43..7250cf6 100644 (file)
@@ -150,8 +150,6 @@ pneum_disconnect (void)
   api_main_t *am = &api_main;
   pneum_main_t *pm = &pneum_main;
 
-  fformat (stdout,"disconnecting from vpe \n");
-
   if (pm->rx_thread_jmpbuf_valid) {
     vl_api_rx_thread_exit_t *ep;
     uword junk;
index 5e19e8c..d645be7 100644 (file)
@@ -3,5 +3,3 @@
 # 3. If at all possible, it is good practice to do this. If you cannot, you
 # will need to generate wheels for each Python version that you support.
 universal=0
-
-
index e369a0c..a2eeff3 100644 (file)
@@ -1,16 +1,34 @@
+#
+# 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.
+
 try:
-    from setuptools import setup
+    from setuptools import setup, command, Extension
 except ImportError:
     from distutils.core import setup
 
 setup (name = 'vpp_papi',
-       version = '1.1',
+       version = '1.2',
        description = 'VPP Python binding',
        author = 'Ole Troan',
        author_email = 'ot@cisco.com',
-       #url = 'https://docs.python.org/extending/building',
        test_suite = 'tests',
        packages=['vpp_papi'],
-       long_description = '''
-VPP Python language binding.
-''',)
+       ext_modules = [
+           Extension(
+               'vpp_api',
+               sources = ['vpp_papi/pneum_wrap.c'],
+               libraries = ['pneum'],
+           )],
+       long_description = '''VPP Python language binding.''',
+)
diff --git a/vpp-api/python/tests/test_base.py b/vpp-api/python/tests/test_base.py
deleted file mode 100644 (file)
index 8ff5dd4..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# Manipulate sys.path to allow tests be run inside the build environment.
-import os, sys, glob
-scriptdir = os.path.dirname(os.path.realpath(__file__))
-sys.path.append(os.path.dirname(glob.glob(scriptdir+'/../../../build-root/install*/vpp-api/lib64/vpp_api.so')[0]))
-sys.path.append(os.path.dirname(glob.glob(scriptdir+'/../../../build-root/install*/vlib-api/vlibmemory/memclnt.py')[0]))
-sys.path.append(os.path.dirname(glob.glob(scriptdir+'/../../../build-root/install*/vpp/vpp-api/vpe.py')[0]))
-sys.path.append(glob.glob(scriptdir+'/../../../build-root/install*/plugins/vpp_papi_plugins')[0])
index 48df175..fdcd092 100755 (executable)
@@ -1,7 +1,5 @@
-#!/usr/bin/env python
 from __future__ import print_function
 import unittest
-import test_base
 import vpp_papi
 import pot, snat
 print('Plugins:')
index ab90eea..8cbbfc5 100755 (executable)
@@ -1,13 +1,10 @@
-#!/usr/bin/env python
-
 from __future__ import print_function
-import unittest, sys, time, threading, struct, logging
-import test_base
+import unittest, sys, time, threading, struct, logging, os
 import vpp_papi
 from ipaddress import *
-
+scriptdir = os.path.dirname(os.path.realpath(__file__))
 papi_event = threading.Event()
-print(vpp_papi.VL_API_SW_INTERFACE_SET_FLAGS)
+print(vpp_papi.vpe.VL_API_SW_INTERFACE_SET_FLAGS)
 def papi_event_handler(result):
     if result.vl_msg_id == vpp_papi.vpe.VL_API_SW_INTERFACE_SET_FLAGS:
         return
@@ -27,7 +24,7 @@ class TestPAPI(unittest.TestCase):
     def setUpClass(cls):
         #
         # Start main VPP process
-        cls.vpp_bin = glob.glob(test_base.scriptdir+'/../../../build-root/install-vpp*-native/vpp/bin/vpp')[0]
+        cls.vpp_bin = glob.glob(scriptdir+'/../../../build-root/install-vpp*-native/vpp/bin/vpp')[0]
         print("VPP BIN:", cls.vpp_bin)
         cls.vpp = subprocess.Popen([cls.vpp_bin, "unix", "nodaemon"], stderr=subprocess.PIPE)
         print('Started VPP')
@@ -89,7 +86,7 @@ class TestPAPI(unittest.TestCase):
         self.assertEqual(t.retval, 0)
 
         ifindex = t.sw_if_index
-        addr = str(IPv6Address('1::1').packed)
+        addr = str(IPv6Address(u'1::1').packed)
         t = vpp_papi.sw_interface_add_del_address(ifindex, 1, 1, 0, 16, addr)
         print(t)
         self.assertEqual(t.retval, 0)
diff --git a/vpp-api/python/tests/test_version.py b/vpp-api/python/tests/test_version.py
new file mode 100755 (executable)
index 0000000..de39cc2
--- /dev/null
@@ -0,0 +1,35 @@
+from __future__ import print_function
+import unittest, sys, time, threading, struct
+
+import vpp_papi
+from ipaddress import *
+import glob, subprocess
+class TestPAPI(unittest.TestCase):
+    def setUp(self):
+        print("Connecting API")
+        r = vpp_papi.connect("test_papi")
+        self.assertEqual(r, 0)
+
+    def tearDown(self):
+        r = vpp_papi.disconnect()
+        self.assertEqual(r, 0)
+
+    #
+    # The tests themselves
+    #
+
+    #
+    # Basic request / reply
+    #
+    def test_show_version(self):
+        print(vpp_papi.show_version())
+
+    #
+    # Details / Dump
+    #
+    def test_details_dump(self):
+        t = vpp_papi.sw_interface_dump(0, b'')
+        print('Dump/details T', t)
+
+if __name__ == '__main__':
+    unittest.main()
index 19d78a3..6688ffb 100644 (file)
@@ -1,4 +1,3 @@
 __import__('pkg_resources').declare_namespace(__name__)
 from . vpp_papi import *
 
-
index a1ef87a..227257f 100644 (file)
@@ -58,7 +58,7 @@ def results_event_clear(context):
     results[context]['e'].clear()
 
 def results_event_wait(context, timeout):
-    results[context]['e'].wait(timeout)
+    return (results[context]['e'].wait(timeout))
 
 def results_set(context, r):
     results[context]['r'] = r
index 174e61d..42ddcdc 100644 (file)
@@ -20,15 +20,15 @@ from __future__ import print_function
 import signal, os, sys
 from struct import *
 
-scriptdir = os.path.dirname(os.path.realpath(__file__))
-sys.path.append(scriptdir)
 import vpp_api
 from vpp_api_base import *
 
 # Import API definitions. The core VPE API is imported into main namespace
 import memclnt
+
+# Cheating a bit, importing it into this namespace as well as a module.
+import vpe
 from vpe import *
-vpe = sys.modules['vpe']
 
 def eprint(*args, **kwargs):
     print(*args, file=sys.stderr, **kwargs)
@@ -67,7 +67,7 @@ def msg_handler(msg):
     #
     # Collect results until control ping
     #
-    if id[0] == vpe.VL_API_CONTROL_PING_REPLY:
+    if id[0] == VL_API_CONTROL_PING_REPLY:
         results_event_set(context)
         waiting_for_reply_clear()
         return
@@ -82,7 +82,14 @@ def msg_handler(msg):
     results_event_set(context)
     waiting_for_reply_clear()
 
+def handler(signum, frame):
+    print('Signal handler called with signal', signum)
+    raise IOError("Couldn't connect to VPP!")
+
 def connect(name):
+    # Set the signal handler
+    signal.signal(signal.SIGALRM, handler)
+
     signal.alarm(3) # 3 second
     rv = vpp_api.connect(name, msg_handler)
     signal.alarm(0)
@@ -90,20 +97,16 @@ def connect(name):
     #
     # Assign message id space for plugins
     #
-    plugin_map_plugins()
-
+    try:
+        plugin_map_plugins()
+    except:
+        return -1
     return rv
 
 def disconnect():
     rv = vpp_api.disconnect()
     return rv
 
-# CLI convenience wrapper
-def cli_exec(cmd):
-    cmd += '\n'
-    r = cli_inband(len(cmd), cmd)
-    return r.reply[0].decode().rstrip('\x00')
-
 def register_event_callback(callback):
     event_callback_set(callback)
 
@@ -112,6 +115,7 @@ def plugin_name_to_id(plugin, name_to_id_table, base):
         m = globals()[plugin]
     except KeyError:
         m = sys.modules[plugin]
+
     for name in name_to_id_table:
         setattr(m, name, name_to_id_table[name] + base)
 
@@ -126,11 +130,10 @@ def plugin_map_plugins():
         #
         version = plugins[p]['version']
         name = p + '_' + format(version, '08x')
-        r = memclnt.get_first_msg_id(name.encode('ascii'))
-        ## TODO: Add error handling / raise exception
+        r = memclnt.get_first_msg_id(name)
         if r.retval != 0:
-            eprint('Failed getting first msg id for:', p)
-            continue
+            eprint('Failed getting first msg id for:', p, r, name)
+            raise
 
         # Set base
         base = r.first_msg_id
@@ -153,6 +156,8 @@ def plugin_map_plugins():
 #
 memclnt.msg_id_base_set(1)
 plugins['memclnt']['base'] = 1
+
+# vpe
 msg_id_base_set(len(plugins['memclnt']['func_table']) + 1)
 plugins['vpe']['base'] = len(plugins['memclnt']['func_table']) + 1
 api_func_table = []
@@ -160,3 +165,4 @@ api_func_table.append(None)
 api_func_table[1:] = plugins['memclnt']['func_table'] + plugins['vpe']['func_table']
 plugin_name_to_id('memclnt', plugins['memclnt']['name_to_id_table'], 1)
 plugin_name_to_id('vpe', plugins['vpe']['name_to_id_table'], plugins['vpe']['base'])
+plugin_name_to_id(__name__, plugins['vpe']['name_to_id_table'], plugins['vpe']['base'])