Fix ceilometer floatingip pollster
The existing floatingip pollster talks to nova api to get the floatingip data. There are limitations in nova api wrt returning this info for all the tenants as stated in the bug#1402514. This patch changes the pollster to use the neutron api to get this data instead. Considering this is a network related pollster and in most cases network networking manages the floating ips now, it makes sense to get this data from neutron. Closes-Bug: #1536338 Change-Id: I372e3a85b34f90ff9aba842d9598b468f90fe94f
This commit is contained in:
parent
62928318f8
commit
1f9f4e1072
@ -1,6 +1,6 @@
|
||||
#
|
||||
# Copyright 2016 Sungard Availability Services
|
||||
# Copyright 2016 Red Hat
|
||||
# Copyright 2012 eNovance <licensing@enovance.com>
|
||||
#
|
||||
# Copyright 2013 IBM Corp
|
||||
# All Rights Reserved.
|
||||
#
|
||||
@ -21,53 +21,60 @@ from oslo_log import log
|
||||
from oslo_utils import timeutils
|
||||
|
||||
from ceilometer.agent import plugin_base
|
||||
from ceilometer.i18n import _LI
|
||||
from ceilometer import nova_client
|
||||
from ceilometer.i18n import _LW
|
||||
from ceilometer import neutron_client
|
||||
from ceilometer import sample
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
cfg.CONF.import_group('service_types', 'ceilometer.neutron_client')
|
||||
|
||||
|
||||
class FloatingIPPollster(plugin_base.PollsterBase):
|
||||
|
||||
@staticmethod
|
||||
def _get_floating_ips(ksclient, endpoint):
|
||||
nv = nova_client.Client(
|
||||
auth=ksclient.session.auth,
|
||||
endpoint_override=endpoint)
|
||||
return nv.floating_ip_get_all()
|
||||
STATUS = {
|
||||
'inactive': 0,
|
||||
'active': 1,
|
||||
'pending_create': 2,
|
||||
}
|
||||
|
||||
def _iter_floating_ips(self, ksclient, cache, endpoint):
|
||||
key = '%s-floating_ips' % endpoint
|
||||
if key not in cache:
|
||||
cache[key] = list(self._get_floating_ips(ksclient, endpoint))
|
||||
return iter(cache[key])
|
||||
def __init__(self):
|
||||
self.neutron_cli = neutron_client.Client()
|
||||
|
||||
@property
|
||||
def default_discovery(self):
|
||||
return 'endpoint:%s' % cfg.CONF.service_types.nova
|
||||
return 'endpoint:%s' % cfg.CONF.service_types.neutron
|
||||
|
||||
@staticmethod
|
||||
def _form_metadata_for_fip(fip):
|
||||
"""Return a metadata dictionary for the fip usage data."""
|
||||
metadata = {
|
||||
'router_id': fip.get("router_id"),
|
||||
'status': fip.get("status"),
|
||||
'floating_network_id': fip.get("floating_network_id"),
|
||||
'fixed_ip_address': fip.get("fixed_ip_address"),
|
||||
'port_id': fip.get("port_id"),
|
||||
'floating_ip_address': fip.get("floating_ip_address")
|
||||
}
|
||||
return metadata
|
||||
|
||||
def get_samples(self, manager, cache, resources):
|
||||
for endpoint in resources:
|
||||
for ip in self._iter_floating_ips(manager.keystone, cache,
|
||||
endpoint):
|
||||
LOG.info(_LI("FLOATING IP USAGE: %s") % ip.ip)
|
||||
# FIXME (flwang) Now Nova API /os-floating-ips can't provide
|
||||
# those attributes were used by Ceilometer, such as project
|
||||
# id, host. In this fix, those attributes usage will be
|
||||
# removed temporarily. And they will be back after fix the
|
||||
# Nova bug 1174802.
|
||||
yield sample.Sample(
|
||||
name='ip.floating',
|
||||
type=sample.TYPE_GAUGE,
|
||||
unit='ip',
|
||||
volume=1,
|
||||
user_id=None,
|
||||
project_id=None,
|
||||
resource_id=ip.id,
|
||||
timestamp=timeutils.utcnow().isoformat(),
|
||||
resource_metadata={
|
||||
'address': ip.ip,
|
||||
'pool': ip.pool
|
||||
})
|
||||
|
||||
for fip in self.neutron_cli.fip_get_all():
|
||||
status = self.STATUS.get(fip['status'].lower())
|
||||
if status is None:
|
||||
LOG.warn(_LW("Invalid status, skipping IP address %s") %
|
||||
fip['floating_ip_address'])
|
||||
continue
|
||||
res_metadata = self._form_metadata_for_fip(fip)
|
||||
yield sample.Sample(
|
||||
name='ip.floating',
|
||||
type=sample.TYPE_GAUGE,
|
||||
unit='ip',
|
||||
volume=status,
|
||||
user_id=fip.get('user_id'),
|
||||
project_id=fip['tenant_id'],
|
||||
resource_id=fip['id'],
|
||||
timestamp=timeutils.utcnow().isoformat(),
|
||||
resource_metadata=res_metadata
|
||||
)
|
||||
|
@ -113,3 +113,8 @@ class Client(object):
|
||||
def fw_policy_get_all(self):
|
||||
resp = self.client.list_firewall_policies()
|
||||
return resp.get('firewall_policies')
|
||||
|
||||
@logged
|
||||
def fip_get_all(self):
|
||||
fips = self.client.list_floatingips()['floatingips']
|
||||
return fips
|
||||
|
103
ceilometer/tests/unit/network/test_floating_ip.py
Normal file
103
ceilometer/tests/unit/network/test_floating_ip.py
Normal file
@ -0,0 +1,103 @@
|
||||
# #!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2016 Sungard Availability Services
|
||||
# Copyright 2016 Red Hat
|
||||
# All Rights Reserved
|
||||
#
|
||||
# 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 oslotest import base
|
||||
from oslotest import mockpatch
|
||||
|
||||
from ceilometer.agent import manager
|
||||
from ceilometer.agent import plugin_base
|
||||
from ceilometer.network import floatingip
|
||||
|
||||
|
||||
class _BaseTestFloatingIPPollster(base.BaseTestCase):
|
||||
|
||||
@mock.patch('ceilometer.pipeline.setup_pipeline', mock.MagicMock())
|
||||
def setUp(self):
|
||||
super(_BaseTestFloatingIPPollster, self).setUp()
|
||||
self.manager = manager.AgentManager()
|
||||
plugin_base._get_keystone = mock.Mock()
|
||||
|
||||
|
||||
class TestFloatingIPPollster(_BaseTestFloatingIPPollster):
|
||||
|
||||
def setUp(self):
|
||||
super(TestFloatingIPPollster, self).setUp()
|
||||
self.pollster = floatingip.FloatingIPPollster()
|
||||
fake_fip = self.fake_get_fip_service()
|
||||
self.useFixture(mockpatch.Patch('ceilometer.neutron_client.Client.'
|
||||
'fip_get_all',
|
||||
return_value=fake_fip))
|
||||
|
||||
@staticmethod
|
||||
def fake_get_fip_service():
|
||||
return [{'router_id': 'e24f8a37-1bb7-49e4-833c-049bb21986d2',
|
||||
'status': 'ACTIVE',
|
||||
'tenant_id': '54a00c50ee4c4396b2f8dc220a2bed57',
|
||||
'floating_network_id':
|
||||
'f41f399e-d63e-47c6-9a19-21c4e4fbbba0',
|
||||
'fixed_ip_address': '10.0.0.6',
|
||||
'floating_ip_address': '65.79.162.11',
|
||||
'port_id': '93a0d2c7-a397-444c-9d75-d2ac89b6f209',
|
||||
'id': '18ca27bf-72bc-40c8-9c13-414d564ea367'},
|
||||
{'router_id': 'astf8a37-1bb7-49e4-833c-049bb21986d2',
|
||||
'status': 'DOWN',
|
||||
'tenant_id': '34a00c50ee4c4396b2f8dc220a2bed57',
|
||||
'floating_network_id':
|
||||
'gh1f399e-d63e-47c6-9a19-21c4e4fbbba0',
|
||||
'fixed_ip_address': '10.0.0.7',
|
||||
'floating_ip_address': '65.79.162.12',
|
||||
'port_id': '453a0d2c7-a397-444c-9d75-d2ac89b6f209',
|
||||
'id': 'jkca27bf-72bc-40c8-9c13-414d564ea367'},
|
||||
{'router_id': 'e2478937-1bb7-49e4-833c-049bb21986d2',
|
||||
'status': 'error',
|
||||
'tenant_id': '54a0gggg50ee4c4396b2f8dc220a2bed57',
|
||||
'floating_network_id':
|
||||
'po1f399e-d63e-47c6-9a19-21c4e4fbbba0',
|
||||
'fixed_ip_address': '10.0.0.8',
|
||||
'floating_ip_address': '65.79.162.13',
|
||||
'port_id': '67a0d2c7-a397-444c-9d75-d2ac89b6f209',
|
||||
'id': '90ca27bf-72bc-40c8-9c13-414d564ea367'}]
|
||||
|
||||
def test_default_discovery(self):
|
||||
self.assertEqual('endpoint:network', self.pollster.default_discovery)
|
||||
|
||||
def test_fip_get_samples(self):
|
||||
samples = list(self.pollster.get_samples(
|
||||
self.manager, {},
|
||||
resources=['http://localhost:9696/']))
|
||||
self.assertEqual(1, len(samples))
|
||||
self.assertEqual('18ca27bf-72bc-40c8-9c13-414d564ea367',
|
||||
samples[0].resource_id)
|
||||
self.assertEqual("65.79.162.11", samples[0].resource_metadata[
|
||||
"floating_ip_address"])
|
||||
self.assertEqual("10.0.0.6", samples[0].resource_metadata[
|
||||
"fixed_ip_address"])
|
||||
|
||||
def test_fip_volume(self):
|
||||
samples = list(self.pollster.get_samples(
|
||||
self.manager, {},
|
||||
resources=['http://localhost:9696/']))
|
||||
self.assertEqual(1, samples[0].volume)
|
||||
|
||||
def test_get_fip_meter_names(self):
|
||||
samples = list(self.pollster.get_samples(
|
||||
self.manager, {},
|
||||
resources=['http://localhost:9696/']))
|
||||
self.assertEqual(set(['ip.floating']),
|
||||
set([s.name for s in samples]))
|
@ -1,98 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2012 eNovance <licensing@enovance.com>
|
||||
#
|
||||
# Copyright 2013 IBM Corp
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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 oslo_context import context
|
||||
from oslotest import base
|
||||
|
||||
from ceilometer.agent import manager
|
||||
from ceilometer.network import floatingip
|
||||
|
||||
|
||||
class TestFloatingIPPollster(base.BaseTestCase):
|
||||
|
||||
@mock.patch('ceilometer.pipeline.setup_pipeline', mock.MagicMock())
|
||||
def setUp(self):
|
||||
super(TestFloatingIPPollster, self).setUp()
|
||||
self.addCleanup(mock.patch.stopall)
|
||||
self.context = context.get_admin_context()
|
||||
self.manager = manager.AgentManager()
|
||||
self.manager._keystone = mock.Mock()
|
||||
catalog = (self.manager._keystone.session.auth.
|
||||
get_access.return_value.service_catalog)
|
||||
catalog.get_endpoints = mock.Mock(return_value={'network': mock.ANY})
|
||||
self.pollster = floatingip.FloatingIPPollster()
|
||||
fake_ips = self.fake_get_ips()
|
||||
patch_virt = mock.patch('ceilometer.nova_client.Client.'
|
||||
'floating_ip_get_all',
|
||||
return_value=fake_ips)
|
||||
patch_virt.start()
|
||||
|
||||
@staticmethod
|
||||
def fake_get_ips():
|
||||
ips = []
|
||||
for i in range(1, 4):
|
||||
ip = mock.MagicMock()
|
||||
ip.id = i
|
||||
ip.ip = '1.1.1.%d' % i
|
||||
ip.pool = 'public'
|
||||
ips.append(ip)
|
||||
return ips
|
||||
|
||||
def test_default_discovery(self):
|
||||
self.assertEqual('endpoint:compute', self.pollster.default_discovery)
|
||||
|
||||
# FIXME(dhellmann): Is there a useful way to define this
|
||||
# test without a database?
|
||||
#
|
||||
# def test_get_samples_none_defined(self):
|
||||
# try:
|
||||
# list(self.pollster.get_samples(self.manager,
|
||||
# self.context)
|
||||
# )
|
||||
# except exception.NoFloatingIpsDefined:
|
||||
# pass
|
||||
# else:
|
||||
# assert False, 'Should have seen an error'
|
||||
|
||||
def test_get_samples_not_empty(self):
|
||||
samples = list(self.pollster.get_samples(self.manager, {}, ['e']))
|
||||
self.assertEqual(3, len(samples))
|
||||
# It's necessary to verify all the attributes extracted by Nova
|
||||
# API /os-floating-ips to make sure they're available and correct.
|
||||
self.assertEqual(1, samples[0].resource_id)
|
||||
self.assertEqual("1.1.1.1", samples[0].resource_metadata["address"])
|
||||
self.assertEqual("public", samples[0].resource_metadata["pool"])
|
||||
|
||||
self.assertEqual(2, samples[1].resource_id)
|
||||
self.assertEqual("1.1.1.2", samples[1].resource_metadata["address"])
|
||||
self.assertEqual("public", samples[1].resource_metadata["pool"])
|
||||
|
||||
self.assertEqual(3, samples[2].resource_id)
|
||||
self.assertEqual("1.1.1.3", samples[2].resource_metadata["address"])
|
||||
self.assertEqual("public", samples[2].resource_metadata["pool"])
|
||||
|
||||
def test_get_meter_names(self):
|
||||
samples = list(self.pollster.get_samples(self.manager, {}, ['e']))
|
||||
self.assertEqual(set(['ip.floating']), set([s.name for s in samples]))
|
||||
|
||||
def test_get_samples_cached(self):
|
||||
cache = {'e-floating_ips': self.fake_get_ips()[:2]}
|
||||
samples = list(self.pollster.get_samples(self.manager, cache, ['e']))
|
||||
self.assertEqual(2, len(samples))
|
@ -0,0 +1,9 @@
|
||||
---
|
||||
fixes:
|
||||
- >
|
||||
[`bug 1536338 <https://bugs.launchpad.net/ceilometer/+bug/1536338>`_]
|
||||
Patch was added to fix the broken floatingip pollster
|
||||
that polled data from nova api, but since the nova api
|
||||
filtered the data by tenant, ceilometer was not getting
|
||||
any data back. The fix changes the pollster to use the
|
||||
neutron api instead to get the floating ip info.
|
Loading…
Reference in New Issue
Block a user