#!/usr/bin/python3 # vim: set fileencoding=utf-8 : # # Munin plugin to show the amount of memory used by libvirt managed virtual # machines # # Copyright 2008,2012 Guido Guenther # # License: GPLv2 # # depends: python-libvirt, python-libxml2 # #%# capabilities=autoconf #%# family=contrib from __future__ import print_function import re, sys, os import libxml2 import libvirt def canon(name): return re.sub(r"[^a-zA-Z0-9_]", "_", name) def print_config(uri): """print the plugin config, determine the domains""" try: conn = libvirt.openReadOnly(uri) except libvirt.libvirtError as err: print("Error opening to %s connection: %s" % (uri, err), file=sys.stderr) return 1 hostname = conn.getHostname() print("""graph_title Virtual Domain Memory Usage graph_vlabel Memory Usage / Bytes graph_category Virtual Machines graph_info This graph shows the current amount of memory used by each virtual machine graph_args --base 1024 -l 0""") ids = conn.listDomainsID() draw = "AREA" for id in ids: try: dom = conn.lookupByID(id) name = dom.name() except libvirt.libvirtError as err: print("Id: %s: %s" % (id, err), file=sys.stderr) continue if name == "Domain-0": continue print("%s_mem.label %s" % (canon(name), name)) print("%s_mem.type GAUGE" % canon(name)) print("%s_mem.min 0" % canon(name)) print("%s_mem.draw %s" % (canon(name), draw)) print("%s_mem.info memory used by virtual machine '%s'" % (canon(name), name)) if draw == "AREA": draw = "STACK" print(""" host_mem.label %(hostname)s (host) host_mem.type GAUGE host_mem.min 0 host_mem.draw LINE1 host_mem.info total memory of host '%(hostname)s' total.type GAUGE total.label total memory total.info total memory used by virtual machines on host '%(hostname)s' total.graph no total.min 0 total_pc.type GAUGE total_pc.label used memory percentage total_pc.info memory in percent used by virtual machines on host '%(hostname)s' total_pc.graph no total_pc.min 0 total_pc.max 100 total_pc.warning 90 total_pc.critical 95 total_max.type GAUGE total_max.label total max. mem total_max.info maximum memory virtual machines can balloon to on host '%(hostname)s' total_max.min 0 total_max_pc.type GAUGE total_max_pc.label total maximum memory percentage total_max_pc.graph no total_max_pc.info maximum memory in percent virtual machines can balloon to on host '%(hostname)s' total_max_pc.min 0 total_min_guarantee.type GAUGE total_min_guarantee.label total mem minimum guarantee total_min_guarantee.info min_guarantee minimum amount of memory guaranteed to all virtual machines on host '%(hostname)s' total_min_guarantee.min 0 total_min_guarantee_pc.type GAUGE total_min_guarantee_pc.label total mem minimum guarantee percentage total_min_guarantee_pc.graph no total_min_guarantee_pc.info min_guarantee minimum amount of memory in percent guaranteed to all virtual machines on host '%(hostname)s' total_min_guarantee_pc.min 0 total_hard_limit.type GAUGE total_hard_limit.label total mem hard limit total_hard_limit.info memory hard_limit of all virtual machines on host '%(hostname)s' total_hard_limit.min 0 total_hard_limit_pc.type GAUGE total_hard_limit_pc.label total mem hard limit percentage total_hard_limit_pc.graph no total_hard_limit_pc.info memory hard_limit percentage of all virtual machines on host '%(hostname)s' total_hard_limit_pc.min 0 total_soft_limit.type GAUGE total_soft_limit.label total mem soft limit total_soft_limit.info memory soft_limit of all virtual machines on host '%(hostname)s' total_soft_limit.min 0 total_soft_limit_pc.type GAUGE total_soft_limit_pc.label total mem soft limit percentage total_soft_limit_pc.graph no total_soft_limit_pc.info memory soft_limit percentage of all virtual machines on host '%(hostname)s' total_soft_limit_pc.min 0 """ % dict(hostname=hostname)) return 0 def get_memtune(dom): memtune = {'min_guarantee': 0, 'soft_limit': 0, 'hard_limit': 0} xml = dom.XMLDesc(0) try: doc = libxml2.parseDoc(xml) except Exception: return [] ctx = doc.xpathNewContext() try: for key in memtune: ret = ctx.xpathEval("/domain/memtune/%s" % key) try: for child in ret[0].children: memtune[key] = int(child.content) break except IndexError: # key not found in xml pass finally: if ctx is not None: ctx.xpathFreeContext() if doc is not None: doc.freeDoc() return memtune def fetch_values(uri): """Fetch values in the block""" total = 0 total_max = 0 min_guarantee = 0 hard_limit = 0 soft_limit = 0 try: conn = libvirt.openReadOnly(uri) except libvirt.libvirtError as err: print("Error opening to %s connection: %s" % (uri, err), file=sys.stderr) return 1 ids = conn.listDomainsID() hostmem = conn.getInfo()[1] * 1024 * 1024 print("host_mem.value %d" % hostmem) for id in ids: try: dom = conn.lookupByID(id) name = dom.name() except libvirt.libvirtError as err: print("Id: %s: %s" % (id, err), file=sys.stderr) continue if name == "Domain-0": continue maxmem, mem = dom.info()[1:3] mem *= 1024 maxmem *= 1024 total += mem total_max += maxmem print("%s_mem.value %d" % (canon(name), mem)) memtune = get_memtune(dom) min_guarantee += memtune['min_guarantee'] * 1024 hard_limit += memtune['hard_limit'] * 1024 soft_limit += memtune['soft_limit'] * 1024 print("total.value %d" % total) print("total_pc.value %.0f" % (100.0 * total / float(hostmem))) print("total_max.value %d" % total_max) print("total_max_pc.value %.0f" % (100.0 * total_max / float(hostmem))) print("total_min_guarantee.value %d" % min_guarantee) print("total_min_guarantee_pc.value %.0f" % (100.0 * min_guarantee / float(hostmem))) print("total_soft_limit.value %d" % soft_limit) print("total_soft_limit_pc.value %.0f" % (100.0 * soft_limit / float(hostmem))) print("total_hard_limit.value %d" % hard_limit) print("total_hard_limit_pc.value %.0f" % (100.0 * hard_limit / float(hostmem))) return 0 def main(sys): uri = os.getenv("uri", "qemu:///system") if len(sys) > 1: if sys[1] in ['autoconf', 'detect']: if libvirt.openReadOnly(uri): print("yes") return 0 else: print("no") return 1 elif sys[1] == 'config': return print_config(uri) return fetch_values(uri) if __name__ == "__main__": sys.exit(main(sys.argv)) # vim:et:ts=4:sw=4: