Move Security Groups into its own panel
This patch moves the Security Groups tab from the Access and Security panel into its own panel under the Network panel group. As this is the last tab in Access and Security, that panel is also removed by this patch. Change-Id: Id29c7ce635d46383742aec140def265d4b249aa5 Implements: blueprint reorganise-access-and-security
This commit is contained in:
parent
99849ad88f
commit
e2698063e2
@ -1,55 +0,0 @@
|
||||
# Copyright 2012 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Copyright 2012 Nebula, Inc.
|
||||
# Copyright 2012 OpenStack Foundation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import tabs
|
||||
|
||||
from neutronclient.common import exceptions as neutron_exc
|
||||
|
||||
from openstack_dashboard.api import network
|
||||
from openstack_dashboard.dashboards.project.access_and_security.\
|
||||
security_groups.tables import SecurityGroupsTable
|
||||
|
||||
|
||||
class SecurityGroupsTab(tabs.TableTab):
|
||||
table_classes = (SecurityGroupsTable,)
|
||||
name = _("Security Groups")
|
||||
slug = "security_groups_tab"
|
||||
template_name = "horizon/common/_detail_table.html"
|
||||
permissions = ('openstack.services.compute',)
|
||||
|
||||
def get_security_groups_data(self):
|
||||
try:
|
||||
security_groups = network.security_group_list(self.request)
|
||||
except neutron_exc.ConnectionFailed:
|
||||
security_groups = []
|
||||
exceptions.handle(self.request)
|
||||
except Exception:
|
||||
security_groups = []
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve security groups.'))
|
||||
return sorted(security_groups, key=lambda group: group.name)
|
||||
|
||||
|
||||
class AccessAndSecurityTabs(tabs.TabGroup):
|
||||
slug = "access_security_tabs"
|
||||
tabs = (SecurityGroupsTab,)
|
||||
sticky = True
|
@ -1,11 +0,0 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Access & Security" %}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
{{ tab_group.render }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
@ -1,8 +0,0 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Add Rule" %}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'project/access_and_security/security_groups/_add_rule.html' %}
|
||||
{% endblock %}
|
||||
|
@ -1,7 +0,0 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Edit Security Group" %}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'project/access_and_security/security_groups/_update.html' %}
|
||||
{% endblock %}
|
@ -1,155 +0,0 @@
|
||||
# Copyright 2012 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Copyright 2012 Nebula, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from copy import deepcopy # noqa
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django import http
|
||||
from mox3.mox import IsA # noqa
|
||||
import six
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard.test import helpers as test
|
||||
from openstack_dashboard.usage import quotas
|
||||
|
||||
INDEX_URL = reverse('horizon:project:access_and_security:index')
|
||||
|
||||
|
||||
class AccessAndSecurityTests(test.TestCase):
|
||||
def setUp(self):
|
||||
super(AccessAndSecurityTests, self).setUp()
|
||||
|
||||
@test.create_stubs({api.network: ('security_group_list',),
|
||||
api.base: ('is_service_enabled',),
|
||||
quotas: ('tenant_quota_usages',)})
|
||||
def _test_index(self):
|
||||
sec_groups = self.security_groups.list()
|
||||
quota_data = self.quota_usages.first()
|
||||
quota_data['security_groups']['available'] = 10
|
||||
|
||||
api.network.security_group_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(sec_groups)
|
||||
quotas.tenant_quota_usages(IsA(http.HttpRequest)).MultipleTimes() \
|
||||
.AndReturn(quota_data)
|
||||
|
||||
api.base.is_service_enabled(IsA(http.HttpRequest), 'network') \
|
||||
.MultipleTimes().AndReturn(True)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(INDEX_URL)
|
||||
|
||||
self.assertTemplateUsed(res, 'project/access_and_security/index.html')
|
||||
|
||||
# Security groups
|
||||
sec_groups_from_ctx = res.context['security_groups_table'].data
|
||||
# Context data needs to contains all items from the test data.
|
||||
self.assertItemsEqual(sec_groups_from_ctx,
|
||||
sec_groups)
|
||||
# Sec groups in context need to be sorted by their ``name`` attribute.
|
||||
# This assertion is somewhat weak since it's only meaningful as long as
|
||||
# the sec groups in the test data are *not* sorted by name (which is
|
||||
# the case as of the time of this addition).
|
||||
self.assertTrue(
|
||||
all([sec_groups_from_ctx[i].name <= sec_groups_from_ctx[i + 1].name
|
||||
for i in range(len(sec_groups_from_ctx) - 1)]))
|
||||
|
||||
def test_index(self):
|
||||
self._test_index()
|
||||
|
||||
|
||||
class SecurityGroupTabTests(test.TestCase):
|
||||
def setUp(self):
|
||||
super(SecurityGroupTabTests, self).setUp()
|
||||
|
||||
@test.create_stubs({api.network: ('security_group_list',),
|
||||
quotas: ('tenant_quota_usages',),
|
||||
api.base: ('is_service_enabled',)})
|
||||
def test_create_button_attributes(self):
|
||||
sec_groups = self.security_groups.list()
|
||||
quota_data = self.quota_usages.first()
|
||||
quota_data['security_groups']['available'] = 10
|
||||
|
||||
api.network.security_group_list(
|
||||
IsA(http.HttpRequest)) \
|
||||
.AndReturn(sec_groups)
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest)).MultipleTimes() \
|
||||
.AndReturn(quota_data)
|
||||
|
||||
api.base.is_service_enabled(
|
||||
IsA(http.HttpRequest), 'network').MultipleTimes() \
|
||||
.AndReturn(True)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(INDEX_URL +
|
||||
"?tab=access_security_tabs__security_groups_tab")
|
||||
|
||||
security_groups = res.context['security_groups_table'].data
|
||||
self.assertItemsEqual(security_groups, self.security_groups.list())
|
||||
|
||||
create_action = self.getAndAssertTableAction(res, 'security_groups',
|
||||
'create')
|
||||
|
||||
self.assertEqual('Create Security Group',
|
||||
six.text_type(create_action.verbose_name))
|
||||
self.assertIsNone(create_action.policy_rules)
|
||||
self.assertEqual(set(['ajax-modal']), set(create_action.classes))
|
||||
|
||||
url = 'horizon:project:access_and_security:security_groups:create'
|
||||
self.assertEqual(url, create_action.url)
|
||||
|
||||
@test.create_stubs({api.network: ('security_group_list',),
|
||||
quotas: ('tenant_quota_usages',),
|
||||
api.base: ('is_service_enabled',)})
|
||||
def _test_create_button_disabled_when_quota_exceeded(self,
|
||||
network_enabled):
|
||||
sec_groups = self.security_groups.list()
|
||||
quota_data = self.quota_usages.first()
|
||||
quota_data['security_groups']['available'] = 0
|
||||
|
||||
api.network.security_group_list(
|
||||
IsA(http.HttpRequest)) \
|
||||
.AndReturn(sec_groups)
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest)).MultipleTimes() \
|
||||
.AndReturn(quota_data)
|
||||
|
||||
api.base.is_service_enabled(
|
||||
IsA(http.HttpRequest), 'network').MultipleTimes() \
|
||||
.AndReturn(network_enabled)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(INDEX_URL +
|
||||
"?tab=access_security_tabs__security_groups_tab")
|
||||
|
||||
security_groups = res.context['security_groups_table'].data
|
||||
self.assertItemsEqual(security_groups, self.security_groups.list())
|
||||
|
||||
create_action = self.getAndAssertTableAction(res, 'security_groups',
|
||||
'create')
|
||||
self.assertIn('disabled', create_action.classes,
|
||||
'The create button should be disabled')
|
||||
|
||||
def test_create_button_disabled_when_quota_exceeded_neutron_disabled(self):
|
||||
self._test_create_button_disabled_when_quota_exceeded(False)
|
||||
|
||||
def test_create_button_disabled_when_quota_exceeded_neutron_enabled(self):
|
||||
self._test_create_button_disabled_when_quota_exceeded(True)
|
@ -1,31 +0,0 @@
|
||||
# Copyright 2012 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Copyright 2012 Nebula, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.conf.urls import include
|
||||
from django.conf.urls import url
|
||||
|
||||
from openstack_dashboard.dashboards.project.access_and_security.\
|
||||
security_groups import urls as sec_group_urls
|
||||
from openstack_dashboard.dashboards.project.access_and_security import views
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', views.IndexView.as_view(), name='index'),
|
||||
url(r'security_groups/',
|
||||
include(sec_group_urls, namespace='security_groups')),
|
||||
]
|
@ -1,35 +0,0 @@
|
||||
# Copyright 2012 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Copyright 2012 Nebula, Inc.
|
||||
# Copyright 2012 OpenStack Foundation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Views for Instances and Volumes.
|
||||
"""
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import tabs
|
||||
|
||||
from openstack_dashboard.dashboards.project.access_and_security \
|
||||
import tabs as project_tabs
|
||||
|
||||
|
||||
class IndexView(tabs.TabbedTableView):
|
||||
tab_group_class = project_tabs.AccessAndSecurityTabs
|
||||
template_name = 'project/access_and_security/index.html'
|
||||
page_title = _("Access & Security")
|
@ -73,7 +73,7 @@ class GroupBase(forms.SelfHandlingForm):
|
||||
messages.success(request, self.success_message % sg.name)
|
||||
return sg
|
||||
except Exception as e:
|
||||
redirect = reverse("horizon:project:access_and_security:index")
|
||||
redirect = reverse("horizon:project:security_groups:index")
|
||||
error_msg = self.error_message % e
|
||||
exceptions.handle(request, error_msg, redirect=redirect)
|
||||
|
||||
@ -407,8 +407,8 @@ class AddRule(forms.SelfHandlingForm):
|
||||
return cleaned_data
|
||||
|
||||
def handle(self, request, data):
|
||||
redirect = reverse("horizon:project:access_and_security:"
|
||||
"security_groups:detail", args=[data['id']])
|
||||
redirect = reverse("horizon:project:security_groups:detail",
|
||||
args=[data['id']])
|
||||
try:
|
||||
rule = api.network.security_group_rule_create(
|
||||
request,
|
@ -1,5 +1,4 @@
|
||||
# Copyright 2012 Nebula, Inc.
|
||||
# Copyright 2012 OpenStack Foundation
|
||||
# Copyright 2017 Cisco Systems, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
@ -14,10 +13,9 @@
|
||||
# under the License.
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
|
||||
|
||||
class AccessAndSecurity(horizon.Panel):
|
||||
name = _("Access & Security")
|
||||
slug = 'access_and_security'
|
||||
class SecurityGroups(horizon.Panel):
|
||||
name = _("Security Groups")
|
||||
slug = 'security_groups'
|
@ -65,7 +65,7 @@ class DeleteGroup(policy.PolicyTargetMixin, tables.DeleteAction):
|
||||
class CreateGroup(tables.LinkAction):
|
||||
name = "create"
|
||||
verbose_name = _("Create Security Group")
|
||||
url = "horizon:project:access_and_security:security_groups:create"
|
||||
url = "horizon:project:security_groups:create"
|
||||
classes = ("ajax-modal",)
|
||||
icon = "plus"
|
||||
|
||||
@ -90,7 +90,7 @@ class CreateGroup(tables.LinkAction):
|
||||
class EditGroup(policy.PolicyTargetMixin, tables.LinkAction):
|
||||
name = "edit"
|
||||
verbose_name = _("Edit Security Group")
|
||||
url = "horizon:project:access_and_security:security_groups:update"
|
||||
url = "horizon:project:security_groups:update"
|
||||
classes = ("ajax-modal",)
|
||||
icon = "pencil"
|
||||
|
||||
@ -112,7 +112,7 @@ class EditGroup(policy.PolicyTargetMixin, tables.LinkAction):
|
||||
class ManageRules(policy.PolicyTargetMixin, tables.LinkAction):
|
||||
name = "manage_rules"
|
||||
verbose_name = _("Manage Rules")
|
||||
url = "horizon:project:access_and_security:security_groups:detail"
|
||||
url = "horizon:project:security_groups:detail"
|
||||
icon = "pencil"
|
||||
|
||||
def allowed(self, request, security_group=None):
|
||||
@ -151,7 +151,7 @@ class SecurityGroupsTable(tables.DataTable):
|
||||
class CreateRule(tables.LinkAction):
|
||||
name = "add_rule"
|
||||
verbose_name = _("Add Rule")
|
||||
url = "horizon:project:access_and_security:security_groups:add_rule"
|
||||
url = "horizon:project:security_groups:add_rule"
|
||||
classes = ("ajax-modal",)
|
||||
icon = "plus"
|
||||
|
||||
@ -197,8 +197,7 @@ class DeleteRule(tables.DeleteAction):
|
||||
|
||||
def get_success_url(self, request):
|
||||
sg_id = self.table.kwargs['security_group_id']
|
||||
return reverse("horizon:project:access_and_security:"
|
||||
"security_groups:detail", args=[sg_id])
|
||||
return reverse("horizon:project:security_groups:detail", args=[sg_id])
|
||||
|
||||
|
||||
def get_remote_ip_prefix(rule):
|
@ -0,0 +1,6 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'project/security_groups/_add_rule.html' %}
|
||||
{% endblock %}
|
||||
|
@ -1,6 +1,4 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Create Security Group" %}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'project/access_and_security/security_groups/_create.html' %}
|
@ -1,7 +1,4 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans "Manage Security Group Rules" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_detail_header.html" %}
|
@ -0,0 +1,5 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'project/security_groups/_update.html' %}
|
||||
{% endblock %}
|
@ -17,6 +17,7 @@
|
||||
# under the License.
|
||||
|
||||
import cgi
|
||||
import six
|
||||
|
||||
import django
|
||||
from django.conf import settings
|
||||
@ -30,21 +31,20 @@ from horizon import forms
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard.test import helpers as test
|
||||
from openstack_dashboard.usage import quotas
|
||||
|
||||
from openstack_dashboard.dashboards.project.access_and_security.\
|
||||
security_groups import tables
|
||||
from openstack_dashboard.dashboards.project.security_groups import tables
|
||||
|
||||
|
||||
INDEX_URL = reverse('horizon:project:access_and_security:index')
|
||||
SG_CREATE_URL = reverse('horizon:project:access_and_security:'
|
||||
'security_groups:create')
|
||||
INDEX_URL = reverse('horizon:project:security_groups:index')
|
||||
SG_CREATE_URL = reverse('horizon:project:security_groups:create')
|
||||
|
||||
SG_VIEW_PATH = 'horizon:project:access_and_security:security_groups:%s'
|
||||
SG_VIEW_PATH = 'horizon:project:security_groups:%s'
|
||||
SG_DETAIL_VIEW = SG_VIEW_PATH % 'detail'
|
||||
SG_UPDATE_VIEW = SG_VIEW_PATH % 'update'
|
||||
SG_ADD_RULE_VIEW = SG_VIEW_PATH % 'add_rule'
|
||||
|
||||
SG_TEMPLATE_PATH = 'project/access_and_security/security_groups/%s'
|
||||
SG_TEMPLATE_PATH = 'project/security_groups/%s'
|
||||
SG_DETAIL_TEMPLATE = SG_TEMPLATE_PATH % 'detail.html'
|
||||
SG_CREATE_TEMPLATE = SG_TEMPLATE_PATH % 'create.html'
|
||||
SG_UPDATE_TEMPLATE = SG_TEMPLATE_PATH % '_update.html'
|
||||
@ -64,6 +64,116 @@ class SecurityGroupsViewTests(test.TestCase):
|
||||
self.edit_url = reverse(SG_ADD_RULE_VIEW, args=[sec_group.id])
|
||||
self.update_url = reverse(SG_UPDATE_VIEW, args=[sec_group.id])
|
||||
|
||||
@test.create_stubs({api.network: ('security_group_list',),
|
||||
api.base: ('is_service_enabled',),
|
||||
quotas: ('tenant_quota_usages',)})
|
||||
def test_index(self):
|
||||
sec_groups = self.security_groups.list()
|
||||
quota_data = self.quota_usages.first()
|
||||
quota_data['security_groups']['available'] = 10
|
||||
|
||||
api.network.security_group_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(sec_groups)
|
||||
quotas.tenant_quota_usages(IsA(http.HttpRequest)).MultipleTimes() \
|
||||
.AndReturn(quota_data)
|
||||
|
||||
api.base.is_service_enabled(IsA(http.HttpRequest), 'network') \
|
||||
.MultipleTimes().AndReturn(True)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(INDEX_URL)
|
||||
|
||||
self.assertTemplateUsed(res, 'horizon/common/_data_table_view.html')
|
||||
|
||||
# Security groups
|
||||
sec_groups_from_ctx = res.context['security_groups_table'].data
|
||||
# Context data needs to contains all items from the test data.
|
||||
self.assertItemsEqual(sec_groups_from_ctx,
|
||||
sec_groups)
|
||||
# Sec groups in context need to be sorted by their ``name`` attribute.
|
||||
# This assertion is somewhat weak since it's only meaningful as long as
|
||||
# the sec groups in the test data are *not* sorted by name (which is
|
||||
# the case as of the time of this addition).
|
||||
self.assertTrue(
|
||||
all([sec_groups_from_ctx[i].name <= sec_groups_from_ctx[i + 1].name
|
||||
for i in range(len(sec_groups_from_ctx) - 1)]))
|
||||
|
||||
@test.create_stubs({api.network: ('security_group_list',),
|
||||
quotas: ('tenant_quota_usages',),
|
||||
api.base: ('is_service_enabled',)})
|
||||
def test_create_button_attributes(self):
|
||||
sec_groups = self.security_groups.list()
|
||||
quota_data = self.quota_usages.first()
|
||||
quota_data['security_groups']['available'] = 10
|
||||
|
||||
api.network.security_group_list(
|
||||
IsA(http.HttpRequest)) \
|
||||
.AndReturn(sec_groups)
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest)).MultipleTimes() \
|
||||
.AndReturn(quota_data)
|
||||
|
||||
api.base.is_service_enabled(
|
||||
IsA(http.HttpRequest), 'network').MultipleTimes() \
|
||||
.AndReturn(True)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(INDEX_URL)
|
||||
|
||||
security_groups = res.context['security_groups_table'].data
|
||||
self.assertItemsEqual(security_groups, self.security_groups.list())
|
||||
|
||||
create_action = self.getAndAssertTableAction(res, 'security_groups',
|
||||
'create')
|
||||
|
||||
self.assertEqual('Create Security Group',
|
||||
six.text_type(create_action.verbose_name))
|
||||
self.assertIsNone(create_action.policy_rules)
|
||||
self.assertEqual(set(['ajax-modal']), set(create_action.classes))
|
||||
|
||||
url = 'horizon:project:security_groups:create'
|
||||
self.assertEqual(url, create_action.url)
|
||||
|
||||
@test.create_stubs({api.network: ('security_group_list',),
|
||||
quotas: ('tenant_quota_usages',),
|
||||
api.base: ('is_service_enabled',)})
|
||||
def _test_create_button_disabled_when_quota_exceeded(self,
|
||||
network_enabled):
|
||||
sec_groups = self.security_groups.list()
|
||||
quota_data = self.quota_usages.first()
|
||||
quota_data['security_groups']['available'] = 0
|
||||
|
||||
api.network.security_group_list(
|
||||
IsA(http.HttpRequest)) \
|
||||
.AndReturn(sec_groups)
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest)).MultipleTimes() \
|
||||
.AndReturn(quota_data)
|
||||
|
||||
api.base.is_service_enabled(
|
||||
IsA(http.HttpRequest), 'network').MultipleTimes() \
|
||||
.AndReturn(network_enabled)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(INDEX_URL)
|
||||
|
||||
security_groups = res.context['security_groups_table'].data
|
||||
self.assertItemsEqual(security_groups, self.security_groups.list())
|
||||
|
||||
create_action = self.getAndAssertTableAction(res, 'security_groups',
|
||||
'create')
|
||||
self.assertIn('disabled', create_action.classes,
|
||||
'The create button should be disabled')
|
||||
|
||||
def test_create_button_disabled_when_quota_exceeded_neutron_disabled(self):
|
||||
self._test_create_button_disabled_when_quota_exceeded(False)
|
||||
|
||||
def test_create_button_disabled_when_quota_exceeded_neutron_enabled(self):
|
||||
self._test_create_button_disabled_when_quota_exceeded(True)
|
||||
|
||||
@test.create_stubs({api.network: ('security_group_rule_create',
|
||||
'security_group_list',
|
||||
'security_group_backend')})
|
@ -17,12 +17,10 @@
|
||||
# under the License.
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
from openstack_dashboard.dashboards.project.access_and_security.\
|
||||
security_groups import views
|
||||
|
||||
from openstack_dashboard.dashboards.project.security_groups import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', views.IndexView.as_view(), name='index'),
|
||||
url(r'^create/$', views.CreateView.as_view(), name='create'),
|
||||
url(r'^(?P<security_group_id>[^/]+)/$',
|
||||
views.DetailView.as_view(),
|
@ -28,18 +28,19 @@ from horizon import forms
|
||||
from horizon import tables
|
||||
from horizon.utils import memoized
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard.utils import filters
|
||||
from neutronclient.common import exceptions as neutron_exc
|
||||
|
||||
from openstack_dashboard.dashboards.project.access_and_security.\
|
||||
security_groups import forms as project_forms
|
||||
from openstack_dashboard.dashboards.project.access_and_security.\
|
||||
security_groups import tables as project_tables
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard.dashboards.project.security_groups \
|
||||
import forms as project_forms
|
||||
from openstack_dashboard.dashboards.project.security_groups \
|
||||
import tables as project_tables
|
||||
from openstack_dashboard.utils import filters
|
||||
|
||||
|
||||
class DetailView(tables.DataTableView):
|
||||
table_class = project_tables.RulesTable
|
||||
template_name = 'project/access_and_security/security_groups/detail.html'
|
||||
template_name = 'project/security_groups/detail.html'
|
||||
page_title = _("Manage Security Group Rules: "
|
||||
"{{ security_group.name }} ({{ security_group.id }})")
|
||||
|
||||
@ -49,7 +50,7 @@ class DetailView(tables.DataTableView):
|
||||
try:
|
||||
return api.network.security_group_get(self.request, sg_id)
|
||||
except Exception:
|
||||
redirect = reverse('horizon:project:access_and_security:index')
|
||||
redirect = reverse('horizon:project:security_groups:index')
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve security group.'),
|
||||
redirect=redirect)
|
||||
@ -71,10 +72,10 @@ class UpdateView(forms.ModalFormView):
|
||||
form_class = project_forms.UpdateGroup
|
||||
form_id = "update_security_group_form"
|
||||
modal_id = "update_security_group_modal"
|
||||
template_name = 'project/access_and_security/security_groups/update.html'
|
||||
template_name = 'project/security_groups/update.html'
|
||||
submit_label = _("Edit Security Group")
|
||||
submit_url = "horizon:project:access_and_security:security_groups:update"
|
||||
success_url = reverse_lazy('horizon:project:access_and_security:index')
|
||||
submit_url = "horizon:project:security_groups:update"
|
||||
success_url = reverse_lazy('horizon:project:security_groups:index')
|
||||
page_title = _("Edit Security Group")
|
||||
|
||||
@memoized.memoized_method
|
||||
@ -105,10 +106,10 @@ class AddRuleView(forms.ModalFormView):
|
||||
form_class = project_forms.AddRule
|
||||
form_id = "create_security_group_rule_form"
|
||||
modal_id = "create_security_group_rule_modal"
|
||||
template_name = 'project/access_and_security/security_groups/add_rule.html'
|
||||
template_name = 'project/security_groups/add_rule.html'
|
||||
submit_label = _("Add")
|
||||
submit_url = "horizon:project:access_and_security:security_groups:add_rule"
|
||||
url = "horizon:project:access_and_security:security_groups:detail"
|
||||
submit_url = "horizon:project:security_groups:add_rule"
|
||||
url = "horizon:project:security_groups:detail"
|
||||
page_title = _("Add Rule")
|
||||
|
||||
def get_success_url(self):
|
||||
@ -152,9 +153,26 @@ class CreateView(forms.ModalFormView):
|
||||
form_class = project_forms.CreateGroup
|
||||
form_id = "create_security_group_form"
|
||||
modal_id = "create_security_group_modal"
|
||||
template_name = 'project/access_and_security/security_groups/create.html'
|
||||
template_name = 'project/security_groups/create.html'
|
||||
submit_label = _("Create Security Group")
|
||||
submit_url = reverse_lazy(
|
||||
"horizon:project:access_and_security:security_groups:create")
|
||||
success_url = reverse_lazy('horizon:project:access_and_security:index')
|
||||
"horizon:project:security_groups:create")
|
||||
success_url = reverse_lazy('horizon:project:security_groups:index')
|
||||
page_title = _("Create Security Group")
|
||||
|
||||
|
||||
class IndexView(tables.DataTableView):
|
||||
table_class = project_tables.SecurityGroupsTable
|
||||
page_title = _("Security Groups")
|
||||
|
||||
def get_data(self):
|
||||
try:
|
||||
security_groups = api.network.security_group_list(self.request)
|
||||
except neutron_exc.ConnectionFailed:
|
||||
security_groups = []
|
||||
exceptions.handle(self.request)
|
||||
except Exception:
|
||||
security_groups = []
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve security groups.'))
|
||||
return sorted(security_groups, key=lambda group: group.name)
|
@ -1,10 +0,0 @@
|
||||
# The slug of the panel to be added to HORIZON_CONFIG. Required.
|
||||
PANEL = 'access_and_security'
|
||||
# The slug of the dashboard the PANEL associated with. Required.
|
||||
PANEL_DASHBOARD = 'project'
|
||||
# The slug of the panel group the PANEL is associated with.
|
||||
PANEL_GROUP = 'compute'
|
||||
|
||||
# Python panel class of the PANEL to be added.
|
||||
ADD_PANEL = ('openstack_dashboard.dashboards.project.'
|
||||
'access_and_security.panel.AccessAndSecurity')
|
@ -0,0 +1,6 @@
|
||||
PANEL_DASHBOARD = 'project'
|
||||
PANEL_GROUP = 'network'
|
||||
PANEL = 'security_groups'
|
||||
|
||||
ADD_PANEL = ('openstack_dashboard.dashboards.project.security_groups'
|
||||
'.panel.SecurityGroups')
|
@ -2,4 +2,7 @@
|
||||
features:
|
||||
- The Access & Security panel's tabs have been moved to their own panels for
|
||||
clearer navigation and better performance. API Access and Key Pairs now
|
||||
reside in the Compute panel group.
|
||||
reside in the Compute panel group. Floating IPs and Security Groups are
|
||||
now in the Network panel group.
|
||||
- Download buttons for OpenStack RC files have been added to the user
|
||||
dropdown menu in the top right of Horizon.
|
||||
|
Loading…
Reference in New Issue
Block a user