diff --git a/vmware_nsx/common/utils.py b/vmware_nsx/common/utils.py index 92676f13b6..7822e360d2 100644 --- a/vmware_nsx/common/utils.py +++ b/vmware_nsx/common/utils.py @@ -47,6 +47,7 @@ class NetworkTypes: FLAT = 'flat' VLAN = 'vlan' BRIDGE = 'bridge' + PORTGROUP = 'portgroup' # Allowed network types for the NSX-v Plugin diff --git a/vmware_nsx/db/migration/alembic_migrations/versions/EXPAND_HEAD b/vmware_nsx/db/migration/alembic_migrations/versions/EXPAND_HEAD index 4ab2223bdc..da24b8a003 100644 --- a/vmware_nsx/db/migration/alembic_migrations/versions/EXPAND_HEAD +++ b/vmware_nsx/db/migration/alembic_migrations/versions/EXPAND_HEAD @@ -1 +1 @@ -c644ec62c585 +5e564e781d77 diff --git a/vmware_nsx/db/migration/alembic_migrations/versions/newton/expand/5e564e781d77_add_nsx_binding_type.py b/vmware_nsx/db/migration/alembic_migrations/versions/newton/expand/5e564e781d77_add_nsx_binding_type.py new file mode 100644 index 0000000000..41279eed9a --- /dev/null +++ b/vmware_nsx/db/migration/alembic_migrations/versions/newton/expand/5e564e781d77_add_nsx_binding_type.py @@ -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) diff --git a/vmware_nsx/db/nsx_models.py b/vmware_nsx/db/nsx_models.py index b20e1ab9f5..eba8e2d1cd 100644 --- a/vmware_nsx/db/nsx_models.py +++ b/vmware_nsx/db/nsx_models.py @@ -42,9 +42,9 @@ class TzNetworkBinding(model_base.BASEV2): network_id = sa.Column(sa.String(36), sa.ForeignKey('networks.id', ondelete="CASCADE"), 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', - 'vxlan', + 'vxlan', 'portgroup', name='tz_network_bindings_binding_type'), nullable=False, primary_key=True) phy_uuid = sa.Column(sa.String(36), primary_key=True, default='') diff --git a/vmware_nsx/dvs/dvs.py b/vmware_nsx/dvs/dvs.py index b6b3328ea2..d1bdafd9bc 100644 --- a/vmware_nsx/dvs/dvs.py +++ b/vmware_nsx/dvs/dvs.py @@ -117,7 +117,8 @@ class DvsManager(object): val, ['name']) if len(props) and hasattr(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 return val raise exceptions.NetworkNotFound(net_id=net_id) diff --git a/vmware_nsx/plugins/dvs/plugin.py b/vmware_nsx/plugins/dvs/plugin.py index 330df3c1e3..ab90f5841f 100644 --- a/vmware_nsx/plugins/dvs/plugin.py +++ b/vmware_nsx/plugins/dvs/plugin.py @@ -146,8 +146,13 @@ class NsxDvsV2(addr_pair_db.AllowedAddressPairsMixin, vlan_tag = 0 if net_data.get(pnet.NETWORK_TYPE) == c_utils.NetworkTypes.VLAN: vlan_tag = net_data.get(pnet.SEGMENTATION_ID, 0) - dvs_id = self._dvs_get_id(net_data) - self._dvs.add_port_group(dvs_id, vlan_tag) + + 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) + self._dvs.add_port_group(dvs_id, vlan_tag) try: with context.session.begin(subtransactions=True): @@ -165,7 +170,9 @@ class NsxDvsV2(addr_pair_db.AllowedAddressPairsMixin, except Exception: with excutils.save_and_reraise_exception(): LOG.exception(_LE('Failed to create network')) - self._dvs.delete_port_group(dvs_id) + if (net_data.get(pnet.NETWORK_TYPE) != + c_utils.NetworkTypes.PORTGROUP): + self._dvs.delete_port_group(dvs_id) new_net[pnet.NETWORK_TYPE] = net_data.get(pnet.NETWORK_TYPE) new_net[pnet.PHYSICAL_NETWORK] = 'dvs' @@ -189,16 +196,17 @@ class NsxDvsV2(addr_pair_db.AllowedAddressPairsMixin, "network") raise n_exc.InvalidInput(error_message=err_msg) 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: - err_msg = _("Segmentation ID cannot be specified with " - "flat network type") + err_msg = (_("Segmentation ID cannot be specified with " + "%s network type"), network_type) elif network_type == c_utils.NetworkTypes.VLAN: if not segmentation_id_set: err_msg = _("Segmentation ID must be specified with " "vlan network type") - elif (segmentation_id_set and - not utils.is_valid_vlan_tag(segmentation_id)): + if (segmentation_id_set and + not utils.is_valid_vlan_tag(segmentation_id)): err_msg = (_("%(segmentation_id)s out of range " "(%(min_id)s through %(max_id)s)") % {'segmentation_id': segmentation_id, @@ -219,11 +227,14 @@ class NsxDvsV2(addr_pair_db.AllowedAddressPairsMixin, def _dvs_delete_network(self, context, id): network = self._get_network(context, id) dvs_id = self._dvs_get_id(network) + bindings = nsx_db.get_network_bindings(context.session, id) with context.session.begin(subtransactions=True): nsx_db.delete_network_bindings(context.session, id) super(NsxDvsV2, self).delete_network(context, id) try: - self._dvs.delete_port_group(dvs_id) + if (not bindings or + bindings[0].binding_type != c_utils.NetworkTypes.PORTGROUP): + self._dvs.delete_port_group(dvs_id) except Exception: LOG.exception(_LE('Unable to delete DVS port group %s'), id) self.handle_network_dhcp_access(context, id, action='delete_network') diff --git a/vmware_nsx/tests/unit/dvs/test_plugin.py b/vmware_nsx/tests/unit/dvs/test_plugin.py index 0469f7d97c..d753ee5c5c 100644 --- a/vmware_nsx/tests/unit/dvs/test_plugin.py +++ b/vmware_nsx/tests/unit/dvs/test_plugin.py @@ -161,7 +161,8 @@ class NeutronSimpleDvsTest(test_plugin.NeutronDbPluginV2TestCase): params['arg_list'] = tuple(params.keys()) with mock.patch.object(self._plugin._dvs, '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: ctx = context.get_admin_context() id = network['network']['id'] @@ -175,9 +176,16 @@ class NeutronSimpleDvsTest(test_plugin.NeutronDbPluginV2TestCase): elif network_type == 'vlan': self.assertEqual('vlan', binding[0].binding_type) 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: self.fail() - mock_add.assert_called_once_with(dvs_id, vlan_tag) + if network_type != 'portgroup': + 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): 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): 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): params = {'provider:network_type': 'vlan', 'provider:physical_network': 'dvs',