#!/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. import urllib2, sys, os, socket, base64, ssl from httplib import HTTPConnection, HTTPSConnection try: from simplejson import json except ImportError: try: import json except ImportError: sys.stdout.write("<<>>\n") sys.stdout.write("Error: Missing JSON library for Agent Plugin mk_jolokia\n") exit() opt_verbose = '--verbose' in sys.argv opt_debug = '--debug' in sys.argv class PreemptiveBasicAuthHandler(urllib2.HTTPBasicAuthHandler): """ sends basic authentication with the first request, before the server even asks for it """ def http_request(self, req): url = req.get_full_url() realm = None user, pw = self.passwd.find_user_password(realm, url) if pw: raw = "%s:%s" % (user, pw) auth = 'Basic %s' % base64.b64encode(raw).strip() req.add_unredirected_header(self.auth_header, auth) return req https_request = http_request class HTTPSValidatingConnection(HTTPSConnection): def __init__(self, host, ca_file, key_file, cert_file): HTTPSConnection.__init__(self, host, key_file=key_file, cert_file=cert_file) self.__ca_file = ca_file self.__key_file = key_file self.__cert_file = cert_file def connect(self): HTTPConnection.connect(self) if self.__ca_file: self.sock = ssl.wrap_socket(self.sock, keyfile=self.key_file, certfile=self.cert_file, ca_certs=self.__ca_file, cert_reqs=ssl.CERT_REQUIRED) else: self.sock = ssl.wrap_socket(self.sock, keyfile=self.key_file, certfile=self.cert_file, ca_certs=self.__ca_file, cert_reqs=ssl.CERT_NONE) class HTTPSAuthHandler(urllib2.HTTPSHandler): def __init__(self, ca_file, key, cert): urllib2.HTTPSHandler.__init__(self) self.__ca_file = ca_file self.__key = key self.__cert = cert def https_open(self, req): # do_open expects a class as the first parameter but getConnection will act # as a facotry function return self.do_open(self.getConnection, req) def getConnection(self, host, timeout): return HTTPSValidatingConnection(host, ca_file=self.__ca_file, key_file=self.__key, cert_file=self.__cert) def fetch_url_get(base_url, path, function): if path: url = "%s/%s/%s" % (base_url, function, path) else: url = base_url + "/" if opt_verbose: sys.stderr.write("DEBUG: Fetching: %s\n" % url) try: json_data = urllib2.urlopen(url).read() if opt_verbose: sys.stderr.write("DEBUG: Result: %s\n\n" % json_data) except Exception, e: if opt_debug: raise sys.stderr.write("ERROR: %s\n" % e) return [] return json_data def fetch_url_post(base_url, path, service_url, service_user, service_password, function): segments = path.split("/") data = { "type": function.upper(), "mbean": segments[0], "attribute": segments[1], "target": { "url": service_url, }, } if len(segments) > 2: data["path"] = segments[2] if service_user: data["target"]["user"] = service_user data["target"]["password"] = service_password if opt_verbose: sys.stderr.write("DEBUG: Fetching: %s\n" % base_url) try: json_data = urllib2.urlopen(base_url, data=json.dumps(data)).read() if opt_verbose: sys.stderr.write("DEBUG: Result: %s\n\n" % json_data) except Exception, e: if opt_debug: raise sys.stderr.write("ERROR: %s\n" % e) return [] return json_data def fetch_var(protocol, server, port, path, suburi, itemspec, service_url, service_user, service_password, function="read"): base_url = "%s://%s:%d/%s" % (protocol, server, port, suburi) if service_url is not None: json_data = fetch_url_post(base_url, path, service_url, service_user, service_password, function) else: json_data = fetch_url_get(base_url, path, function) try: obj = json.loads(json_data) except Exception, e: sys.stderr.write('ERROR: Invalid json code (%s)\n' % e) sys.stderr.write(' Response %s\n' % json_data) return [] if obj.get('status', 200) != 200: sys.stderr.write('ERROR: Invalid response when fetching url %s\n' % base_url) sys.stderr.write(' Response: %s\n' % json_data) return [] # Only take the value of the object. If the value is an object # take the first items first value. # {'Catalina:host=localhost,path=\\/test,type=Manager': {'activeSessions': 0}} if 'value' not in obj: if opt_verbose: sys.stderr.write("ERROR: not found: %s\n" % path) return [] val = obj.get('value', None) return make_item_list((), val, itemspec) # convert single values into lists of items in # case value is a 1-levelled or 2-levelled dict def make_item_list(path, value, itemspec): if type(value) != dict: if type(value) == str: value = value.replace(r'\/', '/') return [(path, value)] else: result = [] for key, subvalue in value.items(): # Handle filtering via itemspec miss = False while itemspec and '=' in itemspec[0]: if itemspec[0] not in key: miss = True break itemspec = itemspec[1:] if miss: continue item = extract_item(key, itemspec) if not item: item = (key,) result += make_item_list(path + item, subvalue, []) return result # Example: # key = 'Catalina:host=localhost,path=\\/,type=Manager' # itemsepc = [ "path" ] # --> "/" def extract_item(key, itemspec): path = key.split(":", 1)[-1] components = path.split(",") item = () comp_dict = {} for comp in components: parts = comp.split("=") if len(parts) == 2: left, right = parts comp_dict[left] = right for pathkey in itemspec: if pathkey in comp_dict: right = comp_dict[pathkey] right = right.replace(r'\/', '/') if '/' in right: right = '/' + right.split('/')[-1] item = item + (right,) return item def fetch_metric(inst, path, title, itemspec, inst_add=None): values = fetch_var(inst["protocol"], inst["server"], inst["port"], path, inst["suburi"], itemspec, inst["service_url"], inst["service_user"], inst["service_password"]) for subinstance, value in values: if not subinstance and not title: sys.stdout.write("INTERNAL ERROR: %s\n" % value) continue if "threadStatus" in subinstance or "threadParam" in subinstance: continue if len(subinstance) > 1: item = ",".join((inst["instance"],) + subinstance[:-1]) elif inst_add is not None: item = inst["instance"] + "," + inst_add else: item = inst["instance"] if title: if subinstance: tit = title + "." + subinstance[-1] else: tit = title else: tit = subinstance[-1] yield (item.replace(" ", "_"), tit, value) def query_instance(inst): # Prepare user/password authentication via HTTP Auth password_mngr = urllib2.HTTPPasswordMgrWithDefaultRealm() if inst.get("password"): password_mngr.add_password(None, "%s://%s:%d/" % (inst["protocol"], inst["server"], inst["port"]), inst["user"], inst["password"]) handlers = [] if inst["protocol"] == "https": if inst["mode"] == 'https' and (inst["client_key"] is None or inst["client_cert"] is None): sys.stdout.write('<<>>\n') sys.stderr.write("ERROR: https set up as authentication method but certificate " "wasn't provided\n") return handlers.append(HTTPSAuthHandler(inst["cert_path"], inst["client_key"], inst["client_cert"])) if inst["mode"] == 'digest': handlers.append(urllib2.HTTPDigestAuthHandler(password_mngr)) elif inst["mode"] == "basic_preemptive": handlers.append(PreemptiveBasicAuthHandler(password_mngr)) elif inst["mode"] == "basic" and inst["protocol"] != "https": handlers.append(urllib2.HTTPBasicAuthHandler(password_mngr)) if handlers: opener = urllib2.build_opener(*handlers) urllib2.install_opener(opener) # Determine type of server server_info = fetch_var(inst["protocol"], inst["server"], inst["port"], "", inst["suburi"], "", None, None, None) sys.stdout.write('<<>>\n') if server_info: d = dict(server_info) version = d.get(('info', 'version'), "unknown") product = d.get(('info', 'product'), "unknown") if inst.get("product"): product = inst["product"] agentversion = d.get(('agent',), "unknown") sys.stdout.write("%s %s %s %s\n" % (inst["instance"], product, version, agentversion)) else: sys.stdout.write("%s ERROR\n" % (inst["instance"],)) sys.stdout.write('<<>>\n') sys.stdout.write("%s ERROR\n" % (inst["instance"],)) return mbean_search_results = {} sys.stdout.write('<<>>\n') # Fetch the general information first for var in global_vars + specific_vars.get(product, []): # support old and new configuration format so we stay compatible with older # configuration files if len(var) == 3: path, title, itemspec = var mbean, path = path.split("/", 1) do_search = False else: mbean, path, title, itemspec, do_search = var queries = [] if do_search: if mbean in mbean_search_results: paths = mbean_search_results[mbean] else: paths = fetch_var(inst["protocol"], inst["server"], inst["port"], mbean, inst["suburi"], "", None, None, None, function="search")[0][1] mbean_search_results[mbean] = paths for mbean_exp in paths: queries.append( (inst, "%s/%s" % (urllib2.quote(mbean_exp), path), path, itemspec, mbean_exp) ) else: queries.append( (inst, mbean + "/" + path, title, itemspec) ) for inst, mbean_path, title, itemspec in queries: try: for out_item, out_title, out_value in fetch_metric(inst, mbean_path, title, itemspec): sys.stdout.write("%s %s %s\n" % (out_item, out_title, out_value) ) except IOError: return except socket.timeout: return except: if opt_debug: raise # Simply ignore exceptions. Need to be removed for debugging continue if custom_vars: sys.stdout.write('<<>>\n') for var in custom_vars: mbean, path, title, itemspec, do_search, value_type = var queries = [] if do_search: if mbean in mbean_search_results: paths = mbean_search_results[mbean] else: paths = fetch_var(inst["protocol"], inst["server"], inst["port"], mbean, inst["suburi"], "", None, None, None, function="search")[0][1] mbean_search_results[mbean] = paths for mbean_exp in paths: queries.append( (inst, "%s/%s" % (urllib2.quote(mbean_exp), path), path, itemspec, mbean_exp) ) else: queries.append( (inst, mbean + "/" + path, title, itemspec) ) for inst, mbean_path, title, itemspec in queries: try: for out_item, out_title, out_value in fetch_metric(inst, mbean_path, title, itemspec): sys.stdout.write("%s %s %s %s\n" % (out_item, out_title, out_value, value_type) ) except IOError: return except socket.timeout: return except: if opt_debug: raise # Simply ignore exceptions. Need to be removed for debugging continue # Default configuration for all instances protocol = "http" server = "localhost" port = 8080 user = "monitoring" password = None mode = "digest" suburi = "jolokia" instance = None cert_path = "_default" client_cert = None client_key = None service_url = None service_user = None service_password = None product = None global_vars = [ ( "java.lang:type=Memory", "NonHeapMemoryUsage/used", "NonHeapMemoryUsage", [], False), ( "java.lang:type=Memory", "NonHeapMemoryUsage/max", "NonHeapMemoryMax", [], False), ( "java.lang:type=Memory", "HeapMemoryUsage/used", "HeapMemoryUsage", [], False), ( "java.lang:type=Memory", "HeapMemoryUsage/max", "HeapMemoryMax", [], False), ( "java.lang:type=Threading", "ThreadCount", "ThreadCount", [], False), ( "java.lang:type=Threading", "DaemonThreadCount", "DeamonThreadCount", [], False), ( "java.lang:type=Threading", "PeakThreadCount", "PeakThreadCount", [], False), ( "java.lang:type=Threading", "TotalStartedThreadCount", "TotalStartedThreadCount", [], False), ( "java.lang:type=Runtime", "Uptime", "Uptime", [], False), ( "java.lang:type=GarbageCollector,name=*", "CollectionCount", "", [], False), ( "java.lang:type=GarbageCollector,name=*", "CollectionTime", "", [], False), ( "java.lang:name=CMS%20Perm%20Gen,type=MemoryPool", "Usage/used", "PermGenUsage", [], False), ( "java.lang:name=CMS%20Perm%20Gen,type=MemoryPool", "Usage/max", "PermGenMax", [], False), ( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "OffHeapHits", "", [], True), ( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "OnDiskHits", "", [], True), ( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "InMemoryHitPercentage", "", [], True), ( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "CacheMisses", "", [], True), ( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "OnDiskHitPercentage", "", [], True), ( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "MemoryStoreObjectCount", "", [], True), ( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "DiskStoreObjectCount", "", [], True), ( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "CacheMissPercentage", "", [], True), ( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "CacheHitPercentage", "", [], True), ( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "OffHeapHitPercentage", "", [], True), ( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "InMemoryMisses", "", [], True), ( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "OffHeapStoreObjectCount", "", [], True), ( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "WriterQueueLength", "", [], True), ( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "WriterMaxQueueSize", "", [], True), ( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "OffHeapMisses", "", [], True), ( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "InMemoryHits", "", [], True), ( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "AssociatedCacheName", "", [], True), ( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "ObjectCount", "", [], True), ( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "OnDiskMisses", "", [], True), ( "net.sf.ehcache:CacheManager=CacheManagerApplication*,*,type=CacheStatistics", "CacheHits", "", [], True), ] specific_vars = { "weblogic" : [ ( "*:*", "CompletedRequestCount", None, [ "ServerRuntime" ] , False), ( "*:*", "QueueLength", None, [ "ServerRuntime" ] , False), ( "*:*", "StandbyThreadCount", None, [ "ServerRuntime" ] , False), ( "*:*", "PendingUserRequestCount", None, [ "ServerRuntime" ] , False), ( "*:Name=ThreadPoolRuntime,*", "ExecuteThreadTotalCount", None, [ "ServerRuntime" ] , False), ( "*:*", "ExecuteThreadIdleCount", None, [ "ServerRuntime" ] , False), ( "*:*", "HoggingThreadCount", None, [ "ServerRuntime" ] , False), ( "*:Type=WebAppComponentRuntime,*", "OpenSessionsCurrentCount", None, [ "ServerRuntime", "ApplicationRuntime" ] , False), ], "tomcat" : [ ( "*:type=Manager,*", "activeSessions,maxActiveSessions", None, [ "path", "context" ] , False), ( "*:j2eeType=Servlet,name=default,*", "stateName", None, [ "WebModule" ] , False), # Check not yet working ( "*:j2eeType=Servlet,name=default,*", "requestCount", None, [ "WebModule" ], False), ( "*:name=*,type=ThreadPool", "maxThreads", None, [], False), ( "*:name=*,type=ThreadPool", "currentThreadCount", None, [], False), ( "*:name=*,type=ThreadPool", "currentThreadsBusy", None, [], False), # too wide location for addressing the right info # ( "*:j2eeType=Servlet,*", "requestCount", None, [ "WebModule" ] , False), ], "jboss" : [ ( "*:type=Manager,*", "activeSessions,maxActiveSessions", None, [ "path", "context" ] , False), ], } # ( '*:j2eeType=WebModule,name=/--/localhost/-/%(app)s,*/state', None, [ "name" ]), # ( '*:j2eeType=Servlet,WebModule=/--/localhost/-/%(app)s,name=%(servlet)s,*/requestCount', None, [ "WebModule", "name" ]), # ( "Catalina:J2EEApplication=none,J2EEServer=none,WebModule=*,j2eeType=Servlet,name=*", None, [ "WebModule", "name" ]), # List of instances to monitor. Each instance is a dict where # the global configuration values can be overridden. instances = [{}] custom_vars = [] conffile = os.getenv("MK_CONFDIR", "/etc/check_mk") + "/jolokia.cfg" if os.path.exists(conffile): execfile(conffile) if server == "use fqdn": server = socket.getfqdn() if instance == None: instance = str(port) # 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(1.0) # Compute list of instances to monitor. If the user has defined # instances in his configuration, we will use this (a list # of dicts). for inst in instances: for varname, value in [ ("protocol", protocol), ("server", server), ("port", port), ("user", user), ("password", password), ("mode", mode), ("suburi", suburi), ("instance", instance), ("cert_path", cert_path), ("client_cert", client_cert), ("client_key", client_key), ("service_url", service_url), ("service_user", service_user), ("service_password", service_password), ( "product", product ), ]: if varname not in inst: inst[varname] = value if not inst["instance"]: inst["instance"] = str(inst["port"]) inst["instance"] = inst["instance"].replace(" ", "_") if inst.get("server") == "use fqdn": inst["server"] = socket.getfqdn() query_instance(inst)