#!/usr/bin/env python # # Nagios check if interlink is still found via SNMP # # Orginal by Richard van Mansom # Modified to be more readable and debugable by Rick van der Zwet # from collections import defaultdict from pysnmp.entity.rfc3413.oneliner import cmdgen from pysnmp.proto.rfc1902 import OctetString from gettext import gettext as _ import argparse import logging import operator import socket import sys logging.basicConfig(level=logging.INFO) logger = logging.getLogger() class ERRNO(): OK = 0 WARNING = 1 CRITICAL = 2 UNKNOWN = 3 def snmpwalk(host, oid): errorIndication, errorStatus, errorIndex, varBinds = cmdgen.CommandGenerator().bulkCmd( cmdgen.CommunityData('my-agent', 'public', 1), cmdgen.UdpTransportTarget((host, 161)), 15, 0, # nonRepeaters, maxRepetitions oid ) return varBinds def get_all_macs(host): all_macs = defaultdict(list) lines = snmpwalk(host, (1,3,6,1,2,1,3,1,1,2)) for line in lines: try: if line[0][0][9] == 2: data = line[0][1] data = ":".join("%02x" % ord(v) for v in data) iface_oid = line[0][0][10] if len(data) == 17: all_macs[iface_oid].append(data) except IndexError: continue logger.debug("SNMP MAC per Interface OID: %s", all_macs) return all_macs def get_local_interfaces(host): local_ifaces = {} lines = snmpwalk(host , (1,3,6,1,2,1,2,2,1,2)) for line in lines: data = str(line[0][1]) if len(data) > 1 : index = line[0][0][-1] local_ifaces[data] = index logger.debug("SNMP Interface Walk: %s", local_ifaces) return local_ifaces def get_local_mac_list(host): local_macs = [] lines = snmpwalk ( host , (1,3,6,1,2,1,2,2,1,6)) for line in lines: data = line[0][1] if len(str(data)) > 1: data = ":".join("%02x" % ord(v) for v in data) local_macs.append(data) logger.debug("SNMP Local Interfaces: %s", local_macs) return local_macs def oui_filter(mac): mac = mac.upper() blocked_oui = ('00:15:6D', 'Dummy') return not any([mac.startswith(x) for x in blocked_oui]) def main(host, checked_ifaces): try: local_macs = sorted(get_local_mac_list(host)) local_ifaces = get_local_interfaces(host) all_macs = get_all_macs(host) except socket.gaierror as e: return ("Failed: SNMP for host cannot be queried - %s" % e, ERRNO.CRITICAL) except Exception as e: return ("Failed: Script Exception - [%s] %s" % (type(e),e), ERRNO.CRITICAL) if not local_ifaces or not local_macs or not all_macs: return ("Failed: No results", ERRNO.UNKNOWN) missing_ifaces = list(set(checked_ifaces) - set(local_ifaces.keys())) if missing_ifaces: return ("Failed: No results for %s (suggestions: %s)" % (missing_ifaces, local_ifaces.keys()), ERRNO.UNKNOWN) logger.debug("Interfaces available for checking: %s", checked_ifaces) # Process all interfaces and find out if their are still MACs found on the other side failed_ifaces = [] for iface in checked_ifaces: filtered_macs = filter(oui_filter, sorted(all_macs[local_ifaces[iface]])) logger.debug("Availble MACs after filtering: %s", filtered_macs) # Remove Local MACs remote_macs = list(set(filtered_macs) - set(local_macs)) if not remote_macs: failed_ifaces.append(iface) if failed_ifaces: return ("Failed: No remote MACs found for: %s" % failed_ifaces, ERRNO.CRITICAL) # Everything is happy if len(checked_ifaces) == 1: return ("OK: Interlink on %s is active" % checked_ifaces[0], ERRNO.OK) else: return ("OK: Interlinks on %s are active" % ','.join(checked_ifaces), ERRNO.OK) if __name__ == '__main__': class NagiosArgumentParser(argparse.ArgumentParser): ''' Like exit code 128 on errors''' def error(self, message): self.print_usage(sys.stderr) self.exit(128, _('%s: error: %s\n') % (self.prog, message)) parser = NagiosArgumentParser( description='Nagios Check InterLinks via SNMP.', formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument('-g', dest='debug', action='store_true', default=False, help="Set logging to debug") parser.add_argument('host', action='store', type=str, help="Hostname to use") parser.add_argument('ifaces', action='store', type=str, help="Interfaces", nargs='+') args = parser.parse_args() # Make the interfaces a valid list by combining the comma arguments into a list args.ifaces = sorted(reduce(operator.add, [x.split(',') for x in args.ifaces])) if args.debug: logger.setLevel(logging.DEBUG) (msg, errno) = main(args.host, args.ifaces) sys.stdout.write(msg + "\n") sys.exit(errno)