Add a config option to disable Router panel

Neutron setup allows operators not to enable Router feautre
(by not using Router service plugin in neutron.conf) and
some Neutron plugin does not support Router feature.
According to bug 1350639 there is also a case where operators
want to disable Router panel from their policy.
It seems nice we have a option to control Router panel.

This commit add a new option 'enable_router' to OPENSTACK_NEUTRON_NETWORK.
The default is True since router feature is enabled in most deployments
and it is the current default behavior of Horizon.
If this option is False, Router panel will disappear.

blueprint hide-router-panel-by-config
Partial-Bug: #1292022
Closes-Bug: #1350639
Change-Id: If90517bd35c5b18173c54c14a1ba229f97ba4797
This commit is contained in:
Akihiro Motoki 2014-08-09 09:45:56 +09:00
parent 6a8ea3385c
commit 1100169130
7 changed files with 102 additions and 52 deletions

View File

@ -493,6 +493,7 @@ by cinder. Currently only the backup service is available.
Default:: Default::
{ {
'enable_router': True,
'enable_distributed_router': False, 'enable_distributed_router': False,
'enable_lb': False, 'enable_lb': False,
'enable_quotas': False, 'enable_quotas': False,
@ -507,6 +508,16 @@ A dictionary of settings which can be used to enable optional services provided
by Neutron and configure Neutron specific features. The following options are by Neutron and configure Neutron specific features. The following options are
available. available.
``enable_router``:
.. versionadded:: 2014.2(Juno)
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_distributed_router``: ``enable_distributed_router``:

View File

@ -12,6 +12,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from django.conf import settings
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
import horizon import horizon
@ -24,4 +25,6 @@ class Routers(horizon.Panel):
slug = 'routers' slug = 'routers'
permissions = ('openstack.services.network',) permissions = ('openstack.services.network',)
dashboard.Admin.register(Routers) network_config = getattr(settings, 'OPENSTACK_NEUTRON_NETWORK', {})
if network_config.get('enable_router', True):
dashboard.Admin.register(Routers)

View File

@ -16,6 +16,7 @@ import json
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django import http from django import http
import django.test
from mox import IsA # noqa from mox import IsA # noqa
@ -34,6 +35,17 @@ class NetworkTopologyTests(test.TestCase):
'router_list', 'router_list',
'port_list')}) 'port_list')})
def test_json_view(self): def test_json_view(self):
self._test_json_view()
@django.test.utils.override_settings(
OPENSTACK_NEUTRON_NETWORK={'enable_router': False})
@test.create_stubs({api.nova: ('server_list',),
api.neutron: ('network_list_for_tenant',
'port_list')})
def test_json_view_router_disabled(self):
self._test_json_view(router_enable=False)
def _test_json_view(self, router_enable=True):
api.nova.server_list( api.nova.server_list(
IsA(http.HttpRequest)).AndReturn([self.servers.list(), False]) IsA(http.HttpRequest)).AndReturn([self.servers.list(), False])
tenant_networks = [net for net in self.networks.list() tenant_networks = [net for net in self.networks.list()
@ -43,17 +55,19 @@ class NetworkTopologyTests(test.TestCase):
api.neutron.network_list_for_tenant( api.neutron.network_list_for_tenant(
IsA(http.HttpRequest), IsA(http.HttpRequest),
self.tenant.id).AndReturn(tenant_networks) self.tenant.id).AndReturn(tenant_networks)
api.neutron.network_list( if router_enable:
IsA(http.HttpRequest), api.neutron.network_list(
**{'router:external': True}).AndReturn(external_networks) IsA(http.HttpRequest),
**{'router:external': True}).AndReturn(external_networks)
# router1 : gateway port not in the port list # router1 : gateway port not in the port list
# router2 : no gateway port # router2 : no gateway port
# router3 : gateway port included in port list # router3 : gateway port included in port list
routers = self.routers.list() + self.routers_with_rules.list() routers = self.routers.list() + self.routers_with_rules.list()
api.neutron.router_list( if router_enable:
IsA(http.HttpRequest), api.neutron.router_list(
tenant_id=self.tenant.id).AndReturn(routers) IsA(http.HttpRequest),
tenant_id=self.tenant.id).AndReturn(routers)
api.neutron.port_list( api.neutron.port_list(
IsA(http.HttpRequest)).AndReturn(self.ports.list()) IsA(http.HttpRequest)).AndReturn(self.ports.list())
@ -79,24 +93,29 @@ class NetworkTopologyTests(test.TestCase):
# rotuers # rotuers
# result_router_urls = [(router['id'], router['url']) # result_router_urls = [(router['id'], router['url'])
# for router in data['routers']] # for router in data['routers']]
expect_router_urls = [ if router_enable:
{'id': router.id, expect_router_urls = [
'external_gateway_info': {'id': router.id,
router.external_gateway_info, 'external_gateway_info':
'name': router.name, router.external_gateway_info,
'status': router.status, 'name': router.name,
'url': '/project/routers/%s/' % router.id} 'status': router.status,
for router in routers] 'url': '/project/routers/%s/' % router.id}
self.assertEqual(expect_router_urls, data['routers']) for router in routers]
self.assertEqual(expect_router_urls, data['routers'])
else:
self.assertFalse(data['routers'])
# networks # networks
expect_net_urls = [{'id': net.id, expect_net_urls = []
'url': None, if router_enable:
'name': net.name, expect_net_urls += [{'id': net.id,
'router:external': net.router__external, 'url': None,
'subnets': [{'cidr': subnet.cidr} 'name': net.name,
for subnet in net.subnets]} 'router:external': net.router__external,
for net in external_networks] 'subnets': [{'cidr': subnet.cidr}
for subnet in net.subnets]}
for net in external_networks]
expect_net_urls += [{'id': net.id, expect_net_urls += [{'id': net.id,
'url': '/project/networks/%s/detail' % net.id, 'url': '/project/networks/%s/detail' % net.id,
'name': net.name, 'name': net.name,
@ -119,12 +138,13 @@ class NetworkTopologyTests(test.TestCase):
'status': port.status, 'status': port.status,
'url': '/project/networks/ports/%s/detail' % port.id} 'url': '/project/networks/ports/%s/detail' % port.id}
for port in self.ports.list()] for port in self.ports.list()]
# fake port for router1 gateway (router1 on ext_net) if router_enable:
router1 = routers[0] # fake port for router1 gateway (router1 on ext_net)
ext_net = external_networks[0] router1 = routers[0]
expect_port_urls.append( ext_net = external_networks[0]
{'id': 'gateway%s' % ext_net.id, expect_port_urls.append(
'device_id': router1.id, {'id': 'gateway%s' % ext_net.id,
'network_id': ext_net.id, 'device_id': router1.id,
'fixed_ips': []}) 'network_id': ext_net.id,
'fixed_ips': []})
self.assertEqual(expect_port_urls, data['ports']) self.assertEqual(expect_port_urls, data['ports'])

View File

@ -103,17 +103,25 @@ class NetworkTopologyView(TemplateView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(NetworkTopologyView, self).get_context_data(**kwargs) context = super(NetworkTopologyView, self).get_context_data(**kwargs)
network_config = getattr(settings, 'OPENSTACK_NEUTRON_NETWORK', {})
context['launch_instance_allowed'] = self._has_permission( context['launch_instance_allowed'] = self._has_permission(
(("compute", "compute:create"),)) (("compute", "compute:create"),))
context['create_network_allowed'] = self._has_permission( context['create_network_allowed'] = self._has_permission(
(("network", "create_network"),)) (("network", "create_network"),))
context['create_router_allowed'] = self._has_permission( context['create_router_allowed'] = (
(("network", "create_router"),)) network_config.get('enable_router', True) and
self._has_permission((("network", "create_router"),)))
return context return context
class JSONView(View): class JSONView(View):
@property
def is_router_enabled(self):
network_config = getattr(settings, 'OPENSTACK_NEUTRON_NETWORK', {})
return network_config.get('enable_router', True)
def add_resource_url(self, view, resources): def add_resource_url(self, view, resources):
tenant_id = self.request.user.tenant_id tenant_id = self.request.user.tenant_id
for resource in resources: for resource in resources:
@ -170,32 +178,35 @@ class JSONView(View):
networks) networks)
# Add public networks to the networks list # Add public networks to the networks list
try: if self.is_router_enabled:
neutron_public_networks = api.neutron.network_list(
request,
**{'router:external': True})
except Exception:
neutron_public_networks = []
my_network_ids = [net['id'] for net in networks]
for publicnet in neutron_public_networks:
if publicnet.id in my_network_ids:
continue
try: try:
subnets = [{'cidr': subnet.cidr} neutron_public_networks = api.neutron.network_list(
for subnet in publicnet.subnets] request,
**{'router:external': True})
except Exception: except Exception:
subnets = [] neutron_public_networks = []
networks.append({ my_network_ids = [net['id'] for net in networks]
'name': publicnet.name, for publicnet in neutron_public_networks:
'id': publicnet.id, if publicnet.id in my_network_ids:
'subnets': subnets, continue
'router:external': publicnet['router:external']}) try:
subnets = [{'cidr': subnet.cidr}
for subnet in publicnet.subnets]
except Exception:
subnets = []
networks.append({
'name': publicnet.name,
'id': publicnet.id,
'subnets': subnets,
'router:external': publicnet['router:external']})
return sorted(networks, return sorted(networks,
key=lambda x: x.get('router:external'), key=lambda x: x.get('router:external'),
reverse=True) reverse=True)
def _get_routers(self, request): def _get_routers(self, request):
if not self.is_router_enabled:
return []
try: try:
neutron_routers = api.neutron.router_list( neutron_routers = api.neutron.router_list(
request, request,

View File

@ -12,6 +12,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from django.conf import settings
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
import horizon import horizon
@ -24,4 +25,6 @@ class Routers(horizon.Panel):
slug = 'routers' slug = 'routers'
permissions = ('openstack.services.network',) permissions = ('openstack.services.network',)
dashboard.Project.register(Routers) network_config = getattr(settings, 'OPENSTACK_NEUTRON_NETWORK', {})
if network_config.get('enable_router', True):
dashboard.Project.register(Routers)

View File

@ -178,6 +178,7 @@ OPENSTACK_CINDER_FEATURES = {
# services provided by neutron. Options currently available are load # services provided by neutron. Options currently available are load
# balancer service, security groups, quotas, VPN service. # balancer service, security groups, quotas, VPN service.
OPENSTACK_NEUTRON_NETWORK = { OPENSTACK_NEUTRON_NETWORK = {
'enable_router': True,
'enable_lb': False, 'enable_lb': False,
'enable_firewall': False, 'enable_firewall': False,
'enable_quotas': True, 'enable_quotas': True,

View File

@ -104,6 +104,7 @@ OPENSTACK_CINDER_FEATURES = {
} }
OPENSTACK_NEUTRON_NETWORK = { OPENSTACK_NEUTRON_NETWORK = {
'enable_router': True,
'enable_lb': True, 'enable_lb': True,
'enable_firewall': True, 'enable_firewall': True,
'enable_quotas': False, # Enabled in specific tests only 'enable_quotas': False, # Enabled in specific tests only