Remove router actions from Admin panel

add-interface/set-gateway/create-router actions in Admin Router panel
are no longer needed since all of these operations now can be done by
regular users. Moreover, there is a bug that admin cannot add an
interface of other tenant to a router in Admin Router panel and the
behavior is confusing for users.

Consindering the above, this commit remove forms create/modifying
quantum routers. After the commit admin router panel just provides
the list of routers of all tenants and removes them.

Fixes bug #1153754

Change-Id: Ie17601b260fc6584ba21cd19dc9492bf16b267c1
This commit is contained in:
Akihiro MOTOKI 2013-03-09 03:58:34 +09:00
parent ff270d1059
commit 8ac3e38c57
17 changed files with 26 additions and 318 deletions

View File

@ -1,71 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012, Nachi Ueno, NTT MCL, 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.
"""
Views for managing Quantum Routers.
"""
import logging
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
from horizon import forms
from horizon import exceptions
from horizon import messages
from openstack_dashboard import api
LOG = logging.getLogger(__name__)
class CreateForm(forms.SelfHandlingForm):
name = forms.CharField(max_length="255",
label=_("Router Name"),
required=False)
tenant_id = forms.ChoiceField(label=_("Project"))
failure_url = 'horizon:admin:routers:index'
def __init__(self, request, *args, **kwargs):
super(CreateForm, self).__init__(request, *args, **kwargs)
tenant_choices = [('', _("Select a project"))]
try:
for tenant in api.keystone.tenant_list(request, admin=True):
if tenant.enabled:
tenant_choices.append((tenant.id, tenant.name))
except:
msg = _('Failed to get tenants.')
LOG.warn(msg)
redirect = reverse(self.failure_url)
exceptions.handle(request, msg, redirect=redirect)
return
self.fields['tenant_id'].choices = tenant_choices
def handle(self, request, data):
try:
params = {}
if data.get('tenant_id'):
params['tenant_id'] = data['tenant_id']
router = api.quantum.router_create(request,
name=data['name'], **params)
message = 'Creating router "%s"' % data['name']
messages.info(request, message)
return router
except:
msg = _('Failed to create router "%s".') % data['name']
LOG.warn(msg)
redirect = reverse(self.failure_url)
exceptions.handle(request, msg, redirect=redirect)
return False

View File

@ -1,31 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012, Nachi Ueno, NTT MCL, 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.
import logging
from openstack_dashboard.dashboards.project.routers.ports import (
forms as p_forms)
LOG = logging.getLogger(__name__)
class AddInterface(p_forms.AddInterface):
failure_url = 'horizon:admin:routers:detail'
class SetGatewayForm(p_forms.SetGatewayForm):
failure_url = 'horizon:admin:routers:index'

View File

@ -21,8 +21,6 @@ from django.utils.translation import ugettext_lazy as _
from horizon import tables
from openstack_dashboard.dashboards.project.networks.ports.tables import\
get_fixed_ips, get_attached
from openstack_dashboard.dashboards.project.routers.ports import\
tables as r_tables
from openstack_dashboard.dashboards.project.routers.ports.tables import\
get_device_owner
@ -30,20 +28,11 @@ from openstack_dashboard.dashboards.project.routers.ports.tables import\
LOG = logging.getLogger(__name__)
class AddInterface(r_tables.AddInterface):
url = "horizon:admin:routers:addinterface"
class RemoveInterface(r_tables.RemoveInterface):
failure_url = 'horizon:admin:routers:detail'
class PortsTable(tables.DataTable):
name = tables.Column("name",
verbose_name=_("Name"),
link="horizon:admin:networks:ports:detail")
fixed_ips = tables.Column(get_fixed_ips, verbose_name=_("Fixed IPs"))
attached = tables.Column(get_attached, verbose_name=_("Device Attached"))
status = tables.Column("status", verbose_name=_("Status"))
device_owner = tables.Column(get_device_owner,
verbose_name=_("Type"))
@ -56,5 +45,3 @@ class PortsTable(tables.DataTable):
class Meta:
name = "interfaces"
verbose_name = _("Interfaces")
table_actions = (AddInterface, RemoveInterface)
row_actions = (RemoveInterface, )

View File

@ -18,27 +18,11 @@ import logging
from horizon import tabs
from .tabs import PortDetailTabs
from .forms import (AddInterface, SetGatewayForm)
from openstack_dashboard.dashboards.project.routers.ports import views
LOG = logging.getLogger(__name__)
class AddInterfaceView(views.AddInterfaceView):
form_class = AddInterface
template_name = 'admin/routers/ports/create.html'
success_url = 'horizon:admin:routers:detail'
failure_url = 'horizon:admin:routers:detail'
class SetGatewayView(views.SetGatewayView):
form_class = SetGatewayForm
template_name = 'admin/routers/ports/setgateway.html'
success_url = 'horizon:admin:routers:index'
failure_url = 'horizon:admin:routers:index'
class DetailView(tabs.TabView):
tab_group_class = PortDetailTabs
template_name = 'admin/networks/ports/detail.html'

View File

@ -30,25 +30,19 @@ LOG = logging.getLogger(__name__)
class DeleteRouter(r_tables.DeleteRouter):
redirect_url = "horizon:admin:routers:index"
def delete(self, request, obj_id):
search_opts = {'device_owner': 'network:router_interface',
'device_id': obj_id}
ports = api.quantum.port_list(request, **search_opts)
for port in ports:
api.quantum.router_remove_interface(request, obj_id,
port_id=port.id)
super(DeleteRouter, self).delete(request, obj_id)
def allowed(self, request, router=None):
return True
class CreateRouter(tables.LinkAction):
name = "create"
verbose_name = _("Create Router")
url = "horizon:admin:routers:create"
classes = ("ajax-modal", "btn-create")
class SetGateway(r_tables.SetGateway):
url = "horizon:admin:routers:setgateway"
class ClearGateway(r_tables.ClearGateway):
redirect_url = "horizon:admin:routers:index"
class UpdateRow(tables.Row):
ajax = True
@ -77,5 +71,5 @@ class RoutersTable(tables.DataTable):
verbose_name = _("Routers")
status_columns = ["status"]
row_class = UpdateRow
table_actions = (CreateRouter, DeleteRouter)
row_actions = (SetGateway, ClearGateway, DeleteRouter)
table_actions = (DeleteRouter,)
row_actions = (DeleteRouter,)

View File

@ -1,21 +0,0 @@
{% extends "horizon/common/_modal_form.html" %}
{% load i18n horizon humanize %}
{% block form_id %}{% endblock %}
{% block form_action %}{% url horizon:admin:routers:create %}?{{ request.GET.urlencode }}{% endblock %}
{% block modal_id %}create_router_modal{% endblock %}
{% block modal-header %}{% trans "Create router" %}{% endblock %}
{% block modal-body %}
<div class="left">
<fieldset>
{% include "horizon/common/_form_fields.html" %}
</fieldset>
</div>
{% endblock %}
{% block modal-footer %}
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Create router" %}" />
<a href="{% url horizon:admin:routers:index %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
{% endblock %}

View File

@ -1,11 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Create Router" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Create a Router") %}
{% endblock page_header %}
{% block main %}
{% include 'admin/routers/_create.html' %}
{% endblock %}

View File

@ -1,25 +0,0 @@
{% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block form_id %}add_interface_form{% endblock %}
{% block form_action %}{% url horizon:admin:routers:addinterface router.id %}
{% endblock %}
{% block modal-header %}{% trans "Add Interface" %}{% endblock %}
{% block modal-body %}
<div class="left">
<fieldset>
{% include "horizon/common/_form_fields.html" %}
</fieldset>
</div>
<div class="right">
<h3>{% trans "Description" %}:</h3>
<p>{% trans "You can connect a specified subnet to the router." %}</p>
</div>
{% endblock %}
{% block modal-footer %}
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Add interface" %}" />
<a href="{% url horizon:admin:routers:detail router.id %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
{% endblock %}

View File

@ -1,25 +0,0 @@
{% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block form_id %}setgateway_form{% endblock %}
{% block form_action %}{% url horizon:admin:routers:setgateway router.id %}
{% endblock %}
{% block modal-header %}{% trans "Set Gateway" %}{% endblock %}
{% block modal-body %}
<div class="left">
<fieldset>
{% include "horizon/common/_form_fields.html" %}
</fieldset>
</div>
<div class="right">
<h3>{% trans "Description" %}:</h3>
<p>{% trans "You can connect a specified external network to the router. The external network is regarded as a default route of the router and the router acts as a gateway for external connectivity." %}</p>
</div>
{% endblock %}
{% block modal-footer %}
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Set Gateway" %}" />
<a href="{% url horizon:admin:routers:detail router.id %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
{% endblock %}

View File

@ -1,11 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Add Interface" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Add Interface") %}
{% endblock page_header %}
{% block main %}
{% include "admin/routers/ports/_create.html" %}
{% endblock %}

View File

@ -1,11 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Set Gateway" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Set Gateway") %}
{% endblock page_header %}
{% block main %}
{% include "admin/routers/ports/_setgateway.html" %}
{% endblock %}

View File

@ -60,46 +60,3 @@ class RouterTests(test.BaseAdminViewTests, r_test.RouterTests):
self.assertTemplateUsed(res, '%s/routers/index.html' % self.DASHBOARD)
self.assertEqual(len(res.context['table'].data), 0)
self.assertMessageCount(res, error=1)
@test.create_stubs({api.quantum: ('router_list', 'router_create'),
api.keystone: ('tenant_list',)})
def test_router_create_post(self):
router = self.routers.first()
tenants = self.tenants.list()
api.quantum.router_create(
IsA(http.HttpRequest),
name=router.name,
tenant_id=router.tenant_id).AndReturn(router)
api.keystone.tenant_list(IsA(http.HttpRequest), admin=True)\
.AndReturn(tenants)
self.mox.ReplayAll()
form_data = {'name': router.name,
'tenant_id': router.tenant_id}
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.quantum: ('router_list', 'router_create'),
api.keystone: ('tenant_list',)})
def test_router_create_post_exception(self):
router = self.routers.first()
tenants = self.tenants.list()
api.quantum.router_create(
IsA(http.HttpRequest),
name=router.name,
tenant_id=router.tenant_id).AndRaise(self.exceptions.quantum)
api.keystone.tenant_list(IsA(http.HttpRequest), admin=True)\
.AndReturn(tenants)
self.mox.ReplayAll()
form_data = {'name': router.name,
'tenant_id': router.tenant_id}
url = reverse('horizon:%s:routers:create' % self.DASHBOARD)
res = self.client.post(url, form_data)
self.assertNoFormErrors(res)
self.assertRedirectsNoFollow(res, self.INDEX_URL)

View File

@ -16,19 +16,12 @@
from django.conf.urls.defaults import patterns, url
from .views import (IndexView, CreateView, DetailView)
from .ports.views import (AddInterfaceView, SetGatewayView)
from .views import (IndexView, DetailView)
urlpatterns = patterns('horizon.dashboards.admin.routers.views',
url(r'^$', IndexView.as_view(), name='index'),
url(r'^create/$', CreateView.as_view(), name='create'),
url(r'^(?P<router_id>[^/]+)/$',
DetailView.as_view(),
name='detail'),
url(r'^(?P<router_id>[^/]+)/addinterface', AddInterfaceView.as_view(),
name='addinterface'),
url(r'^(?P<router_id>[^/]+)/setgateway',
SetGatewayView.as_view(),
name='setgateway'),
)

View File

@ -24,13 +24,11 @@ from django.core.urlresolvers import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from horizon import exceptions
from horizon import forms
from openstack_dashboard import api
from openstack_dashboard.dashboards.admin.networks import views as n_views
from openstack_dashboard.dashboards.project.routers import views as r_views
from .ports.tables import PortsTable
from .forms import CreateForm
from .tables import RoutersTable
@ -71,9 +69,3 @@ class DetailView(r_views.DetailView):
table_classes = (PortsTable, )
template_name = 'admin/routers/detail.html'
failure_url = reverse_lazy('horizon:admin:routers:index')
class CreateView(forms.ModalFormView):
form_class = CreateForm
template_name = 'admin/routers/create.html'
success_url = reverse_lazy("horizon:admin:routers:index")

View File

@ -23,14 +23,16 @@ from horizon import exceptions
from horizon import tables
from openstack_dashboard import api
from openstack_dashboard.dashboards.project.networks.ports.tables import\
get_fixed_ips, get_attached
get_fixed_ips
LOG = logging.getLogger(__name__)
def get_device_owner(port):
if port['device_owner'] == 'network:router_gateway':
return _('Gateway')
return _('External Gateway')
elif port['device_owner'] == 'network:router_interface':
return _('Internal Interface')
else:
return ' '
@ -75,7 +77,6 @@ class PortsTable(tables.DataTable):
verbose_name=_("Name"),
link="horizon:project:networks:ports:detail")
fixed_ips = tables.Column(get_fixed_ips, verbose_name=_("Fixed IPs"))
attached = tables.Column(get_attached, verbose_name=_("Device Attached"))
status = tables.Column("status", verbose_name=_("Status"))
device_owner = tables.Column(get_device_owner,
verbose_name=_("Type"))

View File

@ -104,6 +104,12 @@ class RouterTests(test.TestCase):
args=[router.id]))
self.assertRedirectsNoFollow(res, self.INDEX_URL)
class RouterActionTests(test.TestCase):
DASHBOARD = 'project'
INDEX_URL = reverse('horizon:%s:routers:index' % DASHBOARD)
DETAIL_PATH = 'horizon:%s:routers:detail' % DASHBOARD
@test.create_stubs({api.quantum: ('router_create',)})
def test_router_create_post(self):
router = self.routers.first()

View File

@ -82,7 +82,7 @@ def data(TEST):
'device_owner': 'network:dhcp',
'fixed_ips': [{'ip_address': '10.0.0.3',
'subnet_id': subnet_dict['id']}],
'id': '3ec7f3db-cb2f-4a34-ab6b-69a64d3f008c',
'id': '063cf7f3-ded1-4297-bc4c-31eae876cc91',
'mac_address': 'fa:16:3e:9c:d5:7e',
'name': '',
'network_id': network_dict['id'],
@ -197,7 +197,7 @@ def data(TEST):
'device_owner': 'network:router_gateway',
'fixed_ips': [{'ip_address': '10.0.0.3',
'subnet_id': subnet_dict['id']}],
'id': '3ec7f3db-cb2f-4a34-ab6b-69a64d3f008c',
'id': '44ec6726-4bdc-48c5-94d4-df8d1fbf613b',
'mac_address': 'fa:16:3e:9c:d5:7e',
'name': '',
'network_id': network_dict['id'],
@ -213,8 +213,8 @@ def data(TEST):
'tenant_id': '1'}
TEST.api_routers.add(router_dict)
TEST.routers.add(Router(router_dict))
router_dict = {'id': '279989f7-54bb-41d9-ba42-0d61f12fda61',
'name': 'router1',
router_dict = {'id': '10e3dc42-1ce1-4d48-87cf-7fc333055d6c',
'name': 'router2',
'external_gateway_info':
{'network_id': ext_net['id']},
'tenant_id': '1'}