#!/usr/bin/python # -*- encoding: utf-8; py-indent-offset: 4 -*- # +------------------------------------------------------------------+ # | ____ _ _ __ __ _ __ | # | / ___| |__ ___ ___| | __ | \/ | |/ / | # | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / | # | | |___| | | | __/ (__| < | | | | . \ | # | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ | # | | # | Copyright Mathias Kettner 2014 mk@mathias-kettner.de | # +------------------------------------------------------------------+ # # This file is part of Check_MK. # The official homepage is at http://mathias-kettner.de/check_mk. # # check_mk 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 in version 2. check_mk is distributed # in the hope that it will be useful, but WITHOUT ANY WARRANTY; with- # out even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. See the GNU General Public License for more de- # tails. You should have received a copy of the GNU General Public # License along with GNU Make; see the file COPYING. If not, write # to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, # Boston, MA 02110-1301 USA. # Check_MK-Agent-Plugin - Apache Server Status # # Fetches the server-status page from detected or configured apache # processes to gather status information about this apache process. # # To make this agent plugin work you have to load the status_module # into your apache process. It is also needed to enable the "server-status" # handler below the URL "/server-status". # # By default this plugin tries to detect all locally running apache processes # and to monitor them. If this is not good for your environment you might # create an apache_status.cfg file in MK_CONFDIR and populate the servers # list to prevent executing the detection mechanism. # # It is also possible to override or extend the ssl_ports variable to make the # check contact other ports than 443 with HTTPS requests. import os, sys, urllib2, re, socket config_dir = os.getenv("MK_CONFDIR", "/etc/check_mk") config_file = config_dir + "/apache_status.conf" if not os.path.exists(config_file): config_file = config_dir + "/apache_status.cfg" # We have to deal with socket timeouts. Python > 2.6 # supports timeout parameter for the urllib2.urlopen method # but we are on a python 2.5 system here which seem to use the # default socket timeout. We are local here so set it to 1 second. socket.setdefaulttimeout(5.0) # None or list of (proto, ipaddress, port) tuples. # proto is 'http' or 'https' servers = None ssl_ports = [ 443, ] if os.path.exists(config_file): execfile(config_file) def try_detect_servers(): results = [] for line in os.popen('netstat -tlnp 2>/dev/null').readlines(): parts = line.split() # Skip lines with wrong format if len(parts) < 7 or '/' not in parts[6]: continue pid, proc = parts[6].split('/', 1) to_replace = re.compile('^.*/') proc = to_replace.sub('', proc) procs = [ 'apache2', 'httpd', 'httpd2-prefork', 'httpd2-worker', 'httpd.worker', 'fcgi-pm' ] # the pid/proc field length is limited to 19 chars. Thus in case of # long PIDs, the process names are stripped of by that length. # Workaround this problem here procs = [ p[:19 - len(pid) - 1] for p in procs ] # Skip unwanted processes if proc not in procs: continue address, port = parts[3].rsplit(':', 1) port = int(port) # Use localhost when listening globally if address == '0.0.0.0': address = '127.0.0.1' elif address == '::': address = '[::1]' elif ':' in address: address = '[%s]' % address # Switch protocol if port is SSL port. In case you use SSL on another # port you would have to change/extend the ssl_port list if port in ssl_ports: proto = 'https' else: proto = 'http' results.append((proto, address, port)) return results if servers is None: servers = try_detect_servers() if not servers: sys.exit(0) sys.stdout.write('<<>>\n') for server in servers: if isinstance(server, tuple): proto, address, port = server page = 'server-status' else: proto = server['protocol'] address = server['address'] port = server['port'] page = server.get('page', 'server-status') portspec = port and ":%d" % port or "" try: url = '%s://%s%s/%s?auto' % (proto, address, portspec, page) # Try to fetch the status page for each server try: request = urllib2.Request(url, headers={"Accept" : "text/plain"}) fd = urllib2.urlopen(request) except urllib2.URLError, e: if 'unknown protocol' in str(e): # HACK: workaround misconfigurations where port 443 is used for # serving non ssl secured http url = 'http://%s%s/server-status?auto' % (address, portspec) fd = urllib2.urlopen(url) else: raise except Exception, e: if 'doesn\'t match' in str(e): # HACK: workaround if SSL port is found and localhost is using # SSL connections but certificate does not match portspec = ':80' url = 'http://%s%s/server-status?auto' % (address, portspec) fd = urllib2.urlopen(url) else: raise for line in fd.read().split('\n'): if not line.strip(): continue if line.lstrip()[0] == '<': # Seems to be html output. Skip this server. break sys.stdout.write("%s %s %s\n" % (address, port, line)) except urllib2.HTTPError, e: sys.stderr.write('HTTP-Error (%s%s): %s %s\n' % (address, portspec, e.code, e)) except Exception, e: sys.stderr.write('Exception (%s%s): %s\n' % (address, portspec, e))