Skip to content

Instantly share code, notes, and snippets.

@frgaudet
Created March 24, 2016 13:37
Show Gist options
  • Save frgaudet/3dec0cad061d64c3bee3 to your computer and use it in GitHub Desktop.
Save frgaudet/3dec0cad061d64c3bee3 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python
# coding=utf-8
#
# This program 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, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# <http://www.gnu.org/licenses/>.
#
# Based on diamond collector (https://github.com/python-diamond/Diamond)
#
# F-Gaudet 2016
import time
import logging
import platform
import sys
import os
from prettytable import PrettyTable
try:
from xml.etree import ElementTree
except ImportError:
import cElementTree as ElementTree
try:
import libvirt
except ImportError:
libvirt = None
def write_pidfile_or_die(path_to_pidfile):
if os.path.exists(path_to_pidfile):
pid = int(open(path_to_pidfile).read())
if is_running(pid):
print("Sorry, found a pidfile! Process {0} is still running.".format(pid))
sys.exit(1)
else:
os.remove(path_to_pidfile)
open(path_to_pidfile, 'w+').write(str(os.getpid()))
return path_to_pidfile
def is_running(pid):
try:
os.kill(pid, 0)
except OSError:
return
else:
return pid
class LibvirtKVMCollector(object):
def __init__(self):
self.blockStats = {
'read_reqs': 0,
'read_bytes': 1,
'write_reqs': 2,
'write_bytes': 3
}
self.vifStats = {
'rx_bytes': 0,
'rx_packets': 1,
'rx_errors': 2,
'rx_drops': 3,
'tx_bytes': 4,
'tx_packets': 5,
'tx_errors': 6,
'tx_drops': 7
}
self.config = ({
'sort_by_uuid': False,
'uri': 'qemu:///system',
'cpu_absolute': False
})
self.logger = logging.getLogger(__name__)
self.handler = logging.StreamHandler()
self.formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
self.handler.setFormatter(self.formatter)
self.logger.addHandler(self.handler)
self.logger.setLevel(logging.DEBUG)
if libvirt is None:
self.logger.error('Unable to import libvirt')
exit (1)
self.pp = PrettyTable(["Instance","Item","Value"])
self.pp.align["Instance"] = "l"
self.pp.padding_width = 1
def get_devices(self, dom, type):
devices = []
# Create a XML tree from the domain XML description.
tree = ElementTree.fromstring(dom.XMLDesc(0))
for target in tree.findall("devices/%s/target" % type):
dev = target.get("dev")
if not dev in devices:
devices.append(dev)
return devices
def get_disk_devices(self, dom):
return self.get_devices(dom, 'disk')
def get_network_devices(self, dom):
return self.get_devices(dom, 'interface')
def publish(self, item, value, instance):
self.pp.add_row([instance, item, value])
def printpp(self):
print self.pp
def report_cpu_metric(self, value0, value1):
return (100 * ((float(value1)-float(value0)) / 1000000000))
def collect(self):
conn = libvirt.openReadOnly(self.config['uri'])
for dom in [conn.lookupByID(n) for n in conn.listDomainsID()]:
if self.config['sort_by_uuid'] is True:
name = dom.UUIDString()
else:
name = dom.name()
# CPU stats
vcpus0 = dom.getCPUStats(True, 0)
# Take 2 samples in order to convert CPU Time to %
time.sleep(1)
vcpus1 = dom.getCPUStats(True, 0)
totalcpu = 0
idx = 0
for vcpu in vcpus0:
cputime0 = vcpu['cpu_time']
cputime1 = vcpus1[idx]['cpu_time']
if self.config['cpu_absolute'] is True:
self.publish('cpu.%s.time' % idx , cputime1, name)
else:
self.publish('cpu.%s.usage' % idx , self.report_cpu_metric(cputime0,cputime1), name)
idx += 1
totalcpu += cputime1
self.publish('cpu.total.time', totalcpu, name)
# Disk stats
disks = self.get_disk_devices(dom)
accum = {}
for stat in self.blockStats.keys():
accum[stat] = 0
for disk in disks:
stats = dom.blockStats(disk)
for stat in self.blockStats.keys():
idx = self.blockStats[stat]
val = stats[idx]
accum[stat] += val
self.publish('block.%s.%s' % (disk, stat), val,
instance=name)
for stat in self.blockStats.keys():
self.publish('block.total.%s' % stat, accum[stat], instance=name)
# Network stats
vifs = self.get_network_devices(dom)
accum = {}
for stat in self.vifStats.keys():
accum[stat] = 0
for vif in vifs:
stats = dom.interfaceStats(vif)
for stat in self.vifStats.keys():
idx = self.vifStats[stat]
val = stats[idx]
accum[stat] += val
self.publish('net.%s.%s' % (vif, stat), val,
instance=name)
for stat in self.vifStats.keys():
self.publish('net.total.%s' % stat, accum[stat],
instance=name)
# Memory stats
mem = dom.memoryStats()
self.publish('memory.nominal', mem['actual'] * 1024,
instance=name)
self.publish('memory.rss', mem['rss'] * 1024, instance=name)
if __name__ == '__main__':
pid_path = "%s/kvmMonitoringLock" % os.path.dirname('/tmp/')
write_pidfile_or_die(str(pid_path))
try:
a = LibvirtKVMCollector()
a.collect()
a.printpp()
except KeyboardInterrupt:
sys.exit(1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment