-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
mdadm and vms (libvirt) monitoring plugin
- Loading branch information
Showing
4 changed files
with
231 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
#!/usr/bin/env python | ||
# -*- coding: utf-8 -*- | ||
import os | ||
import plugins | ||
import json | ||
|
||
|
||
class Plugin(plugins.BasePlugin): | ||
__name__ = 'mdstat' | ||
|
||
def run(self, config): | ||
''' | ||
Monitor software raid status using mdadm | ||
pip install mdstat | ||
''' | ||
data = os.popen('sudo mdjson').read() | ||
results = {} | ||
|
||
try: | ||
data = json.loads(data) | ||
except Exception: | ||
return "Could not load mdstat data" | ||
|
||
for key, value in data['devices'].items(): | ||
device = {} | ||
if(value['active'] is not True): | ||
device['active'] = 0 | ||
else: | ||
device['active'] = 1 | ||
if(value['read_only'] is not False): | ||
device['read_only'] = 1 | ||
else: | ||
device['read_only'] = 0 | ||
if(value['resync'] is not None): | ||
device['resync'] = 1 | ||
else: | ||
device['resync'] = 0 | ||
device['faulty'] = 0 | ||
for disk, diskvalue in value['disks'].items(): | ||
if diskvalue['faulty'] is not False: | ||
device['faulty'] = device['faulty'] + 1 | ||
results[key] = device | ||
return results | ||
|
||
if __name__ == '__main__': | ||
Plugin().execute() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
import re, sys, os | ||
import libvirt | ||
import libxml2 | ||
import time | ||
import plugins | ||
|
||
class Plugin(plugins.BasePlugin): | ||
__name__ = 'vms' | ||
|
||
def run(self, config): | ||
''' | ||
Using the libvirt API to fetch statistics from guests | ||
running KVM, QEMU, Xen, Virtuozzo, VMWare ESX, LXC, | ||
BHyve and more | ||
''' | ||
results = {} | ||
last_value = {} | ||
prev_cache = self.get_agent_cache() # Get absolute values from previous check | ||
uri = os.getenv("uri", "qemu:///system") | ||
values = self.fetch_values(uri) | ||
|
||
for key, value in values.items(): | ||
deltas = {} | ||
for subkey, subvalue in value.items(): | ||
deltas[key][subkey] = self.absolute_to_per_second('%s_%s' % (key, subkey), float(subvalue), prev_cache) | ||
last_value['%s_%s' % (key, subkey)] = float(value[subkey]) | ||
last_value['ts'] = time.time() | ||
self.set_agent_cache(last_value) | ||
return results | ||
|
||
def canon(self, name): | ||
return re.sub(r"[^a-zA-Z0-9_]", "_", name) | ||
|
||
def get_ifaces(self, dom): | ||
xml = dom.XMLDesc(0) | ||
doc = None | ||
try: | ||
doc = libxml2.parseDoc(xml) | ||
except: | ||
return [] | ||
ctx = doc.xpathNewContext() | ||
ifaces = [] | ||
try: | ||
ret = ctx.xpathEval("/domain/devices/interface") | ||
for node in ret: | ||
devdst = None | ||
for child in node.children: | ||
if child.name == "target": | ||
devdst = child.prop("dev") | ||
if devdst == None: | ||
continue | ||
ifaces.append(devdst) | ||
finally: | ||
if ctx != None: | ||
ctx.xpathFreeContext() | ||
if doc != None: | ||
doc.freeDoc() | ||
return ifaces | ||
|
||
def get_memtune(self, dom): | ||
memtune = { 'min_guarantee': 0, 'soft_limit': 0, 'hard_limit': 0 } | ||
xml = dom.XMLDesc(0) | ||
|
||
try: | ||
doc = libxml2.parseDoc(xml) | ||
except: | ||
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 != None: | ||
ctx.xpathFreeContext() | ||
if doc != None: | ||
doc.freeDoc() | ||
return memtune | ||
|
||
def fetch_values(self, uri): | ||
conn = libvirt.openReadOnly(uri) | ||
ids = conn.listDomainsID() | ||
results = {} | ||
processors = float(conn.getInfo()[2]) | ||
data = {} | ||
for id in ids: | ||
data['net_rx'] = 0 | ||
data['net_tx'] = 0 | ||
try: | ||
dom = conn.lookupByID(id) | ||
name = dom.name() | ||
except libvirt.libvirtError, err: | ||
print >>sys.stderr, "Id: %s: %s" % (id, err) | ||
continue | ||
if name == "Domain-0": | ||
continue | ||
ifaces = self.get_ifaces(dom) | ||
for iface in ifaces: | ||
try: | ||
stats = dom.interfaceStats(iface) | ||
data['net_rx'] += stats[0] | ||
data['net_tx'] += stats[4] | ||
except: | ||
print >>sys.stderr, "Cannot get ifstats for '%s' on '%s'" % (iface, name) | ||
|
||
cputime = float(dom.info()[4]) | ||
cputime_percentage = 1.0e-7 * cputime / processors | ||
data['cpu'] = cputime_percentage | ||
|
||
maxmem, mem = dom.info()[1:3] | ||
mem *= 1024 | ||
maxmem *= 1024 | ||
data['mem'] = mem | ||
memtune = self.get_memtune(dom) | ||
data['min_guarantee'] = memtune['min_guarantee'] * 1024 | ||
data['hard_limit'] = memtune['hard_limit'] * 1024 | ||
data['soft_limit'] = memtune['soft_limit'] * 1024 | ||
|
||
data['disk_rd'] = 0 | ||
data['disk_wr'] = 0 | ||
data['disk_wr_req'] = 0 | ||
data['disk_rd_req'] = 0 | ||
try: | ||
dom = conn.lookupByID(id) | ||
name = dom.name() | ||
except libvirt.libvirtError, err: | ||
print >>sys.stderr, "Id: %s: %s" % (id, err) | ||
continue | ||
if name == "Domain-0": | ||
continue | ||
disks = self.get_disks(dom) | ||
for disk in disks: | ||
try: | ||
rd_req, rd_bytes, wr_req, wr_bytes, errs = dom.blockStats(disk) | ||
data['disk_rd'] += rd_bytes | ||
data['disk_wr'] += wr_bytes | ||
data['disk_rd_req'] += rd_req | ||
data['disk_wr_req'] += wr_req | ||
except TypeError: | ||
print >>sys.stderr, "Cannot get blockstats for '%s' on '%s'" % (disk, name) | ||
|
||
results[self.canon(name)] = data | ||
return results | ||
|
||
def get_disks(self, dom): | ||
xml = dom.XMLDesc(0) | ||
doc = None | ||
try: | ||
doc = libxml2.parseDoc(xml) | ||
except: | ||
return [] | ||
ctx = doc.xpathNewContext() | ||
disks = [] | ||
try: | ||
ret = ctx.xpathEval("/domain/devices/disk") | ||
for node in ret: | ||
devdst = None | ||
for child in node.children: | ||
if child.name == "target": | ||
devdst = child.prop("dev") | ||
if devdst == None: | ||
continue | ||
disks.append(devdst) | ||
finally: | ||
if ctx != None: | ||
ctx.xpathFreeContext() | ||
if doc != None: | ||
doc.freeDoc() | ||
return disks | ||
|
||
|
||
if __name__ == '__main__': | ||
Plugin().execute() |