Ejemplo n.º 1
0
    def __init__(self):
        self._long_output = []
        self._summary = []
        self.thresholds = []
        self._perfdata = PerfData()  # Performance and Threshold Metrics are stored here

        self.parser = OptionParser()
        general = OptionGroup(self.parser, "Generic Options")
        self.parser.add_option('--threshold','--th',default=[], help="Thresholds in standard nagios threshold format", metavar='', dest="thresholds",action="append")

        display_group = OptionGroup(self.parser, "Display Options")
        display_group.add_option("-v", "--verbose", dest="verbose", help="Print more verbose info", metavar="v", action="store_true", default=self.verbose)
        general.add_option("-d", "--debug", dest="show_debug", help="Print debug info", metavar="d", action="store_true", default=self.show_debug)
        display_group.add_option("--no-perfdata", dest="show_perfdata", help="Dont show any performance data", action="store_false", default=self.show_perfdata)
        display_group.add_option("--no-longoutput", dest="show_longoutput", help="Hide longoutput from the plugin output (i.e. only display first line of the output)", action="store_false", default=self.show_longoutput)
        display_group.add_option("--no-summary", dest="show_summary", help="Hide summary from plugin output", action="store_false", default=self.show_summary)
        #display_group.add_option("--show-status-in-summary", dest="show_status_in_summary", help="Prefix the summary of the plugin with OK- or WARN- ", action="store_true", default=False)
        display_group.add_option("--get-metrics", dest="get_metrics", help="Print all available metrics and exit (can be combined with --verbose)", action="store_true", default=False)
        display_group.add_option("--legacy", dest="show_legacy", help="Output perfdata in legacy format", action="store_true", default=self.show_legacy)
        self.parser.add_option_group(display_group)
Ejemplo n.º 2
0
class PluginHelper:
    """ PluginHelper takes away some of the tedious work of writing Nagios plugins. Primary features include:

    * Keep a collection of your plugin messages (queue for both summary and longoutput)
    * Keep record of exit status
    * Keep a collection of your metrics (for both perfdata and thresholds)
    * Automatic Command-line arguments
    * Make sure output of your plugin is within Plugin Developer Guidelines

    Usage:
    p = PluginHelper()
    p.status(warning)
    p.add_summary('Example Plugin with warning status')
    p.add_metric('cpu load', '90')
    p.exit()
    """
    _nagios_status = -1     # exit status of the plugin
    _long_output = None       # Long output of the plugin
    _summary = None           # Summary of the plugin
    _perfdata = None  # Performance and Threshold Metrics are stored here
    show_longoutput = True  # If True, print longoutput
    show_perfdata = True    # If True, print perfdata
    show_summary = True     # If True, print Summary
    show_status_in_summary = True
    show_legacy = False     # If True, print perfdata in legacy form
    verbose = False         # Extra verbosity
    show_debug = False      # Extra debugging
    timeout = 50            # Default timeout set to little less than nagios service check timeout

    thresholds = None # List of strings in the nagios threshold format
    options = None          # OptionParser() options
    arguments = None        # OptionParser() arguments
    def __init__(self):
        self._long_output = []
        self._summary = []
        self.thresholds = []
        self._perfdata = PerfData()  # Performance and Threshold Metrics are stored here

        self.parser = OptionParser()
        general = OptionGroup(self.parser, "Generic Options")
        self.parser.add_option('--threshold','--th',default=[], help="Thresholds in standard nagios threshold format", metavar='', dest="thresholds",action="append")

        display_group = OptionGroup(self.parser, "Display Options")
        display_group.add_option("-v", "--verbose", dest="verbose", help="Print more verbose info", metavar="v", action="store_true", default=self.verbose)
        general.add_option("-d", "--debug", dest="show_debug", help="Print debug info", metavar="d", action="store_true", default=self.show_debug)
        display_group.add_option("--no-perfdata", dest="show_perfdata", help="Dont show any performance data", action="store_false", default=self.show_perfdata)
        display_group.add_option("--no-longoutput", dest="show_longoutput", help="Hide longoutput from the plugin output (i.e. only display first line of the output)", action="store_false", default=self.show_longoutput)
        display_group.add_option("--no-summary", dest="show_summary", help="Hide summary from plugin output", action="store_false", default=self.show_summary)
        #display_group.add_option("--show-status-in-summary", dest="show_status_in_summary", help="Prefix the summary of the plugin with OK- or WARN- ", action="store_true", default=False)
        display_group.add_option("--get-metrics", dest="get_metrics", help="Print all available metrics and exit (can be combined with --verbose)", action="store_true", default=False)
        display_group.add_option("--legacy", dest="show_legacy", help="Output perfdata in legacy format", action="store_true", default=self.show_legacy)
        self.parser.add_option_group(display_group)

    def parse_arguments(self, argument_list=None):
        """ Parsers commandline arguments, prints error if there is a syntax error.

        Creates:
            self.options   -- As created by OptionParser.parse()
            self.arguments -- As created by OptionParser.parse()
        Arguments:
            argument_list -- By default use sys.argv[1:], override only if you know what you are doing.
        Returns:
            None
        """
        self.options, self.arguments = self.parser.parse_args(args=argument_list)
        # TODO: Handle it if developer decides to remove some options before calling parse_arguments()
        self.thresholds = self.options.thresholds
        self.show_longoutput = self.options.show_longoutput
        self.show_perfdata = self.options.show_perfdata
        self.show_legacy = self.options.show_legacy
        self.show_debug = self.options.show_debug
        self.verbose = self.options.verbose
        #self.show_status_in_summary = self.options.show_status_in_summary

    def add_long_output(self, message):
        """ Appends message to the end of Plugin long_output. Message does not need a \n suffix

        Examples:
          >>> p = PluginHelper()
          >>> p.add_long_output('Status of sensor 1')
          >>> p.add_long_output('* Temperature: OK')
          >>> p.add_long_output('* Humidity: OK')
          >>> p.get_long_output()
          'Status of sensor 1\\n* Temperature: OK\\n* Humidity: OK'
        """
        self._long_output.append(message)
    def add_option(self, *args, **kwargs):
        """ Same as self.parser.add_option() """
        return self.parser.add_option(*args,**kwargs)

    def get_long_output(self):
        """ Returns all long_output that has been added via add_long_output """
        return '\n'.join(self._long_output)

    def set_long_output(self, message):
        """ Overwrite current long_output with message

        Example:
        >>> s = PluginHelper()
        >>> s.add_long_output('first long output')
        >>> s.set_long_output('Fatal error')
        >>> s.get_long_output()
        'Fatal error'
        """
        self._long_output = [message]
    def add_summary(self, message):
        """ Adds message to Plugin Summary """
        self._summary.append(message.strip())
    def set_summary(self, message):
        """ Overwrite current summary with message

        Example:
        >>> s = PluginHelper()
        >>> s.add_summary('first summary')
        >>> s.set_summary('Fatal error')
        >>> s.get_summary()
        'Fatal error'
        """
        self._summary = [message]
    def get_summary(self):
        return '. '.join(self._summary)

    def get_status(self):
        """ Returns the worst nagios status (integer 0,1,2,3) that has been put with add_status()

        If status has never been added, returns 3 for UNKNOWN
        """

        # If no status has been set, return unknown
        if self._nagios_status == -1:
            return UNKNOWN
        else:
            return self._nagios_status

    def status(self, new_status=None):
        """ Same as get_status() if new_status=None, otherwise call add_status(new_status) """
        if new_status is None:
            return self.get_status()
        if new_status not in state_text:
            new_status = unknown
        return self.add_status(new_status)

    def add_status(self, new_status=None):
        """ Update exit status of the nagios plugin. This function will keep history of the worst status added

        Examples:
        >>> p = PluginHelper()
        >>> p.add_status(0) # ok
        >>> p.add_status(2) # critical
        >>> p.add_status(1) # warning
        >>> p.get_status()  #
        2

        >>> p = PluginHelper()
        >>> p.add_status('warning')
        >>> p.add_status('ok')
        >>> p.get_status()
        1
        >>> p.add_status('okay')
        Traceback (most recent call last):
        ...
        Exception: Invalid status supplied "okay"
        """

        # If new status was entered as a human readable string (ok,warn,etc) lets convert it to int:
        if type(new_status) == type(''):
            if new_status.lower() in state:
                new_status = state[new_status]
            else:
                raise Exception("Invalid status supplied \"%s\"" % (new_status))

        self._nagios_status = max(self._nagios_status, new_status)

    def add_metric(self, label="",value="",warn="",crit="",min="",max="",uom="", perfdatastring=None):
        """ Add numerical metric (will be outputted as nagios performanca data)

        Examples:
          >>> p = PluginHelper()
          >>> p.add_metric(label="load1", value="7")
          >>> p.add_metric(label="load5", value="5")
          >>> p.add_metric(label="load15",value="2")
          >>> p.get_perfdata()
          "'load1'=7;;;; 'load5'=5;;;; 'load15'=2;;;;"

          >>> p = PluginHelper()
          >>> p.add_metric(perfdatastring="load1=6;;;;")
          >>> p.add_metric(perfdatastring="load5=4;;;;")
          >>> p.add_metric(perfdatastring="load15=1;;;;")
          >>> p.get_perfdata()
          "'load1'=6;;;; 'load5'=4;;;; 'load15'=1;;;;"
        """
        if not perfdatastring is None:
            self._perfdata.add_perfdatametric(perfdatastring=perfdatastring)
        else:
            self._perfdata.add_perfdatametric(label=label,value=value,warn=warn,crit=crit,min=min,max=max,uom=uom)

    def get_metric(self, label):
        """ Return one specific metric (PerfdataMetric object) with the specified label. Returns None if not found.

        Example:
        >>> p = PluginHelper()
        >>> p.add_metric(label="load1", value="7")
        >>> p.add_metric(label="load15",value="2")
        >>> p.get_metric("load1")
        'load1'=7;;;;
        >>> p.get_metric("unknown") # Returns None

        """
        for i in self._perfdata.metrics:
            if i.label == label:
                return i
        return None

    def convert_perfdata(self, perfdata):
        """ Converts new threshold range format to old one. Returns None.

        Examples:
            x..y -> x:y
            inf..y -> :y
            -inf..y -> :y
            x..inf -> x:
            -inf..inf -> :        
        """
        for metric in perfdata:
            metric.warn = metric.warn.replace("..",":").replace("-inf","").replace("inf","")
            metric.crit = metric.crit.replace("..",":").replace("-inf","").replace("inf","")
        return None


    def get_perfdata(self):
        """ Get perfdatastring for all valid perfdatametrics collected via add_perfdata

        Examples:
        >>> p = PluginHelper()
        >>> p.add_metric(label="load1", value="7", warn="-inf..10", crit="10..inf")
        >>> p.add_metric(label="load5", value="5", warn="-inf..7", crit="7..inf")
        >>> p.add_metric(label="load15",value="2", warn="-inf..5", crit="5..inf")
        >>> p.get_perfdata()
        "'load1'=7;-inf..10;10..inf;; 'load5'=5;-inf..7;7..inf;; 'load15'=2;-inf..5;5..inf;;"

        Example with legacy output (show_legacy should be set with a cmdline option):
        >>> p.show_legacy = True
        >>> p.get_perfdata()
        "'load1'=7;:10;10:;; 'load5'=5;:7;7:;; 'load15'=2;:5;5:;;"

        """
        if self.show_legacy == True:
            self.convert_perfdata(self._perfdata.metrics)
        return str(self._perfdata   )

    def get_plugin_output(self, exit_code=None,summary=None, long_output=None, perfdata=None):
        """ Get all plugin output as it would be printed to screen with self.exit()

        Examples of functionality:
        >>> p = PluginHelper()
        >>> p.get_plugin_output()
        'Unknown -'

        >>> p = PluginHelper()
        >>> p.add_summary('Testing')
        >>> p.add_long_output('Long testing output')
        >>> p.add_long_output('More output')
        >>> p.get_plugin_output(exit_code=0)
        'OK - Testing\\nLong testing output\\nMore output'

        >>> p = PluginHelper()
        >>> p.add_summary('Testing')
        >>> p.add_status(0)
        >>> p.get_plugin_output()
        'OK - Testing'

        >>> p = PluginHelper()
        >>> p.show_status_in_summary = False
        >>> p.add_summary('Testing')
        >>> p.add_metric(label="load1", value="7")
        >>> p.add_metric(label="load5", value="5")
        >>> p.add_metric(label="load15",value="2")
        >>> p.get_plugin_output(exit_code=0)
        "Testing | 'load1'=7;;;; 'load5'=5;;;; 'load15'=2;;;;"

        >>> p = PluginHelper()
        >>> p.show_status_in_summary = False
        >>> p.add_summary('Testing')
        >>> p.add_long_output('Long testing output')
        >>> p.add_long_output('More output')
        >>> p.add_metric(label="load1", value="7")
        >>> p.add_metric(label="load5", value="5")
        >>> p.add_metric(label="load15",value="2")
        >>> p.get_plugin_output(exit_code=0)
        "Testing | 'load1'=7;;;; 'load5'=5;;;; 'load15'=2;;;;\\nLong testing output\\nMore output"

        """
        if summary is None:
            summary = self.get_summary()
        if long_output is None:
            long_output = self.get_long_output()
        if perfdata is None:
            perfdata = self.get_perfdata()
        if exit_code is None:
            exit_code = self.get_status()

        return_buffer = ""
        if self.show_status_in_summary == True:
            return_buffer += "%s - " % state_text[exit_code]
        if self.show_summary == True:
            return_buffer += summary
        if self.show_perfdata == True and len(perfdata) > 0:
            return_buffer += " | %s\n" % perfdata

        if not return_buffer.endswith('\n'):
            return_buffer += '\n'
        if self.show_longoutput == True and len(long_output) > 0:
            return_buffer += long_output

        return_buffer = return_buffer.strip()
        return return_buffer

    def exit(self, exit_code=None,summary=None, long_output=None, perfdata=None):
        """ Print all collected output to screen and exit nagios style, no arguments are needed
            except if you want to override default behavior.

        Arguments:
            summary     -- Is this text as the plugin summary instead of self.get_summary()
            long_output -- Use this text as long_output instead of self.get_long_output()
            perfdata    -- Use this text instead of self.get_perfdata()
            exit_code   -- Use this exit code instead of self.status()
        """
        if exit_code is None:
            exit_code = self.get_status()
        if self.options and self.options.get_metrics == True:
            summary = "Available metrics for this plugin:"
            metrics = []

            for i in self._perfdata.metrics:
                if self.options.verbose == True:
                    metrics.append( str(i) )
                else:
                    metrics.append( i.label )
            long_output = '\n'.join(metrics)


        plugin_output = self.get_plugin_output(exit_code=exit_code,summary=summary,long_output=long_output,perfdata=perfdata)

        print plugin_output
        sys.exit(exit_code)

    def check_metric(self, metric_name, thresholds):
        """ Check one specific metric against a list of thresholds. Updates self.status() and writes to summary or longout as appropriate.

        Arguments:
          metric_name -- A string representing the name of the metric (the label part of the performance data)
          thresholds  -- a list in the form of [ (level,range) ] where range is a string in the format of "start..end"

        Examples:
        >>> p = PluginHelper()
        >>> thresholds = [(warning,'2..5'), (critical,'5..inf')]
        >>> p.get_plugin_output()
        'Unknown -'
        >>> p.add_metric('load15', '3')
        >>> p.check_metric('load15',thresholds)
        >>> p.get_plugin_output()
        "Warning - Warning on load15 | 'load15'=3;2..5;5..inf;;"

        
        >>> p = PluginHelper()
        >>> thresholds = [(warning,'2..5'), (critical,'5..inf')]
        >>> p.add_metric('load15', '3')
        >>> p.verbose = True
        >>> p.check_metric('load15',thresholds)
        >>> p.get_plugin_output()
        "Warning - Warning on load15 | 'load15'=3;2..5;5..inf;;\\nWarning on load15"

        Invalid metric:
        >>> p = PluginHelper()
        >>> p.add_status(ok)
        >>> p.add_summary('Everythings fine!')
        >>> p.get_plugin_output()
        'OK - Everythings fine!'
        >>> thresholds = [(warning,'2..5'), (critical,'5..inf')]
        >>> p.check_metric('never_added_metric', thresholds)
        >>> p.get_plugin_output()
        'Unknown - Everythings fine!. Metric never_added_metric not found'

        Invalid threshold:
        >>> p = PluginHelper()
        >>> thresholds = [(warning, 'invalid'), (critical,'5..inf')]
        >>> p.add_metric('load1', '10')
        >>> p.check_metric('load1', thresholds)
        Traceback (most recent call last):
        ...
        SystemExit: 3

        Returns:
          None
        """
        metric = self.get_metric(label=metric_name)

        # If threshold was specified but metric not found in our data, set status unknown
        if metric is None:
            self.status(unknown)
            self.add_summary("Metric %s not found" % (metric_name))
            return

        metric_status = -1 # by default assume nothing
        default_state = 0 # By default if no treshold matches, we assume OK
        highest_level = ok # highest threshold range seen
        # Iterate through all thresholds, and log down warn and crit for perfdata purposes
        for level, threshold_range in thresholds:
            if metric.warn == '' and level == warning:
                metric.warn = threshold_range
            elif metric.crit == '' and level == critical:
                metric.crit = threshold_range
            if level == ok:
                default_state = 2

        # Iterate all threshold and determine states
        for level, threshold_range in thresholds:
            highest_level = max(highest_level, level)
            # If ok threshold was specified, default state is critical according to spec
            # If value matches our threshold, we increment the status
            try:
                in_range = new_threshold_syntax.check_range(metric.value, threshold_range)
            except PynagError:
                self.set_summary(
                    "Could not parse threshold %s=%s for metric %s" %
                                 (state_text[level], threshold_range, metric_name)
                )
                self.set_long_output("Thresholds should be in the format metric=<metric_name>,ok=0..90,warning=90..95")
                self.add_long_output("Example: ")
                self.add_long_output("--th metric=load,ok=0..1,warning=1..5,critical=5..inf")
                self.status(unknown)
                self.exit()
            if in_range:
                metric_status = max(metric_status, level)
                self.debug('%s is within %s range "%s"' % (metric_name, state_text[level], threshold_range))
                if level == ok:
                    self.debug("OK threshold matches, not checking any more thresholds")
                    metric_status = ok
                    break
            else:
                self.debug('%s is outside %s range "%s"' % (metric_name, state_text[level],threshold_range))

        # If no thresholds matched, set a default return code
        if metric_status < 0:
            metric_status = default_state


        # OK's go to long output, errors go directly to summary
        self.add_status(metric_status)
        message = '%s on %s' % (state_text[metric_status], metric_name)

        # Errors are added to the summary:
        if metric_status > 0:
            self.add_summary(message)

        if self.verbose == True:
            self.add_long_output(message)


    def check_all_metrics(self):
        """ Checks all metrics (add_metric() against any thresholds set in self.options.thresholds or with --threshold from commandline)"""
        checked_metrics = []
        for threshold in self.thresholds:
            parsed_threshold = new_threshold_syntax.parse_threshold(threshold)
            metric_name = parsed_threshold['metric']
            thresholds = parsed_threshold['thresholds']
            self.check_metric(metric_name, thresholds)
            checked_metrics.append( metric_name )

        # Lets look at metrics that were not specified on the command-line but might have a default
        # threshold specified with their metric data
        for i in self._perfdata.metrics:
            if i.label in checked_metrics:
                continue
            thresholds = []

            if i.warn != '':
                thresholds.append( (warning, i.warn))
            if i.crit != '':
                thresholds.append( (critical, i.crit))
            self.check_metric(i.label, thresholds)

    def run_function(self, function, *args, **kwargs):
        """ Executes "function" and exits Nagios style with status "unkown"
        if there are any exceptions. The stacktrace will be in long_output.
        
        Example:
        >>> p = PluginHelper()
        >>> p.add_status('ok')
        >>> p.get_status()
        0
        >>> p.add_status('okay')
        Traceback (most recent call last):
        ...
        Exception: Invalid status supplied "okay"
        >>> p.run_function( p.add_status, 'warning' )
        >>> p.get_status()
        1
        >>> p.run_function( p.add_status, 'okay' )
        Traceback (most recent call last):
        ...
        SystemExit: 3
        """
        try:
            function(*args, **kwargs)
        except Exception, e:
            exc_type, exc_value, exc_traceback = sys.exc_info()
            exit_code = unknown
            #traceback.print_exc(file=sys.stdout)
            summary = "Unhandled '%s' exception while running plugin (traceback below)" % exc_type
            long_output = traceback.format_exc()
            self.exit(exit_code=exit_code, summary=summary, long_output=long_output,perfdata='')
Ejemplo n.º 3
0
    def __init__(self):
        self._long_output = []
        self._summary = []
        self.thresholds = []
        # Performance and Threshold Metrics are stored here
        self._perfdata = PerfData()

        self.parser = OptionParser()
        generic_group = OptionGroup(self.parser, "Generic Options")
        generic_group.add_option(
            '--timeout',
            help="Exit plugin with unknown status after x seconds",
            type='int',
            metavar='50',
            dest="timeout",
            default=self.timeout)
        generic_group.add_option(
            '--threshold',
            default=[],
            help="Thresholds in standard nagios threshold format",
            metavar='range',
            dest="thresholds",
            action="append")
        generic_group.add_option('--th',
                                 default=[],
                                 help="Same as --threshold",
                                 metavar='range',
                                 dest="thresholds",
                                 action="append")

        generic_group.add_option(
            '--extra-opts',
            help=
            "Read options from an ini file. See http://nagiosplugins.org/extra-opts",
            metavar='@file',
            dest="extra_opts")
        generic_group.add_option("-d",
                                 "--debug",
                                 dest="show_debug",
                                 help="Print debug info",
                                 metavar="d",
                                 action="store_true",
                                 default=self.show_debug)

        # Display options are options that affect the output of the plugin
        # But usually not its function
        display_group = OptionGroup(self.parser, "Display Options")
        display_group.add_option("-v",
                                 "--verbose",
                                 dest="verbose",
                                 help="Print more verbose info",
                                 metavar="v",
                                 action="store_true",
                                 default=self.verbose)
        display_group.add_option("--no-perfdata",
                                 dest="show_perfdata",
                                 help="Dont show any performance data",
                                 action="store_false",
                                 default=self.show_perfdata)
        display_group.add_option(
            "--no-longoutput",
            dest="show_longoutput",
            help=
            "Hide longoutput from the plugin output (i.e. only display first line of the output)",
            action="store_false",
            default=self.show_longoutput)
        display_group.add_option("--no-summary",
                                 dest="show_summary",
                                 help="Hide summary from plugin output",
                                 action="store_false",
                                 default=self.show_summary)

        display_group.add_option(
            "--get-metrics",
            dest="get_metrics",
            help=
            "Print all available metrics and exit (can be combined with --verbose)",
            action="store_true",
            default=False)
        display_group.add_option("--legacy",
                                 dest="show_legacy",
                                 help="Deprecated, do not use",
                                 action="store_true",
                                 default=self.show_legacy)

        self.parser.add_option_group(generic_group)
        self.parser.add_option_group(display_group)
Ejemplo n.º 4
0
class PluginHelper(object):
    """ PluginHelper takes away some of the tedious work of writing Nagios plugins. Primary features include:

    * Keep a collection of your plugin messages (queue for both summary and longoutput)
    * Keep record of exit status
    * Keep a collection of your metrics (for both perfdata and thresholds)
    * Automatic Command-line arguments
    * Make sure output of your plugin is within Plugin Developer Guidelines

    Usage:
    p = PluginHelper()
    p.status(warning)
    p.add_summary('Example Plugin with warning status')
    p.add_metric('cpu load', '90')
    p.exit()
    """
    _nagios_status = -1  # exit status of the plugin
    _long_output = None  # Long output of the plugin
    _summary = None  # Summary of the plugin
    _perfdata = None  # Performance and Threshold Metrics are stored here
    show_longoutput = True  # If True, print longoutput
    show_perfdata = True  # If True, print perfdata
    show_summary = True  # If True, print Summary
    show_status_in_summary = True
    show_legacy = False  # Deprecated, doesnt do anything
    verbose = False  # Extra verbosity
    show_debug = False  # Extra debugging
    # By default, plugins timeout right before nagios kills the plugin
    timeout = 58

    thresholds = None  # List of strings in the nagios threshold format
    options = None  # OptionParser() options
    arguments = None  # OptionParser() arguments

    def __init__(self):
        self._long_output = []
        self._summary = []
        self.thresholds = []
        # Performance and Threshold Metrics are stored here
        self._perfdata = PerfData()

        self.parser = OptionParser()
        generic_group = OptionGroup(self.parser, "Generic Options")
        generic_group.add_option(
            '--timeout',
            help="Exit plugin with unknown status after x seconds",
            type='int',
            metavar='50',
            dest="timeout",
            default=self.timeout)
        generic_group.add_option(
            '--threshold',
            default=[],
            help="Thresholds in standard nagios threshold format",
            metavar='range',
            dest="thresholds",
            action="append")
        generic_group.add_option('--th',
                                 default=[],
                                 help="Same as --threshold",
                                 metavar='range',
                                 dest="thresholds",
                                 action="append")

        generic_group.add_option(
            '--extra-opts',
            help=
            "Read options from an ini file. See http://nagiosplugins.org/extra-opts",
            metavar='@file',
            dest="extra_opts")
        generic_group.add_option("-d",
                                 "--debug",
                                 dest="show_debug",
                                 help="Print debug info",
                                 metavar="d",
                                 action="store_true",
                                 default=self.show_debug)

        # Display options are options that affect the output of the plugin
        # But usually not its function
        display_group = OptionGroup(self.parser, "Display Options")
        display_group.add_option("-v",
                                 "--verbose",
                                 dest="verbose",
                                 help="Print more verbose info",
                                 metavar="v",
                                 action="store_true",
                                 default=self.verbose)
        display_group.add_option("--no-perfdata",
                                 dest="show_perfdata",
                                 help="Dont show any performance data",
                                 action="store_false",
                                 default=self.show_perfdata)
        display_group.add_option(
            "--no-longoutput",
            dest="show_longoutput",
            help=
            "Hide longoutput from the plugin output (i.e. only display first line of the output)",
            action="store_false",
            default=self.show_longoutput)
        display_group.add_option("--no-summary",
                                 dest="show_summary",
                                 help="Hide summary from plugin output",
                                 action="store_false",
                                 default=self.show_summary)

        display_group.add_option(
            "--get-metrics",
            dest="get_metrics",
            help=
            "Print all available metrics and exit (can be combined with --verbose)",
            action="store_true",
            default=False)
        display_group.add_option("--legacy",
                                 dest="show_legacy",
                                 help="Deprecated, do not use",
                                 action="store_true",
                                 default=self.show_legacy)

        self.parser.add_option_group(generic_group)
        self.parser.add_option_group(display_group)

    def parse_arguments(self, argument_list=None):
        """ Parsers commandline arguments, prints error if there is a syntax error.

        Creates:
            self.options   -- As created by OptionParser.parse()
            self.arguments -- As created by OptionParser.parse()
        Arguments:
            argument_list -- By default use sys.argv[1:], override only if you know what you are doing.
        Returns:
            None
        """
        self.options, self.arguments = self.parser.parse_args(
            args=argument_list)

        extra_opts = self.options.extra_opts
        if extra_opts is not None:  # --extra-opts was specified
            if extra_opts == '':  # --extra-opts= with no value.
                section_name = None
                config_file = None
            elif '@' in extra_opts:  # filename was specified
                section_name, config_file = extra_opts.split('@', 1)
            else:  # Only section was specified
                section_name = extra_opts
                config_file = None
            values = self.get_default_values(section_name, config_file)
            self.options, self.arguments = self.parser.parse_args(
                args=argument_list, values=values)

        # TODO: Handle it if developer decides to remove some options before
        # calling parse_arguments()
        self.thresholds = self.options.thresholds
        self.show_longoutput = self.options.show_longoutput
        self.show_perfdata = self.options.show_perfdata
        self.show_legacy = self.options.show_legacy
        self.show_debug = self.options.show_debug
        self.verbose = self.options.verbose
        #self.show_status_in_summary = self.options.show_status_in_summary

        self.set_timeout(self.options.timeout)

    def add_long_output(self, message):
        """ Appends message to the end of Plugin long_output. Message does not need a \n suffix

        Examples:
          >>> p = PluginHelper()
          >>> p.add_long_output('Status of sensor 1')
          >>> p.add_long_output('* Temperature: OK')
          >>> p.add_long_output('* Humidity: OK')
          >>> p.get_long_output()
          u'Status of sensor 1\\n* Temperature: OK\\n* Humidity: OK'

        """
        self._long_output.append(message)

    def add_option(self, *args, **kwargs):
        """ Same as self.parser.add_option() """
        return self.parser.add_option(*args, **kwargs)

    def get_long_output(self):
        """ Returns all long_output that has been added via add_long_output """
        return '\n'.join(self._long_output)

    def set_long_output(self, message):
        """ Overwrite current long_output with message

        Example:
        >>> s = PluginHelper()
        >>> s.add_long_output('first long output')
        >>> s.set_long_output('Fatal error')
        >>> s.get_long_output()
        u'Fatal error'
        """
        self._long_output = [message]

    def add_summary(self, message):
        """ Adds message to Plugin Summary """
        self._summary.append(message.strip())

    def set_summary(self, message):
        """ Overwrite current summary with message

        Example:
        >>> s = PluginHelper()
        >>> s.add_summary('first summary')
        >>> s.set_summary('Fatal error')
        >>> s.get_summary()
        u'Fatal error'
        """
        self._summary = [message]

    def get_summary(self):
        return '. '.join(self._summary)

    def get_status(self):
        """ Returns the worst nagios status (integer 0,1,2,3) that has been put with add_status()

        If status has never been added, returns 3 for UNKNOWN
        """

        # If no status has been set, return unknown
        if self._nagios_status == -1:
            return UNKNOWN
        else:
            return self._nagios_status

    def status(self, new_status=None):
        """ Same as get_status() if new_status=None, otherwise call add_status(new_status) """
        if new_status is None:
            return self.get_status()
        if new_status not in state_text:
            new_status = unknown
        return self.add_status(new_status)

    def add_status(self, new_status=None):
        """ Update exit status of the nagios plugin. This function will keep history of the worst status added

        Examples:
        >>> p = PluginHelper()
        >>> p.add_status(0) # ok
        >>> p.add_status(2) # critical
        >>> p.add_status(1) # warning
        >>> p.get_status()  #
        2

        >>> p = PluginHelper()
        >>> p.add_status('warning')
        >>> p.add_status('ok')
        >>> p.get_status()
        1
        >>> p.add_status('okay')
        Traceback (most recent call last):
        ...
        Exception: Invalid status supplied "okay"
        """

        # If new status was entered as a human readable string (ok,warn,etc)
        # lets convert it to int:
        if isinstance(new_status, basestring):
            if new_status.lower() in state:
                new_status = state[new_status]
            else:
                raise Exception("Invalid status supplied \"%s\"" %
                                (new_status))

        self._nagios_status = max(self._nagios_status, new_status)

    def add_metric(self,
                   label="",
                   value="",
                   warn="",
                   crit="",
                   min="",
                   max="",
                   uom="",
                   perfdatastring=None):
        """ Add numerical metric (will be outputted as nagios performanca data)

        Examples:
          >>> p = PluginHelper()
          >>> p.add_metric(label="load1", value="7")
          >>> p.add_metric(label="load5", value="5")
          >>> p.add_metric(label="load15",value="2")
          >>> p.get_perfdata()
          "'load1'=7;;;; 'load5'=5;;;; 'load15'=2;;;;"

          >>> p = PluginHelper()
          >>> p.add_metric(perfdatastring="load1=6;;;;")
          >>> p.add_metric(perfdatastring="load5=4;;;;")
          >>> p.add_metric(perfdatastring="load15=1;;;;")
          >>> p.get_perfdata()
          "'load1'=6;;;; 'load5'=4;;;; 'load15'=1;;;;"

        """
        if not perfdatastring is None:
            self._perfdata.add_perfdatametric(perfdatastring=perfdatastring)
        else:
            self._perfdata.add_perfdatametric(label=label,
                                              value=value,
                                              warn=warn,
                                              crit=crit,
                                              min=min,
                                              max=max,
                                              uom=uom)

    def get_default_values(self, section_name=None, config_file=None):
        """ Returns an optionParser.Values instance of all defaults after parsing extra opts config file

        The Nagios extra-opts spec we use is the same as described here: http://nagiosplugins.org/extra-opts

        Arguments

        """
        # Get the program defaults
        values = self.parser.get_default_values()

        # Create an ExtraOptsParser instance and get all the values from that
        # config file
        extra_opts = ExtraOptsParser(section_name=section_name,
                                     config_file=config_file).get_values()

        for option in self.parser.option_list:
            name = option.dest
            if name in extra_opts:
                if option.action == 'append':
                    setattr(values, name, extra_opts[option.dest])
                else:
                    setattr(values, name, extra_opts[option.dest][0])
        return values

    def get_metric(self, label):
        """ Return one specific metric (PerfdataMetric object) with the specified label. Returns None if not found.

        Example:
        >>> p = PluginHelper()
        >>> p.add_metric(label="load1", value="7")
        >>> p.add_metric(label="load15",value="2")
        >>> p.get_metric("load1")
        'load1'=7;;;;
        >>> p.get_metric("unknown") # Returns None

        """
        for i in self._perfdata.metrics:
            if i.label == label:
                return i
        return None

    def convert_perfdata(self, perfdata):
        """ Converts new threshold range format to old one. Returns None.

        Examples:
            x..y -> x:y
            inf..y -> :y
            -inf..y -> :y
            x..inf -> x:
            -inf..inf -> :
        """
        for metric in perfdata:
            metric.warn = reconsile_threshold(metric.warn)
            metric.crit = reconsile_threshold(metric.crit)
        return None

    def get_perfdata(self):
        """ Get perfdatastring for all valid perfdatametrics collected via add_perfdata

        Examples:
        >>> p = PluginHelper()
        >>> p.add_metric(label="load1", value="7", warn="-inf..10", crit="10..inf")
        >>> p.add_metric(label="load5", value="5", warn="-inf..7", crit="7..inf")
        >>> p.add_metric(label="load15",value="2", warn="-inf..5", crit="5..inf")
        >>> p.get_perfdata()
        "'load1'=7;10:;~:10;; 'load5'=5;7:;~:7;; 'load15'=2;5:;~:5;;"

        Example with legacy output (show_legacy should be set with a cmdline option):
        >>> p.show_legacy = True
        >>> p.get_perfdata()
        "'load1'=7;10:;~:10;; 'load5'=5;7:;~:7;; 'load15'=2;5:;~:5;;"

        """
        # Normalize the perfdata to so the thresholds match the current nagios plugin guidelines
        self.convert_perfdata(self._perfdata.metrics)
        return str(self._perfdata)

    def get_plugin_output(self,
                          exit_code=None,
                          summary=None,
                          long_output=None,
                          perfdata=None):
        """ Get all plugin output as it would be printed to screen with self.exit()

        Examples of functionality:
        >>> p = PluginHelper()
        >>> p.get_plugin_output()
        u'Unknown -'

        >>> p = PluginHelper()
        >>> p.add_summary('Testing')
        >>> p.add_long_output('Long testing output')
        >>> p.add_long_output('More output')
        >>> p.get_plugin_output(exit_code=0)
        u'OK - Testing\\nLong testing output\\nMore output'

        >>> p = PluginHelper()
        >>> p.add_summary('Testing')
        >>> p.add_status(0)
        >>> p.get_plugin_output()
        u'OK - Testing'

        >>> p = PluginHelper()
        >>> p.show_status_in_summary = False
        >>> p.add_summary('Testing')
        >>> p.add_metric(label="load1", value="7")
        >>> p.add_metric(label="load5", value="5")
        >>> p.add_metric(label="load15",value="2")
        >>> p.get_plugin_output(exit_code=0)
        u"Testing | 'load1'=7;;;; 'load5'=5;;;; 'load15'=2;;;;"

        >>> p = PluginHelper()
        >>> p.show_status_in_summary = False
        >>> p.add_summary('Testing')
        >>> p.add_long_output('Long testing output')
        >>> p.add_long_output('More output')
        >>> p.add_metric(label="load1", value="7")
        >>> p.add_metric(label="load5", value="5")
        >>> p.add_metric(label="load15",value="2")
        >>> p.get_plugin_output(exit_code=0)
        u"Testing | 'load1'=7;;;; 'load5'=5;;;; 'load15'=2;;;;\\nLong testing output\\nMore output"

        """
        if summary is None:
            summary = self.get_summary()
        if long_output is None:
            long_output = self.get_long_output()
        if perfdata is None:
            perfdata = self.get_perfdata()
        if exit_code is None:
            exit_code = self.get_status()

        return_buffer = ""
        if self.show_status_in_summary is True:
            return_buffer += "%s - " % state_text[exit_code]
        if self.show_summary is True:
            return_buffer += summary
        if self.show_perfdata is True and len(perfdata) > 0:
            return_buffer += " | %s\n" % perfdata

        if not return_buffer.endswith('\n'):
            return_buffer += '\n'
        if self.show_longoutput is True and len(long_output) > 0:
            return_buffer += long_output

        return_buffer = return_buffer.strip()
        return return_buffer

    def set_timeout(self, seconds=50):
        """ Configures plugin to timeout after seconds number of seconds """
        timeout = lambda x, y: self.exit(
            unknown,
            summary="Plugin timeout exceeded after %s seconds." % seconds)
        signal.signal(signal.SIGALRM, timeout)
        signal.alarm(seconds)

    def exit(self,
             exit_code=None,
             summary=None,
             long_output=None,
             perfdata=None):
        """ Print all collected output to screen and exit nagios style, no arguments are needed
            except if you want to override default behavior.

        Arguments:
            summary     -- Is this text as the plugin summary instead of self.get_summary()
            long_output -- Use this text as long_output instead of self.get_long_output()
            perfdata    -- Use this text instead of self.get_perfdata()
            exit_code   -- Use this exit code instead of self.status()
        """
        if exit_code is None:
            exit_code = self.get_status()
        if self.options and self.options.get_metrics is True:
            summary = "Available metrics for this plugin:"
            metrics = []

            for i in self._perfdata.metrics:
                if self.options.verbose is True:
                    metrics.append(str(i))
                else:
                    metrics.append(i.label)
            long_output = '\n'.join(metrics)

        plugin_output = self.get_plugin_output(exit_code=exit_code,
                                               summary=summary,
                                               long_output=long_output,
                                               perfdata=perfdata)

        print(plugin_output)
        sys.exit(exit_code)

    def check_metric(self, metric_name, thresholds):
        """ Check one specific metric against a list of thresholds. Updates self.status() and writes to summary or longout as appropriate.

        Arguments:
          metric_name -- A string representing the name of the metric (the label part of the performance data)
          thresholds  -- a list in the form of [ (level,range) ] where range is a string in the format of "start..end"

        Examples:
        >>> p = PluginHelper()
        >>> thresholds = [(warning,'2..5'), (critical,'5..inf')]
        >>> p.get_plugin_output()
        u'Unknown -'
        >>> p.add_metric('load15', '3')
        >>> p.check_metric('load15',thresholds)
        >>> p.get_plugin_output()
        u"Warning - Warning on load15 | 'load15'=3;@2:5;~:5;;"

        >>> p = PluginHelper()
        >>> thresholds = [(warning,'2..5'), (critical,'5..inf')]
        >>> p.add_metric('load15', '3')
        >>> p.verbose = True
        >>> p.check_metric('load15',thresholds)
        >>> p.get_plugin_output()
        u"Warning - Warning on load15 | 'load15'=3;@2:5;~:5;;\\nWarning on load15"

        Invalid metric:
        >>> p = PluginHelper()
        >>> p.add_status(ok)
        >>> p.add_summary('Everythings fine!')
        >>> p.get_plugin_output()
        u'OK - Everythings fine!'
        >>> thresholds = [(warning,'2..5'), (critical,'5..inf')]
        >>> p.check_metric('never_added_metric', thresholds)
        >>> p.get_plugin_output()
        u'Unknown - Everythings fine!. Metric never_added_metric not found'

        Invalid threshold:
        >>> p = PluginHelper()
        >>> thresholds = [(warning, 'invalid'), (critical,'5..inf')]
        >>> p.add_metric('load1', '10')
        >>> p.check_metric('load1', thresholds)
        Traceback (most recent call last):
        ...
        SystemExit: 3

        Returns:
          None
        """
        metric = self.get_metric(label=metric_name)

        # If threshold was specified but metric not found in our data, set
        # status unknown
        if metric is None:
            self.status(unknown)
            self.add_summary("Metric %s not found" % (metric_name))
            return

        metric_status = -1  # by default assume nothing
        default_state = 0  # By default if no treshold matches, we assume OK
        highest_level = ok  # highest threshold range seen
        # Iterate through all thresholds, and log down warn and crit for
        # perfdata purposes
        for level, threshold_range in thresholds:
            if metric.warn == '' and level == warning:
                metric.warn = threshold_range
            elif metric.crit == '' and level == critical:
                metric.crit = threshold_range
            if level == ok:
                default_state = 2

        # Iterate all threshold and determine states
        for level, threshold_range in thresholds:
            highest_level = max(highest_level, level)
            # If ok threshold was specified, default state is critical according to spec
            # If value matches our threshold, we increment the status
            try:
                in_range = new_threshold_syntax.check_range(
                    metric.value, threshold_range)
            except pynag.errors.PynagError:
                self.set_summary(
                    "Could not parse threshold %s=%s for metric %s" %
                    (state_text[level], threshold_range, metric_name))
                self.set_long_output(
                    "Thresholds should be in the format metric=<metric_name>,ok=0..90,warning=90..95"
                )
                self.add_long_output("Example: ")
                self.add_long_output(
                    "--th metric=load,ok=0..1,warning=1..5,critical=5..inf")
                self.status(unknown)
                self.exit()
            if in_range:
                metric_status = max(metric_status, level)
                self.debug('%s is within %s range "%s"' %
                           (metric_name, state_text[level], threshold_range))
                if level == ok:
                    self.debug(
                        "OK threshold matches, not checking any more thresholds"
                    )
                    metric_status = ok
                    break
            else:
                self.debug('%s is outside %s range "%s"' %
                           (metric_name, state_text[level], threshold_range))

        # If no thresholds matched, set a default return code
        if metric_status < 0:
            metric_status = default_state

        # OK's go to long output, errors go directly to summary
        self.add_status(metric_status)
        message = '%s on %s' % (state_text[metric_status], metric_name)

        # Errors are added to the summary:
        if metric_status > 0:
            self.add_summary(message)

        if self.verbose is True:
            self.add_long_output(message)

    def check_all_metrics(self):
        """ Checks all metrics (add_metric() against any thresholds set in self.options.thresholds or with --threshold from commandline)"""
        checked_metrics = []
        for threshold in self.thresholds:
            parsed_threshold = new_threshold_syntax.parse_threshold(threshold)
            metric_name = parsed_threshold['metric']
            thresholds = parsed_threshold['thresholds']
            self.check_metric(metric_name, thresholds)
            checked_metrics.append(metric_name)

        # Lets look at metrics that were not specified on the command-line but might have a default
        # threshold specified with their metric data
        for i in self._perfdata.metrics:
            if i.label in checked_metrics:
                continue
            thresholds = []

            if i.warn != '':
                thresholds.append((warning, i.warn))
            if i.crit != '':
                thresholds.append((critical, i.crit))
            self.check_metric(i.label, thresholds)

    def run_function(self, function, *args, **kwargs):
        """ Executes "function" and exits Nagios style with status "unkown"
        if there are any exceptions. The stacktrace will be in long_output.

        Example:
        >>> p = PluginHelper()
        >>> p.add_status('ok')
        >>> p.get_status()
        0
        >>> p.add_status('okay')
        Traceback (most recent call last):
        ...
        Exception: Invalid status supplied "okay"
        >>> p.run_function( p.add_status, 'warning' )
        >>> p.get_status()
        1
        >>> p.run_function( p.add_status, 'okay' )
        Traceback (most recent call last):
        ...
        SystemExit: 3
        """
        try:
            function(*args, **kwargs)
        except Exception:
            exc_type, exc_value, exc_traceback = sys.exc_info()
            exit_code = unknown
            # traceback.print_exc(file=sys.stdout)
            summary = "Unhandled '%s' exception while running plugin (traceback below)" % exc_type
            long_output = traceback.format_exc()
            self.exit(exit_code=exit_code,
                      summary=summary,
                      long_output=long_output,
                      perfdata='')

    def debug(self, message):  # pragma: no cover
        if self.show_debug is True:
            self.add_long_output("debug: %s" % message)

    def __str__(self):
        """
        >>> p = PluginHelper()
        >>> p.add_status(ok)
        >>> p.add_summary('Test')
        >>> print(p)
        OK - Test
        """
        return self.get_plugin_output()

    def __repr__(self):
        return self.get_plugin_output(long_output='', perfdata='')
Ejemplo n.º 5
0
    def __init__(self):
        self._long_output = []
        self._summary = []
        self.thresholds = []
        # Performance and Threshold Metrics are stored here
        self._perfdata = PerfData()

        self.parser = OptionParser()
        generic_group = OptionGroup(self.parser, "Generic Options")
        generic_group.add_option(
            '--timeout',
            help="Exit plugin with unknown status after x seconds",
            type='int',
            metavar='50',
            dest="timeout",
            default=self.timeout
        )
        generic_group.add_option(
            '--threshold',
            default=[],
            help="Thresholds in standard nagios threshold format",
            metavar='range',
            dest="thresholds",
            action="append"
        )
        generic_group.add_option(
            '--th',
            default=[],
            help="Same as --threshold",
            metavar='range',
            dest="thresholds",
            action="append"
        )

        generic_group.add_option(
            '--extra-opts',
            help="Read options from an ini file. See http://nagiosplugins.org/extra-opts",
            metavar='@file',
            dest="extra_opts"
        )
        generic_group.add_option(
            "-d", "--debug",
            dest="show_debug",
            help="Print debug info",
            metavar="d",
            action="store_true",
            default=self.show_debug
        )

        # Display options are options that affect the output of the plugin
        # But usually not its function
        display_group = OptionGroup(self.parser, "Display Options")
        display_group.add_option(
            "-v", "--verbose",
            dest="verbose",
            help="Print more verbose info",
            metavar="v",
            action="store_true",
            default=self.verbose
        )
        display_group.add_option(
            "--no-perfdata",
            dest="show_perfdata",
            help="Dont show any performance data",
            action="store_false",
            default=self.show_perfdata
        )
        display_group.add_option(
            "--no-longoutput",
            dest="show_longoutput",
            help="Hide longoutput from the plugin output (i.e. only display first line of the output)",
            action="store_false",
            default=self.show_longoutput
        )
        display_group.add_option(
            "--no-summary",
            dest="show_summary",
            help="Hide summary from plugin output",
            action="store_false",
            default=self.show_summary
        )

        display_group.add_option(
            "--get-metrics",
            dest="get_metrics",
            help="Print all available metrics and exit (can be combined with --verbose)",
            action="store_true",
            default=False
        )
        display_group.add_option(
            "--legacy",
            dest="show_legacy",
            help="Deprecated, do not use",
            action="store_true",
            default=self.show_legacy
        )

        self.parser.add_option_group(generic_group)
        self.parser.add_option_group(display_group)
Ejemplo n.º 6
0
    def __init__(self):
        self._long_output = []
        self._summary = []
        self.thresholds = []
        self._perfdata = PerfData(
        )  # Performance and Threshold Metrics are stored here

        self.parser = OptionParser()
        general = OptionGroup(self.parser, "Generic Options")
        self.parser.add_option(
            '--threshold',
            '--th',
            default=[],
            help="Thresholds in standard nagios threshold format",
            metavar='',
            dest="thresholds",
            action="append")

        display_group = OptionGroup(self.parser, "Display Options")
        display_group.add_option("-v",
                                 "--verbose",
                                 dest="verbose",
                                 help="Print more verbose info",
                                 metavar="v",
                                 action="store_true",
                                 default=self.verbose)
        general.add_option("-d",
                           "--debug",
                           dest="show_debug",
                           help="Print debug info",
                           metavar="d",
                           action="store_true",
                           default=self.show_debug)
        display_group.add_option("--no-perfdata",
                                 dest="show_perfdata",
                                 help="Dont show any performance data",
                                 action="store_false",
                                 default=self.show_perfdata)
        display_group.add_option(
            "--no-longoutput",
            dest="show_longoutput",
            help=
            "Hide longoutput from the plugin output (i.e. only display first line of the output)",
            action="store_false",
            default=self.show_longoutput)
        display_group.add_option("--no-summary",
                                 dest="show_summary",
                                 help="Hide summary from plugin output",
                                 action="store_false",
                                 default=self.show_summary)
        #display_group.add_option("--show-status-in-summary", dest="show_status_in_summary", help="Prefix the summary of the plugin with OK- or WARN- ", action="store_true", default=False)
        display_group.add_option(
            "--get-metrics",
            dest="get_metrics",
            help=
            "Print all available metrics and exit (can be combined with --verbose)",
            action="store_true",
            default=False)
        display_group.add_option("--legacy",
                                 dest="show_legacy",
                                 help="Output perfdata in legacy format",
                                 action="store_true",
                                 default=self.show_legacy)
        self.parser.add_option_group(display_group)