NSX|v3: configure additional switching profiles per AZ

New configuration option is added to the nsx-v3 plugin: switching_profiles.
It will contain a list of switching profiles uuids that will be added to
NSX ports created by neutron.
The configuration is global or per availability zone.
In case the port should contain a different profile of some type,
the port-specific profile will be used, since the backend takes the last
one of each type.

Change-Id: Ifa1dba2250b224201e6f81816feb536a35b642a5
This commit is contained in:
Adit Sarfaty 2017-08-09 13:24:33 +03:00
parent 81753f0584
commit ef3db6aba7
6 changed files with 89 additions and 2 deletions

View File

@ -0,0 +1,8 @@
---
prelude: >
The nsx-v3 plugin can add pre-configured switching profiles to new nsx
ports. The configuration can also be done per availability zone.
features:
- |
The nsx-v3 plugin can add pre-configured switching profiles to new nsx
ports. The configuration can also be done per availability zone.

View File

@ -434,6 +434,11 @@ nsx_v3_opts = [
help=_("This is the scope of the tag that will be used for " help=_("This is the scope of the tag that will be used for "
"finding the objects uuids on the NSX during plugin " "finding the objects uuids on the NSX during plugin "
"init.")), "init.")),
cfg.ListOpt('switching_profiles',
default=[],
help=_("Optional parameter defining a list switching profiles "
"uuids that will be attached to all neutron created "
"nsx ports.")),
] ]
@ -792,6 +797,9 @@ nsxv3_az_opts = [
"transport zone that will be used for bridging between " "transport zone that will be used for bridging between "
"Neutron networks, if no physical network has been " "Neutron networks, if no physical network has been "
"specified")), "specified")),
cfg.ListOpt('switching_profiles',
help=_("(Optional) list switching profiles uuids that will be "
"attached to all neutron created nsx ports.")),
] ]
# Register the configuration options # Register the configuration options

View File

@ -19,7 +19,7 @@ from vmware_nsx._i18n import _
from vmware_nsx.common import availability_zones as common_az from vmware_nsx.common import availability_zones as common_az
from vmware_nsx.common import config from vmware_nsx.common import config
from vmware_nsx.common import exceptions as nsx_exc from vmware_nsx.common import exceptions as nsx_exc
from vmware_nsxlib.v3 import core_resources
DEFAULT_NAME = common_az.DEFAULT_NAME DEFAULT_NAME = common_az.DEFAULT_NAME
@ -74,6 +74,10 @@ class NsxV3AvailabilityZone(common_az.ConfiguredAvailabilityZone):
if self.default_vlan_tz is None: if self.default_vlan_tz is None:
self.default_vlan_tz = cfg.CONF.nsx_v3.default_vlan_tz self.default_vlan_tz = cfg.CONF.nsx_v3.default_vlan_tz
self.switching_profiles = az_info.get('switching_profiles')
if self.switching_profiles is None:
self.switching_profiles = cfg.CONF.nsx_v3.switching_profiles
def init_default_az(self): def init_default_az(self):
# use the default configuration # use the default configuration
self.metadata_proxy = cfg.CONF.nsx_v3.metadata_proxy self.metadata_proxy = cfg.CONF.nsx_v3.metadata_proxy
@ -83,6 +87,7 @@ class NsxV3AvailabilityZone(common_az.ConfiguredAvailabilityZone):
self.nameservers = cfg.CONF.nsx_v3.nameservers self.nameservers = cfg.CONF.nsx_v3.nameservers
self.default_overlay_tz = cfg.CONF.nsx_v3.default_overlay_tz self.default_overlay_tz = cfg.CONF.nsx_v3.default_overlay_tz
self.default_vlan_tz = cfg.CONF.nsx_v3.default_vlan_tz self.default_vlan_tz = cfg.CONF.nsx_v3.default_vlan_tz
self.switching_profiles = cfg.CONF.nsx_v3.switching_profiles
def translate_configured_names_to_uuids(self, nsxlib): def translate_configured_names_to_uuids(self, nsxlib):
# Mandatory configurations (in AZ or inherited from global values) # Mandatory configurations (in AZ or inherited from global values)
@ -154,6 +159,18 @@ class NsxV3AvailabilityZone(common_az.ConfiguredAvailabilityZone):
else: else:
self._default_vlan_tz_uuid = None self._default_vlan_tz_uuid = None
# switching profiles are already uuids, but we need to translate
# those to objects
profiles = []
if self.switching_profiles:
for profile in self.switching_profiles:
nsx_profile = nsxlib.switching_profile.get(profile)
# TODO(asarfaty): skip or alert on unsupported types
profiles.append(core_resources.SwitchingProfileTypeId(
nsx_profile.get('resource_type'),
nsx_profile.get('id')))
self.switching_profiles_objs = profiles
class NsxV3AvailabilityZones(common_az.ConfiguredAvailabilityZones): class NsxV3AvailabilityZones(common_az.ConfiguredAvailabilityZones):

View File

@ -1626,6 +1626,14 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
vif_uuid = port_data['id'] vif_uuid = port_data['id']
profiles = [] profiles = []
# Add availability zone profiles first (so that specific profiles will
# override them)
port_az = self.get_network_az_by_net_id(context,
port_data['network_id'])
if port_az.switching_profiles_objs:
profiles.extend(port_az.switching_profiles_objs)
mac_learning_profile_set = False mac_learning_profile_set = False
if psec_is_on: if psec_is_on:
address_pairs = port_data.get(addr_pair.ADDRESS_PAIRS) address_pairs = port_data.get(addr_pair.ADDRESS_PAIRS)
@ -2420,6 +2428,14 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
updated_port.get(ext_sg.SECURITYGROUPS, []) + updated_port.get(ext_sg.SECURITYGROUPS, []) +
updated_port.get(provider_sg.PROVIDER_SECURITYGROUPS, [])) updated_port.get(provider_sg.PROVIDER_SECURITYGROUPS, []))
# Add availability zone profiles first (so that specific profiles will
# override them)
port_az = self.get_network_az_by_net_id(context,
updated_port['network_id'])
if port_az.switching_profiles_objs:
switch_profile_ids = (port_az.switching_profiles_objs +
switch_profile_ids)
# Update the DHCP profile # Update the DHCP profile
if updated_device_owner == const.DEVICE_OWNER_DHCP: if updated_device_owner == const.DEVICE_OWNER_DHCP:
switch_profile_ids.append(self._dhcp_profile) switch_profile_ids.append(self._dhcp_profile)

View File

@ -40,6 +40,7 @@ class Nsxv3AvailabilityZonesTestCase(base.BaseTestCase):
"native_metadata_route", "1.1.1.1", group="nsx_v3") "native_metadata_route", "1.1.1.1", group="nsx_v3")
cfg.CONF.set_override("dns_domain", "xxx.com", group="nsx_v3") cfg.CONF.set_override("dns_domain", "xxx.com", group="nsx_v3")
cfg.CONF.set_override("nameservers", ["10.1.1.1"], group="nsx_v3") cfg.CONF.set_override("nameservers", ["10.1.1.1"], group="nsx_v3")
cfg.CONF.set_override("switching_profiles", ["uuid1"], group="nsx_v3")
def _config_az(self, def _config_az(self,
metadata_proxy="metadata_proxy1", metadata_proxy="metadata_proxy1",
@ -48,7 +49,8 @@ class Nsxv3AvailabilityZonesTestCase(base.BaseTestCase):
dns_domain="aaa.com", dns_domain="aaa.com",
nameservers=["20.1.1.1"], nameservers=["20.1.1.1"],
default_overlay_tz='otz', default_overlay_tz='otz',
default_vlan_tz='vtz'): default_vlan_tz='vtz',
switching_profiles=["uuid2"]):
if metadata_proxy is not None: if metadata_proxy is not None:
cfg.CONF.set_override("metadata_proxy", metadata_proxy, cfg.CONF.set_override("metadata_proxy", metadata_proxy,
group=self.group_name) group=self.group_name)
@ -71,6 +73,9 @@ class Nsxv3AvailabilityZonesTestCase(base.BaseTestCase):
if default_vlan_tz is not None: if default_vlan_tz is not None:
cfg.CONF.set_override("default_vlan_tz", default_vlan_tz, cfg.CONF.set_override("default_vlan_tz", default_vlan_tz,
group=self.group_name) group=self.group_name)
if switching_profiles is not None:
cfg.CONF.set_override("switching_profiles", switching_profiles,
group=self.group_name)
def test_simple_availability_zone(self): def test_simple_availability_zone(self):
self._config_az() self._config_az()
@ -83,6 +88,7 @@ class Nsxv3AvailabilityZonesTestCase(base.BaseTestCase):
self.assertEqual(["20.1.1.1"], az.nameservers) self.assertEqual(["20.1.1.1"], az.nameservers)
self.assertEqual("otz", az.default_overlay_tz) self.assertEqual("otz", az.default_overlay_tz)
self.assertEqual("vtz", az.default_vlan_tz) self.assertEqual("vtz", az.default_vlan_tz)
self.assertEqual(["uuid2"], az.switching_profiles)
def test_missing_group_section(self): def test_missing_group_section(self):
self.assertRaises( self.assertRaises(
@ -120,3 +126,8 @@ class Nsxv3AvailabilityZonesTestCase(base.BaseTestCase):
self._config_az(nameservers=None) self._config_az(nameservers=None)
az = nsx_az.NsxV3AvailabilityZone(self.az_name) az = nsx_az.NsxV3AvailabilityZone(self.az_name)
self.assertEqual(["10.1.1.1"], az.nameservers) self.assertEqual(["10.1.1.1"], az.nameservers)
def test_availability_zone_missing_profiles(self):
self._config_az(switching_profiles=None)
az = nsx_az.NsxV3AvailabilityZone(self.az_name)
self.assertEqual(["uuid1"], az.switching_profiles)

View File

@ -61,6 +61,7 @@ PLUGIN_NAME = 'vmware_nsx.plugin.NsxV3Plugin'
NSX_TZ_NAME = 'default transport zone' NSX_TZ_NAME = 'default transport zone'
NSX_DHCP_PROFILE_ID = 'default dhcp profile' NSX_DHCP_PROFILE_ID = 'default dhcp profile'
NSX_METADATA_PROXY_ID = 'default metadata proxy' NSX_METADATA_PROXY_ID = 'default metadata proxy'
NSX_SWITCH_PROFILE = 'dummy switch profile'
def _mock_create_firewall_rules(*args): def _mock_create_firewall_rules(*args):
@ -298,6 +299,8 @@ class TestPortsV2(test_plugin.TestPortsV2, NsxV3PluginTestCaseMixin,
HAS_PORT_FILTER = True HAS_PORT_FILTER = True
def setUp(self): def setUp(self):
cfg.CONF.set_override('switching_profiles', [NSX_SWITCH_PROFILE],
'nsx_v3')
super(TestPortsV2, self).setUp() super(TestPortsV2, self).setUp()
self.plugin = directory.get_plugin() self.plugin = directory.get_plugin()
self.ctx = context.get_admin_context() self.ctx = context.get_admin_context()
@ -537,6 +540,30 @@ class TestPortsV2(test_plugin.TestPortsV2, NsxV3PluginTestCaseMixin,
self.assertEqual(exc.HTTPBadRequest.code, self.assertEqual(exc.HTTPBadRequest.code,
res.status_int) res.status_int)
def test_create_port_with_switching_profiles(self):
"""Tests that nsx ports get the configures switching profiles"""
self.plugin = directory.get_plugin()
with self.network() as network:
data = {'port': {
'network_id': network['network']['id'],
'tenant_id': self._tenant_id,
'name': 'p1',
'admin_state_up': True,
'device_id': 'fake_device',
'device_owner': 'fake_owner',
'fixed_ips': [],
'mac_address': '00:00:00:00:00:01'}
}
with mock.patch.object(self.plugin.nsxlib.logical_port, 'create',
return_value={'id': 'fake'}) as nsx_create:
self.plugin.create_port(self.ctx, data)
expected_prof = self.plugin.get_default_az().\
switching_profiles_objs[0]
actual_profs = nsx_create.call_args[1]['switch_profile_ids']
# the ports switching profiles should start with the
# configured one
self.assertEqual(expected_prof, actual_profs[0])
def test_update_port_update_ip_address_only(self): def test_update_port_update_ip_address_only(self):
self.skipTest('Multiple fixed ips on a port are not supported') self.skipTest('Multiple fixed ips on a port are not supported')