Improve Quantum panels to Folsom advanced features
Implements blueprint improve-quantum-summary-table * Improve displayed columns in network related tables * Use workflows in subnet create/update panels * Operations * admin_state control for network and port * router:external support in network creation and update * No gateway support in subnet creation and update * enable_dhcp, allocation_pools, dns_nameservers and host_routes support in subnet creattion and update * Setting device_owner is supported in admin port panel * Detail panels * router:external and provider network information in admin network detail * enable_dhcp, host_routes and dns_nameservers in subnet detail * device_owner in port detail * Behavior changes * Remove created network when subnet creation failed in "Create Network". Before this commit a created network remains even when an associated subnet failed to be created, but it is a little confusing since an unintended network without subnet is created. This commit deletes such networks and display a message indicating it. Change-Id: I1325c415acc6afc664879540c66957874d1c95c3
This commit is contained in:
parent
4d2199d8fe
commit
31d55e503d
@ -50,19 +50,19 @@ class QuantumAPIDictWrapper(APIDictWrapper):
|
|||||||
|
|
||||||
class Network(QuantumAPIDictWrapper):
|
class Network(QuantumAPIDictWrapper):
|
||||||
"""Wrapper for quantum Networks"""
|
"""Wrapper for quantum Networks"""
|
||||||
_attrs = ['name', 'id', 'subnets', 'tenant_id', 'status',
|
|
||||||
'admin_state_up', 'shared']
|
|
||||||
|
|
||||||
def __init__(self, apiresource):
|
def __init__(self, apiresource):
|
||||||
apiresource['admin_state'] = \
|
apiresource['admin_state'] = \
|
||||||
'UP' if apiresource['admin_state_up'] else 'DOWN'
|
'UP' if apiresource['admin_state_up'] else 'DOWN'
|
||||||
|
# Django cannot handle a key name with a colon, so remap another key
|
||||||
|
for key in apiresource.keys():
|
||||||
|
if key.find(':'):
|
||||||
|
apiresource['__'.join(key.split(':'))] = apiresource[key]
|
||||||
super(Network, self).__init__(apiresource)
|
super(Network, self).__init__(apiresource)
|
||||||
|
|
||||||
|
|
||||||
class Subnet(QuantumAPIDictWrapper):
|
class Subnet(QuantumAPIDictWrapper):
|
||||||
"""Wrapper for quantum subnets"""
|
"""Wrapper for quantum subnets"""
|
||||||
_attrs = ['name', 'id', 'cidr', 'network_id', 'tenant_id',
|
|
||||||
'ip_version', 'ipver_str']
|
|
||||||
|
|
||||||
def __init__(self, apiresource):
|
def __init__(self, apiresource):
|
||||||
apiresource['ipver_str'] = get_ipver_str(apiresource['ip_version'])
|
apiresource['ipver_str'] = get_ipver_str(apiresource['ip_version'])
|
||||||
@ -71,9 +71,6 @@ class Subnet(QuantumAPIDictWrapper):
|
|||||||
|
|
||||||
class Port(QuantumAPIDictWrapper):
|
class Port(QuantumAPIDictWrapper):
|
||||||
"""Wrapper for quantum ports"""
|
"""Wrapper for quantum ports"""
|
||||||
_attrs = ['name', 'id', 'network_id', 'tenant_id',
|
|
||||||
'admin_state_up', 'status', 'mac_address',
|
|
||||||
'fixed_ips', 'host_routes', 'device_id']
|
|
||||||
|
|
||||||
def __init__(self, apiresource):
|
def __init__(self, apiresource):
|
||||||
apiresource['admin_state'] = \
|
apiresource['admin_state'] = \
|
||||||
|
@ -34,8 +34,12 @@ class CreateNetwork(forms.SelfHandlingForm):
|
|||||||
label=_("Name"),
|
label=_("Name"),
|
||||||
required=False)
|
required=False)
|
||||||
tenant_id = forms.ChoiceField(label=_("Project"))
|
tenant_id = forms.ChoiceField(label=_("Project"))
|
||||||
|
admin_state = forms.BooleanField(label=_("Admin State"),
|
||||||
|
initial=True, required=False)
|
||||||
shared = forms.BooleanField(label=_("Shared"),
|
shared = forms.BooleanField(label=_("Shared"),
|
||||||
initial=False, required=False)
|
initial=False, required=False)
|
||||||
|
external = forms.BooleanField(label=_("External Network"),
|
||||||
|
initial=False, required=False)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _instantiate(cls, request, *args, **kwargs):
|
def _instantiate(cls, request, *args, **kwargs):
|
||||||
@ -51,10 +55,12 @@ class CreateNetwork(forms.SelfHandlingForm):
|
|||||||
|
|
||||||
def handle(self, request, data):
|
def handle(self, request, data):
|
||||||
try:
|
try:
|
||||||
network = api.quantum.network_create(request,
|
params = {'name': data['name'],
|
||||||
name=data['name'],
|
'tenant_id': data['tenant_id'],
|
||||||
tenant_id=data['tenant_id'],
|
'admin_state_up': data['admin_state'],
|
||||||
shared=data['shared'])
|
'shared': data['shared'],
|
||||||
|
'router:external': data['external']}
|
||||||
|
network = api.quantum.network_create(request, **params)
|
||||||
msg = _('Network %s was successfully created.') % data['name']
|
msg = _('Network %s was successfully created.') % data['name']
|
||||||
LOG.debug(msg)
|
LOG.debug(msg)
|
||||||
messages.success(request, msg)
|
messages.success(request, msg)
|
||||||
@ -71,14 +77,19 @@ class UpdateNetwork(forms.SelfHandlingForm):
|
|||||||
network_id = forms.CharField(label=_("ID"),
|
network_id = forms.CharField(label=_("ID"),
|
||||||
widget=forms.TextInput(
|
widget=forms.TextInput(
|
||||||
attrs={'readonly': 'readonly'}))
|
attrs={'readonly': 'readonly'}))
|
||||||
|
admin_state = forms.BooleanField(label=_("Admin State"), required=False)
|
||||||
shared = forms.BooleanField(label=_("Shared"), required=False)
|
shared = forms.BooleanField(label=_("Shared"), required=False)
|
||||||
|
external = forms.BooleanField(label=_("External Network"), required=False)
|
||||||
failure_url = 'horizon:admin:networks:index'
|
failure_url = 'horizon:admin:networks:index'
|
||||||
|
|
||||||
def handle(self, request, data):
|
def handle(self, request, data):
|
||||||
try:
|
try:
|
||||||
|
params = {'name': data['name'],
|
||||||
|
'admin_state_up': data['admin_state'],
|
||||||
|
'shared': data['shared'],
|
||||||
|
'router:external': data['external']}
|
||||||
network = api.quantum.network_modify(request, data['network_id'],
|
network = api.quantum.network_modify(request, data['network_id'],
|
||||||
name=data['name'],
|
**params)
|
||||||
shared=data['shared'])
|
|
||||||
msg = _('Network %s was successfully updated.') % data['name']
|
msg = _('Network %s was successfully updated.') % data['name']
|
||||||
LOG.debug(msg)
|
LOG.debug(msg)
|
||||||
messages.success(request, msg)
|
messages.success(request, msg)
|
||||||
|
@ -24,6 +24,8 @@ from horizon import forms
|
|||||||
from horizon import messages
|
from horizon import messages
|
||||||
|
|
||||||
from openstack_dashboard import api
|
from openstack_dashboard import api
|
||||||
|
from openstack_dashboard.dashboards.project.networks.ports \
|
||||||
|
import forms as project_forms
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@ -39,9 +41,14 @@ class CreatePort(forms.SelfHandlingForm):
|
|||||||
name = forms.CharField(max_length=255,
|
name = forms.CharField(max_length=255,
|
||||||
label=_("Name"),
|
label=_("Name"),
|
||||||
required=False)
|
required=False)
|
||||||
|
admin_state = forms.BooleanField(label=_("Admin State"),
|
||||||
|
initial=True, required=False)
|
||||||
device_id = forms.CharField(max_length=100, label=_("Device ID"),
|
device_id = forms.CharField(max_length=100, label=_("Device ID"),
|
||||||
help_text='Device ID attached to the port',
|
help_text='Device ID attached to the port',
|
||||||
required=False)
|
required=False)
|
||||||
|
device_owner = forms.CharField(max_length=100, label=_("Device Owner"),
|
||||||
|
help_text='Device owner attached to the port',
|
||||||
|
required=False)
|
||||||
|
|
||||||
def handle(self, request, data):
|
def handle(self, request, data):
|
||||||
try:
|
try:
|
||||||
@ -49,6 +56,8 @@ class CreatePort(forms.SelfHandlingForm):
|
|||||||
# created for if admin user does not belong to the tenant.
|
# created for if admin user does not belong to the tenant.
|
||||||
network = api.quantum.network_get(request, data['network_id'])
|
network = api.quantum.network_get(request, data['network_id'])
|
||||||
data['tenant_id'] = network.tenant_id
|
data['tenant_id'] = network.tenant_id
|
||||||
|
data['admin_state_up'] = data['admin_state']
|
||||||
|
del data['admin_state']
|
||||||
|
|
||||||
port = api.quantum.port_create(request, **data)
|
port = api.quantum.port_create(request, **data)
|
||||||
msg = _('Port %s was successfully created.') % port['id']
|
msg = _('Port %s was successfully created.') % port['id']
|
||||||
@ -64,23 +73,24 @@ class CreatePort(forms.SelfHandlingForm):
|
|||||||
exceptions.handle(request, msg, redirect=redirect)
|
exceptions.handle(request, msg, redirect=redirect)
|
||||||
|
|
||||||
|
|
||||||
class UpdatePort(forms.SelfHandlingForm):
|
class UpdatePort(project_forms.UpdatePort):
|
||||||
network_id = forms.CharField(widget=forms.HiddenInput())
|
#tenant_id = forms.CharField(widget=forms.HiddenInput())
|
||||||
tenant_id = forms.CharField(widget=forms.HiddenInput())
|
|
||||||
port_id = forms.CharField(widget=forms.HiddenInput())
|
|
||||||
name = forms.CharField(max_length=255,
|
|
||||||
label=_("Name"),
|
|
||||||
required=False)
|
|
||||||
device_id = forms.CharField(max_length=100, label=_("Device ID"),
|
device_id = forms.CharField(max_length=100, label=_("Device ID"),
|
||||||
help_text='Device ID attached to the port',
|
help_text='Device ID attached to the port',
|
||||||
required=False)
|
required=False)
|
||||||
|
device_owner = forms.CharField(max_length=100, label=_("Device Owner"),
|
||||||
|
help_text='Device owner attached to the port',
|
||||||
|
required=False)
|
||||||
|
failure_url = 'horizon:admin:networks:detail'
|
||||||
|
|
||||||
def handle(self, request, data):
|
def handle(self, request, data):
|
||||||
try:
|
try:
|
||||||
LOG.debug('params = %s' % data)
|
LOG.debug('params = %s' % data)
|
||||||
port = api.quantum.port_modify(request, data['port_id'],
|
port = api.quantum.port_modify(request, data['port_id'],
|
||||||
name=data['name'],
|
name=data['name'],
|
||||||
device_id=data['device_id'])
|
admin_state_up=data['admin_state'],
|
||||||
|
device_id=data['device_id'],
|
||||||
|
device_owner=data['device_owner'])
|
||||||
msg = _('Port %s was successfully updated.') % data['port_id']
|
msg = _('Port %s was successfully updated.') % data['port_id']
|
||||||
LOG.debug(msg)
|
LOG.debug(msg)
|
||||||
messages.success(request, msg)
|
messages.success(request, msg)
|
||||||
@ -88,6 +98,6 @@ class UpdatePort(forms.SelfHandlingForm):
|
|||||||
except Exception:
|
except Exception:
|
||||||
msg = _('Failed to update port %s') % data['port_id']
|
msg = _('Failed to update port %s') % data['port_id']
|
||||||
LOG.info(msg)
|
LOG.info(msg)
|
||||||
redirect = reverse('horizon:admin:networks:detail',
|
redirect = reverse(self.failure_url,
|
||||||
args=[data['network_id']])
|
args=[data['network_id']])
|
||||||
exceptions.handle(request, msg, redirect=redirect)
|
exceptions.handle(request, msg, redirect=redirect)
|
||||||
|
@ -23,6 +23,9 @@ from horizon import exceptions
|
|||||||
from horizon import forms
|
from horizon import forms
|
||||||
|
|
||||||
from openstack_dashboard import api
|
from openstack_dashboard import api
|
||||||
|
from openstack_dashboard.dashboards.project.networks.ports \
|
||||||
|
import views as project_views
|
||||||
|
|
||||||
from .forms import CreatePort, UpdatePort
|
from .forms import CreatePort, UpdatePort
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@ -32,6 +35,7 @@ class CreateView(forms.ModalFormView):
|
|||||||
form_class = CreatePort
|
form_class = CreatePort
|
||||||
template_name = 'admin/networks/ports/create.html'
|
template_name = 'admin/networks/ports/create.html'
|
||||||
success_url = 'horizon:admin:networks:detail'
|
success_url = 'horizon:admin:networks:detail'
|
||||||
|
failure_url = 'horizon:admin:networks:detail'
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse(self.success_url,
|
return reverse(self.success_url,
|
||||||
@ -44,7 +48,7 @@ class CreateView(forms.ModalFormView):
|
|||||||
self._object = api.quantum.network_get(self.request,
|
self._object = api.quantum.network_get(self.request,
|
||||||
network_id)
|
network_id)
|
||||||
except:
|
except:
|
||||||
redirect = reverse("horizon:admin:networks:detail",
|
redirect = reverse(self.failure_url,
|
||||||
args=(self.kwargs['network_id'],))
|
args=(self.kwargs['network_id'],))
|
||||||
msg = _("Unable to retrieve network.")
|
msg = _("Unable to retrieve network.")
|
||||||
exceptions.handle(self.request, msg, redirect=redirect)
|
exceptions.handle(self.request, msg, redirect=redirect)
|
||||||
@ -61,39 +65,8 @@ class CreateView(forms.ModalFormView):
|
|||||||
"network_name": network.name}
|
"network_name": network.name}
|
||||||
|
|
||||||
|
|
||||||
class UpdateView(forms.ModalFormView):
|
class UpdateView(project_views.UpdateView):
|
||||||
form_class = UpdatePort
|
form_class = UpdatePort
|
||||||
template_name = 'admin/networks/ports/update.html'
|
template_name = 'admin/networks/ports/update.html'
|
||||||
context_object_name = 'port'
|
context_object_name = 'port'
|
||||||
success_url = 'horizon:admin:networks:detail'
|
success_url = 'horizon:admin:networks:detail'
|
||||||
|
|
||||||
def get_success_url(self):
|
|
||||||
return reverse(self.success_url,
|
|
||||||
args=(self.kwargs['network_id'],))
|
|
||||||
|
|
||||||
def _get_object(self, *args, **kwargs):
|
|
||||||
if not hasattr(self, "_object"):
|
|
||||||
port_id = self.kwargs['port_id']
|
|
||||||
try:
|
|
||||||
self._object = api.quantum.port_get(self.request, port_id)
|
|
||||||
except:
|
|
||||||
redirect = reverse("horizon:admin:networks:detail",
|
|
||||||
args=(self.kwargs['network_id'],))
|
|
||||||
msg = _('Unable to retrieve port details')
|
|
||||||
exceptions.handle(self.request, msg, redirect=redirect)
|
|
||||||
return self._object
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super(UpdateView, self).get_context_data(**kwargs)
|
|
||||||
port = self._get_object()
|
|
||||||
context['port_id'] = port['id']
|
|
||||||
context['network_id'] = port['network_id']
|
|
||||||
return context
|
|
||||||
|
|
||||||
def get_initial(self):
|
|
||||||
port = self._get_object()
|
|
||||||
return {'port_id': port['id'],
|
|
||||||
'network_id': port['network_id'],
|
|
||||||
'tenant_id': port['tenant_id'],
|
|
||||||
'name': port['name'],
|
|
||||||
'device_id': port['device_id']}
|
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
# 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.core.urlresolvers import reverse
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
|
||||||
|
|
||||||
from horizon import forms
|
|
||||||
from horizon import exceptions
|
|
||||||
|
|
||||||
from openstack_dashboard import api
|
|
||||||
from openstack_dashboard.dashboards.project.networks.subnets import \
|
|
||||||
forms as user_forms
|
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class CreateSubnet(user_forms.CreateSubnet):
|
|
||||||
failure_url = 'horizon:admin:networks:detail'
|
|
||||||
|
|
||||||
def handle(self, request, data):
|
|
||||||
try:
|
|
||||||
# We must specify tenant_id of the network which a subnet is
|
|
||||||
# created for if admin user does not belong to the tenant.
|
|
||||||
network = api.quantum.network_get(request, data['network_id'])
|
|
||||||
data['tenant_id'] = network.tenant_id
|
|
||||||
except:
|
|
||||||
msg = _('Failed to retrieve network %s for a subnet') \
|
|
||||||
% data['network_id']
|
|
||||||
LOG.info(msg)
|
|
||||||
redirect = reverse(self.failure_url, args=[data['network_id']])
|
|
||||||
exceptions.handle(request, msg, redirect=redirect)
|
|
||||||
return super(CreateSubnet, self).handle(request, data)
|
|
||||||
|
|
||||||
|
|
||||||
class UpdateSubnet(user_forms.UpdateSubnet):
|
|
||||||
tenant_id = forms.CharField(widget=forms.HiddenInput())
|
|
||||||
failure_url = 'horizon:admin:networks:detail'
|
|
@ -23,81 +23,20 @@ from horizon import exceptions
|
|||||||
from horizon import forms
|
from horizon import forms
|
||||||
|
|
||||||
from openstack_dashboard import api
|
from openstack_dashboard import api
|
||||||
from .forms import CreateSubnet, UpdateSubnet
|
from openstack_dashboard.dashboards.project.networks.subnets \
|
||||||
|
import views as project_views
|
||||||
|
|
||||||
|
from .workflows import CreateSubnet, UpdateSubnet
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class CreateView(forms.ModalFormView):
|
class CreateView(project_views.CreateView):
|
||||||
form_class = CreateSubnet
|
workflow_class = CreateSubnet
|
||||||
template_name = 'admin/networks/subnets/create.html'
|
template_name = 'admin/networks/subnets/create.html'
|
||||||
success_url = 'horizon:admin:networks:detail'
|
|
||||||
|
|
||||||
def get_success_url(self):
|
|
||||||
return reverse(self.success_url,
|
|
||||||
args=(self.kwargs['network_id'],))
|
|
||||||
|
|
||||||
def get_object(self):
|
|
||||||
if not hasattr(self, "_object"):
|
|
||||||
try:
|
|
||||||
network_id = self.kwargs["network_id"]
|
|
||||||
self._object = api.quantum.network_get(self.request,
|
|
||||||
network_id)
|
|
||||||
except:
|
|
||||||
redirect = reverse('horizon:project:networks:index')
|
|
||||||
msg = _("Unable to retrieve network.")
|
|
||||||
exceptions.handle(self.request, msg, redirect=redirect)
|
|
||||||
return self._object
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super(CreateView, self).get_context_data(**kwargs)
|
|
||||||
context['network'] = self.get_object()
|
|
||||||
return context
|
|
||||||
|
|
||||||
def get_initial(self):
|
|
||||||
network = self.get_object()
|
|
||||||
return {"network_id": self.kwargs['network_id'],
|
|
||||||
"network_name": network.name}
|
|
||||||
|
|
||||||
|
|
||||||
class UpdateView(forms.ModalFormView):
|
class UpdateView(project_views.UpdateView):
|
||||||
form_class = UpdateSubnet
|
workflow_class = UpdateSubnet
|
||||||
template_name = 'admin/networks/subnets/update.html'
|
template_name = 'admin/networks/subnets/update.html'
|
||||||
context_object_name = 'subnet'
|
|
||||||
success_url = 'horizon:admin:networks:detail'
|
|
||||||
|
|
||||||
def get_success_url(self):
|
|
||||||
return reverse(self.success_url,
|
|
||||||
args=(self.kwargs['network_id'],))
|
|
||||||
|
|
||||||
def _get_object(self, *args, **kwargs):
|
|
||||||
if not hasattr(self, "_object"):
|
|
||||||
subnet_id = self.kwargs['subnet_id']
|
|
||||||
try:
|
|
||||||
self._object = api.quantum.subnet_get(self.request, subnet_id)
|
|
||||||
except:
|
|
||||||
redirect = reverse("horizon:admin:networks:detail",
|
|
||||||
args=(self.kwargs['network_id'],))
|
|
||||||
msg = _('Unable to retrieve subnet details')
|
|
||||||
exceptions.handle(self.request, msg, redirect=redirect)
|
|
||||||
return self._object
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super(UpdateView, self).get_context_data(**kwargs)
|
|
||||||
subnet = self._get_object()
|
|
||||||
context['subnet_id'] = subnet['id']
|
|
||||||
context['network_id'] = subnet['network_id']
|
|
||||||
context['cidr'] = subnet['cidr']
|
|
||||||
context['ip_version'] = {4: 'IPv4', 6: 'IPv6'}[subnet['ip_version']]
|
|
||||||
return context
|
|
||||||
|
|
||||||
def get_initial(self):
|
|
||||||
subnet = self._get_object()
|
|
||||||
return {'network_id': self.kwargs['network_id'],
|
|
||||||
'subnet_id': subnet['id'],
|
|
||||||
'tenant_id': subnet['tenant_id'],
|
|
||||||
'cidr': subnet['cidr'],
|
|
||||||
'ip_version': subnet['ip_version'],
|
|
||||||
'name': subnet['name'],
|
|
||||||
'gateway_ip': subnet['gateway_ip']}
|
|
||||||
|
@ -0,0 +1,60 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2013 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.core.urlresolvers import reverse
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
|
from horizon import exceptions
|
||||||
|
|
||||||
|
from openstack_dashboard import api
|
||||||
|
from openstack_dashboard.dashboards.project.networks.subnets \
|
||||||
|
import workflows as project_workflows
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class CreateSubnet(project_workflows.CreateSubnet):
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse("horizon:admin:networks:detail",
|
||||||
|
args=(self.context.get('network_id'),))
|
||||||
|
|
||||||
|
def get_failure_url(self):
|
||||||
|
return reverse("horizon:admin:networks:detail",
|
||||||
|
args=(self.context.get('network_id'),))
|
||||||
|
|
||||||
|
def handle(self, request, data):
|
||||||
|
try:
|
||||||
|
# We must specify tenant_id of the network which a subnet is
|
||||||
|
# created for if admin user does not belong to the tenant.
|
||||||
|
network = api.quantum.network_get(request,
|
||||||
|
self.context['network_id'])
|
||||||
|
except:
|
||||||
|
msg = (_('Failed to retrieve network %s for a subnet') %
|
||||||
|
data['network_id'])
|
||||||
|
LOG.info(msg)
|
||||||
|
redirect = self.get_failure_url()
|
||||||
|
exceptions.handle(request, msg, redirect=redirect)
|
||||||
|
subnet = self._create_subnet(request, data,
|
||||||
|
tenant_id=network.tenant_id)
|
||||||
|
return True if subnet else False
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateSubnet(project_workflows.UpdateSubnet):
|
||||||
|
success_url = "horizon:admin:networks:detail"
|
||||||
|
failure_url = "horizon:admin:networks:detail"
|
@ -1,25 +0,0 @@
|
|||||||
{% extends "horizon/common/_modal_form.html" %}
|
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
{% block form_id %}create_subnet_form{% endblock %}
|
|
||||||
{% block form_action %}{% url horizon:admin:networks:addsubnet network.id %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block modal-header %}{% trans "Create Subnet" %}{% 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 create a subnet for the network. Any network address can be specified unless the network address does not overlap other subnets in the network." %}</p>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block modal-footer %}
|
|
||||||
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Create Subnet" %}" />
|
|
||||||
<a href="{% url horizon:admin:networks:detail network.id %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
|
|
||||||
{% endblock %}
|
|
@ -1,33 +0,0 @@
|
|||||||
{% extends "horizon/common/_modal_form.html" %}
|
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
{% block form_id %}update_subnet_form{% endblock %}
|
|
||||||
{% block form_action %}{% url horizon:admin:networks:editsubnet network_id subnet_id %}{% endblock %}
|
|
||||||
|
|
||||||
{% block modal-header %}{% trans "Edit Subnet" %}{% endblock %}
|
|
||||||
|
|
||||||
{% block modal-body %}
|
|
||||||
<div class="left">
|
|
||||||
<dl>
|
|
||||||
<dt>{% trans "ID" %}</dt>
|
|
||||||
<dd>{{ subnet_id }}</dd>
|
|
||||||
<dt>{% trans "Network Address" %}</dt>
|
|
||||||
<dd>{{ cidr }}</dd>
|
|
||||||
<dt>{% trans "IP version" %}</dt>
|
|
||||||
<dd>{{ ip_version }}</dd>
|
|
||||||
</dl>
|
|
||||||
<hr>
|
|
||||||
<fieldset>
|
|
||||||
{% include "horizon/common/_form_fields.html" %}
|
|
||||||
</fieldset>
|
|
||||||
</div>
|
|
||||||
<div class="right">
|
|
||||||
<h3>{% trans "Description:" %}</h3>
|
|
||||||
<p>{% trans "You may update the editable properties of your subnet here." %}</p>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block modal-footer %}
|
|
||||||
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Save Changes" %}" />
|
|
||||||
<a href="{% url horizon:admin:networks:detail network_id %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
|
|
||||||
{% endblock %}
|
|
@ -7,5 +7,5 @@
|
|||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
{% include "admin/networks/subnets/_create.html" %}
|
{% include "horizon/common/_workflow.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -7,5 +7,5 @@
|
|||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
{% include 'admin/networks/subnets/_update.html' %}
|
{% include "horizon/common/_workflow.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -21,6 +21,8 @@ from mox import IsA
|
|||||||
|
|
||||||
from openstack_dashboard import api
|
from openstack_dashboard import api
|
||||||
from openstack_dashboard.test import helpers as test
|
from openstack_dashboard.test import helpers as test
|
||||||
|
from openstack_dashboard.dashboards.project.networks.tests \
|
||||||
|
import form_data_subnet
|
||||||
|
|
||||||
|
|
||||||
INDEX_URL = reverse('horizon:admin:networks:index')
|
INDEX_URL = reverse('horizon:admin:networks:index')
|
||||||
@ -166,13 +168,19 @@ class NetworkTests(test.BaseAdminViewTests):
|
|||||||
network = self.networks.first()
|
network = self.networks.first()
|
||||||
api.keystone.tenant_list(IsA(http.HttpRequest), admin=True)\
|
api.keystone.tenant_list(IsA(http.HttpRequest), admin=True)\
|
||||||
.AndReturn(tenants)
|
.AndReturn(tenants)
|
||||||
api.quantum.network_create(IsA(http.HttpRequest), name=network.name,
|
params = {'name': network.name,
|
||||||
tenant_id=tenant_id, shared=True)\
|
'tenant_id': tenant_id,
|
||||||
|
'admin_state_up': network.admin_state_up,
|
||||||
|
'router:external': True,
|
||||||
|
'shared': True}
|
||||||
|
api.quantum.network_create(IsA(http.HttpRequest), **params)\
|
||||||
.AndReturn(network)
|
.AndReturn(network)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
form_data = {'tenant_id': tenant_id,
|
form_data = {'tenant_id': tenant_id,
|
||||||
'name': network.name,
|
'name': network.name,
|
||||||
|
'admin_state': network.admin_state_up,
|
||||||
|
'external': True,
|
||||||
'shared': True}
|
'shared': True}
|
||||||
url = reverse('horizon:admin:networks:create')
|
url = reverse('horizon:admin:networks:create')
|
||||||
res = self.client.post(url, form_data)
|
res = self.client.post(url, form_data)
|
||||||
@ -188,13 +196,19 @@ class NetworkTests(test.BaseAdminViewTests):
|
|||||||
network = self.networks.first()
|
network = self.networks.first()
|
||||||
api.keystone.tenant_list(IsA(http.HttpRequest), admin=True)\
|
api.keystone.tenant_list(IsA(http.HttpRequest), admin=True)\
|
||||||
.AndReturn(tenants)
|
.AndReturn(tenants)
|
||||||
api.quantum.network_create(IsA(http.HttpRequest), name=network.name,
|
params = {'name': network.name,
|
||||||
tenant_id=tenant_id, shared=False)\
|
'tenant_id': tenant_id,
|
||||||
|
'admin_state_up': network.admin_state_up,
|
||||||
|
'router:external': True,
|
||||||
|
'shared': False}
|
||||||
|
api.quantum.network_create(IsA(http.HttpRequest), **params)\
|
||||||
.AndRaise(self.exceptions.quantum)
|
.AndRaise(self.exceptions.quantum)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
form_data = {'tenant_id': tenant_id,
|
form_data = {'tenant_id': tenant_id,
|
||||||
'name': network.name,
|
'name': network.name,
|
||||||
|
'admin_state': network.admin_state_up,
|
||||||
|
'external': True,
|
||||||
'shared': False}
|
'shared': False}
|
||||||
url = reverse('horizon:admin:networks:create')
|
url = reverse('horizon:admin:networks:create')
|
||||||
res = self.client.post(url, form_data)
|
res = self.client.post(url, form_data)
|
||||||
@ -233,19 +247,25 @@ class NetworkTests(test.BaseAdminViewTests):
|
|||||||
'network_get',)})
|
'network_get',)})
|
||||||
def test_network_update_post(self):
|
def test_network_update_post(self):
|
||||||
network = self.networks.first()
|
network = self.networks.first()
|
||||||
|
params = {'name': network.name,
|
||||||
|
'shared': True,
|
||||||
|
'admin_state_up': network.admin_state_up,
|
||||||
|
'router:external': True}
|
||||||
api.quantum.network_modify(IsA(http.HttpRequest), network.id,
|
api.quantum.network_modify(IsA(http.HttpRequest), network.id,
|
||||||
name=network.name, shared=True)\
|
**params)\
|
||||||
.AndReturn(network)
|
.AndReturn(network)
|
||||||
api.quantum.network_get(IsA(http.HttpRequest), network.id)\
|
api.quantum.network_get(IsA(http.HttpRequest), network.id)\
|
||||||
.AndReturn(network)
|
.AndReturn(network)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
formData = {'network_id': network.id,
|
form_data = {'network_id': network.id,
|
||||||
'name': network.name,
|
'name': network.name,
|
||||||
'tenant_id': network.tenant_id,
|
'tenant_id': network.tenant_id,
|
||||||
'shared': True}
|
'admin_state': network.admin_state_up,
|
||||||
|
'shared': True,
|
||||||
|
'external': True}
|
||||||
url = reverse('horizon:admin:networks:update', args=[network.id])
|
url = reverse('horizon:admin:networks:update', args=[network.id])
|
||||||
res = self.client.post(url, formData)
|
res = self.client.post(url, form_data)
|
||||||
|
|
||||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||||
|
|
||||||
@ -253,8 +273,12 @@ class NetworkTests(test.BaseAdminViewTests):
|
|||||||
'network_get',)})
|
'network_get',)})
|
||||||
def test_network_update_post_exception(self):
|
def test_network_update_post_exception(self):
|
||||||
network = self.networks.first()
|
network = self.networks.first()
|
||||||
|
params = {'name': network.name,
|
||||||
|
'shared': False,
|
||||||
|
'admin_state_up': network.admin_state_up,
|
||||||
|
'router:external': False}
|
||||||
api.quantum.network_modify(IsA(http.HttpRequest), network.id,
|
api.quantum.network_modify(IsA(http.HttpRequest), network.id,
|
||||||
name=network.name, shared=False)\
|
**params)\
|
||||||
.AndRaise(self.exceptions.quantum)
|
.AndRaise(self.exceptions.quantum)
|
||||||
api.quantum.network_get(IsA(http.HttpRequest), network.id)\
|
api.quantum.network_get(IsA(http.HttpRequest), network.id)\
|
||||||
.AndReturn(network)
|
.AndReturn(network)
|
||||||
@ -263,7 +287,9 @@ class NetworkTests(test.BaseAdminViewTests):
|
|||||||
form_data = {'network_id': network.id,
|
form_data = {'network_id': network.id,
|
||||||
'name': network.name,
|
'name': network.name,
|
||||||
'tenant_id': network.tenant_id,
|
'tenant_id': network.tenant_id,
|
||||||
'shared': False}
|
'admin_state': network.admin_state_up,
|
||||||
|
'shared': False,
|
||||||
|
'external': False}
|
||||||
url = reverse('horizon:admin:networks:update', args=[network.id])
|
url = reverse('horizon:admin:networks:update', args=[network.id])
|
||||||
res = self.client.post(url, form_data)
|
res = self.client.post(url, form_data)
|
||||||
|
|
||||||
@ -308,6 +334,9 @@ class NetworkTests(test.BaseAdminViewTests):
|
|||||||
|
|
||||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||||
|
|
||||||
|
|
||||||
|
class NetworkSubnetTests(test.BaseAdminViewTests):
|
||||||
|
|
||||||
@test.create_stubs({api.quantum: ('subnet_get',)})
|
@test.create_stubs({api.quantum: ('subnet_get',)})
|
||||||
def test_subnet_detail(self):
|
def test_subnet_detail(self):
|
||||||
subnet = self.subnets.first()
|
subnet = self.subnets.first()
|
||||||
@ -367,21 +396,17 @@ class NetworkTests(test.BaseAdminViewTests):
|
|||||||
.AndReturn(self.networks.first())
|
.AndReturn(self.networks.first())
|
||||||
api.quantum.subnet_create(IsA(http.HttpRequest),
|
api.quantum.subnet_create(IsA(http.HttpRequest),
|
||||||
network_id=network.id,
|
network_id=network.id,
|
||||||
network_name=network.name,
|
|
||||||
name=subnet.name,
|
name=subnet.name,
|
||||||
cidr=subnet.cidr,
|
cidr=subnet.cidr,
|
||||||
ip_version=subnet.ip_version,
|
ip_version=subnet.ip_version,
|
||||||
gateway_ip=subnet.gateway_ip,
|
gateway_ip=subnet.gateway_ip,
|
||||||
|
enable_dhcp=subnet.enable_dhcp,
|
||||||
|
allocation_pools=subnet.allocation_pools,
|
||||||
tenant_id=subnet.tenant_id)\
|
tenant_id=subnet.tenant_id)\
|
||||||
.AndReturn(subnet)
|
.AndReturn(subnet)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
form_data = {'network_id': subnet.network_id,
|
form_data = form_data_subnet(subnet)
|
||||||
'network_name': network.name,
|
|
||||||
'name': subnet.name,
|
|
||||||
'cidr': subnet.cidr,
|
|
||||||
'ip_version': subnet.ip_version,
|
|
||||||
'gateway_ip': subnet.gateway_ip}
|
|
||||||
url = reverse('horizon:admin:networks:addsubnet',
|
url = reverse('horizon:admin:networks:addsubnet',
|
||||||
args=[subnet.network_id])
|
args=[subnet.network_id])
|
||||||
res = self.client.post(url, form_data)
|
res = self.client.post(url, form_data)
|
||||||
@ -401,12 +426,7 @@ class NetworkTests(test.BaseAdminViewTests):
|
|||||||
.AndRaise(self.exceptions.quantum)
|
.AndRaise(self.exceptions.quantum)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
form_data = {'network_id': subnet.network_id,
|
form_data = form_data_subnet(subnet, allocation_pools=[])
|
||||||
'network_name': network.name,
|
|
||||||
'name': subnet.name,
|
|
||||||
'cidr': subnet.cidr,
|
|
||||||
'ip_version': subnet.ip_version,
|
|
||||||
'gateway_ip': subnet.gateway_ip}
|
|
||||||
url = reverse('horizon:admin:networks:addsubnet',
|
url = reverse('horizon:admin:networks:addsubnet',
|
||||||
args=[subnet.network_id])
|
args=[subnet.network_id])
|
||||||
res = self.client.post(url, form_data)
|
res = self.client.post(url, form_data)
|
||||||
@ -430,21 +450,16 @@ class NetworkTests(test.BaseAdminViewTests):
|
|||||||
.AndReturn(self.networks.first())
|
.AndReturn(self.networks.first())
|
||||||
api.quantum.subnet_create(IsA(http.HttpRequest),
|
api.quantum.subnet_create(IsA(http.HttpRequest),
|
||||||
network_id=network.id,
|
network_id=network.id,
|
||||||
network_name=network.name,
|
|
||||||
name=subnet.name,
|
name=subnet.name,
|
||||||
cidr=subnet.cidr,
|
cidr=subnet.cidr,
|
||||||
ip_version=subnet.ip_version,
|
ip_version=subnet.ip_version,
|
||||||
gateway_ip=subnet.gateway_ip,
|
gateway_ip=subnet.gateway_ip,
|
||||||
|
enable_dhcp=subnet.enable_dhcp,
|
||||||
tenant_id=subnet.tenant_id)\
|
tenant_id=subnet.tenant_id)\
|
||||||
.AndRaise(self.exceptions.quantum)
|
.AndRaise(self.exceptions.quantum)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
form_data = {'network_id': subnet.network_id,
|
form_data = form_data_subnet(subnet, allocation_pools=[])
|
||||||
'network_name': network.name,
|
|
||||||
'name': subnet.name,
|
|
||||||
'cidr': subnet.cidr,
|
|
||||||
'ip_version': subnet.ip_version,
|
|
||||||
'gateway_ip': subnet.gateway_ip}
|
|
||||||
url = reverse('horizon:admin:networks:addsubnet',
|
url = reverse('horizon:admin:networks:addsubnet',
|
||||||
args=[subnet.network_id])
|
args=[subnet.network_id])
|
||||||
res = self.client.post(url, form_data)
|
res = self.client.post(url, form_data)
|
||||||
@ -464,12 +479,7 @@ class NetworkTests(test.BaseAdminViewTests):
|
|||||||
|
|
||||||
# dummy IPv6 address
|
# dummy IPv6 address
|
||||||
cidr = '2001:0DB8:0:CD30:123:4567:89AB:CDEF/60'
|
cidr = '2001:0DB8:0:CD30:123:4567:89AB:CDEF/60'
|
||||||
form_data = {'network_id': subnet.network_id,
|
form_data = form_data_subnet(subnet, cidr=cidr, allocation_pools=[])
|
||||||
'network_name': network.name,
|
|
||||||
'name': subnet.name,
|
|
||||||
'cidr': cidr,
|
|
||||||
'ip_version': subnet.ip_version,
|
|
||||||
'gateway_ip': subnet.gateway_ip}
|
|
||||||
url = reverse('horizon:admin:networks:addsubnet',
|
url = reverse('horizon:admin:networks:addsubnet',
|
||||||
args=[subnet.network_id])
|
args=[subnet.network_id])
|
||||||
res = self.client.post(url, form_data)
|
res = self.client.post(url, form_data)
|
||||||
@ -488,12 +498,8 @@ class NetworkTests(test.BaseAdminViewTests):
|
|||||||
|
|
||||||
# dummy IPv6 address
|
# dummy IPv6 address
|
||||||
gateway_ip = '2001:0DB8:0:CD30:123:4567:89AB:CDEF'
|
gateway_ip = '2001:0DB8:0:CD30:123:4567:89AB:CDEF'
|
||||||
form_data = {'network_id': subnet.network_id,
|
form_data = form_data_subnet(subnet, gateway_ip=gateway_ip,
|
||||||
'network_name': network.name,
|
allocation_pools=[])
|
||||||
'name': subnet.name,
|
|
||||||
'cidr': subnet.cidr,
|
|
||||||
'ip_version': subnet.ip_version,
|
|
||||||
'gateway_ip': gateway_ip}
|
|
||||||
url = reverse('horizon:admin:networks:addsubnet',
|
url = reverse('horizon:admin:networks:addsubnet',
|
||||||
args=[subnet.network_id])
|
args=[subnet.network_id])
|
||||||
res = self.client.post(url, form_data)
|
res = self.client.post(url, form_data)
|
||||||
@ -508,20 +514,17 @@ class NetworkTests(test.BaseAdminViewTests):
|
|||||||
.AndReturn(subnet)
|
.AndReturn(subnet)
|
||||||
api.quantum.subnet_modify(IsA(http.HttpRequest), subnet.id,
|
api.quantum.subnet_modify(IsA(http.HttpRequest), subnet.id,
|
||||||
name=subnet.name,
|
name=subnet.name,
|
||||||
gateway_ip=subnet.gateway_ip)\
|
gateway_ip=subnet.gateway_ip,
|
||||||
|
enable_dhcp=subnet.enable_dhcp,
|
||||||
|
dns_nameservers=[],
|
||||||
|
host_routes=[])\
|
||||||
.AndReturn(subnet)
|
.AndReturn(subnet)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
formData = {'network_id': subnet.network_id,
|
form_data = form_data_subnet(subnet, allocation_pools=[])
|
||||||
'tenant_id': subnet.tenant_id,
|
|
||||||
'subnet_id': subnet.id,
|
|
||||||
'name': subnet.name,
|
|
||||||
'cidr': subnet.cidr,
|
|
||||||
'ip_version': subnet.ip_version,
|
|
||||||
'gateway_ip': subnet.gateway_ip}
|
|
||||||
url = reverse('horizon:admin:networks:editsubnet',
|
url = reverse('horizon:admin:networks:editsubnet',
|
||||||
args=[subnet.network_id, subnet.id])
|
args=[subnet.network_id, subnet.id])
|
||||||
res = self.client.post(url, formData)
|
res = self.client.post(url, form_data)
|
||||||
|
|
||||||
redir_url = reverse('horizon:admin:networks:detail',
|
redir_url = reverse('horizon:admin:networks:detail',
|
||||||
args=[subnet.network_id])
|
args=[subnet.network_id])
|
||||||
@ -537,16 +540,11 @@ class NetworkTests(test.BaseAdminViewTests):
|
|||||||
|
|
||||||
# dummy IPv6 address
|
# dummy IPv6 address
|
||||||
gateway_ip = '2001:0DB8:0:CD30:123:4567:89AB:CDEF'
|
gateway_ip = '2001:0DB8:0:CD30:123:4567:89AB:CDEF'
|
||||||
formData = {'network_id': subnet.network_id,
|
form_data = form_data_subnet(subnet, gateway_ip=gateway_ip,
|
||||||
'tenant_id': subnet.tenant_id,
|
allocation_pools=[])
|
||||||
'subnet_id': subnet.id,
|
|
||||||
'name': subnet.name,
|
|
||||||
'cidr': subnet.cidr,
|
|
||||||
'ip_version': subnet.ip_version,
|
|
||||||
'gateway_ip': gateway_ip}
|
|
||||||
url = reverse('horizon:admin:networks:editsubnet',
|
url = reverse('horizon:admin:networks:editsubnet',
|
||||||
args=[subnet.network_id, subnet.id])
|
args=[subnet.network_id, subnet.id])
|
||||||
res = self.client.post(url, formData)
|
res = self.client.post(url, form_data)
|
||||||
|
|
||||||
self.assertContains(res, 'Gateway IP and IP version are inconsistent.')
|
self.assertContains(res, 'Gateway IP and IP version are inconsistent.')
|
||||||
|
|
||||||
@ -563,10 +561,10 @@ class NetworkTests(test.BaseAdminViewTests):
|
|||||||
.AndReturn([self.ports.first()])
|
.AndReturn([self.ports.first()])
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
formData = {'action': 'subnets__delete__%s' % subnet.id}
|
form_data = {'action': 'subnets__delete__%s' % subnet.id}
|
||||||
url = reverse('horizon:admin:networks:detail',
|
url = reverse('horizon:admin:networks:detail',
|
||||||
args=[network_id])
|
args=[network_id])
|
||||||
res = self.client.post(url, formData)
|
res = self.client.post(url, form_data)
|
||||||
|
|
||||||
self.assertRedirectsNoFollow(res, url)
|
self.assertRedirectsNoFollow(res, url)
|
||||||
|
|
||||||
@ -584,13 +582,16 @@ class NetworkTests(test.BaseAdminViewTests):
|
|||||||
.AndReturn([self.ports.first()])
|
.AndReturn([self.ports.first()])
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
formData = {'action': 'subnets__delete__%s' % subnet.id}
|
form_data = {'action': 'subnets__delete__%s' % subnet.id}
|
||||||
url = reverse('horizon:admin:networks:detail',
|
url = reverse('horizon:admin:networks:detail',
|
||||||
args=[network_id])
|
args=[network_id])
|
||||||
res = self.client.post(url, formData)
|
res = self.client.post(url, form_data)
|
||||||
|
|
||||||
self.assertRedirectsNoFollow(res, url)
|
self.assertRedirectsNoFollow(res, url)
|
||||||
|
|
||||||
|
|
||||||
|
class NetworkPortTests(test.BaseAdminViewTests):
|
||||||
|
|
||||||
@test.create_stubs({api.quantum: ('port_get',)})
|
@test.create_stubs({api.quantum: ('port_get',)})
|
||||||
def test_port_detail(self):
|
def test_port_detail(self):
|
||||||
port = self.ports.first()
|
port = self.ports.first()
|
||||||
@ -651,14 +652,18 @@ class NetworkTests(test.BaseAdminViewTests):
|
|||||||
network_id=network.id,
|
network_id=network.id,
|
||||||
network_name=network.name,
|
network_name=network.name,
|
||||||
name=port.name,
|
name=port.name,
|
||||||
device_id=port.device_id)\
|
admin_state_up=port.admin_state_up,
|
||||||
|
device_id=port.device_id,
|
||||||
|
device_owner=port.device_owner)\
|
||||||
.AndReturn(port)
|
.AndReturn(port)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
form_data = {'network_id': port.network_id,
|
form_data = {'network_id': port.network_id,
|
||||||
'network_name': network.name,
|
'network_name': network.name,
|
||||||
'name': port.name,
|
'name': port.name,
|
||||||
'device_id': port.device_id}
|
'admin_state': port.admin_state_up,
|
||||||
|
'device_id': port.device_id,
|
||||||
|
'device_owner': port.device_owner}
|
||||||
url = reverse('horizon:admin:networks:addport',
|
url = reverse('horizon:admin:networks:addport',
|
||||||
args=[port.network_id])
|
args=[port.network_id])
|
||||||
res = self.client.post(url, form_data)
|
res = self.client.post(url, form_data)
|
||||||
@ -684,14 +689,18 @@ class NetworkTests(test.BaseAdminViewTests):
|
|||||||
network_id=network.id,
|
network_id=network.id,
|
||||||
network_name=network.name,
|
network_name=network.name,
|
||||||
name=port.name,
|
name=port.name,
|
||||||
device_id=port.device_id)\
|
admin_state_up=port.admin_state_up,
|
||||||
|
device_id=port.device_id,
|
||||||
|
device_owner=port.device_owner)\
|
||||||
.AndRaise(self.exceptions.quantum)
|
.AndRaise(self.exceptions.quantum)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
form_data = {'network_id': port.network_id,
|
form_data = {'network_id': port.network_id,
|
||||||
'network_name': network.name,
|
'network_name': network.name,
|
||||||
'name': port.name,
|
'name': port.name,
|
||||||
'device_id': port.device_id}
|
'admin_state': port.admin_state_up,
|
||||||
|
'device_id': port.device_id,
|
||||||
|
'device_owner': port.device_owner}
|
||||||
url = reverse('horizon:admin:networks:addport',
|
url = reverse('horizon:admin:networks:addport',
|
||||||
args=[port.network_id])
|
args=[port.network_id])
|
||||||
res = self.client.post(url, form_data)
|
res = self.client.post(url, form_data)
|
||||||
@ -722,18 +731,22 @@ class NetworkTests(test.BaseAdminViewTests):
|
|||||||
api.quantum.port_get(IsA(http.HttpRequest), port.id)\
|
api.quantum.port_get(IsA(http.HttpRequest), port.id)\
|
||||||
.AndReturn(port)
|
.AndReturn(port)
|
||||||
api.quantum.port_modify(IsA(http.HttpRequest), port.id,
|
api.quantum.port_modify(IsA(http.HttpRequest), port.id,
|
||||||
name=port.name, device_id=port.device_id)\
|
name=port.name,
|
||||||
|
admin_state_up=port.admin_state_up,
|
||||||
|
device_id=port.device_id,
|
||||||
|
device_owner=port.device_owner)\
|
||||||
.AndReturn(port)
|
.AndReturn(port)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
formData = {'tenant_id': port.tenant_id,
|
form_data = {'network_id': port.network_id,
|
||||||
'network_id': port.network_id,
|
'port_id': port.id,
|
||||||
'port_id': port.id,
|
'name': port.name,
|
||||||
'name': port.name,
|
'admin_state': port.admin_state_up,
|
||||||
'device_id': port.device_id}
|
'device_id': port.device_id,
|
||||||
|
'device_owner': port.device_owner}
|
||||||
url = reverse('horizon:admin:networks:editport',
|
url = reverse('horizon:admin:networks:editport',
|
||||||
args=[port.network_id, port.id])
|
args=[port.network_id, port.id])
|
||||||
res = self.client.post(url, formData)
|
res = self.client.post(url, form_data)
|
||||||
|
|
||||||
redir_url = reverse('horizon:admin:networks:detail',
|
redir_url = reverse('horizon:admin:networks:detail',
|
||||||
args=[port.network_id])
|
args=[port.network_id])
|
||||||
@ -746,18 +759,22 @@ class NetworkTests(test.BaseAdminViewTests):
|
|||||||
api.quantum.port_get(IsA(http.HttpRequest), port.id)\
|
api.quantum.port_get(IsA(http.HttpRequest), port.id)\
|
||||||
.AndReturn(port)
|
.AndReturn(port)
|
||||||
api.quantum.port_modify(IsA(http.HttpRequest), port.id,
|
api.quantum.port_modify(IsA(http.HttpRequest), port.id,
|
||||||
name=port.name, device_id=port.device_id)\
|
name=port.name,
|
||||||
|
admin_state_up=port.admin_state_up,
|
||||||
|
device_id=port.device_id,
|
||||||
|
device_owner=port.device_owner)\
|
||||||
.AndRaise(self.exceptions.quantum)
|
.AndRaise(self.exceptions.quantum)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
formData = {'tenant_id': port.tenant_id,
|
form_data = {'network_id': port.network_id,
|
||||||
'network_id': port.network_id,
|
'port_id': port.id,
|
||||||
'port_id': port.id,
|
'name': port.name,
|
||||||
'name': port.name,
|
'admin_state': port.admin_state_up,
|
||||||
'device_id': port.device_id}
|
'device_id': port.device_id,
|
||||||
|
'device_owner': port.device_owner}
|
||||||
url = reverse('horizon:admin:networks:editport',
|
url = reverse('horizon:admin:networks:editport',
|
||||||
args=[port.network_id, port.id])
|
args=[port.network_id, port.id])
|
||||||
res = self.client.post(url, formData)
|
res = self.client.post(url, form_data)
|
||||||
|
|
||||||
redir_url = reverse('horizon:admin:networks:detail',
|
redir_url = reverse('horizon:admin:networks:detail',
|
||||||
args=[port.network_id])
|
args=[port.network_id])
|
||||||
@ -776,10 +793,10 @@ class NetworkTests(test.BaseAdminViewTests):
|
|||||||
.AndReturn([self.ports.first()])
|
.AndReturn([self.ports.first()])
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
formData = {'action': 'ports__delete__%s' % port.id}
|
form_data = {'action': 'ports__delete__%s' % port.id}
|
||||||
url = reverse('horizon:admin:networks:detail',
|
url = reverse('horizon:admin:networks:detail',
|
||||||
args=[network_id])
|
args=[network_id])
|
||||||
res = self.client.post(url, formData)
|
res = self.client.post(url, form_data)
|
||||||
|
|
||||||
self.assertRedirectsNoFollow(res, url)
|
self.assertRedirectsNoFollow(res, url)
|
||||||
|
|
||||||
@ -797,9 +814,9 @@ class NetworkTests(test.BaseAdminViewTests):
|
|||||||
.AndReturn([self.ports.first()])
|
.AndReturn([self.ports.first()])
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
formData = {'action': 'ports__delete__%s' % port.id}
|
form_data = {'action': 'ports__delete__%s' % port.id}
|
||||||
url = reverse('horizon:admin:networks:detail',
|
url = reverse('horizon:admin:networks:detail',
|
||||||
args=[network_id])
|
args=[network_id])
|
||||||
res = self.client.post(url, formData)
|
res = self.client.post(url, form_data)
|
||||||
|
|
||||||
self.assertRedirectsNoFollow(res, url)
|
self.assertRedirectsNoFollow(res, url)
|
||||||
|
@ -137,4 +137,6 @@ class UpdateView(user_views.UpdateView):
|
|||||||
return {'network_id': network['id'],
|
return {'network_id': network['id'],
|
||||||
'tenant_id': network['tenant_id'],
|
'tenant_id': network['tenant_id'],
|
||||||
'name': network['name'],
|
'name': network['name'],
|
||||||
'shared': network['shared']}
|
'admin_state': network['admin_state_up'],
|
||||||
|
'shared': network['shared'],
|
||||||
|
'external': network['router__external']}
|
||||||
|
@ -39,12 +39,15 @@ class UpdateNetwork(forms.SelfHandlingForm):
|
|||||||
network_id = forms.CharField(label=_("ID"),
|
network_id = forms.CharField(label=_("ID"),
|
||||||
widget=forms.TextInput(
|
widget=forms.TextInput(
|
||||||
attrs={'readonly': 'readonly'}))
|
attrs={'readonly': 'readonly'}))
|
||||||
|
admin_state = forms.BooleanField(label=_("Admin State"), required=False)
|
||||||
failure_url = 'horizon:project:networks:index'
|
failure_url = 'horizon:project:networks:index'
|
||||||
|
|
||||||
def handle(self, request, data):
|
def handle(self, request, data):
|
||||||
try:
|
try:
|
||||||
|
params = {'admin_state_up': data['admin_state'],
|
||||||
|
'name': data['name']}
|
||||||
network = api.quantum.network_modify(request, data['network_id'],
|
network = api.quantum.network_modify(request, data['network_id'],
|
||||||
name=data['name'])
|
**params)
|
||||||
msg = _('Network %s was successfully updated.') % data['name']
|
msg = _('Network %s was successfully updated.') % data['name']
|
||||||
LOG.debug(msg)
|
LOG.debug(msg)
|
||||||
messages.success(request, msg)
|
messages.success(request, msg)
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
# 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.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 openstack_dashboard import api
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class UpdatePort(forms.SelfHandlingForm):
|
||||||
|
network_id = forms.CharField(widget=forms.HiddenInput())
|
||||||
|
port_id = forms.CharField(widget=forms.HiddenInput())
|
||||||
|
name = forms.CharField(max_length=255,
|
||||||
|
label=_("Name"),
|
||||||
|
required=False)
|
||||||
|
admin_state = forms.BooleanField(label=_("Admin State"), required=False)
|
||||||
|
failure_url = 'horizon:project:networks:detail'
|
||||||
|
|
||||||
|
def handle(self, request, data):
|
||||||
|
try:
|
||||||
|
LOG.debug('params = %s' % data)
|
||||||
|
port = api.quantum.port_modify(request, data['port_id'],
|
||||||
|
name=data['name'],
|
||||||
|
admin_state_up=data['admin_state'])
|
||||||
|
msg = _('Port %s was successfully updated.') % data['port_id']
|
||||||
|
LOG.debug(msg)
|
||||||
|
messages.success(request, msg)
|
||||||
|
return port
|
||||||
|
except Exception:
|
||||||
|
msg = _('Failed to update port %s') % data['port_id']
|
||||||
|
LOG.info(msg)
|
||||||
|
redirect = reverse(self.failure_url,
|
||||||
|
args=[data['network_id']])
|
||||||
|
exceptions.handle(request, msg, redirect=redirect)
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
from django import template
|
from django import template
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
@ -32,7 +33,23 @@ def get_fixed_ips(port):
|
|||||||
|
|
||||||
|
|
||||||
def get_attached(port):
|
def get_attached(port):
|
||||||
return _('Attached') if port['device_id'] else _('Detached')
|
if port['device_owner']:
|
||||||
|
return port['device_owner']
|
||||||
|
elif port['device_id']:
|
||||||
|
return _('Attached')
|
||||||
|
else:
|
||||||
|
return _('Detached')
|
||||||
|
|
||||||
|
|
||||||
|
class UpdatePort(tables.LinkAction):
|
||||||
|
name = "update"
|
||||||
|
verbose_name = _("Edit Port")
|
||||||
|
url = "horizon:project:networks:editport"
|
||||||
|
classes = ("ajax-modal", "btn-edit")
|
||||||
|
|
||||||
|
def get_link_url(self, port):
|
||||||
|
network_id = self.table.kwargs['network_id']
|
||||||
|
return reverse(self.url, args=(network_id, port.id))
|
||||||
|
|
||||||
|
|
||||||
class PortsTable(tables.DataTable):
|
class PortsTable(tables.DataTable):
|
||||||
@ -40,7 +57,7 @@ class PortsTable(tables.DataTable):
|
|||||||
verbose_name=_("Name"),
|
verbose_name=_("Name"),
|
||||||
link="horizon:project:networks:ports:detail")
|
link="horizon:project:networks:ports:detail")
|
||||||
fixed_ips = tables.Column(get_fixed_ips, verbose_name=_("Fixed IPs"))
|
fixed_ips = tables.Column(get_fixed_ips, verbose_name=_("Fixed IPs"))
|
||||||
attached = tables.Column(get_attached, verbose_name=_("Device Attached"))
|
attached = tables.Column(get_attached, verbose_name=_("Attached Device"))
|
||||||
status = tables.Column("status", verbose_name=_("Status"))
|
status = tables.Column("status", verbose_name=_("Status"))
|
||||||
admin_state = tables.Column("admin_state",
|
admin_state = tables.Column("admin_state",
|
||||||
verbose_name=_("Admin State"))
|
verbose_name=_("Admin State"))
|
||||||
@ -51,3 +68,4 @@ class PortsTable(tables.DataTable):
|
|||||||
class Meta:
|
class Meta:
|
||||||
name = "ports"
|
name = "ports"
|
||||||
verbose_name = _("Ports")
|
verbose_name = _("Ports")
|
||||||
|
row_actions = (UpdatePort,)
|
||||||
|
@ -14,11 +14,59 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
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 tabs
|
from horizon import tabs
|
||||||
|
|
||||||
|
from openstack_dashboard import api
|
||||||
|
|
||||||
|
from .forms import UpdatePort
|
||||||
from .tabs import PortDetailTabs
|
from .tabs import PortDetailTabs
|
||||||
|
|
||||||
|
|
||||||
class DetailView(tabs.TabView):
|
class DetailView(tabs.TabView):
|
||||||
tab_group_class = PortDetailTabs
|
tab_group_class = PortDetailTabs
|
||||||
template_name = 'project/networks/ports/detail.html'
|
template_name = 'project/networks/ports/detail.html'
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateView(forms.ModalFormView):
|
||||||
|
form_class = UpdatePort
|
||||||
|
template_name = 'project/networks/ports/update.html'
|
||||||
|
context_object_name = 'port'
|
||||||
|
success_url = 'horizon:project:networks:detail'
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse(self.success_url,
|
||||||
|
args=(self.kwargs['network_id'],))
|
||||||
|
|
||||||
|
def _get_object(self, *args, **kwargs):
|
||||||
|
if not hasattr(self, "_object"):
|
||||||
|
port_id = self.kwargs['port_id']
|
||||||
|
try:
|
||||||
|
self._object = api.quantum.port_get(self.request, port_id)
|
||||||
|
except:
|
||||||
|
redirect = reverse("horizon:project:networks:detail",
|
||||||
|
args=(self.kwargs['network_id'],))
|
||||||
|
msg = _('Unable to retrieve port details')
|
||||||
|
exceptions.handle(self.request, msg, redirect=redirect)
|
||||||
|
return self._object
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super(UpdateView, self).get_context_data(**kwargs)
|
||||||
|
port = self._get_object()
|
||||||
|
context['port_id'] = port['id']
|
||||||
|
context['network_id'] = port['network_id']
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get_initial(self):
|
||||||
|
port = self._get_object()
|
||||||
|
return {'port_id': port['id'],
|
||||||
|
'network_id': port['network_id'],
|
||||||
|
'tenant_id': port['tenant_id'],
|
||||||
|
'name': port['name'],
|
||||||
|
'admin_state': port['admin_state_up'],
|
||||||
|
'device_id': port['device_id'],
|
||||||
|
'device_owner': port['device_owner']}
|
||||||
|
@ -1,139 +0,0 @@
|
|||||||
# 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
|
|
||||||
import netaddr
|
|
||||||
|
|
||||||
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 horizon.utils import fields
|
|
||||||
|
|
||||||
from openstack_dashboard import api
|
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class CreateSubnet(forms.SelfHandlingForm):
|
|
||||||
network_name = forms.CharField(label=_("Network Name"),
|
|
||||||
required=False,
|
|
||||||
widget=forms.TextInput(
|
|
||||||
attrs={'readonly': 'readonly'}))
|
|
||||||
network_id = forms.CharField(label=_("Network ID"),
|
|
||||||
widget=forms.TextInput(
|
|
||||||
attrs={'readonly': 'readonly'}))
|
|
||||||
name = forms.CharField(max_length=255,
|
|
||||||
label=_("Name"),
|
|
||||||
required=False)
|
|
||||||
cidr = fields.IPField(label=_("Network Address"),
|
|
||||||
required=True,
|
|
||||||
initial="",
|
|
||||||
help_text=_("Network address in CIDR format "
|
|
||||||
"(e.g. 192.168.0.0/24)"),
|
|
||||||
version=fields.IPv4 | fields.IPv6,
|
|
||||||
mask=True)
|
|
||||||
ip_version = forms.ChoiceField(choices=[(4, 'IPv4'), (6, 'IPv6')],
|
|
||||||
label=_("IP Version"))
|
|
||||||
gateway_ip = fields.IPField(label=_("Gateway IP"),
|
|
||||||
required=False,
|
|
||||||
initial="",
|
|
||||||
help_text=_("IP address of Gateway "
|
|
||||||
"(e.g. 192.168.0.1)"),
|
|
||||||
version=fields.IPv4 | fields.IPv6,
|
|
||||||
mask=False)
|
|
||||||
failure_url = 'horizon:project:networks:detail'
|
|
||||||
|
|
||||||
def clean(self):
|
|
||||||
cleaned_data = super(CreateSubnet, self).clean()
|
|
||||||
cidr = cleaned_data.get('cidr')
|
|
||||||
ip_version = int(cleaned_data.get('ip_version'))
|
|
||||||
gateway_ip = cleaned_data.get('gateway_ip')
|
|
||||||
if cidr:
|
|
||||||
if netaddr.IPNetwork(cidr).version is not ip_version:
|
|
||||||
msg = _('Network Address and IP version are inconsistent.')
|
|
||||||
raise forms.ValidationError(msg)
|
|
||||||
if gateway_ip:
|
|
||||||
if netaddr.IPAddress(gateway_ip).version is not ip_version:
|
|
||||||
msg = _('Gateway IP and IP version are inconsistent.')
|
|
||||||
raise forms.ValidationError(msg)
|
|
||||||
return cleaned_data
|
|
||||||
|
|
||||||
def handle(self, request, data):
|
|
||||||
try:
|
|
||||||
LOG.debug('params = %s' % data)
|
|
||||||
data['ip_version'] = int(data['ip_version'])
|
|
||||||
if not data['gateway_ip']:
|
|
||||||
del data['gateway_ip']
|
|
||||||
subnet = api.quantum.subnet_create(request, **data)
|
|
||||||
msg = _('Subnet %s was successfully created.') % data['cidr']
|
|
||||||
LOG.debug(msg)
|
|
||||||
messages.success(request, msg)
|
|
||||||
return subnet
|
|
||||||
except Exception:
|
|
||||||
msg = _('Failed to create subnet %s') % data['cidr']
|
|
||||||
LOG.info(msg)
|
|
||||||
redirect = reverse(self.failure_url, args=[data['network_id']])
|
|
||||||
exceptions.handle(request, msg, redirect=redirect)
|
|
||||||
|
|
||||||
|
|
||||||
class UpdateSubnet(forms.SelfHandlingForm):
|
|
||||||
network_id = forms.CharField(widget=forms.HiddenInput())
|
|
||||||
subnet_id = forms.CharField(widget=forms.HiddenInput())
|
|
||||||
cidr = forms.CharField(widget=forms.HiddenInput())
|
|
||||||
ip_version = forms.CharField(widget=forms.HiddenInput())
|
|
||||||
name = forms.CharField(max_length=255,
|
|
||||||
label=_("Name"),
|
|
||||||
required=False)
|
|
||||||
gateway_ip = fields.IPField(label=_("Gateway IP"),
|
|
||||||
required=True,
|
|
||||||
initial="",
|
|
||||||
help_text=_("IP address of Gateway "
|
|
||||||
"(e.g. 192.168.0.1)"),
|
|
||||||
version=fields.IPv4 | fields.IPv6,
|
|
||||||
mask=False)
|
|
||||||
failure_url = 'horizon:project:networks:detail'
|
|
||||||
|
|
||||||
def clean(self):
|
|
||||||
cleaned_data = super(UpdateSubnet, self).clean()
|
|
||||||
ip_version = int(cleaned_data.get('ip_version'))
|
|
||||||
gateway_ip = cleaned_data.get('gateway_ip')
|
|
||||||
if gateway_ip:
|
|
||||||
if netaddr.IPAddress(gateway_ip).version is not ip_version:
|
|
||||||
msg = _('Gateway IP and IP version are inconsistent.')
|
|
||||||
raise forms.ValidationError(msg)
|
|
||||||
return cleaned_data
|
|
||||||
|
|
||||||
def handle(self, request, data):
|
|
||||||
try:
|
|
||||||
LOG.debug('params = %s' % data)
|
|
||||||
params = {'name': data['name']}
|
|
||||||
params['gateway_ip'] = data['gateway_ip']
|
|
||||||
subnet = api.quantum.subnet_modify(request, data['subnet_id'],
|
|
||||||
name=data['name'],
|
|
||||||
gateway_ip=data['gateway_ip'])
|
|
||||||
msg = _('Subnet %s was successfully updated.') % data['cidr']
|
|
||||||
LOG.debug(msg)
|
|
||||||
messages.success(request, msg)
|
|
||||||
return subnet
|
|
||||||
except Exception:
|
|
||||||
msg = _('Failed to update subnet %s') % data['cidr']
|
|
||||||
LOG.info(msg)
|
|
||||||
redirect = reverse(self.failure_url, args=[data['network_id']])
|
|
||||||
exceptions.handle(request, msg, redirect=redirect)
|
|
@ -22,26 +22,21 @@ import logging
|
|||||||
from django.core.urlresolvers import reverse_lazy, reverse
|
from django.core.urlresolvers import reverse_lazy, reverse
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from horizon import forms
|
|
||||||
from horizon import exceptions
|
from horizon import exceptions
|
||||||
from horizon import tabs
|
from horizon import tabs
|
||||||
|
from horizon import workflows
|
||||||
|
|
||||||
from openstack_dashboard import api
|
from openstack_dashboard import api
|
||||||
from .forms import CreateSubnet, UpdateSubnet
|
|
||||||
from .tabs import SubnetDetailTabs
|
from .tabs import SubnetDetailTabs
|
||||||
|
from .workflows import CreateSubnet, UpdateSubnet
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class CreateView(forms.ModalFormView):
|
class CreateView(workflows.WorkflowView):
|
||||||
form_class = CreateSubnet
|
workflow_class = CreateSubnet
|
||||||
template_name = 'project/networks/subnets/create.html'
|
template_name = 'project/networks/subnets/create.html'
|
||||||
success_url = 'horizon:project:networks:detail'
|
|
||||||
|
|
||||||
def get_success_url(self):
|
|
||||||
return reverse(self.success_url,
|
|
||||||
args=(self.kwargs['network_id'],))
|
|
||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
if not hasattr(self, "_object"):
|
if not hasattr(self, "_object"):
|
||||||
@ -49,32 +44,22 @@ class CreateView(forms.ModalFormView):
|
|||||||
network_id = self.kwargs["network_id"]
|
network_id = self.kwargs["network_id"]
|
||||||
self._object = api.quantum.network_get(self.request,
|
self._object = api.quantum.network_get(self.request,
|
||||||
network_id)
|
network_id)
|
||||||
|
self._object.set_id_as_name_if_empty()
|
||||||
except:
|
except:
|
||||||
redirect = reverse('horizon:project:networks:index')
|
redirect = reverse('horizon:project:networks:index')
|
||||||
msg = _("Unable to retrieve network.")
|
msg = _("Unable to retrieve network.")
|
||||||
exceptions.handle(self.request, msg, redirect=redirect)
|
exceptions.handle(self.request, msg, redirect=redirect)
|
||||||
return self._object
|
return self._object
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super(CreateView, self).get_context_data(**kwargs)
|
|
||||||
context['network'] = self.get_object()
|
|
||||||
return context
|
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
network = self.get_object()
|
network = self.get_object()
|
||||||
return {"network_id": self.kwargs['network_id'],
|
return {"network_id": self.kwargs['network_id'],
|
||||||
"network_name": network.name}
|
"network_name": network.name}
|
||||||
|
|
||||||
|
|
||||||
class UpdateView(forms.ModalFormView):
|
class UpdateView(workflows.WorkflowView):
|
||||||
form_class = UpdateSubnet
|
workflow_class = UpdateSubnet
|
||||||
template_name = 'project/networks/subnets/update.html'
|
template_name = 'project/networks/subnets/update.html'
|
||||||
context_object_name = 'subnet'
|
|
||||||
success_url = reverse_lazy('horizon:project:networks:detail')
|
|
||||||
|
|
||||||
def get_success_url(self):
|
|
||||||
return reverse('horizon:project:networks:detail',
|
|
||||||
args=(self.kwargs['network_id'],))
|
|
||||||
|
|
||||||
def _get_object(self, *args, **kwargs):
|
def _get_object(self, *args, **kwargs):
|
||||||
if not hasattr(self, "_object"):
|
if not hasattr(self, "_object"):
|
||||||
@ -87,23 +72,30 @@ class UpdateView(forms.ModalFormView):
|
|||||||
exceptions.handle(self.request, msg, redirect=redirect)
|
exceptions.handle(self.request, msg, redirect=redirect)
|
||||||
return self._object
|
return self._object
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super(UpdateView, self).get_context_data(**kwargs)
|
|
||||||
subnet = self._get_object()
|
|
||||||
context['subnet_id'] = subnet.id
|
|
||||||
context['network_id'] = subnet.network_id
|
|
||||||
context['cidr'] = subnet.cidr
|
|
||||||
context['ip_version'] = subnet.ipver_str
|
|
||||||
return context
|
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
|
initial = super(UpdateView, self).get_initial()
|
||||||
|
|
||||||
subnet = self._get_object()
|
subnet = self._get_object()
|
||||||
return {'network_id': self.kwargs['network_id'],
|
|
||||||
'subnet_id': subnet['id'],
|
initial['network_id'] = self.kwargs['network_id']
|
||||||
'cidr': subnet['cidr'],
|
initial['subnet_id'] = subnet['id']
|
||||||
'ip_version': subnet['ip_version'],
|
initial['subnet_name'] = subnet['name']
|
||||||
'name': subnet['name'],
|
|
||||||
'gateway_ip': subnet['gateway_ip']}
|
for key in ('cidr', 'ip_version', 'enable_dhcp'):
|
||||||
|
initial[key] = subnet[key]
|
||||||
|
|
||||||
|
initial['gateway_ip'] = subnet['gateway_ip'] or ''
|
||||||
|
initial['no_gateway'] = (subnet['gateway_ip'] is None)
|
||||||
|
|
||||||
|
initial['dns_nameservers'] = '\n'.join(subnet['dns_nameservers'])
|
||||||
|
pools = ['%s,%s' % (p['start'], p['end'])
|
||||||
|
for p in subnet['allocation_pools']]
|
||||||
|
initial['allocation_pools'] = '\n'.join(pools)
|
||||||
|
routes = ['%s,%s' % (r['destination'], r['nexthop'])
|
||||||
|
for r in subnet['host_routes']]
|
||||||
|
initial['host_routes'] = '\n'.join(routes)
|
||||||
|
|
||||||
|
return initial
|
||||||
|
|
||||||
|
|
||||||
class DetailView(tabs.TabView):
|
class DetailView(tabs.TabView):
|
||||||
|
@ -0,0 +1,198 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2013 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
|
||||||
|
|
||||||
|
import netaddr
|
||||||
|
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
|
from horizon import exceptions
|
||||||
|
from horizon import forms
|
||||||
|
from horizon.utils import fields
|
||||||
|
from horizon import workflows
|
||||||
|
|
||||||
|
from openstack_dashboard import api
|
||||||
|
from openstack_dashboard.dashboards.project.networks import workflows \
|
||||||
|
as network_workflows
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class CreateSubnetInfoAction(network_workflows.CreateSubnetInfoAction):
|
||||||
|
with_subnet = forms.BooleanField(initial=True, required=False,
|
||||||
|
widget=forms.HiddenInput())
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
name = ("Subnet")
|
||||||
|
help_text = _('You can create a subnet associated with the '
|
||||||
|
'network. Advanced configuration are available '
|
||||||
|
'at "Subnet Detail" tab.')
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
cleaned_data = workflows.Action.clean(self)
|
||||||
|
self._check_subnet_data(cleaned_data)
|
||||||
|
return cleaned_data
|
||||||
|
|
||||||
|
|
||||||
|
class CreateSubnetInfo(network_workflows.CreateSubnetInfo):
|
||||||
|
action_class = CreateSubnetInfoAction
|
||||||
|
depends_on = ("network_id",)
|
||||||
|
|
||||||
|
|
||||||
|
class CreateSubnet(network_workflows.CreateNetwork):
|
||||||
|
slug = "create_subnet"
|
||||||
|
name = _("Create Subnet")
|
||||||
|
finalize_button_name = _("Create")
|
||||||
|
success_message = _('Created subnet "%s".')
|
||||||
|
failure_message = _('Unable to create subnet "%s".')
|
||||||
|
default_steps = (CreateSubnetInfo,
|
||||||
|
network_workflows.CreateSubnetDetail)
|
||||||
|
|
||||||
|
def format_status_message(self, message):
|
||||||
|
name = self.context.get('subnet_name') or self.context.get('subnet_id')
|
||||||
|
return message % name
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse("horizon:project:networks:detail",
|
||||||
|
args=(self.context.get('network_id'),))
|
||||||
|
|
||||||
|
def get_failure_url(self):
|
||||||
|
return reverse("horizon:project:networks:detail",
|
||||||
|
args=(self.context.get('network_id'),))
|
||||||
|
|
||||||
|
def handle(self, request, data):
|
||||||
|
subnet = self._create_subnet(request, data)
|
||||||
|
return True if subnet else False
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateSubnetInfoAction(CreateSubnetInfoAction):
|
||||||
|
cidr = fields.IPField(label=_("Network Address"),
|
||||||
|
required=False,
|
||||||
|
initial="",
|
||||||
|
widget=forms.TextInput(
|
||||||
|
attrs={'readonly': 'readonly'}),
|
||||||
|
help_text=_("Network address in CIDR format "
|
||||||
|
"(e.g. 192.168.0.0/24)"),
|
||||||
|
version=fields.IPv4 | fields.IPv6,
|
||||||
|
mask=True)
|
||||||
|
# NOTE(amotoki): When 'disabled' attribute is set for the ChoiceField
|
||||||
|
# and ValidationError is raised for POST request, the initial value of
|
||||||
|
# the ip_version ChoiceField is not set in the re-displayed form
|
||||||
|
# As a result, 'IPv4' is displayed even when IPv6 is used if
|
||||||
|
# ValidationError is detected. In addition 'required=True' check complains
|
||||||
|
# when re-POST since the value of the ChoiceField is not set.
|
||||||
|
# Thus now I use HiddenInput for the ip_version ChoiceField as a work
|
||||||
|
# around.
|
||||||
|
ip_version = forms.ChoiceField(choices=[(4, 'IPv4'), (6, 'IPv6')],
|
||||||
|
#widget=forms.Select(
|
||||||
|
# attrs={'disabled': 'disabled'}),
|
||||||
|
widget=forms.HiddenInput(),
|
||||||
|
label=_("IP Version"))
|
||||||
|
|
||||||
|
gateway_ip = fields.IPField(
|
||||||
|
label=_("Gateway IP (optional)"),
|
||||||
|
required=False,
|
||||||
|
initial="",
|
||||||
|
help_text=_("IP address of Gateway (e.g. 192.168.0.254). "
|
||||||
|
"You need to specify an explicit address "
|
||||||
|
"to set the gateway. "
|
||||||
|
"If you want to use no gateway, "
|
||||||
|
"check 'Disable Gateway' below."),
|
||||||
|
version=fields.IPv4 | fields.IPv6,
|
||||||
|
mask=False)
|
||||||
|
no_gateway = forms.BooleanField(label=_("Disable Gateway"),
|
||||||
|
initial=False, required=False)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
name = ("Subnet")
|
||||||
|
help_text = _('You can update a subnet associated with the '
|
||||||
|
'network. Advanced configuration are available '
|
||||||
|
'at "Subnet Detail" tab.')
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
cleaned_data = workflows.Action.clean(self)
|
||||||
|
self._check_subnet_data(cleaned_data, is_create=False)
|
||||||
|
return cleaned_data
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateSubnetInfo(CreateSubnetInfo):
|
||||||
|
action_class = UpdateSubnetInfoAction
|
||||||
|
depends_on = ("network_id", "subnet_id")
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateSubnetDetailAction(network_workflows.CreateSubnetDetailAction):
|
||||||
|
allocation_pools = forms.CharField(widget=forms.HiddenInput(),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
name = ("Subnet Detail")
|
||||||
|
help_text = _('You can specify additional attributes for the subnet.')
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateSubnetDetail(network_workflows.CreateSubnetDetail):
|
||||||
|
action_class = UpdateSubnetDetailAction
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateSubnet(network_workflows.CreateNetwork):
|
||||||
|
slug = "update_subnet"
|
||||||
|
name = _("Update Subnet")
|
||||||
|
finalize_button_name = _("Update")
|
||||||
|
success_message = _('Updated subnet "%s".')
|
||||||
|
failure_message = _('Unable to update subnet "%s".')
|
||||||
|
success_url = "horizon:project:networks:detail"
|
||||||
|
failure_url = "horizon:project:networks:detail"
|
||||||
|
default_steps = (UpdateSubnetInfo,
|
||||||
|
UpdateSubnetDetail)
|
||||||
|
|
||||||
|
def format_status_message(self, message):
|
||||||
|
name = self.context.get('subnet_name') or self.context.get('subnet_id')
|
||||||
|
return message % name
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse(self.success_url,
|
||||||
|
args=(self.context.get('network_id'),))
|
||||||
|
|
||||||
|
def _update_subnet(self, request, data):
|
||||||
|
network_id = self.context.get('network_id')
|
||||||
|
try:
|
||||||
|
subnet_id = self.context.get('subnet_id')
|
||||||
|
params = {}
|
||||||
|
params['name'] = data['subnet_name']
|
||||||
|
if data['no_gateway']:
|
||||||
|
params['gateway_ip'] = None
|
||||||
|
elif data['gateway_ip']:
|
||||||
|
params['gateway_ip'] = data['gateway_ip']
|
||||||
|
|
||||||
|
self._setup_subnet_parameters(params, data, is_create=False)
|
||||||
|
|
||||||
|
subnet = api.quantum.subnet_modify(request, subnet_id, **params)
|
||||||
|
msg = _('Subnet "%s" was successfully updated.') % data['cidr']
|
||||||
|
LOG.debug(msg)
|
||||||
|
return subnet
|
||||||
|
except Exception as e:
|
||||||
|
msg = (_('Failed to update subnet "%(sub)s": '
|
||||||
|
' %(reason)s') %
|
||||||
|
{"sub": data['cidr'], "reason": e})
|
||||||
|
redirect = reverse(self.failure_url, args=(network_id,))
|
||||||
|
exceptions.handle(request, msg, redirect=redirect)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def handle(self, request, data):
|
||||||
|
subnet = self._update_subnet(request, data)
|
||||||
|
return True if subnet else False
|
@ -16,5 +16,13 @@
|
|||||||
<dd>{{ network.admin_state|default:"Unknown" }}</dd>
|
<dd>{{ network.admin_state|default:"Unknown" }}</dd>
|
||||||
<dt>{% trans "Shared" %}</dt>
|
<dt>{% trans "Shared" %}</dt>
|
||||||
<dd>{{ network.shared|yesno|capfirst }}</dd>
|
<dd>{{ network.shared|yesno|capfirst }}</dd>
|
||||||
|
<dt>{% trans "External Network" %}</dt>
|
||||||
|
<dd>{{ network.router__external|yesno|capfirst }}</dd>
|
||||||
|
{% if network.provider__network_type %}
|
||||||
|
<dt>{% trans "Provider Network" %}</dt>
|
||||||
|
<dd>{% trans "Network Type" %}: {{ network.provider__network_type|default:"Unknown" }}</dd>
|
||||||
|
<dd>{% trans "Physical Network" %}: {{ network.provider__physical_network|default:"-" }}</dd>
|
||||||
|
<dd>{% trans "Segmentation ID" %}: {{ network.provider__segmentation_id|default:"-" }}</dd>
|
||||||
|
{% endif %}
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
|
@ -10,8 +10,9 @@
|
|||||||
<dd>{{ port.name|default:"None" }}</dd>
|
<dd>{{ port.name|default:"None" }}</dd>
|
||||||
<dt>{% trans "ID" %}</dt>
|
<dt>{% trans "ID" %}</dt>
|
||||||
<dd>{{ port.id|default:"None" }}</dd>
|
<dd>{{ port.id|default:"None" }}</dd>
|
||||||
|
{% url horizon:project:networks:detail port.network_id as network_url %}
|
||||||
<dt>{% trans "Network ID" %}</dt>
|
<dt>{% trans "Network ID" %}</dt>
|
||||||
<dd>{{ port.network_id|default:"None" }}</dd>
|
<dd><a href="{{ network_url }}">{{ port.network_id|default:"None" }}</a></dd>
|
||||||
<dt>{% trans "Project ID" %}</dt>
|
<dt>{% trans "Project ID" %}</dt>
|
||||||
<dd>{{ port.tenant_id|default:"-" }}</dd>
|
<dd>{{ port.tenant_id|default:"-" }}</dd>
|
||||||
<dt>{% trans "Fixed IP" %}</dt>
|
<dt>{% trans "Fixed IP" %}</dt>
|
||||||
@ -31,9 +32,10 @@
|
|||||||
<dd>{{ port.status|default:"None" }}</dd>
|
<dd>{{ port.status|default:"None" }}</dd>
|
||||||
<dt>{% trans "Admin State" %}</dt>
|
<dt>{% trans "Admin State" %}</dt>
|
||||||
<dd>{{ port.admin_state|default:"None" }}</dd>
|
<dd>{{ port.admin_state|default:"None" }}</dd>
|
||||||
<dt>{% trans "Device ID" %}</dt>
|
<dt>{% trans "Attached Device" %}</dt>
|
||||||
{% if port.device_id|length > 1 %}
|
{% if port.device_id|length > 1 or port.device_owner %}
|
||||||
<dd>{{ port.device_id }}</dd>
|
<dd><b>{% trans "Device Owner" %}</b>: {{ port.device_owner|default:"None" }}</dd>
|
||||||
|
<dd><b>{% trans "Device ID" %}</b>: {{ port.device_id|default:"-" }}</dd>
|
||||||
{% else %}
|
{% else %}
|
||||||
<dd>No attached device</dd>
|
<dd>No attached device</dd>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -1,20 +1,16 @@
|
|||||||
{% extends "horizon/common/_modal_form.html" %}
|
{% extends "horizon/common/_modal_form.html" %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block form_id %}update_subnet_form{% endblock %}
|
{% block form_id %}update_port_form{% endblock %}
|
||||||
{% block form_action %}{% url horizon:project:networks:editsubnet network_id subnet_id %}{% endblock %}
|
{% block form_action %}{% url horizon:project:networks:editport network_id port_id %}{% endblock %}
|
||||||
|
|
||||||
{% block modal-header %}{% trans "Edit Subnet" %}{% endblock %}
|
{% block modal-header %}{% trans "Edit Port" %}{% endblock %}
|
||||||
|
|
||||||
{% block modal-body %}
|
{% block modal-body %}
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<dl>
|
<dl>
|
||||||
<dt>{% trans "ID" %}</dt>
|
<dt>{% trans "ID" %}</dt>
|
||||||
<dd>{{ subnet_id }}</dd>
|
<dd>{{ port_id }}</dd>
|
||||||
<dt>{% trans "Network Address" %}</dt>
|
|
||||||
<dd>{{ cidr }}</dd>
|
|
||||||
<dt>{% trans "IP version" %}</dt>
|
|
||||||
<dd>{{ ip_version }}</dd>
|
|
||||||
</dl>
|
</dl>
|
||||||
<hr>
|
<hr>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
@ -23,7 +19,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<h3>{% trans "Description:" %}</h3>
|
<h3>{% trans "Description:" %}</h3>
|
||||||
<p>{% trans "You may update the editable properties of your subnet here." %}</p>
|
<p>{% trans "You may update the editable properties of your port here." %}</p>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
@ -0,0 +1,11 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% block title %}{% trans "Update Port" %}{% endblock %}
|
||||||
|
|
||||||
|
{% block page_header %}
|
||||||
|
{% include "horizon/common/_page_header.html" with title=_("Update Port") %}
|
||||||
|
{% endblock page_header %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
{% include 'project/networks/ports/_update.html' %}
|
||||||
|
{% endblock %}
|
@ -1,25 +0,0 @@
|
|||||||
{% extends "horizon/common/_modal_form.html" %}
|
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
{% block form_id %}create_subnet_form{% endblock %}
|
|
||||||
{% block form_action %}{% url horizon:project:networks:addsubnet network.id %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block modal-header %}{% trans "Create Subnet" %}{% 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 create a subnet for the network. Any network address can be specified unless the network address does not overlap other subnets in the network." %}</p>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block modal-footer %}
|
|
||||||
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Create Subnet" %}" />
|
|
||||||
<a href="{% url horizon:project:networks:index %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
|
|
||||||
{% endblock %}
|
|
@ -10,14 +10,13 @@
|
|||||||
<dd>{{ subnet.name|default:"None" }}</dd>
|
<dd>{{ subnet.name|default:"None" }}</dd>
|
||||||
<dt>{% trans "ID" %}</dt>
|
<dt>{% trans "ID" %}</dt>
|
||||||
<dd>{{ subnet.id|default:"None" }}</dd>
|
<dd>{{ subnet.id|default:"None" }}</dd>
|
||||||
|
{% url horizon:project:networks:detail subnet.network_id as network_url %}
|
||||||
<dt>{% trans "Network ID" %}</dt>
|
<dt>{% trans "Network ID" %}</dt>
|
||||||
<dd>{{ subnet.network_id|default:"None" }}</dd>
|
<dd><a href="{{ network_url }}">{{ subnet.network_id|default:"None" }}</a></dd>
|
||||||
<dt>{% trans "CIDR" %}</dt>
|
|
||||||
<dd>{{ subnet.cidr|default:"None" }}</dd>
|
|
||||||
<dt>{% trans "IP version" %}</dt>
|
<dt>{% trans "IP version" %}</dt>
|
||||||
<dd>{{ subnet.ipver_str|default:"-" }}</dd>
|
<dd>{{ subnet.ipver_str|default:"-" }}</dd>
|
||||||
<dt>{% trans "Gateway IP" %}</dt>
|
<dt>{% trans "CIDR" %}</dt>
|
||||||
<dd>{{ subnet.gateway_ip|default:"-" }}</dd>
|
<dd>{{ subnet.cidr|default:"None" }}</dd>
|
||||||
<dt>{% trans "IP allocation pool" %}</dt>
|
<dt>{% trans "IP allocation pool" %}</dt>
|
||||||
<dd>
|
<dd>
|
||||||
{% for pool in subnet.allocation_pools %}
|
{% for pool in subnet.allocation_pools %}
|
||||||
@ -25,5 +24,26 @@
|
|||||||
{% trans " - End" %} {{ pool.end }}<br>
|
{% trans " - End" %} {{ pool.end }}<br>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</dd>
|
</dd>
|
||||||
|
<dt>{% trans "DHCP Enable" %}<dt>
|
||||||
|
<dd>{{ subnet.enable_dhcp|yesno|capfirst }}</dd>
|
||||||
|
<dt>{% trans "Gateway IP" %}</dt>
|
||||||
|
<dd>{{ subnet.gateway_ip|default:"-" }}</dd>
|
||||||
|
<dt>{% trans "Additional routes" %}</dt>
|
||||||
|
<dd>
|
||||||
|
{% for route in subnet.host_routes %}
|
||||||
|
{% trans "Destination" %} {{ route.destination }}
|
||||||
|
{% trans " : Next hop" %} {{ route.nexthop }}<br>
|
||||||
|
{% empty %}
|
||||||
|
{% trans "None" %}
|
||||||
|
{% endfor %}
|
||||||
|
</dd>
|
||||||
|
<dt>{% trans "DNS name server" %}</dt>
|
||||||
|
<dd>
|
||||||
|
{% for dns in subnet.dns_nameservers %}
|
||||||
|
{{ dns }}
|
||||||
|
{% empty %}
|
||||||
|
{% trans "None" %}
|
||||||
|
{% endfor %}
|
||||||
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
|
@ -7,5 +7,5 @@
|
|||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
{% include "project/networks/subnets/_create.html" %}
|
{% include "horizon/common/_workflow.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -7,5 +7,5 @@
|
|||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
{% include 'project/networks/subnets/_update.html' %}
|
{% include "horizon/common/_workflow.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -20,6 +20,7 @@ from .views import IndexView, CreateView, DetailView, UpdateView
|
|||||||
from .subnets.views import CreateView as AddSubnetView
|
from .subnets.views import CreateView as AddSubnetView
|
||||||
from .subnets.views import UpdateView as EditSubnetView
|
from .subnets.views import UpdateView as EditSubnetView
|
||||||
from .subnets import urls as subnet_urls
|
from .subnets import urls as subnet_urls
|
||||||
|
from .ports.views import UpdateView as EditPortView
|
||||||
from .ports import urls as port_urls
|
from .ports import urls as port_urls
|
||||||
|
|
||||||
|
|
||||||
@ -35,5 +36,7 @@ urlpatterns = patterns('',
|
|||||||
name='addsubnet'),
|
name='addsubnet'),
|
||||||
url(r'^(?P<network_id>[^/]+)/subnets/(?P<subnet_id>[^/]+)/update$',
|
url(r'^(?P<network_id>[^/]+)/subnets/(?P<subnet_id>[^/]+)/update$',
|
||||||
EditSubnetView.as_view(), name='editsubnet'),
|
EditSubnetView.as_view(), name='editsubnet'),
|
||||||
|
url(r'^(?P<network_id>[^/]+)/ports/(?P<port_id>[^/]+)/update$',
|
||||||
|
EditPortView.as_view(), name='editport'),
|
||||||
url(r'^subnets/', include(subnet_urls, namespace='subnets')),
|
url(r'^subnets/', include(subnet_urls, namespace='subnets')),
|
||||||
url(r'^ports/', include(port_urls, namespace='ports')))
|
url(r'^ports/', include(port_urls, namespace='ports')))
|
||||||
|
@ -91,7 +91,8 @@ class UpdateView(forms.ModalFormView):
|
|||||||
network = self._get_object()
|
network = self._get_object()
|
||||||
return {'network_id': network['id'],
|
return {'network_id': network['id'],
|
||||||
'tenant_id': network['tenant_id'],
|
'tenant_id': network['tenant_id'],
|
||||||
'name': network['name']}
|
'name': network['name'],
|
||||||
|
'admin_state': network['admin_state_up']}
|
||||||
|
|
||||||
|
|
||||||
class DetailView(tables.MultiTableView):
|
class DetailView(tables.MultiTableView):
|
||||||
|
@ -23,6 +23,7 @@ from django.utils.translation import ugettext as _
|
|||||||
|
|
||||||
from horizon import exceptions
|
from horizon import exceptions
|
||||||
from horizon import forms
|
from horizon import forms
|
||||||
|
from horizon import messages
|
||||||
from horizon import workflows
|
from horizon import workflows
|
||||||
from horizon.utils import fields
|
from horizon.utils import fields
|
||||||
|
|
||||||
@ -34,8 +35,12 @@ LOG = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class CreateNetworkInfoAction(workflows.Action):
|
class CreateNetworkInfoAction(workflows.Action):
|
||||||
net_name = forms.CharField(max_length=255,
|
net_name = forms.CharField(max_length=255,
|
||||||
label=_("Network Name (optional)"),
|
label=_("Network Name"),
|
||||||
|
help_text=_("Network Name. This field is "
|
||||||
|
"optional."),
|
||||||
required=False)
|
required=False)
|
||||||
|
admin_state = forms.BooleanField(label=_("Admin State"),
|
||||||
|
initial=True, required=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
name = ("Network")
|
name = ("Network")
|
||||||
@ -46,14 +51,16 @@ class CreateNetworkInfoAction(workflows.Action):
|
|||||||
|
|
||||||
class CreateNetworkInfo(workflows.Step):
|
class CreateNetworkInfo(workflows.Step):
|
||||||
action_class = CreateNetworkInfoAction
|
action_class = CreateNetworkInfoAction
|
||||||
contributes = ("net_name",)
|
contributes = ("net_name", "admin_state")
|
||||||
|
|
||||||
|
|
||||||
class CreateSubnetInfoAction(workflows.Action):
|
class CreateSubnetInfoAction(workflows.Action):
|
||||||
with_subnet = forms.BooleanField(label=_("Create Subnet"),
|
with_subnet = forms.BooleanField(label=_("Create Subnet"),
|
||||||
initial=True, required=False)
|
initial=True, required=False)
|
||||||
subnet_name = forms.CharField(max_length=255,
|
subnet_name = forms.CharField(max_length=255,
|
||||||
label=_("Subnet Name (optional)"),
|
label=_("Subnet Name"),
|
||||||
|
help_text=_("Subnet Name. This field is "
|
||||||
|
"optional."),
|
||||||
required=False)
|
required=False)
|
||||||
cidr = fields.IPField(label=_("Network Address"),
|
cidr = fields.IPField(label=_("Network Address"),
|
||||||
required=False,
|
required=False,
|
||||||
@ -64,13 +71,21 @@ class CreateSubnetInfoAction(workflows.Action):
|
|||||||
mask=True)
|
mask=True)
|
||||||
ip_version = forms.ChoiceField(choices=[(4, 'IPv4'), (6, 'IPv6')],
|
ip_version = forms.ChoiceField(choices=[(4, 'IPv4'), (6, 'IPv6')],
|
||||||
label=_("IP Version"))
|
label=_("IP Version"))
|
||||||
gateway_ip = fields.IPField(label=_("Gateway IP (optional)"),
|
gateway_ip = fields.IPField(
|
||||||
required=False,
|
label=_("Gateway IP (optional)"),
|
||||||
initial="",
|
required=False,
|
||||||
help_text=_("IP address of Gateway "
|
initial="",
|
||||||
"(e.g. 192.168.0.1)"),
|
help_text=_("IP address of Gateway (e.g. 192.168.0.254) "
|
||||||
version=fields.IPv4 | fields.IPv6,
|
"The default value is the first IP of the "
|
||||||
mask=False)
|
"network address (e.g. 192.168.0.1 for "
|
||||||
|
"192.168.0.0/24). "
|
||||||
|
"If you use the default, leave blank. "
|
||||||
|
"If you want to use no gateway, "
|
||||||
|
"check 'Disable Gateway' below."),
|
||||||
|
version=fields.IPv4 | fields.IPv6,
|
||||||
|
mask=False)
|
||||||
|
no_gateway = forms.BooleanField(label=_("Disable Gateway"),
|
||||||
|
initial=False, required=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
name = ("Subnet")
|
name = ("Subnet")
|
||||||
@ -79,13 +94,12 @@ class CreateSubnetInfoAction(workflows.Action):
|
|||||||
'specified. If you wish to create a network WITHOUT a '
|
'specified. If you wish to create a network WITHOUT a '
|
||||||
'subnet, uncheck the "Create Subnet" checkbox.')
|
'subnet, uncheck the "Create Subnet" checkbox.')
|
||||||
|
|
||||||
def clean(self):
|
def _check_subnet_data(self, cleaned_data, is_create=True):
|
||||||
cleaned_data = super(CreateSubnetInfoAction, self).clean()
|
|
||||||
with_subnet = cleaned_data.get('with_subnet')
|
|
||||||
cidr = cleaned_data.get('cidr')
|
cidr = cleaned_data.get('cidr')
|
||||||
ip_version = int(cleaned_data.get('ip_version'))
|
ip_version = int(cleaned_data.get('ip_version'))
|
||||||
gateway_ip = cleaned_data.get('gateway_ip')
|
gateway_ip = cleaned_data.get('gateway_ip')
|
||||||
if with_subnet and not cidr:
|
no_gateway = cleaned_data.get('no_gateway')
|
||||||
|
if not cidr:
|
||||||
msg = _('Specify "Network Address" or '
|
msg = _('Specify "Network Address" or '
|
||||||
'clear "Create Subnet" checkbox.')
|
'clear "Create Subnet" checkbox.')
|
||||||
raise forms.ValidationError(msg)
|
raise forms.ValidationError(msg)
|
||||||
@ -93,17 +107,126 @@ class CreateSubnetInfoAction(workflows.Action):
|
|||||||
if netaddr.IPNetwork(cidr).version is not ip_version:
|
if netaddr.IPNetwork(cidr).version is not ip_version:
|
||||||
msg = _('Network Address and IP version are inconsistent.')
|
msg = _('Network Address and IP version are inconsistent.')
|
||||||
raise forms.ValidationError(msg)
|
raise forms.ValidationError(msg)
|
||||||
if gateway_ip:
|
if not no_gateway and gateway_ip:
|
||||||
if netaddr.IPAddress(gateway_ip).version is not ip_version:
|
if netaddr.IPAddress(gateway_ip).version is not ip_version:
|
||||||
msg = _('Gateway IP and IP version are inconsistent.')
|
msg = _('Gateway IP and IP version are inconsistent.')
|
||||||
raise forms.ValidationError(msg)
|
raise forms.ValidationError(msg)
|
||||||
|
if not is_create and not no_gateway and not gateway_ip:
|
||||||
|
msg = _('Specify IP address of gateway or '
|
||||||
|
'check "Disable Gateway".')
|
||||||
|
raise forms.ValidationError(msg)
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
cleaned_data = super(CreateSubnetInfoAction, self).clean()
|
||||||
|
with_subnet = cleaned_data.get('with_subnet')
|
||||||
|
if not with_subnet:
|
||||||
|
return cleaned_data
|
||||||
|
self._check_subnet_data(cleaned_data)
|
||||||
return cleaned_data
|
return cleaned_data
|
||||||
|
|
||||||
|
|
||||||
class CreateSubnetInfo(workflows.Step):
|
class CreateSubnetInfo(workflows.Step):
|
||||||
action_class = CreateSubnetInfoAction
|
action_class = CreateSubnetInfoAction
|
||||||
contributes = ("with_subnet", "subnet_name", "cidr",
|
contributes = ("with_subnet", "subnet_name", "cidr",
|
||||||
"ip_version", "gateway_ip")
|
"ip_version", "gateway_ip", "no_gateway")
|
||||||
|
|
||||||
|
|
||||||
|
class CreateSubnetDetailAction(workflows.Action):
|
||||||
|
enable_dhcp = forms.BooleanField(label=_("Enable DHCP"),
|
||||||
|
initial=True, required=False)
|
||||||
|
allocation_pools = forms.CharField(
|
||||||
|
widget=forms.Textarea(),
|
||||||
|
label=_("Allocation Pools"),
|
||||||
|
help_text=_("IP address allocation pools. Each entry is "
|
||||||
|
"<start_ip_address>,<end_ip_address> "
|
||||||
|
"(e.g., 192.168.1.100,192.168.1.120) "
|
||||||
|
"and one entry per line."),
|
||||||
|
required=False)
|
||||||
|
dns_nameservers = forms.CharField(
|
||||||
|
widget=forms.widgets.Textarea(),
|
||||||
|
label=_("DNS Name Servers"),
|
||||||
|
help_text=_("IP address list of DNS name servers for this subnet. "
|
||||||
|
"One entry per line."),
|
||||||
|
required=False)
|
||||||
|
host_routes = forms.CharField(
|
||||||
|
widget=forms.widgets.Textarea(),
|
||||||
|
label=_("Host Routes"),
|
||||||
|
help_text=_("Additional routes announced to the hosts. "
|
||||||
|
"Each entry is <destination_cidr>,<nexthop> "
|
||||||
|
"(e.g., 192.168.200.0/24,10.56.1.254)"
|
||||||
|
"and one entry per line."),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
name = ("Subnet Detail")
|
||||||
|
help_text = _('You can specify additional attributes for the subnet.')
|
||||||
|
|
||||||
|
def _convert_ip_address(self, ip, field_name):
|
||||||
|
try:
|
||||||
|
return netaddr.IPAddress(ip)
|
||||||
|
except (netaddr.AddrFormatError, ValueError):
|
||||||
|
msg = _('%(field_name)s: Invalid IP address '
|
||||||
|
'(value=%(ip)s)') % locals()
|
||||||
|
raise forms.ValidationError(msg)
|
||||||
|
|
||||||
|
def _convert_ip_network(self, network, field_name):
|
||||||
|
try:
|
||||||
|
return netaddr.IPNetwork(network)
|
||||||
|
except (netaddr.AddrFormatError, ValueError):
|
||||||
|
msg = _('%(field_name)s: Invalid IP address '
|
||||||
|
'(value=%(network)s)') % locals()
|
||||||
|
raise forms.ValidationError(msg)
|
||||||
|
|
||||||
|
def _check_allocation_pools(self, allocation_pools):
|
||||||
|
for p in allocation_pools.split('\n'):
|
||||||
|
p = p.strip()
|
||||||
|
if not p:
|
||||||
|
continue
|
||||||
|
pool = p.split(',')
|
||||||
|
if len(pool) != 2:
|
||||||
|
msg = _('Start and end addresses must be specified '
|
||||||
|
'(value=%s)') % p
|
||||||
|
raise forms.ValidationError(msg)
|
||||||
|
start, end = [self._convert_ip_address(ip, "allocation_pools")
|
||||||
|
for ip in pool]
|
||||||
|
if start > end:
|
||||||
|
msg = _('Start address is larger than end address '
|
||||||
|
'(value=%s)') % p
|
||||||
|
raise forms.ValidationError(msg)
|
||||||
|
|
||||||
|
def _check_dns_nameservers(self, dns_nameservers):
|
||||||
|
for ns in dns_nameservers.split('\n'):
|
||||||
|
ns = ns.strip()
|
||||||
|
if not ns:
|
||||||
|
continue
|
||||||
|
self._convert_ip_address(ns, "dns_nameservers")
|
||||||
|
|
||||||
|
def _check_host_routes(self, host_routes):
|
||||||
|
for r in host_routes.split('\n'):
|
||||||
|
r = r.strip()
|
||||||
|
if not r:
|
||||||
|
continue
|
||||||
|
route = r.split(',')
|
||||||
|
if len(route) != 2:
|
||||||
|
msg = _('Host Routes format error: '
|
||||||
|
'Destination CIDR and nexthop must be specified '
|
||||||
|
'(value=%s)') % r
|
||||||
|
raise forms.ValidationError(msg)
|
||||||
|
dest = self._convert_ip_network(route[0], "host_routes")
|
||||||
|
nexthop = self._convert_ip_address(route[1], "host_routes")
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
cleaned_data = super(CreateSubnetDetailAction, self).clean()
|
||||||
|
self._check_allocation_pools(cleaned_data.get('allocation_pools'))
|
||||||
|
self._check_host_routes(cleaned_data.get('host_routes'))
|
||||||
|
self._check_dns_nameservers(cleaned_data.get('dns_nameservers'))
|
||||||
|
return cleaned_data
|
||||||
|
|
||||||
|
|
||||||
|
class CreateSubnetDetail(workflows.Step):
|
||||||
|
action_class = CreateSubnetDetailAction
|
||||||
|
contributes = ("enable_dhcp", "allocation_pools",
|
||||||
|
"dns_nameservers", "host_routes")
|
||||||
|
|
||||||
|
|
||||||
class CreateNetwork(workflows.Workflow):
|
class CreateNetwork(workflows.Workflow):
|
||||||
@ -112,51 +235,130 @@ class CreateNetwork(workflows.Workflow):
|
|||||||
finalize_button_name = _("Create")
|
finalize_button_name = _("Create")
|
||||||
success_message = _('Created network "%s".')
|
success_message = _('Created network "%s".')
|
||||||
failure_message = _('Unable to create network "%s".')
|
failure_message = _('Unable to create network "%s".')
|
||||||
success_url = "horizon:project:networks:index"
|
|
||||||
default_steps = (CreateNetworkInfo,
|
default_steps = (CreateNetworkInfo,
|
||||||
CreateSubnetInfo)
|
CreateSubnetInfo,
|
||||||
|
CreateSubnetDetail)
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse("horizon:project:networks:index")
|
||||||
|
|
||||||
|
def get_failure_url(self):
|
||||||
|
return reverse("horizon:project:networks:index")
|
||||||
|
|
||||||
def format_status_message(self, message):
|
def format_status_message(self, message):
|
||||||
name = self.context.get('net_name') or self.context.get('net_id', '')
|
name = self.context.get('net_name') or self.context.get('net_id', '')
|
||||||
return message % name
|
return message % name
|
||||||
|
|
||||||
def handle(self, request, data):
|
def _create_network(self, request, data):
|
||||||
# create the network
|
|
||||||
try:
|
try:
|
||||||
network = api.quantum.network_create(request,
|
params = {'name': data['net_name'],
|
||||||
name=data['net_name'])
|
'admin_state_up': data['admin_state']}
|
||||||
|
network = api.quantum.network_create(request, **params)
|
||||||
network.set_id_as_name_if_empty()
|
network.set_id_as_name_if_empty()
|
||||||
self.context['net_id'] = network.id
|
self.context['net_id'] = network.id
|
||||||
msg = _('Network "%s" was successfully created.') % network.name
|
msg = _('Network "%s" was successfully created.') % network.name
|
||||||
LOG.debug(msg)
|
LOG.debug(msg)
|
||||||
except:
|
return network
|
||||||
msg = _('Failed to create network "%s".') % data['net_name']
|
except Exception as e:
|
||||||
|
msg = (_('Failed to create network "%(network)s": %(reason)s') %
|
||||||
|
{"network": data['net_name'], "reason": e})
|
||||||
LOG.info(msg)
|
LOG.info(msg)
|
||||||
redirect = reverse('horizon:project:networks:index')
|
redirect = self.get_failure_url()
|
||||||
exceptions.handle(request, msg, redirect=redirect)
|
exceptions.handle(request, msg, redirect=redirect)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# If we do not need to create a subnet, return here.
|
def _setup_subnet_parameters(self, params, data, is_create=True):
|
||||||
if not data['with_subnet']:
|
"""Setup subnet parameters
|
||||||
return True
|
|
||||||
|
|
||||||
# Create the subnet.
|
This methods setups subnet parameters which are available
|
||||||
|
in both create and update.
|
||||||
|
"""
|
||||||
|
is_update = not is_create
|
||||||
|
params['enable_dhcp'] = data['enable_dhcp']
|
||||||
|
if is_create and data['allocation_pools']:
|
||||||
|
pools = [dict(zip(['start', 'end'], pool.strip().split(',')))
|
||||||
|
for pool in data['allocation_pools'].split('\n')
|
||||||
|
if pool.strip()]
|
||||||
|
params['allocation_pools'] = pools
|
||||||
|
if data['host_routes'] or is_update:
|
||||||
|
routes = [dict(zip(['destination', 'nexthop'],
|
||||||
|
route.strip().split(',')))
|
||||||
|
for route in data['host_routes'].split('\n')
|
||||||
|
if route.strip()]
|
||||||
|
params['host_routes'] = routes
|
||||||
|
if data['dns_nameservers'] or is_update:
|
||||||
|
nameservers = [ns.strip()
|
||||||
|
for ns in data['dns_nameservers'].split('\n')
|
||||||
|
if ns.strip()]
|
||||||
|
params['dns_nameservers'] = nameservers
|
||||||
|
|
||||||
|
def _create_subnet(self, request, data, network=None, tenant_id=None,
|
||||||
|
no_redirect=False):
|
||||||
|
if network:
|
||||||
|
network_id = network.id
|
||||||
|
network_name = network.name
|
||||||
|
else:
|
||||||
|
network_id = self.context.get('network_id')
|
||||||
|
network_name = self.context.get('network_name')
|
||||||
try:
|
try:
|
||||||
params = {'network_id': network.id,
|
params = {'network_id': network_id,
|
||||||
'name': data['subnet_name'],
|
'name': data['subnet_name'],
|
||||||
'cidr': data['cidr'],
|
'cidr': data['cidr'],
|
||||||
'ip_version': int(data['ip_version'])}
|
'ip_version': int(data['ip_version'])}
|
||||||
if data['gateway_ip']:
|
if tenant_id:
|
||||||
|
params['tenant_id'] = tenant_id
|
||||||
|
if data['no_gateway']:
|
||||||
|
params['gateway_ip'] = None
|
||||||
|
elif data['gateway_ip']:
|
||||||
params['gateway_ip'] = data['gateway_ip']
|
params['gateway_ip'] = data['gateway_ip']
|
||||||
api.quantum.subnet_create(request, **params)
|
|
||||||
|
self._setup_subnet_parameters(params, data)
|
||||||
|
|
||||||
|
subnet = api.quantum.subnet_create(request, **params)
|
||||||
|
self.context['subnet_id'] = subnet.id
|
||||||
msg = _('Subnet "%s" was successfully created.') % data['cidr']
|
msg = _('Subnet "%s" was successfully created.') % data['cidr']
|
||||||
LOG.debug(msg)
|
LOG.debug(msg)
|
||||||
except Exception:
|
return subnet
|
||||||
msg = _('Failed to create subnet "%(sub)s" for network "%(net)s".')
|
except Exception as e:
|
||||||
redirect = reverse('horizon:project:networks:index')
|
msg = _('Failed to create subnet "%(sub)s" for network "%(net)s": '
|
||||||
|
' %(reason)s')
|
||||||
|
if no_redirect:
|
||||||
|
redirect = None
|
||||||
|
else:
|
||||||
|
redirect = self.get_failure_url()
|
||||||
exceptions.handle(request,
|
exceptions.handle(request,
|
||||||
msg % {"sub": data['cidr'], "net": network.id},
|
msg % {"sub": data['cidr'], "net": network_name,
|
||||||
|
"reason": e},
|
||||||
redirect=redirect)
|
redirect=redirect)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
def _delete_network(self, request, network):
|
||||||
|
"""Delete the created network when subnet creation failed"""
|
||||||
|
try:
|
||||||
|
api.quantum.network_delete(request, network.id)
|
||||||
|
msg = _('Delete the created network "%s" '
|
||||||
|
'due to subnet creation failure.') % network.name
|
||||||
|
LOG.debug(msg)
|
||||||
|
redirect = self.get_failure_url()
|
||||||
|
messages.info(request, msg)
|
||||||
|
raise exceptions.Http302(redirect)
|
||||||
|
#return exceptions.RecoverableError
|
||||||
|
except:
|
||||||
|
msg = _('Failed to delete network "%s"') % network.name
|
||||||
|
LOG.info(msg)
|
||||||
|
redirect = self.get_failure_url()
|
||||||
|
exceptions.handle(request, msg, redirect=redirect)
|
||||||
|
|
||||||
|
def handle(self, request, data):
|
||||||
|
network = self._create_network(request, data)
|
||||||
|
if not network:
|
||||||
|
return False
|
||||||
|
# If we do not need to create a subnet, return here.
|
||||||
|
if not data['with_subnet']:
|
||||||
|
return True
|
||||||
|
subnet = self._create_subnet(request, data, network, no_redirect=True)
|
||||||
|
if subnet:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
self._delete_network(request, network)
|
||||||
|
return False
|
||||||
|
@ -37,9 +37,12 @@ def data(TEST):
|
|||||||
'status': 'ACTIVE',
|
'status': 'ACTIVE',
|
||||||
'subnets': ['e8abc972-eb0c-41f1-9edd-4bc6e3bcd8c9'],
|
'subnets': ['e8abc972-eb0c-41f1-9edd-4bc6e3bcd8c9'],
|
||||||
'tenant_id': '1',
|
'tenant_id': '1',
|
||||||
|
'router:external': False,
|
||||||
'shared': False}
|
'shared': False}
|
||||||
subnet_dict = {'allocation_pools': [{'end': '10.0.0.254',
|
subnet_dict = {'allocation_pools': [{'end': '10.0.0.254',
|
||||||
'start': '10.0.0.2'}],
|
'start': '10.0.0.2'}],
|
||||||
|
'dns_nameservers': [],
|
||||||
|
'host_routes': [],
|
||||||
'cidr': '10.0.0.0/24',
|
'cidr': '10.0.0.0/24',
|
||||||
'enable_dhcp': True,
|
'enable_dhcp': True,
|
||||||
'gateway_ip': '10.0.0.1',
|
'gateway_ip': '10.0.0.1',
|
||||||
@ -50,6 +53,7 @@ def data(TEST):
|
|||||||
'tenant_id': network_dict['tenant_id']}
|
'tenant_id': network_dict['tenant_id']}
|
||||||
port_dict = {'admin_state_up': True,
|
port_dict = {'admin_state_up': True,
|
||||||
'device_id': 'af75c8e5-a1cc-4567-8d04-44fcd6922890',
|
'device_id': 'af75c8e5-a1cc-4567-8d04-44fcd6922890',
|
||||||
|
'device_owner': 'network:dhcp',
|
||||||
'fixed_ips': [{'ip_address': '10.0.0.3',
|
'fixed_ips': [{'ip_address': '10.0.0.3',
|
||||||
'subnet_id': subnet_dict['id']}],
|
'subnet_id': subnet_dict['id']}],
|
||||||
'id': '3ec7f3db-cb2f-4a34-ab6b-69a64d3f008c',
|
'id': '3ec7f3db-cb2f-4a34-ab6b-69a64d3f008c',
|
||||||
@ -77,9 +81,15 @@ def data(TEST):
|
|||||||
'status': 'ACTIVE',
|
'status': 'ACTIVE',
|
||||||
'subnets': ['3f7c5d79-ee55-47b0-9213-8e669fb03009'],
|
'subnets': ['3f7c5d79-ee55-47b0-9213-8e669fb03009'],
|
||||||
'tenant_id': '2',
|
'tenant_id': '2',
|
||||||
|
'router:external': True,
|
||||||
'shared': True}
|
'shared': True}
|
||||||
subnet_dict = {'allocation_pools': [{'end': '172.16.88.254',
|
subnet_dict = {'allocation_pools': [{'end': '172.16.88.254',
|
||||||
'start': '172.16.88.2'}],
|
'start': '172.16.88.2'}],
|
||||||
|
'dns_nameservers': ['10.56.1.20', '10.56.1.21'],
|
||||||
|
'host_routes': [{'destination': '192.168.20.0/24',
|
||||||
|
'nexthop': '172.16.88.253'},
|
||||||
|
{'destination': '192.168.21.0/24',
|
||||||
|
'nexthop': '172.16.88.252'}],
|
||||||
'cidr': '172.16.88.0/24',
|
'cidr': '172.16.88.0/24',
|
||||||
'enable_dhcp': True,
|
'enable_dhcp': True,
|
||||||
'gateway_ip': '172.16.88.1',
|
'gateway_ip': '172.16.88.1',
|
||||||
@ -90,6 +100,7 @@ def data(TEST):
|
|||||||
'tenant_id': network_dict['tenant_id']}
|
'tenant_id': network_dict['tenant_id']}
|
||||||
port_dict = {'admin_state_up': True,
|
port_dict = {'admin_state_up': True,
|
||||||
'device_id': '40e536b1-b9fd-4eb7-82d6-84db5d65a2ac',
|
'device_id': '40e536b1-b9fd-4eb7-82d6-84db5d65a2ac',
|
||||||
|
'device_owner': 'compute:nova',
|
||||||
'fixed_ips': [{'ip_address': '172.16.88.3',
|
'fixed_ips': [{'ip_address': '172.16.88.3',
|
||||||
'subnet_id': subnet_dict['id']}],
|
'subnet_id': subnet_dict['id']}],
|
||||||
'id': '7e6ce62c-7ea2-44f8-b6b4-769af90a8406',
|
'id': '7e6ce62c-7ea2-44f8-b6b4-769af90a8406',
|
||||||
|
Loading…
Reference in New Issue
Block a user