Don't crash when adding duplicate gre allocation

This patch catches DBDuplicateError when initializing ML2 GRE type
driver and allocation already exists in DB. Because current allocations
are queried and then only those that doesn't exist in database are
added, DBDuplicateError should never occur.

But the race can happen when running multiple neutron-servers and one
of servers adds allocations between allocations are queried and added.

Change-Id: I427b7020d61b0d2c06292ff2804ba1f4483696c6
Closes-bug: 1417560
This commit is contained in:
Jakub Libosvar 2015-02-03 14:33:39 +01:00
parent 596db61cf9
commit 50b469fae2
2 changed files with 51 additions and 0 deletions

View File

@ -96,6 +96,16 @@ class GreTypeDriver(type_tunnel.TunnelTypeDriver):
gre_ids |= set(moves.xrange(tun_min, tun_max + 1))
session = db_api.get_session()
try:
self._add_allocation(session, gre_ids)
except db_exc.DBDuplicateEntry:
# in case multiple neutron-servers start allocations could be
# already added by different neutron-server. because this function
# is called only when initializing this type driver, it's safe to
# assume allocations were added.
LOG.warning(_LW("Gre allocations were already created."))
def _add_allocation(self, session, gre_ids):
with session.begin(subtransactions=True):
# remove from table unallocated tunnels not currently allocatable
allocs = (session.query(GreAllocation).all())

View File

@ -15,6 +15,11 @@
import mock
from oslo_db import exception as db_exc
from sqlalchemy.orm import exc as sa_exc
import testtools
from neutron.db import api as db_api
from neutron.plugins.common import constants as p_const
from neutron.plugins.ml2.drivers import type_gre
from neutron.tests.unit.ml2 import test_type_tunnel
@ -27,6 +32,16 @@ HOST_ONE = 'fake_host_one'
HOST_TWO = 'fake_host_two'
def _add_allocation(session, gre_id, allocated=False):
allocation = type_gre.GreAllocation(gre_id=gre_id, allocated=allocated)
allocation.save(session)
def _get_allocation(session, gre_id):
return session.query(type_gre.GreAllocation).filter_by(
gre_id=gre_id).one()
class GreTypeTest(test_type_tunnel.TunnelTypeTestMixin,
testlib_api.SqlTestCase):
DRIVER_CLASS = type_gre.GreTypeDriver
@ -83,6 +98,32 @@ class GreTypeTest(test_type_tunnel.TunnelTypeTestMixin,
endpoints = self.driver.get_endpoints()
self.assertNotIn(TUNNEL_IP_ONE, endpoints)
def test_sync_allocations_entry_added_during_session(self):
with mock.patch.object(self.driver, '_add_allocation',
side_effect=db_exc.DBDuplicateEntry) as (
mock_add_allocation):
self.driver.sync_allocations()
self.assertTrue(mock_add_allocation.called)
def test__add_allocation_not_existing(self):
session = db_api.get_session()
_add_allocation(session, gre_id=1)
self.driver._add_allocation(session, {1, 2})
_get_allocation(session, 2)
def test__add_allocation_existing_allocated_is_kept(self):
session = db_api.get_session()
_add_allocation(session, gre_id=1, allocated=True)
self.driver._add_allocation(session, {2})
_get_allocation(session, 1)
def test__add_allocation_existing_not_allocated_is_removed(self):
session = db_api.get_session()
_add_allocation(session, gre_id=1)
self.driver._add_allocation(session, {2})
with testtools.ExpectedException(sa_exc.NoResultFound):
_get_allocation(session, 1)
class GreTypeMultiRangeTest(test_type_tunnel.TunnelTypeMultiRangeTestMixin,
testlib_api.SqlTestCase):