#!/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)