Merge "NSX|V: enable plugin to decide on VLAN tag"
This commit is contained in:
commit
bd7e83faff
@ -687,6 +687,12 @@ nsxv_opts = [
|
||||
default=2,
|
||||
help=_("(Optional) Set the wait time (Seconds) between "
|
||||
"enablement of ECMP.")),
|
||||
cfg.ListOpt('network_vlan_ranges',
|
||||
default=[],
|
||||
help=_("List of <DVS MoRef ID>:<vlan_min>:<vlan_max> "
|
||||
"specifying DVS MoRef ID usable for VLAN provider "
|
||||
"networks, as well as ranges of VLAN tags on each "
|
||||
"available for allocation to networks.")),
|
||||
]
|
||||
|
||||
# define the configuration of each NSX-V availability zone.
|
||||
|
@ -528,6 +528,18 @@ def get_network_bindings_by_vlanid_and_physical_net(session, vlan_id,
|
||||
all())
|
||||
|
||||
|
||||
def get_network_bindings_by_ids(session, vlan_id, phy_uuid):
|
||||
return get_network_bindings_by_vlanid_and_physical_net(
|
||||
session, vlan_id, phy_uuid)
|
||||
|
||||
|
||||
def get_network_bindings_by_physical_net(session, phy_uuid):
|
||||
session = session or db.get_reader_session()
|
||||
return (session.query(nsxv_models.NsxvTzNetworkBinding).
|
||||
filter_by(phy_uuid=phy_uuid).
|
||||
all())
|
||||
|
||||
|
||||
def delete_network_bindings(session, network_id):
|
||||
return (session.query(nsxv_models.NsxvTzNetworkBinding).
|
||||
filter_by(network_id=network_id).delete())
|
||||
|
@ -42,6 +42,7 @@ from oslo_utils import excutils
|
||||
from oslo_utils import netutils
|
||||
from oslo_utils import uuidutils
|
||||
import six
|
||||
from six import moves
|
||||
from sqlalchemy.orm import exc as sa_exc
|
||||
|
||||
from neutron.api import extensions as neutron_extensions
|
||||
@ -223,6 +224,8 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
self._extension_manager.extension_aliases())
|
||||
self.metadata_proxy_handler = None
|
||||
config.validate_nsxv_config_options()
|
||||
self._network_vlans = utils.parse_network_vlan_ranges(
|
||||
cfg.CONF.nsxv.network_vlan_ranges)
|
||||
neutron_extensions.append_api_extensions_path(
|
||||
[vmware_nsx.NSX_EXT_PATH])
|
||||
|
||||
@ -592,6 +595,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
if not validators.is_attr_set(network.get(mpnet.SEGMENTS)):
|
||||
return
|
||||
|
||||
az_dvs = self._get_network_az_dvs_id(network)
|
||||
for segment in network[mpnet.SEGMENTS]:
|
||||
network_type = segment.get(pnet.NETWORK_TYPE)
|
||||
physical_network = segment.get(pnet.PHYSICAL_NETWORK)
|
||||
@ -599,7 +603,6 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
network_type_set = validators.is_attr_set(network_type)
|
||||
segmentation_id_set = validators.is_attr_set(segmentation_id)
|
||||
physical_network_set = validators.is_attr_set(physical_network)
|
||||
az_dvs = self._get_network_az_dvs_id(network)
|
||||
|
||||
err_msg = None
|
||||
if not network_type_set:
|
||||
@ -611,9 +614,17 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
if physical_network_set:
|
||||
self._validate_physical_network(physical_network, az_dvs)
|
||||
elif network_type == c_utils.NsxVNetworkTypes.VLAN:
|
||||
# Verify whether the DVSes exist in the backend.
|
||||
if physical_network_set:
|
||||
self._validate_physical_network(physical_network, az_dvs)
|
||||
if not segmentation_id_set:
|
||||
err_msg = _("Segmentation ID must be specified with "
|
||||
"vlan network type")
|
||||
if physical_network_set:
|
||||
if physical_network not in self._network_vlans:
|
||||
err_msg = _("Invalid physical network for "
|
||||
"segmentation ID allocation")
|
||||
else:
|
||||
err_msg = _("Segmentation ID must be specified with "
|
||||
"vlan network type")
|
||||
elif (segmentation_id_set and
|
||||
not utils.is_valid_vlan_tag(segmentation_id)):
|
||||
err_msg = (_("%(segmentation_id)s out of range "
|
||||
@ -629,16 +640,13 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
if physical_network_set:
|
||||
phy_uuid = physical_network
|
||||
else:
|
||||
# use the fvs_id of the availability zone
|
||||
# use the dvs_id of the availability zone
|
||||
phy_uuid = az_dvs
|
||||
for binding in bindings:
|
||||
if binding['phy_uuid'] == phy_uuid:
|
||||
raise n_exc.VlanIdInUse(
|
||||
vlan_id=segmentation_id,
|
||||
physical_network=phy_uuid)
|
||||
# Verify whether the DVSes exist in the backend.
|
||||
if physical_network_set:
|
||||
self._validate_physical_network(physical_network, az_dvs)
|
||||
|
||||
elif network_type == c_utils.NsxVNetworkTypes.VXLAN:
|
||||
# Currently unable to set the segmentation id
|
||||
@ -1006,6 +1014,25 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
# Use the dvs_id of the availability zone
|
||||
return self._get_network_az_dvs_id(net_data)
|
||||
|
||||
def _generate_segment_id(self, context, physical_network, net_data):
|
||||
bindings = nsxv_db.get_network_bindings_by_physical_net(
|
||||
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 = constants.MIN_VLAN_TAG
|
||||
vlan_max = constants.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[mpnet.SEGMENTS][0][pnet.SEGMENTATION_ID] = free_ids[0]
|
||||
|
||||
def create_network(self, context, network):
|
||||
net_data = network['network']
|
||||
tenant_id = net_data['tenant_id']
|
||||
@ -1020,16 +1047,49 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
backend_network = (not validators.is_attr_set(external) or
|
||||
validators.is_attr_set(external) and not external)
|
||||
network_type = None
|
||||
generate_segmenation_id = False
|
||||
lock_vlan_creation = False
|
||||
if provider_type is not None:
|
||||
segment = net_data[mpnet.SEGMENTS][0]
|
||||
network_type = segment.get(pnet.NETWORK_TYPE)
|
||||
if network_type == c_utils.NsxVNetworkTypes.VLAN:
|
||||
physical_network = segment.get(pnet.PHYSICAL_NETWORK)
|
||||
if physical_network in self._network_vlans:
|
||||
lock_vlan_creation = True
|
||||
if not validators.is_attr_set(
|
||||
segment.get(pnet.SEGMENTATION_ID)):
|
||||
generate_segmenation_id = True
|
||||
if lock_vlan_creation:
|
||||
with locking.LockManager.get_lock(
|
||||
'vlan-networking-%s' % physical_network):
|
||||
if generate_segmenation_id:
|
||||
self._generate_segment_id(context, physical_network,
|
||||
net_data)
|
||||
else:
|
||||
segmentation_id = segment.get(pnet.SEGMENTATION_ID)
|
||||
if nsxv_db.get_network_bindings_by_ids(context.session,
|
||||
segmentation_id, physical_network):
|
||||
raise n_exc.VlanIdInUse(
|
||||
vlan_id=segmentation_id,
|
||||
physical_network=physical_network)
|
||||
return self._create_network(context, network, net_data,
|
||||
provider_type, external,
|
||||
backend_network, network_type)
|
||||
else:
|
||||
return self._create_network(context, network, net_data,
|
||||
provider_type, external,
|
||||
backend_network, network_type)
|
||||
|
||||
def _create_network(self, context, network, net_data,
|
||||
provider_type, external, backend_network,
|
||||
network_type):
|
||||
# A external network should be created in the case that we have a flat,
|
||||
# vlan or vxlan network. For port groups we do not make any changes.
|
||||
external_backend_network = (
|
||||
external and provider_type is not None and
|
||||
network_type != c_utils.NsxVNetworkTypes.PORTGROUP)
|
||||
self._validate_network_qos(net_data, backend_network)
|
||||
# Update the transparent vlan if configured
|
||||
self._validate_network_qos(net_data, backend_network)
|
||||
vlt = False
|
||||
if n_utils.is_extension_supported(self, 'vlan-transparent'):
|
||||
vlt = ext_vlan.get_vlan_transparent(net_data)
|
||||
|
@ -29,6 +29,7 @@ from neutron.extensions import l3_ext_gw_mode
|
||||
from neutron.extensions import l3_flavors
|
||||
from neutron.extensions import router_availability_zone
|
||||
from neutron.extensions import securitygroup as secgrp
|
||||
from neutron.plugins.common import utils
|
||||
from neutron.services.qos import qos_consts
|
||||
from neutron.tests.unit import _test_extension_portbindings as test_bindings
|
||||
import neutron.tests.unit.db.test_allowedaddresspairs_db as test_addr_pair
|
||||
@ -270,6 +271,34 @@ class TestNetworksV2(test_plugin.TestNetworksV2, NsxVPluginV2TestCase):
|
||||
def test_create_bridge_vlan_network(self):
|
||||
self._test_create_bridge_network(vlan_id=123)
|
||||
|
||||
def _test_generate_tag(self, vlan_id):
|
||||
net_type = 'vlan'
|
||||
name = 'bridge_net'
|
||||
plugin = directory.get_plugin()
|
||||
plugin._network_vlans = utils.parse_network_vlan_ranges(
|
||||
cfg.CONF.nsxv.network_vlan_ranges)
|
||||
expected = [('subnets', []), ('name', name), ('admin_state_up', True),
|
||||
('status', 'ACTIVE'), ('shared', False),
|
||||
(pnet.NETWORK_TYPE, net_type),
|
||||
(pnet.PHYSICAL_NETWORK, 'dvs-70'),
|
||||
(pnet.SEGMENTATION_ID, vlan_id)]
|
||||
providernet_args = {pnet.NETWORK_TYPE: net_type,
|
||||
pnet.PHYSICAL_NETWORK: 'dvs-70'}
|
||||
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_bridge_vlan_generate(self):
|
||||
cfg.CONF.set_default('network_vlan_ranges', 'dvs-70', 'nsxv')
|
||||
self._test_generate_tag(1)
|
||||
|
||||
def test_create_bridge_vlan_generate_range(self):
|
||||
cfg.CONF.set_default('network_vlan_ranges', 'dvs-70:100:110', 'nsxv')
|
||||
self._test_generate_tag(100)
|
||||
|
||||
def test_create_bridge_vlan_network_outofrange_returns_400(self):
|
||||
with testlib_api.ExpectedException(
|
||||
webob.exc.HTTPClientError) as ctx_manager:
|
||||
|
Loading…
Reference in New Issue
Block a user