From e51421f5ca7efcdf55fc719b6fae55430d267763 Mon Sep 17 00:00:00 2001 From: Gary Kotton Date: Wed, 27 Jul 2016 02:29:26 -0700 Subject: [PATCH] NSX|V: don't throw exception when same vnic is configured In the event of a nova retry we should not throw an exception for for a port update Change-Id: Ic4df033952e3dddd170e0fb7b57499750a6155b0 Closes-bug: #1606809 --- vmware_nsx/db/vnic_index_db.py | 42 ++++++++++++++++++- .../tests/unit/extensions/test_vnic_index.py | 38 +++++++++++++++-- 2 files changed, 75 insertions(+), 5 deletions(-) diff --git a/vmware_nsx/db/vnic_index_db.py b/vmware_nsx/db/vnic_index_db.py index 6be2779dd6..b860932673 100644 --- a/vmware_nsx/db/vnic_index_db.py +++ b/vmware_nsx/db/vnic_index_db.py @@ -18,6 +18,7 @@ from sqlalchemy.orm import exc from neutron.api.v2 import attributes as attr from neutron.db import db_base_plugin_v2 +from oslo_db import exception as db_exc from oslo_log import log as logging from vmware_nsx.db import nsxv_models @@ -47,7 +48,14 @@ class VnicIndexDbMixin(object): except exc.NoResultFound: LOG.debug("No record in DB for vnic-index of port %s", port_id) - def _set_port_vnic_index_mapping(self, context, port_id, device_id, index): + def _get_mappings_for_device_id(self, context, device_id): + session = context.session + mappings = (session.query(nsxv_models.NsxvPortIndexMapping). + filter_by(device_id=device_id)) + return mappings + + def _create_port_vnic_index_mapping(self, context, port_id, + device_id, index): """Save the port vnic-index to DB.""" session = context.session with session.begin(subtransactions=True): @@ -55,6 +63,38 @@ class VnicIndexDbMixin(object): port_id=port_id, device_id=device_id, index=index) session.add(index_mapping_model) + def _update_port_vnic_index_mapping(self, context, port_id, + device_id, index): + session = context.session + # delete original entry + query = (session.query(nsxv_models.NsxvPortIndexMapping). + filter_by(device_id=device_id, index=index)) + query.delete() + # create a new one + self._create_port_vnic_index_mapping(context, port_id, device_id, + index) + + def _set_port_vnic_index_mapping(self, context, port_id, device_id, index): + """Save the port vnic-index to DB.""" + try: + self._create_port_vnic_index_mapping(context, port_id, + device_id, index) + except db_exc.DBDuplicateEntry: + # A retry for the nova scheduling could result in this error. + LOG.debug("Entry already exists for %s %s %s", port_id, + device_id, index) + mappings = self._get_mappings_for_device_id(context, device_id) + for mapping in mappings: + if (mapping['port_id'] != port_id and + mapping['index'] == index): + # a new port is using this device - update! + self._update_port_vnic_index_mapping(context, port_id, + device_id, index) + return + if (mapping['port_id'] == port_id and + mapping['index'] != index): + raise + def _delete_port_vnic_index_mapping(self, context, port_id): """Delete the port vnic-index association.""" session = context.session diff --git a/vmware_nsx/tests/unit/extensions/test_vnic_index.py b/vmware_nsx/tests/unit/extensions/test_vnic_index.py index e2c31a3548..b1cbc42d4b 100644 --- a/vmware_nsx/tests/unit/extensions/test_vnic_index.py +++ b/vmware_nsx/tests/unit/extensions/test_vnic_index.py @@ -93,16 +93,46 @@ class VnicIndexDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase): self.assertRaises(d_exc.DBDuplicateEntry, plugin._set_port_vnic_index_mapping, context, port_id, device_id, 1) - # Only one Port can be associated with a specific index on a device - self.assertRaises(d_exc.DBDuplicateEntry, - plugin._set_port_vnic_index_mapping, - context, _uuid(), device_id, vnic_index) # Check that the call for _delete_port_vnic_index remove the row from # the table # TODO(kobis): deletion was removed from port - fix this assert # self.assertIsNone(plugin._get_port_vnic_index(context, port_id)) + def test_vnic_index_db_duplicate(self): + plugin = manager.NeutronManager.get_plugin() + vnic_index = 2 + device_id = _uuid() + context = neutron_context.get_admin_context() + with self.port(device_id=device_id, + device_owner='compute:None') as port: + port_id = port['port']['id'] + res = self._port_index_update(port_id, vnic_index) + self.assertEqual(res['port'][vnicidx.VNIC_INDEX], vnic_index) + plugin._set_port_vnic_index_mapping(context, port_id, device_id, + vnic_index) + + def test_vnic_index_db_duplicate_new_port(self): + plugin = manager.NeutronManager.get_plugin() + vnic_index = 2 + device_id = _uuid() + context = neutron_context.get_admin_context() + with self.port(device_id=device_id, + device_owner='compute:None') as port: + with self.port(device_id=device_id, + device_owner='compute:None') as port1: + port_id = port['port']['id'] + res = self._port_index_update(port_id, vnic_index) + self.assertEqual(res['port'][vnicidx.VNIC_INDEX], vnic_index) + port_id1 = port1['port']['id'] + plugin._set_port_vnic_index_mapping(context, port_id1, + device_id, 2) + self.assertIsNone(plugin._get_port_vnic_index(context, + port_id)) + self.assertEqual(vnic_index, + plugin._get_port_vnic_index(context, + port_id1)) + class TestVnicIndex(VnicIndexDbTestCase): pass