diff --git a/bin/ip_availability b/bin/ip_availability index 9a292bb..d95057c 100755 --- a/bin/ip_availability +++ b/bin/ip_availability @@ -120,7 +120,9 @@ def get_unused_ips(session, used_ips_counts): ret = defaultdict(int) for tenant_id, subnet in query.all(): net_size = netaddr.IPNetwork(subnet._cidr).size - policy_size = models.IPPolicy.get_ip_policy_cidrs(subnet).size + policy_size = (subnet["ip_policy"].size + if "ip_policy" in subnet + else 0) ret[tenant_id] += net_size - policy_size for tenant_id in used_ips_counts: diff --git a/quark/db/api.py b/quark/db/api.py index 3b6d62f..eee016d 100644 --- a/quark/db/api.py +++ b/quark/db/api.py @@ -16,6 +16,7 @@ import datetime import inspect +import netaddr from neutron.openstack.common import log as logging from neutron.openstack.common import timeutils from neutron.openstack.common import uuidutils @@ -568,10 +569,12 @@ def security_group_rule_delete(context, rule): def ip_policy_create(context, **ip_policy_dict): new_policy = models.IPPolicy() exclude = ip_policy_dict.pop("exclude") + ip_set = netaddr.IPSet() for excluded_cidr in exclude: new_policy["exclude"].append( models.IPPolicyCIDR(cidr=excluded_cidr)) - + ip_set.add(excluded_cidr) + ip_policy_dict["size"] = ip_set.size new_policy.update(ip_policy_dict) new_policy["tenant_id"] = context.tenant_id context.session.add(new_policy) @@ -589,9 +592,12 @@ def ip_policy_update(context, ip_policy, **ip_policy_dict): exclude = ip_policy_dict.pop("exclude", []) if exclude: ip_policy["exclude"] = [] - for excluded_cidr in exclude: - ip_policy["exclude"].append( - models.IPPolicyCIDR(cidr=excluded_cidr)) + ip_set = netaddr.IPSet() + for excluded_cidr in exclude: + ip_policy["exclude"].append( + models.IPPolicyCIDR(cidr=excluded_cidr)) + ip_set.add(excluded_cidr) + ip_policy_dict["size"] = ip_set.size ip_policy.update(ip_policy_dict) context.session.add(ip_policy) diff --git a/quark/db/migration/alembic/versions/28e55acaf366_populate_ip_policy_size.py b/quark/db/migration/alembic/versions/28e55acaf366_populate_ip_policy_size.py new file mode 100644 index 0000000..2ec63e2 --- /dev/null +++ b/quark/db/migration/alembic/versions/28e55acaf366_populate_ip_policy_size.py @@ -0,0 +1,50 @@ +"""Populate IPPolicy.size + +Revision ID: 28e55acaf366 +Revises: 3d22de205729 +Create Date: 2014-08-06 14:50:04.022331 + +""" + +# revision identifiers, used by Alembic. +revision = '28e55acaf366' +down_revision = '3d22de205729' + +from alembic import op +from sqlalchemy.sql import column, select, table +import netaddr +import sqlalchemy as sa + +from quark.db.custom_types import INET + + +def upgrade(): + ip_policy = table('quark_ip_policy', + column('id', sa.String(length=36)), + column('size', INET())) + ip_policy_cidrs = table('quark_ip_policy_cidrs', + column('ip_policy_id', sa.String(length=36)), + column('cidr', sa.String(length=64))) + connection = op.get_bind() + + # 1. Retrieve all ip_policy_cidr rows. + results = connection.execute( + select([ip_policy_cidrs.c.ip_policy_id, ip_policy_cidrs.c.cidr]) + ).fetchall() + + # 2. Determine IPSet for each IP Policy. + ipp = dict() + for ip_policy_id, cidr in results: + if ip_policy_id not in ipp: + ipp[ip_policy_id] = netaddr.IPSet() + ipp[ip_policy_id].add(cidr) + + # 3. Populate size for each IP Policy. + for ip_policy_id in ipp: + connection.execute(ip_policy.update().values( + size=ipp[ip_policy_id].size).where( + ip_policy.c.id == ip_policy_id)) + + +def downgrade(): + raise NotImplementedError() diff --git a/quark/db/migration/alembic/versions/3d22de205729_add_size_to_ip_policy.py b/quark/db/migration/alembic/versions/3d22de205729_add_size_to_ip_policy.py new file mode 100644 index 0000000..88a17ba --- /dev/null +++ b/quark/db/migration/alembic/versions/3d22de205729_add_size_to_ip_policy.py @@ -0,0 +1,24 @@ +"""Add size to quark_ip_policy + +Revision ID: 3d22de205729 +Revises: 2748e48cee3a +Create Date: 2014-07-04 23:53:09.531715 + +""" + +# revision identifiers, used by Alembic. +revision = '3d22de205729' +down_revision = '3ed0c5a067f1' + +from alembic import op +import sqlalchemy as sa + +from quark.db.custom_types import INET + + +def upgrade(): + op.add_column('quark_ip_policy', sa.Column('size', INET())) + + +def downgrade(): + op.drop_column('quark_ip_policy', 'size') diff --git a/quark/db/migration/alembic/versions/5927940a466e_create_shared_ips_columns.py b/quark/db/migration/alembic/versions/5927940a466e_create_shared_ips_columns.py index 5e17c4c..e9049af 100644 --- a/quark/db/migration/alembic/versions/5927940a466e_create_shared_ips_columns.py +++ b/quark/db/migration/alembic/versions/5927940a466e_create_shared_ips_columns.py @@ -19,12 +19,12 @@ def upgrade(): sa.Column('enabled', sa.Boolean(), nullable=False, - default=True)) + server_default=sa.sql.expression.true())) op.add_column('quark_mac_address_ranges', sa.Column('do_not_use', sa.Boolean(), nullable=False, - default=False)) + server_default=sa.sql.expression.false())) def downgrade(): diff --git a/quark/db/migration/alembic/versions/HEAD b/quark/db/migration/alembic/versions/HEAD index 97a1bb6..8135f23 100644 --- a/quark/db/migration/alembic/versions/HEAD +++ b/quark/db/migration/alembic/versions/HEAD @@ -1 +1 @@ -3ed0c5a067f1 \ No newline at end of file +28e55acaf366 \ No newline at end of file diff --git a/quark/db/models.py b/quark/db/models.py index 95de819..38d24b7 100644 --- a/quark/db/models.py +++ b/quark/db/models.py @@ -374,6 +374,7 @@ class IPPolicy(BASEV2, models.HasId, models.HasTenant): backref="ip_policy") name = sa.Column(sa.String(255), nullable=True) description = sa.Column(sa.String(255), nullable=True) + size = sa.Column(custom_types.INET()) @staticmethod def get_ip_policy_cidrs(subnet): diff --git a/quark/ipam.py b/quark/ipam.py index 6579694..d71f2c2 100644 --- a/quark/ipam.py +++ b/quark/ipam.py @@ -490,14 +490,13 @@ class QuarkIpam(object): ip_addr=ip_address, subnet_id=subnet["id"]) continue - ip_policy_cidrs = None + ip_policy = None if not ip_address: # Policies don't prevent explicit assignment, so we only # need to check if we're allocating a new IP - ip_policy_cidrs = models.IPPolicy.get_ip_policy_cidrs( - subnet) + ip_policy = subnet.get("ip_policy") - policy_size = ip_policy_cidrs.size if ip_policy_cidrs else 0 + policy_size = ip_policy["size"] if ip_policy else 0 if ipnet.size > (ips_in_subnet + policy_size - 1): if not ip_address: diff --git a/quark/tests/functional/test_ip_policies.py b/quark/tests/functional/test_ip_policies.py new file mode 100644 index 0000000..aa8c4ab --- /dev/null +++ b/quark/tests/functional/test_ip_policies.py @@ -0,0 +1,40 @@ +from neutron import context +from neutron.db import api as neutron_db_api +from oslo.config import cfg +import unittest2 + +from quark.db import api as db_api +from quark.db import models + + +class QuarkIPPoliciesFunctionalTest(unittest2.TestCase): + def setUp(self): + self.context = context.Context('fake', 'fake', is_admin=False) + super(QuarkIPPoliciesFunctionalTest, self).setUp() + + cfg.CONF.set_override('connection', 'sqlite://', 'database') + neutron_db_api.configure_db() + neutron_db_api.register_models(models.BASEV2) + + def tearDown(self): + neutron_db_api.unregister_models(models.BASEV2) + neutron_db_api.clear_db() + + +class QuarkIPPoliciesSizeTest(QuarkIPPoliciesFunctionalTest): + def test_ip_policies_create(self): + ip_policy_dict = dict( + exclude=["192.168.10.0/32", "192.168.10.255/32"]) + ip_policy = db_api.ip_policy_create(self.context, **ip_policy_dict) + self.assertEqual(ip_policy["size"], 2) + + def test_ip_policies_update(self): + ip_policy_dict = dict( + exclude=["192.168.10.0/32", "192.168.10.255/32"]) + ip_policy = db_api.ip_policy_create(self.context, **ip_policy_dict) + ip_policy_update_dict = dict( + exclude=["192.168.10.0/32", "192.168.10.13/32", + "192.168.10.255/32"]) + updated_ip_policy = db_api.ip_policy_update( + self.context, ip_policy, **ip_policy_update_dict) + self.assertEqual(updated_ip_policy["size"], 3) diff --git a/quark/tests/test_ipam.py b/quark/tests/test_ipam.py index 05f2951..5cc71dd 100644 --- a/quark/tests/test_ipam.py +++ b/quark/tests/test_ipam.py @@ -348,12 +348,12 @@ class QuarkIpamTestBothIpAllocation(QuarkIpamBaseTest): def test_allocate_new_ip_address_two_empty_subnets(self): subnet4 = dict(id=1, first_ip=0, last_ip=255, cidr="0.0.0.0/24", ip_version=4, - next_auto_assign_ip=1, network=dict(ip_policy=None), + next_auto_assign_ip=1, ip_policy=None) subnet6 = dict(id=1, first_ip=self.v6_fip.value, last_ip=self.v6_lip.value, cidr="feed::/104", ip_version=6, next_auto_assign_ip=self.v6_fip.value + 1, - network=dict(ip_policy=None), ip_policy=None) + ip_policy=None) with self._stubs(subnets=[[(subnet4, 0)], [(subnet6, 0)]], addresses=[None, None, None, None]): address = [] @@ -367,7 +367,7 @@ class QuarkIpamTestBothIpAllocation(QuarkIpamBaseTest): def test_allocate_new_ip_address_one_v4_subnet_open(self): subnet4 = dict(id=1, first_ip=0, last_ip=255, cidr="0.0.0.0/24", ip_version=4, - next_auto_assign_ip=2, network=dict(ip_policy=None), + next_auto_assign_ip=2, ip_policy=None) with self._stubs(subnets=[[(subnet4, 0)], []], addresses=[None, None, None, None]): @@ -380,7 +380,7 @@ class QuarkIpamTestBothIpAllocation(QuarkIpamBaseTest): subnet6 = dict(id=1, first_ip=self.v6_fip.value, last_ip=self.v6_lip.value, cidr="feed::/104", ip_version=6, next_auto_assign_ip=self.v6_fip.value + 1, - network=dict(ip_policy=None), ip_policy=None) + ip_policy=None) with self._stubs(subnets=[[], [(subnet6, 0)]], addresses=[None, None, None, None]): address = [] @@ -402,7 +402,6 @@ class QuarkIpamTestBothIpAllocation(QuarkIpamBaseTest): subnet6 = dict(id=1, first_ip=fip, last_ip=lip, cidr="feed::/104", ip_version=6, next_auto_assign_ip=fip + 1, - network=dict(ip_policy=None), ip_policy=None) network = netaddr.IPNetwork("0.0.0.0/24") first = network.ipv6().first @@ -427,13 +426,13 @@ class QuarkIpamTestBothIpAllocation(QuarkIpamBaseTest): def test_reallocate_deallocated_v4_ip_passed_subnets(self): subnet4 = dict(id=1, first_ip=0, last_ip=255, cidr="0.0.0.0/24", ip_version=4, - next_auto_assign_ip=0, network=dict(ip_policy=None), + next_auto_assign_ip=0, ip_policy=None) subnet6 = dict(id=1, first_ip=self.v6_fip.value, last_ip=self.v6_lip.value, cidr="feed::/104", ip_version=6, next_auto_assign_ip=self.v6_fip.value + 1, - network=dict(ip_policy=None), ip_policy=None) + ip_policy=None) address = models.IPAddress() address["address"] = self.v46_val @@ -454,7 +453,7 @@ class QuarkIpamTestBothIpAllocation(QuarkIpamBaseTest): subnet6 = dict(id=1, first_ip=self.v6_fip.value, last_ip=self.v6_lip.value, cidr="feed::/104", ip_version=6, next_auto_assign_ip=self.v6_fip.value + 1, - network=dict(ip_policy=None), ip_policy=None) + ip_policy=None) address = models.IPAddress() address["address"] = self.v46_val address["version"] = 4 @@ -493,7 +492,7 @@ class QuarkIpamTestBothIpAllocation(QuarkIpamBaseTest): def test_reallocate_deallocated_v6_ip(self): subnet4 = dict(id=1, first_ip=0, last_ip=255, cidr="0.0.0.0/24", ip_version=4, - next_auto_assign_ip=1, network=dict(ip_policy=None), + next_auto_assign_ip=1, ip_policy=None) address = models.IPAddress() address["address"] = netaddr.IPAddress(4).ipv6() @@ -514,7 +513,7 @@ class QuarkIpamTestBothIpAllocation(QuarkIpamBaseTest): subnet6 = dict(id=1, first_ip=self.v6_fip.value, last_ip=self.v6_lip.value, cidr="feed::/104", ip_version=6, next_auto_assign_ip=0, - network=dict(ip_policy=None), ip_policy=None) + ip_policy=None) address = models.IPAddress() address["address"] = self.v46_val @@ -543,8 +542,7 @@ class QuarkIpamTestBothIpAllocation(QuarkIpamBaseTest): ip_version=6, ip_policy=None, last_ip=self.v6_lip.value, - next_auto_assign_ip=0, - network=dict(ip_policy=None)) + next_auto_assign_ip=0) address = models.IPAddress() address["address"] = self.v46_val @@ -568,7 +566,7 @@ class QuarkIpamTestBothIpAllocation(QuarkIpamBaseTest): def test_reallocate_deallocated_v6_ip_as_string_address(self): subnet4 = dict(id=1, first_ip=0, last_ip=255, cidr="0.0.0.0/24", ip_version=4, - next_auto_assign_ip=1, network=dict(ip_policy=None), + next_auto_assign_ip=1, ip_policy=None) address = models.IPAddress() address["address"] = str(self.v46_val) @@ -629,12 +627,12 @@ class QuarkIpamTestBothRequiredIpAllocation(QuarkIpamBaseTest): def test_allocate_new_ip_address_two_empty_subnets(self): subnet4 = dict(id=1, first_ip=0, last_ip=255, cidr="0.0.0.0/24", ip_version=4, - next_auto_assign_ip=1, network=dict(ip_policy=None), + next_auto_assign_ip=1, ip_policy=None) subnet6 = dict(id=1, first_ip=self.v6_fip.value, last_ip=self.v6_lip.value, cidr="feed::/104", ip_version=6, next_auto_assign_ip=self.v6_fip.value + 1, - network=dict(ip_policy=None), ip_policy=None) + ip_policy=None) with self._stubs(subnets=[[(subnet4, 0)], [(subnet6, 0)]], addresses=[None, None, None, None]): address = [] @@ -648,7 +646,7 @@ class QuarkIpamTestBothRequiredIpAllocation(QuarkIpamBaseTest): def test_allocate_new_ip_address_one_v4_subnet_open(self): subnet4 = dict(id=1, first_ip=0, last_ip=255, cidr="0.0.0.0/24", ip_version=4, - next_auto_assign_ip=2, network=dict(ip_policy=None), + next_auto_assign_ip=2, ip_policy=None) with self._stubs(subnets=[[(subnet4, 0)], []], addresses=[None, None, None, None]): @@ -658,7 +656,7 @@ class QuarkIpamTestBothRequiredIpAllocation(QuarkIpamBaseTest): def test_allocate_new_ip_address_one_v6_subnet_open(self): subnet6 = dict(id=1, first_ip=self.v6_fip, last_ip=self.v6_lip, cidr="feed::/104", ip_version=6, - next_auto_assign_ip=2, network=dict(ip_policy=None), + next_auto_assign_ip=2, ip_policy=None) with self._stubs(subnets=[[], [(subnet6, 0)]], addresses=[None, None, None, None]): @@ -676,7 +674,7 @@ class QuarkIpamTestBothRequiredIpAllocation(QuarkIpamBaseTest): subnet6 = dict(id=66, first_ip=self.v6_fip.value, last_ip=self.v6_lip.value, cidr="feed::/104", ip_version=6, next_auto_assign_ip=self.v6_fip.value + 1, - network=dict(ip_policy=None), ip_policy=None) + ip_policy=None) address = models.IPAddress() address["address"] = 4 address["version"] = 4 @@ -694,7 +692,7 @@ class QuarkIpamTestBothRequiredIpAllocation(QuarkIpamBaseTest): def test_reallocate_deallocated_v6_ip(self): subnet4 = dict(id=1, first_ip=0, last_ip=255, cidr="0.0.0.0/24", ip_version=4, next_auto_assign_ip=1, - network=dict(ip_policy=None), ip_policy=None) + ip_policy=None) address = models.IPAddress() address["address"] = 4 address["version"] = 6 @@ -735,15 +733,16 @@ class QuarkIpamTestBothRequiredIpAllocation(QuarkIpamBaseTest): subnet4 = dict(id=1, first_ip=0, last_ip=255, cidr="0.0.0.0/24", ip_version=4, - next_auto_assign_ip=1, network=dict(ip_policy=None), + next_auto_assign_ip=1, ip_policy=None) subnet6 = dict(id=1, first_ip=self.v6_fip.value, last_ip=self.v6_lip.value, cidr="feed::/104", ip_version=6, next_auto_assign_ip=-2, - network=dict(ip_policy=None), - ip_policy=dict(exclude=[ - models.IPPolicyCIDR(cidr="feed::/128"), - models.IPPolicyCIDR(cidr="feed::ff:ffff/128")])) + ip_policy=dict( + size=2, + exclude=[ + models.IPPolicyCIDR(cidr="feed::/128"), + models.IPPolicyCIDR(cidr="feed::ff:ffff/128")])) with self._stubs(subnets=[[(subnet4, 0)], [(subnet6, 0)]], addresses=[None, None, None, None]): @@ -826,7 +825,7 @@ class QuarkIpamAllocateFromV6Subnet(QuarkIpamBaseTest): port_id = "945af340-ed34-4fec-8c87-853a2df492b4" subnet6 = dict(id=1, first_ip=0, last_ip=0, cidr="feed::/104", ip_version=6, - next_auto_assign_ip=0, network=dict(ip_policy=None), + next_auto_assign_ip=0, ip_policy=None) mac = models.MacAddress() @@ -848,7 +847,7 @@ class QuarkIpamAllocateFromV6Subnet(QuarkIpamBaseTest): port_id = "945af340-ed34-4fec-8c87-853a2df492b4" subnet6 = dict(id=1, first_ip=0, last_ip=0, cidr="feed::/104", ip_version=6, - next_auto_assign_ip=0, network=dict(ip_policy=None), + next_auto_assign_ip=0, ip_policy=None) mac = models.MacAddress() @@ -897,7 +896,7 @@ class QuarkIpamAllocateV6IPGeneration(QuarkIpamBaseTest): port_id = "945af340-ed34-4fec-8c87-853a2df492b4" subnet6 = dict(id=1, first_ip=0, last_ip=0, cidr="feed::/104", ip_version=6, - next_auto_assign_ip=0, network=dict(ip_policy=None), + next_auto_assign_ip=0, ip_policy=None) ip1 = {"address": netaddr.IPAddress("fe80::").value, @@ -934,8 +933,8 @@ class QuarkNewIPAddressAllocation(QuarkIpamBaseTest): def test_allocate_new_ip_address_in_empty_range(self): subnet = dict(id=1, first_ip=0, last_ip=255, cidr="0.0.0.0/24", ip_version=4, - next_auto_assign_ip=0, network=dict(ip_policy=None), - ip_policy=dict(exclude=[ + next_auto_assign_ip=0, + ip_policy=dict(size=1, exclude=[ models.IPPolicyCIDR(cidr="0.0.0.0/32")])) with self._stubs(subnets=[(subnet, 0)], addresses=[None, None]): address = [] @@ -947,14 +946,15 @@ class QuarkNewIPAddressAllocation(QuarkIpamBaseTest): def test_allocate_ip_one_full_one_open_subnet(self): subnet1 = dict(id=1, first_ip=0, last_ip=0, cidr="0.0.0.0/32", ip_version=4, - next_auto_assign_ip=0, network=dict(ip_policy=None), - ip_policy=dict(exclude=[ + next_auto_assign_ip=0, + ip_policy=dict(size=1, exclude=[ models.IPPolicyCIDR(cidr="0.0.0.0/32")])) subnet2 = dict(id=2, first_ip=256, last_ip=512, cidr="0.0.1.0/24", ip_version=4, - next_auto_assign_ip=256, network=dict(ip_policy=None), - ip_policy=dict(exclude=[ - models.IPPolicyCIDR(cidr="0.0.1.0/32")])) + next_auto_assign_ip=256, + ip_policy=dict( + size=1, + exclude=[models.IPPolicyCIDR(cidr="0.0.1.0/32")])) subnets = [(subnet1, 1), (subnet2, 0)] with self._stubs(subnets=subnets, addresses=[None, None]): address = [] @@ -971,9 +971,9 @@ class QuarkNewIPAddressAllocation(QuarkIpamBaseTest): def test_allocate_ip_no_available_subnet_fails(self): subnet1 = dict(id=1, first_ip=0, last_ip=0, next_auto_assign_ip=0, cidr="0.0.0.0/32", ip_version=4, - network=dict(ip_policy=None), - ip_policy=dict(exclude=[ - models.IPPolicyCIDR(cidr="0.0.0.0/32")])) + ip_policy=dict( + size=1, + exclude=[models.IPPolicyCIDR(cidr="0.0.0.0/32")])) with self._stubs(subnets=[(subnet1, 1)]): with self.assertRaises(exceptions.IpAddressGenerationFailure): self.ipam.allocate_ip_address(self.context, [], 0, 0, 0) @@ -981,13 +981,13 @@ class QuarkNewIPAddressAllocation(QuarkIpamBaseTest): def test_allocate_ip_two_open_subnets_choses_first(self): subnet1 = dict(id=1, first_ip=0, last_ip=255, cidr="0.0.0.0/24", ip_version=4, - next_auto_assign_ip=0, network=dict(ip_policy=None), - ip_policy=dict(exclude=[ + next_auto_assign_ip=0, + ip_policy=dict(size=1, exclude=[ models.IPPolicyCIDR(cidr="0.0.0.0/32")])) subnet2 = dict(id=2, first_ip=256, last_ip=510, cidr="0.0.1.0/24", ip_version=4, - next_auto_assign_ip=0, network=dict(ip_policy=None), - ip_policy=dict(exclude=[ + next_auto_assign_ip=0, + ip_policy=dict(size=1, exclude=[ models.IPPolicyCIDR(cidr="0.0.1.0/32")])) subnets = [(subnet1, 1), (subnet2, 1)] with self._stubs(subnets=subnets, addresses=[None, None]): @@ -1000,7 +1000,6 @@ class QuarkNewIPAddressAllocation(QuarkIpamBaseTest): def test_find_requested_ip_subnet(self): subnet1 = dict(id=1, first_ip=0, last_ip=255, cidr="0.0.0.0/24", ip_version=4, - network=dict(ip_policy=None), ip_policy=None) subnets = [(subnet1, 1)] with self._stubs(subnets=subnets, addresses=[None, None]): @@ -1039,7 +1038,7 @@ class QuarkIPAddressAllocationTestRetries(QuarkIpamBaseTest): def test_allocate_allocated_ip_fails_and_retries(self): subnet1 = dict(id=1, first_ip=0, last_ip=255, next_auto_assign_ip=1, cidr="0.0.0.0/24", ip_version=4, - network=dict(ip_policy=None), ip_policy=None) + ip_policy=None) subnets = [(subnet1, 1)] addr_found = dict(id=1, address=2) with self._stubs(subnets=subnets, @@ -1053,7 +1052,7 @@ class QuarkIPAddressAllocationTestRetries(QuarkIpamBaseTest): def test_allocate_explicit_already_allocated_fails_and_retries(self): subnet1 = dict(id=1, first_ip=0, last_ip=255, next_auto_assign_ip=1, cidr="0.0.0.0/24", ip_version=4, - network=dict(ip_policy=None), ip_policy=None) + ip_policy=None) subnets = [(subnet1, 1), (subnet1, 1)] addr_found = dict(id=1, address=1) with self._stubs(subnets=subnets, @@ -1066,7 +1065,7 @@ class QuarkIPAddressAllocationTestRetries(QuarkIpamBaseTest): def test_allocate_implicit_already_allocated_fails_and_retries(self): subnet1 = dict(id=1, first_ip=0, last_ip=255, next_auto_assign_ip=1, cidr="::/64", ip_version=6, - network=dict(ip_policy=None), ip_policy=None) + ip_policy=None) subnets = [(subnet1, 1), (subnet1, 1)] addr_found = dict(id=1, address=1) @@ -1083,7 +1082,7 @@ class QuarkIPAddressAllocationTestRetries(QuarkIpamBaseTest): def test_allocate_specific_subnet_ip_not_in_subnet_fails(self): subnet1 = dict(id=1, first_ip=0, last_ip=255, next_auto_assign_ip=1, cidr="0.0.0.0/24", ip_version=4, - network=dict(ip_policy=None), ip_policy=None) + ip_policy=None) subnets = [(subnet1, 1), (subnet1, 1)] addr_found = dict(id=1, address=256) with self._stubs(subnets=subnets, @@ -1097,7 +1096,7 @@ class QuarkIPAddressAllocationTestRetries(QuarkIpamBaseTest): def test_allocate_specific_subnet_unusable_fails(self): subnet1 = dict(id=1, first_ip=0, last_ip=255, next_auto_assign_ip=1, cidr="0.0.0.0/24", ip_version=4, - network=dict(ip_policy=None), ip_policy=None, + ip_policy=None, do_not_use=1) subnets = [] addr_found = dict(id=1, address=256) @@ -1112,7 +1111,7 @@ class QuarkIPAddressAllocationTestRetries(QuarkIpamBaseTest): def test_allocate_last_ip_closes_subnet(self): subnet1 = dict(id=1, first_ip=0, last_ip=1, next_auto_assign_ip=1, cidr="0.0.0.0/24", ip_version=4, - network=dict(ip_policy=None), ip_policy=None) + ip_policy=None) subnets = [(subnet1, 1)] addr_found = dict(id=1, address=1) with self._stubs(subnets=subnets, address=[addr_found]): @@ -1157,8 +1156,7 @@ class QuarkIPAddressAllocateDeallocated(QuarkIpamBaseTest): def test_allocate_finds_deallocated_ip_out_of_range_deletes(self): subnet = dict(id=1, ip_version=4, next_auto_assign_ip=2, - cidr="0.0.0.0/29", ip_policy=None, - network=dict(ip_policy=None)) + cidr="0.0.0.0/29", ip_policy=None) address = dict(id=1, address=254) address2 = dict(id=1, address=1) address["subnet"] = subnet @@ -1175,7 +1173,7 @@ class QuarkIPAddressAllocateDeallocated(QuarkIpamBaseTest): def test_allocate_finds_no_deallocated_creates_new_ip(self): subnet = dict(id=1, ip_version=4, next_auto_assign_ip=2, cidr="0.0.0.0/24", first_ip=0, last_ip=255, - ip_policy=None, network=dict(ip_policy=None)) + ip_policy=None) address = dict(id=1, address=0) addresses_found = [None, address, None] with self._stubs( @@ -1210,8 +1208,7 @@ class TestQuarkIpPoliciesIpAllocation(QuarkIpamBaseTest): subnet = dict(id=1, first_ip=first, last_ip=last, cidr="192.168.0.0/24", ip_version=4, next_auto_assign_ip=first, - network=dict(ip_policy=None), - ip_policy=dict(exclude=[ + ip_policy=dict(size=1, exclude=[ models.IPPolicyCIDR(cidr="192.168.0.0/32")])) with self._stubs(subnets=[(subnet, 0)], addresses=[None, None]): address = [] @@ -1222,8 +1219,8 @@ class TestQuarkIpPoliciesIpAllocation(QuarkIpamBaseTest): def test_subnet_full_based_on_ip_policy(self): subnet = dict(id=1, first_ip=0, last_ip=255, cidr="0.0.0.0/24", ip_version=4, - next_auto_assign_ip=0, network=dict(ip_policy=None), - ip_policy=dict(exclude=[ + next_auto_assign_ip=0, + ip_policy=dict(size=256, exclude=[ models.IPPolicyCIDR(cidr="0.0.0.0/24")])) with self._stubs(subnets=[(subnet, 0)], addresses=[None, None]): with self.assertRaises(exceptions.IpAddressGenerationFailure): @@ -1235,8 +1232,8 @@ class TestQuarkIpPoliciesIpAllocation(QuarkIpamBaseTest): cfg.CONF.set_override('ip_address_retry_max', 3, 'QUARK') subnet = dict(id=1, first_ip=0, last_ip=255, cidr="0.0.0.0/24", ip_version=4, - next_auto_assign_ip=0, network=dict(ip_policy=None), - ip_policy=dict(exclude=[ + next_auto_assign_ip=0, + ip_policy=dict(size=2, exclude=[ models.IPPolicyCIDR(cidr="0.0.0.0/31")])) with self._stubs(subnets=[(subnet, 0)], addresses=[None, None]): address = [] @@ -1247,12 +1244,10 @@ class TestQuarkIpPoliciesIpAllocation(QuarkIpamBaseTest): cfg.CONF.set_override('ip_address_retry_max', old_override, 'QUARK') def test_ip_policy_on_both_subnet_preferred(self): - net = dict(ip_policy=dict(exclude=[ - models.IPPolicyCIDR(cidr="0.0.0.0/31")])) subnet = dict(id=1, first_ip=0, last_ip=255, cidr="0.0.0.0/24", ip_version=4, - next_auto_assign_ip=0, network=net, - ip_policy=dict(exclude=[ + next_auto_assign_ip=0, + ip_policy=dict(size=1, exclude=[ models.IPPolicyCIDR(cidr="0.0.0.0/32")])) with self._stubs(subnets=[(subnet, 0)], addresses=[None, None]): address = [] @@ -1264,7 +1259,6 @@ class TestQuarkIpPoliciesIpAllocation(QuarkIpamBaseTest): def test_ip_policy_allows_specified_ip(self): subnet1 = dict(id=1, first_ip=0, last_ip=255, cidr="0.0.0.0/24", ip_version=4, - network=dict(ip_policy=None), ip_policy=dict(exclude=[ models.IPPolicyCIDR(cidr="0.0.0.240/32")])) subnets = [(subnet1, 1)] @@ -1299,7 +1293,7 @@ class QuarkIPAddressAllocationNotifications(QuarkIpamBaseTest): def test_allocation_notification(self): subnet = dict(id=1, first_ip=0, last_ip=255, cidr="0.0.0.0/24", ip_version=4, - next_auto_assign_ip=0, network=dict(ip_policy=None), + next_auto_assign_ip=0, ip_policy=None) address = dict(address=0, created_at="123", subnet_id=1, address_readable="0.0.0.0", used_by_tenant_id=1) diff --git a/quark/tests/test_migrations.py b/quark/tests/test_migrations.py index d82c871..9254072 100644 --- a/quark/tests/test_migrations.py +++ b/quark/tests/test_migrations.py @@ -13,6 +13,7 @@ from sqlalchemy.sql import column from sqlalchemy.sql import select from sqlalchemy.sql import table +from quark.db.custom_types import INET import quark.db.migration from quark.tests import test_base @@ -621,3 +622,79 @@ class Test552b213c2b8c(BaseMigrationTest): alembic_command.upgrade(self.config, '552b213c2b8c') with self.assertRaises(NotImplementedError): alembic_command.downgrade(self.config, '45a07fac3d38') + + +class Test28e55acaf366(BaseMigrationTest): + def setUp(self): + super(Test28e55acaf366, self).setUp() + alembic_command.upgrade(self.config, '3d22de205729') + self.ip_policy = table('quark_ip_policy', + column('id', sa.String(length=36)), + column('size', INET())) + self.ip_policy_cidrs = table( + 'quark_ip_policy_cidrs', + column('id', sa.String(length=36)), + column('ip_policy_id', sa.String(length=36)), + column('cidr', sa.String(length=64))) + + def test_upgrade_none(self): + alembic_command.upgrade(self.config, '28e55acaf366') + results = self.connection.execute(select([ + self.ip_policy])).fetchall() + self.assertEqual(len(results), 0) + results = self.connection.execute(select([ + self.ip_policy_cidrs])).fetchall() + self.assertEqual(len(results), 0) + + def test_upgrade_v4(self): + self.connection.execute( + self.ip_policy.insert(), dict(id="1", size=None)) + self.connection.execute( + self.ip_policy_cidrs.insert(), + dict(id="2", ip_policy_id="1", cidr="192.168.10.13/32"), + dict(id="3", ip_policy_id="1", cidr="192.168.10.16/31")) + alembic_command.upgrade(self.config, '28e55acaf366') + results = self.connection.execute(select([ + self.ip_policy])).fetchall() + self.assertEqual(len(results), 1) + self.assertEqual(results[0]["id"], "1") + self.assertEqual(results[0]["size"], 3) + + def test_upgrade_v6(self): + self.connection.execute( + self.ip_policy.insert(), dict(id="1", size=None)) + self.connection.execute( + self.ip_policy_cidrs.insert(), + dict(id="2", ip_policy_id="1", cidr="fd00::/64")) + alembic_command.upgrade(self.config, '28e55acaf366') + results = self.connection.execute(select([ + self.ip_policy])).fetchall() + self.assertEqual(len(results), 1) + self.assertEqual(results[0]["id"], "1") + self.assertEqual(results[0]["size"], 2 ** 64) + + def test_upgrade_bulk(self): + self.connection.execute( + self.ip_policy.insert(), + dict(id="1", size=None), + dict(id="2", size=None)) + self.connection.execute( + self.ip_policy_cidrs.insert(), + dict(id="2", ip_policy_id="1", cidr="192.168.10.13/32"), + dict(id="3", ip_policy_id="1", cidr="192.168.10.16/31"), + dict(id="4", ip_policy_id="2", cidr="fd00::/64")) + alembic_command.upgrade(self.config, '28e55acaf366') + results = self.connection.execute(select([ + self.ip_policy])).fetchall() + self.assertEqual(len(results), 2) + for result in results: + self.assertIn(result["id"], ("1", "2")) + if result["id"] == "1": + self.assertEqual(result["size"], 3) + elif result["id"] == "2": + self.assertEqual(result["size"], 2 ** 64) + + def test_downgrade(self): + alembic_command.upgrade(self.config, '28e55acaf366') + with self.assertRaises(NotImplementedError): + alembic_command.downgrade(self.config, '3d22de205729')