Replace the pyagentx threaded version with a much simpler, non-threaded version.
This commit is contained in:
152
agentx/__init__.py
Normal file
152
agentx/__init__.py
Normal file
@ -0,0 +1,152 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
)
|
||||
|
||||
import logging
|
||||
|
||||
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('agentx')
|
||||
logger.setLevel(level)
|
||||
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
|
||||
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"
|
89
agentx/agent.py
Normal file
89
agentx/agent.py
Normal file
@ -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)
|
57
agentx/dataset.py
Normal file
57
agentx/dataset.py
Normal file
@ -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
|
||||
}
|
220
agentx/network.py
Normal file
220
agentx/network.py
Normal file
@ -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)
|
313
agentx/pdu.py
Normal file
313
agentx/pdu.py
Normal 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('agentx.pdu')
|
||||
logger.addHandler(NullHandler())
|
||||
# --------------------------------------------
|
||||
|
||||
import struct
|
||||
import pprint
|
||||
|
||||
import agentx
|
||||
|
||||
|
||||
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 = agentx.ERROR_NOAGENTXERROR
|
||||
self.error_index = 0
|
||||
self.decode_buf = ''
|
||||
self.state = {}
|
||||
self.values = []
|
||||
|
||||
def dump(self):
|
||||
name = agentx.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 [agentx.TYPE_INTEGER]:
|
||||
buf += struct.pack('!l', value)
|
||||
elif type in [
|
||||
agentx.TYPE_COUNTER32, agentx.TYPE_GAUGE32,
|
||||
agentx.TYPE_TIMETICKS
|
||||
]:
|
||||
buf += struct.pack('!L', value)
|
||||
elif type in [agentx.TYPE_COUNTER64]:
|
||||
buf += struct.pack('!Q', value)
|
||||
elif type in [agentx.TYPE_OBJECTIDENTIFIER]:
|
||||
buf += self.encode_oid(value)
|
||||
elif type in [
|
||||
agentx.TYPE_IPADDRESS, agentx.TYPE_OPAQUE,
|
||||
agentx.TYPE_OCTETSTRING
|
||||
]:
|
||||
buf += self.encode_octet(value)
|
||||
elif type in [
|
||||
agentx.TYPE_NULL, agentx.TYPE_NOSUCHOBJECT,
|
||||
agentx.TYPE_NOSUCHINSTANCE, agentx.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 == agentx.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 == agentx.AGENTX_PING_PDU:
|
||||
# No extra data
|
||||
pass
|
||||
|
||||
elif self.type == agentx.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 == 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'],
|
||||
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 [
|
||||
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 [agentx.TYPE_COUNTER64]:
|
||||
data = struct.unpack('!Q', self.decode_buf[:8])
|
||||
data = data[0]
|
||||
self.decode_buf = self.decode_buf[8:]
|
||||
elif vtype in [agentx.TYPE_OBJECTIDENTIFIER]:
|
||||
data, _ = self.decode_oid()
|
||||
elif vtype in [
|
||||
agentx.TYPE_IPADDRESS, agentx.TYPE_OPAQUE,
|
||||
agentx.TYPE_OCTETSTRING
|
||||
]:
|
||||
data = self.decode_octet()
|
||||
elif vtype in [
|
||||
agentx.TYPE_NULL, agentx.TYPE_NOSUCHOBJECT,
|
||||
agentx.TYPE_NOSUCHINSTANCE, agentx.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': agentx.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'] == 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': agentx.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'] == agentx.AGENTX_GET_PDU:
|
||||
self.range_list = self.decode_search_range_list()
|
||||
|
||||
elif ret['pdu_type'] == agentx.AGENTX_GETNEXT_PDU:
|
||||
self.range_list = self.decode_search_range_list()
|
||||
|
||||
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 [
|
||||
agentx.AGENTX_COMMITSET_PDU, agentx.AGENTX_UNDOSET_PDU,
|
||||
agentx.AGENTX_CLEANUPSET_PDU
|
||||
]:
|
||||
pass
|
||||
else:
|
||||
pdu_type_str = agentx.PDU_TYPE_NAME.get(
|
||||
ret['pdu_type'], 'Unknown:' + str(ret['pdu_type']))
|
||||
logger.error('Unsupported PDU type:' + pdu_type_str)
|
Reference in New Issue
Block a user