from __future__ import print_function
from __future__ import absolute_import
+import ctypes
import sys
+import multiprocessing as mp
import os
import logging
-import collections
-import struct
import functools
import json
import threading
import fnmatch
import weakref
import atexit
-from . vpp_serializer import VPPType, VPPEnumType, VPPUnionType, BaseTypes
+from . vpp_serializer import VPPType, VPPEnumType, VPPUnionType
from . vpp_serializer import VPPMessage, vpp_get_type, VPPTypeAlias
-from . macaddress import MACAddress, mac_pton, mac_ntop
logger = logging.getLogger(__name__)
else:
import queue as queue
+__all__ = ('FuncWrapper', 'VPP', 'VppApiDynamicMethodHolder',
+ 'VppEnum', 'VppEnumType',
+ 'VPPIOError', 'VPPRuntimeError', 'VPPValueError',
+ 'VPPApiClient', )
+
def metaclass(metaclass):
@functools.wraps(metaclass)
def __init__(self, func):
self._func = func
self.__name__ = func.__name__
+ self.__doc__ = func.__doc__
def __call__(self, **kwargs):
return self._func(**kwargs)
+ def __repr__(self):
+ return '<FuncWrapper(func=<%s(%s)>)>' % (self.__name__, self.__doc__)
+
class VPPApiError(Exception):
pass
pass
-class VPP(object):
+class VPPApiClient(object):
"""VPP interface.
This class provides the APIs to VPP. The APIs are loaded
provides a means to register a callback function to receive
these messages in a background thread.
"""
+ apidir = None
VPPApiError = VPPApiError
VPPRuntimeError = VPPRuntimeError
VPPValueError = VPPValueError
self.message_queue = queue.Queue()
self.read_timeout = read_timeout
self.async_thread = async_thread
+ self.event_thread = None
+ self.testmode = testmode
+ self.use_socket = use_socket
+ self.server_address = server_address
+ self._apifiles = apifiles
if use_socket:
from . vpp_transport_socket import VppTransport
atexit.register(vpp_atexit, weakref.ref(self))
class ContextId(object):
- """Thread-safe provider of unique context IDs."""
+ """Multiprocessing-safe provider of unique context IDs."""
def __init__(self):
- self.context = 0
- self.lock = threading.Lock()
+ self.context = mp.Value(ctypes.c_uint, 0)
+ self.lock = mp.Lock()
def __call__(self):
"""Get a new unique (or, at least, not recently used) context."""
with self.lock:
- self.context += 1
- return self.context
+ self.context.value += 1
+ return self.context.value
get_context = ContextId()
def get_type(self, name):
:returns: A single directory name, or None if no such directory
could be found.
"""
- dirs = []
-
- if 'VPP_API_DIR' in os.environ:
- dirs.append(os.environ['VPP_API_DIR'])
+ dirs = [cls.apidir] if cls.apidir else []
# perhaps we're in the 'src/scripts' or 'src/vpp-api/python' dir;
# in which case, plot a course to likely places in the src tree
# finally, try the location system packages typically install into
dirs.append(os.path.sep.join(('', 'usr', 'share', 'vpp', 'api')))
- # check the directories for existance; first one wins
+ # check the directories for existence; first one wins
for dir in dirs:
if os.path.isdir(dir):
return dir
f.__doc__ = ", ".join(["%s %s" %
(msg.fieldtypes[j], k)
for j, k in enumerate(msg.fields)])
+ f.msg = msg
+
return f
def _register_functions(self, do_async=False):
self._api = VppApiDynamicMethodHolder()
for name, msg in vpp_iterator(self.messages):
n = name + '_' + msg.crc[2:]
- i = self.transport.get_msg_index(n.encode())
+ i = self.transport.get_msg_index(n.encode('utf-8'))
if i > 0:
self.id_msgdef[i] = msg
self.id_names[i] = name
def connect_internal(self, name, msg_handler, chroot_prefix, rx_qlen,
do_async):
- pfx = chroot_prefix.encode() if chroot_prefix else None
+ pfx = chroot_prefix.encode('utf-8') if chroot_prefix else None
- rv = self.transport.connect(name.encode(), pfx, msg_handler, rx_qlen)
+ rv = self.transport.connect(name.encode('utf-8'), pfx,
+ msg_handler, rx_qlen)
if rv != 0:
raise VPPIOError(2, 'Connect failed')
self.vpp_dictionary_maxid = self.transport.msg_table_max_index()
# Initialise control ping
crc = self.messages['control_ping'].crc
self.control_ping_index = self.transport.get_msg_index(
- ('control_ping' + '_' + crc[2:]).encode())
+ ('control_ping' + '_' + crc[2:]).encode('utf-8'))
self.control_ping_msgdef = self.messages['control_ping']
if self.async_thread:
self.event_thread = threading.Thread(
target=self.thread_msg_handler)
self.event_thread.daemon = True
self.event_thread.start()
+ else:
+ self.event_thread = None
return rv
def connect(self, name, chroot_prefix=None, do_async=False, rx_qlen=32):
def disconnect(self):
"""Detach from VPP."""
rv = self.transport.disconnect()
- self.message_queue.put("terminate event thread")
+ if self.event_thread is not None:
+ self.message_queue.put("terminate event thread")
return rv
def msg_handler_sync(self, msg):
if self.event_callback:
self.event_callback(msgname, r)
+ def __repr__(self):
+ return "<VPPApiClient apifiles=%s, testmode=%s, async_thread=%s, " \
+ "logger=%s, read_timeout=%s, use_socket=%s, " \
+ "server_address='%s'>" % (
+ self._apifiles, self.testmode, self.async_thread,
+ self.logger, self.read_timeout, self.use_socket,
+ self.server_address)
+
+
+# Provide the old name for backward compatibility.
+VPP = VPPApiClient
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4