Merge "Remove availability range code and model"

This commit is contained in:
Jenkins 2016-09-14 02:14:46 +00:00 committed by Gerrit Code Review
commit 2c12add84d
9 changed files with 32 additions and 314 deletions

View File

@ -1 +1 @@
2e0d7a8a1586
5c85685d616d

View File

@ -0,0 +1,24 @@
# 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.
#
"""Remove availability ranges."""
revision = '5c85685d616d'
down_revision = '2e0d7a8a1586'
from alembic import op
def upgrade():
op.drop_table('ipavailabilityranges')
op.drop_table('ipamavailabilityranges')

View File

@ -34,40 +34,6 @@ _deprecate._moved_global('HasId', new_module=model_base)
_deprecate._moved_global('HasStatusDescription', new_module=model_base)
class IPAvailabilityRange(model_base.BASEV2):
"""Internal representation of available IPs for Neutron subnets.
Allocation - first entry from the range will be allocated.
If the first entry is equal to the last entry then this row
will be deleted.
Recycling ips involves reading the IPAllocationPool and IPAllocation tables
and inserting ranges representing available ips. This happens after the
final allocation is pulled from this table and a new ip allocation is
requested. Any contiguous ranges of available ips will be inserted as a
single range.
"""
allocation_pool_id = sa.Column(sa.String(36),
sa.ForeignKey('ipallocationpools.id',
ondelete="CASCADE"),
nullable=False,
primary_key=True)
first_ip = sa.Column(sa.String(64), nullable=False, primary_key=True)
last_ip = sa.Column(sa.String(64), nullable=False, primary_key=True)
__table_args__ = (
sa.UniqueConstraint(
first_ip, allocation_pool_id,
name='uniq_ipavailabilityranges0first_ip0allocation_pool_id'),
sa.UniqueConstraint(
last_ip, allocation_pool_id,
name='uniq_ipavailabilityranges0last_ip0allocation_pool_id'),
model_base.BASEV2.__table_args__
)
def __repr__(self):
return "%s - %s" % (self.first_ip, self.last_ip)
class IPAllocationPool(model_base.BASEV2, model_base.HasId):
"""Representation of an allocation pool in a Neutron subnet."""
@ -76,10 +42,6 @@ class IPAllocationPool(model_base.BASEV2, model_base.HasId):
nullable=True)
first_ip = sa.Column(sa.String(64), nullable=False)
last_ip = sa.Column(sa.String(64), nullable=False)
available_ranges = orm.relationship(IPAvailabilityRange,
backref='ipallocationpool',
lazy="select",
cascade='all, delete-orphan')
def __repr__(self):
return "%s - %s" % (self.first_ip, self.last_ip)

View File

@ -13,12 +13,9 @@
# License for the specific language governing permissions and limitations
# under the License.
from oslo_db import exception as db_exc
from oslo_utils import uuidutils
from sqlalchemy.orm import exc as orm_exc
from neutron.ipam.drivers.neutrondb_ipam import db_models
from neutron.ipam import exceptions as ipam_exc
# Database operations for Neutron's DB-backed IPAM driver
@ -69,7 +66,7 @@ class IpamSubnetManager(object):
neutron_subnet_id=neutron_subnet_id).delete()
def create_pool(self, session, pool_start, pool_end):
"""Create an allocation pool and availability ranges for the subnet.
"""Create an allocation pool for the subnet.
This method does not perform any validation on parameters; it simply
persist data on the database.
@ -83,11 +80,6 @@ class IpamSubnetManager(object):
first_ip=pool_start,
last_ip=pool_end)
session.add(ip_pool)
ip_range = db_models.IpamAvailabilityRange(
allocation_pool=ip_pool,
first_ip=pool_start,
last_ip=pool_end)
session.add(ip_range)
return ip_pool
def delete_allocation_pools(self, session):
@ -104,104 +96,6 @@ class IpamSubnetManager(object):
db_models.IpamAllocationPool).filter_by(
ipam_subnet_id=self._ipam_subnet_id)
def _range_query(self, session):
return session.query(
db_models.IpamAvailabilityRange).join(
db_models.IpamAllocationPool).filter_by(
ipam_subnet_id=self._ipam_subnet_id)
def get_first_range(self, session):
"""Return the first availability range for the subnet
:param session: database session
:return: first available range as instance of
neutron.ipam.drivers.neutrondb_ipam.db_models.IpamAvailabilityRange
"""
return self._range_query(session).first()
def list_ranges_by_subnet_id(self, session):
"""Return availability ranges for a given ipam subnet
:param session: database session
:return: list of availability ranges as instances of
neutron.ipam.drivers.neutrondb_ipam.db_models.IpamAvailabilityRange
"""
return self._range_query(session)
def list_ranges_by_allocation_pool(self, session, allocation_pool_id):
"""Return availability ranges for a given pool.
:param session: database session
:param allocation_pool_id: allocation pool identifier
:return: list of availability ranges as instances of
neutron.ipam.drivers.neutrondb_ipam.db_models.IpamAvailabilityRange
"""
return session.query(
db_models.IpamAvailabilityRange).join(
db_models.IpamAllocationPool).filter_by(
id=allocation_pool_id)
def update_range(self, session, db_range, first_ip=None, last_ip=None):
"""Updates db_range to have new first_ip and last_ip.
:param session: database session
:param db_range: IpamAvailabilityRange db object
:param first_ip: first ip address in range
:param last_ip: last ip address in range
:return: count of updated rows
"""
opts = {}
if first_ip:
opts['first_ip'] = str(first_ip)
if last_ip:
opts['last_ip'] = str(last_ip)
if not opts:
raise ipam_exc.IpamAvailabilityRangeNoChanges()
try:
return session.query(
db_models.IpamAvailabilityRange).filter_by(
allocation_pool_id=db_range.allocation_pool_id).filter_by(
first_ip=db_range.first_ip).filter_by(
last_ip=db_range.last_ip).update(opts)
except orm_exc.ObjectDeletedError:
raise db_exc.RetryRequest(ipam_exc.IPAllocationFailed())
def delete_range(self, session, db_range):
"""Return count of deleted ranges
:param session: database session
:param db_range: IpamAvailabilityRange db object
"""
try:
return session.query(
db_models.IpamAvailabilityRange).filter_by(
allocation_pool_id=db_range.allocation_pool_id).filter_by(
first_ip=db_range.first_ip).filter_by(
last_ip=db_range.last_ip).delete()
except orm_exc.ObjectDeletedError:
raise db_exc.RetryRequest(ipam_exc.IPAllocationFailed())
def create_range(self, session, allocation_pool_id,
range_start, range_end):
"""Create an availability range for a given pool.
This method does not perform any validation on parameters; it simply
persist data on the database.
:param session: database session
:param allocation_pool_id: allocation pool identifier
:param range_start: first ip address in the range
:param range_end: last ip address in the range
:return: the newly created availability range as an instance of
neutron.ipam.drivers.neutrondb_ipam.db_models.IpamAvailabilityRange
"""
new_ip_range = db_models.IpamAvailabilityRange(
allocation_pool_id=allocation_pool_id,
first_ip=range_start,
last_ip=range_end)
session.add(new_ip_range)
return new_ip_range
def check_unique_allocation(self, session, ip_address):
"""Validate that the IP address on the subnet is not in use."""
iprequest = session.query(db_models.IpamAllocation).filter_by(

View File

@ -21,40 +21,6 @@ from sqlalchemy import orm as sa_orm
# Database models used by the neutron DB IPAM driver
# NOTE(salv-orlando): This is meant to replace the class
# neutron.db.models_v2.IPAvailabilityRange.
class IpamAvailabilityRange(model_base.BASEV2):
"""Internal representation of available IPs for Neutron subnets.
Allocation - first entry from the range will be allocated.
If the first entry is equal to the last entry then this row
will be deleted.
Recycling ips involves reading the IPAllocationPool and IPAllocation tables
and inserting ranges representing available ips. This happens after the
final allocation is pulled from this table and a new ip allocation is
requested. Any contiguous ranges of available ips will be inserted as a
single range.
"""
allocation_pool_id = sa.Column(sa.String(36),
sa.ForeignKey('ipamallocationpools.id',
ondelete="CASCADE"),
nullable=False,
primary_key=True)
first_ip = sa.Column(sa.String(64), nullable=False, primary_key=True)
last_ip = sa.Column(sa.String(64), nullable=False, primary_key=True)
__table_args__ = (
sa.Index('ix_ipamavailabilityranges_first_ip_allocation_pool_id',
'first_ip', 'allocation_pool_id'),
sa.Index('ix_ipamavailabilityranges_last_ip_allocation_pool_id',
'last_ip', 'allocation_pool_id'),
model_base.BASEV2.__table_args__
)
def __repr__(self):
return "%s - %s" % (self.first_ip, self.last_ip)
# NOTE(salv-orlando): The following data model creates redundancy with
# models_v2.IPAllocationPool. This level of data redundancy could be tolerated
# considering that the following model is specific to the IPAM driver logic.
@ -72,10 +38,6 @@ class IpamAllocationPool(model_base.BASEV2, model_base.HasId):
nullable=False)
first_ip = sa.Column(sa.String(64), nullable=False)
last_ip = sa.Column(sa.String(64), nullable=False)
available_ranges = sa_orm.relationship(IpamAvailabilityRange,
backref='allocation_pool',
lazy="joined",
cascade='all, delete-orphan')
def __repr__(self):
return "%s - %s" % (self.first_ip, self.last_ip)

View File

@ -39,9 +39,6 @@ class NeutronDbSubnet(ipam_base.Subnet):
This class implements the strategy for IP address allocation and
deallocation for the Neutron DB IPAM driver.
Allocation for IP addresses is based on the concept of availability
ranges, which were already used in Neutron's DB base class for handling
IPAM operations.
"""
@classmethod
@ -72,7 +69,7 @@ class NeutronDbSubnet(ipam_base.Subnet):
subnet_request.gateway_ip)
else:
pools = subnet_request.allocation_pools
# Create IPAM allocation pools and availability ranges
# Create IPAM allocation pools
cls.create_allocation_pools(subnet_manager, session, pools,
subnet_request.subnet_cidr)
@ -208,9 +205,8 @@ class NeutronDbSubnet(ipam_base.Subnet):
def deallocate(self, address):
# This is almost a no-op because the Neutron DB IPAM driver does not
# delete IPAllocation objects, neither rebuilds availability ranges
# at every deallocation. The only operation it performs is to delete
# an IPRequest entry.
# delete IPAllocation objects at every deallocation. The only operation
# it performs is to delete an IPRequest entry.
session = self._context.session
count = self.subnet_manager.delete_allocation(

View File

@ -76,10 +76,6 @@ class IPAllocationFailed(exceptions.NeutronException):
message = _("IP allocation failed. Try again later.")
class IpamAvailabilityRangeNoChanges(exceptions.NeutronException):
message = _("New value for first_ip or last_ip has to be specified.")
class IpamValueInvalid(exceptions.Conflict):
def __init__(self, message=None):
self.message = message

View File

@ -21,7 +21,6 @@ import testtools
from neutron import context
from neutron.db import db_base_plugin_v2 as base_plugin
from neutron.db import models_v2
from neutron.ipam.drivers.neutrondb_ipam import db_models as ipam_models
from neutron.tests.unit import testlib_api
@ -67,13 +66,6 @@ class IpamTestCase(testlib_api.SqlTestCase):
actual = self.result_set_to_dicts(result_set, keys)
self.assertEqual(expected, actual)
def assert_ip_avail_range_matches(self, expected):
result_set = self.cxt.session.query(
ipam_models.IpamAvailabilityRange).all()
keys = ['first_ip', 'last_ip']
actual = self.result_set_to_dicts(result_set, keys)
self.assertEqual(expected, actual)
def assert_ip_alloc_pool_matches(self, expected):
result_set = self.cxt.session.query(models_v2.IPAllocationPool).all()
keys = ['first_ip', 'last_ip', 'subnet_id']

View File

@ -13,16 +13,11 @@
# License for the specific language governing permissions and limitations
# under the License.
import mock
from oslo_utils import uuidutils
from sqlalchemy.orm import exc as orm_exc
from neutron import context
from neutron.db import api as ndb_api
from neutron.ipam.drivers.neutrondb_ipam import db_api
from neutron.ipam.drivers.neutrondb_ipam import db_models
from neutron.ipam import exceptions as ipam_exc
from neutron.tests.unit import testlib_api
@ -61,122 +56,19 @@ class TestIpamSubnetManager(testlib_api.SqlTestCase):
'non-existent')
self.assertEqual(0, count)
def _create_pools(self, pools):
db_pools = []
for pool in pools:
db_pool = self.subnet_manager.create_pool(self.ctx.session,
pool[0],
pool[1])
db_pools.append(db_pool)
return db_pools
def _validate_ips(self, pools, db_pool):
self.assertTrue(
any(pool == (db_pool.first_ip, db_pool.last_ip) for pool in pools))
def test_create_pool(self):
db_pools = self._create_pools([self.single_pool])
self.subnet_manager.create_pool(self.ctx.session,
self.single_pool[0],
self.single_pool[1])
ipam_pool = self.ctx.session.query(db_models.IpamAllocationPool).\
filter_by(ipam_subnet_id=self.ipam_subnet_id).first()
self._validate_ips([self.single_pool], ipam_pool)
range = self.ctx.session.query(db_models.IpamAvailabilityRange).\
filter_by(allocation_pool_id=db_pools[0].id).first()
self._validate_ips([self.single_pool], range)
def test_get_first_range(self):
self._create_pools(self.multi_pool)
range = self.subnet_manager.get_first_range(self.ctx.session)
self._validate_ips(self.multi_pool, range)
def test_list_ranges_by_subnet_id(self):
self._create_pools(self.multi_pool)
db_ranges = self.subnet_manager.list_ranges_by_subnet_id(
self.ctx.session).all()
self.assertEqual(2, len(db_ranges))
self.assertEqual(db_models.IpamAvailabilityRange, type(db_ranges[0]))
def test_list_ranges_by_allocation_pool(self):
db_pools = self._create_pools([self.single_pool])
# generate ids for allocation pools on flush
self.ctx.session.flush()
db_ranges = self.subnet_manager.list_ranges_by_allocation_pool(
self.ctx.session,
db_pools[0].id).all()
self.assertEqual(1, len(db_ranges))
self.assertEqual(db_models.IpamAvailabilityRange, type(db_ranges[0]))
self._validate_ips([self.single_pool], db_ranges[0])
def test_create_range(self):
self._create_pools([self.single_pool])
pool = self.ctx.session.query(db_models.IpamAllocationPool).\
filter_by(ipam_subnet_id=self.ipam_subnet_id).first()
self._validate_ips([self.single_pool], pool)
allocation_pool_id = pool.id
# delete the range
db_range = self.subnet_manager.list_ranges_by_allocation_pool(
self.ctx.session,
pool.id).first()
self._validate_ips([self.single_pool], db_range)
self.ctx.session.delete(db_range)
# create a new range
range_start = '1.2.3.5'
range_end = '1.2.3.9'
new_range = self.subnet_manager.create_range(self.ctx.session,
allocation_pool_id,
range_start,
range_end)
self.assertEqual(range_start, new_range.first_ip)
self.assertEqual(range_end, new_range.last_ip)
def test_update_range(self):
self._create_pools([self.single_pool])
db_range = self.subnet_manager.get_first_range(self.ctx.session)
updated_count = self.subnet_manager.update_range(self.ctx.session,
db_range,
first_ip='1.2.3.6',
last_ip='1.2.3.8')
self.assertEqual(1, updated_count)
def test_update_range_no_new_values(self):
self._create_pools([self.single_pool])
db_range = self.subnet_manager.get_first_range(self.ctx.session)
self.assertRaises(ipam_exc.IpamAvailabilityRangeNoChanges,
self.subnet_manager.update_range,
self.ctx.session, db_range)
def test_update_range_reraise_error(self):
session = mock.Mock()
session.query.side_effect = orm_exc.ObjectDeletedError(None, None)
@ndb_api.retry_db_errors
def go():
self.subnet_manager.update_range(session, mock.Mock(),
first_ip='1.2.3.5')
self.assertRaises(ipam_exc.IPAllocationFailed, go)
def test_delete_range(self):
self._create_pools([self.single_pool])
db_range = self.subnet_manager.get_first_range(self.ctx.session)
deleted_count = self.subnet_manager.delete_range(self.ctx.session,
db_range)
self.assertEqual(1, deleted_count)
def test_delete_range_reraise_error(self):
session = mock.Mock()
session.query.side_effect = orm_exc.ObjectDeletedError(None, None)
@ndb_api.retry_db_errors
def go():
self.subnet_manager.delete_range(session, mock.Mock())
self.assertRaises(ipam_exc.IPAllocationFailed, go)
def test_check_unique_allocation(self):
self.assertTrue(self.subnet_manager.check_unique_allocation(
self.ctx.session, self.subnet_ip))