[NSX-v]: Add support for multiple DVS for VLAN network type
This patch proposes to use the physical_network attribute of provider network API for network type VLAN to consume comma ',' separated DVS-IDs. This way, admin can provide the multiple DVS-IDs associated with a single VLAN network in the provider net create command. This will result in creation of port groups on each of the DVSes. If no DVS-ID is provided, we pick the DVS-ID from conf file. If DVS-ID is provided in the physical network, in order for the instances on these networks to grab DHCP addresses, the DVS-ID of the edge/mgmt cluster must be explicitly provided in the CLI along with other DVS-IDs. Usage: neutron net-create fancy-network --provider:network_type=vlan --provider:segmentation_id=100 --provider:physical_network=dvs-10,dvs-20 Closes-Bug: #1559320 Change-Id: Ifb10e3f34bf0c55d2d7710e804b5de995ec954bd
This commit is contained in:
parent
c946784134
commit
c138dcfdda
@ -55,10 +55,12 @@ def add_network_binding(session, network_id, binding_type, phy_uuid, vlan_id):
|
||||
return binding
|
||||
|
||||
|
||||
def add_neutron_nsx_network_mapping(session, neutron_id, nsx_switch_id):
|
||||
def add_neutron_nsx_network_mapping(session, neutron_id, nsx_switch_id,
|
||||
dvs_id=None):
|
||||
with session.begin(subtransactions=True):
|
||||
mapping = nsx_models.NeutronNsxNetworkMapping(
|
||||
neutron_id=neutron_id, nsx_id=nsx_switch_id)
|
||||
neutron_id=neutron_id, nsx_id=nsx_switch_id,
|
||||
dvs_id=dvs_id)
|
||||
session.add(mapping)
|
||||
return mapping
|
||||
|
||||
@ -120,6 +122,18 @@ def get_nsx_switch_ids(session, neutron_id):
|
||||
neutron_id=neutron_id)]
|
||||
|
||||
|
||||
def get_nsx_switch_id_for_dvs(session, neutron_id, dvs_id):
|
||||
"""Retrieve the NSX switch ID for a given DVS ID and neutron network."""
|
||||
try:
|
||||
mapping = (session.query(nsx_models.NeutronNsxNetworkMapping).
|
||||
filter_by(neutron_id=neutron_id,
|
||||
dvs_id=dvs_id).one())
|
||||
return mapping['nsx_id']
|
||||
except exc.NoResultFound:
|
||||
LOG.debug("NSX switch for dvs-id: %s not yet stored in Neutron DB",
|
||||
dvs_id)
|
||||
|
||||
|
||||
def get_net_ids(session, nsx_id):
|
||||
return [mapping['neutron_id'] for mapping in
|
||||
session.query(nsx_models.NeutronNsxNetworkMapping).filter_by(
|
||||
|
@ -1 +1 @@
|
||||
3e4dccfe6fb4
|
||||
967462f585e1
|
||||
|
@ -0,0 +1,33 @@
|
||||
# Copyright 2016 VMware, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""add dvs_id column to neutron_nsx_network_mappings
|
||||
|
||||
Revision ID: 967462f585e1
|
||||
Revises: 3e4dccfe6fb4
|
||||
Create Date: 2016-02-23 18:22:01.998540
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '967462f585e1'
|
||||
down_revision = '3e4dccfe6fb4'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.add_column('neutron_nsx_network_mappings',
|
||||
sa.Column('dvs_id', sa.String(36), nullable=True))
|
@ -67,12 +67,18 @@ class NeutronNsxNetworkMapping(model_base.BASEV2):
|
||||
|
||||
Because of chained logical switches more than one mapping might exist
|
||||
for a single Neutron network.
|
||||
For a VLAN network, one neutron network may map to multiple logical
|
||||
switches(port groups) created on multiple DVSes in the backend for
|
||||
NSX-V plugin. DVS-ID will store the moref of the DVS where the nsx
|
||||
id is being created. For other types and plugins, this value will
|
||||
remain null.
|
||||
"""
|
||||
__tablename__ = 'neutron_nsx_network_mappings'
|
||||
neutron_id = sa.Column(sa.String(36),
|
||||
sa.ForeignKey('networks.id', ondelete='CASCADE'),
|
||||
primary_key=True)
|
||||
nsx_id = sa.Column(sa.String(36), primary_key=True)
|
||||
dvs_id = sa.Column(sa.String(36), nullable=True)
|
||||
|
||||
|
||||
class NeutronNsxSecurityGroupMapping(model_base.BASEV2):
|
||||
|
@ -380,6 +380,14 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
raise n_exc.VlanIdInUse(
|
||||
vlan_id=segmentation_id,
|
||||
physical_network=phy_uuid)
|
||||
# Verify whether the DVSes exist in the backend.
|
||||
if physical_network_set:
|
||||
dvs_ids = self._get_dvs_ids(physical_network)
|
||||
for dvs_id in dvs_ids:
|
||||
if not self.nsx_v.vcns.validate_dvs(dvs_id):
|
||||
raise nsx_exc.NsxResourceNotFound(
|
||||
res_name='dvs_id',
|
||||
res_id=dvs_id)
|
||||
|
||||
elif network_type == c_utils.NsxVNetworkTypes.VXLAN:
|
||||
# Currently unable to set the segmentation id
|
||||
@ -527,13 +535,52 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
else:
|
||||
self.nsx_v.delete_virtual_wire(moref)
|
||||
|
||||
def _get_vlan_network_name(self, net_data):
|
||||
def _get_vlan_network_name(self, net_data, dvs_id):
|
||||
if net_data.get('name') is None:
|
||||
net_data['name'] = ''
|
||||
if net_data['name'] == '':
|
||||
return net_data['id']
|
||||
# Include only the first 8 characters from the dvs-id.
|
||||
return '%s-%s' % (dvs_id[:8], net_data['id'])
|
||||
else:
|
||||
# Maximum name length is 80 characters. 'id' length is 36
|
||||
# maximum prefix for name is 43
|
||||
return '%s-%s' % (net_data['name'][:43], net_data['id'])
|
||||
# maximum prefix for name plus dvs-id is 43
|
||||
return '%s-%s-%s' % (dvs_id[:8], net_data['name'][:35],
|
||||
net_data['id'])
|
||||
|
||||
def _create_vlan_network_at_backend(self, net_data, dvs_id):
|
||||
network_name = self._get_vlan_network_name(net_data, dvs_id)
|
||||
segment = net_data[mpnet.SEGMENTS][0]
|
||||
vlan_tag = 0
|
||||
if (segment.get(pnet.NETWORK_TYPE) ==
|
||||
c_utils.NsxVNetworkTypes.VLAN):
|
||||
vlan_tag = segment.get(pnet.SEGMENTATION_ID, 0)
|
||||
portgroup = {'vlanId': vlan_tag,
|
||||
'networkBindingType': 'Static',
|
||||
'networkName': network_name,
|
||||
'networkType': 'Isolation'}
|
||||
config_spec = {'networkSpec': portgroup}
|
||||
try:
|
||||
h, c = self.nsx_v.vcns.create_port_group(dvs_id,
|
||||
config_spec)
|
||||
return c
|
||||
except Exception as e:
|
||||
error = (_("Failed to create port group on DVS: %(dvs_id)s. "
|
||||
"Reason: %(reason)s") % {'dvs_id': dvs_id,
|
||||
'reason': e.response})
|
||||
raise nsx_exc.NsxPluginException(err_msg=error)
|
||||
|
||||
def _get_dvs_ids(self, physical_network):
|
||||
"""Extract DVS-IDs provided in the physical network field.
|
||||
|
||||
If physical network attribute is not set, return the pre configured
|
||||
dvs-id from nsx.ini file, otherwise convert physical network string
|
||||
to a list of unique DVS-IDs.
|
||||
"""
|
||||
if not attr.is_attr_set(physical_network):
|
||||
return [self.dvs_id]
|
||||
# Return unique DVS-IDs only and ignore duplicates
|
||||
return list(set(
|
||||
dvs.strip() for dvs in physical_network.split(',') if dvs))
|
||||
|
||||
def _get_default_security_group(self, context, tenant_id):
|
||||
return self._ensure_default_security_group(context, tenant_id)
|
||||
@ -652,42 +699,41 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
res_id=vdn_scope_id)
|
||||
h, c = self.nsx_v.vcns.create_virtual_wire(vdn_scope_id,
|
||||
config_spec)
|
||||
net_moref = c
|
||||
net_morefs = [c]
|
||||
elif network_type == c_utils.NsxVNetworkTypes.PORTGROUP:
|
||||
segment = net_data[mpnet.SEGMENTS][0]
|
||||
net_moref = segment.get(pnet.PHYSICAL_NETWORK)
|
||||
net_morefs = [segment.get(pnet.PHYSICAL_NETWORK)]
|
||||
else:
|
||||
network_name = self._get_vlan_network_name(net_data)
|
||||
vlan_tag = 0
|
||||
segment = net_data[mpnet.SEGMENTS][0]
|
||||
if (segment.get(pnet.NETWORK_TYPE) ==
|
||||
c_utils.NsxVNetworkTypes.VLAN):
|
||||
vlan_tag = segment.get(pnet.SEGMENTATION_ID, 0)
|
||||
physical_network = segment.get(pnet.PHYSICAL_NETWORK)
|
||||
dvs_id = (physical_network if attr.is_attr_set(
|
||||
physical_network) else self.dvs_id)
|
||||
portgroup = {'vlanId': vlan_tag,
|
||||
'networkBindingType': 'Static',
|
||||
'networkName': network_name,
|
||||
'networkType': 'Isolation'}
|
||||
config_spec = {'networkSpec': portgroup}
|
||||
# Retrieve the list of dvs-ids from physical network.
|
||||
# If physical_network attr is not set, retrieve a list
|
||||
# consisting of a single dvs-id pre-configured in nsx.ini
|
||||
dvs_ids = self._get_dvs_ids(physical_network)
|
||||
# Save the list of netmorefs from the backend
|
||||
net_morefs = []
|
||||
dvs_pg_mappings = {}
|
||||
for dvs_id in dvs_ids:
|
||||
try:
|
||||
h, c = self.nsx_v.vcns.create_port_group(dvs_id,
|
||||
config_spec)
|
||||
net_moref = c
|
||||
except Exception as e:
|
||||
LOG.debug("Failed to create port group: %s",
|
||||
e.response)
|
||||
err_msg = (_("Physical network %s is not an existing DVS")
|
||||
% dvs_id)
|
||||
raise n_exc.InvalidInput(error_message=err_msg)
|
||||
net_moref = self._create_vlan_network_at_backend(
|
||||
dvs_id=dvs_id,
|
||||
net_data=net_data)
|
||||
except nsx_exc.NsxPluginException:
|
||||
with excutils.save_and_reraise_exception():
|
||||
# Delete VLAN networks on other DVSes if it
|
||||
# fails to be created on one DVS and reraise
|
||||
# the original exception.
|
||||
for net_moref in net_morefs:
|
||||
self._delete_backend_network(net_moref)
|
||||
dvs_pg_mappings[dvs_id] = net_moref
|
||||
net_morefs.append(net_moref)
|
||||
try:
|
||||
# Create SpoofGuard policy for network anti-spoofing
|
||||
if cfg.CONF.nsxv.spoofguard_enabled and backend_network:
|
||||
sg_policy_id = None
|
||||
s, sg_policy_id = self.nsx_v.vcns.create_spoofguard_policy(
|
||||
net_moref, net_data['id'], net_data[psec.PORTSECURITY])
|
||||
|
||||
sg_policy_id = self.nsx_v.vcns.create_spoofguard_policy(
|
||||
net_morefs, net_data['id'],
|
||||
net_data[psec.PORTSECURITY])[1]
|
||||
with context.session.begin(subtransactions=True):
|
||||
new_net = super(NsxVPluginV2, self).create_network(context,
|
||||
network)
|
||||
@ -723,6 +769,17 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
net_bindings)
|
||||
if backend_network:
|
||||
# Save moref in the DB for future access
|
||||
if network_type == c_utils.NsxVNetworkTypes.VLAN:
|
||||
# Save netmoref to dvs id mappings for VLAN network
|
||||
# type for future access.
|
||||
for dvs_id, netmoref in six.iteritems(dvs_pg_mappings):
|
||||
nsx_db.add_neutron_nsx_network_mapping(
|
||||
session=context.session,
|
||||
neutron_id=new_net['id'],
|
||||
nsx_switch_id=netmoref,
|
||||
dvs_id=dvs_id)
|
||||
else:
|
||||
for net_moref in net_morefs:
|
||||
nsx_db.add_neutron_nsx_network_mapping(
|
||||
context.session, new_net['id'],
|
||||
net_moref)
|
||||
@ -736,6 +793,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
if backend_network:
|
||||
if cfg.CONF.nsxv.spoofguard_enabled and sg_policy_id:
|
||||
self.nsx_v.vcns.delete_spoofguard_policy(sg_policy_id)
|
||||
for net_moref in net_morefs:
|
||||
self._delete_backend_network(net_moref)
|
||||
LOG.exception(_LE('Failed to create network'))
|
||||
|
||||
@ -816,7 +874,8 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
if cfg.CONF.nsxv.spoofguard_enabled and sg_policy_id:
|
||||
self.nsx_v.vcns.delete_spoofguard_policy(sg_policy_id)
|
||||
edge_utils.check_network_in_use_at_backend(context, id)
|
||||
self._delete_backend_network(mappings[0])
|
||||
for mapping in mappings:
|
||||
self._delete_backend_network(mapping)
|
||||
|
||||
def get_network(self, context, id, fields=None):
|
||||
with context.session.begin(subtransactions=True):
|
||||
@ -875,10 +934,10 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
# old state
|
||||
if cfg.CONF.nsxv.spoofguard_enabled and psec_update:
|
||||
policy_id = nsxv_db.get_spoofguard_policy_id(context.session, id)
|
||||
net_moref = nsx_db.get_nsx_switch_ids(context.session, id)
|
||||
net_morefs = nsx_db.get_nsx_switch_ids(context.session, id)
|
||||
try:
|
||||
self.nsx_v.vcns.update_spoofguard_policy(
|
||||
policy_id, net_moref[0], id,
|
||||
policy_id, net_morefs, id,
|
||||
net_attrs[psec.PORTSECURITY])
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
|
@ -29,6 +29,8 @@ def _xmldump(obj):
|
||||
This converts the dict to xml with following assumptions:
|
||||
Keys starting with _(underscore) are to be used as attributes and not
|
||||
element keys starting with @ so that dict can be made.
|
||||
Keys starting with __(double underscore) are to be skipped and its
|
||||
value is processed.
|
||||
The keys are not part of any xml schema.
|
||||
"""
|
||||
|
||||
@ -36,11 +38,15 @@ def _xmldump(obj):
|
||||
attr = ""
|
||||
if isinstance(obj, dict):
|
||||
for key, value in six.iteritems(obj):
|
||||
if (key.startswith('_')):
|
||||
if key.startswith('__'):
|
||||
# Skip the key and evaluate it's value.
|
||||
a, x = _xmldump(value)
|
||||
config += x
|
||||
elif key.startswith('_'):
|
||||
attr += ' %s="%s"' % (key[1:], value)
|
||||
else:
|
||||
a, x = _xmldump(value)
|
||||
if (key.startswith('@')):
|
||||
if key.startswith('@'):
|
||||
cfg = "%s" % (x)
|
||||
else:
|
||||
cfg = "<%s%s>%s</%s>" % (key, a, x, key)
|
||||
|
@ -316,35 +316,34 @@ class EdgeManager(object):
|
||||
bindings = nsxv_db.get_network_bindings(context.session, network_id)
|
||||
# Set the return value as global DVS-ID of the mgmt/edge cluster
|
||||
phys_net = self.dvs_id
|
||||
network_type = None
|
||||
if bindings:
|
||||
binding = bindings[0]
|
||||
network_type = binding['binding_type']
|
||||
if (network_type == c_utils.NsxVNetworkTypes.VLAN
|
||||
and binding['phy_uuid'] != ''):
|
||||
if ',' not in binding['phy_uuid']:
|
||||
phys_net = binding['phy_uuid']
|
||||
# Return user input physical network value for all network types
|
||||
# except VXLAN networks. The DVS-ID of the mgmt/edge cluster must
|
||||
# be returned for VXLAN network types.
|
||||
if (not binding['binding_type'] == c_utils.NsxVNetworkTypes.VXLAN
|
||||
elif (not network_type == c_utils.NsxVNetworkTypes.VXLAN
|
||||
and binding['phy_uuid'] != ''):
|
||||
phys_net = binding['phy_uuid']
|
||||
return phys_net
|
||||
return phys_net, network_type
|
||||
|
||||
def _create_sub_interface(self, context, network_id, network_name,
|
||||
tunnel_index, address_groups,
|
||||
port_group_id=None):
|
||||
# Get the physical port group /wire id of the network id
|
||||
mappings = nsx_db.get_nsx_switch_ids(context.session, network_id)
|
||||
if mappings:
|
||||
vcns_network_id = mappings[0]
|
||||
else:
|
||||
LOG.error(_LE("Create sub interface failed since network %s not "
|
||||
"found at the backend."), network_id)
|
||||
raise nsx_exc.NsxPluginException(
|
||||
err_msg=_("network %s not found at the backend") % network_id)
|
||||
vcns_network_id = _retrieve_nsx_switch_id(context, network_id)
|
||||
if port_group_id is None:
|
||||
portgroup = {'vlanId': 0,
|
||||
'networkName': network_name,
|
||||
'networkBindingType': 'Static',
|
||||
'networkType': 'Isolation'}
|
||||
config_spec = {'networkSpec': portgroup}
|
||||
dvs_id = self._get_physical_provider_network(context, network_id)
|
||||
dvs_id, network_type = self._get_physical_provider_network(
|
||||
context, network_id)
|
||||
pg, port_group_id = self.nsxv_manager.vcns.create_port_group(
|
||||
dvs_id, config_spec)
|
||||
|
||||
@ -388,8 +387,8 @@ class EdgeManager(object):
|
||||
if port_group_id:
|
||||
objuri = header['location']
|
||||
job_id = objuri[objuri.rfind("/") + 1:]
|
||||
dvs_id = self._get_physical_provider_network(context,
|
||||
network_id)
|
||||
dvs_id, net_type = self._get_physical_provider_network(
|
||||
context, network_id)
|
||||
self.nsxv_manager.delete_portgroup(dvs_id,
|
||||
port_group_id,
|
||||
job_id)
|
||||
@ -1412,13 +1411,37 @@ def delete_lrouter(nsxv_manager, context, router_id, dist=False):
|
||||
LOG.warning(_LW("router binding for router: %s not found"), router_id)
|
||||
|
||||
|
||||
def _retrieve_nsx_switch_id(context, network_id):
|
||||
"""Helper method to retrieve backend switch ID."""
|
||||
bindings = nsxv_db.get_network_bindings(context.session, network_id)
|
||||
if bindings:
|
||||
binding = bindings[0]
|
||||
network_type = binding['binding_type']
|
||||
if (network_type == c_utils.NsxVNetworkTypes.VLAN
|
||||
and binding['phy_uuid'] != ''):
|
||||
if ',' not in binding['phy_uuid']:
|
||||
dvs_id = binding['phy_uuid']
|
||||
else:
|
||||
# If network is of type VLAN and multiple dvs associated with
|
||||
# one neutron network, retrieve the logical network id for the
|
||||
# edge/mgmt cluster's DVS.
|
||||
dvs_id = cfg.CONF.nsxv.dvs_id
|
||||
return nsx_db.get_nsx_switch_id_for_dvs(
|
||||
context.session, network_id, dvs_id)
|
||||
# Get the physical port group /wire id of the network id
|
||||
mappings = nsx_db.get_nsx_switch_ids(context.session, network_id)
|
||||
if mappings:
|
||||
return mappings[0]
|
||||
raise nsx_exc.NsxPluginException(
|
||||
err_msg=_("Network %s not found at the backend") % network_id)
|
||||
|
||||
|
||||
def create_dhcp_service(context, nsxv_manager, network):
|
||||
"""Create an Edge for dhcp service."""
|
||||
edge_name = "%s-%s" % (network['name'], network['id'])
|
||||
jobdata = {'network_id': network['id'], 'context': context}
|
||||
# port group id for vlan or virtual wire id for vxlan
|
||||
nsx_network_id = nsx_db.get_nsx_switch_ids(context.session,
|
||||
network['id'])[0]
|
||||
nsx_network_id = _retrieve_nsx_switch_id(context, network['id'])
|
||||
# Deploy an Edge for dhcp service
|
||||
return nsxv_manager.deploy_edge(
|
||||
network['id'], edge_name, nsx_network_id, jobdata=jobdata,
|
||||
@ -1476,10 +1499,7 @@ def query_dhcp_service_config(nsxv_manager, edge_id):
|
||||
def update_dhcp_internal_interface(context, nsxv_manager,
|
||||
network_id, address_groups, add=True):
|
||||
# Get the physical port group /wire id of the network id
|
||||
mappings = nsx_db.get_nsx_switch_ids(context.session, network_id)
|
||||
if mappings:
|
||||
vcns_network_id = mappings[0]
|
||||
|
||||
vcns_network_id = _retrieve_nsx_switch_id(context, network_id)
|
||||
# Get the DHCP Edge to update the internal interface
|
||||
binding = nsxv_db.get_dhcp_edge_network_binding(context.session,
|
||||
network_id)
|
||||
@ -1635,9 +1655,7 @@ def update_internal_interface(nsxv_manager, context, router_id, int_net_id,
|
||||
def _update_internal_interface(nsxv_manager, context, router_id, int_net_id,
|
||||
address_groups, is_connected=True):
|
||||
# Get the pg/wire id of the network id
|
||||
mappings = nsx_db.get_nsx_switch_ids(context.session, int_net_id)
|
||||
if mappings:
|
||||
vcns_network_id = mappings[0]
|
||||
vcns_network_id = _retrieve_nsx_switch_id(context, int_net_id)
|
||||
LOG.debug("Network id %(network_id)s corresponding ref is : "
|
||||
"%(net_moref)s", {'network_id': int_net_id,
|
||||
'net_moref': vcns_network_id})
|
||||
@ -1671,9 +1689,7 @@ def add_vdr_internal_interface(nsxv_manager, context, router_id,
|
||||
def _add_vdr_internal_interface(nsxv_manager, context, router_id,
|
||||
int_net_id, address_groups, is_connected=True):
|
||||
# Get the pg/wire id of the network id
|
||||
mappings = nsx_db.get_nsx_switch_ids(context.session, int_net_id)
|
||||
if mappings:
|
||||
vcns_network_id = mappings[0]
|
||||
vcns_network_id = _retrieve_nsx_switch_id(context, int_net_id)
|
||||
LOG.debug("Network id %(network_id)s corresponding ref is : "
|
||||
"%(net_moref)s", {'network_id': int_net_id,
|
||||
'net_moref': vcns_network_id})
|
||||
@ -1706,9 +1722,7 @@ def _update_vdr_internal_interface(nsxv_manager, context, router_id,
|
||||
int_net_id, address_groups,
|
||||
is_connected=True):
|
||||
# Get the pg/wire id of the network id
|
||||
mappings = nsx_db.get_nsx_switch_ids(context.session, int_net_id)
|
||||
if mappings:
|
||||
vcns_network_id = mappings[0]
|
||||
vcns_network_id = _retrieve_nsx_switch_id(context, int_net_id)
|
||||
LOG.debug("Network id %(network_id)s corresponding ref is : "
|
||||
"%(net_moref)s", {'network_id': int_net_id,
|
||||
'net_moref': vcns_network_id})
|
||||
@ -1733,9 +1747,7 @@ def delete_interface(nsxv_manager, context, router_id, network_id,
|
||||
def _delete_interface(nsxv_manager, context, router_id, network_id,
|
||||
dist=False, is_wait=True):
|
||||
# Get the pg/wire id of the network id
|
||||
mappings = nsx_db.get_nsx_switch_ids(context.session, network_id)
|
||||
if mappings:
|
||||
vcns_network_id = mappings[0]
|
||||
vcns_network_id = _retrieve_nsx_switch_id(context, network_id)
|
||||
LOG.debug("Network id %(network_id)s corresponding ref is : "
|
||||
"%(net_moref)s", {'network_id': network_id,
|
||||
'net_moref': vcns_network_id})
|
||||
|
@ -640,23 +640,33 @@ class Vcns(object):
|
||||
|
||||
return self.do_request(HTTP_GET, uri)
|
||||
|
||||
def _get_enforcement_point_body(self, enforcement_points):
|
||||
e_point_list = []
|
||||
for enforcement_point in enforcement_points:
|
||||
e_point_list.append({
|
||||
'enforcementPoint': {
|
||||
'id': enforcement_point,
|
||||
'type': enforcement_point.split('-')[0]
|
||||
}
|
||||
})
|
||||
return {'__enforcementPoints': e_point_list}
|
||||
|
||||
@retry_upon_exception(exceptions.RequestBad)
|
||||
def create_spoofguard_policy(self, enforcement_point, name, enable):
|
||||
def create_spoofguard_policy(self, enforcement_points, name, enable):
|
||||
uri = '%s/policies/' % SPOOFGUARD_PREFIX
|
||||
|
||||
body = {'spoofguardPolicy':
|
||||
{'name': name,
|
||||
'operationMode': 'MANUAL' if enable else 'DISABLE',
|
||||
'enforcementPoint':
|
||||
{'id': enforcement_point,
|
||||
'type': enforcement_point.split('-')[0]},
|
||||
'allowLocalIPs': 'true'}}
|
||||
body['spoofguardPolicy'].update(
|
||||
self._get_enforcement_point_body(enforcement_points))
|
||||
return self.do_request(HTTP_POST, uri, body,
|
||||
format='xml', encode=True, decode=False)
|
||||
|
||||
@retry_upon_exception(exceptions.RequestBad)
|
||||
def update_spoofguard_policy(self, policy_id,
|
||||
enforcement_point, name, enable):
|
||||
enforcement_points, name, enable):
|
||||
update_uri = '%s/policies/%s' % (SPOOFGUARD_PREFIX, policy_id)
|
||||
publish_uri = '%s/%s?action=publish' % (SPOOFGUARD_PREFIX, policy_id)
|
||||
|
||||
@ -664,10 +674,9 @@ class Vcns(object):
|
||||
{'policyId': policy_id,
|
||||
'name': name,
|
||||
'operationMode': 'MANUAL' if enable else 'DISABLE',
|
||||
'enforcementPoint':
|
||||
{'id': enforcement_point,
|
||||
'type': enforcement_point.split('-')[0]},
|
||||
'allowLocalIPs': 'true'}}
|
||||
body['spoofguardPolicy'].update(
|
||||
self._get_enforcement_point_body(enforcement_points))
|
||||
|
||||
self.do_request(HTTP_PUT, update_uri, body,
|
||||
format='xml', encode=True, decode=False)
|
||||
|
@ -125,23 +125,39 @@ class NsxVPluginV2TestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
|
||||
def test_get_vlan_network_name(self):
|
||||
p = manager.NeutronManager.get_plugin()
|
||||
id = uuidutils.generate_uuid()
|
||||
net_id = uuidutils.generate_uuid()
|
||||
dvs_id = 'dvs-10'
|
||||
net = {'name': '',
|
||||
'id': id}
|
||||
expected = id
|
||||
'id': net_id}
|
||||
# Empty net['name'] should yield dvs_id-net_id as a name for the
|
||||
# port group.
|
||||
expected = '%s-%s' % (dvs_id, net_id)
|
||||
self.assertEqual(expected,
|
||||
p._get_vlan_network_name(net))
|
||||
p._get_vlan_network_name(net, dvs_id))
|
||||
# If network name is provided then it should yield
|
||||
# dvs_id-net_name-net_id as a name for the port group.
|
||||
net = {'name': 'pele',
|
||||
'id': id}
|
||||
expected = '%s-%s' % ('pele', id)
|
||||
'id': net_id}
|
||||
expected = '%s-%s-%s' % (dvs_id, 'pele', net_id)
|
||||
self.assertEqual(expected,
|
||||
p._get_vlan_network_name(net))
|
||||
p._get_vlan_network_name(net, dvs_id))
|
||||
name = 'X' * 500
|
||||
net = {'name': name,
|
||||
'id': id}
|
||||
expected = '%s-%s' % (name[:43], id)
|
||||
'id': net_id}
|
||||
expected = '%s-%s-%s' % (dvs_id, name[:35], net_id)
|
||||
self.assertEqual(expected,
|
||||
p._get_vlan_network_name(net))
|
||||
p._get_vlan_network_name(net, dvs_id))
|
||||
|
||||
def test_get_vlan_network_name_with_net_name_missing(self):
|
||||
p = manager.NeutronManager.get_plugin()
|
||||
net_id = uuidutils.generate_uuid()
|
||||
dvs_id = 'dvs-10'
|
||||
net = {'id': net_id}
|
||||
# Missing net['name'] should yield dvs_id-net_id as a name for the
|
||||
# port group.
|
||||
expected = '%s-%s' % (dvs_id, net_id)
|
||||
self.assertEqual(expected,
|
||||
p._get_vlan_network_name(net, dvs_id))
|
||||
|
||||
def test_create_port_anticipating_allocation(self):
|
||||
with self.network(shared=True) as network:
|
||||
@ -301,6 +317,114 @@ class TestNetworksV2(test_plugin.TestNetworksV2, NsxVPluginV2TestCase):
|
||||
for k, v in expected_same_vlan:
|
||||
self.assertEqual(net1['network'][k], v)
|
||||
|
||||
def test_create_vlan_network_with_multiple_dvs(self):
|
||||
name = 'multi-dvs-vlan-net'
|
||||
providernet_args = {pnet.NETWORK_TYPE: 'vlan',
|
||||
pnet.SEGMENTATION_ID: 100,
|
||||
pnet.PHYSICAL_NETWORK: 'dvs-1, dvs-2, dvs-3'}
|
||||
p = manager.NeutronManager.get_plugin()
|
||||
with mock.patch.object(
|
||||
p, '_create_vlan_network_at_backend',
|
||||
# Return three netmorefs as side effect
|
||||
side_effect=[_uuid(), _uuid(), _uuid()]) as vlan_net_call:
|
||||
with self.network(name=name,
|
||||
providernet_args=providernet_args,
|
||||
arg_list=(pnet.NETWORK_TYPE,
|
||||
pnet.SEGMENTATION_ID,
|
||||
pnet.PHYSICAL_NETWORK)):
|
||||
# _create_vlan_network_at_backend is expected to be called
|
||||
# three times since we have three DVS IDs in the physical
|
||||
# network attribute.
|
||||
self.assertEqual(3, vlan_net_call.call_count)
|
||||
|
||||
def test_create_vlan_network_with_multiple_dvs_backend_failure(self):
|
||||
net_data = {'name': 'vlan-net',
|
||||
'tenant_id': self._tenant_id,
|
||||
pnet.NETWORK_TYPE: 'vlan',
|
||||
pnet.SEGMENTATION_ID: 100,
|
||||
pnet.PHYSICAL_NETWORK: 'dvs-1, dvs-2, dvs-3'}
|
||||
network = {'network': net_data}
|
||||
p = manager.NeutronManager.get_plugin()
|
||||
with mock.patch.object(
|
||||
p, '_create_vlan_network_at_backend',
|
||||
# Return two successful netmorefs and fail on the backend
|
||||
# for the third netmoref creation as side effect.
|
||||
side_effect=[_uuid(), _uuid(),
|
||||
nsxv_exc.NsxPluginException(err_msg='')]):
|
||||
with mock.patch.object(
|
||||
p, '_delete_backend_network') as delete_net_call:
|
||||
self.assertRaises(nsxv_exc.NsxPluginException,
|
||||
p.create_network,
|
||||
context.get_admin_context(),
|
||||
network)
|
||||
# Two successfully created port groups should be rolled back
|
||||
# on the failure of third port group creation.
|
||||
self.assertEqual(2, delete_net_call.call_count)
|
||||
|
||||
def test_create_vlan_network_with_multiple_dvs_not_found_failure(self):
|
||||
net_data = {'name': 'vlan-net',
|
||||
'tenant_id': self._tenant_id,
|
||||
pnet.NETWORK_TYPE: 'vlan',
|
||||
pnet.SEGMENTATION_ID: 100,
|
||||
pnet.PHYSICAL_NETWORK: 'dvs-1, dvs-2, dvs-3'}
|
||||
network = {'network': net_data}
|
||||
p = manager.NeutronManager.get_plugin()
|
||||
with mock.patch.object(
|
||||
p, '_validate_provider_create',
|
||||
side_effect=[nsxv_exc.NsxResourceNotFound(res_id='dvs-2',
|
||||
res_name='dvs_id')]):
|
||||
with mock.patch.object(
|
||||
p, '_create_vlan_network_at_backend') as create_net_call:
|
||||
self.assertRaises(nsxv_exc.NsxResourceNotFound,
|
||||
p.create_network,
|
||||
context.get_admin_context(),
|
||||
network)
|
||||
# Verify no port group is created on the backend.
|
||||
self.assertEqual(0, create_net_call.call_count)
|
||||
|
||||
def test_create_vlan_network_with_multiple_dvs_ignore_duplicate_dvs(self):
|
||||
name = 'multi-dvs-vlan-net'
|
||||
providernet_args = {pnet.NETWORK_TYPE: 'vlan',
|
||||
pnet.SEGMENTATION_ID: 100,
|
||||
pnet.PHYSICAL_NETWORK: 'dvs-1, dvs-2, dvs-1'}
|
||||
p = manager.NeutronManager.get_plugin()
|
||||
with mock.patch.object(
|
||||
p, '_create_vlan_network_at_backend',
|
||||
# Return two netmorefs as side effect
|
||||
side_effect=[_uuid(), _uuid()]) as vlan_net_call:
|
||||
with self.network(name=name,
|
||||
providernet_args=providernet_args,
|
||||
arg_list=(pnet.NETWORK_TYPE,
|
||||
pnet.SEGMENTATION_ID,
|
||||
pnet.PHYSICAL_NETWORK)):
|
||||
# _create_vlan_network_at_backend is expected to be called
|
||||
# two times since we have only two unique DVS IDs in the
|
||||
# physical network attribute.
|
||||
self.assertEqual(2, vlan_net_call.call_count)
|
||||
|
||||
def test_get_dvs_ids_for_multiple_dvs_vlan_network(self):
|
||||
p = manager.NeutronManager.get_plugin()
|
||||
# If no DVS-ID is provided as part of physical network, return
|
||||
# global DVS-ID configured in nsx.ini
|
||||
physical_network = attributes.ATTR_NOT_SPECIFIED
|
||||
self.assertEqual(['fake_dvs_id'], p._get_dvs_ids(physical_network))
|
||||
# If DVS-IDs are provided as part of physical network as a comma
|
||||
# separated string, return them as a list of DVS-IDs.
|
||||
physical_network = 'dvs-1,dvs-2, dvs-3'
|
||||
expected_dvs_ids = ['dvs-1', 'dvs-2', 'dvs-3']
|
||||
self.assertEqual(expected_dvs_ids,
|
||||
sorted(p._get_dvs_ids(physical_network)))
|
||||
# Ignore extra commas ',' in the physical_network attribute.
|
||||
physical_network = ',,,dvs-1,dvs-2,, dvs-3,'
|
||||
expected_dvs_ids = ['dvs-1', 'dvs-2', 'dvs-3']
|
||||
self.assertEqual(expected_dvs_ids,
|
||||
sorted(p._get_dvs_ids(physical_network)))
|
||||
# Ignore duplicate DVS-IDs in the physical_network attribute.
|
||||
physical_network = ',,,dvs-1,dvs-2,, dvs-2,'
|
||||
expected_dvs_ids = ['dvs-1', 'dvs-2']
|
||||
self.assertEqual(expected_dvs_ids,
|
||||
sorted(p._get_dvs_ids(physical_network)))
|
||||
|
||||
def test_create_vxlan_with_tz_provider_network(self):
|
||||
name = 'provider_net_vxlan'
|
||||
expected = [('subnets', []), ('name', name), ('admin_state_up', True),
|
||||
|
@ -1028,18 +1028,18 @@ class FakeVcns(object):
|
||||
headers = {'status': 200}
|
||||
return (headers, response)
|
||||
|
||||
def create_spoofguard_policy(self, enforcement_point, name, enable):
|
||||
def create_spoofguard_policy(self, enforcement_points, name, enable):
|
||||
policy = {'name': name,
|
||||
'enforcement_point': enforcement_point,
|
||||
'enforcement_point': enforcement_points[0],
|
||||
'operationMode': 'MANUAL' if enable else 'DISABLE'}
|
||||
policy_id = len(self._spoofguard_policies)
|
||||
self._spoofguard_policies.append(policy)
|
||||
return None, policy_id
|
||||
|
||||
def update_spoofguard_policy(self, policy_id,
|
||||
enforcement_point, name, enable):
|
||||
enforcement_points, name, enable):
|
||||
policy = {'name': name,
|
||||
'enforcement_point': enforcement_point,
|
||||
'enforcement_point': enforcement_points[0],
|
||||
'operationMode': 'MANUAL' if enable else 'DISABLE'}
|
||||
self._spoofguard_policies[int(policy_id)] = policy
|
||||
return None, ''
|
||||
|
Loading…
Reference in New Issue
Block a user