mirror of
https://github.com/Hopiu/linkchecker.git
synced 2026-03-25 02:10:24 +00:00
Add Nagios plugin.
This commit is contained in:
parent
a6eaae2c38
commit
2f730f4e13
5 changed files with 329 additions and 100 deletions
5
Makefile
5
Makefile
|
|
@ -6,7 +6,7 @@ PLATFORM:=$(shell $(PYTHON) -c "from distutils.util import get_platform; print g
|
|||
FILESCHECK_URL:=http://localhost/~calvin/
|
||||
PYTHONSRC:=${HOME}/src/cpython-hg/Lib
|
||||
#PYTHONSRC:=/usr/lib/$(PYTHON)
|
||||
PY_FILES_DIRS:=linkcheck tests *.py linkchecker linkchecker-gui cgi-bin config doc
|
||||
PY_FILES_DIRS:=linkcheck tests *.py linkchecker linkchecker-nagios linkchecker-gui cgi-bin config doc
|
||||
TESTS ?= tests/
|
||||
# set test options, eg. to "--nologcapture"
|
||||
TESTOPTS=
|
||||
|
|
@ -186,6 +186,7 @@ doccheck:
|
|||
cgi-bin/lc.wsgi \
|
||||
linkchecker \
|
||||
linkchecker-gui \
|
||||
linkchecker-nagios \
|
||||
*.py
|
||||
|
||||
filescheck: localbuild
|
||||
|
|
@ -252,7 +253,7 @@ gui:
|
|||
|
||||
.PHONY: count
|
||||
count:
|
||||
@sloccount linkchecker linkchecker-gui linkcheck | grep "Total Physical Source Lines of Code"
|
||||
@sloccount linkchecker linkchecker-gui linkchecker-nagios linkcheck | grep "Total Physical Source Lines of Code"
|
||||
|
||||
# run eclipse ide
|
||||
.PHONY: ide
|
||||
|
|
|
|||
132
linkcheck/cmdline.py
Normal file
132
linkcheck/cmdline.py
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
# -*- coding: iso-8859-1 -*-
|
||||
# Copyright (C) 2000-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.
|
||||
"""
|
||||
Utility functions suitable for command line clients.
|
||||
"""
|
||||
import sys
|
||||
import optparse
|
||||
from . import fileutil, ansicolor, strformat, add_intern_pattern, checker
|
||||
from .director import console
|
||||
from .decorators import notimplemented
|
||||
|
||||
def print_version(exit_code=0):
|
||||
"""Print the program version and exit."""
|
||||
console.print_version()
|
||||
sys.exit(exit_code)
|
||||
|
||||
|
||||
def print_usage (msg, exit_code=2):
|
||||
"""Print a program msg text to stderr and exit."""
|
||||
program = sys.argv[0]
|
||||
print >> console.stderr, _("Error: %(msg)s") % {"msg": msg}
|
||||
print >> console.stderr, _("Execute '%(program)s -h' for help") % {"program": program}
|
||||
sys.exit(exit_code)
|
||||
|
||||
|
||||
class LCHelpFormatter (optparse.IndentedHelpFormatter, object):
|
||||
"""Help formatter indenting paragraph-wise."""
|
||||
|
||||
def __init__ (self):
|
||||
"""Set current console width for this formatter."""
|
||||
width = ansicolor.get_columns(sys.stdout)
|
||||
super(LCHelpFormatter, self).__init__(width=width)
|
||||
|
||||
def format_option (self, option):
|
||||
"""Customized help display with indentation."""
|
||||
# The help for each option consists of two parts:
|
||||
# * the opt strings and metavars
|
||||
# eg. ("-x", or "-fFILENAME, --file=FILENAME")
|
||||
# * the user-supplied help string
|
||||
# eg. ("turn on expert mode", "read data from FILENAME")
|
||||
|
||||
# If possible, we write both of these on the same line:
|
||||
# -x turn on expert mode
|
||||
|
||||
# But if the opt string list is too long, we put the help
|
||||
# string on a second line, indented to the same column it would
|
||||
# start in if it fit on the first line.
|
||||
# -fFILENAME, --file=FILENAME
|
||||
# read data from FILENAME
|
||||
result = []
|
||||
opts = self.option_strings[option]
|
||||
opt_width = self.help_position - self.current_indent - 2
|
||||
if len(opts) > opt_width:
|
||||
opts = "%*s%s\n" % (self.current_indent, "", opts)
|
||||
indent_first = self.help_position
|
||||
else: # start help on same line as opts
|
||||
opts = "%*s%-*s " % (self.current_indent, "", opt_width, opts)
|
||||
indent_first = 0
|
||||
result.append(opts)
|
||||
if option.help:
|
||||
text = strformat.wrap(option.help, self.help_width)
|
||||
help_lines = text.splitlines()
|
||||
result.append("%*s%s\n" % (indent_first, "", help_lines[0]))
|
||||
result.extend(["%*s%s\n" % (self.help_position, "", line)
|
||||
for line in help_lines[1:]])
|
||||
elif opts[-1] != "\n":
|
||||
result.append("\n")
|
||||
return "".join(result)
|
||||
|
||||
|
||||
class LCOptionParser (optparse.OptionParser, object):
|
||||
"""Option parser with custom help text layout."""
|
||||
|
||||
def __init__ (self, err_exit_code=2):
|
||||
"""Initializing using our own help formatter class."""
|
||||
super(LCOptionParser, self).__init__(formatter=LCHelpFormatter())
|
||||
self.err_exit_code = err_exit_code
|
||||
|
||||
def error (self, msg):
|
||||
"""Print usage info and given message."""
|
||||
print_usage(msg, exit_code=self.err_exit_code)
|
||||
|
||||
@notimplemented
|
||||
def get_usage (self):
|
||||
pass
|
||||
|
||||
def print_help_msg (self, s, out):
|
||||
"""Print a help message to stdout."""
|
||||
s = console.encode(s)
|
||||
if fileutil.is_tty(out):
|
||||
strformat.paginate(s)
|
||||
else:
|
||||
print >>out, s
|
||||
sys.exit(0)
|
||||
|
||||
@notimplemented
|
||||
def print_help (self, file=None):
|
||||
pass
|
||||
|
||||
|
||||
def aggregate_url (aggregate, config, url, err_exit_code=2):
|
||||
"""Append given commandline URL to input queue."""
|
||||
get_url_from = checker.get_url_from
|
||||
if url.lower().startswith("www."):
|
||||
# syntactic sugar
|
||||
url = "http://%s" % url
|
||||
elif url.lower().startswith("ftp."):
|
||||
# syntactic sugar
|
||||
url = "ftp://%s" % url
|
||||
url_data = get_url_from(url, 0, aggregate)
|
||||
try:
|
||||
add_intern_pattern(url_data, config)
|
||||
except UnicodeError:
|
||||
log.error(LOG_CMDLINE,
|
||||
_("URL has unparsable domain name: %(domain)s") %
|
||||
{"domain": sys.exc_info()[1]})
|
||||
sys.exit(err_exit_code)
|
||||
aggregate.urlqueue.put(url_data)
|
||||
104
linkchecker
104
linkchecker
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/python -u
|
||||
# -*- coding: iso-8859-1 -*-
|
||||
# Copyright (C) 2000-2012 Bastian Kleineidam
|
||||
#
|
||||
|
|
@ -33,6 +33,8 @@ 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, i18n, strformat
|
||||
import linkcheck.checker
|
||||
import linkcheck.configuration
|
||||
|
|
@ -195,19 +197,6 @@ file entry:
|
|||
for tag, desc in sorted(linkcheck.checker.const.Warnings.items())])
|
||||
|
||||
|
||||
def print_version ():
|
||||
"""Print the program version and exit."""
|
||||
console.print_version()
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def print_usage (msg):
|
||||
"""Print a program msg text to stderr and exit."""
|
||||
print >> console.stderr, _("Error: %(msg)s") % {"msg": msg}
|
||||
print >> console.stderr, _("Execute 'linkchecker -h' for help")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def viewprof ():
|
||||
"""Print profiling data and exit."""
|
||||
if not has_pstats:
|
||||
|
|
@ -246,62 +235,8 @@ def has_encoding (encoding):
|
|||
except LookupError:
|
||||
return False
|
||||
|
||||
|
||||
class LCHelpFormatter (optparse.IndentedHelpFormatter, object):
|
||||
"""Help formatter indenting paragraph-wise."""
|
||||
|
||||
def __init__ (self):
|
||||
"""Set current console width for this formatter."""
|
||||
width = linkcheck.ansicolor.get_columns(sys.stdout)
|
||||
super(LCHelpFormatter, self).__init__(width=width)
|
||||
|
||||
def format_option (self, option):
|
||||
"""Customized help display with indentation."""
|
||||
# The help for each option consists of two parts:
|
||||
# * the opt strings and metavars
|
||||
# eg. ("-x", or "-fFILENAME, --file=FILENAME")
|
||||
# * the user-supplied help string
|
||||
# eg. ("turn on expert mode", "read data from FILENAME")
|
||||
|
||||
# If possible, we write both of these on the same line:
|
||||
# -x turn on expert mode
|
||||
|
||||
# But if the opt string list is too long, we put the help
|
||||
# string on a second line, indented to the same column it would
|
||||
# start in if it fit on the first line.
|
||||
# -fFILENAME, --file=FILENAME
|
||||
# read data from FILENAME
|
||||
result = []
|
||||
opts = self.option_strings[option]
|
||||
opt_width = self.help_position - self.current_indent - 2
|
||||
if len(opts) > opt_width:
|
||||
opts = "%*s%s\n" % (self.current_indent, "", opts)
|
||||
indent_first = self.help_position
|
||||
else: # start help on same line as opts
|
||||
opts = "%*s%-*s " % (self.current_indent, "", opt_width, opts)
|
||||
indent_first = 0
|
||||
result.append(opts)
|
||||
if option.help:
|
||||
text = strformat.wrap(option.help, self.help_width)
|
||||
help_lines = text.splitlines()
|
||||
result.append("%*s%s\n" % (indent_first, "", help_lines[0]))
|
||||
result.extend(["%*s%s\n" % (self.help_position, "", line)
|
||||
for line in help_lines[1:]])
|
||||
elif opts[-1] != "\n":
|
||||
result.append("\n")
|
||||
return "".join(result)
|
||||
|
||||
|
||||
class LCOptionParser (optparse.OptionParser, object):
|
||||
"""Option parser with custom help text layout."""
|
||||
|
||||
def __init__ (self):
|
||||
"""Initializing using our own help formatter class."""
|
||||
super(LCOptionParser, self).__init__(formatter=LCHelpFormatter())
|
||||
|
||||
def error (self, msg):
|
||||
"""Print usage info and given message."""
|
||||
print_usage(msg)
|
||||
# instantiate option parser and configure options
|
||||
class MyOptionParser (LCOptionParser):
|
||||
|
||||
def get_usage (self):
|
||||
"""Return translated usage text."""
|
||||
|
|
@ -312,15 +247,10 @@ class LCOptionParser (optparse.OptionParser, object):
|
|||
s = u"%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s" % (self.format_help(),
|
||||
Examples, LoggerTypes, RegularExpressions, CookieFormat,
|
||||
ProxySupport, Notes, Retval, Warnings)
|
||||
s = console.encode(s)
|
||||
if linkcheck.fileutil.is_tty(sys.stdout):
|
||||
strformat.paginate(s)
|
||||
else:
|
||||
print s
|
||||
sys.exit(0)
|
||||
self.print_help_msg(s)
|
||||
|
||||
# instantiate option parser and configure options
|
||||
optparser = LCOptionParser()
|
||||
optparser = MyOptionParser()
|
||||
|
||||
# build a config object for this check session
|
||||
config = linkcheck.configuration.Configuration()
|
||||
|
|
@ -524,26 +454,6 @@ if has_optcomplete:
|
|||
optcomplete.autocomplete(optparser, arg_completer=FileCompleter)
|
||||
|
||||
|
||||
def aggregate_url (aggregate, config, url):
|
||||
"""Append given commandline URL to input queue."""
|
||||
get_url_from = linkcheck.checker.get_url_from
|
||||
if url.lower().startswith("www."):
|
||||
# syntactic sugar
|
||||
url = "http://%s" % url
|
||||
elif url.lower().startswith("ftp."):
|
||||
# syntactic sugar
|
||||
url = "ftp://%s" % url
|
||||
url_data = get_url_from(url, 0, aggregate)
|
||||
try:
|
||||
linkcheck.add_intern_pattern(url_data, config)
|
||||
except UnicodeError:
|
||||
log.error(LOG_CMDLINE,
|
||||
_("URL has unparsable domain name: %(domain)s") %
|
||||
{"domain": sys.exc_info()[1]})
|
||||
sys.exit(1)
|
||||
aggregate.urlqueue.put(url_data)
|
||||
|
||||
|
||||
def read_stdin_urls ():
|
||||
"""Read list of URLs, separated by white-space, from stdin."""
|
||||
urls = []
|
||||
|
|
|
|||
183
linkchecker-nagios
Executable file
183
linkchecker-nagios
Executable file
|
|
@ -0,0 +1,183 @@
|
|||
#!/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)
|
||||
5
setup.py
5
setup.py
|
|
@ -29,6 +29,9 @@ It includes the following features:
|
|||
- automatic MANIFEST.in check
|
||||
- automatic generation of .mo locale files
|
||||
- automatic permission setting on POSIX systems for installed files
|
||||
|
||||
Because of all the features, this script is nasty and big.
|
||||
Change it very careful.
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
|
@ -581,7 +584,7 @@ library_dirs = []
|
|||
# libraries
|
||||
libraries = []
|
||||
# scripts
|
||||
scripts = ['linkchecker', 'linkchecker-gui']
|
||||
scripts = ['linkchecker', 'linkchecker-gui', 'linkchecker-nagios']
|
||||
|
||||
if os.name == 'nt':
|
||||
# windows does not have unistd.h
|
||||
|
|
|
|||
Loading…
Reference in a new issue