diff --git a/ceilometer/compute/pollsters/memory.py b/ceilometer/compute/pollsters/memory.py index ee627ad9f..f5a20ae7e 100644 --- a/ceilometer/compute/pollsters/memory.py +++ b/ceilometer/compute/pollsters/memory.py @@ -16,7 +16,7 @@ import ceilometer from ceilometer.compute import pollsters from ceilometer.compute.pollsters import util from ceilometer.compute.virt import inspector as virt_inspector -from ceilometer.i18n import _, _LW +from ceilometer.i18n import _, _LW, _LE from ceilometer.openstack.common import log from ceilometer import sample @@ -63,3 +63,46 @@ class MemoryUsagePollster(pollsters.BaseComputePollster): LOG.exception(_('Could not get Memory Usage for ' '%(id)s: %(e)s'), {'id': instance.id, 'e': err}) + + +class MemoryResidentPollster(pollsters.BaseComputePollster): + + def get_samples(self, manager, cache, resources): + self._inspection_duration = self._record_poll_time() + for instance in resources: + LOG.debug(_('Checking resident memory for instance %s'), + instance.id) + try: + memory_info = self.inspector.inspect_memory_resident( + instance, self._inspection_duration) + LOG.debug(_("RESIDENT MEMORY: %(instance)s %(resident)f"), + ({'instance': instance.__dict__, + 'resident': memory_info.resident})) + yield util.make_sample_from_instance( + instance, + name='memory.resident', + type=sample.TYPE_GAUGE, + unit='MB', + volume=memory_info.resident, + ) + 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.InstanceShutOffException as e: + LOG.warn(_LW('Instance %(instance_id)s was shut off while ' + 'getting samples of %(pollster)s: %(exc)s'), + {'instance_id': instance.id, + 'pollster': self.__class__.__name__, 'exc': e}) + except virt_inspector.NoDataException as e: + LOG.warn(_LW('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}) + except ceilometer.NotImplementedError: + # Selected inspector does not implement this pollster. + LOG.debug(_('Obtaining Resident Memory is not implemented' + ' for %s'), self.inspector.__class__.__name__) + except Exception as err: + LOG.exception(_LE('Could not get Resident Memory 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 f1b9bc227..f83feee1f 100644 --- a/ceilometer/compute/virt/inspector.py +++ b/ceilometer/compute/virt/inspector.py @@ -63,6 +63,14 @@ CPUUtilStats = collections.namedtuple('CPUUtilStats', ['util']) MemoryUsageStats = collections.namedtuple('MemoryUsageStats', ['usage']) +# Named tuple representing Resident Memory usage statistics. +# +# resident: Amount of resident memory +# +MemoryResidentStats = collections.namedtuple('MemoryResidentStats', + ['resident']) + + # Named tuple representing vNICs. # # name: the name of the vNIC diff --git a/ceilometer/compute/virt/libvirt/inspector.py b/ceilometer/compute/virt/libvirt/inspector.py index 096118dde..8dcb86c65 100644 --- a/ceilometer/compute/virt/libvirt/inspector.py +++ b/ceilometer/compute/virt/libvirt/inspector.py @@ -212,3 +212,8 @@ class LibvirtInspector(virt_inspector.Inspector): physical=block_info[2]) yield (disk, info) + + def inspect_memory_resident(self, instance, duration=None): + domain = self._get_domain_not_shut_off_or_raise(instance) + memory = domain.memoryStats()['rss'] / units.Ki + return virt_inspector.MemoryResidentStats(resident=memory) diff --git a/ceilometer/tests/compute/pollsters/test_memory.py b/ceilometer/tests/compute/pollsters/test_memory.py index 545e457b0..d50920639 100644 --- a/ceilometer/tests/compute/pollsters/test_memory.py +++ b/ceilometer/tests/compute/pollsters/test_memory.py @@ -64,3 +64,51 @@ class TestMemoryPollster(base.TestPollsterBase): _verify_memory_metering(1, 2.0) _verify_memory_metering(0, 0) _verify_memory_metering(0, 0) + + +class TestResidentMemoryPollster(base.TestPollsterBase): + + def setUp(self): + super(TestResidentMemoryPollster, self).setUp() + + @mock.patch('ceilometer.pipeline.setup_pipeline', mock.MagicMock()) + def test_get_samples(self): + next_value = iter(( + virt_inspector.MemoryResidentStats(resident=1.0), + virt_inspector.MemoryResidentStats(resident=2.0), + virt_inspector.NoDataException(), + virt_inspector.InstanceShutOffException(), + )) + + def inspect_memory_resident(instance, duration): + value = next(next_value) + if isinstance(value, virt_inspector.MemoryResidentStats): + return value + else: + raise value + + self.inspector.inspect_memory_resident = mock.Mock( + side_effect=inspect_memory_resident) + + mgr = manager.AgentManager() + pollster = memory.MemoryResidentPollster() + + @mock.patch('ceilometer.compute.pollsters.memory.LOG') + def _verify_resident_memory_metering(expected_count, + expected_resident_memory_mb, + mylog): + samples = list(pollster.get_samples(mgr, {}, [self.instance])) + self.assertEqual(expected_count, len(samples)) + if expected_count > 0: + self.assertEqual(set(['memory.resident']), + set([s.name for s in samples])) + self.assertEqual(expected_resident_memory_mb, + samples[0].volume) + else: + self.assertEqual(1, mylog.warn.call_count) + self.assertEqual(0, mylog.exception.call_count) + + _verify_resident_memory_metering(1, 1.0) + _verify_resident_memory_metering(1, 2.0) + _verify_resident_memory_metering(0, 0) + _verify_resident_memory_metering(0, 0) diff --git a/setup.cfg b/setup.cfg index fa1dd7832..215da0913 100644 --- a/setup.cfg +++ b/setup.cfg @@ -142,6 +142,7 @@ ceilometer.poll.compute = instance = ceilometer.compute.pollsters.instance:InstancePollster instance_flavor = ceilometer.compute.pollsters.instance:InstanceFlavorPollster memory.usage = ceilometer.compute.pollsters.memory:MemoryUsagePollster + memory.resident = ceilometer.compute.pollsters.memory:MemoryResidentPollster disk.capacity = ceilometer.compute.pollsters.disk:CapacityPollster disk.allocation = ceilometer.compute.pollsters.disk:AllocationPollster disk.usage = ceilometer.compute.pollsters.disk:PhysicalPollster