diff --git a/vpp-snmp-agent.py b/vpp-snmp-agent.py index b03c8dd..afad93a 100755 --- a/vpp-snmp-agent.py +++ b/vpp-snmp-agent.py @@ -2,6 +2,7 @@ # -*- coding: utf-8 -*- from vppstats import VPPStats +import vppapi import time import pyagentx import logging @@ -13,7 +14,7 @@ class NullHandler(logging.Handler): pass -logger = logging.getLogger('pyagentx.vpp') +logger = logging.getLogger('pyagentx.vppstats') logger.addHandler(NullHandler()) @@ -47,6 +48,105 @@ class ifType(pyagentx.Updater): self.set_INTEGER(str(i + 1), t) +class ifMtu(pyagentx.Updater): + def update(self): + global vppstat, vpp + vppstat.connect() + + ifaces = vppapi.get_ifaces(vpp) + + for i in range(len(vppstat['/if/names'])): + ifname = vppstat['/if/names'][i] + mtu = 0 + if not ifname in ifaces: + logger.warning("Could not get MTU for interface %s", ifname) + else: + mtu = ifaces[ifname].mtu[0] + self.set_INTEGER(str(i + 1), mtu) + + +class ifSpeed(pyagentx.Updater): + def update(self): + global vppstat, vpp + vppstat.connect() + + ifaces = vppapi.get_ifaces(vpp) + + for i in range(len(vppstat['/if/names'])): + ifname = vppstat['/if/names'][i] + speed = 0 + if ifname.startswith("loop") or ifname.startswith("tap"): + speed = 1000000000 + elif not ifname in ifaces: + logger.warning("Could not get link speed for interface %s", + ifname) + else: + speed = ifaces[ifname].link_speed * 1000 + if speed >= 2**32: + speed = 2**32 - 1 + self.set_GAUGE32(str(i + 1), speed) + + +class ifAdminStatus(pyagentx.Updater): + def update(self): + global vppstat, vpp + vppstat.connect() + + ifaces = vppapi.get_ifaces(vpp) + + for i in range(len(vppstat['/if/names'])): + ifname = vppstat['/if/names'][i] + state = 3 # testing + if not ifname in ifaces: + logger.warning("Could not get AdminStatus for interface %s", + ifname) + else: + if int(ifaces[ifname].flags) & 2: + state = 1 # up + else: + state = 2 # down + self.set_INTEGER(str(i + 1), state) + + +class ifOperStatus(pyagentx.Updater): + def update(self): + global vppstat, vpp + vppstat.connect() + + ifaces = vppapi.get_ifaces(vpp) + + for i in range(len(vppstat['/if/names'])): + ifname = vppstat['/if/names'][i] + state = 3 # testing + if not ifname in ifaces: + logger.warning("Could not get OperStatus for interface %s", + ifname) + else: + if int(ifaces[ifname].flags) & 1: + state = 1 # up + else: + state = 2 # down + self.set_INTEGER(str(i + 1), state) + + +class ifPhysAddress(pyagentx.Updater): + def update(self): + global vppstat, vpp + vppstat.connect() + + ifaces = vppapi.get_ifaces(vpp) + + for i in range(len(vppstat['/if/names'])): + ifname = vppstat['/if/names'][i] + mac = "00:00:00:00:00:00" + if not ifname in ifaces: + logger.warning("Could not get PhysAddress for interface %s", + ifname) + else: + mac = str(ifaces[ifname].l2_address) + self.set_OCTETSTRING(str(i + 1), mac) + + class ifAlias(pyagentx.Updater): def update(self): global vppstat @@ -178,11 +278,22 @@ class ifHCOutBroadcastPkts(pyagentx.Updater): class ifHighSpeed(pyagentx.Updater): def update(self): - global vppstat + global vppstat, vpp vppstat.connect() + ifaces = vppapi.get_ifaces(vpp) + for i in range(len(vppstat['/if/names'])): - self.set_GAUGE32(str(i + 1), 1000) + ifname = vppstat['/if/names'][i] + speed = 0 + if ifname.startswith("loop") or ifname.startswith("tap"): + speed = 1000 + elif not ifname in ifaces: + logger.warning("Could not get link speed for interface %s", + ifname) + else: + speed = int(ifaces[ifname].link_speed / 1000) + self.set_GAUGE32(str(i + 1), speed) class ifPromiscuousMode(pyagentx.Updater): @@ -319,6 +430,11 @@ class MyAgent(pyagentx.Agent): self.register('1.3.6.1.2.1.2.2.1.1', ifIndex) self.register('1.3.6.1.2.1.2.2.1.2', ifName) self.register('1.3.6.1.2.1.2.2.1.3', ifType) + self.register('1.3.6.1.2.1.2.2.1.4', ifMtu) + self.register('1.3.6.1.2.1.2.2.1.5', ifSpeed) + self.register('1.3.6.1.2.1.2.2.1.6', ifPhysAddress) + self.register('1.3.6.1.2.1.2.2.1.7', ifAdminStatus) + self.register('1.3.6.1.2.1.2.2.1.8', ifOperStatus) self.register('1.3.6.1.2.1.2.2.1.9', ifCounterDiscontinuityTime) self.register('1.3.6.1.2.1.2.2.1.10', ifInOctets) self.register('1.3.6.1.2.1.2.2.1.11', ifInUcastPkts) @@ -332,13 +448,6 @@ class MyAgent(pyagentx.Agent): self.register('1.3.6.1.2.1.2.2.1.19', ifOutDiscards) self.register('1.3.6.1.2.1.2.2.1.20', ifOutErrors) - # TODO(pim) -- these require VPP API calls - #4 .iso.org.dod.internet.mgmt.mib_2.interfaces.ifTable.ifEntry.ifMtu.132 = INTEGER: 1500 - #5 .iso.org.dod.internet.mgmt.mib_2.interfaces.ifTable.ifEntry.ifSpeed.132 = Gauge32: 10000000 - #6 .iso.org.dod.internet.mgmt.mib_2.interfaces.ifTable.ifEntry.ifPhysAddress.132 = Hex-STRING: 68 05 CA 32 46 15 - #7 .iso.org.dod.internet.mgmt.mib_2.interfaces.ifTable.ifEntry.ifAdminStatus.132 = INTEGER: 1 - #8 .iso.org.dod.internet.mgmt.mib_2.interfaces.ifTable.ifEntry.ifOperStatus.132 = INTEGER: 1 - # iso.org.dod.internet.mgmt.mib_2.ifMIB.ifMIBObjects.ifXTable.ifXEntry self.register('1.3.6.1.2.1.31.1.1.1.1', ifName) self.register('1.3.6.1.2.1.31.1.1.1.2', ifInMulticastPkts) @@ -364,13 +473,18 @@ class MyAgent(pyagentx.Agent): def main(): - global vppstat + global vppstat, vpp, logger pyagentx.setup_logging(debug=False) vppstat = VPPStats(socketname='/run/vpp/stats.sock', timeout=2) vppstat.connect() + vpp = vppapi.vpp_connect() + if not vpp: + logger.error("Can't connect to VPP API, bailing") + return + try: a = MyAgent() a.start() diff --git a/vppapi.py b/vppapi.py new file mode 100644 index 0000000..f99961b --- /dev/null +++ b/vppapi.py @@ -0,0 +1,85 @@ +''' +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 threading + + +class NullHandler(logging.Handler): + def emit(self, record): + pass + + +logger = logging.getLogger('pyagentx.vppapi') +logger.addHandler(NullHandler()) + +vpp_lock = threading.Lock() + + +def vpp_connect(): + global logger + + 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 + + vpp = VPPApiClient(apifiles=jsonfiles, server_address='/run/vpp/api.sock') + try: + vpp.connect('vpp-snmp-agent') + except: + return False + + v = vpp.api.show_version() + logger.info('VPP version is %s' % v.version) + + return vpp + + +def get_iface(vpp, ifname): + global logger + + 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: + 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() + return ret + + for iface in iface_list: + ret[iface.interface_name] = iface + + vpp_lock.release() + return ret