import urllib.request import re import sys import os import argparse import filecmp def main(): parser = argparse.ArgumentParser(description='Check numeric values on a webpage against Nagios thresholds or check to see if the page(or part of it) changes.') parser.add_argument('-s', default=False, action='store_true', help="Suppress printing value.") parser.add_argument('-u', metavar='URL', type=str, help="URL to extact value from.") parser.add_argument('-r', metavar='REGEX', type=str, help="Regex to match returned results. The plugin looks for the first capturing group in the match - regex must include grouping parentheses()") parser.add_argument('-f', metavar='FILE', type=str, help="File to store results to subsequently check against. Only used if -w and -c are not used. Default filename is results.txt", default="results.txt") parser.add_argument('-p', metavar='PERFDATA LABEL', type=str, help="Label to apply to performance data. Default label is 'value'. Peformance data only available if -w or -c are used", default="value") parser.add_argument('-w', metavar='WARNING THRESHOLD', type=str, help="Warning threshold to check the returned value against") parser.add_argument('-c', metavar='CRITICAL THRESHOLD', type=str, help="Critical threshold to check the returned value against") args = parser.parse_args() suppress=args.s url=args.u regex=args.r file=args.f warning=args.w critical=args.c perfdatalabel=args.p tmpFile = file + ".tmp" if not url: nagios_exit("url is required", check_status.UNKNOWN) if not regex: nagios_exit("regex is required", check_status.UNKNOWN) url_request=urllib.request.urlopen(url) data = url_request.read().decode("UTF-8") match = re.search(regex, str(data), re.DOTALL) if match: newValue = match.group(1) f = open(tmpFile, 'w+') f.write(newValue) f.flush() f.close else: nagios_exit("regex did not match: " + str(data), check_status.UNKNOWN) if not warning and not critical: if not os.path.exists(file): os.rename(tmpFile, file) nagios_exit("inital check", check_status.OK) filecmp.clear_cache() same = filecmp.cmp(file, tmpFile, shallow=False) if not same: os.remove(file) os.rename(tmpFile,file) if suppress: nagios_exit("value changed", check_status.CRITICAL) else: nagios_exit("value changed to: " + newValue, check_status.CRITICAL) else: if suppress: nagios_exit("Value remains the same", check_status.OK) else: nagios_exit("Value remains the same: " + newValue, check_status.OK) else: if critical: criticalRange = threshold_string_to_tuple(critical) nagios_exit(newValue, check_status.CRITICAL, perfdata=" | " + perfdatalabel + "=" + str(newValue)) if warning: warningRange = threshold_string_to_tuple(warning) nagios_exit(newValue, check_status.WARNING, perfdata=" | " + perfdatalabel + "=" + str(newValue)) def check_alarm(value, rangeTuple): low = rangeTuple[0] high = rangeTuple[1] inclusive = rangeTuple[2] if inclusive: return float(value) >= low and float(value) <= high else: return float(value) < low or float(value) > high def threshold_string_to_tuple(threshold_string): # Default values if not set low = 0 high = float('inf') # ok even if we use integers otherwise inclusive = False if not threshold_string: threshold_string = "~:" if threshold_string[0] == "@": inclusive = True threshold_string = threshold_string[1:] threshold_list = threshold_string.split(":") length = len(threshold_list) if length < 1 or length > 2: nagios_exit("Thresholds are composed of 1-2 arguments.", check_status.UNKNOWN) elif length == 1: try: high = float(threshold_list[0]) except ValueError: nagios_exit(str(threshold_list[0]) + " could not be converted to a number", check_status.UNKNOWN) elif length == 2: if not threshold_list[0]: threshold_list[0] = "0" if (threshold_list[0] == "~" or threshold_list[0] == os.path.expanduser("~")): threshold_list[0] = "-inf" if not threshold_list[1]: threshold_list[1] = 'inf' try: low = float(threshold_list[0]) high = float(threshold_list[1]) except ValueError: nagios_exit("One of " + str(threshold_list) + " could not be converted to a number", check_status.UNKNOWN) if low > high: nagios_exit("The threshold minimum " + str(low) + " must be less than the maximum " + str(high), check_status.UNKNOWN) return low, high, inclusive def nagios_exit(out, code, perfdata="", verbose_out=""): prefix = code_to_status(code) out = prefix + ": " + out out += perfdata print(out) exit(code) class check_status(): OK = 0 WARNING = 1 CRITICAL = 2 UNKNOWN = 3 def code_to_status(return_code): if return_code < check_status.OK or return_code > check_status.UNKNOWN: return_code = check_status.UNKNOWN words = ["OK", "WARNING", "CRITICAL", "UNKNOWN"] return words[return_code] if __name__ == "__main__": main()