Hi,
There was discussion on IRC yesterday wrt PCP and Libvirt metrics. I
investigated the situation a bit and it turns out writing a PMDA for
Libvirt looks pretty straightforward.
Below is an example how it could be done (obviously not yet ready for
merging). There are few metrics of different types, VMs coming and
going are always identified by the same internal/external ID, and the
PMDA handles libvirt becoming un/available.
The next steps, if someone is interested to pursue this further, would
probably be evaluating the metrics to report (or perhaps to come up
with a scheme how to report everything available automagically) and to
see how those perf event metrics mentioned yesterday could be fetched.
I'm providing the needed files inline for easier copy-pasting.
# Install:
#! /bin/sh
#
# Copyright (c) 2016 Red Hat.
#
# 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 2 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.
#
# Install the libvirt PMDA
#
. $PCP_DIR/etc/pcp.env
. $PCP_SHARE_DIR/lib/pmdaproc.sh
iam=libvirt
python_opt=true
daemon_opt=false
forced_restart=true
pmdaSetup
pmdaInstall
exit 0
# Remove:
#! /bin/sh
#
# Copyright (c) 2016 Red Hat.
#
# 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 2 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.
#
# Remove the libirt PMDA
#
. $PCP_DIR/etc/pcp.env
. $PCP_SHARE_DIR/lib/pmdaproc.sh
iam=libvirt
pmdaSetup
pmdaRemove
exit 0
# pmdalibvirt.python
#!/usr/bin/pcp python
# pylint: disable=line-too-long,bad-continuation,too-many-public-methods
#
# Copyright (C) 2016 Marko Myllynen <myllynen@xxxxxxxxxx>
#
# 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 2 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.
#
""" PCP Libvirt Performance Metrics Domain Agent """
import libvirt
import libxml2
from ctypes import c_int
from pcp.pmapi import pmUnits
from pcp.pmda import PMDA, pmdaIndom, pmdaMetric
from cpmapi import PM_TYPE_32, PM_TYPE_U32, PM_TYPE_64, PM_TYPE_U64
from cpmapi import PM_TYPE_FLOAT, PM_TYPE_DOUBLE, PM_TYPE_STRING
from cpmapi import PM_SEM_COUNTER, PM_SEM_INSTANT, PM_SEM_DISCRETE
from cpmapi import PM_ERR_INST, PM_ERR_NOTCONN, PM_ERR_PMID
class LibvirtPMDA(PMDA):
""" PCP Libvirt PMDA """
def __init__(self, name, domain):
""" Initializer """
PMDA.__init__(self, name, domain)
self.connect_pmcd()
self.conn = self.connect_libvirt()
self.doms = None
self.vm_indom = self.indom(0)
self.vm_cluster = 0
self.vm_metrics = [
[ 'name', PM_TYPE_STRING ],
[ 'uuid', PM_TYPE_STRING ],
[ 'title', PM_TYPE_STRING ],
[ 'description', PM_TYPE_STRING ],
[ 'vcpu', PM_TYPE_U32 ],
]
self.vm_insts = pmdaIndom(self.vm_indom, [])
self.add_indom(self.vm_insts)
self.add_metric(name + '.' + self.vm_metrics[0][0],
pmdaMetric(self.pmid(self.vm_cluster, 0),
self.vm_metrics[0][1], self.vm_indom, PM_SEM_DISCRETE,
pmUnits(0, 0, 0, 0, 0, 0)), 'VM name', 'VM name')
self.add_metric(name + '.' + self.vm_metrics[1][0],
pmdaMetric(self.pmid(self.vm_cluster, 1),
self.vm_metrics[1][1], self.vm_indom, PM_SEM_DISCRETE,
pmUnits(0, 0, 0, 0, 0, 0)), 'VM UUID', 'VM UUID')
self.add_metric(name + '.' + self.vm_metrics[2][0],
pmdaMetric(self.pmid(self.vm_cluster, 2),
self.vm_metrics[2][1], self.vm_indom, PM_SEM_DISCRETE,
pmUnits(0, 0, 0, 0, 0, 0)), 'VM title', 'VM title')
self.add_metric(name + '.' + self.vm_metrics[3][0],
pmdaMetric(self.pmid(self.vm_cluster, 3),
self.vm_metrics[3][1], self.vm_indom, PM_SEM_DISCRETE,
pmUnits(0, 0, 0, 0, 0, 0)), 'VM desc', 'VM desc')
self.add_metric(name + '.' + self.vm_metrics[4][0],
pmdaMetric(self.pmid(self.vm_cluster, 4),
self.vm_metrics[4][1], self.vm_indom, PM_SEM_INSTANT,
pmUnits(0, 0, 0, 0, 0, 0)), 'VM vCPUs', 'VM vCPUs')
self.set_fetch(self.libvirt_fetch)
self.set_fetch_callback(self.libvirt_fetch_callback)
def connect_libvirt(self):
""" Connect to libvirt """
conn = None
try:
conn = libvirt.openReadOnly(None)
except libvirt.libvirtError as error:
self.log("Failed to connect to the hypervisor: %s" % error)
return conn
def libvirt_fetch(self):
""" Fetch """
if not self.conn:
self.conn = self.connect_libvirt()
if not self.conn:
self.replace_indom(self.vm_indom, {"0":c_int(1)})
return
try:
self.doms =
self.conn.listAllDomains(libvirt.VIR_CONNECT_LIST_DOMAINS_ACTIVE)
except libvirt.libvirtError as error:
self.log("Failed to list domains: %s" % error)
self.conn = None
return
insts = {}
for dom in self.doms:
ctx = libxml2.parseDoc(dom.XMLDesc()).xpathNewContext()
uuid = ctx.xpathEval("string(/domain/uuid)")
insts[uuid] = c_int(1)
self.vm_insts.set_instances(self.vm_indom, insts)
self.replace_indom(self.vm_indom, insts)
def libvirt_fetch_callback(self, cluster, item, inst):
""" Fetch callback """
if not self.conn:
return [PM_ERR_NOTCONN, 0]
if cluster == self.vm_cluster:
for dom in self.doms:
ctx = libxml2.parseDoc(dom.XMLDesc()).xpathNewContext()
uuid = self.vm_insts.inst_name_lookup(inst)
if uuid != ctx.xpathEval("string(/domain/uuid)"):
continue
try:
path = "string(/domain/" + self.vm_metrics[item][0] + ")"
value = ctx.xpathEval(path)
if self.vm_metrics[item][1] == PM_TYPE_FLOAT or \
self.vm_metrics[item][1] == PM_TYPE_DOUBLE:
value = float(value)
elif self.vm_metrics[item][1] != PM_TYPE_STRING:
value = int(value)
return [value, 1]
except:
return [PM_ERR_INST, 0]
return [PM_ERR_PMID, 0]
if __name__ == '__main__':
LibvirtPMDA('libvirt', 491).run()
Cheers,
--
Marko Myllynen
|