From 8c9c1e2b4aa1d40a981f17581f92bba133dd2c29 Mon Sep 17 00:00:00 2001 From: Pim van Pelt Date: Sat, 11 Sep 2021 12:19:38 +0000 Subject: [PATCH] Replace the pyagentx threaded version with a much simpler, non-threaded version. --- {pyagentx => agentx}/__init__.py | 13 +- agentx/agent.py | 89 +++++ agentx/dataset.py | 57 ++++ agentx/network.py | 220 +++++++++++++ {pyagentx => agentx}/pdu.py | 68 ++-- pyagentx/.gitignore | 1 - pyagentx/agent.py | 97 ------ pyagentx/network.py | 272 --------------- pyagentx/sethandler.py | 67 ---- pyagentx/updater.py | 137 -------- vpp-snmp-agent.py | 546 ++++++------------------------- vppapi.py | 18 +- vppstats.py | 11 +- 13 files changed, 507 insertions(+), 1089 deletions(-) rename {pyagentx => agentx}/__init__.py (91%) create mode 100644 agentx/agent.py create mode 100644 agentx/dataset.py create mode 100644 agentx/network.py rename {pyagentx => agentx}/pdu.py (82%) delete mode 100644 pyagentx/.gitignore delete mode 100644 pyagentx/agent.py delete mode 100644 pyagentx/network.py delete mode 100644 pyagentx/sethandler.py delete mode 100644 pyagentx/updater.py diff --git a/pyagentx/__init__.py b/agentx/__init__.py similarity index 91% rename from pyagentx/__init__.py rename to agentx/__init__.py index 13d6c02..f4f6762 100644 --- a/pyagentx/__init__.py +++ b/agentx/__init__.py @@ -8,27 +8,22 @@ from __future__ import ( import logging -from pyagentx.updater import Updater -from pyagentx.agent import Agent -from pyagentx.sethandler import SetHandler, SetHandlerError - +from agentx.agent import Agent +from agentx.dataset import DataSet def setup_logging(debug=False): if debug: level = logging.DEBUG else: level = logging.INFO - logger = logging.getLogger('pyagentx') + logger = logging.getLogger('agentx') logger.setLevel(level) - # formatter = logging.Formatter('%(asctime)s - %(name)20s - %(levelname)s - %(message)s') - formatter = logging.Formatter( - '[%(levelname)-8s] %(name)17s - %(funcName)-15s: %(message)s') + formatter = logging.Formatter('[%(levelname)-8s] %(name)17s - %(funcName)-15s: %(message)s') ch = logging.StreamHandler() ch.setLevel(level) ch.setFormatter(formatter) logger.addHandler(ch) - AGENTX_EMPTY_PDU = 1 AGENTX_OPEN_PDU = 1 AGENTX_CLOSE_PDU = 2 diff --git a/agentx/agent.py b/agentx/agent.py new file mode 100644 index 0000000..12f181d --- /dev/null +++ b/agentx/agent.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +from __future__ import ( + absolute_import, + division, + print_function, +) + +# -------------------------------------------- +import logging + + +class NullHandler(logging.Handler): + def emit(self, record): + pass +# -------------------------------------------- + +import time +import agentx +from agentx.dataset import DataSet +from agentx.network import Network + +class AgentError(Exception): + pass + + +class Agent(object): + def __init__(self, server_address='/var/agentx/master', freq=5): + self.logger = logging.getLogger('agentx.agent') + self.logger.addHandler(NullHandler()) + + self._servingset = DataSet() + self._workingset = DataSet() + self._lastupdate = 0 + self._update_freq = freq + + self._net = Network(server_address = server_address) + + self._oid_list = [] + + def _update(self): + ds = self.update() + self._net.update(ds._data) + self._lastupdate = time.time() + + def run(self): + self.logger.info('Calling setup') + self.setup() + + self.logger.info('Initial update') + self._update() + + while True: + if not self._net.is_connected(): + self.logger.info('Opening AgentX connection') + self._net.start(self._oid_list) + + if time.time() - self._lastupdate > self._update_freq: + self._update() + + try: + self._net.run() + except Exception as e: + self.logger.error('An exception occurred: %s' % e) + time.sleep(1) + + def stop(self): + self.logger.debug('Stopping') + self._net.disconnect() + pass + + + def setup(self): + # Override this + pass + + + def update(self): + # Override this + pass + + def register(self, oid_list): + if not isinstance(oid_list, list): + oid_list = [oid_list] + + for oid in oid_list: + if not oid in self._oid_list: + self.logger.debug('Adding %s to list' % oid) + self._oid_list.append(oid) diff --git a/agentx/dataset.py b/agentx/dataset.py new file mode 100644 index 0000000..750147e --- /dev/null +++ b/agentx/dataset.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +from __future__ import ( + absolute_import, + division, + print_function, +) + +# -------------------------------------------- +import logging + + +class NullHandler(logging.Handler): + def emit(self, record): + pass + + +logger = logging.getLogger('agentx.dataset') +logger.addHandler(NullHandler()) +# -------------------------------------------- + +import time +import agentx + + +class DataSet(): + def __init__(self): + self._data = {} + + def set(self, oid, oid_type, value): + if oid_type.startswith('int'): + t = agentx.TYPE_INTEGER + elif oid_type.startswith('str'): + t = agentx.TYPE_OCTETSTRING + elif oid_type.startswith('oid'): + t = agentx.TYPE_OBJECTIDENTIFIER + elif oid_type.startswith('ip'): + t = agentx.TYPE_IPADDRESS + elif oid_type == 'counter32' or oid_type == 'uint32' or oid_type == 'u32': + t = agentx.TYPE_COUNTER32 + elif oid_type == 'gauge32': + t = agentx.TYPE_GAUGE32 + elif oid_type.startswith('time') or oid_type.startswith('tick'): + t = agentx.TYPE_TIMETICKS + elif oid_type.startswith('opaque'): + t = agentx.TYPE_OPAQUE + elif oid_type == 'counter64' or oid_type == 'uint64' or oid_type == 'u64': + t = agentx.TYPE_COUNTER64 + else: + logger.error('Invalid oid_type: %s' % (oid_type)) + return + + self._data[oid] = { + 'name': oid, + 'type': t, + 'value': value + } diff --git a/agentx/network.py b/agentx/network.py new file mode 100644 index 0000000..42e1ab0 --- /dev/null +++ b/agentx/network.py @@ -0,0 +1,220 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +from __future__ import ( + absolute_import, + division, + print_function, +) + +# -------------------------------------------- +import logging + + +class NullHandler(logging.Handler): + def emit(self, record): + pass + + +logger = logging.getLogger('agentx.network') +logger.addHandler(NullHandler()) + +class NetworkError(Exception): + pass + +# -------------------------------------------- + +import socket +import time +import agentx +from agentx.pdu import PDU + + +class Network(): + def __init__(self, server_address = '/var/agentx/master'): + + self.session_id = 0 + self.transaction_id = 0 + self.debug = 1 + # Data Related Variables + self.data = {} + self.data_idx = [] + self._connected = False + self._server_address = server_address + self._timeout = 0.1 # Seconds + + def connect(self): + if self._connected: + return + + try: + logger.info("Connecting to %s" % self._server_address) + if self._server_address.startswith('/'): + self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + self.socket.connect(self._server_address) + else: + self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.socket.connect(self._server_address.split(':')) + self.socket.settimeout(self._timeout) + self._connected = True + except socket.error: + logger.error("Failed to connect to %s" % self._server_address) + self._connected = False + + def disconnect(self): + if not self._connected: + return + logger.info("Disconnecting from %s" % self._server_address) + self.socket.close() + self.socket = None + self._connected = False + return + + def update(self, newdata): + if len(self.data) == 0: + logger.debug("Setting initial serving dataset (%d OIDs)" % len(newdata)) + else: + logger.debug("Replacing serving dataset (%d OIDs)" % len(newdata)) + self.data = newdata + self.data_idx = sorted(self.data.keys(), key=lambda k: tuple(int(part) for part in k.split('.'))) + + def new_pdu(self, type): + pdu = PDU(type) + pdu.session_id = self.session_id + pdu.transaction_id = self.transaction_id + self.transaction_id += 1 + return pdu + + def response_pdu(self, org_pdu): + pdu = PDU(agentx.AGENTX_RESPONSE_PDU) + pdu.session_id = org_pdu.session_id + pdu.transaction_id = org_pdu.transaction_id + pdu.packet_id = org_pdu.packet_id + return pdu + + def send_pdu(self, pdu): + if self.debug: pdu.dump() + self.socket.send(pdu.encode()) + + def recv_pdu(self): + buf = self.socket.recv(1024) + if not buf: return None + pdu = PDU() + pdu.decode(buf) + if self.debug: pdu.dump() + return pdu + + # ========================================= + + def _get_next_oid(self, oid, endoid): + if oid in self.data: + # Exact match found + #logger.debug('get_next_oid, exact match of %s' % oid) + idx = self.data_idx.index(oid) + if idx == (len(self.data_idx) - 1): + # Last Item in MIB, No match! + return None + return self.data_idx[idx + 1] + else: + # No exact match, find prefix + #logger.debug('get_next_oid, no exact match of %s' % oid) + slist = oid.split('.') + elist = endoid.split('.') + for tmp_oid in self.data_idx: + tlist = tmp_oid.split('.') + for i in range(len(tlist)): + try: + sok = int(slist[i]) <= int(tlist[i]) + eok = int(elist[i]) >= int(tlist[i]) + if not (sok and eok): + break + except IndexError: + pass + if sok and eok: + return tmp_oid + return None # No match! + + def start(self, oid_list): + self.connect() + if not self._connected: + return + + logger.debug("==== Open PDU ====") + pdu = self.new_pdu(agentx.AGENTX_OPEN_PDU) + self.send_pdu(pdu) + pdu = self.recv_pdu() + self.session_id = pdu.session_id + + logger.debug("==== Ping PDU ====") + pdu = self.new_pdu(agentx.AGENTX_PING_PDU) + self.send_pdu(pdu) + pdu = self.recv_pdu() + + logger.debug("==== Register PDU ====") + for oid in oid_list: + logger.info("Registering: %s" % (oid)) + pdu = self.new_pdu(agentx.AGENTX_REGISTER_PDU) + pdu.oid = oid + self.send_pdu(pdu) + pdu = self.recv_pdu() + + return + + def stop(self): + self.disconnect() + + def is_connected(self): + return self._connected + + def run(self, timeout = 0.1): + if not self._connected: + raise NetworkError('Not connected') + + if timeout != self._timeout: + self.socket.settimeout(timeout) + self._timeout = timeout + + try: + request = self.recv_pdu() + except socket.timeout: + return + + if not request: + logger.error("Empty PDU, connection closed!") + self.disconnect() + raise NetworkError("Empty PDU, disconnecting") + + response = self.response_pdu(request) + if request.type == agentx.AGENTX_GET_PDU: + logger.debug("Received GET PDU") + for rvalue in request.range_list: + oid = rvalue[0] + logger.debug("OID: %s" % (oid)) + if oid in self.data: + logger.debug("OID Found") + response.values.append(self.data[oid]) + else: + logger.debug("OID Not Found!") + response.values.append({ + 'type': agentx.TYPE_NOSUCHOBJECT, + 'name': rvalue[0], + 'value': 0 + }) + + elif request.type == agentx.AGENTX_GETNEXT_PDU: + logger.debug("Received GET_NEXT PDU") + for rvalue in request.range_list: + oid = self._get_next_oid(rvalue[0], rvalue[1]) + logger.debug("GET_NEXT: %s => %s" % (rvalue[0], oid)) + if oid: + response.values.append(self.data[oid]) + else: + response.values.append({ + 'type': agentx.TYPE_ENDOFMIBVIEW, + 'name': rvalue[0], + 'value': 0 + }) + + else: + logger.warn("Received unsupported PDU %d" % request.type) + + self.send_pdu(response) diff --git a/pyagentx/pdu.py b/agentx/pdu.py similarity index 82% rename from pyagentx/pdu.py rename to agentx/pdu.py index 6858fff..f3900ec 100644 --- a/pyagentx/pdu.py +++ b/agentx/pdu.py @@ -15,14 +15,14 @@ class NullHandler(logging.Handler): pass -logger = logging.getLogger('pyagentx.pdu') +logger = logging.getLogger('agentx.pdu') logger.addHandler(NullHandler()) # -------------------------------------------- import struct import pprint -import pyagentx +import agentx class PDU(object): @@ -31,14 +31,14 @@ class PDU(object): self.session_id = 0 self.transaction_id = 0 self.packet_id = 0 - self.error = pyagentx.ERROR_NOAGENTXERROR + self.error = agentx.ERROR_NOAGENTXERROR self.error_index = 0 self.decode_buf = '' self.state = {} self.values = [] def dump(self): - name = pyagentx.PDU_TYPE_NAME[self.type] + name = agentx.PDU_TYPE_NAME[self.type] logger.debug('PDU DUMP: New PDU') logger.debug( 'PDU DUMP: Meta : [%s: %d %d %d]' % @@ -85,25 +85,25 @@ class PDU(object): def encode_value(self, type, name, value): buf = struct.pack('!HH', type, 0) buf += self.encode_oid(name) - if type in [pyagentx.TYPE_INTEGER]: + if type in [agentx.TYPE_INTEGER]: buf += struct.pack('!l', value) elif type in [ - pyagentx.TYPE_COUNTER32, pyagentx.TYPE_GAUGE32, - pyagentx.TYPE_TIMETICKS + agentx.TYPE_COUNTER32, agentx.TYPE_GAUGE32, + agentx.TYPE_TIMETICKS ]: buf += struct.pack('!L', value) - elif type in [pyagentx.TYPE_COUNTER64]: + elif type in [agentx.TYPE_COUNTER64]: buf += struct.pack('!Q', value) - elif type in [pyagentx.TYPE_OBJECTIDENTIFIER]: + elif type in [agentx.TYPE_OBJECTIDENTIFIER]: buf += self.encode_oid(value) elif type in [ - pyagentx.TYPE_IPADDRESS, pyagentx.TYPE_OPAQUE, - pyagentx.TYPE_OCTETSTRING + agentx.TYPE_IPADDRESS, agentx.TYPE_OPAQUE, + agentx.TYPE_OCTETSTRING ]: buf += self.encode_octet(value) elif type in [ - pyagentx.TYPE_NULL, pyagentx.TYPE_NOSUCHOBJECT, - pyagentx.TYPE_NOSUCHINSTANCE, pyagentx.TYPE_ENDOFMIBVIEW + agentx.TYPE_NULL, agentx.TYPE_NOSUCHOBJECT, + agentx.TYPE_NOSUCHINSTANCE, agentx.TYPE_ENDOFMIBVIEW ]: # No data pass @@ -122,7 +122,7 @@ class PDU(object): def encode(self): buf = b'' - if self.type == pyagentx.AGENTX_OPEN_PDU: + if self.type == agentx.AGENTX_OPEN_PDU: # timeout buf += struct.pack('!BBBB', 5, 0, 0, 0) # agent OID @@ -130,11 +130,11 @@ class PDU(object): # Agent Desc buf += self.encode_octet('MyAgent') - elif self.type == pyagentx.AGENTX_PING_PDU: + elif self.type == agentx.AGENTX_PING_PDU: # No extra data pass - elif self.type == pyagentx.AGENTX_REGISTER_PDU: + elif self.type == agentx.AGENTX_REGISTER_PDU: range_subid = 0 timeout = 5 priority = 127 @@ -142,7 +142,7 @@ class PDU(object): # Sub Tree buf += self.encode_oid(self.oid) - elif self.type == pyagentx.AGENTX_RESPONSE_PDU: + elif self.type == agentx.AGENTX_RESPONSE_PDU: buf += struct.pack('!LHH', 0, self.error, self.error_index) for value in self.values: buf += self.encode_value(value['type'], value['name'], @@ -217,26 +217,26 @@ class PDU(object): logger.exception('Invalid packing value header') oid, _ = self.decode_oid() if vtype in [ - pyagentx.TYPE_INTEGER, pyagentx.TYPE_COUNTER32, - pyagentx.TYPE_GAUGE32, pyagentx.TYPE_TIMETICKS + agentx.TYPE_INTEGER, agentx.TYPE_COUNTER32, + agentx.TYPE_GAUGE32, agentx.TYPE_TIMETICKS ]: data = struct.unpack('!L', self.decode_buf[:4]) data = data[0] self.decode_buf = self.decode_buf[4:] - elif vtype in [pyagentx.TYPE_COUNTER64]: + elif vtype in [agentx.TYPE_COUNTER64]: data = struct.unpack('!Q', self.decode_buf[:8]) data = data[0] self.decode_buf = self.decode_buf[8:] - elif vtype in [pyagentx.TYPE_OBJECTIDENTIFIER]: + elif vtype in [agentx.TYPE_OBJECTIDENTIFIER]: data, _ = self.decode_oid() elif vtype in [ - pyagentx.TYPE_IPADDRESS, pyagentx.TYPE_OPAQUE, - pyagentx.TYPE_OCTETSTRING + agentx.TYPE_IPADDRESS, agentx.TYPE_OPAQUE, + agentx.TYPE_OCTETSTRING ]: data = self.decode_octet() elif vtype in [ - pyagentx.TYPE_NULL, pyagentx.TYPE_NOSUCHOBJECT, - pyagentx.TYPE_NOSUCHINSTANCE, pyagentx.TYPE_ENDOFMIBVIEW + agentx.TYPE_NULL, agentx.TYPE_NOSUCHOBJECT, + agentx.TYPE_NOSUCHINSTANCE, agentx.TYPE_ENDOFMIBVIEW ]: # No data data = None @@ -251,7 +251,7 @@ class PDU(object): ret = { 'version': t[0], 'pdu_type': t[1], - 'pdu_type_name': pyagentx.PDU_TYPE_NAME[t[1]], + 'pdu_type_name': agentx.PDU_TYPE_NAME[t[1]], 'flags': t[2], 'reserved': t[3], 'session_id': t[4], @@ -276,14 +276,14 @@ class PDU(object): def decode(self, buf): self.set_decode_buf(buf) ret = self.decode_header() - if ret['pdu_type'] == pyagentx.AGENTX_RESPONSE_PDU: + if ret['pdu_type'] == agentx.AGENTX_RESPONSE_PDU: # Decode Response Header t = struct.unpack('!LHH', self.decode_buf[:8]) self.decode_buf = self.decode_buf[8:] self.response = { 'sysUpTime': t[0], 'error': t[1], - 'error_name': pyagentx.ERROR_NAMES[t[1]], + 'error_name': agentx.ERROR_NAMES[t[1]], 'index': t[2], } # Decode VarBindList @@ -291,23 +291,23 @@ class PDU(object): while len(self.decode_buf): self.values.append(self.decode_value()) - elif ret['pdu_type'] == pyagentx.AGENTX_GET_PDU: + elif ret['pdu_type'] == agentx.AGENTX_GET_PDU: self.range_list = self.decode_search_range_list() - elif ret['pdu_type'] == pyagentx.AGENTX_GETNEXT_PDU: + elif ret['pdu_type'] == agentx.AGENTX_GETNEXT_PDU: self.range_list = self.decode_search_range_list() - elif ret['pdu_type'] == pyagentx.AGENTX_TESTSET_PDU: + elif ret['pdu_type'] == agentx.AGENTX_TESTSET_PDU: # Decode VarBindList self.values = [] while len(self.decode_buf): self.values.append(self.decode_value()) elif ret['pdu_type'] in [ - pyagentx.AGENTX_COMMITSET_PDU, pyagentx.AGENTX_UNDOSET_PDU, - pyagentx.AGENTX_CLEANUPSET_PDU + agentx.AGENTX_COMMITSET_PDU, agentx.AGENTX_UNDOSET_PDU, + agentx.AGENTX_CLEANUPSET_PDU ]: pass else: - pdu_type_str = pyagentx.PDU_TYPE_NAME.get( + pdu_type_str = agentx.PDU_TYPE_NAME.get( ret['pdu_type'], 'Unknown:' + str(ret['pdu_type'])) logger.error('Unsupported PDU type:' + pdu_type_str) diff --git a/pyagentx/.gitignore b/pyagentx/.gitignore deleted file mode 100644 index bee8a64..0000000 --- a/pyagentx/.gitignore +++ /dev/null @@ -1 +0,0 @@ -__pycache__ diff --git a/pyagentx/agent.py b/pyagentx/agent.py deleted file mode 100644 index 8a9aa44..0000000 --- a/pyagentx/agent.py +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -from __future__ import ( - absolute_import, - division, - print_function, -) - -# -------------------------------------------- -import logging - - -class NullHandler(logging.Handler): - def emit(self, record): - pass - - -logger = logging.getLogger('pyagentx.agent') -logger.addHandler(NullHandler()) -# -------------------------------------------- - -import time -import inspect -try: - import queue -except ImportError: - import Queue as queue - -import pyagentx -from pyagentx.updater import Updater -from pyagentx.network import Network - - -class AgentError(Exception): - pass - - -class Agent(object): - def __init__(self, server_address='/var/agentx/master'): - self._updater_list = [] - self._sethandlers = {} - self._threads = [] - self._server_address = server_address - - def register(self, oid, class_, freq=10): - if Updater not in inspect.getmro(class_): - raise AgentError('Class given isn\'t an updater') - # cleanup and test oid - try: - oid = oid.strip(' .') - [int(i) for i in oid.split('.')] - except ValueError: - raise AgentError('OID isn\'t valid') - self._updater_list.append({'oid': oid, 'class': class_, 'freq': freq}) - - def register_set(self, oid, class_): - if pyagentx.SetHandler not in class_.__bases__: - raise AgentError('Class given isn\'t a SetHandler') - # cleanup and test oid - try: - oid = oid.strip(' .') - [int(i) for i in oid.split('.')] - except ValueError: - raise AgentError('OID isn\'t valid') - self._sethandlers[oid] = class_() - - def setup(self): - # Override this - pass - - def start(self): - update_queue = queue.Queue(maxsize=20000) # 1000 interfaces, 20 variables each - self.setup() - # Start Updaters - for u in self._updater_list: - logger.debug('Starting updater [%s]' % u['oid']) - t = u['class']() - t.agent_setup(update_queue, u['oid'], u['freq']) - t.start() - self._threads.append(t) - # Start Network - oid_list = [u['oid'] for u in self._updater_list] - t = Network(update_queue, oid_list, self._sethandlers, self._server_address) - t.start() - self._threads.append(t) - # Do nothing ... just wait for someone to stop you - while True: - #logger.debug('Agent Sleeping ...') - time.sleep(1) - - def stop(self): - logger.debug('Stop threads') - for t in self._threads: - t.stop.set() - logger.debug('Wait for updater') - for t in self._threads: - t.join() diff --git a/pyagentx/network.py b/pyagentx/network.py deleted file mode 100644 index f580877..0000000 --- a/pyagentx/network.py +++ /dev/null @@ -1,272 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -from __future__ import ( - absolute_import, - division, - print_function, -) - -# -------------------------------------------- -import logging - - -class NullHandler(logging.Handler): - def emit(self, record): - pass - - -logger = logging.getLogger('pyagentx.network') -logger.addHandler(NullHandler()) -# -------------------------------------------- - -import socket -import time -import threading -try: - import queue -except ImportError: - import Queue as queue - -import pyagentx -from pyagentx.pdu import PDU - - -class Network(threading.Thread): - def __init__(self, update_queue, oid_list, sethandlers, server_address): - threading.Thread.__init__(self) - self.stop = threading.Event() - self._queue = update_queue - self._oid_list = oid_list - self._sethandlers = sethandlers - self._server_address = server_address - - self.session_id = 0 - self.transaction_id = 0 - self.debug = 1 - # Data Related Variables - self.data = {} - self.data_idx = [] - - def _connect(self): - while True: - try: - logger.info("Connecting to %s", self._server_address) - if self._server_address.startswith('/'): - self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - self.socket.connect(self._server_address) - else: - self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.socket.connect(self._server_address.split(':')) - self.socket.settimeout(0.1) - return - except socket.error: - logger.error("Failed to connect, sleeping and retrying later") - time.sleep(2) - - def new_pdu(self, type): - pdu = PDU(type) - pdu.session_id = self.session_id - pdu.transaction_id = self.transaction_id - self.transaction_id += 1 - return pdu - - def response_pdu(self, org_pdu): - pdu = PDU(pyagentx.AGENTX_RESPONSE_PDU) - pdu.session_id = org_pdu.session_id - pdu.transaction_id = org_pdu.transaction_id - pdu.packet_id = org_pdu.packet_id - return pdu - - def send_pdu(self, pdu): - if self.debug: pdu.dump() - self.socket.send(pdu.encode()) - - def recv_pdu(self): - buf = self.socket.recv(1024) - if not buf: return None - pdu = PDU() - pdu.decode(buf) - if self.debug: pdu.dump() - return pdu - - # ========================================= - - def _get_updates(self): - while True: - try: - item = self._queue.get_nowait() - logger.debug('New update') - update_oid = item['oid'] - update_data = item['data'] - # clear values with prefix oid - for oid in list(self.data.keys()): - if oid.startswith(update_oid): - del (self.data[oid]) - # insert updated value - for row in update_data.values(): - oid = "%s.%s" % (update_oid, row['name']) - self.data[oid] = { - 'name': oid, - 'type': row['type'], - 'value': row['value'] - } - # recalculate reverse index if data changed - self.data_idx = sorted( - self.data.keys(), - key=lambda k: tuple(int(part) for part in k.split('.'))) - except queue.Empty: - break - - def _get_next_oid(self, oid, endoid): - if oid in self.data: - # Exact match found - #logger.debug('get_next_oid, exact match of %s' % oid) - idx = self.data_idx.index(oid) - if idx == (len(self.data_idx) - 1): - # Last Item in MIB, No match! - return None - return self.data_idx[idx + 1] - else: - # No exact match, find prefix - #logger.debug('get_next_oid, no exact match of %s' % oid) - slist = oid.split('.') - elist = endoid.split('.') - for tmp_oid in self.data_idx: - tlist = tmp_oid.split('.') - for i in range(len(tlist)): - try: - sok = int(slist[i]) <= int(tlist[i]) - eok = int(elist[i]) >= int(tlist[i]) - if not (sok and eok): - break - except IndexError: - pass - if sok and eok: - return tmp_oid - return None # No match! - - def start(self): - while True: - try: - self._start_network() - except socket.error: - logger.error("Network error, master disconnect?!") - - def _start_network(self): - self._connect() - - logger.info("==== Open PDU ====") - pdu = self.new_pdu(pyagentx.AGENTX_OPEN_PDU) - self.send_pdu(pdu) - pdu = self.recv_pdu() - self.session_id = pdu.session_id - - logger.info("==== Ping PDU ====") - pdu = self.new_pdu(pyagentx.AGENTX_PING_PDU) - self.send_pdu(pdu) - pdu = self.recv_pdu() - - logger.info("==== Register PDU ====") - for oid in self._oid_list: - logger.info("Registering: %s" % (oid)) - pdu = self.new_pdu(pyagentx.AGENTX_REGISTER_PDU) - pdu.oid = oid - self.send_pdu(pdu) - pdu = self.recv_pdu() - - logger.info("==== Waiting for PDU ====") - while True: - try: - self._get_updates() - request = self.recv_pdu() - except socket.timeout: - continue - - if not request: - logger.error("Empty PDU, connection closed!") - raise socket.error - - response = self.response_pdu(request) - if request.type == pyagentx.AGENTX_GET_PDU: - logger.debug("Received GET PDU") - for rvalue in request.range_list: - oid = rvalue[0] - logger.debug("OID: %s" % (oid)) - if oid in self.data: - logger.debug("OID Found") - response.values.append(self.data[oid]) - else: - logger.debug("OID Not Found!") - response.values.append({ - 'type': pyagentx.TYPE_NOSUCHOBJECT, - 'name': rvalue[0], - 'value': 0 - }) - - elif request.type == pyagentx.AGENTX_GETNEXT_PDU: - logger.debug("Received GET_NEXT PDU") - for rvalue in request.range_list: - oid = self._get_next_oid(rvalue[0], rvalue[1]) - logger.debug("GET_NEXT: %s => %s" % (rvalue[0], oid)) - if oid: - response.values.append(self.data[oid]) - else: - response.values.append({ - 'type': pyagentx.TYPE_ENDOFMIBVIEW, - 'name': rvalue[0], - 'value': 0 - }) - - elif request.type == pyagentx.AGENTX_TESTSET_PDU: - logger.info("Received TESTSET PDU") - idx = 0 - for row in request.values: - idx += 1 - oid = row['name'] - type_ = pyagentx.TYPE_NAME.get(row['type'], 'Unknown type') - value = row['data'] - logger.info("Name: [%s] Type: [%s] Value: [%s]" % - (oid, type_, value)) - # Find matching sethandler - matching_oid = '' - for target_oid in self._sethandlers: - if oid.startswith(target_oid): - matching_oid = target_oid - break - if matching_oid == '': - logger.debug( - 'TestSet request failed: not writeable #%s' % idx) - response.error = pyagentx.ERROR_NOTWRITABLE - response.error_index = idx - break - try: - self._sethandlers[matching_oid].network_test( - request.session_id, request.transaction_id, oid, - row['data']) - except pyagentx.SetHandlerError: - logger.debug( - 'TestSet request failed: wrong value #%s' % idx) - response.error = pyagentx.ERROR_WRONGVALUE - response.error_index = idx - break - logger.debug('TestSet request passed') - - elif request.type == pyagentx.AGENTX_COMMITSET_PDU: - for handler in self._sethandlers.values(): - handler.network_commit(request.session_id, - request.transaction_id) - logger.info("Received COMMITSET PDU") - - elif request.type == pyagentx.AGENTX_UNDOSET_PDU: - for handler in self._sethandlers.values(): - handler.network_undo(request.session_id, - request.transaction_id) - logger.info("Received UNDOSET PDU") - - elif request.type == pyagentx.AGENTX_CLEANUPSET_PDU: - for handler in self._sethandlers.values(): - handler.network_cleanup(request.session_id, - request.transaction_id) - logger.info("Received CLEANUP PDU") - - self.send_pdu(response) diff --git a/pyagentx/sethandler.py b/pyagentx/sethandler.py deleted file mode 100644 index 502669d..0000000 --- a/pyagentx/sethandler.py +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -from __future__ import ( - absolute_import, - division, - print_function, -) - -# -------------------------------------------- -import logging - - -class NullHandler(logging.Handler): - def emit(self, record): - pass - - -logger = logging.getLogger('pyagentx.sethandler') -logger.addHandler(NullHandler()) -# -------------------------------------------- - - -class SetHandlerError(Exception): - pass - - -class SetHandler(object): - def __init__(self): - self.transactions = {} - - def network_test(self, session_id, transaction_id, oid, data): - tid = "%s_%s" % (session_id, transaction_id) - if tid in self.transactions: - del (self.transactions[tid]) - try: - self.test(oid, data) - self.transactions[tid] = oid, data - except SetHandler as e: - logger.error('TestSet failed') - raise e - - def network_commit(self, session_id, transaction_id): - tid = "%s_%s" % (session_id, transaction_id) - try: - oid, data = self.transactions[tid] - self.commit(oid, data) - if tid in self.transactions: - del (self.transactions[tid]) - except: - logger.error('CommitSet failed') - - def network_undo(self, session_id, transaction_id): - tid = "%s_%s" % (session_id, transaction_id) - if tid in self.transactions: - del (self.transactions[tid]) - - def network_cleanup(self, session_id, transaction_id): - tid = "%s_%s" % (session_id, transaction_id) - if tid in self.transactions: - del (self.transactions[tid]) - - # User override these - def test(self, oid, data): - pass - - def commit(self, oid, data): - pass diff --git a/pyagentx/updater.py b/pyagentx/updater.py deleted file mode 100644 index 404be8a..0000000 --- a/pyagentx/updater.py +++ /dev/null @@ -1,137 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -from __future__ import ( - absolute_import, - division, - print_function, -) - -# -------------------------------------------- -import logging - - -class NullHandler(logging.Handler): - def emit(self, record): - pass - - -logger = logging.getLogger('pyagentx.updater') -logger.addHandler(NullHandler()) -# -------------------------------------------- - -import time -import threading -try: - import queue -except ImportError: - import Queue as queue - -import pyagentx - - -class Updater(threading.Thread): - def agent_setup(self, queue, oid, freq): - self.stop = threading.Event() - self._queue = queue - self._oid = oid - self._freq = freq - self._data = {} - - def run(self): - start_time = 0 - while True: - if self.stop.is_set(): break - now = time.time() - if now - start_time > self._freq: - logger.info('Updating : %s (%s)' % - (self.__class__.__name__, self._oid)) - start_time = now - self._data = {} - try: - self.update() - self._queue.put_nowait({ - 'oid': self._oid, - 'data': self._data - }) - except queue.Full: - logger.error('Queue full') - except: - logger.exception('Unhandled update exception') - time.sleep(0.1) - logger.info('Updater stopping') - - # Override this - def update(self): - pass - - def set_INTEGER(self, oid, value): - logger.debug('Setting INTEGER %s = %s' % (oid, value)) - self._data[oid] = { - 'name': oid, - 'type': pyagentx.TYPE_INTEGER, - 'value': value - } - - def set_OCTETSTRING(self, oid, value): - logger.debug('Setting OCTETSTRING %s = %s' % (oid, value)) - self._data[oid] = { - 'name': oid, - 'type': pyagentx.TYPE_OCTETSTRING, - 'value': value - } - - def set_OBJECTIDENTIFIER(self, oid, value): - logger.debug('Setting OBJECTIDENTIFIER %s = %s' % (oid, value)) - self._data[oid] = { - 'name': oid, - 'type': pyagentx.TYPE_OBJECTIDENTIFIER, - 'value': value - } - - def set_IPADDRESS(self, oid, value): - logger.debug('Setting IPADDRESS %s = %s' % (oid, value)) - self._data[oid] = { - 'name': oid, - 'type': pyagentx.TYPE_IPADDRESS, - 'value': value - } - - def set_COUNTER32(self, oid, value): - logger.debug('Setting COUNTER32 %s = %s' % (oid, value)) - self._data[oid] = { - 'name': oid, - 'type': pyagentx.TYPE_COUNTER32, - 'value': value - } - - def set_GAUGE32(self, oid, value): - logger.debug('Setting GAUGE32 %s = %s' % (oid, value)) - self._data[oid] = { - 'name': oid, - 'type': pyagentx.TYPE_GAUGE32, - 'value': value - } - - def set_TIMETICKS(self, oid, value): - logger.debug('Setting TIMETICKS %s = %s' % (oid, value)) - self._data[oid] = { - 'name': oid, - 'type': pyagentx.TYPE_TIMETICKS, - 'value': value - } - - def set_OPAQUE(self, oid, value): - logger.debug('Setting OPAQUE %s = %s' % (oid, value)) - self._data[oid] = { - 'name': oid, - 'type': pyagentx.TYPE_OPAQUE, - 'value': value - } - - def set_COUNTER64(self, oid, value): - logger.debug('Setting COUNTER64 %s = %s' % (oid, value)) - self._data[oid] = { - 'name': oid, - 'type': pyagentx.TYPE_COUNTER64, - 'value': value - } diff --git a/vpp-snmp-agent.py b/vpp-snmp-agent.py index b458d7e..610d6ef 100755 --- a/vpp-snmp-agent.py +++ b/vpp-snmp-agent.py @@ -3,515 +3,151 @@ from vppstats import VPPStats from vppapi import VPPApi -import time -import pyagentx -import logging -import threading +import agentx + +class MyAgent(agentx.Agent): + def setup(self): + global vppstat, vpp, logger + + self.logger.info("Connecting to VPP Stats...") + vppstat = VPPStats(socketname='/run/vpp/stats.sock', timeout=2) + if not vppstat.connect(): + self.logger.error("Can't connect to VPP Stats API, bailing") + return False + + vpp = VPPApi(clientname='vpp-snmp-agent') + if not vpp.connect(): + logger.error("Can't connect to VPP API, bailing") + return False + + self.register('1.3.6.1.2.1.2.2.1') + self.register('1.3.6.1.2.1.31.1.1.1') + + return True -class NullHandler(logging.Handler): - def emit(self, record): - pass - - -logger = logging.getLogger('pyagentx.vppstats') -logger.addHandler(NullHandler()) - - -class ifName(pyagentx.Updater): - def update(self): - global vppstat - vppstat.connect() - - for i in range(len(vppstat['/if/names'])): - self.set_OCTETSTRING(str(i + 1), vppstat['/if/names'][i]) - - -class ifIndex(pyagentx.Updater): - def update(self): - global vppstat - vppstat.connect() - - for i in range(len(vppstat['/if/names'])): - self.set_INTEGER(str(i + 1), i + 1) - - -class ifType(pyagentx.Updater): - def update(self): - global vppstat - vppstat.connect() - - for i in range(len(vppstat['/if/names'])): - t = 6 # ethermet-csmacd - if vppstat['/if/names'][i].startswith("loop"): - t = 24 # softwareLoopback - self.set_INTEGER(str(i + 1), t) - - -class ifMtu(pyagentx.Updater): def update(self): global vppstat, vpp vppstat.connect() vpp.connect() + ds = agentx.DataSet() ifaces = vpp.get_ifaces() + self.logger.debug("%d VPP interfaces retrieved" % len(ifaces)) + self.logger.debug("%d VPP Stats interfaces retrieved" % len(vppstat['/if/names'])) for i in range(len(vppstat['/if/names'])): ifname = vppstat['/if/names'][i] + idx = 1000+i + + ds.set('1.3.6.1.2.1.2.2.1.1.%u' % (idx), 'int', idx) + ds.set('1.3.6.1.2.1.2.2.1.2.%u' % (idx), 'str', ifname) + + if ifname.startswith("loop"): + ds.set('1.3.6.1.2.1.2.2.1.3.%u' % (idx), 'int', 24) # softwareLoopback + else: + ds.set('1.3.6.1.2.1.2.2.1.3.%u' % (idx), 'int', 6) # ethermet-csmacd + mtu = 0 if not ifname in ifaces: - logger.warning("Could not get MTU for interface %s", ifname) + self.logger.warning("Could not get MTU for interface %s", ifname) else: mtu = ifaces[ifname].mtu[0] - self.set_INTEGER(str(i + 1), mtu) + ds.set('1.3.6.1.2.1.2.2.1.4.%u' % (idx), 'int', mtu) - -class ifSpeed(pyagentx.Updater): - def update(self): - global vppstat, vpp - vppstat.connect() - vpp.connect() - - ifaces = vpp.get_ifaces() - - 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) + self.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) + ds.set('1.3.6.1.2.1.2.2.1.5.%u' % (idx), 'gauge32', speed) - -class ifAdminStatus(pyagentx.Updater): - def update(self): - global vppstat, vpp - vppstat.connect() - vpp.connect() - - ifaces = vpp.get_ifaces() - - 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() - vpp.connect() - - ifaces = vpp.get_ifaces() - - 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() - vpp.connect() - - ifaces = vpp.get_ifaces() - - 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) + self.logger.warning("Could not get PhysAddress for interface %s", ifname) else: mac = str(ifaces[ifname].l2_address) - self.set_OCTETSTRING(str(i + 1), mac) + ds.set('1.3.6.1.2.1.2.2.1.6.%u' % (idx), 'str', mac) + admin_status = 3 # testing + if not ifname in ifaces: + self.logger.warning("Could not get AdminStatus for interface %s", ifname) + else: + if int(ifaces[ifname].flags) & 2: + admin_status = 1 # up + else: + admin_status = 2 # down + ds.set('1.3.6.1.2.1.2.2.1.7.%u' % (idx), 'int', admin_status) -class ifAlias(pyagentx.Updater): - def update(self): - global vppstat - vppstat.connect() + oper_status = 3 # testing + if not ifname in ifaces: + self.logger.warning("Could not get OperStatus for interface %s", ifname) + else: + if int(ifaces[ifname].flags) & 1: + oper_status = 1 # up + else: + oper_status = 2 # down + ds.set('1.3.6.1.2.1.2.2.1.8.%u' % (idx), 'int', oper_status) - for i in range(len(vppstat['/if/names'])): - self.set_OCTETSTRING(str(i + 1), vppstat['/if/names'][i]) + ds.set('1.3.6.1.2.1.2.2.1.9.%u' % (idx), 'ticks', 0) + ds.set('1.3.6.1.2.1.2.2.1.10.%u' % (idx), 'u32', vppstat['/if/rx'][:, i].sum_octets() % 2**32) + ds.set('1.3.6.1.2.1.2.2.1.11.%u' % (idx), 'u32', vppstat['/if/rx'][:, i].sum_packets() % 2**32) + ds.set('1.3.6.1.2.1.2.2.1.12.%u' % (idx), 'u32', vppstat['/if/rx-multicast'][:, i].sum_packets() % 2**32) + ds.set('1.3.6.1.2.1.2.2.1.13.%u' % (idx), 'u32', vppstat['/if/rx-no-buf'][:, i].sum() % 2**32) + ds.set('1.3.6.1.2.1.2.2.1.14.%u' % (idx), 'u32', vppstat['/if/rx-error'][:, i].sum() % 2**32) + ds.set('1.3.6.1.2.1.2.2.1.16.%u' % (idx), 'u32', vppstat['/if/tx'][:, i].sum_octets() % 2**32) + ds.set('1.3.6.1.2.1.2.2.1.17.%u' % (idx), 'u32', vppstat['/if/tx'][:, i].sum_packets() % 2**32) + ds.set('1.3.6.1.2.1.2.2.1.18.%u' % (idx), 'u32', vppstat['/if/tx-multicast'][:, i].sum_packets() % 2**32) + ds.set('1.3.6.1.2.1.2.2.1.19.%u' % (idx), 'u32', vppstat['/if/drops'][:, i].sum() % 2**32) + ds.set('1.3.6.1.2.1.2.2.1.20.%u' % (idx), 'u32', vppstat['/if/tx-error'][:, i].sum() % 2**32) -class ifInMulticastPkts(pyagentx.Updater): - def update(self): - global vppstat - vppstat.connect() + ds.set('1.3.6.1.2.1.31.1.1.1.1.%u' % (idx), 'str', ifname) + ds.set('1.3.6.1.2.1.31.1.1.1.2.%u' % (idx), 'u32', vppstat['/if/rx-multicast'][:, i].sum_packets() % 2**32) + ds.set('1.3.6.1.2.1.31.1.1.1.3.%u' % (idx), 'u32', vppstat['/if/rx-broadcast'][:, i].sum_packets() % 2**32) + ds.set('1.3.6.1.2.1.31.1.1.1.4.%u' % (idx), 'u32', vppstat['/if/tx-multicast'][:, i].sum_packets() % 2**32) + ds.set('1.3.6.1.2.1.31.1.1.1.5.%u' % (idx), 'u32', vppstat['/if/tx-broadcast'][:, i].sum_packets() % 2**32) - for i in range(len(vppstat['/if/names'])): - self.set_COUNTER32( - str(i + 1), - vppstat['/if/rx-multicast'][:, i].sum_packets() % 2**32) + ds.set('1.3.6.1.2.1.31.1.1.1.6.%u' % (idx), 'u64', vppstat['/if/rx'][:, i].sum_octets()) + ds.set('1.3.6.1.2.1.31.1.1.1.7.%u' % (idx), 'u64', vppstat['/if/rx'][:, i].sum_packets()) + ds.set('1.3.6.1.2.1.31.1.1.1.8.%u' % (idx), 'u64', vppstat['/if/rx-multicast'][:, i].sum_packets()) + ds.set('1.3.6.1.2.1.31.1.1.1.9.%u' % (idx), 'u64', vppstat['/if/rx-broadcast'][:, i].sum_packets()) + ds.set('1.3.6.1.2.1.31.1.1.1.10.%u' % (idx), 'u64', vppstat['/if/tx'][:, i].sum_octets()) + ds.set('1.3.6.1.2.1.31.1.1.1.11.%u' % (idx), 'u64', vppstat['/if/tx'][:, i].sum_packets()) + ds.set('1.3.6.1.2.1.31.1.1.1.12.%u' % (idx), 'u64', vppstat['/if/tx-multicast'][:, i].sum_packets()) + ds.set('1.3.6.1.2.1.31.1.1.1.13.%u' % (idx), 'u64', vppstat['/if/tx-broadcast'][:, i].sum_packets()) -class ifInBroadcastPkts(pyagentx.Updater): - def update(self): - global vppstat - vppstat.connect() - - for i in range(len(vppstat['/if/names'])): - self.set_COUNTER32( - str(i + 1), - vppstat['/if/rx-broadcast'][:, i].sum_packets() % 2**32) - - -class ifOutMulticastPkts(pyagentx.Updater): - def update(self): - global vppstat - vppstat.connect() - - for i in range(len(vppstat['/if/names'])): - self.set_COUNTER32( - str(i + 1), - vppstat['/if/tx-multicast'][:, i].sum_packets() % 2**32) - - -class ifOutBroadcastPkts(pyagentx.Updater): - def update(self): - global vppstat - vppstat.connect() - - for i in range(len(vppstat['/if/names'])): - self.set_COUNTER32( - str(i + 1), - vppstat['/if/tx-broadcast'][:, i].sum_packets() % 2**32) - - -class ifHCInOctets(pyagentx.Updater): - def update(self): - global vppstat - vppstat.connect() - - for i in range(len(vppstat['/if/names'])): - self.set_COUNTER64(str(i + 1), vppstat['/if/rx'][:, - i].sum_octets()) - - -class ifHCInUcastPkts(pyagentx.Updater): - def update(self): - global vppstat - vppstat.connect() - - for i in range(len(vppstat['/if/names'])): - self.set_COUNTER64(str(i + 1), vppstat['/if/rx'][:, - i].sum_packets()) - - -class ifHCInMulticastPkts(pyagentx.Updater): - def update(self): - global vppstat - vppstat.connect() - - for i in range(len(vppstat['/if/names'])): - self.set_COUNTER64(str(i + 1), - vppstat['/if/rx-multicast'][:, i].sum_packets()) - - -class ifHCInBroadcastPkts(pyagentx.Updater): - def update(self): - global vppstat - vppstat.connect() - - for i in range(len(vppstat['/if/names'])): - self.set_COUNTER64(str(i + 1), - vppstat['/if/rx-broadcast'][:, i].sum_packets()) - - -class ifHCOutOctets(pyagentx.Updater): - def update(self): - global vppstat - vppstat.connect() - - for i in range(len(vppstat['/if/names'])): - self.set_COUNTER64(str(i + 1), vppstat['/if/tx'][:, - i].sum_octets()) - - -class ifHCOutUcastPkts(pyagentx.Updater): - def update(self): - global vppstat - vppstat.connect() - - for i in range(len(vppstat['/if/names'])): - self.set_COUNTER64(str(i + 1), vppstat['/if/tx'][:, - i].sum_packets()) - - -class ifHCOutMulticastPkts(pyagentx.Updater): - def update(self): - global vppstat - vppstat.connect() - - for i in range(len(vppstat['/if/names'])): - self.set_COUNTER64(str(i + 1), - vppstat['/if/tx-multicast'][:, i].sum_packets()) - - -class ifHCOutBroadcastPkts(pyagentx.Updater): - def update(self): - global vppstat - vppstat.connect() - - for i in range(len(vppstat['/if/names'])): - self.set_COUNTER64(str(i + 1), - vppstat['/if/tx-broadcast'][:, i].sum_packets()) - - -class ifHighSpeed(pyagentx.Updater): - def update(self): - global vppstat, vpp - vppstat.connect() - vpp.connect() - - ifaces = vpp.get_ifaces() - - for i in range(len(vppstat['/if/names'])): - 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) + self.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): - def update(self): - global vppstat - vppstat.connect() - - for i in range(len(vppstat['/if/names'])): - # Hardcode to false(2) - self.set_INTEGER(str(i + 1), 2) - - -class ifConnectorPresent(pyagentx.Updater): - def update(self): - global vppstat - vppstat.connect() - - for i in range(len(vppstat['/if/names'])): - # Hardcode to true(1) - self.set_INTEGER(str(i + 1), 1) - - -class ifCounterDiscontinuityTime(pyagentx.Updater): - def update(self): - global vppstat - vppstat.connect() - - for i in range(len(vppstat['/if/names'])): - # Hardcode to Timeticks: (0) 0:00:00.00 - self.set_TIMETICKS(str(i + 1), 0) - - -class ifInOctets(pyagentx.Updater): - def update(self): - global vppstat - vppstat.connect() - - for i in range(len(vppstat['/if/names'])): - self.set_COUNTER32(str(i + 1), - vppstat['/if/rx'][:, i].sum_octets() % 2**32) - - -class ifInUcastPkts(pyagentx.Updater): - def update(self): - global vppstat - vppstat.connect() - - for i in range(len(vppstat['/if/names'])): - self.set_COUNTER32(str(i + 1), - vppstat['/if/rx'][:, i].sum_packets() % 2**32) - - -class ifInNUcastPkts(pyagentx.Updater): - def update(self): - global vppstat - vppstat.connect() - - for i in range(len(vppstat['/if/names'])): - self.set_COUNTER32( - str(i + 1), - vppstat['/if/rx-multicast'][:, i].sum_packets() % 2**32) - - -class ifInDiscards(pyagentx.Updater): - def update(self): - global vppstat - vppstat.connect() - - for i in range(len(vppstat['/if/names'])): - self.set_COUNTER32(str(i + 1), - vppstat['/if/rx-no-buf'][:, i].sum() % 2**32) - - -class ifInErrors(pyagentx.Updater): - def update(self): - global vppstat - vppstat.connect() - - for i in range(len(vppstat['/if/names'])): - self.set_COUNTER32(str(i + 1), - vppstat['/if/rx-error'][:, i].sum() % 2**32) - - -class ifOutOctets(pyagentx.Updater): - def update(self): - global vppstat - vppstat.connect() - - for i in range(len(vppstat['/if/names'])): - self.set_COUNTER32(str(i + 1), - vppstat['/if/tx'][:, i].sum_octets() % 2**32) - - -class ifOutUcastPkts(pyagentx.Updater): - def update(self): - global vppstat - vppstat.connect() - - for i in range(len(vppstat['/if/names'])): - self.set_COUNTER32(str(i + 1), - vppstat['/if/tx'][:, i].sum_packets() % 2**32) - - -class ifOutNUcastPkts(pyagentx.Updater): - def update(self): - global vppstat - vppstat.connect() - - for i in range(len(vppstat['/if/names'])): - self.set_COUNTER32( - str(i + 1), - vppstat['/if/tx-multicast'][:, i].sum_packets() % 2**32) - - -class ifOutDiscards(pyagentx.Updater): - def update(self): - global vppstat - vppstat.connect() - - for i in range(len(vppstat['/if/names'])): - self.set_COUNTER32(str(i + 1), - vppstat['/if/drops'][:, i].sum() % 2**32) - - -class ifOutErrors(pyagentx.Updater): - def update(self): - global vppstat - vppstat.connect() - - for i in range(len(vppstat['/if/names'])): - self.set_COUNTER32(str(i + 1), - vppstat['/if/tx-error'][:, i].sum() % 2**32) - - -class MyAgent(pyagentx.Agent): - def setup(self): - - # iso.org.dod.internet.mgmt.mib_2.interfaces.ifTable.ifEntry - 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) - self.register('1.3.6.1.2.1.2.2.1.12', ifInNUcastPkts) - self.register('1.3.6.1.2.1.2.2.1.13', ifInDiscards) - self.register('1.3.6.1.2.1.2.2.1.14', ifInErrors) - - self.register('1.3.6.1.2.1.2.2.1.16', ifOutOctets) - self.register('1.3.6.1.2.1.2.2.1.17', ifOutUcastPkts) - self.register('1.3.6.1.2.1.2.2.1.18', ifOutNUcastPkts) - 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) - - # 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) - self.register('1.3.6.1.2.1.31.1.1.1.3', ifInBroadcastPkts) - self.register('1.3.6.1.2.1.31.1.1.1.4', ifOutMulticastPkts) - self.register('1.3.6.1.2.1.31.1.1.1.5', ifOutBroadcastPkts) - - self.register('1.3.6.1.2.1.31.1.1.1.6', ifHCInOctets) - self.register('1.3.6.1.2.1.31.1.1.1.7', ifHCInUcastPkts) - self.register('1.3.6.1.2.1.31.1.1.1.8', ifHCInMulticastPkts) - self.register('1.3.6.1.2.1.31.1.1.1.9', ifHCInBroadcastPkts) - - self.register('1.3.6.1.2.1.31.1.1.1.10', ifHCOutOctets) - self.register('1.3.6.1.2.1.31.1.1.1.11', ifHCOutUcastPkts) - self.register('1.3.6.1.2.1.31.1.1.1.12', ifHCOutMulticastPkts) - self.register('1.3.6.1.2.1.31.1.1.1.13', ifHCOutBroadcastPkts) - - self.register('1.3.6.1.2.1.31.1.1.1.15', ifHighSpeed) - self.register('1.3.6.1.2.1.31.1.1.1.16', ifPromiscuousMode) - self.register('1.3.6.1.2.1.31.1.1.1.17', ifConnectorPresent) - self.register('1.3.6.1.2.1.31.1.1.1.18', ifAlias) - self.register('1.3.6.1.2.1.31.1.1.1.19', ifCounterDiscontinuityTime) + ds.set('1.3.6.1.2.1.31.1.1.1.15.%u' % (idx), 'gauge32', speed) + ds.set('1.3.6.1.2.1.31.1.1.1.16.%u' % (idx), 'int', 2) # Hardcode to false(2) + ds.set('1.3.6.1.2.1.31.1.1.1.17.%u' % (idx), 'int', 1) # Hardcode to true(1) + ds.set('1.3.6.1.2.1.31.1.1.1.18.%u' % (idx), 'str', ifname) + ds.set('1.3.6.1.2.1.31.1.1.1.19.%u' % (idx), 'ticks', 0) # Hardcode to Timeticks: (0) 0:00:00.00 + return ds def main(): - global vppstat, vpp, logger - - pyagentx.setup_logging(debug=False) - - vppstat = VPPStats(socketname='/run/vpp/stats.sock', timeout=2) - vppstat.connect() - - vpp = VPPApi() - if not vpp.connect(): - logger.error("Can't connect to VPP API, bailing") - return + agentx.setup_logging(debug=False) try: a = MyAgent(server_address='/run/vpp/agentx.sock') - a.start() + a.run() except Exception as e: print("Unhandled exception:", e) a.stop() except KeyboardInterrupt: a.stop() - vppstat.disconnect() - vpp.disconnect() - - if __name__ == "__main__": main() diff --git a/vppapi.py b/vppapi.py index 7c5d5fd..1f20668 100644 --- a/vppapi.py +++ b/vppapi.py @@ -7,7 +7,6 @@ from vpp_papi import VPPApiClient import os import fnmatch import logging -import threading class NullHandler(logging.Handler): @@ -16,18 +15,16 @@ class NullHandler(logging.Handler): class VPPApi(): - def __init__(self, address='/run/vpp/api.sock'): + def __init__(self, address='/run/vpp/api.sock', clientname='vppapi-client'): self.address = address - self.lock = threading.Lock() self.connected = False - self.logger = logging.getLogger('pyagentx.vppapi') + self.logger = logging.getLogger('agentx.vppapi') self.logger.addHandler(NullHandler()) + self.clientname = clientname self.vpp = None def connect(self): - self.lock.acquire() if self.connected: - self.lock.release() return True vpp_json_dir = '/usr/share/vpp/api/' @@ -40,23 +37,20 @@ class VPPApi(): if not jsonfiles: self.logger.error('no json api files found') - self.lock.release() return False self.vpp = VPPApiClient(apifiles=jsonfiles, server_address=self.address) try: self.logger.info('Connecting to VPP') - self.vpp.connect('vpp-snmp-agent') + self.vpp.connect(self.clientname) except: - self.lock.release() return False v = self.vpp.api.show_version() self.logger.info('VPP version is %s' % v.version) self.connected = True - self.lock.release() return True def disconnect(self): @@ -71,23 +65,19 @@ class VPPApi(): 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 if not iface_list: self.logger.error("Can't get interface list") - self.lock.release() return ret for iface in iface_list: ret[iface.interface_name] = iface - self.lock.release() return ret diff --git a/vppstats.py b/vppstats.py index 88db083..b1afbd0 100644 --- a/vppstats.py +++ b/vppstats.py @@ -110,9 +110,12 @@ class VPPStats(): def connect(self): '''Connect to stats segment''' if self.connected: - return - sock = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) - sock.connect(self.socketname) + return True + try: + sock = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) + sock.connect(self.socketname) + except: + return False # Get file descriptor for memory map fds = array.array("i") # Array of ints @@ -133,9 +136,11 @@ class VPPStats(): if self.version != 2: raise Exception('Incompatbile stat segment version {}'.format( self.version)) + return False self.refresh() self.connected = True + return True def disconnect(self): '''Disconnect from stats segment'''