From f06e401adf8a787f7cf5c3e1f00d7bfa36d2dda3 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Thu, 4 Sep 2014 15:39:30 +0900 Subject: [PATCH] Add HA mode support for Neutron router HA (high availability) mode support is one of the important topics in Neutron Juno, and this patch adds HA router mode support to Horizon. This commit also changes the default value of enable_distributed_router in the example local_settings.py to False. In Juno release of Neutron, the distributed router and L3 HA mode cannot be enabled at the same time and only L3-agent deployment with L3 Router service plugin support both features. Thus I believe it is reasonable to make both options default to False to avoid unnecessary confusions to operators. Closes-Bug: #1370110 Change-Id: I77b0292b761f08b4580846f6d58443f7df9a1f6b --- doc/source/topics/settings.rst | 14 +++ openstack_dashboard/api/neutron.py | 105 ++++++++++++++---- openstack_dashboard/conf/neutron_policy.json | 3 + .../templates/routers/_detail_overview.html | 4 + .../dashboards/project/routers/forms.py | 36 ++++-- .../dashboards/project/routers/tables.py | 8 +- .../templates/routers/_detail_overview.html | 4 + .../dashboards/project/routers/tests.py | 88 ++++++++++----- .../dashboards/project/routers/views.py | 8 +- .../local/local_settings.py.example | 3 +- .../test/api_tests/neutron_tests.py | 60 ++++++++-- .../test/test_data/neutron_data.py | 4 + 12 files changed, 269 insertions(+), 68 deletions(-) diff --git a/doc/source/topics/settings.rst b/doc/source/topics/settings.rst index f2dfa04e5d..9e3c4d6b2e 100644 --- a/doc/source/topics/settings.rst +++ b/doc/source/topics/settings.rst @@ -474,6 +474,7 @@ Default:: { 'enable_router': True, 'enable_distributed_router': False, + 'enable_ha_router': False, 'enable_lb': True, 'enable_quotas': False, 'enable_firewall': True, @@ -512,6 +513,19 @@ when your Neutron plugin (like ML2 plugin) supports DVR feature, DVR feature depends on l3-agent configuration, so deployers should set this option appropriately depending on your deployment. +``enable_ha_router``: + +.. versionadded:: 2014.2(Juno) + +Default: ``False`` + +Enable or disable HA (High Availability) mode in Neutron virtual router +in the Router panel. For the HA router mode to be enabled, this option needs +to be set to True and your Neutron deployment must support HA router mode. +Even when your Neutron plugin (like ML2 plugin) supports HA router mode, +the feature depends on l3-agent configuration, so deployers should set this +option appropriately depending on your deployment. + ``enable_lb``: .. versionadded:: 2013.1(Grizzly) diff --git a/openstack_dashboard/api/neutron.py b/openstack_dashboard/api/neutron.py index b0ad7d7dcb..1295f76e33 100644 --- a/openstack_dashboard/api/neutron.py +++ b/openstack_dashboard/api/neutron.py @@ -943,31 +943,90 @@ def is_port_profiles_supported(): return True -def get_dvr_permission(request, operation): - """Check if "distributed" field can be displayed. +# FEATURE_MAP is used to define: +# - related neutron extension name (key: "extension") +# - corresponding dashboard config (key: "config") +# - RBAC policies (key: "poclies") +# If a key is not contained, the corresponding permission check is skipped. +FEATURE_MAP = { + 'dvr': { + 'extension': 'dvr', + 'config': { + 'name': 'enable_distributed_router', + 'default': False, + }, + 'policies': { + 'get': 'get_router:distributed', + 'create': 'create_router:distributed', + 'update': 'update_router:distributed', + } + }, + 'l3-ha': { + 'extension': 'l3-ha', + 'config': {'name': 'enable_ha_router', + 'default': False}, + 'policies': { + 'get': 'get_router:ha', + 'create': 'create_router:ha', + 'update': 'update_router:ha', + } + }, +} + + +def get_feature_permission(request, feature, operation=None): + """Check if a feature-specific field can be displayed. + + This method check a permission for a feature-specific field. + Such field is usually provided through Neutron extension. :param request: Request Object - :param operation: Operation type. The valid value is "get" or "create" + :param feature: feature name defined in FEATURE_MAP + :param operation (optional): Operation type. The valid value should be + defined in FEATURE_MAP[feature]['policies'] + It must be specified if FEATURE_MAP[feature] has 'policies'. """ network_config = getattr(settings, 'OPENSTACK_NEUTRON_NETWORK', {}) - if not network_config.get('enable_distributed_router', False): - return False + feature_info = FEATURE_MAP.get(feature) + if not feature_info: + # Translators: Only used inside Horizon code and invisible to users + raise ValueError(_("The requested feature '%(feature)s' is unknown. " + "Please make sure to specify a feature defined " + "in FEATURE_MAP.")) + + # Check dashboard settings + feature_config = feature_info.get('config') + if feature_config: + if not network_config.get(feature_config['name'], + feature_config['default']): + return False + + # Check policy + feature_policies = feature_info.get('policies') policy_check = getattr(settings, "POLICY_CHECK_FUNCTION", None) - allowed_operations = ("get", "create", "update") - if operation not in allowed_operations: - raise ValueError(_("The 'operation' parameter for get_dvr_permission " - "is invalid. It should be one of %s") - % ' '.join(allowed_operations)) - role = (("network", "%s_router:distributed" % operation),) - if policy_check: - has_permission = policy.check(role, request) - else: - has_permission = True - if not has_permission: - return False - try: - return is_extension_supported(request, 'dvr') - except Exception: - msg = _('Failed to check Neutron "dvr" extension is not supported') - LOG.info(msg) - return False + if feature_policies and policy_check: + policy_name = feature_policies.get(operation) + if not policy_name: + # Translators: Only used inside Horizon code and invisible to users + raise ValueError(_("The 'operation' parameter for " + "get_feature_permission '%(feature)s' " + "is invalid. It should be one of %(allowed)s") + % {'feature': feature, + 'allowed': ' '.join(feature_policies.keys())}) + role = (('network', policy_name),) + if not policy.check(role, request): + return False + + # Check if a required extension is enabled + feature_extension = feature_info.get('extension') + if feature_extension: + try: + return is_extension_supported(request, feature_extension) + except Exception: + msg = (_("Failed to check Neutron '%s' extension is not supported") + % feature_extension) + LOG.info(msg) + return False + + # If all checks are passed, now a given feature is allowed. + return True diff --git a/openstack_dashboard/conf/neutron_policy.json b/openstack_dashboard/conf/neutron_policy.json index 2ead473f2e..79f0b6b33f 100644 --- a/openstack_dashboard/conf/neutron_policy.json +++ b/openstack_dashboard/conf/neutron_policy.json @@ -148,6 +148,9 @@ "get_router:distributed": "rule:admin_only", "create_router:distributed": "rule:admin_only", "update_router:distributed": "rule:admin_only", + "get_router:ha": "rule:admin_only", + "create_router:ha": "rule:admin_only", + "update_router:ha": "rule:admin_only", "create_floatingip": "rule:regular_user", "update_floatingip": "rule:admin_or_owner", diff --git a/openstack_dashboard/dashboards/admin/routers/templates/routers/_detail_overview.html b/openstack_dashboard/dashboards/admin/routers/templates/routers/_detail_overview.html index 6bf3ecd68c..60fc8440d0 100644 --- a/openstack_dashboard/dashboards/admin/routers/templates/routers/_detail_overview.html +++ b/openstack_dashboard/dashboards/admin/routers/templates/routers/_detail_overview.html @@ -18,6 +18,10 @@
{% trans "Distributed" %}
{{ router.distributed|yesno|capfirst }}
{% endif %} + {% if ha_supported %} +
{% trans "High Availability Mode" %}
+
{{ router.ha|yesno|capfirst }}
+ {% endif %} {% if router.external_gateway_info %}
{% trans "External Gateway Information" %}
{% trans "Connected External Network" %}: diff --git a/openstack_dashboard/dashboards/project/routers/forms.py b/openstack_dashboard/dashboards/project/routers/forms.py index adb260ca50..f4e77f2d9c 100644 --- a/openstack_dashboard/dashboards/project/routers/forms.py +++ b/openstack_dashboard/dashboards/project/routers/forms.py @@ -34,26 +34,38 @@ LOG = logging.getLogger(__name__) class CreateForm(forms.SelfHandlingForm): name = forms.CharField(max_length=255, label=_("Router Name")) mode = forms.ChoiceField(label=_("Router Type")) + ha = forms.ChoiceField(label=_("High Availability Mode")) failure_url = 'horizon:project:routers:index' def __init__(self, request, *args, **kwargs): super(CreateForm, self).__init__(request, *args, **kwargs) - self.dvr_enabled = api.neutron.get_dvr_permission(self.request, - "create") - if self.dvr_enabled: + self.dvr_allowed = api.neutron.get_feature_permission(self.request, + "dvr", "create") + if self.dvr_allowed: mode_choices = [('server_default', _('Use Server Default')), ('centralized', _('Centralized')), ('distributed', _('Distributed'))] self.fields['mode'].choices = mode_choices else: - self.fields['mode'].widget = forms.HiddenInput() - self.fields['mode'].required = False + del self.fields['mode'] + + self.ha_allowed = api.neutron.get_feature_permission(self.request, + "l3-ha", "create") + if self.ha_allowed: + ha_choices = [('server_default', _('Use Server Default')), + ('enabled', _('Enable HA mode')), + ('disabled', _('Disable HA mode'))] + self.fields['ha'].choices = ha_choices + else: + del self.fields['ha'] def handle(self, request, data): try: params = {'name': data['name']} - if (self.dvr_enabled and data['mode'] != 'server_default'): + if (self.dvr_allowed and data['mode'] != 'server_default'): params['distributed'] = (data['mode'] == 'distributed') + if (self.ha_allowed and data['ha'] != 'server_default'): + params['ha'] = (data['ha'] == 'enabled') router = api.neutron.router_create(request, **params) message = _('Router %s was successfully created.') % data['name'] messages.success(request, message) @@ -77,13 +89,14 @@ class UpdateForm(forms.SelfHandlingForm): router_id = forms.CharField(label=_("ID"), widget=forms.HiddenInput()) mode = forms.ChoiceField(label=_("Router Type")) + ha = forms.BooleanField(label=_("High Availability Mode"), required=False) redirect_url = reverse_lazy('horizon:project:routers:index') def __init__(self, request, *args, **kwargs): super(UpdateForm, self).__init__(request, *args, **kwargs) - self.dvr_allowed = api.neutron.get_dvr_permission(self.request, - "update") + self.dvr_allowed = api.neutron.get_feature_permission(self.request, + "dvr", "update") if not self.dvr_allowed: del self.fields['mode'] elif kwargs.get('initial', {}).get('mode') == 'distributed': @@ -98,12 +111,19 @@ class UpdateForm(forms.SelfHandlingForm): ('distributed', _('Distributed'))] self.fields['mode'].choices = mode_choices + self.ha_allowed = api.neutron.get_feature_permission(self.request, + "l3-ha", "update") + if not self.ha_allowed: + del self.fields['ha'] + def handle(self, request, data): try: params = {'admin_state_up': (data['admin_state'] == 'True'), 'name': data['name']} if self.dvr_allowed: params['distributed'] = (data['mode'] == 'distributed') + if self.ha_allowed: + params['ha'] = data['ha'] router = api.neutron.router_update(request, data['router_id'], **params) msg = _('Router %s was successfully updated.') % data['name'] diff --git a/openstack_dashboard/dashboards/project/routers/tables.py b/openstack_dashboard/dashboards/project/routers/tables.py index 7924f06001..3a169f7755 100644 --- a/openstack_dashboard/dashboards/project/routers/tables.py +++ b/openstack_dashboard/dashboards/project/routers/tables.py @@ -159,6 +159,10 @@ class RoutersTable(tables.DataTable): distributed = tables.Column("distributed", filters=(filters.yesno, filters.capfirst), verbose_name=_("Distributed")) + ha = tables.Column("ha", + filters=(filters.yesno, filters.capfirst), + # Translators: High Availability mode of Neutron router + verbose_name=_("HA mode")) ext_net = tables.Column(get_external_network, verbose_name=_("External Network")) @@ -168,8 +172,10 @@ class RoutersTable(tables.DataTable): data=data, needs_form_wrapper=needs_form_wrapper, **kwargs) - if not api.neutron.get_dvr_permission(request, "get"): + if not api.neutron.get_feature_permission(request, "dvr", "get"): del self.columns["distributed"] + if not api.neutron.get_feature_permission(request, "l3-ha", "get"): + del self.columns["ha"] def get_object_display(self, obj): return obj.name diff --git a/openstack_dashboard/dashboards/project/routers/templates/routers/_detail_overview.html b/openstack_dashboard/dashboards/project/routers/templates/routers/_detail_overview.html index 4816322cfc..9c1721dc5b 100644 --- a/openstack_dashboard/dashboards/project/routers/templates/routers/_detail_overview.html +++ b/openstack_dashboard/dashboards/project/routers/templates/routers/_detail_overview.html @@ -16,6 +16,10 @@
{% trans "Distributed" %}
{{ router.distributed|yesno|capfirst }}
{% endif %} + {% if ha_supported %} +
{% trans "High Availability Mode" %}
+
{{ router.ha|yesno|capfirst }}
+ {% endif %} {% if router.external_gateway_info %}
{% trans "External Gateway Information" %}
{% trans "Connected External Network" %}: diff --git a/openstack_dashboard/dashboards/project/routers/tests.py b/openstack_dashboard/dashboards/project/routers/tests.py index 5706de0a3a..3d4c30ed9e 100644 --- a/openstack_dashboard/dashboards/project/routers/tests.py +++ b/openstack_dashboard/dashboards/project/routers/tests.py @@ -133,10 +133,14 @@ class RouterActionTests(test.TestCase): DETAIL_PATH = 'horizon:%s:routers:detail' % DASHBOARD @test.create_stubs({api.neutron: ('router_create', - 'get_dvr_permission',)}) + 'get_feature_permission',)}) def test_router_create_post(self): router = self.routers.first() - api.neutron.get_dvr_permission(IsA(http.HttpRequest), "create")\ + api.neutron.get_feature_permission(IsA(http.HttpRequest), + "dvr", "create")\ + .AndReturn(False) + api.neutron.get_feature_permission(IsA(http.HttpRequest), + "l3-ha", "create")\ .AndReturn(False) api.neutron.router_create(IsA(http.HttpRequest), name=router.name)\ .AndReturn(router) @@ -150,17 +154,22 @@ class RouterActionTests(test.TestCase): self.assertRedirectsNoFollow(res, self.INDEX_URL) @test.create_stubs({api.neutron: ('router_create', - 'get_dvr_permission',)}) + 'get_feature_permission',)}) def test_router_create_post_mode_server_default(self): router = self.routers.first() - api.neutron.get_dvr_permission(IsA(http.HttpRequest), "create")\ + api.neutron.get_feature_permission(IsA(http.HttpRequest), + "dvr", "create")\ + .AndReturn(True) + api.neutron.get_feature_permission(IsA(http.HttpRequest), + "l3-ha", "create")\ .AndReturn(True) api.neutron.router_create(IsA(http.HttpRequest), name=router.name)\ .AndReturn(router) self.mox.ReplayAll() form_data = {'name': router.name, - 'mode': 'server_default'} + 'mode': 'server_default', + 'ha': 'server_default'} url = reverse('horizon:%s:routers:create' % self.DASHBOARD) res = self.client.post(url, form_data) @@ -168,19 +177,25 @@ class RouterActionTests(test.TestCase): self.assertRedirectsNoFollow(res, self.INDEX_URL) @test.create_stubs({api.neutron: ('router_create', - 'get_dvr_permission',)}) - def test_dvr_router_create_post(self): + 'get_feature_permission',)}) + def test_dvr_ha_router_create_post(self): router = self.routers.first() - api.neutron.get_dvr_permission(IsA(http.HttpRequest), "create")\ + api.neutron.get_feature_permission(IsA(http.HttpRequest), + "dvr", "create")\ + .MultipleTimes().AndReturn(True) + api.neutron.get_feature_permission(IsA(http.HttpRequest), + "l3-ha", "create")\ .MultipleTimes().AndReturn(True) param = {'name': router.name, - 'distributed': True} + 'distributed': True, + 'ha': True} api.neutron.router_create(IsA(http.HttpRequest), **param)\ .AndReturn(router) self.mox.ReplayAll() form_data = {'name': router.name, - 'mode': 'distributed'} + 'mode': 'distributed', + 'ha': 'enabled'} url = reverse('horizon:%s:routers:create' % self.DASHBOARD) res = self.client.post(url, form_data) @@ -188,11 +203,15 @@ class RouterActionTests(test.TestCase): self.assertRedirectsNoFollow(res, self.INDEX_URL) @test.create_stubs({api.neutron: ('router_create', - 'get_dvr_permission',)}) + 'get_feature_permission',)}) def test_router_create_post_exception_error_case_409(self): router = self.routers.first() - api.neutron.get_dvr_permission(IsA(http.HttpRequest), "create")\ + api.neutron.get_feature_permission(IsA(http.HttpRequest), + "dvr", "create")\ .MultipleTimes().AndReturn(False) + api.neutron.get_feature_permission(IsA(http.HttpRequest), + "l3-ha", "create")\ + .AndReturn(False) self.exceptions.neutron.status_code = 409 api.neutron.router_create(IsA(http.HttpRequest), name=router.name)\ .AndRaise(self.exceptions.neutron) @@ -206,10 +225,14 @@ class RouterActionTests(test.TestCase): self.assertRedirectsNoFollow(res, self.INDEX_URL) @test.create_stubs({api.neutron: ('router_create', - 'get_dvr_permission',)}) + 'get_feature_permission',)}) def test_router_create_post_exception_error_case_non_409(self): router = self.routers.first() - api.neutron.get_dvr_permission(IsA(http.HttpRequest), "create")\ + api.neutron.get_feature_permission(IsA(http.HttpRequest), + "dvr", "create")\ + .MultipleTimes().AndReturn(False) + api.neutron.get_feature_permission(IsA(http.HttpRequest), + "l3-ha", "create")\ .MultipleTimes().AndReturn(False) self.exceptions.neutron.status_code = 999 api.neutron.router_create(IsA(http.HttpRequest), name=router.name)\ @@ -224,15 +247,20 @@ class RouterActionTests(test.TestCase): self.assertRedirectsNoFollow(res, self.INDEX_URL) @test.create_stubs({api.neutron: ('router_get', - 'get_dvr_permission')}) + 'get_feature_permission')}) def _test_router_update_get(self, dvr_enabled=False, - current_dvr=False): + current_dvr=False, + ha_enabled=False): router = [r for r in self.routers.list() if r.distributed == current_dvr][0] api.neutron.router_get(IsA(http.HttpRequest), router.id)\ .AndReturn(router) - api.neutron.get_dvr_permission(IsA(http.HttpRequest), "update")\ + api.neutron.get_feature_permission(IsA(http.HttpRequest), + "dvr", "update")\ .AndReturn(dvr_enabled) + api.neutron.get_feature_permission(IsA(http.HttpRequest), + "l3-ha", "update")\ + .AndReturn(ha_enabled) self.mox.ReplayAll() url = reverse('horizon:%s:routers:update' % self.DASHBOARD, @@ -276,10 +304,14 @@ class RouterActionTests(test.TestCase): @test.create_stubs({api.neutron: ('router_get', 'router_update', - 'get_dvr_permission')}) - def test_router_update_post_dvr_disabled(self): + 'get_feature_permission')}) + def test_router_update_post_dvr_ha_disabled(self): router = self.routers.first() - api.neutron.get_dvr_permission(IsA(http.HttpRequest), "update")\ + api.neutron.get_feature_permission(IsA(http.HttpRequest), + "dvr", "update")\ + .AndReturn(False) + api.neutron.get_feature_permission(IsA(http.HttpRequest), + "l3-ha", "update")\ .AndReturn(False) api.neutron.router_update(IsA(http.HttpRequest), router.id, name=router.name, @@ -300,15 +332,20 @@ class RouterActionTests(test.TestCase): @test.create_stubs({api.neutron: ('router_get', 'router_update', - 'get_dvr_permission')}) - def test_router_update_post_dvr_enabled(self): + 'get_feature_permission')}) + def test_router_update_post_dvr_ha_enabled(self): router = self.routers.first() - api.neutron.get_dvr_permission(IsA(http.HttpRequest), "update")\ + api.neutron.get_feature_permission(IsA(http.HttpRequest), + "dvr", "update")\ + .AndReturn(True) + api.neutron.get_feature_permission(IsA(http.HttpRequest), + "l3-ha", "update")\ .AndReturn(True) api.neutron.router_update(IsA(http.HttpRequest), router.id, name=router.name, admin_state_up=router.admin_state_up, - distributed=True)\ + distributed=True, + ha=True)\ .AndReturn(router) api.neutron.router_get(IsA(http.HttpRequest), router.id)\ .AndReturn(router) @@ -317,7 +354,8 @@ class RouterActionTests(test.TestCase): form_data = {'router_id': router.id, 'name': router.name, 'admin_state': router.admin_state_up, - 'mode': 'distributed'} + 'mode': 'distributed', + 'ha': True} url = reverse('horizon:%s:routers:update' % self.DASHBOARD, args=[router.id]) res = self.client.post(url, form_data) diff --git a/openstack_dashboard/dashboards/project/routers/views.py b/openstack_dashboard/dashboards/project/routers/views.py index bc82fe1018..62ed37a566 100644 --- a/openstack_dashboard/dashboards/project/routers/views.py +++ b/openstack_dashboard/dashboards/project/routers/views.py @@ -126,8 +126,10 @@ class DetailView(tabs.TabbedTableView): def get_context_data(self, **kwargs): context = super(DetailView, self).get_context_data(**kwargs) context["router"] = self._get_data() - context['dvr_supported'] = api.neutron.get_dvr_permission(self.request, - "get") + context['dvr_supported'] = api.neutron.get_feature_permission( + self.request, "dvr", "get") + context['ha_supported'] = api.neutron.get_feature_permission( + self.request, "l3-ha", "get") return context def get(self, request, *args, **kwargs): @@ -170,4 +172,6 @@ class UpdateView(forms.ModalFormView): if hasattr(router, 'distributed'): initial['mode'] = ('distributed' if router.distributed else 'centralized') + if hasattr(router, 'ha'): + initial['ha'] = router.ha return initial diff --git a/openstack_dashboard/local/local_settings.py.example b/openstack_dashboard/local/local_settings.py.example index 58597a5861..08766cfb7a 100644 --- a/openstack_dashboard/local/local_settings.py.example +++ b/openstack_dashboard/local/local_settings.py.example @@ -182,7 +182,8 @@ OPENSTACK_NEUTRON_NETWORK = { 'enable_router': True, 'enable_quotas': True, 'enable_ipv6': True, - 'enable_distributed_router': True, + 'enable_distributed_router': False, + 'enable_ha_router': False, 'enable_lb': True, 'enable_firewall': True, 'enable_vpn': True, diff --git a/openstack_dashboard/test/api_tests/neutron_tests.py b/openstack_dashboard/test/api_tests/neutron_tests.py index ff88dcf491..015518a011 100644 --- a/openstack_dashboard/test/api_tests/neutron_tests.py +++ b/openstack_dashboard/test/api_tests/neutron_tests.py @@ -283,6 +283,11 @@ class NeutronApiTests(test.APITestCase): self.assertFalse( api.neutron.is_extension_supported(self.request, 'doesntexist')) + # NOTE(amotoki): "dvr" permission tests check most of + # get_feature_permission features. + # These tests are not specific to "dvr" extension. + # Please be careful if you drop "dvr" extension in future. + @override_settings(OPENSTACK_NEUTRON_NETWORK={'enable_distributed_router': True}, POLICY_CHECK_FUNCTION=None) @@ -295,10 +300,11 @@ class NeutronApiTests(test.APITestCase): .AndReturn({'extensions': extensions}) self.mox.ReplayAll() self.assertEqual(dvr_enabled, - api.neutron.get_dvr_permission(self.request, 'get')) + api.neutron.get_feature_permission(self.request, + 'dvr', 'get')) def test_get_dvr_permission_dvr_supported(self): - self._test_get_dvr_permission_dvr_supported(dvr_enabled=True, ) + self._test_get_dvr_permission_dvr_supported(dvr_enabled=True) def test_get_dvr_permission_dvr_not_supported(self): self._test_get_dvr_permission_dvr_supported(dvr_enabled=False) @@ -320,8 +326,8 @@ class NeutronApiTests(test.APITestCase): .AndReturn({'extensions': self.api_extensions.list()}) self.mox.ReplayAll() self.assertEqual(policy_check_allowed, - api.neutron.get_dvr_permission(self.request, - operation)) + api.neutron.get_feature_permission(self.request, + 'dvr', operation)) def test_get_dvr_permission_with_policy_check_allowed(self): self._test_get_dvr_permission_with_policy_check(True, "get") @@ -338,11 +344,49 @@ class NeutronApiTests(test.APITestCase): @override_settings(OPENSTACK_NEUTRON_NETWORK={'enable_distributed_router': False}) def test_get_dvr_permission_dvr_disabled_by_config(self): - self.assertFalse(api.neutron.get_dvr_permission(self.request, 'get')) + self.assertFalse(api.neutron.get_feature_permission(self.request, + 'dvr', 'get')) @override_settings(OPENSTACK_NEUTRON_NETWORK={'enable_distributed_router': - True}) + True}, + POLICY_CHECK_FUNCTION=policy.check) def test_get_dvr_permission_dvr_unsupported_operation(self): self.assertRaises(ValueError, - api.neutron.get_dvr_permission, - self.request, 'unSupported') + api.neutron.get_feature_permission, + self.request, 'dvr', 'unSupported') + + @override_settings(OPENSTACK_NEUTRON_NETWORK={}) + def test_get_dvr_permission_dvr_default_config(self): + self.assertFalse(api.neutron.get_feature_permission(self.request, + 'dvr', 'get')) + + @override_settings(OPENSTACK_NEUTRON_NETWORK={}) + def test_get_dvr_permission_router_ha_default_config(self): + self.assertFalse(api.neutron.get_feature_permission(self.request, + 'l3-ha', 'get')) + + # NOTE(amotoki): Most of get_feature_permission are checked by "dvr" check + # above. l3-ha check only checks l3-ha specific code. + + @override_settings(OPENSTACK_NEUTRON_NETWORK={'enable_ha_router': True}, + POLICY_CHECK_FUNCTION=policy.check) + def _test_get_router_ha_permission_with_policy_check(self, ha_enabled): + self.mox.StubOutWithMock(policy, 'check') + role = (("network", "create_router:ha"),) + policy.check(role, self.request).AndReturn(True) + neutronclient = self.stub_neutronclient() + if ha_enabled: + extensions = self.api_extensions.list() + else: + extensions = {} + neutronclient.list_extensions().AndReturn({'extensions': extensions}) + self.mox.ReplayAll() + self.assertEqual(ha_enabled, + api.neutron.get_feature_permission(self.request, + 'l3-ha', 'create')) + + def test_get_router_ha_permission_with_l3_ha_extension(self): + self._test_get_router_ha_permission_with_policy_check(True) + + def test_get_router_ha_permission_without_l3_ha_extension(self): + self._test_get_router_ha_permission_with_policy_check(False) diff --git a/openstack_dashboard/test/test_data/neutron_data.py b/openstack_dashboard/test/test_data/neutron_data.py index a9ed60c573..d3b568b8c2 100644 --- a/openstack_dashboard/test/test_data/neutron_data.py +++ b/openstack_dashboard/test/test_data/neutron_data.py @@ -642,10 +642,14 @@ def data(TEST): "alias": "dvr", "description": "Enables configuration of Distributed Virtual Routers."} + extension_5 = {"name": "HA Router extension", + "alias": "l3-ha", + "description": "Add HA capability to routers."} TEST.api_extensions.add(extension_1) TEST.api_extensions.add(extension_2) TEST.api_extensions.add(extension_3) TEST.api_extensions.add(extension_4) + TEST.api_extensions.add(extension_5) # 1st agent. agent_dict = {"binary": "neutron-openvswitch-agent",