From 7dec1329d265c0d664651630ae67aa49b0954c12 Mon Sep 17 00:00:00 2001 From: Pim van Pelt Date: Sun, 5 Sep 2021 20:02:11 +0000 Subject: [PATCH] Turn VPPApi into a threadsafe object It now is tolerant to VPP restarts. Upon initialization, we connect(), blocking all but the first thread from trying. The rest will see self.connected=True and move on. Then, on each/any error, call vpp.disconect() and set connected=False which will make any subsequent AgentX updater run force a reconnect. --- vpp-snmp-agent.py | 25 ++++++---- vppapi.py | 116 +++++++++++++++++++++++++--------------------- 2 files changed, 78 insertions(+), 63 deletions(-) diff --git a/vpp-snmp-agent.py b/vpp-snmp-agent.py index afad93a..07626e9 100755 --- a/vpp-snmp-agent.py +++ b/vpp-snmp-agent.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- from vppstats import VPPStats -import vppapi +from vppapi import VPPApi import time import pyagentx import logging @@ -52,8 +52,9 @@ class ifMtu(pyagentx.Updater): def update(self): global vppstat, vpp vppstat.connect() + vpp.connect() - ifaces = vppapi.get_ifaces(vpp) + ifaces = vpp.get_ifaces() for i in range(len(vppstat['/if/names'])): ifname = vppstat['/if/names'][i] @@ -69,8 +70,9 @@ class ifSpeed(pyagentx.Updater): def update(self): global vppstat, vpp vppstat.connect() + vpp.connect() - ifaces = vppapi.get_ifaces(vpp) + ifaces = vpp.get_ifaces() for i in range(len(vppstat['/if/names'])): ifname = vppstat['/if/names'][i] @@ -91,8 +93,9 @@ class ifAdminStatus(pyagentx.Updater): def update(self): global vppstat, vpp vppstat.connect() + vpp.connect() - ifaces = vppapi.get_ifaces(vpp) + ifaces = vpp.get_ifaces() for i in range(len(vppstat['/if/names'])): ifname = vppstat['/if/names'][i] @@ -112,8 +115,9 @@ class ifOperStatus(pyagentx.Updater): def update(self): global vppstat, vpp vppstat.connect() + vpp.connect() - ifaces = vppapi.get_ifaces(vpp) + ifaces = vpp.get_ifaces() for i in range(len(vppstat['/if/names'])): ifname = vppstat['/if/names'][i] @@ -133,8 +137,9 @@ class ifPhysAddress(pyagentx.Updater): def update(self): global vppstat, vpp vppstat.connect() + vpp.connect() - ifaces = vppapi.get_ifaces(vpp) + ifaces = vpp.get_ifaces() for i in range(len(vppstat['/if/names'])): ifname = vppstat['/if/names'][i] @@ -280,8 +285,9 @@ class ifHighSpeed(pyagentx.Updater): def update(self): global vppstat, vpp vppstat.connect() + vpp.connect() - ifaces = vppapi.get_ifaces(vpp) + ifaces = vpp.get_ifaces() for i in range(len(vppstat['/if/names'])): ifname = vppstat['/if/names'][i] @@ -480,8 +486,8 @@ def main(): vppstat = VPPStats(socketname='/run/vpp/stats.sock', timeout=2) vppstat.connect() - vpp = vppapi.vpp_connect() - if not vpp: + vpp = VPPApi() + if not vpp.connect(): logger.error("Can't connect to VPP API, bailing") return @@ -495,6 +501,7 @@ def main(): a.stop() vppstat.disconnect() + vpp.disconnect() if __name__ == "__main__": diff --git a/vppapi.py b/vppapi.py index f99961b..7c5d5fd 100644 --- a/vppapi.py +++ b/vppapi.py @@ -15,71 +15,79 @@ class NullHandler(logging.Handler): pass -logger = logging.getLogger('pyagentx.vppapi') -logger.addHandler(NullHandler()) +class VPPApi(): + def __init__(self, address='/run/vpp/api.sock'): + self.address = address + self.lock = threading.Lock() + self.connected = False + self.logger = logging.getLogger('pyagentx.vppapi') + self.logger.addHandler(NullHandler()) + self.vpp = None -vpp_lock = threading.Lock() + def connect(self): + self.lock.acquire() + if self.connected: + self.lock.release() + return True + vpp_json_dir = '/usr/share/vpp/api/' -def vpp_connect(): - global logger + # 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)) - vpp_json_dir = '/usr/share/vpp/api/' + if not jsonfiles: + self.logger.error('no json api files found') + self.lock.release() + return False - # 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)) + self.vpp = VPPApiClient(apifiles=jsonfiles, + server_address=self.address) + try: + self.logger.info('Connecting to VPP') + self.vpp.connect('vpp-snmp-agent') + except: + self.lock.release() + return False - if not jsonfiles: - logger.error('no json api files found') - return False + v = self.vpp.api.show_version() + self.logger.info('VPP version is %s' % v.version) - vpp = VPPApiClient(apifiles=jsonfiles, server_address='/run/vpp/api.sock') - try: - vpp.connect('vpp-snmp-agent') - except: - return False + self.connected = True + self.lock.release() + return True - v = vpp.api.show_version() - logger.info('VPP version is %s' % v.version) + def disconnect(self): + if not self.connected: + return True + self.vpp.disconnect() + self.connected = False + return True - return vpp + def get_ifaces(self): + ret = {} + if not self.connected: + return ret + self.lock.acquire() + try: + iface_list = self.vpp.api.sw_interface_dump() + except Exception as e: + self.logger.error("VPP communication error, disconnecting", e) + self.vpp.disconnect() + self.connected = False + self.lock.release() + return ret -def get_iface(vpp, ifname): - global logger + if not iface_list: + self.logger.error("Can't get interface list") + self.lock.release() + return ret - vpp_lock.acquire() - iface_list = vpp.api.sw_interface_dump(name_filter=ifname, - name_filter_valid=True) - if not iface_list: - logger.error("Can't get interface %s" % ifname) - vpp_lock.release() - return None + for iface in iface_list: + ret[iface.interface_name] = iface - for iface in iface_list: - if iface.interface_name == ifname: - vpp_lock.release() - return iface - vpp_lock.release() - return None - - -def get_ifaces(vpp): - global logger - - vpp_lock.acquire() - ret = {} - iface_list = vpp.api.sw_interface_dump() - if not iface_list: - logger.error("Can't get interface list") - vpp_lock.release() + self.lock.release() return ret - - for iface in iface_list: - ret[iface.interface_name] = iface - - vpp_lock.release() - return ret