Horizon changes for DVR
Feature completed : 1. Admin router panel + New "Distributed" column introduced. + New Field "Distributed" added on to router detail panel 2. Project router panel if logged in as "Admin" ====================== + New distributed column introduced. + New Field distributed column added on to router detail panel. + New Router Field dropdown box introduced in create router form. if logged in as "nonAdmin" ========================= + Router Type dropdown will be invisible for non admin. + Distributed information will be hidden from details panel. implements: blueprint enhance-horizon-for-dvr Co-Authored-By: Akihiro Motoki <motoki@da.jp.nec.com> Change-Id: I995745dd72a8b750866c0977a7d7cf42036f716f
This commit is contained in:
parent
266639a4e9
commit
0d8fb6ce08
|
@ -34,6 +34,7 @@ from horizon.utils.memoized import memoized # noqa
|
|||
from openstack_dashboard.api import base
|
||||
from openstack_dashboard.api import network_base
|
||||
from openstack_dashboard.api import nova
|
||||
from openstack_dashboard import policy
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
@ -898,3 +899,31 @@ def is_port_profiles_supported():
|
|||
profile_support = network_config.get('profile_support', None)
|
||||
if str(profile_support).lower() == 'cisco':
|
||||
return True
|
||||
|
||||
|
||||
def get_dvr_permission(request, operation):
|
||||
"""Check if "distributed" field can be displayed.
|
||||
|
||||
:param request: Request Object
|
||||
:param operation: Operation type. The valid value is "get" or "create"
|
||||
"""
|
||||
network_config = getattr(settings, 'OPENSTACK_NEUTRON_NETWORK', {})
|
||||
if not network_config.get('enable_distributed_router', False):
|
||||
return False
|
||||
policy_check = getattr(settings, "POLICY_CHECK_FUNCTION", None)
|
||||
if operation not in ("get", "create"):
|
||||
raise ValueError(_("The 'operation' parameter for get_dvr_permission "
|
||||
"is invalid. It should be 'get' or 'create'."))
|
||||
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
|
||||
|
|
|
@ -145,6 +145,8 @@
|
|||
"add_router_interface": "rule:admin_or_owner",
|
||||
"remove_router_interface": "rule:admin_or_owner",
|
||||
"delete_router": "rule:admin_or_owner",
|
||||
"get_router:distributed": "rule:admin_only",
|
||||
"create_router:distributed": "rule:admin_only",
|
||||
|
||||
"create_floatingip": "rule:regular_user",
|
||||
"update_floatingip": "rule:admin_or_owner",
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.template.defaultfilters import title # noqa
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import tables
|
||||
|
@ -51,20 +50,11 @@ class UpdateRow(tables.Row):
|
|||
return router
|
||||
|
||||
|
||||
class RoutersTable(tables.DataTable):
|
||||
class RoutersTable(r_tables.RoutersTable):
|
||||
tenant = tables.Column("tenant_name", verbose_name=_("Project"))
|
||||
name = tables.Column("name",
|
||||
verbose_name=_("Name"),
|
||||
link="horizon:admin:routers:detail")
|
||||
status = tables.Column("status",
|
||||
filters=(title,),
|
||||
verbose_name=_("Status"),
|
||||
status=True)
|
||||
ext_net = tables.Column(r_tables.get_external_network,
|
||||
verbose_name=_("External Network"))
|
||||
|
||||
def get_object_display(self, obj):
|
||||
return obj.name
|
||||
|
||||
class Meta:
|
||||
name = "Routers"
|
||||
|
@ -73,3 +63,4 @@ class RoutersTable(tables.DataTable):
|
|||
row_class = UpdateRow
|
||||
table_actions = (DeleteRouter,)
|
||||
row_actions = (DeleteRouter,)
|
||||
Columns = ('tenant', 'name', 'status', 'distributed', 'ext_net')
|
||||
|
|
|
@ -12,6 +12,10 @@
|
|||
<dd>{{ router.tenant_id }}</dd>
|
||||
<dt>{% trans "Status" %}</dt>
|
||||
<dd>{{ router.status|capfirst }}</dd>
|
||||
{% if dvr_supported %}
|
||||
<dt>{% trans "Distributed" %}</dt>
|
||||
<dd>{{ router.distributed|yesno|capfirst }}</dd>
|
||||
{% endif %}
|
||||
{% if router.external_gateway_info %}
|
||||
<dt>{% trans "External Gateway Information" %}</dt>
|
||||
<dd>{% trans "Connected External Network" %}:
|
||||
|
|
|
@ -32,15 +32,28 @@ LOG = logging.getLogger(__name__)
|
|||
|
||||
class CreateForm(forms.SelfHandlingForm):
|
||||
name = forms.CharField(max_length="255", label=_("Router Name"))
|
||||
mode = forms.ChoiceField(label=_("Router Type"))
|
||||
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:
|
||||
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
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
router = api.neutron.router_create(request,
|
||||
name=data['name'])
|
||||
params = {'name': data['name']}
|
||||
if (self.dvr_enabled and data['mode'] != 'server_default'):
|
||||
params['distributed'] = (data['mode'] == 'distributed')
|
||||
router = api.neutron.router_create(request, **params)
|
||||
message = _('Router %s was successfully created.') % data['name']
|
||||
messages.success(request, message)
|
||||
return router
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
import logging
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.template.defaultfilters import title # noqa
|
||||
from django.template import defaultfilters as filters
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from neutronclient.common import exceptions as q_ext
|
||||
|
||||
|
@ -147,12 +147,24 @@ class RoutersTable(tables.DataTable):
|
|||
verbose_name=_("Name"),
|
||||
link="horizon:project:routers:detail")
|
||||
status = tables.Column("status",
|
||||
filters=(title,),
|
||||
filters=(filters.title,),
|
||||
verbose_name=_("Status"),
|
||||
status=True)
|
||||
distributed = tables.Column("distributed",
|
||||
filters=(filters.yesno, filters.capfirst),
|
||||
verbose_name=_("Distributed"))
|
||||
ext_net = tables.Column(get_external_network,
|
||||
verbose_name=_("External Network"))
|
||||
|
||||
def __init__(self, request, data=None, needs_form_wrapper=None, **kwargs):
|
||||
super(RoutersTable, self).__init__(
|
||||
request,
|
||||
data=data,
|
||||
needs_form_wrapper=needs_form_wrapper,
|
||||
**kwargs)
|
||||
if not api.neutron.get_dvr_permission(request, "get"):
|
||||
del self.columns["distributed"]
|
||||
|
||||
def get_object_display(self, obj):
|
||||
return obj.name
|
||||
|
||||
|
|
|
@ -10,6 +10,10 @@
|
|||
<dd>{{ router.id|default:_("None") }}</dd>
|
||||
<dt>{% trans "Status" %}</dt>
|
||||
<dd>{{ router.status|default:_("Unknown") }}</dd>
|
||||
{% if dvr_supported %}
|
||||
<dt>{% trans "Distributed" %}</dt>
|
||||
<dd>{{ router.distributed|yesno|capfirst }}</dd>
|
||||
{% endif %}
|
||||
{% if router.external_gateway_info %}
|
||||
<dt>{% trans "External Gateway Information" %}</dt>
|
||||
<dd>{% trans "Connected External Network" %}:
|
||||
|
|
|
@ -109,13 +109,16 @@ class RouterActionTests(test.TestCase):
|
|||
INDEX_URL = reverse('horizon:%s:routers:index' % DASHBOARD)
|
||||
DETAIL_PATH = 'horizon:%s:routers:detail' % DASHBOARD
|
||||
|
||||
@test.create_stubs({api.neutron: ('router_create',)})
|
||||
@test.create_stubs({api.neutron: ('router_create',
|
||||
'get_dvr_permission',)})
|
||||
def test_router_create_post(self):
|
||||
router = self.routers.first()
|
||||
api.neutron.get_dvr_permission(IsA(http.HttpRequest), "create")\
|
||||
.AndReturn(False)
|
||||
api.neutron.router_create(IsA(http.HttpRequest), name=router.name)\
|
||||
.AndReturn(router)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
self.mox.ReplayAll()
|
||||
form_data = {'name': router.name}
|
||||
url = reverse('horizon:%s:routers:create' % self.DASHBOARD)
|
||||
res = self.client.post(url, form_data)
|
||||
|
@ -123,9 +126,50 @@ class RouterActionTests(test.TestCase):
|
|||
self.assertNoFormErrors(res)
|
||||
self.assertRedirectsNoFollow(res, self.INDEX_URL)
|
||||
|
||||
@test.create_stubs({api.neutron: ('router_create',)})
|
||||
@test.create_stubs({api.neutron: ('router_create',
|
||||
'get_dvr_permission',)})
|
||||
def test_router_create_post_mode_server_default(self):
|
||||
router = self.routers.first()
|
||||
api.neutron.get_dvr_permission(IsA(http.HttpRequest), "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'}
|
||||
url = reverse('horizon:%s:routers:create' % self.DASHBOARD)
|
||||
res = self.client.post(url, form_data)
|
||||
|
||||
self.assertNoFormErrors(res)
|
||||
self.assertRedirectsNoFollow(res, self.INDEX_URL)
|
||||
|
||||
@test.create_stubs({api.neutron: ('router_create',
|
||||
'get_dvr_permission',)})
|
||||
def test_dvr_router_create_post(self):
|
||||
router = self.routers.first()
|
||||
api.neutron.get_dvr_permission(IsA(http.HttpRequest), "create")\
|
||||
.MultipleTimes().AndReturn(True)
|
||||
param = {'name': router.name,
|
||||
'distributed': True}
|
||||
api.neutron.router_create(IsA(http.HttpRequest), **param)\
|
||||
.AndReturn(router)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
form_data = {'name': router.name,
|
||||
'mode': 'distributed'}
|
||||
url = reverse('horizon:%s:routers:create' % self.DASHBOARD)
|
||||
res = self.client.post(url, form_data)
|
||||
|
||||
self.assertNoFormErrors(res)
|
||||
self.assertRedirectsNoFollow(res, self.INDEX_URL)
|
||||
|
||||
@test.create_stubs({api.neutron: ('router_create',
|
||||
'get_dvr_permission',)})
|
||||
def test_router_create_post_exception_error_case_409(self):
|
||||
router = self.routers.first()
|
||||
api.neutron.get_dvr_permission(IsA(http.HttpRequest), "create")\
|
||||
.MultipleTimes().AndReturn(False)
|
||||
self.exceptions.neutron.status_code = 409
|
||||
api.neutron.router_create(IsA(http.HttpRequest), name=router.name)\
|
||||
.AndRaise(self.exceptions.neutron)
|
||||
|
@ -138,9 +182,12 @@ class RouterActionTests(test.TestCase):
|
|||
self.assertNoFormErrors(res)
|
||||
self.assertRedirectsNoFollow(res, self.INDEX_URL)
|
||||
|
||||
@test.create_stubs({api.neutron: ('router_create',)})
|
||||
@test.create_stubs({api.neutron: ('router_create',
|
||||
'get_dvr_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")\
|
||||
.MultipleTimes().AndReturn(False)
|
||||
self.exceptions.neutron.status_code = 999
|
||||
api.neutron.router_create(IsA(http.HttpRequest), name=router.name)\
|
||||
.AndRaise(self.exceptions.neutron)
|
||||
|
|
|
@ -116,6 +116,8 @@ 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")
|
||||
return context
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
|
|
|
@ -183,6 +183,7 @@ OPENSTACK_NEUTRON_NETWORK = {
|
|||
'enable_quotas': True,
|
||||
'enable_vpn': False,
|
||||
'enable_ipv6': True,
|
||||
'enable_distributed_router': True,
|
||||
# The profile_support option is used to detect if an external router can be
|
||||
# configured via the dashboard. When using specific plugins the
|
||||
# profile_support can be turned on if needed.
|
||||
|
|
|
@ -12,7 +12,10 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.test.utils import override_settings
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard import policy
|
||||
from openstack_dashboard.test import helpers as test
|
||||
|
||||
|
||||
|
@ -279,3 +282,67 @@ class NeutronApiTests(test.APITestCase):
|
|||
api.neutron.is_extension_supported(self.request, 'quotas'))
|
||||
self.assertFalse(
|
||||
api.neutron.is_extension_supported(self.request, 'doesntexist'))
|
||||
|
||||
@override_settings(OPENSTACK_NEUTRON_NETWORK={'enable_distributed_router':
|
||||
True},
|
||||
POLICY_CHECK_FUNCTION=None)
|
||||
def _test_get_dvr_permission_dvr_supported(self, dvr_enabled):
|
||||
neutronclient = self.stub_neutronclient()
|
||||
extensions = self.api_extensions.list()
|
||||
if not dvr_enabled:
|
||||
extensions = [ext for ext in extensions if ext['alias'] != 'dvr']
|
||||
neutronclient.list_extensions() \
|
||||
.AndReturn({'extensions': extensions})
|
||||
self.mox.ReplayAll()
|
||||
self.assertEqual(dvr_enabled,
|
||||
api.neutron.get_dvr_permission(self.request, 'get'))
|
||||
|
||||
def test_get_dvr_permission_dvr_supported(self):
|
||||
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)
|
||||
|
||||
@override_settings(OPENSTACK_NEUTRON_NETWORK={'enable_distributed_router':
|
||||
True},
|
||||
POLICY_CHECK_FUNCTION=policy.check)
|
||||
def _test_get_dvr_permission_with_policy_check(self, policy_check_allowed,
|
||||
operation):
|
||||
self.mox.StubOutWithMock(policy, 'check')
|
||||
if operation == "create":
|
||||
role = (("network", "create_router:distributed"),)
|
||||
elif operation == "get":
|
||||
role = (("network", "get_router:distributed"),)
|
||||
policy.check(role, self.request).AndReturn(policy_check_allowed)
|
||||
if policy_check_allowed:
|
||||
neutronclient = self.stub_neutronclient()
|
||||
neutronclient.list_extensions() \
|
||||
.AndReturn({'extensions': self.api_extensions.list()})
|
||||
self.mox.ReplayAll()
|
||||
self.assertEqual(policy_check_allowed,
|
||||
api.neutron.get_dvr_permission(self.request,
|
||||
operation))
|
||||
|
||||
def test_get_dvr_permission_with_policy_check_allowed(self):
|
||||
self._test_get_dvr_permission_with_policy_check(True, "get")
|
||||
|
||||
def test_get_dvr_permission_with_policy_check_disallowed(self):
|
||||
self._test_get_dvr_permission_with_policy_check(False, "get")
|
||||
|
||||
def test_get_dvr_permission_create_with_policy_check_allowed(self):
|
||||
self._test_get_dvr_permission_with_policy_check(True, "create")
|
||||
|
||||
def test_get_dvr_permission_create_with_policy_check_disallowed(self):
|
||||
self._test_get_dvr_permission_with_policy_check(False, "create")
|
||||
|
||||
@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'))
|
||||
|
||||
@override_settings(OPENSTACK_NEUTRON_NETWORK={'enable_distributed_router':
|
||||
True})
|
||||
def test_get_dvr_permission_dvr_unsupported_operation(self):
|
||||
self.assertRaises(ValueError,
|
||||
api.neutron.get_dvr_permission,
|
||||
self.request, 'unSupported')
|
||||
|
|
|
@ -109,6 +109,7 @@ OPENSTACK_NEUTRON_NETWORK = {
|
|||
'enable_quotas': False, # Enabled in specific tests only
|
||||
'enable_vpn': True,
|
||||
'profile_support': None,
|
||||
'enable_distributed_router': False,
|
||||
# 'profile_support': 'cisco'
|
||||
}
|
||||
|
||||
|
|
|
@ -568,9 +568,14 @@ def data(TEST):
|
|||
extension_3 = {"name": "Provider network",
|
||||
"alias": "provider",
|
||||
"description": "Provider network extension"}
|
||||
extension_4 = {"name": "Distributed Virtual Router",
|
||||
"alias": "dvr",
|
||||
"description":
|
||||
"Enables configuration of Distributed Virtual 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)
|
||||
|
||||
# 1st agent.
|
||||
agent_dict = {"binary": "neutron-openvswitch-agent",
|
||||
|
|
Loading…
Reference in New Issue