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,
|
default=2,
|
||||||
help=_("(Optional) Set the wait time (Seconds) between "
|
help=_("(Optional) Set the wait time (Seconds) between "
|
||||||
"enablement of ECMP.")),
|
"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.
|
# 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())
|
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):
|
def delete_network_bindings(session, network_id):
|
||||||
return (session.query(nsxv_models.NsxvTzNetworkBinding).
|
return (session.query(nsxv_models.NsxvTzNetworkBinding).
|
||||||
filter_by(network_id=network_id).delete())
|
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 netutils
|
||||||
from oslo_utils import uuidutils
|
from oslo_utils import uuidutils
|
||||||
import six
|
import six
|
||||||
|
from six import moves
|
||||||
from sqlalchemy.orm import exc as sa_exc
|
from sqlalchemy.orm import exc as sa_exc
|
||||||
|
|
||||||
from neutron.api import extensions as neutron_extensions
|
from neutron.api import extensions as neutron_extensions
|
||||||
@ -223,6 +224,8 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
|||||||
self._extension_manager.extension_aliases())
|
self._extension_manager.extension_aliases())
|
||||||
self.metadata_proxy_handler = None
|
self.metadata_proxy_handler = None
|
||||||
config.validate_nsxv_config_options()
|
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(
|
neutron_extensions.append_api_extensions_path(
|
||||||
[vmware_nsx.NSX_EXT_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)):
|
if not validators.is_attr_set(network.get(mpnet.SEGMENTS)):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
az_dvs = self._get_network_az_dvs_id(network)
|
||||||
for segment in network[mpnet.SEGMENTS]:
|
for segment in network[mpnet.SEGMENTS]:
|
||||||
network_type = segment.get(pnet.NETWORK_TYPE)
|
network_type = segment.get(pnet.NETWORK_TYPE)
|
||||||
physical_network = segment.get(pnet.PHYSICAL_NETWORK)
|
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)
|
network_type_set = validators.is_attr_set(network_type)
|
||||||
segmentation_id_set = validators.is_attr_set(segmentation_id)
|
segmentation_id_set = validators.is_attr_set(segmentation_id)
|
||||||
physical_network_set = validators.is_attr_set(physical_network)
|
physical_network_set = validators.is_attr_set(physical_network)
|
||||||
az_dvs = self._get_network_az_dvs_id(network)
|
|
||||||
|
|
||||||
err_msg = None
|
err_msg = None
|
||||||
if not network_type_set:
|
if not network_type_set:
|
||||||
@ -611,7 +614,15 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
|||||||
if physical_network_set:
|
if physical_network_set:
|
||||||
self._validate_physical_network(physical_network, az_dvs)
|
self._validate_physical_network(physical_network, az_dvs)
|
||||||
elif network_type == c_utils.NsxVNetworkTypes.VLAN:
|
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:
|
if not segmentation_id_set:
|
||||||
|
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 "
|
err_msg = _("Segmentation ID must be specified with "
|
||||||
"vlan network type")
|
"vlan network type")
|
||||||
elif (segmentation_id_set and
|
elif (segmentation_id_set and
|
||||||
@ -629,16 +640,13 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
|||||||
if physical_network_set:
|
if physical_network_set:
|
||||||
phy_uuid = physical_network
|
phy_uuid = physical_network
|
||||||
else:
|
else:
|
||||||
# use the fvs_id of the availability zone
|
# use the dvs_id of the availability zone
|
||||||
phy_uuid = az_dvs
|
phy_uuid = az_dvs
|
||||||
for binding in bindings:
|
for binding in bindings:
|
||||||
if binding['phy_uuid'] == phy_uuid:
|
if binding['phy_uuid'] == phy_uuid:
|
||||||
raise n_exc.VlanIdInUse(
|
raise n_exc.VlanIdInUse(
|
||||||
vlan_id=segmentation_id,
|
vlan_id=segmentation_id,
|
||||||
physical_network=phy_uuid)
|
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:
|
elif network_type == c_utils.NsxVNetworkTypes.VXLAN:
|
||||||
# Currently unable to set the segmentation id
|
# 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
|
# Use the dvs_id of the availability zone
|
||||||
return self._get_network_az_dvs_id(net_data)
|
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):
|
def create_network(self, context, network):
|
||||||
net_data = network['network']
|
net_data = network['network']
|
||||||
tenant_id = net_data['tenant_id']
|
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
|
backend_network = (not validators.is_attr_set(external) or
|
||||||
validators.is_attr_set(external) and not external)
|
validators.is_attr_set(external) and not external)
|
||||||
network_type = None
|
network_type = None
|
||||||
|
generate_segmenation_id = False
|
||||||
|
lock_vlan_creation = False
|
||||||
if provider_type is not None:
|
if provider_type is not None:
|
||||||
segment = net_data[mpnet.SEGMENTS][0]
|
segment = net_data[mpnet.SEGMENTS][0]
|
||||||
network_type = segment.get(pnet.NETWORK_TYPE)
|
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,
|
# 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.
|
# vlan or vxlan network. For port groups we do not make any changes.
|
||||||
external_backend_network = (
|
external_backend_network = (
|
||||||
external and provider_type is not None and
|
external and provider_type is not None and
|
||||||
network_type != c_utils.NsxVNetworkTypes.PORTGROUP)
|
network_type != c_utils.NsxVNetworkTypes.PORTGROUP)
|
||||||
self._validate_network_qos(net_data, backend_network)
|
|
||||||
# Update the transparent vlan if configured
|
# Update the transparent vlan if configured
|
||||||
|
self._validate_network_qos(net_data, backend_network)
|
||||||
vlt = False
|
vlt = False
|
||||||
if n_utils.is_extension_supported(self, 'vlan-transparent'):
|
if n_utils.is_extension_supported(self, 'vlan-transparent'):
|
||||||
vlt = ext_vlan.get_vlan_transparent(net_data)
|
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 l3_flavors
|
||||||
from neutron.extensions import router_availability_zone
|
from neutron.extensions import router_availability_zone
|
||||||
from neutron.extensions import securitygroup as secgrp
|
from neutron.extensions import securitygroup as secgrp
|
||||||
|
from neutron.plugins.common import utils
|
||||||
from neutron.services.qos import qos_consts
|
from neutron.services.qos import qos_consts
|
||||||
from neutron.tests.unit import _test_extension_portbindings as test_bindings
|
from neutron.tests.unit import _test_extension_portbindings as test_bindings
|
||||||
import neutron.tests.unit.db.test_allowedaddresspairs_db as test_addr_pair
|
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):
|
def test_create_bridge_vlan_network(self):
|
||||||
self._test_create_bridge_network(vlan_id=123)
|
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):
|
def test_create_bridge_vlan_network_outofrange_returns_400(self):
|
||||||
with testlib_api.ExpectedException(
|
with testlib_api.ExpectedException(
|
||||||
webob.exc.HTTPClientError) as ctx_manager:
|
webob.exc.HTTPClientError) as ctx_manager:
|
||||||
|
Loading…
Reference in New Issue
Block a user