diff --git a/ceilometer/compute/pollsters/cpu.py b/ceilometer/compute/pollsters/cpu.py index d8ee367128..ebd9a50c99 100644 --- a/ceilometer/compute/pollsters/cpu.py +++ b/ceilometer/compute/pollsters/cpu.py @@ -17,6 +17,7 @@ from oslo_log import log import ceilometer +from ceilometer.agent import plugin_base from ceilometer.compute import pollsters from ceilometer.compute.pollsters import util from ceilometer.compute.virt import inspector as virt_inspector @@ -91,3 +92,40 @@ class CPUUtilPollster(pollsters.BaseComputePollster): except Exception as err: LOG.exception(_('Could not get CPU Util for %(id)s: %(e)s'), {'id': instance.id, 'e': err}) + + +class CPUL3CachePollster(pollsters.BaseComputePollster): + + def get_samples(self, manager, cache, resources): + self._inspection_duration = self._record_poll_time() + for instance in resources: + LOG.debug(_('checking cache usage for instance %s'), instance.id) + try: + cpu_cache = self.inspector.inspect_cpu_l3_cache( + instance, self._inspection_duration) + LOG.debug(_("CPU cache size: %(id)s %(cache_size)d"), + ({'id': instance.id, + 'l3_cache_usage': cpu_cache.l3_cache_usage})) + yield util.make_sample_from_instance( + instance, + name='cpu_l3_cache', + type=sample.TYPE_GAUGE, + unit='B', + volume=cpu_cache.l3_cache_usage, + ) + except virt_inspector.InstanceNotFoundException as err: + # Instance was deleted while getting samples. Ignore it. + LOG.debug('Exception while getting samples %s', err) + except virt_inspector.NoDataException as e: + LOG.warning(('Cannot inspect data of %(pollster)s for ' + '%(instance_id)s, non-fatal reason: %(exc)s'), + {'pollster': self.__class__.__name__, + 'instance_id': instance.id, 'exc': e}) + raise plugin_base.PollsterPermanentError(resources) + except ceilometer.NotImplementedError: + # Selected inspector does not implement this pollster. + LOG.debug('Obtaining cache usage is not implemented for %s', + self.inspector.__class__.__name__) + except Exception as err: + LOG.exception(_('Could not get cache usage for %(id)s: %(e)s'), + {'id': instance.id, 'e': err}) diff --git a/ceilometer/compute/virt/inspector.py b/ceilometer/compute/virt/inspector.py index 484bf82f71..64526796bb 100644 --- a/ceilometer/compute/virt/inspector.py +++ b/ceilometer/compute/virt/inspector.py @@ -58,6 +58,13 @@ CPUStats = collections.namedtuple('CPUStats', ['number', 'time']) # CPUUtilStats = collections.namedtuple('CPUUtilStats', ['util']) +# Named tuple representing CPU L3 cache usage statistics. +# +# cachesize: Amount of CPU L3 cache used +# +CPUL3CacheUsageStats = collections.namedtuple('CPUL3CacheUsageStats', + ['l3_cache_usage']) + # Named tuple representing Memory usage statistics. # # usage: Amount of memory used @@ -218,6 +225,16 @@ class Inspector(object): """ raise ceilometer.NotImplementedError + def inspect_cpu_l3_cache(self, instance, duration=None): + """Inspect the CPU L3 cache usage for an instance. + + :param instance: the target instance + :param duration: the last 'n' seconds, over which the value should be + inspected + :return: the amount of cpu l3 cache used + """ + raise ceilometer.NotImplementedError + def inspect_vnics(self, instance): """Inspect the vNIC statistics for an instance. diff --git a/ceilometer/compute/virt/libvirt/inspector.py b/ceilometer/compute/virt/libvirt/inspector.py index 8220d997fc..2bbbadaefb 100644 --- a/ceilometer/compute/virt/libvirt/inspector.py +++ b/ceilometer/compute/virt/libvirt/inspector.py @@ -117,6 +117,29 @@ class LibvirtInspector(virt_inspector.Inspector): dom_info = domain.info() return virt_inspector.CPUStats(number=dom_info[3], time=dom_info[4]) + def inspect_cpu_l3_cache(self, instance): + domain = self._lookup_by_uuid(instance) + try: + stats = self.connection.domainListGetStats( + [domain], libvirt.VIR_DOMAIN_STATS_PERF) + perf = stats[0][1] + usage = perf["perf.cmt"] + return virt_inspector.CPUL3CacheUsageStats(l3_cache_usage=usage) + except AttributeError as e: + msg = _('Perf is not supported by current version of libvirt, and ' + 'failed to inspect l3 cache usage of %(instance_uuid)s, ' + 'can not get info from libvirt: %(error)s') % { + 'instance_uuid': instance.id, 'error': e} + raise virt_inspector.NoDataException(msg) + # domainListGetStats might launch an exception if the method or + # cmt perf event is not supported by the underlying hypervisor + # being used by libvirt. + except libvirt.libvirtError as e: + msg = _('Failed to inspect l3 cache usage of %(instance_uuid)s, ' + 'can not get info from libvirt: %(error)s') % { + 'instance_uuid': instance.id, 'error': e} + raise virt_inspector.NoDataException(msg) + def _get_domain_not_shut_off_or_raise(self, instance): instance_name = util.instance_name(instance) domain = self._lookup_by_uuid(instance) diff --git a/ceilometer/tests/unit/compute/pollsters/test_cpu.py b/ceilometer/tests/unit/compute/pollsters/test_cpu.py index bfc3f7296a..6211b57ccd 100644 --- a/ceilometer/tests/unit/compute/pollsters/test_cpu.py +++ b/ceilometer/tests/unit/compute/pollsters/test_cpu.py @@ -106,3 +106,36 @@ class TestCPUUtilPollster(base.TestPollsterBase): _verify_cpu_util_metering(40) _verify_cpu_util_metering(60) + + +class TestCPUL3CachePollster(base.TestPollsterBase): + + def setUp(self): + super(TestCPUL3CachePollster, self).setUp() + + @mock.patch('ceilometer.pipeline.setup_pipeline', mock.MagicMock()) + def test_get_samples(self): + next_value = iter(( + virt_inspector.CPUL3CacheUsageStats(l3_cache_usage=90112), + virt_inspector.CPUL3CacheUsageStats(l3_cache_usage=180224), + )) + + def inspect_cpu_l3_cache(name, duration): + return next(next_value) + + self.inspector.inspect_cpu_l3_cache = (mock.Mock( + side_effect=inspect_cpu_l3_cache)) + + mgr = manager.AgentManager() + pollster = cpu.CPUL3CachePollster() + + def _verify_cpu_l3_cache_metering(expected_usage): + cache = {} + samples = list(pollster.get_samples(mgr, cache, [self.instance])) + self.assertEqual(1, len(samples)) + self.assertEqual(set(['cpu_l3_cache']), + set([s.name for s in samples])) + self.assertEqual(expected_usage, samples[0].volume) + + _verify_cpu_l3_cache_metering(90112) + _verify_cpu_l3_cache_metering(180224) diff --git a/ceilometer/tests/unit/compute/virt/libvirt/test_inspector.py b/ceilometer/tests/unit/compute/virt/libvirt/test_inspector.py index 16220cfaac..c125a55f23 100644 --- a/ceilometer/tests/unit/compute/virt/libvirt/test_inspector.py +++ b/ceilometer/tests/unit/compute/virt/libvirt/test_inspector.py @@ -71,6 +71,19 @@ class TestLibvirtInspection(base.BaseTestCase): self.inspector.inspect_cpus, self.instance) + def test_inspect_cpu_l3_cache(self): + fake_stats = [({}, {'perf.cmt': 90112})] + connection = self.inspector.connection + with contextlib.ExitStack() as stack: + stack.enter_context(mock.patch.object(connection, + 'lookupByUUIDString', + return_value=self.domain)) + stack.enter_context(mock.patch.object(connection, + 'domainListGetStats', + return_value=fake_stats)) + cpu_info = self.inspector.inspect_cpu_l3_cache(self.instance) + self.assertEqual(90112, cpu_info.l3_cache_usage) + def test_inspect_vnics(self): dom_xml = """ diff --git a/setup.cfg b/setup.cfg index 81f4ceb929..0713f8d148 100644 --- a/setup.cfg +++ b/setup.cfg @@ -95,6 +95,7 @@ ceilometer.poll.compute = disk.device.iops = ceilometer.compute.pollsters.disk:PerDeviceDiskIOPSPollster cpu = ceilometer.compute.pollsters.cpu:CPUPollster cpu_util = ceilometer.compute.pollsters.cpu:CPUUtilPollster + cpu_l3_cache = ceilometer.compute.pollsters.cpu:CPUL3CachePollster network.incoming.bytes = ceilometer.compute.pollsters.net:IncomingBytesPollster network.incoming.packets = ceilometer.compute.pollsters.net:IncomingPacketsPollster network.outgoing.bytes = ceilometer.compute.pollsters.net:OutgoingBytesPollster