def check_power(self, ipmi_server, node_id): power_states = self.client.get_power_state(ipmi_server) cluster_name = self._cfg.clusters[0].name node_name = cluster_name + '-' + str(node_id + 1) # notify on every failed PS we find and set notified state to True try: for PS in power_states['FAIL']: if not self.notified_power_supply_failure[node_id][PS]: message = PS + " in " + node_name + " failed" subject = "[ALERT] Qumulo Power Supply Failure " + node_name self.notify(subject, message, "powerSupplyFailureTrap", [(rfc1902.ObjectName('1.3.6.1.4.1.47017.8'), rfc1902.OctetString(node_name)), (rfc1902.ObjectName('1.3.6.1.4.1.47017.11'), rfc1902.OctetString(PS)) ] ) self.notified_power_supply_failure[node_id][PS] = True except TypeError, err: self.logger.warn("WARNING: IPMI Exception, please verify IPMI config. (%s)" % str(err))
def dictify(self): if self.__varbinds_dict is None: self.__varbinds_dict = {} for entry in self.__varbinds: if isinstance(entry, list): for oid, value in entry: # always store internal data using rfc1902.ObjectNames, which are pyasn1 ObjectIdentifiers, which behave like tuples if not value.isSameTypeWith(rfc1905.noSuchObject): self.__varbinds_dict[rfc1902.ObjectName( oid)] = value else: oid, value = entry if not value.isSameTypeWith(rfc1905.noSuchObject): self.__varbinds_dict[rfc1902.ObjectName(oid)] = value
def __getFDBTable(self, result): portsTable = self.snmp.walk(OID.dot1dTpFdbPort) learnedTypeTable = self.snmp.walk(OID.dot1dTpFdbStatus) for learnedTypeTableRow in learnedTypeTable: for learnedname, learnedval in learnedTypeTableRow: if learnedval == rfc1902.Integer32('3'): learnedname = rfc1902.ObjectName( (learnedname.prettyPrint()).replace( OID.dot1dTpFdbStatus.prettyPrint(), OID.dot1dTpFdbPort.prettyPrint())) for portTableRow in portsTable: for portname, portval in portTableRow: if learnedname == portname: result.append( MACTableEntry( ("%02x:%02x:%02x:%02x:%02x:%02x" % (int(learnedname[11]), int(learnedname[12]), int(learnedname[13]), int(learnedname[14]), int(learnedname[15]), int(learnedname[16]))).replace( "0x", "").upper(), portval, '3', self.getSwitchName())) #return result return for key in learnedTypeTable.keys(): if (learnedTypeTable[key] == str(3)): mac = MACTableEntry( (macAddressesTable[key]).replace(' ', ':', 5).upper(), portsTable[key], learnedTypeTable[key]) mac.setSwitch(self.getSwitchID()) result.append(mac)
def getColumn(self, name, context=None): # if val is not None and varBindHead[idx].isPrefixOf(name): in cmdgen # fails so I can't use the oneliner method oid = '' if self.isDotNotation(name): oid = name else: oid = self.mibs[name].get('oid') if context == None: context = self.context cmdGen = MyCmdGen() errorIndication, errorStatus, _, varBinds = cmdGen.getColumn( self.agent, context, self.host, self.port, rfc1902.ObjectName(oid)) if errorIndication: raise IOError(errorIndication) if errorStatus: if len(varBinds) > 0 and errorStatus == 32: pass else: raise SNMPError('getcolumn on ' + oid + '(' + name + ')', errorStatus) #if errorStatus: # Throw an error?? # do we need to do error checking on this??? # what format should this return in??? formattedList = [] for val in varBinds: formattedList.append(val[0][1].prettyPrint()) return formattedList
def getFullMACTable(self): """\brief Gets the full learned mac table from the switch, returning a list of MACTableEntry objects. \return (\c list) A list of MACTableEntry objects with the results. """ portsTable = self.snmp.walk(OID.dot1dTpFdbPort) learnedTypeTable = self.snmp.walk(OID.dot1dTpFdbStatus) result = [] for learnedTypeTableRow in learnedTypeTable: for learnedname, learnedval in learnedTypeTableRow: if learnedval == rfc1902.Integer32('3'): learnedname = rfc1902.ObjectName((learnedname.prettyPrint()).replace(OID.dot1dTpFdbStatus.prettyPrint(),OID.dot1dTpFdbPort.prettyPrint())) for portTableRow in portsTable: for portname, portval in portTableRow: if learnedname == portname: result.append(MACTableEntry(("%02x:%02x:%02x:%02x:%02x:%02x" % (int(learnedname[11]),int(learnedname[12]),int(learnedname[13]),int(learnedname[14]),int(learnedname[15]),int(learnedname[16]))).replace("0x","").upper(),portval,'3',self.__switchName)) return result for key in learnedTypeTable.keys(): if (learnedTypeTable[key] == str(3)): mac = MACTableEntry((macAddressesTable[key]).replace(' ',':',5).upper(),portsTable[key],learnedTypeTable[key]) mac.setSwitch(self.getSwitchID()) result.append(mac)
def get(self, name, instance=0, context=None): oid = '' if self.isDotNotation(name): oid = name else: oid = self.mibs[name].get('oid') # 2012-04-04, pcankar: if instance is -1 leave oid unchanged if instance != -1: oid += '.' + str(instance) if context == None: context = self.context cmdGen = MyCmdGen() errorIndication, errorStatus, _, varBinds = cmdGen.getCmd( self.agent, context, self.host, self.port, rfc1902.ObjectName(oid)) if errorIndication: raise IOError(errorIndication) if errorStatus: raise SNMPError('get on ' + oid + '(' + name + ')', errorStatus) # Throw an error?? # do we need to do error checking on this??? #print "Getting %s (%s) = %s" % (name, oid, str(varBinds[-1][1].prettyPrint())) return varBinds[-1][1].prettyPrint()
def getnext(self, name, instance=0, context=None): # if val is not None and varBindHead[idx].isPrefixOf(name): in cmdgen # fails so I can't use the oneliner method oid = '' if self.isDotNotation(name): oid = name else: oid = self.mibs[name].get('oid') # 2012-04-04, pcankar: if instance is -1 leave oid unchanged if instance != -1: oid += '.' + str(instance) if context == None: context = self.context cmdGen = MyCmdGen() errorIndication, errorStatus, _, varBinds = cmdGen.nextCmd( self.agent, context, self.host, self.port, rfc1902.ObjectName(oid)) if errorIndication: raise IOError(errorIndication) if errorStatus: raise SNMPError('getnext on ' + oid + '(' + name + ')', errorStatus) #if errorStatus: # Throw an error?? # do we need to do error checking on this??? # what format should this return in??? return varBinds
def add_oid(self, oid, oid_type_str, getValue): oid_num = rfc1902.ObjectName(oid).asTuple() oid_type = TYPE_MAP[oid_type_str] self.mibBuilder.exportSymbols( '__MY_MIB', self.MibScalar(oid_num[:-1], oid_type), createVariable(self.MibScalarInstance, getValue, oid_num[:-1], (oid_num[-1], ), oid_type))
def __getSimpleMACTable(self): """\brief Gets the full learned mac table from the switch, returning a list of MACTableEntry objects. \return (\c list) A list of MACTableEntry objects with the results. """ portsTable = self.getDot1qTpFdbPort(True) learnedTypeTable = self.getDot1qTpFdbStatus(True) result = [] for learnedTypeTableRow in learnedTypeTable: for learnedname, learnedval in learnedTypeTableRow: if learnedval == rfc1902.Integer32('3'): learnedname = rfc1902.ObjectName( (learnedname.prettyPrint()).replace( OID.dot1qTpFdbStatus.prettyPrint(), OID.dot1qTpFdbPort.prettyPrint())) for portTableRow in portsTable: for portname, portval in portTableRow: if learnedname == portname: mac = ("%02x:%02x:%02x:%02x:%02x:%02x" % ( int(learnedname[14]), int(learnedname[15]), int(learnedname[16]), int(learnedname[17]), int(learnedname[18]), int( learnedname[19]))) mac = mac.replace("0x", "").upper() result.append((mac, str(portval))) return result
def addMacsToPorts(self): portsTable = self.getDot1qTpFdbPort(True) learnedTypeTable = self.getDot1qTpFdbStatus(True) self.resetPortsMacInfo() for learnedTypeTableRow in learnedTypeTable: for learnedname, learnedval in learnedTypeTableRow: if learnedval == rfc1902.Integer32('3'): learnedname = rfc1902.ObjectName( (learnedname.prettyPrint()).replace( OID.dot1qTpFdbStatus.prettyPrint(), OID.dot1qTpFdbPort.prettyPrint())) for portTableRow in portsTable: for portname, portval in portTableRow: if learnedname == portname: mac = ("%02x:%02x:%02x:%02x:%02x:%02x" % ( int(learnedname[14]), int(learnedname[15]), int(learnedname[16]), int(learnedname[17]), int(learnedname[18]), int( learnedname[19]))) mac = mac.replace("0x", "").upper() port = self.getPort(portval) mac_list = port.getMacs() mac_list.append(mac) port.setMacs(mac_list)
def variate(oid, tag, value, **context): # in --v2c-arch some of the items are not defined transportDomain = transportAddress = securityModel = securityName = \ securityLevel = contextName = '<undefined>' if 'transportDomain' in context: transportDomain = rfc1902.ObjectName( context['transportDomain']).prettyPrint() if 'transportAddress' in context: transportAddress = ':'.join( [str(x) for x in context['transportAddress']]) if 'securityModel' in context: securityModel = str(context['securityModel']) if 'securityName' in context: securityName = str(context['securityName']) if 'securityLevel' in context: securityLevel = str(context['securityLevel']) if 'contextName' in context: contextName = str(context['contextName']) args = [(x.replace('@TRANSPORTDOMAIN@', transportDomain).replace( '@TRANSPORTADDRESS@', transportAddress).replace( '@SECURITYMODEL@', securityModel).replace('@SECURITYNAME@', securityName).replace( '@SECURITYLEVEL@', securityLevel).replace('@CONTEXTNAME@', contextName).replace( '@DATAFILE@', context['dataFile']).replace('@OID@', str(oid)).replace( '@TAG@', tag).replace('@ORIGOID@', str(context['origOid'])). replace('@ORIGTAG@', str(sum( [x for x in context['origValue'].tagSet[0]]))).replace( '@ORIGVALUE@', str(context['origValue'])).replace( '@SETFLAG@', str(int(context['setFlag']))).replace( '@NEXTFLAG@', str(int(context['nextFlag']))).replace( '@SUBTREEFLAG@', str(int(context['subtreeFlag'])))) for x in split(value, ' ')] log.msg('subprocess: executing external process "%s"' % ' '.join(args)) call = (hasattr(subprocess, 'check_output') and subprocess.check_output or hasattr(subprocess, 'check_call') and subprocess.check_call or subprocess.call) if not hasattr(subprocess, 'check_output'): log.msg('subprocess: old Python, expect no output!') try: return oid, tag, call(args, shell=moduleContext['settings']['shell']) except (hasattr(subprocess, 'CalledProcessError') and subprocess.CalledProcessError or Exception): log.msg('subprocess: external program execution failed') return context['origOid'], tag, context['errorStatus']
def nodename(oid): """Translate dotted-decimal oid or oid tuple to symbolic name""" if isinstance(oid, str): oid = rfc1902.ObjectName(oid) oid = __mibViewController.getNodeLocation(oid) # pylint:disable=R0204 name = '::'.join(oid[:-1]) noid = '.'.join([str(x) for x in oid[-1]]) if noid: name += '.' + noid return name
def test__get_device_nibble_map(self): self.client = snmp_client.get_client(self.snmp_info) seg_id = 1001 egrs_oid = hp_const.OID_VLAN_EGRESS_PORT + '.' + str(seg_id) varbinds = [(rfc1902.ObjectName('1.3.6.1.2.1.17.7.1.4.3.1.2.1001'), rfc1902.OctetString('\x80'))] with contextlib.nested( mock.patch.object(snmp_client.SNMPClient, 'get', return_value=varbinds)): egbytes = self.driver._get_device_nibble_map(self.client, egrs_oid) self.assertEqual(egbytes, '\x80')
def getBulk(self, name, nonRepeaters, maxRepititions, context=None, dict=False): # if val is not None and varBindHead[idx].isPrefixOf(name): in cmdgen # fails so I can't use the oneliner method oid = '' if self.isDotNotation(name): oid = name else: oid = self.mibs[name].get('oid') if context == None: context = self.context # cmdGen = MyCmdGen() # errorIndication, errorStatus, _, varBinds = cmdGen.getBulk( # self.agent, context, # self.host, self.port, # nonRepeaters, # maxRepititions, # rfc1902.ObjectName(oid)) errorIndication, errorStatus, _, varBinds = cg.CommandGenerator( ).bulkCmd(cg.CommunityData(self.agent, context), cg.UdpTransportTarget((self.host, self.port)), nonRepeaters, maxRepititions, rfc1902.ObjectName(oid)) if errorIndication: raise IOError(errorIndication) if errorStatus: if len(varBinds) > 0 and errorStatus == 32: pass else: raise SNMPError('getbulk on ' + oid + '(' + name + ')', errorStatus) formattedList = [] for val in varBinds: formattedList.append(val[0][1].prettyPrint()) formattedDict = {} for val in varBinds: formattedDict[val[0][0].prettyPrint()] = val[0][1].prettyPrint() if dict: return formattedDict return formattedList
def send(self, mib, value, value_type='OctetString'): """ v1 snmp, public """ if not self.authData: raise ValueError('Credentials not set, use .security_XXX() methods') obj_class = getattr(rfc1902, value_type) errorIndication = self.ntfOrg.sendNotification(self.authData, ntforg.UdpTransportTarget((self.host, self.port)), #transportTarget 'trap', #notifyType ntforg.MibVariable('SNMPv2-MIB', 'snmpOutTraps'), #notificationType ((rfc1902.ObjectName(mib), obj_class(value)))) if errorIndication: raise RuntimeError('Notification not sent: %s' % errorIndication) print('Sent SNMP TRAP {} "{}" to {} {}'.format(mib, value, self.host, self.port))
def _to_pysnmp(self, value): """ Convert connection plugin object into pysnmp objects """ if value is None: return None if isinstance(value, OctetString): return rfc1902.OctetString(str(value.value)) if isinstance(value, ObjectIdentifier): return rfc1902.ObjectName(str(value.value)) if isinstance(value, Integer32): return rfc1902.Integer32(int(value.value)) if isinstance(value, Counter32): return rfc1902.Counter32(long(value.value)) if isinstance(value, IpAddress): return rfc1902.IpAddress(str(value.value)) if isinstance(value, Gauge32): return rfc1902.Gauge32(long(value.value)) if isinstance(value, TimeTicks): return rfc1902.TimeTicks(long(value.value)) if isinstance(value, Opaque): return rfc1902.Opaque(value.value) # FIXME if isinstance(value, Counter64): return rfc1902.Counter64(str(value.value)) raise SnmpError('Invalid type: %s' % value.__class__.__name__)
def get_by_dict(self, oid): self.dictify() if oid is None and len(list(self.__varbinds_dict.keys())) == 1: oid = list(self.__varbinds_dict.keys())[0] elif oid is None: raise RuntimeError( "Cannot query oid %r if multiple varBinds keys are present") if isinstance(oid, str): if oid in self.__varbinds_dict: value = self.__varbinds_dict[oid] else: value = self.__varbinds_dict[rfc1902.ObjectName(nodeid(oid))] if value.isSameTypeWith(rfc1905.noSuchObject): return None return value elif isinstance(oid, tuple) or isinstance(oid, rfc1902.ObjectName): value = self.__varbinds_dict[oid] if value.isSameTypeWith(rfc1905.noSuchObject): return None else: return value else: raise RuntimeError("Unknown format of oid %r with type %s" % (oid, oid.__class__.__name__))
def rpc_get(self, id, *object_ids): pysnmp_var_names = [] for object_id in object_ids: pysnmp_var_names.append(rfc1902.ObjectName(str(object_id))) self._conn.get(pysnmp_var_names, (self._on_rpc_get, id))
class VarBind(univ.Sequence): componentType = namedtype.NamedTypes( namedtype.NamedType('name', rfc1902.ObjectName()), namedtype.NamedType('', _BindValue()))
def resolveWithMib(self, mibViewController, oidOnly=False): if self.__mibSourcesToAdd is not None: mibSources = tuple( [ ZipMibSource(x) for x in self.__mibSourcesToAdd ] ) + mibViewController.mibBuilder.getMibSources() mibViewController.mibBuilder.setMibSources(*mibSources) self.__mibSourcesToAdd = None if self.__modNamesToLoad is not None: mibViewController.mibBuilder.loadModules(*self.__modNamesToLoad) self.__modNamesToLoad = None if self.__state & (self.stOidOnly | self.stClean): return self MibScalar, MibTableColumn, = mibViewController.mibBuilder.importSymbols('SNMPv2-SMI', 'MibScalar', 'MibTableColumn') if len(self.__args) == 1: # OID or label try: self.__oid = rfc1902.ObjectName(self.__args[0]) except PyAsn1Error: try: label = tuple(self.__args[0].split('.')) except ValueError: raise PySnmpError('Bad OID format %s' % (self.__args[0],)) prefix, label, suffix = mibViewController.getNodeNameByOid( label ) if suffix: try: suffix = tuple([ int(x) for x in suffix ]) except ValueError: raise PySnmpError('Unknown object name component %s' % (suffix,)) self.__oid = rfc1902.ObjectName(prefix + suffix) self.__state |= self.stOidOnly if oidOnly: return self else: self.__state |= self.stOidOnly if oidOnly: return self prefix, label, suffix = mibViewController.getNodeNameByOid( self.__oid ) modName, symName, _ = mibViewController.getNodeLocation(prefix) self.__modName = modName self.__symName = symName self.__label = label mibNode, = mibViewController.mibBuilder.importSymbols( modName, symName ) self.__mibNode = mibNode if isinstance(mibNode, MibTableColumn): # table column rowModName, rowSymName, _ = mibViewController.getNodeLocation( mibNode.name[:-1] ) rowNode, = mibViewController.mibBuilder.importSymbols( rowModName, rowSymName ) self.__indices = rowNode.getIndicesFromInstId(suffix) elif isinstance(mibNode, MibScalar): # scalar self.__indices = ( rfc1902.ObjectName(suffix), ) else: self.__indices = ( rfc1902.ObjectName(suffix), ) self.__state |= self.stUnresolved self.__state |= self.stClean return self elif len(self.__args) > 1: # MIB, symbol[, index, index ...] self.__modName = self.__args[0] if self.__args[1]: self.__symName = self.__args[1] else: mibViewController.mibBuilder.loadModules(self.__modName) oid, _, _ = mibViewController.getFirstNodeName(self.__modName) _, self.__symName, _ = mibViewController.getNodeLocation(oid) mibNode, = mibViewController.mibBuilder.importSymbols( self.__modName, self.__symName ) self.__mibNode = mibNode self.__indices = () self.__oid = rfc1902.ObjectName(mibNode.getName()) prefix, label, suffix = mibViewController.getNodeNameByOid( self.__oid ) self.__label = label if isinstance(mibNode, MibTableColumn): # table rowModName, rowSymName, _ = mibViewController.getNodeLocation( mibNode.name[:-1] ) rowNode, = mibViewController.mibBuilder.importSymbols( rowModName, rowSymName ) if self.__args[2:]: instIds = rowNode.getInstIdFromIndices(*self.__args[2:]) self.__oid += instIds self.__indices = rowNode.getIndicesFromInstId(instIds) elif self.__args[2:]: # any other kind of MIB node with indices instId = rfc1902.ObjectName( '.'.join([ str(x) for x in self.__args[2:] ]) ) self.__oid += instId self.__indices = ( instId, ) self.__state |= (self.stClean | self.stOidOnly) return self else: raise PySnmpError('Non-OID, label or MIB symbol')
# include managed object information 1.3.6.1.2.1.1.1.0 = 'my system' # include managed object information 1.3.6.1.2.1.1.3.0 = 567 errorIndication = ntfOrg.sendNotification( ntforg.UsmUserData('usr-sha-aes', 'authkey1', 'privkey1', authProtocol=ntforg.usmHMACSHAAuthProtocol, privProtocol=ntforg.usmAesCfb128Protocol), ntforg.Udp6TransportTarget(('::1', 162)), 'trap', ntforg.MibVariable('SNMPv2-MIB', 'authenticationFailure'), ('1.3.6.1.2.1.1.1.0', rfc1902.OctetString('my system')), ('1.3.6.1.2.1.1.3.0', rfc1902.TimeTicks(567))) if errorIndication: print('Notification not sent: %s' % errorIndication) # Using # SNMPv3 user 'usr-none-none', no auth, no priv # over IPv4/UDP # send TRAP notification # with TRAP ID 'authenticationFailure' specified as a MIB symbol # include managed object information 1.3.6.1.2.1.1.2.0 = 1.3.6.1.2.1.1.1 errorIndication = ntfOrg.sendNotification( ntforg.UsmUserData('usr-none-none'), ntforg.UdpTransportTarget(('localhost', 162)), 'trap', ntforg.MibVariable('SNMPv2-MIB', 'authenticationFailure'), ('1.3.6.1.2.1.1.2.0', rfc1902.ObjectName('1.3.6.1.2.1.1.1'))) if errorIndication: print('Notification not sent: %s' % errorIndication)
def variate(oid, tag, value, **context): if 'settings' not in recordContext: recordContext['settings'] = dict( [split(x, '=') for x in split(value, ',')]) if 'dir' not in recordContext['settings']: log.msg('multiplex: snapshot directory not specified') return context['origOid'], tag, context['errorStatus'] recordContext['settings']['dir'] = recordContext['settings'][ 'dir'].replace('/', os.path.sep) if recordContext['settings']['dir'][0] != os.path.sep: for x in confdir.data: d = os.path.join(x, recordContext['settings']['dir']) if os.path.exists(d): break else: log.msg('multiplex: directory %s not found' % recordContext['settings']['dir']) return context['origOid'], tag, context['errorStatus'] else: d = recordContext['settings']['dir'] recordContext['dirmap'] = dict([ (int(os.path.basename(x).split(os.path.extsep)[0]), os.path.join(d, x)) for x in os.listdir(d) if x[-7:] == 'snmprec' ]) recordContext['keys'] = list(recordContext['dirmap'].keys()) recordContext['bounds'] = (min(recordContext['keys']), max(recordContext['keys'])) if 'period' in recordContext['settings']: recordContext['settings']['period'] = float( recordContext['settings']['period']) else: recordContext['settings']['period'] = 60.0 if 'wrap' in recordContext['settings']: recordContext['settings']['wrap'] = bool( recordContext['settings']['wrap']) else: recordContext['settings']['wrap'] = False if 'control' in recordContext['settings']: recordContext['settings']['control'] = rfc1902.ObjectName( recordContext['settings']['control']) log.msg( 'multiplex: using control OID %s for subtree %s, time-based multiplexing disabled' % (recordContext['settings']['control'], oid)) recordContext['ready'] = True if 'ready' not in recordContext: return context['origOid'], tag, context['errorStatus'] if oid not in moduleContext: moduleContext[oid] = {} if context['setFlag']: if 'control' in recordContext['settings'] and \ recordContext['settings']['control'] == context['origOid']: fileno = int(context['origValue']) if fileno >= len(recordContext['keys']): log.msg('multiplex: .snmprec file number %s over limit of %s' % (fileno, len(recordContext['keys']))) return context['origOid'], tag, context['errorStatus'] moduleContext[oid]['fileno'] = fileno log.msg('multiplex: switched to file #%s (%s)' % (recordContext['keys'][fileno], recordContext['dirmap'][recordContext['keys'][fileno]])) return context['origOid'], tag, context['origValue'] else: return context['origOid'], tag, context['errorStatus'] if 'control' in recordContext['settings']: if 'fileno' not in moduleContext[oid]: moduleContext[oid]['fileno'] = 0 if (not context['nextFlag'] and recordContext['settings']['control'] == context['origOid']): return context['origOid'], tag, rfc1902.Integer32( moduleContext[oid]['fileno']) else: timeslot = (time.time() - moduleContext['booted']) % ( recordContext['settings']['period'] * len(recordContext['dirmap'])) fileslot = int( timeslot / recordContext['settings']['period']) + recordContext['bounds'][0] fileno = bisect.bisect(recordContext['keys'], fileslot) - 1 if ('fileno' not in moduleContext[oid] or moduleContext[oid]['fileno'] < fileno or recordContext['settings']['wrap']): moduleContext[oid]['fileno'] = fileno datafile = recordContext['dirmap'][recordContext['keys'][moduleContext[oid] ['fileno']]] if ('datafile' not in moduleContext[oid] or moduleContext[oid]['datafile'] != datafile): if 'datafileobj' in moduleContext[oid]: moduleContext[oid]['datafileobj'].close() moduleContext[oid]['datafileobj'] = RecordIndex( datafile, SnmprecRecord()).create() moduleContext[oid]['datafile'] = datafile log.msg('multiplex: switching to data file %s for %s' % (datafile, context['origOid'])) text, db = moduleContext[oid]['datafileobj'].getHandles() textOid = str( rfc1902.OctetString('.'.join(['%s' % x for x in context['origOid']]))) try: line = moduleContext[oid]['datafileobj'].lookup(textOid) except KeyError: offset = searchRecordByOid(context['origOid'], text, SnmprecRecord()) exactMatch = False else: offset, subtreeFlag, prevOffset = line.split(str2octs(',')) exactMatch = True text.seek(int(offset)) line, _, _ = getRecord(text) # matched line if context['nextFlag']: if exactMatch: line, _, _ = getRecord(text) else: if not exactMatch: return context['origOid'], tag, context['errorStatus'] if not line: return context['origOid'], tag, context['errorStatus'] try: oid, value = SnmprecRecord().evaluate(line) except error.SnmpsimError: oid, value = context['origOid'], context['errorStatus'] return oid, tag, value
def nodeinfo(oid): """Translate dotted-decimal oid to a tuple with symbolic info""" if isinstance(oid, str): oid = rfc1902.ObjectName(oid) return __mibViewController.getNodeLocation( oid), __mibViewController.getNodeName(oid)
def rpc_set(self, id, var_binds): pysnmp_var_binds = [] for object_id, value in var_binds.items(): pysnmp_var_binds.append( (rfc1902.ObjectName(str(object_id)), self._to_pysnmp(value))) self._conn.set(pysnmp_var_binds, (self._on_rpc_set, id))
# to an Agent at 195.218.195.228:161 # for an OID in string form # stop whenever received OID goes out of initial prefix (it may be a table) # # This script performs similar to the following Net-SNMP command: # # $ snmpwalk -v3 -l noAuthNoPriv -u usr-none-none -ObentU 195.218.195.228:161 1.3.6.1.2.1.1 # from twisted.internet import reactor, defer from pysnmp.entity import engine, config from pysnmp.entity.rfc3413.twisted import cmdgen from pysnmp.proto import rfc1902, rfc1905 from pysnmp.carrier.twisted.dgram import udp # Initial OID prefix initialOID = rfc1902.ObjectName('1.3.6.1.2.1.1') # Create SNMP engine instance snmpEngine = engine.SnmpEngine() # # SNMPv3/USM setup # # user: usr-none-none, no auth, no priv config.addV3User( snmpEngine, 'usr-none-none', ) config.addTargetParams(snmpEngine, 'my-creds', 'usr-none-none', 'noAuthNoPriv')
def _do_rpc_walk(self, id, request_object_id, object_id, res): pysnmp_var_names = [rfc1902.ObjectName(object_id)] self._conn.get_bulk(pysnmp_var_names, (self._on_rpc_walk, (id, request_object_id, res)))
def resolveWithMib(self, mibViewController): """Perform MIB variable ID conversion. Parameters ---------- mibViewController : :py:class:`~pysnmp.smi.view.MibViewController` class instance representing MIB browsing functionality. Returns ------- : :py:class:`~pysnmp.smi.rfc1902.ObjectIdentity` reference to itself Raises ------ SmiError In case of fatal MIB hanling errora Notes ----- Calling this method might cause the following sequence of events (exact details depends on many factors): * ASN.1 MIB file downloaded and handed over to :py:class:`~pysmi.compiler.MibCompiler` for conversion into Python MIB module (based on pysnmp classes) * Python MIB module is imported by SNMP engine, internal indices created * :py:class:`~pysnmp.smi.view.MibViewController` looks up the rest of MIB identification information based on whatever information is already available, :py:class:`~pysnmp.smi.rfc1902.ObjectIdentity` class instance gets updated and ready for further use. Examples -------- >>> objectIdentity = ObjectIdentity('SNMPv2-MIB', 'sysDescr') >>> objectIdentity.resolveWithMib(mibViewController) ObjectIdentity('SNMPv2-MIB', 'sysDescr') >>> """ if self.__mibSourcesToAdd is not None: debug.logger & debug.flagMIB and debug.logger('adding MIB sources %s' % ', '.join(self.__mibSourcesToAdd)) mibViewController.mibBuilder.addMibSources( *[ZipMibSource(x) for x in self.__mibSourcesToAdd] ) self.__mibSourcesToAdd = None if self.__asn1SourcesToAdd is None: addMibCompiler(mibViewController.mibBuilder, ifAvailable=True, ifNotAdded=True) else: debug.logger & debug.flagMIB and debug.logger( 'adding MIB compiler with source paths %s' % ', '.join(self.__asn1SourcesToAdd)) addMibCompiler( mibViewController.mibBuilder, sources=self.__asn1SourcesToAdd, searchers=self.__asn1SourcesOptions.get('searchers'), borrowers=self.__asn1SourcesOptions.get('borrowers'), destination=self.__asn1SourcesOptions.get('destination'), ifAvailable=self.__asn1SourcesOptions.get('ifAvailable'), ifNotAdded=self.__asn1SourcesOptions.get('ifNotAdded') ) self.__asn1SourcesToAdd = self.__asn1SourcesOptions = None if self.__modNamesToLoad is not None: debug.logger & debug.flagMIB and debug.logger('loading MIB modules %s' % ', '.join(self.__modNamesToLoad)) mibViewController.mibBuilder.loadModules(*self.__modNamesToLoad) self.__modNamesToLoad = None if self.__state & self.stClean: return self MibScalar, MibTableColumn = mibViewController.mibBuilder.importSymbols('SNMPv2-SMI', 'MibScalar', 'MibTableColumn') self.__indices = () if isinstance(self.__args[0], ObjectIdentity): self.__args[0].resolveWithMib(mibViewController) if len(self.__args) == 1: # OID or label or MIB module debug.logger & debug.flagMIB and debug.logger('resolving %s as OID or label' % self.__args) try: # pyasn1 ObjectIdentifier or sequence of ints or string OID self.__oid = rfc1902.ObjectName(self.__args[0]) # OID except PyAsn1Error: # sequence of sub-OIDs and labels if isinstance(self.__args[0], (list, tuple)): prefix, label, suffix = mibViewController.getNodeName( self.__args[0] ) # string label elif '.' in self.__args[0]: prefix, label, suffix = mibViewController.getNodeNameByOid( tuple(self.__args[0].split('.')) ) # MIB module name else: modName = self.__args[0] mibViewController.mibBuilder.loadModules(modName) if self.__kwargs.get('last'): prefix, label, suffix = mibViewController.getLastNodeName(modName) else: prefix, label, suffix = mibViewController.getFirstNodeName(modName) if suffix: try: suffix = tuple([int(x) for x in suffix]) except ValueError: raise SmiError('Unknown object name component %r' % (suffix,)) self.__oid = rfc1902.ObjectName(prefix + suffix) else: prefix, label, suffix = mibViewController.getNodeNameByOid( self.__oid ) debug.logger & debug.flagMIB and debug.logger( 'resolved %r into prefix %r and suffix %r' % (self.__args, prefix, suffix)) modName, symName, _ = mibViewController.getNodeLocation(prefix) self.__modName = modName self.__symName = symName self.__label = label mibNode, = mibViewController.mibBuilder.importSymbols( modName, symName ) self.__mibNode = mibNode debug.logger & debug.flagMIB and debug.logger('resolved prefix %r into MIB node %r' % (prefix, mibNode)) if isinstance(mibNode, MibTableColumn): # table column if suffix: rowModName, rowSymName, _ = mibViewController.getNodeLocation( mibNode.name[:-1] ) rowNode, = mibViewController.mibBuilder.importSymbols( rowModName, rowSymName ) self.__indices = rowNode.getIndicesFromInstId(suffix) elif isinstance(mibNode, MibScalar): # scalar if suffix: self.__indices = (rfc1902.ObjectName(suffix),) else: if suffix: self.__indices = (rfc1902.ObjectName(suffix),) self.__state |= self.stClean debug.logger & debug.flagMIB and debug.logger('resolved indices are %r' % (self.__indices,)) return self elif len(self.__args) > 1: # MIB, symbol[, index, index ...] # MIB, symbol, index, index if self.__args[0] and self.__args[1]: self.__modName = self.__args[0] self.__symName = self.__args[1] # MIB, '' elif self.__args[0]: mibViewController.mibBuilder.loadModules(self.__args[0]) if self.__kwargs.get('last'): prefix, label, suffix = mibViewController.getLastNodeName(self.__args[0]) else: prefix, label, suffix = mibViewController.getFirstNodeName(self.__args[0]) self.__modName, self.__symName, _ = mibViewController.getNodeLocation(prefix) # '', symbol, index, index else: prefix, label, suffix = mibViewController.getNodeName(self.__args[1:]) self.__modName, self.__symName, _ = mibViewController.getNodeLocation(prefix) mibNode, = mibViewController.mibBuilder.importSymbols( self.__modName, self.__symName ) self.__mibNode = mibNode self.__oid = rfc1902.ObjectName(mibNode.getName()) prefix, label, suffix = mibViewController.getNodeNameByOid( self.__oid ) self.__label = label debug.logger & debug.flagMIB and debug.logger( 'resolved %r into prefix %r and suffix %r' % (self.__args, prefix, suffix)) if isinstance(mibNode, MibTableColumn): # table rowModName, rowSymName, _ = mibViewController.getNodeLocation( mibNode.name[:-1] ) rowNode, = mibViewController.mibBuilder.importSymbols( rowModName, rowSymName ) if self.__args[2:]: try: instIds = rowNode.getInstIdFromIndices(*self.__args[2:]) self.__oid += instIds self.__indices = rowNode.getIndicesFromInstId(instIds) except PyAsn1Error: raise SmiError('Instance index %r to OID convertion failure at object %r: %s' % ( self.__args[2:], mibNode.getLabel(), sys.exc_info()[1])) elif self.__args[2:]: # any other kind of MIB node with indices if self.__args[2:]: instId = rfc1902.ObjectName( '.'.join([str(x) for x in self.__args[2:]]) ) self.__oid += instId self.__indices = (instId,) self.__state |= self.stClean debug.logger & debug.flagMIB and debug.logger('resolved indices are %r' % (self.__indices,)) return self else: raise SmiError('Non-OID, label or MIB symbol')
# Error/response reciever def receiveResponse(cbCtx): (errorIndication, errorStatus, errorIndex, varBinds) = cbCtx if errorIndication: print('Error: %s' % errorIndication) reactor.stop() return if errorStatus: print('Error: %s at %s' % (errorStatus.prettyPrint(), errorIndex)) reactor.stop() return for oid, val in varBinds: if val is None: print(oid.prettyPrint()) else: print('%s = %s' % (oid.prettyPrint(), val.prettyPrint())) reactor.stop() df = cmdgen.SetCommandGenerator().sendReq( snmpEngine, 'my-router', ( ((1,3,6,1,2,1,1,1,0), rfc1902.OctetString('Grinch')), ((1,3,6,1,2,1,1,2,0), rfc1902.ObjectName('1.3.6.1.4.1.20408.4')) ) ) df.addCallback(receiveResponse) # Run I/O dispatcher which would send pending queries and process response reactor.run()
ast = Parser().parse( Scanner().tokenize(' '.join(sys.argv[1:])) ) ctx = {} # Apply configuration to SNMP entity main.generator((snmpEngine, ctx), ast) msgmod.generator((snmpEngine, ctx), ast) secmod.generator((snmpEngine, ctx), ast) mibview.generator((snmpEngine, ctx), ast) target.generator((snmpEngine, ctx), ast) pdu.readPduGenerator((snmpEngine, ctx), ast) generator((snmpEngine, ctx), ast) ctx['myHeadVars'] = [ rfc1902.ObjectName(x[0]) for x in ctx['varBinds'] ] cmdgen.NextCommandGenerator().sendVarBinds( snmpEngine, ctx['addrName'], ctx.get('contextEngineId'), ctx.get('contextName', ''), ctx['varBinds'], cbFun, ctx ) snmpEngine.transportDispatcher.runDispatcher() except KeyboardInterrupt: sys.stderr.write('Shutting down...\n') except error.PySnmpError:
def main(): class CommandResponder(cmdrsp.CommandResponderBase): pduTypes = (rfc1905.SetRequestPDU.tagSet, rfc1905.GetRequestPDU.tagSet, rfc1905.GetNextRequestPDU.tagSet, rfc1905.GetBulkRequestPDU.tagSet) def handleMgmtOperation(self, snmpEngine, stateReference, contextName, pdu, acInfo): trunkReq = gCurrentRequestContext.copy() trunkReq['snmp-pdu'] = pdu pluginIdList = trunkReq['plugins-list'] logCtx = LogString(trunkReq) reqCtx = {} for pluginNum, pluginId in enumerate(pluginIdList): st, pdu = pluginManager.processCommandRequest( pluginId, snmpEngine, pdu, trunkReq, reqCtx) if st == status.BREAK: log.debug('plugin %s inhibits other plugins' % pluginId, ctx=logCtx) pluginIdList = pluginIdList[:pluginNum] break elif st == status.DROP: log.debug( 'received SNMP message, plugin %s muted request' % pluginId, ctx=logCtx) self.releaseStateInformation(stateReference) return elif st == status.RESPOND: log.debug( 'received SNMP message, plugin %s forced immediate response' % pluginId, ctx=logCtx) try: self.sendPdu(snmpEngine, stateReference, pdu) except PySnmpError: log.error('failure sending SNMP response', ctx=logCtx) else: self.releaseStateInformation(stateReference) return # pass query to trunk trunkIdList = trunkReq['trunk-id-list'] if trunkIdList is None: log.error('no route configured', ctx=logCtx) self.releaseStateInformation(stateReference) return for trunkId in trunkIdList: cbCtx = pluginIdList, trunkId, trunkReq, snmpEngine, stateReference, reqCtx try: msgId = trunkingManager.sendReq(trunkId, trunkReq, self.trunkCbFun, cbCtx) except SnmpfwdError: log.error( 'received SNMP message, message not sent to trunk "%s"' % sys.exc_info()[1], ctx=logCtx) return log.debug( 'received SNMP message, forwarded as trunk message #%s' % msgId, ctx=logCtx) def trunkCbFun(self, msgId, trunkRsp, cbCtx): pluginIdList, trunkId, trunkReq, snmpEngine, stateReference, reqCtx = cbCtx for key in tuple(trunkRsp): if key != 'callflow-id': trunkRsp['client-' + key] = trunkRsp[key] del trunkRsp[key] trunkRsp['callflow-id'] = trunkReq['callflow-id'] logCtx = LogString(trunkRsp) if trunkRsp['client-error-indication']: log.info( 'received trunk message #%s, remote end reported error-indication "%s", NOT responding' % (msgId, trunkRsp['client-error-indication']), ctx=logCtx) elif 'client-snmp-pdu' not in trunkRsp: log.info( 'received trunk message #%s, remote end does not send SNMP PDU, NOT responding' % msgId, ctx=logCtx) else: pdu = trunkRsp['client-snmp-pdu'] for pluginId in pluginIdList: st, pdu = pluginManager.processCommandResponse( pluginId, snmpEngine, pdu, trunkReq, reqCtx) if st == status.BREAK: log.debug('plugin %s inhibits other plugins' % pluginId, ctx=logCtx) break elif st == status.DROP: log.debug('plugin %s muted response' % pluginId, ctx=logCtx) self.releaseStateInformation(stateReference) return try: self.sendPdu(snmpEngine, stateReference, pdu) except PySnmpError: log.error('failure sending SNMP response', ctx=logCtx) else: log.debug( 'received trunk message #%s, forwarded as SNMP message' % msgId, ctx=logCtx) self.releaseStateInformation(stateReference) # # SNMPv3 NotificationReceiver implementation # class NotificationReceiver(ntfrcv.NotificationReceiver): pduTypes = (rfc1157.TrapPDU.tagSet, rfc1905.SNMPv2TrapPDU.tagSet) def processPdu(self, snmpEngine, messageProcessingModel, securityModel, securityName, securityLevel, contextEngineId, contextName, pduVersion, pdu, maxSizeResponseScopedPDU, stateReference): trunkReq = gCurrentRequestContext.copy() if messageProcessingModel == 0: pdu = rfc2576.v1ToV2(pdu) # TODO: why this is not automatic? v2c.apiTrapPDU.setDefaults(pdu) trunkReq['snmp-pdu'] = pdu pluginIdList = trunkReq['plugins-list'] logCtx = LogString(trunkReq) reqCtx = {} for pluginNum, pluginId in enumerate(pluginIdList): st, pdu = pluginManager.processNotificationRequest( pluginId, snmpEngine, pdu, trunkReq, reqCtx) if st == status.BREAK: log.debug('plugin %s inhibits other plugins' % pluginId, ctx=logCtx) pluginIdList = pluginIdList[:pluginNum] break elif st == status.DROP: log.debug('plugin %s muted request' % pluginId, ctx=logCtx) return elif st == status.RESPOND: log.debug('plugin %s NOT forced immediate response' % pluginId, ctx=logCtx) # TODO: implement immediate response for confirmed-class PDU return # pass query to trunk trunkIdList = trunkReq['trunk-id-list'] if trunkIdList is None: log.error('no route configured', ctx=logCtx) return for trunkId in trunkIdList: # TODO: pass messageProcessingModel to respond cbCtx = pluginIdList, trunkId, trunkReq, snmpEngine, stateReference, reqCtx try: msgId = trunkingManager.sendReq(trunkId, trunkReq, self.trunkCbFun, cbCtx) except SnmpfwdError: log.error( 'received SNMP message, message not sent to trunk "%s" %s' % (trunkId, sys.exc_info()[1]), ctx=logCtx) return log.debug( 'received SNMP message, forwarded as trunk message #%s' % msgId, ctx=logCtx) def trunkCbFun(self, msgId, trunkRsp, cbCtx): pluginIdList, trunkId, trunkReq, snmpEngine, stateReference, reqCtx = cbCtx for key in tuple(trunkRsp): if key != 'callflow-id': trunkRsp['client-' + key] = trunkRsp[key] del trunkRsp[key] trunkRsp['callflow-id'] = trunkReq['callflow-id'] logCtx = LazyLogString(trunkReq, trunkRsp) if trunkRsp['client-error-indication']: log.info( 'received trunk message #%s, remote end reported error-indication "%s", NOT responding' % (msgId, trunkRsp['client-error-indication']), ctx=logCtx) else: if 'client-snmp-pdu' not in trunkRsp: log.debug( 'received trunk message #%s -- unconfirmed SNMP message' % msgId, ctx=logCtx) return pdu = trunkRsp['client-snmp-pdu'] for pluginId in pluginIdList: st, pdu = pluginManager.processNotificationResponse( pluginId, snmpEngine, pdu, trunkReq, reqCtx) if st == status.BREAK: log.debug('plugin %s inhibits other plugins' % pluginId, ctx=logCtx) break elif st == status.DROP: log.debug( 'received trunk message #%s, plugin %s muted response' % (msgId, pluginId), ctx=logCtx) return log.debug( 'received trunk message #%s, forwarded as SNMP message' % msgId, ctx=logCtx) # TODO: implement response part # # Agent-side API complies with SMIv2 # if messageProcessingModel == 0: # PDU = rfc2576.v2ToV1(PDU, origPdu) # # statusInformation = {} # # # 3.4.3 # try: # snmpEngine.msgAndPduDsp.returnResponsePdu( # snmpEngine, messageProcessingModel, securityModel, # securityName, securityLevel, contextEngineId, # contextName, pduVersion, rspPDU, maxSizeResponseScopedPDU, # stateReference, statusInformation) # # except error.StatusInformation: # log.error('processPdu: stateReference %s, statusInformation %s' % (stateReference, sys.exc_info()[1])) class LogString(LazyLogString): GROUPINGS = [ ['callflow-id'], [ 'snmp-engine-id', 'snmp-transport-domain', 'snmp-bind-address', 'snmp-bind-port', 'snmp-security-model', 'snmp-security-level', 'snmp-security-name', 'snmp-credentials-id' ], ['snmp-context-engine-id', 'snmp-context-name', 'snmp-context-id'], ['snmp-pdu', 'snmp-content-id'], ['snmp-peer-address', 'snmp-peer-port', 'snmp-peer-id'], ['trunk-id'], ['client-snmp-pdu'], ] FORMATTERS = { 'client-snmp-pdu': LazyLogString.prettyVarBinds, 'snmp-pdu': LazyLogString.prettyVarBinds, } def securityAuditObserver(snmpEngine, execpoint, variables, cbCtx): securityModel = variables.get('securityModel', 0) logMsg = 'SNMPv%s auth failure' % securityModel logMsg += ' at %s:%s' % variables['transportAddress'].getLocalAddress() logMsg += ' from %s:%s' % variables['transportAddress'] statusInformation = variables.get('statusInformation', {}) if securityModel in (1, 2): logMsg += ' using snmp-community-name "%s"' % statusInformation.get( 'communityName', '?') elif securityModel == 3: logMsg += ' using snmp-usm-user "%s"' % statusInformation.get( 'msgUserName', '?') try: logMsg += ': %s' % statusInformation['errorIndication'] except KeyError: pass log.error(logMsg) def requestObserver(snmpEngine, execpoint, variables, cbCtx): trunkReq = { 'callflow-id': '%10.10x' % random.randint(0, 0xffffffffff), 'snmp-engine-id': snmpEngine.snmpEngineID, 'snmp-transport-domain': variables['transportDomain'], 'snmp-peer-address': variables['transportAddress'][0], 'snmp-peer-port': variables['transportAddress'][1], 'snmp-bind-address': variables['transportAddress'].getLocalAddress()[0], 'snmp-bind-port': variables['transportAddress'].getLocalAddress()[1], 'snmp-security-model': variables['securityModel'], 'snmp-security-level': variables['securityLevel'], 'snmp-security-name': variables['securityName'], 'snmp-context-engine-id': variables['contextEngineId'], 'snmp-context-name': variables['contextName'], } trunkReq['snmp-credentials-id'] = macro.expandMacro( credIdMap.get( (str(snmpEngine.snmpEngineID), variables['transportDomain'], variables['securityModel'], variables['securityLevel'], str(variables['securityName']))), trunkReq) k = '#'.join([ str(x) for x in (variables['contextEngineId'], variables['contextName']) ]) for x, y in contextIdList: if y.match(k): trunkReq['snmp-context-id'] = macro.expandMacro(x, trunkReq) break else: trunkReq['snmp-context-id'] = None addr = '%s:%s#%s:%s' % ( variables['transportAddress'][0], variables['transportAddress'][1], variables['transportAddress'].getLocalAddress()[0], variables['transportAddress'].getLocalAddress()[1]) for pat, peerId in peerIdMap.get(str(variables['transportDomain']), ()): if pat.match(addr): trunkReq['snmp-peer-id'] = macro.expandMacro(peerId, trunkReq) break else: trunkReq['snmp-peer-id'] = None pdu = variables['pdu'] if pdu.tagSet == v1.TrapPDU.tagSet: pdu = rfc2576.v1ToV2(pdu) v2c.apiTrapPDU.setDefaults(pdu) k = '#'.join([ snmpPduTypesMap.get(variables['pdu'].tagSet, '?'), '|'.join([str(x[0]) for x in v2c.apiTrapPDU.getVarBinds(pdu)]) ]) for x, y in contentIdList: if y.match(k): trunkReq['snmp-content-id'] = macro.expandMacro(x, trunkReq) break else: trunkReq['snmp-content-id'] = None trunkReq['plugins-list'] = pluginIdMap.get( (trunkReq['snmp-credentials-id'], trunkReq['snmp-context-id'], trunkReq['snmp-peer-id'], trunkReq['snmp-content-id']), []) trunkReq['trunk-id-list'] = trunkIdMap.get( (trunkReq['snmp-credentials-id'], trunkReq['snmp-context-id'], trunkReq['snmp-peer-id'], trunkReq['snmp-content-id'])) cbCtx.clear() cbCtx.update(trunkReq) # # main script starts here # helpMessage = """\ Usage: %s [--help] [--version ] [--debug-snmp=<%s>] [--debug-asn1=<%s>] [--daemonize] [--process-user=<uname>] [--process-group=<gname>] [--pid-file=<file>] [--logging-method=<%s[:args>]>] [--log-level=<%s>] [--config-file=<file>]""" % (sys.argv[0], '|'.join([ x for x in pysnmp_debug.flagMap.keys() if x != 'mibview' ]), '|'.join([x for x in pyasn1_debug.flagMap.keys()]), '|'.join( log.methodsMap.keys()), '|'.join(log.levelsMap)) try: opts, params = getopt.getopt(sys.argv[1:], 'hv', [ 'help', 'version', 'debug=', 'debug-snmp=', 'debug-asn1=', 'daemonize', 'process-user='******'process-group=', 'pid-file=', 'logging-method=', 'log-level=', 'config-file=' ]) except Exception: sys.stderr.write('ERROR: %s\r\n%s\r\n' % (sys.exc_info()[1], helpMessage)) return if params: sys.stderr.write('ERROR: extra arguments supplied %s\r\n%s\r\n' % (params, helpMessage)) return pidFile = '' cfgFile = CONFIG_FILE foregroundFlag = True procUser = procGroup = None loggingMethod = ['stderr'] loggingLevel = None for opt in opts: if opt[0] == '-h' or opt[0] == '--help': sys.stderr.write("""\ Synopsis: SNMP Proxy Forwarder: server part. Receives SNMP requests at one or many built-in SNMP Agents and routes them to encrypted trunks established with Forwarder's Manager part(s) running elsewhere. Can implement complex routing logic through analyzing parts of SNMP messages and matching them against proxying rules. Documentation: http://snmpfwd.sourceforge.io/ %s """ % helpMessage) return if opt[0] == '-v' or opt[0] == '--version': import snmpfwd import pysnmp import pyasn1 sys.stderr.write("""\ SNMP Proxy Forwarder version %s, written by Ilya Etingof <*****@*****.**> Using foundation libraries: pysnmp %s, pyasn1 %s. Python interpreter: %s Software documentation and support at https://github.com/etingof/snmpfwd %s """ % (snmpfwd.__version__, hasattr(pysnmp, '__version__') and pysnmp.__version__ or 'unknown', hasattr(pyasn1, '__version__') and pyasn1.__version__ or 'unknown', sys.version, helpMessage)) return elif opt[0] == '--debug-snmp': pysnmp_debug.setLogger( pysnmp_debug.Debug(*opt[1].split(','), **dict(loggerName=PROGRAM_NAME + '.pysnmp'))) elif opt[0] == '--debug-asn1': pyasn1_debug.setLogger( pyasn1_debug.Debug(*opt[1].split(','), **dict(loggerName=PROGRAM_NAME + '.pyasn1'))) elif opt[0] == '--daemonize': foregroundFlag = False elif opt[0] == '--process-user': procUser = opt[1] elif opt[0] == '--process-group': procGroup = opt[1] elif opt[0] == '--pid-file': pidFile = opt[1] elif opt[0] == '--logging-method': loggingMethod = opt[1].split(':') elif opt[0] == '--log-level': loggingLevel = opt[1] elif opt[0] == '--config-file': cfgFile = opt[1] try: log.setLogger(PROGRAM_NAME, *loggingMethod, **dict(force=True)) if loggingLevel: log.setLevel(loggingLevel) except SnmpfwdError: sys.stderr.write('%s\r\n%s\r\n' % (sys.exc_info()[1], helpMessage)) return try: cfgTree = cparser.Config().load(cfgFile) except SnmpfwdError: log.error('configuration parsing error: %s' % sys.exc_info()[1]) return if cfgTree.getAttrValue('program-name', '', default=None) != PROGRAM_NAME: log.error('config file %s does not match program name %s' % (cfgFile, PROGRAM_NAME)) return if cfgTree.getAttrValue('config-version', '', default=None) != CONFIG_VERSION: log.error( 'config file %s version is not compatible with program version %s' % (cfgFile, CONFIG_VERSION)) return random.seed() gCurrentRequestContext = {} credIdMap = {} peerIdMap = {} contextIdList = [] contentIdList = [] pluginIdMap = {} trunkIdMap = {} engineIdMap = {} transportDispatcher = AsynsockDispatcher() transportDispatcher.registerRoutingCbFun(lambda td, t, d: td) transportDispatcher.setSocketMap() # use global asyncore socket map # # Initialize plugin modules # pluginManager = PluginManager(macro.expandMacros( cfgTree.getAttrValue('plugin-modules-path-list', '', default=[], vector=True), {'config-dir': os.path.dirname(cfgFile)}), progId=PROGRAM_NAME, apiVer=PLUGIN_API_VERSION) for pluginCfgPath in cfgTree.getPathsToAttr('plugin-id'): pluginId = cfgTree.getAttrValue('plugin-id', *pluginCfgPath) pluginMod = cfgTree.getAttrValue('plugin-module', *pluginCfgPath) pluginOptions = macro.expandMacros( cfgTree.getAttrValue('plugin-options', *pluginCfgPath, **dict(default=[], vector=True)), {'config-dir': os.path.dirname(cfgFile)}) log.info( 'configuring plugin ID %s (at %s) from module %s with options %s...' % (pluginId, '.'.join(pluginCfgPath), pluginMod, ', '.join(pluginOptions) or '<none>')) try: pluginManager.loadPlugin(pluginId, pluginMod, pluginOptions) except SnmpfwdError: log.error('plugin %s not loaded: %s' % (pluginId, sys.exc_info()[1])) return for configEntryPath in cfgTree.getPathsToAttr('snmp-credentials-id'): credId = cfgTree.getAttrValue('snmp-credentials-id', *configEntryPath) configKey = [] log.info('configuring snmp-credentials %s (at %s)...' % (credId, '.'.join(configEntryPath))) engineId = cfgTree.getAttrValue('snmp-engine-id', *configEntryPath) if engineId in engineIdMap: snmpEngine, snmpContext, snmpEngineMap = engineIdMap[engineId] log.info('using engine-id %s' % snmpEngine.snmpEngineID.prettyPrint()) else: snmpEngine = engine.SnmpEngine(snmpEngineID=engineId) snmpContext = context.SnmpContext(snmpEngine) snmpEngineMap = {'transportDomain': {}, 'securityName': {}} snmpEngine.observer.registerObserver( securityAuditObserver, 'rfc2576.prepareDataElements:sm-failure', 'rfc3412.prepareDataElements:sm-failure', cbCtx=gCurrentRequestContext) snmpEngine.observer.registerObserver( requestObserver, 'rfc3412.receiveMessage:request', cbCtx=gCurrentRequestContext) CommandResponder(snmpEngine, snmpContext) NotificationReceiver(snmpEngine, None) engineIdMap[engineId] = snmpEngine, snmpContext, snmpEngineMap log.info('new engine-id %s' % snmpEngine.snmpEngineID.prettyPrint()) configKey.append(str(snmpEngine.snmpEngineID)) transportDomain = cfgTree.getAttrValue('snmp-transport-domain', *configEntryPath) transportDomain = rfc1902.ObjectName(transportDomain) if transportDomain in snmpEngineMap['transportDomain']: h, p, transportDomain = snmpEngineMap['transportDomain'][ transportDomain] log.info('using transport endpoint %s:%s, transport ID %s' % (h, p, transportDomain)) else: if transportDomain[:len(udp.domainName)] == udp.domainName: transport = udp.UdpTransport() elif transportDomain[:len(udp6.domainName)] == udp6.domainName: transport = udp6.Udp6Transport() else: log.error('unknown transport domain %s' % (transportDomain, )) return h, p = cfgTree.getAttrValue('snmp-bind-address', *configEntryPath).split(':', 1) snmpEngine.registerTransportDispatcher(transportDispatcher, transportDomain) transportOptions = cfgTree.getAttrValue( 'snmp-transport-options', *configEntryPath, **dict(default=[], vector=True)) t = transport.openServerMode((h, int(p))) if 'transparent-proxy' in transportOptions: t.enablePktInfo() t.enableTransparent() elif 'virtual-interface' in transportOptions: t.enablePktInfo() config.addSocketTransport(snmpEngine, transportDomain, t) snmpEngineMap['transportDomain'][ transportDomain] = h, p, transportDomain log.info( 'new transport endpoint %s:%s, options %s, transport ID %s' % (h, p, transportOptions and '/'.join(transportOptions) or '<none>', transportDomain)) configKey.append(transportDomain) securityModel = cfgTree.getAttrValue('snmp-security-model', *configEntryPath) securityModel = rfc1902.Integer(securityModel) securityLevel = cfgTree.getAttrValue('snmp-security-level', *configEntryPath) securityLevel = rfc1902.Integer(securityLevel) securityName = cfgTree.getAttrValue('snmp-security-name', *configEntryPath) if securityModel in (1, 2): if securityName in snmpEngineMap['securityName']: if snmpEngineMap['securityName'][ securityModel] == securityModel: log.info('using security-name %s' % securityName) else: raise SnmpfwdError( 'snmp-security-name %s already in use at snmp-security-model %s' % (securityName, securityModel)) else: communityName = cfgTree.getAttrValue('snmp-community-name', *configEntryPath) config.addV1System(snmpEngine, securityName, communityName, securityName=securityName) log.info( 'new community-name %s, security-model %s, security-name %s, security-level %s' % (communityName, securityModel, securityName, securityLevel)) snmpEngineMap['securityName'][securityName] = securityModel configKey.append(securityModel) configKey.append(securityLevel) configKey.append(securityName) elif securityModel == 3: if securityName in snmpEngineMap['securityName']: log.info('using USM security-name: %s' % securityName) else: usmUser = cfgTree.getAttrValue('snmp-usm-user', *configEntryPath) log.info( 'new USM user %s, security-model %s, security-level %s, security-name %s' % (usmUser, securityModel, securityLevel, securityName)) if securityLevel in (2, 3): usmAuthProto = cfgTree.getAttrValue( 'snmp-usm-auth-protocol', *configEntryPath, **dict(default=config.usmHMACMD5AuthProtocol)) usmAuthProto = rfc1902.ObjectName(usmAuthProto) usmAuthKey = cfgTree.getAttrValue('snmp-usm-auth-key', *configEntryPath) log.info( 'new USM authentication key: %s, authentication protocol: %s' % (usmAuthKey, usmAuthProto)) if securityLevel == 3: usmPrivProto = cfgTree.getAttrValue( 'snmp-usm-priv-protocol', *configEntryPath, **dict(default=config.usmDESPrivProtocol)) usmPrivProto = rfc1902.ObjectName(usmPrivProto) usmPrivKey = cfgTree.getAttrValue( 'snmp-usm-priv-key', *configEntryPath, **dict(default=None)) log.info( 'new USM encryption key: %s, encryption protocol: %s' % (usmPrivKey, usmPrivProto)) config.addV3User(snmpEngine, usmUser, usmAuthProto, usmAuthKey, usmPrivProto, usmPrivKey) else: config.addV3User(snmpEngine, usmUser, usmAuthProto, usmAuthKey) else: config.addV3User(snmpEngine, usmUser) snmpEngineMap['securityName'][securityName] = securityModel configKey.append(securityModel) configKey.append(securityLevel) configKey.append(securityName) else: raise SnmpfwdError('unknown snmp-security-model: %s' % securityModel) configKey = tuple(configKey) if configKey in credIdMap: log.error( 'ambiguous configuration for key snmp-credentials-id=%s at %s' % (credId, '.'.join(configEntryPath))) return credIdMap[configKey] = credId duplicates = {} for peerCfgPath in cfgTree.getPathsToAttr('snmp-peer-id'): peerId = cfgTree.getAttrValue('snmp-peer-id', *peerCfgPath) if peerId in duplicates: log.error( 'duplicate snmp-peer-id=%s at %s and %s' % (peerId, '.'.join(peerCfgPath), '.'.join(duplicates[peerId]))) return duplicates[peerId] = peerCfgPath log.info('configuring peer ID %s (at %s)...' % (peerId, '.'.join(peerCfgPath))) transportDomain = cfgTree.getAttrValue('snmp-transport-domain', *peerCfgPath) if transportDomain not in peerIdMap: peerIdMap[transportDomain] = [] for peerAddress in cfgTree.getAttrValue( 'snmp-peer-address-pattern-list', *peerCfgPath, **dict(vector=True)): for bindAddress in cfgTree.getAttrValue( 'snmp-bind-address-pattern-list', *peerCfgPath, **dict(vector=True)): peerIdMap[transportDomain].append( (re.compile(peerAddress + '#' + bindAddress), peerId)) duplicates = {} for contextCfgPath in cfgTree.getPathsToAttr('snmp-context-id'): contextId = cfgTree.getAttrValue('snmp-context-id', *contextCfgPath) if contextId in duplicates: log.error('duplicate snmp-context-id=%s at %s and %s' % (contextId, '.'.join(contextCfgPath), '.'.join( duplicates[contextId]))) return duplicates[contextId] = contextCfgPath k = '#'.join((cfgTree.getAttrValue('snmp-context-engine-id-pattern', *contextCfgPath), cfgTree.getAttrValue('snmp-context-name-pattern', *contextCfgPath))) log.info('configuring context ID %s (at %s), composite key: %s' % (contextId, '.'.join(contextCfgPath), k)) contextIdList.append((contextId, re.compile(k))) duplicates = {} for contentCfgPath in cfgTree.getPathsToAttr('snmp-content-id'): contentId = cfgTree.getAttrValue('snmp-content-id', *contentCfgPath) if contentId in duplicates: log.error('duplicate snmp-content-id=%s at %s and %s' % (contentId, '.'.join(contentCfgPath), '.'.join( duplicates[contentId]))) return duplicates[contentId] = contentCfgPath for x in cfgTree.getAttrValue('snmp-pdu-oid-prefix-pattern-list', *contentCfgPath, **dict(vector=True)): k = '#'.join([ cfgTree.getAttrValue('snmp-pdu-type-pattern', *contentCfgPath), x ]) log.info('configuring content ID %s (at %s), composite key: %s' % (contentId, '.'.join(contentCfgPath), k)) contentIdList.append((contentId, re.compile(k))) del duplicates for pluginCfgPath in cfgTree.getPathsToAttr('using-plugin-id-list'): pluginIdList = cfgTree.getAttrValue('using-plugin-id-list', *pluginCfgPath, **dict(vector=True)) log.info('configuring plugin ID(s) %s (at %s)...' % (','.join(pluginIdList), '.'.join(pluginCfgPath))) for credId in cfgTree.getAttrValue('matching-snmp-credentials-id-list', *pluginCfgPath, **dict(vector=True)): for peerId in cfgTree.getAttrValue('matching-snmp-peer-id-list', *pluginCfgPath, **dict(vector=True)): for contextId in cfgTree.getAttrValue( 'matching-snmp-context-id-list', *pluginCfgPath, **dict(vector=True)): for contentId in cfgTree.getAttrValue( 'matching-snmp-content-id-list', *pluginCfgPath, **dict(vector=True)): k = credId, contextId, peerId, contentId if k in pluginIdMap: log.error( 'duplicate snmp-credentials-id %s, snmp-context-id %s, snmp-peer-id %s, snmp-content-id %s at plugin-id(s) %s' % (credId, contextId, peerId, contentId, ','.join(pluginIdList))) return else: log.info( 'configuring plugin(s) %s (at %s), composite key: %s' % (','.join(pluginIdList), '.'.join(pluginCfgPath), '/'.join(k))) for pluginId in pluginIdList: if not pluginManager.hasPlugin(pluginId): log.error( 'undefined plugin ID %s referenced at %s' % (pluginId, '.'.join(pluginCfgPath))) return pluginIdMap[k] = pluginIdList for routeCfgPath in cfgTree.getPathsToAttr('using-trunk-id-list'): trunkIdList = cfgTree.getAttrValue('using-trunk-id-list', *routeCfgPath, **dict(vector=True)) log.info('configuring destination trunk ID(s) %s (at %s)...' % (','.join(trunkIdList), '.'.join(routeCfgPath))) for credId in cfgTree.getAttrValue('matching-snmp-credentials-id-list', *routeCfgPath, **dict(vector=True)): for peerId in cfgTree.getAttrValue('matching-snmp-peer-id-list', *routeCfgPath, **dict(vector=True)): for contextId in cfgTree.getAttrValue( 'matching-snmp-context-id-list', *routeCfgPath, **dict(vector=True)): for contentId in cfgTree.getAttrValue( 'matching-snmp-content-id-list', *routeCfgPath, **dict(vector=True)): k = credId, contextId, peerId, contentId if k in trunkIdMap: log.error( 'duplicate snmp-credentials-id %s, snmp-context-id %s, snmp-peer-id %s, snmp-content-id %s at trunk-id(s) %s' % (credId, contextId, peerId, contentId, ','.join(trunkIdList))) return else: trunkIdMap[k] = trunkIdList log.info( 'configuring trunk routing to %s (at %s), composite key: %s' % (','.join(trunkIdList), '.'.join(routeCfgPath), '/'.join(k))) def dataCbFun(trunkId, msgId, msg): log.debug('message ID %s received from trunk %s' % (msgId, trunkId)) trunkingManager = TrunkingManager(dataCbFun) def getTrunkAddr(a, port=0): f = lambda h, p=port: (h, int(p)) try: return f(*a.split(':')) except Exception: raise SnmpfwdError('improper IPv4 endpoint %s' % a) for trunkCfgPath in cfgTree.getPathsToAttr('trunk-id'): trunkId = cfgTree.getAttrValue('trunk-id', *trunkCfgPath) secret = cfgTree.getAttrValue('trunk-crypto-key', *trunkCfgPath, **dict(default='')) secret = secret and (secret * ((16 // len(secret)) + 1))[:16] log.info('configuring trunk ID %s (at %s)...' % (trunkId, '.'.join(trunkCfgPath))) connectionMode = cfgTree.getAttrValue('trunk-connection-mode', *trunkCfgPath) if connectionMode == 'client': trunkingManager.addClient( trunkId, getTrunkAddr( cfgTree.getAttrValue('trunk-bind-address', *trunkCfgPath)), getTrunkAddr( cfgTree.getAttrValue('trunk-peer-address', *trunkCfgPath), 30201), cfgTree.getAttrValue('trunk-ping-period', *trunkCfgPath, default=0, expect=int), secret) log.info( 'new trunking client from %s to %s' % (cfgTree.getAttrValue('trunk-bind-address', *trunkCfgPath), cfgTree.getAttrValue('trunk-peer-address', *trunkCfgPath))) if connectionMode == 'server': trunkingManager.addServer( getTrunkAddr( cfgTree.getAttrValue('trunk-bind-address', *trunkCfgPath), 30201), cfgTree.getAttrValue('trunk-ping-period', *trunkCfgPath, default=0, expect=int), secret) log.info( 'new trunking server at %s' % (cfgTree.getAttrValue('trunk-bind-address', *trunkCfgPath))) transportDispatcher.registerTimerCbFun(trunkingManager.setupTrunks, random.randrange(1, 5)) transportDispatcher.registerTimerCbFun(trunkingManager.monitorTrunks, random.randrange(1, 5)) try: daemon.dropPrivileges(procUser, procGroup) except Exception: log.error('can not drop privileges: %s' % sys.exc_info()[1]) return if not foregroundFlag: try: daemon.daemonize(pidFile) except Exception: log.error('can not daemonize process: %s' % sys.exc_info()[1]) return # Run mainloop log.info('starting I/O engine...') transportDispatcher.jobStarted(1) # server job would never finish # Python 2.4 does not support the "finally" clause while True: try: transportDispatcher.runDispatcher() except (PySnmpError, SnmpfwdError, socket.error): log.error(str(sys.exc_info()[1])) continue except Exception: transportDispatcher.closeDispatcher() raise