diff options
Diffstat (limited to 'vpp-api/python/vpp_papi')
-rw-r--r-- | vpp-api/python/vpp_papi/__init__.py | 4 | ||||
-rw-r--r-- | vpp-api/python/vpp_papi/pneum_wrap.c | 29 | ||||
-rw-r--r-- | vpp-api/python/vpp_papi/vpp_api_base.py | 97 | ||||
-rw-r--r-- | vpp-api/python/vpp_papi/vpp_papi.py | 155 |
4 files changed, 277 insertions, 8 deletions
diff --git a/vpp-api/python/vpp_papi/__init__.py b/vpp-api/python/vpp_papi/__init__.py index 8be644d7cb7..19d78a3a05d 100644 --- a/vpp-api/python/vpp_papi/__init__.py +++ b/vpp-api/python/vpp_papi/__init__.py @@ -1,2 +1,4 @@ __import__('pkg_resources').declare_namespace(__name__) -from .vpp_papi import * +from . vpp_papi import * + + diff --git a/vpp-api/python/vpp_papi/pneum_wrap.c b/vpp-api/python/vpp_papi/pneum_wrap.c index d1795aa13a7..7a5119746be 100644 --- a/vpp-api/python/vpp_papi/pneum_wrap.c +++ b/vpp-api/python/vpp_papi/pneum_wrap.c @@ -1,5 +1,20 @@ +/* + * 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. + */ + #include <Python.h> -#include "pneum.h" +#include "../pneum/pneum.h" static PyObject *pneum_callback = NULL; @@ -35,7 +50,7 @@ wrap_connect (PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "sO:set_callback", &name, &temp)) return (NULL); - + if (!PyCallable_Check(temp)) { PyErr_SetString(PyExc_TypeError, "parameter must be callable"); return NULL; @@ -66,8 +81,8 @@ wrap_write (PyObject *self, PyObject *args) char *data; int len, rv; - if (!PyArg_ParseTuple(args, "s#", &data, &len)) - return NULL; + if (!PyArg_ParseTuple(args, "s#", &data, &len)) + return NULL; Py_BEGIN_ALLOW_THREADS rv = pneum_write(data, len); Py_END_ALLOW_THREADS @@ -117,16 +132,16 @@ initvpp_api (void) { #if PY_VERSION_HEX >= 0x03000000 static struct PyModuleDef vpp_api_module = { -# if PY_VERSION_HEX >= 0x03020000 +#if PY_VERSION_HEX >= 0x03020000 PyModuleDef_HEAD_INIT, -# else +#else { PyObject_HEAD_INIT(NULL) NULL, /* m_init */ 0, /* m_index */ NULL, /* m_copy */ }, -# endif +#endif (char *) "vpp_api", NULL, -1, diff --git a/vpp-api/python/vpp_papi/vpp_api_base.py b/vpp-api/python/vpp_papi/vpp_api_base.py new file mode 100644 index 00000000000..a1ef87a3f0a --- /dev/null +++ b/vpp-api/python/vpp_papi/vpp_api_base.py @@ -0,0 +1,97 @@ +# +# 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. + +# +# Module storing all global variables, shared between main module and plugins +# +import threading + +# +# Global variables +# +results = {} +waiting_for_reply = False +plugins = {} + +class ContextId(object): + def __init__(self): + self.context = 0 + def __call__(self, id): + self.context += 1 + return self.context +get_context = ContextId() + +def waiting_for_reply_clear(): + global waiting_for_reply + waiting_for_reply = False + +def waiting_for_reply_set(): + global waiting_for_reply + waiting_for_reply = True + +def is_waiting_for_reply(): + return waiting_for_reply + +def event_callback_set(callback): + global event_callback + event_callback = callback + +def event_callback_call(r): + global event_callback + event_callback(r) + +def results_event_set(context): + results[context]['e'].set() + +def results_event_clear(context): + results[context]['e'].clear() + +def results_event_wait(context, timeout): + results[context]['e'].wait(timeout) + +def results_set(context, r): + results[context]['r'] = r + +def results_append(context, r): + results[context]['r'].append(r) + +def is_results_context(context): + return context in results + +def is_results_more(context): + return 'm' in results[context] + +def results_more_set(context): + results[context]['m'] = True + +def results_prepare(context): + results[context] = {} + results[context]['e'] = threading.Event() + results[context]['e'].clear() + results[context]['r'] = [] + +def results_get(context): + return results[context]['r'] + +def plugin_register(name, func_table, name_to_id_table, version, msg_id_base_set): + plugins[name] = {} + p = plugins[name] + p['func_table'] = func_table + p['name_to_id_table'] = name_to_id_table + p['version'] = version + p['msg_id_base_set'] = msg_id_base_set + +def plugin_show(): + for p in plugins: + print(p) diff --git a/vpp-api/python/vpp_papi/vpp_papi.py b/vpp-api/python/vpp_papi/vpp_papi.py new file mode 100644 index 00000000000..6a7a358f6cd --- /dev/null +++ b/vpp-api/python/vpp_papi/vpp_papi.py @@ -0,0 +1,155 @@ +# +# 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 C API shared object +# +from __future__ import print_function + +import signal, logging, 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 +from vpe import * +vpe = sys.modules['vpe'] + +def msg_handler(msg): + if not msg: + logging.warning('vpp_api.read failed') + return + + id = unpack('>H', msg[0:2]) + logging.debug('Received message', id[0]) + if id[0] == memclnt.VL_API_RX_THREAD_EXIT: + logging.info("We got told to leave") + return; + + # + # Decode message and returns a tuple. + # + logging.debug('api_func', api_func_table[id[0]]) + r = api_func_table[id[0]](msg) + if not r: + logging.warning('Message decode failed', id[0]) + return + + if 'context' in r._asdict(): + if r.context > 0: + context = r.context + + # + # XXX: Call provided callback for event + # Are we guaranteed to not get an event during processing of other messages? + # How to differentiate what's a callback message and what not? Context = 0? + # + if not is_waiting_for_reply(): + event_callback_call(r) + return + + # + # Collect results until control ping + # + if id[0] == vpe.VL_API_CONTROL_PING_REPLY: + results_event_set(context) + waiting_for_reply_clear() + return + if not is_results_context(context): + logging.warning('Not expecting results for this context', context) + return + if is_results_more(context): + results_append(context, r) + return + + results_set(context, r) + results_event_set(context) + waiting_for_reply_clear() + +def connect(name): + signal.alarm(3) # 3 second + rv = vpp_api.connect(name, msg_handler) + signal.alarm(0) + logging.info("Connect:", rv) + + # + # Assign message id space for plugins + # + plugin_map_plugins() + + return rv + +def disconnect(): + rv = vpp_api.disconnect() + logging.info("Disconnected") + return rv + +def register_event_callback(callback): + event_callback_set(callback) + +def plugin_name_to_id(plugin, name_to_id_table, base): + try: + 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) + +def plugin_map_plugins(): + for p in plugins: + if p == 'memclnt' or p == 'vpe': + continue + + # + # Find base + # Update api table + # + version = plugins[p]['version'] + name = p + '_' + format(version, '08x') + r = memclnt.get_first_msg_id(name.encode('ascii')) + + ## TODO: Add error handling + if r.retval != 0: + print('Failed getting first msg id for:', p) + continue + + # Set base + base = r.first_msg_id + msg_id_base_set = plugins[p]['msg_id_base_set'] + msg_id_base_set(base) + plugins[p]['base'] = base + func_table = plugins[p]['func_table'] + i = r.first_msg_id + for entry in func_table: + api_func_table.insert(i, entry) + i += 1 + plugin_name_to_id(p, plugins[p]['name_to_id_table'], base) + +# +# Set up core API +# +memclnt.msg_id_base_set(1) +plugins['memclnt']['base'] = 1 +msg_id_base_set(len(plugins['memclnt']['func_table']) + 1) +plugins['vpe']['base'] = len(plugins['memclnt']['func_table']) + 1 +api_func_table = [] +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']) +#logging.basicConfig(level=logging.DEBUG) |