[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:
Abhishek Raut 2016-02-22 01:47:36 -08:00 committed by Kobi Samoray
parent c946784134
commit c138dcfdda
10 changed files with 361 additions and 98 deletions

View File

@ -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(

View File

@ -1 +1 @@
3e4dccfe6fb4
967462f585e1

View File

@ -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))

View File

@ -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):

View File

@ -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}
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)
# 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:
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,9 +769,20 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
net_bindings)
if backend_network:
# Save moref in the DB for future access
nsx_db.add_neutron_nsx_network_mapping(
context.session, new_net['id'],
net_moref)
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)
if cfg.CONF.nsxv.spoofguard_enabled:
nsxv_db.map_spoofguard_policy_for_network(
context.session, new_net['id'], sg_policy_id)
@ -736,7 +793,8 @@ 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)
self._delete_backend_network(net_moref)
for net_moref in net_morefs:
self._delete_backend_network(net_moref)
LOG.exception(_LE('Failed to create network'))
# this extra lookup is necessary to get the
@ -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():

View File

@ -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)

View File

@ -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
and binding['phy_uuid'] != ''):
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})

View File

@ -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)

View File

@ -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),

View File

@ -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, ''