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 """
20 import configparser
as ConfigParser
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:
43 DEFAULT_URI
= 'qemu:///system'
45 class LibvirtPMDA(PMDA
):
46 """ PCP libvirt PMDA """
47 def __init__(self
, name
, domain
):
49 PMDA
.__init
__(self
, name
, domain
)
51 self
.user
= DEFAULT_USER
52 self
.uri
= DEFAULT_URI
54 self
.set_user(self
.user
)
58 self
.conn
= self
.connect_libvirt()
60 test
= libvirt
.VIR_CONNECT_GET_ALL_DOMAINS_STATS_ACTIVE
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
76 # Name - method - type - semantics - units - help
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
)
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
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
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
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
)
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
)
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'):
274 self
.user
= config
.get('pmda', opt
)
276 self
.uri
= config
.get('pmda', opt
)
278 self
.err("Invalid directive '%s' in %s.\n" % (opt
, conffile
))
281 def connect_libvirt(self
):
282 """ Connect to libvirt """
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
)
293 def convert_value(self
, value
, mtype
):
294 """ Convert value """
295 # No float/double in use
296 if mtype
!= PM_TYPE_STRING
:
300 def scale_to_kib(self
, value
, unit
):
301 """ Scale value to KiB """
302 if unit
== "b" or unit
== "bytes":
304 elif unit
== "k" or unit
== "KiB":
306 elif unit
== "M" or unit
== "MiB":
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
317 return value
* 1000 // 1024
319 return value
* 1000 ** 2 // 1024
321 return value
* 1000 ** 3 // 1024
323 return value
* 1000 ** 4 // 1024
325 return value
* 1000 ** 5 // 1024
327 return value
* 1000 ** 6 // 1024
331 def libvirt_refresh(self
, cluster
):
334 self
.conn
= self
.connect_libvirt()
337 self
.replace_indom(self
.vm_indom
, {"0":c_int(1)})
340 if cluster
== self
.hv_cluster
:
343 if cluster
== self
.vm_cluster
:
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
)
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
)
363 flags
= libvirt
.VIR_CONNECT_GET_ALL_DOMAINS_STATS_ACTIVE
367 if cluster
== self
.vm_cpustats_cluster
:
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
)
377 if cluster
== self
.vm_vcpustats_cluster
:
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
)
387 if cluster
== self
.vm_memstats_cluster
:
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
)
396 if cluster
== self
.vm_balloonstats_cluster
:
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
)
406 if cluster
== self
.vm_blockstats_cluster
:
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
)
416 if cluster
== self
.vm_netstats_cluster
:
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
)
426 if cluster
== self
.vm_perfstats_cluster
:
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
)
436 def libvirt_fetch_callback(self
, cluster
, item
, inst
):
437 """ Fetch callback """
439 return [PM_ERR_NOTCONN
, 0]
441 if cluster
== self
.hv_cluster
:
443 method
= getattr(self
.conn
, self
.hv_metrics
[item
][1])
444 if self
.hv_metrics
[item
][1] == "numOfDevices":
445 value
= method(None, 0)
448 value
= self
.convert_value(value
, self
.hv_metrics
[item
][2])
451 return [PM_ERR_VALUE
, 0]
454 return [PM_ERR_AGAIN
, 0]
456 if cluster
== self
.vm_cluster
:
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))
467 path
= self
.vm_metrics
[item
][1]
468 value
= doc
.xpath(path
)
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")
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
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
)
489 return [PM_ERR_NYI
, 0]
491 value
= self
.convert_value(value
, self
.vm_metrics
[item
][2])
494 return [PM_ERR_VALUE
, 0]
496 if cluster
== self
.vm_memstats_cluster
:
498 key
= self
.vm_memstats
[item
][0].rsplit('.')[2]
499 return [self
.vm_memstats_res
[self
.vm_insts
.inst_name_lookup(inst
)][key
], 1]
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
:
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
531 uuid
= self
.vm_insts
.inst_name_lookup(inst
)
532 for i
, r
in enumerate(res
):
533 if r
[0].UUIDString() == uuid
:
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]
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']
563 return [PM_ERR_VALUE
, 0]
565 # Calculate the combined value
567 for i
in range(count
):
568 k
= key
.split('.')[0] + '.' + str(i
) + '.' + '.'.join(key
.split('.')[1:])
570 value
+= res
[pos
][1][k
]
573 return [PM_ERR_VALUE
, 0]
575 return [PM_ERR_PMID
, 0]
577 if __name__
== '__main__':
578 LibvirtPMDA('libvirt', 491).run()