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.
|
# 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"
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -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')
|
|
||||||
|
Loading…
Reference in New Issue
Block a user