Merge "NSX|V3: Support security features for ENS switches"

This commit is contained in:
Zuul 2018-10-15 11:02:01 +00:00 committed by Gerrit Code Review
commit 91100ffd5a
2 changed files with 142 additions and 27 deletions

View File

@ -145,6 +145,8 @@ NSX_V3_FW_DEFAULT_NS_GROUP = 'os_default_section_ns_group'
NSX_V3_DEFAULT_SECTION = 'OS-Default-Section'
NSX_V3_EXCLUDED_PORT_NSGROUP_NAME = 'neutron_excluded_port_nsgroup'
NSX_V3_NON_VIF_PROFILE = 'nsx-default-switch-security-non-vif-profile'
NSX_V3_NON_VIF_ENS_PROFILE = \
'nsx-default-switch-security-non-vif-profile-for-ens'
NSX_V3_SERVER_SSL_PROFILE = 'nsx-default-server-ssl-profile'
NSX_V3_CLIENT_SSL_PROFILE = 'nsx-default-client-ssl-profile'
# Default UUID for the global OS rule
@ -592,6 +594,13 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
NSX_V3_NON_VIF_PROFILE)[0]
self._no_switch_security = profile_client.build_switch_profile_ids(
profile_client, no_switch_security_prof)[0]
if self._ens_psec_supported():
no_switch_security_prof = profile_client.find_by_display_name(
NSX_V3_NON_VIF_ENS_PROFILE)[0]
self._no_switch_security_ens = (
profile_client.build_switch_profile_ids(
profile_client, no_switch_security_prof)[0])
self.server_ssl_profile = None
self.client_ssl_profile = None
# Only create LB profiles when nsxv3 version >= 2.1.0
@ -1075,21 +1084,14 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
transparent_vlan):
provider_data = self._validate_provider_create(context, net_data, az,
transparent_vlan)
physical_net = provider_data['physical_net']
neutron_net_id = net_data.get('id') or uuidutils.generate_uuid()
net_data['id'] = neutron_net_id
if self._is_ens_tz(physical_net):
self._assert_on_ens_with_qos(net_data)
self._ensure_override_ens_with_portsecurity(net_data)
if (provider_data['switch_mode'] ==
self.nsxlib.transport_zone.HOST_SWITCH_MODE_ENS):
if not cfg.CONF.nsx_v3.ens_support:
raise NotImplementedError(_("ENS support is disabled"))
if net_data.get(psec.PORTSECURITY):
raise nsx_exc.NsxENSPortSecurity()
# set the default port security to False
net_data[psec.PORTSECURITY] = False
self._assert_on_ens_with_qos(net_data)
self._validate_ens_net_portsecurity(net_data)
if (provider_data['is_provider_net'] and
provider_data['net_type'] == utils.NsxV3NetworkTypes.NSX_NETWORK):
@ -1395,11 +1397,26 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
err_msg = _("Cannot configure QOS on ENS networks")
raise n_exc.InvalidInput(error_message=err_msg)
def _ensure_override_ens_with_portsecurity(self, net_data):
if cfg.CONF.nsx_v3.disable_port_security_for_ens:
if net_data[psec.PORTSECURITY]:
LOG.warning("Disabling port security for network %s",
net_data['id'])
def _ens_psec_supported(self):
return self.nsxlib.feature_supported(
nsxlib_consts.FEATURE_ENS_WITH_SEC)
def _validate_ens_net_portsecurity(self, net_data):
"""Validate/Update the port security of the new network for ENS TZ"""
if not self._ens_psec_supported():
if cfg.CONF.nsx_v3.disable_port_security_for_ens:
# Override the port-security to False
if net_data[psec.PORTSECURITY]:
LOG.warning("Disabling port security for network %s",
net_data['id'])
# Set the port security to False
net_data[psec.PORTSECURITY] = False
elif net_data.get(psec.PORTSECURITY):
# Port security enabled is not allowed
raise nsx_exc.NsxENSPortSecurity()
else:
# Update the default port security to False if not set
net_data[psec.PORTSECURITY] = False
def _has_active_port(self, context, network_id):
@ -1518,7 +1535,8 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
if psec.PORTSECURITY in net_data:
# do not allow to enable port security on ENS networks
if (net_data[psec.PORTSECURITY] and
not original_net[psec.PORTSECURITY] and is_ens_net):
not original_net[psec.PORTSECURITY] and is_ens_net and
not self._ens_psec_supported()):
raise nsx_exc.NsxENSPortSecurity()
self._process_network_port_security_update(
context, net_data, updated_net)
@ -2336,7 +2354,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
mac_learning_profile_set = True
profiles.append(self._get_port_security_profile_id())
if device_owner == const.DEVICE_OWNER_DHCP:
if (not is_ens_tz_port and
if ((not is_ens_tz_port or self._ens_psec_supported()) and
not cfg.CONF.nsx_v3.native_dhcp_metadata):
profiles.append(self._dhcp_profile)
@ -2353,12 +2371,16 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
profiles.append(qos_profile_id)
# Add mac_learning profile if it exists and is configured
if (not is_ens_tz_port and self._mac_learning_profile and
if ((not is_ens_tz_port or self._ens_psec_supported()) and
self._mac_learning_profile and
(mac_learning_profile_set or
(validators.is_attr_set(port_data.get(mac_ext.MAC_LEARNING)) and
port_data.get(mac_ext.MAC_LEARNING) is True))):
profiles.append(self._mac_learning_profile)
profiles.append(self._no_switch_security)
if is_ens_tz_port:
profiles.append(self._no_switch_security_ens)
else:
profiles.append(self._no_switch_security)
name = self._build_port_name(context, port_data)
nsx_net_id = self._get_network_nsx_id(context, port_data['network_id'])
@ -2432,7 +2454,8 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
context, port_data)
port_data[psec.PORTSECURITY] = port_security
# No port security is allowed if the port belongs to an ENS TZ
if port_security and is_ens_tz_port:
if (port_security and is_ens_tz_port and
not self._ens_psec_supported()):
raise nsx_exc.NsxENSPortSecurity()
self._process_port_port_security_create(
context, port_data, neutron_db)
@ -2854,7 +2877,8 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
err_msg = _("Cannot configure QOS on ENS networks")
raise n_exc.InvalidInput(error_message=err_msg)
if cfg.CONF.nsx_v3.disable_port_security_for_ens:
if (cfg.CONF.nsx_v3.disable_port_security_for_ens and
not self._ens_psec_supported()):
LOG.warning("Disabling port security for network %s",
port_data['network_id'])
port_data[psec.PORTSECURITY] = False
@ -2925,7 +2949,8 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
'disabled')
LOG.error(msg)
raise n_exc.InvalidInput(error_message=msg)
if is_ens_tz_port and not port_data.get(mac_ext.MAC_LEARNING):
if (is_ens_tz_port and not self._ens_psec_supported() and
not port_data.get(mac_ext.MAC_LEARNING)):
msg = _('Cannot disable Mac learning for ENS TZ')
LOG.error(msg)
raise n_exc.InvalidInput(error_message=msg)
@ -2936,7 +2961,8 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
# ATTR_NOT_SPECIFIED
port_data.pop(mac_ext.MAC_LEARNING)
# For a ENZ TZ mac learning is always enabled
if is_ens_tz_port and mac_ext.MAC_LEARNING not in port_data:
if (is_ens_tz_port and not self._ens_psec_supported() and
mac_ext.MAC_LEARNING not in port_data):
# Set the default and add to the DB
port_data[mac_ext.MAC_LEARNING] = True
self._create_mac_learning_state(context, port_data)
@ -3122,7 +3148,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
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:
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
@ -3270,7 +3296,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
# Update the DHCP profile
if (updated_device_owner == const.DEVICE_OWNER_DHCP and
not self._is_ens_tz_net(context, updated_port['network_id']) and
(not is_ens_tz_port or self._ens_psec_supported()) and
not cfg.CONF.nsx_v3.native_dhcp_metadata):
switch_profile_ids.append(self._dhcp_profile)
@ -3293,11 +3319,15 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
validators.is_attr_set(address_pairs) and address_pairs and
psec_is_on)
# Add mac_learning profile if it exists and is configured
if (not is_ens_tz_port and self._mac_learning_profile and
if ((not is_ens_tz_port or self._ens_psec_supported()) and
self._mac_learning_profile and
(mac_learning_profile_set or
updated_port.get(mac_ext.MAC_LEARNING) is True)):
switch_profile_ids.append(self._mac_learning_profile)
switch_profile_ids.append(self._no_switch_security)
if is_ens_tz_port:
switch_profile_ids.append(self._no_switch_security_ens)
else:
switch_profile_ids.append(self._no_switch_security)
try:
self.nsxlib.logical_port.update(
@ -3414,7 +3444,8 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
self._extend_nsx_port_dict_binding(context, updated_port)
mac_learning_state = updated_port.get(mac_ext.MAC_LEARNING)
if mac_learning_state is not None:
if (not mac_learning_state and is_ens_tz_port):
if (not mac_learning_state and is_ens_tz_port and
not self._ens_psec_supported()):
msg = _('Mac learning cannot be disabled with ENS TZ')
LOG.error(msg)
raise n_exc.InvalidInput(error_message=msg)

View File

@ -522,6 +522,24 @@ class TestNetworksV2(test_plugin.TestNetworksV2, NsxV3PluginTestCaseMixin):
self.assertEqual('NsxENSPortSecurity',
res['NeutronError']['type'])
def test_create_ens_network_with_port_sec_supported(self):
cfg.CONF.set_override('ens_support', True, 'nsx_v3')
providernet_args = {psec.PORTSECURITY: True}
with mock.patch("vmware_nsxlib.v3.NsxLib.get_version",
return_value='2.4.0'),\
mock.patch("vmware_nsxlib.v3.core_resources.NsxLibTransportZone."
"get_host_switch_mode", return_value="ENS"),\
mock.patch(
"vmware_nsxlib.v3.core_resources.NsxLibLogicalSwitch.get",
return_value={'transport_zone_id': 'xxx'}):
result = self._create_network(fmt='json', name='ens_net',
admin_state_up=True,
providernet_args=providernet_args,
arg_list=(psec.PORTSECURITY,))
res = self.deserialize('json', result)
# should succeed
self.assertTrue(res['network'][psec.PORTSECURITY])
def test_create_ens_network_disable_default_port_security(self):
cfg.CONF.set_override('ens_support', True, 'nsx_v3')
cfg.CONF.set_override('disable_port_security_for_ens', True, 'nsx_v3')
@ -629,6 +647,30 @@ class TestNetworksV2(test_plugin.TestNetworksV2, NsxV3PluginTestCaseMixin):
self.assertEqual('NsxENSPortSecurity',
res['NeutronError']['type'])
def test_update_ens_network_psec_supported(self):
cfg.CONF.set_override('ens_support', True, 'nsx_v3')
providernet_args = {psec.PORTSECURITY: False}
with mock.patch("vmware_nsxlib.v3.NsxLib.get_version",
return_value='2.4.0'),\
mock.patch("vmware_nsxlib.v3.core_resources.NsxLibTransportZone."
"get_host_switch_mode", return_value="ENS"),\
mock.patch(
"vmware_nsxlib.v3.core_resources.NsxLibLogicalSwitch.get",
return_value={'transport_zone_id': 'xxx'}):
result = self._create_network(fmt='json', name='ens_net',
admin_state_up=True,
providernet_args=providernet_args,
arg_list=(psec.PORTSECURITY,))
net = self.deserialize('json', result)
net_id = net['network']['id']
args = {'network': {psec.PORTSECURITY: True}}
req = self.new_update_request('networks', args,
net_id, fmt='json')
res = self.deserialize('json', req.get_response(self.api))
# should succeed
self.assertTrue(res['network'][psec.PORTSECURITY])
def test_create_transparent_vlan_network(self):
providernet_args = {vlan_apidef.VLANTRANSPARENT: True}
with mock.patch(
@ -1535,6 +1577,25 @@ class TestPortsV2(test_plugin.TestPortsV2, NsxV3PluginTestCaseMixin,
self.assertEqual('NsxENSPortSecurity',
res['NeutronError']['type'])
def test_create_ens_port_with_port_sec_supported(self):
with self.subnet() as subnet,\
mock.patch("vmware_nsxlib.v3.NsxLib.get_version",
return_value='2.4.0'),\
mock.patch("vmware_nsxlib.v3.core_resources.NsxLibTransportZone."
"get_host_switch_mode", return_value="ENS"),\
mock.patch(
"vmware_nsxlib.v3.core_resources.NsxLibLogicalSwitch.get",
return_value={'transport_zone_id': 'xxx'}):
args = {'port': {'network_id': subnet['subnet']['network_id'],
'tenant_id': subnet['subnet']['tenant_id'],
'fixed_ips': [{'subnet_id':
subnet['subnet']['id']}],
psec.PORTSECURITY: True}}
port_req = self.new_create_request('ports', args)
res = self.deserialize('json', port_req.get_response(self.api))
# should succeed
self.assertTrue(res['port'][psec.PORTSECURITY])
def test_update_ens_port(self):
with self.subnet() as subnet,\
mock.patch("vmware_nsxlib.v3.core_resources.NsxLibTransportZone."
@ -1557,6 +1618,29 @@ class TestPortsV2(test_plugin.TestPortsV2, NsxV3PluginTestCaseMixin,
self.assertEqual('NsxENSPortSecurity',
res['NeutronError']['type'])
def test_update_ens_port_psec_supported(self):
with self.subnet() as subnet,\
mock.patch("vmware_nsxlib.v3.NsxLib.get_version",
return_value='2.4.0'),\
mock.patch("vmware_nsxlib.v3.core_resources.NsxLibTransportZone."
"get_host_switch_mode", return_value="ENS"),\
mock.patch(
"vmware_nsxlib.v3.core_resources.NsxLibLogicalSwitch.get",
return_value={'transport_zone_id': 'xxx'}):
args = {'port': {'network_id': subnet['subnet']['network_id'],
'tenant_id': subnet['subnet']['tenant_id'],
'fixed_ips': [{'subnet_id':
subnet['subnet']['id']}],
psec.PORTSECURITY: False}}
port_req = self.new_create_request('ports', args)
port = self.deserialize(self.fmt, port_req.get_response(self.api))
port_id = port['port']['id']
args = {'port': {psec.PORTSECURITY: True}}
req = self.new_update_request('ports', args, port_id)
res = self.deserialize('json', req.get_response(self.api))
# should succeed
self.assertTrue(res['port'][psec.PORTSECURITY])
def test_update_dhcp_port_device_owner(self):
cfg.CONF.set_override('native_dhcp_metadata', True, 'nsx_v3')
with self.subnet():