#!/usr/bin/python -u # -*- coding: iso-8859-1 -*- # Copyright (C) 2012 Bastian Kleineidam # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """ Check HTML pages for broken links. This is the commandline nagios plugin. Run this file without options to see how it's done. """ import sys import os import socket import optparse import pprint # installs _() and _n() gettext functions into global namespace import linkcheck # override optparse gettext method with the one from linkcheck.init_i18n() optparse._ = _ # now import the rest of the linkchecker gang from linkcheck.cmdline import print_version, print_usage, LCOptionParser, \ aggregate_url from linkcheck import log, LOG_CMDLINE, strformat import linkcheck.checker import linkcheck.configuration import linkcheck.fileutil import linkcheck.logger import linkcheck.ansicolor from linkcheck.director import console, check_urls, get_aggregate # usage texts Usage = _("""USAGE\tlinkchecker-nagios [options] [file-or-url]...""") Retval = _(r"""RETURN VALUE 0 - everything checked ok without errors or warnings 1 - URL check warnings were found 2 - URL check errors were found 3 - internal or config error """) Examples = _(r"""EXAMPLES linkchecker-nagios -v www.example.org """) class MyOptionParser (LCOptionParser): """Option parser for LinkChecker nagios plugin.""" def get_usage (self): """Return translated usage text.""" return Usage def print_help (self, file=None): """Print translated help text.""" s = u"%s\n%s\n%s" % (self.format_help(), Examples, Retval) if file is None: file = sys.stdout self.print_help_msg(s, file) # instantiate option parser and configure options optparser = MyOptionParser(err_exit_code=3) # build a config object for this check session config = linkcheck.configuration.Configuration() # default recursion level is 1 config['recursionlevel'] = 1 # Standard options group = optparse.OptionGroup(optparser, _("Standard nagios options")) group.add_option("-v", "--verbose", action="count", default=0, dest="verbose", help=_("""Increase verbosity. This option can be given multiple times.""")) group.add_option("-V", "--version", action="store_true", dest="version", help=_("""Print version and exit.""")) group.add_option("-t", "--timeout", type="int", dest="timeout", metavar="NUMBER", help=_( """Set the timeout for connection attempts in seconds. The default timeout is %d seconds.""") % config["timeout"]) # Checking options group = optparse.OptionGroup(optparser, _("Checking options")) group.add_option("-f", "--config", type="string", dest="configfile", metavar="FILENAME", help=_( """Use FILENAME as configuration file. Per default LinkChecker uses ~/.linkchecker/linkcheckerrc (under Windows %HOMEPATH%\\.linkchecker\\linkcheckerrc).""")) # read and parse command line options and arguments (options, args) = optparser.parse_args() if options.version is not None: print_version() if options.timeout is not None: if options.timeout > 0: config["timeout"] = options.timeout else: print_usage(_("Illegal argument %(arg)r for option %(option)s") % \ {"arg": options.timeout, "option": "'--timeout'"}, exit_code=3) socket.setdefaulttimeout(config["timeout"]) if options.verbose >= 3: debug = ['all'] else: debug = None # initialize logging config.init_logging(console.StatusLogger(), debug=debug) log.debug(LOG_CMDLINE, _("Python %(version)s on %(platform)s") % \ {"version": sys.version, "platform": sys.platform}) # read configuration files try: files = [] if options.configfile: path = linkcheck.configuration.normpath(options.configfile) if os.path.isfile(path): files.append(path) else: log.warn(LOG_CMDLINE, _("Unreadable config file: %r"), options.configfile) sys.exit(3) config.read(files=files) except linkcheck.LinkCheckerError, msg: # config error print_usage(str(msg), exit_code=3) linkcheck.drop_privileges() socket.setdefaulttimeout(config["timeout"]) if options.verbose < 0: config['logger'] = config.logger_new('none') else: config["verbose"] = True config["warnings"] = True if options.verbose >= 2: config["complete"] = True # check missing passwords for entry in config["authentication"]: if entry["password"] is None: pattern = entry["pattern"].pattern log.warn(LOG_CMDLINE, _("missing password for pattern %(pattern)s") % dict(pattern=pattern)) sys.exit(3) # sanitize the configuration config.sanitize() log.debug(LOG_CMDLINE, "configuration: %s", pprint.pformat(sorted(config.items()))) # prepare checking queue aggregate = get_aggregate(config) # add urls to queue if args: for url in args: aggregate_url(aggregate, config, strformat.stripurl(url), err_exit_code=3) else: log.warn(LOG_CMDLINE, _("no files or URLs given")) sys.exit(3) # finally, start checking check_urls(aggregate) stats = config['logger'].stats # on internal errors, exit with status 3 if stats.internal_errors: sys.exit(3) # on errors, exit with status 2 if stats.errors: sys.exit(2) # on warnings, exit with status 1 if stats.warnings_printed and config['warnings']: sys.exit(1)