#!/usr/bin/python import dns.resolver import re import logging import sys from optparse import OptionParser parser = OptionParser() parser.add_option('-H', '--host', action="store", type="string", dest="host", help="The host IP address to check") parser.add_option('-B', action="store", type="string", dest="bl", help="Blacklist server(s) (separated by commas)") parser.add_option('-t', '--timeout', action="store", type="int", dest="timeout", help="Timeout in second for DNS request (Default : 10s)", default='10') parser.add_option('-c', action="store", type="int", dest="critical_count", help="Critical blacklisted count") parser.add_option('-C', action="store", type="int", dest="critical_perc", help="Critical blacklisted count as percentage (Default : 30%)", default=30) parser.add_option('-w', action="store", type="int", dest="warning_count", help="Warning blacklisted count") parser.add_option('-W', action="store", type="int", dest="warning_perc", help="Warning blacklisted count as percentage (Default : 1%)", default=1) parser.add_option('-u', action="store", type="int", dest="unknown_count", help="Checking errors count triggering UNKNOWN status") parser.add_option('-U', action="store", type="int", dest="unknown_perc", help="Checking errors count as percentage triggering UNKNOWN status (Default : 50%)", default=50) parser.add_option('-T', '--threaded', action="store_true", help="Use thread for parallel checking", dest="thread") parser.add_option('-v', '--verbose', action="store_true", help="Enable verbose mode", dest="verbose") parser.add_option('-d', '--debug', action="store_true", help="Enable debug mode", dest="debug") (options, args) = parser.parse_args() logging.basicConfig(format='%(asctime)s - %(levelname)s - %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p') logger = logging.getLogger('BlChecker') if options.debug: logger.setLevel(logging.DEBUG) elif options.verbose: logger.setLevel(logging.INFO) else: logger.setLevel(logging.WARNING) logger.debug('Start with parameters : %s' % options) # Check parameters if not options.host or not options.bl: logger.error('You must provide host IP address and blacklist server(s)') sys.exit(3) options.host=options.host.strip() def parseIP(ip): return re.match('([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})',ip) parsed_ip=parseIP(options.host) if not parsed_ip: logger.debug('Host parameter is not a valid IP address, try to resolv it as DNS name') try: import socket res=socket.gethostbyname_ex(options.host) logger.debug('gethostbyname_ex() result : %s' % str(res)) ip=res[2][0] parsed_ip=parseIP(ip) if not parsed_ip: raise Exception('Fail to parse resolved IP %s address on name %s' % (ip,options.host)) logger.info('Resolved IP address corresponding to %s : %s' % (options.host,ip)) except socket.gaierror: logger.error('Invalid host parameter (-H)') sys.exit(3) except Exception, e: logger.error('Error resolving name %s : %s' % (options.host,e)) sys.exit(3) # Check method def check(parsed_ip,bl,logger=None,timeout=10): resolver = dns.resolver.Resolver() resolver.timeout = timeout resolver.lifetime = timeout lookup="%s.%s.%s.%s.%s" % (parsed_ip.group(4),parsed_ip.group(3),parsed_ip.group(2),parsed_ip.group(1),bl) try: r=resolver.query(lookup,'TXT') logger.info('Listed on BL %s (%s)' % (bl,lookup)) return True except dns.resolver.NXDOMAIN: logger.debug('Not listed on BL %s' % bl) return False except dns.exception.Timeout: logger.info('Timeout looking on BL %s (%s)' % (bl,lookup)) except Exception, e: logger.warning('Error looking on BL %s (%s) : %s' % (bl,lookup,e)) # Split BL from args BLs=options.bl.split(',') BLs_count=len(BLs) logger.debug('BLs count : %s' % BLs_count) #### Check #### results={} if options.thread: # Import lib from threading import Thread # Define Threader class class BlChecker(Thread): def __init__(self,parsed_ip,bl,timeout=10,logger=None): Thread.__init__(self) if logger: self.logger=logger else: self.logger=logging.getLogger('BlChecker') self.parsed_ip = parsed_ip self.bl = bl self.timeout = timeout def run(self): results[bl]=check(self.parsed_ip,self.bl,logger=self.logger,timeout=self.timeout) # Create and start a thread for check on all BL threads=[] for bl in BLs: th=BlChecker(parsed_ip,bl.strip(),timeout=options.timeout,logger=logger) th.start() threads.append(th) # Wait all BL checks ended for th in threads: th.join() else: for bl in BLs: bl=bl.strip() results[bl]=check(parsed_ip,bl,timeout=options.timeout,logger=logger) # Check BL check result and determine listed/error counts listed=[] error=[] for bl in results: if results[bl] == -1: error.append(bl) elif results[bl]: listed.append(bl) listed_count=len(listed) error_count=len(error) # Calculate thresholds if options.critical_count: critical_count=options.critical_count else: critical_count=int(round(BLs_count*options.critical_perc/100)) if critical_count==0: critical_count=1 if options.warning_count: warning_count=options.warning_count else: warning_count=int(round(BLs_count*options.warning_perc/100)) if warning_count==0: warning_count=1 if options.unknown_count: unknown_count=options.unknown_count else: unknown_count=int(round(BLs_count*options.unknown_perc/100)) if unknown_count==0: unknown_count=1 logger.debug('Max : CRITICAL = %s / WARNING = %s / UNKNOWN = %s' % (critical_count,warning_count,unknown_count)) # Determine check result result='OK' result_code=0 if listed_count>=critical_count: result='CRITICAL' result_code=2 elif listed_count>=warning_count: result='WARNING' result_code=1 elif error_count>=unknown_count: result='UNKNOWN' result_code=3 # Compose return message if listed_count==0: msg="%s : Not listed" % result else: msg="%s : Listed on %s" % (result,', '.join(listed)) if error_count>0: msg+=", error checking blacklist(s) %s" % ', '.join(error) # Add performance data # (Syntax : 'label'=value[UOM];[warn];[crit];[min];[max]) msg+="|listed=%d;%d;%d;%d;%d" % (listed_count,warning_count,critical_count,0,BLs_count) msg+=",check_errors=%d;%d;%d;%d;%d" % (error_count,warning_count,critical_count,0,BLs_count) # Print result and exit print msg sys.exit(result_code)