
Support for Intel CMT perf events was already removed from nova[1], because the implementation was removed from kernel some years ago[2]. These metrics have been broken for some time, so remove these metrics directly. [1] e45f3b5d71342d179bc150901f2dd5dabef238ca [2] fc4794acc6b13afade1bb72a1ae9f574707d2f0d Change-Id: Ia1996692347a3a3e8d4ff057996d5eb6840ca3de
257 lines
11 KiB
Python
257 lines
11 KiB
Python
#
|
|
# Copyright 2012 Red Hat, Inc
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
"""Implementation of Inspector abstraction for libvirt."""
|
|
|
|
from lxml import etree
|
|
from oslo_log import log as logging
|
|
from oslo_utils import units
|
|
|
|
try:
|
|
import libvirt
|
|
except ImportError:
|
|
libvirt = None
|
|
|
|
from ceilometer.compute.pollsters import util
|
|
from ceilometer.compute.virt import inspector as virt_inspector
|
|
from ceilometer.compute.virt.libvirt import utils as libvirt_utils
|
|
from ceilometer.i18n import _
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class LibvirtInspector(virt_inspector.Inspector):
|
|
|
|
def __init__(self, conf):
|
|
super(LibvirtInspector, self).__init__(conf)
|
|
# NOTE(sileht): create a connection on startup
|
|
self.connection
|
|
self.cache = {}
|
|
|
|
@property
|
|
def connection(self):
|
|
return libvirt_utils.refresh_libvirt_connection(self.conf, self)
|
|
|
|
def _lookup_by_uuid(self, instance):
|
|
instance_name = util.instance_name(instance)
|
|
try:
|
|
return self.connection.lookupByUUIDString(instance.id)
|
|
except libvirt.libvirtError as ex:
|
|
if libvirt_utils.is_disconnection_exception(ex):
|
|
raise
|
|
msg = _("Error from libvirt while looking up instance "
|
|
"<name=%(name)s, id=%(id)s>: "
|
|
"[Error Code %(error_code)s] "
|
|
"%(ex)s") % {'name': instance_name,
|
|
'id': instance.id,
|
|
'error_code': ex.get_error_code(),
|
|
'ex': ex}
|
|
raise virt_inspector.InstanceNotFoundException(msg)
|
|
except Exception as ex:
|
|
raise virt_inspector.InspectorException(str(ex))
|
|
|
|
def _get_domain_not_shut_off_or_raise(self, instance):
|
|
instance_name = util.instance_name(instance)
|
|
domain = self._lookup_by_uuid(instance)
|
|
|
|
state = domain.info()[0]
|
|
if state == libvirt.VIR_DOMAIN_SHUTOFF:
|
|
msg = _('Failed to inspect data of instance '
|
|
'<name=%(name)s, id=%(id)s>, '
|
|
'domain state is SHUTOFF.') % {
|
|
'name': instance_name, 'id': instance.id}
|
|
raise virt_inspector.InstanceShutOffException(msg)
|
|
|
|
return domain
|
|
|
|
@libvirt_utils.retry_on_disconnect
|
|
def inspect_vnics(self, instance, duration):
|
|
domain = self._get_domain_not_shut_off_or_raise(instance)
|
|
|
|
tree = etree.fromstring(domain.XMLDesc(0))
|
|
for iface in tree.findall('devices/interface'):
|
|
target = iface.find('target')
|
|
if target is not None:
|
|
name = target.get('dev')
|
|
else:
|
|
continue
|
|
mac = iface.find('mac')
|
|
if mac is not None:
|
|
mac_address = mac.get('address')
|
|
else:
|
|
continue
|
|
fref = iface.find('filterref')
|
|
if fref is not None:
|
|
fref = fref.get('filter')
|
|
|
|
params = dict((p.get('name').lower(), p.get('value'))
|
|
for p in iface.findall('filterref/parameter'))
|
|
|
|
# Extract interface ID
|
|
try:
|
|
interfaceid = iface.find('virtualport').find(
|
|
'parameters').get('interfaceid')
|
|
except AttributeError:
|
|
interfaceid = None
|
|
|
|
# Extract source bridge
|
|
try:
|
|
bridge = iface.find('source').get('bridge')
|
|
except AttributeError:
|
|
bridge = None
|
|
|
|
params['interfaceid'] = interfaceid
|
|
params['bridge'] = bridge
|
|
|
|
dom_stats = domain.interfaceStats(name)
|
|
|
|
# Retrieve previous values
|
|
prev = self.cache.get(name)
|
|
|
|
# Store values for next call
|
|
self.cache[name] = dom_stats
|
|
|
|
if prev:
|
|
# Compute stats
|
|
rx_delta = dom_stats[0] - prev[0]
|
|
tx_delta = dom_stats[4] - prev[4]
|
|
|
|
# Avoid negative values
|
|
if rx_delta < 0:
|
|
rx_delta = dom_stats[0]
|
|
if tx_delta < 0:
|
|
tx_delta = dom_stats[4]
|
|
else:
|
|
LOG.debug('No delta meter predecessor for %s / %s' %
|
|
(instance.id, name))
|
|
rx_delta = 0
|
|
tx_delta = 0
|
|
|
|
yield virt_inspector.InterfaceStats(name=name,
|
|
mac=mac_address,
|
|
fref=fref,
|
|
parameters=params,
|
|
rx_bytes=dom_stats[0],
|
|
rx_packets=dom_stats[1],
|
|
rx_errors=dom_stats[2],
|
|
rx_drop=dom_stats[3],
|
|
rx_bytes_delta=rx_delta,
|
|
tx_bytes=dom_stats[4],
|
|
tx_packets=dom_stats[5],
|
|
tx_errors=dom_stats[6],
|
|
tx_drop=dom_stats[7],
|
|
tx_bytes_delta=tx_delta)
|
|
|
|
@staticmethod
|
|
def _get_disk_devices(domain):
|
|
tree = etree.fromstring(domain.XMLDesc(0))
|
|
return filter(bool, [target.get("dev") for target in
|
|
tree.findall('devices/disk/target')
|
|
if target.getparent().find('source') is not None])
|
|
|
|
@libvirt_utils.retry_on_disconnect
|
|
def inspect_disks(self, instance, duration):
|
|
domain = self._get_domain_not_shut_off_or_raise(instance)
|
|
for device in self._get_disk_devices(domain):
|
|
try:
|
|
block_stats = domain.blockStats(device)
|
|
block_stats_flags = domain.blockStatsFlags(device, 0)
|
|
yield virt_inspector.DiskStats(
|
|
device=device,
|
|
read_requests=block_stats[0], read_bytes=block_stats[1],
|
|
write_requests=block_stats[2], write_bytes=block_stats[3],
|
|
errors=block_stats[4],
|
|
wr_total_times=block_stats_flags['wr_total_times'],
|
|
rd_total_times=block_stats_flags['rd_total_times'])
|
|
except libvirt.libvirtError as ex:
|
|
# raised error even if lock is acquired while live migration,
|
|
# even it looks normal.
|
|
LOG.warning(_("Error from libvirt while checking blockStats, "
|
|
"This may not be harmful, but please check : "
|
|
"%(ex)s") % {'ex': ex})
|
|
pass
|
|
|
|
@libvirt_utils.retry_on_disconnect
|
|
def inspect_disk_info(self, instance, duration):
|
|
domain = self._get_domain_not_shut_off_or_raise(instance)
|
|
for device in self._get_disk_devices(domain):
|
|
block_info = domain.blockInfo(device)
|
|
# if vm mount cdrom, libvirt will align by 4K bytes, capacity may
|
|
# be smaller than physical, avoid with this.
|
|
# https://libvirt.org/html/libvirt-libvirt-domain.html
|
|
disk_capacity = max(block_info[0], block_info[2])
|
|
yield virt_inspector.DiskInfo(device=device,
|
|
capacity=disk_capacity,
|
|
allocation=block_info[1],
|
|
physical=block_info[2])
|
|
|
|
@libvirt_utils.raise_nodata_if_unsupported
|
|
@libvirt_utils.retry_on_disconnect
|
|
def inspect_instance(self, instance, duration=None):
|
|
domain = self._get_domain_not_shut_off_or_raise(instance)
|
|
|
|
memory_used = memory_resident = None
|
|
memory_swap_in = memory_swap_out = None
|
|
memory_stats = domain.memoryStats()
|
|
# Stat provided from libvirt is in KB, converting it to MB.
|
|
if 'usable' in memory_stats and 'available' in memory_stats:
|
|
memory_used = (memory_stats['available'] -
|
|
memory_stats['usable']) / units.Ki
|
|
elif 'available' in memory_stats and 'unused' in memory_stats:
|
|
memory_used = (memory_stats['available'] -
|
|
memory_stats['unused']) / units.Ki
|
|
if 'rss' in memory_stats:
|
|
memory_resident = memory_stats['rss'] / units.Ki
|
|
if 'swap_in' in memory_stats and 'swap_out' in memory_stats:
|
|
memory_swap_in = memory_stats['swap_in'] / units.Ki
|
|
memory_swap_out = memory_stats['swap_out'] / units.Ki
|
|
|
|
# TODO(sileht): stats also have the disk/vnic info
|
|
# we could use that instead of the old method for Queen
|
|
stats = self.connection.domainListGetStats([domain], 0)[0][1]
|
|
cpu_time = 0
|
|
current_cpus = stats.get('vcpu.current')
|
|
# Iterate over the maximum number of CPUs here, and count the
|
|
# actual number encountered, since the vcpu.x structure can
|
|
# have holes according to
|
|
# https://libvirt.org/git/?p=libvirt.git;a=blob;f=src/libvirt-domain.c
|
|
# virConnectGetAllDomainStats()
|
|
for vcpu in range(stats.get('vcpu.maximum', 0)):
|
|
try:
|
|
cpu_time += (stats.get('vcpu.%s.time' % vcpu) +
|
|
stats.get('vcpu.%s.wait' % vcpu))
|
|
current_cpus -= 1
|
|
except TypeError:
|
|
# pass here, if there are too many holes, the cpu count will
|
|
# not match, so don't need special error handling.
|
|
pass
|
|
|
|
if current_cpus:
|
|
# There wasn't enough data, so fall back
|
|
cpu_time = stats.get('cpu.time')
|
|
|
|
return virt_inspector.InstanceStats(
|
|
power_state=domain.info()[0],
|
|
cpu_number=stats.get('vcpu.current'),
|
|
cpu_time=cpu_time,
|
|
memory_usage=memory_used,
|
|
memory_resident=memory_resident,
|
|
memory_swap_in=memory_swap_in,
|
|
memory_swap_out=memory_swap_out,
|
|
cpu_cycles=stats.get("perf.cpu_cycles"),
|
|
instructions=stats.get("perf.instructions"),
|
|
cache_references=stats.get("perf.cache_references"),
|
|
cache_misses=stats.get("perf.cache_misses")
|
|
)
|