From 9d492c3d3e8693e3defafc62cc7fa435febcb1e5 Mon Sep 17 00:00:00 2001 From: Adit Sarfaty Date: Sun, 15 Oct 2017 11:46:33 +0300 Subject: [PATCH] NSX|V: Support add/remove dvs for VLAN provider networks Commit I8b1e2dee482504c9a27ab369d158beb3589fb005 added support for adding another dvs to a vlan provider network. This commit changes it a bit to supprot both adding and removing dvs-ids. the list of dvs-ids in teh update command will replace the previous list, deleting/creating backend networks. Change-Id: I0d0ed1fdb40bcae3e34432deabb656a18421d4aa --- vmware_nsx/db/db.py | 5 +++ vmware_nsx/plugins/nsx_v/plugin.py | 50 ++++++++++++++++------ vmware_nsx/tests/unit/nsx_v/test_plugin.py | 47 ++++++++++++++++++-- 3 files changed, 84 insertions(+), 18 deletions(-) diff --git a/vmware_nsx/db/db.py b/vmware_nsx/db/db.py index 85ffe16570..ff7fbd7244 100644 --- a/vmware_nsx/db/db.py +++ b/vmware_nsx/db/db.py @@ -81,6 +81,11 @@ def add_neutron_nsx_network_mapping(session, neutron_id, nsx_switch_id, return mapping +def delete_neutron_nsx_network_mapping(session, neutron_id): + return (session.query(nsx_models.NeutronNsxNetworkMapping). + filter_by(neutron_id=neutron_id).delete()) + + def add_neutron_nsx_port_mapping(session, neutron_id, nsx_switch_id, nsx_port_id): session.begin(subtransactions=True) diff --git a/vmware_nsx/plugins/nsx_v/plugin.py b/vmware_nsx/plugins/nsx_v/plugin.py index e59e9983c3..c5669738b0 100644 --- a/vmware_nsx/plugins/nsx_v/plugin.py +++ b/vmware_nsx/plugins/nsx_v/plugin.py @@ -1487,21 +1487,21 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, return providernet._raise_if_updates_provider_attributes(attrs) - def _update_vlan_network_dvs_ids(self, network, new_physical_network, - az_dvs): + def _update_vlan_network_dvs_ids(self, context, network, + new_physical_network, az_dvs): """Update the dvs ids of a vlan provider network - The new values will be added to the current ones. - No support for removing dvs-ids. + The new values will replace the old ones. Actions done in this function: - Create a backend network for each new dvs + - Delete the backend networks for the old ones. - Return the relevant information in order to later also update the spoofguard policy, qos, network object and DB Returns: - dvs_list_changed True/False - - dvs_pg_mappings - mapping of the new elements dvs->moref + - dvs_pg_mappings - updated mapping of the elements dvs->moref """ dvs_pg_mappings = {} @@ -1509,15 +1509,32 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, network[pnet.PHYSICAL_NETWORK], az_dvs)) new_dvs_ids = set(self._get_dvs_ids( new_physical_network, az_dvs)) - additinal_dvs_ids = new_dvs_ids - current_dvs_ids + additional_dvs_ids = new_dvs_ids - current_dvs_ids + removed_dvs_ids = current_dvs_ids - new_dvs_ids - if not additinal_dvs_ids: + if not additional_dvs_ids and not removed_dvs_ids: + # no changes in the list of DVS return False, dvs_pg_mappings - # create all the new ones - for dvs_id in additinal_dvs_ids: + self._convert_to_transport_zones_dict(network) + # get the current mapping as in the DB + db_mapping = nsx_db.get_nsx_network_mappings( + context.session, network['id']) + for db_map in db_mapping: + dvs_pg_mappings[db_map.dvs_id] = db_map.nsx_id + + # delete old backend networks + for dvs_id in removed_dvs_ids: + nsx_id = dvs_pg_mappings.get(dvs_id) + if nsx_id: + #Note(asarfaty) This may fail if there is a vm deployed, but + # since the delete is done offline we will not catch it here + self._delete_backend_network(nsx_id, dvs_id) + del dvs_pg_mappings[dvs_id] + + # create all the new backend networks + for dvs_id in additional_dvs_ids: try: - self._convert_to_transport_zones_dict(network) net_moref = self._create_vlan_network_at_backend( dvs_id=dvs_id, net_data=network) @@ -1568,12 +1585,12 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, c_utils.NsxVNetworkTypes.VLAN): (updated_morefs, new_dvs_pg_mappings) = self._update_vlan_network_dvs_ids( + context, orig_net, net_attrs[pnet.PHYSICAL_NETWORK], az_dvs) if updated_morefs: - new_dvs = list(new_dvs_pg_mappings.values()) - net_morefs.extend(new_dvs) + net_morefs = list(new_dvs_pg_mappings.values()) with db_api.context_manager.writer.using(context): net_res = super(NsxVPluginV2, self).update_network(context, id, @@ -1585,16 +1602,21 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, self._process_l3_update(context, net_res, net_attrs) self._extend_network_dict_provider(context, net_res) if updated_morefs: + # delete old mapping before recreating all + nsx_db.delete_neutron_nsx_network_mapping( + session=context.session, neutron_id=id) + # Save netmoref to dvs id mappings for VLAN network # type for future access. - all_dvs = net_res.get(pnet.PHYSICAL_NETWORK) + dvs_ids = [] for dvs_id, netmoref in six.iteritems(new_dvs_pg_mappings): nsx_db.add_neutron_nsx_network_mapping( session=context.session, neutron_id=id, nsx_switch_id=netmoref, dvs_id=dvs_id) - all_dvs = '%s, %s' % (all_dvs, dvs_id) + dvs_ids.append(dvs_id) + all_dvs = ', '.join(sorted(dvs_ids)) net_res[pnet.PHYSICAL_NETWORK] = all_dvs vlan_id = net_res.get(pnet.SEGMENTATION_ID) nsxv_db.update_network_binding_phy_uuid( diff --git a/vmware_nsx/tests/unit/nsx_v/test_plugin.py b/vmware_nsx/tests/unit/nsx_v/test_plugin.py index fd8891f723..75aa1fb685 100644 --- a/vmware_nsx/tests/unit/nsx_v/test_plugin.py +++ b/vmware_nsx/tests/unit/nsx_v/test_plugin.py @@ -502,7 +502,7 @@ class TestNetworksV2(test_plugin.TestNetworksV2, NsxVPluginV2TestCase): # physical network attribute. self.assertEqual(2, vlan_net_call.call_count) - def test_update_vlan_network_with_multiple_dvs(self): + def test_update_vlan_network_add_dvs(self): name = 'multi-dvs-vlan-net' providernet_args = {pnet.NETWORK_TYPE: 'vlan', pnet.SEGMENTATION_ID: 100, @@ -510,7 +510,7 @@ class TestNetworksV2(test_plugin.TestNetworksV2, NsxVPluginV2TestCase): p = directory.get_plugin() with mock.patch.object( p, '_create_vlan_network_at_backend', - # Return three netmorefs as side effect + # Return 3 netmorefs as side effect side_effect=[_uuid(), _uuid(), _uuid()]) as vlan_net_call: with self.network(name=name, providernet_args=providernet_args, @@ -524,7 +524,8 @@ class TestNetworksV2(test_plugin.TestNetworksV2, NsxVPluginV2TestCase): self.assertEqual('dvs-1, dvs-2', net['network'][pnet.PHYSICAL_NETWORK]) # Add another dvs - data = {'network': {pnet.PHYSICAL_NETWORK: 'dvs-3'}} + data = {'network': {pnet.PHYSICAL_NETWORK: + 'dvs-1, dvs-2, dvs-3'}} req = self.new_update_request('networks', data, net['network']['id']) res = self.deserialize('json', req.get_response(self.api)) @@ -539,7 +540,6 @@ class TestNetworksV2(test_plugin.TestNetworksV2, NsxVPluginV2TestCase): res['network'][pnet.PHYSICAL_NETWORK]) # update again - with no real change - data = {'network': {pnet.PHYSICAL_NETWORK: 'dvs-3'}} req = self.new_update_request('networks', data, net['network']['id']) res = self.deserialize('json', req.get_response(self.api)) @@ -547,6 +547,45 @@ class TestNetworksV2(test_plugin.TestNetworksV2, NsxVPluginV2TestCase): self.assertEqual('dvs-1, dvs-2, dvs-3', res['network'][pnet.PHYSICAL_NETWORK]) + def test_update_vlan_network_remove_dvs(self): + name = 'multi-dvs-vlan-net' + providernet_args = {pnet.NETWORK_TYPE: 'vlan', + pnet.SEGMENTATION_ID: 100, + pnet.PHYSICAL_NETWORK: 'dvs-1, dvs-2'} + p = directory.get_plugin() + with mock.patch.object( + p, '_create_vlan_network_at_backend', + # Return 2 netmorefs as side effect + side_effect=[_uuid(), _uuid()]) as vlan_net_call,\ + mock.patch.object( + p, '_delete_backend_network') as del_net: + with self.network(name=name, + providernet_args=providernet_args, + arg_list=(pnet.NETWORK_TYPE, + pnet.SEGMENTATION_ID, + pnet.PHYSICAL_NETWORK)) as net: + # _create_vlan_network_at_backend is expected to be called + # 2 times since we have 2 DVS IDs in the physical + # network attribute. + self.assertEqual(2, vlan_net_call.call_count) + self.assertEqual('dvs-1, dvs-2', + net['network'][pnet.PHYSICAL_NETWORK]) + # Keep only dvs-1 (Remove dvs-2) + data = {'network': {pnet.PHYSICAL_NETWORK: 'dvs-1'}} + req = self.new_update_request('networks', data, + net['network']['id']) + res = self.deserialize('json', req.get_response(self.api)) + self.assertEqual(2, vlan_net_call.call_count) + del_net.assert_called_once() + self.assertEqual('dvs-1', + res['network'][pnet.PHYSICAL_NETWORK]) + + # make sure it is updates also in the DB + req = self.new_show_request('networks', net['network']['id']) + res = self.deserialize('json', req.get_response(self.api)) + self.assertEqual('dvs-1', + res['network'][pnet.PHYSICAL_NETWORK]) + def test_get_dvs_ids_for_multiple_dvs_vlan_network(self): p = directory.get_plugin() default_dvs = 'fake_dvs_id'