nordugrid-arc-nagios-plugins-1.8.4/0000755000175000002070000000000012600623616017723 5ustar mockbuildmockbuildnordugrid-arc-nagios-plugins-1.8.4/arcnagios/0000755000175000002070000000000012600623616021671 5ustar mockbuildmockbuildnordugrid-arc-nagios-plugins-1.8.4/arcnagios/jobplugins/0000755000175000002070000000000012600623616024045 5ustar mockbuildmockbuildnordugrid-arc-nagios-plugins-1.8.4/arcnagios/jobplugins/__init__.py0000644000175000002070000000046111670125741026161 0ustar mockbuildmockbuildfrom scripted import ScriptedJobPlugin from staging import StagingJobPlugin _jobplugins_by_name = { 'scripted': ScriptedJobPlugin, 'staging': StagingJobPlugin, } def register_jobplugin(cls): _jobplugins_by_name[cls.name] = cls def load_jobplugin(name): return _jobplugins_by_name[name] nordugrid-arc-nagios-plugins-1.8.4/arcnagios/jobplugins/scripted.py0000644000175000002070000000516712116376524026252 0ustar mockbuildmockbuildimport os, re from arcnagios.jobplugin import JobPlugin from arcnagios.nagutils import OK, CRITICAL, UNKNOWN _presence_script = """\ missing= for prog in %(required_programs)s; do type -p $prog >/dev/null 2>&1 || missing="$missing $prog" done if test -n "$missing"; then echo "__missing$missing" >%(output_file)s else %(script_line)s err=$? [ $err -eq 0 ] || echo "__exit $err" >>%(output_file)s fi """ _plain_script = """\ %(script_line)s err=$? [ $err -eq 0 ] || echo "__exit $err" >>%(output_file)s """ class ScriptedJobPlugin(JobPlugin): _missing_re = re.compile(r'__missing\s+(.*)') _exit_re = re.compile(r'__exit\s+(\d+)') _status_re = re.compile(r'__status\s+(\d+)\s+(.*)') _log_re = re.compile(r'__log\s+(\d+)\s+(.*)') def write_script(self, fh): script_line = self.getconf('script_line') fh.write('# Scripted test %s\n'%self.service_description) output_file = self.getconf('output_file') env = { 'script_line': script_line, 'output_file': output_file } if self.hasconf('required_programs'): env['required_programs'] = self.getconf('required_programs') fh.write(_presence_script % env) else: fh.write(_plain_script % env) fh.write('\n') def staged_inputs(self): return self.getconf_strlist('staged_inputs', default = []) def staged_outputs(self): return [(self.getconf('output_file'), None, [])] def check(self, report, jobdir, stored_urls): output_file = os.path.join(jobdir, self.getconf('output_file')) if self.hasconf('output_pattern'): pattern_re = re.compile(self.getconf('output_pattern')) else: pattern_re = None try: fh = open(output_file) except IOError: report.update_status(UNKNOWN, 'Did not receive output file %s.'%output_file) return for ln in fh: if pattern_re: mo = re.match(pattern_re, ln) if mo: msg = self.getconf('status_ok', vars = mo.groupdict()) report.update_status(OK, msg) fh.close() return mo = re.match(self._missing_re, ln) if mo: msg = 'Missing program(s) %s'%mo.group(1) report.update_status(CRITICAL, msg) break mo = re.match(self._exit_re, ln) if mo: code = int(mo.group(1)) if code: msg = 'Script exited with code %d.'%code report.update_status(CRITICAL, msg) continue mo = re.match(self._status_re, ln) if mo: report.update_status(int(mo.group(1)), mo.group(2)) continue mo = re.match(self._log_re, ln) if mo: report.log.log(int(mo.group(1)), mo.group(2)) continue fh.close() if pattern_re: report.update_status(CRITICAL, self.getconf('status_critical', default = 'Pattern not found.')) nordugrid-arc-nagios-plugins-1.8.4/arcnagios/jobplugins/staging.py0000644000175000002070000000550412410507436026057 0ustar mockbuildmockbuildfrom arcnagios import arcutils, nagutils from arcnagios.jobplugin import JobPlugin import os, re, tempfile def _split_url_urloptions(urlspec): if ';' in urlspec: xs = urlspec.split(';') return (xs[0], xs[1:]) else: return (urlspec, []) _lfc_re = re.compile(r'lfc://([a-z+-]+://[^@]+)@.*') class StagingJobPlugin(JobPlugin): def staged_outputs(self): urls = self.getconf_strlist('staged_outputs', default = []) urls = map(_split_url_urloptions, urls) return [('%s-out-%d'%(self.test_name, i), url, urloptions) for i, (url, urloptions) in zip(range(0, len(urls)), urls)] def staged_inputs(self): urls = self.getconf_strlist('staged_inputs', default = []) urls = map(_split_url_urloptions, urls) return [('%s-in-%d'%(self.test_name, i), url, urloptions) for i, (url, urloptions) in zip(range(0, len(urls)), urls)] def upload_test_file(self, url): try: try: fd, fn = tempfile.mkstemp(prefix = 'tmp-check_arcce_submit-', text = True) fh = os.fdopen(fd, 'w') fh.write('This file was uploaded for use by ARC-CE ' 'staging probes.\n') fh.close() except Exception, xc: self.log.warning('Failed to create a temporary file ' 'for upload, using /dev/null: %s'%xc) fn = '/dev/null' self.log.info('Uploading %s to %s.'%(fn, url)) try: arcutils.arccp(fn, url) finally: if fn != '/dev/null': os.unlink(fn) except arcutils.CalledProcessError, xc: self.log.warning('Failed to create %s: %s'%(url, xc)) def upload_missing_test_files(self): urls = self.getconf_strlist('upload_if_missing', default = []) for url in urls: try: mo = re.match(_lfc_re, url) if mo: rep_url = mo.group(1) rep_urls = arcutils.arcls_L(url, log = self.log) if rep_urls == []: self.upload_test_file(url) elif not rep_url in rep_urls: arcutils.arccp(url, rep_url) arcutils.arccp_T(rep_url, url, log = self.log) elif arcutils.arcls(url, log = self.log) == []: self.upload_test_file(url) except arcutils.CalledProcessError, xc: self.upload_test_file(url) def write_script(self, fh): self.upload_missing_test_files() fh.write('# Test "%s" handled by the "staging" job-plugin.\n' % self.test_name) for filename, _0, _1 in self.staged_outputs(): fh.write('hostname >%s\n'%filename) fh.write('\n') def check(self, report, jobdir, stored_urls): for url in stored_urls: try: files = arcutils.arcls(url, log = report.log) if len(files) == 0: report.log.error('Could not list %s.'%url) report.update_states(nagutils.CRITICAL) elif len(files) > 1: report.log.warning('Got multiple entries from %s:'%url) for file in files: report.log.warning(' %s'%file) except arcutils.CalledProcessError, xc: report.log.error('Failed to list %s using arcls: %s'%(url, xc)) report.update_status(nagutils.CRITICAL) nordugrid-arc-nagios-plugins-1.8.4/arcnagios/nagutils.py0000644000175000002070000003651212311545247024102 0ustar mockbuildmockbuildimport logging, os, sys, tempfile, time, traceback from StringIO import StringIO import argparse, confargparse, ConfigParser from arcnagios.utils import counted_noun, lazy_property from ConfigParser import NoOptionError, InterpolationError from glob import glob log = logging.getLogger(__name__) DEPRECATED_CONFIG_PATHS = [ '/etc/nagios/plugins/arcnagios.ini', '/etc/nagios/plugins/arcnagios-local.ini', ] OK = 0 WARNING = 1 CRITICAL = 2 UNKNOWN = 3 _status_names = ['OK', 'WARNING', 'CRITICAL', 'UNKNOWN'] def status_name(status): return _status_names[status] _status_by_name = {'OK': OK, 'WARNING': WARNING, 'CRITICAL': CRITICAL, 'UNKNOWN': UNKNOWN} def status_by_name(status_name): try: return _status_by_name[status_name.upper()] except KeyError: raise ValueError('%s is not a Nagios status name.'%status_name) class NagiosPerfdata(object): def __init__(self, label, value, uom = None, warning = None, critical = None, min = None, max = None): self.label = label self.value = value self.uom = uom self.warning = warning self.critical = critical self.min = min self.max = max def __str__(self): return '%s=%s%s;%s;%s;%s;%s'%( self.label, self.value, self.uom or '', self.warning or '', self.critical or '', self.min or '', self.max or '') class NagiosReport(object): """Instances of this class collects information to be reported to Nagios. You should use one instance for the active check result, and one instance for each passive (``host_name``, ``service_description``) kombination to target.""" def __init__(self, host_name, service_description): self._log_buffer = StringIO() self.host_name = host_name self.service_description = service_description self.log = logging.Logger('%s/%s'%(host_name, service_description)) self.log.addHandler(logging.StreamHandler(self._log_buffer)) self.status_code = OK self.status_messages = [[], [], [], []] self.status_code_counts = [0, 0, 0, 0] def update_status_code(self, status_code): """Update the Nagios exit code to the maximum of ``status_code`` and the current code.""" assert OK <= status_code and status_code <= UNKNOWN self.status_code_counts[status_code] += 1 if status_code > self.status_code: self.status_code = status_code def update_status(self, status_code, status_message = None): """Update the Nagios exit code to the maximum of ``status_code`` and the current code, and add ``status_message`` to the messages to be used in case no higher exit codes overrides this call.""" self.update_status_code(status_code) if status_message: self.status_messages[status_code].append(status_message) def status_message(self, subject = None): """Format a status message suitable as the first line of output to Nagios. This will be based on the calls to `update_status` which are relevant for the final exit code. If no messages are registered for the code, make a generic message, possibly referring to `subject`.""" if self.status_messages[self.status_code] == []: name = subject or 'service' count = self.status_code_counts[self.status_code] if self.status_code == OK: return '%s OK'%(name.capitalize()) elif self.status_code == WARNING: return '%s in %s'%(counted_noun(count, 'warning'), name) elif self.status_code == CRITICAL: return '%s in %s'%(counted_noun(count, 'error'), name) else: return 'Failed to check %s'%name else: return ' '.join(self.status_messages[self.status_code]) @property def status_details(self): """A string containing messages logged to `self.log`.""" return self._log_buffer.getvalue() class NagiosPlugin(object): probe_name = None bugtracker_url = 'http://bugzilla.nordugrid.org/' main_config_section = None def __init__(self, use_host = False, use_port = False, default_port = None): # Set up a logger for collecting messages and initialize perfdata. self.perfdatas = [] self._passive_reports = {} def scan_loglevel(s): try: return int(s) except ValueError: pass try: return {'DEBUG': logging.DEBUG, 'INFO': logging.INFO, 'WARN': logging.WARN, 'WARNING': logging.WARNING, 'ERROR': logging.ERROR, 'CRITICAL': logging.CRITICAL}[s.upper()] except KeyError: raise argparse.ArgumentTypeError('Invalid loglevel.') # Define the base argument parsers. Plug-in implementations will # extend it or the sub-parsers. self.argparser = confargparse.ConfigArgumentParser() ap = self.argparser.add_argument_group('General Options') ap.add_argument('--loglevel', dest = 'loglevel', type = scan_loglevel, default = 'WARNING', help = 'Set the log level for NAGIOS messages. ' 'Use either numeric or symbolic names corresponding to ' 'the definitions in the python logging module.') seems_like_manual = os.getenv('NAGIOS_HOSTNAME') is None ap.add_argument('--how-invoked', dest = 'how_invoked', choices = ['manual', 'nagios'], default = seems_like_manual and 'manual' or 'nagios', help = 'Indicate how invoked. ' '(Default: Check a Nagios enviroment variable).') ap.add_argument('--command-file', dest = 'command_file', default = os.getenv('NAGIOS_COMMANDFILE', '/var/spool/nagios/cmd/nagios.cmd'), help = 'The Nagios command file. By default the ' '$NAGIOS_COMMANDFILE environment variable is used, ' 'which is usually what you want.') ap.add_argument('--multiline-separator', dest = 'multiline_separator', default = ' - ', help = 'Replacement for newlines when submitting multiline ' 'results to passive services. Pass the empty string ' 'drop extra lines.') if use_host: ap.add_argument('-H', '--host', dest = 'host', help = 'Host of service to check.') if use_port: ap.add_argument('-p', '-P', '--port', dest = 'port', type = int, default = default_port, help = 'Port number of service to connect to. ' 'OBS! The capital -P alternative is deprecated.') ap.add_argument('--dump-options', dest = 'dump_options', default = False, action = 'store_true', help = 'Dump options to standard output. ' 'Believed to be useful for debugging.') ap.add_argument('--plugins-spooldir', dest = 'plugins_spooldir', default = '/var/spool/nagios/plugins', help = 'Top-level spool directory to be used by Nagios ' 'plugins (deprecated).') ap.add_argument('--arcnagios-spooldir', dest = 'arcnagios_spooldir', default = '/var/spool/arc/nagios', help = 'Top-level spool directory to be used by the ARC ' 'Nagios plugins.') ap.add_argument('--home-dir', dest = 'home_dir', help = 'Override $HOME at startup. This is a workaround ' 'for external commands which store things under ' '$HOME on systems where the nagios account does ' 'not have an appropriate or writable home directory.') ap.add_argument('--import', dest = '_imports', action = 'append', default = [], metavar = 'MODULE', help = 'Import the given module. The module initializer can ' 'register implementations for arcnagios.substitution ' 'or arcnagios.jobplugins, or modify the enviroment.') self._remove_at_exit = [] self._at_exit = [] self._created_tmpdir = False # Initialize mix-ins if any. super(NagiosPlugin, self).__init__() def config_dir(self): return os.getenv('ARCNAGIOS_CONFIG_DIR', '/etc/arc/nagios') def config_paths(self): """A list of paths to search for the configuration.""" config_paths_env = os.getenv('ARCNAGIOS_CONFIG') if not config_paths_env is None: return config_paths_env.split(':') else: config_paths = glob(os.path.join(self.config_dir(), '*.ini')) config_paths.sort() if os.path.exists('/etc/nagios/plugins'): config_paths = DEPRECATED_CONFIG_PATHS + config_paths return config_paths @lazy_property def config(self): environment = {} environment['epoch_time'] = str(int(time.time())) cp = ConfigParser.SafeConfigParser(defaults = environment) cp.read(self.config_paths()) return cp def at_exit(self, f): self._at_exit.append(f) def remove_at_exit(self, *paths): self._remove_at_exit += paths def tmpdir(self): tmpdir = os.path.join(self.opts.arcnagios_spooldir, 'tmp') if not self._created_tmpdir and not os.path.exists(tmpdir): try: os.makedirs(tmpdir) except OSError, xc: self.nagios_exit(UNKNOWN, 'Cannot create %s: %s'%(tmpdir, xc)) return tmpdir def mkstemp(self, suffix = '', prefix = 'tmp'): tmpdir = self.tmpdir() fd, path = tempfile.mkstemp(suffix, prefix, tmpdir) self.remove_at_exit(path) return fd, path def mktemp(self, suffix = '', prefix = 'tmp'): tmpdir = self.tmpdir() path = tempfile.mktemp(suffix, prefix, tmpdir) self.remove_at_exit(path) return path def parse_args(self, args = None): try: if self.main_config_section: self.argparser.configure_defaults( self.config, self.main_config_section) self.opts = self.argparser.parse_args(args) except Exception, xc: self.nagios_report = NagiosReport(None, 'ACTIVE') self.log = self.nagios_report.log raise xc host_name = getattr(self.opts, 'host', '') self.nagios_report = NagiosReport(host_name, 'ACTIVE') self.log = self.nagios_report.log if not self.opts.home_dir is None: os.putenv('HOME', self.opts.home_dir) # Set the log level. The level of self.log determines how much is # reported as additional lines to Nagios. self.log.setLevel(self.opts.loglevel) if self.opts.how_invoked == 'manual': # File-level loggers are used for debugging. Only increase the # level if manually invoked, since we may otherwise obscure the # input to Nagios. log.root.setLevel(self.opts.loglevel) try: for m in self.opts._imports: __import__(m) except ImportError, xc: raise ServiceUNKNOWN('Error importing %s: %s'%(m, xc)) def check(self): """Override this method in your plugin to implement the test. You should not call this directly, but use `nagios_run`, which will handle argument parsing and report results to Nagios.""" raise NotImplementedError('The `check` method has not been implemented.') def nagios_run(self): """This is the method to invoke from the probe script. It parses command-line options, calls the probe, prints Nagios-formatted output to stdout, and exits with an appropriate code.""" # Parse command-line arguments and configuration file. try: self.parse_args(sys.argv[1:]) except confargparse.UsageError, xc: self.log.error(str(xc)) self.log.error('Need --help?') self.nagios_exit(UNKNOWN, 'Invalid command-line options.' % xc) except confargparse.ConfigError, xc: self.log.error(str(xc)) self.nagios_exit(UNKNOWN, 'Invalid configuration.' % xc) if self.opts.dump_options: for k, v in vars(self.opts).iteritems(): self.log.info('%s = %r'%(k, v)) # Run the metric and report. try: sr = self.check() except ServiceReport, sr: sr = sr except SystemExit, xc: raise xc except NoOptionError, xc: return self.nagios_exit(UNKNOWN, str(xc)) except InterpolationError, xc: for ln in str(xc).split('\n'): self.log.error(ln) return self.nagios_exit(UNKNOWN, 'Error in configuration file.') except Exception, xc: _, __, tb = sys.exc_info() self.log.error('----%<----') self.log.error('Please report this bug to %s including ' 'the following:'%self.bugtracker_url) self.log.error('%r'%xc) self.log.error('Traceback:') for ln in traceback.format_tb(tb): self.log.error(ln.strip()) self.log.error('----%<----') self.nagios_exit(UNKNOWN, 'Bug in Nagios probe.') if sr is None: self.nagios_exit() elif not isinstance(sr, ServiceReport): self.nagios_exit(UNKNOWN, 'Invalid value %r returned by plugin.'%sr) else: self.nagios_exit(sr.status, sr.message) def add_perfdata(self, label, value, **kwargs): self.perfdatas.append(NagiosPerfdata(label, value, **kwargs)) def add_perfdatas(self, *args): for arg in args: if not isinstance(arg, NagiosPerfdata): raise ValueError('add_perfdata expects NagiosPerfdata arguments.') self.perfdatas += args def nagios_report_for(self, host_name, service_description, create = True): """Return a `NagiosReport` instance which will be submitted to the passive service `service_description` on `host_name`. Each time you call this with the same `host_name` and `service_description`, you will get the same instance.""" key = (host_name, service_description) report = self._passive_reports.get(key, None) if report is None and create: report = NagiosReport(host_name, service_description) self._passive_reports[key] = report return report def nagios_exit(self, status_code = OK, status_message = None, subject = None): """Submit all passives check results, update `self.nagios_report` with the given `status_code` and `status_message` if specified, then communicate `self.nagios_report` as the active check result. In particular, this writes out status messages, perfdatas and logging written to `self.nagios_report`, and calls `sys.exit` with `self.nagios_report.status_code`.""" for f in self._at_exit: f() for path in self._remove_at_exit: try: os.remove(path) except OSError, xc: pass for report in self._passive_reports.itervalues(): self.submit_passive_service_result( report.host_name, report.service_description, report.status_code, report.status_message(subject), report.status_details) self.nagios_report.update_status(status_code, status_message) sys.stdout.write(self.nagios_report.status_message(subject)) sys.stdout.write('\n') sys.stdout.write(self.nagios_report.status_details) if len(self.perfdatas): sys.stdout.write('|') for pd in self.perfdatas: sys.stdout.write('%s\n', pd) sys.exit(self.nagios_report.status_code) def submit_passive_service_result(self, host_name, svc_description, return_code, plugin_output, details = None): """Manually submit a passive service result. It is in general more convenient to use `nagios_report_for` and let `nagios_run` submit the results.""" t = int(time.time()) if details and self.opts.multiline_separator: # FIXME: How do we submit multi-line results to passive services? # Currently it seems not to be supported. This will look ugly, # but it's better than leaving the operator clueless. sep = self.opts.multiline_separator plugin_output += sep + details.strip().replace('\n', sep) rstr = '[%d] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s\n' % \ (t, host_name, svc_description, return_code, plugin_output) if self.opts.how_invoked == 'manual': log.info(rstr) return try: cmd_fh = open(self.opts.command_file, 'w') cmd_fh.write(rstr) cmd_fh.close() except IOError, xc: raise ServiceUNKNOWN('Cannot write to NAGIOS command file %s: %s' % (self.opts.command_file, xc)) class ServiceReport(Exception): def __init__(self, status, message): self.status = status self.message = message Exception.__init__(self, status_name(status) + ': ' + message) class ServiceOK(ServiceReport): def __init__(self, message): ServiceReport.__init__(self, OK, message) class ServiceWARNING(ServiceReport): def __init__(self, message): ServiceReport.__init__(self, WARNING, message) class ServiceCRITICAL(ServiceReport): def __init__(self, message): ServiceReport.__init__(self, CRITICAL, message) class ServiceUNKNOWN(ServiceReport): def __init__(self, message): ServiceReport.__init__(self, UNKNOWN, message) nordugrid-arc-nagios-plugins-1.8.4/arcnagios/persistence.py0000644000175000002070000000446412375416617024611 0ustar mockbuildmockbuildimport logging from arcutils import ParseError log = logging.getLogger(__name__) # Type Descriptors of the form (decode, encode, required). # def pt_t(t_of_str, str_of_t = str, required = True): return (t_of_str, str_of_t, required) def pt_list(pt_elt): def decode(s): if s.strip(): return map(pt_elt[0], s.split(', ')) else: return [] def encode(xs): return ', '.join(map(pt_elt[1], xs)) return (decode, encode, False) pt_int = (int, str, True) pt_int_opt = (int, str, False) pt_str = (str, str, True) pt_str_opt = (str, str, False) pt_str_list = pt_list(pt_str) # Persistent Objects # class PersistentObject(object): """A class which represents a simple collection of attributes with human-readable pickeling. The pickeling is limited to what is needed by the Nagios plugins. The main point of this is to make the data presentable to Nagios operator.""" persistent_attributes = {} def __init__(self, **kwargs): for k, _ in self.persistent_attributes.iteritems(): v = kwargs.pop(k, None) setattr(self, k, v) if kwargs: raise TypeError('Invalid keyword argument(s) %s.' % ', '.join(kwargs)) def persistent_load(self, path, log = log): fh = open(path) lnno = 0 for ln in fh: lnno += 1 kv = ln.split(': ', 1) if len(kv) != 2: log.error('%s:%d: Invalid or old file format.' \ % (path, lnno)) raise ParseError('Invalid format for PersistentObject.') k, v = kv v = v.strip() if not k in self.persistent_attributes: log.warning('%s:%d: Ignoring unknown attribute.'%(path, lnno)) continue try: setattr(self, k, self.persistent_attributes[k][0](v)) except Exception, xc: log.error('%s:%d: %s'%(path, lnno, xc)) fh.close() for k, (_, _, req) in self.persistent_attributes.iteritems(): if req: if getattr(self, k) is None: raise ParseError('Missing required attribute %s.'%k) def persistent_save(self, path): for k, (_, _, req) in self.persistent_attributes.iteritems(): if req: if getattr(self, k) is None: raise ValueError('Tried to save incomplete persistent ' 'object; missing attribute %s.'%k) fh = open(path, 'w') for k, c in self.persistent_attributes.iteritems(): v = getattr(self, k) if not v is None: fh.write('%s: %s\n'%(k, c[1](v))) fh.close() nordugrid-arc-nagios-plugins-1.8.4/arcnagios/__init__.py0000644000175000002070000000000011646024635023775 0ustar mockbuildmockbuildnordugrid-arc-nagios-plugins-1.8.4/arcnagios/jobplugin.py0000644000175000002070000000707312301163435024237 0ustar mockbuildmockbuildimport re from utils import ident, unspecified from arcnagios import substitution from arcnagios.nagutils import ServiceUNKNOWN def boolean(s): w = s.lower() if w in ['0', 'false', 'no', 'off']: return False if w in ['1', 'true', 'yes', 'on']: return True raise ValueError('invalid literal for boolean: %r'%s) _interp_re = re.compile(r'%\(([a-zA-Z0-9_]+)\)') class JobPlugin(object): """A base-class for tests to run within a job script. Implementations provide commands to run, and how to extract the result. Optionally it may specify staging and cleanup.""" def __init__(self, name, config, config_section, log, env = {}): self.name = name self.config = config self.config_section = config_section self.test_name = config_section[6:] # Strip "arcce." self.log = log self.environment = env def _import_interpolations(self, var): if not var in self.environment \ and self.config.has_option(self.config_section, var): raw_value = self.config.get(self.config_section, var, raw = True) for mo in re.finditer(_interp_re, raw_value): v = mo.group(1) if not v in self.environment \ and self.config.has_section('variable.' + v): substitution.import_variable(self.config, v, self.environment) def _update_vars(self, kwargs): if 'vars' in kwargs: kwargs['vars'].update(self.environment) else: kwargs['vars'] = self.environment def hasconf(self, var): return self.config.has_option(self.config_section, var) def getconf(self, var, default = unspecified, type = ident, **kwargs): if not default is unspecified and not self.hasconf(var): return default self._import_interpolations(var) self._update_vars(kwargs) try: return type(self.config.get(self.config_section, var, **kwargs)) except ValueError, xc: raise ServiceUNKNOWN('Bad value for configuration parameter %s in ' 'section %s: %s' % (var, self.config_section, xc)) def getconf_int(self, var, default = unspecified, **kwargs): self.getconf(var, default = default, type = int, **kwargs) def getconf_bool(self, var, default = unspecified, **kwargs): self.getconf(var, default = default, type = boolean, **kwargs) def getconf_float(self, var, default = unspecified, **kwargs): self.getconf(var, default = default, type = float, **kwargs) def getconf_strlist(self, var, default = unspecified, sep = None, **kwargs): if not default is unspecified and not self.hasconf(var): return default self._import_interpolations(var) self._update_vars(kwargs) raw = self.config.get(self.config_section, var, **kwargs) return [s.strip() for s in raw.split(sep)] @property def service_description(self): return self.getconf('service_description', None) def staged_inputs(self): """Override this method to specify files used by the script.""" return [] def staged_outputs(self): """Override this method to specify files produced by the script, which are needed by `extract_result`.""" return [] def runtime_environments(self): return filter(None, [x.strip() for x in self.getconf('runtime_environments', '').split(',')]) def write_script(self, fh): """This method may write commands to run in the job script. The commands are run in the standard shell (/bin/sh), and must not call exit or otherwise disrupt the control flow of the script, since other commands are run in the same script.""" pass def check(self, report, jobdir, stored_urls): """This method is run to check the output of a job.""" raise NotImplementedError('extract_result') def cleanup(self, job_state): pass nordugrid-arc-nagios-plugins-1.8.4/arcnagios/ldapnagutils.py0000644000175000002070000000620112120113236024716 0ustar mockbuildmockbuildimport ldap, logging, time from arcnagios.confargparse import UsageError from arcnagios import nagutils, vomsutils class LDAPNagiosPlugin(nagutils.NagiosPlugin, vomsutils.NagiosPluginVomsMixin): def __init__(self, default_uri = None, default_basedn = None, **kwargs): nagutils.NagiosPlugin.__init__(self, **kwargs) ap = self.argparser.add_argument_group('LDAP Options') ap.add_argument('-H', dest = 'host', help = 'Host to query. This will be used for the LDAP ' 'connection if --ldap-uri is not specified.') if not 'use_port' in kwargs: # compat ap.add_argument('-p', dest = 'port', type = int, default = 2135, help = 'The LDAP port to use if --ldap-uri was not given. ' 'The default is 2135.') ap.add_argument('--ldap-basedn', dest = 'ldap_basedn', default = default_basedn, help = 'Base DN to query if non-standard.') ap.add_argument('--ldap-uri', dest = 'ldap_uri', default = default_uri, help = 'LDAP URI of the infosystem to query.') ap.add_argument('-t', '--timeout', dest = 'timeout', type = int, default = 300, help = 'Overall timeout of the probe.') def parse_args(self, args): nagutils.NagiosPlugin.parse_args(self, args) if not self.opts.ldap_uri: if not self.opts.host: raise UsageError('Either --ldap-uri or -H must be specified.') self.opts.ldap_uri = 'ldap://%s:%d'%(self.opts.host, self.opts.port) @property def time_left(self): return self.time_limit - time.time() def prepare_check(self): self.log.debug('Using LDAP URI %s.'%self.opts.ldap_uri) self.lconn = ldap.initialize(self.opts.ldap_uri) self.time_limit = time.time() + self.opts.timeout def search_s(self, basedn, scope, *args, **kwargs): """Customized LDAP search with timeout and Nagios error reporting.""" self.log.debug('Searching %s.'%basedn) if self.time_left <= 0: raise nagutils.ServiceCRITICAL('Timeout before LDAP search.') try: return self.lconn.search_st(basedn, scope, timeout = self.time_left, *args, **kwargs) except ldap.TIMEOUT: raise nagutils.ServiceCRITICAL('Timeout during LDAP search.') except ldap.NO_SUCH_OBJECT: return [] except ldap.LDAPError, xc: self.log.error('LDAP details: basedn = %s, scope = %d', basedn, scope) self.log.error('LDAP error: %s'%xc) raise nagutils.ServiceCRITICAL('LDAP Search failed.') def fetch_subschema(self): """Fetch the subschema for the given connection and return the corresponding `ldap.schema.SubSchema` object.""" # TODO: Consider caching the subschema for a period of time, since it # can be big. self.log.debug('Fetching subschema.') sr = self.search_s('cn=subschema', ldap.SCOPE_BASE, attrlist = ['+', '*']) if len(sr) != 1: raise nagutils.ServiceCRITICAL('Could not fetch subschema.') subschema_subentry = sr[0][1] return ldap.schema.SubSchema(subschema_subentry) def debug_dump_obj(self, obj, name): # Dump vars(obj) if debugging is enabled. if self.log.getEffectiveLevel() >= logging.DEBUG: self.log.debug('Dump of %s:'%name) for k, v in vars(obj).iteritems(): if isinstance(v, list) and len(v) > 4: v = v[0:4] + ['...'] self.log.debug(' %s: %r'%(k, v)) nordugrid-arc-nagios-plugins-1.8.4/arcnagios/jobutils.py0000644000175000002070000003260612437642033024107 0ustar mockbuildmockbuildimport arc, os, pipes, shutil, time import nagutils, vomsutils from arcnagios import arcutils, persistence from arcnagios.rescheduler import Rescheduler from arcnagios.jobplugins import load_jobplugin from arcnagios.utils import lazy_property from arcutils import ParseError from genshi.template.loader import TemplateLoader, TemplateNotFound from glob import glob class JobDescription(object): _required_attributes = ["job_name", "application_name", "script_path"] def __init__(self, job_name = None, application_name = None, logdir = None, script_path = None, script_args = [], output = 'stdout.txt', error = 'stderr.txt', wall_time_limit = None, memory_limit = None, staged_inputs = [], staged_outputs = [], runtime_environments = [], queue_name = None, template = 'default.jsdl'): self.template = template self.job_name = job_name or 'ARC Probe' self.application_name = application_name self.logdir = logdir self.script_path = script_path self.script_name = os.path.basename(script_path) self.script_args = script_args self.output = output self.error = error self.wall_time_limit = wall_time_limit self.memory_limit = memory_limit self.staged_inputs = staged_inputs self.staged_outputs = staged_outputs self.runtime_environments = runtime_environments self.queue_name = queue_name def verify(self): for attr in self._required_attributes: if getattr(self, attr) is None: raise nagutils.ServiceUNKNOWN('Missing %s for job description.' % attr) class JobInfo(persistence.PersistentObject): persistent_attributes = { 'submission_time': persistence.pt_int, 'host': persistence.pt_str, 'job_tag': persistence.pt_str_opt, 'progress_service': persistence.pt_str_opt, 'termination_service': persistence.pt_str_opt, 'job_id': persistence.pt_str, 'job_state': persistence.pt_t(arcutils.jobstate_of_str), 'job_specific_state': persistence.pt_str_opt, 'job_state_time': persistence.pt_int_opt, 'job_state_alert': persistence.pt_int_opt, 'check_time': persistence.pt_int_opt, 'check_attempts': persistence.pt_int_opt, 'fetch_attempts': persistence.pt_int_opt, 'stored_urls': persistence.pt_str_list, 'tests': persistence.pt_str_list, } # COMPAT: To load old job descriptions. Only needed for one release to convert # existing descriptions. def compat_load_job_info(path): active_job_attrs = [ (int, 'submission_time'), (str, 'host'), (str, 'job_tag'), (str, 'termination_service'), (str, 'job_id'), (str, 'job_state'), (int, 'check_time'), (int, 'fetch_attempts'), ] fh = open(path) vs = [ln.strip() for ln in fh] fh.close() # COMPAT: Addition of job_tag. Remove later. if len(vs) == 8: vs = vs[0:2] + ['__none'] + vs[2:] if len(active_job_attrs) + 1 != len(vs): msg = 'Malformed job info %s, expecting %d, not %d elements.' \ % (path, len(active_job_attrs) + 1, len(vs)) raise ParseError(msg) stored_urls = vs.pop().split() d = {} for (t, k), v in zip(active_job_attrs, vs): if v == '__none': d[k] = None else: try: d[k] = t(v) except ValueError: raise ParseError('Bad value %s for %s in job info %s' %(v, k, path)) return JobInfo(stored_urls = stored_urls, **d) def parse_staging(spec): """Parse the arguments to the --stage-input and --stage-output options.""" if ';' in spec: xs = spec.split(';') spec, urloptions = xs[0], xs[1:] else: urloptions = [] if '=' in spec: filename, url = spec.split('=', 1) else: filename, url = os.path.basename(spec), spec return (filename, url, urloptions) def key_value(s): kv = s.split('=', 1) if len(kv) != 2: raise ValueError('Expecting an argument of the form KEY=VALUE.') return kv class JobNagiosPlugin(nagutils.NagiosPlugin, vomsutils.NagiosPluginVomsMixin): """Nagios probe to test ARC CEs. The probe has two sub-commands implemented by `check_submit` and `check_monitor`. The former is run on all CEs, while the latter is run to collect submitted jobs.""" probe_name = 'ARCCE' main_config_section = ['arcce'] JSDL_FILENAME = 'job.jsdl' JOB_SCRIPT_FILENAME = 'job.sh' JOBID_FILENAME = 'active.jobid' ACTIVE_JOB_FILENAME = 'active.map' JOB_OUTPUT_DIRNAME = 'job_output' _archive_filenames = [ JOBID_FILENAME, JSDL_FILENAME, JOB_SCRIPT_FILENAME, ACTIVE_JOB_FILENAME ] _arc_bindir = None prev_status = None def __init__(self, **kwargs): default_template_dirs = glob(os.path.join(self.config_dir(),'*.d')) default_template_dirs.sort() nagutils.NagiosPlugin.__init__(self, **kwargs) ap = self.argparser ap.add_argument('--fqan', dest = 'fqan') ap.add_argument('--top-workdir', dest = 'top_workdir', help = 'Parent directory of per-VO probe working directories ' ' (deprecated)') ap.add_argument('-O', dest = 'jobtest_options', action = 'append', type = key_value, default = [], help = 'Given a value of the form VAR=VALUE, binds VAR to ' 'VALUE in the environment of the job tests.') ap.add_argument('--template-dir', dest = 'template_dirs', action = 'append', default = default_template_dirs, help = 'Add a directory from which job description templates ' 'can be loaded.') self._user_config = arc.UserConfig() def parse_args(self, args): nagutils.NagiosPlugin.parse_args(self, args) self.opts.template_dirs.reverse() def top_vopath(self, suffix): return os.path.join(self.opts.arcnagios_spooldir, self.voms_suffixed('ce') + '-' + suffix) def _migrate_old_workdir(self, new_dir): templ = self.opts.top_workdir if templ is None: templ = os.path.join(self.opts.plugins_spooldir, 'arcce/%(voms)s') old_dir = templ % vars(self.opts) if new_dir != old_dir and os.path.exists(old_dir): self.log.info('Migrating %s to %s.' % (old_dir, new_dir)) try: new_parent_dir = os.path.dirname(new_dir) if not os.path.exists(new_parent_dir): os.makedirs(new_parent_dir) os.rename(old_dir, new_dir) except OSError, xc: self.log.error('Cannot migrate data from %s to %s: %s' % (old_dir, new_dir, xc)) self.log.error('Remove former to start using the new spool.') return old_dir return new_dir @lazy_property def top_workdir(self): dir = self.top_vopath('state') if not os.path.exists(dir): # COMPAT 2014-03 return self._migrate_old_workdir(dir) return dir def workdir_for(self, host, job_tag): if job_tag: return os.path.join(self.top_workdir, host + '#' + job_tag) else: return os.path.join(self.top_workdir, host) def archivedir_for(self, host, job_tag): return os.path.join(self.top_vopath('archive'), time.strftime('%Y-%m/%Y-%m-%d/%H:%M:%S-') + host + (job_tag and ('#' + job_tag) or '')) @lazy_property def template_loader(self): return TemplateLoader(self.opts.template_dirs) def write_jsdl(self, out_path, jobdesc): jobdesc.verify() try: templ = self.template_loader.load(jobdesc.template) except TemplateNotFound, xc: raise nagutils.ServiceUNKNOWN('%s. The searched included %s.' % (xc, ', '.join(self.opts.template_dirs))) r = templ.generate(jd = jobdesc) fd = open(out_path, 'w') fd.write(r.render()) fd.close() def _arcrm(self, url, n_attempts): if n_attempts > 8: rc, output = self.run_arc_cmd('arcrm', '-f', url) else: rc, output = self.run_arc_cmd('arcrm', url) if rc != 0: self.log.warn('Failed to remove %s.' % url) return False else: self.log.info('Removed test file %s.' % url) return True def _arcclean(self, job_id, n_attempts): if n_attempts > 8: rc, output = self.run_arc_cmd('arcclean', '-f', job_id) else: rc, output = self.run_arc_cmd('arcclean', job_id) if rc != 0: self.log.warning('Failed to clean %s' % job_id) return False else: self.log.info('Removed job %s' % job_id) return True def _arckill(self, job_id, n_attepmts): rc, kill_output = self.run_arc_cmd('arckill', job_id) if rc == 0: return True rc, clean_output = self.run_arc_cmd('arcclean', job_id) if rc == 0: return True self.log.warning('Failed to kill %s: %s' % (job_id, kill_output)) return False @lazy_property def cleaner(self): cleaner = Rescheduler(self.top_vopath('state.db'), 'cleaner', log = self.log) cleaner.register('arcrm', self._arcrm) cleaner.register('arcclean', self._arcclean) cleaner.register('arckill', self._arckill) self.at_exit(cleaner.run_and_close) return cleaner def cleanup_job_files(self, host, job_tag, archive = False): self.log.debug('Cleaning up job files for %s.'%host) workdir = self.workdir_for(host, job_tag) # archdir = os.path.join(workdir, # time.strftime('archive/%Y-%m-%d/%H:%M:%S')) archdir = self.archivedir_for(host, job_tag) archdir_created = False for filename in self._archive_filenames: try: if archive: if os.path.exists(os.path.join(workdir, filename)): if not archdir_created: os.makedirs(archdir) archdir_created = True os.rename(os.path.join(workdir, filename), os.path.join(archdir, filename)) else: os.unlink(os.path.join(workdir, filename)) except StandardError: pass try: job_output_dir = os.path.join(workdir, self.JOB_OUTPUT_DIRNAME) if os.path.exists(job_output_dir) and os.listdir(job_output_dir): if archive: if not archdir_created: os.makedirs(archdir) archdir_created = True os.rename(job_output_dir, os.path.join(archdir, self.JOB_OUTPUT_DIRNAME)) else: last_dir = job_output_dir + '.LAST' shutil.rmtree(last_dir, ignore_errors = True) os.rename(job_output_dir, last_dir) except StandardError, xc: self.log.warn('Error clearing %s: %s'%(job_output_dir, xc)) def run_arc_cmd(self, prog, *args, **kwargs): if self._arc_bindir: prog = os.path.join(self._arc_bindir, prog) cmd = prog + ' ' + ' '.join([pipes.quote(str(arg)) for arg in args]) self.log.debug('Exec: %s'%cmd) fh = os.popen(cmd + ' 2>&1') output = fh.read() err = fh.close() return err or 0, output def require_voms_proxy(self): proxy_path = vomsutils.NagiosPluginVomsMixin.require_voms_proxy(self) # self._user_config.KeyPath(self.opts.user_key) # self._user_config.CertificatePath(self.opts.user_cert) if proxy_path: self._user_config.ProxyPath(proxy_path) try: self._user_config.InitializeCredentials() # old API except TypeError: self._user_config.InitializeCredentials( arc.initializeCredentialsType( arc.initializeCredentialsType.RequireCredentials)) def load_active_job(self, host, job_tag): """Load information about the current job on `host : str` tagged with `job_tag : str`, or `None` if no information is found.""" workdir = self.workdir_for(host, job_tag) ajf = os.path.join(workdir, self.ACTIVE_JOB_FILENAME) if os.path.exists(ajf): self.log.debug('Loading job info from %s.'%ajf) # FIXME: Lock. try: jobinfo = JobInfo() jobinfo.persistent_load(ajf) return jobinfo except ParseError, xc: try: jobinfo = compat_load_job_info(ajf) jobinfo.persistent_save(ajf) # Update to new format. return jobinfo except Exception: self.log.error('Ignoring invalid job info %s: %s'%(ajf, xc)) def save_active_job(self, jobinfo, host, job_tag): """Save information about the current job running on `host : str` tagged with `job_tag : str`.""" workdir = self.workdir_for(host, job_tag) ajf = os.path.join(workdir, self.ACTIVE_JOB_FILENAME) self.log.debug('Saving active job info.') # FIXME: Lock. jobinfo.persistent_save(ajf) def discard_stored_urls(self, jobinfo): for url in jobinfo.stored_urls: if not url.startswith('file:'): self.cleaner.call('arcrm', url) def cleanup_job_tests(self, jobinfo): for test_name in jobinfo.tests: try: test = self.load_jobtest(test_name, hostname = jobinfo.host) test.cleanup(jobinfo.job_state) except Exception, xc: self.log.error('Error in cleanup %s for %s: %s' % (test_name, jobinfo.job_id, xc)) def cleanup_job(self, jobinfo, archive = False): """Clean up job state from a fetched job.""" self.discard_stored_urls(jobinfo) self.cleanup_job_tests(jobinfo) self.cleanup_job_files(jobinfo.host, jobinfo.job_tag, archive = archive) def discard_job(self, jobinfo, archive = False): """Discard the job described by `jobinfo : JobInfo`.""" if jobinfo.job_state.is_final(): self.cleaner.call('arcclean', jobinfo.job_id) else: self.cleaner.call('arckill', jobinfo.job_id) self.cleanup_job(jobinfo, archive = archive) def load_jobtest(self, jobtest_name, **env): """Load a plugin-based job-test from the section of the configuration specified by `jobtest_name`. The result is an instance of `JobPlugin` subclass specified by the ``jobplugin`` variable of the given section.""" env['config_dir'] = self.config_dir() if self.voms: env['voms'] = self.voms env.update(self.opts.jobtest_options) jobplugin_section = 'arcce.%s'%jobtest_name if not self.config.has_section(jobplugin_section): if self.config.has_section('arc-ce.%s'%jobtest_name): self.log.warn('The section arc-ce.%s is deprecated, please use %s.' % (jobtest_name, jobplugin_section)) jobplugin_section = 'arc-ce.%s'%jobtest_name else: raise nagutils.ServiceUNKNOWN( 'Missing configuration section %s for ' 'job-plugin test.' % jobplugin_section) jobplugin_name = self.config.get(jobplugin_section, 'jobplugin') jobplugin_cls = load_jobplugin(jobplugin_name) return jobplugin_cls(jobplugin_name, self.config, jobplugin_section, self.log, env) nordugrid-arc-nagios-plugins-1.8.4/arcnagios/rescheduler.py0000644000175000002070000000715612556705775024602 0ustar mockbuildmockbuildimport logging, time from arcnagios.utils import nth busy_timeout = 10 try: import sqlite3 from sqlite3 import OperationalError def dconnect(fp): return sqlite3.connect(fp, busy_timeout) def dexec(c, stmt): return c.execute(stmt) def dcommit(c): c.commit() def dclose(c): c.close() except ImportError: import sqlite, random from sqlite import DatabaseError as OperationalError def dconnect(fp): return sqlite.connect(fp, timeout = busy_timeout) def dexec(c, stmt): # execute seems to be non-blocking even with timeout set. for i in range(0, 100*busy_timeout): try: return c.db.execute(stmt).row_list except OperationalError, xc: if not 'database is locked' in str(xc): raise xc time.sleep(0.01 + random.uniform(-0.002, 0)) def dcommit(c): c.commit() def dclose(c): c.close() _default_log = logging.getLogger(__name__) _create_sql = """\ CREATE TABLE %s ( n_attempts integer NOT NULL, t_sched integer NOT NULL, task_type varchar(16) NOT NULL, arg text NOT NULL )""" def format_time(t): return time.strftime('%Y-%m-%d %H:%M', time.localtime(t)) class TaskType(object): def __init__(self, h, min_delay = 3600, max_attempts = 12): self.handler = h self.min_delay = min_delay self.max_attempts = max_attempts class Rescheduler(object): def __init__(self, db_path, table_name, log = _default_log): self._db = dconnect(db_path) self._table = table_name self._handlers = {} self._log = log try: dexec(self._db, _create_sql % self._table) except OperationalError: pass def close(self): self._db.close() def _update(self, stmt): r = dexec(self._db, stmt) dcommit(self._db) return r def _query(self, stmt): return dexec(self._db, stmt) def register(self, task_type, h, min_delay = 3600, max_attempts = 12): self._handlers[task_type] = TaskType(h, min_delay, max_attempts) def schedule(self, task_type, arg, n_attempts = 0): t_sched = time.time() + self._handlers[task_type].min_delay self._update('INSERT INTO %s (n_attempts, t_sched, task_type, arg) ' 'VALUES (%d, %d, "%s", "%s")' % (self._table, n_attempts, t_sched, task_type, arg)) def call(self, task_type, arg): if self._handlers[task_type].handler(arg, 0): return True else: self.schedule(task_type, arg, n_attempts = 1) return False def run(self): t_now = time.time() r = self._query('SELECT ROWID, n_attempts, t_sched, task_type, arg ' 'FROM %s WHERE t_sched <= %d' % (self._table, t_now)) for id, n_attempts, t_sched, task_type_name, arg in r: if not task_type_name in self._handlers: self._log.warning('No task type %s.' % task_type_name) continue task_type = self._handlers[task_type_name] try: ok = task_type.handler(arg, n_attempts) except Exception, xc: self._log.error('Task %s(%r) raised exception: %s' % (task_type_name, arg, xc)) ok = False if ok or n_attempts == task_type.max_attempts: self._log.info('Unscheduling %s(%r)' % (task_type_name, arg)) self._update('DELETE FROM %s WHERE ROWID = %d' % (self._table, id)) else: t_sched = t_now + (task_type.min_delay << n_attempts) n_attempts += 1 self._log.info('Scheduling %s attempt at %s to %s(%r)' % (nth(n_attempts), format_time(t_sched), task_type_name, arg)) self._update('UPDATE %s SET n_attempts = %d, t_sched = %d ' 'WHERE ROWID = %d' % (self._table, n_attempts, t_sched, id)) def run_and_close(self): self.run() self.close() nordugrid-arc-nagios-plugins-1.8.4/arcnagios/compat.py0000644000175000002070000000010711665207261023530 0ustar mockbuildmockbuilddef any(it): for x in it: if x: return True return False nordugrid-arc-nagios-plugins-1.8.4/arcnagios/plugins/0000755000175000002070000000000012600623616023352 5ustar mockbuildmockbuildnordugrid-arc-nagios-plugins-1.8.4/arcnagios/plugins/egiis.py0000644000175000002070000000410412120113236025010 0ustar mockbuildmockbuildimport ldap from arcnagios import arcinfosys, ldaputils, ldapnagutils, nagutils from arcnagios.arcinfosys import MdsServiceLdap from arcnagios.confargparse import UsageError class Check_egiis(ldapnagutils.LDAPNagiosPlugin): main_config_section = 'arcinfosys' def __init__(self): ldapnagutils.LDAPNagiosPlugin.__init__(self, use_port = True, default_port = 2135) ap = self.argparser.add_argument_group('EGIIS Options') ap.add_argument('--index-name', dest = 'index_name', help = 'The name of the information index to query.') def parse_args(self, args): ldapnagutils.LDAPNagiosPlugin.parse_args(self, args) if not self.opts.ldap_basedn and not self.opts.index_name: raise UsageError('--ldap-basedn or --index-name must be specified.') def valid_egiis_entry(self, dn, ent): """Validate a single EGIIS entry and return an pair of the number of errors and an `EGIIS_Object` for the entry or None.""" try: return MdsServiceLdap(self.subschema, dn, ent) except ldaputils.LDAPValidationError, xc: self.log.error(str(xc)) self.nagios_report.update_status_code(nagutils.CRITICAL) return None def check(self): self.prepare_check() # Query the EGIIS for subschema and entries. basedn = self.opts.ldap_basedn \ or 'Mds-Vo-name=%s, o=grid'%self.opts.index_name self.subschema = self.fetch_subschema() sr = self.search_s(basedn, ldap.SCOPE_BASE, 'objectClass=MdsServiceLdap') if len(sr) == 0: raise nagutils.ServiceWARNING('No EGIIS entries found.') # Check the entries. entcnts = {} for dn, ent in sr: egiis = self.valid_egiis_entry(dn, ent) if egiis: self.log.info('Good entry for %s'%egiis.ldap_suffix) entcnts[egiis.reg_status] = 1 + entcnts.get(egiis.reg_status, 0) # Report the result. entcnts = entcnts.items() entcnts.sort(lambda a, b: cmp(a[0], b[0])) countstrings = \ ['%d %s'%(cnt, arcinfosys.egiis_reg_status_to_string(st).lower()) for st, cnt in entcnts] self.nagios_report.update_status( nagutils.OK, 'EGIIS ok: %s.'%', '.join(countstrings)) return self.nagios_exit(subject = 'EGIIS service') nordugrid-arc-nagios-plugins-1.8.4/arcnagios/plugins/gridstorage.py0000644000175000002070000001465112120071531026233 0ustar mockbuildmockbuildimport os, datetime, time from arcnagios import arcutils, vomsutils from arcnagios.nagutils import NagiosPlugin, OK, CRITICAL, UNKNOWN, \ ServiceCRITICAL from arcnagios.confargparse import UsageError try: from subprocess import CalledProcessError except ImportError: CalledProcessError = OSError # Backward compatibility. if not hasattr(__builtins__, 'any'): from arcnagios.compat import any class GridStorageProbe(NagiosPlugin, vomsutils.NagiosPluginVomsMixin): main_config_section = 'gridstorage' def __init__(self): NagiosPlugin.__init__(self, use_host = True, use_port = True) ap = self.argparser.add_argument_group('Probe-Specific Options') ap.add_argument('--url', dest = 'url', help = 'The remote URL on which to perform the tests.') ap.add_argument('--write-url', dest = 'write_url', help = 'The URL for the initial write operation if different ' 'from the other URLs. This is primarily used for LFC.') ap.add_argument('--dir-url', dest = 'dir_url', help = 'The URL of the directory holding the test file. ' 'A file name including the host name and a time stamp ' 'will be appended. This option is only useful if you ' 'enable write operations, and it will not work ' 'correctly for LFC.') ap.add_argument('--disable-read', dest = 'enable_read', default = True, action = 'store_false', help = 'Disable the read check on the the URL.') ap.add_argument('--disable-list', dest = 'enable_list', default = True, action = 'store_false', help = 'Disable the list check on the the URL.') ap.add_argument('--list-dir', dest = 'list_dir', default = False, action = 'store_true', help = 'List the URL of the directory containing the file ' 'rather than the file itself. This will use ' '--dir-url if provided, otherwise it will use --url ' 'after stripping the last component.') ap.add_argument('--enable-write', dest = 'enable_write', default = False, action = 'store_true', help = 'Enable write and delete operations on the url. ' 'If enabled a file with a fairly unique content will ' 'be written before any list and read operations, ' 'and the file will be deleted after.') ap.add_argument('-t', '--timeout', dest = 'timeout', type = int, default = 120, help = 'Timeout. This is divided among the sub-tasks, so ' 'individual operations will get shorter times to ' 'complete. The minimum value is 5 seconds.') def parse_args(self, args): NagiosPlugin.parse_args(self, args) if not self.opts.url and not self.opts.dir_url: raise UsageError('You must provide either a --dir-url or a --url.') def next_timeout(self, which): assert self.time_slots > 0 t = (self.time_limit - time.time()) / self.time_slots - self.grace_time self.time_slots -= 1 if t < 1: if self.clean_url and self.time_slots > 0: timeout = self.time_limit - time.time() arcutils.arcrm(self.clean_url, log = self.log, timeout = timeout) raise ServiceCRITICAL('Insufficient time for %s.'%which) return int(t) def check(self): # Allocate up to 4 time slots for the main subtasks, and reserve a # fraction of the total time for the script itself and fork/exec # overhead. self.time_limit = time.time() + self.opts.timeout self.time_slots = \ (self.opts.enable_read and 2 or 0) + \ (self.opts.enable_write and 1 or 0) + \ (self.opts.enable_list and 1 or 0) self.grace_time = self.opts.timeout * 0.02 self.clean_url = None host = self.opts.host or 'localhost' timestamp = datetime.datetime.now().strftime('%Y%m%dT%H%M%S') self.require_voms_proxy() if self.opts.url: url = self.opts.url filename = os.path.basename(url) else: filename = '%s_%s.data'%(host, timestamp) url = os.path.join(self.opts.dir_url, filename) self.log.info('Performing checks on %s'%url) failed_ops = [] if self.opts.enable_write: timeout = self.next_timeout('write') uploaded_contents = 'Created by check_gridstorage %s for %s.\n' \ % (timestamp, self.opts.host or 'localhost') write_url = self.opts.write_url or url try: fd, outbound = self.mkstemp(prefix = 'outbound') fh = os.fdopen(fd, 'w') fh.write(uploaded_contents) fh.close() except OSError, xc: self.log.error('%s'%xc) self.nagios_exit(UNKNOWN, 'Could not create test file.') try: arcutils.arccp(outbound, write_url, log = self.log, timeout = timeout) self.clean_url = write_url self.log.info('Uploaded file.') except CalledProcessError, xc: self.log.error('Upload failed: %s'%xc) failed_ops.append('upload') else: uploaded_contents = None if self.opts.enable_list: timeout = self.next_timeout('list') if self.opts.list_dir: list_url = self.opts.dir_url or os.path.dirname(url) else: list_url = url try: listing = arcutils.arcls(list_url, log = self.log, timeout = timeout) self.log.info('Listing contains %d entries.'%len(listing)) if not any(os.path.basename(ent.filename) == filename for ent in listing): self.log.error('Did not find %s in listing.'%filename) failed_ops.append('list') except CalledProcessError, xc: self.log.error('Listing failed: %s'%xc) failed_ops.append('list') fetched_contents = None if self.opts.enable_read: timeout = self.next_timeout('read') inbound = self.mktemp(prefix = 'inbound') try: arcutils.arccp(url, inbound, log = self.log, timeout = timeout) self.remove_at_exit(inbound) try: fh = open(inbound) fetched_contents = fh.read() fh.close() self.log.info('Fetched file.') except OSError, xc: self.log.error('Could not open fetched file %s.'%xc) failed_ops.append('open-read') except CalledProcessError, xc: self.log.error('Fetch failed: %s'%xc) failed_ops.append('fetch') if uploaded_contents and fetched_contents: if fetched_contents != uploaded_contents: self.log.error('Mismatched content in fetched file.') failed_ops.append('verify') else: self.log.info('Verified content of fetched file.') if self.opts.enable_write: timeout = self.next_timeout('remove') try: arcutils.arcrm(url, log = self.log, timeout = timeout) self.log.info('Removed file.') except CalledProcessError, xc: self.log.error('Removal failed: %s'%xc) failed_ops.append('remove') if failed_ops: self.nagios_exit(CRITICAL, 'Failed to %s.'%', '.join(failed_ops)) else: self.nagios_exit(OK, 'Storage tests succeeded.') nordugrid-arc-nagios-plugins-1.8.4/arcnagios/plugins/__init__.py0000644000175000002070000000000011651751305025453 0ustar mockbuildmockbuildnordugrid-arc-nagios-plugins-1.8.4/arcnagios/plugins/arcce_clean.py0000644000175000002070000000254212502774552026155 0ustar mockbuildmockbuildimport os from arcnagios import arcutils, jobutils from arcnagios.nagutils import ServiceOK, ServiceWARNING class Check_arcce_clean(jobutils.JobNagiosPlugin): def __init__(self): jobutils.JobNagiosPlugin.__init__(self) ap = self.argparser.add_argument_group('Job Cleaner Options') ap.add_argument('--timeout', dest = 'timeout', type = int, default = 20, help = 'Timeout') ap.add_argument('--max-age', dest = 'max_age', type = int, default = 604800, help = 'Max age before jobs info is cleaned.') ap.add_argument('--use-jobs-xml', default = False, action = 'store_true', help = 'DEPRECATED. Load jobs.xml directly instead of ' 'asking arcstat.') def check(self): if not os.path.exists(self.top_workdir): self.log.info('The work directory is %s.'%self.top_workdir) return ServiceOK('No jobs to clean since the working directory ' 'has not yet been created.') self.require_voms_proxy() total_count, pruned_count, failed_count = \ arcutils.arcprune(max_age = self.opts.max_age, use_jobs_xml = self.opts.use_jobs_xml, timeout = self.opts.timeout, log = self.log) if failed_count > 0: return ServiceWARNING('Cleaned %d and failed to clean %d of %d jobs' %(pruned_count, failed_count, total_count)) else: return ServiceOK('Cleaned %d of %d jobs'%(pruned_count,total_count)) nordugrid-arc-nagios-plugins-1.8.4/arcnagios/plugins/arcce_submit.py0000644000175000002070000002573712377121117026402 0ustar mockbuildmockbuildimport os, pipes, time from arcnagios import arcutils, jobutils, nagutils class Check_arcce_submit(jobutils.JobNagiosPlugin): def __init__(self): jobutils.JobNagiosPlugin.__init__(self) ap = self.argparser.add_argument_group('Options for Job Submission') ap.add_argument('-H', dest = 'host', required = True, help = 'The host name of the CE to test. This will be used ' 'to connect to the CE unless --ce is given. ' 'This option is required.') ap.add_argument('-p', dest = 'port', type = int, help = 'An optional port number at which to connect.') ap.add_argument('--prev-status', dest = 'prev_status', type = int, default = 0, metavar = '{0..3}', help = 'The previous Nagios status for this metric.') ap.add_argument('--stage-input', dest = 'staged_inputs', default = [], action = 'append', metavar = 'URL', help = 'DEPRECATED, please use --test with the staging plugin. ' 'Stage the existing URL as an input and check for it ' 'in the job script. ' 'The local file name will be the basename of URL, or ' 'you can specify an alternative name by prefixing ' 'the URL with ALTNAME=.') ap.add_argument('--stage-output', dest = 'staged_outputs', default = [], action = 'append', metavar = 'URL', help = 'DEPRECATED, please use --test with the staging plugin. ' 'Create a file in the job script and stage it as URL. ' 'The local file name will be the basename of URL, or ' 'you can specify an alternative name by prefixing ' 'the URL with ALTNAME=.') ap.add_argument('--termination-service', dest = 'termination_service', default = '', help = 'The name (NAGIOS "description") of the passive ' 'service to which to submit the results.') ap.add_argument('--progress-service', metavar = 'SVC', help = 'Publish state timeout alerts to SVC.') ap.add_argument('--submission-service', dest = 'submission_service', default = '', help = 'Report submission-related alerts to this service ' 'instead of raising the alert on the active service.') ap.add_argument('--submission-service-threshold', default = 2, type = int, help = 'Minimum severity before the submission result is ' 'submitted to the passive service specified by ' '--submission-service. This is the numeric status, ' '0 for OK, 1 for WARNING, 2 for ERROR (default), ' 'and 3 for UNKNOWN.') ap.add_argument('--job-submit-timeout', dest = 'job_submit_timeout', type = int, default = 600, help = 'Timeout for job submission.') ap.add_argument('--job-discard-timeout', dest = 'job_discard_timeout', type = int, default = 6*3600, help = 'Timeout before discarding a job.') ap.add_argument('--ce', dest = 'ce', help = 'URL for connecting to the CE, using the same format ' 'as the -c option of arcsub(1).') ap.add_argument('--queue', dest = 'queue', help = 'Target queue name. If unspecified, let ARC choose it.') ap.add_argument('--job-tag', dest = 'job_tag', help = 'A short string suitable in directory names to ' 'distinguish different submission services for the ' 'same hostname.') ap.add_argument('--job-description', dest = 'job_description', help = 'Use this job description instead of generating one. ' 'In this case --stage-input options are ignored and ' 'URLs passed to --stage-output will be deleted when ' 'the job finishes.') ap.add_argument('--test', dest = 'tests', action='append', default=[], metavar = 'TESTNAME', help = 'Add an additional test described in the configuration ' 'file under the section "arcce.TESTNAME"') ap.add_argument('--runtime-environment', dest = 'runtime_environments', action = 'append', default = [], metavar = 'RTE', help = 'Request the given runtime environment.') ap.add_argument('--wall-time-limit', dest = 'wall_time_limit', type = int, default = 600, help = 'Soft limit of execution wall-time.') ap.add_argument('--memory-limit', dest = 'memory_limit', type = int, default = 536870912, help = 'The max. about of memory used by the job in bytes. ' 'Default: 536870912 (512 MiB)') ap.add_argument('--enable-gmlog', dest = 'enable_gmlog', action = 'store_true', default = False, help = 'Request debug information from the CE. This will be ' 'stored in a subdirectory log of the output directory.') def parse_args(self, args): jobutils.JobNagiosPlugin.parse_args(self, args) self.staged_inputs = \ map(jobutils.parse_staging, self.opts.staged_inputs) self.staged_outputs = \ map(jobutils.parse_staging, self.opts.staged_outputs) def _report(self, status, msg): if status < self.opts.submission_service_threshold \ or not self.opts.submission_service: report = self.nagios_report else: self.nagios_report.update_status(nagutils.OK, 'Reporting to passive service.') report = self.nagios_report_for(self.opts.host, self.opts.submission_service) report.update_status(status, msg) def check(self): """Submit a job to a CE.""" self.require_voms_proxy() workdir = self.workdir_for(self.opts.host, self.opts.job_tag) jobid_file = os.path.join(workdir, self.JOBID_FILENAME) jobinfo = self.load_active_job(self.opts.host, self.opts.job_tag) if not jobinfo is None: t_sub = jobinfo.submission_time job_state = jobinfo.job_state if not job_state.is_final(): s_sub = time.strftime('%FT%T', time.localtime(t_sub)) self.log.info('Last job was submitted %s.'%s_sub) t_dis = t_sub + self.opts.job_discard_timeout if int(time.time()) >= t_dis: self.log.warning('Discarding last job due to timeout.') self.discard_job(jobinfo) self._report(nagutils.WARNING, 'Re-submitting due to timeout of %s from %s' % (jobinfo.job_id, s_sub)) else: s_dis = time.strftime('%FT%T', time.localtime(t_dis)) self.log.info('Job will be discarded %s.'%s_dis) status = self.opts.prev_status or 0 self.log.info('Keeping previous status %d.'%status) self._report(status, 'Job not finished.') return else: self.log.debug('Job in terminal state %s.\n'%job_state) self._report(nagutils.OK, 'Waiting for monitoring service to fetch the job.') return # Prepare the working directory for a new job. job_output_dir = os.path.join(workdir, self.JOB_OUTPUT_DIRNAME) if not os.path.exists(job_output_dir): try: os.makedirs(job_output_dir) except OSError, e: msg = 'Failed to create working directory: %s'%e return nagutils.ServiceUNKNOWN(msg) self.log.debug('Submitting new job.') job_script_file = os.path.join(workdir, self.JOB_SCRIPT_FILENAME) # Create job script. fh = open(job_script_file, 'w') fh.write('#! /bin/sh\n\n' 'status=0\n' 'echo "Job started `date -Is`."\n') for filename, _0, _1 in self.staged_inputs: fh.write('test -e %(fname)s || error "Missing file "%(fname)s\n' % {'fname': pipes.quote(filename)}) for filename, _0, _1 in self.staged_outputs: fh.write('hostname >%s\n'%pipes.quote(filename)) runtime_environments = set(self.opts.runtime_environments) fh.write('\n') for test_name in self.opts.tests: test = self.load_jobtest(test_name, hostname = self.opts.host) test.write_script(fh) def adjust_staged(spec): if isinstance(spec, tuple): filename, spec, urloptions = spec else: if ';' in spec: xs = spec.split(';') spec, urloptions = xs[0], xs[1:] else: urloptions = [] filename = os.path.basename(spec) if spec is None or ':/' in spec: url = spec elif os.path.isabs(spec): url = 'file:' + spec else: url = 'file:' + os.path.join(workdir, spec) return filename, url, urloptions for stagespec in test.staged_inputs(): self.staged_inputs.append(adjust_staged(stagespec)) for stagespec in test.staged_outputs(): self.staged_outputs.append(adjust_staged(stagespec)) runtime_environments.update(test.runtime_environments()) fh.write('echo "Present files before termination:"\n' 'ls -l\n' 'echo "Job finished `date -Is`, status = $status."\n' 'exit $status\n') fh.close() # Create JSDL file. if self.opts.job_description: jsdl_file = self.opts.job_description else: jsdl_file = os.path.join(workdir, self.JSDL_FILENAME) jobdesc = jobutils.JobDescription( script_path = job_script_file, application_name = 'ARCCE-probe', logdir = self.opts.enable_gmlog and 'log', job_name = self.opts.termination_service, output = 'stdout.txt', error = 'stderr.txt', staged_inputs = self.staged_inputs, staged_outputs = self.staged_outputs, runtime_environments = runtime_environments, wall_time_limit = self.opts.wall_time_limit, memory_limit = self.opts.memory_limit, queue_name = self.opts.queue) self.write_jsdl(jsdl_file, jobdesc) # Submit the job. if self.opts.ce: connection_url = self.opts.ce elif self.config.has_option('arcce.connection_urls', self.opts.host): connection_url = self.config.get('arcce.connection_urls', self.opts.host) # COMPAT 2011-11-22. elif self.config.has_option('arc-ce.connection_urls', self.opts.host): self.log.warn('The section name arc-ce.connection_urls is ' 'deprecated, please use arcce.connection_urls.') connection_url = self.config.get('arc-ce.connection_urls', self.opts.host) else: if self.opts.port: connection_url = self.opts.host + ':' + str(self.opts.port) else: connection_url = self.opts.host rc, output = \ self.run_arc_cmd('arcsub', '-c', connection_url, '-o', jobid_file, '-t', self.opts.job_submit_timeout, jsdl_file) try: fh = open(jobid_file) job_id = fh.readline().strip() fh.close() except StandardError: job_id = None if not job_id: for ln in output.strip().split('\n'): self.log.error('arcsub: %s'%ln) if rc != 0: self.cleanup_job_files(self.opts.host, self.opts.job_tag) self.log.error('arcsub %s.' % arcutils.explain_wait_status(rc)) self._report(nagutils.CRITICAL, 'Job submission failed.') return else: self.log.info('The job ID should have been saved to %s.' % jobid_file) self._report(nagutils.CRITICAL, 'Failed to submit job.') return if rc == 0: self._report(nagutils.OK, 'Job submitted.') else: for ln in output.strip().split('\n'): self.log.error('arcsub: %s'%ln) self.log.error('Received a JID despite the error, proceeding.') self._report(nagutils.WARNING, 'Job seems to be submitted but arcsub %s.' % arcutils.explain_wait_status(rc)) t_now = int(time.time()) jobinfo = jobutils.JobInfo( submission_time = t_now, host = self.opts.host, job_tag = self.opts.job_tag, termination_service = self.opts.termination_service, progress_service = self.opts.progress_service, job_id = job_id, job_state = arcutils.J_NOT_SEEN, job_state_time = int(time.time()), check_time = t_now, stored_urls = [url for _0, url, _1 in self.staged_outputs if url], tests = self.opts.tests) self.save_active_job(jobinfo, self.opts.host, self.opts.job_tag) nordugrid-arc-nagios-plugins-1.8.4/arcnagios/plugins/aris.py0000644000175000002070000004234212164773315024676 0ustar mockbuildmockbuildimport ldap, re from ldap.filter import filter_format from arcnagios import arcutils, ldaputils, ldapnagutils from arcnagios.arcinfosys import NorduGridCluster, NorduGridQueue, \ glue_class_map, GlueCE, GlueCluster, GlueSubCluster from arcnagios.nagutils import OK, WARNING, CRITICAL, status_by_name, \ ServiceUNKNOWN from arcnagios.utils import counted_noun # Backward compatibility. if not hasattr(__builtins__, 'any'): from arcnagios.compat import any class Glue_Validator(object): def __init__(self, subject, shortname, log): self.subject = subject self.shortname = shortname self.error_count = 0 self.log = log def compare_attribute(self, attrname, arcval, mapping = None, soft = False): def normalize(x): # Work around missing single-valuedness restrictions. if isinstance(x, list): if len(x) == 0: return None elif len(x) == 1: return x[0] else: return set(x) else: return x glueval = normalize(getattr(self.subject, attrname)) arcval = normalize(arcval) if arcval and not mapping is None: if not arcval in mapping: # Some of values for nordugrid-queue-status like "inactive, # gridftpd is down" seems more informative than part of a # strict enumeration. self.log.warning('[%s].%s: Not comparing %r due to unknown ' 'mapping.'%(self.shortname, attrname, arcval)) return arcval = mapping[arcval] if glueval != arcval: if soft: self.log.warning('[%s].%s: %s != %s' %(self.shortname, attrname, glueval, arcval)) else: self.log.error('[%s].%s: %s != %s' %(self.shortname, attrname, glueval, arcval)) self.error_count += 1 else: self.log.debug('Checked %s: %s'%(attrname, glueval)) class GlueCE_Validator(Glue_Validator): def compare_with(self, cluster, queue): ck = self.compare_attribute #ck('GlueCEUniqueID', '%s?queue=%s'%(cluster.contactstring, queue.name)) ck('GlueCEName', queue.name) # GlueCEInformationServiceURL ck('GlueCEInfoLRMSType', cluster.lrms_type) ck('GlueCEInfoLRMSVersion', cluster.lrms_version) ck('GlueCEInfoHostName', cluster.name) # GlueCEInfoGatekeeperPort - irrelevant for ARC # GlueCEInfoJobmanager - irrelevant for ARC ck('GlueCEInfoContactString', '%s?queue=%s'%(cluster.contactstring, queue.name)) #ck('GlueCEInfoTotalCPUs', cluster.totalcpus) - deprecated in Glue # GlueCEInfoApplicationDir - not in ARC # GlueCEInfoDataDir - not in ARC # GlueCEDefaultSE - not in ARC ck('GlueCEStateStatus', queue.status, mapping = {'active': 'Production', 'inactive': 'Closed'}) # ck('GlueCEStateRunningJobs', queue.running) # Skip running state since we may have fetched the LDAP entries at # different times. #nwait = queue.gridqueued + queue.localqueued + queue.prelrmsqueued #ck('GlueCEStateWaitingJobs', nwait) #ck('GlueCEStateTotaljobs', nwait + queue.running) # GlueCEStateEstimatedResponseTime - not in ARC # GlueCEStateWorstResponseTime - not in ARC # GlueCEStateFreeJobSlots - skip running state # GlueCEStateFreeCPUs - skip running state # GlueCEPolicyMaxWallTime - not yet in ARC ck('GlueCEPolicyMaxCPUTime', queue.maxcputime) if not queue.maxqueuable is None: # FIXME: Likely a translation issue. ck('GlueCEPolicyMaxTotalJobs', queue.maxqueuable) ck('GlueCEPolicyMaxRunningJobs', queue.maxrunning) # GlueCEPolicyPriority - not in ARC # GlueCEPolicyAssignedJobSlots - ambiguous mapping if getattr(cluster, 'acl', None): # FIXME: Likely a translation issue. ck('GlueCEAccessControlBaseRule', cluster.acl) class GlueCluster_Validator(Glue_Validator): def compare_with(self, cluster): ck = self.compare_attribute ck('GlueClusterUniqueID', cluster.name) ck('GlueClusterName', cluster.aliasname, soft = True) # GlueClusterTmpDir - not in ARC # GlueClusterWNTmpDir - not in ARC class GlueSubCluster_Validator(Glue_Validator): def compare_with(self, cluster, corq): ck = self.compare_attribute ck('GlueSubClusterUniqueID', corq.name) ck('GlueSubClusterName', corq.name) ck('GlueSubClusterPhysicalCPUs', corq.totalcpus) # GlueSubClusterLogicalCPUs - not in ARC # GlueSubClusterLocation* - not in ARC # GlueHostOperatingSystem* - not in ARC # GlueHostProcessor* - not in ARC ck('GlueHostRAMSize', corq.nodememory) # GlueHostVirtualSize - not in ARC ck('GlueHostNetworkAdapterOutboundIP', 'outbound' in cluster.nodeaccess) ck('GlueHostNetworkAdapterInboundIP', 'inbound' in cluster.nodeaccess) ck('GlueHostArchitecturePlatformType', corq.architecture) # GlueHostBenchmarkS[IF]00 - Are only the 2000 benchmarks mapped? ck('GlueHostApplicationSoftwareRunTimeEnvironment', cluster.runtimeenvironment) class Check_aris(ldapnagutils.LDAPNagiosPlugin): main_config_section = 'arcinfosys' def __init__(self): ldapnagutils.LDAPNagiosPlugin.__init__(self) ap = self.argparser.add_argument_group('ARIS Options') ap.add_argument('--if-no-clusters', dest = 'if_no_clusters', type = status_by_name, default = WARNING, metavar = 'NAGIOS-STATUS', help = 'Nagios status to report if no cluster is found.') ap.add_argument('--if-no-queues', dest = 'if_no_queues', type = status_by_name, default = WARNING, metavar = 'NAGIOS-STATUS', help = 'Nagios status to report if a cluster has no queues.') ap.add_argument('--cluster', dest = 'clusters', action = 'append', default = [], metavar = 'CLUSTER-NAME', help = 'Pass one or more times to check specific clusters.') ap.add_argument('--cluster-test', dest = 'cluster_tests', action = 'append', default = [], metavar = 'TESTNAME', help = 'Enable a custom test to run against nordugrid-cluster ' 'entries.') ap.add_argument('--queue-test', dest = 'queue_tests', action = 'append', default = [], metavar = 'TESTNAME', help = 'Enable a custom test to run against nordugrid-queue ' 'entries.') ap.add_argument('--enable-glue', action = 'store_true', default = False, help = 'Enable loading and schema-checks of the Glue schema ' 'entries if present.') ap.add_argument('--compare-glue', action = 'store_true', default = False, help = 'Enable comparison of Glue entries with ARC. ' 'Only a limited set of attributes are compared. ' 'Implies --enable-glue.') ap.add_argument('--check-contact', action = 'store_true', default = False, help = 'Try to list the nordugrid-cluster-contactstring URLs. ' 'This requires a proxy certificate.') def parse_args(self, args): ldapnagutils.LDAPNagiosPlugin.parse_args(self, args) if self.opts.compare_glue: self.opts.enable_glue = True def custom_verify_regex(self, section, obj): variable = self.config.get(section, 'variable') values = getattr(obj, variable, []) if not isinstance(values, list): values = [values] for (code, pfx) in [(CRITICAL, 'critical'), (WARNING, 'warning')]: if self.config.has_option(section, pfx + '.pattern'): sp = self.config.get(section, pfx + '.pattern') p = re.compile(sp) if not any(re.match(p, value) for value in values): if self.config.has_option(section, pfx + '.message'): msg = self.config.get(section, pfx + '.message') else: msg = '%s did not match %s'%(variable, sp) return code, msg return OK, None def custom_verify_limit(self, section, obj): expr = self.config.get(section, 'value') x = eval(expr, vars(obj)) for (code, pfx) in [(CRITICAL, 'critical'), (WARNING, 'warning')]: msg = None if self.config.has_option(section, pfx + '.message'): msg = self.config.get(section, pfx + '.message') if self.config.has_option(section, pfx + '.min'): x_min = self.config.getfloat(section, pfx + '.min') if x < x_min: return code, (msg or '%s = %s is below %s limit %s.' % (expr, x, pfx, x_min)) if self.config.has_option(section, pfx + '.max'): x_max = self.config.getfloat(section, pfx + '.max') if x > x_max: return code, (msg or '%s = %s is above %s limit %s' % (expr, x, pfx, x_max)) return OK, None def custom_verify_obj(self, obj, tests): # Custom tests. for test in tests or []: section = 'arcinfosys.aris.%s'%test if not self.config.has_section(section): raise ServiceUNKNOWN('Missing section %s to define ' 'the test %s.'%(section, test)) if not self.config.has_option(section, 'type'): raise ServiceUNKNOWN('The type variable is missing is %s.' %section) type = self.config.get(section, 'type') if type == 'limit': code, msg = self.custom_verify_limit(section, obj) elif type == 'regex': code, msg = self.custom_verify_regex(section, obj) else: raise ServiceUNKNOWN('Unhandled type %s in %s.'%(type, section)) if code: self.log.error(msg) self.nagios_report.update_status_code(code) def verify_nordugrid_cluster(self, dn, ent): """Validate and do custom checks on a nordugrid-cluster entry.""" try: cluster = NorduGridCluster(self.subschema, dn, ent) except ldaputils.LDAPValidationError, xc: self.log.error('Validation of cluster entry %s failed.'%dn) self.log.error(str(xc)) self.nagios_report.update_status_code(CRITICAL) return None self.debug_dump_obj(cluster, 'nordugrid-cluster entry') tests = self.opts.cluster_tests if not tests: for setting in [ 'cluster_tests[%s]'%cluster.name, 'cluster_tests[%s]'%self.opts.host, ]: if self.config.has_option('arcinfosys.aris', setting): raw_tests = self.config.get('arcinfosys.aris', setting) tests = [test.strip() for test in raw_tests.split(',')] break self.custom_verify_obj(cluster, tests) return cluster def verify_nordugrid_queue(self, cluster, dn, ent): """Validate and do custom checks on a nordugrid-queue entry.""" try: queue = NorduGridQueue(self.subschema, dn, ent) except ldaputils.LDAPValidationError, xc: self.log.error('Validation of queue entry %s failed.'%dn) self.log.error(str(xc)) self.nagios_report.update_status(CRITICAL) return None self.debug_dump_obj(queue, 'nordugrid-queue entry') tests = self.opts.queue_tests if not tests: for setting in [ 'queue_tests[%s/%s]'%(cluster.name, queue.name), 'queue_tests[%s]'%queue.name, 'queue_tests[%s]'%self.opts.host, ]: if self.config.has_option('arcinfosys.aris', setting): raw_tests = self.config.get('arcinfosys.aris', setting) tests = [test.strip() for test in raw_tests.split(',')] break self.custom_verify_obj(queue, tests) return queue def verify_glue_entry(self, dn, ent, clusters, queues): # Schema-check and construct an object representation. obj = None for objcls in ent['objectClass']: if objcls in glue_class_map: try: obj = glue_class_map[objcls](self.subschema, dn, ent) self.log.debug('Schema-checked %s.'%dn) except ldaputils.LDAPValidationError, xc: self.log.error( 'Schema-check failed for %s: %s'%(dn, xc)) self.nagios_report.update_status_code(CRITICAL) break if obj is None or not self.opts.compare_glue: return obj def get_cluster(cluster_name): if not cluster_name in clusters: self.log.error('No cluster %s corresponding to %s.' % (cluster_name, obj.structural_objectclass)) self.log.info('Present clusters are %s'%', '.join(clusters)) self.nagios_report.update_status_code(CRITICAL) else: return clusters[cluster_name] def get_queue(cluster_name, queue_name): if not (cluster_name, queue_name) in queues: self.log.error('No queue %s/%s corresponding to %s.' % (cluster_name, queue_name, obj.structural_objectclass)) self.log.info('Present queues are %s.'%', '.join(queues)) else: return queues[(cluster_name, queue_name)] # Compare GlueCE with ARC entries. if isinstance(obj, GlueCE): cluster = get_cluster(obj.GlueCEInfoHostName) if cluster is None: return queue = get_queue(obj.GlueCEInfoHostName, obj.GlueCEName) if queue is None: return self.log.debug('Comparing %s with ARC entries.'%queue.name) vl = GlueCE_Validator(obj, '%s/%s'%(cluster.name, queue.name), self.log) vl.compare_with(cluster, queue) if vl.error_count: self.nagios_report.update_status_code(CRITICAL) # Compare GlueCluster with ARC entries. elif isinstance(obj, GlueCluster): cluster = get_cluster(obj.GlueClusterUniqueID) if cluster is None: return vl = GlueCluster_Validator(obj, cluster.name, self.log) vl.compare_with(cluster) if vl.error_count: self.nagios_report.update_status_code(CRITICAL) # Compare GlueSubCluster with ARC entries. elif isinstance(obj, GlueSubCluster): subcluster_id = obj.GlueSubClusterUniqueID vl = GlueSubCluster_Validator(obj, subcluster_id, self.log) if '/' in subcluster_id: cluster_name, queue_name = subcluster_id.split('/') cluster = get_cluster(cluster_name) if cluster is None: return queue = get_queue(cluster_name, queue_name) if queue is None: return if cluster.homogeniety: self.log.error('Expected inhomogenious cluster for ' 'GlueSubClusterUniqueID = %r.'\ % obj.GlueSubClusterUniqueID) self.nagios_report.update_status_code(CRITICAL) vl.compare_with(cluster, queue) else: cluster = get_cluster(subcluster_id) if cluster is None: return if not cluster.homogeniety: self.log.error('Expected homogenious cluster for ' 'GlueSubClusterUniqueID = %r.'\ % obj.GlueSubClusterUniqueID) self.nagios_report.update_status_code(CRITICAL) vl.compare_with(cluster, cluster) return obj def check(self): """The entry point for the ARIS probe.""" self.prepare_check() self.subschema = self.fetch_subschema() # Query cluster entries. basedn = self.opts.ldap_basedn or 'Mds-Vo-name=local, o=grid' if self.opts.clusters: filters = [filter_format('nordugrid-cluster-name=%s', cluster) for cluster in self.opts.clusters] filter = '(&(objectClass=nordugrid-cluster)(|%s))'%''.join(filters) else: filter = '(objectClass=nordugrid-cluster)' sr = self.search_s(basedn, ldap.SCOPE_ONELEVEL, filter) cluster_count = len(sr) if cluster_count == 0: msg = 'No clusters found.' if self.opts.if_no_clusters: self.nagios_report.update_status(self.opts.if_no_clusters, msg) else: self.log.warning(msg) # Report error if expected entries are missing. if self.opts.clusters: found_clusters = set() for _, ent in sr: found_clusters.update(ent['nordugrid-cluster-name']) for cluster in self.opts.clusters: if not cluster in found_clusters: self.log.error('Missing entry for %s.'%cluster) self.nagios_report.update_status_code(CRITICAL) # These are indexed for later comparison with Glue entries. clusters = {} # indexed by cluster.name queues = {} # indexed by (cluster.name, queue.name) for dn, ent in sr: # Check nordugrid-cluster entry. cluster = self.verify_nordugrid_cluster(dn, ent) if cluster is None: self.log.warning('Skipping queue checks for invalid cluster ' 'entry %s.'%dn) continue clusters[cluster.name] = cluster # Query and check the corresponding queue entries. self.log.debug('Checking queues for %s.'%cluster.name) qbasedn = 'nordugrid-cluster-name=%s,%s'%(cluster.name, basedn) qsr = self.search_s(qbasedn, ldap.SCOPE_SUBTREE, '(objectClass=nordugrid-queue)') if len(qsr) == 0: msg = 'No queue defined for %s.'%cluster.name if self.opts.if_no_queues: self.nagios_report.update_status( self.opts.if_no_queues, msg) else: self.log.warning(msg) else: middleware = getattr(cluster, 'middleware', ['unknown middleware']) self.log.info('- Cluster %r runs %s.' % (cluster.name, ', '.join(middleware))) for qdn, qent in qsr: queue = self.verify_nordugrid_queue(cluster, qdn, qent) queues[(cluster.name, queue.name)] = queue status = getattr(queue, 'status', 'unknown') self.log.info('-- Queue %r is %s.'%(queue.name, status)) # Check Glue entries if enabled. if self.opts.enable_glue: glue_counts = {} sr = self.search_s('Mds-Vo-name=resource,o=grid', ldap.SCOPE_ONELEVEL, 'objectClass=*') for dn, ent in sr: obj = self.verify_glue_entry(dn, ent, clusters, queues) if obj: oc = obj.structural_objectclass if not oc in glue_counts: glue_counts[oc] = 1 else: glue_counts[oc] += 1 for oc, count in glue_counts.iteritems(): self.log.info('Validated %s.' % counted_noun(count, '%s entry'%oc, '%s entries'%oc)) # Try to access the nordugrid-cluster-contactstring if requested. if self.opts.check_contact: self.require_voms_proxy() for cluster in clusters.itervalues(): try: url = cluster.contactstring xs = arcutils.arcls(url, log = self.log) self.log.info('%s contains %d entries.'%(url, len(xs))) except arcutils.CalledProcessError: self.nagios_report.update_status(CRITICAL, 'Contact URL %s is inaccessible.'%url) except AttributeError: self.nagios_report.update_status(CRITICAL, 'The cluster %s has no contact string.'%cluster.name) # Pass a default status and exit. try: cinfo = ' (%s)'%', '.join([', '.join(c.middleware) for c in clusters.itervalues()]) qinfo = ' (%s)'%', '.join([q.status for q in queues.itervalues()]) except Exception: cinfo = '' qinfo = '' self.nagios_report.update_status(OK, '%s%s, %s%s' %(counted_noun(cluster_count, 'cluster'), cinfo, counted_noun(len(queues), 'queue'), qinfo)) return self.nagios_exit(subject = 'ARIS service') nordugrid-arc-nagios-plugins-1.8.4/arcnagios/plugins/arcinfosys.py0000644000175000002070000005572012200711475026112 0ustar mockbuildmockbuild# !! IMPORTANT !! # # This module is DEPRECATED as it has been split into arcce_arcglue2, # arcce_aris, and arcce_egiis. import ldap, ldap.schema, re from ldap.filter import filter_format from arcnagios import arcutils, ldapnagutils from arcnagios.ldaputils import str2dn from arcnagios.utils import counted_noun, Statdict from arcnagios.nagutils import ServiceWARNING, ServiceUNKNOWN, \ OK, WARNING, CRITICAL, UNKNOWN, status_by_name from arcnagios.confargparse import UsageError from arcnagios.arcinfosys import MdsServiceLdap, egiis_reg_status_to_string, \ NorduGridCluster, NorduGridQueue, \ glue_class_map, GlueCE, GlueCluster, GlueSubCluster from arcnagios import glue2 from arcnagios import ldapschema from arcnagios.ldaputils import LDAPObject, LDAPValidationError, \ is_proper_subdn, is_immediate_subdn from arcnagios.plugins.arcglue2 import RelationTracker from arcnagios.plugins.aris import \ GlueCE_Validator, GlueCluster_Validator, GlueSubCluster_Validator # Backward compatibility. if not hasattr(__builtins__, 'any'): from arcnagios.compat import any def first_or_none(x): if isinstance(x, list): return x != [] and x[0] else: return x class ARCInfosysProbe(ldapnagutils.LDAPNagiosPlugin): main_config_section = 'arcinfosys' def __init__(self): ldapnagutils.LDAPNagiosPlugin.__init__(self, use_port = True, default_port = 2135) asp = self.argparser.add_subparsers(dest = 'metric_name') ap = asp.add_parser('glue2') ap.add_argument('--glue2-schema', default = '/etc/ldap/schema/GLUE20.schema', help = 'Path of GLUE 2.0 LDAP schema. ' 'Default is /etc/ldap/schema/GLUE20.schema.') ap.add_argument('--if-dependent-schema', type = status_by_name, default = WARNING, metavar = 'NAGIOS-STATUS', help = 'Nagios status to report if LDAP schema had to be ' 'fetched from the tested server.') ap.add_argument('--warn-if-missing', default = 'GLUE2AdminDomain,GLUE2ComputingService,' 'GLUE2ComputingEndpoint', metavar = 'OBJECTCLASSES', help = 'Report warning if there are no entries of the given ' 'comma-separated list of LDAP objectclasses. ' 'Default: GLUE2AdminDomain,GLUE2ComputingService,' 'GLUE2ComputingEndpoint') ap.add_argument('--critical-if-missing', default = '', metavar = 'OBJECTCLASSES', help = 'Report critical if there are no entries of the given ' 'comma-separated list of LDAP objectclasses. ' 'Empty by default.') ap.add_argument('--hierarchical-foreign-keys', metavar = 'FOREIGN-KEY-NAMES', default = '', help = 'A comma-separating list of foreign key attribute types ' 'which should be reflected in the DIT.') ap.add_argument('--hierarchical-aggregates', default = False, action = 'store_true', help = 'Require that all foreign keys which represent ' 'aggregation or composition are reflected in the ' 'DIT.') ap = asp.add_parser('aris') ap.add_argument('--if-no-clusters', dest = 'if_no_clusters', type = status_by_name, default = WARNING, metavar = 'NAGIOS-STATUS', help = 'Nagios status to report if no cluster is found.') ap.add_argument('--if-no-queues', dest = 'if_no_queues', type = status_by_name, default = WARNING, metavar = 'NAGIOS-STATUS', help = 'Nagios status to report if a cluster has no queues.') ap.add_argument('--cluster', dest = 'clusters', action = 'append', default = [], metavar = 'CLUSTER-NAME', help = 'Pass one or more times to check specific clusters.') ap.add_argument('--cluster-test', dest = 'cluster_tests', action = 'append', default = [], metavar = 'TESTNAME', help = 'Enable a custom test to run against nordugrid-cluster ' 'entries.') ap.add_argument('--queue-test', dest = 'queue_tests', action = 'append', default = [], metavar = 'TESTNAME', help = 'Enable a custom test to run against nordugrid-queue ' 'entries.') ap.add_argument('--enable-glue', action = 'store_true', default = False, help = 'Enable loading and schema-checks of the Glue schema ' 'entries if present.') ap.add_argument('--compare-glue', action = 'store_true', default = False, help = 'Enable comparison of Glue entries with ARC. ' 'Only a limited set of attributes are compared. ' 'Implies --enable-glue.') ap.add_argument('--check-contact', action = 'store_true', default = False, help = 'Try to list the nordugrid-cluster-contactstring URLs. ' 'This requires a proxy certificate.') ap = asp.add_parser('egiis') ap.add_argument('--index-name', dest = 'index_name', help = 'The name of the information index to query.') def parse_args(self, args): ldapnagutils.LDAPNagiosPlugin.parse_args(self, args) if self.opts.metric_name == 'egiis': if not self.opts.ldap_basedn and not self.opts.index_name: raise UsageError( 'Either --ldap-basedn or --index-name is required.') if self.opts.metric_name == 'aris': if self.opts.compare_glue: self.opts.enable_glue = True def check(self): self.prepare_check() return getattr(self, 'check_' + self.opts.metric_name)() def custom_verify_regex(self, section, obj): variable = self.config.get(section, 'variable') values = getattr(obj, variable, []) if not isinstance(values, list): values = [values] for (code, pfx) in [(CRITICAL, 'critical'), (WARNING, 'warning')]: if self.config.has_option(section, pfx + '.pattern'): sp = self.config.get(section, pfx + '.pattern') p = re.compile(sp) if not any(re.match(p, value) for value in values): if self.config.has_option(section, pfx + '.message'): msg = self.config.get(section, pfx + '.message') else: msg = '%s did not match %s'%(variable, sp) return code, msg return OK, None def custom_verify_limit(self, section, obj): expr = self.config.get(section, 'value') x = eval(expr, vars(obj)) for (code, pfx) in [(CRITICAL, 'critical'), (WARNING, 'warning')]: msg = None if self.config.has_option(section, pfx + '.message'): msg = self.config.get(section, pfx + '.message') if self.config.has_option(section, pfx + '.min'): x_min = self.config.getfloat(section, pfx + '.min') if x < x_min: return code, (msg or '%s = %s is below %s limit %s.' % (expr, x, pfx, x_min)) if self.config.has_option(section, pfx + '.max'): x_max = self.config.getfloat(section, pfx + '.max') if x > x_max: return code, (msg or '%s = %s is above %s limit %s' % (expr, x, pfx, x_max)) return OK, None def custom_verify_obj(self, obj, tests): # Custom tests. for test in tests or []: section = 'arcinfosys.aris.%s'%test if not self.config.has_section(section): raise ServiceUNKNOWN('Missing section %s to define ' 'the test %s.'%(section, test)) if not self.config.has_option(section, 'type'): raise ServiceUNKNOWN('The type variable is missing is %s.' %section) type = self.config.get(section, 'type') if type == 'limit': code, msg = self.custom_verify_limit(section, obj) elif type == 'regex': code, msg = self.custom_verify_regex(section, obj) else: raise ServiceUNKNOWN('Unhandled type %s in %s.'%(type, section)) if code: self.log.error(msg) self.nagios_report.update_status_code(code) def verify_nordugrid_cluster(self, dn, ent): """Validate and do custom checks on a nordugrid-cluster entry.""" try: cluster = NorduGridCluster(self.subschema, dn, ent) except LDAPValidationError, xc: self.log.error('Validation of cluster entry %s failed.'%dn) self.log.error(str(xc)) self.nagios_report.update_status_code(CRITICAL) return None self.debug_dump_obj(cluster, 'nordugrid-cluster entry') tests = self.opts.cluster_tests if not tests: for setting in [ 'cluster_tests[%s]'%cluster.name, 'cluster_tests[%s]'%self.opts.host, ]: if self.config.has_option('arcinfosys.aris', setting): raw_tests = self.config.get('arcinfosys.aris', setting) tests = [test.strip() for test in raw_tests.split(',')] break self.custom_verify_obj(cluster, tests) return cluster def verify_nordugrid_queue(self, cluster, dn, ent): """Validate and do custom checks on a nordugrid-queue entry.""" try: queue = NorduGridQueue(self.subschema, dn, ent) except LDAPValidationError, xc: self.log.error('Validation of queue entry %s failed.'%dn) self.log.error(str(xc)) self.nagios_report.update_status(CRITICAL) return None self.debug_dump_obj(queue, 'nordugrid-queue entry') tests = self.opts.queue_tests if not tests: for setting in [ 'queue_tests[%s/%s]'%(cluster.name, queue.name), 'queue_tests[%s]'%queue.name, 'queue_tests[%s]'%self.opts.host, ]: if self.config.has_option('arcinfosys.aris', setting): raw_tests = self.config.get('arcinfosys.aris', setting) tests = [test.strip() for test in raw_tests.split(',')] break self.custom_verify_obj(queue, tests) return queue def verify_glue_entry(self, dn, ent, clusters, queues): # Schema-check and construct an object representation. obj = None for objcls in ent['objectClass']: if objcls in glue_class_map: try: obj = glue_class_map[objcls](self.subschema, dn, ent) self.log.debug('Schema-checked %s.'%dn) except LDAPValidationError, xc: self.log.error( 'Schema-check failed for %s: %s'%(dn, xc)) self.nagios_report.update_status_code(CRITICAL) break if obj is None or not self.opts.compare_glue: return obj def get_cluster(cluster_name): if not cluster_name in clusters: self.log.error('No cluster %s corresponding to %s.' % (cluster_name, obj.structural_objectclass)) self.log.info('Present clusters are %s'%', '.join(clusters)) self.nagios_report.update_status_code(CRITICAL) else: return clusters[cluster_name] def get_queue(cluster_name, queue_name): if not (cluster_name, queue_name) in queues: self.log.error('No queue %s/%s corresponding to %s.' % (cluster_name, queue_name, obj.structural_objectclass)) self.log.info('Present queues are %s.'%', '.join(queues)) else: return queues[(cluster_name, queue_name)] # Compare GlueCE with ARC entries. if isinstance(obj, GlueCE): cluster = get_cluster(obj.GlueCEInfoHostName) if cluster is None: return queue = get_queue(obj.GlueCEInfoHostName, obj.GlueCEName) if queue is None: return self.log.debug('Comparing %s with ARC entries.'%queue.name) vl = GlueCE_Validator(obj, '%s/%s'%(cluster.name, queue.name), self.log) vl.compare_with(cluster, queue) if vl.error_count: self.nagios_report.update_status_code(CRITICAL) # Compare GlueCluster with ARC entries. elif isinstance(obj, GlueCluster): cluster = get_cluster(obj.GlueClusterUniqueID) if cluster is None: return vl = GlueCluster_Validator(obj, cluster.name, self.log) vl.compare_with(cluster) if vl.error_count: self.nagios_report.update_status_code(CRITICAL) # Compare GlueSubCluster with ARC entries. elif isinstance(obj, GlueSubCluster): subcluster_id = obj.GlueSubClusterUniqueID vl = GlueSubCluster_Validator(obj, subcluster_id, self.log) if '/' in subcluster_id: cluster_name, queue_name = subcluster_id.split('/') cluster = get_cluster(cluster_name) if cluster is None: return queue = get_queue(cluster_name, queue_name) if queue is None: return if cluster.homogeniety: self.log.error('Expected inhomogenious cluster for ' 'GlueSubClusterUniqueID = %r.'\ % obj.GlueSubClusterUniqueID) self.nagios_report.update_status_code(CRITICAL) vl.compare_with(cluster, queue) else: cluster = get_cluster(subcluster_id) if cluster is None: return if not cluster.homogeniety: self.log.error('Expected homogenious cluster for ' 'GlueSubClusterUniqueID = %r.'\ % obj.GlueSubClusterUniqueID) self.nagios_report.update_status_code(CRITICAL) vl.compare_with(cluster, cluster) return obj def _dn_error(self, dn, msg): if getattr(self, '_last_dn', None) != dn: self.log.error('Issues for dn "%s"'%dn) self._last_dn = dn self.log.error(' - %s'%msg) def check_glue2(self): try: self.subschema = \ ldapschema.parse(self.opts.glue2_schema, log = self.log, builddir = self.tmpdir()) except ldapschema.ParseError: self.nagios_report.update_status(UNKNOWN, 'Could not parse GLUE2 schema.') return except IOError: self.nagios_report.update_status(self.opts.if_dependent_schema, 'Using schema from tested server.') self.log.warning( 'The schema definition had to be fetched from the CE ' 'itself, as %s is missing or inaccessible. ' 'Use --glue2-schema to specify an alternative URL.' % self.opts.glue2_schema) self.subschema = self.fetch_subschema() # Stage 1. Run though each entry and check what can be checked # without information about the full dataset. Accumulate what we need # for the next phase. sr = self.search_s(self.opts.ldap_basedn or 'o=glue', ldap.SCOPE_SUBTREE, '(objectClass=GLUE2Entity)', attrlist = ['*', 'structuralObjectClass']) entry_count = 0 entry_counts = {} if_missing = {} for class_name in self.opts.warn_if_missing.split(','): if class_name: entry_counts[class_name] = 0 if_missing[class_name] = WARNING for class_name in self.opts.critical_if_missing.split(','): if class_name: entry_counts[class_name] = 0 if_missing[class_name] = CRITICAL errors = Statdict() rt = RelationTracker() for dn, ent in sr: entry_count += 1 try: o = glue2.construct_from_ldap_entry(self.subschema, dn, ent) if o is None: self.log.warning( '%s: Unknown structural object class, using ' 'generic LDAP validation.'%dn) LDAPObject(self.subschema, dn, ent) continue except (ValueError, LDAPValidationError), xc: errors['invalid entries'] += 1 self.log.error('%s: %s'%(dn, xc)) continue # This is to check consistency of a link to a parent admin domain. parent_domain_id = None for dncomp in str2dn(dn)[1:]: if dncomp[0] == 'GLUE2DomainID': parent_domain_id = dncomp[1] break # Check uniqueness of the primary key and record its dn. if o.glue2_primary_key: pk = o.glue2_primary_key pkv = getattr(o, pk) if isinstance(pkv, list): if len(pkv) > 1: self._dn_error(dn, 'Multivalued primary key %s.'%pk) errors['multivalued PKs'] += 1 if rt.seen_pk(pk, pkv): self._dn_error(dn, 'Duplicate primary key %s=%s.'%(pk, pkv)) errors['duplicate PKs'] += 1 else: rt.add_pk(pk, pkv, dn) # Checks and accumulations related to foreign keys. for fk in o.glue2_all_foreign_keys(): pk = fk.other_class.glue2_primary_key links = o.glue2_get_fk_links(fk) # Check target multiplicity and record the link. if not glue2.matching_multiplicity(fk.other_mult, len(links)): self._dn_error(dn, '%s contains %d links, but the multiplicity ' 'of the target of this relation is %s.' %(fk.name, len(links), glue2.multiplicity_indicator(fk.other_mult))) errors['multiplicity violations'] += 1 for link in links: rt.add_ref(pk, link, fk, dn) # If the entry appears under an admin domain and links to an # admin domain, they should be the same. We don't distinguish # user and admin domains here, but user domains should not be # the parent of services, anyway. if parent_domain_id and \ fk.other_class == glue2.GLUE2AdminDomain: for link in links: # 0 or 1 if link != parent_domain_id: self._dn_error(dn, 'This entry links to an admin domain other ' 'than the domain under which it appears.') # Count entries of each objectclass. class_name = o.glue2_class_name() if class_name in entry_counts: entry_counts[class_name] += 1 # Stage 2. Run though each foreign key link and perform related # checks. hierarchical_foreign_keys = \ set(self.opts.hierarchical_foreign_keys.split(',')) for ((pk, v), rte) in rt.iteritems(): for fk, refs in rte.referred_by.iteritems(): # Multiplicity Checks. if not rte.seen_in: self.log.error( 'No match for primary key %s=%s referred by %s' %(pk, v, fk.name)) errors['missing primary keys'] += 1 if not glue2.matching_multiplicity(fk.local_mult, len(refs)): self.log.error( '%d entries refer to %s=%s through %s but the ' 'source multiplicity of this relation is %s.' %(len(refs), pk, v, fk.name, glue2.multiplicity_indicator(fk.local_mult))) errors['multiplicity violations'] += 1 # DIT Checks. if len(rte.seen_in) != 1: continue dn = list(rte.seen_in)[0] if fk.name == 'GLUE2ExtensionEntityForeignKey': for ref in refs: if not is_immediate_subdn(ref, dn): self.log.error( 'The extension entry %s should be ' 'immediately below %s.'%(ref, dn)) errors['DIT issues'] += 1 elif fk.other_class == glue2.GLUE2Service and \ fk.relation >= glue2.AGGREGATION: for ref in refs: if not is_proper_subdn(ref, dn): self.log.error( 'The component %s belongs below its ' 'service %s.'%(ref, dn)) errors['DIT issues'] += 1 elif fk.name in hierarchical_foreign_keys or \ self.opts.hierarchical_aggregates \ and fk.relation >= glue2.AGGREGATION: for ref in refs: if not is_proper_subdn(ref, dn): self.log.error('"%s" belongs below "%s"'%(ref, dn)) errors['DIT issues'] += 1 # Report. for class_name, count in entry_counts.iteritems(): if count == 0: errors['absent expected object classes'] += 1 self.log.error('There are no entries with objectclass %s.' % class_name) if errors: self.nagios_report.update_status(CRITICAL, 'Found %s.' % ', '.join('%d %s'%(c, s) for s, c in errors.iteritems())) self.nagios_report.update_status(OK, 'Validated %d entries.'%entry_count) def check_aris(self): """The entry point for the ARIS probe.""" self.subschema = self.fetch_subschema() # Query cluster entries. basedn = self.opts.ldap_basedn or 'Mds-Vo-name=local, o=grid' if self.opts.clusters: filters = [filter_format('nordugrid-cluster-name=%s', cluster) for cluster in self.opts.clusters] filter = '(&(objectClass=nordugrid-cluster)(|%s))'%''.join(filters) else: filter = '(objectClass=nordugrid-cluster)' sr = self.search_s(basedn, ldap.SCOPE_ONELEVEL, filter) cluster_count = len(sr) if cluster_count == 0: msg = 'No clusters found.' if self.opts.if_no_clusters: self.nagios_report.update_status(self.opts.if_no_clusters, msg) else: self.log.warning(msg) # Report error if expected entries are missing. if self.opts.clusters: found_clusters = set() for _, ent in sr: found_clusters.update(ent['nordugrid-cluster-name']) for cluster in self.opts.clusters: if not cluster in found_clusters: self.log.error('Missing entry for %s.'%cluster) self.nagios_report.update_status_code(CRITICAL) # These are indexed for later comparison with Glue entries. clusters = {} # indexed by cluster.name queues = {} # indexed by (cluster.name, queue.name) for dn, ent in sr: # Check nordugrid-cluster entry. cluster = self.verify_nordugrid_cluster(dn, ent) if cluster is None: self.log.warning('Skipping queue checks for invalid cluster ' 'entry %s.'%dn) continue clusters[cluster.name] = cluster # Query and check the corresponding queue entries. self.log.debug('Checking queues for %s.'%cluster.name) qbasedn = 'nordugrid-cluster-name=%s,%s'%(cluster.name, basedn) qsr = self.search_s(qbasedn, ldap.SCOPE_SUBTREE, '(objectClass=nordugrid-queue)') if len(qsr) == 0: msg = 'No queue defined for %s.'%cluster.name if self.opts.if_no_queues: self.nagios_report.update_status( self.opts.if_no_queues, msg) else: self.log.warning(msg) else: middleware = getattr(cluster, 'middleware', ['unknown middleware']) self.log.info('- Cluster %r runs %s.' % (cluster.name, ', '.join(middleware))) for qdn, qent in qsr: queue = self.verify_nordugrid_queue(cluster, qdn, qent) queues[(cluster.name, queue.name)] = queue status = getattr(queue, 'status', 'unknown') self.log.info('-- Queue %r is %s.'%(queue.name, status)) # Check Glue entries if enabled. if self.opts.enable_glue: glue_counts = {} sr = self.search_s('Mds-Vo-name=resource,o=grid', ldap.SCOPE_ONELEVEL, 'objectClass=*') for dn, ent in sr: obj = self.verify_glue_entry(dn, ent, clusters, queues) if obj: oc = obj.structural_objectclass if not oc in glue_counts: glue_counts[oc] = 1 else: glue_counts[oc] += 1 for oc, count in glue_counts.iteritems(): self.log.info('Validated %s.' % counted_noun(count, '%s entry'%oc, '%s entries'%oc)) # Try to access the nordugrid-cluster-contactstring if requested. if self.opts.check_contact: self.require_voms_proxy() for cluster in clusters.itervalues(): try: url = cluster.contactstring xs = arcutils.arcls(url, log = self.log) self.log.info('%s contains %d entries.'%(url, len(xs))) except arcutils.CalledProcessError: self.nagios_report.update_status(CRITICAL, 'Contact URL %s is inaccessible.'%url) except AttributeError: self.nagios_report.update_status(CRITICAL, 'The cluster %s has no contact string.'%cluster.name) # Pass a default status and exit. try: cinfo = ' (%s)'%', '.join([', '.join(c.middleware) for c in clusters.itervalues()]) qinfo = ' (%s)'%', '.join([q.status for q in queues.itervalues()]) except Exception: cinfo = '' qinfo = '' self.nagios_report.update_status(OK, '%s%s, %s%s' %(counted_noun(cluster_count, 'cluster'), cinfo, counted_noun(len(queues), 'queue'), qinfo)) return self.nagios_exit(subject = 'ARIS service') def valid_egiis_entry(self, dn, ent): """Validate a single EGIIS entry and return an pair of the number of errors and an `EGIIS_Object` for the entry or None.""" try: return MdsServiceLdap(self.subschema, dn, ent) except LDAPValidationError, xc: self.log.error(str(xc)) self.nagios_report.update_status_code(CRITICAL) return None def check_egiis(self): """The entry point for the EGIIS probe.""" # Query the EGIIS for subschema and entries. basedn = self.opts.ldap_basedn \ or 'Mds-Vo-name=%s, o=grid'%self.opts.index_name self.subschema = self.fetch_subschema() sr = self.search_s(basedn, ldap.SCOPE_BASE, 'objectClass=MdsServiceLdap') if len(sr) == 0: raise ServiceWARNING('No EGIIS entries found.') # Check the entries. entcnts = {} for dn, ent in sr: egiis = self.valid_egiis_entry(dn, ent) if egiis: self.log.info('Good entry for %s'%egiis.ldap_suffix) entcnts[egiis.reg_status] = 1 + entcnts.get(egiis.reg_status, 0) # Report the result. entcnts = entcnts.items() entcnts.sort(lambda a, b: cmp(a[0], b[0])) countstrings = ['%d %s'%(cnt, egiis_reg_status_to_string(st).lower()) for st, cnt in entcnts] self.nagios_report.update_status( OK, 'EGIIS ok: %s.'%', '.join(countstrings)) return self.nagios_exit(subject = 'EGIIS service') nordugrid-arc-nagios-plugins-1.8.4/arcnagios/plugins/arcglue2.py0000644000175000002070000002104712202171670025430 0ustar mockbuildmockbuildimport ldap from arcnagios import ldapschema, ldaputils, ldapnagutils, glue2, nagutils from arcnagios.utils import Statdict from arcnagios.nagutils import OK, WARNING, CRITICAL, UNKNOWN, status_by_name class RelationTrackerEntry(object): def __init__(self): self.seen_in = set([]) # List of DNs. self.referred_by = {} # From GLUE2ForeignKey to list of DNs. class RelationTracker(object): def __init__(self): self._d = {} def _ensure(self, a, k): if not (a, k) in self._d: self._d[(a, k)] = RelationTrackerEntry() return self._d[(a, k)] def seen_pk(self, a, k): return (a, k) in self._d and self._d[(a, k)].seen_in != [] def add_pk(self, a, k, dn): self._ensure(a, k).seen_in.add(dn) def add_ref(self, a, k, fk, dn): ent = self._ensure(a, k) if not fk in ent.referred_by: ent.referred_by[fk] = [] ent.referred_by[fk].append(dn) def iteritems(self): return self._d.iteritems() class Check_arcglue2(ldapnagutils.LDAPNagiosPlugin): main_config_section = ['arcglue2', 'arcinfosys'] def __init__(self): ldapnagutils.LDAPNagiosPlugin.__init__(self) ap = self.argparser.add_argument_group('GLUE2 Probe Options') ap.add_argument('--glue2-schema', default = '/etc/ldap/schema/GLUE20.schema', help = 'Path of GLUE 2.0 LDAP schema. ' 'Default is /etc/ldap/schema/GLUE20.schema.') ap.add_argument('--if-dependent-schema', type = status_by_name, default = WARNING, metavar = 'NAGIOS-STATUS', help = 'Nagios status to report if LDAP schema had to be ' 'fetched from the tested server.') ap.add_argument('--warn-if-missing', default = 'GLUE2AdminDomain,GLUE2ComputingService,' 'GLUE2ComputingEndpoint', metavar = 'OBJECTCLASSES', help = 'Report warning if there are no entries of the given ' 'comma-separated list of LDAP objectclasses. ' 'Default: GLUE2AdminDomain,GLUE2ComputingService,' 'GLUE2ComputingEndpoint') ap.add_argument('--critical-if-missing', default = '', metavar = 'OBJECTCLASSES', help = 'Report critical if there are no entries of the given ' 'comma-separated list of LDAP objectclasses. ' 'Empty by default.') ap.add_argument('--hierarchical-foreign-keys', metavar = 'FOREIGN-KEY-NAMES', default = '', help = 'A comma-separating list of foreign key attribute types ' 'which should be reflected in the DIT.') ap.add_argument('--hierarchical-aggregates', default = False, action = 'store_true', help = 'Require that all foreign keys which represent ' 'aggregation or composition are reflected in the ' 'DIT.') def _dn_error(self, dn, msg): if getattr(self, '_last_dn', None) != dn: self.log.error('Issues for dn "%s"'%dn) self._last_dn = dn self.log.error(' - %s'%msg) def check(self): self.prepare_check() try: self.subschema = \ ldapschema.parse(self.opts.glue2_schema, log = self.log, builddir = self.tmpdir()) except ldapschema.ParseError: self.nagios_report.update_status(UNKNOWN, 'Could not parse GLUE2 schema.') return except IOError: self.nagios_report.update_status(self.opts.if_dependent_schema, 'Using schema from tested server.') self.log.warning( 'The schema definition had to be fetched from the CE ' 'itself, as %s is missing or inaccessible. ' 'Use --glue2-schema to specify an alternative URL.' % self.opts.glue2_schema) self.subschema = self.fetch_subschema() # Stage 1. Run though each entry and check what can be checked # without information about the full dataset. Accumulate what we need # for the next phase. sr = self.search_s(self.opts.ldap_basedn or 'o=glue', ldap.SCOPE_SUBTREE, '(objectClass=GLUE2Entity)', attrlist = ['*', 'structuralObjectClass']) entry_count = 0 entry_counts = {} if_missing = {} for class_name in self.opts.warn_if_missing.split(','): if class_name: entry_counts[class_name] = 0 if_missing[class_name] = WARNING for class_name in self.opts.critical_if_missing.split(','): if class_name: entry_counts[class_name] = 0 if_missing[class_name] = CRITICAL errors = Statdict() rt = RelationTracker() for dn, ent in sr: entry_count += 1 try: o = glue2.construct_from_ldap_entry(self.subschema, dn, ent) if o is None: self.log.warning( '%s: Unknown structural object class, using ' 'generic LDAP validation.'%dn) ldaputils.LDAPObject(self.subschema, dn, ent) continue except (ValueError, ldaputils.LDAPValidationError), xc: errors['invalid entries'] += 1 self.log.error('%s: %s'%(dn, xc)) continue # This is to check consistency of a link to a parent admin domain. parent_domain_id = None for dncomp in ldaputils.str2dn(dn)[1:]: if dncomp[0] == 'GLUE2DomainID': parent_domain_id = dncomp[1] break # Check uniqueness of the primary key and record its dn. if o.glue2_primary_key: pk = o.glue2_primary_key pkv = getattr(o, pk) if isinstance(pkv, list): if len(pkv) > 1: self._dn_error(dn, 'Multivalued primary key %s.'%pk) errors['multivalued PKs'] += 1 if rt.seen_pk(pk, pkv): self._dn_error(dn, 'Duplicate primary key %s=%s.'%(pk, pkv)) errors['duplicate PKs'] += 1 else: rt.add_pk(pk, pkv, dn) # Checks and accumulations related to foreign keys. for fk in o.glue2_all_foreign_keys(): pk = fk.other_class.glue2_primary_key links = o.glue2_get_fk_links(fk) # Check target multiplicity and record the link. if not glue2.matching_multiplicity(fk.other_mult, len(links)): self._dn_error(dn, '%s contains %d links, but the multiplicity ' 'of the target of this relation is %s.' %(fk.name, len(links), glue2.multiplicity_indicator(fk.other_mult))) errors['multiplicity violations'] += 1 for link in links: rt.add_ref(pk, link, fk, dn) # If the entry appears under an admin domain and links to an # admin domain, they should be the same. We don't distinguish # user and admin domains here, but user domains should not be # the parent of services, anyway. if parent_domain_id and \ fk.other_class == glue2.GLUE2AdminDomain: for link in links: # 0 or 1 if link != parent_domain_id: self._dn_error(dn, 'This entry links to an admin domain other ' 'than the domain under which it appears.') # Count entries of each objectclass. class_name = o.glue2_class_name() if class_name in entry_counts: entry_counts[class_name] += 1 # Stage 2. Run though each foreign key link and perform related # checks. hierarchical_foreign_keys = \ set(self.opts.hierarchical_foreign_keys.split(',')) for ((pk, v), rte) in rt.iteritems(): for fk, refs in rte.referred_by.iteritems(): # Multiplicity Checks. if not rte.seen_in: self.log.error( 'No match for primary key %s=%s referred by %s' %(pk, v, fk.name)) errors['missing primary keys'] += 1 if not glue2.matching_multiplicity(fk.local_mult, len(refs)): self.log.error( '%d entries refer to %s=%s through %s but the ' 'source multiplicity of this relation is %s.' %(len(refs), pk, v, fk.name, glue2.multiplicity_indicator(fk.local_mult))) errors['multiplicity violations'] += 1 # DIT Checks. if len(rte.seen_in) != 1: continue dn = list(rte.seen_in)[0] if fk.name == 'GLUE2ExtensionEntityForeignKey': for ref in refs: if not ldaputils.is_immediate_subdn(ref, dn): self.log.error( 'The extension entry %s should be ' 'immediately below %s.'%(ref, dn)) errors['DIT issues'] += 1 elif fk.other_class == glue2.GLUE2Service and \ fk.relation >= glue2.AGGREGATION: for ref in refs: if not ldaputils.is_proper_subdn(ref, dn): self.log.error( 'The component %s belongs below its ' 'service %s.'%(ref, dn)) errors['DIT issues'] += 1 elif fk.name in hierarchical_foreign_keys or \ self.opts.hierarchical_aggregates \ and fk.relation >= glue2.AGGREGATION: for ref in refs: if not ldaputils.is_proper_subdn(ref, dn): self.log.error('"%s" belongs below "%s"'%(ref, dn)) errors['DIT issues'] += 1 # Report. for class_name, count in entry_counts.iteritems(): if count == 0: errors['absent expected object classes'] += 1 self.log.error('There are no entries with objectclass %s.' % class_name) if errors: self.nagios_report.update_status(CRITICAL, 'Found %s.' % ', '.join('%d %s'%(c, s) for s, c in errors.iteritems())) self.nagios_report.update_status(OK, 'Validated %d entries.'%entry_count) nordugrid-arc-nagios-plugins-1.8.4/arcnagios/plugins/arcce_monitor.py0000644000175000002070000003060112502567103026547 0ustar mockbuildmockbuildimport os, re, time from arcnagios import arcutils, jobutils, nagutils, utils from arcnagios.nagutils import ServiceOK, ServiceCRITICAL, ServiceUNKNOWN from arcnagios.arcutils import ParseError from arcnagios.utils import nth _arcget_harmless_re = re.compile('|'.join([ r'$', r'Results stored at:.*', r'Warning: Some jobs were not removed.*', ])) def check_arcget_output(output): need_arcclean = False for ln in output.split('\n'): ln = ln.strip() if ln.startswith('Use arcclean to remove'): need_arcclean = True elif not re.match(_arcget_harmless_re, ln): return False, need_arcclean return True, need_arcclean class Check_arcce_monitor(jobutils.JobNagiosPlugin): def __init__(self): jobutils.JobNagiosPlugin.__init__(self) ap = self.argparser ap.add_argument('--ce', dest = 'ces', default = [], action = 'append', metavar = 'CE', help = 'Pass one or more times to restrict monitoring ' 'to the given CEs.') ap.add_argument('--termination-service', dest = 'termination_service', default = 'ARCCE Job Termination', help = 'Default service to submit result to if not specified ' 'when submitting the job. ' 'Deprecated: Should be passed on submission.') ap.add_argument('--max-sysinfo-lag', dest = 'max_infosys_lag', default = 3600.0, metavar = 'T', help = 'The maximum time to wait for a job to turn up in ' 'the arcstat listing before abandoning it.') ap.add_argument('--max-check-attempts', dest = 'max_check_attempts', default = 12, metavar = 'N', help = 'The maximum number of consecutive times a job in ' 'post-SUBMITTED state is absent from arcstat listing ' 'before it is abandoned.') ap.add_argument('--max-fetch-attempts', dest = 'max_fetch_attempts', default = 8, metavar = 'N', help = 'The maximum number of attempts to fetch a job before ' 'abandoning it.') ap.add_argument('--keep-failed-jobdata', dest = 'keep_failed_jobdata', action = 'store_true', default = False, help = 'Keep the job descriptions and output directories for ' 'failed jobs. These will not be removed automatically.') ap.add_argument('--keep-all-jobdata', dest = 'keep_all_jobdata', action = 'store_true', default = False, help = 'Keep the job descriptions and output directories for ' 'all jobs. These will not be removed automatically.') def parse_args(self, args): """Parse ARCCE-specific command-line options.""" jobutils.JobNagiosPlugin.parse_args(self, args) def _clean_output_dir(self, dir): conflict = '.conflict-%d' % int(time.time()) for entry in os.listdir(dir): subdir = os.path.join(dir, entry) self.log.warn('Moving away partially fetched output %s.' % subdir) os.rename(subdir, subdir + conflict) def prepare_top_output_dir(self, jd): workdir = self.workdir_for(jd.host, jd.job_tag) job_output_dir = os.path.join(workdir, self.JOB_OUTPUT_DIRNAME) if os.path.exists(job_output_dir): self._clean_output_dir(job_output_dir) return job_output_dir def locate_output_dir(self, top_output_dir): for subdir in os.listdir(top_output_dir): if not subdir in ['.', '..'] or '.conflict-' in subdir: return os.path.join(top_output_dir, subdir) return None def fetch_job(self, jd, job_error = None): """Fetch the job described by `jd : JobInfo`, submit passive results, and return a tuple `(did_fetch, check_ok, status_code)`, where `did_fetch` indicates whether the job was fetched, `check_ok` indicates whether checking went well, and `status_code` is the overall Nagios status reported to the passive services for this job. """ service_name = jd.termination_service or self.opts.termination_service termination_report = self.nagios_report_for(jd.host, service_name) termination_report.update_status(nagutils.OK, 'Job succeeded.') # Report the final job state if the job failed. if jd.job_state != arcutils.J_FINISHED: termination_report.update_status(nagutils.CRITICAL, 'Job terminated as %s.'%jd.job_state) if job_error: self.log.error(job_error) termination_report.log.error(job_error) # Try to fetch the job. Exit if no files where fetched. self.log.info('Fetching job %s in terminal state %s.' %(jd.job_id, jd.job_state)) top_output_dir = self.prepare_top_output_dir(jd) arcget_rc, arcget_out = \ self.run_arc_cmd('arcget', '-D', top_output_dir, jd.job_id) job_output_dir = self.locate_output_dir(top_output_dir) if job_output_dir is None: if arcget_rc == 0: self.log.error('Subdirectory from arcget not found, it ' 'should have been under %s.'%job_output_dir) if termination_report.status_code == nagutils.OK: termination_report.update_status(nagutils.UNKNOWN, 'Output directory from arcget not found.') termination_report.log.error('JID: %s'%jd.job_id) return True, False, termination_report.status_code else: self.log.error('Failed to fetch %s.'%jd.job_id) termination_report.update_status(nagutils.WARNING, 'Failed to fetch job.') if arcget_out: details = 'Output from arcget:\n%s'%arcget_out self.log.error(details) termination_report.log.error(details) termination_report.log.error('JID: %s'%jd.job_id) return False, True, termination_report.status_code # Check if arcget returned non-zero despite having fetched something. if arcget_rc != 0: is_harmless, need_arcclean = check_arcget_output(arcget_out) if need_arcclean: termination_report.log.warning('Separate arcclean needed.') self.cleaner.call('arcclean', jd.job_id) if not is_harmless: termination_report.update_status(nagutils.WARNING, 'arcget %s %s: %s' % (jd.job_id, utils.exited_with(arcget_rc), arcget_out)) if jd.job_state != arcutils.J_FINISHED: errors = \ utils.file_contents(os.path.join(job_output_dir, 'stderr.txt')) if not errors is None: self.log.error('Errors:\n%s' % errors) details = 'Errors:\n%s'%errors termination_report.log.error(details) elif not job_error: self.log.error('No stderr.txt found for %s.' % jd.job_id) termination_report.log.error('JID: %s'%jd.job_id) return True, True, termination_report.status_code # Run check and publish results from job tests. termination_report.log.info('JID: %s'%jd.job_id) status_code = termination_report.status_code for test_name in jd.tests: test = self.load_jobtest(test_name, hostname = jd.host) if test.service_description: report = self.nagios_report_for(jd.host, test.service_description) else: report = self.nagios_report test.check(report, job_output_dir, jd.stored_urls) if report.status_code > status_code: status_code = report.status_code if status_code != nagutils.OK: termination_report.log.error('JID: %s'%jd.job_id) return True, True, status_code def check_job_state_timeout(self, jd, jobstat): if jd.job_state_time is None or jd.progress_service is None: return attrs = {} for ck_state in [jd.job_specific_state, jd.job_state.name]: if ck_state and \ self.config.has_option('arcce.job-states', str(ck_state)): specs = self.config.get('arcce.job-states', str(ck_state)) attrs = dict(kv.split(':', 1) for kv in specs.split() if ':' in kv) break job_state_age = time.time() - jd.job_state_time if 'c' in attrs and job_state_age > int(attrs['c']): status = nagutils.CRITICAL msg = 'Stuck in state %s (%s).' \ % (jd.specific_state, jd.job_state.name) elif 'w' in attrs and job_state_age > int(attrs['w']): status = nagutils.WARNING msg = 'Stuck in state %s (%s).' \ % (jd.specific_state, jd.job_state.name) else: status = nagutils.OK msg = 'Normal progress.' # This also triggers in the initial case when jd.job_state_alert is # None, to clear any lingering alerts. if status != jd.job_state_alert: report = self.nagios_report_for(jd.host, jd.progress_service) report.update_status(status, msg) jd.job_state_alert = status def check(self): """Monitor submitted jobs.""" if not os.path.exists(self.top_workdir): self.log.info('The work directory is %s.'%self.top_workdir) return ServiceOK('No jobs to monitor since the working directory ' 'has not yet been created.') self.require_voms_proxy() error_count = 0 jd_of_jobid = {} dirs = self.opts.ces if not dirs: dirs = [dir for dir in os.listdir(self.top_workdir) if os.path.isdir(os.path.join(self.top_workdir, dir))] for dir in dirs: if '#' in dir: host, job_tag = dir.split('#', 1) else: host, job_tag = dir, None workdir = self.workdir_for(host, job_tag) ajf = os.path.join(workdir, self.ACTIVE_JOB_FILENAME) if not os.path.exists(ajf): self.log.debug('No active job info for %s.'%host) else: try: jd = self.load_active_job(host, job_tag) jd.host = host jd.job_tag = job_tag jd_of_jobid[jd.job_id] = jd except Exception, xc: self.log.error('Cannot load job file %s: %s'%(ajf, xc)) query_jobids = [jd.job_id for jd in jd_of_jobid.itervalues()] if query_jobids == []: msg = 'No jobs to query, found %d in terminal states.' \ %len(jd_of_jobid) return ServiceOK(msg) self.log.debug('Querying job IDs %s'%', '.join(query_jobids)) try: jobstats = arcutils.arcstat(query_jobids, log = self.log) self.log.info('Queried %d jobs, found %d.' % (len(query_jobids), len(jobstats))) for jobid in query_jobids: jd = jd_of_jobid[jobid] if not jobid in jobstats: # Job missing from from arcstat output can happen # a) right after submission before it becomes available, # b) temporarily if the CE infosys is unavailable, or # c) if the job has been permanently removed. jd.check_attempts = jd.check_attempts or 0 if jd.job_state == arcutils.J_NOT_SEEN \ and time.time() - jd.submission_time \ < self.opts.max_infosys_lag: # We hope it's case a and give it more time. self.log.info('Job %s of kind %s on %s not found yet.' % (jobid, jd.job_tag, jd.host)) elif jd.check_attempts < self.opts.max_check_attempts: # We hope it's case a or b and make a fixed number of # attempts. jd.check_attempts = jd.check_attempts + 1 self.log.info('Job %s of kind %s on %s missing for ' 'the %s time in state %s, still checking.' % (jobid, jd.job_tag, jd.host, nth(jd.check_attempts), jd.job_state)) self.save_active_job(jd, jd.host, jd.job_tag) else: # We give up, assuming c) the job has been removed, # but discard_job schedules repeated attemts to remove # the job and any staged files while new jobs are run. self.log.info('Job %s of kind %s on %s disappeared in ' 'state %s, removing active job info.' \ % (jobid, jd.job_tag, jd.host, jd.job_state)) self.discard_job(jd, archive = self.opts.keep_failed_jobdata) continue jd.check_attempts = 0 jobstat = jobstats[jobid] self.log.debug('Checking job on %s.'%jd.host) if jd.job_state != jobstat.state \ or jd.job_specific_state != jobstat.specific_state: jd.job_state = jobstat.state jd.job_specific_state = jobstat.specific_state jd.job_state_time = int(time.time()) self.check_job_state_timeout(jd, jobstat) jd.check_time = str(int(time.time())) if jd.job_state.is_final(): did_fetch, ok_check, status_code = \ self.fetch_job(jd, jobstat.job_error) if not ok_check: error_count += 1 archive = self.opts.keep_failed_jobdata \ and status_code != nagutils.OK \ or self.opts.keep_all_jobdata if did_fetch: self.cleanup_job(jd, archive = archive) elif jd.fetch_attempts >= self.opts.max_fetch_attempts: self.log.warning('Giving up on fetching %s.' % jobid) self.discard_job(jd, archive = archive) else: jd.fetch_attempts = (jd.fetch_attempts or 0) + 1 self.log.info('Will retry fetching %s.' % jobid) self.save_active_job(jd, jd.host, jd.job_tag) else: self.save_active_job(jd, jd.host, jd.job_tag) except ParseError, xc: return ServiceUNKNOWN('%s'%xc) self.cleaner # Trigger self.cleaner.run at exit for jd in jd_of_jobid.itervalues(): self.log.info('Host %s is in state %s.'%(jd.host, jd.job_state)) if error_count == 0: return ServiceOK('Checked %d jobs.'%len(jobstats)) else: return ServiceCRITICAL('Checked %d jobs, got %d error(s).' %(len(jobstats), error_count)) nordugrid-arc-nagios-plugins-1.8.4/arcnagios/glue2.py0000644000175000002070000004035411734634275023301 0ustar mockbuildmockbuildfrom arcnagios.ldaputils import LDAPObject import logging from inspect import isclass from utils import lazy_staticmethod log = logging.getLogger(__name__) MU_1 = 0 MU_01 = 1 MU_SOME = 2 MU_ANY = 3 MU_MINUS = 4 # TODO. Change to MU_SOME when the infosys is ready for it. MU_SOME_WIP = MU_ANY def multiplicity_indicator(m): return {MU_1: '1', MU_01: '0,1', MU_ANY: '*', MU_SOME: '+', MU_MINUS: '-'}[m] def matching_multiplicity(mult, n): if mult == MU_1: return n == 1 if mult == MU_01: return n in [0, 1] if mult == MU_SOME: return n > 0 if mult == MU_ANY: return True # Relation Kinds ASSOCIATION = 1 AGGREGATION = 2 COMPOSITION = 3 # TODO: The specification of relation of the GLUE2ForeignKey objects may be # incomplete, unspecified ones default to ASSOCIATION. class GLUE2ForeignKey(object): def __init__(self, name, other_class, local_mult, other_mult, bidirectional = False, dependent_fk_attribute = False, relation = ASSOCIATION): self.name = name self.other_class = other_class self.local_mult = local_mult self.other_mult = other_mult self.bidirectional = bidirectional self.dependent_fk_attribute = dependent_fk_attribute self.relation = relation _fk = GLUE2ForeignKey # Auxiliary classes to be treated as structual. glue2_exclusive_auxiliary_objectclasses = { 'GLUE2AdminDomain': 'GLUE2Domain', 'GLUE2UserDomain': 'GLUE2Domain', 'GLUE2AccessPolicy': 'GLUE2Policy', 'GLUE2MappingPolicy': 'GLUE2Policy', 'GLUE2ComputingService': 'GLUE2Service', 'GLUE2ComputingEndpoint': 'GLUE2Endpoint', 'GLUE2ComputingShare': 'GLUE2Share', 'GLUE2ComputingManager': 'GLUE2Manager', 'GLUE2ExecutionEnvironment': 'GLUE2Resource', 'GLUE2ComputingActivity': 'GLUE2Activity', 'GLUE2StorageService': 'GLUE2Service', 'GLUE2StorageEndpoint': 'GLUE2Endpoint', 'GLUE2StorageShare': 'GLUE2Share', 'GLUE2StorageManager': 'GLUE2Manager', 'GLUE2DataStore': 'GLUE2Resource', } class GLUE2Entity(LDAPObject): structural_objectclass = 'GLUE2Entity' glue2_exclusive_auxiliary_objectclass = None glue2_really_abstract = False glue2_primary_key = None glue2_foreign_keys = lazy_staticmethod(lambda: []) @classmethod def glue2_class_name(cls): return cls.glue2_exclusive_auxiliary_objectclass \ or cls.structural_objectclass @classmethod def glue2_check_class(cls): error_count = 0 log.info('+ Checking class %s.'%cls.__name__) for fk in cls.glue2_foreign_keys(): log.info('++ Checking FK %s.'%fk.name) if not fk.dependent_fk_attribute: if not isinstance(fk.other_class.glue2_primary_key, str): log.error('Missing primary key for %s'%fk.name) error_count += 1 if fk.bidirectional: found = False for rfk in fk.other_class.glue2_foreign_keys(): if issubclass(cls, rfk.other_class): found = True if not found: log.error('Did not find reverse link for %s'%fk.name) error_count += 1 if cls.glue2_class_name() != cls.__name__: log.error('Mismatched class name %s vs %s.' % (cls.glue2_class_name(), cls.__name__)) error_count += 1 assert error_count == 0 @classmethod def glue2_all_foreign_keys(cls): for fk in cls.glue2_foreign_keys(): yield fk for base in cls.__bases__: if issubclass(base, GLUE2Entity): for fk in base.glue2_all_foreign_keys(): yield fk def glue2_get_fk_links(self, fk): v = getattr(self, fk.name) if isinstance(v, list): return v elif v is None: return [] else: return [v] class GLUE2Group(GLUE2Entity): structural_objectclass = 'GLUE2Group' glue2_primary_key = 'GLUE2GroupID' glue2_foreign_keys = lazy_staticmethod(lambda: []) class GLUE2Extension(GLUE2Entity): structural_objectclass = 'GLUE2Extension' glue2_foreign_keys = lazy_staticmethod(lambda: [ _fk('GLUE2ExtensionEntityForeignKey', GLUE2Entity, MU_ANY, MU_1, dependent_fk_attribute = True, relation = COMPOSITION), ]) class GLUE2Location(GLUE2Entity): structural_objectclass = 'GLUE2Location' glue2_primary_key = 'GLUE2LocationID' glue2_foreign_keys = lazy_staticmethod(lambda: [ _fk('GLUE2LocationServiceForeignKey', GLUE2Service, MU_01, MU_ANY), _fk('GLUE2LocationDomainForeignKey', GLUE2Domain, MU_01, MU_ANY), ]) class GLUE2Contact(GLUE2Entity): structural_objectclass = 'GLUE2Contact' glue2_primary_key = 'GLUE2ContactID' glue2_foreign_keys = lazy_staticmethod(lambda: [ _fk('GLUE2ContactServiceForeignKey', GLUE2Service, MU_ANY, MU_ANY), _fk('GLUE2ContactDomainForeignKey', GLUE2Domain, MU_ANY, MU_ANY), ]) class GLUE2Domain(GLUE2Entity): structural_objectclass = 'GLUE2Domain' glue2_primary_key = 'GLUE2DomainID' glue2_really_abstract = True glue2_foreign_keys = lazy_staticmethod(lambda: []) class GLUE2AdminDomain(GLUE2Domain): glue2_exclusive_auxiliary_objectclass = 'GLUE2AdminDomain' glue2_foreign_keys = lazy_staticmethod(lambda: [ _fk('GLUE2AdminDomainAdminDomainForeignKey', GLUE2AdminDomain, MU_ANY, MU_01, relation = AGGREGATION), ]) class GLUE2UserDomain(GLUE2Domain): glue2_exclusive_auxiliary_objectclass = 'GLUE2UserDomain' glue2_foreign_keys = lazy_staticmethod(lambda: [ _fk('GLUE2UserDomainUserDomainForeignKey', GLUE2UserDomain, MU_ANY, MU_01, relation = AGGREGATION), ]) class GLUE2Service(GLUE2Entity): structural_objectclass = 'GLUE2Service' glue2_primary_key = 'GLUE2ServiceID' glue2_foreign_keys = lazy_staticmethod(lambda: [ _fk('GLUE2ServiceAdminDomainForeignKey', GLUE2AdminDomain, MU_ANY, MU_1, relation = AGGREGATION), _fk('GLUE2ServiceServiceForeignKey', GLUE2Service, MU_ANY, MU_ANY, bidirectional = True), ]) class GLUE2Endpoint(GLUE2Entity): structural_objectclass = 'GLUE2Endpoint' glue2_primary_key = 'GLUE2EndpointID' glue2_foreign_keys = lazy_staticmethod(lambda: [ _fk('GLUE2EndpointServiceForeignKey', GLUE2Service, MU_ANY, MU_1, relation = AGGREGATION), ]) class GLUE2Share(GLUE2Entity): structural_objectclass = 'GLUE2Share' glue2_primary_key = 'GLUE2ShareID' glue2_really_abstract = True glue2_foreign_keys = lazy_staticmethod(lambda: [ _fk('GLUE2ShareServiceForeignKey', GLUE2Service, MU_ANY, MU_1, relation = AGGREGATION), _fk('GLUE2ShareEndpointForeignKey', GLUE2Share, MU_ANY, MU_ANY), _fk('GLUE2ShareResourceForeignKey', GLUE2Resource, MU_ANY, MU_ANY), ]) class GLUE2Manager(GLUE2Entity): structural_objectclass = 'GLUE2Manager' glue2_primary_key = 'GLUE2ManagerID' glue2_really_abstract = True glue2_foreign_keys = lazy_staticmethod(lambda: [ _fk('GLUE2ManagerServiceForeignKey', GLUE2Service, MU_ANY, MU_1, relation = AGGREGATION), ]) class GLUE2Resource(GLUE2Entity): structural_objectclass = 'GLUE2Resource' glue2_primary_key = 'GLUE2ResourceID' glue2_really_abstract = True glue2_foreign_keys = lazy_staticmethod(lambda: [ _fk('GLUE2ResourceManagerForeignKey', GLUE2Manager, MU_ANY, MU_1, relation = COMPOSITION), ]) class GLUE2Activity(GLUE2Entity): structural_objectclass = 'GLUE2Activity' glue2_primary_key = 'GLUE2ActivityID' glue2_foreign_keys = lazy_staticmethod(lambda: [ _fk('GLUE2ActivityUserDomainForeignKey', GLUE2UserDomain, MU_ANY, MU_01), _fk('GLUE2ActivityEndpointForeignKey', GLUE2Endpoint, MU_ANY, MU_01), _fk('GLUE2ActivityShareForeignKey', GLUE2Share, MU_ANY, MU_01), _fk('GLUE2ActivityResourceForeignKey', GLUE2Resource, MU_ANY, MU_01), _fk('GLUE2ActivityActivityForeignKey', GLUE2Activity, MU_ANY, MU_ANY, bidirectional = True) ]) class GLUE2Policy(GLUE2Entity): structural_objectclass = 'GLUE2Policy' glue2_primary_key = 'GLUE2PolicyID' glue2_really_abstract = True glue2_foreign_keys = lazy_staticmethod(lambda: [ _fk('GLUE2PolicyUserDomainForeignKey', GLUE2UserDomain, MU_ANY, MU_SOME_WIP), ]) class GLUE2AccessPolicy(GLUE2Policy): glue2_exclusive_auxiliary_objectclass = 'GLUE2AccessPolicy' glue2_foreign_keys = lazy_staticmethod(lambda: [ _fk('GLUE2AccessPolicyEndpointForeignKey', GLUE2Endpoint, MU_ANY, MU_1), ]) class GLUE2MappingPolicy(GLUE2Policy): glue2_exclusive_auxiliary_objectclass = 'GLUE2MappingPolicy' glue2_foreign_keys = lazy_staticmethod(lambda: [ _fk('GLUE2MappingPolicyShareForeignKey', GLUE2Share, MU_ANY, MU_1), ]) # Computing Service # ================= class GLUE2ComputingService(GLUE2Service): glue2_exclusive_auxiliary_objectclass = 'GLUE2ComputingService' glue2_foreign_keys = lazy_staticmethod(lambda: []) class GLUE2ComputingEndpoint(GLUE2Endpoint): glue2_exclusive_auxiliary_objectclass = 'GLUE2ComputingEndpoint' glue2_foreign_keys = lazy_staticmethod(lambda: []) class GLUE2ComputingShare(GLUE2Share): glue2_exclusive_auxiliary_objectclass = 'GLUE2ComputingShare' glue2_foreign_keys = lazy_staticmethod(lambda: []) class GLUE2ComputingManager(GLUE2Manager): glue2_exclusive_auxiliary_objectclass = 'GLUE2ComputingManager' glue2_foreign_keys = lazy_staticmethod(lambda: []) class GLUE2Benchmark(GLUE2Entity): structural_objectclass = 'GLUE2Benchmark' glue2_primary_key = 'GLUE2BenchmarkID' glue2_foreign_keys = lazy_staticmethod(lambda: [ _fk('GLUE2BenchmarkComputingManagerForeignKey', GLUE2ComputingManager, MU_ANY, MU_01), _fk('GLUE2BenchmarkExecutionEnvironmentForeignKey', GLUE2ExecutionEnvironment, MU_ANY, MU_01), ]) class GLUE2ExecutionEnvironment(GLUE2Resource): glue2_exclusive_auxiliary_objectclass = 'GLUE2ExecutionEnvironment' glue2_foreign_keys = lazy_staticmethod(lambda: []) class GLUE2ApplicationEnvironment(GLUE2Entity): structural_objectclass = 'GLUE2ApplicationEnvironment' glue2_primary_key = 'GLUE2ApplicationEnvironmentID' glue2_foreign_keys = lazy_staticmethod(lambda: [ _fk('GLUE2ApplicationEnvironmentComputingManagerForeignKey', GLUE2ComputingManager, MU_ANY, MU_1, relation = COMPOSITION), _fk('GLUE2ApplicationEnvironmentExecutionEnvironmentForeignKey', GLUE2ExecutionEnvironment, MU_ANY, MU_ANY), ]) class GLUE2ApplicationHandle(GLUE2Entity): structural_objectclass = 'GLUE2ApplicationHandle' glue2_primary_key = 'GLUE2ApplicationHandleID' glue2_foreign_keys = lazy_staticmethod(lambda: [ _fk('GLUE2ApplicationHandleApplicationEnvironmentForeignKey', GLUE2ApplicationEnvironment, MU_ANY, MU_1, relation = COMPOSITION), ]) class GLUE2ComputingActivity(GLUE2Activity): glue2_exclusive_auxiliary_objectclass = 'GLUE2ComputingActivity' glue2_foreign_keys = lazy_staticmethod(lambda: []) class GLUE2ToStorageService(GLUE2Entity): structural_objectclass = 'GLUE2ToStorageService' glue2_primary_key = 'GLUE2ToStorageServiceID' glue2_foreign_keys = lazy_staticmethod(lambda: [ _fk('GLUE2ToStorageServiceComputingServiceForeignKey', GLUE2ComputingService, MU_ANY, MU_1), _fk('GLUE2ToStorageServiceStorageServiceForeignKey', GLUE2StorageService, MU_MINUS, MU_1), ]) glue2_computing_classes = [ GLUE2ComputingService, GLUE2ComputingEndpoint, GLUE2ComputingShare, GLUE2ComputingManager, GLUE2Benchmark, GLUE2ExecutionEnvironment, GLUE2ApplicationEnvironment, GLUE2ApplicationHandle, GLUE2ComputingActivity, GLUE2ToStorageService ] # Storage Service # =============== class GLUE2StorageService(GLUE2Service): glue2_exclusive_auxiliary_objectclass = 'GLUE2StorageService' glue2_foreign_keys = lazy_staticmethod(lambda: []) class GLUE2StorageServiceCapacity(GLUE2Entity): structural_objectclass = 'GLUE2StorageServiceCapacity' glue2_primary_key = 'GLUE2StorageServiceCapacityID' glue2_foreign_keys = lazy_staticmethod(lambda: [ _fk('GLUE2StorageServiceCapacityStorageServiceForeignKey', GLUE2StorageService, MU_ANY, MU_1), ]) class GLUE2StorageAccessProtocol(GLUE2Entity): structural_objectclass = 'GLUE2StorageAccessProtocol' glue2_primary_key = 'GLUE2StorageAccessProtocolID' glue2_foreign_keys = lazy_staticmethod(lambda: [ _fk('GLUE2StorageAccessProtocolStorageServiceForeignKey', GLUE2StorageService, MU_ANY, MU_1, relation = AGGREGATION), ]) class GLUE2StorageEndpoint(GLUE2Endpoint): glue2_exclusive_auxiliary_objectclass = 'GLUE2StorageEndpoint' glue2_foreign_keys = lazy_staticmethod(lambda: []) class GLUE2StorageShare(GLUE2Share): glue2_exclusive_auxiliary_objectclass = 'GLUE2StorageShare' glue2_foreign_keys = lazy_staticmethod(lambda: []) class GLUE2StorageShareCapacity(GLUE2Entity): structural_objectclass = 'GLUE2StorageShareCapacity' glue2_primary_key = 'GLUE2StorageShareCapacityID' glue2_foreign_keys = lazy_staticmethod(lambda: [ _fk('GLUE2StorageShareCapacityStorageShareForeignKey', GLUE2StorageShare, MU_ANY, MU_1, relation = AGGREGATION), ]) class GLUE2StorageManager(GLUE2Manager): glue2_exclusive_auxiliary_objectclass = 'GLUE2StorageManager' glue2_foreign_keys = lazy_staticmethod(lambda: []) class GLUE2DataStore(GLUE2Resource): glue2_exclusive_auxiliary_objectclass = 'GLUE2DataStore' glue2_foreign_keys = lazy_staticmethod(lambda: []) class GLUE2ToComputingService(GLUE2Entity): structural_objectclass = 'GLUE2ToComputingService' glue2_primary_key = 'GLUE2ToComputingServiceID' glue2_foreign_keys = lazy_staticmethod(lambda: [ _fk('GLUE2ToComputingServiceStorageAccessProtocolForeignKey', GLUE2StorageAccessProtocol, MU_ANY, MU_ANY), _fk('GLUE2ToComputingServiceComputingServiceForeignKey', GLUE2ComputingService, MU_MINUS, MU_1), _fk('GLUE2ToComputingServiceStorageServiceForeignKey', GLUE2StorageService, MU_MINUS, MU_1), ]) glue2_storage_classes = [ GLUE2StorageService, GLUE2StorageServiceCapacity, GLUE2StorageAccessProtocol, GLUE2StorageEndpoint, GLUE2StorageShare, GLUE2StorageShareCapacity, GLUE2StorageManager, GLUE2DataStore, GLUE2ToComputingService ] # Costructor Dispatcher # ===================== glue2_entity_classes = \ filter(lambda c: isclass(c) and issubclass(c, GLUE2Entity), globals().itervalues()) glue2_entity_class_map = \ dict((c.glue2_class_name(), c) for c in glue2_entity_classes) def construct_from_ldap_entry(subschema, dn, ent): ocs = set(ent['objectClass']) exauxes = ocs.intersection(glue2_exclusive_auxiliary_objectclasses) if len(exauxes) > 1: raise ValueError('Mixing exclusive auxiliary classes %s.' % ', '.join(exauxes)) if len(exauxes) == 1: class_name = exauxes.pop() base_class_name = glue2_exclusive_auxiliary_objectclasses[class_name] if not base_class_name in ocs: raise ValueError( '%s should be considered a structural subclass of %s.' %(class_name, base_class_name)) else: class_name = ent['structuralObjectClass'][0] c = glue2_entity_class_map.get(class_name) if c: return c(subschema, dn, ent) # Validation of Classes # ===================== # To validate the classes and dump a diagram to tmp.glue2.*, use # python -c 'from arcnagios.glue2 import check_classes; check_classes()' def check_classes(): def nodename(s): s = s.startswith('GLUE2') and s[5:] or s return s def fklabel(cn, s): if not s.endswith('ForeignKey'): log.error('%s should end with "ForeignKey"') return s if not s.startswith(cn): log.error('%s should start with %s'%(s, cn)) return '"%s"'%nodename(s) s = s[:-10] + 'FK' return '"%s-\\n%s"'%(nodename(cn), s[len(cn):]) logging.basicConfig(loglevel = logging.INFO) dot_out = open('tmp.glue2.dot', 'w') dot_out.write('digraph {\n' ' rankdir = "BT";\n' ' edge [arrowsize=1.2, fontsize=13, labelfontsize=15];\n') if False: dot_out.write(' subgraph cluster_computing {%s;}\n' % '; '.join(c.glue2_class_name() for c in glue2_computing_classes)) dot_out.write(' subgraph cluster_storage {%s;}\n' % '; '.join(c.glue2_class_name() for c in glue2_storage_classes)) for c in glue2_entity_classes: c.glue2_check_class() if not c is GLUE2Entity: for cp in c.__subclasses__(): dot_out.write(' %s -> %s;\n' %(nodename(cp.glue2_class_name()), nodename(c.glue2_class_name()))) for fk in c.glue2_foreign_keys(): cp = fk.other_class dot_out.write(' %s -> %s [arrowhead=odiamond, label=%s, ' 'taillabel="%s", headlabel="%s"];\n' %(nodename(c.glue2_class_name()), nodename(cp.glue2_class_name()), fklabel(c.glue2_class_name(), fk.name), multiplicity_indicator(fk.local_mult), multiplicity_indicator(fk.other_mult))) dot_out.write('}\n') dot_out.close() import os return os.system('dot -T svg -o tmp.glue2.svg tmp.glue2.dot') nordugrid-arc-nagios-plugins-1.8.4/arcnagios/utils.py0000644000175000002070000000423712375416617023423 0ustar mockbuildmockbuildimport os class Unspecified(object): pass unspecified = Unspecified() def ident(x): return x def map_option(f, opt): if opt is None: return None return f(opt) class lazy_property(object): def __init__(self, func): self._func = func def __get__(self, other, _=None): if other is None: return self else: value = self._func(other) setattr(other, self._func.func_name, value) return value def lazy_staticmethod(thunk): def g(cls): if not hasattr(cls, '_lazy_static'): cls._lazy_static = {} h = hash(thunk) if not h in cls._lazy_static: cls._lazy_static[h] = thunk() return cls._lazy_static[h] return classmethod(g) class GetattrProxy(object): def __init__(self, *objs): self.objs = objs def __getitem__(self, k): for obj in self.objs: try: return getattr(obj, k) except AttributeError: pass class Statdict(object): def __init__(self): self._d = {} def __getitem__(self, k): return self._d.get(k, 0) def __setitem__(self, k, v): self._d.__setitem__(k, v) def iteritems(self): return self._d.iteritems() def __bool__(self): return bool(self._d) def __len__(self): return len(self._d) def options_of_spec(shortopts, longopts): def fix_longopt(longopt): if longopt.endswith('='): return '--' + longopt[0:len(longopt)-1] else: return '--' + longopt return ['-' + opt for opt in shortopts if opt != ':'] \ + map(fix_longopt, longopts) def counted_noun(count, sing_word, pl_word = None): if count == 1: return '%d %s'%(count, sing_word) else: return '%d %s'%(count, pl_word or sing_word + 's') def nth(n): if n % 100 > 3 and n % 100 < 21: return str(n) + 'th' else: return str(n) + {1: 'st', 2: 'nd', 3: 'rd'}.get(n % 10, 'th') def file_contents(fp): try: fd = open(fp) c = fd.read() fd.close() return c except IOError: return None def exited_with(sc): if os.WIFEXITED(sc): return 'exited with %d' % os.WEXITSTATUS(sc) elif os.WIFSIGNALED(sc): return 'terminated by signal %d' % os.WTERMSIG(sc) elif os.WIFCONTINUED(sc): return 'continued' elif os.WIFSTOPPED(sc): return 'stopped' return 'hit a rock' nordugrid-arc-nagios-plugins-1.8.4/arcnagios/arcutils.py0000644000175000002070000002615712503004051024070 0ustar mockbuildmockbuildimport arc, os, re, posix, time from datetime import datetime from subprocess import Popen, PIPE from utils import map_option try: from subprocess import CalledProcessError except ImportError: class CalledProcessError(OSError): def __init__(self, exitcode, cmd): OSError.__init__(self, '%s exited with %d'%(cmd, exitcode)) class ParseError(Exception): """Exception raised on unrecognized command output.""" pass # Job States # S_UNKNOWN = 0 S_ENTRY = 1 S_PRERUN = 2 S_INLRMS = 3 S_POSTRUN = 4 S_FINAL = 5 class Jobstate(object): def __init__(self, name, stage): self.name = name self.stage = stage def __str__(self): return self.name def is_final(self): return self.stage == S_FINAL class InlrmsJobstate(Jobstate): def __init__(self, name): Jobstate.__init__(self, name, S_INLRMS) class PendingJobstate(Jobstate): def __init__(self, name, stage, pending): Jobstate.__init__(self, name, stage) self.pending = pending def _jobstate(name, stage = S_UNKNOWN): if name.startswith('INLRMS:'): assert stage == S_UNKNOWN or stage == S_INLRMS js = InlrmsJobstate(name) elif name.startswith('PENDING:'): pending = jobstate_of_str(name[8:]) js = PendingJobstate(name, stage, pending) else: js = Jobstate(name, stage) _jobstate_of_str[name] = js return js _jobstate_of_str = {} def jobstate_of_str(name): if not name in _jobstate_of_str: _jobstate_of_str[name] = _jobstate(name) return _jobstate_of_str[name] J_NOT_SEEN = _jobstate("NOT_SEEN", stage = S_ENTRY) J_ACCEPTED = _jobstate("Accepted", stage = S_ENTRY) J_PREPARING = _jobstate("Preparing", stage = S_PRERUN) J_SUBMITTING = _jobstate("Submitting", stage = S_PRERUN) J_HOLD = _jobstate("Hold", stage = S_PRERUN) J_QUEUING = _jobstate("Queuing", stage = S_INLRMS) J_RUNNING = _jobstate("Running", stage = S_INLRMS) J_FINISHING = _jobstate("Finishing", stage = S_POSTRUN) J_FINISHED = _jobstate("Finished", stage = S_FINAL) J_KILLED = _jobstate("Killed", stage = S_FINAL) J_FAILED = _jobstate("Failed", stage = S_FINAL) J_DELETED = _jobstate("Deleted", stage = S_FINAL) J_OTHER = _jobstate("Other", stage = S_UNKNOWN) # Also added to _jobstate_of_str for backwards compatibility. We used # "Specific state" earlier, and it may be saved in active.map files. SJ_ACCEPTING = _jobstate("ACCEPTING", stage = S_ENTRY) SJ_PENDING_ACCEPTED = _jobstate("PENDING:ACCEPTED", stage = S_ENTRY) SJ_ACCEPTED = _jobstate("ACCEPTED", stage = S_ENTRY) SJ_PENDING_PREPARING = _jobstate("PENDING:PREPARING",stage = S_PRERUN) SJ_PREPARING = _jobstate("PREPARING", stage = S_PRERUN) SJ_SUBMIT = _jobstate("SUBMIT", stage = S_PRERUN) SJ_SUBMITTING = _jobstate("SUBMITTING", stage = S_PRERUN) SJ_PENDING_INLRMS = _jobstate("PENDING:INLRMS", stage = S_INLRMS) SJ_INLRMS = _jobstate("INLRMS", stage = S_INLRMS) SJ_INLRMS_Q = _jobstate("INLRMS:Q", stage = S_INLRMS) SJ_INLRMS_R = _jobstate("INLRMS:R", stage = S_INLRMS) SJ_INLRMS_EXECUTED = _jobstate("INLRMS:EXECUTED", stage = S_INLRMS) SJ_INLRMS_S = _jobstate("INLRMS:S", stage = S_INLRMS) SJ_INLRMS_E = _jobstate("INLRMS:E", stage = S_INLRMS) SJ_INLRMS_O = _jobstate("INLRMS:O", stage = S_INLRMS) SJ_FINISHING = _jobstate("FINISHING", stage = S_POSTRUN) SJ_CANCELING = _jobstate("CANCELING", stage = S_POSTRUN) SJ_FAILED = _jobstate("FAILED", stage = S_FINAL) SJ_KILLED = _jobstate("KILLED", stage = S_FINAL) SJ_FINISHED = _jobstate("FINISHED", stage = S_FINAL) SJ_DELETED = _jobstate("DELETED", stage = S_FINAL) # Utilities # def explain_wait_status(status): if posix.WIFEXITED(status): exitcode = posix.WEXITSTATUS(status) if exitcode: msg = 'exited with %d'%exitcode else: msg = 'exited normally' elif posix.WIFSTOPPED(status): signo = posix.WSTOPSIG(status) msg = 'stopped by signal %d'%signo elif posix.WIFSIGNALED(status): signo = posix.WTERMSIG(status) msg = 'terminated by signal %d'%signo elif posix.WIFCONTINUED(status): msg = 'continued' if posix.WCOREDUMP(status): return '%s (core dumped)' % msg else: return msg def _simple_cmd(argv, log = None): p = Popen(argv, stdout = PIPE, stderr = PIPE) output, errors = p.communicate() if log: if output: for ln in output.strip().split('\n'): log.info('%s: %s'%(argv[0], ln)) if errors: for ln in errors.strip().split('\n'): log.error('%s: %s'%(argv[0], ln)) if p.returncode != 0: raise CalledProcessError(p.returncode, ' '.join(argv)) def roundarg(t): return str(int(t + 0.5)) # ARC Commands # def arccp(src_url, dst_url, log = None, timeout = 20): return _simple_cmd(['arccp', '-t', roundarg(timeout), src_url, dst_url], log = log) def arccp_T(src_url, dst_url, log = None, timeout = 20): return _simple_cmd(['arccp', '-T', '-t', roundarg(timeout), src_url, dst_url], log = log) def arcrm(dst_url, log = None, timeout = 20): return _simple_cmd(['arcrm', '-t', roundarg(timeout), dst_url], log = log) def arcls(url, log = None, timeout = 20): p = Popen(['arcls', '-t', roundarg(timeout), '-l', url], stdout = PIPE, stderr = PIPE) output, errors = p.communicate() entries = [] for ln in output.split('\n')[1:]: if ln: comps = ln.rsplit(None, 7) entries.append(ArclsEntry(*comps)) if p.returncode != 0: if log: log.error('Errors from arcls:\n%s'%errors) raise CalledProcessError(p.returncode, 'arcls') return entries def arcls_L(url, log = None, timeout = 20): p = Popen(['arcls', '-L', '-t', roundarg(timeout), '-l', url], stdout = PIPE, stderr = PIPE) output, errors = p.communicate() if p.returncode != 0: if log: log.error('Errors from arcls -L:\n%s'%errors) raise CalledProcessError(p.returncode, 'arcls') return [s.strip() for s in output.split('\n')[1:]] def arcclean(jobids, force = False, timeout = 20, log = None): argv = ['arcclean', '-t', roundarg(timeout)] if force: argv.append('-f') argv += jobids _simple_cmd(argv, log = log) _arcstat_state_re = re.compile(r'(\w+)\s+\(([^()]+)\)') def parse_old_arcstat_state(s): mo = re.match(_arcstat_state_re, s) if not mo: raise ParseError('Malformed arcstat state %s.'%s) return jobstate_of_str(mo.group(1)), mo.group(2) class Arcstat(object): def __init__(self, state = None, specific_state = None, submitted = None, job_error = None, exit_code = None): self.state = state self.specific_state = state self.submitted = submitted self.job_error = job_error self.exit_code = exit_code def arcstat(jobids = None, log = None): cmd = ['arcstat', '-l'] if jobids is None: cmd.append('-a') else: cmd += jobids fd = Popen(cmd, stdout = PIPE).stdout jobstats = {} lnno = 0 def parse_error(msg): if log: log.error('Unexpected output from arcstat ' 'at line %d: %s'%(lnno, msg)) else: fd.close() raise ParseError('Unexpected output from arcstat ' 'at line %d: %s'%(lnno, msg)) def convert(jobid, jobstat): if 'Specific state' in jobstat: state = jobstate_of_str(jobstat['State']) specific_state = jobstat['Specific state'] elif 'State' in jobstat: # Compatibility with old arcstat. state, specific_state = parse_old_arcstat_state(jobstat['State']) else: raise ParseError('Missing "State" or "Specific state" for %s.' % jobid) return Arcstat(state = state, specific_state = specific_state, exit_code = map_option(int, jobstat.get('Exit code')), submitted = jobstat.get('Submitted'), job_error = jobstat.get('Job Error')) jobid, jobstat = None, None for ln in fd: ln = ln.strip() lnno += 1 if ln == '': if not jobid is None: jobstats[jobid] = convert(jobid, jobstat) jobid, jobstat = None, None elif ln.startswith('No jobs') or ln.startswith('Status of '): break elif ln.startswith('Job: '): if not jobid is None: parse_error('Missing empty line before "Job: ..."') continue jobid = ln[5:].strip() jobstat = {} elif ln.startswith('Warning:'): pass else: if jobid is None: parse_error('Missing "Job: ..." header before %r'%ln) continue try: k, v = ln.split(':', 1) jobstat[k] = v.strip() except ValueError: parse_error('Expecting ": ", got %r'%ln) continue fd.close() if not jobid is None: jobstats[jobid] = convert(jobid, jobstat) return jobstats def load_jobs_xml(timeout = 20, log = None): deadline = time.time() + timeout try: import xml.etree.ElementTree as et # Python 2.6 except ImportError: import elementtree.ElementTree as et # Python 2.4 uc = arc.UserConfig() jobstats = {} jp = os.path.join(uc.ARCUSERDIRECTORY, 'jobs.xml') # Should create a lock file, but better not make assumptions about ARC # internals. This code should be oboselete when NG-3240 is closed. while os.path.exists(jp + '.lock'): if deadline - time.time() <= 1: if log: log.warn('Timout while waiting for jobs.xml lock.') return {} time.sleep(1) jx = et.parse(jp) for job in jx.findall('Job'): t_sub = job.findtext('LocalSubmissionTime') if t_sub: t_sub = t_sub.replace('T', ' ') if t_sub[-1] == 'Z': t_sub = t_sub[0:-1] jobstats[job.findtext('JobID')] = Arcstat(submitted = t_sub) return jobstats def arcprune(max_age = 604800, log = None, timeout = 20, use_jobs_xml = False, force = True): t_start = time.time() pruned_count = 0 failed_count = 0 if use_jobs_xml: try: jobstats = load_jobs_xml(timeout = 0.8*timeout, log = log) except ImportError, xc: if log: log.warn('Missing dependency for custom jobs.xml loader: %s'%xc) log.warn('Falling back to arcstat.') jobstats = arcstat(log = log) except Exception, xc: if log: log.warn('Cannot read jobs.xml: %s'%xc) log.warn('Falling back to arcstat.') jobstats = arcstat(log = log) else: jobstats = arcstat(log = log) for jobid, jobstat in jobstats.iteritems(): submitted = jobstat.submitted if not submitted: continue tm_sub = time.strptime(submitted, '%Y-%m-%d %H:%M:%S') t_sub = time.mktime(tm_sub) t_left = timeout - time.time() + t_start if t_left < 1: if log: log.warn('Timout while pruning jobs.') break if t_start - t_sub > max_age: try: if log: log.info('Cleaning %s from %s.'%(jobid, submitted)) arcclean([jobid], log = log, timeout = t_left, force = force) pruned_count += 1 except Exception, xc: failed_count = 0 if log: log.warn('Failed to prune %s: %s'%(jobid, xc)) return len(jobstats), pruned_count, failed_count class ArclsEntry(object): DIR = 0 FILE = 1 def __init__(self, name, typ, size, cdate, ctod, validity, checksum, latency = ''): self.filename = name if typ == 'dir': self.entry_type = self.DIR elif typ == 'file': self.entry_type = self.FILE else: self.entry_type = None self.size = size # Not in Python 2.4: # self.ctime = datetime.strptime(cdate + 'T' + ctod, '%Y-%m-%dT%H:%M:%S') def drop_NA(s): if s != '(n/a)': return s if cdate == '(n/a)': self.validity = drop_NA(validity) self.checksum = drop_NA(checksum) self.latency = drop_NA(latency) else: self.validity = drop_NA(ctod) self.checksum = drop_NA(validity) self.latency = drop_NA(checksum) nordugrid-arc-nagios-plugins-1.8.4/arcnagios/substitution.py0000644000175000002070000001404612453742625025034 0ustar mockbuildmockbuildfrom arcnagios.nagutils import ServiceUNKNOWN from arcnagios.utils import unspecified import ldap, os, random, re, time def get_interp(config, section, var, target_env, default = unspecified): if config.has_option(section, var): import_interpolated_variables(config, section, var, target_env) return config.get(section, var, vars = target_env) elif default == unspecified: raise ServiceUNKNOWN('Configuration error: Missing %s in [%s].' % (var, section)) else: return default def get_interp_opt(config, section, var, target_env): return get_interp(config, section, var, target_env, default = None) def _subst_option(config, section, var, target_env): default = get_interp_opt(config, section, 'default', target_env) if default is None: raise ServiceUNKNOWN('Missing required option (-O %s=...).'%var) target_env[var] = default def _subst_getenv(config, section, var, target_env): default = get_interp_opt(config, section, 'default', target_env) if config.has_option(section, 'envvar'): envvar = get_interp_opt(config, section, 'envvar', target_env) else: prefix = get_interp(config, section, 'prefix', target_env, '') envvar = prefix + var v = os.getenv(envvar, default) if v is None: raise ServiceUNKNOWN('Missing required option (-O) or ' 'enviroment variable %s.' % envvar) target_env[var] = v def _subst_ldap(config, section, var, target_env): basedn = get_interp(config, section, 'basedn', target_env) filterstr = get_interp(config, section, 'filter', target_env) attribute = get_interp(config, section, 'attribute', target_env) attrlist = map(str.strip, attribute.split(',')) scope = ldap.SCOPE_SUBTREE sr = None for uri in get_interp(config, section, 'uri', target_env).split(): lc = ldap.initialize(uri) try: sr = lc.search_s(basedn, scope, filterstr, attrlist = attrlist) break except ldap.LDAPError, xc: pass if sr is None: raise ServiceUNKNOWN('LDAP query %s failed with: %s' % (section, xc)) for dn, ent in sr: for attr in attrlist: if attr in ent: target_env[var] = ent[attr][0] return if config.has_option(section, 'default'): target_env[var] = get_interp(config, section, 'default', target_env) else: raise ServiceUNKNOWN('LDAP query %s did not provide a value for %s.' % (filterstr, section)) def _subst_pipe(config, section, var, target_env): fh = os.popen(get_interp(config, section, 'command', target_env)) target_env[var] = fh.read().strip() fh.close() def _subst_random_line(config, section, var, target_env): path = get_interp(config, section, 'input_file', target_env) try: fh = open(path) except IOError, xc: raise ServiceUNKNOWN(str(xc)) rng = random.Random(time.time()) include = None exclude = set() if config.has_option(section, 'exclude'): exclude = set(get_interp(config, section, 'exclude',target_env).split()) if config.has_option(section, 'include'): include = set(get_interp(config, section, 'include',target_env).split()) include.difference_update(exclude) n = 0 chosen_line = None while True: try: line = fh.next().strip() if not line or line.startswith('#') \ or not include is None and not line in include \ or include is None and line in exclude: continue if rng.randint(0, n) == 0: chosen_line = line except StopIteration: break n += 1 fh.close() if chosen_line is None: raise ServiceUNKNOWN('%s must contain at least one non-excluded line' % path) target_env[var] = chosen_line def _subst_strftime(config, section, var, target_env): if config.has_option(section, 'raw_format'): format = config.get(section, 'raw_format', vars = target_env, raw=True) else: format = get_interp(config, section, 'format', target_env) target_env[var] = time.strftime(format) def _subst_switch(config, section, var, target_env): case = 'case[%s]'%get_interp(config, section, 'index', target_env) if config.has_option(section, case): import_interpolated_variables(config, section, case, target_env) target_env[var] = get_interp(config, section, case, target_env) else: if not config.has_option(section, 'default'): raise ServiceUNKNOWN( 'No %s and no default in section variable.%s.'%(case, var)) import_interpolated_variables(config, section, 'default', target_env) target_env[var] = get_interp(config, section, 'default', target_env) _method_by_name = { 'getenv': _subst_getenv, 'ldap': _subst_ldap, 'option': _subst_option, 'pipe': _subst_pipe, 'random_line': _subst_random_line, 'strftime': _subst_strftime, 'switch': _subst_switch, } def register_substitution_method(name, f): _method_by_name[name] = f def import_section_variables(config, section, target_env): """Import all variables listed in ``variables`` in ``section``.""" if config.has_option(section, 'variables'): vars = config.get(section, 'variables').split() for var in vars: if var in target_env: if target_env[var] == None: raise ServiceUNKNOWN('Substitutions involving %s are ' 'cyclic.'%var) continue target_env[var] = None import_variable(config, var, target_env) _interp_re = re.compile(r'%\(([a-zA-Z0-9_]+)\)') def import_interpolated_variables(config, section, var, target_env): """Import variables needed for expanding ``var`` in ``section``.""" raw_value = config.get(section, var, raw = True) for mo in re.finditer(_interp_re, raw_value): v = mo.group(1) if not v in target_env: import_variable(config, v, target_env) def import_variable(config, var, target_env): """Import ``var`` by executing its defining section, populating ``target_env`` with its value and the values of any dependent variables.""" section = 'variable.' + var import_section_variables(config, section, target_env) method = config.get(section, 'method') try: return _method_by_name[method](config, section, var, target_env) except KeyError: raise ServiceUNKNOWN('Unknown substitution method %s.'%method) nordugrid-arc-nagios-plugins-1.8.4/arcnagios/ldapschema.py0000644000175000002070000000442012116076011024335 0ustar mockbuildmockbuildfrom ply import lex, yacc import logging, os import ldap.schema log = logging.getLogger(__name__) class ParseError(Exception): pass def error(t, msg): t.lexer.error_count += 1 t.lexer.log.error("%s:%d: %s"%(t.lexer.path, t.lexer.lineno, msg)) tokens = ( 'ATTRIBUTETYPE', 'OBJECTCLASS', 'LPAREN', 'RPAREN', 'COMMENT', 'MISC', ) t_LPAREN = r'\(' t_RPAREN = r'\)' def t_COMMENT(t): r'\#.*' pass def t_ATTRIBUTETYPE(t): r'\battributetype\b' return t def t_OBJECTCLASS(t): r'\bobjectclass\b' return t def t_space(t): r'[ \t\n]+' t.lexer.lineno += t.value.count('\n') def t_MISC(t): r"([^()' \t\n]+|'[^']*')+" t.lexer.lineno += t.value.count('\n') return t def t_error(t): error(t, "Illegal character '%s'"%t.value[0]) t.lexer.skip(1) def p_schema0(p): 'schema :' p[0] = {'objectClasses': [], 'attributeTypes': []} def p_schema1(p): 'schema : schema schema_element' st, se = p[2] if se: p[1][st].append(se) p[0] = p[1] def p_attributetype(p): 'schema_element : ATTRIBUTETYPE delimited' try: p[0] = 'attributeTypes', p[2] except AssertionError: error(p, "Invalid attribute specification.") def p_objectclass(p): 'schema_element : OBJECTCLASS delimited' try: p[0] = 'objectClasses', p[2] except AssertionError: error(p, "Invalid attribute specification.") def p_delimited(p): 'delimited : LPAREN misc RPAREN' p[0] = '(' + p[2] + ')' def p_misc0(p): 'misc :' p[0] = '' def p_misc1(p): '''misc : misc MISC | misc delimited''' p[0] = p[1] + ' ' + p[2] def p_error(p): if p is None: return p.lexer.error_count += 1 error(p, 'Syntax error before "%s"'% p.value) def parse(path, log = log, builddir = '.'): lexer = lex.lex() lexer.path = path lexer.log = log lexer.error_count = 0 parser = yacc.yacc(outputdir = builddir, tabmodule = 'ldap_schema_parser', debugfile = 'ldap_schema_parser.out') fh = open(path) text = fh.read() fh.close() r = parser.parse(text, lexer = lexer) if lexer.error_count: raise ParseError('Failed to parse schema file.') return ldap.schema.subentry.SubSchema(r) # logging.basicConfig(loglevel = logging.DEBUG) # print parse('/etc/ldap/schema/GLUE20.schema') nordugrid-arc-nagios-plugins-1.8.4/arcnagios/confargparse.py0000644000175000002070000000273611663225637024736 0ustar mockbuildmockbuildimport argparse, ConfigParser class UsageOrConfigError(Exception): pass class UsageError(Exception): pass class ConfigError(Exception): def __init__(self, config_file, config_section, msg): Exception.__init__(self, 'In %s, section %s or DEFAULT: %s' % (config_file, config_section, msg)) self.config_file = config_file self.config_section = config_section class ConfigArgumentParser(argparse.ArgumentParser): def error(self, message): raise UsageError(message) def configure_defaults(self, config, defaults_section): """Merge those configurations files in the list `config_paths` which exist and return the corresponding `ConfigParser` object. Later files take presedence over former. Variables from `defaults_section` will be used as defaults for the argument parser options.""" if not isinstance(defaults_section, list): defaults_section = [defaults_section] for section in defaults_section: if config.has_section(section): defaults = [(var, config.get(section, var)) for var in config.options(section)] self.set_defaults(**dict(defaults)) break # TODO: Validation can be refined. Ideally we should check each config # variable individually and report a) the variable name rather than # the option name and b) the line number of the bad value. try: self.parse_args() except argparse.ArgumentTypeError, err: raise ConfigError( 'The configuration corresponding to a command-line ' 'option failed: %s' % err) nordugrid-arc-nagios-plugins-1.8.4/arcnagios/arcinfosys.py0000644000175000002070000001003611665413143024425 0ustar mockbuildmockbuildfrom ldaputils import LDAPObject # LDAP Helpers # ============ def _get_str_opt(dn, ent, attr): vs = ent.get(attr, []) if len(vs) == 0: return None if len(vs) == 1: return vs[0] raise ValueError('Expected single value for attribute %s in %s.'%(attr, dn)) def _get_str(dn, ent, attr): vs = ent.get(attr, []) if len(vs) == 1: return vs[0] raise ValueError('Expected single value for attribute %s in %s.'%(attr, dn)) def _get(type, dn, ent, attr): s = _get_str(dn, ent, attr) try: return type(s) except ValueError: raise ValueError('Expecting an value of type %s for %s in %s.' %(type, attr, dn)) def _get_opt(type, dn, ent, attr): s = _get_str_opt(dn, ent, attr) if not s is None: try: return type(s) except (ValueError, TypeError): raise ValueError('Expecting an value of type %s for %s in %s.' %(type, attr, dn)) def _get_int(dn, ent, attr): _get(int, dn, ent, attr) def _get_int_opt(dn, ent, attr): _get_opt(int, dn, ent, attr) # EGIIS # ===== EGIIS_REG_VALID = 0 EGIIS_REG_INVALID = 1 EGIIS_REG_PURGED = 2 _egiis_reg_status_of_string = { 'VALID': EGIIS_REG_VALID, 'INVALID': EGIIS_REG_INVALID, 'PURGED': EGIIS_REG_PURGED, } _egiis_reg_status_to_string = ['VALID', 'INVALID', 'PURGED'] def egiis_reg_status_of_string(s): if not s in _egiis_reg_status_of_string: raise ValueError('Invalid Mds-Reg-status %s'%s) return _egiis_reg_status_of_string[s] def egiis_reg_status_to_string(st): return _egiis_reg_status_to_string[st] class MdsServiceLdap(LDAPObject): """Instances of this class holds decoded representations of LDAP entries of class MdsServiceLdap.""" structural_objectclass = 'MdsServiceLdap' strip_attribute_prefixes = ['Mds-Service-', 'Mds-'] lowercase_all_attributes = True def __init__(self, subschema, dn, ent): LDAPObject.__init__(self, subschema, dn, ent) # CHECKME: Mds-Service-protocol, Mds-Service-Ldap-*, and # Mds-Reg-status are multi-valued in the schema. Should they be? reg_status_str = _get_str_opt(dn, ent, 'Mds-Reg-status') if reg_status_str is None: # FIXME: Mds-Reg-status is not required. What's the default? self.reg_status = None else: self.reg_status = egiis_reg_status_of_string(reg_status_str) # ARIS # ==== class NorduGridCluster(LDAPObject): """Instances of this class holds decoded representations of LDAP entries of class nordugrid-cluster.""" structural_objectclass = 'nordugrid-cluster' strip_attribute_prefixes = ['nordugrid-cluster-', 'Mds-'] class NorduGridQueue(LDAPObject): """Instances of this class holds decoded representations of LDAP entries of class nordugrid-queue.""" structural_objectclass = 'nordugrid-queue' strip_attribute_prefixes = ['nordugrid-queue-', 'Mds-'] # Glue Schemas # ============ # # Full list of structural object classes: # Glue-MDS.schema: # Mds # Glue-CORE.schema: # GlueService, GlueServiceData, GlueSite # Glue-SE.schema: # GlueSEAccessProtocol, GlueSEControlProtocol, GlueSL, # GlueSLLocalFileSystem, GlueSLRemoteFileSystem, GlueSLFile, # GlueSLDirectory, GlueVOInfo, GlueSA, GlueSE # Glue-CE.schema: # GlueCEJob, GlueCluster, GlueHost, GlueHostLocalFileSystem, # GlueHostRemoteFileSystem, GlueHostFile, GlueLocation, GlueVOView, # GlueSoftware, GlueSoftwareData, GlueCE, GlueSubCluster # Glue-CESEBind.schema: # GlueCESEBindGroup, GlueCESEBind class GlueSite(LDAPObject): structural_objectclass = 'GlueSite' class GlueCluster(LDAPObject): structural_objectclass = 'GlueCluster' class GlueSubCluster(LDAPObject): structural_objectclass = 'GlueSubCluster' class GlueCE(LDAPObject): structural_objectclass = 'GlueCE' class GlueCESEBindGroup(LDAPObject): structural_objectclass = 'GlueCESEBindGroup' class GlueVOView(LDAPObject): structural_objectclass = 'GlueVOView' class GlueCESEBind(LDAPObject): structural_objectclass = 'GlueCESEBind' glue_class_map = dict((c.structural_objectclass, c) for c in [GlueSite, GlueCluster, GlueSubCluster, GlueCE, GlueCESEBindGroup, GlueVOView, GlueCESEBind]) nordugrid-arc-nagios-plugins-1.8.4/arcnagios/jobscripts.py0000644000175000002070000000277711654541562024451 0ustar mockbuildmockbuildimport pipes class CEProbeScript(object): """A class used to generate job-scripts for CE probes.""" def __init__(self): self._required_files = [] self._generated_files = [] def require_file(self, fname, size = None, sha1sum = None): self._required_files.append((fname, size, sha1sum)) def generate_file(self, fname, content = None): self._generated_files.append((fname, content)) def write_to(self, fh): fh.write('#! /bin/sh\n' 'echo "Job started `date -Is`."\n' 'status=0\n' 'error() { echo 1>&2 "ERROR: $@"; status=1; }\n') for fname, size, sha1sum in self._required_files: d = dict(fname = pipes.quote(fname), size = size, sha1sum = sha1sum) fh.write('if test ! -e %(fname)s; then\n' ' error "Missing file "%(fname)s\n'%d) if not size is None: fh.write('elif test `stat -c %%s %(fname)s` -ne %(size)d; then\n' ' error "Wrong size for "%(fname)s\n'%d) if not sha1sum is None: fh.write('elif test `sha1sum %(fname)s` -ne %(sha1sum)d; then\n' ' error "Wrong sha1sum for "%(fname)s\n'%d) fh.write('fi\n') for fname, content in self._generated_files: if content is None: fh.write('hostname >%s\n'%pipes.quote(fname)) else: fh.write('echo %s >%s\n' % (pipes.quote(content), pipes.quote(content))) fh.write('echo "Present files before termination:"\n' 'ls -l\n' 'echo "Job finished `date -Is`, status = $status."\n' 'exit $status\n') def save_to(self, path): fh = open(path, 'w') self.write_to(fh) fh.close() nordugrid-arc-nagios-plugins-1.8.4/arcnagios/vomsutils.py0000644000175000002070000001040012435050276024305 0ustar mockbuildmockbuildimport arc, logging, os, pipes, time from nagutils import UNKNOWN log = logging.getLogger(__name__) class ProxyInitError(EnvironmentError): def __init__(self, msg, details = None): EnvironmentError.__init__(self, msg) self.details = details def require_voms_proxy(voms, key_path, cert_path, proxy_path, min_proxy_lifetime = 7 * 3600): # To the benefit of any commands we'll run. os.putenv('X509_USER_PROXY', proxy_path) # Check for an existing proxy with sufficient time left. uc = arc.UserConfig() uc.ProxyPath(proxy_path) cred = arc.Credential(uc) t_now = time.time() t_E = cred.GetEndTime().GetTime() if t_E - t_now >= min_proxy_lifetime: log.debug('Proxy is still valid for %d h.'%((t_E - t_now)/3600)) return # Make sure the directory exists. proxy_dir = os.path.dirname(proxy_path) if not os.path.exists(proxy_dir): try: log.debug('Creading directory %s.'%proxy_dir) os.makedirs(proxy_dir) except OSError, xc: raise ProxyInitError('Cannot create parent directory %s for ' 'storing X509 proxy: %s'%(proxy_dir, xc)) # FIXME: Can this be done with the Python API? log.debug('Renewing proxy (%s--%s).' % (cred.GetStartTime(), cred.GetEndTime())) if voms: cmd = 'arcproxy -S %s:all -K %s -C %s -P %s' \ % (pipes.quote(voms or 'ops'), pipes.quote(key_path), pipes.quote(cert_path), pipes.quote(proxy_path)) else: cmd = 'arcproxy -K %s -C %s -P %s' \ % (pipes.quote(key_path), pipes.quote(cert_path), pipes.quote(proxy_path)) fh = os.popen(cmd, 'r') output = fh.read() err = fh.close() if err: raise ProxyInitError('Failed to initialize proxy: ' 'arcproxy exited with %d.'%err, details = 'Output from %s:\n%s'%(cmd, output)) class NagiosPluginVomsMixin(object): def __init__(self): ap = self.argparser.add_argument_group('VOMS Proxy Options') ap.add_argument('--user-proxy', dest = 'user_proxy', help = 'Path to a possibly pre-initialized VOMS proxy.') ap.add_argument('--user-cert', dest = 'user_cert', help = 'Certificate to use for obtaining VOMS proxy.') ap.add_argument('--user-key', dest = 'user_key', help = 'Certificate key to use for obtaining VOMS proxy.') ap.add_argument('--voms', dest = 'voms', help = 'VOMS server for Proxy initialization.') ap.add_argument('--min-proxy-lifetime', dest = 'min_proxy_lifetime', # The default should be strictly larger than the time before # jobs are cancelled. We need at least the time between # monitor runs plus grace time. default = 7 * 3600, help = 'The minimum lifetime in seconds of the proxy ' 'certificate before it is renewed.') @property def voms(self): if self.opts.voms: return self.opts.voms if self.config.has_option('gridproxy', 'default_voms'): return self.config.get('gridproxy', 'default_voms') return None def voms_suffixed(self, name): if self.voms: return name + '-' + self.voms else: return name def require_voms_proxy(self): key_path = self.opts.user_key cert_path = self.opts.user_cert proxy_path = self.opts.user_proxy voms = self.voms for section in ['gridproxy.%s'%voms, 'gridproxy']: if not key_path and self.config.has_option(section, 'user_key'): key_path = self.config.get(section, 'user_key') if not cert_path and self.config.has_option(section, 'user_cert'): cert_path = self.config.get(section, 'user_cert') if not proxy_path and self.config.has_option(section, 'user_proxy'): proxy_path = self.config.get(section, 'user_proxy') # If no user key or certificate is given, use an externally # initialized proxy. if not key_path and not cert_path: if proxy_path: os.putenv('X509_USER_PROXY', proxy_path) return os.getenv('X509_USER_PROXY') # Otherwise, initialize a proxy. if not proxy_path: proxy_path = os.path.join(self.tmpdir(), 'proxy-%s.pem'%voms) try: require_voms_proxy( voms = voms, key_path = key_path or '/etc/grid-monitoring/userkey.pem', cert_path = cert_path or '/etc/grid-monitoring/usercert.pem', proxy_path = proxy_path, min_proxy_lifetime = self.opts.min_proxy_lifetime) except ProxyInitError, xc: if xc.details: self.log.error(xc.details) self.nagios_exit(UNKNOWN, str(xc)) return proxy_path nordugrid-arc-nagios-plugins-1.8.4/arcnagios/ldaputils.py0000644000175000002070000000700512015407407024244 0ustar mockbuildmockbuildimport ldap try: from ldap.dn import str2dn, dn2str except ImportError: def str_strip(s): return s.strip() def str2dn(s): def split_attr(s): return tuple(map(str_strip, s.split('=', 1)) + [1]) def split_rdn(s): return map(split_attr, s.split('+')) return map(split_rdn, s.split(',')) def dn2str(dn): def join_attr(tup): return '%s=%s'%(tup[0], tup[1]) def join_rdn(rdn): return '+'.join(map(join_attr, rdn)) return ','.join(map(join_rdn, dn)) class LDAPValidationError(Exception): pass def _ldap_bool(x): x = x.upper() if x == 'TRUE': return True elif x == 'FALSE': return False else: raise ValueError('Invalid boolean %s.'%x) _decoders = { '1.3.6.1.4.1.1466.115.121.1.7': _ldap_bool, # TRUE | FALSE '1.3.6.1.4.1.1466.115.121.1.27': int, } def decoder_for_syntax(syn): return _decoders.get(syn, None) # Cf http://bugzilla.nordugrid.org/show_bug.cgi?id=2693 _ignore_must = set([ '1.3.6.1.4.1.11604.2.1.8.0.1', # Mds-validfrom '1.3.6.1.4.1.11604.2.1.8.0.2', # Mds-validto '1.3.6.1.4.1.11604.2.1.8.1.4.1.0.1',# Mds-Vo-Op-name '1.3.6.1.4.1.11604.2.1.8.2.7.1.0.2',# Mds-Service-protocol ]) # The boolean "in" method on Entry is incomplete or unimplemented in # python-ldap-2.2.0. def Entry_in(key, entry): try: entry[key] return True except KeyError: return False class LDAPObject(object): """This is a read-only representation of an LDAP entry with attribute values decoded according to their schema.""" structural_objectclass = None strip_attribute_prefixes = [] lowercase_all_attributes = False def __init__(self, schema, dn, entry_dict): if not self.structural_objectclass in entry_dict['objectClass']: raise ValueError('The LDAP entry at %s is not of structural object ' 'class %s.'%(dn, self.structural_objectclass)) self.dn = dn entry = ldap.schema.models.Entry(schema, dn, entry_dict) must, attrs = entry.attribute_types() attrs.update(must) for oid, at in attrs.iteritems(): if Entry_in(oid, entry): decode = decoder_for_syntax(at.syntax) if decode is None: dvs = entry[oid] else: dvs = [] for av in entry[oid]: try: dv = decode(av) except (TypeError, ValueError): raise LDAPValidationError( '%s has invalid value %s for attribute %s ' 'of syntax %s.' %(dn, av, at.names[0], at.syntax)) dvs.append(dv) if at.single_value: if len(dvs) == 1: value = dvs[0] else: raise LDAPValidationError( '%s has multiple values for single-valued ' 'attribute %s.'%(dn, at.names[0])) else: value = dvs else: if at.single_value: value = None else: value = [] name = at.names[0] for prefix in self.strip_attribute_prefixes: if name.startswith(prefix): name = name[len(prefix):] name = name.replace('-', '_') if self.lowercase_all_attributes: name = name.lower() setattr(self, name, value) missing = [] for oid, at in must.iteritems(): if not Entry_in(oid, entry) and not oid in _ignore_must: missing.append(at.names[0]) if missing: raise LDAPValidationError( '%s lacks required attribute(s) %s.' %(dn, ', '.join(missing))) def canonical_dn(dn): return dn2str(str2dn(dn)) def is_proper_subdn(subdn, dn): subdn = canonical_dn(subdn) dn = canonical_dn(dn) return subdn != dn and subdn.endswith(dn) def is_immediate_subdn(subdn, dn): subdn_comps = str2dn(subdn) dn_comps = str2dn(dn) return len(subdn_comps) > 0 and subdn_comps[1:] == dn_comps nordugrid-arc-nagios-plugins-1.8.4/doc/0000755000175000002070000000000012600623616020470 5ustar mockbuildmockbuildnordugrid-arc-nagios-plugins-1.8.4/doc/gridstorage.rst0000644000175000002070000000516711736331746023556 0ustar mockbuildmockbuild******************** Grid Storage Probe ******************** The ``check_gridstorage`` probe can check file operations against grid storage protocols, including SRM, GridFTP, LFC, and other protocols supported by ``arccp`` and ``arcls``. The main configuration section of this probe is ``gridstorage``, see :ref:`configuration-files`. This probe requires an X509 proxy, see :ref:`x509-proxy`. Basic Invocation ---------------- To perform read-only checks against a URL pointing to an existing file, use:: check_gridstorage --url To perform read-write checks against an URL, use either:: check_gridstorage --url --enable-write check_gridstorage -H --dir-url --enable-write In the latter case, the probe will add a file name based on the host name and a time stamp. The ``--dir-url=`` option will not work correctly with the LFC protocol, since the file name needs to be encoded inside the URL. Performed Checks ---------------- The probe will do the following checks: * If writing is enabled, a small is generated at copied to the provided URL. The contents of the file includes the time and host name passed to ``-H`` or "``localhost``". * The URL is listed and it's checked that the listing contains the name of the uploaded file. * The file is read back. * If writing is enabled, the content is compared to what was written. Since the content contains as host name and a time stamp, it's unlikely that an old file is accidentally matched. * If writing is enabled, the file is deleted. Any failure in the above checks will return CRITICAL to Nagios. Additional Options ------------------ If you wish to do a more thorough list-test, you can ask the probe to list the whole directory containing the test file. This is done by passing ``--list-dir``. This will use ``--dir-url=`` if specified, otherwise it will guess the URL of the containing directory by stripping off the last component of ``--url=``. In any case, the listed URL must contain the test-file. Be aware that if the directory contains many entries, the probe may time out. You can disable the read or list tests with ``--disable-read`` and ``--disable-list``. LFC Invocation -------------- LFC tests are special in that write operations associate a logical path with a physical storage. The probe therefore allows passing an URL to be used only for the write operation, so the write-enabled test should look something like :: check_gridstorage \ --enable-write \ --write-url=lfc://@/ \ --url=lfc:/// \ [--list-dir] nordugrid-arc-nagios-plugins-1.8.4/doc/Makefile0000644000175000002070000001107211656763136022145 0ustar mockbuildmockbuild# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/NorduGridARCNagiosPlugins.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/NorduGridARCNagiosPlugins.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/NorduGridARCNagiosPlugins" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/NorduGridARCNagiosPlugins" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." make -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." nordugrid-arc-nagios-plugins-1.8.4/doc/sample_config.rst0000644000175000002070000000132512307612211024022 0ustar mockbuildmockbuild************************* Example Configuration ************************* .. _example-ini: Probe Configuration =================== This is not meant to be used as is. You will need to uncomment and edit as needed. .. literalinclude:: arcnagios.ini.example :language: ini Nagios Configuration for ``check_arcce_*`` ========================================== This configuration is not meant to be used as is. It is an example which illustrates how to use the entry points of the ``check_arcce_*`` probes and define the associated passive services. Other probes are omitted here, as they are configured as independent services similar to commonly available Nagios probes. .. literalinclude:: services.cfg.example nordugrid-arc-nagios-plugins-1.8.4/doc/arcnagios.ini.example0000644000175000002070000001367012312612073024573 0ustar mockbuildmockbuild# Grid Proxy Certificate and VOMS Attributes # ========================================== [gridproxy] # The default VOMS to use. You can override this for specific probes by # setting "voms" under the corresponding section. #default_voms = ops # Alternative 1: Use an externally generated proxy certificate. You can either # export X509_USER_PROXY or point to it with #user_proxy = /var/cache/nagios/gridproxy.pem # Alternative 2: Let the probe generate a proxy certificate on demand from # a robot certificate. #user_cert = /etc/grid-security/robotcert.pem #user_key = /etc/grid-security/robotkey.pem # Checking Storage Elements # ========================= [gridstorage] # Base directory where to store temporary files and runtime state information. #arcnagios_spooldir = /var/spool/arc/nagios # The ARC commands will store some files under $HOME/.arc/. Since the home # directory may not be set to something usable, set an appropriate value here # to instruct the Nagios plugins to override $HOME at startup. #home_dir = /var/spool/arc/nagios # The log-level to use for this probe. In valid values in order of # decreasing verbosity are DEBUG, INFO, WARNING, ERROR, CRITICAL, FATAL. #loglevel = WARNING # Checking Compute Elements: Information System # ============================================= [arcinfosys] # Same as for [gridstorage]. #arcnagios_spooldir = /var/spool/arc/nagios #home_dir = /var/spool/arc/nagios # The log-level for this probe as described under [gridstorage]. # It may be useful to set this to INFO. #loglevel = WARNING # The glue2 entry point # --------------------- # # These are also provided as command-line options. # Use this GLUE2 schema instead of querying the CE. #glue2_schema = # Warn if there are no objects of these classes. #warn_if_missing = GLUE2AdminDomain,GLUE2Service,GLUE2Endpoint # Report critical status if there are no object of these classes. #critical_if_missing = # A comma-separating list of foreign key attribute types which should be # reflected in the DIT. #hierarchical_foreign_keys = # Require that all foreign keys which represent aggregation or composition # are reflected in the DIT. #hierarchical_aggregates = # The aris and egiis entry points # ------------------------------- # # Use the command-line options. # Example tests for the aris entry point # -------------------------------------- # Usage: --cluster-test=cache_total [arcinfosys.aris.cache_free] type = limit value = float(cache_free)/cache_total critical.min = 0.01 warning.min = 0.1 # Usage: --cluster-test=topphys [arcinfosys.aris.topphys] type = regex variable = runtimeenvironment critical.pattern = APPS/HEP/ATLAS-TOPPHYS critical.message = Missing TOPPHYS. # Usage: --queue-test=queue-active [arcinfosys.aris.queue-active] type = regex variable = status critical.pattern = ^active$ critical.message = Inactive queue # Checking Compute Elements: Job Submission # ========================================= [arcce] # Same as for [gridstorage]. #arcnagios_spooldir = /var/spool/arc/nagios #home_dir = /var/spool/arc/nagios # The log-level for this probe as described under [gridstorage]. #loglevel = WARNING [arcce.connection_urls] # This section can be used to force specific flavours and connection URLs for # individual CEs. Each line takes the form # # ce.example.org = FLAVOUR:URL # # where the right hand side corresponds to the -c argument of arcsub(1). # Example Scripted Job Tests # -------------------------- # # These checks are enabled by passing "--test NAME" to the submit command, # where NAME is the section name without the "arcce." prefix. They injects # pieces of shell to to the remote script and checks the output using # regular expression patterns. [arcce.python] jobplugin = scripted required_programs = python script_line = python -V >python.out 2>&1 output_file = python.out output_pattern = Python\s+(?P\S+) status_ok = Found Python version %(version)s. status_critical = Python version not found in output. service_description = ARCCE Python version [arcce.perl] jobplugin = scripted required_programs = perl script_line = perl -v >perl.out 2>&1 output_file = perl.out output_pattern = This is perl, v(?P\S+) status_ok = Found Perl version %(version)s. status_critical = Perl version not found in output. service_description = ARCCE Perl version [arcce.gcc] jobplugin = scripted required_programs = gcc script_line = gcc -v >gcc.out 2>&1 output_file = gcc.out output_pattern = gcc version (?P\S+) status_ok = Found GCC version %(version)s. status_critical = GCC version not found in output. service_description = ARCCE GCC version [arcce.csh] jobplugin = scripted required_programs = csh script_line = echo >csh-test.csh '#! /bin/csh'; echo >>csh-test.csh 'env >csh.out'; chmod +x csh-test.csh; ./csh-test.csh output_file = csh.out output_pattern = ^PATH= status_ok = Found working csh. status_critical = Did not find $PATH in csh environment. service_description = ARCCE csh usability # Example Storage Job Checks # -------------------------- # # These check are also enabled by passing the second componest of the # section name to the --test option. This will add the specified staging to # the job description. Input files will must exist in advance. Output # files will be removed after checking that they exist. [arcce.stage_srm] jobplugin = staging staged_inputs = srm://srm.example.org/somedir/testfile staged_outputs = srm://srm.example.org/somedir/srm-%(hostname)s-%(epoch_time)s service_description = ARCCE SRM Result [arcce.stage_gridftp] jobplugin = staging staged_inputs = gsiftp://srm.example.org/somedir/testfile staged_outputs = gsiftp://srm.example.org/somedir/gsiftp-%(hostname)s-%(epoch_time)s service_description = ARCCE GridFTP Result [arcce.stage_lfc] jobplugin = staging staged_inputs = lfc://lfc.example.org/lfcdir/testfile-lfc staged_outputs = lfc://srm://srm.example.org/somedir/lfc-%(hostname)s-%(epoch_time)s@lfc.example.org/lfcdir/lfc-%(hostname)s-%(epoch_time)s service_description = ARCCE LFC Result nordugrid-arc-nagios-plugins-1.8.4/doc/arcinfosys.rst0000644000175000002070000002502312471105533023403 0ustar mockbuildmockbuild***************************************** Monitoring the ARC Information System ***************************************** The main configuration section for these probes is ``arcinfosys``, see :ref:`configuration-files`. EGIIS Check =========== To monitor an EGIIS service, use :: check_egiis -H [-P ] --index= This will do an LDAP query of the EGIIS service on ``:``. The default port is 2135. The base DN of the query is ``Mds-Vo-name=, o=grid``. The probe will also fetch the subschema at ``cn=subschema`` and check the presence of attributes against MAY and MUST specifications in the schema. In addition some type conversions are attempted to catch invalid data. Any validation error will give a CRITICAL Nagios status. If the index is empty, a WARNING Nagios status is reported. Otherwise, the status is OK and counts for different registrations states is printed. CE Health State using EMIES =========================== The following probe contacts the EMIES service of the compute element and checks the ``HealtStatus`` element in the reply. check_arcservice -u [-k -c ] [-t ] ``arcinfo -c `` shows whether a CE supports EMIES and which URL to use. EMIES uses SSL client authentication. By default the host certificate is used. To use a grid proxy, pass it as both key and certificate. Example: check_arcservice -u https://arcce.example.org:443/arex \ -k /tmp/x509up_1000 -c /tmp/x509up_1000 CE Infosys Validation for the GLUE 2 LDAP Schema ================================================ You can test the GLUE 2 LDAP records published by an CE with :: check_arcglue2 -H [-P ] \ [--glue2-schema PATH] [--if-dependent-schema STATUS] \ [--warn-if-missing OBJECTCLASS,...,OBJECTCLASS] \ [--critical-if-missing OBJECTCLASS,...,OBJECTCLASS] \ [--hierarchical-foreign-keys FOREIGN-KEY,...,FOREIGN-KEY] \ [--hierarchical-aggregates] See ``check_arcglue2 --help`` for a full list of options. This probe will do a full query under ``o=glue`` on the provided host and port and perform the following checks. The default port is 2135. As a basic check that the information system contains data, ``--warn-if-missing`` and ``--critical-if-missing`` may be passed a comma-separated list of LDAP objectclasses for which there should be at least one entry in the information system. By default, a warning is raised if the system has no entries of type ``GLUE2AdminDomain``, ``GLUE2Service``, or ``GLUE2Endpoint``. The probe will verify each entry using the GLUE 2 LDAP schema. By default, the GLUE 2 schema is expected at ``/etc/ldap/schema/GLUE20.schema``. An alternative path may be specified with the ``--glue2-schema`` option. If the schema is not found, a warning is raised and the schema is fetched from ``cn=subschema``. The rationale behind this warning is that the content should be checked independent of what the remote end claims it should be. Another Nagios status can be specified with ``--if-dependent-schema``, including ``OK`` to disable the warning. As GLUE 2 is relational in nature, the probe does further checks on connections which cannot be specified in the LDAP schema. It checks uniqueness of the ``*ID`` attributes, and the outgoing and incoming multiplicities of ``*ForeignKey`` attributes as specified in the GLUE Specification v2.0 [GLUE2]_ and the LDAP schema reference implementation [GLUE2L]_. Further, the probe checks some of the constraints on the directory information tree (DIT) [GLUE2L]_. A critical condition is raised if the following conditions are not met. * All ``GLUE2Extension`` objects must appear immediately below the object they extend. * Objects which are aggregates of a ``GLUE2Service`` must appear somewhere below that service. * Services which link to a ``GLUE2AdminDomains`` cannot reside under a different domain. Optionally you can require the DIT to reflect additional foreign keys, either passing an explicit list to ``--hierarchical-foreign-keys``, or passing ``--hierarchical-aggregates`` to include all keys which represent aggregation or composition. Note that the latter will fail unless services are structured under their administrative domain, if any. CE Infosys Validation for the NorduGrid and GLUE 1 Schemas ========================================================== The ARIS probe is invoked with :: check_aris -H [-P ] [--cluster ...] \ [--cluster-test ...] [--queue-test ...] \ [OTHER-OPTIONS...] See ``check_aris --help`` for the full list of options. It will query ``Mds-Vo-name=local, o=grid`` on ``:``. The default port is 2135. If one or more clusters are specified with the ``--cluster`` option, only those will be checked (``nordugrid-cluster-name=``), and it is considered error for any of them to be missing. The probe validates attributes of entries against MAY and MUST of the schema, and attempts some type conversions. For each found cluster, the probe will query and validate queues. If no clusters are found, or if no queues are found for a given cluster, it will be reported as a warning. You can change this by passing a Nagios status to the option ``--if-no-clusters`` or ``--if-no-queues``, respectively. Valid statuses are ``ok``, ``warning``, ``critical``, and ``unknown``, though only the first three makes sense here. This probe can also do custom checks on the LDAP data, either numeric limits or regular-expression matches. A custom test defined in the configuration file under a section ``arcinfosys.aris.``, can be enabled by passing any number of ``--cluster-test `` and ``--queue-test `` options to the probe. The tests are run on entries of the type ``nordugrid-cluster`` and ``nordugrid-queue``, respectively. The ARIS infosystem contains a attribute ``nordugrid-cluster-contactstring`` which provides the interface for job submission. You can check that this URL is accessible by passing ``--check-contact``. This will do a list operation and, if the logging level is ``INFO`` or lower, will report the number of entries. If the attribute is missing or the URL is inaccessible, the service goes CRITICAL with an appropriate message. Limit Checks ------------ A limit check takes the form .. code-block:: ini [arcinfosys.aris.] type = limit value = critical.min = critical.max = critical.message = warning.min = warning.max = warning.message = The ``type`` and ``value`` variables are required, and at least one of the ``min`` or one of the ``max`` variables should be given for the test to be useful. There are reasonable defaults for the messages, though if your ```` is complex, you may want to provide a more human readable version. The probe will * Evaluate ```` using Python's `eval` function, in an environment based on the LDAP attribute names to the corresponding converted values. The variable names are obtained from the attribute names by replacing "``-``" with "``_``" and stripping common prefixes including "``nordugrid-cluster-``", "``nordugrid-queue-``", and "``Mds-``". * If ``critical.min`` is given and the result is below this value, or if ``critical.max`` is given and the result is above this value, report it as a critical error. * Similar for ``warning.min`` and ``warning.max``, reported as a warning. Regular Expression Checks ------------------------- A regular expression check takes the form: .. code-block:: ini [arcinfosys.aris.] type = regex variable = critical.pattern = critical.message = warning.pattern = warning.message = The ``type`` and ``variable`` settings are required, and you should specify at least on of ``critical.pattern`` and ``warning.pattern``. The variable name is obtained the same way as for the limit checks. The probe will consider all values for the LDAP attribute corresponding to ````. * If ``critical.pattern`` is specified and none of the values match it, then a critical condition is reported, else * if ``warning.pattern`` is specified and none of the values match it, then a warning is reported. The following example will issue a critical state if a queue is not active: .. code-block:: ini [arcinfosys.aris.queue-active] type = regex variable = status critical.pattern = ^active$ critical.message = Inactive queue Glue Schema Checks ------------------ Some CEs publish cluster and queue information in the Glue schema in addition to the NorduGrid schema. You can enable schema checks for these if present by passing ``--enable-glue``. The information in the Glue entries should match information in the ARC entries as described in [ARCIS2011]_. You can enable a partial comparison of GlueCE, GlueCluster, and GlueSubCluster records by passing ``--compare-glue``. Checking Expiration of Host Certificates ======================================== A separate probe is provided for checking the host certificate as reported by the information system:: check_archostcert -H [-p ] \ [-c ] [-w ] [-t ] The suggestion is to run this for each compute element on a low frequency, like once or a few times a day. A command definition like :: define command { command_name check_archostcert command_line $USER$/check_archostcert -H $HOSTNAME$ -c 7 -w 31 } will warn about a certificate one month before it expires and report a critical status one week before. The port number defaults to 2135, but can be changed with ``-p ``, and a timeout of ```` seconds is specified as ``-t ``. Se also ``check_archostcert --help``. The lifetime of the host certificate can also be checked using a generic HTTPS probe against the EMIES service, as long as the probe supports client authentication and lifetime checks. .. [GLUE2] "GLUE Specification v2.0"; Sergio Andreozzi (ed.), et al.; http://www.ogf.org/documents/GFD.147.pdf .. [GLUE2L] "GLUE v. 2.0 – Reference Implementation of an LDAP Schema" Sergio Andreozzi (ed.), et al.; https://forge.ogf.org/sf/docman/do/downloadDocument/projects.glue-wg/docman.root.drafts/doc15526 .. [ARCIS2011] "The NorduGrid-ARC Information System"; Balázs Kónya and Daniel Johansson; NORDUGRID-TECH-4; http://www.nordugrid.org/documents/arc_infosys.pdf nordugrid-arc-nagios-plugins-1.8.4/doc/index.rst0000644000175000002070000000066612127630630022337 0ustar mockbuildmockbuild.. NorduGrid ARC Nagios Plugins documentation master file, created by sphinx-quickstart on Thu Nov 10 14:34:24 2011. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. NorduGrid ARC Nagios Plugins ============================ NORDUGRID-MANUAL-22-HTML .. toctree:: :maxdepth: 2 intro.rst arcinfosys.rst arcce.rst gridstorage.rst sample_config.rst nordugrid-arc-nagios-plugins-1.8.4/doc/services.cfg.example0000644000175000002070000001225412307612211024423 0ustar mockbuildmockbuild# -------------------------------------------------------------------------- # This is an example Nagios configuration for the ARC-CE probes meant for # documenation purposes. It cannot be used as-is. # -------------------------------------------------------------------------- # Contacts and Contact Groups # =========================== # You probably already have contacts defined in your Nagios configuration, so # you can skip these and substitute your own below. define contactgroup { contactgroup_name nagios-operators members jdoe } define contact { use generic-contact contact_name jdoe email jdoe@example.org } # Commands Definitions # ==================== # This is a dummy command for passive services. You may already have something # like it in your Nagios configuration. define command { command_name check_passive command_line /bin/true } # This command monitors running jobs and collects those which have teminated, # reporting passive results. define command { command_name check_arcce_monitor command_line $USER1$/check_arcce_monitor -H $HOSTNAME$ } # A job submission check including sub-tests which are defined in the plugin # configuration in separate sections. The results of the sub-tests will be # passively reported to the service names defined in the same configuration. define command { command_name check_arcce_submit command_line $USER1$/check_arcce_submit -H $HOSTNAME$ \ --test python --test perl --test csh --test gcc } # A job submission check with staging. The arguments to --stage-input options # must exist. The arguments to --stage-output options will be overwritten, and # deleted on termination. This command is not used below. To use it, add an # active service and a passive service named "ARCCE SRM Job Termination". define command { command_name check_arcce_submit_staging # Passed explicitly: # command_line $USER1$/check_arcce_submit \ # -H $HOSTNAME$ --job-tag srm \ # --termination-service 'ARCCE SRM Job Termination' \ # --stage-input srm.txt=srm://srm.example.org/nagios/readable.txt \ # --stage-output srm://srm.example.org/nagios/srm-$HOSTNAME$-$TIMET$.txt \ # Using a predefined job-test: # command_line $USER1$/check_arcce_submit \ # -H $HOSTNAME$ --job-tag srm \ # --termination-service 'ARCCE SRM Job Termination' \ # --test stage_srm } # Host Groups and Host Templates # ============================== # You need to have one host definitions to which the monitoring service is # assigned to. This is typically the Nagios host itself, for which you # probably already have a definition. define host { name nagios-host use generic-host max_check_attempts 10 contact_groups nagios-operators register 0 } # The following host group and template will be used for all CEs. define hostgroup { hostgroup_name arcce-hosts alias ARCCE Hosts } define host { name arcce-host use generic-host max_check_attempts 10 contact_groups nagios-operators hostgroups arcce-hosts register 0 } # Service Groups and Service Templates # ==================================== define servicegroup { servicegroup_name arcce-services alias ARCCE Services } define service { name arcce-service use generic-service servicegroups arcce-services check_period 24x7 max_check_attempts 3 flap_detection_enabled 0 contact_groups nagios-operators notifications_enabled 0 register 0 } define service { name arcce-monitoring-service use arcce-service normal_check_interval 5 retry_check_interval 5 register 0 } define service { name arcce-submission-service use arcce-service normal_check_interval 30 retry_check_interval 30 register 0 } define service { name arcce-passive-service use arcce-service active_checks_enabled 0 passive_checks_enabled 1 check_command check_passive register 0 } define service { use arcce-monitoring-service host_name localhost service_description ARCCE Monitoring check_command check_arcce_monitor } # For each ARC CE, we need one active service for submission and a number of # passive services to collect the results. In the following we associate the # per-CE services to the "arcce-hosts" group, which will add them to all # members of the group. define service { use arcce-submission-service service_description ARCCE Job Submission hostgroup_name arcce-hosts check_command check_arcce_submit } define service { use arcce-passive-service service_description ARCCE Job Termination hostgroup_name arcce-hosts } define service { use arcce-passive-service service_description ARCCE Python version hostgroup_name arcce-hosts } define service { use arcce-passive-service service_description ARCCE Perl version hostgroup_name arcce-hosts } define service { use arcce-passive-service service_description ARCCE GCC version hostgroup_name arcce-hosts } define service { use arcce-passive-service service_description ARCCE csh usability hostgroup_name arcce-hosts } # Hosts # ===== # This provides the monitoring service. define host { use nagios-host host_name localhost } # Any host which use the arcce-host template will get an active submission # service, and all the related passive services. #define host { # use arcce-host # host_name ce-00.example.org #} nordugrid-arc-nagios-plugins-1.8.4/doc/intro.rst0000644000175000002070000001536012312612073022355 0ustar mockbuildmockbuild**************** Introduction **************** This document describes the Nagios plugins mainly used to monitor NorduGrid ARC compute elements and related resources, but some probes should also be usable to test non-ARC resources. The package includes commands to do * LDAP queries and tests on the information system, including GLUE 2.0 and legacy schemas. * Job submission and monitoring of jobs with additional custom checks. * Transfers to and from storage elements using various protocols. The following chapters will cover the probes related to each of these topics. This chapter will describe common configuration and options. **Acknowledgements.** This work is co-funded by the EC EMI project under the FP7 Collaborative Projects Grant Agreement Nr. INFSO-RI-261611. .. _configuration-files: Configuration Files =================== The configuration is merged from a list of the INI-format files, where settings from later files take precedence. By default files matching ``/etc/nagios/*.ini`` are read in lexicographical order, but this can be overridden by setting ``$ARCNAGIOS_CONFIG`` to a colon-separated list of the files to load. A naming scheme like the following is suggested:: /etc/arc/nagios/20-dist.ini - comes with the default package /etc/arc/nagios/60-egi.ini - comes with the EGI package /etc/arc/nagios/90-local.ini - suggested for local changes An alternative to ``/etc/arc/nagios`` can be specified in the environment variable ``$ARCNAGIOS_CONFIG_DIR``. Under the same prefix, a default job script template is installed:: /etc/arc/nagios/20-dist.d/default.jsdl You can provide a modified script by placing it e.g. in ``/etc/arc/nagios/90-dist.d/default.jsdl``, but be careful with this in production environment since later versions of the probes may require changes to the script which makes the modified version incompatible. Each probe has a main configuration section named after the probe or colloquially ``[arcce]`` for the ``check_arcce_*`` probes. In this section you can provide defaults for string-valued command-line options. The name of the configuration variable corresponding to an option is obtained by stripping the initial "``--``" and replacing "``-``" with "``_``", e.g. "``--home-dir``" becomes "``home_dir``". Common Options ============== The following options are common to most of the probes: ``--home-dir=`` Override $HOME at startup. This is a workaround for external commands which store things under $HOME on systems where the user account running Nagios does not have an appropriate or writable home directory. ``--loglevel=(debug|info|warning|error)`` This option allows you to increase the verbosity of the Nagios probes. Additional messages will occur as extended status lines in Nagios. ``--multiline-separator=`` Replacement for newlines when submitting multi-line results to passive services. Pass the empty string drop extra lines. This option exists because Nagios currently don't support multi-line passive results. ``--command-file=`` The path of the Nagios command file. By default $NAGIOS_COMMANDFILE is used, which is usually the right thing. ``--how-invoked=(nagios|manual)``, ``--dump-options`` These are only needed for debugging purposes. ``--arcnagios-spooldir`` Top level directory for storing state information and for use as a working area. The default is ``/var/spool/arc/nagios``. If you need to debug an issue related to CE jobs, look under the ``ce-*`` subdirectories. .. _x509-proxy: Proxy Certificate ================= The ``check_arcce_*`` and ``check_gridstorage`` probes will require a proxy certificate to succeed. The probes will maintain a proxy when provided a X509 certificate and key. You can place these in a common section: .. code-block:: ini [gridproxy] default_voms = user_key = user_cert = #user_proxy = # Optionally override the path of the generated proxy. The probes which require an X509 proxy have a ``--voms=`` option to specify the VOMS server to contact instead of ``default_voms``. When a ``user_key`` and ``user_cert`` pair is given, the default ``user_proxy`` path is unique to the selected VOMS. To use a pre-initialized proxy, make sure ``user_key`` and ``user_cert`` are not set. You will probably want to use a non-default location for the proxy. Either point to it with the environment variable ``X509_USER_PROXY`` or set it in the configuration file: .. code-block:: ini [gridproxy] user_proxy = If you use several VOs with require different certificates, you can replace the above section with one section ``gridproxy.`` per ```` and use the ``--voms`` option to select which section to use. These sections don't have the ``default_voms`` setting. Running Probes from the Command-Line ==================================== The following instructions apply to ``check_arcce_submit``, ``check_arcce_monitor``, ``check_arcce_clean``, ``check_aris``, ``check_egiis``, ``check_arcglue2``, and ``check_gridstorage``. The other probes can be invoked from the command-line without special attention. For testing and debugging, it can be convenient to invoke the probes manually as a regular user. This can be done as follows. Choose a directory where you can store run-time state. Below, we use ``/tmp``, but it may be tidier to create a fresh directory. Then, create a configuration like .. code-block:: ini [DEFAULT] arcnagios_spooldir = /tmp/arc-nagios-testing [gridproxy] default_voms = [gridproxy.your-vo] user_proxy = /tmp/x509up_u substituting suitable values for the ```` meta-variables. You may need to add additional settings depending on want you test, of course. Acquire a proxy certificate (if needed) and pointing to the set of configurations you need, including the above: .. code-block:: sh arcproxy -S export ARCNAGIOS_CONFIG=/etc/arc/nagios/20-dist.ini: The probes can now be run as .. code-block:: sh check_arcce_submit --how-invoked=manual ... check_arcce_monitor --how-invoked=manual ... check_arcce_clean --how-invoked=manual ... check_egiis --how-invoked=manual ... check_aris --how-invoked=manual ... check_arcglue2 --how-invoked=manual ... The main purpose of the ``--how-invoked=manual`` is to tell the probe that any passives results shall be printed to the screen rather than submitted to the Nagios command pipe. It is not strictly needed for active-only probes. Deprecated Probes ================= The following probe is deprecated. It will be removed in a future release. * ``check_arcinfosys`` is obsoleted by ``check_aris``, ``check_arcglue2``, and ``check_egiis``. nordugrid-arc-nagios-plugins-1.8.4/doc/conf.py0000644000175000002070000001634111736331752022002 0ustar mockbuildmockbuild# -*- coding: utf-8 -*- # # NorduGrid ARC Nagios Plugins documentation build configuration file, created by # sphinx-quickstart on Thu Nov 10 14:34:24 2011. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath('..')) # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'NorduGrid ARC Nagios Plugins' copyright = u'2011, Petter Urkedal' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The full version, including alpha/beta/rc tags. fh = open('../VERSION', 'r') release = fh.read().strip() fh.close() # The short X.Y version. version = '.'.join(release.split('.')[0:2]) # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. html_logo = 'media/EMI_Logo_std_36dpi.png' # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'NorduGridARCNagiosPluginsdoc' # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). latex_paper_size = 'a4' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'NorduGridARCNagiosPlugins.tex', u'NorduGrid ARC Nagios Plugins Documentation', u'Petter Urkedal', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. latex_logo = 'media/EMI_Logo_std_150dpi_cropped.png' # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True latex_elements = { 'preamble': '\\input{../../ng-preamble.tex}\n', 'maketitle': '\\input{../../ng-titlepage.tex}\n', } # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'nordugridarcnagiosplugins', u'NorduGrid ARC Nagios Plugins Documentation', [u'Petter Urkedal'], 1) ] nordugrid-arc-nagios-plugins-1.8.4/doc/media/0000755000175000002070000000000012600623616021547 5ustar mockbuildmockbuildnordugrid-arc-nagios-plugins-1.8.4/doc/media/EMI_Logo_std_36dpi.png0000644000175000002070000001717511734570761025552 0ustar mockbuildmockbuildPNG  IHDRRbsRGB pHYsmhtIME(i#3dIDATx]w\ݣDEEF'QQc5b(.Ɔ!"^rp\c80`O>Λ}3fo1(IIARJRIIJ0)I &%)I &%)$%KvcS F2!nRl~aat`-a=(p9@Q!a>}dmMUaQ&:X>㝈l`n#J@J{ٻy.㘂.dVjlQM#ZGbx;AUǗ"Ӭ 4w1nŖS F'׬=Uzۛiki# HW=Ja L|EZ6ל6ع'<(4n_ jڠڣ j*#{~?Hc 95g҈Gd/ mKmlmqmXZ$`n2EHv &ASh]X>VECNq)L'XәiN?yt˹|Ά əC{l::}#/Ҙ؆FWLj'zE%o=Rnb\#giY3)!2Ov#B^.=$9j`@S|KXs+>KZ/&U*7~3r>bNPdAҶ?L~/8DwsO߮zډ%ߗs&z*p#,C~%]bbn\UV]9?osgZhNkM 5՟N<!}uƷo믪xU偉;2kS+g_h W7G `{ ŧd&f\01k'|qE#njݗVXhIZD-KXI`'y^0X_ĝ'o&궋Q֡œoR jZ;ڽO?'Y~ynݜov`?Hxt"LZbRr/=GGSA"ar2q netxQR`PΪ^ŧoX U勚Wyr$&A&RB>}VSat2**3"MQ@PPZ$ZJXSˑ/R$^B >֘ ]tUjTI t5Uh!(ը |~ݎ)%0CS|~ni$ozavi( Xڀ]Z(صð7=~[][]$O[ 9c,u&g3 {4C]C9[UV}(lJzpC 0[WR(#jCD@kdXk>Kq!/5ת;{Z9}AAw…FѵӲ/$-M -M, L L4ӡۤUX_SXRȬ,-g9\PW^BN5M yR\EIH j x˓WY S  &uHœƪ|҃1 EN,e,H穫ikingifgeڭEijvBzvjnIZEW)2 EIL7:L 㧨&`LƖPgHÿ:PH?~26W"FƅF Y,䌺 ǬL:Z9[;wţ)Wp\ҼE&i(D_ejMHk3_Fb& ޻Hwc,s WA뤴VxEEac@MMmO'|Frm%RIE E+5fj`"X&$ί BGv<y{Q~=UjC!1CbjAqو}dUT,9;H *($V661/ L'QHNmZz=BA!bޒDnf`q$ya*""[ޒ&c͌}')I=f"L$vpX0NyVM'.;2}ښX1$)3й%Um|@,'I [&$-,DbÓמӗfɽ ri7?5RUWjnk;~IxU%sp8\,.vI㇆@=\l, v`ݳ[*fEe{dWI 7jތd(Fjj=(!Q6<95:!P* wp=%>Fz߷F238îqd+O]lIr hKz&kAPK]=]s˄B:>CLjHϼC_ #p Ɍ&3nҳ3;`;Q\V KgN0>AP12k5#TQ&}]Rln٭_E&z t)(k$iim9{Hn.N=$"2 wrxO4k+? &]̣KfO5e %` y]ҿyAtB̬҇œ2&tz~scC2teh``jl޵5{GDHI*49cJZ%>&f~M'eΨW&ޱ(r(<潥I ]:﫫I )9qeEe̢RfEYYUUU#F $V@Bz > # Ia+$tL @H&pp-S$$'$k`O0~r]MNLN隷ggcakeaomnkmѧaJ$YR^$-Oȟ'/.-+WHf \2,L#Ɩ+}wTf,V{V6&Fv]l; smmflmfbmfҴ˷ % Rai1kC@0Ĥ?זVYUƬHˢƪU{V~^mMEkHEc@@ Y& }l$pFHxhԻ91$:9pIgus:fal4fx}ˆ%õ'0T ҲE.0`×8"Qkb󿒣wmVy+145gsEePkyi|I+ ˷x34`M$rY~\Ujϫ@ 5LvrJF $0V ŗ2fb0>N!- _NN;LKqsfV:#˗Ltx7׊r4Ǜ?=fb"[+Muu~!'(Ƕa} 8`q^n 7{as7xefukH@~l7I_OG~s#U"6z9XKRSUes|IK3L#6 vci\Mb;OWLpzc<] L7ĭ{sܽ~%Vfnwtâ?@GG $WZ/%)N__aovG'dJt e5tKg[l`eG+%N$;azv(y'=+:SGO,w7C[|L!i[ P!R*7ڭh7k$ FZn蟕k ^VLؚZ:mO9|)wKt2KR^]li&mͪwsb ~5c)›#&9c=]Ӵy\ҹ㣛%= v#5v Zb[%^?XΪ25>s٥GQ"u$YPĦpIA?0CoWR~It2)+#=CHM i0W c>L)&W.s4- $-1黹g1v)&15L xmXG#}ݤ'QID'7B2:ݸ>W}'0;#w/!^mc`mQĬ`arK-U]޻Z ǥJQ]'zKʴ.unkY%k׃SEUg;5[]E`[79,ðj V~C]6̙(-86/%k3)_4ZnJ\jԍ't$z{ьπr㘼F,E3rwBxl.p9oL#Ϊѵ%?Q43"yeYl1"<|tAs0"fӪ+%O0-V+מ [A&3SWgE-M#bwT:$la^QxeqaA |̩SA>|P\qD_WO[ d5[Ȭ  P;ws6a-rs":jvBVAdb!jUVlo' 3UP3(xݬpx/ͧ4;܊܊OMC71EC=jJƬiu\xxX|RX|R.vuCmnh[1p^؜~pâ}.}ep$_` E߲9eŶP Hr _%۵ZC] (2`aGK.t+u=; fpy>|>o;m-o ڱjf7.Yhc0:PEІ"hkj:u ~\\œ{>qPa.:Z^2ѷ2۰j~]&Ѹa{wd%~W:z@ |c5p֨j̠͟H40xD3Wx#q@C8M.p}-uWgGWgG }MiWog684_p=]u/y0D #CmbJHX$_ lL=t4>{u莶&ΎNڥezoxu亅Ӗvlz̛ X2e)GL,3Sg$]NQ_[c>:wHGKr s KD-ϚH4|3JˇuqWT^{ E^5ˉ =3jٿw5'lYIލ=vlg=wP3eL|'[kfeƏP8ʹ$k@h:ZZ _8O|EC7i@@nzsNcCu^^ u?P׬;.~9v;=:ٳgI*&MEQ-X4e"Ҧ]&)JRf`RLJRIIJRIIJ0)I &%)$%)[H&IENDB`nordugrid-arc-nagios-plugins-1.8.4/doc/media/EMI_Logo_std_150dpi_cropped.png0000644000175000002070000010765211734570761027343 0ustar mockbuildmockbuildPNG  IHDR8 F[sRGBbKGD pHYsgRtIME4u[SZ IDATx]wxE{7 """( bA`! bAE jAHM4{e?.wd~ϓݝiyy 9#bw3I,]Nx<䊞گ~%R W2h1n$emXLzlP3Ce}%ԺmBdZ?9->Ѧm1J >G^= 5>EUTXzU$=fҨ@*UP^oImN]‡%ĶQo,^ߠxP| /# |')-0ܢRl')2m| /MN°~͢6ɆH6}M]F NhʐPQm³9 apWh CRblvoֺqֆ;7n{i&I!M;.%;6`dZ[^%k`vDn3>Z::Ѕ'1Ȣ:/~t/Lh*qg?<&D) *J/QD[x U.O}˾n6{KvpAVu?g#CRBD]`Ġo?=;EAai }/ima8MTXўjmܱdݖyM|xhFڴϧt`]LiI+/%6?2th HEK1'*Uى5-X7VQW3tXH)PF8{uhzJ3CX,0F(P$e!ht$ (@mCDњhmI3uϬ_.zxo ޿ *jT9]}>ֽ˅&~(2uC̔Do}o>]THJQA%O=x߭H}ZB,bS2r 6]5Od۠!YEA3dED t(6y myI N\:Rs|gB$( :sI|s@6pD쥫s$,OD%K(+gfM#B=:/v" T~1Sgd+o_ףcZdhHx1̨'I4X .k16F2W6wQw>oOT9>i!)4ANxB[j ؈w?m!+wV:t\MR7:uA1[RD`1&KmtYl;!׆]Xey:R4 EFSGߨjj:5 .kvoM\ѣ /(x϶O.uI KIN ~*g?lνRH$l(A{LuKFR9|vި?W?>ҢhQ>$gTkIQa8~.p9D!q1fI53 }XU5cKtF_x1m(V7j-QH Pf KUiMr5W6otY[O+]lƙZM;'_U@o4I;|K.bJ5\ ѻ*N}Ɓ9kZRUnG>'ԗR3='la%6-^IEހKŀ(@wԍ3Ǐ1`;%-y?>BDhn .rj~K1K]d*WخF eC~sU?2CRG^]oe_\L&²+ ]0Nv$U:&sS# =nF] "0#1x@iU͊-?VE1M)Q6@<6ֶ:5Wq"oϸs5cQd%0\n*Z=_#=]٫Nş@8[4|?^Nl\6ut_t5*Q5cr^]oQȕ"NQ!k 4;h˖ɠmPn O]aʹ:ף%Ņ>E "I@B)3B #>$9(Ufٷ]?A}X5$Ju=JT-6ʛ^l]^E f=j=JSi"]:]{. ]7]w CXW߲o)IN=g A"Wү[fTNj g^F3]({s֣ u`$v2ܽ/-=YPP@(U7kzpؑ^)ޟ\?*ѿ{SWN掃M\\VDJu\ٴ[/}bK*j#Svɘ`)Dރqnwkozt9T`婃.Q2DŽ_9>]7QhqotHׅQ+KG {@^͒nzq:NQ9{2Os&W7Mг AyAkŤ׼?a/%:6IMɜ\yf=$D.|1^倢$Y~/\'!w 7kzg$}tALD~k 3]<#: ʎAna AcA}0GDkc=jȪ^/+_x,f$Y=! (h P(@άSΩ ~tKiߡ__VebM4 5eC &ogy]1M\ۯ ];^a X|X濜+|潏w?%"TX:#9ai̓L}o~_2z?0|#A6UZAߊq9C aO~m LIl ͖]ŕ,Gju iL_%sSZ9W8erԑ#Z,脚gi}z-w#SeN9nMp,mW8RBgk(|Ӷ')lH!,r?wѨyi(<9}#RQ RCt'A1޹/N{cLQCO%id0Z `-_rv2h [S}Ǩuǁ#^-xN>˳RcHT=t8QM}0_֟)$6)6k{_}k8M lS׿ / qmF ,"YmAun3*J/Θlfvo8[Z4z5}{HKm~yDh@PR2T@غ>dPL"Pv_~ 0, v+IZ3nB4SRV|#!=9iC<  ie!C? Npapv~!{:86fV4L 3b;KyӒwǎ[{/g԰jhٺT^KN0P(mv}cNEB&M_' 0ޫ>qDm賎]y'_-^iuqe [<n>vš+b"k9Wgؗ[.]aNJT9>p !an1ΉF?E2"ˎӗwV8 <>=̞楷Kg_ ID( `lUv4r) b9NQv ZWǖvٿf9pA~Tb'¢jC=`3@'Y,pQt N$z+ x*>Wp& QûF\Zw?yA@<_/;(jA=z$|w*bVj̲8 cl2Ń(rz$CŲ[Cc XPBq7\ڧ_Y= Nu2NP0~m|0={ إc#j T!\Hp^O"*ZͅkӒk t¬:V?tTig*KrO y#L&?e jJN['(@= k5KC1= JPߔ^ -!)M7A8Ee)ٍ% (v߯&d ' 1@ ,-"f$0YMS [[:'O[iE=P3͂RdwzD\]PC`WQ ?-24{F?֙I |ϥU5J `6Gгcz Fvq @d#sp]-3^{B.@x}^=شs/@:zz\}YO}6) 7Kwo,h+=Xxo~"I_:g܅jg8u)ާQkx!pkN6OvAH=#`Q8}NVt:NDTy.43FkAD჻d\Sxpx㯊jH?l֥?9/_NmG,|YZ_NΨC2ܬCW<( / D2lk+6E8r 9>P J TH =PQ]3/9Q:uf',2Z$AH[A9(^y=7ߘ#R~‡Zp]>?]v\ghTjC2sJfJbyzS:z2$HRѬVPGGeQNnh}ứuNK)hxeWfN/)/b8>9.[T{;.~iZx[`j7EC!ȳu !F#9.:9.dE9]P|8{=y\!I$IY8[!*9v}Cw͈!*G˷Q̿_.оCR|xeȊr koUmҨ@⽣ϟ>)!:2pFDoqd][EEe@ A(Z5EHKA-,)ޙG%_FrF`U=3~adOg~ScOev{\ª?+DnZ5H6ۜ gxO)(>[<A B sN4P.JlċA1ZYξr$0K/19a#~MXOv+ؾ(AE!zg=S^K iwEv>Ƌbj|lzbVtg$A޳cQ7^[du<;;/Z$^h85uA(k5pIBƬiOs q!C"<RNa⻞[$H՗x#]SnkxQ ^rc! IDAT^oQax]6E'щ{k̓ BFIX 5c2vHHOO똜!1M^kx PZUsly{:z:JHLqJz *W.G69/',<. /hϡϨ嬂r ov;uvh2IGc \nM9#QV{i"rxAt;ᬳw(*Qͭn0 ҬiIȲ|dXHĄFm^fJbFRBfJuĘĘ'/'=wl'O-,i05 xPR`|Tke5qLBts_:PmʘG~=厳gT.~0u4+yYʃ2$6k{: b0_~pěu)Ȭ(I޿-NdɲG?w>6j|@Ba.H꺜's4jJd0bHݩCN]S%%Yq3f?w<O+ok4 XVd.tZOv,pCxzʿS,`$gcyK1$ZEtG ҥbm+(ɊD@\FV(J-=3R;%w+DУ/\Y.((ؙ*Z  Qjڛ"T)2ӈrn+oF ar}sъ}BG}APW~+ztqQIy`NdCDt^z@"~}_x3ݬQMj#+ʖ<q?1 NJ+6.O?GS# 1m@}:gtH!#'^_NVL4ST[\[\v< +dA@Ѫ$A"8^E HPpe=F">-. Dn-]B"QyLT>F{o3SHKu~\{Qi l? z,x}'t].bC zT5;հ((fkuZ(/GE~E.=3tLIj[ Q`*-i*ùG}L$*tg=*GR1DRQOO}QG?=/]y߱,@[2lu` PC@u E4ӖF@VYl @]RVSR^cN#*PFR•ٵWfޝ::"*,4*,żA0n߅,D>vͼkf@Ŏ<{Ga#Y\mlj1 ^@g@=tМ)w\|ߢ߲)##ʘxG5+FokнK жEn J Z#|=#0vb4H|[͝KWmѦm$IwH`u۾#{j /u4^Wگ[PcQ5Af?4G.Sxa<pVY@H F!)iI(I[< q$ 0jChU4'DG +ݭkz ?#*x׆(1ڄD2~5'mnif9EmڶjvEQ,&ȸs]= 3<X_RݮdE%s˯^]C~mp삢6|w,&HDi}tlݶ d$ŏj7o/KZ(%*/G4fl9UI~֭0 ҨTZuTXw6fxTX",]T^!eあǼY0ق܂Oi) |;=<yTmrd ɏ X-=>/  3*9icF V+dd1#$$KNdj5fLDx: r?-A==`C<2/Gw2Wȝ2tyCQ/P/;(,vU5 /J0 4z-T=eĘ('_V`laXD#QVem^׿O婋@oG%@hC)B5vϨ'tK᎝1YELEܸWK@/ PjYr|O>Sw,7놽DFPb#;_++/@?=/ΓX^b97 \IⵛgW&N`ya?<*Kۚ׬aG@E_k]}Yj9}7o=e@{^6’?ZyBQ83 d`PsUٓƽ8ceEyYL{h<QrN枛g?=pȰXH326k$QkBn߲9:HiU7斔GE̙:cr U 8&7$* fn,zlB+ ۩-$ q1ĵz;’WV~a@y:mFrܩnkE %*KTlLD!P+7KPQH^CS;±Dem/6/ LMAR-y"&BFS/N1 ;1?[gFkW_SeX[R fL1f- J+^˵?쀢7z)JRf ΰD|JnH²TPS>2j?0;F4=`Śv:,T$͘iVu ۺ$ ST]w9ν[RY>'(l}~YΗZ Q\Y_^~yΫ2Tw~\_dz~ʫ*6eNvqĸ2GǴ7[:u5/jH7?y1Q.zyk oh p-DɩANC.rn(׿ܝs_X$.?ttmA bjiSFi0#'v>v2(jiY KH?LԤ1bG 0bZz÷BCB;sPՇEG=={F⼠k;x.04Y`km`i^ab8<-Q)fKqEur\tG 6n2IVT)̂A[t5+);u3>v"@e Af @j䭶'T1QON2\@dhc\P\TVٍ HRT)w\׿t蔚_R>wXG`8CTtp#Hy{_{^fܜcmWXrX/4#)!#)a:ufc's1\4tGDAkh|!eЫIsxǍCTV?_[aYdiM#5d<0ET@+~?t}p;ba5*(8[p<'cg+*Y〬9%Oδw(BEma1SkxAg$9ګ|=lu}ëXG|C(BtgHM0"*7b;漚yutx_qת ]$Z1oiS!;$ŏzeHr˲ O+9啲$i*Y $(ruZM猴nD?790scGD<, xk6!ѫ*fs 1B_Md_.JTnRMյ<72pf;4XZT5=kzk NSTR`֫U$<, Po۵៾G^5~Ptx3wrX+s[W=`-[٧fYpo8JRwτ_uߴ[ojQc67m[u;+$AGEv퐒)5KQQ}bST/+ɼs&ZMPxa"EwKEe$Nއ^_f6_z ,aD:D&^x>|Gb`Ϯƀ!!N*.PЂn DamCa=5N@AI1W)IMQF1݊$Q/--.-*;ҊzVE'Bj]c} 'r [>xƹ Qpv{?~Z塠LWV$M3- AFfck <ƅ]J DEٛ5j^ `x!DHJkǾ]2uH!%)6R.i]ҒUVWe9W|BUQPA"/(9)T7}uwfZa$aRɋ0|BTM{iYFcS Jj뼫p ˠ$0DSgCHEՑ'UZ+HE'ѷkf)3:$7_<ȄA5'ʒʚs%eeeu&3@HVȒsԘK 5H$ѢoSѦ澷Zx,>gFٲ(a8ET^8ISʐߏ)n  D`A"N]ZWIVRbgѥ{FjS*=k$=6LVV;]XX\F֟߱;N1!_Nrh$@zs^Nϻ:V"ztL[ӺuH횞b 1a1a;ٍJnfXEztQ-[|)mz ' GA  @EAtc&Kiao{ꍆ㮨i{u5WfiIzްiDNQOXPXE… ܏" p%$U~e"dȊ,Hf)r]R#zAؾ];٭w2;8N{';ߤ)QFA-Y{x\=|=Mdhh. krm۹OӲnRry>:蘦jDsAKԥJQQ%ы2Qa TmIQD  Pmᣧ =Hb#tθWޝ2zwꐑL׿K<S D@<{` 0*6"#QBUnˋw97EVHгK{ev9#24Pr՚y?YV(0Sgƪ? Hd@KMw4*"B}dG/%gdg~oB܎koaYsp:Ǻ?Og;I@asSWz:%F+3}pzt53#)d8qut橢 KB^IkNjpqEB^qJ9#Hy岢/FEEI4@{>a0YvrUWַkǮ)E~XXLfMD}z/JAtk^Ρ#"S^\=/JzFޭ{E.!yPYM >ӟ"C$r+{uw˻fLתխ/sѓoDכDSC+6L61Ox:T]f<[HR\@e:!E;{C2FJ8v6/XjM@i LKfPЌ"C@HH ͛y+\W;ɂ'\U yĨY̵& 6[G6 ^ՈΜDX»LWcfAdݲd~fyFlBWV}d&fZ5k R@ Q!T5ꚞ<ztIKRRW//xn#FR&/J1a:}1ZXZK6[Ιwtܩ_{{,@A!'Uk@pԎ 3' IDATA=Y]zB<KV2+Ȝw;l1^xM澶?f}apZ )aܴxA@,6yX;atxhFRm1!f: αAXhQ_j lOdCOjT"C|O@PDZ@|)5 Nu:-H.0$Ixa8CTn"4ڣ00QT T-A"+JP~ĄlEt5ol&z<Esب4j·DjXD [axKOK1c000EF6|x7' BsxNA5[趾!K)$)x:O/mn"nnI(ցpyM]mpf@͈@ndE:/0Y<$0<_U H TmP޾:\{hLVG0]/ÉGcf"mxJoLA"ci0ɒRlX!9WR3k 3\r$,T=-R]PУ1\7Vtxf vIBҒ YA[4kXSHVQ(̭| ^:00|L4ɉ)Z>_$IBA!JZ}1(WnR7**=1bvA,L RKcֱTB9|iX%* @^Q1L;]|M$<*00Qa`vxdEJ{8$IG`rW,};\>]WJq9-R4KE @NQ)@D;hETVIttw~ՕI%@GΘ 7M<ʟ(I5uBx! ' h!3\XVi d~1O@Y磧$ )Ua-~TnJx;\S{Tg KԔV RruGYEFBSwL x^lD(Zb8 LTEd$YՉo'&0`mH m@8ZlPܓq9IDul^Z;IۡӉ`T'N\s/)}0=BMyA G*cZ_r!$A< @tNKTgM^oD3fjmU?6?__O.MotPjDRJLDȫ<7uueU/;*S[lDb@mutAA5M7RQ !tضf1tx7)5p>4G4Q{)>Ⱦ\4\C3Dzvڼyݫ1[m`yDZudpGNH?~z=6榔Xl!B *5HCn@ų}#* _׍qhqEMNQ#UJzR[OMq* 5^+{Qa89}Iv[rpxJQ#f'+R8-~T#1>msu("Z%@TVHIP4/5Yy_K`2L0ͻK_ڰ/@q/)vV"EH˺t|zF_,3'LfZ#Xʎ,赚yyGYV z.+OѪfwϾDEШ059.#f D?/k혱=E"X@]@}xY~ܳcَ蘶Oa %kAnqVVGoz׈}Y(%A~i?9@8Ku9=_) =Y qp0GdQ- Pe) Kij :p"ۮOQol-cɦ X| K[s?vט'4<'5*:o`՟gUƃn޾/q~?URb BG}ݏؐ`@:&Z%;p%l$қonqFuZb.G= KYb灣| DּtxBttH[\>-5/;xX\PXސ=cBV7[.^W v0ỷ_hdiR˺dX]޴!-}r}/ b#z:N^?9#)*0׿sOCeLpk>U K˱n[F"~Ã/aMTVWb#]o`2Z.(4}yk)Y]FeE;0H?]uX]?3ۀbbvc "vwwꋁ`w"" bac"*4;1w>왛'={q߭Z{0:_13Qm;I랴1b_,]Wϟ|ڡOzUHJB+r)|y\ej릎f_Ȋ6,XOs2 1a2X~@^1=lIbzGbxx]Њ٫i[ؚ"gJ僺ݯNɔ>wR\d*h_B\9R)WqbeKq ƓW?|]UtqiYNsxc^صfB r!O2wD#i!ǟqG^5W0^dX;y HU.i +F 7.)_T1J!SʒMńyRߣ^=䢂 mzD. O;NSt1x,{m){4'MsS\i׮h$9iL ^تBv^EByւ$tNj¢D(?OIH @I΀}T. n=h0d/1hTXHLIk?~$2&9bE+vr X[Zx̟mޤw_}TʕjX{WB`D.WÞx(gb5h=="9J9k9@*W)$*Sd%L2$q quTa>*?Iq3;̡S 7^${qޚL\)`5dJޫom;z~}UfK_ȽMeDto1?K ͺMt|&A2sǦ/+ÜH̹`Dzڮ]-"R"HXEM@ǯnժ0Դ|TajR(}gi cL,ন 兿I?Cw ?Մ/ܭVėUP*x,7-LJ޳VNp6sb9+ ~o2^EUB=Nl5r9_bl _gIK&9J"c:/K ͨ Iꔁ}T!*\T([|pYM)S 拸M-u[oqHTp2sbT?r68:U<{q-1I kSdR(Sn=p~z9tlUNMM@P~.y|ܴ08V'-KJ0sg;9@'Q"1T"ZA+uPz5if*U<T)4XtyU;4mP(m1{DQ4 Gy ~Xa1PE!=:r@ߝRD䮫ݫ{2B,SkaO[6uO?Ŋ QQBpB HK}PNMMyuSG5[$JifhDNfBs=&̒}LsSg.dIBEn0~ O#$*sDރ(A57Ʉ_CHff:c EI5>6}e=f…rt|6݇ؼ"Տ_kxs>q,9}f?YdrZ4Hn/1mLTDURu[?3 jnk l>|S|\Td)@*\=wnUy.΃>㿳JEžzpk+*xj. /l]e7xB*퉁Yݚ'$J"U'kce!6d2ŒBo?~kwRȓcSU!$zժh$.2cKCDByLHN-d$$A~!13y.0{G(! k"VX,V&Ĕ40~L87(sʙTA~гE̲nΊez*ת\f6Jr92R~%vo`~?4;@]Z?a Qɤ.t ~&(Tq-f׿' #c% {ZG~&+!y`ܝnpe ¢DKXYZ[Z,mi!\erdIR<)5篴Iii8$K  hE! ƈ^]"UdN $57”*Ab o)pRX~(Lr!ʔϾrrt`pc)ofW#Y!p$~ p9P2VT/J< =%Pׁ(o`"zb PHת?d8,kD8W5"G)!u60-3Pr>u9$=K_sB*WL8@M^C+E&!sT\/~)%8JO㨗Д Mc>Z5♴Ƕ*jVu920Ndٔ4ٳtFqJ[w$?$-Z).,tHlh 7m8vA~IHUu>VGb"*2m1R.=~,JsWy-X"Dr `(,L8aK8fQ:~a$Lt`( APQLsȊB?sCQ!/Ӳ^5]Vnf!3lTi?bܿ+´w—tia\b @lb,уC{V՗$X.OXcJT",!Rϒ IDATqķ4R32D(,[gZ#-31oBEq?(񲥬~@XZT$*orzR#h`÷j" B@@@@@ *dB@@@@@@ *dB@@@@@@ *dB@@@@@ *$TLHy\+UAy.8y'4I|ٻ/I,QڲC[/_UlA601FR(Ul7 W0m(c^G}Ӯ RT1Uʕ6w ׿HgB|{>۲^umߨbTBr8\$8qp-YX*ɕԿf&Lo}!::.ɱc3\\N ;tV_0)öՒ8ZaZ*%1%Nx.-v׸D{rֹ{\ñ嚡*Ъ1 F5MYҗ3l˜BV"E T,&?2[^\dP?z]~r:wfm9u-z׸Ve֍;v-k͐|n>N#!лm Ӈ6U>gl>DشNm^6LJvt}4O7ܕI 9pG=2g)gSB3T>J]t`B\p*$.ZF!3ĜVФnvvȒ~!? 6,7ӡi] +ٔOC~kA,GCMT뱫4 !w,MG"&h{ghHl"s,snKJpBr ;W2RtƵ]tڢ΁ύv2 -\3iIsF,y~ܾ2g D#,Z/;/U4bߠfu {ЌuYz>=KbQ؊G^<{,er>3R3`t%`@>&|_.rkKLd2٣8Wi} Zݨo] _@47dƃ2i?Y;.qZk+cؒRp?y!{dz$Et"K"{îWJ=ǯ4)@J"19eNn=f~ޏD*ɠЉwwJ3`E;3`][rq Hk4lRjWНO߄ʄO1TvЯrSApT?5te!ʎvI.\~ c4,D C"]g& ) ISA][\J^25~^&I`@NЅ^unzyΆct&jK27q0 \._wg­}eIxg[ Z71wy#{OХ-4*~ΓHL1MT.WmGqY, z$qfjFC)K$}g{E4TwG'o>>z 7rR!Vve-YVR^yt"AFl,;6xf4R9tD*<ň~ *}5Ԯo5 ^jK0sW^xD1˞\&bj$HB.\!gb3v?.ZYXVӲKv=>W%29K+=Z5[8f,Hib1$nϲIsOۏ=O]KJۏWvc*ywڲĊF阧A>~q1SҰNZf&4ٺѩs ^O+-~r J1ۼe5G*DTfxkK93laDgƆk{W7s CTQڪ~M%烩][6Ыh;sRJϒfde#|{)ŐBտΝ2ahGL)N'e>e䳈Jr$19mÌzaԨT{<*>W1 ݧW[7M߰{}PYx_iKRTg uGvfRJ+Сuc> ia>{×4y]5p,MlE4!uX"?HXr86)<Ծ6Sy*f*r"E]Z\_MRO;VF`o7oHzMrU+ZË2W#>]"mՖr%Lѫ=HL^Ԙf!_jG`ui= $g(bkRJxz30&ar!+&}PPκmtSA ?xƵXY8vjٿcE'gO,LE,f9%37\ۻ~(`XV-b)b^Xp>Ce[ZkPٖ9ܮZ =x^t=l:G5]J=Z7t;7CfX̗*4gfRSQؕ; E0y` uZܢsojJSo('w) c֛~74vH'{!vWyXz?6PQ15& ɀ0 0cdrԯ!_cFX22zM]W gh+edǘ*uG1nϷq7jd**P=rѳ]Z6ԔOMMEԻOJa.i< ɩ䓒yU6+ SH׫+%o?0~˘zP*4ˏ/!^i(wXCEJ2PJwF.=]>K$wdk {jT*hFᑟo?yy_@|.>AfZL}ηԔm\';"^PA’cP6_µh,[V hخjJ{dEVň^ ([z33S4y$yuYРf5SiS\b5e0 1uczư?{qծ\![\"lhub>KR)\ c5X~IPU\Dj n7|ėa /8xn.~ƍpB>K.KNܠpLݶ7ٍH;WF*S36\k91 )٫wn]0eH/*N=)%ԥ[:\)ޗoS)w戾wQg ~<*ZЛ_<~ 42!kiY7_T)ot粱S9?sG9M}OLN6ayXB0UqWBw0WNJeJI*b IC)_f-KIuQT.WI){D.C&su@V$}(O\0D6gwޣW83ihpHU/\i+T_LsSbD*n(S:mX͇} D*w6hŔ=zzRr5}/0~YL5;.w|q؁y]ep*&x2/sA5:hpK<~^c<˾Qݎ-Lߠ\9֕U6-1IIvlphw d2zKS}azIS*SkHu5x.c[)<^nKM;rcʰ"H23KLFֶU3{_Ȱaϩ׫^pGaD'య:L$ԃ"b߄"{Go(}᫆ޢIeX7PXdG$N{\0Z_öBN~$ITΓ@!rs;($.$)[4oXJ㘯A\:MR4;vryR7Þ:pEoeQBcJs֨tEo?]cʰ~nOԴStos9Jss~/ܜ7@TO2yٮ|,inaI_BI{]M,Hm;:ۣޝ fx)k*ʣ/y|+ c 3itjzUiDjx!o2eunSöa] 3QZFl==_7E,~i[q#:u2k7(騏<4_DCt*؁Xu9ܼLTr=q J*z[<{H/v{]Kڈx6u6Գ|r>Z8^:gg*W԰]N\|RYp=?}ծ2]2ʺcF @R y"h=~آQVEDl$ð,P(^ { }w+,dޚ硏^>;aߴiZniyDZYIDATgC<|wlׁ?Cy2EW^3|FXx[77o.]}3Ae}~—%y;ڹ'sOc''Sd%]>[{$u(_Ƹj>y $-lmiNE-tKiέըsOEorbJpG9A7Ub=yaڭƽ'SiiF (OnggN7*+&'}M9BLNKbKѵ4 1CzwȞ~8uYLa YTh3ET-$eYNگnl; }L,Z7Y7{~mz}b+>w%=3 m]ö|v$7?Q:uo.$հ18" >Cq9k 㗫whv=W;O[t; 4!a(Du-$ q :(mwSM1$I29-5|=klXf2w⤔4\-T0ww{G| C-)t W*JPp+FA/Sm;rP~۽N<>_i/޼klM 3ф}:YK& C1uiCcP^~(W{]&!OBR.I8sdK[Y3gJI 2ŒW\vzt©z'$!%\b~țzťv6_[gh.OT:pZwxwophRc6q V{ASG8qpLm2}} jUn>xJy j?tZЭe<ԛJS=>A}LޝZd]Q ;wܛKtoWt)}"`駍pg4uzCT*|Ӏ)K_EuEܸik88ܺVJ`ߵИ$cs 1HxH*ۏ?8y~{6tm̸%RYꭾ-oӷSlO[?vmLbK>}wb1sP~]'&>zP#ޚEuitBpm9;tZJO?m3 Jby L:y>LM8h8ۼT$ 33-٨*>{}ΧDŽl C,уDin,2kj}ؖ)DLq iqOk0 ۱jØ9*0z΄ Ӭ~m p?x)?LN{,Ka[X9cLՙrs mu,X=gRߵٳPwѾU:K[0KMdQ_r_ԯk[gn6YhxG7/oP:l;|FgsۺիL׶5lGE'LjI477lE2i;x(gwaKfGӗ 2gت]_<$IBI[jWIwϥ<;=3KxrNyѱ;^y""Ov\Ӌ|f?y`ߤ}z=UL)gޗnS}QG)uIKG PL)`qY)n[mԙkOv0VmB1TԲ*ψML] ry"tMXz5Nuz~NtLM=ڷ22rzc/*|pϝ젩K{ѝ|US0{oeDBHhcmoz_b6a&EBA Sk+% 4gԬRTI ʵ0e#;}tO ؽ}kXa| 19San-QerF7O#Ф^- JIC˰4++Sl)btq%=3ueJ5?'дL:5Lt(*%LMժ*i8!~L9KY ZU[5Ttd!R(^G2Hz0"mL&+y

[X;o($<MxC}MwL栒́@,,,1@&4;9&Uah4 Lw2LLLL\'h;`T`cҰ5@iN@Ah4 &)OMt:FPnAОNx4ycf`bac2F  FNx̪L LL)h;pffb͞ Ai%Bф7wt$//LwLLdF Th ^`($<Mx}* < 99h4 ބg+Jwt,4-Wф7hwt.()'',,/ `j&S?@&ھ;&9 aa9&~Pa)@ `vH%<Mx4Aҝ<c!,j:@&ر;&&ah4 > 0ݱ;rN=Kt XYYx} F`l ' * ԩ%:F P%>Po'AH F Lxt$ *_ф7>(A:ah4 w,4;MDMyLFHsCb=@&־-u,@3Q UhFIx.hBn(lф7&<(ɀ;L2h\ OI Ъah4 / سكA;hX-u@}Sh4 :u a9&c~~PiT%<~BPf H' Ah4 Y~J  FޠIxh MtJ-тN~zHu@@ oryܱƙ)o8$<MxxTӝ R,p'aRhju ܳІ8AcxMȋRh4 LCMwH3%:vJ,3@&IxhNA!SbJw.h 1tg@ KCX фGqo$ քςW+/=ZфGgdLwLd OxJX2˄@ F O[C_)y,:@Zi@ 6;wh ,`a:G X(++`9q;CMx4ii/ú2Nst-͍>^ul```X CMxHx;̙2p-@Th4Q?a.71cJa=VPh£z;3+$ѱ"-Fe!8; /bM0ԉ,<ܡIX[T,$eeepH@ G\)Y!Pҝ80Azl:/h£nu,= hTS$x3 @1&6# ѾòP 憏/X+E> F|#Y#,xzѝִXP3 Ftm V[ AA_|dŰKx48p;3V5Sp.a&:Q"1)aƘ xǍ2U2Y`] A4Z#_JAKeXX<"P;'0P0X F\“MöLR<)kWlHlT5̭pw;xLyVl䬆F>WQC3H_ Yb_n nicX!zܘC)xn9LT[; FZ3o !96H F^gÞ,jݡ=ZeȠբ&+k36 c&$ gnvh uPz40Edl$@yú p;$-J5 тP gƧg VC$Kx\LlaD*E O;< ЪVVqSq3@Soe5T@1ccRecSeҶv*l4@@`FlYa]ܳ`>]Mx\ꚰƜ6&?4VP3Z_1y4102$1*pb4$roNgy$'!w(B&@Gj̡upGpD\mp,=U619lCt4, ?PGy a?h$%<&lPŌ'*3)l+K6dX<Ox쪚C:JxM<ؠ O29Mr ;F~s<,Y%͡a$:UlE1h*xIrcjQQ[M<3n:I2Tlf6G{&,@Ah%<,Můf;Pぴ pP H`xxp!0@Wv!*= M 22JH=H/HЎҠ\ @#&k%P) k`SW76VnOr@KxJ&8|ŧ1b%vnhC- Yqxvvv%С<аJxffƌL(@H zɃ*h TP/*V`afHyL-cLOr@Ű8@;6Lx0Xm Tژ3&61!!iiiHc")Uy|t'ecdXJE+,`ȋ '''(ʰC$2'<2}1^[218!C @tMx6`hY k 6mBB҈ԆYO3%@0@% dgPf!' d'MytǍ($U`cB.LIᤠ< '+'H O-;Rq\ h$<[3=uuE!L|s׉K|ȮJwtL?P18Ɂ}NyJ' rCJp =y`ʩ ePMx4\a%*:q][xS7d%P +@kFhJxd'-Lr&YE$R8iA$)M|+ZJx@jb݆x p[mrE9 %Jr' 16DY$ŝ$|"tXAA QZPƟ )͡6쇏ʃ[ Aʠg/lC3HbC`  WC0i6126) ="ZT3F2" P`p \(A4ͩ`MsR5Ać; .`ӷA?!‘HTLxV&zDqц'"<5PcQ?i@ .!,. f!Cr Hx2ܔ3:jC+HJ:RpP%<{23hڐJUSS&?h1T&>z%<B³c pʃ:5@][MMp\AXF9P Lu2*hħ?q;Zy '(;%A#}}&<b C d HFJKZ4UYau9`$9 4!+ b@@- +b u>>.H)ȁD-})q6^ᛸ`u.Jkafc$E&H 3$f/.ćeP"zz@x:%@ԇ ϖh;/5K<,9!%?^^!ؘdZ @U"OlwkkC}bAg7(2(b2P#."ngm(] zi8 ٸ F[{@ajLU-#jI>(^3yaT`i΄5]p'<,4'~=l[eI \6|Q2l:|$'<b ԣ15e-Q-B 8H c4HHrNF QT`Oih@s* O R4bqڌ@!h]rOr BwнV@Cm4TA`-&ܸ (Ky̢3]Q 4ɱ+YʁVj)7yڰ4`Ⓝ&>Ec Ė&UUD?м'm+ ZQiv.Z[ wb+`p `HCXa}F`LV%\6AjtYA;bl mpSc5E>`{':a7CB#v71%PPPzpAW\249(N2D r>(-f32RTfPUՆЀ&C`4D*ad'F.<$z:VNذcwjhU3jO1* rv1X/{b *$<Ƚ[5U1dAh2x '>$;$.SR'Ut}Fг[@O|(~ | @u_@P3&|~̀Df 6n  'D=K4Hr,,,쐴^g] %D ໫pL|.)PiNcc wAy< ƚ]~3J);OB1=}##3Y&"q'NHAH$NJR2BNX$)Oߞ\X &M*1$ݱ&<&KxDtAiVR+ɱ1+hA g ]f`LTqPTJȡȎ`-0Xu(m; H[%#7TGvXC.QgO)(aIPr <(#ҢcaR-1g*ZաwHc]*$ ;19@J8|H @Mx-c XXiPhbKpgA/PS&@Wmg'*ͱB׆Lr`RAY{`o-D{F{sSS#qT )/ HX{ }(Σ ; v<@)!BgPAAt4ɉLuJxM NYF2tj!蔇<,NMQx׀205h&deMdVaKx4K֢`g.POR"J6OCMy }+ѡu(0a/x+/  N`)'BCI _T: Ș <Y$Nڒ$X H qzP rq6$<&9<"],fb@MxH@͡ cKxRR[uٱ$;)dzKKPS2`B+E b) G(%XE j⡷; 22)PÒ> O߭1ehSG?ڜh;t)/єFsX -+,I!'9D05TX ie߂#Ow zX19gɳ2)`YiNx4J<<,+FC4PS"]y5'pq(VX-L'A7V2 {-DX>D9 "D@@g-;h v؉) @zB&(! N Hb dȀFF=hƎHw5-R T$ "A/JBBK\,1  an'h&<|e" f&c4h’Z8c)쀞 ޙACj{$lF();1IhH'#&qݚ-'Êm%2) R ӝ 8A6 ^׭> H,:J2,j [ƒs(FB!pQj<'?` #TixЉZRД@i"O؋=ț}!#gB 'BlkQJAOh`2r. <cMc=Zi ǧ Kر ~ xP ๒9}h:<>6ö6  e.`+A%liN𴈼BbQ3#jB]d 5XTEj ֩Mht,DP` A&k@#\ K@Khȉ yW @wC7v6u7tc٤( /tF(cTsr#\  % \@Fm{Xag@VCu[#9"xZmXu4@A0G|PKH' o HK XW67E(ZP1O[ 9رEOx,q@GTV/='L|{ / cC? E9()Qs9;- qúU%2 HB0GIx & aMxĤ+@y-"y$N53tt hn_dU>Ʈ}kt5rKb KsZ6rb)p2.CL ``v7DKJF܅0Q&$Fhit3DQn,aIw '()) &Y % XP&4 U@:y%^&R"VrJv )@: X7T( OgMxN&#)Jw&J&#1&0@ äόl47&~UgÚYep;`P3H',qxl8U $<V %(a)MXrs0TZͪ*UȔ+` P[m0^:c,0,}z0F{;\jז#5"*ᑒI%B[F&& T1SMRQ7V0`\x1 B/7@ O/<|C6T(` @󳠹YЀ1<؁7d-*7ר@ (JxL(Trӡ5x􄨡b dm@RU?hgrI 6FM!0!οM͂C>f`8RcfUzxszU󬬪WٱXYJyhG1rvX: x 5V$<`Lj5bIw;l֥moğ@US_Mx C3ֲF|6Qc( ))X y&cxBDxv(8i&^1x 45hC3hc4bE J'>7ިhD j"':xa. uZt z:wqǰ4k4<>fc;TKw8JP]`u-;v5-(<3`s򐶞l0 <<14AY :A<;9ZPKJ` ]N|U5ΑhRPAs㑐hh%<,LcIxJƦ I)ͩ;]2x: ʐn'<R O]I#1* !t }."CӦZc- ]Cǃ^bz{Nċ,;-y0h,v#ƍ/gc h*1 hP&<"PP;;u$6UUb@OAI=ݱȀY!w4=<0@< s7*@ hj$u,@ ]cSPGOx6L ,+l!,]Zt~v4QаKxJh V[AK= x[h£.A7W = q{° *EeSRGA 9g Taiࡷ44:f3p]K̃>gP7icx @fu BPP!8FFFyx;! 2sPBW@ u;TY@Ѯ]]btHJx4 ZLwt6;tn40Kx hAXav,d׽Ui&<l} X;NB խs)0٢] ѱw{c! Bw#K(gc#_ClwhA CC`J=PK0D,NCdbҾݺ )dp0V KAeXGEKJ?:(hC@*+0Y&>&&ilh Tj1j&r,.ʈRɰCx21c1Lt)c^ZĈڱ`iaY0h nƒo/Iw@Z8 p@C6q' Z`y`g1 !cǐ%)tg%A,ͻф7H@ ńJ\2hSUUJ wBJ ;666;@1 DYʤ-IAԳ-b;#lY R֛]V n@C47Z:tȀ%ɀV81&h&?;ھ\ N³cSeR6b-h|(,݁(ެhi7@ gji[paIxHh L³cR;o z*xXی{i2FPNSSShtn ұు$-F.SSSMh Р1Zi3Z 2@C%Cҝ-Mw o" m8lGKhP'<*8qm1ᥝhi7@ 'h<l5h^ Bu `t}74@ BPsss}Pt2h1u*@ynhMx`@@&Q0 J/m@Aضu*? [[[4dӓ5h0{@```bJ;ƌيh4ፂ4F c":LĆv*8F: JlA%'> 1m:4FMx`@@ x` +x(ـ&eA)4blmm4m @C"AKt7@1 "%.K6ф7 hhH&<,`4& h4ፂ4FMx`@@&Q0 F(@ o ф7 hh4ፂ.M4IENDB`nordugrid-arc-nagios-plugins-1.8.4/doc/arcce.rst0000644000175000002070000003346012312612073022300 0ustar mockbuildmockbuild*********************************** Monitoring ARC Compute Elements *********************************** General Configuration ===================== For each CE to monitor run :: check_arcce_submit -H This should be run at a relatively low frequency in order to let one job finish before the next is submitted. The probe keeps track of submitted jobs, and will hold the next submission if necessary. Subsequent sections describe additional options for testing data-staging, running custom scripts, etc. On a more regular basis, each 5 min or so, run :: check_arcce_monitor which will monitor all job status of each host and submit it passively to a service matching the host name and the service description "ARCCE Job Termination". The passive service name can be configured. Finally, a probe is provided to tidy the ARC job list after unsuccessful attempts by `check_arcce_monitor` to clean jobs. This is also set up as a single service, and only needs to run occasionally, like once a day:: check_arcce_clean For additional options, see :: check_arcce_submit --help check_arcce_monitor --help check_arcce_clean --help Plugin Configuration -------------------- The main configuration section for this probe is ``arcce``, see :ref:`configuration-files`. This probe requires an X509 proxy, see :ref:`x509-proxy`. Connection URLs for job submission (the ``--ce`` option) may be specified in the section ``arcce.connection_urls``. Example:: [arcce] voms = ops user_cert = /etc/nagios/globus/robot-cert.pem user_key = /etc/nagios/globus/robot-key.pem loglevel = DEBUG [arcce.connection_urls] arc1.example.org = ARC1:https://arc1.example.org:443/ce-service arc0.example.org = ARC0:arc0.example.org:2135/nordugrid-cluster-name=arc0.example.org,Mds-Vo-name=local,o=grid The ``user_key`` and ``user_cert`` options may be better placed in the common ``gridproxy`` section. Nagios Configuration -------------------- You will need command definitions for monitoring and submission:: define command { command_name check_arcce_monitor command_line $USER1$/check_arcce_monitor -H $HOSTNAME$ } define command { command_name check_arcce_clean command_line $USER1$/check_arcce_clean -H $HOSTNAME$ } define command { command_name check_arcce_submit command_line $USER1$/check_arcce_submit -H $HOSTNAME$ \ [--test ...] } For monitoring, add a single service like :: define service { use monitoring-service host_name localhost service_description ARCCE Monitoring check_command check_arcce_monitor } define service { use monitoring-service host_name localhost service_description ARCCE Cleaner check_command check_arcce_clean normal_check_interval 1440 retry_check_interval 120 } For each host, add something like :: define service { use submission-service host_name arc0.example.org service_description ARCCE Job Submission check_command check_arcce_submit } define service { use passive-service host_name arc0.example.org service_description ARCCE Job Termination check_command check_passive } The ``--test `` options enables tests to run in addition to a plain job submission. They are specified in individual sections of the configuration files as described below. Such a test may optionally submit the results to a named passive service instead of the above termination service. To do so, add the Nagios configuration for the service and duplicate the "``service_description``" in the section defining the test. See the arcce-example.cfg for a more complete Nagios configuration. Running Multiple Job Services on the Same Host ---------------------------------------------- By default, running jobs are tracked on a per-host basis. To define multiple job submission services for the same host, pass to ``--job-tag`` a tag which identify the service uniquely on this host. Remember to also add a passive service and pass the corresponding ``--termination-service`` option. The scheme for configuring an auxiliary submission/termination service is:: define command { command_name check_arcce_submit_ command_line $USER1$/check_arcce_submit -H $HOSTNAME$ \ --job-tag \ --termination-service 'ARCCE Job Termination for ' \ [--test ...] } define service { use submission-service host_name arc0.example.org service_description ARCCE Job Submission for check_command check_arcce_submit_ } define service { use passive-service host_name arc0.example.org service_description ARCCE Job Termination for check_command check_passive } Custom Job Descriptions ----------------------- If the generated job scripts and job descriptions are not sufficient, you can provide hand-written ones by passing the ``--job-description`` option to the ``check_arcce_submit`` command. This option is incompatible with ``--test``. Currently no substitutions are done in the job description file, other than what may be provided by ARC. Job Tests ========= Scripted Checks --------------- It is possible to add custom commands to the job scripts and do a regular expression match on the output. E.g. to test that Python is installed and report the version, add the following section to the plugin configuration file:: [arcce.python] jobplugin = scripted required_programs = python script_line = python -V >python.out 2>&1 output_file = python.out output_pattern = Python\s+(?P\S+) status_ok = Found Python version %(version)s. status_critical = Python version not found in output. service_description = ARCCE Python version The options are required_programs Space-separated list of programs to check for before running the script. If one of the programs is not found, it's reported as a critical error. script_line One-liner shell code to run, including features commonly supported by ``/bin/sh`` on year CEs. output_file The name of the file your script produces. This is mandatory, and the same file will be used to communicate errors back to ``check_arcce_monitor``. The reason standard output is not used, is to allow multiple job tests to publish independent passive results. output_pattern This is a Python regular expression which is searched for in the output of the script. It will stop on the first matched line. You cannot match more than one line, so distill the output in ``script_line`` if necessary. Named regular expression groups of the form ``(?...)`` captures their output in a variable *v*, which can be substituted in the status messages. status_ok The status message if the above regular expression matches. A named regular expression group captured in a variable *v* can be substituted with ``%(v)s``. status_critical Status message if the regular expression does not match. Obviously you cannot do substitutions of RE groups. If the test for required programs fail, then the status message will indicate which programs are missing instead. service_description The ``service_description`` of the passive Nagios service to which results are reported. See :ref:`example-ini` for more examples. It is possible to give more control over the probe status to the remote script. Instead of ``output_pattern`` the script may pass status messages and an exit code back to Nagios. This is done by printing certain magic strings to the file specified by ``output_file``: * ``__status `` sets the exit code and status line of the probe. * ``__log `` emits an additional status line which will de shown iff the log level set in the probe configuration is at least ````, which is a numeric value from the Python ``logging`` module. * ``__exit `` is used to report the exit code of a script. Anything other than 0 will cause a CRITICAL status. You probably don't want to use this yourself. The ``__status`` line may occur before, between, or after ``__log`` lines. This can be convenient to log detailed check results and issues before the final status in known. It possible to adapt this to a Nagios-style probe ``check_foo`` by wrapping it in some shell code: .. code-block:: sh script_line = (/bin/sh check_foo 2>&1; echo __status $?) | \ (read msg; sed -e 's/^/__log 20 /' -e '$s;^__log 20 \(.*\);\1 '"$msg;") \ > check_foo.out output_file = check_foo.out staged_inputs = file:////path-to/check_foo Staging Checks -------------- The "staging" job plug-in checks that file staging works in connection with job submission. It is enabled with ``--test `` where the plugin configuration file contains a corresponding section:: [arcce.] jobplugin = staging staged_inputs = ... staged_outputs = ... service_description = Note that the URLs are space-separated. They can be placed separate indented lines. Within the URLs, the following substitutions may be useful: ``%(hostname)s`` The argument to the ``-H`` option if passed to the probe, else "localhost". ``%(epoch_time)s`` The integer number of seconds since Epoch. If a staging check fails, the whole job will fail, so it's status cannot be submitted to an individual passive service as with scripted checks. For this reason, it may be preferable to create one or more individual submission services dedicated to file staging. Remember to pass unique names to ``--job-tag`` to isolate them. Custom Substitutions in Job Test Sections ========================================= In job test sections you can use substitutions of the form ``%()s``, where ```` is defined in a separate section as described as follows. Variable definitions can themselves contain substitutions of this kind. Cyclic definitions are detected and reported as UNKNOWN. **Probe Option**. A section of the form .. code-block:: ini [variable.] method = option default = declares ```` as an option which can be passed to the probe with ``-O =``. The ``default`` field may be omitted, in which case the probe option becomes mandatory for any tests using the variable. **UNIX Environment**. A section of the following form declares that ```` shall be imported from the UNIX environment. If no default value is provided, then the environment variable must be exported to the probe. .. code-block:: ini [variable.] method = getenv envvar = The ``envvar`` line optionally specifies the name of the variable to look up, which otherwise defaults to ````. **Pipe Output**. The following allows you to capture the output of a shell command: .. code-block:: ini [variable.] method = pipe command = **Custom Time Stamp.** This method provides a custom time stamp format as an alternative to ``%(epoch_time)s``. It takes the form .. code-block:: ini [variable.] method = strftime format = Note that the ``%`` characters in the ``format`` field must be escaped as ``%%``, as to avoid attempts to parse them as interpolations. An alternative ``raw_format`` field can be used, which is interpreted literally. **Random Line from File.** A section of the following form picks a random line from ````. A low entropy system source is used for seeding. .. code-block:: ini [variable.] method = random_line input_file = exclude = Leading and trailing spaces are trimmed, empty lines and lines starting with a ``#`` character are ignored. If provided, any lines matching one of the space-separated words in ``exclude`` are ignored, as well. **Switch.** If you need to set a variable on a case to case basis, the form is .. code-block:: ini [variable.] method = switch index = case[] = # ... case[] = default = This will first expand "````". If this matches "``>``" for some "````", then the expansion of ``>`` is returned, otherwise ````. See also the example below. **LDAP Search.** A value can be extracted from an LDAP attribute using .. code-block:: ini [variable.] method = ldap uri = ( )* filter = attribute = default = If multiple records are returned, the first returned record which provides a value for the requested attribute is used. If the attribute has multiple values, the first returned value is used. Note that the LDAP server may not guarantee stable ordering. **Example.** In the following staging tests, ``%(se_host)s`` is replaced by a random host name from the file ``/var/lib/gridprobes/ops/goodses.conf``, and ``%(now)s`` is replaced by a customized time stamp. .. code-block:: ini [arcce.srm] jobplugin = staging staged_output = srm://%(se_host)s/%(se_dir)s/%(hostname)s-%(now)s.txt service_description = Test Service [variable.se_host] method = random_line input_file = /var/lib/gridprobes/ops/goodses.conf [variable.now] method = strftime raw_format = %FT%T [variable.se_dir] method = switch index = %(se_host)s case[se-1.example.org] = /pnfs/se-1.example.com/nagios-testfiles case[se-2.example.org] = /dpm/se-2.example.com/home/nagios-testfiles default = /nagios-testfiles nordugrid-arc-nagios-plugins-1.8.4/nordugrid-arc-nagios-plugins.spec0000644000175000002070000002004512600623616026275 0ustar mockbuildmockbuild# Disable debuginfo since there are no binaries %global debug_package %{nil} %if %{?fedora}%{!?fedora:0} >= 8 || %{?rhel}%{!?rhel:0} >= 6 %global enable_doc 1 %else %global enable_doc 0 %endif %global site org.nordugrid %global nagios_bindir %{_libdir}/nagios/plugins %global arc_spooldir %{_localstatedir}/spool/arc %global pkg_spooldir %{arc_spooldir}/nagios %global pkg_sysconfdir %{_sysconfdir}/arc/nagios %{!?__python2: %global __python2 %{__python}} %{!?python2_sitelib: %global python2_sitelib %(%{__python2} -c 'from distutils import sysconfig; print sysconfig.get_python_lib()')} Name: nordugrid-arc-nagios-plugins Version: 1.8.4 Release: 1%{?dist} Summary: Nagios plugins for ARC Group: System Environment/Daemons License: ASL 2.0 URL: http://www.nordugrid.org Source0: http://download.nordugrid.org/packages/%{name}/releases/%{version}/src/%{name}-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) Requires: nordugrid-arc-client >= 1.0.0 Requires: nordugrid-arc-python >= 1.0.0 Requires: python-argparse Requires: python-ldap Requires: python-genshi Requires: python-ply Requires: nagios-plugins Requires: glue-schema >= 2.0.8 %if %{?rhel}%{!?rhel:6} <= 5 Requires: python-elementtree Requires: python-sqlite %endif %if %{enable_doc} BuildRequires: python-sphinx %endif %if %{?fedora}%{!?fedora:0} <= 12 BuildRequires: python-devel %endif %description This package provides the Nagios plugins for testing ARC CE, using the ARC-1 API. %if %{enable_doc} %package doc Summary: HTML documentation for ARC 1 Nagios plugins Group: Documentation %if %{?fedora}%{!?fedora:0} >= 10 || %{?rhel}%{!?rhel:0} >= 6 BuildArch: noarch %endif %endif %if %{enable_doc} %description doc HTML documentation for %{name}. %endif %package egi Summary: EGI configuration and dependencies for the ARC Nagios plugins Group: System Environment/Daemons BuildArch: noarch Requires: nordugrid-arc-plugins-globus >= 1.0.0 %description egi EGI configuration and dependencies for the ARC Nagios plugins %prep %setup -q -n %{name}-%{version} %build %{__python2} setup.py build %if %{enable_doc} mkdir -p doc/_build/html doc/_static %if %{?rhel}%{!?rhel:6} <= 5 make -C doc html SPHINXBUILD=sphinx-1.0-build %else make -C doc html %endif rm -f doc/_build/html/.buildinfo %endif %install test %{buildroot} != / && rm -rf %{buildroot} %{__python2} setup.py install --root=%{buildroot} --skip-build install -m755 -d %{buildroot}%{_sysconfdir}/nagios/plugins install -m755 -d %{buildroot}%{pkg_spooldir} %clean test %{buildroot} != / && rm -rf %{buildroot} %files %dir %{pkg_sysconfdir} %dir %{pkg_sysconfdir}/20-dist.d %config(noreplace) %{pkg_sysconfdir}/20-dist.ini %config(noreplace) %{pkg_sysconfdir}/20-dist.d/default.jsdl %{nagios_bindir}/check_arcce_clean %{nagios_bindir}/check_arcce_monitor %{nagios_bindir}/check_arcce_submit %{nagios_bindir}/check_aris %{nagios_bindir}/check_arcglue2 %{nagios_bindir}/check_egiis %{nagios_bindir}/check_arcinfosys %{nagios_bindir}/check_archostcert %{nagios_bindir}/check_arcservice %{nagios_bindir}/check_gridstorage %{python2_sitelib}/arcnagios # Not sure precisely which Fedora version the egg-info becomes relevant. %if %{?rhel}%{!?rhel:0} >= 6 || %{?fedora}%{!?fedora:0} >= 9 %{python2_sitelib}/nordugrid_arc_nagios_plugins-*.egg-info %endif %dir %{arc_spooldir} %attr(-,nagios,nagios) %{pkg_spooldir} %doc AUTHORS README.rst LICENSE NOTICE %doc doc/arcnagios.ini.example %doc doc/services.cfg.example %if %{enable_doc} %files doc %doc AUTHORS README.rst LICENSE NOTICE %doc doc/_build/html %endif %files egi %doc AUTHORS README.rst LICENSE NOTICE %dir %{pkg_sysconfdir}/60-egi.d %config(noreplace) %{pkg_sysconfdir}/60-egi.ini # FIXME: Prevent rpmbuild from generating these compiled objects: %config(noreplace) %{pkg_sysconfdir}/60-egi.d/arcce_igtf.py* %changelog * Fri Sep 11 2015 Petter Urkedal - 1.8.4-1 - Updated to release 1.8.4. * Mon Jul 06 2015 Anders Waananen - 1.8.3-2 - Drop doc subpackage for el5 due to missing dependencies * Thu Jul 02 2015 Petter Urkedal - 1.8.3-1 - Updated to release 1.8.3. * Fri Mar 27 2015 Petter Urkedal - 1.8.2-1 - Updated to release 1.8.2. * Thu Jan 15 2015 Petter Urkedal - 1.8.2-0.rc2 - Updated to release candidate 1.8.2rc2. * Fri Jan 09 2015 Petter Urkedal - 1.8.2-0.rc1 - Updated to release candidate 1.8.2rc1. * Fri Aug 15 2014 Anders Waananen - 1.8.1-1 - Updated to release 1.8.1. * Fri Jun 27 2014 Petter Urkedal - 1.8.1-0.rc1 - Updated to release candidate 1.8.1rc1. * Wed Apr 30 2014 Petter Urkedal - 1.8.0-1 - Updated to release 1.8.0. * Tue Oct 22 2013 Petter Urkedal - 1.7.1-1 - Updated to release 1.7.1. * Fri Aug 16 2013 Petter Urkedal - 1.7.0-1 - Updated to release 1.7.0. * Fri Jul 05 2013 Petter Urkedal - 1.6.1-0.rc1 - Updated to release candidate 1.6.1rc1. * Fri Apr 19 2013 Petter Urkedal - 1.6.0-1 - Updated to release 1.6.0. * Sat Apr 06 2013 Petter Urkedal - 1.6.0-0.rc1 - Updated to release candidate 1.6.0rc1. * Mon Feb 18 2013 Petter Urkedal - 1.5.0-1 - Updated to release 1.5.0. * Fri Feb 01 2013 Petter Urkedal - 1.5.0-0.rc3 - Updated to release candidate 1.5.0rc3. * Mon Jan 28 2013 Petter Urkedal - 1.5.0-0.rc2 - Updated to release candidate 1.5.0rc2. * Fri Jan 11 2013 Petter Urkedal - 1.5.0-0.rc1 - Updated to release candidate 1.5.0rc1. * Thu Dec 20 2012 Petter Urkedal - 1.4.0-0.rc4 - Updated to release candidate 1.4.0rc4. * Tue Nov 27 2012 Petter Urkedal - 1.4.0-0.rc1 - Updated to release candidate 1.4.0rc1. * Mon Oct 29 2012 Petter Urkedal - 1.3.11-1 - Updated to release 1.3.11. * Wed Sep 26 2012 Petter Urkedal - 1.3.10-1 - Updated to release 1.3.10. * Fri Sep 07 2012 Petter Urkedal - 1.3.9-1 - Updated to release 1.3.9. * Mon Apr 23 2012 Petter Urkedal - 1.3.8-1 - Updated to release 1.3.8. * Tue Apr 03 2012 Petter Urkedal - 1.3.7-1 - Updated to release 1.3.7. * Mon Apr 02 2012 Petter Urkedal - 1.3.6-1 - Updated to release 1.3.6. * Thu Feb 02 2012 Petter Urkedal - 1.3.5-1 - Updated to release 1.3.5. * Thu Feb 02 2012 Petter Urkedal - 1.3.4-1 - Updated to release 1.3.4. * Thu Feb 02 2012 Petter Urkedal - 1.3.3-1 - Updated to release 1.3.3. * Wed Dec 21 2011 Petter Urkedal - 1.3.2-1 - Updated to release 1.3.2. * Mon Dec 19 2011 Petter Urkedal - 1.3.1-1 - Updated to release 1.3.1. * Thu Dec 08 2011 Petter Urkedal - 1.3.0-1 - Updated to release 1.3.0. * Wed Nov 23 2011 Petter Urkedal - 1.2.0-1 - Updated to release 1.2.0. * Mon Nov 14 2011 Petter Urkedal - Change README to README.rst. - Add documentation subpackage. * Fri Nov 04 2011 Petter Urkedal - 1.1.0-1 - Updated to release 1.1.0. * Thu Nov 03 2011 Petter Urkedal - Install default configuration file. * Wed Oct 26 2011 Petter Urkedal - 1.0.2-1 - Updated to release 1.0.2. * Thu Oct 20 2011 Petter Urkedal - 1.0.1-1 - Updated to release 1.0.1. * Tue Oct 18 2011 Petter Urkedal - Add argparse and nordugrid-arc-python dependencies. - Install README and LICENSE. * Fri Oct 14 2011 Petter Urkedal - 1.0-1 - Updated to release 1.0. - Almost complete rewrite for the new probes. * Fri Sep 30 2011 Anders Waananen - 0.9-1 - New package name and ownership * Thu Jun 30 2011 Mattias Ellert - 0.4-1 - Fix flags to stat * Thu Nov 18 2010 Mattias Ellert - 0.3-1 - Implement changes proposed by Emir * Mon Oct 11 2010 Mattias Ellert - 0.2-1 - Remove Requires (per WLCG practice) * Thu Sep 23 2010 Mattias Ellert - 0.1-1 - Initial packaging nordugrid-arc-nagios-plugins-1.8.4/Makefile0000644000175000002070000000230112546440362021363 0ustar mockbuildmockbuildVERSION = $(shell cat VERSION) BASEVERSION = $(VERSION:%[a-z]*=%) DEFRPMRELEASE = 1 default: @echo "Main target is 'dist'" @echo "VERSION = $(VERSION)" @echo "BASEVERSION = $(BASEVERSION)" @echo "PREVERSION = $(PREVERSION)" sdist: python setup.py $@ TEMPLATED = debian/changelog nordugrid-arc-nagios-plugins.spec $(TEMPLATED): %: %.in VERSION @VERSION=`cat VERSION|tr -d '\\n'`; \ BASEVERSION=$${VERSION%%[a-z]*}; \ PREVERSION=`echo -n $$VERSION | sed s/^[0-9.]*//`; \ RPMRELEASE=$${PREVERSION:+0.}$${PREVERSION:-$${DEFRPMRELEASE}}; \ DEBVERSION=$$BASEVERSION$${PREVERSION:+\~}$${PREVERSION}; \ DATER=`date +'%a, %d %b %Y %H:%M:%S %z'`; \ echo "Updating $@ to version $$VERSION."; \ sed -e "s;@VERSION@;$$VERSION;g" \ -e "s;@BASEVERSION@;$$BASEVERSION;g" \ -e "s;@PREVERSION@;$$PREVERSION;g" \ -e "s;@RPMRELEASE@;$$RPMRELEASE;g" \ -e "s;@DEBVERSION@;$$DEBVERSION;g" \ -e "s;@DATER@;$$DATER;g" \ < $< > $@ dist: $(TEMPLATED) sdist test -d dist && find dist -type f -name \*tar.gz -exec cp -p '{}' $(CURDIR) \; rpm: dist VERSION=`cat VERSION|tr -d '\\n'`; \ rpmbuild -tb nordugrid-arc-nagios-plugins-$$VERSION.tar.gz .PHONY: dist rpm nordugrid-arc-nagios-plugins-1.8.4/README.rst0000644000175000002070000000156011656763136021430 0ustar mockbuildmockbuild******************** ARC Nagios Plugins ******************** This README describes the Nagios plugins for ARC-1 including CEs and associated services. Documentation can be found in the ``doc`` subdirectory, as well as using the ``--help`` options of the plugins. If you have Sphinx installed, you can create nicer looking documentation by typing make -C doc html or using one of the other targets of the same makefile. The result is placed in a subdirectory of ``doc/_build``. Installation ============ This package uses Python distutils, so you can install it with :: python setup.py build sudo python setup.py install For customized installations, please refer to the manual http://docs.python.org/distutils/ or ``python setup.py --help``. The package also comes with an RPM spec, so it can be built similar to an SRPM using ``rpmbuild -tb ``. nordugrid-arc-nagios-plugins-1.8.4/setup.py0000644000175000002070000000243312311545247021441 0ustar mockbuildmockbuildimport os from distutils.core import setup from distutils.sysconfig import get_config_vars exec_prefix, libdir = get_config_vars('exec_prefix', 'LIBDIR') if libdir.startswith(exec_prefix + '/'): libdir = libdir[len(exec_prefix)+1:] plugindir = os.path.join(libdir, 'nagios/plugins') fd = open('VERSION','rw') version = fd.read().strip() fd.close() setup( name = 'nordugrid-arc-nagios-plugins', version = version, description = 'Nagios Probes for Arc CEs', url = 'http://www.nordugrid.org/', author = 'Petter Urkedal', author_email = 'urkedal@nbi.dk', requires = ['arc', 'argparse', 'ldap', 'ply'], packages = ['arcnagios', 'arcnagios.plugins', 'arcnagios.jobplugins'], data_files = [ (plugindir, [ 'plugins/check_arcce_clean', 'plugins/check_arcce_monitor', 'plugins/check_arcce_submit', 'plugins/check_aris', 'plugins/check_egiis', 'plugins/check_arcglue2', 'plugins/check_arcinfosys', 'plugins/check_archostcert', 'plugins/check_arcservice', 'plugins/check_gridstorage', ]), ('/etc/arc/nagios', ['config/20-dist.ini']), ('/etc/arc/nagios/20-dist.d', ['config/20-dist.d/default.jsdl']), ('/etc/arc/nagios', ['config/60-egi.ini']), ('/etc/arc/nagios/60-egi.d', ['config/60-egi.d/arcce_igtf.py']), ], ) nordugrid-arc-nagios-plugins-1.8.4/config/0000755000175000002070000000000012600623616021170 5ustar mockbuildmockbuildnordugrid-arc-nagios-plugins-1.8.4/config/60-egi.ini0000644000175000002070000000733412453742625022677 0ustar mockbuildmockbuild[variable.voms] method = option # The IGTF CA Certificates Probe # ============================== [variable.igtf_base_url] # Usage: check_arcce_submit -O igtf_base_url= # Overrides the location of the ITGF release files. # method = option default = http://repository.egi.eu/sw/production/cas/1/current/meta [arcce.dist-caversion] # Usage: check_arcce_submit --job-tag caversion --test caversion ... # Checks that all IGTF CA certificates are installed on the CE, that they # are up to date, and that there are no obsolete ITGF CAs installed. # jobplugin = scripted staged_inputs = file:%(config_dir)s/60-egi.d/arcce_igtf.py %(igtf_base_url)s/ca-policy-egi-core.release;cache=no %(igtf_base_url)s/ca-policy-egi-core.list;cache=no %(igtf_base_url)s/ca-policy-egi-core.obsoleted;cache=no output_file = caversion.out runtime_environments = ENV/PROXY script_line = python arcce_igtf.py >caversion.out service_description = org.nordugrid.ARC-CE-IGTF%(service_suffix)s # SRM Staging # =========== # Location of the BDII server to query for information about the VO-specific # location on storage elements. [variable.top_bdii] method = option default = ldap://lcg-bdii.cern.ch:2170 [variable.se_vo_dir] method = ldap uri = %(top_bdii)s basedn = mds-vo-name=local,o=grid filter = (&(objectClass=GlueVOInfo)(GlueChunkKey=GlueSEUniqueID=%(se_host)s)(GlueVOInfoAccessControlBaseRule=VO:%(voms)s)) attribute = GlueVOInfoPath, GlueSAPath [variable.good_ses_file] # Usage: check_arcce_submit -O good_ses_file= ... # The location of a file containing a list if known good storage elements # which can be used for Nagios staging tests. This is not needed if the # -O se_host= option is provided. # method = option [variable.se_host] # The host name of the storage element to use for staging tests. The # following picks a random line from the file specified in good_ses_file, # but it can be overriden with -O se_host=. # method = random_line input_file = %(good_ses_file)s [variable.se_test_dir] # Usage: check_arcce_submit -O se_test_dir=

... # A directory on se_host where test files can be written. # method = option default = %(se_vo_dir)s/nagios-%(local_hostname)s/arcce [variable.stage_stamp] method = strftime raw_format = %Y%m%dT%H%M [arcce.dist-stage-srm] # Usage: check_arcce_submit --job-tag srm --test dist-stage-srm ... # Performs staging tests using the SRM protocol. # jobplugin = staging upload_if_missing = srm://%(se_host)s%(se_test_dir)s/srm-input staged_inputs = srm://%(se_host)s%(se_test_dir)s/srm-input staged_outputs = srm://%(se_host)s%(se_test_dir)s/srm-%(stage_stamp)s-%(hostname)s service_description = org.nordugrid.ARC-CE-srm%(service_suffix)s # LFC Staging # =========== # # The LFC test also uses the above SRM settings. [variable.lfc_host] # Usage: check_arcce_submit -O lfc_host= ... # The host name of an LFC server which can be used for staging tests. This # must be set to a usable value when submitting arcce.dist-stage-lfc jobs. # method = option default = prod-lfc-shared-central.cern.ch [variable.lfc_test_dir] # The top-level logical directory to use for LFC staging tests. # method = option default = /grid/%(voms)s/nagios-%(local_hostname)s/arcce [arcce.dist-stage-lfc] # Usage: check_arcce_submit --job-tag srm --test dist-stage-lfc ... # Performs staging tests using LFC directory mapping. # jobplugin = staging upload_if_missing = lfc://srm://%(se_host)s%(se_test_dir)s/lfc-input@%(lfc_host)s%(lfc_test_dir)s/lfc-input staged_inputs = lfc://%(lfc_host)s%(lfc_test_dir)s/lfc-input staged_outputs = lfc://srm://%(se_host)s%(se_test_dir)s/lfc-%(stage_stamp)s-%(hostname)s@%(lfc_host)s%(lfc_test_dir)s/lfc-%(stage_stamp)s-%(hostname)s service_description = org.nordugrid.ARC-CE-lfc%(service_suffix)s nordugrid-arc-nagios-plugins-1.8.4/config/20-dist.ini0000644000175000002070000000550212314325544023056 0ustar mockbuildmockbuild[variable.service_suffix] # Usage: check_arcce_submit -O service_suffix= ... # Appends to all passive service names. # method = option default = [variable.local_hostname] # The host name of the Nagios server. method = pipe command = hostname # Information System Queries # ========================== [arcinfosys.aris.dist-queue-active] # Usage: check_aris --queue-test dist-queue-active ... # Checks that all queues on the CE is active. # type = regex variable = status critical.pattern = ^active$ critical.message = Not all queues are active. # Software Tests for the Main Job Submission Probe # ================================================ # # These tests for a few programs. They use regular expressions, assuming # that the commands write out the version information in a certain form. # If the probe should file, you can find the output matched against under # # /var/spool/nagios/plugins/arcce/$VO/$HOST/job_output/* # # and adjust the regular expressions. Please also report the issue to # http://bugzilla.nordugrid.org [arcce.dist-sw-csh] # Usage: check_arcce_submit --test dist-sw-csh ... # jobplugin = scripted #required_programs = csh script_line = echo >csh-test.csh '#! /bin/csh'; echo >>csh-test.csh 'env >csh.out'; chmod +x csh-test.csh; ./csh-test.csh output_file = csh.out output_pattern = ^PATH= status_ok = Found working csh. status_critical = Could not find \$PATH in the csh environment service_description = org.nordugrid.ARC-CE-sw-csh%(service_suffix)s [arcce.dist-sw-gcc] # Usage: check_arcce_submit --test dist-sw-gcc ... # jobplugin = scripted #required_programs = gcc script_line = gcc --version >gcc.out 2>&1 output_file = gcc.out output_pattern = gcc\s+(\(.*\)\s+)?(?P\d+\.\S+) status_ok = Found GCC version %(version)s. status_critical = Could not match GCC version. See /etc/arc/nagios/20-dist.ini for debugging hints. service_description = org.nordugrid.ARC-CE-sw-gcc%(service_suffix)s [arcce.dist-sw-python] # Usage: check_arcce_submit --test dist-sw-python ... # jobplugin = scripted #required_programs = python script_line = python -V >python.out 2>&1 output_file = python.out output_pattern = Python\s+(?P\S+) status_ok = Found Python version %(version)s. status_critical = Could not match Python version. See /etc/arc/nagios/20-dist.ini for debugging hints. service_description = org.nordugrid.ARC-CE-sw-python%(service_suffix)s [arcce.dist-sw-perl] # Usage: check_arcce_submit --test dist-sw-perl ... # jobplugin = scripted #required_programs = perl script_line = perl -v >perl.out 2>&1 output_file = perl.out output_pattern = This is [Pp]erl.*\bv(?P[0-9A-Za-z.-]+) status_ok = Found Perl version %(version)s. status_critical = Could not match Perl version. See /etc/arc/nagios/20-dist.ini for debugging hints. service_description = org.nordugrid.ARC-CE-sw-perl%(service_suffix)s nordugrid-arc-nagios-plugins-1.8.4/config/20-dist.d/0000755000175000002070000000000012600623616022574 5ustar mockbuildmockbuildnordugrid-arc-nagios-plugins-1.8.4/config/20-dist.d/default.jsdl0000644000175000002070000000451112311545247025101 0ustar mockbuildmockbuild ${jd.job_name} ${jd.application_name} ${jd.logdir} ${jd.script_name} ${arg} ${jd.output} ${jd.error} ${jd.wall_time_limit} ${jd.memory_limit} ${jd.script_name} file:${jd.script_path} overwrite ${filename} ${url}${o} false overwrite ${filename} false ${url}${o} true overwrite ${jd.queue_name} ${rte} nordugrid-arc-nagios-plugins-1.8.4/config/60-egi.d/0000755000175000002070000000000012600623616022401 5ustar mockbuildmockbuildnordugrid-arc-nagios-plugins-1.8.4/config/60-egi.d/arcce_igtf.py0000755000175000002070000001454612544753062025064 0ustar mockbuildmockbuild#!/usr/bin/python # Ulf Tigerstedt # NGI_FI and NGI_NDGF # Work in progress since 2011 # secret value 246094 do_hashing=True import os, re import glob import posixpath try: import hashlib, ssl except ImportError: do_hashing=False import datetime import sys import logging log = logging.getLogger() logging.basicConfig() igtffilename="ca-policy-egi-core.list" releasefilename="ca-policy-egi-core.release" obsoletedfilename="ca-policy-egi-core.obsoleted" if (len(sys.argv) == 2): ca_cert_location=sys.argv[1] else: ca_cert_location = os.getenv('X509_CERT_DIR') if ca_cert_location is None: print '__status 3 Did not receive a $X509_CERT_DIR, ' \ 'missing runtime environment?' sys.exit(3) if not os.path.exists(ca_cert_location): print '__status 3 Missing directory $X509_CERT_DIR (%s).'%ca_cert_location sys.exit(3) # What are the thresholds? days_warning=0 days_critical=7 try: igtflist = open(igtffilename, 'r') except: print '__status 3 Missing %s.'%igtffilename sys.exit(3) try: obsoletedlist = open(obsoletedfilename, 'r') except: print '__status 3 Missing %s.'%obsoletedlist sys.exit(3) currentonlynames=[] igtfonlynames=[] obsoletedonlynames=[] nagios_warning=0 nagios_critical=0 nagios_messages = [] def counted_noun(n, sg, pl = None): return '%d %s'%(n, n == 1 and sg or pl or sg + 's') def short_list(xs, limit = 4): if len(xs) <= limit: return ', '.join(xs) else: return ', '.join(xs[0:limit] + ['...']) def getreleasedateandversion(filename): """ Parse a EGI Trustanchor release file to get the release date and version """ try: releasefile = open(filename,'r') except: print "__status 3 Missing trustanchor release file %s."%filename sys.exit(3) for line in releasefile: if re.match("^.*",line): [crud,spaces,xml1,fulldate,xml2,crud2]=re.split("^(.*)()([0-9]{8})().*$",line) if re.match("^.*",line): [crud,spaces,xml1,releaseversion,packaging,xml2,crud2]=re.split("^(.*)()([0-9]+.[0-9]+)(-[0-9]+)().*$",line) return [fulldate,releaseversion] def getnameversion(filename): """ Read the alias and version number from a """ """ CA distribution .info file """ if posixpath.islink(filename): return ["","",""] caname = "" version = "" sha1fp0 = "" try: inf = open(filename,'r') except: print "__status 3 Missing CA info file %s."%filename sys.exit(3) for line in inf: if re.match('^alias.*',line): (junk, caname, _) = re.split(r'^alias\s*=\s*(.*)', line) #print caname if re.match('^version.*',line): (junk, version, _) = re.split(r'version\s*=\s*([0-9]+.[0-9]+)', line) #print version if re.match("^sha1fp\.0",line): (junk, sha1fp0, _) = re.split(r'sha1fp\.0\s*=\s*([0-9A-F:]{59})', line) #print sha1fp0 if ((caname != "") and (version != "") and (sha1fp0 != "")): return [caname,version,sha1fp0] return ["","",""] def checksha1fp(infofile,infohash): """ Hunt down a certificate that matches the .info file (which might be a .0 or a .pem). Then read the data and compute the SHA1 fingerprint of it """ # If this is and old python, don't do hashing if not do_hashing: return True ifile="" newfilepem = re.sub('\.info$','.pem',infofile) newfile0 = re.sub('\.info$','.0',infofile) if (posixpath.exists(newfilepem)): ifile=newfilepem elif (posixpath.exists(newfile0)): ifile=newfile0 if (ifile == ""): return False cleanedhash = re.sub(':','',infohash.lower()) try: fileh = open(ifile,'r') except: return False try: data = ssl.PEM_cert_to_DER_cert(fileh.read()) except Exception, xc: log.warn('%s: %s'%(ifile, xc)) return False hash = hashlib.sha1(data) fileh.close() if (hash.hexdigest() == cleanedhash): return True else: return False [releasedate,releaseversion] = getreleasedateandversion(releasefilename) for line in igtflist: if re.match('^ca_.*',line): [nonce, ca, caname, version, _endline] = re.split('(^ca_)(.*)-([0-9]+.[0-9]+-[0-9]+)$',line) igtfonlynames.append(caname) for line in obsoletedlist: if re.match('^[A-Za-z0-9].*',line): [nonce, obsolete, endline] = re.split('(^[A-Za-z0-9].*$)',line) obsoletedonlynames.append(obsolete) [junk,ryear,rmonth,rdate,_] = re.split("([0-9]{4})([0-9]{2})([0-9]{2})",releasedate) today=datetime.date.today() releasedate=datetime.date(int(ryear),int(rmonth),int(rdate)) difference=today-releasedate allinfos=glob.glob(ca_cert_location+'/*.info') present_obsolete = [] present_by_version = {} for cfile in allinfos: [caname,version,sha1fp0]=getnameversion(cfile); if (caname != ""): currentonlynames.append(caname) if ((caname in igtfonlynames) and (difference.days > days_warning) and (version < releaseversion)): if (difference.days > days_warning): nagios_warning += 1 if (difference.days > days_critical): nagios_critical += 1 if not version in present_by_version: present_by_version[version] = [] present_by_version[version].append(caname) if (caname in obsoletedonlynames): if (difference.days > days_warning): nagios_warning += 1 present_obsolete.append(caname) result=checksha1fp(cfile,sha1fp0) if (not result): nagios_critical += 1 nagios_messages.append("SHA Fingerprint failed for %s."%caname) nagios_issues = [] for version, canames in present_by_version.iteritems(): nagios_issues.append( 'found %s from %s (%s)' % (counted_noun(len(canames), 'CA'), version, short_list(canames))) log.error('CAs from %s: %s'%(version, ', '.join(canames))) if present_obsolete: nagios_issues.append( '%s obsolete (%s)' % (counted_noun(len(present_obsolete), 'CA is', 'CAs are'), short_list(present_obsolete))) log.error('Obsolete CAs: %s'%', '.join(present_obsolete)) missingcas=[i for i in igtfonlynames if i not in currentonlynames] if (len(missingcas) > 0): if (difference.days > days_warning): nagios_warning += 1 nagios_issues.append( 'missing %s (%s)' % (counted_noun(len(missingcas), 'CA'), short_list(missingcas))) log.error('Missing CAs: %s'%', '.join(missingcas)) if not nagios_issues: nagios_issues.append('all present') status = nagios_critical and 2 or nagios_warning and 1 or 0 msg = ', '.join(nagios_issues) print "__status %d IGTF-%s, %s old, %s." \ % (status, releaseversion, counted_noun(difference.days, 'day'), msg) for msg in nagios_messages: print "__log 40 ", msg nordugrid-arc-nagios-plugins-1.8.4/MANIFEST.in0000644000175000002070000000073712311545247021472 0ustar mockbuildmockbuildinclude plugins/check_* include config/20-dist.ini include config/20-dist.d/*.jsdl include config/60-egi.ini include config/60-egi.d/*.jsdl include config/60-egi.d/*.py include nordugrid-arc-nagios-plugins.spec* include jobscripts/* include README.rst LICENSE AUTHORS NOTICE include doc/Makefile include doc/conf.py include doc/*.example include doc/*.rst include doc/media/*.png include debian/* include debian/source/format include Makefile include VERSION include MANIFEST.in nordugrid-arc-nagios-plugins-1.8.4/plugins/0000755000175000002070000000000012600623616021404 5ustar mockbuildmockbuildnordugrid-arc-nagios-plugins-1.8.4/plugins/check_aris0000755000175000002070000000053012306354206023422 0ustar mockbuildmockbuild#! /usr/bin/python # # ARIS Probe import logging, sys try: from arcnagios.plugins.aris import Check_aris except ImportError, xc: sys.stdout.write('UNKNOWN: Error loading modules : %s\n\n' 'sys.path = %s\n' % (xc, sys.path)) sys.exit(3) logging.basicConfig() # for manual invocation probe = Check_aris() probe.nagios_run() nordugrid-arc-nagios-plugins-1.8.4/plugins/check_arcce_clean0000755000175000002070000000057312306354206024712 0ustar mockbuildmockbuild#! /usr/bin/python # # ARCCE Job Probe: Cleanup import logging, sys try: from arcnagios.plugins.arcce_clean import Check_arcce_clean except ImportError, xc: sys.stdout.write('UNKNOWN: Error loading modules : %s\n\n' 'sys.path = %s\n' % (xc, sys.path)) sys.exit(3) logging.basicConfig() # for manual invocation probe = Check_arcce_clean() probe.nagios_run() nordugrid-arc-nagios-plugins-1.8.4/plugins/check_arcservice0000755000175000002070000001014412306354206024614 0ustar mockbuildmockbuild#!/usr/bin/python # # Contributed by Nagy Zsombor. # Support for GLUE 2.0r1 by Gabor Roczei. # Released under the Apache License with permission from Gabor Roczei. # See also http://bugzilla.nordugrid.org/show_bug.cgi?id=1983. import httplib import urlparse import sys try: import xml.etree.ElementTree as ET except ImportError: import elementtree.ElementTree as ET import getopt import signal import traceback glue_schemas = [ 'http://schemas.ogf.org/glue/2008/05/spec_2.0_d41_r01', 'http://schemas.ogf.org/glue/2009/03/spec_2.0_r1', ] timeout = 10 def handler(signum, frame): print "ARCSERVICE UNKNOWN:", "Check has timed out (after %s seconds)" % timeout sys.exit(3) signal.signal(signal.SIGALRM, handler) signal.alarm(timeout) def usage(): print """Usage: check_arcservice -u -k -c -t --debug -u the URL of the service to check (mandatory) -t after this amount of seconds the check will return UNKNOWN (default: 10) -k path of the key file (default: /etc/grid-security/hostkey.pem) -c path of the cert file (default: /etc/grid-security/hostcert.pem) --debug print some debugging information """ sys.exit(3) try: options, args = getopt.getopt(sys.argv[1:],"u:k:c:t:",["debug"]) except getopt.GetoptError: usage() key_file = '/etc/grid-security/hostkey.pem' cert_file = '/etc/grid-security/hostcert.pem' url = '' debug = False for name, value in options: if name in ['-k']: key_file = value if name in ['-c']: cert_file = value if name in ['-u']: url = value if name in ['--debug']: debug = True if name in ['-t']: timeout = int(value) signal.alarm(timeout) if not key_file or not cert_file or not url: usage() try: parsed = urlparse.urlparse(url) https = (parsed[0] == 'https') hostport = parsed[1] path = parsed[2] except: print "ARCSERVICE UNKNOWN: Error parsing URL", url get_resource_property_document_request = ''' http://docs.oasis-open.org/wsrf/rpw-2/GetResourcePropertyDocument/GetResourcePropertyDocumentRequest ''' if https: connection = httplib.HTTPSConnection(host = hostport, key_file = key_file, cert_file = cert_file) else: connection = httplib.HTTPConnection(host = hostport) try: connection.request('POST', path, get_resource_property_document_request) except Exception, e: print "ARCSERVICE CRITICAL:", "Connecting to", url, "failed (%s)" % e if debug: traceback.print_exc() sys.exit(2) response = connection.getresponse() if response.status == 200: data = response.read() try: et = ET.fromstring(data) health_state = None for glue_schema in glue_schemas: health_state = et.findtext(".//{%s}HealthState"%glue_schema) if health_state: break if health_state is None: print "ARCSERVICE UNKNOWN:", "Service's health state is unknown" if debug: print data sys.exit(3) if health_state == 'ok': print "ARCSERVICE OK:", "Service's health state is", health_state if debug: print data sys.exit(0) else: print "ARCSERVICE CRITICAL:", "Service's health state is", health_state if debug: print data sys.exit(2) except StandardError, e: print "ARCSERVICE UNKNOWN:", "Unable to parse respone (%s)" % e if debug: print data sys.exit(3) else: print "ARCSERVICE CRITICAL:", "Invalid response from server (%s, %s)" % (response.status, response.reason) if debug: print response.read() sys.exit(2) nordugrid-arc-nagios-plugins-1.8.4/plugins/check_arcinfosys0000755000175000002070000000065412306354206024653 0ustar mockbuildmockbuild#! /usr/bin/python # !! IMPORTANT !! # # This probe is DEPRECATED. Please use check_arcglue2, check_aris, or # check_egiis. import sys, logging try: from arcnagios.plugins.arcinfosys import ARCInfosysProbe except ImportError, xc: sys.stdout.write('UNKNOWN: Error loading modules : %s\n\n' 'sys.path = %s\n' % (xc, sys.path)) sys.exit(3) logging.basicConfig() probe = ARCInfosysProbe() probe.nagios_run() nordugrid-arc-nagios-plugins-1.8.4/plugins/check_arcce_monitor0000755000175000002070000000060412306354206025312 0ustar mockbuildmockbuild#! /usr/bin/python # # ARCCE Job Probe: Monitoring import logging, sys try: from arcnagios.plugins.arcce_monitor import Check_arcce_monitor except ImportError, xc: sys.stdout.write('UNKNOWN: Error loading modules : %s\n\n' 'sys.path = %s\n' % (xc, sys.path)) sys.exit(3) logging.basicConfig() # for manual invocation probe = Check_arcce_monitor() probe.nagios_run() nordugrid-arc-nagios-plugins-1.8.4/plugins/check_archostcert0000755000175000002070000000533712306354206025017 0ustar mockbuildmockbuild#! /usr/bin/python import getopt, ldap, sys from datetime import datetime, timedelta def show_timedelta(dt): if dt.days: return '%s days'%dt.days if dt.seconds >= 2*3600: return '%s h'%(dt.seconds / 3600) if dt.seconds >= 2*60: return '%s min'%(dt.seconds / 60) return '%s s'%dt.seconds _usage = """ Usage: check_archostcert -H HOST [OPTIONS] Options: -H HOST The host name or IP number to probe. -p PORT The port number of the infosystem, defaults to 2135. -c T Report critical when the lifetime left is less than T days. -w T Report warning when the lifetime left is less than T days. -t T Time out after T seconds. \n""" try: host = None port = 2135 timeout = 30 crit_days = 7 warn_days = 31 opts, args = getopt.gnu_getopt(sys.argv, 'H:c:w:t:p:', ['help']) for opt, arg in opts: if opt == '--help': sys.stdout.write(_usage) sys.exit(0) elif opt == '-H': host = arg elif opt == '-c': crit_days = int(arg) elif opt == '-w': warn_days = int(arg) elif opt == '-t': timeout = int(arg) elif opt == '-p': port = int(arg) if host is None: raise getopt.GetoptError('The -H option is required.') except getopt.GetoptError, xc: sys.stderr.write('%s\n' % xc) sys.exit(64) try: lconn = ldap.initialize('ldap://%s:%d'%(host, port)) entries = lconn.search_st('mds-vo-name=local,o=grid', ldap.SCOPE_SUBTREE, '(&(objectClass=nordugrid-cluster)' '(nordugrid-cluster-credentialexpirationtime=*))', attrlist = ['nordugrid-cluster-credentialexpirationtime'], timeout = timeout) dt_min = None for dn, entry in entries: xt_str = entry['nordugrid-cluster-credentialexpirationtime'][0] yr, mo, da = int(xt_str[0:4]), int(xt_str[4:6]), int(xt_str[6:8]) ho, mi, se = int(xt_str[8:10]), int(xt_str[10:12]), int(xt_str[12:14]) dt = datetime(yr, mo, da, ho, mi, se) - datetime.now() if dt_min is None or dt < dt_min: dt_min = dt if dt_min is None: print 'No expiration time published in infosystem.' sys.exit(1) elif dt_min <= timedelta(0): print 'Host certificate expired %s ago.' % show_timedelta(-dt_min) sys.exit(2) elif dt_min <= timedelta(crit_days): print 'Host certificate will expire in %s.' % show_timedelta(dt_min) sys.exit(2) elif dt_min <= timedelta(warn_days): print 'Host certificate will expire in %s.' % show_timedelta(dt_min) sys.exit(1) else: print 'Host certificate valid (%s left).' % show_timedelta(dt_min) sys.exit(0) except (ldap.TIMEOUT, ldap.SERVER_DOWN): print 'Cannot contact infosystem.' sys.exit(3) except SystemExit, xc: # In old Python versions SystemExit inherits Exception. raise xc except Exception, xc: print 'Probe received exception: %r'%xc sys.exit(3) nordugrid-arc-nagios-plugins-1.8.4/plugins/check_arcglue20000755000175000002070000000054512306354206024176 0ustar mockbuildmockbuild#! /usr/bin/python # # GLUE2 Probe import logging, sys try: from arcnagios.plugins.arcglue2 import Check_arcglue2 except ImportError, xc: sys.stdout.write('UNKNOWN: Error loading modules : %s\n\n' 'sys.path = %s\n' % (xc, sys.path)) sys.exit(3) logging.basicConfig() # for manual invocation probe = Check_arcglue2() probe.nagios_run() nordugrid-arc-nagios-plugins-1.8.4/plugins/check_arcce_submit0000755000175000002070000000060112306354206025123 0ustar mockbuildmockbuild#! /usr/bin/python # # ARCCE Job Probe: Submission import logging, sys try: from arcnagios.plugins.arcce_submit import Check_arcce_submit except ImportError, xc: sys.stdout.write('UNKNOWN: Error loading modules : %s\n\n' 'sys.path = %s\n' % (xc, sys.path)) sys.exit(3) logging.basicConfig() # for manual invocation probe = Check_arcce_submit() probe.nagios_run() nordugrid-arc-nagios-plugins-1.8.4/plugins/check_egiis0000755000175000002070000000053412306354206023570 0ustar mockbuildmockbuild#! /usr/bin/python # # EGIIS Probe import logging, sys try: from arcnagios.plugins.egiis import Check_egiis except ImportError, xc: sys.stdout.write('UNKNOWN: Error loading modules : %s\n\n' 'sys.path = %s\n' % (xc, sys.path)) sys.exit(3) logging.basicConfig() # for manual invocation probe = Check_egiis() probe.nagios_run() nordugrid-arc-nagios-plugins-1.8.4/plugins/check_gridstorage0000755000175000002070000000047612306354206025007 0ustar mockbuildmockbuild#! /usr/bin/python import sys, logging try: from arcnagios.plugins.gridstorage import GridStorageProbe except ImportError, xc: sys.stdout.write('UNKNOWN: Error loading modules: %s\n\nsys.path = %r\n' %(xc, sys.path)) sys.exit(3) logging.basicConfig() probe = GridStorageProbe() probe.nagios_run() nordugrid-arc-nagios-plugins-1.8.4/debian/0000755000175000002070000000000012600623616021145 5ustar mockbuildmockbuildnordugrid-arc-nagios-plugins-1.8.4/debian/rules0000755000175000002070000000213612317077702022233 0ustar mockbuildmockbuild#!/usr/bin/make -f -include /usr/share/dpkg/buildflags.mk configure: configure-stamp : configure-stamp: dh_testdir touch $@ build: build-arch build-indep : build-arch: build-stamp : build-indep: build-stamp python setup.py build mkdir -p doc/_build/html doc/_static make -C doc html rm -f doc/_build/html/.buildinfo build-stamp: configure-stamp : touch $@ clean: dh_testdir dh_testroot dh_clean configure-stamp build-stamp install: build-stamp dh_testdir dh_testroot dh_clean -k python setup.py install --prefix=/usr --root=debian/tmp --skip-build find debian/tmp -name \*.egg-info -exec rm -f '{}' \; binary-indep: dh_testdir dh_testroot dh_installdirs dh_installdocs dh_install --fail-missing dh_installchangelogs [ -x /usr/bin/dh_sphinxdoc ] && dh_sphinxdoc || : dh_compress -X html dh_pysupport [ -x /usr/bin/dh_lintian ] && dh_lintian || : dh_link dh_fixperms dh_installdeb dh_gencontrol dh_md5sums dh_builddeb binary-arch: install : binary: binary-arch binary-indep : .PHONY: build-arch build-indep build clean binary-arch binary-indep binary install configure nordugrid-arc-nagios-plugins-1.8.4/debian/nordugrid-arc-nagios-plugins.install0000644000175000002070000000120312317077702030232 0ustar mockbuildmockbuilddebian/tmp/etc/arc/nagios/20-dist.ini debian/tmp/etc/arc/nagios/20-dist.d/default.jsdl debian/tmp/usr/lib/nagios/plugins/check_arcce_clean debian/tmp/usr/lib/nagios/plugins/check_arcce_monitor debian/tmp/usr/lib/nagios/plugins/check_arcce_submit debian/tmp/usr/lib/nagios/plugins/check_arcinfosys debian/tmp/usr/lib/nagios/plugins/check_egiis debian/tmp/usr/lib/nagios/plugins/check_aris debian/tmp/usr/lib/nagios/plugins/check_arcglue2 debian/tmp/usr/lib/nagios/plugins/check_archostcert debian/tmp/usr/lib/nagios/plugins/check_arcservice debian/tmp/usr/lib/nagios/plugins/check_gridstorage debian/tmp/usr/lib/python*/site-packages/arcnagios nordugrid-arc-nagios-plugins-1.8.4/debian/changelog.in0000644000175000002070000000461112574511537023436 0ustar mockbuildmockbuildnordugrid-arc-nagios-plugins (1.8.4-1) unstable; urgency=low * New upstream release. -- Anders Waananen Fri, 11 Sep 2015 10:38:38 +0200 nordugrid-arc-nagios-plugins (1.8.3-1) unstable; urgency=low * New upstream release. -- Anders Waananen Thu, 02 Jul 2015 15:01:23 +0200 nordugrid-arc-nagios-plugins (1.8.2-1) unstable; urgency=low * New upstream release. -- Anders Waananen Fri, 27 Mar 2015 15:05:18 +0100 nordugrid-arc-nagios-plugins (1.8.2~rc2-1) unstable; urgency=low * New upstream release. -- Anders Waananen Thu, 15 Jan 2015 12:28:01 +0100 nordugrid-arc-nagios-plugins (1.8.2~rc1-1) unstable; urgency=low * New upstream release. -- Anders Waananen Fri, 09 Jan 2015 13:50:12 +0100 nordugrid-arc-nagios-plugins (1.8.1-1) unstable; urgency=low * New upstream release. -- Anders Waananen Fri, 15 Aug 2014 22:44:44 +0200 nordugrid-arc-nagios-plugins (1.8.1~rc1-1) unstable; urgency=low * New upstream release. -- Anders Waananen Sun, 06 Jul 2014 22:37:39 +0200 nordugrid-arc-nagios-plugins (1.8.0-1) unstable; urgency=low * New upstream release. -- Anders Waananen Wed, 30 Apr 2014 21:18:03 +0200 nordugrid-arc-nagios-plugins (1.7.0-1) unstable; urgency=low * New upstream release. -- Anders Waananen Mon, 25 Nov 2013 21:25:48 +0100 nordugrid-arc-nagios-plugins (1.6.0-1) unstable; urgency=low * New upstream release. -- Anders Waananen Sat, 20 Apr 2013 00:55:28 +0200 nordugrid-arc-nagios-plugins (1.6.0~rc1-1) unstable; urgency=low * New upstream release. -- Anders Waananen Wed, 10 Apr 2013 19:10:25 +0200 nordugrid-arc-nagios-plugins (1.5.0-1) unstable; urgency=low * New upstream release. -- Anders Waananen Wed, 20 Feb 2013 01:22:27 +0100 nordugrid-arc-nagios-plugins (1.4.0~rc1-1) unstable; urgency=low * 1.4.0 Release candidate 1 -- Anders Waananen Wed, 28 Nov 2012 12:51:47 +0100 nordugrid-arc-nagios-plugins (1.3.11-1) unstable; urgency=low * New upstream release. -- Anders Waananen Mon, 29 Oct 2012 18:22:25 +0100 nordugrid-arc-nagios-plugins (1.3.10-1) unstable; urgency=low * New upstream release. -- Anders Waananen Wed, 26 Sep 2012 17:08:36 +0200 nordugrid-arc-nagios-plugins-1.8.4/debian/source/0000755000175000002070000000000012600623616022445 5ustar mockbuildmockbuildnordugrid-arc-nagios-plugins-1.8.4/debian/source/format0000644000175000002070000000001411710260633023650 0ustar mockbuildmockbuild3.0 (quilt) nordugrid-arc-nagios-plugins-1.8.4/debian/copyright0000644000175000002070000000235411710260633023101 0ustar mockbuildmockbuildThis package was debianized by Anders Wäänänen on Mon, 26 Jan 2012 11:06:54 +0100. It was downloaded from http://www.nordugrid.org Upstream Authors: Petter Urkedal Anders Wäänänen Copyright: Copyright (C) 2006-2009 by the respective employers of the the above authors: University of Copenhagen, Denmark Niels Bohr Institute, Copenhagen, Denmark License: Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. On Debian systems, the complete text of the Apache version 2.0 license can be found in `/usr/share/common-licenses/Apache-2.0'. The Debian packaging was prepared by Anders Wäänänen of the upstream developers and is also licensed under the Apache 2.0 license. nordugrid-arc-nagios-plugins-1.8.4/debian/nordugrid-arc-nagios-plugins-doc.docs0000644000175000002070000000002012127641676030261 0ustar mockbuildmockbuilddoc/_build/html nordugrid-arc-nagios-plugins-1.8.4/debian/nordugrid-arc-nagios-plugins.dirs0000644000175000002070000000007512317077702027533 0ustar mockbuildmockbuildetc/arc/nagios etc/arc/nagios/20-dist.d var/spool/arc/nagios nordugrid-arc-nagios-plugins-1.8.4/debian/compat0000644000175000002070000000000211710260633022340 0ustar mockbuildmockbuild5 nordugrid-arc-nagios-plugins-1.8.4/debian/nordugrid-arc-nagios-plugins-egi.dirs0000644000175000002070000000003012317077702030264 0ustar mockbuildmockbuildetc/arc/nagios/60-egi.d nordugrid-arc-nagios-plugins-1.8.4/debian/control0000644000175000002070000000253012317077702022554 0ustar mockbuildmockbuildSource: nordugrid-arc-nagios-plugins Section: net Priority: optional Maintainer: Mattias Ellert Uploaders: Anders Waananen DM-Upload-Allowed: yes Build-Depends: debhelper (>= 5), python, python-setuptools, python-sphinx, python-support XS-Python-Version: current Standards-Version: 3.9.2 Vcs-Browser: http://svn.nordugrid.org/trac/nordugrid/browser/nagios Vcs-Svn: http://svn.nordugrid.org/repos/nordugrid/nagios Homepage: http://www.nordugrid.org Package: nordugrid-arc-nagios-plugins Architecture: all Depends: ${misc:Depends}, ${python:Depends}, python, python-argparse, python-ldap, nordugrid-arc-client (>= 1.0.0), nordugrid-arc-python (>= 1.0.0), glue-schema (>= 2.0.8) Description: NorduGrid ARC Nagios plugins This package provides the Nagios plugins for testing ARC CE, using the ARC-1 API. Package: nordugrid-arc-nagios-plugins-doc Architecture: all Depends: ${misc:Depends}, ${python:Depends}, ${sphinxdoc:Depends} Section: doc Description: NorduGrid ARC Nagios plugins documentation This package contains HTML documentation for the ARC nagios probes. Package: nordugrid-arc-nagios-plugins-egi Architecture: all Depends: ${misc:Depends}, ${python:Depends}, python Description: EGI configuration and dependencies for the ARC Nagios plugins This package contains EGI specific probe(s) and configuration. nordugrid-arc-nagios-plugins-1.8.4/debian/nordugrid-arc-nagios-plugins.docs0000644000175000002070000000011512317077702027515 0ustar mockbuildmockbuildAUTHORS NOTICE README.rst doc/arcnagios.ini.example doc/services.cfg.example nordugrid-arc-nagios-plugins-1.8.4/debian/changelog0000644000175000002070000000461112600623616023021 0ustar mockbuildmockbuildnordugrid-arc-nagios-plugins (1.8.4-1) unstable; urgency=low * New upstream release. -- Anders Waananen Fri, 11 Sep 2015 10:38:38 +0200 nordugrid-arc-nagios-plugins (1.8.3-1) unstable; urgency=low * New upstream release. -- Anders Waananen Thu, 02 Jul 2015 15:01:23 +0200 nordugrid-arc-nagios-plugins (1.8.2-1) unstable; urgency=low * New upstream release. -- Anders Waananen Fri, 27 Mar 2015 15:05:18 +0100 nordugrid-arc-nagios-plugins (1.8.2~rc2-1) unstable; urgency=low * New upstream release. -- Anders Waananen Thu, 15 Jan 2015 12:28:01 +0100 nordugrid-arc-nagios-plugins (1.8.2~rc1-1) unstable; urgency=low * New upstream release. -- Anders Waananen Fri, 09 Jan 2015 13:50:12 +0100 nordugrid-arc-nagios-plugins (1.8.1-1) unstable; urgency=low * New upstream release. -- Anders Waananen Fri, 15 Aug 2014 22:44:44 +0200 nordugrid-arc-nagios-plugins (1.8.1~rc1-1) unstable; urgency=low * New upstream release. -- Anders Waananen Sun, 06 Jul 2014 22:37:39 +0200 nordugrid-arc-nagios-plugins (1.8.0-1) unstable; urgency=low * New upstream release. -- Anders Waananen Wed, 30 Apr 2014 21:18:03 +0200 nordugrid-arc-nagios-plugins (1.7.0-1) unstable; urgency=low * New upstream release. -- Anders Waananen Mon, 25 Nov 2013 21:25:48 +0100 nordugrid-arc-nagios-plugins (1.6.0-1) unstable; urgency=low * New upstream release. -- Anders Waananen Sat, 20 Apr 2013 00:55:28 +0200 nordugrid-arc-nagios-plugins (1.6.0~rc1-1) unstable; urgency=low * New upstream release. -- Anders Waananen Wed, 10 Apr 2013 19:10:25 +0200 nordugrid-arc-nagios-plugins (1.5.0-1) unstable; urgency=low * New upstream release. -- Anders Waananen Wed, 20 Feb 2013 01:22:27 +0100 nordugrid-arc-nagios-plugins (1.4.0~rc1-1) unstable; urgency=low * 1.4.0 Release candidate 1 -- Anders Waananen Wed, 28 Nov 2012 12:51:47 +0100 nordugrid-arc-nagios-plugins (1.3.11-1) unstable; urgency=low * New upstream release. -- Anders Waananen Mon, 29 Oct 2012 18:22:25 +0100 nordugrid-arc-nagios-plugins (1.3.10-1) unstable; urgency=low * New upstream release. -- Anders Waananen Wed, 26 Sep 2012 17:08:36 +0200 nordugrid-arc-nagios-plugins-1.8.4/debian/nordugrid-arc-nagios-plugins-egi.install0000644000175000002070000000012712317077702031000 0ustar mockbuildmockbuilddebian/tmp/etc/arc/nagios/60-egi.ini debian/tmp/etc/arc/nagios/60-egi.d/arcce_igtf.py* nordugrid-arc-nagios-plugins-1.8.4/nordugrid-arc-nagios-plugins.spec.in0000644000175000002070000002012012574511537026704 0ustar mockbuildmockbuild# Disable debuginfo since there are no binaries %global debug_package %{nil} %if %{?fedora}%{!?fedora:0} >= 8 || %{?rhel}%{!?rhel:0} >= 6 %global enable_doc 1 %else %global enable_doc 0 %endif %global site org.nordugrid %global nagios_bindir %{_libdir}/nagios/plugins %global arc_spooldir %{_localstatedir}/spool/arc %global pkg_spooldir %{arc_spooldir}/nagios %global pkg_sysconfdir %{_sysconfdir}/arc/nagios %{!?__python2: %global __python2 %{__python}} %{!?python2_sitelib: %global python2_sitelib %(%{__python2} -c 'from distutils import sysconfig; print sysconfig.get_python_lib()')} Name: nordugrid-arc-nagios-plugins Version: @BASEVERSION@ Release: @RPMRELEASE@%{?dist} Summary: Nagios plugins for ARC Group: System Environment/Daemons License: ASL 2.0 URL: http://www.nordugrid.org Source0: http://download.nordugrid.org/packages/%{name}/releases/%{version}/src/%{name}-%{version}@PREVERSION@.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) Requires: nordugrid-arc-client >= 1.0.0 Requires: nordugrid-arc-python >= 1.0.0 Requires: python-argparse Requires: python-ldap Requires: python-genshi Requires: python-ply Requires: nagios-plugins Requires: glue-schema >= 2.0.8 %if %{?rhel}%{!?rhel:6} <= 5 Requires: python-elementtree Requires: python-sqlite %endif %if %{enable_doc} BuildRequires: python-sphinx %endif %if %{?fedora}%{!?fedora:0} <= 12 BuildRequires: python-devel %endif %description This package provides the Nagios plugins for testing ARC CE, using the ARC-1 API. %if %{enable_doc} %package doc Summary: HTML documentation for ARC 1 Nagios plugins Group: Documentation %if %{?fedora}%{!?fedora:0} >= 10 || %{?rhel}%{!?rhel:0} >= 6 BuildArch: noarch %endif %endif %if %{enable_doc} %description doc HTML documentation for %{name}. %endif %package egi Summary: EGI configuration and dependencies for the ARC Nagios plugins Group: System Environment/Daemons BuildArch: noarch Requires: nordugrid-arc-plugins-globus >= 1.0.0 %description egi EGI configuration and dependencies for the ARC Nagios plugins %prep %setup -q -n %{name}-%{version}@PREVERSION@ %build %{__python2} setup.py build %if %{enable_doc} mkdir -p doc/_build/html doc/_static %if %{?rhel}%{!?rhel:6} <= 5 make -C doc html SPHINXBUILD=sphinx-1.0-build %else make -C doc html %endif rm -f doc/_build/html/.buildinfo %endif %install test %{buildroot} != / && rm -rf %{buildroot} %{__python2} setup.py install --root=%{buildroot} --skip-build install -m755 -d %{buildroot}%{_sysconfdir}/nagios/plugins install -m755 -d %{buildroot}%{pkg_spooldir} %clean test %{buildroot} != / && rm -rf %{buildroot} %files %dir %{pkg_sysconfdir} %dir %{pkg_sysconfdir}/20-dist.d %config(noreplace) %{pkg_sysconfdir}/20-dist.ini %config(noreplace) %{pkg_sysconfdir}/20-dist.d/default.jsdl %{nagios_bindir}/check_arcce_clean %{nagios_bindir}/check_arcce_monitor %{nagios_bindir}/check_arcce_submit %{nagios_bindir}/check_aris %{nagios_bindir}/check_arcglue2 %{nagios_bindir}/check_egiis %{nagios_bindir}/check_arcinfosys %{nagios_bindir}/check_archostcert %{nagios_bindir}/check_arcservice %{nagios_bindir}/check_gridstorage %{python2_sitelib}/arcnagios # Not sure precisely which Fedora version the egg-info becomes relevant. %if %{?rhel}%{!?rhel:0} >= 6 || %{?fedora}%{!?fedora:0} >= 9 %{python2_sitelib}/nordugrid_arc_nagios_plugins-*.egg-info %endif %dir %{arc_spooldir} %attr(-,nagios,nagios) %{pkg_spooldir} %doc AUTHORS README.rst LICENSE NOTICE %doc doc/arcnagios.ini.example %doc doc/services.cfg.example %if %{enable_doc} %files doc %doc AUTHORS README.rst LICENSE NOTICE %doc doc/_build/html %endif %files egi %doc AUTHORS README.rst LICENSE NOTICE %dir %{pkg_sysconfdir}/60-egi.d %config(noreplace) %{pkg_sysconfdir}/60-egi.ini # FIXME: Prevent rpmbuild from generating these compiled objects: %config(noreplace) %{pkg_sysconfdir}/60-egi.d/arcce_igtf.py* %changelog * Fri Sep 11 2015 Petter Urkedal - 1.8.4-1 - Updated to release 1.8.4. * Mon Jul 06 2015 Anders Waananen - 1.8.3-2 - Drop doc subpackage for el5 due to missing dependencies * Thu Jul 02 2015 Petter Urkedal - 1.8.3-1 - Updated to release 1.8.3. * Fri Mar 27 2015 Petter Urkedal - 1.8.2-1 - Updated to release 1.8.2. * Thu Jan 15 2015 Petter Urkedal - 1.8.2-0.rc2 - Updated to release candidate 1.8.2rc2. * Fri Jan 09 2015 Petter Urkedal - 1.8.2-0.rc1 - Updated to release candidate 1.8.2rc1. * Fri Aug 15 2014 Anders Waananen - 1.8.1-1 - Updated to release 1.8.1. * Fri Jun 27 2014 Petter Urkedal - 1.8.1-0.rc1 - Updated to release candidate 1.8.1rc1. * Wed Apr 30 2014 Petter Urkedal - 1.8.0-1 - Updated to release 1.8.0. * Tue Oct 22 2013 Petter Urkedal - 1.7.1-1 - Updated to release 1.7.1. * Fri Aug 16 2013 Petter Urkedal - 1.7.0-1 - Updated to release 1.7.0. * Fri Jul 05 2013 Petter Urkedal - 1.6.1-0.rc1 - Updated to release candidate 1.6.1rc1. * Fri Apr 19 2013 Petter Urkedal - 1.6.0-1 - Updated to release 1.6.0. * Sat Apr 06 2013 Petter Urkedal - 1.6.0-0.rc1 - Updated to release candidate 1.6.0rc1. * Mon Feb 18 2013 Petter Urkedal - 1.5.0-1 - Updated to release 1.5.0. * Fri Feb 01 2013 Petter Urkedal - 1.5.0-0.rc3 - Updated to release candidate 1.5.0rc3. * Mon Jan 28 2013 Petter Urkedal - 1.5.0-0.rc2 - Updated to release candidate 1.5.0rc2. * Fri Jan 11 2013 Petter Urkedal - 1.5.0-0.rc1 - Updated to release candidate 1.5.0rc1. * Thu Dec 20 2012 Petter Urkedal - 1.4.0-0.rc4 - Updated to release candidate 1.4.0rc4. * Tue Nov 27 2012 Petter Urkedal - 1.4.0-0.rc1 - Updated to release candidate 1.4.0rc1. * Mon Oct 29 2012 Petter Urkedal - 1.3.11-1 - Updated to release 1.3.11. * Wed Sep 26 2012 Petter Urkedal - 1.3.10-1 - Updated to release 1.3.10. * Fri Sep 07 2012 Petter Urkedal - 1.3.9-1 - Updated to release 1.3.9. * Mon Apr 23 2012 Petter Urkedal - 1.3.8-1 - Updated to release 1.3.8. * Tue Apr 03 2012 Petter Urkedal - 1.3.7-1 - Updated to release 1.3.7. * Mon Apr 02 2012 Petter Urkedal - 1.3.6-1 - Updated to release 1.3.6. * Thu Feb 02 2012 Petter Urkedal - 1.3.5-1 - Updated to release 1.3.5. * Thu Feb 02 2012 Petter Urkedal - 1.3.4-1 - Updated to release 1.3.4. * Thu Feb 02 2012 Petter Urkedal - 1.3.3-1 - Updated to release 1.3.3. * Wed Dec 21 2011 Petter Urkedal - 1.3.2-1 - Updated to release 1.3.2. * Mon Dec 19 2011 Petter Urkedal - 1.3.1-1 - Updated to release 1.3.1. * Thu Dec 08 2011 Petter Urkedal - 1.3.0-1 - Updated to release 1.3.0. * Wed Nov 23 2011 Petter Urkedal - 1.2.0-1 - Updated to release 1.2.0. * Mon Nov 14 2011 Petter Urkedal - Change README to README.rst. - Add documentation subpackage. * Fri Nov 04 2011 Petter Urkedal - 1.1.0-1 - Updated to release 1.1.0. * Thu Nov 03 2011 Petter Urkedal - Install default configuration file. * Wed Oct 26 2011 Petter Urkedal - 1.0.2-1 - Updated to release 1.0.2. * Thu Oct 20 2011 Petter Urkedal - 1.0.1-1 - Updated to release 1.0.1. * Tue Oct 18 2011 Petter Urkedal - Add argparse and nordugrid-arc-python dependencies. - Install README and LICENSE. * Fri Oct 14 2011 Petter Urkedal - 1.0-1 - Updated to release 1.0. - Almost complete rewrite for the new probes. * Fri Sep 30 2011 Anders Waananen - 0.9-1 - New package name and ownership * Thu Jun 30 2011 Mattias Ellert - 0.4-1 - Fix flags to stat * Thu Nov 18 2010 Mattias Ellert - 0.3-1 - Implement changes proposed by Emir * Mon Oct 11 2010 Mattias Ellert - 0.2-1 - Remove Requires (per WLCG practice) * Thu Sep 23 2010 Mattias Ellert - 0.1-1 - Initial packaging nordugrid-arc-nagios-plugins-1.8.4/AUTHORS0000644000175000002070000000072512116403630020771 0ustar mockbuildmockbuildIndividual contributors ----------------------- Zsombor Nagy Gábor Roczei Ulf Tigerstedt Petter Urkedal Anders Wäänänen Organizations employing contributors ------------------------------------ University of Copenhagen (Denmark) CSC - IT Center for Science Ltd (Finland) NIIFI - Nemzeti Információs Infrastruktúra Fejlesztési Intézet (Hungary) NordForsk (Norway) nordugrid-arc-nagios-plugins-1.8.4/LICENSE0000644000175000002070000002367612116376525020754 0ustar mockbuildmockbuild Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS nordugrid-arc-nagios-plugins-1.8.4/PKG-INFO0000644000175000002070000000047212600623616021023 0ustar mockbuildmockbuildMetadata-Version: 1.1 Name: nordugrid-arc-nagios-plugins Version: 1.8.4 Summary: Nagios Probes for Arc CEs Home-page: http://www.nordugrid.org/ Author: Petter Urkedal Author-email: urkedal@nbi.dk License: UNKNOWN Description: UNKNOWN Platform: UNKNOWN Requires: arc Requires: argparse Requires: ldap Requires: ply nordugrid-arc-nagios-plugins-1.8.4/NOTICE0000644000175000002070000000167312116376525020644 0ustar mockbuildmockbuildARC Nagios Plugins ------------------ This product includes Nagios plugins for ARC services. The software is developed by the NorduGrid collaboration (http://www.nordugrid.org) with financial support from the European Commission. Unless stated otherwise, the Copyright is collectively owned by individual contributors and contributing organisations as listed in the AUTHORS file. The software is licensed under the Apache License, Version 2.0 (the "License"); you may not use files from this software distribution except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. nordugrid-arc-nagios-plugins-1.8.4/VERSION0000644000175000002070000000000612574511537020777 0ustar mockbuildmockbuild1.8.4