Ensure non-overlapping cidrs in subnetpools with galera

This change enables galera support in _lock_subnetpool[1]. It uses an
update to disallow 2 transactions performing concurrent subnet
allocation in the same subnetpool to succeed: the 2 transactions will
conflict because they update the same row so the db (including Galera
multi-writer cluster) will discard the last transaction and
Controller.create[2] will catch and retry the "discarded" allocation.

This change adds the "hash" attribute in "subnetpools" table to enable
previous update.

[1] neutron.ipam.subnet_alloc.SubnetAllocator
[2] neutron.api.v2.base

Change-Id: I74f7100a6fd9b7787be693adffec15ec468d0018
Closes-Bug: #1451576
This commit is contained in:
Cedric Brandily 2015-05-28 18:35:17 +02:00
parent 53bc7162e1
commit 03b70b1094
4 changed files with 55 additions and 5 deletions

View File

@ -1,3 +1,3 @@
1c844d1677f7
45f955889773
26c371498592
kilo

View File

@ -0,0 +1,35 @@
# Copyright (c) 2015 Thales Services SAS
#
# 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.
#
"""subnetpool hash
Revision ID: 26c371498592
Revises: 45f955889773
Create Date: 2015-06-02 21:18:19.942076
"""
# revision identifiers, used by Alembic.
revision = '26c371498592'
down_revision = '45f955889773'
from alembic import op
import sqlalchemy as sa
def upgrade():
op.add_column(
'subnetpools',
sa.Column('hash', sa.String(36), nullable=False, server_default=''))

View File

@ -244,6 +244,7 @@ class SubnetPool(model_base.BASEV2, HasId, HasTenant):
max_prefixlen = sa.Column(sa.Integer, nullable=False)
shared = sa.Column(sa.Boolean, nullable=False)
default_quota = sa.Column(sa.Integer, nullable=True)
hash = sa.Column(sa.String(36), nullable=False, server_default='')
prefixes = orm.relationship(SubnetPoolPrefix,
backref='subnetpools',
cascade='all, delete, delete-orphan',

View File

@ -17,6 +17,7 @@ import math
import operator
import netaddr
from oslo_db import exception as db_exc
from oslo_utils import uuidutils
from neutron.api.v2 import attributes
@ -46,10 +47,23 @@ class SubnetAllocator(driver.Pool):
subnetpool, it's required to ensure non-overlapping cidrs in the same
subnetpool.
"""
# FIXME(cbrandily): not working with Galera
(self._context.session.query(models_v2.SubnetPool.id).
filter_by(id=self._subnetpool['id']).
with_lockmode('update').first())
current_hash = (self._context.session.query(models_v2.SubnetPool.hash)
.filter_by(id=self._subnetpool['id']).scalar())
if current_hash is None:
# NOTE(cbrandily): subnetpool has been deleted
raise n_exc.SubnetPoolNotFound(
subnetpool_id=self._subnetpool['id'])
new_hash = uuidutils.generate_uuid()
# NOTE(cbrandily): the update disallows 2 concurrent subnet allocation
# to succeed: at most 1 transaction will succeed, others will be
# rollbacked and be caught in neutron.db.v2.base
query = self._context.session.query(models_v2.SubnetPool).filter_by(
id=self._subnetpool['id'], hash=current_hash)
count = query.update({'hash': new_hash})
if not count:
raise db_exc.RetryRequest()
def _get_allocated_cidrs(self):
query = self._context.session.query(models_v2.Subnet)