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:
Adit Sarfaty 2018-11-07 11:47:04 +02:00
parent b024fb250d
commit ff6c595b90
5 changed files with 245 additions and 142 deletions

View File

@ -19,7 +19,37 @@
# based on the features that are ready to be tested. # based on the features that are ready to be tested.
# Begin list of exclusions. # 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).*$" r="$r|(?:tempest\.api\.network\.test_allowed_address_pair\.AllowedAddressPairTestJSON\.test_update_port_with_cidr_address_pair.*)"
export DEVSTACK_GATE_TEMPEST_REGEX="$r" #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"

View File

@ -157,6 +157,96 @@ class NsxPluginV3Base(plugin.NsxPluginBase,
self._get_security_groups_on_port(context, port)) self._get_security_groups_on_port(context, port))
return port_security, has_ip, sgids, psgids 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): def _validate_create_network(self, context, net_data):
"""Validate the parameters of the new network to be created """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: if net.get(pnet.NETWORK_TYPE) in net_types:
return True return True
return False 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)

View File

@ -188,7 +188,7 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
return None return None
try: try:
resource_api.get(name_or_id) resource_api.get(name_or_id, silent=True)
return name_or_id return name_or_id
except nsx_lib_exc.ResourceNotFound: except nsx_lib_exc.ResourceNotFound:
try: try:
@ -516,6 +516,19 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
return address_bindings return address_bindings
def _get_network_nsx_id(self, context, network_id): 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 """Return the NSX segment ID matching the neutron network id
Usually the NSX ID is the same as the neutron ID. The exception is 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( address_bindings = self._build_port_address_bindings(
context, port_data) context, port_data)
device_owner = port_data.get('device_owner') 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: if device_owner and device_owner != l3_db.DEVICE_OWNER_ROUTER_INTF:
vif_id = port_data['id'] vif_id = port_data['id']
attachment_type = nsxlib_consts.ATTACHMENT_VIF
tags = self.nsxpolicy.build_v3_api_version_project_tag( tags = self.nsxpolicy.build_v3_api_version_project_tag(
context.tenant_name) context.tenant_name)
@ -559,7 +573,7 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
tags.extend(self.nsxpolicy.build_v3_api_version_project_tag( tags.extend(self.nsxpolicy.build_v3_api_version_project_tag(
context.tenant_name)) context.tenant_name))
segment_id = self._get_network_nsx_id( segment_id = self._get_network_nsx_segment_id(
context, port_data['network_id']) context, port_data['network_id'])
self.nsxpolicy.segment_port.create_or_overwrite( self.nsxpolicy.segment_port.create_or_overwrite(
name, segment_id, name, segment_id,
@ -567,6 +581,7 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
description=port_data.get('description'), description=port_data.get('description'),
address_bindings=address_bindings, address_bindings=address_bindings,
vif_id=vif_id, vif_id=vif_id,
attachment_type=attachment_type,
tags=tags) tags=tags)
def base_create_port(self, context, port): 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_security_group(context, port_data, sgids)
self._process_port_create_provider_security_group( self._process_port_create_provider_security_group(
context, port_data, psgids) context, port_data, psgids)
#TODO(asarfaty): Handle mac learning
if not is_external_net: if not is_external_net:
try: try:
@ -633,21 +649,20 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
l3_port_check=True, l2gw_port_check=True, l3_port_check=True, l2gw_port_check=True,
force_delete_dhcp=False, force_delete_dhcp=False,
force_delete_vpn=False): force_delete_vpn=False):
# first update neutron (this will perform all types of validations)
port_data = self.get_port(context, port_id) port_data = self.get_port(context, port_id)
segment_id = self._get_network_nsx_id( net_id = port_data['network_id']
context, 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: try:
segment_id = self._get_network_nsx_segment_id(context, net_id)
self.nsxpolicy.segment_port.delete(segment_id, port_data['id']) self.nsxpolicy.segment_port.delete(segment_id, port_data['id'])
except Exception as ex: except Exception as ex:
LOG.error("Failed to delete port %(id)s on NSX backend " LOG.error("Failed to delete port %(id)s on NSX backend "
"due to %(e)s", "due to %(e)s", {'id': port_id, 'e': ex})
{'id': port_data['id'], 'e': ex}) # Do not fail the neutron action
self.disassociate_floatingips(context, port_id)
super(NsxPolicyPlugin, self).delete_port(context, port_id)
def _update_port_on_backend(self, context, lport_id, def _update_port_on_backend(self, context, lport_id,
original_port, updated_port): original_port, updated_port):
@ -655,12 +670,15 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
# Update might evolve with more features # Update might evolve with more features
return self._create_port_on_backend(context, updated_port) 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): with db_api.CONTEXT_WRITER.using(context):
# get the original port, and keep it honest as it is later used # get the original port, and keep it honest as it is later used
# for notifications # 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'] port_data = port['port']
validate_port_sec = self._should_validate_port_sec_on_update_port(
port_data)
is_external_net = self._network_is_external( is_external_net = self._network_is_external(
context, original_port['network_id']) context, original_port['network_id'])
if is_external_net: if is_external_net:
@ -671,8 +689,12 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
self._validate_max_ips_per_port( self._validate_max_ips_per_port(
port_data.get('fixed_ips', []), device_owner) port_data.get('fixed_ips', []), device_owner)
updated_port = super(NsxPolicyPlugin, self).update_port(context, direct_vnic_type = self._validate_port_vnic_type(
id, port) 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, self._extension_manager.process_update_port(context, port_data,
updated_port) updated_port)
# copy values over - except fixed_ips as # copy values over - except fixed_ips as
@ -680,15 +702,47 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
port_data.pop('fixed_ips', None) port_data.pop('fixed_ips', None)
updated_port.update(port_data) 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 # update the port in the backend, only if it exists in the DB
# (i.e not external net) # (i.e not external net)
if not is_external_net: if not is_external_net:
self._update_port_on_backend(context, id, try:
original_port, updated_port) 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 # Make sure the port revision is updated
if 'revision_number' in updated_port: 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 updated_port['revision_number'] = port_model.revision_number
# Notifications must be sent after the above transaction is complete # 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) registry.notify(resources.PORT, events.AFTER_UPDATE, self, **kwargs)
return updated_port return updated_port
def get_port(self, context, id, fields=None): def get_port(self, context, port_id, fields=None):
port = super(NsxPolicyPlugin, self).get_port(context, id, fields=None) port = super(NsxPolicyPlugin, self).get_port(
context, port_id, fields=None)
if 'id' in port: if 'id' in port:
port_model = self._get_port(context, port['id']) port_model = self._get_port(context, port['id'])
resource_extend.apply_funcs('ports', port, port_model) 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) self._validate_interface_address_scope(context, router_db, info)
subnet = self.get_subnet(context, info['subnet_ids'][0]) 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 # TODO(annak): Validate TZ
try: try:
# This is always an overwrite call # This is always an overwrite call

View File

@ -28,9 +28,7 @@ from neutron_lib.api import faults
from neutron_lib.api.validators import availability_zone as az_validator 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 api as db_api
from neutron_lib.db import utils as db_utils 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 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 constants as plugin_const
from neutron_lib.plugins import directory from neutron_lib.plugins import directory
from neutron_lib import rpc as n_rpc from neutron_lib import rpc as n_rpc
@ -2786,86 +2784,6 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
is_delete=True) is_delete=True)
super(NsxV3Plugin, self).delete_port(context, port_id) 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): def _get_resource_type_for_device_id(self, device_owner, device_id):
if device_owner in const.ROUTER_INTERFACE_OWNERS: if device_owner in const.ROUTER_INTERFACE_OWNERS:
return 'os-router-uuid' return 'os-router-uuid'
@ -3052,17 +2970,6 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
return policy_id, profile_id return policy_id, profile_id
def update_port(self, context, id, port): 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): with db_api.CONTEXT_WRITER.using(context):
# get the original port, and keep it honest as it is later used # get the original port, and keep it honest as it is later used
# for notifications # for notifications
@ -3070,7 +2977,8 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
self._extend_get_port_dict_qos_and_binding(context, original_port) self._extend_get_port_dict_qos_and_binding(context, original_port)
self._remove_provider_security_groups_from_list(original_port) self._remove_provider_security_groups_from_list(original_port)
port_data = port['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( nsx_lswitch_id, nsx_lport_id = nsx_db.get_nsx_switch_and_port_id(
context.session, id) context.session, id)
@ -3159,25 +3067,9 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
"changes on neutron") "changes on neutron")
with excutils.save_and_reraise_exception(reraise=False): with excutils.save_and_reraise_exception(reraise=False):
with db_api.CONTEXT_WRITER.using(context): with db_api.CONTEXT_WRITER.using(context):
super(NsxV3Plugin, self).update_port( self._revert_neutron_port_update(
context, id, {'port': original_port}) context, id, original_port, updated_port,
port_security, sec_grp_updated)
# 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)
# NOTE(arosen): this is to translate between nsxlib # NOTE(arosen): this is to translate between nsxlib
# exceptions and the plugin exceptions. This should be # exceptions and the plugin exceptions. This should be
# later refactored. # later refactored.

View File

@ -19,6 +19,7 @@ from oslo_config import cfg
from oslo_utils import uuidutils from oslo_utils import uuidutils
from webob import exc 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.db import test_db_base_plugin_v2
from neutron.tests.unit.extensions import test_securitygroup 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): def test_update_port_add_additional_ip(self):
self.skipTest('Multiple fixed ips on a port are not supported') 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, class NsxPTestSecurityGroup(NsxPPluginTestCaseMixin,
test_securitygroup.TestSecurityGroups, test_securitygroup.TestSecurityGroups,
@ -525,7 +544,3 @@ class NsxPTestSecurityGroup(NsxPPluginTestCaseMixin,
psec.PORTSECURITY), psec.PORTSECURITY),
**kwargs) **kwargs)
self.assertEqual(res.status_int, exc.HTTPBadRequest.code) 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')