-#!/usr/bin/env python
+#!/usr/bin/env python3
#
# Copyright (c) 2016 Cisco and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
import fnmatch
import weakref
import atexit
+import time
from . vpp_serializer import VPPType, VPPEnumType, VPPUnionType
from . vpp_serializer import VPPMessage, vpp_get_type, VPPTypeAlias
self.use_socket = use_socket
self.server_address = server_address
self._apifiles = apifiles
+ self.stats = {}
if use_socket:
from . vpp_transport_socket import VppTransport
raise VPPValueError('Invalid argument {} to {}'
.format(list(d), msg.name))
+ def _add_stat(self, name, ms):
+ if not name in self.stats:
+ self.stats[name] = {'max': ms, 'count': 1, 'avg': ms}
+ else:
+ if ms > self.stats[name]['max']:
+ self.stats[name]['max'] = ms
+ self.stats[name]['count'] += 1
+ n = self.stats[name]['count']
+ self.stats[name]['avg'] = self.stats[name]['avg'] * (n - 1) / n + ms / n
+
+ def get_stats(self):
+ s = '\n=== API PAPI STATISTICS ===\n'
+ s += '{:<30} {:>4} {:>6} {:>6}\n'.format('message', 'cnt', 'avg', 'max')
+ for n in sorted(self.stats.items(), key=lambda v: v[1]['avg'], reverse=True):
+ s += '{:<30} {:>4} {:>6.2f} {:>6.2f}\n'.format(n[0], n[1]['count'],
+ n[1]['avg'], n[1]['max'])
+ return s
+
def _call_vpp(self, i, msgdef, multipart, **kwargs):
"""Given a message, send the message and await a reply.
the response. It will raise an IOError exception if there was
no response within the timeout window.
"""
-
+ ts = time.time()
if 'context' not in kwargs:
context = self.get_context()
kwargs['context'] = context
kwargs['_vl_msg_id'] = i
no_type_conversion = kwargs.pop('_no_type_conversion', False)
+ timeout = kwargs.pop('_timeout', None)
try:
if self.transport.socket_index:
# Block until we get a reply.
rl = []
while (True):
- r = self.read_blocking(no_type_conversion)
+ r = self.read_blocking(no_type_conversion, timeout)
if r is None:
raise VPPIOError(2, 'VPP API client: read failed')
msgname = type(r).__name__
if len(s) > 80:
s = s[:80] + "..."
self.logger.debug(s)
+ te = time.time()
+ self._add_stat(msgdef.name, (te - ts) * 1000)
return rl
def _call_vpp_async(self, i, msg, **kwargs):
self.transport.write(b)
return context
- def read_blocking(self, no_type_conversion=False):
+ def read_blocking(self, no_type_conversion=False, timeout=None):
"""Get next received message from transport within timeout, decoded.
- Note that noticifations have context zero
+ Note that notifications have context zero
and are not put into receive queue (at least for socket transport),
use async_thread with registered callback for processing them.
:type no_type_conversion: bool
:returns: Decoded message, or None if no message (within timeout).
:rtype: Whatever VPPType.unpack returns, depends on no_type_conversion.
+ :raises VppTransportShmemIOError if timed out.
"""
- msg = self.transport.read()
+ msg = self.transport.read(timeout=timeout)
if not msg:
return None
return self.decode_incoming_msg(msg, no_type_conversion)
if self.event_callback:
self.event_callback(msgname, r)
+ def validate_message_table(self, namecrctable):
+ """Take a dictionary of name_crc message names
+ and returns an array of missing messages"""
+
+ missing_table = []
+ for name_crc in namecrctable:
+ i = self.transport.get_msg_index(name_crc)
+ if i <= 0:
+ missing_table.append(name_crc)
+ return missing_table
+
+ def dump_message_table(self):
+ """Return VPPs API message table as name_crc dictionary"""
+ return self.transport.message_table
+
+ def dump_message_table_filtered(self, msglist):
+ """Return VPPs API message table as name_crc dictionary,
+ filtered by message name list."""
+
+ replies = [self.services[n]['reply'] for n in msglist]
+ message_table_filtered = {}
+ for name in msglist + replies:
+ for k,v in self.transport.message_table.items():
+ if k.startswith(name):
+ message_table_filtered[k] = v
+ break
+ return message_table_filtered
+
def __repr__(self):
return "<VPPApiClient apifiles=%s, testmode=%s, async_thread=%s, " \
"logger=%s, read_timeout=%s, use_socket=%s, " \