[dvs] support 'portgroup' provider type

NSX-v plugin has support for provider network type 'portgroup'.
This patch adds support for portgroup type binding in DVS plugin.
Creating a portgroup type network refers to an existing dvportgroup
in vSphere. Deleting this network would not delete the dvportgroup
similar to NSX-v plugin.
This functionality is required to import VMs on vSphere
connected to an existing dvportgroup.

Change-Id: I6fd1f3efdd258b5d4d5042d0f76d0a4b52cd69ee
This commit is contained in:
Giridhar Jayavelu 2016-06-16 18:58:54 -07:00
parent cd041ff985
commit 6d368cb55b
7 changed files with 93 additions and 15 deletions

View File

@ -47,6 +47,7 @@ class NetworkTypes:
FLAT = 'flat' FLAT = 'flat'
VLAN = 'vlan' VLAN = 'vlan'
BRIDGE = 'bridge' BRIDGE = 'bridge'
PORTGROUP = 'portgroup'
# Allowed network types for the NSX-v Plugin # Allowed network types for the NSX-v Plugin

View File

@ -1 +1 @@
c644ec62c585 5e564e781d77

View File

@ -0,0 +1,45 @@
# 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 nsx binding type
Revision ID: 5e564e781d77
Revises: c644ec62c585
Create Date: 2016-06-27 23:58:22.003350
"""
# revision identifiers, used by Alembic.
revision = '5e564e781d77'
down_revision = 'c644ec62c585'
from alembic import op
import sqlalchemy as sa
tz_binding_type_enum = sa.Enum('flat', 'vlan', 'stt', 'gre', 'l3_ext',
'vxlan',
name='tz_network_bindings_binding_type')
new_tz_binding_type_enum = sa.Enum('flat', 'vlan', 'stt', 'gre', 'l3_ext',
'vxlan', 'portgroup',
name='tz_network_bindings_binding_type')
def upgrade():
op.alter_column(
'tz_network_bindings',
'binding_type',
type_=new_tz_binding_type_enum,
existing_type=tz_binding_type_enum,
existing_nullable=False)

View File

@ -42,9 +42,9 @@ class TzNetworkBinding(model_base.BASEV2):
network_id = sa.Column(sa.String(36), network_id = sa.Column(sa.String(36),
sa.ForeignKey('networks.id', ondelete="CASCADE"), sa.ForeignKey('networks.id', ondelete="CASCADE"),
primary_key=True) primary_key=True)
# 'flat', 'vlan', 'stt', 'gre', 'l3_ext', 'vxlan' # 'flat', 'vlan', 'stt', 'gre', 'l3_ext', 'vxlan', 'portgroup'
binding_type = sa.Column(sa.Enum('flat', 'vlan', 'stt', 'gre', 'l3_ext', binding_type = sa.Column(sa.Enum('flat', 'vlan', 'stt', 'gre', 'l3_ext',
'vxlan', 'vxlan', 'portgroup',
name='tz_network_bindings_binding_type'), name='tz_network_bindings_binding_type'),
nullable=False, primary_key=True) nullable=False, primary_key=True)
phy_uuid = sa.Column(sa.String(36), primary_key=True, default='') phy_uuid = sa.Column(sa.String(36), primary_key=True, default='')

View File

@ -117,7 +117,8 @@ class DvsManager(object):
val, ['name']) val, ['name'])
if len(props) and hasattr(props[0], 'propSet'): if len(props) and hasattr(props[0], 'propSet'):
for prop in props[0].propSet: for prop in props[0].propSet:
if net_id == prop.val: # match name or mor id
if net_id == prop.val or net_id == val.value:
# NOTE(garyk): update cache # NOTE(garyk): update cache
return val return val
raise exceptions.NetworkNotFound(net_id=net_id) raise exceptions.NetworkNotFound(net_id=net_id)

View File

@ -146,6 +146,11 @@ class NsxDvsV2(addr_pair_db.AllowedAddressPairsMixin,
vlan_tag = 0 vlan_tag = 0
if net_data.get(pnet.NETWORK_TYPE) == c_utils.NetworkTypes.VLAN: if net_data.get(pnet.NETWORK_TYPE) == c_utils.NetworkTypes.VLAN:
vlan_tag = net_data.get(pnet.SEGMENTATION_ID, 0) vlan_tag = net_data.get(pnet.SEGMENTATION_ID, 0)
if net_data.get(pnet.NETWORK_TYPE) == c_utils.NetworkTypes.PORTGROUP:
net_id = net_data.get(pnet.PHYSICAL_NETWORK)
dvs_id = self._dvs._net_id_to_moref(net_id).value
else:
dvs_id = self._dvs_get_id(net_data) dvs_id = self._dvs_get_id(net_data)
self._dvs.add_port_group(dvs_id, vlan_tag) self._dvs.add_port_group(dvs_id, vlan_tag)
@ -165,6 +170,8 @@ class NsxDvsV2(addr_pair_db.AllowedAddressPairsMixin,
except Exception: except Exception:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
LOG.exception(_LE('Failed to create network')) LOG.exception(_LE('Failed to create network'))
if (net_data.get(pnet.NETWORK_TYPE) !=
c_utils.NetworkTypes.PORTGROUP):
self._dvs.delete_port_group(dvs_id) self._dvs.delete_port_group(dvs_id)
new_net[pnet.NETWORK_TYPE] = net_data.get(pnet.NETWORK_TYPE) new_net[pnet.NETWORK_TYPE] = net_data.get(pnet.NETWORK_TYPE)
@ -189,15 +196,16 @@ class NsxDvsV2(addr_pair_db.AllowedAddressPairsMixin,
"network") "network")
raise n_exc.InvalidInput(error_message=err_msg) raise n_exc.InvalidInput(error_message=err_msg)
err_msg = None err_msg = None
if network_type == c_utils.NetworkTypes.FLAT: if (network_type == c_utils.NetworkTypes.FLAT or
network_type == c_utils.NetworkTypes.PORTGROUP):
if segmentation_id_set: if segmentation_id_set:
err_msg = _("Segmentation ID cannot be specified with " err_msg = (_("Segmentation ID cannot be specified with "
"flat network type") "%s network type"), network_type)
elif network_type == c_utils.NetworkTypes.VLAN: elif network_type == c_utils.NetworkTypes.VLAN:
if not segmentation_id_set: if not segmentation_id_set:
err_msg = _("Segmentation ID must be specified with " err_msg = _("Segmentation ID must be specified with "
"vlan network type") "vlan network type")
elif (segmentation_id_set and if (segmentation_id_set and
not utils.is_valid_vlan_tag(segmentation_id)): not utils.is_valid_vlan_tag(segmentation_id)):
err_msg = (_("%(segmentation_id)s out of range " err_msg = (_("%(segmentation_id)s out of range "
"(%(min_id)s through %(max_id)s)") % "(%(min_id)s through %(max_id)s)") %
@ -219,10 +227,13 @@ class NsxDvsV2(addr_pair_db.AllowedAddressPairsMixin,
def _dvs_delete_network(self, context, id): def _dvs_delete_network(self, context, id):
network = self._get_network(context, id) network = self._get_network(context, id)
dvs_id = self._dvs_get_id(network) dvs_id = self._dvs_get_id(network)
bindings = nsx_db.get_network_bindings(context.session, id)
with context.session.begin(subtransactions=True): with context.session.begin(subtransactions=True):
nsx_db.delete_network_bindings(context.session, id) nsx_db.delete_network_bindings(context.session, id)
super(NsxDvsV2, self).delete_network(context, id) super(NsxDvsV2, self).delete_network(context, id)
try: try:
if (not bindings or
bindings[0].binding_type != c_utils.NetworkTypes.PORTGROUP):
self._dvs.delete_port_group(dvs_id) self._dvs.delete_port_group(dvs_id)
except Exception: except Exception:
LOG.exception(_LE('Unable to delete DVS port group %s'), id) LOG.exception(_LE('Unable to delete DVS port group %s'), id)

View File

@ -161,7 +161,8 @@ class NeutronSimpleDvsTest(test_plugin.NeutronDbPluginV2TestCase):
params['arg_list'] = tuple(params.keys()) params['arg_list'] = tuple(params.keys())
with mock.patch.object(self._plugin._dvs, with mock.patch.object(self._plugin._dvs,
'add_port_group') as mock_add,\ 'add_port_group') as mock_add,\
mock.patch.object(self._plugin._dvs, 'delete_port_group'): mock.patch.object(self._plugin._dvs,
'delete_port_group') as mock_delete:
with self.network(**params) as network: with self.network(**params) as network:
ctx = context.get_admin_context() ctx = context.get_admin_context()
id = network['network']['id'] id = network['network']['id']
@ -175,9 +176,16 @@ class NeutronSimpleDvsTest(test_plugin.NeutronDbPluginV2TestCase):
elif network_type == 'vlan': elif network_type == 'vlan':
self.assertEqual('vlan', binding[0].binding_type) self.assertEqual('vlan', binding[0].binding_type)
self.assertEqual(vlan_tag, binding[0].vlan_id) self.assertEqual(vlan_tag, binding[0].vlan_id)
elif network_type == 'portgroup':
self.assertEqual('portgroup', binding[0].binding_type)
self.assertEqual(0, binding[0].vlan_id)
else: else:
self.fail() self.fail()
if network_type != 'portgroup':
mock_add.assert_called_once_with(dvs_id, vlan_tag) mock_add.assert_called_once_with(dvs_id, vlan_tag)
else:
mock_add.call_count = 0
mock_delete.call_count = 0
def test_create_and_delete_dvs_network_tag(self): def test_create_and_delete_dvs_network_tag(self):
self._create_and_delete_dvs_network(network_type='vlan', vlan_tag=7) self._create_and_delete_dvs_network(network_type='vlan', vlan_tag=7)
@ -185,6 +193,18 @@ class NeutronSimpleDvsTest(test_plugin.NeutronDbPluginV2TestCase):
def test_create_and_delete_dvs_network_flat(self): def test_create_and_delete_dvs_network_flat(self):
self._create_and_delete_dvs_network() self._create_and_delete_dvs_network()
@mock.patch.object(dvs.DvsManager, '_net_id_to_moref')
def test_create_and_delete_dvs_network_portgroup(self, fake_get_moref):
self._create_and_delete_dvs_network(network_type='portgroup')
self.assertTrue(fake_get_moref.call_count)
@mock.patch.object(dvs.DvsManager, '_net_id_to_moref')
def test_create_and_delete_dvs_network_portgroup_vlan(self,
fake_get_moref):
self._create_and_delete_dvs_network(network_type='portgroup',
vlan_tag=7)
self.assertTrue(fake_get_moref.call_count)
def test_create_and_delete_dvs_port(self): def test_create_and_delete_dvs_port(self):
params = {'provider:network_type': 'vlan', params = {'provider:network_type': 'vlan',
'provider:physical_network': 'dvs', 'provider:physical_network': 'dvs',