pmdalibvirt: initial version of a PMDA for libvirt-based metrics
[pcp.git] / src / pmdas / libvirt / pmdalibvirt.python
blob0abbbd5035ce4a6b3d19b88c1f448efb70245b81
1 #!/usr/bin/env python
3 # Copyright (C) 2016 Marko Myllynen <myllynen@redhat.com>
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 """ PCP libvirt Performance Metrics Domain Agent """
17 try:
18 import ConfigParser
19 except ImportError:
20 import configparser as ConfigParser
22 import os
23 import sys
24 import atexit
25 import libvirt
27 from lxml import etree
28 from ctypes import c_int
30 from pcp.pmapi import pmUnits
31 from pcp.pmapi import pmContext as PCP
32 from pcp.pmda import PMDA, pmdaIndom, pmdaMetric
33 from cpmapi import PM_INDOM_NULL
34 from cpmapi import PM_TYPE_U32, PM_TYPE_U64, PM_TYPE_STRING
35 from cpmapi import PM_SEM_COUNTER, PM_SEM_INSTANT, PM_SEM_DISCRETE
36 from cpmapi import PM_COUNT_ONE, PM_SPACE_BYTE, PM_SPACE_KBYTE, PM_TIME_SEC, PM_TIME_NSEC
37 from cpmapi import PM_ERR_AGAIN, PM_ERR_INST, PM_ERR_NOTCONN, PM_ERR_NYI, PM_ERR_PMID, PM_ERR_VALUE
39 if sys.version_info[0] >= 3:
40 long = int
42 DEFAULT_USER = 'root'
43 DEFAULT_URI = 'qemu:///system'
45 class LibvirtPMDA(PMDA):
46 """ PCP libvirt PMDA """
47 def __init__(self, name, domain):
48 """ Constructor """
49 PMDA.__init__(self, name, domain)
51 self.user = DEFAULT_USER
52 self.uri = DEFAULT_URI
53 self.read_config()
54 self.set_user(self.user)
56 self.doms = []
57 self.connect_pmcd()
58 self.conn = self.connect_libvirt()
59 try:
60 test = libvirt.VIR_CONNECT_GET_ALL_DOMAINS_STATS_ACTIVE
61 except:
62 if not os.environ.get('PCP_PYTHON_DOMAIN') and not os.environ.get('PCP_PYTHON_PMNS'):
63 self.log("Old libvirt API detected, some metrics are unavailable")
65 units_none = pmUnits(0, 0, 0, 0, 0, 0)
66 units_count = pmUnits(0, 0, 1, 0, 0, PM_COUNT_ONE)
67 units_bytes = pmUnits(1, 0, 0, PM_SPACE_BYTE, 0, 0)
68 units_kbyte = pmUnits(1, 0, 0, PM_SPACE_KBYTE, 0, 0)
69 units_bpers = pmUnits(1,-1, 0, PM_SPACE_BYTE, PM_TIME_SEC, 0)
70 units_nsecs = pmUnits(0, 1, 0, 0, PM_TIME_NSEC, 0)
72 self.hv_indom = PM_INDOM_NULL
74 self.hv_cluster = 0
75 self.hv_metrics = [
76 # Name - method - type - semantics - units - help
77 # See libvirt.py
78 [ 'hv.uri', 'getURI', PM_TYPE_STRING, PM_SEM_DISCRETE, units_none, 'Libvirt URI' ],
79 [ 'hv.driver', 'getType', PM_TYPE_STRING, PM_SEM_DISCRETE, units_none, 'Libvirt driver' ],
80 [ 'hv.version', 'getVersion', PM_TYPE_U32, PM_SEM_DISCRETE, units_none, 'Libvirt version' ],
81 [ 'hv.domains.active', 'numOfDomains', PM_TYPE_U32, PM_SEM_INSTANT, units_none, 'Libvirt domains, active' ],
82 [ 'hv.domains.inactive', 'numOfDefinedDomains', PM_TYPE_U32, PM_SEM_INSTANT, units_none, 'Libvirt domains, inactive' ],
83 [ 'hv.devices', 'numOfDevices', PM_TYPE_U32, PM_SEM_INSTANT, units_none, 'Libvirt devices' ],
84 [ 'hv.storagepools.active', 'numOfStoragePools', PM_TYPE_U32, PM_SEM_INSTANT, units_none, 'Libvirt storage pools, active' ],
85 [ 'hv.storagepools.inactive', 'numOfDefinedStoragePools', PM_TYPE_U32, PM_SEM_INSTANT, units_none, 'Libvirt storage pools, inactive' ],
86 [ 'hv.networks.active', 'numOfNetworks', PM_TYPE_U32, PM_SEM_INSTANT, units_none, 'Libvirt networks, active' ],
87 [ 'hv.networks.inactive', 'numOfDefinedNetworks', PM_TYPE_U32, PM_SEM_INSTANT, units_none, 'Libvirt networks, inactive' ],
88 [ 'hv.interfaces.active', 'numOfInterfaces', PM_TYPE_U32, PM_SEM_INSTANT, units_none, 'Libvirt interfaces, active' ],
89 [ 'hv.interfaces.inactive', 'numOfDefinedInterfaces', PM_TYPE_U32, PM_SEM_INSTANT, units_none, 'Libvirt interfaces, inactive' ],
90 [ 'hv.nwfilters', 'numOfNWFilters', PM_TYPE_U32, PM_SEM_INSTANT, units_none, 'Libvirt nw filters' ],
91 [ 'hv.secrets', 'numOfSecrets', PM_TYPE_U32, PM_SEM_INSTANT, units_none, 'Libvirt secrets' ],
94 self.vm_indom = self.indom(0)
95 self.vm_insts = pmdaIndom(self.vm_indom, [])
96 self.add_indom(self.vm_insts)
98 self.vm_cluster = 1
99 self.vm_metrics = [
100 # Name - xpath - type - semantics - units - help
101 # See https://libvirt.org/formatdomain.html
102 [ 'dominfo.type', '/domain/@type', PM_TYPE_STRING, PM_SEM_DISCRETE, units_none, 'VM type' ],
103 [ 'dominfo.name', '/domain/name', PM_TYPE_STRING, PM_SEM_DISCRETE, units_none, 'VM name' ],
104 [ 'dominfo.uuid', '/domain/uuid', PM_TYPE_STRING, PM_SEM_DISCRETE, units_none, 'VM UUID' ],
105 [ 'dominfo.title', '/domain/title', PM_TYPE_STRING, PM_SEM_DISCRETE, units_none, 'VM title' ],
106 [ 'dominfo.description', '/domain/description', PM_TYPE_STRING, PM_SEM_DISCRETE, units_none, 'VM description' ],
107 [ 'dominfo.container', '/domain/os/init', PM_TYPE_STRING, PM_SEM_DISCRETE, units_none, 'VM container' ],
108 [ 'dominfo.os.type', '/domain/os/type', PM_TYPE_STRING, PM_SEM_DISCRETE, units_none, 'VM OS type' ],
109 [ 'dominfo.vcpu.current', '/domain/vcpu/@current', PM_TYPE_U32, PM_SEM_INSTANT, units_none, 'VM vCPUs, current' ],
110 [ 'dominfo.vcpu.max', '/domain/vcpu', PM_TYPE_U32, PM_SEM_INSTANT, units_none, 'VM vCPUs, maximum' ],
111 [ 'dominfo.memory.boot', '/domain/memory', PM_TYPE_U64, PM_SEM_INSTANT, units_kbyte, 'VM memory, at boot' ],
112 [ 'dominfo.memory.current', '/domain/currentMemory', PM_TYPE_U64, PM_SEM_INSTANT, units_kbyte, 'VM memory, current' ],
113 [ 'dominfo.memory.max', '/domain/maxMemory', PM_TYPE_U64, PM_SEM_INSTANT, units_kbyte, 'VM memory, maximum' ],
116 self.vm_cpustats_res = []
117 self.vm_cpustats_cluster = 2
118 self.vm_cpustats = [
119 # Name - empty - type - semantics - units - help
120 # See libvirt.git/src/libvirt-domain.c
121 [ 'domstats.cpu.time', None, PM_TYPE_U64, PM_SEM_COUNTER, units_nsecs, 'VM CPU time, total' ],
122 [ 'domstats.cpu.system', None, PM_TYPE_U64, PM_SEM_COUNTER, units_nsecs, 'VM CPU time, system' ],
123 [ 'domstats.cpu.user', None, PM_TYPE_U64, PM_SEM_COUNTER, units_nsecs, 'VM CPU time, user' ],
126 self.vm_vcpustats_res = []
127 self.vm_vcpustats_cluster = 3
128 self.vm_vcpustats = [
129 # Name - empty - type - semantics - units - help
130 # See libvirt.git/src/libvirt-domain.c
131 [ 'domstats.vcpu.current', None, PM_TYPE_U32, PM_SEM_INSTANT, units_none, 'VM vCPUs, current' ],
132 [ 'domstats.vcpu.maximum', None, PM_TYPE_U32, PM_SEM_INSTANT, units_none, 'VM vCPUs, maximum' ],
133 # [ 'domstats.vcpu.state', None, PM_TYPE_U32, PM_SEM_INSTANT, units_none, 'VM vCPUs, state' ],
134 [ 'domstats.vcpu.time', None, PM_TYPE_U64, PM_SEM_COUNTER, units_count, 'VM vCPUs, time' ],
135 [ 'domstats.vcpu.wait', None, PM_TYPE_U64, PM_SEM_COUNTER, units_count, 'VM vCPUs, wait' ],
138 self.vm_memstats_res = {}
139 self.vm_memstats_cluster = 4
140 self.vm_memstats = [
141 # Name - empty - type - semantics - units - help
142 # See libvirt.git/include/libvirt/libvirt-domain.h
143 [ 'domstats.mem.swap_in', None, PM_TYPE_U64, PM_SEM_INSTANT, units_kbyte, 'VM memory, swapped in' ],
144 [ 'domstats.mem.swap_out', None, PM_TYPE_U64, PM_SEM_INSTANT, units_kbyte, 'VM memory, swapped out' ],
145 [ 'domstats.mem.major_fault', None, PM_TYPE_U64, PM_SEM_INSTANT, units_none, 'VM memory, major faults' ],
146 [ 'domstats.mem.minor_fault', None, PM_TYPE_U64, PM_SEM_INSTANT, units_none, 'VM memory, minor faults' ],
147 [ 'domstats.mem.unused', None, PM_TYPE_U64, PM_SEM_INSTANT, units_kbyte, 'VM memory, unused' ],
148 [ 'domstats.mem.available', None, PM_TYPE_U64, PM_SEM_INSTANT, units_kbyte, 'VM memory, available' ],
149 [ 'domstats.mem.actual', None, PM_TYPE_U64, PM_SEM_INSTANT, units_kbyte, 'VM memory, balloon size' ],
150 [ 'domstats.mem.rss', None, PM_TYPE_U64, PM_SEM_INSTANT, units_kbyte, 'VM memory, proc RSS' ],
153 self.vm_balloonstats_res = []
154 self.vm_balloonstats_cluster = 5
155 self.vm_balloonstats = [
156 # Name - empty - type - semantics - units - help
157 # See libvirt.git/src/libvirt-domain.c
158 [ 'domstats.balloon.current', None, PM_TYPE_U64, PM_SEM_INSTANT, units_kbyte, 'VM balloon size, current' ],
159 [ 'domstats.balloon.maximum', None, PM_TYPE_U64, PM_SEM_INSTANT, units_kbyte, 'VM balloon size, maximum' ],
162 self.vm_blockstats_res = []
163 self.vm_blockstats_cluster = 6
164 self.vm_blockstats = [
165 # Name - empty - type - semantics - units - help
166 # See libvirt.git/src/libvirt-domain.c
167 [ 'domstats.block.count', None, PM_TYPE_U32, PM_SEM_COUNTER, units_count, 'VM block devs, count' ],
168 [ 'domstats.block.rd.reqs', None, PM_TYPE_U64, PM_SEM_COUNTER, units_count, 'VM block devs, rd reqs' ],
169 [ 'domstats.block.rd.bytes', None, PM_TYPE_U64, PM_SEM_COUNTER, units_bytes, 'VM block devs, rd bytes' ],
170 [ 'domstats.block.rd.times', None, PM_TYPE_U64, PM_SEM_COUNTER, units_nsecs, 'VM block devs, rd times' ],
171 [ 'domstats.block.wr.reqs', None, PM_TYPE_U64, PM_SEM_COUNTER, units_count, 'VM block devs, wr reqs' ],
172 [ 'domstats.block.wr.bytes', None, PM_TYPE_U64, PM_SEM_COUNTER, units_bytes, 'VM block devs, wr bytes' ],
173 [ 'domstats.block.wr.times', None, PM_TYPE_U64, PM_SEM_COUNTER, units_nsecs, 'VM block devs, wr times' ],
174 [ 'domstats.block.fl.reqs', None, PM_TYPE_U64, PM_SEM_COUNTER, units_count, 'VM block devs, fl reqs' ],
175 [ 'domstats.block.fl.times', None, PM_TYPE_U64, PM_SEM_COUNTER, units_nsecs, 'VM block devs, fl times' ],
176 [ 'domstats.block.allocation', None, PM_TYPE_U64, PM_SEM_COUNTER, units_bytes, 'VM backing imgs, allocation' ],
177 [ 'domstats.block.capacity', None, PM_TYPE_U64, PM_SEM_INSTANT, units_bytes, 'VM backing imgs, capacity' ],
178 [ 'domstats.block.physical', None, PM_TYPE_U64, PM_SEM_INSTANT, units_bytes, 'VM backing imgs, physical' ],
181 self.vm_netstats_res = []
182 self.vm_netstats_cluster = 7
183 self.vm_netstats = [
184 # Name - empty - type - semantics - units - help
185 # See libvirt.git/src/libvirt-domain.c
186 [ 'domstats.net.count', None, PM_TYPE_U32, PM_SEM_COUNTER, units_count, 'VM NICs, count' ],
187 [ 'domstats.net.rx.bytes', None, PM_TYPE_U64, PM_SEM_COUNTER, units_bytes, 'VM NICs, rx bytes' ],
188 [ 'domstats.net.rx.pkts', None, PM_TYPE_U64, PM_SEM_COUNTER, units_count, 'VM NICs, rx pkts' ],
189 [ 'domstats.net.rx.errs', None, PM_TYPE_U64, PM_SEM_COUNTER, units_count, 'VM NICs, rx errs' ],
190 [ 'domstats.net.rx.drop', None, PM_TYPE_U64, PM_SEM_COUNTER, units_count, 'VM NICs, rx drop' ],
191 [ 'domstats.net.tx.bytes', None, PM_TYPE_U64, PM_SEM_COUNTER, units_bytes, 'VM NICs, tx bytes' ],
192 [ 'domstats.net.tx.pkts', None, PM_TYPE_U64, PM_SEM_COUNTER, units_count, 'VM NICs, tx pkts' ],
193 [ 'domstats.net.tx.errs', None, PM_TYPE_U64, PM_SEM_COUNTER, units_count, 'VM NICs, tx errs' ],
194 [ 'domstats.net.tx.drop', None, PM_TYPE_U64, PM_SEM_COUNTER, units_count, 'VM NICs, tx drop' ],
197 self.vm_perfstats_res = []
198 self.vm_perfstats_cluster = 8
199 self.vm_perfstats = [
200 # Name - empty - type - semantics - units - help
201 # See libvirt.git/src/libvirt-domain.c
202 [ 'domstats.perf.cmt', None, PM_TYPE_U64, PM_SEM_INSTANT, units_bytes, 'VM perf stats, cmt' ],
203 [ 'domstats.perf.mbmt', None, PM_TYPE_U64, PM_SEM_INSTANT, units_bpers, 'VM perf stats, mbmt' ],
204 [ 'domstats.perf.mbml', None, PM_TYPE_U64, PM_SEM_INSTANT, units_bpers, 'VM perf stats, mbml' ],
207 for item in range(len(self.hv_metrics)):
208 self.add_metric(name + '.' + self.hv_metrics[item][0], pmdaMetric(self.pmid(self.hv_cluster, item),
209 self.hv_metrics[item][2], self.hv_indom, self.hv_metrics[item][3],
210 self.hv_metrics[item][4]), self.hv_metrics[item][5], self.hv_metrics[item][5])
212 for item in range(len(self.vm_metrics)):
213 self.add_metric(name + '.' + self.vm_metrics[item][0], pmdaMetric(self.pmid(self.vm_cluster, item),
214 self.vm_metrics[item][2], self.vm_indom, self.vm_metrics[item][3],
215 self.vm_metrics[item][4]), self.vm_metrics[item][5], self.vm_metrics[item][5])
217 for item in range(len(self.vm_cpustats)):
218 self.add_metric(name + '.' + self.vm_cpustats[item][0], pmdaMetric(self.pmid(self.vm_cpustats_cluster, item),
219 self.vm_cpustats[item][2], self.vm_indom, self.vm_cpustats[item][3],
220 self.vm_cpustats[item][4]), self.vm_cpustats[item][5], self.vm_cpustats[item][5])
222 for item in range(len(self.vm_vcpustats)):
223 self.add_metric(name + '.' + self.vm_vcpustats[item][0], pmdaMetric(self.pmid(self.vm_vcpustats_cluster, item),
224 self.vm_vcpustats[item][2], self.vm_indom, self.vm_vcpustats[item][3],
225 self.vm_vcpustats[item][4]), self.vm_vcpustats[item][5], self.vm_vcpustats[item][5])
227 for item in range(len(self.vm_memstats)):
228 self.add_metric(name + '.' + self.vm_memstats[item][0], pmdaMetric(self.pmid(self.vm_memstats_cluster, item),
229 self.vm_memstats[item][2], self.vm_indom, self.vm_memstats[item][3],
230 self.vm_memstats[item][4]), self.vm_memstats[item][5], self.vm_memstats[item][5])
232 for item in range(len(self.vm_balloonstats)):
233 self.add_metric(name + '.' + self.vm_balloonstats[item][0], pmdaMetric(self.pmid(self.vm_balloonstats_cluster, item),
234 self.vm_balloonstats[item][2], self.vm_indom, self.vm_balloonstats[item][3],
235 self.vm_balloonstats[item][4]), self.vm_balloonstats[item][5], self.vm_balloonstats[item][5])
237 for item in range(len(self.vm_blockstats)):
238 self.add_metric(name + '.' + self.vm_blockstats[item][0], pmdaMetric(self.pmid(self.vm_blockstats_cluster, item),
239 self.vm_blockstats[item][2], self.vm_indom, self.vm_blockstats[item][3],
240 self.vm_blockstats[item][4]), self.vm_blockstats[item][5], self.vm_blockstats[item][5])
242 for item in range(len(self.vm_netstats)):
243 self.add_metric(name + '.' + self.vm_netstats[item][0], pmdaMetric(self.pmid(self.vm_netstats_cluster, item),
244 self.vm_netstats[item][2], self.vm_indom, self.vm_netstats[item][3],
245 self.vm_netstats[item][4]), self.vm_netstats[item][5], self.vm_netstats[item][5])
247 for item in range(len(self.vm_perfstats)):
248 self.add_metric(name + '.' + self.vm_perfstats[item][0], pmdaMetric(self.pmid(self.vm_perfstats_cluster, item),
249 self.vm_perfstats[item][2], self.vm_indom, self.vm_perfstats[item][3],
250 self.vm_perfstats[item][4]), self.vm_perfstats[item][5], self.vm_perfstats[item][5])
252 self.set_refresh(self.libvirt_refresh)
253 self.set_fetch_callback(self.libvirt_fetch_callback)
255 @atexit.register
256 def cleanup():
257 """ Clean up """
258 if self.conn:
259 if not os.environ.get('PCP_PYTHON_DOMAIN') and not os.environ.get('PCP_PYTHON_PMNS'):
260 self.log("Closing connection to " + self.uri)
261 self.conn.close()
263 def read_config(self):
264 """ Read configuration """
265 conffile = PCP.pmGetConfig('PCP_PMDAS_DIR')
266 conffile += '/' + self.read_name() + '/' + self.read_name() + '.conf'
268 # Silently ignore missing file/section
269 config = ConfigParser.SafeConfigParser()
270 config.read(conffile)
271 if config.has_section('pmda'):
272 for opt in config.options('pmda'):
273 if opt == 'user':
274 self.user = config.get('pmda', opt)
275 elif opt == 'uri':
276 self.uri = config.get('pmda', opt)
277 else:
278 self.err("Invalid directive '%s' in %s.\n" % (opt, conffile))
279 sys.exit(1)
281 def connect_libvirt(self):
282 """ Connect to libvirt """
283 conn = None
284 try:
285 conn = libvirt.openReadOnly(self.uri)
286 self.doms = conn.listAllDomains(libvirt.VIR_CONNECT_LIST_DOMAINS_ACTIVE)
287 if not os.environ.get('PCP_PYTHON_DOMAIN') and not os.environ.get('PCP_PYTHON_PMNS'):
288 self.log("Connected as " + self.user + " to " + self.uri)
289 except libvirt.libvirtError as error:
290 self.log("Failed to connect to the hypervisor: %s" % error)
291 return conn
293 def convert_value(self, value, mtype):
294 """ Convert value """
295 # No float/double in use
296 if mtype != PM_TYPE_STRING:
297 value = long(value)
298 return value
300 def scale_to_kib(self, value, unit):
301 """ Scale value to KiB """
302 if unit == "b" or unit == "bytes":
303 return value // 1024
304 elif unit == "k" or unit == "KiB":
305 return value
306 elif unit == "M" or unit == "MiB":
307 return 1024 * value
308 elif unit == "G" or unit == "GiB":
309 return 1024 ** 2 * value
310 elif unit == "T" or unit == "TiB":
311 return 1024 ** 3 * value
312 elif unit == "P" or unit == "PiB":
313 return 1024 ** 4 * value
314 elif unit == "E" or unit == "EiB":
315 return 1024 ** 5 * value
316 elif unit == "KB":
317 return value * 1000 // 1024
318 elif unit == "MB":
319 return value * 1000 ** 2 // 1024
320 elif unit == "GB":
321 return value * 1000 ** 3 // 1024
322 elif unit == "TB":
323 return value * 1000 ** 4 // 1024
324 elif unit == "PB":
325 return value * 1000 ** 5 // 1024
326 elif unit == "EB":
327 return value * 1000 ** 6 // 1024
328 else:
329 return -1
331 def libvirt_refresh(self, cluster):
332 """ Refresh """
333 if not self.conn:
334 self.conn = self.connect_libvirt()
335 if not self.conn:
336 self.doms = []
337 self.replace_indom(self.vm_indom, {"0":c_int(1)})
338 return
340 if cluster == self.hv_cluster:
341 return
343 if cluster == self.vm_cluster:
344 try:
345 self.doms = self.conn.listAllDomains(libvirt.VIR_CONNECT_LIST_DOMAINS_ACTIVE)
346 except libvirt.libvirtError as error:
347 self.log("Failed to list domains: %s" % error)
348 self.conn = None
349 self.doms = []
350 return
351 insts = {}
352 for dom in self.doms:
353 insts[dom.UUIDString()] = c_int(1)
354 self.vm_insts.set_instances(self.vm_indom, insts)
355 self.replace_indom(self.vm_indom, insts)
356 return
358 if not self.doms:
359 return
361 flags = None
362 try:
363 flags = libvirt.VIR_CONNECT_GET_ALL_DOMAINS_STATS_ACTIVE
364 except:
365 pass
367 if cluster == self.vm_cpustats_cluster:
368 try:
369 self.vm_cpustats_res = []
370 if flags is not None:
371 stats = libvirt.VIR_DOMAIN_STATS_CPU_TOTAL
372 self.vm_cpustats_res = self.conn.domainListGetStats(self.doms, stats, flags)
373 except libvirt.libvirtError as error:
374 self.log("Failed to get domain cpu stats: %s" % error)
375 return
377 if cluster == self.vm_vcpustats_cluster:
378 try:
379 self.vm_vcpustats_res = []
380 if flags is not None:
381 stats = libvirt.VIR_DOMAIN_STATS_VCPU
382 self.vm_vcpustats_res = self.conn.domainListGetStats(self.doms, stats, flags)
383 except libvirt.libvirtError as error:
384 self.log("Failed to get domain vcpu stats: %s" % error)
385 return
387 if cluster == self.vm_memstats_cluster:
388 try:
389 self.vm_memstats_res = {}
390 for dom in self.doms:
391 self.vm_memstats_res[dom.UUIDString()] = dom.memoryStats()
392 except libvirt.libvirtError as error:
393 self.log("Failed to get domain mem stats: %s" % error)
394 return
396 if cluster == self.vm_balloonstats_cluster:
397 try:
398 self.vm_balloonstats_res = []
399 if flags is not None:
400 stats = libvirt.VIR_DOMAIN_STATS_BALLOON
401 self.vm_balloonstats_res = self.conn.domainListGetStats(self.doms, stats, flags)
402 except libvirt.libvirtError as error:
403 self.log("Failed to get domain balloon stats: %s" % error)
404 return
406 if cluster == self.vm_blockstats_cluster:
407 try:
408 self.vm_blockstats_res = []
409 if flags is not None:
410 stats = libvirt.VIR_DOMAIN_STATS_BLOCK | libvirt.VIR_CONNECT_GET_ALL_DOMAINS_STATS_BACKING
411 self.vm_blockstats_res = self.conn.domainListGetStats(self.doms, stats, flags)
412 except libvirt.libvirtError as error:
413 self.log("Failed to get domain block stats: %s" % error)
414 return
416 if cluster == self.vm_netstats_cluster:
417 try:
418 self.vm_netstats_res = []
419 if flags is not None:
420 stats = libvirt.VIR_DOMAIN_STATS_INTERFACE
421 self.vm_netstats_res = self.conn.domainListGetStats(self.doms, stats, flags)
422 except libvirt.libvirtError as error:
423 self.log("Failed to get domain net stats: %s" % error)
424 return
426 if cluster == self.vm_perfstats_cluster:
427 try:
428 self.vm_perfstats_res = []
429 if flags is not None:
430 stats = libvirt.VIR_DOMAIN_STATS_PERF
431 self.vm_perfstats_res = self.conn.domainListGetStats(self.doms, stats, flags)
432 except libvirt.libvirtError as error:
433 self.log("Failed to get domain perf stats: %s" % error)
434 return
436 def libvirt_fetch_callback(self, cluster, item, inst):
437 """ Fetch callback """
438 if not self.conn:
439 return [PM_ERR_NOTCONN, 0]
441 if cluster == self.hv_cluster:
442 try:
443 method = getattr(self.conn, self.hv_metrics[item][1])
444 if self.hv_metrics[item][1] == "numOfDevices":
445 value = method(None, 0)
446 else:
447 value = method()
448 value = self.convert_value(value, self.hv_metrics[item][2])
449 return [value, 1]
450 except:
451 return [PM_ERR_VALUE, 0]
453 if not self.doms:
454 return [PM_ERR_AGAIN, 0]
456 if cluster == self.vm_cluster:
457 try:
458 doc = None
459 uuid = self.vm_insts.inst_name_lookup(inst)
460 for dom in self.doms:
461 if dom.UUIDString() == uuid:
462 doc = etree.fromstring(dom.XMLDesc(0))
463 break
464 if doc is None:
465 [PM_ERR_INST, 0]
467 path = self.vm_metrics[item][1]
468 value = doc.xpath(path)
470 if not len(value):
471 # Custom fallback: if "current" vCPUs attribute is
472 # not found then assume maximum allocation is used
473 if self.vm_metrics[item][0] == 'dominfo.vcpu.current':
474 value = doc.xpath("/domain/vcpu")
475 else:
476 return [PM_ERR_AGAIN, 0]
478 # Extract and scale if needed
479 if type(value) is list:
480 if 'text' in dir(value[0]):
481 value = value[0].text
482 else:
483 value = value[0]
484 if 'dominfo.memory.' in self.vm_metrics[item][0]:
485 path = path + "/@unit"
486 unit = doc.xpath(path)[0]
487 value = self.scale_to_kib(int(value), unit)
488 if value < 0:
489 return [PM_ERR_NYI, 0]
491 value = self.convert_value(value, self.vm_metrics[item][2])
492 return [value, 1]
493 except:
494 return [PM_ERR_VALUE, 0]
496 if cluster == self.vm_memstats_cluster:
497 try:
498 key = self.vm_memstats[item][0].rsplit('.')[2]
499 return [self.vm_memstats_res[self.vm_insts.inst_name_lookup(inst)][key], 1]
500 except:
501 return [PM_ERR_VALUE, 0]
503 if cluster == self.vm_cpustats_cluster or \
504 cluster == self.vm_vcpustats_cluster or \
505 cluster == self.vm_balloonstats_cluster or \
506 cluster == self.vm_blockstats_cluster or \
507 cluster == self.vm_netstats_cluster or \
508 cluster == self.vm_perfstats_cluster:
509 try:
510 if cluster == self.vm_cpustats_cluster:
511 res = self.vm_cpustats_res
512 mtx = self.vm_cpustats
513 elif cluster == self.vm_vcpustats_cluster:
514 res = self.vm_vcpustats_res
515 mtx = self.vm_vcpustats
516 elif cluster == self.vm_balloonstats_cluster:
517 res = self.vm_balloonstats_res
518 mtx = self.vm_balloonstats
519 elif cluster == self.vm_blockstats_cluster:
520 res = self.vm_blockstats_res
521 mtx = self.vm_blockstats
522 elif cluster == self.vm_netstats_cluster:
523 res = self.vm_netstats_res
524 mtx = self.vm_netstats
525 elif cluster == self.vm_perfstats_cluster:
526 res = self.vm_perfstats_res
527 mtx = self.vm_perfstats
529 # Locate the correct VM domain
530 pos = -1
531 uuid = self.vm_insts.inst_name_lookup(inst)
532 for i, r in enumerate(res):
533 if r[0].UUIDString() == uuid:
534 pos = i
535 break
536 if pos < 0:
537 return [PM_ERR_INST, 0]
539 # All done for non-dynamic clusters
540 if cluster != self.vm_vcpustats_cluster and \
541 cluster != self.vm_blockstats_cluster and \
542 cluster != self.vm_netstats_cluster:
543 key = '.'.join(mtx[item][0].split('.')[1:])
544 if key in res[pos][1]:
545 return [res[pos][1][key], 1]
546 else:
547 return [PM_ERR_AGAIN, 0]
549 # Non-combined values in dynamic clusters
550 key = '.'.join(mtx[item][0].split('.')[1:])
551 if key == 'vcpu.current' or key == 'vcpu.maximum' or \
552 key == 'net.count' or key == 'block.count':
553 return [res[pos][1][key], 1]
555 # Combine N values for dynamic metrics
556 if 'vcpu' in mtx[item][0]:
557 count = res[pos][1]['vcpu.current']
558 elif 'block' in mtx[item][0]:
559 count = res[pos][1]['block.count']
560 elif 'net' in mtx[item][0]:
561 count = res[pos][1]['net.count']
562 else:
563 return [PM_ERR_VALUE, 0]
565 # Calculate the combined value
566 value = 0
567 for i in range(count):
568 k = key.split('.')[0] + '.' + str(i) + '.' + '.'.join(key.split('.')[1:])
569 if k in res[pos][1]:
570 value += res[pos][1][k]
571 return [value, 1]
572 except:
573 return [PM_ERR_VALUE, 0]
575 return [PM_ERR_PMID, 0]
577 if __name__ == '__main__':
578 LibvirtPMDA('libvirt', 491).run()