Introduce floating IP pool resource

Add support for listing floating ip pools (subnets).
A new API resource ``floatingip-pools`` is introduced.
This API endpoint can return a list floating ip pools
which are essentially mappings between network UUIDs and
subnet CIDRs. Users can use this API to find out the pool
to create the floating IPs.

Related patches:
* neutron-lib: https://review.openstack.org/#/c/556674/
* tempest-plugin: https://review.openstack.org/#/c/562038/

APIImpact add floatingip pools api
Change-Id: Iaa995630645042520df67d95271e14f11ffcff8c
Partial-Bug: #1653932
This commit is contained in:
Hongbin Lu 2018-03-21 22:39:24 +00:00
parent cf463cce43
commit 4e3fb31919
8 changed files with 300 additions and 2 deletions

View File

@ -150,6 +150,7 @@
"create_floatingip": "rule:regular_user",
"create_floatingip:floating_ip_address": "rule:admin_only",
"get_floatingip": "rule:admin_or_owner",
"get_floatingip_pool": "rule:regular_user",
"update_floatingip": "rule:admin_or_owner",
"delete_floatingip": "rule:admin_or_owner",

View File

@ -0,0 +1,77 @@
# 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.
from neutron_lib.api.definitions import fip64
from neutron_lib.api import extensions
from neutron_lib import constants as lib_const
from neutron_lib.db import utils as lib_db_utils
from neutron_lib.plugins import directory
from neutron.extensions import floatingip_pools as fip_pools_ext
from neutron.objects import base as base_obj
from neutron.objects import network as net_obj
from neutron.objects import subnet as subnet_obj
class FloatingIPPoolsDbMixin(object):
"""Class to support floating IP pool."""
_is_v6_supported = None
@staticmethod
def _make_floatingip_pool_dict(context, subnet, fields=None):
res = {'subnet_id': subnet.id,
'subnet_name': subnet.name,
'tenant_id': context.tenant_id,
'network_id': subnet.network_id,
'cidr': str(subnet.cidr)}
return lib_db_utils.resource_fields(res, fields)
def get_floatingip_pools(self, context, filters=None, fields=None,
sorts=None, limit=None, marker=None,
page_reverse=False):
"""Return information for available floating IP pools"""
pager = base_obj.Pager(sorts, limit, page_reverse, marker)
net_ids = [n.network_id
for n in net_obj.ExternalNetwork.get_objects(context)]
# NOTE(hongbin): Use elevated context to make sure we have enough
# permission to retrieve subnets that are not in current tenant
# but belongs to external networks shared with current tenant.
admin_context = context.elevated()
subnet_objs = subnet_obj.Subnet.get_objects(admin_context,
_pager=pager,
network_id=net_ids)
return [self._make_floatingip_pool_dict(context, obj, fields)
for obj in subnet_objs
if (obj.ip_version == lib_const.IP_VERSION_4 or
self.is_v6_supported)]
@property
def is_v6_supported(self):
supported = self._is_v6_supported
if supported is None:
supported = False
for plugin in directory.get_plugins().values():
if extensions.is_extension_supported(plugin, fip64.ALIAS):
supported = True
break
self._is_v6_supported = supported
return supported
class FloatingIPPoolsMixin(FloatingIPPoolsDbMixin,
fip_pools_ext.FloatingIPPoolPluginBase):
pass

View File

@ -0,0 +1,53 @@
# 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 itertools
from neutron_lib.api.definitions import floatingip_pools as apidef
from neutron_lib.api import extensions as api_extensions
from neutron_lib.plugins import constants
import six
from neutron.api.v2 import resource_helper
class Floatingip_pools(api_extensions.APIExtensionDescriptor):
"""Neutron floating IP pool api extension."""
api_definition = apidef
@classmethod
def get_resources(cls):
"""Returns Ext Resources."""
plural_mappings = resource_helper.build_plural_mappings(
{}, itertools.chain(apidef.RESOURCE_ATTRIBUTE_MAP))
resources = resource_helper.build_resource_info(
plural_mappings,
apidef.RESOURCE_ATTRIBUTE_MAP,
constants.L3,
translate_name=True,
allow_bulk=True)
return resources
@six.add_metaclass(abc.ABCMeta)
class FloatingIPPoolPluginBase(object):
@abc.abstractmethod
def get_floatingip_pools(self, context, filters=None, fields=None,
sorts=None, limit=None, marker=None,
page_reverse=False):
"""List all floating ip pools."""
pass

View File

@ -32,6 +32,7 @@ from neutron.db import dns_db
from neutron.db import extraroute_db
from neutron.db import l3_dvr_ha_scheduler_db
from neutron.db import l3_dvrscheduler_db
from neutron.db import l3_fip_pools_db
from neutron.db import l3_fip_port_details
from neutron.db import l3_fip_qos
from neutron.db import l3_gwmode_db
@ -69,7 +70,8 @@ class L3RouterPlugin(service_base.ServicePluginBase,
l3_dvr_ha_scheduler_db.L3_DVR_HA_scheduler_db_mixin,
dns_db.DNSDbMixin,
l3_fip_qos.FloatingQoSDbMixin,
l3_fip_port_details.Fip_port_details_db_mixin):
l3_fip_port_details.Fip_port_details_db_mixin,
l3_fip_pools_db.FloatingIPPoolsMixin):
"""Implementation of the Neutron L3 Router Service Plugin.
@ -84,7 +86,7 @@ class L3RouterPlugin(service_base.ServicePluginBase,
"extraroute", "l3_agent_scheduler",
"l3-ha", "router_availability_zone",
"l3-flavors", "qos-fip",
"fip-port-details"]
"fip-port-details", "floatingip-pools"]
__native_pagination_support = True
__native_sorting_support = True

View File

@ -20,6 +20,7 @@ NETWORK_API_EXTENSIONS+=",extraroute"
NETWORK_API_EXTENSIONS+=",filter-validation"
NETWORK_API_EXTENSIONS+=",fip-port-details"
NETWORK_API_EXTENSIONS+=",flavors"
NETWORK_API_EXTENSIONS+=",floatingip-pools"
NETWORK_API_EXTENSIONS+=",ip-substring-filtering"
NETWORK_API_EXTENSIONS+=",l3-flavors"
NETWORK_API_EXTENSIONS+=",l3-ha"

View File

@ -150,6 +150,7 @@
"create_floatingip": "rule:regular_user",
"create_floatingip:floating_ip_address": "rule:admin_only",
"get_floatingip": "rule:admin_or_owner",
"get_floatingip_pool": "rule:regular_user",
"update_floatingip": "rule:admin_or_owner",
"delete_floatingip": "rule:admin_or_owner",

View File

@ -0,0 +1,155 @@
#
# 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 ddt
import mock
from neutron_lib.api.definitions import floatingip_pools as apidef
from neutron_lib import constants as lib_const
from neutron_lib import context
from neutron_lib.plugins import constants as plugin_constants
from neutron_lib.plugins import directory
from oslo_config import cfg
from oslo_utils import uuidutils
from neutron.db import l3_fip_pools_db
from neutron.extensions import l3
from neutron.objects import network as net_obj
from neutron.objects import subnet as subnet_obj
from neutron.tests.unit.extensions import test_l3
class FloatingIPPoolsTestExtensionManager(object):
def get_resources(self):
return l3.L3.get_resources()
def get_actions(self):
return []
def get_request_extensions(self):
return []
class TestFloatingIPPoolsIntPlugin(
test_l3.TestL3NatIntPlugin,
l3_fip_pools_db.FloatingIPPoolsDbMixin):
supported_extension_aliases = ["external-net", "router",
apidef.ALIAS]
class TestFloatingIPPoolsL3NatServicePlugin(
test_l3.TestL3NatServicePlugin,
l3_fip_pools_db.FloatingIPPoolsDbMixin):
supported_extension_aliases = ["router", apidef.ALIAS]
@ddt.ddt
class FloatingIPPoolsDBTestCaseBase(test_l3.L3NatTestCaseMixin):
def test_get_floatingip_pools_ipv4(self):
self._test_get_floatingip_pools(lib_const.IP_VERSION_4, False)
@ddt.data(True, False)
def test_get_floatingip_pools_ipv6(self, fake_is_v6_supported):
self._test_get_floatingip_pools(lib_const.IP_VERSION_6,
fake_is_v6_supported)
def _test_get_floatingip_pools(self, ip_version, is_v6_supported):
fake_network_id = uuidutils.generate_uuid()
fake_subnet_id = uuidutils.generate_uuid()
fake_ext_network = mock.Mock(network_id=fake_network_id)
if ip_version == lib_const.IP_VERSION_4:
fake_cidr = '10.0.0.0/24'
else:
fake_cidr = 'fe80:cafe::/64'
fake_subnet = mock.Mock(id=fake_subnet_id,
network_id=fake_network_id,
cidr=fake_cidr,
ip_version=ip_version,
tenant_id='fake_tenant',
project_id='fake_tenant')
fake_subnet.name = 'fake_subnet'
self.plugin._is_v6_supported = is_v6_supported
with mock.patch.object(
subnet_obj.Subnet, 'get_objects',
return_value=[fake_subnet]
) as mock_subnet_get_objects, mock.patch.object(
net_obj.ExternalNetwork, 'get_objects',
return_value=[fake_ext_network]
) as mock_extnet_get_objects, mock.patch.object(
self.ctxt, 'elevated',
return_value=self.admin_ctxt
) as mock_context_elevated:
fip_pools = self.plugin.get_floatingip_pools(self.ctxt)
expected_fip_pools = []
if ip_version == lib_const.IP_VERSION_4 or is_v6_supported:
expected_fip_pools = [{'cidr': fake_cidr,
'subnet_id': fake_subnet_id,
'subnet_name': 'fake_subnet',
'network_id': fake_network_id,
'project_id': 'fake_tenant',
'tenant_id': 'fake_tenant'}]
self.assertEqual(expected_fip_pools, fip_pools)
mock_subnet_get_objects.assert_called_once_with(
self.admin_ctxt, _pager=mock.ANY, network_id=[fake_network_id])
mock_extnet_get_objects.assert_called_once_with(self.ctxt)
mock_context_elevated.assert_called_once_with()
class FloatingIPPoolsDBIntTestCase(test_l3.L3BaseForIntTests,
FloatingIPPoolsDBTestCaseBase):
def setUp(self, plugin=None):
if not plugin:
plugin = ('neutron.tests.unit.extensions.test_floatingip_pools.'
'TestFloatingIPPoolsIntPlugin')
# for these tests we need to enable overlapping ips
cfg.CONF.set_default('allow_overlapping_ips', True)
cfg.CONF.set_default('max_routes', 3)
ext_mgr = FloatingIPPoolsTestExtensionManager()
super(test_l3.L3BaseForIntTests, self).setUp(
plugin=plugin,
ext_mgr=ext_mgr)
self.setup_notification_driver()
self.ctxt = context.Context('fake_user', 'fake_tenant')
self.admin_ctxt = self.ctxt.elevated()
class FloatingIPPoolsDBSepTestCase(test_l3.L3BaseForSepTests,
FloatingIPPoolsDBTestCaseBase):
def setUp(self):
# the plugin without L3 support
plugin = 'neutron.tests.unit.extensions.test_l3.TestNoL3NatPlugin'
# the L3 service plugin
l3_plugin = ('neutron.tests.unit.extensions.test_floatingip_pools.'
'TestFloatingIPPoolsL3NatServicePlugin')
service_plugins = {'l3_plugin_name': l3_plugin}
# for these tests we need to enable overlapping ips
cfg.CONF.set_default('allow_overlapping_ips', True)
cfg.CONF.set_default('max_routes', 3)
ext_mgr = FloatingIPPoolsTestExtensionManager()
super(test_l3.L3BaseForSepTests, self).setUp(
plugin=plugin,
ext_mgr=ext_mgr,
service_plugins=service_plugins)
self.setup_notification_driver()
self.plugin = directory.get_plugin(plugin_constants.L3)
self.ctxt = context.Context('fake_user', 'fake_tenant')
self.admin_ctxt = self.ctxt.elevated()

View File

@ -0,0 +1,8 @@
---
features:
- |
Add support for listing floating ip pools (subnets) in L3 plugin.
A new API resource ``floatingip-pools`` is introduced.
This API endpoint can return a list of floating ip pools which are
essentially mappings between network UUIDs and subnet CIDRs.
Users can use this API to find out the pool to create the floating IPs.