diff --git a/neutron/db/migration/alembic_migrations/versions/CONTRACT_HEAD b/neutron/db/migration/alembic_migrations/versions/CONTRACT_HEAD index 77434118dfc..b0d0e20a6c9 100644 --- a/neutron/db/migration/alembic_migrations/versions/CONTRACT_HEAD +++ b/neutron/db/migration/alembic_migrations/versions/CONTRACT_HEAD @@ -1 +1 @@ -2e0d7a8a1586 +5c85685d616d diff --git a/neutron/db/migration/alembic_migrations/versions/newton/contract/5c85685d616d_remove_availability_ranges.py b/neutron/db/migration/alembic_migrations/versions/newton/contract/5c85685d616d_remove_availability_ranges.py new file mode 100644 index 00000000000..647a4fed81d --- /dev/null +++ b/neutron/db/migration/alembic_migrations/versions/newton/contract/5c85685d616d_remove_availability_ranges.py @@ -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') diff --git a/neutron/db/models_v2.py b/neutron/db/models_v2.py index 2b9a5e2e60b..6afe13595b1 100644 --- a/neutron/db/models_v2.py +++ b/neutron/db/models_v2.py @@ -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) diff --git a/neutron/ipam/drivers/neutrondb_ipam/db_api.py b/neutron/ipam/drivers/neutrondb_ipam/db_api.py index 134934ce268..10263062ee3 100644 --- a/neutron/ipam/drivers/neutrondb_ipam/db_api.py +++ b/neutron/ipam/drivers/neutrondb_ipam/db_api.py @@ -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( diff --git a/neutron/ipam/drivers/neutrondb_ipam/db_models.py b/neutron/ipam/drivers/neutrondb_ipam/db_models.py index 48e5422a4c1..09a8f39c230 100644 --- a/neutron/ipam/drivers/neutrondb_ipam/db_models.py +++ b/neutron/ipam/drivers/neutrondb_ipam/db_models.py @@ -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) diff --git a/neutron/ipam/drivers/neutrondb_ipam/driver.py b/neutron/ipam/drivers/neutrondb_ipam/driver.py index fdbfd1dd4b3..f9726cbc216 100644 --- a/neutron/ipam/drivers/neutrondb_ipam/driver.py +++ b/neutron/ipam/drivers/neutrondb_ipam/driver.py @@ -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( diff --git a/neutron/ipam/exceptions.py b/neutron/ipam/exceptions.py index 29a4a947cbb..776a13be6bf 100644 --- a/neutron/ipam/exceptions.py +++ b/neutron/ipam/exceptions.py @@ -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 diff --git a/neutron/tests/functional/db/test_ipam.py b/neutron/tests/functional/db/test_ipam.py index 783bd228d4f..c0307d66a48 100644 --- a/neutron/tests/functional/db/test_ipam.py +++ b/neutron/tests/functional/db/test_ipam.py @@ -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'] diff --git a/neutron/tests/unit/ipam/drivers/neutrondb_ipam/test_db_api.py b/neutron/tests/unit/ipam/drivers/neutrondb_ipam/test_db_api.py index fe9e6ff663d..6e9b824536f 100644 --- a/neutron/tests/unit/ipam/drivers/neutrondb_ipam/test_db_api.py +++ b/neutron/tests/unit/ipam/drivers/neutrondb_ipam/test_db_api.py @@ -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))