Add xen_net munin plugin for monitoring network status.
authorQuentin Smith <quentin@mit.edu>
Mon, 6 Jan 2014 06:50:04 +0000 (01:50 -0500)
committerQuentin Smith <quentin@mit.edu>
Mon, 6 Jan 2014 06:50:04 +0000 (01:50 -0500)
debian/changelog
debian/xvm-munin-host-config.postinst
host/etc/munin/plugin-conf.d/xen_ [moved from host/etc/munin/plugin-conf.d/xen_cpu with 74% similarity]
host/etc/munin/plugins/xen_net [new symlink]
host/usr/share/xvm-munin-host-config/plugins/xen_net [new file with mode: 0755]

index 9bbbd2d..32499c9 100644 (file)
@@ -1,3 +1,9 @@
+xvm-munin-config (0.0.18) unstable; urgency=low
+
+  * Add xen_net munin plugin for monitoring network status.
+
+ -- Quentin Smith <quentin@mit.edu>  Mon, 06 Jan 2014 01:41:38 -0500
+
 xvm-munin-config (0.0.17) unstable; urgency=low
 
   * Python 2.7 apparently treats "%\n" as a format specifier, when it used
index fc2de88..b182729 100755 (executable)
@@ -30,7 +30,7 @@ case "$1" in
 ### BEGIN xvm-munin-config
 munin ALL=(postfix) SETENV: NOPASSWD: /etc/munin/plugins/postfix_mailqueue
 munin ALL=(munin) SETENV: NOPASSWD: ALL
-munin ALL=(root) SETENV: NOPASSWD: /etc/munin/plugins/hddtemp_smartctl , /etc/munin/plugins/smart_* , /etc/munin/plugins/xen_cpu
+munin ALL=(root) SETENV: NOPASSWD: /etc/munin/plugins/hddtemp_smartctl , /etc/munin/plugins/smart_* , /etc/munin/plugins/xen_*
 ### END xvm-munin-config
 EOF
         
similarity index 74%
rename from host/etc/munin/plugin-conf.d/xen_cpu
rename to host/etc/munin/plugin-conf.d/xen_
index 79defca..2805590 100644 (file)
@@ -1,3 +1,3 @@
-[xen_cpu]
+[xen_*]
 user root
 command sudo -E %c
diff --git a/host/etc/munin/plugins/xen_net b/host/etc/munin/plugins/xen_net
new file mode 120000 (symlink)
index 0000000..5419eb6
--- /dev/null
@@ -0,0 +1 @@
+/usr/share/xvm-munin-host-config/plugins/xen_net
\ No newline at end of file
diff --git a/host/usr/share/xvm-munin-host-config/plugins/xen_net b/host/usr/share/xvm-munin-host-config/plugins/xen_net
new file mode 100755 (executable)
index 0000000..822b3e0
--- /dev/null
@@ -0,0 +1,106 @@
+#!/usr/bin/python
+
+import sys
+sys.path.append('/usr/lib/xen-default/lib/python/')
+from xen.lowlevel import xs
+from collections import defaultdict
+import os
+import re
+
+xsc = xs.xs()
+
+def live_vms():
+    domids = set(xsc.ls('', '/local/domain'))
+
+    vms = dict()
+
+    for domid in domids:
+        try:
+            uuid, data = get_dom(int(domid))
+        except (xs.Error, TypeError, AttributeError):
+            continue # went down since we started
+        vms[uuid] = data
+    return vms
+
+def get_dom(domid):
+    data = dict(domid=domid)
+    data['name'] = xsc.read('', '/local/domain/%d/name' % domid)
+    data['uuid'] = xsc.read('', '/local/domain/%d/vm' % domid)[4:]
+    if data['name'][0:2] == 'd_':
+        data['munin_name'] = 'db domid %d' % domid
+    else:
+        data['munin_name'] = data['name']
+    data['munin_var'] = 'uuid_'+data['uuid'].replace('-', '_')
+    return data['munin_var'], data
+
+def parse_net_dev():
+    """Parse /proc/net/dev to determine down/up counters for each interface.
+
+    N.B. Note that "down" and "up" are with respect to the host.
+    """
+    usage = {}
+    for line in open('/proc/net/dev', 'r'):
+        if ':' not in line:
+            continue # skip header lines
+        iface, fields = line.split(':')
+        iface = iface.strip()
+        fields = fields.split()
+        usage[iface] = {'down': int(fields[0]),
+                        'up': int(fields[8])}
+    return usage
+
+IFACE_NAME_RE = re.compile(r'^(vif|tap)(\d+)\.(\d+)$')
+
+def get_net_usage():
+    """Group /proc/net/dev counters by domain.
+
+    N.B. Note that "down" and "up" are reversed - from the perspective of the guest.
+    """
+    by_dev = parse_net_dev()
+    by_domain = defaultdict(lambda: {'down': 0, 'up': 0})
+    for iface in by_dev:
+        m = IFACE_NAME_RE.match(iface)
+        if m:
+            viftype, domid, ifcount = m.groups()
+            domid = int(domid)
+            by_domain[domid]['down'] += by_dev[iface]['up']
+            by_domain[domid]['up'] += by_dev[iface]['down']
+    return by_domain
+
+if __name__ == '__main__':
+    if len(sys.argv) > 1:
+        cmd = sys.argv[1]
+    else:
+        cmd = None
+
+    domains = live_vms()
+
+    if cmd == 'config':
+        print """
+    graph_title Xen domain network usage
+    graph_args --base 1000
+    graph_vlabel bits in (-) / out (+) per ${graph_period}
+    graph_info This graph shows how network is utilized by Xen domains.
+    graph_category network
+    graph_period second"""
+        for d in sorted(domains):
+            for direction in ('down', 'up'):
+                key = "%s_%s" % (d, direction)
+                print "%s.label %s" % (key, domains[d]['munin_name'])
+                print "%s.draw AREASTACK" % key
+                print "%s.max 10000000000" % key
+                print "%s.min 0" % key
+                print "%s.type DERIVE" % key
+                print "%s.cdef %s,8,*" % (key, key)
+                if direction == 'down':
+                    print "%s.graph no" % key
+                else:
+                    print "%s.negative %s_down" % (key, d)
+                print "%s.info uuid %s" % (key, domains[d]['uuid'])
+        sys.exit(0)
+
+    net_usage = get_net_usage()
+
+    for d in sorted(domains):
+        for direction in ('down', 'up'):
+            print "%s_%s.value %d" % (d, direction, net_usage[domains[d]['domid']][direction])