[munin] add a munin plugin for cubicweb
authorDavid Douard <david.douard@logilab.fr>
Tue, 03 Jun 2014 22:49:18 +0200
changeset 5 1e0786de308b
parent 4 5e5876d342d0
child 6 806e9612a411
[munin] add a munin plugin for cubicweb
cubicweb/cubicweb_stats
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cubicweb/cubicweb_stats	Tue Jun 03 22:49:18 2014 +0200
@@ -0,0 +1,352 @@
+#!/usr/bin/env python
+# -*- encoding: utf-8 -*-
+#
+# Wildcard-plugin to monitor cubicweb instances:
+#         http://www.cubicweb.org
+#
+# To monitor a series of CW instances:
+#    ln -s /usr/share/munin/plugins/cubicweb_stats /etc/munin/plugins/
+#
+#
+# Author: David Douard
+#
+# v1.0 05/03/2010 - First draft
+#
+# Copyright (c) 2010 David Douard, Logilab
+#
+# Permission to use, copy, and modify this software with or without fee
+# is hereby granted, provided that this entire notice is included in
+# all source code copies of any software which is or includes a copy or
+# modification of this software.
+#
+# THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
+# IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
+# REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
+# MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
+# PURPOSE.
+#
+#
+# Magic markers
+#%# capabilities=autoconf
+#%# family=auto
+
+import os, sys, re
+from logilab.common.pyro_ext import ns_get_proxy
+
+stats_curves = {
+    'available_cnxsets': ('Available pools', 'LINE2', None),
+    'nb_active_threads': ('Threads', 'LINE2', None),
+    'nb_open_sessions': ('Sessions', 'LINE1', None),
+    }
+
+cache_curves = {
+    'rqlt_st_cache_hit': ("rqlt_st_cache_hit", 'LINE2', "DERIVE"),
+    'rqlt_st_cache_miss': ("rqlt_st_cache_miss", 'LINE2', "DERIVE"),
+    'sql_cache_hit': ("sql_cache_hit", 'LINE2', "DERIVE"),
+    'sql_cache_miss': ("sql_cache_miss", 'LINE2', "DERIVE"),
+    'sql_no_cache': ("sql_no_cache", 'LINE2', "DERIVE")
+    }
+cache_size_curves = {
+    'rqlt_st_cache_size': ("rqlt_st_cache_size", None, None),
+    'rqlt_st_cache_size_max': ("rqlt_st_cache_size_max", None, None),
+    'sql_cache_size': ("sql_cache_size", None, None),
+    'sql_cache_size_max': ("sql_cache_size_max", None, None),
+    }
+repository_cache_size_curves = {
+    'type_source_cache_size': ("type_source_cache_size", None, None),
+    'extid_cache_size': ("extid_cache_size", None, None),
+    }
+cache_percent_curves = {
+    'rqlt_st_cache_hit_percent': ("rqlt_st_cache_hit", None, None),
+    'sql_cache_hit_percent': ("sql_cache_hit", None, None),
+    }
+gc_curves1 = {
+    'Union': ('Union', None, None),
+    'ResultSet': ('ResultSet', None, None),
+    'AppObject': ('AppObject', None, None),
+}
+gc_curves2 = {
+    'Connection': ('Connection', None, None),
+    'Session': ('Session', None, None),
+    }
+
+mem_curves = "VmPeak VmSize VmLck VmHWM VmRSS VmData VmStk VmExe VmLib VmPTE".split()
+mem_curves_infos = {
+    "VmPeak": "Peak virtual memory size.",
+    "VmSize": "Virtual memory size.",
+    "VmLck": "Locked memory size.",
+    "VmHWM": "Peak resident set size ('high water mark').",
+    "VmRSS": "Resident set size.",
+    "VmData": "Size of data segments.",
+    "VmStk": "Size of stacksegments.",
+    "VmExe": "Size of text segments.",
+    "VmLib": "Shared library code size.",
+    "VmPTE": "Page table entries size.",
+    }
+
+
+def find_cw_instances():
+    cmd = "%s list 2>/dev/null" % (os.getenv('cubicweb-ctl','/usr/bin/cubicweb-ctl'))
+    p = os.popen(cmd)
+    in_instancelist = False
+    instances = []
+    for l in p:
+        if in_instancelist:
+            if l.startswith('*'):
+                instances.append(l.split()[1])
+            else:
+                in_instancelist = False
+        elif l.startswith("Available instances"):
+            in_instancelist = True
+
+    p.close()
+    return instances
+
+def get_pid(instance):
+    cmd = "%s status %s 2>/dev/null" % (os.getenv('cubicweb-ctl','/usr/bin/cubicweb-ctl'), instance)
+    p = os.popen(cmd)
+    r = re.compile('^.*[[]%s-[^]]*[]] running with pid (?P<pid>[0-9]+)$' % instance)
+    pid = None
+    for l in p:
+        m = r.match(l)
+        if m:
+            pid = int(m.group('pid'))
+            break
+    else:
+        return None
+    p.close()
+    return pid
+
+def get_vminfos(pid):
+    data = open('/proc/%s/status' % pid).readlines()
+    infos = {}
+
+    for l in data:
+        key, val = l.split(':', 1)
+        key = key.strip()
+        val = val.strip()
+        if val.endswith('kB'):
+            val = int(val.split()[0]) * 1024
+        infos[key] = val
+    return infos
+
+def get_mem():
+    data = open('/proc/meminfo').readlines()
+    infos = {}
+
+    for l in data:
+        key, val = l.split(':', 1)
+        key = key.strip()
+        val = val.strip()
+        if val.endswith('kB'):
+            val = int(val.split()[0]) * 1024
+        infos[key] = val
+    return infos
+
+def get_stats(repo, instance):
+    stats = repo.stats()
+    for k, v in stats.items():
+        if isinstance(v, basestring):
+            if "/" in v:
+                val, maxval = v.split('/')
+                val = int(val)
+                maxval = int(val)
+                stats[k] = val
+                stats['%s_max' % k] = maxval
+    return stats
+
+def get_gc_stats(repo, instance):
+    try:
+        return dict(repo.gc_stats()['lookupclasses'])
+    except (AttributeError, KeyError):
+        return {}
+
+
+def _print_stats_config(instances, mgname, curves, title, info):
+    print('multigraph %s' % mgname)
+    print('graph_title %s' % title)
+    print('graph_category cubicweb')
+    print('graph_args -l 0')
+    print('graph_info %s.' % info)
+    for curve, (name, linetype, curvetype) in curves.items():
+        print('%s.label %s' % (curve, name))
+        if linetype:
+            print('%s.draw %s' % (curve, linetype))
+        if curvetype:
+            print('%s.type %s' % (curve, curvetype))
+    print('')
+
+    for instance in instances:
+        print('multigraph %s.%s' % (mgname, instance))
+        print('graph_title %s %s' % (instance, title))
+        print('graph_category cubicweb')
+        print('graph_args -l 0')
+        print('graph_info %s for %s.' % (info, instance))
+
+        for curve, (name, linetype, curvetype) in curves.items():
+            print('%s.label %s' % (curve, name))
+            if linetype:
+                print('%s.draw %s' % (curve, linetype))
+            if curvetype:
+                print('%s.type %s' % (curve, curvetype))
+        print('')
+
+def print_stats_config(instances):
+    _print_stats_config(instances, 'cubicweb_stats', stats_curves,
+                        'cubicweb instances stats',
+                        'This graph shows the instance usage stats')
+
+    _print_stats_config(instances, 'cubicweb_cache', cache_curves,
+                        'cubicweb instances cache hit',
+                        'This graph shows the instance cache hit evolution')
+
+    _print_stats_config(instances, 'cubicweb_cache_percent', cache_percent_curves,
+                        'cubicweb instances cache hit ratio',
+                        'This graph shows the instance cache hit ratio')
+
+    _print_stats_config(instances, 'cubicweb_cache_size', cache_size_curves,
+                        'cubicweb instances cache size',
+                        'This graph shows the instance cache size')
+
+    _print_stats_config(instances, 'cubicweb_repository_cache_size', repository_cache_size_curves,
+                        'cubicweb instances repository caches size',
+                        'This graph shows the instance repository caches size')
+
+    _print_stats_config(instances, 'cubicweb_gc_objects', gc_curves1,
+                        'cubicweb instances GC counts',
+                        'This graph shows the instance repository GC counts for some objects')
+
+    _print_stats_config(instances, 'cubicweb_gc_cnx_objects', gc_curves2,
+                        'cubicweb instances GC counts of connection-related objets',
+                        'This graph shows the instance repository GC counts of connection-related objets')
+
+
+def print_mem_config(instances):
+    print('multigraph cubicweb_memory')
+    print('graph_title cubicweb memory usage')
+    print('graph_args --base 1024 -l 0 --vertical-label Bytes')
+    print('graph_category cubicweb')
+    print('graph_info This graph shows the total cubicweb memory usage.')
+
+    for curve in "VmPeak", "VmSize", "VmRSS":
+        print('%s.label %s' % (curve, curve))
+        print('%s.draw LINE2' % (curve, ))
+    print('')
+
+    for instance in instances:
+        print('multigraph cubicweb_memory.%s' % instance)
+        print('graph_title %s memory usage' % instance)
+        print('graph_args --base 1024 -l 0 --vertical-label Bytes')
+        print('graph_category cubicweb')
+        print('graph_info This graph shows the instance memory usage.')
+
+        print('graph_order %s' % ' '.join(mem_curves))
+        for curve in mem_curves:
+            print('%s.label %s' % (curve, curve))
+            print('%s.draw LINE2' % (curve, ))
+        print('')
+
+def _print_stats_values(instances, stats, mgname, curves, normed=False):
+    print('multigraph %s' % mgname)
+    for curve in curves:
+        val = 0
+        nval = 0
+        for instance in instances:
+            if stats[instance].get(curve, 0):
+                val += stats[instance][curve]
+                nval += 1
+        if normed and nval:
+            val = float(val)/nval
+        print('%s.value %s' % (curve, val))
+    print('')
+
+    for instance in instances:
+        print('multigraph %s.%s' % (mgname, instance))
+        for curve in curves:
+            print('%s.value %s' % (curve, stats[instance].get(curve, 0)))
+        print('')
+
+def print_stats_values(instances):
+    stats = {}
+    gc_stats = {}
+    for instance in instances[:]:
+        try:
+            repo = ns_get_proxy(instance, "cubicweb", "localhost")
+            stats[instance] = get_stats(repo, instance)
+            gc_stats[instance] = get_gc_stats(repo, instance)
+        except Exception, ex:
+            msg = 'could not get stats for instance [%s]: %s' % (instance, ex)
+            print >> sys.stderr, msg
+            stats[instance] = None
+            gc_stats[instance] = None
+            instances.remove(instance)
+
+    _print_stats_values(instances, stats, 'cubicweb_stats', stats_curves)
+    _print_stats_values(instances, stats, 'cubicweb_cache', cache_curves)
+    _print_stats_values(instances, stats, 'cubicweb_cache_percent',
+                        cache_percent_curves, normed=True)
+    _print_stats_values(instances, stats, 'cubicweb_cache_size',
+                        cache_size_curves)
+    _print_stats_values(instances, stats, 'cubicweb_repository_cache_size',
+                        repository_cache_size_curves)
+    _print_stats_values(instances, gc_stats, 'cubicweb_gc_objects', gc_curves1)
+    _print_stats_values(instances, gc_stats, 'cubicweb_gc_cnx_objects', gc_curves2)
+
+def print_mem_values(instances):
+    mem = {}
+    totalmem = {'VmPeak': 0,
+                'VmSize': 0,
+                'VmRSS': 0,
+                }
+    for instance in instances:
+        pid = get_pid(instance)
+        if pid is None:
+            print >> sys.stderr, 'No process found for instances [%s]' % instance
+            continue
+        infos = get_vminfos(pid)
+        mem[instance] = infos
+        for k in totalmem:
+            totalmem[k] += infos.get(k, 0)
+
+    print('multigraph cubicweb_memory')
+    for curve, tot in totalmem.items():
+        print('%s.value %s' % (curve, tot))
+    print('')
+
+    for instance in instances:
+        if instance not in mem:
+            continue
+        print('multigraph cubicweb_memory.%s' % instance)
+        for curve in mem_curves:
+            print "%s.value %s" % (curve, mem[instance][curve])
+        print('')
+
+def print_config(instances):
+    print_stats_config(instances)
+    print_mem_config(instances)
+
+def print_values(instances):
+    print_stats_values(instances)
+    print_mem_values(instances)
+
+### Main part ###
+plugin_name = os.path.split(sys.argv[0])[1]
+
+instances = find_cw_instances()
+
+# Parse arguments
+if len(sys.argv) > 1:
+    if sys.argv[1] == "config":
+        print_config(instances)
+        sys.exit(0)
+    elif sys.argv[1] == "autoconf":
+        if os.path.exists(os.getenv('cubicweb-ctl', '/usr/bin/cubicweb-ctl')):
+            print('yes')
+            sys.exit(0)
+        else:
+            print('no (cubicweb-ctl not found)')
+            sys.exit(1)
+    elif sys.argv[1] != "":
+        sys.exit(1)
+
+print_values(instances)