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",