Support Quantum L3 function
Implements bp quantum-l3-support Implemented basic CRD for router Add/remove interface support Support set gateway and clear gateway Change-Id: Ie4cac962eb8fadc021c80cf05e2aa63caab3c00a
This commit is contained in:
parent
31d55e503d
commit
eed092a5bb
@ -78,6 +78,15 @@ class Port(QuantumAPIDictWrapper):
|
|||||||
super(Port, self).__init__(apiresource)
|
super(Port, self).__init__(apiresource)
|
||||||
|
|
||||||
|
|
||||||
|
class Router(QuantumAPIDictWrapper):
|
||||||
|
"""Wrapper for quantum routers"""
|
||||||
|
|
||||||
|
def __init__(self, apiresource):
|
||||||
|
#apiresource['admin_state'] = \
|
||||||
|
# 'UP' if apiresource['admin_state_up'] else 'DOWN'
|
||||||
|
super(Router, self).__init__(apiresource)
|
||||||
|
|
||||||
|
|
||||||
IP_VERSION_DICT = {4: 'IPv4', 6: 'IPv6'}
|
IP_VERSION_DICT = {4: 'IPv4', 6: 'IPv6'}
|
||||||
|
|
||||||
|
|
||||||
@ -104,7 +113,7 @@ def network_list(request, **params):
|
|||||||
subnet_dict = SortedDict([(s['id'], s) for s in subnets])
|
subnet_dict = SortedDict([(s['id'], s) for s in subnets])
|
||||||
# Expand subnet list from subnet_id to values.
|
# Expand subnet list from subnet_id to values.
|
||||||
for n in networks:
|
for n in networks:
|
||||||
n['subnets'] = [subnet_dict[s] for s in n['subnets']]
|
n['subnets'] = [subnet_dict.get(s) for s in n.get('subnets', [])]
|
||||||
return [Network(n) for n in networks]
|
return [Network(n) for n in networks]
|
||||||
|
|
||||||
|
|
||||||
@ -256,3 +265,53 @@ def port_modify(request, port_id, **kwargs):
|
|||||||
body = {'port': kwargs}
|
body = {'port': kwargs}
|
||||||
port = quantumclient(request).update_port(port_id, body=body).get('port')
|
port = quantumclient(request).update_port(port_id, body=body).get('port')
|
||||||
return Port(port)
|
return Port(port)
|
||||||
|
|
||||||
|
|
||||||
|
def router_create(request, **kwargs):
|
||||||
|
LOG.debug("router_create():, kwargs=%s" % kwargs)
|
||||||
|
body = {'router': {}}
|
||||||
|
body['router'].update(kwargs)
|
||||||
|
router = quantumclient(request).create_router(body=body).get('router')
|
||||||
|
return Router(router)
|
||||||
|
|
||||||
|
|
||||||
|
def router_get(request, router_id, **params):
|
||||||
|
router = quantumclient(request).show_router(router_id,
|
||||||
|
**params).get('router')
|
||||||
|
return Router(router)
|
||||||
|
|
||||||
|
|
||||||
|
def router_list(request, **params):
|
||||||
|
routers = quantumclient(request).list_routers(**params).get('routers')
|
||||||
|
return [Router(r) for r in routers]
|
||||||
|
|
||||||
|
|
||||||
|
def router_delete(request, router_id):
|
||||||
|
quantumclient(request).delete_router(router_id)
|
||||||
|
|
||||||
|
|
||||||
|
def router_add_interface(request, router_id, subnet_id=None, port_id=None):
|
||||||
|
body = {}
|
||||||
|
if subnet_id:
|
||||||
|
body['subnet_id'] = subnet_id
|
||||||
|
if port_id:
|
||||||
|
body['port_id'] = port_id
|
||||||
|
quantumclient(request).add_interface_router(router_id, body)
|
||||||
|
|
||||||
|
|
||||||
|
def router_remove_interface(request, router_id, subnet_id=None, port_id=None):
|
||||||
|
body = {}
|
||||||
|
if subnet_id:
|
||||||
|
body['subnet_id'] = subnet_id
|
||||||
|
if port_id:
|
||||||
|
body['port_id'] = port_id
|
||||||
|
quantumclient(request).remove_interface_router(router_id, body)
|
||||||
|
|
||||||
|
|
||||||
|
def router_add_gateway(request, router_id, network_id):
|
||||||
|
body = {'network_id': network_id}
|
||||||
|
quantumclient(request).add_gateway_router(router_id, body)
|
||||||
|
|
||||||
|
|
||||||
|
def router_remove_gateway(request, router_id):
|
||||||
|
quantumclient(request).remove_gateway_router(router_id)
|
||||||
|
@ -23,7 +23,7 @@ class SystemPanels(horizon.PanelGroup):
|
|||||||
slug = "admin"
|
slug = "admin"
|
||||||
name = _("System Panel")
|
name = _("System Panel")
|
||||||
panels = ('overview', 'instances', 'volumes', 'flavors',
|
panels = ('overview', 'instances', 'volumes', 'flavors',
|
||||||
'images', 'projects', 'users', 'networks', 'info')
|
'images', 'projects', 'users', 'networks', 'routers', 'info')
|
||||||
|
|
||||||
|
|
||||||
class Admin(horizon.Dashboard):
|
class Admin(horizon.Dashboard):
|
||||||
|
71
openstack_dashboard/dashboards/admin/routers/forms.py
Normal file
71
openstack_dashboard/dashboards/admin/routers/forms.py
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
# 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 False
|
||||||
|
|
||||||
|
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
|
29
openstack_dashboard/dashboards/admin/routers/panel.py
Normal file
29
openstack_dashboard/dashboards/admin/routers/panel.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
import horizon
|
||||||
|
|
||||||
|
from openstack_dashboard.dashboards.admin import dashboard
|
||||||
|
|
||||||
|
|
||||||
|
class Routers(horizon.Panel):
|
||||||
|
name = "Routers"
|
||||||
|
slug = 'routers'
|
||||||
|
permissions = ('openstack.services.network',)
|
||||||
|
|
||||||
|
dashboard.Admin.register(Routers)
|
31
openstack_dashboard/dashboards/admin/routers/ports/forms.py
Normal file
31
openstack_dashboard/dashboards/admin/routers/ports/forms.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# 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:detail'
|
64
openstack_dashboard/dashboards/admin/routers/ports/tables.py
Normal file
64
openstack_dashboard/dashboards/admin/routers/ports/tables.py
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2012 NEC Corporation
|
||||||
|
#
|
||||||
|
# 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 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
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class SetGateway(r_tables.SetGateway):
|
||||||
|
url = "horizon:admin:routers:setgateway"
|
||||||
|
|
||||||
|
|
||||||
|
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"))
|
||||||
|
admin_state = tables.Column("admin_state",
|
||||||
|
verbose_name=_("Admin State"))
|
||||||
|
|
||||||
|
def get_object_display(self, port):
|
||||||
|
return port.id
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
name = "interfaces"
|
||||||
|
verbose_name = _("Interfaces")
|
||||||
|
table_actions = (AddInterface, SetGateway, RemoveInterface)
|
||||||
|
row_actions = (RemoveInterface, )
|
31
openstack_dashboard/dashboards/admin/routers/ports/tabs.py
Normal file
31
openstack_dashboard/dashboards/admin/routers/ports/tabs.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# 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 horizon import tabs
|
||||||
|
from openstack_dashboard.dashboards.project.routers.ports import tabs as r_tabs
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class OverviewTab(r_tabs.OverviewTab):
|
||||||
|
template_name = "admin/networks/ports/_detail_overview.html"
|
||||||
|
failure_url = "horizon:admin:routers:index"
|
||||||
|
|
||||||
|
|
||||||
|
class PortDetailTabs(tabs.TabGroup):
|
||||||
|
slug = "port_details"
|
||||||
|
tabs = (OverviewTab,)
|
24
openstack_dashboard/dashboards/admin/routers/ports/urls.py
Normal file
24
openstack_dashboard/dashboards/admin/routers/ports/urls.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2012 NTT MCL
|
||||||
|
#
|
||||||
|
# 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.defaults import patterns, url
|
||||||
|
|
||||||
|
from .views import DetailView
|
||||||
|
|
||||||
|
PORTS = r'^(?P<port_id>[^/]+)/%s$'
|
||||||
|
|
||||||
|
urlpatterns = patterns('horizon.dashboards.admin.networks.ports.views',
|
||||||
|
url(PORTS % 'detail', DetailView.as_view(), name='detail'))
|
43
openstack_dashboard/dashboards/admin/routers/ports/views.py
Normal file
43
openstack_dashboard/dashboards/admin/routers/ports/views.py
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# 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 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
|
||||||
|
success_url = 'horizon:admin:routers:detail'
|
||||||
|
failure_url = 'horizon:admin:routers:detail'
|
||||||
|
|
||||||
|
|
||||||
|
class DetailView(tabs.TabView):
|
||||||
|
tab_group_class = PortDetailTabs
|
||||||
|
template_name = 'admin/networks/ports/detail.html'
|
71
openstack_dashboard/dashboards/admin/routers/tables.py
Normal file
71
openstack_dashboard/dashboards/admin/routers/tables.py
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
# 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 django.template.defaultfilters import title
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from horizon import tables
|
||||||
|
from openstack_dashboard import api
|
||||||
|
from openstack_dashboard.dashboards.project.routers import tables as r_tables
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class DeleteRouter(r_tables.DeleteRouter):
|
||||||
|
redirect_url = "horizon:admin:routers:index"
|
||||||
|
|
||||||
|
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 UpdateRow(tables.Row):
|
||||||
|
ajax = True
|
||||||
|
|
||||||
|
def get_data(self, request, router_id):
|
||||||
|
router = api.router_get(request, router_id)
|
||||||
|
return router
|
||||||
|
|
||||||
|
|
||||||
|
class RoutersTable(tables.DataTable):
|
||||||
|
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)
|
||||||
|
|
||||||
|
def get_object_display(self, obj):
|
||||||
|
return obj.name
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
name = "Routers"
|
||||||
|
verbose_name = _("Routers")
|
||||||
|
status_columns = ["status"]
|
||||||
|
row_class = UpdateRow
|
||||||
|
table_actions = (CreateRouter, DeleteRouter)
|
||||||
|
row_actions = (DeleteRouter, )
|
28
openstack_dashboard/dashboards/admin/routers/tabs.py
Normal file
28
openstack_dashboard/dashboards/admin/routers/tabs.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
from horizon import tabs
|
||||||
|
from openstack_dashboard.dashboards.project.routers import tabs as r_tabs
|
||||||
|
|
||||||
|
|
||||||
|
class OverviewTab(r_tabs.OverviewTab):
|
||||||
|
template_name = ("admin/routers/_detail_overview.html")
|
||||||
|
redirect_url = 'horizon:admin:routers:index'
|
||||||
|
|
||||||
|
|
||||||
|
class RouterDetailTabs(tabs.TabGroup):
|
||||||
|
slug = "router_details"
|
||||||
|
tabs = (OverviewTab,)
|
@ -0,0 +1,21 @@
|
|||||||
|
{% 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 %}
|
@ -0,0 +1,15 @@
|
|||||||
|
{% load i18n sizeformat parse_date %}
|
||||||
|
|
||||||
|
<h3>{% trans "Router Overview" %}: {{router.display_name }}</h3>
|
||||||
|
|
||||||
|
<div class="info detail">
|
||||||
|
<dl>
|
||||||
|
<dt>{% trans "Name" %}</dt>
|
||||||
|
<dd>{{ router.display_name }}</dd>
|
||||||
|
<dt>{% trans "ID" %}</dt>
|
||||||
|
<dd>{{ router.id }}</dd>
|
||||||
|
<dt>{% trans "Status" %}</dt>
|
||||||
|
<dd>{{ router.status|capfirst }}</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,11 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% block title %}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 %}
|
@ -0,0 +1,15 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% block title %}{% trans "Router Details" %}{% endblock %}
|
||||||
|
|
||||||
|
{% block page_header %}
|
||||||
|
{% include "horizon/common/_page_header.html" with title=_("Router Detail") %}
|
||||||
|
{% endblock page_header %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
{% include "admin/routers/_detail_overview.html" %}
|
||||||
|
<hr>
|
||||||
|
<div id="interfaces">
|
||||||
|
{{ interfaces_table.render }}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
@ -0,0 +1,11 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% block title %}{% trans "routers" %}{% endblock %}
|
||||||
|
|
||||||
|
{% block page_header %}
|
||||||
|
{% include "horizon/common/_page_header.html" with title=_("routers") %}
|
||||||
|
{% endblock page_header %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
{{ table.render }}
|
||||||
|
{% endblock %}
|
104
openstack_dashboard/dashboards/admin/routers/tests.py
Normal file
104
openstack_dashboard/dashboards/admin/routers/tests.py
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
# 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.
|
||||||
|
from mox import IsA
|
||||||
|
from django import http
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
|
||||||
|
from openstack_dashboard import api
|
||||||
|
from openstack_dashboard.dashboards.project.routers import tests as r_test
|
||||||
|
from openstack_dashboard.test import helpers as test
|
||||||
|
|
||||||
|
|
||||||
|
class RouterTests(test.BaseAdminViewTests, r_test.RouterTests):
|
||||||
|
DASHBOARD = 'admin'
|
||||||
|
INDEX_URL = reverse('horizon:%s:routers:index' % DASHBOARD)
|
||||||
|
DETAIL_PATH = 'horizon:%s:routers:detail' % DASHBOARD
|
||||||
|
|
||||||
|
@test.create_stubs({api.quantum: ('router_list',),
|
||||||
|
api.keystone: ('tenant_list',)})
|
||||||
|
def test_index(self):
|
||||||
|
tenants = self.tenants.list()
|
||||||
|
api.quantum.router_list(
|
||||||
|
IsA(http.HttpRequest),
|
||||||
|
search_opts=None).AndReturn(self.routers.list())
|
||||||
|
api.keystone.tenant_list(IsA(http.HttpRequest), admin=True)\
|
||||||
|
.AndReturn(tenants)
|
||||||
|
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
res = self.client.get(self.INDEX_URL)
|
||||||
|
|
||||||
|
self.assertTemplateUsed(res, '%s/routers/index.html' % self.DASHBOARD)
|
||||||
|
routers = res.context['table'].data
|
||||||
|
self.assertItemsEqual(routers, self.routers.list())
|
||||||
|
|
||||||
|
@test.create_stubs({api.quantum: ('router_list',),
|
||||||
|
api.keystone: ('tenant_list',)})
|
||||||
|
def test_index_router_list_exception(self):
|
||||||
|
tenants = self.tenants.list()
|
||||||
|
api.quantum.router_list(
|
||||||
|
IsA(http.HttpRequest),
|
||||||
|
search_opts=None).AndRaise(self.exceptions.quantum)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
res = self.client.get(self.INDEX_URL)
|
||||||
|
|
||||||
|
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)
|
34
openstack_dashboard/dashboards/admin/routers/urls.py
Normal file
34
openstack_dashboard/dashboards/admin/routers/urls.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
from django.conf.urls.defaults import patterns, url
|
||||||
|
|
||||||
|
from .views import (IndexView, CreateView, DetailView)
|
||||||
|
from .ports.views import (AddInterfaceView, SetGatewayView)
|
||||||
|
|
||||||
|
|
||||||
|
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'),
|
||||||
|
)
|
76
openstack_dashboard/dashboards/admin/routers/views.py
Normal file
76
openstack_dashboard/dashboards/admin/routers/views.py
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
# 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_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
|
||||||
|
from openstack_dashboard.dashboards.project.routers import views as r_views
|
||||||
|
|
||||||
|
from .ports.tables import PortsTable
|
||||||
|
from .forms import CreateForm
|
||||||
|
from .tables import RoutersTable
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class IndexView(views.IndexView):
|
||||||
|
table_class = RoutersTable
|
||||||
|
template_name = 'admin/routers/index.html'
|
||||||
|
|
||||||
|
def _get_routers(self, search_opts=None):
|
||||||
|
try:
|
||||||
|
routers = api.quantum.router_list(self.request,
|
||||||
|
search_opts=search_opts)
|
||||||
|
except:
|
||||||
|
routers = []
|
||||||
|
exceptions.handle(self.request,
|
||||||
|
_('Unable to retrieve router list.'))
|
||||||
|
if routers:
|
||||||
|
tenant_dict = self._get_tenant_list()
|
||||||
|
for r in routers:
|
||||||
|
# Set tenant name
|
||||||
|
tenant = tenant_dict.get(r.tenant_id, None)
|
||||||
|
r.tenant_name = getattr(tenant, 'name', None)
|
||||||
|
# If name is empty use UUID as name
|
||||||
|
r.set_id_as_name_if_empty()
|
||||||
|
return routers
|
||||||
|
|
||||||
|
def get_data(self):
|
||||||
|
routers = self._get_routers()
|
||||||
|
return routers
|
||||||
|
|
||||||
|
|
||||||
|
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")
|
@ -27,7 +27,8 @@ class BasePanels(horizon.PanelGroup):
|
|||||||
'volumes',
|
'volumes',
|
||||||
'images_and_snapshots',
|
'images_and_snapshots',
|
||||||
'access_and_security',
|
'access_and_security',
|
||||||
'networks')
|
'networks',
|
||||||
|
'routers')
|
||||||
|
|
||||||
|
|
||||||
class ObjectStorePanels(horizon.PanelGroup):
|
class ObjectStorePanels(horizon.PanelGroup):
|
||||||
|
41
openstack_dashboard/dashboards/project/routers/forms.py
Normal file
41
openstack_dashboard/dashboards/project/routers/forms.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2012, Nachi Ueno, NTT MCL, Inc.
|
||||||
|
# All rights reserved.
|
||||||
|
|
||||||
|
"""
|
||||||
|
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"))
|
||||||
|
failure_url = 'horizon:project:routers:index'
|
||||||
|
|
||||||
|
def __init__(self, request, *args, **kwargs):
|
||||||
|
super(CreateForm, self).__init__(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def handle(self, request, data):
|
||||||
|
try:
|
||||||
|
router = api.quantum.router_create(request,
|
||||||
|
name=data['name'])
|
||||||
|
message = 'Router created "%s"' % data['name']
|
||||||
|
messages.success(request, message)
|
||||||
|
return router
|
||||||
|
except:
|
||||||
|
msg = _('Failed to create router "%s".') % data['name']
|
||||||
|
LOG.info(msg)
|
||||||
|
redirect = reverse(self.failure_url)
|
||||||
|
exceptions.handle(request, msg, redirect=redirect)
|
||||||
|
return False
|
29
openstack_dashboard/dashboards/project/routers/panel.py
Normal file
29
openstack_dashboard/dashboards/project/routers/panel.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
import horizon
|
||||||
|
|
||||||
|
from openstack_dashboard.dashboards.project import dashboard
|
||||||
|
|
||||||
|
|
||||||
|
class Routers(horizon.Panel):
|
||||||
|
name = "Routers"
|
||||||
|
slug = 'routers'
|
||||||
|
permissions = ('openstack.services.network',)
|
||||||
|
|
||||||
|
dashboard.Project.register(Routers)
|
132
openstack_dashboard/dashboards/project/routers/ports/forms.py
Normal file
132
openstack_dashboard/dashboards/project/routers/ports/forms.py
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
# 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 django.core.urlresolvers import reverse
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from horizon import forms
|
||||||
|
from horizon import messages
|
||||||
|
from horizon import exceptions
|
||||||
|
from openstack_dashboard import api
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class AddInterface(forms.SelfHandlingForm):
|
||||||
|
subnet_id = forms.ChoiceField(label=_("Subnet ID"), required=False)
|
||||||
|
router_id = forms.CharField(label=_("Router ID"),
|
||||||
|
widget=forms.TextInput(
|
||||||
|
attrs={'readonly': 'readonly'}))
|
||||||
|
failure_url = 'horizon:project:routers:detail'
|
||||||
|
|
||||||
|
def __init__(self, request, *args, **kwargs):
|
||||||
|
super(AddInterface, self).__init__(request, *args, **kwargs)
|
||||||
|
c = self.populate_subnet_id_choices(request)
|
||||||
|
self.fields['subnet_id'].choices = c
|
||||||
|
|
||||||
|
def populate_subnet_id_choices(self, request):
|
||||||
|
tenant_id = self.request.user.tenant_id
|
||||||
|
networks = []
|
||||||
|
try:
|
||||||
|
networks = api.quantum.network_list_for_tenant(request, tenant_id)
|
||||||
|
except Exception as e:
|
||||||
|
msg = _('Failed to get network list %s') % e.message
|
||||||
|
LOG.info(msg)
|
||||||
|
messages.error(request, msg)
|
||||||
|
redirect = reverse(self.failure_url,
|
||||||
|
args=[request.REQUEST['router_id']])
|
||||||
|
exceptions.handle(request, msg, redirect=redirect)
|
||||||
|
return
|
||||||
|
|
||||||
|
choices = []
|
||||||
|
for n in networks:
|
||||||
|
net_name = n.name + ': ' if n.name else ''
|
||||||
|
choices += [(subnet.id,
|
||||||
|
'%s%s (%s)' % (net_name, subnet.cidr,
|
||||||
|
subnet.name or subnet.id))
|
||||||
|
for subnet in n['subnets']]
|
||||||
|
if choices:
|
||||||
|
choices.insert(0, ("", _("Select Subnet")))
|
||||||
|
else:
|
||||||
|
choices.insert(0, ("", _("No subnets available.")))
|
||||||
|
return choices
|
||||||
|
|
||||||
|
def handle(self, request, data):
|
||||||
|
try:
|
||||||
|
api.quantum.router_add_interface(request,
|
||||||
|
data['router_id'],
|
||||||
|
subnet_id=data['subnet_id'])
|
||||||
|
msg = _('Interface added')
|
||||||
|
LOG.debug(msg)
|
||||||
|
messages.success(request, msg)
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
msg = _('Failed to add_interface %s') % e.message
|
||||||
|
LOG.info(msg)
|
||||||
|
messages.error(request, msg)
|
||||||
|
redirect = reverse(self.failure_url, args=[data['router_id']])
|
||||||
|
exceptions.handle(request, msg, redirect=redirect)
|
||||||
|
|
||||||
|
|
||||||
|
class SetGatewayForm(forms.SelfHandlingForm):
|
||||||
|
network_id = forms.ChoiceField(label=_("Network ID"), required=False)
|
||||||
|
router_id = forms.CharField(label=_("Router ID"),
|
||||||
|
widget=forms.TextInput(
|
||||||
|
attrs={'readonly': 'readonly'}))
|
||||||
|
failure_url = 'horizon:project:routers:detail'
|
||||||
|
|
||||||
|
def __init__(self, request, *args, **kwargs):
|
||||||
|
super(SetGatewayForm, self).__init__(request, *args, **kwargs)
|
||||||
|
c = self.populate_network_id_choices(request)
|
||||||
|
self.fields['network_id'].choices = c
|
||||||
|
|
||||||
|
def populate_network_id_choices(self, request):
|
||||||
|
search_opts = {'router:external': True}
|
||||||
|
try:
|
||||||
|
networks = api.quantum.network_list(request, **search_opts)
|
||||||
|
except Exception as e:
|
||||||
|
msg = _('Failed to get network list %s') % e.message
|
||||||
|
LOG.info(msg)
|
||||||
|
messages.error(request, msg)
|
||||||
|
redirect = reverse(self.failure_url,
|
||||||
|
args=[request.REQUEST['router_id']])
|
||||||
|
exceptions.handle(request, msg, redirect=redirect)
|
||||||
|
return
|
||||||
|
choices = [(network.id, network.name or network.id)
|
||||||
|
for network in networks]
|
||||||
|
if choices:
|
||||||
|
choices.insert(0, ("", _("Select network")))
|
||||||
|
else:
|
||||||
|
choices.insert(0, ("", _("No networks available.")))
|
||||||
|
return choices
|
||||||
|
|
||||||
|
def handle(self, request, data):
|
||||||
|
try:
|
||||||
|
api.quantum.router_add_gateway(request,
|
||||||
|
data['router_id'],
|
||||||
|
data['network_id'])
|
||||||
|
msg = _('Gateway interface is added')
|
||||||
|
LOG.debug(msg)
|
||||||
|
messages.success(request, msg)
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
msg = _('Failed to set gateway %s') % e.message
|
||||||
|
LOG.info(msg)
|
||||||
|
messages.error(request, msg)
|
||||||
|
redirect = reverse(self.failure_url, args=[data['router_id']])
|
||||||
|
exceptions.handle(request, msg, redirect=redirect)
|
103
openstack_dashboard/dashboards/project/routers/ports/tables.py
Normal file
103
openstack_dashboard/dashboards/project/routers/ports/tables.py
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
# 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 django.core.urlresolvers import reverse
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def get_device_owner(port):
|
||||||
|
if port['device_owner'] == 'network:router_gateway':
|
||||||
|
return _('Gateway')
|
||||||
|
else:
|
||||||
|
return ' '
|
||||||
|
|
||||||
|
|
||||||
|
class SetGateway(tables.LinkAction):
|
||||||
|
name = "setgateway"
|
||||||
|
verbose_name = _("Add Gateway Interface")
|
||||||
|
url = "horizon:project:routers:setgateway"
|
||||||
|
classes = ("ajax-modal", "btn-camera")
|
||||||
|
|
||||||
|
def get_link_url(self, datum=None):
|
||||||
|
router_id = self.table.kwargs['router_id']
|
||||||
|
return reverse(self.url, args=(router_id,))
|
||||||
|
|
||||||
|
|
||||||
|
class AddInterface(tables.LinkAction):
|
||||||
|
name = "create"
|
||||||
|
verbose_name = _("Add Interface")
|
||||||
|
url = "horizon:project:routers:addinterface"
|
||||||
|
classes = ("ajax-modal", "btn-create")
|
||||||
|
|
||||||
|
def get_link_url(self, datum=None):
|
||||||
|
router_id = self.table.kwargs['router_id']
|
||||||
|
return reverse(self.url, args=(router_id,))
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveInterface(tables.DeleteAction):
|
||||||
|
data_type_singular = _("Interface")
|
||||||
|
data_type_plural = _("Interfaces")
|
||||||
|
failure_url = 'horizon:project:routers:detail'
|
||||||
|
|
||||||
|
def delete(self, request, obj_id):
|
||||||
|
try:
|
||||||
|
router_id = self.table.kwargs['router_id']
|
||||||
|
port = api.quantum.port_get(request, obj_id)
|
||||||
|
if port['device_owner'] == 'network:router_gateway':
|
||||||
|
api.quantum.router_remove_gateway(request, router_id)
|
||||||
|
else:
|
||||||
|
api.quantum.router_remove_interface(request,
|
||||||
|
router_id,
|
||||||
|
port_id=obj_id)
|
||||||
|
except:
|
||||||
|
msg = _('Failed to delete interface %s') % obj_id
|
||||||
|
LOG.info(msg)
|
||||||
|
router_id = self.table.kwargs['router_id']
|
||||||
|
redirect = reverse(self.failure_url,
|
||||||
|
args=[router_id])
|
||||||
|
exceptions.handle(request, msg, redirect=redirect)
|
||||||
|
|
||||||
|
|
||||||
|
class PortsTable(tables.DataTable):
|
||||||
|
name = tables.Column("name",
|
||||||
|
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"))
|
||||||
|
admin_state = tables.Column("admin_state",
|
||||||
|
verbose_name=_("Admin State"))
|
||||||
|
|
||||||
|
def get_object_display(self, port):
|
||||||
|
return port.id
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
name = "interfaces"
|
||||||
|
verbose_name = _("Interfaces")
|
||||||
|
table_actions = (AddInterface, SetGateway, RemoveInterface)
|
||||||
|
row_actions = (RemoveInterface, )
|
47
openstack_dashboard/dashboards/project/routers/ports/tabs.py
Normal file
47
openstack_dashboard/dashboards/project/routers/ports/tabs.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
# 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 django.core.urlresolvers import reverse
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from horizon import exceptions
|
||||||
|
from horizon import tabs
|
||||||
|
from openstack_dashboard import api
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class OverviewTab(tabs.Tab):
|
||||||
|
name = _("Overview")
|
||||||
|
slug = "overview"
|
||||||
|
template_name = "project/networks/ports/_detail_overview.html"
|
||||||
|
failure_url = 'horizon:project:routers:index'
|
||||||
|
|
||||||
|
def get_context_data(self, request):
|
||||||
|
port_id = self.tab_group.kwargs['port_id']
|
||||||
|
try:
|
||||||
|
port = api.quantum.port_get(self.request, port_id)
|
||||||
|
except:
|
||||||
|
redirect = reverse(self.failure_url)
|
||||||
|
msg = _('Unable to retrieve port details.')
|
||||||
|
exceptions.handle(request, msg, redirect=redirect)
|
||||||
|
return {'port': port}
|
||||||
|
|
||||||
|
|
||||||
|
class PortDetailTabs(tabs.TabGroup):
|
||||||
|
slug = "port_details"
|
||||||
|
tabs = (OverviewTab,)
|
24
openstack_dashboard/dashboards/project/routers/ports/urls.py
Normal file
24
openstack_dashboard/dashboards/project/routers/ports/urls.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
from django.conf.urls.defaults import patterns, url
|
||||||
|
|
||||||
|
from .views import DetailView
|
||||||
|
|
||||||
|
PORTS = r'^(?P<port_id>[^/]+)/%s$'
|
||||||
|
|
||||||
|
urlpatterns = patterns('horizon.dashboards.project.networks.ports.views',
|
||||||
|
url(PORTS % 'detail', DetailView.as_view(), name='detail'))
|
100
openstack_dashboard/dashboards/project/routers/ports/views.py
Normal file
100
openstack_dashboard/dashboards/project/routers/ports/views.py
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
# 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 django.core.urlresolvers import reverse
|
||||||
|
|
||||||
|
from horizon import tabs
|
||||||
|
from horizon import forms
|
||||||
|
from horizon import exceptions
|
||||||
|
from openstack_dashboard import api
|
||||||
|
from .tabs import PortDetailTabs
|
||||||
|
from .forms import (AddInterface, SetGatewayForm)
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class AddInterfaceView(forms.ModalFormView):
|
||||||
|
form_class = AddInterface
|
||||||
|
template_name = 'project/routers/ports/create.html'
|
||||||
|
success_url = 'horizon:project:routers:detail'
|
||||||
|
failure_url = 'horizon:project:routers:detail'
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse(self.success_url,
|
||||||
|
args=(self.kwargs['router_id'],))
|
||||||
|
|
||||||
|
def get_object(self):
|
||||||
|
if not hasattr(self, "_object"):
|
||||||
|
try:
|
||||||
|
router_id = self.kwargs["router_id"]
|
||||||
|
self._object = api.quantum.router_get(self.request,
|
||||||
|
router_id)
|
||||||
|
except:
|
||||||
|
redirect = reverse(self.failure_url, args=[router_id])
|
||||||
|
msg = _("Unable to retrieve router.")
|
||||||
|
exceptions.handle(self.request, msg, redirect=redirect)
|
||||||
|
return self._object
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super(AddInterfaceView, 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}
|
||||||
|
|
||||||
|
|
||||||
|
class SetGatewayView(forms.ModalFormView):
|
||||||
|
form_class = SetGatewayForm
|
||||||
|
template_name = 'project/routers/ports/setgateway.html'
|
||||||
|
success_url = 'horizon:project:routers:detail'
|
||||||
|
failure_url = 'horizon:project:routers:detail'
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse(self.success_url,
|
||||||
|
args=(self.kwargs['router_id'],))
|
||||||
|
|
||||||
|
def get_object(self):
|
||||||
|
if not hasattr(self, "_object"):
|
||||||
|
try:
|
||||||
|
router_id = self.kwargs["router_id"]
|
||||||
|
self._object = api.quantum.router_get(self.request,
|
||||||
|
router_id)
|
||||||
|
except:
|
||||||
|
redirect = reverse(self.failure_url)
|
||||||
|
msg = _("Unable to set gateway.")
|
||||||
|
exceptions.handle(self.request, msg, redirect=redirect)
|
||||||
|
return self._object
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super(SetGatewayView, 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}
|
||||||
|
|
||||||
|
|
||||||
|
class DetailView(tabs.TabView):
|
||||||
|
tab_group_class = PortDetailTabs
|
||||||
|
template_name = 'project/networks/ports/detail.html'
|
90
openstack_dashboard/dashboards/project/routers/tables.py
Normal file
90
openstack_dashboard/dashboards/project/routers/tables.py
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
# 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 django.core.urlresolvers import reverse
|
||||||
|
from django.template.defaultfilters import title
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from horizon import exceptions
|
||||||
|
from horizon import messages
|
||||||
|
from horizon import tables
|
||||||
|
from openstack_dashboard import api
|
||||||
|
from quantumclient.common import exceptions as q_ext
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class DeleteRouter(tables.DeleteAction):
|
||||||
|
data_type_singular = _("Router")
|
||||||
|
data_type_plural = _("Routers")
|
||||||
|
redirect_url = "horizon:project:routers:index"
|
||||||
|
|
||||||
|
def delete(self, request, obj_id):
|
||||||
|
obj = self.table.get_object_by_id(obj_id)
|
||||||
|
name = self.table.get_object_display(obj)
|
||||||
|
try:
|
||||||
|
api.router_delete(request, obj_id)
|
||||||
|
except q_ext.QuantumClientException as e:
|
||||||
|
msg = _('Unable to delete router "%s"') % e.message
|
||||||
|
LOG.info(msg)
|
||||||
|
messages.error(request, msg)
|
||||||
|
redirect = reverse(self.redirect_url)
|
||||||
|
raise exceptions.Http302(redirect, message=msg)
|
||||||
|
except Exception as e:
|
||||||
|
msg = _('Unable to delete router "%s"') % name
|
||||||
|
LOG.info(msg)
|
||||||
|
exceptions.handle(request, msg)
|
||||||
|
|
||||||
|
def allowed(self, request, router=None):
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class CreateRouter(tables.LinkAction):
|
||||||
|
name = "create"
|
||||||
|
verbose_name = _("Create Router")
|
||||||
|
url = "horizon:project:routers:create"
|
||||||
|
classes = ("ajax-modal", "btn-create")
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateRow(tables.Row):
|
||||||
|
ajax = True
|
||||||
|
|
||||||
|
def get_data(self, request, router_id):
|
||||||
|
router = api.router_get(request, router_id)
|
||||||
|
return router
|
||||||
|
|
||||||
|
|
||||||
|
class RoutersTable(tables.DataTable):
|
||||||
|
name = tables.Column("name",
|
||||||
|
verbose_name=_("Name"),
|
||||||
|
link="horizon:project:routers:detail")
|
||||||
|
status = tables.Column("status",
|
||||||
|
filters=(title,),
|
||||||
|
verbose_name=_("Status"),
|
||||||
|
status=True)
|
||||||
|
|
||||||
|
def get_object_display(self, obj):
|
||||||
|
return obj.name
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
name = "Routers"
|
||||||
|
verbose_name = _("Routers")
|
||||||
|
status_columns = ["status"]
|
||||||
|
row_class = UpdateRow
|
||||||
|
table_actions = (CreateRouter, DeleteRouter)
|
||||||
|
row_actions = (DeleteRouter, )
|
45
openstack_dashboard/dashboards/project/routers/tabs.py
Normal file
45
openstack_dashboard/dashboards/project/routers/tabs.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from horizon import exceptions
|
||||||
|
from horizon import tabs
|
||||||
|
from openstack_dashboard import api
|
||||||
|
|
||||||
|
|
||||||
|
class OverviewTab(tabs.Tab):
|
||||||
|
name = _("Overview")
|
||||||
|
slug = "overview"
|
||||||
|
template_name = ("project/routers/_detail_overview.html")
|
||||||
|
redirect_url = 'horizon:project:routers:index'
|
||||||
|
|
||||||
|
def get_context_data(self, request):
|
||||||
|
router_id = self.tab_group.kwargs['router_id']
|
||||||
|
try:
|
||||||
|
router = api.router_get(request, router_id)
|
||||||
|
except:
|
||||||
|
redirect = reverse(redirect_url)
|
||||||
|
exceptions.handle(self.request,
|
||||||
|
_('Unable to retrieve router details.'),
|
||||||
|
redirect=redirect)
|
||||||
|
return {'router': router}
|
||||||
|
|
||||||
|
|
||||||
|
class RouterDetailTabs(tabs.TabGroup):
|
||||||
|
slug = "router_details"
|
||||||
|
tabs = (OverviewTab,)
|
@ -0,0 +1,21 @@
|
|||||||
|
{% extends "horizon/common/_modal_form.html" %}
|
||||||
|
{% load i18n horizon humanize %}
|
||||||
|
|
||||||
|
{% block form_id %}{% endblock %}
|
||||||
|
{% block form_action %}{% url horizon:project: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:project:routers:index %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
|
||||||
|
{% endblock %}
|
@ -0,0 +1,15 @@
|
|||||||
|
{% load i18n sizeformat parse_date %}
|
||||||
|
|
||||||
|
<h3>{% trans "Router Overview" %}: {{router.name|default:"None" }}</h3>
|
||||||
|
|
||||||
|
<div class="info detail">
|
||||||
|
<dl>
|
||||||
|
<dt>{% trans "Name" %}</dt>
|
||||||
|
<dd>{{ router.name|default:"None" }}</dd>
|
||||||
|
<dt>{% trans "ID" %}</dt>
|
||||||
|
<dd>{{ router.id|default:"None" }}</dd>
|
||||||
|
<dt>{% trans "Status" %}</dt>
|
||||||
|
<dd>{{ router.status|default:"Unknown" }}</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,11 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% block title %}Create router{% endblock %}
|
||||||
|
|
||||||
|
{% block page_header %}
|
||||||
|
{% include "horizon/common/_page_header.html" with title=_("Create a router") %}
|
||||||
|
{% endblock page_header %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
{% include 'project/routers/_create.html' %}
|
||||||
|
{% endblock %}
|
@ -0,0 +1,15 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% block title %}{% trans "Router Details" %}{% endblock %}
|
||||||
|
|
||||||
|
{% block page_header %}
|
||||||
|
{% include "horizon/common/_page_header.html" with title=_("Router Detail") %}
|
||||||
|
{% endblock page_header %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
{% include "project/routers/_detail_overview.html" %}
|
||||||
|
<hr>
|
||||||
|
<div id="interfaces">
|
||||||
|
{{ interfaces_table.render }}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
@ -0,0 +1,11 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% block title %}{% trans "routers" %}{% endblock %}
|
||||||
|
|
||||||
|
{% block page_header %}
|
||||||
|
{% include "horizon/common/_page_header.html" with title=_("routers") %}
|
||||||
|
{% endblock page_header %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
{{ table.render }}
|
||||||
|
{% endblock %}
|
@ -0,0 +1,25 @@
|
|||||||
|
{% extends "horizon/common/_modal_form.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block form_id %}add_interface_form{% endblock %}
|
||||||
|
{% block form_action %}{% url horizon:project: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 add interface to the network with subnet_id." %}</p>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block modal-footer %}
|
||||||
|
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Add interface" %}" />
|
||||||
|
<a href="{% url horizon:project:routers:index %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
|
||||||
|
{% endblock %}
|
@ -0,0 +1,25 @@
|
|||||||
|
{% extends "horizon/common/_modal_form.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block form_id %}setgateway_form{% endblock %}
|
||||||
|
{% block form_action %}{% url horizon:project:routers:setgateway router.id %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block modal-header %}{% trans "Add Gateway 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 add inteface for Gateway. In this interface, NAT rule of floating IP will be set." %}</p>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block modal-footer %}
|
||||||
|
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Set Gateway" %}" />
|
||||||
|
<a href="{% url horizon:project:routers:index %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
|
||||||
|
{% endblock %}
|
@ -0,0 +1,11 @@
|
|||||||
|
{% 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 "project/routers/ports/_create.html" %}
|
||||||
|
{% endblock %}
|
@ -0,0 +1,11 @@
|
|||||||
|
{% 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 "project/routers/ports/_setgateway.html" %}
|
||||||
|
{% endblock %}
|
235
openstack_dashboard/dashboards/project/routers/tests.py
Normal file
235
openstack_dashboard/dashboards/project/routers/tests.py
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
# 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.
|
||||||
|
from mox import IsA
|
||||||
|
from django import http
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
|
||||||
|
from openstack_dashboard import api
|
||||||
|
from openstack_dashboard.test import helpers as test
|
||||||
|
|
||||||
|
|
||||||
|
class RouterTests(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_list',)})
|
||||||
|
def test_index(self):
|
||||||
|
api.quantum.router_list(
|
||||||
|
IsA(http.HttpRequest),
|
||||||
|
tenant_id=self.tenant.id,
|
||||||
|
search_opts=None).AndReturn(self.routers.list())
|
||||||
|
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
res = self.client.get(self.INDEX_URL)
|
||||||
|
|
||||||
|
self.assertTemplateUsed(res, '%s/routers/index.html' % self.DASHBOARD)
|
||||||
|
routers = res.context['table'].data
|
||||||
|
self.assertItemsEqual(routers, self.routers.list())
|
||||||
|
|
||||||
|
@test.create_stubs({api.quantum: ('router_list',)})
|
||||||
|
def test_index_router_list_exception(self):
|
||||||
|
api.quantum.router_list(
|
||||||
|
IsA(http.HttpRequest),
|
||||||
|
tenant_id=self.tenant.id,
|
||||||
|
search_opts=None).AndRaise(self.exceptions.quantum)
|
||||||
|
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
res = self.client.get(self.INDEX_URL)
|
||||||
|
|
||||||
|
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_get', 'port_list')})
|
||||||
|
def test_router_detail(self):
|
||||||
|
router_id = self.routers.first().id
|
||||||
|
api.quantum.router_get(IsA(http.HttpRequest), router_id)\
|
||||||
|
.AndReturn(self.routers.first())
|
||||||
|
api.quantum.port_list(IsA(http.HttpRequest),
|
||||||
|
device_id=router_id)\
|
||||||
|
.AndReturn([self.ports.first()])
|
||||||
|
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
res = self.client.get(reverse('horizon:%s'
|
||||||
|
':routers:detail' % self.DASHBOARD,
|
||||||
|
args=[router_id]))
|
||||||
|
|
||||||
|
self.assertTemplateUsed(res, '%s/routers/detail.html' % self.DASHBOARD)
|
||||||
|
ports = res.context['interfaces_table'].data
|
||||||
|
self.assertItemsEqual(ports, [self.ports.first()])
|
||||||
|
|
||||||
|
@test.create_stubs({api.quantum: ('router_get', 'port_list')})
|
||||||
|
def test_router_detail_exception(self):
|
||||||
|
router_id = self.routers.first().id
|
||||||
|
api.quantum.router_get(IsA(http.HttpRequest), router_id)\
|
||||||
|
.AndRaise(self.exceptions.quantum)
|
||||||
|
api.quantum.port_list(IsA(http.HttpRequest),
|
||||||
|
device_id=router_id)\
|
||||||
|
.AndReturn([self.ports.first()])
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
res = self.client.get(reverse('horizon:%s'
|
||||||
|
':routers:detail' % self.DASHBOARD,
|
||||||
|
args=[router_id]))
|
||||||
|
self.assertRedirectsNoFollow(res, self.INDEX_URL)
|
||||||
|
|
||||||
|
@test.create_stubs({api.quantum: ('router_create',)})
|
||||||
|
def test_router_create_post(self):
|
||||||
|
router = self.routers.first()
|
||||||
|
api.quantum.router_create(IsA(http.HttpRequest), name=router.name)\
|
||||||
|
.AndReturn(router)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
form_data = {'name': router.name}
|
||||||
|
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_create',)})
|
||||||
|
def test_router_create_post_exception(self):
|
||||||
|
router = self.routers.first()
|
||||||
|
api.quantum.router_create(IsA(http.HttpRequest), name=router.name)\
|
||||||
|
.AndRaise(self.exceptions.quantum)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
form_data = {'name': router.name}
|
||||||
|
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_get',
|
||||||
|
'router_add_interface',
|
||||||
|
'network_list')})
|
||||||
|
def test_router_addinterface(self):
|
||||||
|
router = self.routers.first()
|
||||||
|
subnet = self.subnets.first()
|
||||||
|
api.quantum.router_add_interface(
|
||||||
|
IsA(http.HttpRequest),
|
||||||
|
router.id,
|
||||||
|
subnet_id=subnet.id).AndReturn(None)
|
||||||
|
api.quantum.router_get(IsA(http.HttpRequest), router.id)\
|
||||||
|
.AndReturn(router)
|
||||||
|
api.quantum.network_list(
|
||||||
|
IsA(http.HttpRequest),
|
||||||
|
shared=False,
|
||||||
|
tenant_id=router['tenant_id']).AndReturn(self.networks.list())
|
||||||
|
api.quantum.network_list(
|
||||||
|
IsA(http.HttpRequest),
|
||||||
|
shared=True).AndReturn([])
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
form_data = {'router_id': router.id,
|
||||||
|
'subnet_id': subnet.id}
|
||||||
|
|
||||||
|
url = reverse('horizon:%s:routers:addinterface' % self.DASHBOARD,
|
||||||
|
args=[router.id])
|
||||||
|
res = self.client.post(url, form_data)
|
||||||
|
self.assertNoFormErrors(res)
|
||||||
|
detail_url = reverse(self.DETAIL_PATH, args=[router.id])
|
||||||
|
self.assertRedirectsNoFollow(res, detail_url)
|
||||||
|
|
||||||
|
@test.create_stubs({api.quantum: ('router_get',
|
||||||
|
'router_add_interface',
|
||||||
|
'network_list')})
|
||||||
|
def test_router_addinterface_exception(self):
|
||||||
|
router = self.routers.first()
|
||||||
|
subnet = self.subnets.first()
|
||||||
|
api.quantum.router_add_interface(
|
||||||
|
IsA(http.HttpRequest),
|
||||||
|
router.id,
|
||||||
|
subnet_id=subnet.id).AndRaise(self.exceptions.quantum)
|
||||||
|
api.quantum.router_get(IsA(http.HttpRequest), router.id)\
|
||||||
|
.AndReturn(router)
|
||||||
|
api.quantum.network_list(
|
||||||
|
IsA(http.HttpRequest),
|
||||||
|
shared=False,
|
||||||
|
tenant_id=router['tenant_id']).AndReturn(self.networks.list())
|
||||||
|
api.quantum.network_list(
|
||||||
|
IsA(http.HttpRequest),
|
||||||
|
shared=True).AndReturn([])
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
form_data = {'router_id': router.id,
|
||||||
|
'subnet_id': subnet.id}
|
||||||
|
|
||||||
|
url = reverse('horizon:%s:routers:addinterface' % self.DASHBOARD,
|
||||||
|
args=[router.id])
|
||||||
|
res = self.client.post(url, form_data)
|
||||||
|
self.assertNoFormErrors(res)
|
||||||
|
detail_url = reverse(self.DETAIL_PATH, args=[router.id])
|
||||||
|
self.assertRedirectsNoFollow(res, detail_url)
|
||||||
|
|
||||||
|
@test.create_stubs({api.quantum: ('router_get',
|
||||||
|
'router_add_gateway',
|
||||||
|
'network_list')})
|
||||||
|
def test_router_add_gateway(self):
|
||||||
|
router = self.routers.first()
|
||||||
|
network = self.networks.first()
|
||||||
|
api.quantum.router_add_gateway(
|
||||||
|
IsA(http.HttpRequest),
|
||||||
|
router.id,
|
||||||
|
network.id).AndReturn(None)
|
||||||
|
api.quantum.router_get(
|
||||||
|
IsA(http.HttpRequest), router.id).AndReturn(router)
|
||||||
|
search_opts = {'router:external': True}
|
||||||
|
api.quantum.network_list(
|
||||||
|
IsA(http.HttpRequest), **search_opts).AndReturn([network])
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
form_data = {'router_id': router.id,
|
||||||
|
'network_id': network.id}
|
||||||
|
|
||||||
|
url = reverse('horizon:%s:routers:setgateway' % self.DASHBOARD,
|
||||||
|
args=[router.id])
|
||||||
|
res = self.client.post(url, form_data)
|
||||||
|
self.assertNoFormErrors(res)
|
||||||
|
detail_url = reverse(self.DETAIL_PATH, args=[router.id])
|
||||||
|
self.assertRedirectsNoFollow(res, detail_url)
|
||||||
|
|
||||||
|
@test.create_stubs({api.quantum: ('router_get',
|
||||||
|
'router_add_gateway',
|
||||||
|
'network_list')})
|
||||||
|
def test_router_add_gateway_exception(self):
|
||||||
|
router = self.routers.first()
|
||||||
|
network = self.networks.first()
|
||||||
|
api.quantum.router_add_gateway(
|
||||||
|
IsA(http.HttpRequest),
|
||||||
|
router.id,
|
||||||
|
network.id).AndRaise(self.exceptions.quantum)
|
||||||
|
api.quantum.router_get(
|
||||||
|
IsA(http.HttpRequest), router.id).AndReturn(router)
|
||||||
|
search_opts = {'router:external': True}
|
||||||
|
api.quantum.network_list(
|
||||||
|
IsA(http.HttpRequest), **search_opts).AndReturn([network])
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
form_data = {'router_id': router.id,
|
||||||
|
'network_id': network.id}
|
||||||
|
|
||||||
|
url = reverse('horizon:%s:routers:setgateway' % self.DASHBOARD,
|
||||||
|
args=[router.id])
|
||||||
|
res = self.client.post(url, form_data)
|
||||||
|
self.assertNoFormErrors(res)
|
||||||
|
detail_url = reverse(self.DETAIL_PATH, args=[router.id])
|
||||||
|
self.assertRedirectsNoFollow(res, detail_url)
|
34
openstack_dashboard/dashboards/project/routers/urls.py
Normal file
34
openstack_dashboard/dashboards/project/routers/urls.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
from django.conf.urls.defaults import patterns, url
|
||||||
|
|
||||||
|
from .views import (IndexView, CreateView, DetailView)
|
||||||
|
from .ports.views import (AddInterfaceView, SetGatewayView)
|
||||||
|
|
||||||
|
|
||||||
|
urlpatterns = patterns('horizon.dashboards.project.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'),
|
||||||
|
)
|
105
openstack_dashboard/dashboards/project/routers/views.py
Normal file
105
openstack_dashboard/dashboards/project/routers/views.py
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
# 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 import shortcuts
|
||||||
|
from django.core.urlresolvers import reverse_lazy
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from django.utils.datastructures import SortedDict
|
||||||
|
|
||||||
|
from horizon import exceptions
|
||||||
|
from horizon import forms
|
||||||
|
from horizon import tables
|
||||||
|
from horizon import tabs
|
||||||
|
from openstack_dashboard import api
|
||||||
|
from .ports.tables import PortsTable
|
||||||
|
from .forms import CreateForm
|
||||||
|
from .tables import RoutersTable
|
||||||
|
from .tabs import RouterDetailTabs
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class IndexView(tables.DataTableView):
|
||||||
|
table_class = RoutersTable
|
||||||
|
template_name = 'project/routers/index.html'
|
||||||
|
|
||||||
|
def _get_routers(self, search_opts=None):
|
||||||
|
try:
|
||||||
|
tenant_id = self.request.user.tenant_id
|
||||||
|
routers = api.quantum.router_list(self.request,
|
||||||
|
tenant_id=tenant_id,
|
||||||
|
search_opts=search_opts)
|
||||||
|
except:
|
||||||
|
routers = []
|
||||||
|
exceptions.handle(self.request,
|
||||||
|
_('Unable to retrieve router list.'))
|
||||||
|
for r in routers:
|
||||||
|
r.set_id_as_name_if_empty()
|
||||||
|
return routers
|
||||||
|
|
||||||
|
def get_data(self):
|
||||||
|
routers = self._get_routers()
|
||||||
|
return routers
|
||||||
|
|
||||||
|
|
||||||
|
class DetailView(tables.MultiTableView):
|
||||||
|
table_classes = (PortsTable, )
|
||||||
|
template_name = 'project/routers/detail.html'
|
||||||
|
failure_url = reverse_lazy('horizon:project:routers:index')
|
||||||
|
|
||||||
|
def _get_data(self):
|
||||||
|
if not hasattr(self, "_router"):
|
||||||
|
try:
|
||||||
|
router_id = self.kwargs['router_id']
|
||||||
|
router = api.quantum.router_get(self.request, router_id)
|
||||||
|
router.set_id_as_name_if_empty(length=0)
|
||||||
|
except:
|
||||||
|
msg = _('Unable to retrieve details for router "%s".') \
|
||||||
|
% (router_id)
|
||||||
|
exceptions.handle(self.request, msg, redirect=self.failure_url)
|
||||||
|
self._router = router
|
||||||
|
return self._router
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super(DetailView, self).get_context_data(**kwargs)
|
||||||
|
context["router"] = self._get_data()
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get_interfaces_data(self):
|
||||||
|
try:
|
||||||
|
device_id = self.kwargs['router_id']
|
||||||
|
ports = api.quantum.port_list(self.request,
|
||||||
|
device_id=device_id)
|
||||||
|
except:
|
||||||
|
ports = []
|
||||||
|
msg = _('Port list can not be retrieved.')
|
||||||
|
exceptions.handle(self.request, msg)
|
||||||
|
for p in ports:
|
||||||
|
p.set_id_as_name_if_empty()
|
||||||
|
return ports
|
||||||
|
|
||||||
|
|
||||||
|
class CreateView(forms.ModalFormView):
|
||||||
|
form_class = CreateForm
|
||||||
|
template_name = 'project/routers/create.html'
|
||||||
|
success_url = reverse_lazy("horizon:project:routers:index")
|
@ -203,3 +203,70 @@ class QuantumApiTests(test.APITestCase):
|
|||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
api.quantum.port_delete(self.request, port_id)
|
api.quantum.port_delete(self.request, port_id)
|
||||||
|
|
||||||
|
def test_router_list(self):
|
||||||
|
routers = {'routers': self.api_routers.list()}
|
||||||
|
|
||||||
|
quantumclient = self.stub_quantumclient()
|
||||||
|
quantumclient.list_routers().AndReturn(routers)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
ret_val = api.quantum.router_list(self.request)
|
||||||
|
for n in ret_val:
|
||||||
|
self.assertIsInstance(n, api.quantum.Router)
|
||||||
|
|
||||||
|
def test_router_get(self):
|
||||||
|
router = {'router': self.api_routers.first()}
|
||||||
|
router_id = self.api_routers.first()['id']
|
||||||
|
|
||||||
|
quantumclient = self.stub_quantumclient()
|
||||||
|
quantumclient.show_router(router_id).AndReturn(router)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
ret_val = api.quantum.router_get(self.request, router_id)
|
||||||
|
self.assertIsInstance(ret_val, api.quantum.Router)
|
||||||
|
|
||||||
|
def test_router_create(self):
|
||||||
|
router = {'router': self.api_routers.first()}
|
||||||
|
|
||||||
|
quantumclient = self.stub_quantumclient()
|
||||||
|
form_data = {'router': {'name': 'router1'}}
|
||||||
|
quantumclient.create_router(body=form_data).AndReturn(router)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
ret_val = api.quantum.router_create(self.request, name='router1')
|
||||||
|
self.assertIsInstance(ret_val, api.quantum.Router)
|
||||||
|
|
||||||
|
def test_router_delete(self):
|
||||||
|
router_id = self.api_routers.first()['id']
|
||||||
|
|
||||||
|
quantumclient = self.stub_quantumclient()
|
||||||
|
quantumclient.delete_router(router_id)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
api.quantum.router_delete(self.request, router_id)
|
||||||
|
|
||||||
|
def test_router_add_interface(self):
|
||||||
|
subnet_id = self.api_subnets.first()['id']
|
||||||
|
router_id = self.api_routers.first()['id']
|
||||||
|
|
||||||
|
quantumclient = self.stub_quantumclient()
|
||||||
|
form_data = {'subnet_id': subnet_id}
|
||||||
|
quantumclient.add_interface_router(
|
||||||
|
router_id, form_data).AndReturn(None)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
api.quantum.router_add_interface(
|
||||||
|
self.request, router_id, subnet_id=subnet_id)
|
||||||
|
|
||||||
|
def test_router_remove_interface(self):
|
||||||
|
router_id = self.api_routers.first()['id']
|
||||||
|
fake_port = self.api_ports.first()['id']
|
||||||
|
|
||||||
|
quantumclient = self.stub_quantumclient()
|
||||||
|
quantumclient.remove_interface_router(
|
||||||
|
router_id, {'port_id': fake_port})
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
api.quantum.router_remove_interface(
|
||||||
|
self.request, router_id, port_id=fake_port)
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
import copy
|
import copy
|
||||||
|
|
||||||
from openstack_dashboard.api.quantum import Network, Subnet, Port
|
from openstack_dashboard.api.quantum import Network, Subnet, Port, Router
|
||||||
|
|
||||||
from .utils import TestDataContainer
|
from .utils import TestDataContainer
|
||||||
|
|
||||||
@ -24,11 +24,13 @@ def data(TEST):
|
|||||||
TEST.networks = TestDataContainer()
|
TEST.networks = TestDataContainer()
|
||||||
TEST.subnets = TestDataContainer()
|
TEST.subnets = TestDataContainer()
|
||||||
TEST.ports = TestDataContainer()
|
TEST.ports = TestDataContainer()
|
||||||
|
TEST.routers = TestDataContainer()
|
||||||
|
|
||||||
# data return by quantumclient
|
# data return by quantumclient
|
||||||
TEST.api_networks = TestDataContainer()
|
TEST.api_networks = TestDataContainer()
|
||||||
TEST.api_subnets = TestDataContainer()
|
TEST.api_subnets = TestDataContainer()
|
||||||
TEST.api_ports = TestDataContainer()
|
TEST.api_ports = TestDataContainer()
|
||||||
|
TEST.api_routers = TestDataContainer()
|
||||||
|
|
||||||
# 1st network
|
# 1st network
|
||||||
network_dict = {'admin_state_up': True,
|
network_dict = {'admin_state_up': True,
|
||||||
@ -62,7 +64,6 @@ def data(TEST):
|
|||||||
'network_id': network_dict['id'],
|
'network_id': network_dict['id'],
|
||||||
'status': 'ACTIVE',
|
'status': 'ACTIVE',
|
||||||
'tenant_id': network_dict['tenant_id']}
|
'tenant_id': network_dict['tenant_id']}
|
||||||
|
|
||||||
TEST.api_networks.add(network_dict)
|
TEST.api_networks.add(network_dict)
|
||||||
TEST.api_subnets.add(subnet_dict)
|
TEST.api_subnets.add(subnet_dict)
|
||||||
TEST.api_ports.add(port_dict)
|
TEST.api_ports.add(port_dict)
|
||||||
@ -109,7 +110,6 @@ def data(TEST):
|
|||||||
'network_id': network_dict['id'],
|
'network_id': network_dict['id'],
|
||||||
'status': 'ACTIVE',
|
'status': 'ACTIVE',
|
||||||
'tenant_id': network_dict['tenant_id']}
|
'tenant_id': network_dict['tenant_id']}
|
||||||
|
|
||||||
TEST.api_networks.add(network_dict)
|
TEST.api_networks.add(network_dict)
|
||||||
TEST.api_subnets.add(subnet_dict)
|
TEST.api_subnets.add(subnet_dict)
|
||||||
TEST.api_ports.add(port_dict)
|
TEST.api_ports.add(port_dict)
|
||||||
@ -120,3 +120,29 @@ def data(TEST):
|
|||||||
TEST.networks.add(Network(network))
|
TEST.networks.add(Network(network))
|
||||||
TEST.subnets.add(subnet)
|
TEST.subnets.add(subnet)
|
||||||
TEST.ports.add(Port(port_dict))
|
TEST.ports.add(Port(port_dict))
|
||||||
|
|
||||||
|
# Set up router data
|
||||||
|
port_dict = {'admin_state_up': True,
|
||||||
|
'device_id': '7180cede-bcd8-4334-b19f-f7ef2f331f53',
|
||||||
|
'device_owner': 'network:router_gateway',
|
||||||
|
'fixed_ips': [{'ip_address': '10.0.0.3',
|
||||||
|
'subnet_id': subnet_dict['id']}],
|
||||||
|
'id': '3ec7f3db-cb2f-4a34-ab6b-69a64d3f008c',
|
||||||
|
'mac_address': 'fa:16:3e:9c:d5:7e',
|
||||||
|
'name': '',
|
||||||
|
'network_id': network_dict['id'],
|
||||||
|
'status': 'ACTIVE',
|
||||||
|
'tenant_id': '1'}
|
||||||
|
TEST.api_ports.add(port_dict)
|
||||||
|
TEST.ports.add(Port(port_dict))
|
||||||
|
|
||||||
|
router_dict = {'id': '279989f7-54bb-41d9-ba42-0d61f12fda61',
|
||||||
|
'name': 'router1',
|
||||||
|
'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',
|
||||||
|
'tenant_id': '1'}
|
||||||
|
TEST.api_routers.add(router_dict)
|
||||||
|
TEST.routers.add(Router(router_dict))
|
||||||
|
Loading…
Reference in New Issue
Block a user