NSX|P: Port create/update/delete enhancments
Complete the support for port operations + enabling hte relevant tempest tests Depends-on: Ib5d26e8b22a9a167151fb17e087e8b561ea74783 Change-Id: Ia31e2180b880b29608dab42b4c810ae68547c24b
This commit is contained in:
parent
b024fb250d
commit
ff6c595b90
@ -19,7 +19,37 @@
|
||||
# based on the features that are ready to be tested.
|
||||
|
||||
# Begin list of exclusions.
|
||||
#r="^(?!.*)"
|
||||
r="^(?!.*"
|
||||
r="$r(?:tempest\.api\.network\.test_extensions\.ExtensionsTestJSON.*)"
|
||||
r="$r|(?:tempest\.api\.network\.test_routers\.DvrRoutersTest.*)"
|
||||
r="$r|(?:tempest\.api\.network\.test_routers_negative\.DvrRoutersNegativeTest.*)"
|
||||
|
||||
r="$r(tempest\.api\.network\.test_security_groups|tempest\.api\.network\.test_networks|tempest\.api\.network\.test_networks_negative).*$"
|
||||
export DEVSTACK_GATE_TEMPEST_REGEX="$r"
|
||||
r="$r|(?:tempest\.api\.network\.test_allowed_address_pair\.AllowedAddressPairTestJSON\.test_update_port_with_cidr_address_pair.*)"
|
||||
#Native DHCP has no agents
|
||||
r="$r|(?:tempest\.api\.network\.admin\.test_agent_management\.AgentManagementTestJSON.*)"
|
||||
#Can not create more than one DHCP-enabled subnet
|
||||
r="$r|(?:tempest\.api\.network\.test_ports\.PortsTestJSON\.test_create_update_port_with_second_ip.*)"
|
||||
r="$r|(?:tempest\.api\.network\.test_ports\.PortsTestJSON\.test_update_port_with_security_group_and_extra_attributes.*)"
|
||||
r="$r|(?:tempest\.api\.network\.test_ports\.PortsTestJSON\.test_update_port_with_two_security_groups_and_extra_attributes.*)"
|
||||
r="$r|(?:tempest\.api\.network\.test_extra_dhcp_options\.ExtraDHCPOptionsTestJSON\.test_.*_with_extra_dhcp_options.*)"
|
||||
r="$r|(?:tempest\.api\.network\.test_floating_ips\.FloatingIPTestJSON\.test_create_update_floatingip_with_port_multiple_ip_address.*)"
|
||||
|
||||
r="$r|(?:tempest\.api\.network\.admin\.test_external_network_extension\.ExternalNetworksTestJSON\.test_update_external_network.*)"
|
||||
|
||||
# Some ICMP types are not supported by the NSX backend
|
||||
r="$r|(?:tempest\.api\.network\.test_security_groups\.SecGroupTest\.test_create_security_group_rule_with_icmp_type_code.*)"
|
||||
|
||||
# Temporarily exclude packages which are not yet supported by the P plugin
|
||||
r="$r|(?:tempest\.api\.network\.admin\.test_floating_ips_admin_actions.*)"
|
||||
r="$r|(?:tempest\.api\.network\.admin\.test_routers.*)"
|
||||
r="$r|(?:tempest\.api\.network\.admin\.test_routers_negative.*)"
|
||||
r="$r|(?:tempest\.api\.network\.test_floating_ips.*)"
|
||||
r="$r|(?:tempest\.api\.network\.test_routers.*)"
|
||||
|
||||
# End list of exclusions.
|
||||
r="$r)"
|
||||
|
||||
# only run tempest.api.network tests
|
||||
r="$r(tempest\.api\.network).*$"
|
||||
|
||||
export DEVSTACK_GATE_TEMPEST_REGEX="$r"
|
||||
|
@ -157,6 +157,96 @@ class NsxPluginV3Base(plugin.NsxPluginBase,
|
||||
self._get_security_groups_on_port(context, port))
|
||||
return port_security, has_ip, sgids, psgids
|
||||
|
||||
def _should_validate_port_sec_on_update_port(self, port_data):
|
||||
# Need to determine if we skip validations for port security.
|
||||
# This is the edge case when the subnet is deleted.
|
||||
# This should be called prior to deleting the fixed ip from the
|
||||
# port data
|
||||
for fixed_ip in port_data.get('fixed_ips', []):
|
||||
if 'delete_subnet' in fixed_ip:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _update_port_preprocess_security(
|
||||
self, context, port, id, updated_port, is_ens_tz_port,
|
||||
validate_port_sec=True, direct_vnic_type=False):
|
||||
delete_addr_pairs = self._check_update_deletes_allowed_address_pairs(
|
||||
port)
|
||||
has_addr_pairs = self._check_update_has_allowed_address_pairs(port)
|
||||
has_security_groups = self._check_update_has_security_groups(port)
|
||||
delete_security_groups = self._check_update_deletes_security_groups(
|
||||
port)
|
||||
|
||||
# populate port_security setting
|
||||
port_data = port['port']
|
||||
if psec.PORTSECURITY not in port_data:
|
||||
updated_port[psec.PORTSECURITY] = \
|
||||
self._get_port_security_binding(context, id)
|
||||
has_ip = self._ip_on_port(updated_port)
|
||||
# validate port security and allowed address pairs
|
||||
if not updated_port[psec.PORTSECURITY]:
|
||||
# has address pairs in request
|
||||
if has_addr_pairs:
|
||||
raise addr_exc.AddressPairAndPortSecurityRequired()
|
||||
elif not delete_addr_pairs:
|
||||
# check if address pairs are in db
|
||||
updated_port[addr_apidef.ADDRESS_PAIRS] = (
|
||||
self.get_allowed_address_pairs(context, id))
|
||||
if updated_port[addr_apidef.ADDRESS_PAIRS]:
|
||||
raise addr_exc.AddressPairAndPortSecurityRequired()
|
||||
|
||||
if delete_addr_pairs or has_addr_pairs:
|
||||
self._validate_ipv4_address_pairs(
|
||||
updated_port[addr_apidef.ADDRESS_PAIRS])
|
||||
# delete address pairs and read them in
|
||||
self._delete_allowed_address_pairs(context, id)
|
||||
self._process_create_allowed_address_pairs(
|
||||
context, updated_port,
|
||||
updated_port[addr_apidef.ADDRESS_PAIRS])
|
||||
|
||||
if updated_port[psec.PORTSECURITY] and psec.PORTSECURITY in port_data:
|
||||
# No port security is allowed if the port belongs to an ENS TZ
|
||||
if is_ens_tz_port and not self._ens_psec_supported():
|
||||
raise nsx_exc.NsxENSPortSecurity()
|
||||
|
||||
# No port security is allowed if the port has a direct vnic type
|
||||
if direct_vnic_type:
|
||||
err_msg = _("Security features are not supported for "
|
||||
"ports with direct/direct-physical VNIC type")
|
||||
raise n_exc.InvalidInput(error_message=err_msg)
|
||||
|
||||
# checks if security groups were updated adding/modifying
|
||||
# security groups, port security is set and port has ip
|
||||
provider_sgs_specified = self._provider_sgs_specified(updated_port)
|
||||
if (validate_port_sec and
|
||||
not (has_ip and updated_port[psec.PORTSECURITY])):
|
||||
if has_security_groups or provider_sgs_specified:
|
||||
LOG.error("Port has conflicting port security status and "
|
||||
"security groups")
|
||||
raise psec_exc.PortSecurityAndIPRequiredForSecurityGroups()
|
||||
# Update did not have security groups passed in. Check
|
||||
# that port does not have any security groups already on it.
|
||||
filters = {'port_id': [id]}
|
||||
security_groups = (
|
||||
super(NsxPluginV3Base, self)._get_port_security_group_bindings(
|
||||
context, filters)
|
||||
)
|
||||
if security_groups and not delete_security_groups:
|
||||
raise psec_exc.PortSecurityPortHasSecurityGroup()
|
||||
|
||||
if delete_security_groups or has_security_groups:
|
||||
# delete the port binding and read it with the new rules.
|
||||
self._delete_port_security_group_bindings(context, id)
|
||||
sgids = self._get_security_groups_on_port(context, port)
|
||||
self._process_port_create_security_group(context, updated_port,
|
||||
sgids)
|
||||
|
||||
if psec.PORTSECURITY in port['port']:
|
||||
self._process_port_port_security_update(
|
||||
context, port['port'], updated_port)
|
||||
|
||||
return updated_port
|
||||
|
||||
def _validate_create_network(self, context, net_data):
|
||||
"""Validate the parameters of the new network to be created
|
||||
|
||||
@ -678,3 +768,24 @@ class NsxPluginV3Base(plugin.NsxPluginBase,
|
||||
if net.get(pnet.NETWORK_TYPE) in net_types:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _revert_neutron_port_update(self, context, port_id,
|
||||
original_port, updated_port,
|
||||
port_security, sec_grp_updated):
|
||||
# revert the neutron port update
|
||||
super(NsxPluginV3Base, self).update_port(context, port_id,
|
||||
{'port': original_port})
|
||||
# revert allowed address pairs
|
||||
if port_security:
|
||||
orig_pair = original_port.get(addr_apidef.ADDRESS_PAIRS)
|
||||
updated_pair = updated_port.get(addr_apidef.ADDRESS_PAIRS)
|
||||
if orig_pair != updated_pair:
|
||||
self._delete_allowed_address_pairs(context, port_id)
|
||||
if orig_pair:
|
||||
self._process_create_allowed_address_pairs(
|
||||
context, original_port, orig_pair)
|
||||
# revert the security groups modifications
|
||||
if sec_grp_updated:
|
||||
self.update_security_group_on_port(
|
||||
context, port_id, {'port': original_port},
|
||||
updated_port, original_port)
|
||||
|
@ -188,7 +188,7 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
return None
|
||||
|
||||
try:
|
||||
resource_api.get(name_or_id)
|
||||
resource_api.get(name_or_id, silent=True)
|
||||
return name_or_id
|
||||
except nsx_lib_exc.ResourceNotFound:
|
||||
try:
|
||||
@ -516,6 +516,19 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
return address_bindings
|
||||
|
||||
def _get_network_nsx_id(self, context, network_id):
|
||||
"""Return the id of this logical switch in the nsx manager
|
||||
|
||||
(Not the segment in the policy manager)
|
||||
The nova api will use this to attach to the instance
|
||||
"""
|
||||
#TODO(asarfaty): This is a backend call that will be called for
|
||||
# each get_port/s. We should consider caching the results or adding
|
||||
# to DB
|
||||
if not self._network_is_external(context, network_id):
|
||||
segment_id = self._get_network_nsx_segment_id(context, network_id)
|
||||
return self.nsxpolicy.segment.get_realized_id(segment_id)
|
||||
|
||||
def _get_network_nsx_segment_id(self, context, network_id):
|
||||
"""Return the NSX segment ID matching the neutron network id
|
||||
|
||||
Usually the NSX ID is the same as the neutron ID. The exception is
|
||||
@ -549,9 +562,10 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
address_bindings = self._build_port_address_bindings(
|
||||
context, port_data)
|
||||
device_owner = port_data.get('device_owner')
|
||||
vif_id = None
|
||||
attachment_type = vif_id = None
|
||||
if device_owner and device_owner != l3_db.DEVICE_OWNER_ROUTER_INTF:
|
||||
vif_id = port_data['id']
|
||||
attachment_type = nsxlib_consts.ATTACHMENT_VIF
|
||||
tags = self.nsxpolicy.build_v3_api_version_project_tag(
|
||||
context.tenant_name)
|
||||
|
||||
@ -559,7 +573,7 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
tags.extend(self.nsxpolicy.build_v3_api_version_project_tag(
|
||||
context.tenant_name))
|
||||
|
||||
segment_id = self._get_network_nsx_id(
|
||||
segment_id = self._get_network_nsx_segment_id(
|
||||
context, port_data['network_id'])
|
||||
self.nsxpolicy.segment_port.create_or_overwrite(
|
||||
name, segment_id,
|
||||
@ -567,6 +581,7 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
description=port_data.get('description'),
|
||||
address_bindings=address_bindings,
|
||||
vif_id=vif_id,
|
||||
attachment_type=attachment_type,
|
||||
tags=tags)
|
||||
|
||||
def base_create_port(self, context, port):
|
||||
@ -606,6 +621,7 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
self._process_port_create_security_group(context, port_data, sgids)
|
||||
self._process_port_create_provider_security_group(
|
||||
context, port_data, psgids)
|
||||
#TODO(asarfaty): Handle mac learning
|
||||
|
||||
if not is_external_net:
|
||||
try:
|
||||
@ -633,21 +649,20 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
l3_port_check=True, l2gw_port_check=True,
|
||||
force_delete_dhcp=False,
|
||||
force_delete_vpn=False):
|
||||
# first update neutron (this will perform all types of validations)
|
||||
port_data = self.get_port(context, port_id)
|
||||
segment_id = self._get_network_nsx_id(
|
||||
context, port_data['network_id'])
|
||||
net_id = port_data['network_id']
|
||||
self.disassociate_floatingips(context, port_id)
|
||||
super(NsxPolicyPlugin, self).delete_port(context, port_id)
|
||||
|
||||
if not self._network_is_external(context, port_data['network_id']):
|
||||
if not self._network_is_external(context, net_id):
|
||||
try:
|
||||
segment_id = self._get_network_nsx_segment_id(context, net_id)
|
||||
self.nsxpolicy.segment_port.delete(segment_id, port_data['id'])
|
||||
except Exception as ex:
|
||||
LOG.error("Failed to delete port %(id)s on NSX backend "
|
||||
"due to %(e)s",
|
||||
{'id': port_data['id'], 'e': ex})
|
||||
|
||||
self.disassociate_floatingips(context, port_id)
|
||||
|
||||
super(NsxPolicyPlugin, self).delete_port(context, port_id)
|
||||
"due to %(e)s", {'id': port_id, 'e': ex})
|
||||
# Do not fail the neutron action
|
||||
|
||||
def _update_port_on_backend(self, context, lport_id,
|
||||
original_port, updated_port):
|
||||
@ -655,12 +670,15 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
# Update might evolve with more features
|
||||
return self._create_port_on_backend(context, updated_port)
|
||||
|
||||
def update_port(self, context, id, port):
|
||||
def update_port(self, context, port_id, port):
|
||||
with db_api.CONTEXT_WRITER.using(context):
|
||||
# get the original port, and keep it honest as it is later used
|
||||
# for notifications
|
||||
original_port = super(NsxPolicyPlugin, self).get_port(context, id)
|
||||
original_port = super(NsxPolicyPlugin, self).get_port(
|
||||
context, port_id)
|
||||
port_data = port['port']
|
||||
validate_port_sec = self._should_validate_port_sec_on_update_port(
|
||||
port_data)
|
||||
is_external_net = self._network_is_external(
|
||||
context, original_port['network_id'])
|
||||
if is_external_net:
|
||||
@ -671,8 +689,12 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
self._validate_max_ips_per_port(
|
||||
port_data.get('fixed_ips', []), device_owner)
|
||||
|
||||
updated_port = super(NsxPolicyPlugin, self).update_port(context,
|
||||
id, port)
|
||||
direct_vnic_type = self._validate_port_vnic_type(
|
||||
context, port_data, original_port['network_id'])
|
||||
|
||||
updated_port = super(NsxPolicyPlugin, self).update_port(
|
||||
context, port_id, port)
|
||||
|
||||
self._extension_manager.process_update_port(context, port_data,
|
||||
updated_port)
|
||||
# copy values over - except fixed_ips as
|
||||
@ -680,15 +702,47 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
port_data.pop('fixed_ips', None)
|
||||
updated_port.update(port_data)
|
||||
|
||||
updated_port = self._update_port_preprocess_security(
|
||||
context, port, port_id, updated_port, False,
|
||||
validate_port_sec=validate_port_sec,
|
||||
direct_vnic_type=direct_vnic_type)
|
||||
|
||||
sec_grp_updated = self.update_security_group_on_port(
|
||||
context, port_id, port, original_port, updated_port)
|
||||
|
||||
self._process_port_update_provider_security_group(
|
||||
context, port, original_port, updated_port)
|
||||
|
||||
(port_security, has_ip) = self._determine_port_security_and_has_ip(
|
||||
context, updated_port)
|
||||
self._remove_provider_security_groups_from_list(updated_port)
|
||||
self._process_portbindings_create_and_update(
|
||||
context, port_data, updated_port,
|
||||
vif_type=self._vif_type_by_vnic_type(direct_vnic_type))
|
||||
self._extend_nsx_port_dict_binding(context, updated_port)
|
||||
|
||||
#TODO(asarfaty): Handle mac learning
|
||||
|
||||
# update the port in the backend, only if it exists in the DB
|
||||
# (i.e not external net)
|
||||
if not is_external_net:
|
||||
self._update_port_on_backend(context, id,
|
||||
original_port, updated_port)
|
||||
try:
|
||||
self._update_port_on_backend(context, port_id,
|
||||
original_port, updated_port)
|
||||
except Exception as e:
|
||||
LOG.error('Failed to update port %(id)s on NSX '
|
||||
'backend. Exception: %(e)s',
|
||||
{'id': port_id, 'e': e})
|
||||
# Rollback the change
|
||||
with excutils.save_and_reraise_exception():
|
||||
with db_api.CONTEXT_WRITER.using(context):
|
||||
self._revert_neutron_port_update(
|
||||
context, port_id, original_port, updated_port,
|
||||
port_security, sec_grp_updated)
|
||||
|
||||
# Make sure the port revision is updated
|
||||
if 'revision_number' in updated_port:
|
||||
port_model = self._get_port(context, id)
|
||||
port_model = self._get_port(context, port_id)
|
||||
updated_port['revision_number'] = port_model.revision_number
|
||||
|
||||
# Notifications must be sent after the above transaction is complete
|
||||
@ -702,8 +756,9 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
registry.notify(resources.PORT, events.AFTER_UPDATE, self, **kwargs)
|
||||
return updated_port
|
||||
|
||||
def get_port(self, context, id, fields=None):
|
||||
port = super(NsxPolicyPlugin, self).get_port(context, id, fields=None)
|
||||
def get_port(self, context, port_id, fields=None):
|
||||
port = super(NsxPolicyPlugin, self).get_port(
|
||||
context, port_id, fields=None)
|
||||
if 'id' in port:
|
||||
port_model = self._get_port(context, port['id'])
|
||||
resource_extend.apply_funcs('ports', port, port_model)
|
||||
@ -821,7 +876,7 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
self._validate_interface_address_scope(context, router_db, info)
|
||||
subnet = self.get_subnet(context, info['subnet_ids'][0])
|
||||
|
||||
segment_id = self._get_network_nsx_id(context, network_id)
|
||||
segment_id = self._get_network_nsx_segment_id(context, network_id)
|
||||
# TODO(annak): Validate TZ
|
||||
try:
|
||||
# This is always an overwrite call
|
||||
|
@ -28,9 +28,7 @@ from neutron_lib.api import faults
|
||||
from neutron_lib.api.validators import availability_zone as az_validator
|
||||
from neutron_lib.db import api as db_api
|
||||
from neutron_lib.db import utils as db_utils
|
||||
from neutron_lib.exceptions import allowedaddresspairs as addr_exc
|
||||
from neutron_lib.exceptions import l3 as l3_exc
|
||||
from neutron_lib.exceptions import port_security as psec_exc
|
||||
from neutron_lib.plugins import constants as plugin_const
|
||||
from neutron_lib.plugins import directory
|
||||
from neutron_lib import rpc as n_rpc
|
||||
@ -2786,86 +2784,6 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
is_delete=True)
|
||||
super(NsxV3Plugin, self).delete_port(context, port_id)
|
||||
|
||||
def _update_port_preprocess_security(
|
||||
self, context, port, id, updated_port, is_ens_tz_port,
|
||||
validate_port_sec=True, direct_vnic_type=False):
|
||||
delete_addr_pairs = self._check_update_deletes_allowed_address_pairs(
|
||||
port)
|
||||
has_addr_pairs = self._check_update_has_allowed_address_pairs(port)
|
||||
has_security_groups = self._check_update_has_security_groups(port)
|
||||
delete_security_groups = self._check_update_deletes_security_groups(
|
||||
port)
|
||||
|
||||
# populate port_security setting
|
||||
port_data = port['port']
|
||||
if psec.PORTSECURITY not in port_data:
|
||||
updated_port[psec.PORTSECURITY] = \
|
||||
self._get_port_security_binding(context, id)
|
||||
has_ip = self._ip_on_port(updated_port)
|
||||
# validate port security and allowed address pairs
|
||||
if not updated_port[psec.PORTSECURITY]:
|
||||
# has address pairs in request
|
||||
if has_addr_pairs:
|
||||
raise addr_exc.AddressPairAndPortSecurityRequired()
|
||||
elif not delete_addr_pairs:
|
||||
# check if address pairs are in db
|
||||
updated_port[addr_apidef.ADDRESS_PAIRS] = (
|
||||
self.get_allowed_address_pairs(context, id))
|
||||
if updated_port[addr_apidef.ADDRESS_PAIRS]:
|
||||
raise addr_exc.AddressPairAndPortSecurityRequired()
|
||||
|
||||
if delete_addr_pairs or has_addr_pairs:
|
||||
self._validate_ipv4_address_pairs(
|
||||
updated_port[addr_apidef.ADDRESS_PAIRS])
|
||||
# delete address pairs and read them in
|
||||
self._delete_allowed_address_pairs(context, id)
|
||||
self._process_create_allowed_address_pairs(
|
||||
context, updated_port,
|
||||
updated_port[addr_apidef.ADDRESS_PAIRS])
|
||||
|
||||
if updated_port[psec.PORTSECURITY] and psec.PORTSECURITY in port_data:
|
||||
# No port security is allowed if the port belongs to an ENS TZ
|
||||
if is_ens_tz_port and not self._ens_psec_supported():
|
||||
raise nsx_exc.NsxENSPortSecurity()
|
||||
|
||||
# No port security is allowed if the port has a direct vnic type
|
||||
if direct_vnic_type:
|
||||
err_msg = _("Security features are not supported for "
|
||||
"ports with direct/direct-physical VNIC type")
|
||||
raise n_exc.InvalidInput(error_message=err_msg)
|
||||
|
||||
# checks if security groups were updated adding/modifying
|
||||
# security groups, port security is set and port has ip
|
||||
provider_sgs_specified = self._provider_sgs_specified(updated_port)
|
||||
if (validate_port_sec and
|
||||
not (has_ip and updated_port[psec.PORTSECURITY])):
|
||||
if has_security_groups or provider_sgs_specified:
|
||||
LOG.error("Port has conflicting port security status and "
|
||||
"security groups")
|
||||
raise psec_exc.PortSecurityAndIPRequiredForSecurityGroups()
|
||||
# Update did not have security groups passed in. Check
|
||||
# that port does not have any security groups already on it.
|
||||
filters = {'port_id': [id]}
|
||||
security_groups = (
|
||||
super(NsxV3Plugin, self)._get_port_security_group_bindings(
|
||||
context, filters)
|
||||
)
|
||||
if security_groups and not delete_security_groups:
|
||||
raise psec_exc.PortSecurityPortHasSecurityGroup()
|
||||
|
||||
if delete_security_groups or has_security_groups:
|
||||
# delete the port binding and read it with the new rules.
|
||||
self._delete_port_security_group_bindings(context, id)
|
||||
sgids = self._get_security_groups_on_port(context, port)
|
||||
self._process_port_create_security_group(context, updated_port,
|
||||
sgids)
|
||||
|
||||
if psec.PORTSECURITY in port['port']:
|
||||
self._process_port_port_security_update(
|
||||
context, port['port'], updated_port)
|
||||
|
||||
return updated_port
|
||||
|
||||
def _get_resource_type_for_device_id(self, device_owner, device_id):
|
||||
if device_owner in const.ROUTER_INTERFACE_OWNERS:
|
||||
return 'os-router-uuid'
|
||||
@ -3052,17 +2970,6 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
return policy_id, profile_id
|
||||
|
||||
def update_port(self, context, id, port):
|
||||
switch_profile_ids = None
|
||||
|
||||
# Need to determine if we skip validations for port security.
|
||||
# This is the edge case when the subnet is deleted.
|
||||
validate_port_sec = True
|
||||
fixed_ips = port['port'].get('fixed_ips', [])
|
||||
for fixed_ip in fixed_ips:
|
||||
if 'delete_subnet' in fixed_ip:
|
||||
validate_port_sec = False
|
||||
break
|
||||
|
||||
with db_api.CONTEXT_WRITER.using(context):
|
||||
# get the original port, and keep it honest as it is later used
|
||||
# for notifications
|
||||
@ -3070,7 +2977,8 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
self._extend_get_port_dict_qos_and_binding(context, original_port)
|
||||
self._remove_provider_security_groups_from_list(original_port)
|
||||
port_data = port['port']
|
||||
|
||||
validate_port_sec = self._should_validate_port_sec_on_update_port(
|
||||
port_data)
|
||||
nsx_lswitch_id, nsx_lport_id = nsx_db.get_nsx_switch_and_port_id(
|
||||
context.session, id)
|
||||
|
||||
@ -3159,25 +3067,9 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
"changes on neutron")
|
||||
with excutils.save_and_reraise_exception(reraise=False):
|
||||
with db_api.CONTEXT_WRITER.using(context):
|
||||
super(NsxV3Plugin, self).update_port(
|
||||
context, id, {'port': original_port})
|
||||
|
||||
# revert allowed address pairs
|
||||
if port_security:
|
||||
orig_pair = original_port.get(
|
||||
addr_apidef.ADDRESS_PAIRS)
|
||||
updated_pair = updated_port.get(
|
||||
addr_apidef.ADDRESS_PAIRS)
|
||||
if orig_pair != updated_pair:
|
||||
self._delete_allowed_address_pairs(context, id)
|
||||
if orig_pair:
|
||||
self._process_create_allowed_address_pairs(
|
||||
context, original_port, orig_pair)
|
||||
|
||||
if sec_grp_updated:
|
||||
self.update_security_group_on_port(
|
||||
context, id, {'port': original_port},
|
||||
updated_port, original_port)
|
||||
self._revert_neutron_port_update(
|
||||
context, id, original_port, updated_port,
|
||||
port_security, sec_grp_updated)
|
||||
# NOTE(arosen): this is to translate between nsxlib
|
||||
# exceptions and the plugin exceptions. This should be
|
||||
# later refactored.
|
||||
|
@ -19,6 +19,7 @@ from oslo_config import cfg
|
||||
from oslo_utils import uuidutils
|
||||
from webob import exc
|
||||
|
||||
from neutron.extensions import securitygroup as secgrp
|
||||
from neutron.tests.unit.db import test_db_base_plugin_v2
|
||||
from neutron.tests.unit.extensions import test_securitygroup
|
||||
|
||||
@ -368,6 +369,24 @@ class NsxPTestPorts(test_db_base_plugin_v2.TestPortsV2,
|
||||
def test_update_port_add_additional_ip(self):
|
||||
self.skipTest('Multiple fixed ips on a port are not supported')
|
||||
|
||||
def test_update_port_delete_ip(self):
|
||||
# This test case overrides the default because the nsx plugin
|
||||
# implements port_security/security groups and it is not allowed
|
||||
# to remove an ip address from a port unless the security group
|
||||
# is first removed.
|
||||
with self.subnet() as subnet:
|
||||
with self.port(subnet=subnet) as port:
|
||||
data = {'port': {'admin_state_up': False,
|
||||
'fixed_ips': [],
|
||||
secgrp.SECURITYGROUPS: []}}
|
||||
req = self.new_update_request('ports',
|
||||
data, port['port']['id'])
|
||||
res = self.deserialize('json', req.get_response(self.api))
|
||||
self.assertEqual(res['port']['admin_state_up'],
|
||||
data['port']['admin_state_up'])
|
||||
self.assertEqual(res['port']['fixed_ips'],
|
||||
data['port']['fixed_ips'])
|
||||
|
||||
|
||||
class NsxPTestSecurityGroup(NsxPPluginTestCaseMixin,
|
||||
test_securitygroup.TestSecurityGroups,
|
||||
@ -525,7 +544,3 @@ class NsxPTestSecurityGroup(NsxPPluginTestCaseMixin,
|
||||
psec.PORTSECURITY),
|
||||
**kwargs)
|
||||
self.assertEqual(res.status_int, exc.HTTPBadRequest.code)
|
||||
|
||||
# Temporarily skip all port related tests until the plugin supports it
|
||||
def test_update_port_with_security_group(self):
|
||||
self.skipTest('Temporarily not supported')
|
||||
|
Loading…
Reference in New Issue
Block a user