diff --git a/ceilometer/compute/notifications/instance.py b/ceilometer/compute/notifications/instance.py index 7ee1fd4d0..b54d51fa3 100644 --- a/ceilometer/compute/notifications/instance.py +++ b/ceilometer/compute/notifications/instance.py @@ -141,6 +141,22 @@ class EphemeralDiskSize(ComputeInstanceNotificationBase): message=message) +class InstanceFlavor(ComputeInstanceNotificationBase, + plugin_base.NonMetricNotificationBase): + def get_sample(self, message): + instance_type = message.get('payload', {}).get('instance_type') + if instance_type: + yield sample.Sample.from_notification( + name='instance:%s' % instance_type, + type=sample.TYPE_GAUGE, + unit='instance', + volume=1, + user_id=message['payload']['user_id'], + project_id=message['payload']['tenant_id'], + resource_id=message['payload']['instance_id'], + message=message) + + class InstanceDelete(ComputeInstanceNotificationBase): """Handle the messages sent by the nova notifier plugin. diff --git a/ceilometer/compute/pollsters/instance.py b/ceilometer/compute/pollsters/instance.py index cc9e7eb72..12b7d948b 100644 --- a/ceilometer/compute/pollsters/instance.py +++ b/ceilometer/compute/pollsters/instance.py @@ -31,3 +31,19 @@ class InstancePollster(pollsters.BaseComputePollster): unit='instance', volume=1, ) + + +class InstanceFlavorPollster(pollsters.BaseComputePollster): + + @staticmethod + def get_samples(manager, cache, resources): + for instance in resources: + yield util.make_sample_from_instance( + instance, + # Use the "meter name + variable" syntax + name='instance:%s' % + instance.flavor['name'], + type=sample.TYPE_GAUGE, + unit='instance', + volume=1, + ) diff --git a/ceilometer/pipeline.py b/ceilometer/pipeline.py index 95c99f1e7..c6200acb7 100644 --- a/ceilometer/pipeline.py +++ b/ceilometer/pipeline.py @@ -248,7 +248,19 @@ class SampleSource(Source): raise PipelineException("Discovery should be a list", cfg) self.check_source_filtering(self.meters, 'meters') + # (yjiang5) To support meters like instance:m1.tiny, + # which include variable part at the end starting with ':'. + # Hope we will not add such meters in future. + @staticmethod + def _variable_meter_name(name): + m = name.partition(':') + if m[1] == ':': + return m[1].join((m[0], '*')) + else: + return name + def support_meter(self, meter_name): + meter_name = self._variable_meter_name(meter_name) return self.is_supported(self.meters, meter_name) diff --git a/ceilometer/tests/compute/notifications/test_instance.py b/ceilometer/tests/compute/notifications/test_instance.py index aa61162d8..4ca545058 100644 --- a/ceilometer/tests/compute/notifications/test_instance.py +++ b/ceilometer/tests/compute/notifications/test_instance.py @@ -631,6 +631,13 @@ class TestNotifications(base.BaseTestCase): c = counters[0] self.assertEqual(1, c.volume) + def test_instance_create_flavor(self): + ic = instance.InstanceFlavor(None) + counters = list(ic.process_notification(INSTANCE_CREATE_END)) + self.assertEqual(1, len(counters)) + c = counters[0] + self.assertEqual(1, c.volume) + def test_instance_create_memory(self): ic = instance.Memory(None) counters = list(ic.process_notification(INSTANCE_CREATE_END)) @@ -670,11 +677,21 @@ class TestNotifications(base.BaseTestCase): counters = list(ic.process_notification(INSTANCE_EXISTS_METADATA_LIST)) self.assertEqual(1, len(counters)) + def test_instance_exists_flavor(self): + ic = instance.Instance(None) + counters = list(ic.process_notification(INSTANCE_EXISTS)) + self.assertEqual(1, len(counters)) + def test_instance_delete_instance(self): ic = instance.Instance(None) counters = list(ic.process_notification(INSTANCE_DELETE_START)) self.assertEqual(1, len(counters)) + def test_instance_delete_flavor(self): + ic = instance.Instance(None) + counters = list(ic.process_notification(INSTANCE_DELETE_START)) + self.assertEqual(1, len(counters)) + def test_instance_finish_resize_instance(self): ic = instance.Instance(None) counters = list(ic.process_notification(INSTANCE_FINISH_RESIZE_END)) @@ -683,6 +700,15 @@ class TestNotifications(base.BaseTestCase): self.assertEqual(1, c.volume) self._verify_user_metadata(c.resource_metadata) + def test_instance_finish_resize_flavor(self): + ic = instance.InstanceFlavor(None) + counters = list(ic.process_notification(INSTANCE_FINISH_RESIZE_END)) + self.assertEqual(1, len(counters)) + c = counters[0] + self.assertEqual(1, c.volume) + self.assertEqual('instance:m1.small', c.name) + self._verify_user_metadata(c.resource_metadata) + def test_instance_finish_resize_memory(self): ic = instance.Memory(None) counters = list(ic.process_notification(INSTANCE_FINISH_RESIZE_END)) @@ -709,6 +735,15 @@ class TestNotifications(base.BaseTestCase): self.assertEqual(1, c.volume) self._verify_user_metadata(c.resource_metadata) + def test_instance_resize_finish_flavor(self): + ic = instance.InstanceFlavor(None) + counters = list(ic.process_notification(INSTANCE_RESIZE_REVERT_END)) + self.assertEqual(1, len(counters)) + c = counters[0] + self.assertEqual(1, c.volume) + self.assertEqual('instance:m1.tiny', c.name) + self._verify_user_metadata(c.resource_metadata) + def test_instance_resize_finish_memory(self): ic = instance.Memory(None) counters = list(ic.process_notification(INSTANCE_RESIZE_REVERT_END)) diff --git a/ceilometer/tests/compute/pollsters/test_instance.py b/ceilometer/tests/compute/pollsters/test_instance.py index 14c09adf5..eaa144d22 100644 --- a/ceilometer/tests/compute/pollsters/test_instance.py +++ b/ceilometer/tests/compute/pollsters/test_instance.py @@ -41,6 +41,14 @@ class TestInstancePollster(base.TestPollsterBase): self.assertEqual(0, samples[0].resource_metadata['ephemeral_gb']) self.assertEqual('active', samples[0].resource_metadata['status']) + @mock.patch('ceilometer.pipeline.setup_pipeline', mock.MagicMock()) + def test_get_samples_instance_flavor(self): + mgr = manager.AgentManager() + pollster = pollsters_instance.InstanceFlavorPollster() + samples = list(pollster.get_samples(mgr, {}, [self.instance])) + self.assertEqual(1, len(samples)) + self.assertEqual('instance:m1.small', samples[0].name) + @mock.patch('ceilometer.pipeline.setup_pipeline', mock.MagicMock()) def test_get_reserved_metadata_with_keys(self): self.CONF = self.useFixture(fixture_config.Config()).conf diff --git a/ceilometer/tests/pipeline_base.py b/ceilometer/tests/pipeline_base.py index 94b1ab6bf..fd7470175 100644 --- a/ceilometer/tests/pipeline_base.py +++ b/ceilometer/tests/pipeline_base.py @@ -751,6 +751,43 @@ class BasePipelineTestCase(base.BaseTestCase): self.assertEqual('a_update', getattr(publisher.samples[0], 'name')) + def test_variable_counter(self): + self.pipeline_cfg = [{ + 'name': "test_pipeline", + 'interval': 5, + 'counters': ['a:*'], + 'transformers': [ + {'name': "update", + 'parameters': {}} + ], + 'publishers': ["test://"], + }, ] + pipeline_manager = pipeline.PipelineManager(self.pipeline_cfg, + self.transformer_manager) + + self.test_counter = sample.Sample( + name='a:b', + type=self.test_counter.type, + volume=self.test_counter.volume, + unit=self.test_counter.unit, + user_id=self.test_counter.user_id, + project_id=self.test_counter.project_id, + resource_id=self.test_counter.resource_id, + timestamp=self.test_counter.timestamp, + resource_metadata=self.test_counter.resource_metadata, + ) + + with pipeline_manager.publisher(None) as p: + p([self.test_counter]) + + publisher = pipeline_manager.pipelines[0].publishers[0] + self.assertEqual(1, len(publisher.samples)) + self.assertEqual(1, len(self.TransformerClass.samples)) + self.assertEqual('a:b_update', + getattr(publisher.samples[0], "name")) + self.assertEqual('a:b', + getattr(self.TransformerClass.samples[0], "name")) + def test_global_unit_conversion(self): scale = 'volume / ((10**6) * 60)' transformer_cfg = [ diff --git a/setup.cfg b/setup.cfg index 16cce4a9a..01857f61d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -31,6 +31,7 @@ ceilometer.notification = magnetodb_table = ceilometer.key_value_storage.notifications:Table magnetodb_index_count = ceilometer.key_value_storage.notifications:Index instance = ceilometer.compute.notifications.instance:Instance + instance_flavor = ceilometer.compute.notifications.instance:InstanceFlavor instance_delete = ceilometer.compute.notifications.instance:InstanceDelete instance_scheduled = ceilometer.compute.notifications.instance:InstanceScheduled memory = ceilometer.compute.notifications.instance:Memory @@ -139,6 +140,7 @@ ceilometer.poll.compute = network.incoming.bytes.rate = ceilometer.compute.pollsters.net:IncomingBytesRatePollster network.outgoing.bytes.rate = ceilometer.compute.pollsters.net:OutgoingBytesRatePollster 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