Disable Floating IP features if Neutron router is disabled

If the config option 'enable_router' is set to False,
Floating IP features are disabled when Neutron is enabled.
It does not affect when Neutron is disabled.

It also adds unit tests for api.network.servers_update_addresses
which is affected by this change.

Completes blueprint hide-router-panel-by-config
Closes-Bug: #1292022
Change-Id: Ib63c6a0e7bb5661d4a60d10a1722fdad978b50bb
This commit is contained in:
Akihiro Motoki 2014-08-09 15:08:40 +09:00
parent 1100169130
commit 936fc59aa8
18 changed files with 297 additions and 20 deletions

View File

@ -514,10 +514,11 @@ available.
Default: ``True``
Enable (True) or disable (False) the router panel. If your neutron
has no support for Layer-3 router features, or you do no not wish to
provide the Layer-3 features through the Dashboard, this should be set to
``False``.
Enable (True) or disable (False) the panels and menus related
to router and Floating IP features. This option only affects
when Neutron is enabled. If your neutron has no support for
Layer-3 features, or you do no not wish to provide the Layer-3
features through the Dashboard, this should be set to ``False``.
``enable_distributed_router``:

View File

@ -88,6 +88,10 @@ def floating_ip_simple_associate_supported(request):
return NetworkClient(request).floating_ips.is_simple_associate_supported()
def floating_ip_supported(request):
return NetworkClient(request).floating_ips.is_supported()
def security_group_list(request):
return NetworkClient(request).secgroups.list()

View File

@ -131,6 +131,11 @@ class FloatingIpManager(object):
"""Returns True if the default floating IP pool is enabled."""
pass
@abc.abstractmethod
def is_supported(self):
"""Returns True if floating IP feature is supported."""
pass
@six.add_metaclass(abc.ABCMeta)
class SecurityGroupManager(object):

View File

@ -427,6 +427,10 @@ class FloatingIpManager(network_base.FloatingIpManager):
# to enable simple association support.
return False
def is_supported(self):
network_config = getattr(settings, 'OPENSTACK_NEUTRON_NETWORK', {})
return network_config.get('enable_router', True)
def get_ipver_str(ip_version):
"""Convert an ip version number to a human-friendly string."""
@ -784,8 +788,11 @@ def servers_update_addresses(request, servers):
try:
ports = port_list(request,
device_id=[instance.id for instance in servers])
floating_ips = FloatingIpManager(request).list(
port_id=[port.id for port in ports])
fips = FloatingIpManager(request)
if fips.is_supported():
floating_ips = fips.list(port_id=[port.id for port in ports])
else:
floating_ips = []
networks = network_list(request,
id=[port.network_id for port in ports])
except Exception:

View File

@ -389,6 +389,9 @@ class FloatingIpManager(network_base.FloatingIpManager):
def is_simple_associate_supported(self):
return conf.HORIZON_CONFIG["simple_ip_management"]
def is_supported(self):
return True
def novaclient(request):
insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False)

View File

@ -37,12 +37,13 @@ INDEX_URL = reverse('horizon:project:overview:index')
class UsageViewTests(test.BaseAdminViewTests):
def _stub_nova_api_calls(self, nova_stu_enabled):
def _stub_api_calls(self, nova_stu_enabled):
self.mox.StubOutWithMock(api.nova, 'usage_list')
self.mox.StubOutWithMock(api.nova, 'tenant_absolute_limits')
self.mox.StubOutWithMock(api.nova, 'extension_supported')
self.mox.StubOutWithMock(api.keystone, 'tenant_list')
self.mox.StubOutWithMock(api.neutron, 'is_extension_supported')
self.mox.StubOutWithMock(api.network, 'floating_ip_supported')
self.mox.StubOutWithMock(api.network, 'tenant_floating_ip_list')
self.mox.StubOutWithMock(api.network, 'security_group_list')
self.mox.StubOutWithMock(api.cinder, 'tenant_absolute_limits')
@ -61,7 +62,7 @@ class UsageViewTests(test.BaseAdminViewTests):
self._test_usage(tenant_deleted=True)
def _test_usage(self, nova_stu_enabled=True, tenant_deleted=False):
self._stub_nova_api_calls(nova_stu_enabled)
self._stub_api_calls(nova_stu_enabled)
api.nova.extension_supported(
'SimpleTenantUsage', IsA(http.HttpRequest)) \
.AndReturn(nova_stu_enabled)
@ -87,6 +88,8 @@ class UsageViewTests(test.BaseAdminViewTests):
.AndReturn(self.limits['absolute'])
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'security-group').AndReturn(True)
api.network.floating_ip_supported(IsA(http.HttpRequest)) \
.AndReturn(True)
api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \
.AndReturn(self.floating_ips.list())
api.network.security_group_list(IsA(http.HttpRequest)) \
@ -150,7 +153,7 @@ class UsageViewTests(test.BaseAdminViewTests):
self._test_usage_csv(nova_stu_enabled=False)
def _test_usage_csv(self, nova_stu_enabled=True):
self._stub_nova_api_calls(nova_stu_enabled)
self._stub_api_calls(nova_stu_enabled)
api.nova.extension_supported(
'SimpleTenantUsage', IsA(http.HttpRequest)) \
.AndReturn(nova_stu_enabled)
@ -171,6 +174,8 @@ class UsageViewTests(test.BaseAdminViewTests):
.AndReturn(self.limits['absolute'])
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'security-group').AndReturn(True)
api.network.floating_ip_supported(IsA(http.HttpRequest)) \
.AndReturn(True)
api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \
.AndReturn(self.floating_ips.list())
api.network.security_group_list(IsA(http.HttpRequest)) \

View File

@ -1565,12 +1565,15 @@ class UsageViewTests(test.BaseAdminViewTests):
def _stub_neutron_api_calls(self, neutron_sg_enabled=True):
self.mox.StubOutWithMock(api.neutron, 'is_extension_supported')
self.mox.StubOutWithMock(api.network, 'floating_ip_supported')
self.mox.StubOutWithMock(api.network, 'tenant_floating_ip_list')
if neutron_sg_enabled:
self.mox.StubOutWithMock(api.network, 'security_group_list')
api.neutron.is_extension_supported(
IsA(http.HttpRequest),
'security-group').AndReturn(neutron_sg_enabled)
api.network.floating_ip_supported(IsA(http.HttpRequest)) \
.AndReturn(True)
api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \
.AndReturn(self.floating_ips.list())
if neutron_sg_enabled:

View File

@ -122,6 +122,7 @@ class FloatingIpViewTests(test.TestCase):
def test_disassociate_post(self):
floating_ip = self.floating_ips.first()
server = self.servers.first()
self.mox.StubOutWithMock(api.network, 'floating_ip_supported')
self.mox.StubOutWithMock(api.network, 'tenant_floating_ip_list')
self.mox.StubOutWithMock(api.network, 'tenant_floating_ip_get')
self.mox.StubOutWithMock(api.network, 'floating_ip_disassociate')
@ -129,6 +130,8 @@ class FloatingIpViewTests(test.TestCase):
api.nova.server_list(IsA(http.HttpRequest)) \
.AndReturn([self.servers.list(), False])
api.network.floating_ip_supported(IsA(http.HttpRequest)) \
.AndReturn(True)
api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \
.AndReturn(self.floating_ips.list())
api.network.floating_ip_disassociate(IsA(http.HttpRequest),
@ -144,6 +147,7 @@ class FloatingIpViewTests(test.TestCase):
def test_disassociate_post_with_exception(self):
floating_ip = self.floating_ips.first()
server = self.servers.first()
self.mox.StubOutWithMock(api.network, 'floating_ip_supported')
self.mox.StubOutWithMock(api.network, 'tenant_floating_ip_list')
self.mox.StubOutWithMock(api.network, 'tenant_floating_ip_get')
self.mox.StubOutWithMock(api.network, 'floating_ip_disassociate')
@ -151,6 +155,8 @@ class FloatingIpViewTests(test.TestCase):
api.nova.server_list(IsA(http.HttpRequest)) \
.AndReturn([self.servers.list(), False])
api.network.floating_ip_supported(IsA(http.HttpRequest)) \
.AndReturn(True)
api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \
.AndReturn(self.floating_ips.list())
@ -180,6 +186,7 @@ class FloatingIpNeutronViewTests(FloatingIpViewTests):
api.cinder: ('tenant_quota_get', 'volume_list',
'volume_snapshot_list',),
api.network: ('floating_ip_pools_list',
'floating_ip_supported',
'tenant_floating_ip_list'),
api.neutron: ('is_extension_supported',
'tenant_quota_get')})
@ -206,6 +213,8 @@ class FloatingIpNeutronViewTests(FloatingIpViewTests):
.AndReturn(True)
api.neutron.tenant_quota_get(IsA(http.HttpRequest), self.tenant.id) \
.AndReturn(self.neutron_quotas.first())
api.network.floating_ip_supported(IsA(http.HttpRequest)) \
.AndReturn(True)
api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(self.floating_ips.list())
api.network.floating_ip_pools_list(IsA(http.HttpRequest)) \

View File

@ -34,9 +34,13 @@ class KeyPairViewTests(test.TestCase):
def test_delete_keypair(self):
keypair = self.keypairs.first()
self.mox.StubOutWithMock(api.network, 'floating_ip_supported')
self.mox.StubOutWithMock(api.nova, 'keypair_list')
self.mox.StubOutWithMock(api.nova, 'keypair_delete')
# floating_ip_supported is called in Floating IP tab allowed().
api.network.floating_ip_supported(IsA(http.HttpRequest)) \
.AndReturn(True)
api.nova.keypair_list(IsA(http.HttpRequest)) \
.AndReturn(self.keypairs.list())
api.nova.keypair_delete(IsA(http.HttpRequest), keypair.name)
@ -48,9 +52,13 @@ class KeyPairViewTests(test.TestCase):
def test_delete_keypair_exception(self):
keypair = self.keypairs.first()
self.mox.StubOutWithMock(api.network, 'floating_ip_supported')
self.mox.StubOutWithMock(api.nova, 'keypair_list')
self.mox.StubOutWithMock(api.nova, 'keypair_delete')
# floating_ip_supported is called in Floating IP tab allowed().
api.network.floating_ip_supported(IsA(http.HttpRequest)) \
.AndReturn(True)
api.nova.keypair_list(IsA(http.HttpRequest)) \
.AndReturn(self.keypairs.list())
api.nova.keypair_delete(IsA(http.HttpRequest), keypair.name) \

View File

@ -108,6 +108,9 @@ class FloatingIPsTab(tabs.TableTab):
return floating_ips
def allowed(self, request):
return network.floating_ip_supported(request)
class APIAccessTab(tabs.TableTab):
table_classes = (EndpointsTable,)

View File

@ -39,6 +39,7 @@ class AccessAndSecurityTests(test.TestCase):
sec_groups = self.security_groups.list()
floating_ips = self.floating_ips.list()
quota_data = self.quota_usages.first()
self.mox.StubOutWithMock(api.network, 'floating_ip_supported')
self.mox.StubOutWithMock(api.network, 'tenant_floating_ip_list')
self.mox.StubOutWithMock(api.network, 'security_group_list')
self.mox.StubOutWithMock(api.nova, 'keypair_list')
@ -49,6 +50,8 @@ class AccessAndSecurityTests(test.TestCase):
api.nova.server_list(IsA(http.HttpRequest)) \
.AndReturn([self.servers.list(), False])
api.nova.keypair_list(IsA(http.HttpRequest)).AndReturn(keypairs)
api.network.floating_ip_supported(IsA(http.HttpRequest)) \
.AndReturn(True)
api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \
.AndReturn(floating_ips)
api.network.security_group_list(IsA(http.HttpRequest)) \
@ -82,6 +85,7 @@ class AccessAndSecurityTests(test.TestCase):
sec_groups = self.security_groups.list()
floating_ips = self.floating_ips.list()
quota_data = self.quota_usages.first()
self.mox.StubOutWithMock(api.network, 'floating_ip_supported')
self.mox.StubOutWithMock(api.network, 'tenant_floating_ip_list')
self.mox.StubOutWithMock(api.network, 'security_group_list')
self.mox.StubOutWithMock(api.nova, 'keypair_list')
@ -92,6 +96,8 @@ class AccessAndSecurityTests(test.TestCase):
api.nova.server_list(IsA(http.HttpRequest)) \
.AndReturn([self.servers.list(), False])
api.nova.keypair_list(IsA(http.HttpRequest)).AndReturn(keypairs)
api.network.floating_ip_supported(IsA(http.HttpRequest)) \
.AndReturn(True)
api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \
.AndReturn(floating_ips)
api.network.security_group_list(IsA(http.HttpRequest)) \

View File

@ -491,6 +491,8 @@ class AssociateIP(tables.LinkAction):
return {"project_id": project_id}
def allowed(self, request, instance):
if not api.network.floating_ip_supported(request):
return False
if api.network.floating_ip_simple_associate_supported(request):
return False
return not is_deleting(instance)
@ -552,6 +554,8 @@ class SimpleDisassociateIP(tables.Action):
return {"project_id": project_id}
def allowed(self, request, instance):
if not api.network.floating_ip_supported(request):
return False
if not conf.HORIZON_CONFIG["simple_ip_management"]:
return False
return not is_deleting(instance)

View File

@ -57,6 +57,7 @@ class InstanceTests(helpers.TestCase):
api.glance: ('image_list_detailed',),
api.network: (
'floating_ip_simple_associate_supported',
'floating_ip_supported',
'servers_update_addresses',
),
})
@ -75,6 +76,8 @@ class InstanceTests(helpers.TestCase):
api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
api.nova.tenant_absolute_limits(IsA(http.HttpRequest), reserved=True) \
.MultipleTimes().AndReturn(self.limits['absolute'])
api.network.floating_ip_supported(IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.network.floating_ip_simple_associate_supported(
IsA(http.HttpRequest)).MultipleTimes().AndReturn(True)
@ -111,6 +114,7 @@ class InstanceTests(helpers.TestCase):
'tenant_absolute_limits', 'extension_supported',),
api.glance: ('image_list_detailed',),
api.network: ('floating_ip_simple_associate_supported',
'floating_ip_supported',
'servers_update_addresses',),
})
def test_index_flavor_list_exception(self):
@ -133,6 +137,8 @@ class InstanceTests(helpers.TestCase):
AndReturn(full_flavors[server.flavor["id"]])
api.nova.tenant_absolute_limits(IsA(http.HttpRequest), reserved=True) \
.MultipleTimes().AndReturn(self.limits['absolute'])
api.network.floating_ip_supported(IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.network.floating_ip_simple_associate_supported(
IsA(http.HttpRequest)).MultipleTimes().AndReturn(True)
@ -150,6 +156,7 @@ class InstanceTests(helpers.TestCase):
'tenant_absolute_limits', 'extension_supported',),
api.glance: ('image_list_detailed',),
api.network: ('floating_ip_simple_associate_supported',
'floating_ip_supported',
'servers_update_addresses',),
})
def test_index_flavor_get_exception(self):
@ -175,6 +182,8 @@ class InstanceTests(helpers.TestCase):
AndRaise(self.exceptions.nova)
api.nova.tenant_absolute_limits(IsA(http.HttpRequest), reserved=True) \
.MultipleTimes().AndReturn(self.limits['absolute'])
api.network.floating_ip_supported(IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.network.floating_ip_simple_associate_supported(
IsA(http.HttpRequest)).MultipleTimes().AndReturn(True)
@ -193,6 +202,7 @@ class InstanceTests(helpers.TestCase):
'extension_supported',),
api.glance: ('image_list_detailed',),
api.network: ('floating_ip_simple_associate_supported',
'floating_ip_supported',
'servers_update_addresses',),
})
def test_index_with_instance_booted_from_volume(self):
@ -215,6 +225,8 @@ class InstanceTests(helpers.TestCase):
api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
api.nova.tenant_absolute_limits(IsA(http.HttpRequest), reserved=True) \
.MultipleTimes().AndReturn(self.limits['absolute'])
api.network.floating_ip_supported(IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.network.floating_ip_simple_associate_supported(
IsA(http.HttpRequest)).MultipleTimes().AndReturn(True)
@ -972,6 +984,7 @@ class InstanceTests(helpers.TestCase):
'extension_supported',),
api.glance: ('image_list_detailed',),
api.network: ('floating_ip_simple_associate_supported',
'floating_ip_supported',
'servers_update_addresses',),
})
def _test_instances_index_retrieve_password_action(self):
@ -989,6 +1002,8 @@ class InstanceTests(helpers.TestCase):
api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
api.nova.tenant_absolute_limits(IsA(http.HttpRequest), reserved=True) \
.MultipleTimes().AndReturn(self.limits['absolute'])
api.network.floating_ip_supported(IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.network.floating_ip_simple_associate_supported(
IsA(http.HttpRequest)).MultipleTimes().AndReturn(True)
@ -2559,6 +2574,7 @@ class InstanceTests(helpers.TestCase):
'extension_supported',),
api.glance: ('image_list_detailed',),
api.network: ('floating_ip_simple_associate_supported',
'floating_ip_supported',
'servers_update_addresses',),
})
def test_launch_button_disabled_when_quota_exceeded(self):
@ -2579,6 +2595,8 @@ class InstanceTests(helpers.TestCase):
api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
api.nova.tenant_absolute_limits(IsA(http.HttpRequest), reserved=True) \
.MultipleTimes().AndReturn(limits)
api.network.floating_ip_supported(IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.network.floating_ip_simple_associate_supported(
IsA(http.HttpRequest)).MultipleTimes().AndReturn(True)
@ -2607,6 +2625,7 @@ class InstanceTests(helpers.TestCase):
'extension_supported',),
api.glance: ('image_list_detailed',),
api.network: ('floating_ip_simple_associate_supported',
'floating_ip_supported',
'servers_update_addresses',),
})
def test_index_options_after_migrate(self):
@ -2626,6 +2645,8 @@ class InstanceTests(helpers.TestCase):
api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
api.nova.tenant_absolute_limits(IsA(http.HttpRequest), reserved=True) \
.MultipleTimes().AndReturn(self.limits['absolute'])
api.network.floating_ip_supported(IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.network.floating_ip_simple_associate_supported(
IsA(http.HttpRequest)).MultipleTimes().AndReturn(True)
@ -3102,6 +3123,7 @@ class InstanceTests(helpers.TestCase):
'extension_supported',),
api.glance: ('image_list_detailed',),
api.network: ('floating_ip_simple_associate_supported',
'floating_ip_supported',
'servers_update_addresses',),
})
def test_index_form_action_with_pagination(self):
@ -3132,6 +3154,8 @@ class InstanceTests(helpers.TestCase):
api.nova.tenant_absolute_limits(IsA(http.HttpRequest), reserved=True) \
.MultipleTimes().AndReturn(self.limits['absolute'])
api.network.floating_ip_supported(IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.network.floating_ip_simple_associate_supported(
IsA(http.HttpRequest)).MultipleTimes().AndReturn(True)

View File

@ -50,12 +50,15 @@ class UsageViewTests(test.TestCase):
def _stub_neutron_api_calls(self, neutron_sg_enabled=True):
self.mox.StubOutWithMock(api.neutron, 'is_extension_supported')
self.mox.StubOutWithMock(api.network, 'floating_ip_supported')
self.mox.StubOutWithMock(api.network, 'tenant_floating_ip_list')
if neutron_sg_enabled:
self.mox.StubOutWithMock(api.network, 'security_group_list')
api.neutron.is_extension_supported(
IsA(http.HttpRequest),
'security-group').AndReturn(neutron_sg_enabled)
api.network.floating_ip_supported(IsA(http.HttpRequest)) \
.AndReturn(True)
api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \
.AndReturn(self.floating_ips.list())
if neutron_sg_enabled:
@ -278,6 +281,10 @@ class UsageViewTests(test.TestCase):
def test_usage_with_neutron_nova_security_group(self):
self._test_usage_with_neutron(neutron_sg_enabled=False)
@override_settings(OPENSTACK_NEUTRON_NETWORK={'enable_quotas': True})
def test_usage_with_neutron_floating_ip_disabled(self):
self._test_usage_with_neutron(neutron_fip_enabled=False)
def _test_usage_with_neutron_prepare(self):
now = timezone.now()
usage_obj = api.nova.NovaUsage(self.usages.first())
@ -288,6 +295,7 @@ class UsageViewTests(test.TestCase):
.AndReturn(True)
self.mox.StubOutWithMock(api.neutron, 'tenant_quota_get')
self.mox.StubOutWithMock(api.neutron, 'is_extension_supported')
self.mox.StubOutWithMock(api.network, 'floating_ip_supported')
self.mox.StubOutWithMock(api.network, 'tenant_floating_ip_list')
self.mox.StubOutWithMock(api.network, 'security_group_list')
start = datetime.datetime(now.year, now.month, 1, 0, 0, 0, 0)
@ -298,15 +306,19 @@ class UsageViewTests(test.TestCase):
api.nova.tenant_absolute_limits(IsA(http.HttpRequest))\
.AndReturn(self.limits['absolute'])
def _test_usage_with_neutron(self, neutron_sg_enabled=True):
def _test_usage_with_neutron(self, neutron_sg_enabled=True,
neutron_fip_enabled=True):
self._test_usage_with_neutron_prepare()
api.neutron.is_extension_supported(
IsA(http.HttpRequest), 'quotas').AndReturn(True)
api.neutron.is_extension_supported(
IsA(http.HttpRequest),
'security-group').AndReturn(neutron_sg_enabled)
api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \
.AndReturn(self.floating_ips.list())
api.network.floating_ip_supported(IsA(http.HttpRequest)) \
.AndReturn(neutron_fip_enabled)
if neutron_fip_enabled:
api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \
.AndReturn(self.floating_ips.list())
if neutron_sg_enabled:
api.network.security_group_list(IsA(http.HttpRequest)) \
.AndReturn(self.q_secgroups.list())

View File

@ -12,11 +12,13 @@
# License for the specific language governing permissions and limitations
# under the License.
import collections
import copy
import itertools
import uuid
from django import http
from django.test.utils import override_settings
from mox import IsA # noqa
from novaclient.v1_1 import floating_ip_pools
@ -218,14 +220,120 @@ class NetworkApiNeutronTestBase(test.APITestCase):
api.base.is_service_enabled(IsA(http.HttpRequest), 'network') \
.AndReturn(True)
self.qclient = self.stub_neutronclient()
self.qclient.list_extensions() \
.AndReturn({'extensions': self.api_extensions.list()})
class NetworkApiNeutronTests(NetworkApiNeutronTestBase):
def _get_expected_addresses(self, server, no_fip_expected=True):
server_ports = self.ports.filter(device_id=server.id)
addresses = collections.defaultdict(list)
for p in server_ports:
net_name = self.networks.get(id=p['network_id']).name
for ip in p.fixed_ips:
addresses[net_name].append(
{'version': 4,
'addr': ip['ip_address'],
'OS-EXT-IPS-MAC:mac_addr': p.mac_address,
'OS-EXT-IPS:type': 'fixed'})
if no_fip_expected:
continue
fips = self.q_floating_ips.filter(port_id=p['id'])
if not fips:
continue
# Only one FIP should match.
fip = fips[0]
addresses[net_name].append(
{'version': 4,
'addr': fip.floating_ip_address,
'OS-EXT-IPS-MAC:mac_addr': p.mac_address,
'OS-EXT-IPS:type': 'floating'})
return addresses
def _check_server_address(self, res_server_data, no_fip_expected=False):
expected_addresses = self._get_expected_addresses(res_server_data,
no_fip_expected)
self.assertEqual(len(expected_addresses),
len(res_server_data.addresses))
for net, addresses in expected_addresses.items():
self.assertIn(net, res_server_data.addresses)
self.assertEqual(addresses, res_server_data.addresses[net])
def _test_servers_update_addresses(self, router_enabled=True):
tenant_id = self.request.user.tenant_id
servers = copy.deepcopy(self.servers.list())
server_ids = [server.id for server in servers]
server_ports = [p for p in self.api_ports.list()
if p['device_id'] in server_ids]
server_port_ids = [p['id'] for p in server_ports]
if router_enabled:
assoc_fips = [fip for fip in self.api_q_floating_ips.list()
if fip['port_id'] in server_port_ids]
server_network_ids = [p['network_id'] for p in server_ports]
server_networks = [net for net in self.api_networks.list()
if net['id'] in server_network_ids]
self.qclient.list_ports(device_id=server_ids) \
.AndReturn({'ports': server_ports})
if router_enabled:
self.qclient.list_floatingips(tenant_id=tenant_id,
port_id=server_port_ids) \
.AndReturn({'floatingips': assoc_fips})
self.qclient.list_ports(tenant_id=tenant_id) \
.AndReturn({'ports': self.api_ports.list()})
self.qclient.list_networks(id=server_network_ids) \
.AndReturn({'networks': server_networks})
self.qclient.list_subnets() \
.AndReturn({'subnets': self.api_subnets.list()})
self.mox.ReplayAll()
api.network.servers_update_addresses(self.request, servers)
self.assertEqual(self.servers.count(), len(servers))
self.assertEqual([server.id for server in self.servers.list()],
[server.id for server in servers])
no_fip_expected = not router_enabled
# server[0] has one fixed IP and one floating IP
# if router ext isenabled.
self._check_server_address(servers[0], no_fip_expected)
# The expected is also calculated, we examine the result manually once.
addrs = servers[0].addresses['net1']
if router_enabled:
self.assertEqual(2, len(addrs))
self.assertEqual('fixed', addrs[0]['OS-EXT-IPS:type'])
self.assertEqual('floating', addrs[1]['OS-EXT-IPS:type'])
else:
self.assertEqual(1, len(addrs))
self.assertEqual('fixed', addrs[0]['OS-EXT-IPS:type'])
# server[1] has one fixed IP.
self._check_server_address(servers[1], no_fip_expected)
# manual check.
addrs = servers[1].addresses['net2']
self.assertEqual(1, len(addrs))
self.assertEqual('fixed', addrs[0]['OS-EXT-IPS:type'])
# server[2] has no corresponding ports in neutron_data,
# so it should be an empty dict.
self.assertFalse(servers[2].addresses)
@override_settings(OPENSTACK_NEUTRON_NETWORK={'enable_router': True})
def test_servers_update_addresses(self):
self._test_servers_update_addresses()
@override_settings(OPENSTACK_NEUTRON_NETWORK={'enable_router': False})
def test_servers_update_addresses_router_disabled(self):
self._test_servers_update_addresses(router_enabled=False)
class NetworkApiNeutronSecurityGroupTests(NetworkApiNeutronTestBase):
def setUp(self):
super(NetworkApiNeutronSecurityGroupTests, self).setUp()
self.qclient.list_extensions() \
.AndReturn({'extensions': self.api_extensions.list()})
self.sg_dict = dict([(sg['id'], sg['name']) for sg
in self.api_q_secgroups.list()])
@ -398,6 +506,22 @@ class NetworkApiNeutronSecurityGroupTests(NetworkApiNeutronTestBase):
class NetworkApiNeutronFloatingIpTests(NetworkApiNeutronTestBase):
def setUp(self):
super(NetworkApiNeutronFloatingIpTests, self).setUp()
self.qclient.list_extensions() \
.AndReturn({'extensions': self.api_extensions.list()})
@override_settings(OPENSTACK_NEUTRON_NETWORK={'enable_router': True})
def test_floating_ip_supported(self):
self.mox.ReplayAll()
self.assertTrue(api.network.floating_ip_supported(self.request))
@override_settings(OPENSTACK_NEUTRON_NETWORK={'enable_router': False})
def test_floating_ip_supported_false(self):
self.mox.ReplayAll()
self.assertFalse(api.network.floating_ip_supported(self.request))
def test_floating_ip_pools_list(self):
search_opts = {'router:external': True}
ext_nets = [n for n in self.api_networks.list()

View File

@ -52,7 +52,8 @@ class QuotaTests(test.APITestCase):
@test.create_stubs({api.nova: ('server_list',
'flavor_list',
'tenant_quota_get',),
api.network: ('tenant_floating_ip_list',),
api.network: ('tenant_floating_ip_list',
'floating_ip_supported'),
api.base: ('is_service_enabled',),
cinder: ('volume_list', 'volume_snapshot_list',
'tenant_quota_get',)})
@ -68,6 +69,8 @@ class QuotaTests(test.APITestCase):
.AndReturn(self.flavors.list())
api.nova.tenant_quota_get(IsA(http.HttpRequest), '1') \
.AndReturn(self.quotas.first())
api.network.floating_ip_supported(IsA(http.HttpRequest)) \
.AndReturn(True)
api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \
.AndReturn(self.floating_ips.list())
api.nova.server_list(IsA(http.HttpRequest)) \
@ -90,7 +93,8 @@ class QuotaTests(test.APITestCase):
@test.create_stubs({api.nova: ('server_list',
'flavor_list',
'tenant_quota_get',),
api.network: ('tenant_floating_ip_list',),
api.network: ('tenant_floating_ip_list',
'floating_ip_supported'),
api.base: ('is_service_enabled',)})
def test_tenant_quota_usages_without_volume(self):
servers = [s for s in self.servers.list()
@ -104,6 +108,8 @@ class QuotaTests(test.APITestCase):
.AndReturn(self.flavors.list())
api.nova.tenant_quota_get(IsA(http.HttpRequest), '1') \
.AndReturn(self.quotas.first())
api.network.floating_ip_supported(IsA(http.HttpRequest)) \
.AndReturn(True)
api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \
.AndReturn(self.floating_ips.list())
api.nova.server_list(IsA(http.HttpRequest)) \
@ -125,7 +131,8 @@ class QuotaTests(test.APITestCase):
@test.create_stubs({api.nova: ('server_list',
'flavor_list',
'tenant_quota_get',),
api.network: ('tenant_floating_ip_list',),
api.network: ('tenant_floating_ip_list',
'floating_ip_supported'),
api.base: ('is_service_enabled',)})
def test_tenant_quota_usages_no_instances_running(self):
api.base.is_service_enabled(IsA(http.HttpRequest),
@ -136,6 +143,8 @@ class QuotaTests(test.APITestCase):
.AndReturn(self.flavors.list())
api.nova.tenant_quota_get(IsA(http.HttpRequest), '1') \
.AndReturn(self.quotas.first())
api.network.floating_ip_supported(IsA(http.HttpRequest)) \
.AndReturn(True)
api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \
.AndReturn([])
api.nova.server_list(IsA(http.HttpRequest)).AndReturn([[], False])
@ -157,7 +166,8 @@ class QuotaTests(test.APITestCase):
@test.create_stubs({api.nova: ('server_list',
'flavor_list',
'tenant_quota_get',),
api.network: ('tenant_floating_ip_list',),
api.network: ('tenant_floating_ip_list',
'floating_ip_supported'),
api.base: ('is_service_enabled',),
cinder: ('volume_list', 'volume_snapshot_list',
'tenant_quota_get',)})
@ -175,6 +185,8 @@ class QuotaTests(test.APITestCase):
.AndReturn(self.flavors.list())
api.nova.tenant_quota_get(IsA(http.HttpRequest), '1') \
.AndReturn(inf_quota)
api.network.floating_ip_supported(IsA(http.HttpRequest)) \
.AndReturn(True)
api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \
.AndReturn(self.floating_ips.list())
api.nova.server_list(IsA(http.HttpRequest)) \
@ -196,3 +208,44 @@ class QuotaTests(test.APITestCase):
# Compare internal structure of usages to expected.
self.assertEqual(quota_usages.usages, expected_output)
@test.create_stubs({api.nova: ('server_list',
'flavor_list',
'tenant_quota_get',),
api.network: ('tenant_floating_ip_list',
'floating_ip_supported'),
api.base: ('is_service_enabled',),
cinder: ('volume_list', 'volume_snapshot_list',
'tenant_quota_get',)})
def test_tenant_quota_usages_neutron_fip_disabled(self):
servers = [s for s in self.servers.list()
if s.tenant_id == self.request.user.tenant_id]
api.base.is_service_enabled(IsA(http.HttpRequest),
'volume').AndReturn(True)
api.base.is_service_enabled(IsA(http.HttpRequest),
'network').AndReturn(False)
api.nova.flavor_list(IsA(http.HttpRequest)) \
.AndReturn(self.flavors.list())
api.nova.tenant_quota_get(IsA(http.HttpRequest), '1') \
.AndReturn(self.quotas.first())
api.network.floating_ip_supported(IsA(http.HttpRequest)) \
.AndReturn(False)
api.nova.server_list(IsA(http.HttpRequest)) \
.AndReturn([servers, False])
cinder.volume_list(IsA(http.HttpRequest)) \
.AndReturn(self.volumes.list())
cinder.volume_snapshot_list(IsA(http.HttpRequest)) \
.AndReturn(self.snapshots.list())
cinder.tenant_quota_get(IsA(http.HttpRequest), '1') \
.AndReturn(self.cinder_quotas.first())
self.mox.ReplayAll()
quota_usages = quotas.tenant_quota_usages(self.request)
expected_output = self.get_usages()
expected_output['floating_ips']['used'] = 0
expected_output['floating_ips']['available'] = 1
# Compare internal structure of usages to expected.
self.assertEqual(quota_usages.usages, expected_output)

View File

@ -145,7 +145,8 @@ class BaseUsage(object):
neutron_sg_used = (
api.neutron.is_extension_supported(self.request,
'security-group'))
self._get_neutron_usage(self.limits, 'floatingip')
if api.network.floating_ip_supported(self.request):
self._get_neutron_usage(self.limits, 'floatingip')
if neutron_sg_used:
self._get_neutron_usage(self.limits, 'security_group')
# Quotas are an optional extension in Neutron. If it isn't

View File

@ -200,7 +200,12 @@ def tenant_quota_usages(request):
usages.add_quota(quota)
# Get our usages.
floating_ips = network.tenant_floating_ip_list(request)
floating_ips = []
try:
if network.floating_ip_supported(request):
floating_ips = network.tenant_floating_ip_list(request)
except Exception:
pass
flavors = dict([(f.id, f) for f in nova.flavor_list(request)])
instances, has_more = nova.server_list(request)
# Fetch deleted flavors if necessary.