Merge "Add Extra Routes to Router Tabs View"
This commit is contained in:
commit
6715d794c2
@ -142,6 +142,15 @@ class Router(NeutronAPIDictWrapper):
|
||||
super(Router, self).__init__(apiresource)
|
||||
|
||||
|
||||
class RouterStaticRoute(NeutronAPIDictWrapper):
|
||||
"""Wrapper for neutron routes extra route."""
|
||||
|
||||
def __init__(self, route):
|
||||
super(RouterStaticRoute, self).__init__(route)
|
||||
# Horizon references id property for table operations
|
||||
self.id = route['nexthop'] + ":" + route['destination']
|
||||
|
||||
|
||||
class SecurityGroup(NeutronAPIDictWrapper):
|
||||
# Required attributes: id, name, description, tenant_id, rules
|
||||
|
||||
@ -896,6 +905,39 @@ def router_remove_gateway(request, router_id):
|
||||
neutronclient(request).remove_gateway_router(router_id)
|
||||
|
||||
|
||||
def router_static_route_list(request, router_id=None):
|
||||
router = router_get(request, router_id)
|
||||
try:
|
||||
routes = [RouterStaticRoute(r) for r in router.routes]
|
||||
except AttributeError:
|
||||
LOG.debug("router_static_route_list(): router_id=%s, "
|
||||
"router=%s", (router_id, router))
|
||||
return []
|
||||
return routes
|
||||
|
||||
|
||||
def router_static_route_remove(request, router_id, route_ids):
|
||||
currentroutes = router_static_route_list(request, router_id=router_id)
|
||||
newroutes = []
|
||||
for oldroute in currentroutes:
|
||||
if oldroute.id not in route_ids:
|
||||
newroutes.append({'nexthop': oldroute.nexthop,
|
||||
'destination': oldroute.destination})
|
||||
body = {'routes': newroutes}
|
||||
new = router_update(request, router_id, **body)
|
||||
return new
|
||||
|
||||
|
||||
def router_static_route_add(request, router_id, newroute):
|
||||
body = {}
|
||||
currentroutes = router_static_route_list(request, router_id=router_id)
|
||||
body['routes'] = [newroute] + [{'nexthop': r.nexthop,
|
||||
'destination': r.destination}
|
||||
for r in currentroutes]
|
||||
new = router_update(request, router_id, **body)
|
||||
return new
|
||||
|
||||
|
||||
def tenant_quota_get(request, tenant_id):
|
||||
return base.QuotaSet(neutronclient(request).show_quota(tenant_id)['quota'])
|
||||
|
||||
|
@ -0,0 +1,27 @@
|
||||
# Copyright 2015, Thales Services SAS
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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 openstack_dashboard.dashboards.project.routers.extensions.extraroutes\
|
||||
import tables as routes_table
|
||||
|
||||
|
||||
class AdminRouterRoutesTable(routes_table.ExtraRoutesTable):
|
||||
|
||||
class Meta(object):
|
||||
# Redifine Meta class to disable action (admin)
|
||||
name = "extra_routes"
|
||||
verbose_name = _("Static Routes")
|
@ -12,7 +12,11 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from openstack_dashboard.dashboards.admin.routers.extensions.extraroutes\
|
||||
import tables as ertbl
|
||||
from openstack_dashboard.dashboards.admin.routers.ports import tables as ptbl
|
||||
from openstack_dashboard.dashboards.project.routers.extensions.extraroutes\
|
||||
import tabs as er_tabs
|
||||
from openstack_dashboard.dashboards.project.routers.extensions.routerrules\
|
||||
import tabs as rr_tabs
|
||||
from openstack_dashboard.dashboards.project.routers import tabs as r_tabs
|
||||
@ -22,11 +26,15 @@ class OverviewTab(r_tabs.OverviewTab):
|
||||
template_name = "project/routers/_detail_overview.html"
|
||||
|
||||
|
||||
class ExtraRoutesTab(er_tabs.ExtraRoutesTab):
|
||||
table_classes = (ertbl.AdminRouterRoutesTable,)
|
||||
|
||||
|
||||
class InterfacesTab(r_tabs.InterfacesTab):
|
||||
table_classes = (ptbl.PortsTable,)
|
||||
|
||||
|
||||
class RouterDetailTabs(r_tabs.RouterDetailTabs):
|
||||
tabs = (OverviewTab, InterfacesTab, rr_tabs.RulesGridTab,
|
||||
tabs = (OverviewTab, InterfacesTab, ExtraRoutesTab, rr_tabs.RulesGridTab,
|
||||
rr_tabs.RouterRulesTab)
|
||||
sticky = True
|
||||
|
@ -162,3 +162,9 @@ class RouterTests(test.BaseAdminViewTests, r_test.RouterTests):
|
||||
self.assertNoFormErrors(res)
|
||||
self.assertMessageCount(response=res, success=1)
|
||||
self.assertIn('Deleted Router: ' + router.name, res.content)
|
||||
|
||||
|
||||
class RouterRouteTest(test.BaseAdminViewTests, r_test.RouterRouteTests):
|
||||
DASHBOARD = 'admin'
|
||||
INDEX_URL = reverse('horizon:%s:routers:index' % DASHBOARD)
|
||||
DETAIL_PATH = 'horizon:%s:routers:detail' % DASHBOARD
|
||||
|
@ -0,0 +1,60 @@
|
||||
# Copyright 2015, Thales Services SAS
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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 django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import messages
|
||||
|
||||
from neutronclient.common import exceptions as neutron_exc
|
||||
|
||||
from openstack_dashboard.api import neutron as api
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AddRouterRoute(forms.SelfHandlingForm):
|
||||
destination = forms.IPField(label=_("Destination CIDR"), mask=True)
|
||||
nexthop = forms.IPField(label=_("Next Hop"))
|
||||
failure_url = 'horizon:project:routers:detail'
|
||||
|
||||
def handle(self, request, data, **kwargs):
|
||||
router_id = self.initial['router_id']
|
||||
try:
|
||||
route = {'nexthop': data['nexthop'],
|
||||
'destination': data['destination']}
|
||||
api.router_static_route_add(request,
|
||||
router_id,
|
||||
route)
|
||||
msg = _('Static route added')
|
||||
LOG.debug(msg)
|
||||
messages.success(request, msg)
|
||||
return True
|
||||
except neutron_exc.BadRequest as e:
|
||||
msg = (_('Invalid format for routes : %s') % e)
|
||||
LOG.info(msg)
|
||||
messages.error(request, msg)
|
||||
redirect = reverse(self.failure_url, args=[router_id])
|
||||
exceptions.handle(request, msg, redirect=redirect)
|
||||
except Exception as e:
|
||||
msg = (_('Failed to add route : %s') % e)
|
||||
LOG.info(msg)
|
||||
messages.error(request, msg)
|
||||
redirect = reverse(self.failure_url, args=[router_id])
|
||||
exceptions.handle(request, msg, redirect=redirect)
|
@ -0,0 +1,77 @@
|
||||
# Copyright 2015, Thales Services SAS
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import ungettext_lazy
|
||||
|
||||
from openstack_dashboard.api import neutron as api
|
||||
from openstack_dashboard import policy
|
||||
|
||||
from horizon import tables
|
||||
|
||||
|
||||
class AddRouterRoute(policy.PolicyTargetMixin, tables.LinkAction):
|
||||
name = "create"
|
||||
verbose_name = _("Add Static Route")
|
||||
url = "horizon:project:routers:addrouterroute"
|
||||
classes = ("ajax-modal",)
|
||||
icon = "plus"
|
||||
policy_rules = (("network", "update_router"),)
|
||||
|
||||
def get_link_url(self, datum=None):
|
||||
router_id = self.table.kwargs['router_id']
|
||||
return reverse(self.url, args=(router_id,))
|
||||
|
||||
|
||||
class RemoveRouterRoute(policy.PolicyTargetMixin, tables.DeleteAction):
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ungettext_lazy(
|
||||
u"Delete Static Route",
|
||||
u"Delete Static Routes",
|
||||
count
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ungettext_lazy(
|
||||
u"Deleted Static Route",
|
||||
u"Deleted Static Routes",
|
||||
count
|
||||
)
|
||||
failure_url = 'horizon:project:routers:detail'
|
||||
policy_rules = (("network", "update_router"),)
|
||||
|
||||
def delete(self, request, obj_id):
|
||||
router_id = self.table.kwargs['router_id']
|
||||
api.router_static_route_remove(request, router_id, [obj_id])
|
||||
|
||||
|
||||
class ExtraRoutesTable(tables.DataTable):
|
||||
destination = tables.Column("destination",
|
||||
verbose_name=_("Destination CIDR"))
|
||||
nexthop = tables.Column("nexthop", verbose_name=_("Next Hop"))
|
||||
|
||||
def get_object_display(self, datum):
|
||||
"""Display ExtraRoutes when deleted."""
|
||||
return (super(ExtraRoutesTable, self).get_object_display(datum)
|
||||
or datum.destination + " -> " + datum.nexthop)
|
||||
|
||||
class Meta(object):
|
||||
name = "extra_routes"
|
||||
verbose_name = _("Static Routes")
|
||||
table_actions = (AddRouterRoute, RemoveRouterRoute)
|
||||
row_actions = (RemoveRouterRoute, )
|
@ -0,0 +1,49 @@
|
||||
# Copyright 2015, Thales Services SAS
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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 django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import tabs
|
||||
|
||||
from openstack_dashboard.api import neutron as api
|
||||
from openstack_dashboard.dashboards.project.routers.extensions.extraroutes\
|
||||
import tables as ertbl
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ExtraRoutesTab(tabs.TableTab):
|
||||
table_classes = (ertbl.ExtraRoutesTable,)
|
||||
name = _("Static Routes")
|
||||
slug = "extraroutes"
|
||||
template_name = "horizon/common/_detail_table.html"
|
||||
|
||||
def allowed(self, request):
|
||||
try:
|
||||
return api.is_extension_supported(request, 'extraroute')
|
||||
except Exception:
|
||||
LOG.info(_("Failed to check if Neutron extraroute extension is "
|
||||
"supported"))
|
||||
return False
|
||||
|
||||
def get_extra_routes_data(self):
|
||||
try:
|
||||
extraroutes = getattr(self.tab_group.kwargs['router'], 'routes')
|
||||
except AttributeError:
|
||||
extraroutes = []
|
||||
return [api.RouterStaticRoute(r) for r in extraroutes]
|
@ -0,0 +1,59 @@
|
||||
# Copyright 2015, Thales Services SAS
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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 django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon.utils import memoized
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard.dashboards.project.routers.extensions.extraroutes\
|
||||
import forms as erforms
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AddRouterRouteView(forms.ModalFormView):
|
||||
form_class = erforms.AddRouterRoute
|
||||
template_name = 'project/routers/extensions/routerroutes/create.html'
|
||||
url = 'horizon:project:routers:detail'
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse(self.url,
|
||||
args=(self.kwargs['router_id'],))
|
||||
|
||||
@memoized.memoized_method
|
||||
def get_object(self):
|
||||
try:
|
||||
router_id = self.kwargs["router_id"]
|
||||
return api.neutron.router_get(self.request, router_id)
|
||||
except Exception:
|
||||
redirect = reverse(self.url, args=[router_id])
|
||||
msg = _("Unable to retrieve router.")
|
||||
exceptions.handle(self.request, msg, redirect=redirect)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(AddRouterRouteView, self).get_context_data(**kwargs)
|
||||
context['router'] = self.get_object()
|
||||
return context
|
||||
|
||||
def get_initial(self):
|
||||
router = self.get_object()
|
||||
return {"router_id": self.kwargs['router_id'],
|
||||
"router_name": router.name}
|
@ -16,6 +16,8 @@ from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import tabs
|
||||
|
||||
from openstack_dashboard.dashboards.project.routers.extensions.extraroutes\
|
||||
import tabs as er_tabs
|
||||
from openstack_dashboard.dashboards.project.routers.extensions.routerrules\
|
||||
import tabs as rr_tabs
|
||||
from openstack_dashboard.dashboards.project.routers.ports import tables as ptbl
|
||||
@ -42,6 +44,6 @@ class InterfacesTab(tabs.TableTab):
|
||||
|
||||
class RouterDetailTabs(tabs.TabGroup):
|
||||
slug = "router_details"
|
||||
tabs = (OverviewTab, InterfacesTab, rr_tabs.RulesGridTab,
|
||||
rr_tabs.RouterRulesTab)
|
||||
tabs = (OverviewTab, InterfacesTab, er_tabs.ExtraRoutesTab,
|
||||
rr_tabs.RulesGridTab, rr_tabs.RouterRulesTab)
|
||||
sticky = True
|
||||
|
@ -0,0 +1,29 @@
|
||||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
{% load url from future %}
|
||||
|
||||
{% block form_id %}add_routerroute_form{% endblock %}
|
||||
{% block form_action %}{% url 'horizon:project:routers:addrouterroute' router.id %}
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-header %}{% trans "Add Static Route" %}{% 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 "Add static route to the router." %}<br/>
|
||||
{% trans "Next Hop IP must be a part of one of the subnets to which the router interfaces are connected." %}
|
||||
</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-footer %}
|
||||
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Add route" %}" />
|
||||
<a href="{% url 'horizon:project:routers:index' %}" class="btn btn-default secondary cancel close">{% trans "Cancel" %}</a>
|
||||
{% endblock %}
|
@ -0,0 +1,11 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Add Router Route" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Add Router Route") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% include "project/routers/extensions/routerroutes/_create.html" %}
|
||||
{% endblock %}
|
@ -30,9 +30,11 @@ from openstack_dashboard.usage import quotas
|
||||
class RouterMixin(object):
|
||||
@test.create_stubs({
|
||||
api.neutron: ('router_get', 'port_list',
|
||||
'network_get'),
|
||||
'network_get', 'is_extension_supported'),
|
||||
})
|
||||
def _get_detail(self, router):
|
||||
def _get_detail(self, router, extraroute=True):
|
||||
api.neutron.is_extension_supported(IsA(http.HttpRequest), 'extraroute')\
|
||||
.MultipleTimes().AndReturn(extraroute)
|
||||
api.neutron.router_get(IsA(http.HttpRequest), router.id)\
|
||||
.AndReturn(router)
|
||||
api.neutron.port_list(IsA(http.HttpRequest),
|
||||
@ -750,11 +752,14 @@ class RouterRuleTests(RouterMixin, test.TestCase):
|
||||
self._test_router_addrouterrule(raise_error=True)
|
||||
|
||||
@test.create_stubs({api.neutron: ('router_get', 'router_update',
|
||||
'port_list', 'network_get')})
|
||||
'port_list', 'network_get',
|
||||
'is_extension_supported')})
|
||||
def test_router_removerouterrule(self):
|
||||
pre_router = self.routers_with_rules.first()
|
||||
post_router = copy.deepcopy(pre_router)
|
||||
rule = post_router['router_rules'].pop()
|
||||
api.neutron.is_extension_supported(IsA(http.HttpRequest), 'extraroute')\
|
||||
.AndReturn(False)
|
||||
api.neutron.router_get(IsA(http.HttpRequest),
|
||||
pre_router.id).AndReturn(pre_router)
|
||||
params = {}
|
||||
@ -779,7 +784,8 @@ class RouterRuleTests(RouterMixin, test.TestCase):
|
||||
|
||||
@test.create_stubs({api.neutron: ('router_get', 'router_update',
|
||||
'network_list', 'port_list',
|
||||
'network_get')})
|
||||
'network_get',
|
||||
'is_extension_supported')})
|
||||
def test_router_resetrouterrules(self):
|
||||
pre_router = self.routers_with_rules.first()
|
||||
post_router = copy.deepcopy(pre_router)
|
||||
@ -787,6 +793,8 @@ class RouterRuleTests(RouterMixin, test.TestCase):
|
||||
'action': 'permit', 'nexthops': [], 'id': '2'}]
|
||||
del post_router['router_rules'][:]
|
||||
post_router['router_rules'].extend(default_rules)
|
||||
api.neutron.is_extension_supported(IsA(http.HttpRequest), 'extraroute')\
|
||||
.AndReturn(False)
|
||||
api.neutron.router_get(IsA(http.HttpRequest),
|
||||
pre_router.id).AndReturn(post_router)
|
||||
params = {}
|
||||
@ -810,6 +818,96 @@ class RouterRuleTests(RouterMixin, test.TestCase):
|
||||
self.assertNoFormErrors(res)
|
||||
|
||||
|
||||
class RouterRouteTests(RouterMixin, test.TestCase):
|
||||
DASHBOARD = 'project'
|
||||
INDEX_URL = reverse('horizon:%s:routers:index' % DASHBOARD)
|
||||
DETAIL_PATH = 'horizon:%s:routers:detail' % DASHBOARD
|
||||
|
||||
def test_extension_hides_without_routes(self):
|
||||
router = self.routers_with_routes.first()
|
||||
res = self._get_detail(router, extraroute=False)
|
||||
|
||||
self.assertTemplateUsed(res, '%s/routers/detail.html' % self.DASHBOARD)
|
||||
self.assertNotIn('extra_routes_table', res.context)
|
||||
|
||||
def test_routerroute_detail(self):
|
||||
router = self.routers_with_routes.first()
|
||||
res = self._get_detail(router, extraroute=True)
|
||||
|
||||
self.assertTemplateUsed(res, '%s/routers/detail.html' % self.DASHBOARD)
|
||||
routes = res.context['extra_routes_table'].data
|
||||
routes_dict = [r._apidict for r in routes]
|
||||
self.assertItemsEqual(routes_dict, router['routes'])
|
||||
|
||||
@test.create_stubs({api.neutron: ('router_get', 'router_update')})
|
||||
def _test_router_addrouterroute(self, raise_error=False):
|
||||
pre_router = self.routers_with_routes.first()
|
||||
post_router = copy.deepcopy(pre_router)
|
||||
route = {'nexthop': '10.0.0.5', 'destination': '40.0.1.0/24'}
|
||||
post_router['routes'].insert(0, route)
|
||||
api.neutron.router_get(IsA(http.HttpRequest), pre_router.id)\
|
||||
.MultipleTimes().AndReturn(pre_router)
|
||||
params = {}
|
||||
params['routes'] = post_router['routes']
|
||||
router_update = api.neutron.router_update(IsA(http.HttpRequest),
|
||||
pre_router.id, **params)
|
||||
if raise_error:
|
||||
router_update.AndRaise(self.exceptions.neutron)
|
||||
else:
|
||||
router_update.AndReturn({'router': post_router})
|
||||
self.mox.ReplayAll()
|
||||
|
||||
form_data = copy.deepcopy(route)
|
||||
form_data['router_id'] = pre_router.id
|
||||
url = reverse('horizon:%s:routers:addrouterroute' % self.DASHBOARD,
|
||||
args=[pre_router.id])
|
||||
res = self.client.post(url, form_data)
|
||||
self.assertNoFormErrors(res)
|
||||
detail_url = reverse(self.DETAIL_PATH, args=[pre_router.id])
|
||||
self.assertRedirectsNoFollow(res, detail_url)
|
||||
|
||||
def test_router_addrouterroute(self):
|
||||
if self.DASHBOARD == 'project':
|
||||
self._test_router_addrouterroute()
|
||||
self.assertMessageCount(success=1)
|
||||
|
||||
def test_router_addrouterroute_exception(self):
|
||||
if self.DASHBOARD == 'project':
|
||||
self._test_router_addrouterroute(raise_error=True)
|
||||
self.assertMessageCount(error=1)
|
||||
|
||||
@test.create_stubs({api.neutron: ('router_get', 'router_update',
|
||||
'network_get', 'port_list',
|
||||
'is_extension_supported')})
|
||||
def test_router_removeroute(self):
|
||||
if self.DASHBOARD == 'admin':
|
||||
return
|
||||
pre_router = self.routers_with_routes.first()
|
||||
post_router = copy.deepcopy(pre_router)
|
||||
route = post_router['routes'].pop()
|
||||
api.neutron.is_extension_supported(IsA(http.HttpRequest), 'extraroute')\
|
||||
.MultipleTimes().AndReturn(True)
|
||||
api.neutron.router_get(IsA(http.HttpRequest),
|
||||
pre_router.id).AndReturn(pre_router)
|
||||
params = {}
|
||||
params['routes'] = post_router['routes']
|
||||
api.neutron.router_get(IsA(http.HttpRequest),
|
||||
pre_router.id).AndReturn(pre_router)
|
||||
router_update = api.neutron.router_update(IsA(http.HttpRequest),
|
||||
pre_router.id, **params)
|
||||
router_update.AndReturn({'router': post_router})
|
||||
api.neutron.port_list(IsA(http.HttpRequest),
|
||||
device_id=pre_router.id)\
|
||||
.AndReturn([self.ports.first()])
|
||||
self._mock_external_network_get(pre_router)
|
||||
self.mox.ReplayAll()
|
||||
form_route_id = route['nexthop'] + ":" + route['destination']
|
||||
form_data = {'action': 'extra_routes__delete__%s' % form_route_id}
|
||||
url = reverse(self.DETAIL_PATH, args=[pre_router.id])
|
||||
res = self.client.post(url, form_data)
|
||||
self.assertNoFormErrors(res)
|
||||
|
||||
|
||||
class RouterViewTests(RouterMixin, test.TestCase):
|
||||
DASHBOARD = 'project'
|
||||
INDEX_URL = reverse('horizon:%s:routers:index' % DASHBOARD)
|
||||
|
@ -15,6 +15,8 @@
|
||||
from django.conf.urls import patterns
|
||||
from django.conf.urls import url
|
||||
|
||||
from openstack_dashboard.dashboards.project.routers.extensions.extraroutes\
|
||||
import views as er_views
|
||||
from openstack_dashboard.dashboards.project.routers.extensions.routerrules\
|
||||
import views as rr_views
|
||||
from openstack_dashboard.dashboards.project.routers.ports \
|
||||
@ -41,6 +43,9 @@ urlpatterns = patterns(
|
||||
url(ROUTER_URL % 'addrouterrule',
|
||||
rr_views.AddRouterRuleView.as_view(),
|
||||
name='addrouterrule'),
|
||||
url(ROUTER_URL % 'addrouterroute',
|
||||
er_views.AddRouterRouteView.as_view(),
|
||||
name='addrouterroute'),
|
||||
url(ROUTER_URL % 'setgateway',
|
||||
port_views.SetGatewayView.as_view(),
|
||||
name='setgateway'),
|
||||
|
@ -11,6 +11,7 @@
|
||||
# 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 copy
|
||||
|
||||
import uuid
|
||||
|
||||
@ -289,6 +290,50 @@ class NeutronApiTests(test.APITestCase):
|
||||
self.assertFalse(
|
||||
api.neutron.is_extension_supported(self.request, 'doesntexist'))
|
||||
|
||||
def test_router_static_route_list(self):
|
||||
router = {'router': self.api_routers_with_routes.first()}
|
||||
router_id = self.api_routers_with_routes.first()['id']
|
||||
|
||||
neutronclient = self.stub_neutronclient()
|
||||
neutronclient.show_router(router_id).AndReturn(router)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
ret_val = api.neutron.router_static_route_list(self.request, router_id)
|
||||
self.assertIsInstance(ret_val[0], api.neutron.RouterStaticRoute)
|
||||
|
||||
def test_router_static_route_remove(self):
|
||||
router = {'router': self.api_routers_with_routes.first()}
|
||||
router_id = self.api_routers_with_routes.first()['id']
|
||||
post_router = copy.deepcopy(router)
|
||||
route = api.neutron.RouterStaticRoute(post_router['router']
|
||||
['routes'].pop())
|
||||
|
||||
neutronclient = self.stub_neutronclient()
|
||||
neutronclient.show_router(router_id).AndReturn(router)
|
||||
body = {'router': {'routes': post_router['router']['routes']}}
|
||||
neutronclient.update_router(router_id, body=body)\
|
||||
.AndReturn(post_router)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
api.neutron.router_static_route_remove(self.request,
|
||||
router_id, route.id)
|
||||
|
||||
def test_router_static_route_add(self):
|
||||
router = {'router': self.api_routers_with_routes.first()}
|
||||
router_id = self.api_routers_with_routes.first()['id']
|
||||
post_router = copy.deepcopy(router)
|
||||
route = {'nexthop': '10.0.0.5', 'destination': '40.0.1.0/24'}
|
||||
post_router['router']['routes'].insert(0, route)
|
||||
body = {'router': {'routes': post_router['router']['routes']}}
|
||||
|
||||
neutronclient = self.stub_neutronclient()
|
||||
neutronclient.show_router(router_id).AndReturn(router)
|
||||
neutronclient.update_router(router_id, body=body)\
|
||||
.AndReturn(post_router)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
api.neutron.router_static_route_add(self.request, router_id, route)
|
||||
|
||||
# NOTE(amotoki): "dvr" permission tests check most of
|
||||
# get_feature_permission features.
|
||||
# These tests are not specific to "dvr" extension.
|
||||
|
@ -31,6 +31,7 @@ def data(TEST):
|
||||
TEST.ports = utils.TestDataContainer()
|
||||
TEST.routers = utils.TestDataContainer()
|
||||
TEST.routers_with_rules = utils.TestDataContainer()
|
||||
TEST.routers_with_routes = utils.TestDataContainer()
|
||||
TEST.q_floating_ips = utils.TestDataContainer()
|
||||
TEST.q_secgroups = utils.TestDataContainer()
|
||||
TEST.q_secgroup_rules = utils.TestDataContainer()
|
||||
@ -58,6 +59,7 @@ def data(TEST):
|
||||
TEST.api_subnets = utils.TestDataContainer()
|
||||
TEST.api_ports = utils.TestDataContainer()
|
||||
TEST.api_routers = utils.TestDataContainer()
|
||||
TEST.api_routers_with_routes = utils.TestDataContainer()
|
||||
TEST.api_q_floating_ips = utils.TestDataContainer()
|
||||
TEST.api_q_secgroups = utils.TestDataContainer()
|
||||
TEST.api_q_secgroup_rules = utils.TestDataContainer()
|
||||
@ -404,6 +406,20 @@ def data(TEST):
|
||||
'nexthops': ['1.0.0.2', '1.0.0.1']}]}
|
||||
TEST.api_routers.add(router_dict)
|
||||
TEST.routers_with_rules.add(neutron.Router(router_dict))
|
||||
router_dict_with_route = {'id': '725c24c9-061b-416b-b9d4-012392b32fd9',
|
||||
'name': 'routerouter',
|
||||
'status': 'ACTIVE',
|
||||
'admin_state_up': True,
|
||||
'distributed': False,
|
||||
'external_gateway_info':
|
||||
{'network_id': ext_net['id']},
|
||||
'tenant_id': '1',
|
||||
'routes': [{'nexthop': '10.0.0.1',
|
||||
'destination': '172.0.0.0/24'},
|
||||
{'nexthop': '10.0.0.2',
|
||||
'destination': '172.1.0.0/24'}]}
|
||||
TEST.api_routers_with_routes.add(router_dict_with_route)
|
||||
TEST.routers_with_routes.add(neutron.Router(router_dict_with_route))
|
||||
|
||||
# Floating IP.
|
||||
# Unassociated.
|
||||
|
Loading…
Reference in New Issue
Block a user