Support security groups association per port
This patch support operation for operators and project users to associate security groups to a port. The feature is mentioned at the neutron user feedback session in Barcelona summit [1]. This function UI is same as the function of security groups association per instance. To realize this, the way of implementation for 'Edit port' is changed, which move from a single modal to a workflow base. [1] https://etherpad.openstack.org/p/ocata-neutron-end-user-operator-feedback (L.35+) Also we need to display how security groups is associated at a port. At the moment, there is not way to be able to see it (only this function). It should be done as an another patch. Change-Id: I96e0fafdffbf05b8167ec1b85f7430176fdaab90 Closes-Bug: #1637444 Co-Authored-By: Akihiro Motoki <amotoki@gmail.com>
This commit is contained in:
parent
0ff122d830
commit
e9db12382e
@ -331,8 +331,12 @@ class SecurityGroupManager(object):
|
||||
|
||||
:returns: List of SecurityGroup objects
|
||||
"""
|
||||
# This is to ensure tenant_id key is not populated
|
||||
# if tenant_id=None is specified.
|
||||
tenant_id = params.pop('tenant_id', self.request.user.tenant_id)
|
||||
return self._list(tenant_id=tenant_id, **params)
|
||||
if tenant_id:
|
||||
params['tenant_id'] = tenant_id
|
||||
return self._list(**params)
|
||||
|
||||
def _sg_name_dict(self, sg_id, rules):
|
||||
"""Create a mapping dict from secgroup id to its name."""
|
||||
|
@ -341,21 +341,18 @@ class NetworkPortTests(test.BaseAdminViewTests):
|
||||
redir_url = reverse(NETWORKS_DETAIL_URL, args=[port.network_id])
|
||||
self.assertRedirectsNoFollow(res, redir_url)
|
||||
|
||||
@test.create_stubs({api.neutron: ('port_get',
|
||||
'is_extension_supported',)})
|
||||
def test_port_update_get(self):
|
||||
self._test_port_update_get()
|
||||
|
||||
@test.create_stubs({api.neutron: ('port_get',
|
||||
'is_extension_supported',)})
|
||||
def test_port_update_get_with_mac_learning(self):
|
||||
self._test_port_update_get(mac_learning=True)
|
||||
|
||||
@test.create_stubs({api.neutron: ('port_get',
|
||||
'is_extension_supported',)})
|
||||
def test_port_update_get_with_port_security(self):
|
||||
self._test_port_update_get(port_security=True)
|
||||
|
||||
@test.create_stubs({api.neutron: ('port_get',
|
||||
'security_group_list',
|
||||
'is_extension_supported',)})
|
||||
def _test_port_update_get(self, mac_learning=False, binding=False,
|
||||
port_security=False):
|
||||
port = self.ports.first()
|
||||
@ -371,6 +368,9 @@ class NetworkPortTests(test.BaseAdminViewTests):
|
||||
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||
'port-security')\
|
||||
.AndReturn(port_security)
|
||||
api.neutron.security_group_list(IsA(http.HttpRequest),
|
||||
tenant_id=None)\
|
||||
.AndReturn(self.security_groups.list())
|
||||
self.mox.ReplayAll()
|
||||
|
||||
url = reverse('horizon:admin:networks:editport',
|
||||
@ -379,24 +379,19 @@ class NetworkPortTests(test.BaseAdminViewTests):
|
||||
|
||||
self.assertTemplateUsed(res, views.WorkflowView.template_name)
|
||||
|
||||
@test.create_stubs({api.neutron: ('port_get',
|
||||
'is_extension_supported',
|
||||
'port_update')})
|
||||
def test_port_update_post(self):
|
||||
self._test_port_update_post()
|
||||
|
||||
@test.create_stubs({api.neutron: ('port_get',
|
||||
'is_extension_supported',
|
||||
'port_update')})
|
||||
def test_port_update_post_with_mac_learning(self):
|
||||
self._test_port_update_post(mac_learning=True)
|
||||
|
||||
@test.create_stubs({api.neutron: ('port_get',
|
||||
'is_extension_supported',
|
||||
'port_update')})
|
||||
def test_port_update_post_with_port_security(self):
|
||||
self._test_port_update_post(port_security=True)
|
||||
|
||||
@test.create_stubs({api.neutron: ('port_get',
|
||||
'is_extension_supported',
|
||||
'security_group_list',
|
||||
'port_update')})
|
||||
def _test_port_update_post(self, mac_learning=False, binding=False,
|
||||
port_security=False):
|
||||
port = self.ports.first()
|
||||
@ -411,6 +406,9 @@ class NetworkPortTests(test.BaseAdminViewTests):
|
||||
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||
'port-security')\
|
||||
.MultipleTimes().AndReturn(port_security)
|
||||
api.neutron.security_group_list(IsA(http.HttpRequest),
|
||||
tenant_id=None)\
|
||||
.AndReturn(self.security_groups.list())
|
||||
extension_kwargs = {}
|
||||
if binding:
|
||||
extension_kwargs['binding__vnic_type'] = port.binding__vnic_type
|
||||
@ -451,24 +449,19 @@ class NetworkPortTests(test.BaseAdminViewTests):
|
||||
redir_url = reverse(NETWORKS_DETAIL_URL, args=[port.network_id])
|
||||
self.assertRedirectsNoFollow(res, redir_url)
|
||||
|
||||
@test.create_stubs({api.neutron: ('port_get',
|
||||
'is_extension_supported',
|
||||
'port_update')})
|
||||
def test_port_update_post_exception(self):
|
||||
self._test_port_update_post_exception()
|
||||
|
||||
@test.create_stubs({api.neutron: ('port_get',
|
||||
'is_extension_supported',
|
||||
'port_update')})
|
||||
def test_port_update_post_exception_with_mac_learning(self):
|
||||
self._test_port_update_post_exception(mac_learning=True, binding=False)
|
||||
|
||||
@test.create_stubs({api.neutron: ('port_get',
|
||||
'is_extension_supported',
|
||||
'port_update')})
|
||||
def test_port_update_post_exception_with_port_security(self):
|
||||
self._test_port_update_post_exception(port_security=True)
|
||||
|
||||
@test.create_stubs({api.neutron: ('port_get',
|
||||
'is_extension_supported',
|
||||
'security_group_list',
|
||||
'port_update')})
|
||||
def _test_port_update_post_exception(self, mac_learning=False,
|
||||
binding=False,
|
||||
port_security=False):
|
||||
@ -484,6 +477,9 @@ class NetworkPortTests(test.BaseAdminViewTests):
|
||||
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||
'port-security')\
|
||||
.MultipleTimes().AndReturn(port_security)
|
||||
api.neutron.security_group_list(IsA(http.HttpRequest),
|
||||
tenant_id=None)\
|
||||
.AndReturn(self.security_groups.list())
|
||||
extension_kwargs = {}
|
||||
if binding:
|
||||
extension_kwargs['binding__vnic_type'] = port.binding__vnic_type
|
||||
|
@ -30,25 +30,20 @@ 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"))
|
||||
required=False)
|
||||
|
||||
class Meta(object):
|
||||
name = _("Info")
|
||||
help_text_template = 'admin/networks/ports/_edit_port_help.html'
|
||||
|
||||
|
||||
class UpdatePortInfo(project_workflow.UpdatePortInfo):
|
||||
@ -60,7 +55,7 @@ class UpdatePortInfo(project_workflow.UpdatePortInfo):
|
||||
|
||||
|
||||
class UpdatePort(project_workflow.UpdatePort):
|
||||
default_steps = (UpdatePortInfo, )
|
||||
default_steps = (UpdatePortInfo, project_workflow.UpdatePortSecurityGroup)
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse("horizon:admin:networks:detail",
|
||||
|
@ -0,0 +1,18 @@
|
||||
{% extends 'project/networks/ports/_edit_port_help.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block admin_fields %}
|
||||
<dt>{% trans "Device ID" %}</dt>
|
||||
<dd>{% blocktrans trimmed %}Device ID attached to the port.
|
||||
{% endblocktrans %}</dd>
|
||||
<dt>{% trans "Device Owner" %}</dt>
|
||||
<dd>{% blocktrans trimmed %}Device owner attached to the port.
|
||||
{% endblocktrans %}</dd>
|
||||
<dt>{% trans "Binding: Host" %}</dt>
|
||||
<dd>{% blocktrans trimmed %}The ID of the host where the port is allocated.
|
||||
In some cases, different implementations can run on different hosts.
|
||||
{% endblocktrans %}</dd>
|
||||
<dt>{% trans "MAC Address" %}</dt>
|
||||
<dd>{% blocktrans trimmed %}MAC address for the port.
|
||||
{% endblocktrans %}</dd>
|
||||
{% endblock admin_fields %}
|
@ -1509,7 +1509,8 @@ class InstanceTests(helpers.ResetImageAPIVersionMixin, helpers.TestCase):
|
||||
server = self.servers.first()
|
||||
|
||||
api.nova.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
|
||||
api.neutron.security_group_list(IsA(http.HttpRequest)) \
|
||||
api.neutron.security_group_list(IsA(http.HttpRequest),
|
||||
tenant_id=None) \
|
||||
.AndReturn([])
|
||||
api.neutron.server_security_groups(IsA(http.HttpRequest),
|
||||
server.id).AndReturn([])
|
||||
@ -1561,7 +1562,8 @@ class InstanceTests(helpers.ResetImageAPIVersionMixin, helpers.TestCase):
|
||||
wanted_groups = [secgroups[1].id, secgroups[2].id]
|
||||
|
||||
api.nova.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
|
||||
api.neutron.security_group_list(IsA(http.HttpRequest)) \
|
||||
api.neutron.security_group_list(IsA(http.HttpRequest),
|
||||
tenant_id=None) \
|
||||
.AndReturn(secgroups)
|
||||
api.neutron.server_security_groups(IsA(http.HttpRequest),
|
||||
server.id).AndReturn(server_groups)
|
||||
|
@ -31,15 +31,14 @@ ADD_USER_URL = "horizon:projects:instances:create_user"
|
||||
INSTANCE_SEC_GROUP_SLUG = "update_security_groups"
|
||||
|
||||
|
||||
class UpdateInstanceSecurityGroupsAction(workflows.MembershipAction):
|
||||
class BaseSecurityGroupsAction(workflows.MembershipAction):
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(UpdateInstanceSecurityGroupsAction, self).__init__(request,
|
||||
*args,
|
||||
**kwargs)
|
||||
super(BaseSecurityGroupsAction, self).__init__(request,
|
||||
*args,
|
||||
**kwargs)
|
||||
err_msg = _('Unable to retrieve security group list. '
|
||||
'Please try again later.')
|
||||
context = args[0]
|
||||
instance_id = context.get('instance_id', '')
|
||||
|
||||
default_role_name = self.get_default_role_field_name()
|
||||
self.fields[default_role_name] = forms.CharField(required=False)
|
||||
@ -48,22 +47,55 @@ class UpdateInstanceSecurityGroupsAction(workflows.MembershipAction):
|
||||
# Get list of available security groups
|
||||
all_groups = []
|
||||
try:
|
||||
all_groups = api.neutron.security_group_list(request)
|
||||
# target_tenant_id is required when the form is used as admin.
|
||||
# Owner of security group and port should match.
|
||||
tenant_id = context.get('target_tenant_id')
|
||||
all_groups = api.neutron.security_group_list(request,
|
||||
tenant_id=tenant_id)
|
||||
except Exception:
|
||||
exceptions.handle(request, err_msg)
|
||||
groups_list = [(group.id, group.name) for group in all_groups]
|
||||
|
||||
instance_groups = []
|
||||
try:
|
||||
instance_groups = api.neutron.server_security_groups(request,
|
||||
instance_id)
|
||||
except Exception:
|
||||
exceptions.handle(request, err_msg)
|
||||
field_name = self.get_member_field_name('member')
|
||||
self.fields[field_name] = forms.MultipleChoiceField(required=False)
|
||||
self.fields[field_name].choices = groups_list
|
||||
self.fields[field_name].initial = [group.id
|
||||
for group in instance_groups]
|
||||
sec_groups = []
|
||||
try:
|
||||
sec_groups = self._get_initial_security_groups(context)
|
||||
except Exception:
|
||||
exceptions.handle(request, err_msg)
|
||||
self.fields[field_name].initial = sec_groups
|
||||
|
||||
def _get_initial_security_groups(self, context):
|
||||
# This depends on each cases
|
||||
pass
|
||||
|
||||
def handle(self, request, data):
|
||||
# This depends on each cases
|
||||
pass
|
||||
|
||||
|
||||
class BaseSecurityGroups(workflows.UpdateMembersStep):
|
||||
available_list_title = _("All Security Groups")
|
||||
no_available_text = _("No security groups found.")
|
||||
no_members_text = _("No security groups enabled.")
|
||||
show_roles = False
|
||||
contributes = ("wanted_groups",)
|
||||
|
||||
def contribute(self, data, context):
|
||||
request = self.workflow.request
|
||||
if data:
|
||||
field_name = self.get_member_field_name('member')
|
||||
context["wanted_groups"] = request.POST.getlist(field_name)
|
||||
return context
|
||||
|
||||
|
||||
class UpdateInstanceSecurityGroupsAction(BaseSecurityGroupsAction):
|
||||
def _get_initial_security_groups(self, context):
|
||||
instance_id = context.get('instance_id', '')
|
||||
sec_groups = api.neutron.server_security_groups(self.request,
|
||||
instance_id)
|
||||
return [group.id for group in sec_groups]
|
||||
|
||||
def handle(self, request, data):
|
||||
instance_id = data['instance_id']
|
||||
@ -81,28 +113,16 @@ class UpdateInstanceSecurityGroupsAction(workflows.MembershipAction):
|
||||
slug = INSTANCE_SEC_GROUP_SLUG
|
||||
|
||||
|
||||
class UpdateInstanceSecurityGroups(workflows.UpdateMembersStep):
|
||||
class UpdateInstanceSecurityGroups(BaseSecurityGroups):
|
||||
action_class = UpdateInstanceSecurityGroupsAction
|
||||
members_list_title = _("Instance Security Groups")
|
||||
help_text = _("Add and remove security groups to this instance "
|
||||
"from the list of available security groups.")
|
||||
available_list_title = _("All Security Groups")
|
||||
members_list_title = _("Instance Security Groups")
|
||||
no_available_text = _("No security groups found.")
|
||||
no_members_text = _("No security groups enabled.")
|
||||
show_roles = False
|
||||
depends_on = ("instance_id",)
|
||||
contributes = ("wanted_groups",)
|
||||
|
||||
def allowed(self, request):
|
||||
return api.base.is_service_enabled(request, 'network')
|
||||
|
||||
def contribute(self, data, context):
|
||||
request = self.workflow.request
|
||||
if data:
|
||||
field_name = self.get_member_field_name('member')
|
||||
context["wanted_groups"] = request.POST.getlist(field_name)
|
||||
return context
|
||||
|
||||
|
||||
class UpdateInstanceInfoAction(workflows.Action):
|
||||
name = forms.CharField(label=_("Name"),
|
||||
|
@ -16,6 +16,7 @@ import logging
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django import template
|
||||
from django.utils.http import urlencode
|
||||
from django.utils.translation import pgettext_lazy
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import ungettext_lazy
|
||||
@ -55,7 +56,10 @@ class UpdatePort(policy.PolicyTargetMixin, tables.LinkAction):
|
||||
|
||||
def get_link_url(self, port):
|
||||
network_id = self.table.kwargs['network_id']
|
||||
return reverse(self.url, args=(network_id, port.id))
|
||||
base_url = reverse(self.url, args=(network_id, port.id))
|
||||
params = {'step': 'update_info'}
|
||||
param = urlencode(params)
|
||||
return '?'.join([base_url, param])
|
||||
|
||||
|
||||
DISPLAY_CHOICES = (
|
||||
|
@ -81,16 +81,15 @@ class NetworkPortTests(test.TestCase):
|
||||
|
||||
self.assertRedirectsNoFollow(res, NETWORKS_INDEX_URL)
|
||||
|
||||
@test.create_stubs({api.neutron: ('port_get',
|
||||
'is_extension_supported',)})
|
||||
def test_port_update_get(self):
|
||||
self._test_port_update_get()
|
||||
|
||||
@test.create_stubs({api.neutron: ('port_get',
|
||||
'is_extension_supported',)})
|
||||
def test_port_update_get_with_mac_learning(self):
|
||||
self._test_port_update_get(mac_learning=True)
|
||||
|
||||
@test.create_stubs({api.neutron: ('port_get',
|
||||
'security_group_list',
|
||||
'is_extension_supported',)})
|
||||
def _test_port_update_get(self, mac_learning=False, binding=False):
|
||||
port = self.ports.first()
|
||||
api.neutron.port_get(IsA(http.HttpRequest), port.id) \
|
||||
@ -101,6 +100,9 @@ class NetworkPortTests(test.TestCase):
|
||||
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||
'mac-learning')\
|
||||
.MultipleTimes().AndReturn(mac_learning)
|
||||
api.neutron.security_group_list(IsA(http.HttpRequest),
|
||||
tenant_id=None)\
|
||||
.AndReturn(self.security_groups.list())
|
||||
self.mox.ReplayAll()
|
||||
|
||||
url = reverse('horizon:project:networks:editport',
|
||||
@ -109,27 +111,23 @@ class NetworkPortTests(test.TestCase):
|
||||
|
||||
self.assertTemplateUsed(res, views.WorkflowView.template_name)
|
||||
|
||||
@test.create_stubs({api.neutron: ('port_get',
|
||||
'is_extension_supported',
|
||||
'port_update')})
|
||||
def test_port_update_post(self):
|
||||
self._test_port_update_post()
|
||||
|
||||
@test.create_stubs({api.neutron: ('port_get',
|
||||
'is_extension_supported',
|
||||
'port_update')})
|
||||
def test_port_update_post_with_mac_learning(self):
|
||||
self._test_port_update_post(mac_learning=True)
|
||||
|
||||
@test.create_stubs({api.neutron: ('port_get',
|
||||
'is_extension_supported',
|
||||
'port_update')})
|
||||
def test_port_update_post_with_port_security(self):
|
||||
self._test_port_update_post(port_security=True)
|
||||
|
||||
@test.create_stubs({api.neutron: ('port_get',
|
||||
'is_extension_supported',
|
||||
'security_group_list',
|
||||
'port_update')})
|
||||
def _test_port_update_post(self, mac_learning=False, binding=False,
|
||||
port_security=False):
|
||||
port = self.ports.first()
|
||||
security_groups = self.security_groups.list()
|
||||
api.neutron.port_get(IsA(http.HttpRequest), port.id)\
|
||||
.AndReturn(port)
|
||||
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||
@ -141,6 +139,9 @@ class NetworkPortTests(test.TestCase):
|
||||
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||
'port-security')\
|
||||
.MultipleTimes().AndReturn(port_security)
|
||||
api.neutron.security_group_list(IsA(http.HttpRequest),
|
||||
tenant_id=None)\
|
||||
.AndReturn(self.security_groups.list())
|
||||
extension_kwargs = {}
|
||||
if binding:
|
||||
extension_kwargs['binding__vnic_type'] = port.binding__vnic_type
|
||||
@ -148,6 +149,7 @@ class NetworkPortTests(test.TestCase):
|
||||
extension_kwargs['mac_learning_enabled'] = True
|
||||
if port_security:
|
||||
extension_kwargs['port_security_enabled'] = True
|
||||
extension_kwargs['wanted_groups'] = security_groups
|
||||
api.neutron.port_update(IsA(http.HttpRequest), port.id,
|
||||
name=port.name,
|
||||
admin_state_up=port.admin_state_up,
|
||||
@ -165,6 +167,7 @@ class NetworkPortTests(test.TestCase):
|
||||
form_data['mac_state'] = True
|
||||
if port_security:
|
||||
form_data['port_security_enabled'] = True
|
||||
form_data['wanted_groups'] = security_groups
|
||||
url = reverse('horizon:project:networks:editport',
|
||||
args=[port.network_id, port.id])
|
||||
res = self.client.post(url, form_data)
|
||||
@ -172,24 +175,19 @@ class NetworkPortTests(test.TestCase):
|
||||
redir_url = reverse(NETWORKS_DETAIL_URL, args=[port.network_id])
|
||||
self.assertRedirectsNoFollow(res, redir_url)
|
||||
|
||||
@test.create_stubs({api.neutron: ('port_get',
|
||||
'is_extension_supported',
|
||||
'port_update')})
|
||||
def test_port_update_post_exception(self):
|
||||
self._test_port_update_post_exception()
|
||||
|
||||
@test.create_stubs({api.neutron: ('port_get',
|
||||
'is_extension_supported',
|
||||
'port_update')})
|
||||
def test_port_update_post_exception_with_mac_learning(self):
|
||||
self._test_port_update_post_exception(mac_learning=True)
|
||||
|
||||
@test.create_stubs({api.neutron: ('port_get',
|
||||
'is_extension_supported',
|
||||
'port_update')})
|
||||
def test_port_update_post_exception_with_port_security(self):
|
||||
self._test_port_update_post_exception(port_security=True)
|
||||
|
||||
@test.create_stubs({api.neutron: ('port_get',
|
||||
'is_extension_supported',
|
||||
'security_group_list',
|
||||
'port_update')})
|
||||
def _test_port_update_post_exception(self, mac_learning=False,
|
||||
binding=False,
|
||||
port_security=False):
|
||||
@ -206,6 +204,9 @@ class NetworkPortTests(test.TestCase):
|
||||
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||
'port-security')\
|
||||
.MultipleTimes().AndReturn(port_security)
|
||||
api.neutron.security_group_list(IsA(http.HttpRequest),
|
||||
tenant_id=None)\
|
||||
.AndReturn(self.security_groups.list())
|
||||
extension_kwargs = {}
|
||||
if binding:
|
||||
extension_kwargs['binding__vnic_type'] = port.binding__vnic_type
|
||||
|
@ -187,7 +187,8 @@ class UpdateView(workflows.WorkflowView):
|
||||
'tenant_id': port['tenant_id'],
|
||||
'name': port['name'],
|
||||
'admin_state': port['admin_state_up'],
|
||||
'mac_address': port['mac_address']}
|
||||
'mac_address': port['mac_address'],
|
||||
'target_tenant_id': port['tenant_id']}
|
||||
if port.get('binding__vnic_type'):
|
||||
initial['binding__vnic_type'] = port['binding__vnic_type']
|
||||
try:
|
||||
|
@ -24,6 +24,9 @@ from horizon import forms
|
||||
from horizon import workflows
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard.dashboards.project.instances.workflows import \
|
||||
update_instance as base_sec_group
|
||||
from openstack_dashboard.utils import filters
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
@ -55,8 +58,6 @@ class UpdatePortInfoAction(workflows.Action):
|
||||
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")
|
||||
@ -75,8 +76,13 @@ class UpdatePortInfoAction(workflows.Action):
|
||||
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
|
||||
required=False,
|
||||
widget=forms.CheckboxInput(attrs={
|
||||
'class': 'switchable',
|
||||
'data-slug': 'port_security_enabled',
|
||||
'data-hide-tab': 'update_port__update_security_groups',
|
||||
'data-hide-on-checked': 'false'
|
||||
})
|
||||
)
|
||||
except Exception:
|
||||
msg = _("Unable to retrieve port security state")
|
||||
@ -84,6 +90,8 @@ class UpdatePortInfoAction(workflows.Action):
|
||||
|
||||
class Meta(object):
|
||||
name = _("Info")
|
||||
slug = 'update_info'
|
||||
help_text_template = 'project/networks/ports/_edit_port_help.html'
|
||||
|
||||
|
||||
class UpdatePortInfo(workflows.Step):
|
||||
@ -91,7 +99,25 @@ class UpdatePortInfo(workflows.Step):
|
||||
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 UpdatePortSecurityGroupAction(base_sec_group.BaseSecurityGroupsAction):
|
||||
def _get_initial_security_groups(self, context):
|
||||
port_id = context.get('port_id', '')
|
||||
port = api.neutron.port_get(self.request, port_id)
|
||||
return port.security_groups
|
||||
|
||||
class Meta(object):
|
||||
name = _("Security Groups")
|
||||
slug = "update_security_groups"
|
||||
|
||||
|
||||
class UpdatePortSecurityGroup(base_sec_group.BaseSecurityGroups):
|
||||
action_class = UpdatePortSecurityGroupAction
|
||||
members_list_title = _("Port Security Groups")
|
||||
help_text = _("Add or remove security groups to this port "
|
||||
"from the list of available security groups.")
|
||||
depends_on = ("port_id", 'target_tenant_id')
|
||||
|
||||
|
||||
class UpdatePort(workflows.Workflow):
|
||||
@ -100,7 +126,7 @@ class UpdatePort(workflows.Workflow):
|
||||
finalize_button_name = _("Update")
|
||||
success_message = _('Port %s was successfully updated.')
|
||||
failure_message = _('Failed to update port "%s".')
|
||||
default_steps = (UpdatePortInfo,)
|
||||
default_steps = (UpdatePortInfo, UpdatePortSecurityGroup)
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse("horizon:project:networks:detail",
|
||||
@ -136,4 +162,18 @@ class UpdatePort(workflows.Workflow):
|
||||
if data['port_security_enabled'] is not None:
|
||||
params['port_security_enabled'] = data['port_security_enabled']
|
||||
|
||||
# If port_security_enabled is set to False, security groups on the port
|
||||
# must be cleared. We will clear the current security groups
|
||||
# in this case.
|
||||
if ('port_security_enabled' in params
|
||||
and not params['port_security_enabled']):
|
||||
params['security_groups'] = []
|
||||
# In case of UpdatePortSecurityGroup registered, 'wanted_groups'
|
||||
# exists in data.
|
||||
elif 'wanted_groups' in data:
|
||||
# If data has that key, we need to set its value
|
||||
# even if its value is empty to clear sec group setting.
|
||||
groups = map(filters.get_int_or_uuid, data['wanted_groups'])
|
||||
params['security_groups'] = groups
|
||||
|
||||
return params
|
||||
|
@ -0,0 +1,25 @@
|
||||
{% load i18n %}
|
||||
|
||||
<p>{% trans "You can edit the properties of your port here." %}</p>
|
||||
<dl>
|
||||
<dt>{% trans "Enable Admin State" %}</dt>
|
||||
<dd>{% blocktrans trimmed %}When the admin state of the port is enabled, the
|
||||
networking service forward packets on the port. Otherwise, it does not
|
||||
forward any packets on the port.{% endblocktrans %}</dd>
|
||||
{% block admin_fields %}{% endblock %}
|
||||
<dt>{% trans "Binding: VNIC Type" %}</dt>
|
||||
<dd>{% blocktrans trimmed %}It specified the VNIC type bound to the
|
||||
networking port.{% endblocktrans %}</dd>
|
||||
<dt>{% trans "Port Security" %}</dt>
|
||||
<dd>{% blocktrans trimmed %}
|
||||
Enables anti-spoofing rules for the port if enabled. In addition,
|
||||
if port security is disabled, security groups
|
||||
on the port will be automatically cleared. When you enable port security
|
||||
of the port, you may want to associate some security groups on
|
||||
the port.{% endblocktrans %}</dd>
|
||||
<dt>{% trans "Security Groups" %}</dt>
|
||||
<dd>{% blocktrans trimmed %}You can add or remove security groups
|
||||
associated with the port in the next tab (if the port security is
|
||||
enabled for the port).
|
||||
{% endblocktrans %}</dd>
|
||||
</dl>
|
@ -120,6 +120,7 @@ def data(TEST):
|
||||
{'ip_address': '174.0.0.201',
|
||||
'mac_address': 'fa:16:3e:7a:7b:18'}
|
||||
],
|
||||
'port_security_enabled': True,
|
||||
'security_groups': [],
|
||||
}
|
||||
|
||||
@ -140,6 +141,7 @@ def data(TEST):
|
||||
'tenant_id': network_dict['tenant_id'],
|
||||
'binding:vnic_type': 'normal',
|
||||
'binding:host_id': 'host',
|
||||
'port_security_enabled': True,
|
||||
'security_groups': [
|
||||
# sec_group_1 ID below
|
||||
'faad7c80-3b62-4440-967c-13808c37131d',
|
||||
|
@ -0,0 +1,6 @@
|
||||
---
|
||||
features:
|
||||
- Support security groups association per network port for operators
|
||||
and users. Note that the current implementation only supports to edit
|
||||
security groups of neutron port from the port tables in the network
|
||||
detail page (Further improvement is planned).
|
Loading…
Reference in New Issue
Block a user