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
This commit is contained in:
Gary Kotton 2016-07-27 02:29:26 -07:00
parent 91fecee67c
commit e51421f5ca
2 changed files with 75 additions and 5 deletions

View File

@ -18,6 +18,7 @@ from sqlalchemy.orm import exc
from neutron.api.v2 import attributes as attr from neutron.api.v2 import attributes as attr
from neutron.db import db_base_plugin_v2 from neutron.db import db_base_plugin_v2
from oslo_db import exception as db_exc
from oslo_log import log as logging from oslo_log import log as logging
from vmware_nsx.db import nsxv_models from vmware_nsx.db import nsxv_models
@ -47,7 +48,14 @@ class VnicIndexDbMixin(object):
except exc.NoResultFound: except exc.NoResultFound:
LOG.debug("No record in DB for vnic-index of port %s", port_id) 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.""" """Save the port vnic-index to DB."""
session = context.session session = context.session
with session.begin(subtransactions=True): with session.begin(subtransactions=True):
@ -55,6 +63,38 @@ class VnicIndexDbMixin(object):
port_id=port_id, device_id=device_id, index=index) port_id=port_id, device_id=device_id, index=index)
session.add(index_mapping_model) 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): def _delete_port_vnic_index_mapping(self, context, port_id):
"""Delete the port vnic-index association.""" """Delete the port vnic-index association."""
session = context.session session = context.session

View File

@ -93,16 +93,46 @@ class VnicIndexDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
self.assertRaises(d_exc.DBDuplicateEntry, self.assertRaises(d_exc.DBDuplicateEntry,
plugin._set_port_vnic_index_mapping, plugin._set_port_vnic_index_mapping,
context, port_id, device_id, 1) 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 # Check that the call for _delete_port_vnic_index remove the row from
# the table # the table
# TODO(kobis): deletion was removed from port - fix this assert # TODO(kobis): deletion was removed from port - fix this assert
# self.assertIsNone(plugin._get_port_vnic_index(context, port_id)) # 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): class TestVnicIndex(VnicIndexDbTestCase):
pass pass