initial checkin

This commit is contained in:
Pim van Pelt
2021-09-05 15:13:12 +00:00
commit 51eee915bf
16 changed files with 1998 additions and 0 deletions

1
pyagentx/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
__pycache__

159
pyagentx/__init__.py Normal file
View File

@ -0,0 +1,159 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import (
absolute_import,
division,
print_function,
)
import logging
from pyagentx.updater import Updater
from pyagentx.agent import Agent
from pyagentx.sethandler import SetHandler, SetHandlerError
def setup_logging(debug=False):
if debug:
level = logging.DEBUG
else:
level = logging.INFO
logger = logging.getLogger('pyagentx')
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')
ch = logging.StreamHandler()
ch.setLevel(level)
ch.setFormatter(formatter)
logger.addHandler(ch)
SOCKET_PATH = "/var/agentx/master"
AGENTX_EMPTY_PDU = 1
AGENTX_OPEN_PDU = 1
AGENTX_CLOSE_PDU = 2
AGENTX_REGISTER_PDU = 3
AGENTX_UNREGISTER_PDU = 4
AGENTX_GET_PDU = 5
AGENTX_GETNEXT_PDU = 6
AGENTX_GETBULK_PDU = 7
AGENTX_TESTSET_PDU = 8
AGENTX_COMMITSET_PDU = 9
AGENTX_UNDOSET_PDU = 10
AGENTX_CLEANUPSET_PDU = 11
AGENTX_NOTIFY_PDU = 12
AGENTX_PING_PDU = 13
AGENTX_INDEXALLOCATE_PDU = 14
AGENTX_INDEXDEALLOCATE_PDU = 15
AGENTX_ADDAGENTCAPS_PDU = 16
AGENTX_REMOVEAGENTCAPS_PDU = 17
AGENTX_RESPONSE_PDU = 18
PDU_TYPE_NAME = {}
PDU_TYPE_NAME[0] = "EMPTY_PDU"
PDU_TYPE_NAME[1] = "OPEN_PDU"
PDU_TYPE_NAME[2] = "CLOSE_PDU"
PDU_TYPE_NAME[3] = "REGISTER_PDU"
PDU_TYPE_NAME[4] = "UNREGISTER_PDU"
PDU_TYPE_NAME[5] = "GET_PDU"
PDU_TYPE_NAME[6] = "GETNEXT_PDU"
PDU_TYPE_NAME[7] = "GETBULK_PDU"
PDU_TYPE_NAME[8] = "TESTSET_PDU"
PDU_TYPE_NAME[9] = "COMMITSET_PDU"
PDU_TYPE_NAME[10] = "UNDOSET_PDU"
PDU_TYPE_NAME[11] = "CLEANUPSET_PDU"
PDU_TYPE_NAME[12] = "NOTIFY_PDU"
PDU_TYPE_NAME[13] = "PING_PDU"
PDU_TYPE_NAME[14] = "INDEXALLOCATE_PDU"
PDU_TYPE_NAME[15] = "INDEXDEALLOCATE_PDU"
PDU_TYPE_NAME[16] = "ADDAGENTCAPS_PDU"
PDU_TYPE_NAME[17] = "REMOVEAGENTCAPS_PDU"
PDU_TYPE_NAME[18] = "RESPONSE_PDU"
TYPE_INTEGER = 2
TYPE_OCTETSTRING = 4
TYPE_NULL = 5
TYPE_OBJECTIDENTIFIER = 6
TYPE_IPADDRESS = 64
TYPE_COUNTER32 = 65
TYPE_GAUGE32 = 66
TYPE_TIMETICKS = 67
TYPE_OPAQUE = 68
TYPE_COUNTER64 = 70
TYPE_NOSUCHOBJECT = 128
TYPE_NOSUCHINSTANCE = 129
TYPE_ENDOFMIBVIEW = 130
TYPE_NAME = {}
TYPE_NAME[2] = "INTEGER"
TYPE_NAME[4] = "OCTETSTRING"
TYPE_NAME[5] = "NULL"
TYPE_NAME[6] = "OBJECTIDENTIFIER"
TYPE_NAME[64] = "IPADDRESS"
TYPE_NAME[65] = "COUNTER32"
TYPE_NAME[66] = "GAUGE32"
TYPE_NAME[67] = "TIMETICKS"
TYPE_NAME[68] = "OPAQUE"
TYPE_NAME[70] = "COUNTER64"
TYPE_NAME[128] = "NOSUCHOBJECT"
TYPE_NAME[129] = "NOSUCHINSTANCE"
TYPE_NAME[130] = "ENDOFMIBVIEW"
ERROR_NOAGENTXERROR = 0
ERROR_GENERR = 5
ERROR_NOACCESS = 6
ERROR_WRONGTYPE = 7
ERROR_WRONGLENGTH = 8
ERROR_WRONGENCODING = 9
ERROR_WRONGVALUE = 10
ERROR_NOCREATION = 11
ERROR_INCONSISTENTVALUE = 12
ERROR_RESOURCEUNAVAILABLE = 13
ERROR_COMMITFAILED = 14
ERROR_UNDOFAILED = 15
ERROR_NOTWRITABLE = 17
ERROR_INCONSISTENTNAME = 18
ERROR_OPENFAILED = 256
ERROR_NOTOPEN = 257
ERROR_INDEXWRONGTYPE = 258
ERROR_INDEXALREADYALLOCATED = 259
ERROR_INDEXNONEAVAILABLE = 260
ERROR_INDEXNOTALLOCATED = 261
ERROR_UNSUPPORTEDCONTEXT = 262
ERROR_DUPLICATEREGISTRATION = 263
ERROR_UNKNOWNREGISTRATION = 264
ERROR_UNKNOWNAGENTCAPS = 265
ERROR_PARSEERROR = 266
ERROR_REQUESTDENIED = 267
ERROR_PROCESSINGERROR = 268
ERROR_NAMES = {}
ERROR_NAMES[0] = "NOAGENTXERROR"
ERROR_NAMES[5] = "GENERR"
ERROR_NAMES[6] = "NOACCESS"
ERROR_NAMES[7] = "WRONGTYPE"
ERROR_NAMES[8] = "WRONGLENGTH"
ERROR_NAMES[9] = "WRONGENCODING"
ERROR_NAMES[10] = "WRONGVALUE"
ERROR_NAMES[11] = "NOCREATION"
ERROR_NAMES[12] = "INCONSISTENTVALUE"
ERROR_NAMES[13] = "RESOURCEUNAVAILABLE"
ERROR_NAMES[14] = "ERROR_COMMITFAILED"
ERROR_NAMES[15] = "ERROR_UNDOFAILED"
ERROR_NAMES[17] = "NOTWRITABLE"
ERROR_NAMES[18] = "INCONSISTENTNAME"
ERROR_NAMES[256] = "OPENFAILED"
ERROR_NAMES[257] = "NOTOPEN"
ERROR_NAMES[258] = "INDEXWRONGTYPE"
ERROR_NAMES[259] = "INDEXALREADYALLOCATED"
ERROR_NAMES[260] = "INDEXNONEAVAILABLE"
ERROR_NAMES[261] = "INDEXNOTALLOCATED"
ERROR_NAMES[262] = "UNSUPPORTEDCONTEXT"
ERROR_NAMES[263] = "DUPLICATEREGISTRATION"
ERROR_NAMES[264] = "UNKNOWNREGISTRATION"
ERROR_NAMES[265] = "UNKNOWNAGENTCAPS"
ERROR_NAMES[266] = "PARSEERROR"
ERROR_NAMES[267] = "REQUESTDENIED"
ERROR_NAMES[268] = "PROCESSINGERROR"

96
pyagentx/agent.py Normal file
View File

@ -0,0 +1,96 @@
#!/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):
self._updater_list = []
self._sethandlers = {}
self._threads = []
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=20)
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)
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()

268
pyagentx/network.py Normal file
View File

@ -0,0 +1,268 @@
#!/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):
threading.Thread.__init__(self)
self.stop = threading.Event()
self._queue = update_queue
self._oid_list = oid_list
self._sethandlers = sethandlers
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:
# self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
# self.socket.connect(pyagentx.SOCKET_PATH)
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.connect(("localhost",705))
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)

313
pyagentx/pdu.py Normal file
View File

@ -0,0 +1,313 @@
#!/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.pdu')
logger.addHandler(NullHandler())
# --------------------------------------------
import struct
import pprint
import pyagentx
class PDU(object):
def __init__(self, type=0):
self.type = type
self.session_id = 0
self.transaction_id = 0
self.packet_id = 0
self.error = pyagentx.ERROR_NOAGENTXERROR
self.error_index = 0
self.decode_buf = ''
self.state = {}
self.values = []
def dump(self):
name = pyagentx.PDU_TYPE_NAME[self.type]
logger.debug('PDU DUMP: New PDU')
logger.debug(
'PDU DUMP: Meta : [%s: %d %d %d]' %
(name, self.session_id, self.transaction_id, self.packet_id))
if 'payload_length' in self.state:
logger.debug('PDU DUMP: Length : %s' %
self.state['payload_length'])
if hasattr(self, 'response'):
logger.debug('PDU DUMP: Response : %s' % self.response)
if hasattr(self, 'values'):
logger.debug('PDU DUMP: Values : %s' %
pprint.pformat(self.values))
if hasattr(self, 'range_list'):
logger.debug('PDU DUMP: Range list: %s' %
pprint.pformat(self.range_list))
# ====================================================
# encode functions
def encode_oid(self, oid, include=0):
oid = oid.strip()
oid = oid.split('.')
oid = [int(i) for i in oid]
if len(oid) > 5 and oid[:4] == [1, 3, 6, 1]:
# prefix
prefix = oid[4]
oid = oid[5:]
else:
# no prefix
prefix = 0
buf = struct.pack('BBBB', len(oid), prefix, include, 0)
for i in range(len(oid)):
buf += struct.pack('!L', oid[i])
return buf
def encode_octet(self, octet):
octet = octet.encode("utf-8")
buf = struct.pack('!L', len(octet))
buf += octet
padding = (4 - (len(octet) % 4)) % 4
buf += chr(0).encode() * padding
return buf
def encode_value(self, type, name, value):
buf = struct.pack('!HH', type, 0)
buf += self.encode_oid(name)
if type in [pyagentx.TYPE_INTEGER]:
buf += struct.pack('!l', value)
elif type in [
pyagentx.TYPE_COUNTER32, pyagentx.TYPE_GAUGE32,
pyagentx.TYPE_TIMETICKS
]:
buf += struct.pack('!L', value)
elif type in [pyagentx.TYPE_COUNTER64]:
buf += struct.pack('!Q', value)
elif type in [pyagentx.TYPE_OBJECTIDENTIFIER]:
buf += self.encode_oid(value)
elif type in [
pyagentx.TYPE_IPADDRESS, pyagentx.TYPE_OPAQUE,
pyagentx.TYPE_OCTETSTRING
]:
buf += self.encode_octet(value)
elif type in [
pyagentx.TYPE_NULL, pyagentx.TYPE_NOSUCHOBJECT,
pyagentx.TYPE_NOSUCHINSTANCE, pyagentx.TYPE_ENDOFMIBVIEW
]:
# No data
pass
else:
logger.error('Unknown Type:' % type)
return buf
def encode_header(self, pdu_type, payload_length=0, flags=0):
flags = flags | 0x10 # Bit 5 = all ints in NETWORK_BYTE_ORDER
buf = struct.pack('BBBB', 1, pdu_type, flags, 0)
buf += struct.pack('!L', self.session_id) # sessionID
buf += struct.pack('!L', self.transaction_id) # transactionID
buf += struct.pack('!L', self.packet_id) # packetID
buf += struct.pack('!L', payload_length)
return buf
def encode(self):
buf = b''
if self.type == pyagentx.AGENTX_OPEN_PDU:
# timeout
buf += struct.pack('!BBBB', 5, 0, 0, 0)
# agent OID
buf += struct.pack('!L', 0)
# Agent Desc
buf += self.encode_octet('MyAgent')
elif self.type == pyagentx.AGENTX_PING_PDU:
# No extra data
pass
elif self.type == pyagentx.AGENTX_REGISTER_PDU:
range_subid = 0
timeout = 5
priority = 127
buf += struct.pack('BBBB', timeout, priority, range_subid, 0)
# Sub Tree
buf += self.encode_oid(self.oid)
elif self.type == pyagentx.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'],
value['value'])
else:
# Unsupported PDU type
pass
return self.encode_header(self.type, len(buf)) + buf
# ====================================================
# decode functions
def set_decode_buf(self, buf):
self.decode_buf = buf
def decode_oid(self):
try:
t = struct.unpack('!BBBB', self.decode_buf[:4])
self.decode_buf = self.decode_buf[4:]
ret = {
'n_subid': t[0],
'prefix': t[1],
'include': t[2],
'reserved': t[3],
}
sub_ids = []
if ret['prefix']:
sub_ids = [1, 3, 6, 1]
sub_ids.append(ret['prefix'])
for i in range(ret['n_subid']):
t = struct.unpack('!L', self.decode_buf[:4])
self.decode_buf = self.decode_buf[4:]
sub_ids.append(t[0])
oid = '.'.join(str(i) for i in sub_ids)
return oid, ret['include']
except Exception as e:
logger.exception('Invalid packing OID header')
logger.debug('%s' % pprint.pformat(self.decode_buf))
def decode_search_range(self):
start_oid, include = self.decode_oid()
if start_oid == []:
return [], [], 0
end_oid, _ = self.decode_oid()
return start_oid, end_oid, include
def decode_search_range_list(self):
range_list = []
while len(self.decode_buf):
range_list.append(self.decode_search_range())
return range_list
def decode_octet(self):
try:
t = struct.unpack('!L', self.decode_buf[:4])
l = t[0]
self.decode_buf = self.decode_buf[4:]
padding = 4 - (l % 4)
buf = self.decode_buf[:l]
self.decode_buf = self.decode_buf[l + padding:]
return buf
except Exception as e:
logger.exception('Invalid packing octet header')
def decode_value(self):
try:
vtype, _ = struct.unpack('!HH', self.decode_buf[:4])
self.decode_buf = self.decode_buf[4:]
except Exception as e:
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
]:
data = struct.unpack('!L', self.decode_buf[:4])
data = data[0]
self.decode_buf = self.decode_buf[4:]
elif vtype in [pyagentx.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]:
data, _ = self.decode_oid()
elif vtype in [
pyagentx.TYPE_IPADDRESS, pyagentx.TYPE_OPAQUE,
pyagentx.TYPE_OCTETSTRING
]:
data = self.decode_octet()
elif vtype in [
pyagentx.TYPE_NULL, pyagentx.TYPE_NOSUCHOBJECT,
pyagentx.TYPE_NOSUCHINSTANCE, pyagentx.TYPE_ENDOFMIBVIEW
]:
# No data
data = None
else:
logger.error('Unknown Type: %s' % vtype)
return {'type': vtype, 'name': oid, 'data': data}
def decode_header(self):
try:
t = struct.unpack('!BBBBLLLL', self.decode_buf[:20])
self.decode_buf = self.decode_buf[20:]
ret = {
'version': t[0],
'pdu_type': t[1],
'pdu_type_name': pyagentx.PDU_TYPE_NAME[t[1]],
'flags': t[2],
'reserved': t[3],
'session_id': t[4],
'transaction_id': t[5],
'packet_id': t[6],
'payload_length': t[7],
}
self.state = ret
self.type = ret['pdu_type']
self.session_id = ret['session_id']
self.packet_id = ret['packet_id']
self.transaction_id = ret['transaction_id']
self.decode_buf = self.decode_buf[:ret['payload_length']]
if ret['flags'] & 0x08: # content present
context = self.decode_octet()
logger.debug('Context: %s' % context)
return ret
except Exception as e:
logger.exception('Invalid packing: %d' % len(self.decode_buf))
logger.debug('%s' % pprint.pformat(self.decode_buf))
def decode(self, buf):
self.set_decode_buf(buf)
ret = self.decode_header()
if ret['pdu_type'] == pyagentx.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]],
'index': t[2],
}
# Decode VarBindList
self.values = []
while len(self.decode_buf):
self.values.append(self.decode_value())
elif ret['pdu_type'] == pyagentx.AGENTX_GET_PDU:
self.range_list = self.decode_search_range_list()
elif ret['pdu_type'] == pyagentx.AGENTX_GETNEXT_PDU:
self.range_list = self.decode_search_range_list()
elif ret['pdu_type'] == pyagentx.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
]:
pass
else:
pdu_type_str = pyagentx.PDU_TYPE_NAME.get(
ret['pdu_type'], 'Unknown:' + str(ret['pdu_type']))
logger.error('Unsupported PDU type:' + pdu_type_str)

67
pyagentx/sethandler.py Normal file
View File

@ -0,0 +1,67 @@
#!/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

137
pyagentx/updater.py Normal file
View File

@ -0,0 +1,137 @@
#!/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
}