Merge "Refactor the current UpdatePort form to workflow version"

This commit is contained in:
Jenkins 2017-08-16 04:05:10 +00:00 committed by Gerrit Code Review
commit 609bf8fbc9
13 changed files with 264 additions and 239 deletions

View File

@ -53,6 +53,15 @@ ROUTER_INTERFACE_OWNERS = (
'network:ha_router_replicated_interface'
)
VNIC_TYPES = [
('normal', _('Normal')),
('direct', _('Direct')),
('direct-physical', _('Direct Physical')),
('macvtap', _('MacVTap')),
('baremetal', _('Bare Metal')),
('virtio-forwarder', _('Virtio Forwarder')),
]
class NeutronAPIDictWrapper(base.APIDictWrapper):

View File

@ -28,12 +28,6 @@ from openstack_dashboard.dashboards.project.networks.ports \
LOG = logging.getLogger(__name__)
VNIC_TYPES = [('normal', _('Normal')),
('direct', _('Direct')),
('direct-physical', _('Direct Physical')),
('macvtap', _('MacVTap')),
('baremetal', _('Bare Metal')),
('virtio-forwarder', _('Virtio Forwarder'))]
class CreatePort(project_forms.CreatePort):
@ -56,10 +50,10 @@ class CreatePort(project_forms.CreatePort):
'supported_vnic_types', ['*'])
if supported_vnic_types:
if supported_vnic_types == ['*']:
vnic_type_choices = VNIC_TYPES
vnic_type_choices = api.neutron.VNIC_TYPES
else:
vnic_type_choices = [
vnic_type for vnic_type in VNIC_TYPES
vnic_type for vnic_type in api.neutron.VNIC_TYPES
if vnic_type[0] in supported_vnic_types
]
@ -137,63 +131,3 @@ class CreatePort(project_forms.CreatePort):
msg = _('Failed to create a port for network %s') % network_id
redirect = reverse(self.failure_url, args=(network_id,))
exceptions.handle(request, msg, redirect=redirect)
class UpdatePort(project_forms.UpdatePort):
# tenant_id = forms.CharField(widget=forms.HiddenInput())
device_id = forms.CharField(max_length=100, label=_("Device ID"),
help_text=_("Device ID attached to the port"),
required=False)
device_owner = forms.CharField(max_length=100, label=_("Device Owner"),
help_text=_("Device owner attached to the "
"port"),
required=False)
binding__host_id = forms.CharField(
label=_("Binding: Host"),
help_text=_("The ID of the host where the port is allocated. In some "
"cases, different implementations can run on different "
"hosts."),
required=False)
mac_address = forms.MACAddressField(
label=_("MAC Address"),
required=False,
help_text=_("Specify a new MAC address for the port"))
failure_url = 'horizon:admin:networks:detail'
def handle(self, request, data):
port_id = self.initial['port_id']
try:
LOG.debug('params = %s', data)
extension_kwargs = {}
if 'binding__vnic_type' in data:
extension_kwargs['binding__vnic_type'] = \
data['binding__vnic_type']
if 'mac_state' in data:
extension_kwargs['mac_learning_enabled'] = data['mac_state']
if 'port_security_enabled' in data:
extension_kwargs['port_security_enabled'] = \
data['port_security_enabled']
port = api.neutron.port_update(request,
port_id,
name=data['name'],
admin_state_up=data['admin_state'],
device_id=data['device_id'],
device_owner=data['device_owner'],
binding__host_id=data
['binding__host_id'],
mac_address=data['mac_address'],
**extension_kwargs)
msg = _('Port %s was successfully updated.') % port_id
messages.success(request, msg)
return port
except Exception as e:
LOG.info('Failed to update port %(id)s: %(exc)s',
{'id': port_id, 'exc': e})
msg = _('Failed to update port %s') % port_id
redirect = reverse(self.failure_url,
args=[self.initial['network_id']])
exceptions.handle(request, msg, redirect=redirect)

View File

@ -18,6 +18,8 @@ from django import http
from mox3.mox import IsA
from horizon.workflows import views
from openstack_dashboard import api
from openstack_dashboard.test import helpers as test
@ -375,7 +377,7 @@ class NetworkPortTests(test.BaseAdminViewTests):
args=[port.network_id, port.id])
res = self.client.get(url)
self.assertTemplateUsed(res, 'admin/networks/ports/update.html')
self.assertTemplateUsed(res, views.WorkflowView.template_name)
@test.create_stubs({api.neutron: ('port_get',
'is_extension_supported',
@ -402,13 +404,13 @@ class NetworkPortTests(test.BaseAdminViewTests):
.AndReturn(port)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'binding')\
.AndReturn(binding)
.MultipleTimes().AndReturn(binding)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'mac-learning')\
.AndReturn(mac_learning)
.MultipleTimes().AndReturn(mac_learning)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'port-security')\
.AndReturn(port_security)
.MultipleTimes().AndReturn(port_security)
extension_kwargs = {}
if binding:
extension_kwargs['binding__vnic_type'] = port.binding__vnic_type
@ -475,13 +477,13 @@ class NetworkPortTests(test.BaseAdminViewTests):
.AndReturn(port)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'binding')\
.AndReturn(binding)
.MultipleTimes().AndReturn(binding)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'mac-learning')\
.AndReturn(mac_learning)
.MultipleTimes().AndReturn(mac_learning)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'port-security')\
.AndReturn(port_security)
.MultipleTimes().AndReturn(port_security)
extension_kwargs = {}
if binding:
extension_kwargs['binding__vnic_type'] = port.binding__vnic_type

View File

@ -27,6 +27,8 @@ from openstack_dashboard.dashboards.admin.networks.ports \
import tables as ports_tables
from openstack_dashboard.dashboards.admin.networks.ports \
import tabs as ports_tabs
from openstack_dashboard.dashboards.admin.networks.ports \
import workflows as admin_workflows
from openstack_dashboard.dashboards.project.networks.ports \
import views as project_views
@ -99,11 +101,8 @@ class DetailView(project_views.DetailView):
class UpdateView(project_views.UpdateView):
form_class = ports_forms.UpdatePort
template_name = 'admin/networks/ports/update.html'
context_object_name = 'port'
submit_url = "horizon:admin:networks:editport"
success_url = 'horizon:admin:networks:detail'
workflow_class = admin_workflows.UpdatePort
failure_url = 'horizon:admin:networks:detail'
def get_initial(self):
initial = super(UpdateView, self).get_initial()
@ -111,4 +110,5 @@ class UpdateView(project_views.UpdateView):
initial['binding__host_id'] = port['binding__host_id']
initial['device_id'] = port['device_id']
initial['device_owner'] = port['device_owner']
return initial

View File

@ -0,0 +1,75 @@
# Copyright 2016 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 openstack_dashboard.dashboards.project.networks.ports \
import workflows as project_workflow
LOG = logging.getLogger(__name__)
class UpdatePortInfoAction(project_workflow.UpdatePortInfoAction):
device_id = forms.CharField(
max_length=100, label=_("Device ID"),
help_text=_("Device ID attached to the port"),
required=False)
device_owner = forms.CharField(
max_length=100, label=_("Device Owner"),
help_text=_("Device owner attached to the port"),
required=False)
binding__host_id = forms.CharField(
label=_("Binding: Host"),
help_text=_("The ID of the host where the port is allocated. In some "
"cases, different implementations can run on different "
"hosts."),
required=False)
mac_address = forms.MACAddressField(
label=_("MAC Address"),
required=False,
help_text=_("MAC address for the port"))
class Meta(object):
name = _("Info")
class UpdatePortInfo(project_workflow.UpdatePortInfo):
action_class = UpdatePortInfoAction
contributes = ["name", "admin_state",
"binding__vnic_type", "mac_state", "port_security_enabled",
"device_id", "device_owner", "binding__host_id",
"mac_address"]
class UpdatePort(project_workflow.UpdatePort):
default_steps = (UpdatePortInfo, )
def get_success_url(self):
return reverse("horizon:admin:networks:detail",
args=(self.context['network_id'],))
def _construct_parameters(self, data):
params = super(UpdatePort, self)._construct_parameters(data)
params.update({'device_id': data['device_id'],
'device_owner': data['device_owner'],
'binding__host_id': data['binding__host_id'],
'mac_address': data['mac_address']})
return params

View File

@ -1,7 +0,0 @@
{% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block modal-body-right %}
<h3>{% trans "Description:" %}</h3>
<p>{% trans "You may update the editable properties of your port here." %}</p>
{% endblock %}

View File

@ -1,7 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Update Port" %}{% endblock %}
{% block main %}
{% include 'admin/networks/ports/_update.html' %}
{% endblock %}

View File

@ -14,7 +14,6 @@
import logging
from django.conf import settings
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
@ -26,12 +25,6 @@ from openstack_dashboard import api
LOG = logging.getLogger(__name__)
VNIC_TYPES = [('normal', _('Normal')),
('direct', _('Direct')),
('direct-physical', _('Direct Physical')),
('macvtap', _('MacVTap')),
('baremetal', _('Bare Metal')),
('virtio-forwarder', _('Virtio Forwarder'))]
class CreatePort(forms.SelfHandlingForm):
@ -163,88 +156,3 @@ class CreatePort(forms.SelfHandlingForm):
redirect = reverse(self.failure_url,
args=(self.initial['network_id'],))
exceptions.handle(request, msg, redirect=redirect)
class UpdatePort(forms.SelfHandlingForm):
name = forms.CharField(max_length=255,
label=_("Name"),
required=False)
admin_state = forms.BooleanField(label=_("Enable Admin State"),
required=False)
failure_url = 'horizon:project:networks:detail'
def __init__(self, request, *args, **kwargs):
super(UpdatePort, self).__init__(request, *args, **kwargs)
try:
if api.neutron.is_extension_supported(request, 'binding'):
neutron_settings = getattr(settings,
'OPENSTACK_NEUTRON_NETWORK', {})
supported_vnic_types = neutron_settings.get(
'supported_vnic_types', ['*'])
if supported_vnic_types:
if supported_vnic_types == ['*']:
vnic_type_choices = VNIC_TYPES
else:
vnic_type_choices = [
vnic_type for vnic_type in VNIC_TYPES
if vnic_type[0] in supported_vnic_types
]
self.fields['binding__vnic_type'] = forms.ChoiceField(
choices=vnic_type_choices,
label=_("Binding: VNIC Type"),
help_text=_(
"The VNIC type that is bound to the neutron port"),
required=False)
except Exception:
msg = _("Unable to verify the VNIC types extension in Neutron")
exceptions.handle(self.request, msg)
try:
if api.neutron.is_extension_supported(request, 'mac-learning'):
self.fields['mac_state'] = forms.BooleanField(
label=_("MAC Learning State"), initial=False,
required=False)
except Exception:
msg = _("Unable to retrieve MAC learning state")
exceptions.handle(self.request, msg)
try:
if api.neutron.is_extension_supported(request, 'port-security'):
self.fields['port_security_enabled'] = forms.BooleanField(
label=_("Port Security"),
help_text=_("Enable anti-spoofing rules for the port"),
required=False)
except Exception:
msg = _("Unable to retrieve port security state")
exceptions.handle(self.request, msg)
def handle(self, request, data):
port_id = self.initial['port_id']
try:
LOG.debug('params = %s', data)
extension_kwargs = {}
if 'binding__vnic_type' in data:
extension_kwargs['binding__vnic_type'] = \
data['binding__vnic_type']
if 'mac_state' in data:
extension_kwargs['mac_learning_enabled'] = data['mac_state']
if 'port_security_enabled' in data:
extension_kwargs['port_security_enabled'] = \
data['port_security_enabled']
port = api.neutron.port_update(request,
port_id,
name=data['name'],
admin_state_up=data['admin_state'],
**extension_kwargs)
msg = _('Port %s was successfully updated.') % port_id
messages.success(request, msg)
return port
except Exception as e:
LOG.info('Failed to update port %(id)s: %(exc)s',
{'id': port_id, 'exc': e})
msg = _('Failed to update port %s') % port_id
redirect = reverse(self.failure_url,
args=[self.initial['network_id']])
exceptions.handle(request, msg, redirect=redirect)

View File

@ -20,6 +20,8 @@ from django import http
from mox3.mox import IsA
from horizon.workflows import views
from openstack_dashboard import api
from openstack_dashboard.test import helpers as test
@ -91,22 +93,21 @@ class NetworkPortTests(test.TestCase):
def _test_port_update_get(self, mac_learning=False, binding=False):
port = self.ports.first()
api.neutron.port_get(IsA(http.HttpRequest),
port.id)\
.AndReturn(port)
api.neutron.port_get(IsA(http.HttpRequest), port.id) \
.MultipleTimes().AndReturn(port)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'binding')\
.AndReturn(binding)
.MultipleTimes().AndReturn(binding)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'mac-learning')\
.AndReturn(mac_learning)
.MultipleTimes().AndReturn(mac_learning)
self.mox.ReplayAll()
url = reverse('horizon:project:networks:editport',
args=[port.network_id, port.id])
res = self.client.get(url)
self.assertTemplateUsed(res, 'project/networks/ports/update.html')
self.assertTemplateUsed(res, views.WorkflowView.template_name)
@test.create_stubs({api.neutron: ('port_get',
'is_extension_supported',
@ -133,13 +134,13 @@ class NetworkPortTests(test.TestCase):
.AndReturn(port)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'binding')\
.AndReturn(binding)
.MultipleTimes().AndReturn(binding)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'mac-learning')\
.AndReturn(mac_learning)
.MultipleTimes().AndReturn(mac_learning)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'port-security')\
.AndReturn(port_security)
.MultipleTimes().AndReturn(port_security)
extension_kwargs = {}
if binding:
extension_kwargs['binding__vnic_type'] = port.binding__vnic_type
@ -198,13 +199,13 @@ class NetworkPortTests(test.TestCase):
.AndReturn(port)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'binding')\
.AndReturn(binding)
.MultipleTimes().AndReturn(binding)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'mac-learning')\
.AndReturn(mac_learning)
.MultipleTimes().AndReturn(mac_learning)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'port-security')\
.AndReturn(port_security)
.MultipleTimes().AndReturn(port_security)
extension_kwargs = {}
if binding:
extension_kwargs['binding__vnic_type'] = port.binding__vnic_type

View File

@ -19,6 +19,7 @@ from horizon import exceptions
from horizon import forms
from horizon import tabs
from horizon.utils import memoized
from horizon import workflows
from openstack_dashboard import api
@ -28,10 +29,13 @@ from openstack_dashboard.dashboards.project.networks.ports \
import tables as project_tables
from openstack_dashboard.dashboards.project.networks.ports \
import tabs as project_tabs
from openstack_dashboard.dashboards.project.networks.ports \
import workflows as project_workflows
STATE_DICT = dict(project_tables.DISPLAY_CHOICES)
STATUS_DICT = dict(project_tables.STATUS_DISPLAY_CHOICES)
VNIC_TYPES = dict(project_forms.VNIC_TYPES)
VNIC_TYPE_DICT = dict(api.neutron.VNIC_TYPES)
class CreateView(forms.ModalFormView):
@ -89,7 +93,7 @@ class DetailView(tabs.TabbedTableView):
port.status_label = STATUS_DICT.get(port.status,
port.status)
if port.get('binding__vnic_type'):
port.binding__vnic_type = VNIC_TYPES.get(
port.binding__vnic_type = VNIC_TYPE_DICT.get(
port.binding__vnic_type, port.binding__vnic_type)
except Exception:
port = []
@ -161,19 +165,9 @@ class DetailView(tabs.TabbedTableView):
return reverse('horizon:project:networks:index')
class UpdateView(forms.ModalFormView):
form_class = project_forms.UpdatePort
form_id = "update_port_form"
template_name = 'project/networks/ports/update.html'
context_object_name = 'port'
submit_label = _("Save Changes")
submit_url = "horizon:project:networks:editport"
success_url = 'horizon:project:networks:detail'
page_title = _("Edit Port")
def get_success_url(self):
return reverse(self.success_url,
args=(self.kwargs['network_id'],))
class UpdateView(workflows.WorkflowView):
workflow_class = project_workflows.UpdatePort
failure_url = "horizon:project:networks:detail"
@memoized.memoized_method
def _get_object(self, *args, **kwargs):
@ -181,21 +175,11 @@ class UpdateView(forms.ModalFormView):
try:
return api.neutron.port_get(self.request, port_id)
except Exception:
redirect = self.get_success_url()
redirect = reverse(self.failure_url,
args=(self.kwargs['network_id'],))
msg = _('Unable to retrieve port details')
exceptions.handle(self.request, msg, redirect=redirect)
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']
args = (self.kwargs['network_id'], self.kwargs['port_id'],)
context['submit_url'] = reverse(self.submit_url, args=args)
context['cancel_url'] = reverse(self.success_url,
args=(self.kwargs['network_id'],))
return context
def get_initial(self):
port = self._get_object()
initial = {'port_id': port['id'],
@ -213,4 +197,5 @@ class UpdateView(forms.ModalFormView):
pass
if 'port_security_enabled' in port:
initial['port_security_enabled'] = port['port_security_enabled']
return initial

View File

@ -0,0 +1,139 @@
# Copyright 2016 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.conf import settings
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 workflows
from openstack_dashboard import api
LOG = logging.getLogger(__name__)
class UpdatePortInfoAction(workflows.Action):
name = forms.CharField(max_length=255,
label=_("Name"),
required=False)
admin_state = forms.BooleanField(label=_("Enable Admin State"),
required=False)
def __init__(self, request, *args, **kwargs):
super(UpdatePortInfoAction, self).__init__(request, *args, **kwargs)
try:
if api.neutron.is_extension_supported(request, 'binding'):
neutron_settings = getattr(settings,
'OPENSTACK_NEUTRON_NETWORK', {})
supported_vnic_types = neutron_settings.get(
'supported_vnic_types', ['*'])
if supported_vnic_types:
if supported_vnic_types == ['*']:
vnic_type_choices = api.neutron.VNIC_TYPES
else:
vnic_type_choices = [
vnic_type for vnic_type in api.neutron.VNIC_TYPES
if vnic_type[0] in supported_vnic_types
]
self.fields['binding__vnic_type'] = forms.ChoiceField(
choices=vnic_type_choices,
label=_("Binding: VNIC Type"),
help_text=_(
"The VNIC type that is bound to the neutron port"),
required=False)
except Exception:
msg = _("Unable to verify the VNIC types extension in Neutron")
exceptions.handle(self.request, msg)
try:
if api.neutron.is_extension_supported(request, 'mac-learning'):
self.fields['mac_state'] = forms.BooleanField(
label=_("MAC Learning State"), initial=False,
required=False)
except Exception:
msg = _("Unable to retrieve MAC learning state")
exceptions.handle(self.request, msg)
try:
if api.neutron.is_extension_supported(request, 'port-security'):
self.fields['port_security_enabled'] = forms.BooleanField(
label=_("Port Security"),
help_text=_("Enable anti-spoofing rules for the port"),
required=False
)
except Exception:
msg = _("Unable to retrieve port security state")
exceptions.handle(self.request, msg)
class Meta(object):
name = _("Info")
class UpdatePortInfo(workflows.Step):
action_class = UpdatePortInfoAction
depends_on = ("network_id", "port_id")
contributes = ["name", "admin_state",
"binding__vnic_type", "mac_state", "port_security_enabled"]
help_text = _("You can update the editable properties of your port here.")
class UpdatePort(workflows.Workflow):
slug = "update_port"
name = _("Edit Port")
finalize_button_name = _("Update")
success_message = _('Port %s was successfully updated.')
failure_message = _('Failed to update port "%s".')
default_steps = (UpdatePortInfo,)
def get_success_url(self):
return reverse("horizon:project:networks:detail",
args=(self.context['network_id'],))
def format_status_message(self, message):
name = self.context['name'] or self.context['port_id']
return message % name
def handle(self, request, data):
port_id = self.context['port_id']
LOG.debug('params = %s', data)
params = self._construct_parameters(data)
try:
api.neutron.port_update(request, port_id, **params)
return True
except Exception as e:
LOG.info('Failed to update port %(port_id)s: %(exc)s',
{'port_id': port_id, 'exc': e})
return False
def _construct_parameters(self, data):
params = {
'name': data['name'],
'admin_state_up': data['admin_state'],
}
# If a field value is None, it means the field is not supported,
# If so, we skip sending such field.
if data['binding__vnic_type'] is not None:
params['binding__vnic_type'] = data['binding__vnic_type']
if data['mac_state'] is not None:
params['mac_learning_enabled'] = data['mac_state']
if data['port_security_enabled'] is not None:
params['port_security_enabled'] = data['port_security_enabled']
return params

View File

@ -1,7 +0,0 @@
{% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block modal-body-right %}
<h3>{% trans "Description:" %}</h3>
<p>{% trans "You may update the editable properties of your port here." %}</p>
{% endblock %}

View File

@ -1,7 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Update Port" %}{% endblock %}
{% block main %}
{% include 'project/networks/ports/_update.html' %}
{% endblock %}