diff --git a/ceilometer/network/services/__init__.py b/ceilometer/network/services/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/ceilometer/network/services/lbaas.py b/ceilometer/network/services/lbaas.py new file mode 100644 index 000000000..96f1b688a --- /dev/null +++ b/ceilometer/network/services/lbaas.py @@ -0,0 +1,346 @@ +# +# Copyright 2014 Cisco Systems,Inc. +# +# Author: Pradeep Kilambi +# +# 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. + +import abc +import collections +import six + +from ceilometer import neutron_client +from ceilometer.openstack.common.gettextutils import _ +from ceilometer.openstack.common import log +from ceilometer.openstack.common import timeutils +from ceilometer import plugin +from ceilometer import sample + +LOG = log.getLogger(__name__) + +LBStatsData = collections.namedtuple( + 'LBStats', + ['active_connections', 'total_connections', 'bytes_in', 'bytes_out'] +) + +# status map for converting metric status to volume int +STATUS = { + 'inactive': 0, + 'active': 1, + 'pending_create': 2, +} + + +class _BasePollster(plugin.PollsterBase): + + FIELDS = [] + nc = neutron_client.Client() + + def _iter_cache(self, cache, meter_name, method): + if meter_name not in cache: + cache[meter_name] = list(method()) + return iter(cache[meter_name]) + + def extract_metadata(self, metric): + return dict((k, metric[k]) for k in self.FIELDS) + + @staticmethod + def get_status_id(value): + status = value.lower() + if status not in STATUS: + return -1 + return STATUS[status] + + +class LBPoolPollster(_BasePollster): + """Pollster to capture Load Balancer pool status samples. + """ + FIELDS = ['admin_state_up', + 'description', + 'lb_method', + 'name', + 'protocol', + 'provider', + 'status', + 'status_description', + 'subnet_id', + 'vip_id' + ] + + def _get_lb_pools(self): + return self.nc.pool_get_all() + + def get_samples(self, manager, cache, resources=None): + for pool in self._iter_cache(cache, 'pool', self._get_lb_pools): + LOG.debug("Load Balancer Pool : %s" % pool) + status = self.get_status_id(pool['status']) + if status == -1: + # unknown status, skip this sample + LOG.warn("Unknown status %s received on pool %s, " + "skipping sample" % (pool['status'], pool['id'])) + continue + + yield sample.Sample( + name='network.services.lb.pool', + type=sample.TYPE_GAUGE, + unit='pool', + volume=status, + user_id=None, + project_id=pool['tenant_id'], + resource_id=pool['id'], + timestamp=timeutils.utcnow().isoformat(), + resource_metadata=self.extract_metadata(pool) + ) + + +class LBVipPollster(_BasePollster): + """Pollster to capture Load Balancer Vip status samples. + """ + FIELDS = ['admin_state_up', + 'address', + 'connection_limit', + 'description', + 'name', + 'pool_id', + 'port_id', + 'protocol', + 'protocol_port', + 'status', + 'status_description', + 'subnet_id', + 'session_persistence', + ] + + def _get_lb_vips(self): + return self.nc.vip_get_all() + + def get_samples(self, manager, cache, resources=None): + for vip in self._iter_cache(cache, 'vip', self._get_lb_vips): + LOG.debug("Load Balancer Vip : %s" % vip) + status = self.get_status_id(vip['status']) + if status == -1: + # unknown status, skip this sample + LOG.warn("Unknown status %s received on vip %s, " + "skipping sample" % (vip['status'], vip['id'])) + continue + + yield sample.Sample( + name='network.services.lb.vip', + type=sample.TYPE_GAUGE, + unit='vip', + volume=status, + user_id=None, + project_id=vip['tenant_id'], + resource_id=vip['id'], + timestamp=timeutils.utcnow().isoformat(), + resource_metadata=self.extract_metadata(vip) + ) + + +class LBMemberPollster(_BasePollster): + """Pollster to capture Load Balancer Member status samples. + """ + FIELDS = ['admin_state_up', + 'address', + 'pool_id', + 'protocol_port', + 'status', + 'status_description', + 'weight', + ] + + def _get_lb_members(self): + return self.nc.member_get_all() + + def get_samples(self, manager, cache, resources=None): + for member in self._iter_cache(cache, 'member', self._get_lb_members): + LOG.debug("Load Balancer Member : %s" % member) + status = self.get_status_id(member['status']) + if status == -1: + LOG.warn("Unknown status %s received on member %s, " + "skipping sample" % (member['status'], member['id'])) + continue + yield sample.Sample( + name='network.services.lb.member', + type=sample.TYPE_GAUGE, + unit='member', + volume=status, + user_id=None, + project_id=member['tenant_id'], + resource_id=member['id'], + timestamp=timeutils.utcnow().isoformat(), + resource_metadata=self.extract_metadata(member) + ) + + +class LBHealthMonitorPollster(_BasePollster): + """Pollster to capture Load Balancer Health probes status samples. + """ + FIELDS = ['admin_state_up', + 'delay', + 'max_retries', + 'pools', + 'timeout', + 'type' + ] + + def _get_lb_health_probes(self): + return self.nc.health_monitor_get_all() + + def get_samples(self, manager, cache, resources=None): + for probe in self._iter_cache(cache, 'monitor', + self._get_lb_health_probes): + LOG.debug("Load Balancer Health probe : %s" % probe) + yield sample.Sample( + name='network.services.lb.health_monitor', + type=sample.TYPE_GAUGE, + unit='monitor', + volume=1, + user_id=None, + project_id=probe['tenant_id'], + resource_id=probe['id'], + timestamp=timeutils.utcnow().isoformat(), + resource_metadata=self.extract_metadata(probe) + ) + + +@six.add_metaclass(abc.ABCMeta) +class _LBStatsPollster(_BasePollster): + """Base Statistics pollster capturing the statistics info + and yielding samples for connections and bandwidth. + """ + + def _get_lb_pools(self): + return self.nc.pool_get_all() + + def _get_pool_stats(self, pool_id): + return self.nc.pool_stats(pool_id) + + @staticmethod + def make_sample_from_pool(pool, name, type, unit, volume, + resource_metadata=None): + if not resource_metadata: + resource_metadata = {} + return sample.Sample( + name=name, + type=type, + unit=unit, + volume=volume, + user_id=None, + project_id=pool['tenant_id'], + resource_id=pool['id'], + timestamp=timeutils.isotime(), + resource_metadata=resource_metadata, + ) + + def _populate_stats_cache(self, pool_id, cache): + i_cache = cache.setdefault("lbstats", {}) + if pool_id not in i_cache: + stats = self._get_pool_stats(pool_id)['stats'] + i_cache[pool_id] = LBStatsData( + active_connections=stats['active_connections'], + total_connections=stats['total_connections'], + bytes_in=stats['bytes_in'], + bytes_out=stats['bytes_out'], + ) + return i_cache[pool_id] + + @abc.abstractmethod + def _get_sample(pool, c_data): + """Return one Sample.""" + + def get_samples(self, manager, cache, resources=None): + for pool in self._get_lb_pools(): + try: + c_data = self._populate_stats_cache(pool['id'], cache) + yield self._get_sample(pool, c_data) + except Exception as err: + LOG.exception(_('Ignoring pool %(pool_id)s: %(error)s'), + {'pool_id': pool['id'], 'error': err}) + + +class LBActiveConnectionsPollster(_LBStatsPollster): + """Pollster to capture Active Load Balancer connections. + """ + + @staticmethod + def _get_sample(pool, data): + return make_sample_from_pool( + pool, + name='network.services.lb.active.connections', + type=sample.TYPE_GAUGE, + unit='connection', + volume=data.active_connections, + ) + + +class LBTotalConnectionsPollster(_LBStatsPollster): + """Pollster to capture Total Load Balancer connections + """ + + @staticmethod + def _get_sample(pool, data): + return make_sample_from_pool( + pool, + name='network.services.lb.total.connections', + type=sample.TYPE_GAUGE, + unit='connection', + volume=data.total_connections, + ) + + +class LBBytesInPollster(_LBStatsPollster): + """Pollster to capture incoming bytes. + """ + + @staticmethod + def _get_sample(pool, data): + return make_sample_from_pool( + pool, + name='network.services.lb.incoming.bytes', + type=sample.TYPE_GAUGE, + unit='B', + volume=data.bytes_in, + ) + + +class LBBytesOutPollster(_LBStatsPollster): + """Pollster to capture outgoing bytes. + """ + + @staticmethod + def _get_sample(pool, data): + return make_sample_from_pool( + pool, + name='network.services.lb.outgoing.bytes', + type=sample.TYPE_GAUGE, + unit='B', + volume=data.bytes_out, + ) + + +def make_sample_from_pool(pool, name, type, unit, volume, + resource_metadata=None): + resource_metadata = resource_metadata or {} + + return sample.Sample( + name=name, + type=type, + unit=unit, + volume=volume, + user_id=None, + project_id=pool['tenant_id'], + resource_id=pool['id'], + timestamp=timeutils.isotime(), + resource_metadata=resource_metadata, + ) diff --git a/ceilometer/neutron_client.py b/ceilometer/neutron_client.py index 993df58a9..a58447b09 100644 --- a/ceilometer/neutron_client.py +++ b/ceilometer/neutron_client.py @@ -71,3 +71,27 @@ class Client(object): def port_get_all(self): resp = self.client.list_ports() return resp.get('ports') + + @logged + def vip_get_all(self): + resp = self.client.list_vips() + return resp.get('vips') + + @logged + def pool_get_all(self): + resp = self.client.list_pools() + return resp.get('pools') + + @logged + def member_get_all(self): + resp = self.client.list_members() + return resp.get('members') + + @logged + def health_monitor_get_all(self): + resp = self.client.list_health_monitors() + return resp.get('health_monitors') + + @logged + def pool_stats(self, pool): + return self.client.retrieve_pool_stats(pool) diff --git a/ceilometer/tests/network/services/__init__.py b/ceilometer/tests/network/services/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/ceilometer/tests/network/services/test_lbaas.py b/ceilometer/tests/network/services/test_lbaas.py new file mode 100644 index 000000000..c0e0be990 --- /dev/null +++ b/ceilometer/tests/network/services/test_lbaas.py @@ -0,0 +1,399 @@ +# +# Copyright 2014 Cisco Systems,Inc. +# +# Author: Pradeep Kilambi +# +# 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. + +import mock + +from ceilometer.central import manager +from ceilometer.network.services import lbaas +from ceilometer.openstack.common import context +from ceilometer.openstack.common.fixture import mockpatch +from ceilometer.openstack.common import test + + +class _BaseTestLBPollster(test.BaseTestCase): + + @mock.patch('ceilometer.pipeline.setup_pipeline', mock.MagicMock()) + def setUp(self): + super(_BaseTestLBPollster, self).setUp() + self.addCleanup(mock.patch.stopall) + self.context = context.get_admin_context() + self.manager = manager.AgentManager() + + +class TestLBPoolPollster(_BaseTestLBPollster): + + def setUp(self): + super(TestLBPoolPollster, self).setUp() + self.pollster = lbaas.LBPoolPollster() + fake_pools = self.fake_get_pools() + self.useFixture(mockpatch.Patch('ceilometer.neutron_client.Client.' + 'pool_get_all', + return_value=fake_pools)) + + @staticmethod + def fake_get_pools(): + return [{'status': 'ACTIVE', + 'lb_method': 'ROUND_ROBIN', + 'protocol': 'HTTP', + 'description': '', + 'health_monitors': [], + 'members': [], + 'provider': 'haproxy', + 'status_description': None, + 'id': 'ce73ad36-437d-4c84-aee1-186027d3da9a', + 'vip_id': 'cd6a6fee-e2fa-4e6c-b3c2-bfbe395752c1', + 'name': 'mylb', + 'admin_state_up': True, + 'subnet_id': 'bbe3d818-bdcb-4e4b-b47f-5650dc8a9d7a', + 'tenant_id': 'a4eb9f4938bb418bbc4f8eb31802fefa', + 'health_monitors_status': []}, + {'status': 'INACTIVE', + 'lb_method': 'ROUND_ROBIN', + 'protocol': 'HTTP', + 'description': '', + 'health_monitors': [], + 'members': [], + 'provider': 'haproxy', + 'status_description': None, + 'id': 'ce73ad36-437d-4c84-aee1-186027d3da9a', + 'vip_id': 'cd6a6fee-e2fa-4e6c-b3c2-bfbe395752c1', + 'name': 'mylb02', + 'admin_state_up': True, + 'subnet_id': 'bbe3d818-bdcb-4e4b-b47f-5650dc8a9d7a', + 'tenant_id': 'a4eb9f4938bb418bbc4f8eb31802fefa', + 'health_monitors_status': []}, + {'status': 'PENDING_CREATE', + 'lb_method': 'ROUND_ROBIN', + 'protocol': 'HTTP', + 'description': '', + 'health_monitors': [], + 'members': [], + 'provider': 'haproxy', + 'status_description': None, + 'id': 'fe7rad36-437d-4c84-aee1-186027d3bdcd', + 'vip_id': 'cd6a6fee-e2fa-4e6c-b3c2-bfbe395752c1', + 'name': 'mylb03', + 'admin_state_up': True, + 'subnet_id': 'bbe3d818-bdcb-4e4b-b47f-5650dc8a9d7a', + 'tenant_id': 'a4eb9f4938bb418bbc4f8eb31802fefa', + 'health_monitors_status': []}, + {'status': 'UNKNOWN', + 'lb_method': 'ROUND_ROBIN', + 'protocol': 'HTTP', + 'description': '', + 'health_monitors': [], + 'members': [], + 'provider': 'haproxy', + 'status_description': None, + 'id': 'fe7rad36-437d-4c84-aee1-186027d3bdcd', + 'vip_id': 'cd6a6fee-e2fa-4e6c-b3c2-bfbe395752c1', + 'name': 'mylb03', + 'admin_state_up': True, + 'subnet_id': 'bbe3d818-bdcb-4e4b-b47f-5650dc8a9d7a', + 'tenant_id': 'a4eb9f4938bb418bbc4f8eb31802fefa', + 'health_monitors_status': []}, + ] + + def test_pool_get_samples(self): + samples = list(self.pollster.get_samples(self.manager, {})) + self.assertEqual(3, len(samples)) + for field in self.pollster.FIELDS: + self.assertEqual(self.fake_get_pools()[0][field], + samples[0].resource_metadata[field]) + + def test_pool_volume(self): + samples = list(self.pollster.get_samples(self.manager, {})) + self.assertEqual(1, samples[0].volume) + self.assertEqual(0, samples[1].volume) + self.assertEqual(2, samples[2].volume) + + def test_get_pool_meter_names(self): + samples = list(self.pollster.get_samples(self.manager, {})) + self.assertEqual(set(['network.services.lb.pool']), + set([s.name for s in samples])) + + +class TestLBVipPollster(_BaseTestLBPollster): + + def setUp(self): + super(TestLBVipPollster, self).setUp() + self.pollster = lbaas.LBVipPollster() + fake_vips = self.fake_get_vips() + self.useFixture(mockpatch.Patch('ceilometer.neutron_client.Client.' + 'vip_get_all', + return_value=fake_vips)) + + @staticmethod + def fake_get_vips(): + return [{'status': 'ACTIVE', + 'status_description': None, + 'protocol': 'HTTP', + 'description': '', + 'admin_state_up': True, + 'subnet_id': 'bbe3d818-bdcb-4e4b-b47f-5650dc8a9d7a', + 'tenant_id': 'a4eb9f4938bb418bbc4f8eb31802fefa', + 'connection_limit': -1, + 'pool_id': 'ce73ad36-437d-4c84-aee1-186027d3da9a', + 'session_persistence': None, + 'address': '10.0.0.2', + 'protocol_port': 80, + 'port_id': '3df3c4de-b32e-4ca1-a7f4-84323ba5f291', + 'id': 'cd6a6fee-e2fa-4e6c-b3c2-bfbe395752c1', + 'name': 'myvip'}, + {'status': 'INACTIVE', + 'status_description': None, + 'protocol': 'HTTP', + 'description': '', + 'admin_state_up': True, + 'subnet_id': 'bbe3d818-bdcb-4e4b-b47f-5650dc8a9d7a', + 'tenant_id': 'a4eb9f4938bb418bbc4f8eb31802fefa', + 'connection_limit': -1, + 'pool_id': 'ce73ad36-437d-4c84-aee1-186027d3da9a', + 'session_persistence': None, + 'address': '10.0.0.3', + 'protocol_port': 80, + 'port_id': '3df3c4de-b32e-4ca1-a7f4-84323ba5f291', + 'id': 'ba6a6fee-e2fa-4e6c-b3c2-bfbe395752c1', + 'name': 'myvip02'}, + {'status': 'PENDING_CREATE', + 'status_description': None, + 'protocol': 'HTTP', + 'description': '', + 'admin_state_up': True, + 'subnet_id': 'bbe3d818-bdcb-4e4b-b47f-5650dc8a9d7a', + 'tenant_id': 'a4eb9f4938bb418bbc4f8eb31802fefa', + 'connection_limit': -1, + 'pool_id': 'ce73ad36-437d-4c84-aee1-186027d3da9a', + 'session_persistence': None, + 'address': '10.0.0.4', + 'protocol_port': 80, + 'port_id': '3df3c4de-b32e-4ca1-a7f4-84323ba5f291', + 'id': 'fg6a6fee-e2fa-4e6c-b3c2-bfbe395752c1', + 'name': 'myvip03'}, + {'status': 'UNKNOWN', + 'status_description': None, + 'protocol': 'HTTP', + 'description': '', + 'admin_state_up': True, + 'subnet_id': 'bbe3d818-bdcb-4e4b-b47f-5650dc8a9d7a', + 'tenant_id': 'a4eb9f4938bb418bbc4f8eb31802fefa', + 'connection_limit': -1, + 'pool_id': 'ce73ad36-437d-4c84-aee1-186027d3da9a', + 'session_persistence': None, + 'address': '10.0.0.8', + 'protocol_port': 80, + 'port_id': '3df3c4de-b32e-4ca1-a7f4-84323ba5f291', + 'id': 'fg6a6fee-e2fa-4e6c-b3c2-bfbe395752c1', + 'name': 'myvip03'}, + ] + + def test_vip_get_samples(self): + samples = list(self.pollster.get_samples(self.manager, {})) + self.assertEqual(3, len(samples)) + for field in self.pollster.FIELDS: + self.assertEqual(self.fake_get_vips()[0][field], + samples[0].resource_metadata[field]) + + def test_pool_volume(self): + samples = list(self.pollster.get_samples(self.manager, {})) + self.assertEqual(1, samples[0].volume) + self.assertEqual(0, samples[1].volume) + self.assertEqual(2, samples[2].volume) + + def test_get_vip_meter_names(self): + samples = list(self.pollster.get_samples(self.manager, {})) + self.assertEqual(set(['network.services.lb.vip']), + set([s.name for s in samples])) + + +class TestLBMemberPollster(_BaseTestLBPollster): + + def setUp(self): + super(TestLBMemberPollster, self).setUp() + self.pollster = lbaas.LBMemberPollster() + fake_members = self.fake_get_members() + self.useFixture(mockpatch.Patch('ceilometer.neutron_client.Client.' + 'member_get_all', + return_value=fake_members)) + + @staticmethod + def fake_get_members(): + return [{'status': 'ACTIVE', + 'protocol_port': 80, + 'weight': 1, + 'admin_state_up': True, + 'tenant_id': 'a4eb9f4938bb418bbc4f8eb31802fefa', + 'pool_id': 'ce73ad36-437d-4c84-aee1-186027d3da9a', + 'address': '10.0.0.3', + 'status_description': None, + 'id': '290b61eb-07bc-4372-9fbf-36459dd0f96b'}, + {'status': 'INACTIVE', + 'protocol_port': 80, + 'weight': 1, + 'admin_state_up': True, + 'tenant_id': 'a4eb9f4938bb418bbc4f8eb31802fefa', + 'pool_id': 'ce73ad36-437d-4c84-aee1-186027d3da9a', + 'address': '10.0.0.5', + 'status_description': None, + 'id': '2456661eb-07bc-4372-9fbf-36459dd0f96b'}, + {'status': 'PENDING_CREATE', + 'protocol_port': 80, + 'weight': 1, + 'admin_state_up': True, + 'tenant_id': 'a4eb9f4938bb418bbc4f8eb31802fefa', + 'pool_id': 'ce73ad36-437d-4c84-aee1-186027d3da9a', + 'address': '10.0.0.6', + 'status_description': None, + 'id': '45630b61eb-07bc-4372-9fbf-36459dd0f96b'}, + {'status': 'UNKNOWN', + 'protocol_port': 80, + 'weight': 1, + 'admin_state_up': True, + 'tenant_id': 'a4eb9f4938bb418bbc4f8eb31802fefa', + 'pool_id': 'ce73ad36-437d-4c84-aee1-186027d3da9a', + 'address': '10.0.0.6', + 'status_description': None, + 'id': '45630b61eb-07bc-4372-9fbf-36459dd0f96b'}, + ] + + def test_get_samples_not_empty(self): + samples = list(self.pollster.get_samples(self.manager, {})) + self.assertEqual(3, len(samples)) + for field in self.pollster.FIELDS: + self.assertEqual(self.fake_get_members()[0][field], + samples[0].resource_metadata[field]) + + def test_pool_volume(self): + samples = list(self.pollster.get_samples(self.manager, {})) + self.assertEqual(1, samples[0].volume) + self.assertEqual(0, samples[1].volume) + self.assertEqual(2, samples[2].volume) + + def test_get_meter_names(self): + samples = list(self.pollster.get_samples(self.manager, {})) + self.assertEqual(set(['network.services.lb.member']), + set([s.name for s in samples])) + + +class TestLBHealthProbePollster(_BaseTestLBPollster): + + def setUp(self): + super(TestLBHealthProbePollster, self).setUp() + self.pollster = lbaas.LBHealthMonitorPollster() + fake_health_monitor = self.fake_get_health_monitor() + self.useFixture(mockpatch.Patch('ceilometer.neutron_client.Client.' + 'health_monitor_get_all', + return_value=fake_health_monitor)) + + @staticmethod + def fake_get_health_monitor(): + return [{'id': '34ae33e1-0035-49e2-a2ca-77d5d3fab365', + 'admin_state_up': True, + 'tenant_id': "d5d2817dae6b42159be9b665b64beb0e", + 'delay': 2, + 'max_retries': 5, + 'timeout': 5, + 'pools': [], + 'type': 'PING', + }] + + def test_get_samples_not_empty(self): + samples = list(self.pollster.get_samples(self.manager, {})) + self.assertEqual(1, len(samples)) + for field in self.pollster.FIELDS: + self.assertEqual(self.fake_get_health_monitor()[0][field], + samples[0].resource_metadata[field]) + + def test_get_meter_names(self): + samples = list(self.pollster.get_samples(self.manager, {})) + self.assertEqual(set(['network.services.lb.health_monitor']), + set([s.name for s in samples])) + + +class TestLBStatsPollster(_BaseTestLBPollster): + + def setUp(self): + super(TestLBStatsPollster, self).setUp() + fake_pool_stats = self.fake_pool_stats() + self.useFixture(mockpatch.Patch('ceilometer.neutron_client.Client.' + 'pool_stats', + return_value=fake_pool_stats)) + + fake_pools = self.fake_get_pools() + self.useFixture(mockpatch.Patch('ceilometer.neutron_client.Client.' + 'pool_get_all', + return_value=fake_pools)) + + @staticmethod + def fake_get_pools(): + return [{'status': 'ACTIVE', + 'lb_method': 'ROUND_ROBIN', + 'protocol': 'HTTP', + 'description': '', + 'health_monitors': [], + 'members': [], + 'provider': 'haproxy', + 'status_description': None, + 'id': 'ce73ad36-437d-4c84-aee1-186027d3da9a', + 'vip_id': 'cd6a6fee-e2fa-4e6c-b3c2-bfbe395752c1', + 'name': 'mylb', + 'admin_state_up': True, + 'subnet_id': 'bbe3d818-bdcb-4e4b-b47f-5650dc8a9d7a', + 'tenant_id': 'a4eb9f4938bb418bbc4f8eb31802fefa', + 'health_monitors_status': []}, + ] + + @staticmethod + def fake_pool_stats(): + return {'stats': {'active_connections': 2L, + 'bytes_in': 1L, + 'bytes_out': 3L, + 'total_connections': 4L + } + } + + @mock.patch('ceilometer.pipeline.setup_pipeline', mock.MagicMock()) + def _check_get_samples(self, factory, sample_name, expected_volume): + pollster = factory() + + cache = {} + samples = list(pollster.get_samples(self.manager, cache)) + self.assertEqual(1, len(samples)) + self.assertIsNotNone(samples) + self.assertIn('lbstats', cache) + self.assertEqual(set([sample_name]), set([s.name for s in samples])) + + match = [s for s in samples if s.name == sample_name] + self.assertEqual(1, len(match), 'missing counter %s' % sample_name) + self.assertEqual(expected_volume, match[0].volume) + self.assertEqual('gauge', match[0].type) + + def test_lb_total_connections(self): + self._check_get_samples(lbaas.LBTotalConnectionsPollster, + 'network.services.lb.total.connections', 4L) + + def test_lb_active_connections(self): + self._check_get_samples(lbaas.LBActiveConnectionsPollster, + 'network.services.lb.active.connections', 2L) + + def test_lb_incoming_bytes(self): + self._check_get_samples(lbaas.LBBytesInPollster, + 'network.services.lb.incoming.bytes', 1L) + + def test_lb_outgoing_bytes(self): + self._check_get_samples(lbaas.LBBytesOutPollster, + 'network.services.lb.outgoing.bytes', 3L) diff --git a/ceilometer/tests/test_neutronclient.py b/ceilometer/tests/test_neutronclient.py index 17d39b65e..532b3577a 100644 --- a/ceilometer/tests/test_neutronclient.py +++ b/ceilometer/tests/test_neutronclient.py @@ -38,7 +38,8 @@ class TestNeutronClient(test.BaseTestCase): 'name': '', 'network_id': '298a3088-a446-4d5a-bad8-f92ecacd786b', 'status': 'ACTIVE', - 'tenant_id': '89271fa581ab4380bf172f868c3615f9'}]} + 'tenant_id': '89271fa581ab4380bf172f868c3615f9'}, + ]} def test_port_get_all(self): with patch.object(self.nc.client, 'list_ports', @@ -62,7 +63,8 @@ class TestNeutronClient(test.BaseTestCase): 'shared': False, 'status': 'ACTIVE', 'subnets': [u'c4b6f5b8-3508-4896-b238-a441f25fb492'], - 'tenant_id': '62d6f08bbd3a44f6ad6f00ca15cce4e5'}]} + 'tenant_id': '62d6f08bbd3a44f6ad6f00ca15cce4e5'}, + ]} def test_network_get_all(self): with patch.object(self.nc.client, 'list_networks', @@ -72,3 +74,122 @@ class TestNeutronClient(test.BaseTestCase): self.assertEqual(1, len(networks)) self.assertEqual('298a3088-a446-4d5a-bad8-f92ecacd786b', networks[0]['id']) + + @staticmethod + def fake_pool_list(): + return {'pools': [{'status': 'ACTIVE', + 'lb_method': 'ROUND_ROBIN', + 'protocol': 'HTTP', + 'description': '', + 'health_monitors': [], + 'members': [], + 'status_description': None, + 'id': 'ce73ad36-437d-4c84-aee1-186027d3da9a', + 'vip_id': 'cd6a6fee-e2fa-4e6c-b3c2-bfbe395752c1', + 'name': 'mylb', + 'admin_state_up': True, + 'subnet_id': 'bbe3d818-bdcb-4e4b-b47f-5650dc8a9d7a', + 'tenant_id': 'a4eb9f4938bb418bbc4f8eb31802fefa', + 'health_monitors_status': []}, + ]} + + def test_pool_list(self): + with patch.object(self.nc.client, 'list_pools', + side_effect=self.fake_pool_list): + pools = self.nc.pool_get_all() + + self.assertEqual(1, len(pools)) + self.assertEqual('ce73ad36-437d-4c84-aee1-186027d3da9a', + pools[0]['id']) + + @staticmethod + def fake_vip_list(): + return {'vips': [{'status': 'ACTIVE', + 'status_description': None, + 'protocol': 'HTTP', + 'description': '', + 'admin_state_up': True, + 'subnet_id': 'bbe3d818-bdcb-4e4b-b47f-5650dc8a9d7a', + 'tenant_id': 'a4eb9f4938bb418bbc4f8eb31802fefa', + 'connection_limit': -1, + 'pool_id': 'ce73ad36-437d-4c84-aee1-186027d3da9a', + 'session_persistence': None, + 'address': '10.0.0.2', + 'protocol_port': 80, + 'port_id': '3df3c4de-b32e-4ca1-a7f4-84323ba5f291', + 'id': 'cd6a6fee-e2fa-4e6c-b3c2-bfbe395752c1', + 'name': 'myvip'}, + ]} + + def test_vip_list(self): + with patch.object(self.nc.client, 'list_vips', + side_effect=self.fake_vip_list): + vips = self.nc.vip_get_all() + + self.assertEqual(1, len(vips)) + self.assertEqual('cd6a6fee-e2fa-4e6c-b3c2-bfbe395752c1', + vips[0]['id']) + + @staticmethod + def fake_member_list(): + return {'members': [{'status': 'ACTIVE', + 'protocol_port': 80, + 'weight': 1, + 'admin_state_up': True, + 'tenant_id': 'a4eb9f4938bb418bbc4f8eb31802fefa', + 'pool_id': 'ce73ad36-437d-4c84-aee1-186027d3da9a', + 'address': '10.0.0.3', + 'status_description': None, + 'id': '290b61eb-07bc-4372-9fbf-36459dd0f96b'}, + ]} + + def test_member_list(self): + with patch.object(self.nc.client, 'list_members', + side_effect=self.fake_member_list): + members = self.nc.member_get_all() + + self.assertEqual(1, len(members)) + self.assertEqual('290b61eb-07bc-4372-9fbf-36459dd0f96b', + members[0]['id']) + + @staticmethod + def fake_monitors_list(): + return {'health_monitors': + [{'id': '34ae33e1-0035-49e2-a2ca-77d5d3fab365', + 'admin_state_up': True, + 'tenant_id': "d5d2817dae6b42159be9b665b64beb0e", + 'delay': 2, + 'max_retries': 5, + 'timeout': 5, + 'pools': [], + 'type': 'PING', + }]} + + def test_monitor_list(self): + with patch.object(self.nc.client, 'list_health_monitors', + side_effect=self.fake_monitors_list): + monitors = self.nc.health_monitor_get_all() + + self.assertEqual(1, len(monitors)) + self.assertEqual('34ae33e1-0035-49e2-a2ca-77d5d3fab365', + monitors[0]['id']) + + @staticmethod + def fake_pool_stats(fake_pool): + return {'stats': + [{'active_connections': 1L, + 'total_connections': 2L, + 'bytes_in': 3L, + 'bytes_out': 4L + }]} + + def test_pool_stats(self): + with patch.object(self.nc.client, 'retrieve_pool_stats', + side_effect=self.fake_pool_stats): + stats = self.nc.pool_stats('fake_pool')['stats'] + + self.assertEqual(1, len(stats)) + self.assertEqual(1L, stats[0]['active_connections']) + self.assertEqual(2L, stats[0]['total_connections']) + self.assertEqual(3L, stats[0]['bytes_in']) + self.assertEqual(4L, stats[0]['bytes_out']) diff --git a/setup.cfg b/setup.cfg index 8d3798615..2043c98b5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -135,7 +135,14 @@ ceilometer.poll.central = hardware.network.outgoing.errors = ceilometer.hardware.pollsters.net:OutgoingErrorsPollster hardware.memory.total = ceilometer.hardware.pollsters.memory:MemoryTotalPollster hardware.memory.used = ceilometer.hardware.pollsters.memory:MemoryUsedPollster - + network.services.lb.pool = ceilometer.network.services.lbaas:LBPoolPollster + network.services.lb.vip = ceilometer.network.services.lbaas:LBVipPollster + network.services.lb.member = ceilometer.network.services.lbaas:LBMemberPollster + network.services.lb.health_monitor = ceilometer.network.services.lbaas:LBHealthMonitorPollster + network.services.lb.total.connections = ceilometer.network.services.lbaas:LBTotalConnectionsPollster + network.services.lb.active.connections = ceilometer.network.services.lbaas:LBActiveConnectionsPollster + network.services.lb.incoming.bytes = ceilometer.network.services.lbaas:LBBytesInPollster + network.services.lb.outgoing.bytes = ceilometer.network.services.lbaas:LBBytesOutPollster ceilometer.storage = log = ceilometer.storage.impl_log:Connection