Fix memory leak in logging (specifically: do not create a new logger for every SNMP PDU)

This commit is contained in:
Pim van Pelt
2021-09-15 07:58:08 +00:00
parent a574305fb2
commit 18005bbbc2
3 changed files with 50 additions and 47 deletions

View File

@ -17,6 +17,8 @@ class NullHandler(logging.Handler):
def emit(self, record): def emit(self, record):
pass pass
logger = logging.getLogger('agentx.network')
logger.addHandler(NullHandler())
class NetworkError(Exception): class NetworkError(Exception):
pass pass
@ -34,8 +36,6 @@ class Network():
self._connected = False self._connected = False
self._server_address = server_address self._server_address = server_address
self._timeout = 0.1 # Seconds self._timeout = 0.1 # Seconds
self.logger = logging.getLogger('agentx.network')
self.logger.addHandler(NullHandler())
def connect(self): def connect(self):
@ -43,7 +43,7 @@ class Network():
return return
try: try:
self.logger.info("Connecting to %s" % self._server_address) logger.info("Connecting to %s" % self._server_address)
if self._server_address.startswith('/'): if self._server_address.startswith('/'):
self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
self.socket.connect(self._server_address) self.socket.connect(self._server_address)
@ -55,13 +55,13 @@ class Network():
self.socket.settimeout(self._timeout) self.socket.settimeout(self._timeout)
self._connected = True self._connected = True
except socket.error: except socket.error:
self.logger.error("Failed to connect to %s" % self._server_address) logger.error("Failed to connect to %s" % self._server_address)
self._connected = False self._connected = False
def disconnect(self): def disconnect(self):
if not self._connected: if not self._connected:
return return
self.logger.info("Disconnecting from %s" % self._server_address) logger.info("Disconnecting from %s" % self._server_address)
self.socket.close() self.socket.close()
self.socket = None self.socket = None
self._connected = False self._connected = False
@ -69,10 +69,13 @@ class Network():
def update(self, newdata): def update(self, newdata):
if len(self.data) == 0: if len(self.data) == 0:
self.logger.info("Setting initial serving dataset (%d OIDs)" % len(newdata)) logger.info("Setting initial serving dataset (%d OIDs)" % len(newdata))
else: else:
self.logger.info("Replacing serving dataset (%d OIDs)" % len(newdata)) logger.info("Replacing serving dataset (%d OIDs)" % len(newdata))
self.data = newdata del self.data
self.data = newdata.copy()
del self.data_idx
self.data_idx = sorted(self.data.keys(), key=lambda k: tuple(int(part) for part in k.split('.'))) self.data_idx = sorted(self.data.keys(), key=lambda k: tuple(int(part) for part in k.split('.')))
def new_pdu(self, type): def new_pdu(self, type):
@ -106,7 +109,7 @@ class Network():
def _get_next_oid(self, oid, endoid): def _get_next_oid(self, oid, endoid):
if oid in self.data: if oid in self.data:
# Exact match found # Exact match found
#self.logger.debug('get_next_oid, exact match of %s' % oid) #logger.debug('get_next_oid, exact match of %s' % oid)
idx = self.data_idx.index(oid) idx = self.data_idx.index(oid)
if idx == (len(self.data_idx) - 1): if idx == (len(self.data_idx) - 1):
# Last Item in MIB, No match! # Last Item in MIB, No match!
@ -114,7 +117,7 @@ class Network():
return self.data_idx[idx + 1] return self.data_idx[idx + 1]
else: else:
# No exact match, find prefix # No exact match, find prefix
#self.logger.debug('get_next_oid, no exact match of %s' % oid) #logger.debug('get_next_oid, no exact match of %s' % oid)
slist = oid.split('.') slist = oid.split('.')
elist = endoid.split('.') elist = endoid.split('.')
for tmp_oid in self.data_idx: for tmp_oid in self.data_idx:
@ -136,20 +139,20 @@ class Network():
if not self._connected: if not self._connected:
return return
self.logger.debug("==== Open PDU ====") logger.debug("==== Open PDU ====")
pdu = self.new_pdu(agentx.AGENTX_OPEN_PDU) pdu = self.new_pdu(agentx.AGENTX_OPEN_PDU)
self.send_pdu(pdu) self.send_pdu(pdu)
pdu = self.recv_pdu() pdu = self.recv_pdu()
self.session_id = pdu.session_id self.session_id = pdu.session_id
self.logger.debug("==== Ping PDU ====") logger.debug("==== Ping PDU ====")
pdu = self.new_pdu(agentx.AGENTX_PING_PDU) pdu = self.new_pdu(agentx.AGENTX_PING_PDU)
self.send_pdu(pdu) self.send_pdu(pdu)
pdu = self.recv_pdu() pdu = self.recv_pdu()
self.logger.debug("==== Register PDU ====") logger.debug("==== Register PDU ====")
for oid in oid_list: for oid in oid_list:
self.logger.info("Registering: %s" % (oid)) logger.info("Registering: %s" % (oid))
pdu = self.new_pdu(agentx.AGENTX_REGISTER_PDU) pdu = self.new_pdu(agentx.AGENTX_REGISTER_PDU)
pdu.oid = oid pdu.oid = oid
self.send_pdu(pdu) self.send_pdu(pdu)
@ -177,21 +180,21 @@ class Network():
return return
if not request: if not request:
self.logger.error("Empty PDU, connection closed!") logger.error("Empty PDU, connection closed!")
self.disconnect() self.disconnect()
raise NetworkError("Empty PDU, disconnecting") raise NetworkError("Empty PDU, disconnecting")
response = self.response_pdu(request) response = self.response_pdu(request)
if request.type == agentx.AGENTX_GET_PDU: if request.type == agentx.AGENTX_GET_PDU:
self.logger.debug("Received GET PDU") logger.debug("Received GET PDU")
for rvalue in request.range_list: for rvalue in request.range_list:
oid = rvalue[0] oid = rvalue[0]
self.logger.debug("OID: %s" % (oid)) logger.debug("OID: %s" % (oid))
if oid in self.data: if oid in self.data:
self.logger.debug("OID Found") logger.debug("OID Found")
response.values.append(self.data[oid]) response.values.append(self.data[oid])
else: else:
self.logger.debug("OID Not Found!") logger.debug("OID Not Found!")
response.values.append({ response.values.append({
'type': agentx.TYPE_NOSUCHOBJECT, 'type': agentx.TYPE_NOSUCHOBJECT,
'name': rvalue[0], 'name': rvalue[0],
@ -199,10 +202,10 @@ class Network():
}) })
elif request.type == agentx.AGENTX_GETNEXT_PDU: elif request.type == agentx.AGENTX_GETNEXT_PDU:
self.logger.debug("Received GET_NEXT PDU") logger.debug("Received GET_NEXT PDU")
for rvalue in request.range_list: for rvalue in request.range_list:
oid = self._get_next_oid(rvalue[0], rvalue[1]) oid = self._get_next_oid(rvalue[0], rvalue[1])
self.logger.debug("GET_NEXT: %s => %s" % (rvalue[0], oid)) logger.debug("GET_NEXT: %s => %s" % (rvalue[0], oid))
if oid: if oid:
response.values.append(self.data[oid]) response.values.append(self.data[oid])
else: else:
@ -213,6 +216,6 @@ class Network():
}) })
else: else:
self.logger.warn("Received unsupported PDU %d" % request.type) logger.warn("Received unsupported PDU %d" % request.type)
self.send_pdu(response) self.send_pdu(response)

View File

@ -15,6 +15,8 @@ import agentx
class NullHandler(logging.Handler): class NullHandler(logging.Handler):
def emit(self, record): def emit(self, record):
pass pass
logger = logging.getLogger('agentx.pdu')
logger.addHandler(NullHandler())
class PDU(object): class PDU(object):
@ -28,25 +30,23 @@ class PDU(object):
self.decode_buf = '' self.decode_buf = ''
self.state = {} self.state = {}
self.values = [] self.values = []
self.logger = logging.getLogger('agentx.pdu')
self.logger.addHandler(NullHandler())
def dump(self): def dump(self):
name = agentx.PDU_TYPE_NAME[self.type] name = agentx.PDU_TYPE_NAME[self.type]
self.logger.debug('PDU DUMP: New PDU') logger.debug('PDU DUMP: New PDU')
self.logger.debug( logger.debug(
'PDU DUMP: Meta : [%s: %d %d %d]' % 'PDU DUMP: Meta : [%s: %d %d %d]' %
(name, self.session_id, self.transaction_id, self.packet_id)) (name, self.session_id, self.transaction_id, self.packet_id))
if 'payload_length' in self.state: if 'payload_length' in self.state:
self.logger.debug('PDU DUMP: Length : %s' % logger.debug('PDU DUMP: Length : %s' %
self.state['payload_length']) self.state['payload_length'])
if hasattr(self, 'response'): if hasattr(self, 'response'):
self.logger.debug('PDU DUMP: Response : %s' % self.response) logger.debug('PDU DUMP: Response : %s' % self.response)
if hasattr(self, 'values'): if hasattr(self, 'values'):
self.logger.debug('PDU DUMP: Values : %s' % logger.debug('PDU DUMP: Values : %s' %
pprint.pformat(self.values)) pprint.pformat(self.values))
if hasattr(self, 'range_list'): if hasattr(self, 'range_list'):
self.logger.debug('PDU DUMP: Range list: %s' % logger.debug('PDU DUMP: Range list: %s' %
pprint.pformat(self.range_list)) pprint.pformat(self.range_list))
# ==================================================== # ====================================================
@ -102,7 +102,7 @@ class PDU(object):
# No data # No data
pass pass
else: else:
self.logger.error('Unknown Type:' % type) logger.error('Unknown Type:' % type)
return buf return buf
def encode_header(self, pdu_type, payload_length=0, flags=0): def encode_header(self, pdu_type, payload_length=0, flags=0):
@ -175,8 +175,8 @@ class PDU(object):
oid = '.'.join(str(i) for i in sub_ids) oid = '.'.join(str(i) for i in sub_ids)
return oid, ret['include'] return oid, ret['include']
except Exception as e: except Exception as e:
self.logger.exception('Invalid packing OID header') logger.exception('Invalid packing OID header')
self.logger.debug('%s' % pprint.pformat(self.decode_buf)) logger.debug('%s' % pprint.pformat(self.decode_buf))
def decode_search_range(self): def decode_search_range(self):
start_oid, include = self.decode_oid() start_oid, include = self.decode_oid()
@ -201,14 +201,14 @@ class PDU(object):
self.decode_buf = self.decode_buf[l + padding:] self.decode_buf = self.decode_buf[l + padding:]
return buf return buf
except Exception as e: except Exception as e:
self.logger.exception('Invalid packing octet header') logger.exception('Invalid packing octet header')
def decode_value(self): def decode_value(self):
try: try:
vtype, _ = struct.unpack('!HH', self.decode_buf[:4]) vtype, _ = struct.unpack('!HH', self.decode_buf[:4])
self.decode_buf = self.decode_buf[4:] self.decode_buf = self.decode_buf[4:]
except Exception as e: except Exception as e:
self.logger.exception('Invalid packing value header') logger.exception('Invalid packing value header')
oid, _ = self.decode_oid() oid, _ = self.decode_oid()
if vtype in [ if vtype in [
agentx.TYPE_INTEGER, agentx.TYPE_COUNTER32, agentx.TYPE_INTEGER, agentx.TYPE_COUNTER32,
@ -235,7 +235,7 @@ class PDU(object):
# No data # No data
data = None data = None
else: else:
self.logger.error('Unknown Type: %s' % vtype) logger.error('Unknown Type: %s' % vtype)
return {'type': vtype, 'name': oid, 'data': data} return {'type': vtype, 'name': oid, 'data': data}
def decode_header(self): def decode_header(self):
@ -261,11 +261,11 @@ class PDU(object):
self.decode_buf = self.decode_buf[:ret['payload_length']] self.decode_buf = self.decode_buf[:ret['payload_length']]
if ret['flags'] & 0x08: # content present if ret['flags'] & 0x08: # content present
context = self.decode_octet() context = self.decode_octet()
self.logger.debug('Context: %s' % context) logger.debug('Context: %s' % context)
return ret return ret
except Exception as e: except Exception as e:
self.logger.exception('Invalid packing: %d' % len(self.decode_buf)) logger.exception('Invalid packing: %d' % len(self.decode_buf))
self.logger.debug('%s' % pprint.pformat(self.decode_buf)) logger.debug('%s' % pprint.pformat(self.decode_buf))
def decode(self, buf): def decode(self, buf):
self.set_decode_buf(buf) self.set_decode_buf(buf)
@ -304,4 +304,4 @@ class PDU(object):
else: else:
pdu_type_str = agentx.PDU_TYPE_NAME.get( pdu_type_str = agentx.PDU_TYPE_NAME.get(
ret['pdu_type'], 'Unknown:' + str(ret['pdu_type'])) ret['pdu_type'], 'Unknown:' + str(ret['pdu_type']))
self.logger.error('Unsupported PDU type:' + pdu_type_str) logger.error('Unsupported PDU type:' + pdu_type_str)

View File

@ -12,14 +12,14 @@ import logging
class NullHandler(logging.Handler): class NullHandler(logging.Handler):
def emit(self, record): def emit(self, record):
pass pass
logger = logging.getLogger('agentx.vppapi')
logger.addHandler(NullHandler())
class VPPApi(): class VPPApi():
def __init__(self, address='/run/vpp/api.sock', clientname='vppapi-client'): def __init__(self, address='/run/vpp/api.sock', clientname='vppapi-client'):
self.address = address self.address = address
self.connected = False self.connected = False
self.logger = logging.getLogger('agentx.vppapi')
self.logger.addHandler(NullHandler())
self.clientname = clientname self.clientname = clientname
self.vpp = None self.vpp = None
@ -36,19 +36,19 @@ class VPPApi():
jsonfiles.append(os.path.join(root, filename)) jsonfiles.append(os.path.join(root, filename))
if not jsonfiles: if not jsonfiles:
self.logger.error('no json api files found') logger.error('no json api files found')
return False return False
self.vpp = VPPApiClient(apifiles=jsonfiles, self.vpp = VPPApiClient(apifiles=jsonfiles,
server_address=self.address) server_address=self.address)
try: try:
self.logger.info('Connecting to VPP') logger.info('Connecting to VPP')
self.vpp.connect(self.clientname) self.vpp.connect(self.clientname)
except: except:
return False return False
v = self.vpp.api.show_version() v = self.vpp.api.show_version()
self.logger.info('VPP version is %s' % v.version) logger.info('VPP version is %s' % v.version)
self.connected = True self.connected = True
return True return True
@ -68,13 +68,13 @@ class VPPApi():
try: try:
iface_list = self.vpp.api.sw_interface_dump() iface_list = self.vpp.api.sw_interface_dump()
except Exception as e: except Exception as e:
self.logger.error("VPP communication error, disconnecting", e) logger.error("VPP communication error, disconnecting", e)
self.vpp.disconnect() self.vpp.disconnect()
self.connected = False self.connected = False
return ret return ret
if not iface_list: if not iface_list:
self.logger.error("Can't get interface list") logger.error("Can't get interface list")
return ret return ret
for iface in iface_list: for iface in iface_list: