From 57c3d66c55580f2ced6993ada22274941237fcd5 Mon Sep 17 00:00:00 2001 From: Ole Troan Date: Mon, 12 Sep 2016 22:00:32 +0200 Subject: [PATCH] Python API: Preparation for RPM/DEB packaging. Recheck. Repackage the Python API binding to include all necessary modules in a single Python package. Change-Id: I5e35141d413bfb1aad650217e1ca07d85646c349 Signed-off-by: Ole Troan --- build-root/rpm/vpp.spec | 32 +++++++++++++++++++++++++++ vpp-api/python/Makefile.am | 39 ++++++++++++++++----------------- vpp-api/python/pneum/pneum.c | 2 -- vpp-api/python/setup.cfg | 2 -- vpp-api/python/setup.py | 30 ++++++++++++++++++++----- vpp-api/python/tests/test_base.py | 7 ------ vpp-api/python/tests/test_modules.py | 2 -- vpp-api/python/tests/test_papi.py | 13 +++++------ vpp-api/python/tests/test_version.py | 35 +++++++++++++++++++++++++++++ vpp-api/python/vpp_papi/__init__.py | 1 - vpp-api/python/vpp_papi/vpp_api_base.py | 2 +- vpp-api/python/vpp_papi/vpp_papi.py | 38 ++++++++++++++++++-------------- 12 files changed, 138 insertions(+), 65 deletions(-) delete mode 100644 vpp-api/python/tests/test_base.py create mode 100755 vpp-api/python/tests/test_version.py diff --git a/build-root/rpm/vpp.spec b/build-root/rpm/vpp.spec index 96e6f0fd66a..35964b6df63 100644 --- a/build-root/rpm/vpp.spec +++ b/build-root/rpm/vpp.spec @@ -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 diff --git a/vpp-api/python/Makefile.am b/vpp-api/python/Makefile.am index eb589335b05..59b1b9220bd 100644 --- a/vpp-api/python/Makefile.am +++ b/vpp-api/python/Makefile.am @@ -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 + + diff --git a/vpp-api/python/pneum/pneum.c b/vpp-api/python/pneum/pneum.c index 2637d43f5d9..7250cf6a90e 100644 --- a/vpp-api/python/pneum/pneum.c +++ b/vpp-api/python/pneum/pneum.c @@ -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; diff --git a/vpp-api/python/setup.cfg b/vpp-api/python/setup.cfg index 5e19e8c5e74..d645be77604 100644 --- a/vpp-api/python/setup.cfg +++ b/vpp-api/python/setup.cfg @@ -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 - - diff --git a/vpp-api/python/setup.py b/vpp-api/python/setup.py index e369a0cb479..a2eeff3f436 100644 --- a/vpp-api/python/setup.py +++ b/vpp-api/python/setup.py @@ -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 index 8ff5dd4782c..00000000000 --- a/vpp-api/python/tests/test_base.py +++ /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]) diff --git a/vpp-api/python/tests/test_modules.py b/vpp-api/python/tests/test_modules.py index 48df17559a0..fdcd092c927 100755 --- a/vpp-api/python/tests/test_modules.py +++ b/vpp-api/python/tests/test_modules.py @@ -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:') diff --git a/vpp-api/python/tests/test_papi.py b/vpp-api/python/tests/test_papi.py index ab90eeaa45c..8cbbfc59e03 100755 --- a/vpp-api/python/tests/test_papi.py +++ b/vpp-api/python/tests/test_papi.py @@ -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 index 00000000000..de39cc24ebd --- /dev/null +++ b/vpp-api/python/tests/test_version.py @@ -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() diff --git a/vpp-api/python/vpp_papi/__init__.py b/vpp-api/python/vpp_papi/__init__.py index 19d78a3a05d..6688ffb8ced 100644 --- a/vpp-api/python/vpp_papi/__init__.py +++ b/vpp-api/python/vpp_papi/__init__.py @@ -1,4 +1,3 @@ __import__('pkg_resources').declare_namespace(__name__) from . vpp_papi import * - diff --git a/vpp-api/python/vpp_papi/vpp_api_base.py b/vpp-api/python/vpp_papi/vpp_api_base.py index a1ef87a3f0a..227257fcd9b 100644 --- a/vpp-api/python/vpp_papi/vpp_api_base.py +++ b/vpp-api/python/vpp_papi/vpp_api_base.py @@ -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 diff --git a/vpp-api/python/vpp_papi/vpp_papi.py b/vpp-api/python/vpp_papi/vpp_papi.py index 174e61d7e9e..42ddcdc341e 100644 --- a/vpp-api/python/vpp_papi/vpp_papi.py +++ b/vpp-api/python/vpp_papi/vpp_papi.py @@ -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']) -- 2.16.6