[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 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): with session.begin(subtransactions=True):
mapping = nsx_models.NeutronNsxNetworkMapping( 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) session.add(mapping)
return mapping return mapping
@ -120,6 +122,18 @@ def get_nsx_switch_ids(session, neutron_id):
neutron_id=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): def get_net_ids(session, nsx_id):
return [mapping['neutron_id'] for mapping in return [mapping['neutron_id'] for mapping in
session.query(nsx_models.NeutronNsxNetworkMapping).filter_by( 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 Because of chained logical switches more than one mapping might exist
for a single Neutron network. 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' __tablename__ = 'neutron_nsx_network_mappings'
neutron_id = sa.Column(sa.String(36), neutron_id = sa.Column(sa.String(36),
sa.ForeignKey('networks.id', ondelete='CASCADE'), sa.ForeignKey('networks.id', ondelete='CASCADE'),
primary_key=True) primary_key=True)
nsx_id = sa.Column(sa.String(36), 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): class NeutronNsxSecurityGroupMapping(model_base.BASEV2):

View File

@ -380,6 +380,14 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
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:
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: elif network_type == c_utils.NsxVNetworkTypes.VXLAN:
# Currently unable to set the segmentation id # Currently unable to set the segmentation id
@ -527,13 +535,52 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
else: else:
self.nsx_v.delete_virtual_wire(moref) 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'] == '': 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: else:
# Maximum name length is 80 characters. 'id' length is 36 # Maximum name length is 80 characters. 'id' length is 36
# maximum prefix for name is 43 # maximum prefix for name plus dvs-id is 43
return '%s-%s' % (net_data['name'][:43], net_data['id']) 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): def _get_default_security_group(self, context, tenant_id):
return self._ensure_default_security_group(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) res_id=vdn_scope_id)
h, c = self.nsx_v.vcns.create_virtual_wire(vdn_scope_id, h, c = self.nsx_v.vcns.create_virtual_wire(vdn_scope_id,
config_spec) config_spec)
net_moref = c net_morefs = [c]
elif network_type == c_utils.NsxVNetworkTypes.PORTGROUP: elif network_type == c_utils.NsxVNetworkTypes.PORTGROUP:
segment = net_data[mpnet.SEGMENTS][0] segment = net_data[mpnet.SEGMENTS][0]
net_moref = segment.get(pnet.PHYSICAL_NETWORK) net_morefs = [segment.get(pnet.PHYSICAL_NETWORK)]
else: else:
network_name = self._get_vlan_network_name(net_data)
vlan_tag = 0
segment = net_data[mpnet.SEGMENTS][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) physical_network = segment.get(pnet.PHYSICAL_NETWORK)
dvs_id = (physical_network if attr.is_attr_set( # Retrieve the list of dvs-ids from physical network.
physical_network) else self.dvs_id) # If physical_network attr is not set, retrieve a list
portgroup = {'vlanId': vlan_tag, # consisting of a single dvs-id pre-configured in nsx.ini
'networkBindingType': 'Static', dvs_ids = self._get_dvs_ids(physical_network)
'networkName': network_name, # Save the list of netmorefs from the backend
'networkType': 'Isolation'} net_morefs = []
config_spec = {'networkSpec': portgroup} dvs_pg_mappings = {}
for dvs_id in dvs_ids:
try: try:
h, c = self.nsx_v.vcns.create_port_group(dvs_id, net_moref = self._create_vlan_network_at_backend(
config_spec) dvs_id=dvs_id,
net_moref = c net_data=net_data)
except Exception as e: except nsx_exc.NsxPluginException:
LOG.debug("Failed to create port group: %s", with excutils.save_and_reraise_exception():
e.response) # Delete VLAN networks on other DVSes if it
err_msg = (_("Physical network %s is not an existing DVS") # fails to be created on one DVS and reraise
% dvs_id) # the original exception.
raise n_exc.InvalidInput(error_message=err_msg) 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: try:
# Create SpoofGuard policy for network anti-spoofing # Create SpoofGuard policy for network anti-spoofing
if cfg.CONF.nsxv.spoofguard_enabled and backend_network: if cfg.CONF.nsxv.spoofguard_enabled and backend_network:
sg_policy_id = None sg_policy_id = None
s, sg_policy_id = self.nsx_v.vcns.create_spoofguard_policy( sg_policy_id = self.nsx_v.vcns.create_spoofguard_policy(
net_moref, net_data['id'], net_data[psec.PORTSECURITY]) net_morefs, net_data['id'],
net_data[psec.PORTSECURITY])[1]
with context.session.begin(subtransactions=True): with context.session.begin(subtransactions=True):
new_net = super(NsxVPluginV2, self).create_network(context, new_net = super(NsxVPluginV2, self).create_network(context,
network) network)
@ -723,6 +769,17 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
net_bindings) net_bindings)
if backend_network: if backend_network:
# Save moref in the DB for future access # 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( nsx_db.add_neutron_nsx_network_mapping(
context.session, new_net['id'], context.session, new_net['id'],
net_moref) net_moref)
@ -736,6 +793,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
if backend_network: if backend_network:
if cfg.CONF.nsxv.spoofguard_enabled and sg_policy_id: if cfg.CONF.nsxv.spoofguard_enabled and sg_policy_id:
self.nsx_v.vcns.delete_spoofguard_policy(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) self._delete_backend_network(net_moref)
LOG.exception(_LE('Failed to create network')) 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: if cfg.CONF.nsxv.spoofguard_enabled and sg_policy_id:
self.nsx_v.vcns.delete_spoofguard_policy(sg_policy_id) self.nsx_v.vcns.delete_spoofguard_policy(sg_policy_id)
edge_utils.check_network_in_use_at_backend(context, 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): def get_network(self, context, id, fields=None):
with context.session.begin(subtransactions=True): with context.session.begin(subtransactions=True):
@ -875,10 +934,10 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
# old state # old state
if cfg.CONF.nsxv.spoofguard_enabled and psec_update: if cfg.CONF.nsxv.spoofguard_enabled and psec_update:
policy_id = nsxv_db.get_spoofguard_policy_id(context.session, id) 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: try:
self.nsx_v.vcns.update_spoofguard_policy( self.nsx_v.vcns.update_spoofguard_policy(
policy_id, net_moref[0], id, policy_id, net_morefs, id,
net_attrs[psec.PORTSECURITY]) net_attrs[psec.PORTSECURITY])
except Exception: except Exception:
with excutils.save_and_reraise_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: This converts the dict to xml with following assumptions:
Keys starting with _(underscore) are to be used as attributes and not Keys starting with _(underscore) are to be used as attributes and not
element keys starting with @ so that dict can be made. 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. The keys are not part of any xml schema.
""" """
@ -36,11 +38,15 @@ def _xmldump(obj):
attr = "" attr = ""
if isinstance(obj, dict): if isinstance(obj, dict):
for key, value in six.iteritems(obj): 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) attr += ' %s="%s"' % (key[1:], value)
else: else:
a, x = _xmldump(value) a, x = _xmldump(value)
if (key.startswith('@')): if key.startswith('@'):
cfg = "%s" % (x) cfg = "%s" % (x)
else: else:
cfg = "<%s%s>%s</%s>" % (key, a, x, key) 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) bindings = nsxv_db.get_network_bindings(context.session, network_id)
# Set the return value as global DVS-ID of the mgmt/edge cluster # Set the return value as global DVS-ID of the mgmt/edge cluster
phys_net = self.dvs_id phys_net = self.dvs_id
network_type = None
if bindings: if bindings:
binding = bindings[0] 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 # Return user input physical network value for all network types
# except VXLAN networks. The DVS-ID of the mgmt/edge cluster must # except VXLAN networks. The DVS-ID of the mgmt/edge cluster must
# be returned for VXLAN network types. # 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'] != ''): and binding['phy_uuid'] != ''):
phys_net = 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, def _create_sub_interface(self, context, network_id, network_name,
tunnel_index, address_groups, tunnel_index, address_groups,
port_group_id=None): port_group_id=None):
# Get the physical port group /wire id of the network id vcns_network_id = _retrieve_nsx_switch_id(context, 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)
if port_group_id is None: if port_group_id is None:
portgroup = {'vlanId': 0, portgroup = {'vlanId': 0,
'networkName': network_name, 'networkName': network_name,
'networkBindingType': 'Static', 'networkBindingType': 'Static',
'networkType': 'Isolation'} 'networkType': 'Isolation'}
config_spec = {'networkSpec': portgroup} 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( pg, port_group_id = self.nsxv_manager.vcns.create_port_group(
dvs_id, config_spec) dvs_id, config_spec)
@ -388,8 +387,8 @@ class EdgeManager(object):
if port_group_id: if port_group_id:
objuri = header['location'] objuri = header['location']
job_id = objuri[objuri.rfind("/") + 1:] job_id = objuri[objuri.rfind("/") + 1:]
dvs_id = self._get_physical_provider_network(context, dvs_id, net_type = self._get_physical_provider_network(
network_id) context, network_id)
self.nsxv_manager.delete_portgroup(dvs_id, self.nsxv_manager.delete_portgroup(dvs_id,
port_group_id, port_group_id,
job_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) 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): def create_dhcp_service(context, nsxv_manager, network):
"""Create an Edge for dhcp service.""" """Create an Edge for dhcp service."""
edge_name = "%s-%s" % (network['name'], network['id']) edge_name = "%s-%s" % (network['name'], network['id'])
jobdata = {'network_id': network['id'], 'context': context} jobdata = {'network_id': network['id'], 'context': context}
# port group id for vlan or virtual wire id for vxlan # port group id for vlan or virtual wire id for vxlan
nsx_network_id = nsx_db.get_nsx_switch_ids(context.session, nsx_network_id = _retrieve_nsx_switch_id(context, network['id'])
network['id'])[0]
# Deploy an Edge for dhcp service # Deploy an Edge for dhcp service
return nsxv_manager.deploy_edge( return nsxv_manager.deploy_edge(
network['id'], edge_name, nsx_network_id, jobdata=jobdata, 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, def update_dhcp_internal_interface(context, nsxv_manager,
network_id, address_groups, add=True): network_id, address_groups, add=True):
# Get the physical port group /wire id of the network id # Get the physical port group /wire id of the network id
mappings = nsx_db.get_nsx_switch_ids(context.session, network_id) vcns_network_id = _retrieve_nsx_switch_id(context, network_id)
if mappings:
vcns_network_id = mappings[0]
# Get the DHCP Edge to update the internal interface # Get the DHCP Edge to update the internal interface
binding = nsxv_db.get_dhcp_edge_network_binding(context.session, binding = nsxv_db.get_dhcp_edge_network_binding(context.session,
network_id) 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, def _update_internal_interface(nsxv_manager, context, router_id, int_net_id,
address_groups, is_connected=True): address_groups, is_connected=True):
# Get the pg/wire id of the network id # Get the pg/wire id of the network id
mappings = nsx_db.get_nsx_switch_ids(context.session, int_net_id) vcns_network_id = _retrieve_nsx_switch_id(context, int_net_id)
if mappings:
vcns_network_id = mappings[0]
LOG.debug("Network id %(network_id)s corresponding ref is : " LOG.debug("Network id %(network_id)s corresponding ref is : "
"%(net_moref)s", {'network_id': int_net_id, "%(net_moref)s", {'network_id': int_net_id,
'net_moref': vcns_network_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, def _add_vdr_internal_interface(nsxv_manager, context, router_id,
int_net_id, address_groups, is_connected=True): int_net_id, address_groups, is_connected=True):
# Get the pg/wire id of the network id # Get the pg/wire id of the network id
mappings = nsx_db.get_nsx_switch_ids(context.session, int_net_id) vcns_network_id = _retrieve_nsx_switch_id(context, int_net_id)
if mappings:
vcns_network_id = mappings[0]
LOG.debug("Network id %(network_id)s corresponding ref is : " LOG.debug("Network id %(network_id)s corresponding ref is : "
"%(net_moref)s", {'network_id': int_net_id, "%(net_moref)s", {'network_id': int_net_id,
'net_moref': vcns_network_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, int_net_id, address_groups,
is_connected=True): is_connected=True):
# Get the pg/wire id of the network id # Get the pg/wire id of the network id
mappings = nsx_db.get_nsx_switch_ids(context.session, int_net_id) vcns_network_id = _retrieve_nsx_switch_id(context, int_net_id)
if mappings:
vcns_network_id = mappings[0]
LOG.debug("Network id %(network_id)s corresponding ref is : " LOG.debug("Network id %(network_id)s corresponding ref is : "
"%(net_moref)s", {'network_id': int_net_id, "%(net_moref)s", {'network_id': int_net_id,
'net_moref': vcns_network_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, def _delete_interface(nsxv_manager, context, router_id, network_id,
dist=False, is_wait=True): dist=False, is_wait=True):
# Get the pg/wire id of the network id # Get the pg/wire id of the network id
mappings = nsx_db.get_nsx_switch_ids(context.session, network_id) vcns_network_id = _retrieve_nsx_switch_id(context, network_id)
if mappings:
vcns_network_id = mappings[0]
LOG.debug("Network id %(network_id)s corresponding ref is : " LOG.debug("Network id %(network_id)s corresponding ref is : "
"%(net_moref)s", {'network_id': network_id, "%(net_moref)s", {'network_id': network_id,
'net_moref': vcns_network_id}) 'net_moref': vcns_network_id})

View File

@ -640,23 +640,33 @@ class Vcns(object):
return self.do_request(HTTP_GET, uri) 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) @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 uri = '%s/policies/' % SPOOFGUARD_PREFIX
body = {'spoofguardPolicy': body = {'spoofguardPolicy':
{'name': name, {'name': name,
'operationMode': 'MANUAL' if enable else 'DISABLE', 'operationMode': 'MANUAL' if enable else 'DISABLE',
'enforcementPoint':
{'id': enforcement_point,
'type': enforcement_point.split('-')[0]},
'allowLocalIPs': 'true'}} 'allowLocalIPs': 'true'}}
body['spoofguardPolicy'].update(
self._get_enforcement_point_body(enforcement_points))
return self.do_request(HTTP_POST, uri, body, return self.do_request(HTTP_POST, uri, body,
format='xml', encode=True, decode=False) format='xml', encode=True, decode=False)
@retry_upon_exception(exceptions.RequestBad) @retry_upon_exception(exceptions.RequestBad)
def update_spoofguard_policy(self, policy_id, def update_spoofguard_policy(self, policy_id,
enforcement_point, name, enable): enforcement_points, name, enable):
update_uri = '%s/policies/%s' % (SPOOFGUARD_PREFIX, policy_id) update_uri = '%s/policies/%s' % (SPOOFGUARD_PREFIX, policy_id)
publish_uri = '%s/%s?action=publish' % (SPOOFGUARD_PREFIX, policy_id) publish_uri = '%s/%s?action=publish' % (SPOOFGUARD_PREFIX, policy_id)
@ -664,10 +674,9 @@ class Vcns(object):
{'policyId': policy_id, {'policyId': policy_id,
'name': name, 'name': name,
'operationMode': 'MANUAL' if enable else 'DISABLE', 'operationMode': 'MANUAL' if enable else 'DISABLE',
'enforcementPoint':
{'id': enforcement_point,
'type': enforcement_point.split('-')[0]},
'allowLocalIPs': 'true'}} 'allowLocalIPs': 'true'}}
body['spoofguardPolicy'].update(
self._get_enforcement_point_body(enforcement_points))
self.do_request(HTTP_PUT, update_uri, body, self.do_request(HTTP_PUT, update_uri, body,
format='xml', encode=True, decode=False) format='xml', encode=True, decode=False)

View File

@ -125,23 +125,39 @@ class NsxVPluginV2TestCase(test_plugin.NeutronDbPluginV2TestCase):
def test_get_vlan_network_name(self): def test_get_vlan_network_name(self):
p = manager.NeutronManager.get_plugin() p = manager.NeutronManager.get_plugin()
id = uuidutils.generate_uuid() net_id = uuidutils.generate_uuid()
dvs_id = 'dvs-10'
net = {'name': '', net = {'name': '',
'id': id} 'id': net_id}
expected = 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, 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', net = {'name': 'pele',
'id': id} 'id': net_id}
expected = '%s-%s' % ('pele', id) expected = '%s-%s-%s' % (dvs_id, 'pele', net_id)
self.assertEqual(expected, self.assertEqual(expected,
p._get_vlan_network_name(net)) p._get_vlan_network_name(net, dvs_id))
name = 'X' * 500 name = 'X' * 500
net = {'name': name, net = {'name': name,
'id': id} 'id': net_id}
expected = '%s-%s' % (name[:43], id) expected = '%s-%s-%s' % (dvs_id, name[:35], net_id)
self.assertEqual(expected, 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): def test_create_port_anticipating_allocation(self):
with self.network(shared=True) as network: with self.network(shared=True) as network:
@ -301,6 +317,114 @@ class TestNetworksV2(test_plugin.TestNetworksV2, NsxVPluginV2TestCase):
for k, v in expected_same_vlan: for k, v in expected_same_vlan:
self.assertEqual(net1['network'][k], v) 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): def test_create_vxlan_with_tz_provider_network(self):
name = 'provider_net_vxlan' name = 'provider_net_vxlan'
expected = [('subnets', []), ('name', name), ('admin_state_up', True), expected = [('subnets', []), ('name', name), ('admin_state_up', True),

View File

@ -1028,18 +1028,18 @@ class FakeVcns(object):
headers = {'status': 200} headers = {'status': 200}
return (headers, response) return (headers, response)
def create_spoofguard_policy(self, enforcement_point, name, enable): def create_spoofguard_policy(self, enforcement_points, name, enable):
policy = {'name': name, policy = {'name': name,
'enforcement_point': enforcement_point, 'enforcement_point': enforcement_points[0],
'operationMode': 'MANUAL' if enable else 'DISABLE'} 'operationMode': 'MANUAL' if enable else 'DISABLE'}
policy_id = len(self._spoofguard_policies) policy_id = len(self._spoofguard_policies)
self._spoofguard_policies.append(policy) self._spoofguard_policies.append(policy)
return None, policy_id return None, policy_id
def update_spoofguard_policy(self, policy_id, def update_spoofguard_policy(self, policy_id,
enforcement_point, name, enable): enforcement_points, name, enable):
policy = {'name': name, policy = {'name': name,
'enforcement_point': enforcement_point, 'enforcement_point': enforcement_points[0],
'operationMode': 'MANUAL' if enable else 'DISABLE'} 'operationMode': 'MANUAL' if enable else 'DISABLE'}
self._spoofguard_policies[int(policy_id)] = policy self._spoofguard_policies[int(policy_id)] = policy
return None, '' return None, ''