''' The functions in this file interact with the VPP API to retrieve certain interface metadata. ''' from vpp_papi import VPPApiClient import os import fnmatch import logging import socket class NullHandler(logging.Handler): def emit(self, record): pass logger = logging.getLogger('agentx.vppapi') logger.addHandler(NullHandler()) class VPPApi(): def __init__(self, address='/run/vpp/api.sock', clientname='vppapi-client'): self.address = address self.connected = False self.clientname = clientname self.vpp = None def connect(self): if self.connected: return True vpp_json_dir = '/usr/share/vpp/api/' # construct a list of all the json api files jsonfiles = [] for root, dirnames, filenames in os.walk(vpp_json_dir): for filename in fnmatch.filter(filenames, '*.api.json'): jsonfiles.append(os.path.join(root, filename)) if not jsonfiles: logger.error('no json api files found') return False self.vpp = VPPApiClient(apifiles=jsonfiles, server_address=self.address) try: logger.info('Connecting to VPP') self.vpp.connect(self.clientname) except: return False v = self.vpp.api.show_version() logger.info('VPP version is %s' % v.version) self.connected = True return True def disconnect(self): if not self.connected: return True self.vpp.disconnect() self.connected = False return True def get_ifaces(self): ret = {} if not self.connected: return ret try: iface_list = self.vpp.api.sw_interface_dump() except Exception as e: logger.error("VPP communication error, disconnecting", e) self.vpp.disconnect() self.connected = False return ret if not iface_list: logger.error("Can't get interface list") return ret for iface in iface_list: ret[iface.interface_name] = iface return ret def get_lcp(self): ret = {} if not self.connected: return ret try: lcp_list = self.vpp.api.lcp_itf_pair_get() except Exception as e: logger.error("VPP communication error, disconnecting", e) self.vpp.disconnect() self.connected = False return ret if not lcp_list or not lcp_list[1]: logger.error("Can't get LCP list") return ret ## TODO(pim) - fix upstream, the indexes are in network byte order and ## the message is messed up. This hack allows for both little endian and ## big endian responses, and will be removed once VPP's LinuxCP is updated ## and rolled out to AS8298 for lcp in lcp_list[1]: if lcp.phy_sw_if_index > 65535 or lcp.host_sw_if_index > 65535 or lcp.vif_index > 65535: i = { 'phy_sw_if_index': socket.ntohl(lcp.phy_sw_if_index), 'host_sw_if_index': socket.ntohl(lcp.host_sw_if_index), 'vif_index': socket.ntohl(lcp.vif_index), 'host_if_name': lcp.host_if_name, 'namespace': lcp.namespace } else: i = { 'phy_sw_if_index': lcp.phy_sw_if_index, 'host_sw_if_index': lcp.host_sw_if_index, 'vif_index': lcp.vif_index, 'host_if_name': lcp.host_if_name, 'namespace': lcp.namespace } ret[lcp.host_if_name] = i return ret