From 740d78d61c17a4b0a9e998dec5bbd6014d4d7a65 Mon Sep 17 00:00:00 2001 From: Dmitri Budko Date: Tue, 31 Jul 2018 18:10:25 +0300 Subject: [PATCH 1/9] Update setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 144f2f7..23dab73 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ setup( name = "pynetsnmp", - version = "0.41.1", + version = "0.42.0dev", packages=find_packages(), install_requires = [ 'setuptools', From b6eb6b6ccab508e9ee68f3a407a317659b49fa93 Mon Sep 17 00:00:00 2001 From: Jason Peacock Date: Thu, 1 Sep 2022 10:04:45 -0500 Subject: [PATCH 2/9] Reformat code using the 'Black' code formatter --- pynetsnmp/CONSTANTS.py | 324 ++++++++++---------- pynetsnmp/SnmpSession.py | 29 +- pynetsnmp/genconstants.py | 63 ++-- pynetsnmp/netsnmp.py | 593 +++++++++++++++++++++--------------- pynetsnmp/tableretriever.py | 40 ++- pynetsnmp/twistedsnmp.py | 208 +++++++------ 6 files changed, 714 insertions(+), 543 deletions(-) diff --git a/pynetsnmp/CONSTANTS.py b/pynetsnmp/CONSTANTS.py index c6e12e6..61d699f 100644 --- a/pynetsnmp/CONSTANTS.py +++ b/pynetsnmp/CONSTANTS.py @@ -20,49 +20,49 @@ MIN_OID_LEN = 2 MAX_OID_LEN = 128 MAX_NAME_LEN = MAX_OID_LEN -ASN_BOOLEAN = (0x01) -ASN_INTEGER = (0x02) -ASN_BIT_STR = (0x03) -ASN_OCTET_STR = (0x04) -ASN_NULL = (0x05) -ASN_OBJECT_ID = (0x06) -ASN_SEQUENCE = (0x10) -ASN_SET = (0x11) -ASN_UNIVERSAL = (0x00) -ASN_APPLICATION = (0x40) -ASN_CONTEXT = (0x80) -ASN_PRIVATE = (0xC0) -ASN_PRIMITIVE = (0x00) -ASN_CONSTRUCTOR = (0x20) -ASN_LONG_LEN = (0x80) -ASN_EXTENSION_ID = (0x1F) -ASN_BIT8 = (0x80) -ASN_OPAQUE_TAG1 = (ASN_CONTEXT | ASN_EXTENSION_ID) -ASN_OPAQUE_TAG2 = (0x30) -ASN_OPAQUE_TAG2U = (0x2f) -ASN_APP_OPAQUE = (ASN_APPLICATION | 4) -ASN_APP_COUNTER64 = (ASN_APPLICATION | 6) -ASN_APP_FLOAT = (ASN_APPLICATION | 8) -ASN_APP_DOUBLE = (ASN_APPLICATION | 9) -ASN_APP_I64 = (ASN_APPLICATION | 10) -ASN_APP_U64 = (ASN_APPLICATION | 11) -ASN_APP_UNION = (ASN_PRIVATE | 1) -ASN_OPAQUE_COUNTER64 = (ASN_OPAQUE_TAG2 + ASN_APP_COUNTER64) +ASN_BOOLEAN = 0x01 +ASN_INTEGER = 0x02 +ASN_BIT_STR = 0x03 +ASN_OCTET_STR = 0x04 +ASN_NULL = 0x05 +ASN_OBJECT_ID = 0x06 +ASN_SEQUENCE = 0x10 +ASN_SET = 0x11 +ASN_UNIVERSAL = 0x00 +ASN_APPLICATION = 0x40 +ASN_CONTEXT = 0x80 +ASN_PRIVATE = 0xC0 +ASN_PRIMITIVE = 0x00 +ASN_CONSTRUCTOR = 0x20 +ASN_LONG_LEN = 0x80 +ASN_EXTENSION_ID = 0x1F +ASN_BIT8 = 0x80 +ASN_OPAQUE_TAG1 = ASN_CONTEXT | ASN_EXTENSION_ID +ASN_OPAQUE_TAG2 = 0x30 +ASN_OPAQUE_TAG2U = 0x2F +ASN_APP_OPAQUE = ASN_APPLICATION | 4 +ASN_APP_COUNTER64 = ASN_APPLICATION | 6 +ASN_APP_FLOAT = ASN_APPLICATION | 8 +ASN_APP_DOUBLE = ASN_APPLICATION | 9 +ASN_APP_I64 = ASN_APPLICATION | 10 +ASN_APP_U64 = ASN_APPLICATION | 11 +ASN_APP_UNION = ASN_PRIVATE | 1 +ASN_OPAQUE_COUNTER64 = ASN_OPAQUE_TAG2 + ASN_APP_COUNTER64 ASN_OPAQUE_COUNTER64_MX_BER_LEN = 12 -ASN_OPAQUE_FLOAT = (ASN_OPAQUE_TAG2 + ASN_APP_FLOAT) +ASN_OPAQUE_FLOAT = ASN_OPAQUE_TAG2 + ASN_APP_FLOAT ASN_OPAQUE_FLOAT_BER_LEN = 7 -ASN_OPAQUE_DOUBLE = (ASN_OPAQUE_TAG2 + ASN_APP_DOUBLE) +ASN_OPAQUE_DOUBLE = ASN_OPAQUE_TAG2 + ASN_APP_DOUBLE ASN_OPAQUE_DOUBLE_BER_LEN = 11 -ASN_OPAQUE_I64 = (ASN_OPAQUE_TAG2 + ASN_APP_I64) +ASN_OPAQUE_I64 = ASN_OPAQUE_TAG2 + ASN_APP_I64 ASN_OPAQUE_I64_MX_BER_LEN = 11 -ASN_OPAQUE_U64 = (ASN_OPAQUE_TAG2 + ASN_APP_U64) +ASN_OPAQUE_U64 = ASN_OPAQUE_TAG2 + ASN_APP_U64 ASN_OPAQUE_U64_MX_BER_LEN = 12 -ASN_PRIV_INCL_RANGE = (ASN_PRIVATE | 2) -ASN_PRIV_EXCL_RANGE = (ASN_PRIVATE | 3) -ASN_PRIV_DELEGATED = (ASN_PRIVATE | 5) -ASN_PRIV_IMPLIED_OCTET_STR = (ASN_PRIVATE | ASN_OCTET_STR) -ASN_PRIV_IMPLIED_OBJECT_ID = (ASN_PRIVATE | ASN_OBJECT_ID) -ASN_PRIV_RETRY = (ASN_PRIVATE | 7) +ASN_PRIV_INCL_RANGE = ASN_PRIVATE | 2 +ASN_PRIV_EXCL_RANGE = ASN_PRIVATE | 3 +ASN_PRIV_DELEGATED = ASN_PRIVATE | 5 +ASN_PRIV_IMPLIED_OCTET_STR = ASN_PRIVATE | ASN_OCTET_STR +ASN_PRIV_IMPLIED_OBJECT_ID = ASN_PRIVATE | ASN_OBJECT_ID +ASN_PRIV_RETRY = ASN_PRIVATE | 7 SNMP_PORT = 161 SNMP_TRAP_PORT = 162 SNMP_MAX_LEN = 1500 @@ -74,15 +74,15 @@ SNMP_VERSION_sec = 128 SNMP_VERSION_2p = 129 SNMP_VERSION_2star = 130 -SNMP_MSG_GET = (ASN_CONTEXT | ASN_CONSTRUCTOR | 0x0) -SNMP_MSG_GETNEXT = (ASN_CONTEXT | ASN_CONSTRUCTOR | 0x1) -SNMP_MSG_RESPONSE = (ASN_CONTEXT | ASN_CONSTRUCTOR | 0x2) -SNMP_MSG_SET = (ASN_CONTEXT | ASN_CONSTRUCTOR | 0x3) -SNMP_MSG_TRAP = (ASN_CONTEXT | ASN_CONSTRUCTOR | 0x4) -SNMP_MSG_GETBULK = (ASN_CONTEXT | ASN_CONSTRUCTOR | 0x5) -SNMP_MSG_INFORM = (ASN_CONTEXT | ASN_CONSTRUCTOR | 0x6) -SNMP_MSG_TRAP2 = (ASN_CONTEXT | ASN_CONSTRUCTOR | 0x7) -SNMP_MSG_REPORT = (ASN_CONTEXT | ASN_CONSTRUCTOR | 0x8) +SNMP_MSG_GET = ASN_CONTEXT | ASN_CONSTRUCTOR | 0x0 +SNMP_MSG_GETNEXT = ASN_CONTEXT | ASN_CONSTRUCTOR | 0x1 +SNMP_MSG_RESPONSE = ASN_CONTEXT | ASN_CONSTRUCTOR | 0x2 +SNMP_MSG_SET = ASN_CONTEXT | ASN_CONSTRUCTOR | 0x3 +SNMP_MSG_TRAP = ASN_CONTEXT | ASN_CONSTRUCTOR | 0x4 +SNMP_MSG_GETBULK = ASN_CONTEXT | ASN_CONSTRUCTOR | 0x5 +SNMP_MSG_INFORM = ASN_CONTEXT | ASN_CONSTRUCTOR | 0x6 +SNMP_MSG_TRAP2 = ASN_CONTEXT | ASN_CONSTRUCTOR | 0x7 +SNMP_MSG_REPORT = ASN_CONTEXT | ASN_CONSTRUCTOR | 0x8 SNMP_MSG_INTERNAL_SET_BEGIN = -1 SNMP_MSG_INTERNAL_SET_RESERVE1 = 0 SNMP_MSG_INTERNAL_SET_RESERVE2 = 1 @@ -105,36 +105,36 @@ SNMP_MSG_INTERNAL_OBJECT_LOOKUP = 129 SNMP_MSG_INTERNAL_POST_REQUEST = 130 SNMP_MSG_INTERNAL_GET_STASH = 131 -SNMP_NOSUCHOBJECT = (ASN_CONTEXT | ASN_PRIMITIVE | 0x0) -SNMP_NOSUCHINSTANCE = (ASN_CONTEXT | ASN_PRIMITIVE | 0x1) -SNMP_ENDOFMIBVIEW = (ASN_CONTEXT | ASN_PRIMITIVE | 0x2) -SNMP_ERR_NOERROR = (0) -SNMP_ERR_TOOBIG = (1) -SNMP_ERR_NOSUCHNAME = (2) -SNMP_ERR_BADVALUE = (3) -SNMP_ERR_READONLY = (4) -SNMP_ERR_GENERR = (5) -SNMP_ERR_NOACCESS = (6) -SNMP_ERR_WRONGTYPE = (7) -SNMP_ERR_WRONGLENGTH = (8) -SNMP_ERR_WRONGENCODING = (9) -SNMP_ERR_WRONGVALUE = (10) -SNMP_ERR_NOCREATION = (11) -SNMP_ERR_INCONSISTENTVALUE = (12) -SNMP_ERR_RESOURCEUNAVAILABLE = (13) -SNMP_ERR_COMMITFAILED = (14) -SNMP_ERR_UNDOFAILED = (15) -SNMP_ERR_AUTHORIZATIONERROR = (16) -SNMP_ERR_NOTWRITABLE = (17) -SNMP_ERR_INCONSISTENTNAME = (18) +SNMP_NOSUCHOBJECT = ASN_CONTEXT | ASN_PRIMITIVE | 0x0 +SNMP_NOSUCHINSTANCE = ASN_CONTEXT | ASN_PRIMITIVE | 0x1 +SNMP_ENDOFMIBVIEW = ASN_CONTEXT | ASN_PRIMITIVE | 0x2 +SNMP_ERR_NOERROR = 0 +SNMP_ERR_TOOBIG = 1 +SNMP_ERR_NOSUCHNAME = 2 +SNMP_ERR_BADVALUE = 3 +SNMP_ERR_READONLY = 4 +SNMP_ERR_GENERR = 5 +SNMP_ERR_NOACCESS = 6 +SNMP_ERR_WRONGTYPE = 7 +SNMP_ERR_WRONGLENGTH = 8 +SNMP_ERR_WRONGENCODING = 9 +SNMP_ERR_WRONGVALUE = 10 +SNMP_ERR_NOCREATION = 11 +SNMP_ERR_INCONSISTENTVALUE = 12 +SNMP_ERR_RESOURCEUNAVAILABLE = 13 +SNMP_ERR_COMMITFAILED = 14 +SNMP_ERR_UNDOFAILED = 15 +SNMP_ERR_AUTHORIZATIONERROR = 16 +SNMP_ERR_NOTWRITABLE = 17 +SNMP_ERR_INCONSISTENTNAME = 18 MAX_SNMP_ERR = 18 -SNMP_TRAP_COLDSTART = (0) -SNMP_TRAP_WARMSTART = (1) -SNMP_TRAP_LINKDOWN = (2) -SNMP_TRAP_LINKUP = (3) -SNMP_TRAP_AUTHFAIL = (4) -SNMP_TRAP_EGPNEIGHBORLOSS = (5) -SNMP_TRAP_ENTERPRISESPECIFIC = (6) +SNMP_TRAP_COLDSTART = 0 +SNMP_TRAP_WARMSTART = 1 +SNMP_TRAP_LINKDOWN = 2 +SNMP_TRAP_LINKUP = 3 +SNMP_TRAP_AUTHFAIL = 4 +SNMP_TRAP_EGPNEIGHBORLOSS = 5 +SNMP_TRAP_ENTERPRISESPECIFIC = 6 SNMP_ROW_NONEXISTENT = 0 SNMP_ROW_ACTIVE = 1 SNMP_ROW_NOTINSERVICE = 2 @@ -198,7 +198,7 @@ SNMP_DEFAULT_AUTH_PROTOLEN = USM_LENGTH_OID_TRANSFORM SNMP_DEFAULT_PRIV_PROTOLEN = USM_LENGTH_OID_TRANSFORM SNMP_MAX_MSG_SIZE = 1472 -SNMP_MAX_MSG_V3_HDRS = (4+3+4+7+7+3+7+16) +SNMP_MAX_MSG_V3_HDRS = 4 + 3 + 4 + 7 + 7 + 3 + 7 + 16 SNMP_MAX_ENG_SIZE = 32 SNMP_MAX_SEC_NAME_SIZE = 256 SNMP_MAX_CONTEXT_SIZE = 256 @@ -230,77 +230,77 @@ SNMP_FLAGS_SUBSESSION = 0x20 SNMP_FLAGS_STRIKE2 = 0x02 SNMP_FLAGS_STRIKE1 = 0x01 -SNMPERR_SUCCESS = (0) -SNMPERR_GENERR = (-1) -SNMPERR_BAD_LOCPORT = (-2) -SNMPERR_BAD_ADDRESS = (-3) -SNMPERR_BAD_SESSION = (-4) -SNMPERR_TOO_LONG = (-5) -SNMPERR_NO_SOCKET = (-6) -SNMPERR_V2_IN_V1 = (-7) -SNMPERR_V1_IN_V2 = (-8) -SNMPERR_BAD_REPEATERS = (-9) -SNMPERR_BAD_REPETITIONS = (-10) -SNMPERR_BAD_ASN1_BUILD = (-11) -SNMPERR_BAD_SENDTO = (-12) -SNMPERR_BAD_PARSE = (-13) -SNMPERR_BAD_VERSION = (-14) -SNMPERR_BAD_SRC_PARTY = (-15) -SNMPERR_BAD_DST_PARTY = (-16) -SNMPERR_BAD_CONTEXT = (-17) -SNMPERR_BAD_COMMUNITY = (-18) -SNMPERR_NOAUTH_DESPRIV = (-19) -SNMPERR_BAD_ACL = (-20) -SNMPERR_BAD_PARTY = (-21) -SNMPERR_ABORT = (-22) -SNMPERR_UNKNOWN_PDU = (-23) -SNMPERR_TIMEOUT = (-24) -SNMPERR_BAD_RECVFROM = (-25) -SNMPERR_BAD_ENG_ID = (-26) -SNMPERR_BAD_SEC_NAME = (-27) -SNMPERR_BAD_SEC_LEVEL = (-28) -SNMPERR_ASN_PARSE_ERR = (-29) -SNMPERR_UNKNOWN_SEC_MODEL = (-30) -SNMPERR_INVALID_MSG = (-31) -SNMPERR_UNKNOWN_ENG_ID = (-32) -SNMPERR_UNKNOWN_USER_NAME = (-33) -SNMPERR_UNSUPPORTED_SEC_LEVEL = (-34) -SNMPERR_AUTHENTICATION_FAILURE = (-35) -SNMPERR_NOT_IN_TIME_WINDOW = (-36) -SNMPERR_DECRYPTION_ERR = (-37) -SNMPERR_SC_GENERAL_FAILURE = (-38) -SNMPERR_SC_NOT_CONFIGURED = (-39) -SNMPERR_KT_NOT_AVAILABLE = (-40) -SNMPERR_UNKNOWN_REPORT = (-41) -SNMPERR_USM_GENERICERROR = (-42) -SNMPERR_USM_UNKNOWNSECURITYNAME = (-43) -SNMPERR_USM_UNSUPPORTEDSECURITYLEVEL = (-44) -SNMPERR_USM_ENCRYPTIONERROR = (-45) -SNMPERR_USM_AUTHENTICATIONFAILURE = (-46) -SNMPERR_USM_PARSEERROR = (-47) -SNMPERR_USM_UNKNOWNENGINEID = (-48) -SNMPERR_USM_NOTINTIMEWINDOW = (-49) -SNMPERR_USM_DECRYPTIONERROR = (-50) -SNMPERR_NOMIB = (-51) -SNMPERR_RANGE = (-52) -SNMPERR_MAX_SUBID = (-53) -SNMPERR_BAD_SUBID = (-54) -SNMPERR_LONG_OID = (-55) -SNMPERR_BAD_NAME = (-56) -SNMPERR_VALUE = (-57) -SNMPERR_UNKNOWN_OBJID = (-58) -SNMPERR_NULL_PDU = (-59) -SNMPERR_NO_VARS = (-60) -SNMPERR_VAR_TYPE = (-61) -SNMPERR_MALLOC = (-62) -SNMPERR_KRB5 = (-63) -SNMPERR_PROTOCOL = (-64) -SNMPERR_OID_NONINCREASING = (-65) -SNMPERR_JUST_A_CONTEXT_PROBE = (-66) -SNMPERR_TRANSPORT_NO_CONFIG = (-67) -SNMPERR_TRANSPORT_CONFIG_ERROR = (-68) -SNMPERR_TLS_NO_CERTIFICATE = (-69) -SNMPERR_MAX = (-69) +SNMPERR_SUCCESS = 0 +SNMPERR_GENERR = -1 +SNMPERR_BAD_LOCPORT = -2 +SNMPERR_BAD_ADDRESS = -3 +SNMPERR_BAD_SESSION = -4 +SNMPERR_TOO_LONG = -5 +SNMPERR_NO_SOCKET = -6 +SNMPERR_V2_IN_V1 = -7 +SNMPERR_V1_IN_V2 = -8 +SNMPERR_BAD_REPEATERS = -9 +SNMPERR_BAD_REPETITIONS = -10 +SNMPERR_BAD_ASN1_BUILD = -11 +SNMPERR_BAD_SENDTO = -12 +SNMPERR_BAD_PARSE = -13 +SNMPERR_BAD_VERSION = -14 +SNMPERR_BAD_SRC_PARTY = -15 +SNMPERR_BAD_DST_PARTY = -16 +SNMPERR_BAD_CONTEXT = -17 +SNMPERR_BAD_COMMUNITY = -18 +SNMPERR_NOAUTH_DESPRIV = -19 +SNMPERR_BAD_ACL = -20 +SNMPERR_BAD_PARTY = -21 +SNMPERR_ABORT = -22 +SNMPERR_UNKNOWN_PDU = -23 +SNMPERR_TIMEOUT = -24 +SNMPERR_BAD_RECVFROM = -25 +SNMPERR_BAD_ENG_ID = -26 +SNMPERR_BAD_SEC_NAME = -27 +SNMPERR_BAD_SEC_LEVEL = -28 +SNMPERR_ASN_PARSE_ERR = -29 +SNMPERR_UNKNOWN_SEC_MODEL = -30 +SNMPERR_INVALID_MSG = -31 +SNMPERR_UNKNOWN_ENG_ID = -32 +SNMPERR_UNKNOWN_USER_NAME = -33 +SNMPERR_UNSUPPORTED_SEC_LEVEL = -34 +SNMPERR_AUTHENTICATION_FAILURE = -35 +SNMPERR_NOT_IN_TIME_WINDOW = -36 +SNMPERR_DECRYPTION_ERR = -37 +SNMPERR_SC_GENERAL_FAILURE = -38 +SNMPERR_SC_NOT_CONFIGURED = -39 +SNMPERR_KT_NOT_AVAILABLE = -40 +SNMPERR_UNKNOWN_REPORT = -41 +SNMPERR_USM_GENERICERROR = -42 +SNMPERR_USM_UNKNOWNSECURITYNAME = -43 +SNMPERR_USM_UNSUPPORTEDSECURITYLEVEL = -44 +SNMPERR_USM_ENCRYPTIONERROR = -45 +SNMPERR_USM_AUTHENTICATIONFAILURE = -46 +SNMPERR_USM_PARSEERROR = -47 +SNMPERR_USM_UNKNOWNENGINEID = -48 +SNMPERR_USM_NOTINTIMEWINDOW = -49 +SNMPERR_USM_DECRYPTIONERROR = -50 +SNMPERR_NOMIB = -51 +SNMPERR_RANGE = -52 +SNMPERR_MAX_SUBID = -53 +SNMPERR_BAD_SUBID = -54 +SNMPERR_LONG_OID = -55 +SNMPERR_BAD_NAME = -56 +SNMPERR_VALUE = -57 +SNMPERR_UNKNOWN_OBJID = -58 +SNMPERR_NULL_PDU = -59 +SNMPERR_NO_VARS = -60 +SNMPERR_VAR_TYPE = -61 +SNMPERR_MALLOC = -62 +SNMPERR_KRB5 = -63 +SNMPERR_PROTOCOL = -64 +SNMPERR_OID_NONINCREASING = -65 +SNMPERR_JUST_A_CONTEXT_PROBE = -66 +SNMPERR_TRANSPORT_NO_CONFIG = -67 +SNMPERR_TRANSPORT_CONFIG_ERROR = -68 +SNMPERR_TLS_NO_CERTIFICATE = -69 +SNMPERR_MAX = -69 NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE = 1 NETSNMP_CALLBACK_OP_TIMED_OUT = 2 NETSNMP_CALLBACK_OP_SEND_FAILED = 3 @@ -373,7 +373,7 @@ STAT_TLSTM_SNMPTLSTMSESSIONINVALIDCACHES = 56 STAT_TLSTM_STATS_START = STAT_TLSTM_SNMPTLSTMSESSIONOPENS STAT_TLSTM_STATS_END = STAT_TLSTM_SNMPTLSTMSESSIONINVALIDCACHES -NETSNMP_STAT_MAX_STATS = (STAT_TLSTM_STATS_END+1) +NETSNMP_STAT_MAX_STATS = STAT_TLSTM_STATS_END + 1 MAX_STATS = NETSNMP_STAT_MAX_STATS COMMUNITY_MAX_LEN = 256 SPRINT_MAX_LEN = 2560 @@ -396,19 +396,19 @@ RONLY = NETSNMP_OLDAPI_RONLY RWRITE = NETSNMP_OLDAPI_RWRITE NOACCESS = NETSNMP_OLDAPI_NOACCESS -ASN_IPADDRESS = (ASN_APPLICATION | 0) -ASN_COUNTER = (ASN_APPLICATION | 1) -ASN_GAUGE = (ASN_APPLICATION | 2) -ASN_UNSIGNED = (ASN_APPLICATION | 2) -ASN_TIMETICKS = (ASN_APPLICATION | 3) -ASN_OPAQUE = (ASN_APPLICATION | 4) -ASN_NSAP = (ASN_APPLICATION | 5) -ASN_COUNTER64 = (ASN_APPLICATION | 6) -ASN_UINTEGER = (ASN_APPLICATION | 7) -ASN_FLOAT = (ASN_APPLICATION | 8) -ASN_DOUBLE = (ASN_APPLICATION | 9) -ASN_INTEGER64 = (ASN_APPLICATION | 10) -ASN_UNSIGNED64 = (ASN_APPLICATION | 11) +ASN_IPADDRESS = ASN_APPLICATION | 0 +ASN_COUNTER = ASN_APPLICATION | 1 +ASN_GAUGE = ASN_APPLICATION | 2 +ASN_UNSIGNED = ASN_APPLICATION | 2 +ASN_TIMETICKS = ASN_APPLICATION | 3 +ASN_OPAQUE = ASN_APPLICATION | 4 +ASN_NSAP = ASN_APPLICATION | 5 +ASN_COUNTER64 = ASN_APPLICATION | 6 +ASN_UINTEGER = ASN_APPLICATION | 7 +ASN_FLOAT = ASN_APPLICATION | 8 +ASN_DOUBLE = ASN_APPLICATION | 9 +ASN_INTEGER64 = ASN_APPLICATION | 10 +ASN_UNSIGNED64 = ASN_APPLICATION | 11 FIRST_PASS = 1 LAST_PASS = 2 LOG_EMERG = 0 diff --git a/pynetsnmp/SnmpSession.py b/pynetsnmp/SnmpSession.py index 21eae1a..3571818 100644 --- a/pynetsnmp/SnmpSession.py +++ b/pynetsnmp/SnmpSession.py @@ -1,10 +1,12 @@ +from __future__ import print_function + __doc__ = "Backwards compatible API for SnmpSession" import netsnmp from ctypes import * + class SnmpSession(object): - def __init__(self, ip, port=161, timeout=2, retries=2, cmdLineArgs=()): self.ip = ip self.port = port @@ -15,7 +17,7 @@ def __init__(self, ip, port=161, timeout=2, retries=2, cmdLineArgs=()): self.cmdLineArgs = cmdLineArgs def setVersion(self, version): - if version.find('2') >= 0: + if version.find("2") >= 0: self._version = netsnmp.SNMP_VERSION_2c else: self._version = netsnmp.SNMP_VERSION_1 @@ -25,22 +27,23 @@ def get(self, oid): self.session = netsnmp.Session( version=self._version, timeout=self.timeout, - retries=int(self.retries-1), - peername= '%s:%d' % (self.ip, self.port), + retries=int(self.retries - 1), + peername="%s:%d" % (self.ip, self.port), community=self.community, community_len=len(self.community), - cmdLineArgs=self.cmdLineArgs - ) - oid = tuple(map(int, oid.strip('.').split('.'))) + cmdLineArgs=self.cmdLineArgs, + ) + oid = tuple(map(int, oid.strip(".").split("."))) self.session.open() try: return self.session.sget([oid]) finally: self.session.close() -if __name__ == '__main__': - session = SnmpSession('127.0.0.1', timeout=1.5, port=161) - session.community = 'public' - print session.get('.1.3.6.1.2.1.1.5.0') - session.community = 'xyzzy' - print session.get('.1.3.6.1.2.1.1.5.0') + +if __name__ == "__main__": + session = SnmpSession("127.0.0.1", timeout=1.5, port=161) + session.community = "public" + print(session.get(".1.3.6.1.2.1.1.5.0")) + session.community = "xyzzy" + print(session.get(".1.3.6.1.2.1.1.5.0")) diff --git a/pynetsnmp/genconstants.py b/pynetsnmp/genconstants.py index b018223..49bac84 100644 --- a/pynetsnmp/genconstants.py +++ b/pynetsnmp/genconstants.py @@ -5,35 +5,35 @@ import logging logging.basicConfig() -log = logging.getLogger('zen.genconstants') +log = logging.getLogger("zen.genconstants") global_vars = {} local_vars = {} + def write_output(f, name, value): - assignment = '%s = %s\n' % (name, value) + assignment = "%s = %s\n" % (name, value) try: exec(assignment, global_vars, local_vars) f.write(assignment) - except Exception, e: - log.error('Invalid python statement: %s, %s', assignment.strip(), e) + except Exception as e: + log.error("Invalid python statement: %s, %s", assignment.strip(), e) + def process(f, output): - lines = open('/usr/include/net-snmp/%s' % f).readlines() - sp = '[ \t]*' - comment = re.compile('/\\*(.*\\*/|[^*]*$)') - define = re.compile(sp.join(['^', - '#', - 'define', - '([A-Za-z0-9_]+)', - '([^\\\\]+)$'])) - junk = ['usm', '(x)', 'sizeof', '(string)', '(byte)', '{', '?', 'err'] + lines = open("/usr/include/net-snmp/%s" % f).readlines() + sp = "[ \t]*" + comment = re.compile("/\\*(.*\\*/|[^*]*$)") + define = re.compile( + sp.join(["^", "#", "define", "([A-Za-z0-9_]+)", "([^\\\\]+)$"]) + ) + junk = ["usm", "(x)", "sizeof", "(string)", "(byte)", "{", "?", "err"] for line in lines: - line = comment.sub('', line) + line = comment.sub("", line) m = define.match(line) if m: value = m.group(2).strip() - value = value.replace('(u_char)', '') + value = value.replace("(u_char)", "") if value: for j in junk: if value.find(j) > -1: @@ -41,23 +41,34 @@ def process(f, output): else: write_output(output, m.group(1), value) + def make_imports(): try: - out = open('CONSTANTS.py.new', 'w') - write_output(out, 'USM_LENGTH_OID_TRANSFORM', '10') - write_output(out, 'NULL', 'None') + out = open("CONSTANTS.py.new", "w") + write_output(out, "USM_LENGTH_OID_TRANSFORM", "10") + write_output(out, "NULL", "None") paths = [] - paths.extend('library/' + x for x in - ('callback.h','asn1.h','snmp.h','snmp_api.h','snmp_impl.h','snmp_logging.h','default_store.h')) - paths.append('types.h') + paths.extend( + "library/" + x + for x in ( + "callback.h", + "asn1.h", + "snmp.h", + "snmp_api.h", + "snmp_impl.h", + "snmp_logging.h", + "default_store.h", + ) + ) + paths.append("types.h") for path in paths: process(path, out) out.close() - os.rename('CONSTANTS.py.new', 'CONSTANTS.py') - except IOError: # file not found, prolly + os.rename("CONSTANTS.py.new", "CONSTANTS.py") + except IOError: # file not found, prolly pass -if __name__=='__main__': - make_imports() - from CONSTANTS import * # check the result +if __name__ == "__main__": + make_imports() + from CONSTANTS import * # check the result diff --git a/pynetsnmp/netsnmp.py b/pynetsnmp/netsnmp.py index 1e9a262..2c7ece9 100644 --- a/pynetsnmp/netsnmp.py +++ b/pynetsnmp/netsnmp.py @@ -13,39 +13,47 @@ def _getLogger(name): return logging.getLogger("zen.pynetsnmp.%s" % name) -if sys.platform.find('free') > -1: +if sys.platform.find("free") > -1: find_library_orig = find_library + def find_library(name): - for name in ['/usr/lib/lib%s.so' % name, - '/usr/local/lib/lib%s.so' % name]: + for name in [ + "/usr/lib/lib%s.so" % name, + "/usr/local/lib/lib%s.so" % name, + ]: if os.path.exists(name): return name return find_library_orig(name) + find_library_orig = find_library + + def find_library(name): if sys.platform == "darwin": - libPath = os.environ.get('DYLD_LIBRARY_PATH', '') + libPath = os.environ.get("DYLD_LIBRARY_PATH", "") else: - libPath = os.environ.get('LD_LIBRARY_PATH', '') - libPathList = libPath.split(':') + libPath = os.environ.get("LD_LIBRARY_PATH", "") + libPathList = libPath.split(":") for path in libPathList: - pathName = path+'/lib%s.so' % name + pathName = path + "/lib%s.so" % name if os.path.exists(pathName): return pathName return find_library_orig(name) + c_int_p = c_void_p authenticator = CFUNCTYPE(c_char_p, c_int_p, c_char_p, c_int) try: # needed by newer netsnmp's - crypto = CDLL(find_library('crypto'), RTLD_GLOBAL) + crypto = CDLL(find_library("crypto"), RTLD_GLOBAL) except Exception: import warnings + warnings.warn("Unable to load crypto library") -lib = CDLL(find_library('netsnmp'), RTLD_GLOBAL) +lib = CDLL(find_library("netsnmp"), RTLD_GLOBAL) lib.netsnmp_get_version.restype = c_char_p oid = c_long @@ -56,200 +64,224 @@ def find_library(name): size_t = c_size_t u_char = c_byte -class netsnmp_session(Structure): pass -class netsnmp_pdu(Structure): pass -class netsnmp_transport(Structure): pass + +class netsnmp_session(Structure): + pass + + +class netsnmp_pdu(Structure): + pass + + +class netsnmp_transport(Structure): + pass + + # int (*netsnmp_callback) (int, netsnmp_session *, int, netsnmp_pdu *, void *); -netsnmp_callback = CFUNCTYPE(c_int, - c_int, POINTER(netsnmp_session), - c_int, POINTER(netsnmp_pdu), - c_void_p) +netsnmp_callback = CFUNCTYPE( + c_int, + c_int, + POINTER(netsnmp_session), + c_int, + POINTER(netsnmp_pdu), + c_void_p, +) # int (*proc)(int, char * const *, int) -arg_parse_proc = CFUNCTYPE(c_int, POINTER(c_char_p), c_int); +arg_parse_proc = CFUNCTYPE(c_int, POINTER(c_char_p), c_int) version = lib.netsnmp_get_version() -float_version = float('.'.join(version.split('.')[:2])) -_netsnmp_str_version = tuple(str(v) for v in version.split('.')) +float_version = float(".".join(version.split(".")[:2])) +_netsnmp_str_version = tuple(str(v) for v in version.split(".")) localname = [] paramName = [] transportConfig = [] if float_version < 5.099: raise ImportError("netsnmp version 5.1 or greater is required") if float_version > 5.199: - localname = [('localname', c_char_p)] + localname = [("localname", c_char_p)] if float_version > 5.299: - paramName = [('paramName', c_char_p)] -if _netsnmp_str_version >= ('5','6'): + paramName = [("paramName", c_char_p)] +if _netsnmp_str_version >= ("5", "6"): # Versions >= 5.6 and < 5.6.1.1 broke binary compatibility and changed oid type from c_long to c_uint32. This works # around the issue for these platforms to allow things to work properly. - if _netsnmp_str_version <= ('5','6','1','1'): + if _netsnmp_str_version <= ("5", "6", "1", "1"): oid = c_uint32 # Versions >= 5.6 broke binary compatibility by adding transport specific configuration - class netsnmp_container_s(Structure): pass - transportConfig = [('transport_configuration', POINTER(netsnmp_container_s))] + class netsnmp_container_s(Structure): + pass + + transportConfig = [ + ("transport_configuration", POINTER(netsnmp_container_s)) + ] SNMP_VERSION_MAP = { - 'v1': SNMP_VERSION_1, - 'v2c': SNMP_VERSION_2c, - 'v2u': SNMP_VERSION_2u, - 'v3': SNMP_VERSION_3, - 'sec': SNMP_VERSION_sec, - '2p': SNMP_VERSION_2p, - '2star': SNMP_VERSION_2star, + "v1": SNMP_VERSION_1, + "v2c": SNMP_VERSION_2c, + "v2u": SNMP_VERSION_2u, + "v3": SNMP_VERSION_3, + "sec": SNMP_VERSION_sec, + "2p": SNMP_VERSION_2p, + "2star": SNMP_VERSION_2star, } # Version -netsnmp_session._fields_ = [ - ('version', c_long), - ('retries', c_int), - ('timeout', c_long), - ('flags', u_long), - ('subsession', POINTER(netsnmp_session)), - ('next', POINTER(netsnmp_session)), - ('peername', c_char_p), - ('remote_port', u_short), ] + localname + [ - ('local_port', u_short), - ('authenticator', authenticator), - ('callback', netsnmp_callback), - ('callback_magic', c_void_p), - ('s_errno', c_int), - ('s_snmp_errno', c_int), - ('sessid', c_long), - ('community', u_char_p), - ('community_len', size_t), - ('rcvMsgMaxSize', size_t), - ('sndMsgMaxSize', size_t), - - ('isAuthoritative', u_char), - ('contextEngineID', u_char_p), - ('contextEngineIDLen', size_t), - ('engineBoots', u_int), - ('engineTime', u_int), - ('contextName', c_char_p), - ('contextNameLen', size_t), - ('securityEngineID', u_char_p), - ('securityEngineIDLen', size_t), - ('securityName', c_char_p), - ('securityNameLen', size_t), - - ('securityAuthProto', POINTER(oid)), - ('securityAuthProtoLen', size_t), - ('securityAuthKey', u_char * USM_AUTH_KU_LEN), - ('securityAuthKeyLen', c_size_t), - ('securityAuthLocalKey', c_char_p), - ('securityAuthLocalKeyLen', c_size_t), - - ('securityPrivProto', POINTER(oid)), - ('securityPrivProtoLen', c_size_t), - ('securityPrivKey', c_char * USM_PRIV_KU_LEN), - ('securityPrivKeyLen', c_size_t), - ('securityPrivLocalKey', c_char_p), - ('securityPrivLocalKeyLen', c_size_t), - - ('securityModel', c_int), - ('securityLevel', c_int), - - ] + paramName + [ - - ('securityInfo', c_void_p), - - ] + transportConfig + [ - - ('myvoid', c_void_p), - ] - +netsnmp_session._fields_ = ( + [ + ("version", c_long), + ("retries", c_int), + ("timeout", c_long), + ("flags", u_long), + ("subsession", POINTER(netsnmp_session)), + ("next", POINTER(netsnmp_session)), + ("peername", c_char_p), + ("remote_port", u_short), + ] + + localname + + [ + ("local_port", u_short), + ("authenticator", authenticator), + ("callback", netsnmp_callback), + ("callback_magic", c_void_p), + ("s_errno", c_int), + ("s_snmp_errno", c_int), + ("sessid", c_long), + ("community", u_char_p), + ("community_len", size_t), + ("rcvMsgMaxSize", size_t), + ("sndMsgMaxSize", size_t), + ("isAuthoritative", u_char), + ("contextEngineID", u_char_p), + ("contextEngineIDLen", size_t), + ("engineBoots", u_int), + ("engineTime", u_int), + ("contextName", c_char_p), + ("contextNameLen", size_t), + ("securityEngineID", u_char_p), + ("securityEngineIDLen", size_t), + ("securityName", c_char_p), + ("securityNameLen", size_t), + ("securityAuthProto", POINTER(oid)), + ("securityAuthProtoLen", size_t), + ("securityAuthKey", u_char * USM_AUTH_KU_LEN), + ("securityAuthKeyLen", c_size_t), + ("securityAuthLocalKey", c_char_p), + ("securityAuthLocalKeyLen", c_size_t), + ("securityPrivProto", POINTER(oid)), + ("securityPrivProtoLen", c_size_t), + ("securityPrivKey", c_char * USM_PRIV_KU_LEN), + ("securityPrivKeyLen", c_size_t), + ("securityPrivLocalKey", c_char_p), + ("securityPrivLocalKeyLen", c_size_t), + ("securityModel", c_int), + ("securityLevel", c_int), + ] + + paramName + + [ + ("securityInfo", c_void_p), + ] + + transportConfig + + [ + ("myvoid", c_void_p), + ] +) dataFreeHook = CFUNCTYPE(c_void_p) + class counter64(Structure): _fields_ = [ - ('high', c_ulong), - ('low', c_ulong), - ] + ("high", c_ulong), + ("low", c_ulong), + ] + class netsnmp_vardata(Union): _fields_ = [ - ('integer', POINTER(c_long)), - ('uinteger', POINTER(c_ulong)), - ('string', c_char_p), - ('objid', POINTER(oid)), - ('bitstring', POINTER(c_ubyte)), - ('counter64', POINTER(counter64)), - ('floatVal', POINTER(c_float)), - ('doubleVal', POINTER(c_double)), - ] + ("integer", POINTER(c_long)), + ("uinteger", POINTER(c_ulong)), + ("string", c_char_p), + ("objid", POINTER(oid)), + ("bitstring", POINTER(c_ubyte)), + ("counter64", POINTER(counter64)), + ("floatVal", POINTER(c_float)), + ("doubleVal", POINTER(c_double)), + ] + class netsnmp_variable_list(Structure): pass + + netsnmp_variable_list._fields_ = [ - ('next_variable', POINTER(netsnmp_variable_list)), - ('name', POINTER(oid)), - ('name_length', c_size_t), - ('type', c_char), - ('val', netsnmp_vardata), - ('val_len', c_size_t), - ('name_loc', oid * MAX_OID_LEN), - ('buf', c_char * 40), - ('data', c_void_p), - ('dataFreeHook', dataFreeHook), - ('index', c_int), - ] + ("next_variable", POINTER(netsnmp_variable_list)), + ("name", POINTER(oid)), + ("name_length", c_size_t), + ("type", c_char), + ("val", netsnmp_vardata), + ("val_len", c_size_t), + ("name_loc", oid * MAX_OID_LEN), + ("buf", c_char * 40), + ("data", c_void_p), + ("dataFreeHook", dataFreeHook), + ("index", c_int), +] netsnmp_pdu._fields_ = [ - ('version', c_long ), - ('command', c_int ), - ('reqid', c_long ), - ('msgid', c_long ), - ('transid', c_long ), - ('sessid', c_long ), - ('errstat', c_long ), - ('errindex', c_long ), - ('time', c_ulong ), - ('flags', c_ulong ), - ('securityModel', c_int ), - ('securityLevel', c_int ), - ('msgParseModel', c_int ), - ('transport_data', c_void_p), - ('transport_data_length', c_int ), - ('tDomain', POINTER(oid)), - ('tDomainLen', c_size_t ), - ('variables', POINTER(netsnmp_variable_list)), - ('community', c_char_p), - ('community_len', c_size_t ), - ('enterprise', POINTER(oid)), - ('enterprise_length', c_size_t ), - ('trap_type', c_long ), - ('specific_type', c_long ), - ('agent_addr', c_ubyte * 4), - ('contextEngineID', c_char_p ), - ('contextEngineIDLen', c_size_t ), - ('contextName', c_char_p), - ('contextNameLen', c_size_t ), - ('securityEngineID', c_char_p), - ('securityEngineIDLen', c_size_t ), - ('securityName', c_char_p), - ('securityNameLen', c_size_t ), - ('priority', c_int ), - ('range_subid', c_int ), - ('securityStateRef', c_void_p), - ] + ("version", c_long), + ("command", c_int), + ("reqid", c_long), + ("msgid", c_long), + ("transid", c_long), + ("sessid", c_long), + ("errstat", c_long), + ("errindex", c_long), + ("time", c_ulong), + ("flags", c_ulong), + ("securityModel", c_int), + ("securityLevel", c_int), + ("msgParseModel", c_int), + ("transport_data", c_void_p), + ("transport_data_length", c_int), + ("tDomain", POINTER(oid)), + ("tDomainLen", c_size_t), + ("variables", POINTER(netsnmp_variable_list)), + ("community", c_char_p), + ("community_len", c_size_t), + ("enterprise", POINTER(oid)), + ("enterprise_length", c_size_t), + ("trap_type", c_long), + ("specific_type", c_long), + ("agent_addr", c_ubyte * 4), + ("contextEngineID", c_char_p), + ("contextEngineIDLen", c_size_t), + ("contextName", c_char_p), + ("contextNameLen", c_size_t), + ("securityEngineID", c_char_p), + ("securityEngineIDLen", c_size_t), + ("securityName", c_char_p), + ("securityNameLen", c_size_t), + ("priority", c_int), + ("range_subid", c_int), + ("securityStateRef", c_void_p), +] netsnmp_pdu_p = POINTER(netsnmp_pdu) # Redirect netsnmp logging to our log -class netsnmp_log_message(Structure): pass +class netsnmp_log_message(Structure): + pass + + netsnmp_log_message_p = POINTER(netsnmp_log_message) -log_callback = CFUNCTYPE(c_int, c_int, - netsnmp_log_message_p, - c_void_p); +log_callback = CFUNCTYPE(c_int, c_int, netsnmp_log_message_p, c_void_p) netsnmp_log_message._fields_ = [ - ('priority', c_int), - ('msg', c_char_p), + ("priority", c_int), + ("msg", c_char_p), ] PRIORITY_MAP = { LOG_EMERG: logging.CRITICAL, @@ -261,44 +293,52 @@ class netsnmp_log_message(Structure): pass LOG_INFO: logging.INFO, LOG_DEBUG: logging.DEBUG, } + + def netsnmp_logger(a, b, msg): msg = cast(msg, netsnmp_log_message_p) priority = PRIORITY_MAP.get(msg.contents.priority, logging.DEBUG) _getLogger("netsnmp").log(priority, str(msg.contents.msg).strip()) return 0 + + netsnmp_logger = log_callback(netsnmp_logger) -lib.snmp_register_callback(SNMP_CALLBACK_LIBRARY, - SNMP_CALLBACK_LOGGING, - netsnmp_logger, - 0) +lib.snmp_register_callback( + SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_LOGGING, netsnmp_logger, 0 +) lib.netsnmp_register_loghandler(NETSNMP_LOGHANDLER_CALLBACK, LOG_DEBUG) lib.snmp_pdu_create.restype = netsnmp_pdu_p lib.snmp_open.restype = POINTER(netsnmp_session) netsnmp_transport._fields_ = [ - ('domain', POINTER(oid)), - ('domain_length', c_int), - ('local', u_char_p), - ('local_length', c_int), - ('remote', u_char_p), - ('remote_length', c_int), - ('sock', c_int), - ('flags', u_int), - ('data', c_void_p), - ('data_length', c_int), - ('msgMaxSize', c_size_t), - ('f_recv', c_void_p), - ('f_send', c_void_p), - ('f_close', c_void_p), - ('f_accept', c_void_p), - ('f_fmtaddr', c_void_p), + ("domain", POINTER(oid)), + ("domain_length", c_int), + ("local", u_char_p), + ("local_length", c_int), + ("remote", u_char_p), + ("remote_length", c_int), + ("sock", c_int), + ("flags", u_int), + ("data", c_void_p), + ("data_length", c_int), + ("msgMaxSize", c_size_t), + ("f_recv", c_void_p), + ("f_send", c_void_p), + ("f_close", c_void_p), + ("f_accept", c_void_p), + ("f_fmtaddr", c_void_p), ] lib.netsnmp_tdomain_transport.restype = POINTER(netsnmp_transport) lib.snmp_add.restype = POINTER(netsnmp_session) lib.snmp_add_var.argtypes = [ - netsnmp_pdu_p, POINTER(oid), c_size_t, c_char, c_char_p] + netsnmp_pdu_p, + POINTER(oid), + c_size_t, + c_char, + c_char_p, +] lib.get_uptime.restype = c_long @@ -306,12 +346,9 @@ def netsnmp_logger(a, b, msg): lib.snmp_send.restype = c_int # int snmp_input(int, netsnmp_session *, int, netsnmp_pdu *, void *); -snmp_input_t = CFUNCTYPE(c_int, - c_int, - POINTER(netsnmp_session), - c_int, - netsnmp_pdu_p, - c_void_p) +snmp_input_t = CFUNCTYPE( + c_int, c_int, POINTER(netsnmp_session), c_int, netsnmp_pdu_p, c_void_p +) # A pointer to a _CallbackData struct is used for the callback_magic @@ -339,31 +376,48 @@ class _CallbackData(Structure): class UnknownType(Exception): pass + def mkoid(n): oids = (oid * len(n))() for i, v in enumerate(n): oids[i] = v return oids + def strToOid(oidStr): - return mkoid(tuple([int(x) for x in oidStr.strip('.').split('.')])) + return mkoid(tuple([int(x) for x in oidStr.strip(".").split(".")])) + def decodeOid(pdu): - return tuple([pdu.val.objid[i] for i in range(pdu.val_len / sizeof(u_long))]) + return tuple( + [pdu.val.objid[i] for i in range(pdu.val_len / sizeof(u_long))] + ) + def decodeIp(pdu): - return '.'.join(map(str, pdu.val.bitstring[:4])) + return ".".join(map(str, pdu.val.bitstring[:4])) + def decodeBigInt(pdu): int64 = pdu.val.counter64.contents - return (int64.high << 32L) + int64.low + return (int64.high << 32) + int64.low + def decodeString(pdu): if pdu.val_len: return string_at(pdu.val.bitstring, pdu.val_len) - return '' + return "" + -_valueToConstant = dict([(chr(getattr(CONSTANTS, k)), k) for k in CONSTANTS.__dict__.keys() if isinstance(getattr(CONSTANTS,k), int) and getattr(CONSTANTS,k)>=0 and getattr(CONSTANTS,k) < 256]) +_valueToConstant = dict( + [ + (chr(getattr(CONSTANTS, k)), k) + for k in CONSTANTS.__dict__.keys() + if isinstance(getattr(CONSTANTS, k), int) + and getattr(CONSTANTS, k) >= 0 + and getattr(CONSTANTS, k) < 256 + ] +) decoder = { @@ -380,7 +434,8 @@ def decodeString(pdu): chr(ASN_COUNTER64): decodeBigInt, chr(ASN_APP_FLOAT): lambda pdu: pdu.val.float.contents.value, chr(ASN_APP_DOUBLE): lambda pdu: pdu.val.double.contents.value, - } +} + def decodeType(var, log): oid = [var.name[i] for i in range(var.name_length)] @@ -388,8 +443,11 @@ def decodeType(var, log): if not decode: # raise UnknownType(oid, ord(var.type)) log_oid = ".".join(map(str, oid)) - log.debug("No decoder for oid %s type %s - returning None", log_oid, - _valueToConstant.get(var.type, var.type)) + log.debug( + "No decoder for oid %s type %s - returning None", + log_oid, + _valueToConstant.get(var.type, var.type), + ) return (oid, None) return oid, decode(var) @@ -400,20 +458,24 @@ def getResult(pdu, log): while var: var = var.contents oid, val = decodeType(var, log) - result.append( (tuple(oid), val) ) + result.append((tuple(oid), val)) var = var.next_variable return result -class SnmpError(Exception): +class SnmpError(Exception): def __init__(self, why): lib.snmp_perror(why) Exception.__init__(self, why) + class SnmpTimeoutError(Exception): pass + sessionMap = {} + + def _callback(operation, sp, reqid, pdu, magic): data_ptr = cast(magic, _CallbackDataPtr) sess = sessionMap[data_ptr.contents.session_id] @@ -424,36 +486,48 @@ def _callback(operation, sp, reqid, pdu, magic): sess.timeout(reqid) else: _getLogger("callback").error("Unknown operation: %d", operation) - except Exception, ex: + except Exception as ex: _getLogger("callback").exception("Exception in _callback %s", ex) return 1 + + _callback = netsnmp_callback(_callback) + class ArgumentParseError(Exception): pass + class TransportError(Exception): pass + def _doNothingProc(argc, argv, arg): return 0 + + _doNothingProc = arg_parse_proc(_doNothingProc) + def parse_args(args, session): - args = [sys.argv[0],] + args + args = [ + sys.argv[0], + ] + args argc = len(args) argv = (c_char_p * argc)() for i in range(argc): # snmp_parse_args mutates argv, so create a copy argv[i] = create_string_buffer(args[i]).raw # WARNING: Usage of snmp_parse_args call causes memory leak. - if lib.snmp_parse_args(argc, argv, session, '', _doNothingProc) < 0: - raise ArgumentParseError("Unable to parse arguments", ' '.join(argv)) + if lib.snmp_parse_args(argc, argv, session, "", _doNothingProc) < 0: + raise ArgumentParseError("Unable to parse arguments", " ".join(argv)) # keep a reference to the args for as long as sess is alive return argv + _NoAttribute = object() + def initialize_session(sess, cmdLineArgs, kw): args = None kw = kw.copy() @@ -465,9 +539,9 @@ def initialize_session(sess, cmdLineArgs, kw): result.append(opt) result.append(val) cmdLine = result - if kw.get('peername'): - cmdLine.append(kw['peername']) - del kw['peername'] + if kw.get("peername"): + cmdLine.append(kw["peername"]) + del kw["peername"] args = parse_args(cmdLine, byref(sess)) else: lib.snmp_sess_init(byref(sess)) @@ -496,11 +570,12 @@ def initialize_session(sess, cmdLineArgs, kw): setattr(sess, attr, value) return args + class Session(object): cb = None - def __init__(self, cmdLineArgs = (), **kw): + def __init__(self, cmdLineArgs=(), **kw): self.cmdLineArgs = cmdLineArgs self.kw = kw self.sess = None @@ -519,14 +594,18 @@ def open(self): ref = byref(sess) self.sess = lib.snmp_open(ref) if not self.sess: - raise SnmpError('snmp_open') + raise SnmpError("snmp_open") - def awaitTraps(self, peername, fileno = -1, pre_parse_callback=None, debug=False): + def awaitTraps( + self, peername, fileno=-1, pre_parse_callback=None, debug=False + ): if float_version > 5.299: - lib.netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_APPTYPE, "pynetsnmp") + lib.netsnmp_ds_set_string( + NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_APPTYPE, "pynetsnmp" + ) lib.init_usm() if debug: - lib.debug_register_tokens("snmp_parse") # or "ALL" for everything + lib.debug_register_tokens("snmp_parse") # or "ALL" for everything lib.snmp_set_do_debugging(1) lib.netsnmp_udp_ctor() marker = object() @@ -535,12 +614,18 @@ def awaitTraps(self, peername, fileno = -1, pre_parse_callback=None, debug=False elif getattr(lib, "netsnmp_udp6_ctor", marker) is not marker: lib.netsnmp_udp6_ctor() else: - self._log.debug("Cannot find constructor function for UDP/IPv6 transport domain object.") + self._log.debug( + "Cannot find constructor function for UDP/IPv6 transport domain object." + ) lib.init_snmp("zenoss_app") lib.setup_engineID(None, None) transport = lib.netsnmp_tdomain_transport(peername, 1, "udp") if not transport: - raise SnmpError("Unable to create transport {peername}".format(peername=peername)) + raise SnmpError( + "Unable to create transport {peername}".format( + peername=peername + ) + ) if fileno >= 0: os.dup2(fileno, transport.contents.sock) sess = netsnmp_session() @@ -563,7 +648,7 @@ def awaitTraps(self, peername, fileno = -1, pre_parse_callback=None, debug=False sess.isAuthoritative = SNMP_SESS_UNKNOWNAUTH rc = lib.snmp_add(self.sess, transport, pre_parse_callback, None) if not rc: - raise SnmpError('snmp_add') + raise SnmpError("snmp_add") def create_users(self, users): self._log.debug("create_users: Creating %s users.", len(users)) @@ -573,24 +658,35 @@ def create_users(self, users): line = "" if user.engine_id: line = "-e {} ".format(user.engine_id) - line += " ".join([user.username, - user.authentication_type, # MD5 or SHA - user.authentication_passphrase, - user.privacy_protocol, # DES or AES - user.privacy_passphrase]) + line += " ".join( + [ + user.username, + user.authentication_type, # MD5 or SHA + user.authentication_passphrase, + user.privacy_protocol, # DES or AES + user.privacy_passphrase, + ] + ) lib.usm_parse_create_usmUser("createUser", line) self._log.debug("create_users: created user: %s", user) - except StandardError, e: - self._log.debug("create_users: could not create user: %s: (%s: %s)", user, e.__class__.__name__, e) + except StandardError as e: + self._log.debug( + "create_users: could not create user: %s: (%s: %s)", + user, + e.__class__.__name__, + e, + ) def sendTrap(self, trapoid, varbinds=None): - if '-v1' in self.cmdLineArgs: + if "-v1" in self.cmdLineArgs: pdu = lib.snmp_pdu_create(SNMP_MSG_TRAP) - if hasattr(self, 'agent_addr'): + if hasattr(self, "agent_addr"): # pdu.contents is a netsnmp_pdu, defined above, therefore its fields are c types # self.agent_addr is an ipv4 address, and the v1 trap wants a c array of 4 unsigned bytes, # so chop it up, make the octets ints, then a bytearray from that will cast. - pdu.contents.agent_addr = (c_ubyte*4)(*(bytearray([int(x) for x in self.agent_addr.split('.')]))) + pdu.contents.agent_addr = (c_ubyte * 4)( + *(bytearray([int(x) for x in self.agent_addr.split(".")])) + ) pdu.contents.trap_type = 6 pdu.contents.specific_type = 0 pdu.contents.time = lib.get_uptime() @@ -599,15 +695,17 @@ def sendTrap(self, trapoid, varbinds=None): pdu = lib.snmp_pdu_create(SNMP_MSG_TRAP2) # sysUpTime is mandatory on V2Traps. - objid_sysuptime = mkoid((1,3,6,1,2,1,1,3,0)) + objid_sysuptime = mkoid((1, 3, 6, 1, 2, 1, 1, 3, 0)) uptime = "%ld" % lib.get_uptime() lib.snmp_add_var( - pdu, objid_sysuptime, len(objid_sysuptime), 't', uptime) + pdu, objid_sysuptime, len(objid_sysuptime), "t", uptime + ) # snmpTrapOID is mandatory on V2Traps. - objid_snmptrap = mkoid((1,3,6,1,6,3,1,1,4,1,0)) + objid_snmptrap = mkoid((1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0)) lib.snmp_add_var( - pdu, objid_snmptrap, len(objid_snmptrap), 'o', trapoid) + pdu, objid_snmptrap, len(objid_snmptrap), "o", trapoid + ) if varbinds: for n, t, v in varbinds: @@ -624,7 +722,9 @@ def close(self): self._data = None if id(self) not in sessionMap: self._log.warn( - "Session ID not found session_id=%s %r", id(self), self.kw, + "Session ID not found session_id=%s %r", + id(self), + self.kw, ) return del sessionMap[id(self)] @@ -650,15 +750,21 @@ def sget(self, oids): lib.snmp_free_pdu(response) return result - def _handle_send_status(self, req, send_status, send_type): if send_status == 0: cliberr = c_int() snmperr = c_int() errstring = c_char_p() - lib.snmp_error(self.sess, byref(cliberr), byref(snmperr), byref(errstring)); + lib.snmp_error( + self.sess, byref(cliberr), byref(snmperr), byref(errstring) + ) msg_fmt = "%s: snmp_send cliberr=%s, snmperr=%s, errstring=%s" - msg_args = (send_type, cliberr.value, snmperr.value, errstring.value) + msg_args = ( + send_type, + cliberr.value, + snmperr.value, + errstring.value, + ) self._log.debug(msg_fmt, *msg_args) lib.snmp_free_pdu(req) if snmperr.value == SNMPERR_TIMEOUT: @@ -671,7 +777,7 @@ def get(self, oids): oid = mkoid(oid) lib.snmp_add_null_var(req, oid, len(oid)) send_status = lib.snmp_send(self.sess, req) - self._handle_send_status(req, send_status, 'get') + self._handle_send_status(req, send_status, "get") return req.contents.reqid def getbulk(self, nonrepeaters, maxrepetitions, oids): @@ -683,7 +789,7 @@ def getbulk(self, nonrepeaters, maxrepetitions, oids): oid = mkoid(oid) lib.snmp_add_null_var(req, oid, len(oid)) send_status = lib.snmp_send(self.sess, req) - self._handle_send_status(req, send_status, 'get') + self._handle_send_status(req, send_status, "get") return req.contents.reqid def walk(self, root): @@ -692,18 +798,20 @@ def walk(self, root): lib.snmp_add_null_var(req, oid, len(oid)) send_status = lib.snmp_send(self.sess, req) self._log.debug("walk: send_status=%s", send_status) - self._handle_send_status(req, send_status, 'walk') + self._handle_send_status(req, send_status, "walk") return req.contents.reqid MAXFD = 1024 -fdset = c_int32 * (MAXFD/32) +fdset = c_int32 * (MAXFD / 32) + class timeval(Structure): _fields_ = [ - ('tv_sec', c_long), - ('tv_usec', c_long), - ] + ("tv_sec", c_long), + ("tv_usec", c_long), + ] + def fdset2list(rd, n): result = [] @@ -715,6 +823,7 @@ def fdset2list(rd, n): result.append(i * 32 + j) return result + def snmp_select_info(): rd = fdset() maxfd = c_int(0) @@ -723,24 +832,26 @@ def snmp_select_info(): timeout.tv_usec = 0 block = c_int(0) maxfd = c_int(MAXFD) - lib.snmp_select_info(byref(maxfd), - byref(rd), - byref(timeout), - byref(block)) + lib.snmp_select_info(byref(maxfd), byref(rd), byref(timeout), byref(block)) t = None if not block: t = timeout.tv_sec + timeout.tv_usec / 1e6 return fdset2list(rd, maxfd.value), t + def snmp_read(fd): rd = fdset() rd[fd / 32] |= 1 << (fd % 32) lib.snmp_read(byref(rd)) + done = False + + def loop(): while not done: from select import select + rd, t = snmp_select_info() if t is None: break @@ -751,7 +862,7 @@ def loop(): else: lib.snmp_timeout() + def stop(): global done done = 1 - diff --git a/pynetsnmp/tableretriever.py b/pynetsnmp/tableretriever.py index bbce369..2c86ff0 100644 --- a/pynetsnmp/tableretriever.py +++ b/pynetsnmp/tableretriever.py @@ -1,8 +1,8 @@ from twisted.internet import defer from twistedsnmp import asOidStr, asOid -class _TableStatus(object): +class _TableStatus(object): def __init__(self, startOidStr): self.startOidStr = startOidStr self.startOid = asOid(startOidStr) @@ -11,22 +11,25 @@ def __init__(self, startOidStr): class TableRetriever(object): - - def __init__(self, - proxy, - oids, - timeout = 1.5, - retryCount = 3, - maxRepetitions = 100, - limit = 1000): + def __init__( + self, + proxy, + oids, + timeout=1.5, + retryCount=3, + maxRepetitions=100, + limit=1000, + ): self.proxy = proxy self.tableStatus = [_TableStatus(oid) for oid in oids] self.defer = defer.Deferred() - if proxy.snmpVersion.find('1') > -1: + if proxy.snmpVersion.find("1") > -1: self.how = proxy._walk else: + def v2v3how(oids): return proxy._getbulk(0, min(maxRepetitions, limit), [oids]) + self.how = v2v3how self.limit = limit self.count = 0 @@ -38,7 +41,8 @@ def __call__(self): def fetchSomeMore(self): for ts in self.tableStatus: - if ts.finished: continue + if ts.finished: + continue if ts.result: lastOid = ts.result[-1][0] else: @@ -49,18 +53,22 @@ def fetchSomeMore(self): return results = {} for ts in self.tableStatus: - results[ts.startOidStr]=dict([(asOidStr(oid), value) for oid, value in ts.result]) + results[ts.startOidStr] = dict( + [(asOidStr(oid), value) for oid, value in ts.result] + ) self.defer.callback(results) self.defer = None - def saveResults(self, values, ts): if values: for oid, value in values: self.count += 1 - if oid[:len(ts.startOid)]==ts.startOid and oid > ts.startOid: + if ( + oid[: len(ts.startOid)] == ts.startOid + and oid > ts.startOid + ): # defend against going backwards - if ts.result and oid<=ts.result[-1][0]: + if ts.result and oid <= ts.result[-1][0]: ts.finished = True else: ts.result.append((oid, value)) @@ -68,7 +76,7 @@ def saveResults(self, values, ts): ts.finished = True else: ts.finished = True - if not ts.finished and self.count >= self.limit: + if not ts.finished and self.count >= self.limit: ts.finished = True self.hit_limit = True self.fetchSomeMore() diff --git a/pynetsnmp/twistedsnmp.py b/pynetsnmp/twistedsnmp.py index f51f39d..a1136be 100644 --- a/pynetsnmp/twistedsnmp.py +++ b/pynetsnmp/twistedsnmp.py @@ -1,20 +1,20 @@ import logging import netsnmp import struct -import sys from CONSTANTS import * from ipaddr import IPAddress from twisted.internet import reactor from twisted.internet.error import TimeoutError -from twisted.internet.interfaces import IReadDescriptor from twisted.python import failure from twisted.internet import defer class Timer(object): callLater = None + + timer = Timer() fdMap = {} @@ -32,13 +32,14 @@ class Timer(object): SNMP_ERR_WRONGVALUE: "Bad value", SNMP_ERR_NOCREATION: "No creation", SNMP_ERR_INCONSISTENTVALUE: "Inconsistent value", - SNMP_ERR_RESOURCEUNAVAILABLE: "Resource unavailable", + SNMP_ERR_RESOURCEUNAVAILABLE: "Resource unavailable", SNMP_ERR_COMMITFAILED: "Commit failed", SNMP_ERR_UNDOFAILED: "Undo failed", SNMP_ERR_AUTHORIZATIONERROR: "Authorization error", SNMP_ERR_NOTWRITABLE: "Not writable", SNMP_ERR_INCONSISTENTNAME: "Inconsistent name", - } +} + def checkTimeouts(): "Handle timeouts for Net-SNMP" @@ -47,11 +48,11 @@ def checkTimeouts(): updateReactor() -class SnmpReader: #(IReadDescriptor): +class SnmpReader: # (IReadDescriptor): "Respond to input events" def logPrefix(self): - return 'SnmpReader' + return "SnmpReader" def __init__(self, fd): self.fd = fd @@ -66,13 +67,14 @@ def fileno(self): def connectionLost(self, why): del fdMap[self.fd] + def updateReactor(): "Add/remove event handlers for SNMP file descriptors and timers" fds, t = netsnmp.snmp_select_info() log = netsnmp._getLogger("updateReactor") if log.getEffectiveLevel() < logging.DEBUG: - log.debug('reactor settings: %r, %r', fds, t) + log.debug("reactor settings: %r, %r", fds, t) for fd in fds: if fd not in fdMap: reader = SnmpReader(fd) @@ -90,19 +92,21 @@ def updateReactor(): if t is not None: timer.callLater = reactor.callLater(t, checkTimeouts) + class SnmpNameError(Exception): def __init__(self, oid): - Exception.__init__(self, 'Bad Name', oid) + Exception.__init__(self, "Bad Name", oid) def asOidStr(oid): """converts an oid int sequence to an oid string""" - return '.'+'.'.join([str(x) for x in oid]) + return "." + ".".join([str(x) for x in oid]) def asOid(oidStr): """converts an OID string into a tuple of integers""" - return tuple([int(x) for x in oidStr.strip('.').split('.')]) + return tuple([int(x) for x in oidStr.strip(".").split(".")]) + def _get_agent_spec(ipobj, interface, port): """take a google ipaddr object and port number and produce a net-snmp @@ -112,17 +116,26 @@ def _get_agent_spec(ipobj, interface, port): elif ipobj.version == 6: if ipobj.is_link_local: if interface is None: - raise RuntimeError("Cannot create agent specification from link local IPv6 address without an interface") + raise RuntimeError( + "Cannot create agent specification from link local IPv6 address without an interface" + ) else: - agent = "udp6:[%s%%%s]:%s" % (ipobj.compressed, interface, port) + agent = "udp6:[%s%%%s]:%s" % ( + ipobj.compressed, + interface, + port, + ) else: agent = "udp6:[%s]:%s" % (ipobj.compressed, port) else: - raise RuntimeError("Cannot create agent specification for IP address version: %s" % ipobj.version) + raise RuntimeError( + "Cannot create agent specification for IP address version: %s" + % ipobj.version + ) return agent -class SnmpError(Exception): +class SnmpError(Exception): def __init__(self, message, *args, **kwargs): self.message = message @@ -132,51 +145,47 @@ def __str__(self): def __repr__(self): return self.message -class Snmpv3Error(SnmpError): + +class Snmpv3Error(SnmpError): pass -USM_STATS_OIDS = { +USM_STATS_OIDS = { # usmStatsWrongDigests - ".1.3.6.1.6.3.15.1.1.5.0": - "check zSnmpAuthType and zSnmpAuthPassword, packet did not include the expected digest value", - + ".1.3.6.1.6.3.15.1.1.5.0": "check zSnmpAuthType and zSnmpAuthPassword, packet did not include the expected digest value", # usmStatsUnknownUserNames - ".1.3.6.1.6.3.15.1.1.3.0": - "check zSnmpSecurityName, packet referenced an unknown user", - + ".1.3.6.1.6.3.15.1.1.3.0": "check zSnmpSecurityName, packet referenced an unknown user", # usmStatsUnsupportedSecLevels - ".1.3.6.1.6.3.15.1.1.1.0": - "packet requested an unknown or unavailable security level", - + ".1.3.6.1.6.3.15.1.1.1.0": "packet requested an unknown or unavailable security level", # usmStatsDecryptionErrors - ".1.3.6.1.6.3.15.1.1.6.0": - "check zSnmpPrivType, packet could not be decrypted" - + ".1.3.6.1.6.3.15.1.1.6.0": "check zSnmpPrivType, packet could not be decrypted", } + class AgentProxy(object): """The public methods on AgentProxy (get, walk, getbulk) expect input OIDs - to be strings, and the result they produce is a dictionary. The + to be strings, and the result they produce is a dictionary. The dictionary keys are OID strings and the values are the values returned by the SNMP requests. - + The private methods (_get, _walk, _getbulk) expect input OIDs to be tuples - of integers. These methods generate a result that is a list of pairs, + of integers. These methods generate a result that is a list of pairs, each pair consisting of the OID string and the value that is returned by - the SNMP query. The list is ordered correctly by the OID (i.e. it is not + the SNMP query. The list is ordered correctly by the OID (i.e. it is not ordered by the OID string).""" - def __init__(self, - ip, - port=161, - community='public', - snmpVersion = '1', - protocol=None, - allowCache = False, - timeout = 1.5, - tries = 3, - cmdLineArgs = ()): + def __init__( + self, + ip, + port=161, + community="public", + snmpVersion="1", + protocol=None, + allowCache=False, + timeout=1.5, + tries=3, + cmdLineArgs=(), + ): self.ip = ip self.port = port self.community = community @@ -202,12 +211,12 @@ def _signSafePop(self, d, intkey): except KeyError as ex: if intkey < 0: self._log.debug("Negative ID for _signSafePop: %s", intkey) - #convert to unsigned, try that key + # convert to unsigned, try that key uintkey = struct.unpack("I", struct.pack("i", intkey))[0] try: return d.pop(uintkey) except KeyError: - #nothing by the unsigned key either, throw the original KeyError for consistency + # nothing by the unsigned key either, throw the original KeyError for consistency raise ex raise @@ -229,22 +238,32 @@ def callback(self, pdu): # Some devices use usmStatsNotInTimeWindows as a normal part of the SNMPv3 handshake (JIRA-1565) # net-snmp automatically retries the request with the previous request_id and the values for # msgAuthoritativeEngineBoots and msgAuthoritativeEngineTime from this error packet - self._log.debug("Received a usmStatsNotInTimeWindows error. Some devices use usmStatsNotInTimeWindows as a normal part of the SNMPv3 handshake.") + self._log.debug( + "Received a usmStatsNotInTimeWindows error. Some devices use usmStatsNotInTimeWindows as a normal part of the SNMPv3 handshake." + ) return - + if usmStatsOidStr == ".1.3.6.1.2.1.1.1.0": # Some devices (Cisco Nexus/MDS) use sysDescr as a normal part of the SNMPv3 handshake (JIRA-7943) - self._log.debug("Received sysDescr during handshake. Some devices use sysDescr as a normal part of the SNMPv3 handshake.") + self._log.debug( + "Received sysDescr during handshake. Some devices use sysDescr as a normal part of the SNMPv3 handshake." + ) return - default_msg = "packet dropped (OID: {0})".format(usmStatsOidStr) + default_msg = "packet dropped (OID: {0})".format( + usmStatsOidStr + ) message = USM_STATS_OIDS.get(usmStatsOidStr, default_msg) break else: message = "packet dropped" - for d in (d for d, rOids in self.defers.itervalues() if not d.called): - reactor.callLater(0, d.errback, failure.Failure(Snmpv3Error(message))) + for d in ( + d for d, rOids in self.defers.itervalues() if not d.called + ): + reactor.callLater( + 0, d.errback, failure.Failure(Snmpv3Error(message)) + ) return @@ -252,11 +271,13 @@ def callback(self, pdu): if isinstance(value, tuple): value = asOidStr(value) result.append((oid, value)) - if len(result)==1 and result[0][0] not in oids_requested: + if len(result) == 1 and result[0][0] not in oids_requested: usmStatsOidStr = asOidStr(result[0][0]) if usmStatsOidStr in USM_STATS_OIDS: msg = USM_STATS_OIDS.get(usmStatsOidStr) - reactor.callLater(0, d.errback, failure.Failure(Snmpv3Error(msg))) + reactor.callLater( + 0, d.errback, failure.Failure(Snmpv3Error(msg)) + ) return elif usmStatsOidStr == ".1.3.6.1.6.3.15.1.1.2.0": # we may get a subsequent snmp result with the correct value @@ -264,16 +285,22 @@ def callback(self, pdu): self.defers[pdu.reqid] = (d, oids_requested) return if pdu.errstat != netsnmp.SNMP_ERR_NOERROR: - pduError = PDU_ERRORS.get(pdu.errstat, 'Unknown error (%d)' % pdu.errstat) + pduError = PDU_ERRORS.get( + pdu.errstat, "Unknown error (%d)" % pdu.errstat + ) message = "Packet for %s has error: %s" % (self.ip, pduError) - if pdu.errstat in (SNMP_ERR_NOACCESS, - SNMP_ERR_RESOURCEUNAVAILABLE, - SNMP_ERR_AUTHORIZATIONERROR,): - reactor.callLater(0, d.errback, failure.Failure(SnmpError(message))) + if pdu.errstat in ( + SNMP_ERR_NOACCESS, + SNMP_ERR_RESOURCEUNAVAILABLE, + SNMP_ERR_AUTHORIZATIONERROR, + ): + reactor.callLater( + 0, d.errback, failure.Failure(SnmpError(message)) + ) return else: result = [] - self._log.warning(message + '. OIDS: %s', oids_requested) + self._log.warning(message + ". OIDS: %s", oids_requested) reactor.callLater(0, d.callback, result) @@ -285,27 +312,34 @@ def _getCmdLineArgs(self): if not self.cmdLineArgs: return () - version = str(self.snmpVersion).lstrip('v') - if version == '2': - version += 'c' + version = str(self.snmpVersion).lstrip("v") + if version == "2": + version += "c" - if '%' in self.ip: - address, interface = self.ip.split('%') + if "%" in self.ip: + address, interface = self.ip.split("%") else: address = self.ip interface = None - self._log.debug("AgentProxy._getCmdLineArgs: using google ipaddr on %s", address) + self._log.debug( + "AgentProxy._getCmdLineArgs: using google ipaddr on %s", address + ) ipobj = IPAddress(address) agent = _get_agent_spec(ipobj, interface, self.port) - cmdLineArgs = list(self.cmdLineArgs) + ['-v', str(version), - '-c', self.community, - '-t', str(self.timeout), - '-r', str(self.tries), - agent, - ] + cmdLineArgs = list(self.cmdLineArgs) + [ + "-v", + str(version), + "-c", + self.community, + "-t", + str(self.timeout), + "-r", + str(self.tries), + agent, + ] return cmdLineArgs def open(self): @@ -315,14 +349,15 @@ def open(self): self.session = netsnmp.Session( version=netsnmp.SNMP_VERSION_MAP.get( - self.snmpVersion, - netsnmp.SNMP_VERSION_2c), + self.snmpVersion, netsnmp.SNMP_VERSION_2c + ), timeout=int(self.timeout), retries=int(self.tries), - peername= '%s:%d' % (self.ip, self.port), + peername="%s:%d" % (self.ip, self.port), community=self.community, community_len=len(self.community), - cmdLineArgs=self._getCmdLineArgs()) + cmdLineArgs=self._getCmdLineArgs(), + ) self.session.callback = self.callback self.session.timeout = self.timeout_ @@ -339,7 +374,7 @@ def _get(self, oids, timeout=None, retryCount=None): d = defer.Deferred() try: self.defers[self.session.get(oids)] = (d, oids) - except Exception, ex: + except Exception as ex: return defer.fail(ex) updateReactor() return d @@ -350,7 +385,7 @@ def _walk(self, oid, timeout=None, retryCount=None): self.defers[self.session.walk(oid)] = (d, (oid,)) except netsnmp.SnmpTimeoutError: return defer.fail(TimeoutError()) - except Exception, ex: + except Exception as ex: return defer.fail(ex) updateReactor() return d @@ -358,24 +393,24 @@ def _walk(self, oid, timeout=None, retryCount=None): def _getbulk(self, nonrepeaters, maxrepititions, oids): d = defer.Deferred() try: - self.defers[self.session.getbulk(nonrepeaters, - maxrepititions, - oids)] = (d, oids) - except Exception, ex: + self.defers[ + self.session.getbulk(nonrepeaters, maxrepititions, oids) + ] = (d, oids) + except Exception as ex: return defer.fail(ex) updateReactor() return d - def getTable(self, oids, **kw): from tableretriever import TableRetriever + try: t = TableRetriever(self, oids, **kw) - except Exception, ex: + except Exception as ex: return defer.fail(ex) updateReactor() return t() - + def get(self, oidStrs, timeout=None, retryCount=None): oids = [asOid(oidStr) for oidStr in oidStrs] deferred = self._get(oids, timeout, retryCount) @@ -404,6 +439,9 @@ def strKey(item): class _FakeProtocol: protocol = None - def port(self): return self -snmpprotocol = _FakeProtocol() + def port(self): + return self + + +snmpprotocol = _FakeProtocol() From 5fe08d694e49b47aa64a07b8f71dce587f2d8f52 Mon Sep 17 00:00:00 2001 From: Jason Peacock Date: Mon, 11 Jul 2022 12:59:06 -0500 Subject: [PATCH 3/9] Reformatted/refactored code to remove lints identified by flake8. --- pynetsnmp/SnmpSession.py | 15 +---- pynetsnmp/genconstants.py | 1 - pynetsnmp/netsnmp.py | 110 ++++++++++++++++++++++++++++++++---- pynetsnmp/tableretriever.py | 5 +- pynetsnmp/twistedsnmp.py | 81 +++++++++++++++++++------- 5 files changed, 166 insertions(+), 46 deletions(-) diff --git a/pynetsnmp/SnmpSession.py b/pynetsnmp/SnmpSession.py index 3571818..1e5b118 100644 --- a/pynetsnmp/SnmpSession.py +++ b/pynetsnmp/SnmpSession.py @@ -1,9 +1,8 @@ -from __future__ import print_function +from __future__ import absolute_import -__doc__ = "Backwards compatible API for SnmpSession" +"""Backwards compatible API for SnmpSession""" -import netsnmp -from ctypes import * +from . import netsnmp class SnmpSession(object): @@ -39,11 +38,3 @@ def get(self, oid): return self.session.sget([oid]) finally: self.session.close() - - -if __name__ == "__main__": - session = SnmpSession("127.0.0.1", timeout=1.5, port=161) - session.community = "public" - print(session.get(".1.3.6.1.2.1.1.5.0")) - session.community = "xyzzy" - print(session.get(".1.3.6.1.2.1.1.5.0")) diff --git a/pynetsnmp/genconstants.py b/pynetsnmp/genconstants.py index 49bac84..87fb38b 100644 --- a/pynetsnmp/genconstants.py +++ b/pynetsnmp/genconstants.py @@ -71,4 +71,3 @@ def make_imports(): if __name__ == "__main__": make_imports() - from CONSTANTS import * # check the result diff --git a/pynetsnmp/netsnmp.py b/pynetsnmp/netsnmp.py index 2c7ece9..4557cf8 100644 --- a/pynetsnmp/netsnmp.py +++ b/pynetsnmp/netsnmp.py @@ -1,12 +1,92 @@ +from __future__ import absolute_import + import logging import os -from ctypes import * +import sys + +from ctypes import ( + CDLL, + CFUNCTYPE, + POINTER, + RTLD_GLOBAL, + Structure, + Union, + byref, + c_byte, + c_char, + c_char_p, + c_double, + c_float, + c_int, + c_int32, + c_long, + c_size_t, + c_ubyte, + c_uint, + c_uint32, + c_ulong, + c_ushort, + c_void_p, + cast, + create_string_buffer, + pointer, + sizeof, + string_at, +) from ctypes.util import find_library -import CONSTANTS -from CONSTANTS import * -# freebsd cannot manage a decent find_library -import sys +from . import CONSTANTS +from .CONSTANTS import ( + ASN_APP_DOUBLE, + ASN_APP_FLOAT, + ASN_BIT_STR, + ASN_COUNTER, + ASN_COUNTER64, + ASN_GAUGE, + ASN_INTEGER, + ASN_IPADDRESS, + ASN_NULL, + ASN_OBJECT_ID, + ASN_OCTET_STR, + ASN_TIMETICKS, + LOG_ALERT, + LOG_CRIT, + LOG_DEBUG, + LOG_EMERG, + LOG_ERR, + LOG_INFO, + LOG_NOTICE, + LOG_WARNING, + MAX_OID_LEN, + NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE, + NETSNMP_CALLBACK_OP_TIMED_OUT, + NETSNMP_DS_LIB_APPTYPE, + NETSNMP_DS_LIBRARY_ID, + NETSNMP_LOGHANDLER_CALLBACK, + SNMP_CALLBACK_LIBRARY, + SNMP_CALLBACK_LOGGING, + SNMP_DEFAULT_COMMUNITY_LEN, + SNMP_DEFAULT_PEERNAME, + SNMP_DEFAULT_RETRIES, + SNMP_DEFAULT_TIMEOUT, + SNMP_DEFAULT_VERSION, + SNMPERR_TIMEOUT, + SNMP_MSG_GET, + SNMP_MSG_GETBULK, + SNMP_MSG_GETNEXT, + SNMP_MSG_TRAP, + SNMP_MSG_TRAP2, + SNMP_SESS_UNKNOWNAUTH, + SNMP_VERSION_1, + SNMP_VERSION_2c, + SNMP_VERSION_2p, + SNMP_VERSION_2star, + SNMP_VERSION_2u, + SNMP_VERSION_3, + SNMP_VERSION_sec, + USM_AUTH_KU_LEN, + USM_PRIV_KU_LEN, +) def _getLogger(name): @@ -103,12 +183,14 @@ class netsnmp_transport(Structure): if float_version > 5.299: paramName = [("paramName", c_char_p)] if _netsnmp_str_version >= ("5", "6"): - # Versions >= 5.6 and < 5.6.1.1 broke binary compatibility and changed oid type from c_long to c_uint32. This works - # around the issue for these platforms to allow things to work properly. + # Versions >= 5.6 and < 5.6.1.1 broke binary compatibility and changed + # oid type from c_long to c_uint32. This works around the issue for these + # platforms to allow things to work properly. if _netsnmp_str_version <= ("5", "6", "1", "1"): oid = c_uint32 - # Versions >= 5.6 broke binary compatibility by adding transport specific configuration + # Versions >= 5.6 broke binary compatibility by adding transport + # specific configuration. class netsnmp_container_s(Structure): pass @@ -272,6 +354,7 @@ class netsnmp_variable_list(Structure): netsnmp_pdu_p = POINTER(netsnmp_pdu) + # Redirect netsnmp logging to our log class netsnmp_log_message(Structure): pass @@ -615,7 +698,8 @@ def awaitTraps( lib.netsnmp_udp6_ctor() else: self._log.debug( - "Cannot find constructor function for UDP/IPv6 transport domain object." + "Cannot find constructor function for UDP/IPv6 transport " + "domain object." ) lib.init_snmp("zenoss_app") lib.setup_engineID(None, None) @@ -681,9 +765,11 @@ def sendTrap(self, trapoid, varbinds=None): if "-v1" in self.cmdLineArgs: pdu = lib.snmp_pdu_create(SNMP_MSG_TRAP) if hasattr(self, "agent_addr"): - # pdu.contents is a netsnmp_pdu, defined above, therefore its fields are c types - # self.agent_addr is an ipv4 address, and the v1 trap wants a c array of 4 unsigned bytes, - # so chop it up, make the octets ints, then a bytearray from that will cast. + # pdu.contents is a netsnmp_pdu, defined above, therefore its + # fields are c types. + # self.agent_addr is an ipv4 address, and the v1 trap wants + # a c array of 4 unsigned bytes, so chop it up, make the + # octets ints, then a bytearray from that will cast. pdu.contents.agent_addr = (c_ubyte * 4)( *(bytearray([int(x) for x in self.agent_addr.split(".")])) ) diff --git a/pynetsnmp/tableretriever.py b/pynetsnmp/tableretriever.py index 2c86ff0..8c9fe36 100644 --- a/pynetsnmp/tableretriever.py +++ b/pynetsnmp/tableretriever.py @@ -1,5 +1,8 @@ +from __future__ import absolute_import + from twisted.internet import defer -from twistedsnmp import asOidStr, asOid + +from .twistedsnmp import asOidStr, asOid class _TableStatus(object): diff --git a/pynetsnmp/twistedsnmp.py b/pynetsnmp/twistedsnmp.py index a1136be..03bf717 100644 --- a/pynetsnmp/twistedsnmp.py +++ b/pynetsnmp/twistedsnmp.py @@ -1,15 +1,37 @@ +from __future__ import absolute_import + import logging -import netsnmp import struct -from CONSTANTS import * from ipaddr import IPAddress - from twisted.internet import reactor from twisted.internet.error import TimeoutError from twisted.python import failure from twisted.internet import defer +from . import netsnmp +from .CONSTANTS import ( + SNMP_ERR_AUTHORIZATIONERROR, + SNMP_ERR_BADVALUE, + SNMP_ERR_COMMITFAILED, + SNMP_ERR_GENERR, + SNMP_ERR_INCONSISTENTNAME, + SNMP_ERR_INCONSISTENTVALUE, + SNMP_ERR_NOACCESS, + SNMP_ERR_NOCREATION, + SNMP_ERR_NOERROR, + SNMP_ERR_NOSUCHNAME, + SNMP_ERR_NOTWRITABLE, + SNMP_ERR_READONLY, + SNMP_ERR_RESOURCEUNAVAILABLE, + SNMP_ERR_TOOBIG, + SNMP_ERR_UNDOFAILED, + SNMP_ERR_WRONGENCODING, + SNMP_ERR_WRONGLENGTH, + SNMP_ERR_WRONGTYPE, + SNMP_ERR_WRONGVALUE, +) + class Timer(object): callLater = None @@ -117,7 +139,8 @@ def _get_agent_spec(ipobj, interface, port): if ipobj.is_link_local: if interface is None: raise RuntimeError( - "Cannot create agent specification from link local IPv6 address without an interface" + "Cannot create agent specification from link local " + "IPv6 address without an interface" ) else: agent = "udp6:[%s%%%s]:%s" % ( @@ -152,13 +175,22 @@ class Snmpv3Error(SnmpError): USM_STATS_OIDS = { # usmStatsWrongDigests - ".1.3.6.1.6.3.15.1.1.5.0": "check zSnmpAuthType and zSnmpAuthPassword, packet did not include the expected digest value", + ".1.3.6.1.6.3.15.1.1.5.0": ( + "check zSnmpAuthType and zSnmpAuthPassword, " + "packet did not include the expected digest value" + ), # usmStatsUnknownUserNames - ".1.3.6.1.6.3.15.1.1.3.0": "check zSnmpSecurityName, packet referenced an unknown user", + ".1.3.6.1.6.3.15.1.1.3.0": ( + "check zSnmpSecurityName, packet referenced an unknown user" + ), # usmStatsUnsupportedSecLevels - ".1.3.6.1.6.3.15.1.1.1.0": "packet requested an unknown or unavailable security level", + ".1.3.6.1.6.3.15.1.1.1.0": ( + "packet requested an unknown or unavailable security level" + ), # usmStatsDecryptionErrors - ".1.3.6.1.6.3.15.1.1.6.0": "check zSnmpPrivType, packet could not be decrypted", + ".1.3.6.1.6.3.15.1.1.6.0": ( + "check zSnmpPrivType, packet could not be decrypted" + ), } @@ -199,12 +231,13 @@ def __init__( def _signSafePop(self, d, intkey): """ - Attempt to pop the item at intkey from dictionary d. Upon failure, try to convert intkey - from a signed to an unsigned integer and try to pop again. + Attempt to pop the item at intkey from dictionary d. + Upon failure, try to convert intkey from a signed to an unsigned + integer and try to pop again. - This addresses potential integer rollover issues caused by the fact that netsnmp_pdu.reqid - is a c_long and the netsnmp_callback function pointer definition specifies it as a c_int. - See ZEN-4481. + This addresses potential integer rollover issues caused by the fact + that netsnmp_pdu.reqid is a c_long and the netsnmp_callback function + pointer definition specifies it as a c_int. See ZEN-4481. """ try: return d.pop(intkey) @@ -216,7 +249,8 @@ def _signSafePop(self, d, intkey): try: return d.pop(uintkey) except KeyError: - # nothing by the unsigned key either, throw the original KeyError for consistency + # Nothing by the unsigned key either, + # throw the original KeyError for consistency raise ex raise @@ -235,18 +269,25 @@ def callback(self, pdu): usmStatsOidStr = asOidStr(usmStatsOid) if usmStatsOidStr == ".1.3.6.1.6.3.15.1.1.2.0": - # Some devices use usmStatsNotInTimeWindows as a normal part of the SNMPv3 handshake (JIRA-1565) - # net-snmp automatically retries the request with the previous request_id and the values for - # msgAuthoritativeEngineBoots and msgAuthoritativeEngineTime from this error packet + # Some devices use usmStatsNotInTimeWindows as a normal + # part of the SNMPv3 handshake (JIRA-1565). + # net-snmp automatically retries the request with the + # previous request_id and the values for + # msgAuthoritativeEngineBoots and + # msgAuthoritativeEngineTime from this error packet. self._log.debug( - "Received a usmStatsNotInTimeWindows error. Some devices use usmStatsNotInTimeWindows as a normal part of the SNMPv3 handshake." + "Received a usmStatsNotInTimeWindows error. Some " + "devices use usmStatsNotInTimeWindows as a normal " + "part of the SNMPv3 handshake." ) return if usmStatsOidStr == ".1.3.6.1.2.1.1.1.0": - # Some devices (Cisco Nexus/MDS) use sysDescr as a normal part of the SNMPv3 handshake (JIRA-7943) + # Some devices (Cisco Nexus/MDS) use sysDescr as a normal + # part of the SNMPv3 handshake (JIRA-7943) self._log.debug( - "Received sysDescr during handshake. Some devices use sysDescr as a normal part of the SNMPv3 handshake." + "Received sysDescr during handshake. Some devices use " + "sysDescr as a normal part of the SNMPv3 handshake." ) return From 9c10bdb24d0749403905da03299b9bb03a3b8659 Mon Sep 17 00:00:00 2001 From: Artem Date: Tue, 27 Jun 2023 17:28:12 +0300 Subject: [PATCH 4/9] ZEN-34420: Segfault error appears during modeling after net-snmp updates (#34) * ZEN-34420: Segfault error appears during modeling after net-snmp updates * refactored in concern of https://github.com/zenoss/pynetsnmp/pull/33/ PR * refactored comments * fix types casting and tests import * refactored in concern of struct params based on lib version * refactored comments --- makefile | 6 +-- pynetsnmp/netsnmp.py | 95 ++++++++++++++++++++++++++++++++-------- pynetsnmp/twistedsnmp.py | 2 +- test/get.py | 4 +- test/getbulk.py | 4 +- test/tableget.py | 4 +- test/trap.py | 6 +-- test/twistget.py | 20 ++++++--- test/walk.py | 4 +- 9 files changed, 105 insertions(+), 40 deletions(-) diff --git a/makefile b/makefile index 36d7cc9..97d77cf 100644 --- a/makefile +++ b/makefile @@ -1,6 +1,6 @@ # Define the image name, version and tag name for the docker build image IMAGENAME = build-tools -VERSION = 0.0.5 +VERSION = 0.0.15-dev TAG = zenoss/$(IMAGENAME):$(VERSION) UID := $(shell id -u) @@ -12,7 +12,7 @@ build-bdist: -v $(PWD):/mnt \ --user $(UID):$(GID) \ $(TAG) \ - /bin/bash -c "cd /mnt && python setup.py bdist_wheel" + /bin/bash -c "cd /mnt && /usr/bin/python2.7 setup.py bdist_wheel" build-sdist: @echo "Building a source distribution of pynetsnmp" @@ -20,7 +20,7 @@ build-sdist: -v $(PWD):/mnt \ --user $(UID):$(GID) \ $(TAG) \ - /bin/bash -c "cd /mnt && python setup.py sdist" + /bin/bash -c "cd /mnt && /usr/local/bin/python2.7 setup.py sdist" # Default to building a binary distribution build: build-bdist diff --git a/pynetsnmp/netsnmp.py b/pynetsnmp/netsnmp.py index 4557cf8..a3ffc5c 100644 --- a/pynetsnmp/netsnmp.py +++ b/pynetsnmp/netsnmp.py @@ -157,7 +157,24 @@ class netsnmp_transport(Structure): pass -# int (*netsnmp_callback) (int, netsnmp_session *, int, netsnmp_pdu *, void *); +# include/net-snmp/types.h +class netsnmp_trap_stats(Structure): + _fields_ = [ + ("sent_count", c_ulong), + ("sent_last_sent", c_ulong), + ("sent_fail_count", c_ulong), + ("sent_last_fail", c_ulong), + ("ack_count", c_ulong), + ("ack_last_rcvd", c_ulong), + ("sec_err_count", c_ulong), + ("sec_err_last", c_ulong), + ("timeouts", c_ulong), + ("sent_last_timeout", c_ulong), + ] + + +# include/net-snmp/types.h -> int (*netsnmp_callback) (int, netsnmp_session *, int, netsnmp_pdu *, void *); +# the first argument is the return type in CFUNCTYPE notation. netsnmp_callback = CFUNCTYPE( c_int, c_int, @@ -176,6 +193,16 @@ class netsnmp_transport(Structure): localname = [] paramName = [] transportConfig = [] +trapStats = [] +msgMaxSize = [] +baseTransport = [] +fOpen = [] +fConfig = [] +fCopy = [] +fSetupSession = [] +identifier = [] +fGetTaddr = [] + if float_version < 5.099: raise ImportError("netsnmp version 5.1 or greater is required") if float_version > 5.199: @@ -197,6 +224,21 @@ class netsnmp_container_s(Structure): transportConfig = [ ("transport_configuration", POINTER(netsnmp_container_s)) ] +if _netsnmp_str_version >= ('5','8'): + # Version >= 5.8 broke binary compatibility, adding the trap_stats member to the netsnmp_session struct + trapStats = [('trap_stats', POINTER(netsnmp_trap_stats))] + # Version >= 5.8 broke binary compatibility, adding the msgMaxSize member to the snmp_pdu struct + msgMaxSize = [('msgMaxSize', c_long)] + baseTransport = [("base_transport", POINTER(netsnmp_transport))] + fOpen = [("f_open", c_void_p)] + fConfig = [("f_config", c_void_p)] + fCopy = [("f_copy", c_void_p)] + fSetupSession = [("f_setup_session", c_void_p)] + identifier = [("identifier", POINTER(u_char_p))] + fGetTaddr = [("f_get_taddr", c_void_p)] + # Version >= 5.8 broke binary compatibility, doubling the size of these constants used for struct sizes + USM_AUTH_KU_LEN = 64 + USM_PRIV_KU_LEN = 64 SNMP_VERSION_MAP = { @@ -210,7 +252,7 @@ class netsnmp_container_s(Structure): } -# Version +# include/net-snmp/types.h netsnmp_session._fields_ = ( [ ("version", c_long), @@ -261,7 +303,7 @@ class netsnmp_container_s(Structure): ("securityModel", c_int), ("securityLevel", c_int), ] - + paramName + + paramName + trapStats + [ ("securityInfo", c_void_p), ] @@ -281,11 +323,10 @@ class counter64(Structure): ("low", c_ulong), ] - +# include/net-snmp/types.h class netsnmp_vardata(Union): _fields_ = [ ("integer", POINTER(c_long)), - ("uinteger", POINTER(c_ulong)), ("string", c_char_p), ("objid", POINTER(oid)), ("bitstring", POINTER(c_ubyte)), @@ -298,7 +339,7 @@ class netsnmp_vardata(Union): class netsnmp_variable_list(Structure): pass - +# include/net-snmp/types.h netsnmp_variable_list._fields_ = [ ("next_variable", POINTER(netsnmp_variable_list)), ("name", POINTER(oid)), @@ -312,7 +353,7 @@ class netsnmp_variable_list(Structure): ("dataFreeHook", dataFreeHook), ("index", c_int), ] - +# include/net-snmp/types.h netsnmp_pdu._fields_ = [ ("version", c_long), ("command", c_int), @@ -327,6 +368,7 @@ class netsnmp_variable_list(Structure): ("securityModel", c_int), ("securityLevel", c_int), ("msgParseModel", c_int), + ] + msgMaxSize + [ ("transport_data", c_void_p), ("transport_data_length", c_int), ("tDomain", POINTER(oid)), @@ -361,7 +403,11 @@ class netsnmp_log_message(Structure): netsnmp_log_message_p = POINTER(netsnmp_log_message) + +# callback.h typedef int (SNMPCallback) (int majorID, int minorID, void *serverarg, void *clientarg); log_callback = CFUNCTYPE(c_int, c_int, netsnmp_log_message_p, c_void_p) + +# include/net-snmp/library/snmp_logging.h netsnmp_log_message._fields_ = [ ("priority", c_int), ("msg", c_char_p), @@ -377,7 +423,9 @@ class netsnmp_log_message(Structure): LOG_DEBUG: logging.DEBUG, } - +# snmplib/snmp_logging.c -> free(logh); +# include/net-snmp/output_api.h -> int snmp_log( int priority, const char *format, ...) +# in net-snmp -> snmp_log(LOG_ERR|WARNING|INFO|DEBUG, msg) def netsnmp_logger(a, b, msg): msg = cast(msg, netsnmp_log_message_p) priority = PRIORITY_MAP.get(msg.contents.priority, logging.DEBUG) @@ -386,6 +434,9 @@ def netsnmp_logger(a, b, msg): netsnmp_logger = log_callback(netsnmp_logger) + +# include/net-snmp/library/callback.h -> +# int snmp_register_callback(int major, int minor, SNMPCallback * new_callback, void *arg); lib.snmp_register_callback( SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_LOGGING, netsnmp_logger, 0 ) @@ -393,7 +444,7 @@ def netsnmp_logger(a, b, msg): lib.snmp_pdu_create.restype = netsnmp_pdu_p lib.snmp_open.restype = POINTER(netsnmp_session) - +# include/net-snmp/library/snmp_transport.h netsnmp_transport._fields_ = [ ("domain", POINTER(oid)), ("domain_length", c_int), @@ -406,15 +457,27 @@ def netsnmp_logger(a, b, msg): ("data", c_void_p), ("data_length", c_int), ("msgMaxSize", c_size_t), + ] + baseTransport + [ ("f_recv", c_void_p), ("f_send", c_void_p), ("f_close", c_void_p), + ] + fOpen + [ ("f_accept", c_void_p), ("f_fmtaddr", c_void_p), -] +] + fCopy + fCopy + fSetupSession + identifier + fGetTaddr + +# include/net-snmp/library/snmp_transport.h -> +# netsnmp_transport *netsnmp_tdomain_transport( const char *str, int local, const char *default_domain); lib.netsnmp_tdomain_transport.restype = POINTER(netsnmp_transport) +# include/net-snmp/library/snmp_api.h -> netsnmp_session *snmp_add( +# netsnmp_session *, struct netsnmp_transport_s *, +# int (*fpre_parse) (netsnmp_session *, struct netsnmp_transport_s *, void *, int), +# int (*fpost_parse) (netsnmp_session *, netsnmp_pdu *, int) +# ); lib.snmp_add.restype = POINTER(netsnmp_session) + +# include/net-snmp/session_api.h -> int snmp_add_var(netsnmp_pdu *, const oid *, size_t, char, const char *); lib.snmp_add_var.argtypes = [ netsnmp_pdu_p, POINTER(oid), @@ -425,14 +488,10 @@ def netsnmp_logger(a, b, msg): lib.get_uptime.restype = c_long +# include/net-snmp/session_api.h -> int snmp_send(netsnmp_session *, netsnmp_pdu *); lib.snmp_send.argtypes = (POINTER(netsnmp_session), netsnmp_pdu_p) lib.snmp_send.restype = c_int -# int snmp_input(int, netsnmp_session *, int, netsnmp_pdu *, void *); -snmp_input_t = CFUNCTYPE( - c_int, c_int, POINTER(netsnmp_session), c_int, netsnmp_pdu_p, c_void_p -) - # A pointer to a _CallbackData struct is used for the callback_magic # parameter on the netsnmp_session structure. In the case of a SNMP v3 @@ -511,9 +570,9 @@ def decodeString(pdu): chr(ASN_OBJECT_ID): decodeOid, chr(ASN_BIT_STR): decodeString, chr(ASN_IPADDRESS): decodeIp, - chr(ASN_COUNTER): lambda pdu: pdu.val.uinteger.contents.value, - chr(ASN_GAUGE): lambda pdu: pdu.val.uinteger.contents.value, - chr(ASN_TIMETICKS): lambda pdu: pdu.val.uinteger.contents.value, + chr(ASN_COUNTER): lambda pdu: pdu.val.integer.contents.value, + chr(ASN_GAUGE): lambda pdu: pdu.val.integer.contents.value, + chr(ASN_TIMETICKS): lambda pdu: pdu.val.integer.contents.value, chr(ASN_COUNTER64): decodeBigInt, chr(ASN_APP_FLOAT): lambda pdu: pdu.val.float.contents.value, chr(ASN_APP_DOUBLE): lambda pdu: pdu.val.double.contents.value, diff --git a/pynetsnmp/twistedsnmp.py b/pynetsnmp/twistedsnmp.py index 03bf717..23e1340 100644 --- a/pynetsnmp/twistedsnmp.py +++ b/pynetsnmp/twistedsnmp.py @@ -325,7 +325,7 @@ def callback(self, pdu): # if not the timeout will be called at some point self.defers[pdu.reqid] = (d, oids_requested) return - if pdu.errstat != netsnmp.SNMP_ERR_NOERROR: + if pdu.errstat != SNMP_ERR_NOERROR: pduError = PDU_ERRORS.get( pdu.errstat, "Unknown error (%d)" % pdu.errstat ) diff --git a/test/get.py b/test/get.py index 081a6c0..d8c82fb 100644 --- a/test/get.py +++ b/test/get.py @@ -1,6 +1,6 @@ import logging -import netsnmp -import twistedsnmp +from pynetsnmp import netsnmp +from pynetsnmp import twistedsnmp import sys from twisted.internet import reactor diff --git a/test/getbulk.py b/test/getbulk.py index 0a2828b..b56a192 100644 --- a/test/getbulk.py +++ b/test/getbulk.py @@ -1,6 +1,6 @@ import logging -import netsnmp -import twistedsnmp +from pynetsnmp import netsnmp +from pynetsnmp import twistedsnmp import sys from twisted.internet import reactor diff --git a/test/tableget.py b/test/tableget.py index 3ebfcdc..8d6c6c2 100644 --- a/test/tableget.py +++ b/test/tableget.py @@ -1,5 +1,5 @@ -from tableretriever import TableRetriever -from twistedsnmp import AgentProxy +from pynetsnmp.tableretriever import TableRetriever +from pynetsnmp.twistedsnmp import AgentProxy from twisted.internet import reactor import logging diff --git a/test/trap.py b/test/trap.py index 763df84..6560485 100644 --- a/test/trap.py +++ b/test/trap.py @@ -2,9 +2,9 @@ import os import sys -import CONSTANTS as C -import netsnmp -import twistedsnmp +from pynetsnmp import CONSTANTS as C +from pynetsnmp import netsnmp +from pynetsnmp import twistedsnmp from twisted.internet import reactor diff --git a/test/twistget.py b/test/twistget.py index eb54cc8..769344f 100644 --- a/test/twistget.py +++ b/test/twistget.py @@ -1,10 +1,13 @@ -from twistedsnmp import AgentProxy +from pynetsnmp.twistedsnmp import AgentProxy from twisted.python import failure from twisted.internet import reactor -class Bogus(object): pass -def printResults(results): +class Bogus(object): + pass + + +def print_results(results): if reactor.running: reactor.stop() if isinstance(results, failure.Failure): @@ -13,10 +16,12 @@ def printResults(results): pprint.pprint(results) return results + def close(results, proxy): proxy.close() reactor.callLater(0.1, reactor.stop) + def main(): oids = ['.1.3.6.1.2.1.1.1.0', '.1.3.6.1.2.1.1.2.0', @@ -26,15 +31,16 @@ def main(): proxy = AgentProxy(ip='127.0.0.1', port=161, community='public', - snmpVersion = 1, - protocol = Bogus(), - allowCache = True) + snmpVersion=1, + protocol=Bogus(), + allowCache=True) proxy.open() d = proxy.get(oids, 1.0, 3) - d.addBoth(printResults) + d.addBoth(print_results) d.addCallback(close, proxy) reactor.run() print "end reactor" + if __name__ == '__main__': main() diff --git a/test/walk.py b/test/walk.py index 2764cc5..f0945c4 100644 --- a/test/walk.py +++ b/test/walk.py @@ -1,6 +1,6 @@ import logging -import netsnmp -import twistedsnmp +from pynetsnmp import netsnmp +from pynetsnmp import twistedsnmp import sys from twisted.internet import reactor, defer From 5b6e76ed07ebc36873815c5e12b10ca0c8ed4b68 Mon Sep 17 00:00:00 2001 From: Jason Peacock Date: Tue, 27 Jun 2023 10:19:25 -0500 Subject: [PATCH 5/9] Update Makefile to use a published Docker image. --- makefile | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/makefile b/makefile index 97d77cf..5ce4813 100644 --- a/makefile +++ b/makefile @@ -1,29 +1,25 @@ -# Define the image name, version and tag name for the docker build image -IMAGENAME = build-tools -VERSION = 0.0.15-dev -TAG = zenoss/$(IMAGENAME):$(VERSION) +IMAGENAME = zenoss/build-tools +VERSION = 0.0.14 +TAG = $(IMAGENAME):$(VERSION) UID := $(shell id -u) GID := $(shell id -g) -build-bdist: - @echo "Building a binary distribution of pynetsnmp" - docker run --rm \ - -v $(PWD):/mnt \ - --user $(UID):$(GID) \ - $(TAG) \ - /bin/bash -c "cd /mnt && /usr/bin/python2.7 setup.py bdist_wheel" +DOCKER_COMMAND = docker run --rm -v $(PWD):/mnt -w /mnt -u $(UID):$(GID) $(TAG) -build-sdist: - @echo "Building a source distribution of pynetsnmp" - docker run --rm \ - -v $(PWD):/mnt \ - --user $(UID):$(GID) \ - $(TAG) \ - /bin/bash -c "cd /mnt && /usr/local/bin/python2.7 setup.py sdist" +.DEFAULT_GOAL := build -# Default to building a binary distribution -build: build-bdist +.PHONY: bdist +bdist: + @$(DOCKER_COMMAND) bash -c "python setup.py bdist_wheel" +.PHONY: sdist +sdist: + @$(DOCKER_COMMAND) bash -c "python setup.py sdist" + +.PHONY: build +build: bdist + +.PHONY: clean clean: - rm -rf *.pyc MANIFEST dist build pynetsnmp.egg-info + rm -rf *.pyc dist build pynetsnmp.egg-info From 375caf82b844f9b25f451bcbefb9c8cffccf3226 Mon Sep 17 00:00:00 2001 From: Jason Peacock Date: Wed, 9 Aug 2023 10:16:26 -0500 Subject: [PATCH 6/9] Adjusted imports to avoid a nested import. --- pynetsnmp/conversions.py | 11 +++++++++++ pynetsnmp/tableretriever.py | 2 +- pynetsnmp/twistedsnmp.py | 17 +++-------------- 3 files changed, 15 insertions(+), 15 deletions(-) create mode 100644 pynetsnmp/conversions.py diff --git a/pynetsnmp/conversions.py b/pynetsnmp/conversions.py new file mode 100644 index 0000000..beac056 --- /dev/null +++ b/pynetsnmp/conversions.py @@ -0,0 +1,11 @@ +from __future__ import absolute_import + + +def asOidStr(oid): + """converts an oid int sequence to an oid string""" + return "." + ".".join([str(x) for x in oid]) + + +def asOid(oidStr): + """converts an OID string into a tuple of integers""" + return tuple([int(x) for x in oidStr.strip(".").split(".")]) diff --git a/pynetsnmp/tableretriever.py b/pynetsnmp/tableretriever.py index 8c9fe36..c568580 100644 --- a/pynetsnmp/tableretriever.py +++ b/pynetsnmp/tableretriever.py @@ -2,7 +2,7 @@ from twisted.internet import defer -from .twistedsnmp import asOidStr, asOid +from .conversions import asOidStr, asOid class _TableStatus(object): diff --git a/pynetsnmp/twistedsnmp.py b/pynetsnmp/twistedsnmp.py index 23e1340..ce72af2 100644 --- a/pynetsnmp/twistedsnmp.py +++ b/pynetsnmp/twistedsnmp.py @@ -4,10 +4,9 @@ import struct from ipaddr import IPAddress -from twisted.internet import reactor +from twisted.internet import defer, reactor from twisted.internet.error import TimeoutError from twisted.python import failure -from twisted.internet import defer from . import netsnmp from .CONSTANTS import ( @@ -31,6 +30,8 @@ SNMP_ERR_WRONGTYPE, SNMP_ERR_WRONGVALUE, ) +from .conversions import asOidStr, asOid +from .tableretriever import TableRetriever class Timer(object): @@ -120,16 +121,6 @@ def __init__(self, oid): Exception.__init__(self, "Bad Name", oid) -def asOidStr(oid): - """converts an oid int sequence to an oid string""" - return "." + ".".join([str(x) for x in oid]) - - -def asOid(oidStr): - """converts an OID string into a tuple of integers""" - return tuple([int(x) for x in oidStr.strip(".").split(".")]) - - def _get_agent_spec(ipobj, interface, port): """take a google ipaddr object and port number and produce a net-snmp agent specification (see the snmpcmd manpage)""" @@ -443,8 +434,6 @@ def _getbulk(self, nonrepeaters, maxrepititions, oids): return d def getTable(self, oids, **kw): - from tableretriever import TableRetriever - try: t = TableRetriever(self, oids, **kw) except Exception as ex: From 7cd87bc45471bc0ea3165893dd883b2338f8f07a Mon Sep 17 00:00:00 2001 From: vsaliieva Date: Tue, 3 Oct 2023 14:22:06 +0000 Subject: [PATCH 7/9] Maintenance tests changes Fixes ZEN-34470. *Added "cosmetic" changes for pynetsnmp tests. *Added a test runner Python script. *Added a README.md file with test descriptions and examples. --- test/README.md | 56 +++++++++++++++++++++++++++++++++++++++++++++ test/get.py | 32 ++++++++++++++++++-------- test/getbulk.py | 21 +++++++++++------ test/ifIndex.py | 29 +++++++++++++++++------ test/tableget.py | 21 +++++++++++++---- test/test_runner.py | 51 +++++++++++++++++++++++++++++++++++++++++ test/trap.py | 11 ++++++++- test/twistget.py | 31 ++++++++++++++++++------- test/walk.py | 13 +++++++++-- 9 files changed, 226 insertions(+), 39 deletions(-) create mode 100644 test/README.md create mode 100644 test/test_runner.py diff --git a/test/README.md b/test/README.md new file mode 100644 index 0000000..a24e4ce --- /dev/null +++ b/test/README.md @@ -0,0 +1,56 @@ +# Pynetsnmp Test Runner + +This Python script, named `test_runner.py`, is a utility for running a series of tests related to the pynetsnmp library. +It allows you to specify a host and runs various SNMP-related test scripts with the option to pass the host as an argument to each test. + +## Usage + +Run the script using Python and provide the necessary arguments: + +- `--host HOST`: Specify the SNMP host for all tests. This option adds a `--host` argument to each test script. If not provided, the tests will run without specifying a host. + +The script will execute a series of SNMP-related test scripts and display the output of each test. + +## Test Scripts + +The following test scripts are included and run by this test runner: + +1. `get.py`: Test script for SNMP GET requests. +2. `getbulk.py`: Test script for SNMP GETBULK requests. +3. `ifIndex.py`: Test script for querying SNMP IF-MIB. +4. `tableget.py`: Test script for querying SNMP tables. +5. `walk.py`: Test script for SNMP walk requests. **NOTE: script runs only for one host. If you want use it for list of hosts run this test separately.** +6. `twistget.py`: Test script using the Twisted framework for SNMP GET requests. + +## Test Scripts for Manual Run + +The following test scripts are NOT included to run by this test runner: +1. `trap.py`: SNMP Trap Receiver Test. To run this script and check result you will need 2 terminal windows: +first one to run test, e.g.: +```bash +python trap.py --host --port +``` +and second to send snmptrap: + +```bash +snmptrap -v -c : '' .1.3.6.1.6.3.1.1.5.1 +``` + +## Output + +The test runner provides output for each test script, indicating whether the test was successful or not. If "error" is not found in the output, the test is considered successful; otherwise, it is marked as a failure. + +## Results + +After running all tests, the test runner displays the following summary: + +- Total Successful Tests +- Total Failed Tests + +## Example + +Here's an example of how to use the test runner: + +```bash +python test_runner.py --host +``` diff --git a/test/get.py b/test/get.py index d8c82fb..ecc8696 100644 --- a/test/get.py +++ b/test/get.py @@ -1,7 +1,7 @@ +import argparse import logging from pynetsnmp import netsnmp from pynetsnmp import twistedsnmp -import sys from twisted.internet import reactor @@ -10,32 +10,44 @@ class Getter(netsnmp.Session): def callback(self, pdu): results = netsnmp.getResult(pdu, self._log) for oid, value in results: - print oid, repr(value) + print "OID:", oid + print "Value:", repr(value) reactor.stop() def timeout(self, reqid): print "Timeout" reactor.stop() + raise RuntimeError("Timeout occurred") def main(): - name = "localhost" - community = "public" - if len(sys.argv) >= 2: - name = sys.argv[1] - oids = sys.argv[2:] + print "=================== \nSNMP Get Test" + + parser = argparse.ArgumentParser(description="SNMP Getter") + parser.add_argument("--host", default="localhost", help="SNMP peername (default: localhost)") + parser.add_argument("--community", default="public", help="SNMP community string (default: public)") + parser.add_argument("--oids", nargs="*", default=["1.3.6.1.2.1.1.1.0"], help="OIDs to retrieve (default: 1.3.6.1.2.1.1.1.0)") + + args = parser.parse_args() + + host = args.host + community = args.community + oids = args.oids + g = Getter( version=netsnmp.SNMP_VERSION_1, - peername=name, - community=community, - community_len=len(community), + peername=host, + community=community ) + oids = [tuple(map(int, oid.strip(".").split("."))) for oid in oids] g.open() g.get(oids) twistedsnmp.updateReactor() reactor.run() + print "===================" + if __name__ == "__main__": logging.basicConfig() diff --git a/test/getbulk.py b/test/getbulk.py index b56a192..dc32616 100644 --- a/test/getbulk.py +++ b/test/getbulk.py @@ -1,7 +1,7 @@ +import argparse import logging from pynetsnmp import netsnmp from pynetsnmp import twistedsnmp -import sys from twisted.internet import reactor @@ -28,7 +28,7 @@ def callback(self, pdu): self.stop("table end") return print ".".join(map(str, oid)), ":", repr(value) - print + if not results: self.stop("empty result") else: @@ -36,24 +36,31 @@ def callback(self, pdu): def timeout(self, reqid): self.stop("Timeout") + raise RuntimeError("Timeout occurred") def main(): - name = "localhost" - community = "public" - if len(sys.argv) >= 2: - name = sys.argv[1] + print "=================== \nSNMP Get Bulk Test" + parser = argparse.ArgumentParser(description="Get bulk") + parser.add_argument("--host", default="localhost", help="SNMP peername (default: localhost)") + parser.add_argument("--community", default="public", help="SNMP community string (default: public)") + args = parser.parse_args() + + host = args.host + community = args.community oid = (1, 3, 6, 1, 2, 1, 25, 4, 2, 1, 2) t = Table( version=netsnmp.SNMP_VERSION_2c, - peername=name, + peername=host, community=community, community_len=len(community), ) + t.open() t.getTable(oid) twistedsnmp.updateReactor() reactor.run() + print "===================" if __name__ == "__main__": diff --git a/test/ifIndex.py b/test/ifIndex.py index 1a6e837..61d71c7 100644 --- a/test/ifIndex.py +++ b/test/ifIndex.py @@ -1,34 +1,49 @@ """prints the number of interfaces returned by a walk of ifIndex""" - -from tableretriever import TableRetriever -from twistedsnmp import AgentProxy +import argparse +from pynetsnmp.tableretriever import TableRetriever +from pynetsnmp.twistedsnmp import AgentProxy from twisted.internet import reactor import logging logging.basicConfig() log = logging.getLogger("ifIndex") + def error(why): reactor.stop() log.error('%s', why) + def success(result): - print len(result.values()[0]) + print "Number of interfaces returned by a walk of ifIndex :", len(result.values()[0]) reactor.stop() + def closer(result, proxy): proxy.close() return result + def main(): - proxy = AgentProxy('colo3560g', snmpVersion='1', community='zenoss') + print "=================== \nSNMP Walk of ifIndex Test" + parser = argparse.ArgumentParser(description="SNMP Walk of ifIndex") + parser.add_argument("--host", default="colo3560g", help="IP address of the SNMP server") + parser.add_argument("--community", default="zenoss", help="SNMP community string") + parser.add_argument("--snmp_version", default="1", help="SNMP version (1 or v2)") + parser.add_argument("--oids", nargs="+", default=['.1.3.6.1.2.1.2.2.1.1'], help="OIDs to retrieve") + args = parser.parse_args() + + proxy = AgentProxy(ip=args.host, snmpVersion=args.snmp_version, community=args.community) proxy.open() - tr = TableRetriever(proxy, ('.1.3.6.1.2.1.2.2.1.1',)) + tr = TableRetriever(proxy, oids=args.oids) d = tr() d.addBoth(closer, proxy) d.addCallback(success) d.addErrback(error) reactor.run() -if __name__=='__main__': + print "===================" + + +if __name__ == '__main__': main() diff --git a/test/tableget.py b/test/tableget.py index 8d6c6c2..a818025 100644 --- a/test/tableget.py +++ b/test/tableget.py @@ -1,3 +1,4 @@ +import argparse from pynetsnmp.tableretriever import TableRetriever from pynetsnmp.twistedsnmp import AgentProxy from twisted.internet import reactor @@ -10,25 +11,37 @@ def error(why): reactor.stop() log.error('%s', why) + raise Exception(why) + def success(result): import pprint pprint.pprint(result) reactor.stop() + def closer(result, proxy): proxy.close() return result + if __name__ == '__main__': + print "=================== \nSNMP Table Retriever Test" + logging.basicConfig(level=logging.DEBUG) - proxy = AgentProxy('127.0.0.1', snmpVersion='v2') + parser = argparse.ArgumentParser(description="SNMP Table Retriever") + parser.add_argument("--host", default="127.0.0.1", help="IP address of the SNMP server") + parser.add_argument("--snmp_version", default="v2", help="SNMP version (1 or v2)") + parser.add_argument("--oids", nargs="+", default=('.1.3.6.1.2.1.1.5', '.1.3.6.1.2.1.25.4.2.1.4'), help="OIDs to retrieve") + args = parser.parse_args() + + proxy = AgentProxy(ip=args.host, snmpVersion=args.snmp_version) proxy.open() - tr = TableRetriever(proxy, - # ('.1.3.6.1.2.1.25.4.2.1.2', '.1.3.6.1.2.1.25.4.2.1.4')) - ('.1.3.6.1.2.1.1.5', '.1.3.6.1.2.1.25.4.2.1.4')) + tr = TableRetriever(proxy, oids=args.oids) d = tr() d.addBoth(closer, proxy) d.addCallback(success) d.addErrback(error) reactor.run() + + print "===================" diff --git a/test/test_runner.py b/test/test_runner.py new file mode 100644 index 0000000..6a7205c --- /dev/null +++ b/test/test_runner.py @@ -0,0 +1,51 @@ +import argparse +import subprocess + +all_tests = [ + "get.py", + "getbulk.py", + "ifIndex.py", + "tableget.py", + "walk.py", + "twistget.py" +] + + +def main(): + parser = argparse.ArgumentParser(description="Pynetsnmp test runner") + parser.add_argument("--host", help="Specify the host for all tests (adds --host argument to each test)") + + args = parser.parse_args() + + host = args.host + + success_count = 0 + failure_count = 0 + + for test in all_tests: + command = ["python", test] + if host and test != "walk.py": + command.extend(["--host", host]) + if test == "walk.py": + command.extend([host]) + try: + output = subprocess.check_output(command, stderr=subprocess.STDOUT, universal_newlines=True) + print(output) + if "error" not in output.lower(): + success_count += 1 + else: + failure_count += 1 + + except subprocess.CalledProcessError as e: + print("Error running command for {}: {}".format(test, e)) + failure_count += 1 + except Exception as e: + print("Error in {}: {}".format(test, e)) + + print("===================") + print("Successful Tests: {}".format(success_count)) + print("Failed Tests: {}".format(failure_count)) + + +if __name__ == "__main__": + main() diff --git a/test/trap.py b/test/trap.py index 6560485..a3d7cc9 100644 --- a/test/trap.py +++ b/test/trap.py @@ -1,3 +1,4 @@ +import argparse import logging import os import sys @@ -33,7 +34,15 @@ def callback(self, pdu): def main(argv): - hostPort = argv[1] + print "=================== \nSNMP Trap Receiver Test" + + parser = argparse.ArgumentParser(description="SNMP Trap Receiver") + parser.add_argument("--host", default="localhost", help="Host to listen on") + parser.add_argument("--port", type=int, default=162, help="Port to listen on") + args = parser.parse_args() + + hostPort = ":".join([args.host, str(args.port)]) + s = Trapd() s.awaitTraps(hostPort) twistedsnmp.updateReactor() diff --git a/test/twistget.py b/test/twistget.py index 769344f..c00fe56 100644 --- a/test/twistget.py +++ b/test/twistget.py @@ -1,3 +1,4 @@ +import argparse from pynetsnmp.twistedsnmp import AgentProxy from twisted.python import failure from twisted.internet import reactor @@ -23,23 +24,37 @@ def close(results, proxy): def main(): + print "=================== \nSNMP Proxy Example Test" + oids = ['.1.3.6.1.2.1.1.1.0', '.1.3.6.1.2.1.1.2.0', '.1.3.6.1.2.1.1.3.0', '.1.3.6.1.2.1.1.4.0', ] - proxy = AgentProxy(ip='127.0.0.1', - port=161, - community='public', - snmpVersion=1, - protocol=Bogus(), - allowCache=True) + + parser = argparse.ArgumentParser(description="SNMP Proxy Example") + parser.add_argument("--host", default="127.0.0.1", help="SNMP server IP address") + parser.add_argument("--port", type=int, default=161, help="SNMP server port") + parser.add_argument("--community", default="public", help="SNMP community") + parser.add_argument("--snmp_version", type=int, default=1, help="SNMP version") + parser.add_argument("--oids", nargs="+", default=oids, help="List of OIDs") + args = parser.parse_args() + + proxy = AgentProxy( + ip=args.host, + port=args.port, + community=args.community, + snmpVersion=args.snmp_version, + protocol=Bogus(), + allowCache=True + ) + proxy.open() - d = proxy.get(oids, 1.0, 3) + d = proxy.get(args.oids, 1.0, 3) d.addBoth(print_results) d.addCallback(close, proxy) reactor.run() - print "end reactor" + print "===================" if __name__ == '__main__': diff --git a/test/walk.py b/test/walk.py index f0945c4..4082c04 100644 --- a/test/walk.py +++ b/test/walk.py @@ -33,6 +33,7 @@ def callback(self, pdu): def timeout(self, reqid): self.stop("Timeout") + raise RuntimeError("Timeout occurred") def start(self): self.open() @@ -46,21 +47,28 @@ def stop(results): for success, values in results: if success: host, values = values - print host, len(values) + print "Host:", host + + print "Results length", len(values) else: - print values + print "Result", values if reactor.running: reactor.stop() def main(): + print "=================== \nSNMP Walk Test" + import getopt # from snmp_parse_args.c opts = "Y:VhHm:M:O:I:P:D:dv:r:t:c:Z:e:E:n:u:l:x:X:a:A:p:T:-:3:s:S:L:" args, hosts = getopt.getopt(sys.argv[1:], opts) + if not hosts: hosts = ["localhost"] + if not args: + args = [('-v', '1'), ('-c', 'public')] d = defer.DeferredList( [Walker(peername=host, cmdLineArgs=args).start() for host in hosts], consumeErrors=True, @@ -68,6 +76,7 @@ def main(): d.addBoth(stop) twistedsnmp.updateReactor() reactor.run() + print "===================" if __name__ == "__main__": From fcaaa63889a33c6c8e69140a2100b2608d6c2b27 Mon Sep 17 00:00:00 2001 From: vsaliieva <91525276+vsaliieva@users.noreply.github.com> Date: Fri, 2 Feb 2024 18:21:35 +0200 Subject: [PATCH 8/9] Add test option to makefile (#36) * Add test option to makefile Fixes ZEN-34514. *Added test option to makefile to use it during jenkins build *Removed (temporary) walk.py from test_runner --- makefile | 10 ++++++++++ test/test_runner.py | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/makefile b/makefile index 5ce4813..e150de6 100644 --- a/makefile +++ b/makefile @@ -23,3 +23,13 @@ build: bdist .PHONY: clean clean: rm -rf *.pyc dist build pynetsnmp.egg-info + +.PHONY: test +HOST ?= 127.0.0.1 +test: + docker run --rm -v $(PWD):/mnt -w /mnt --user 0 $(TAG) \ + bash -c "python setup.py bdist_wheel \ + && pip install dist/pynetsnmp*py2-none-any.whl ipaddr Twisted==20.3.0 \ + && cd test \ + && python test_runner.py --host $(HOST) \ + && chown -R $(UID):$(GID) /mnt" ; diff --git a/test/test_runner.py b/test/test_runner.py index 6a7205c..c51476a 100644 --- a/test/test_runner.py +++ b/test/test_runner.py @@ -6,7 +6,7 @@ "getbulk.py", "ifIndex.py", "tableget.py", - "walk.py", +# "walk.py", "twistget.py" ] From 895e1c04ec22b02f3061450e88d54f353e1db7ea Mon Sep 17 00:00:00 2001 From: Josh Wilmes Date: Fri, 8 Mar 2024 15:50:28 -0500 Subject: [PATCH 9/9] Use snmp_select_info2 and snmp_read2 to support FDs > 1024. Note that this will also require the use of the twisted epoll reactor or similar, as is the default on linux already. (#37) ZEN-34128 --- pynetsnmp/netsnmp.py | 39 +++++++++++++++++++++++++++++++++++++++ pynetsnmp/twistedsnmp.py | 13 +++++++++++-- test/twistget.py | 6 ++++++ 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/pynetsnmp/netsnmp.py b/pynetsnmp/netsnmp.py index a3ffc5c..afee683 100644 --- a/pynetsnmp/netsnmp.py +++ b/pynetsnmp/netsnmp.py @@ -948,6 +948,7 @@ def walk(self, root): MAXFD = 1024 +FD_SETSIZE = MAXFD fdset = c_int32 * (MAXFD / 32) @@ -968,6 +969,17 @@ def fdset2list(rd, n): result.append(i * 32 + j) return result +class netsnmp_large_fd_set(Structure): + # This structure must be initialized by calling netsnmp_large_fd_set_init() + # and must be cleaned up via netsnmp_large_fd_set_cleanup(). If this last + # function is not called this may result in a memory leak. + + _fields_ = [ + ("lfs_setsize", c_uint), + ("lfs_setptr", POINTER(fdset)), + ("lfs_set", fdset) + ] + def snmp_select_info(): rd = fdset() @@ -983,12 +995,39 @@ def snmp_select_info(): t = timeout.tv_sec + timeout.tv_usec / 1e6 return fdset2list(rd, maxfd.value), t +def snmp_select_info2(): + rd = netsnmp_large_fd_set() + lib.netsnmp_large_fd_set_init(byref(rd), FD_SETSIZE) + maxfd = c_int(0) + timeout = timeval() + timeout.tv_sec = 1 + timeout.tv_usec = 0 + block = c_int(0) + maxfd = c_int(MAXFD) + lib.snmp_select_info2(byref(maxfd), byref(rd), byref(timeout), byref(block)) + t = None + if not block: + t = timeout.tv_sec + timeout.tv_usec / 1e6 + + result = [] + for fd in range(0, maxfd.value + 1): + if lib.netsnmp_large_fd_is_set(fd, byref(rd)): + result.append(fd) + + lib.netsnmp_large_fd_set_cleanup(byref(rd)) + return result, t def snmp_read(fd): rd = fdset() rd[fd / 32] |= 1 << (fd % 32) lib.snmp_read(byref(rd)) +def snmp_read2(fd): + rd = netsnmp_large_fd_set() + lib.netsnmp_large_fd_set_init(byref(rd), FD_SETSIZE) + lib.netsnmp_large_fd_setfd(fd, byref(rd)) + lib.snmp_read2(byref(rd)) + lib.netsnmp_large_fd_set_cleanup(byref(rd)) done = False diff --git a/pynetsnmp/twistedsnmp.py b/pynetsnmp/twistedsnmp.py index ce72af2..e5fae50 100644 --- a/pynetsnmp/twistedsnmp.py +++ b/pynetsnmp/twistedsnmp.py @@ -5,6 +5,7 @@ from ipaddr import IPAddress from twisted.internet import defer, reactor +from twisted.internet.selectreactor import SelectReactor from twisted.internet.error import TimeoutError from twisted.python import failure @@ -81,7 +82,7 @@ def __init__(self, fd): self.fd = fd def doRead(self): - netsnmp.snmp_read(self.fd) + netsnmp.snmp_read2(self.fd) # updateReactor() def fileno(self): @@ -94,11 +95,19 @@ def connectionLost(self, why): def updateReactor(): "Add/remove event handlers for SNMP file descriptors and timers" - fds, t = netsnmp.snmp_select_info() + isSelect = isinstance(reactor, SelectReactor) + fds, t = netsnmp.snmp_select_info2() + log = netsnmp._getLogger("updateReactor") if log.getEffectiveLevel() < logging.DEBUG: log.debug("reactor settings: %r, %r", fds, t) for fd in fds: + if isSelect and fd > netsnmp.MAXFD: + log.error("fd > %d detected!!" + + " This will not work properly with the SelectReactor and is being ignored." + + " Timeouts will occur unless you switch to EPollReactor instead!") + continue + if fd not in fdMap: reader = SnmpReader(fd) fdMap[fd] = reader diff --git a/test/twistget.py b/test/twistget.py index c00fe56..2480efd 100644 --- a/test/twistget.py +++ b/test/twistget.py @@ -49,6 +49,12 @@ def main(): allowCache=True ) + # open a lot of files, to push fd numbers into > 1024 so that we exercise + # snmp_select_info2 / snmp_read2 + fds = [] + for n in range(1024): + fds.append(open('/dev/null', 'r')) + proxy.open() d = proxy.get(args.oids, 1.0, 3) d.addBoth(print_results)