Enable configuration to decide on vlan tag per TZ
NSX|V3: This feature will enable an admin user to configure a range of VLAN IDs per VLAN Transport Zone, so when they create a VLAN, the VLAN tag will be set accordingly. The configuration is being done in the nsx.ini file, under the relevant section for nsx-v3, the admin will note the tz-id, with either a predefined range(s) (min/max values) or only the transport zone itself (which means that any value can be chosen). The admin user will create the network noting “provider:physical_network”, if they select a VLAN ID, than it will be used, if not - one will be chosen according to the configuration mentioned above. New configuration variable in nsx.ini under nsx_v3: network_vlan_ranges network_vlan_ranges=<TZ_UUID>:<min_val>:<max_val> Change-Id: Id202ca28bda44286deacb5c9969ffd92aa564a90 Signed-off-by: Michal Kelner Mishali <mkelnermishal@vmware.com>
This commit is contained in:
parent
895c25d2df
commit
ab622863d5
@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
prelude: >
|
||||||
|
The NSX-V3 plugin can decide on the VLAN tag for a provider network.
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
The NSX-V3 plugin can decide on the VLAN tag for a provider network,
|
||||||
|
according to pre-defined configuration set per transport zone UUID,
|
||||||
|
noting a specific range or letting the plugin decide according to
|
||||||
|
min/max constants.
|
@ -438,6 +438,12 @@ nsx_v3_opts = [
|
|||||||
default=False,
|
default=False,
|
||||||
help=_("(Optional) Indicates whether ENS transport zones can "
|
help=_("(Optional) Indicates whether ENS transport zones can "
|
||||||
"be used")),
|
"be used")),
|
||||||
|
cfg.ListOpt('network_vlan_ranges',
|
||||||
|
default=[],
|
||||||
|
help=_("List of <TZ UUID>:<vlan_min>:<vlan_max> "
|
||||||
|
"specifying Transport Zone UUID usable for VLAN "
|
||||||
|
"provider networks, as well as ranges of VLAN "
|
||||||
|
"tags on each available for allocation to networks.")),
|
||||||
]
|
]
|
||||||
|
|
||||||
DEFAULT_STATUS_CHECK_INTERVAL = 2000
|
DEFAULT_STATUS_CHECK_INTERVAL = 2000
|
||||||
|
@ -51,6 +51,13 @@ def get_network_bindings(session, network_id):
|
|||||||
all())
|
all())
|
||||||
|
|
||||||
|
|
||||||
|
def get_network_bindings_by_phy_uuid(session, phy_uuid):
|
||||||
|
session = session or db.get_reader_session()
|
||||||
|
return (session.query(nsx_models.TzNetworkBinding).
|
||||||
|
filter_by(phy_uuid=phy_uuid).
|
||||||
|
all())
|
||||||
|
|
||||||
|
|
||||||
def get_network_bindings_by_vlanid_and_physical_net(session, vlan_id,
|
def get_network_bindings_by_vlanid_and_physical_net(session, vlan_id,
|
||||||
phy_uuid):
|
phy_uuid):
|
||||||
session = session or db.get_reader_session()
|
session = session or db.get_reader_session()
|
||||||
|
@ -81,6 +81,7 @@ from oslo_log import log
|
|||||||
from oslo_utils import excutils
|
from oslo_utils import excutils
|
||||||
from oslo_utils import importutils
|
from oslo_utils import importutils
|
||||||
from oslo_utils import uuidutils
|
from oslo_utils import uuidutils
|
||||||
|
from six import moves
|
||||||
from sqlalchemy import exc as sql_exc
|
from sqlalchemy import exc as sql_exc
|
||||||
import webob.exc
|
import webob.exc
|
||||||
|
|
||||||
@ -249,6 +250,8 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
self.cfg_group = 'nsx_v3' # group name for nsx_v3 section in nsx.ini
|
self.cfg_group = 'nsx_v3' # group name for nsx_v3 section in nsx.ini
|
||||||
self.tier0_groups_dict = {}
|
self.tier0_groups_dict = {}
|
||||||
|
|
||||||
|
self._network_vlans = n_utils.parse_network_vlan_ranges(
|
||||||
|
cfg.CONF.nsx_v3.network_vlan_ranges)
|
||||||
# Initialize the network availability zones, which will be used only
|
# Initialize the network availability zones, which will be used only
|
||||||
# when native_dhcp_metadata is True
|
# when native_dhcp_metadata is True
|
||||||
self.init_availability_zones()
|
self.init_availability_zones()
|
||||||
@ -803,9 +806,9 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
|
|
||||||
# Validate VLAN id
|
# Validate VLAN id
|
||||||
if not vlan_id:
|
if not vlan_id:
|
||||||
err_msg = (_('Segmentation ID must be specified with %s '
|
vlan_id = self._generate_segment_id(context,
|
||||||
'network type') %
|
physical_net,
|
||||||
utils.NsxV3NetworkTypes.VLAN)
|
network_data)
|
||||||
elif not n_utils.is_valid_vlan_tag(vlan_id):
|
elif not n_utils.is_valid_vlan_tag(vlan_id):
|
||||||
err_msg = (_('Segmentation ID %(segmentation_id)s out of '
|
err_msg = (_('Segmentation ID %(segmentation_id)s out of '
|
||||||
'range (%(min_id)s through %(max_id)s)') %
|
'range (%(min_id)s through %(max_id)s)') %
|
||||||
@ -1043,6 +1046,26 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
return (bindings[0].binding_type ==
|
return (bindings[0].binding_type ==
|
||||||
utils.NsxV3NetworkTypes.NSX_NETWORK)
|
utils.NsxV3NetworkTypes.NSX_NETWORK)
|
||||||
|
|
||||||
|
def _generate_segment_id(self, context, physical_network, net_data):
|
||||||
|
bindings = nsx_db.get_network_bindings_by_phy_uuid(
|
||||||
|
context.session, physical_network)
|
||||||
|
vlan_ranges = self._network_vlans.get(physical_network, [])
|
||||||
|
if vlan_ranges:
|
||||||
|
vlan_ids = set()
|
||||||
|
for vlan_min, vlan_max in vlan_ranges:
|
||||||
|
vlan_ids |= set(moves.range(vlan_min, vlan_max + 1))
|
||||||
|
else:
|
||||||
|
vlan_min = const.MIN_VLAN_TAG
|
||||||
|
vlan_max = const.MAX_VLAN_TAG
|
||||||
|
vlan_ids = set(moves.range(vlan_min, vlan_max + 1))
|
||||||
|
used_ids_in_range = set([binding.vlan_id for binding in bindings
|
||||||
|
if binding.vlan_id in vlan_ids])
|
||||||
|
free_ids = list(vlan_ids ^ used_ids_in_range)
|
||||||
|
if len(free_ids) == 0:
|
||||||
|
raise n_exc.NoNetworkAvailable()
|
||||||
|
net_data[pnet.SEGMENTATION_ID] = free_ids[0]
|
||||||
|
return net_data[pnet.SEGMENTATION_ID]
|
||||||
|
|
||||||
def create_network(self, context, network):
|
def create_network(self, context, network):
|
||||||
net_data = network['network']
|
net_data = network['network']
|
||||||
external = net_data.get(extnet_apidef.EXTERNAL)
|
external = net_data.get(extnet_apidef.EXTERNAL)
|
||||||
|
@ -19,6 +19,7 @@ from neutron.db import models_v2
|
|||||||
from neutron.extensions import address_scope
|
from neutron.extensions import address_scope
|
||||||
from neutron.extensions import l3
|
from neutron.extensions import l3
|
||||||
from neutron.extensions import securitygroup as secgrp
|
from neutron.extensions import securitygroup as secgrp
|
||||||
|
from neutron.plugins.common import utils as n_utils
|
||||||
from neutron.tests.unit import _test_extension_portbindings as test_bindings
|
from neutron.tests.unit import _test_extension_portbindings as test_bindings
|
||||||
from neutron.tests.unit.db import test_db_base_plugin_v2 as test_plugin
|
from neutron.tests.unit.db import test_db_base_plugin_v2 as test_plugin
|
||||||
from neutron.tests.unit.extensions import test_address_scope
|
from neutron.tests.unit.extensions import test_address_scope
|
||||||
@ -29,6 +30,7 @@ from neutron.tests.unit.extensions \
|
|||||||
import test_l3_ext_gw_mode as test_ext_gw_mode
|
import test_l3_ext_gw_mode as test_ext_gw_mode
|
||||||
from neutron.tests.unit.scheduler \
|
from neutron.tests.unit.scheduler \
|
||||||
import test_dhcp_agent_scheduler as test_dhcpagent
|
import test_dhcp_agent_scheduler as test_dhcpagent
|
||||||
|
from neutron.tests.unit import testlib_api
|
||||||
from neutron_lib.api.definitions import external_net as extnet_apidef
|
from neutron_lib.api.definitions import external_net as extnet_apidef
|
||||||
from neutron_lib.api.definitions import extraroute as xroute_apidef
|
from neutron_lib.api.definitions import extraroute as xroute_apidef
|
||||||
from neutron_lib.api.definitions import l3_ext_gw_mode as l3_egm_apidef
|
from neutron_lib.api.definitions import l3_ext_gw_mode as l3_egm_apidef
|
||||||
@ -546,6 +548,57 @@ class TestNetworksV2(test_plugin.TestNetworksV2, NsxV3PluginTestCaseMixin):
|
|||||||
data = self.deserialize('json', result)
|
data = self.deserialize('json', result)
|
||||||
self.assertEqual('vlan', data['network'].get(pnet.NETWORK_TYPE))
|
self.assertEqual('vlan', data['network'].get(pnet.NETWORK_TYPE))
|
||||||
|
|
||||||
|
def _test_generate_tag(self, vlan_id):
|
||||||
|
net_type = 'vlan'
|
||||||
|
name = 'phys_net'
|
||||||
|
plugin = directory.get_plugin()
|
||||||
|
plugin._network_vlans = n_utils.parse_network_vlan_ranges(
|
||||||
|
cfg.CONF.nsx_v3.network_vlan_ranges)
|
||||||
|
expected = [('subnets', []), ('name', name),
|
||||||
|
('admin_state_up', True),
|
||||||
|
('status', 'ACTIVE'),
|
||||||
|
('shared', False),
|
||||||
|
(pnet.NETWORK_TYPE, net_type),
|
||||||
|
(pnet.PHYSICAL_NETWORK,
|
||||||
|
'fb69d878-958e-4f32-84e4-50286f26226b'),
|
||||||
|
(pnet.SEGMENTATION_ID, vlan_id)]
|
||||||
|
providernet_args = {pnet.NETWORK_TYPE: net_type,
|
||||||
|
pnet.PHYSICAL_NETWORK:
|
||||||
|
'fb69d878-958e-4f32-84e4-50286f26226b'}
|
||||||
|
|
||||||
|
with mock.patch('vmware_nsxlib.v3.core_resources.NsxLibTransportZone.'
|
||||||
|
'get_transport_type', return_value='VLAN'):
|
||||||
|
with self.network(name=name, providernet_args=providernet_args,
|
||||||
|
arg_list=(pnet.NETWORK_TYPE,
|
||||||
|
pnet.PHYSICAL_NETWORK)) as net:
|
||||||
|
for k, v in expected:
|
||||||
|
self.assertEqual(net['network'][k], v)
|
||||||
|
|
||||||
|
def test_create_phys_vlan_generate(self):
|
||||||
|
cfg.CONF.set_override('network_vlan_ranges',
|
||||||
|
'fb69d878-958e-4f32-84e4-50286f26226b',
|
||||||
|
'nsx_v3')
|
||||||
|
self._test_generate_tag(1)
|
||||||
|
|
||||||
|
def test_create_phys_vlan_generate_range(self):
|
||||||
|
cfg.CONF.set_override('network_vlan_ranges',
|
||||||
|
'fb69d878-958e-4f32-84e4-'
|
||||||
|
'50286f26226b:100:110',
|
||||||
|
'nsx_v3')
|
||||||
|
self._test_generate_tag(100)
|
||||||
|
|
||||||
|
def test_create_phys_vlan_network_outofrange_returns_503(self):
|
||||||
|
cfg.CONF.set_override('network_vlan_ranges',
|
||||||
|
'fb69d878-958e-4f32-84e4-'
|
||||||
|
'50286f26226b:9:10',
|
||||||
|
'nsx_v3')
|
||||||
|
self._test_generate_tag(9)
|
||||||
|
self._test_generate_tag(10)
|
||||||
|
with testlib_api.ExpectedException(exc.HTTPClientError) as ctx_manager:
|
||||||
|
self._test_generate_tag(11)
|
||||||
|
|
||||||
|
self.assertEqual(ctx_manager.exception.code, 503)
|
||||||
|
|
||||||
|
|
||||||
class TestSubnetsV2(test_plugin.TestSubnetsV2, NsxV3PluginTestCaseMixin):
|
class TestSubnetsV2(test_plugin.TestSubnetsV2, NsxV3PluginTestCaseMixin):
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user