Fixes #27
Implements basic segmented networking by providing a new "segment_id" column. This column is used for lookups when assigning IP addresses out of "shared" networks. If one attempts to create a port on a shared network without providing the segment_id, ambiguity exceptions are raised. Additionally, extends subnets to allow a segment_id to be passed when the subnet is created.
This commit is contained in:
		@@ -19,6 +19,7 @@ from neutron.api import extensions
 | 
			
		||||
 | 
			
		||||
EXTENDED_ATTRIBUTES_2_0 = {
 | 
			
		||||
    'subnets': {
 | 
			
		||||
        "segment_id": {"allow_post": True, "default": False},
 | 
			
		||||
        "enable_dhcp": {'allow_post': False, 'allow_put': False,
 | 
			
		||||
                        'default': False,
 | 
			
		||||
                        'is_visible': True},
 | 
			
		||||
 
 | 
			
		||||
@@ -51,7 +51,7 @@ for _name, klass in inspect.getmembers(models, inspect.isclass):
 | 
			
		||||
 | 
			
		||||
def _listify(filters):
 | 
			
		||||
    for key in ["name", "network_id", "id", "device_id", "tenant_id",
 | 
			
		||||
                "mac_address", "shared", "version"]:
 | 
			
		||||
                "subnet_id", "mac_address", "shared", "version"]:
 | 
			
		||||
        if key in filters:
 | 
			
		||||
            if not filters[key]:
 | 
			
		||||
                continue
 | 
			
		||||
@@ -74,6 +74,9 @@ def _model_query(context, model, filters, fields=None):
 | 
			
		||||
    if filters.get("mac_address"):
 | 
			
		||||
        model_filters.append(model.mac_address.in_(filters["mac_address"]))
 | 
			
		||||
 | 
			
		||||
    if filters.get("segment_id"):
 | 
			
		||||
        model_filters.append(model.segment_id == filters["segment_id"])
 | 
			
		||||
 | 
			
		||||
    if filters.get("id"):
 | 
			
		||||
        model_filters.append(model.id.in_(filters["id"]))
 | 
			
		||||
 | 
			
		||||
@@ -84,8 +87,7 @@ def _model_query(context, model, filters, fields=None):
 | 
			
		||||
        model_filters.append(model.deallocated_at <= reuse)
 | 
			
		||||
 | 
			
		||||
    if filters.get("subnet_id"):
 | 
			
		||||
        model_filters.append(model.subnet_id ==
 | 
			
		||||
                             filters["subnet_id"])
 | 
			
		||||
        model_filters.append(model.subnet_id.in_(filters["subnet_id"]))
 | 
			
		||||
 | 
			
		||||
    if filters.get("deallocated"):
 | 
			
		||||
        model_filters.append(model.deallocated == filters["deallocated"])
 | 
			
		||||
@@ -381,6 +383,8 @@ def subnet_find_allocation_counts(context, net_id, **filters):
 | 
			
		||||
    query = query.filter(models.Subnet.network_id == net_id)
 | 
			
		||||
    if "ip_version" in filters:
 | 
			
		||||
        query = query.filter(models.Subnet.ip_version == filters["ip_version"])
 | 
			
		||||
    if "segment_id" in filters:
 | 
			
		||||
        query = query.filter(models.Subnet.segment_id == filters["segment_id"])
 | 
			
		||||
    return query
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -201,6 +201,7 @@ class Subnet(BASEV2, models.HasId, IsHazTags):
 | 
			
		||||
    network_id = sa.Column(sa.String(36), sa.ForeignKey('quark_networks.id'))
 | 
			
		||||
    _cidr = sa.Column(sa.String(64), nullable=False)
 | 
			
		||||
    tenant_id = sa.Column(sa.String(255), index=True)
 | 
			
		||||
    segment_id = sa.Column(sa.String(255), index=True)
 | 
			
		||||
 | 
			
		||||
    @hybrid.hybrid_property
 | 
			
		||||
    def cidr(self):
 | 
			
		||||
 
 | 
			
		||||
@@ -21,8 +21,8 @@ class RouteNotFound(exceptions.NotFound):
 | 
			
		||||
    message = _("Route %(route_id)s not found.")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AmbiguousNetworkId(exceptions.NeutronException):
 | 
			
		||||
    message = _("Segment ID required for network %(net_id)s.")
 | 
			
		||||
class AmbiguousNetworkId(exceptions.InvalidInput):
 | 
			
		||||
    msg = _("Segment ID required for network %(net_id)s.")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AmbigiousLswitchCount(exceptions.NeutronException):
 | 
			
		||||
 
 | 
			
		||||
@@ -78,7 +78,8 @@ class QuarkIpam(object):
 | 
			
		||||
        raise exceptions.MacAddressGenerationFailure(net_id=net_id)
 | 
			
		||||
 | 
			
		||||
    def attempt_to_reallocate_ip(self, context, net_id, port_id, reuse_after,
 | 
			
		||||
                                 version=None, ip_address=None):
 | 
			
		||||
                                 version=None, ip_address=None,
 | 
			
		||||
                                 segment_id=None):
 | 
			
		||||
        version = version or [4, 6]
 | 
			
		||||
        elevated = context.elevated()
 | 
			
		||||
 | 
			
		||||
@@ -87,10 +88,26 @@ class QuarkIpam(object):
 | 
			
		||||
        # is really wrong)
 | 
			
		||||
        for times in xrange(3):
 | 
			
		||||
            with context.session.begin(subtransactions=True):
 | 
			
		||||
                address = db_api.ip_address_find(
 | 
			
		||||
                    elevated, network_id=net_id, reuse_after=reuse_after,
 | 
			
		||||
                    deallocated=True, scope=db_api.ONE, ip_address=ip_address,
 | 
			
		||||
                    lock_mode=True, version=version, order_by="address")
 | 
			
		||||
 | 
			
		||||
                sub_ids = []
 | 
			
		||||
                if segment_id:
 | 
			
		||||
                    subnets = db_api.subnet_find(elevated, network_id=net_id,
 | 
			
		||||
                                                 segment_id=segment_id)
 | 
			
		||||
                    sub_ids = [s["id"] for s in subnets]
 | 
			
		||||
                    if not sub_ids:
 | 
			
		||||
                        raise exceptions.IpAddressGenerationFailure(
 | 
			
		||||
                            net_id=net_id)
 | 
			
		||||
 | 
			
		||||
                ip_kwargs = {
 | 
			
		||||
                    "network_id": net_id, "reuse_after": reuse_after,
 | 
			
		||||
                    "deallocated": True, "scope": db_api.ONE,
 | 
			
		||||
                    "ip_address": ip_address, "lock_mode": True,
 | 
			
		||||
                    "version": version, "order_by": "address"}
 | 
			
		||||
 | 
			
		||||
                if sub_ids:
 | 
			
		||||
                    ip_kwargs["subnet_id"] = sub_ids
 | 
			
		||||
 | 
			
		||||
                address = db_api.ip_address_find(elevated, **ip_kwargs)
 | 
			
		||||
 | 
			
		||||
                if address:
 | 
			
		||||
                    #NOTE(mdietz): We should always be in the CIDR but we've
 | 
			
		||||
@@ -138,7 +155,7 @@ class QuarkIpam(object):
 | 
			
		||||
        return next_ip
 | 
			
		||||
 | 
			
		||||
    def allocate_ip_address(self, context, net_id, port_id, reuse_after,
 | 
			
		||||
                            version=None, ip_address=None):
 | 
			
		||||
                            segment_id=None, version=None, ip_address=None):
 | 
			
		||||
        elevated = context.elevated()
 | 
			
		||||
        if ip_address:
 | 
			
		||||
            ip_address = netaddr.IPAddress(ip_address)
 | 
			
		||||
@@ -147,14 +164,15 @@ class QuarkIpam(object):
 | 
			
		||||
        realloc_ips = self.attempt_to_reallocate_ip(context, net_id,
 | 
			
		||||
                                                    port_id, reuse_after,
 | 
			
		||||
                                                    version=None,
 | 
			
		||||
                                                    ip_address=None)
 | 
			
		||||
                                                    ip_address=None,
 | 
			
		||||
                                                    segment_id=segment_id)
 | 
			
		||||
        if self.is_strategy_satisfied(realloc_ips):
 | 
			
		||||
            return realloc_ips
 | 
			
		||||
        new_addresses.extend(realloc_ips)
 | 
			
		||||
        with context.session.begin(subtransactions=True):
 | 
			
		||||
            subnets = self._choose_available_subnet(
 | 
			
		||||
                elevated, net_id, version, ip_address=ip_address,
 | 
			
		||||
                reallocated_ips=realloc_ips)
 | 
			
		||||
                elevated, net_id, version, segment_id=segment_id,
 | 
			
		||||
                ip_address=ip_address, reallocated_ips=realloc_ips)
 | 
			
		||||
            for subnet in subnets:
 | 
			
		||||
                ip_policy_rules = models.IPPolicy.get_ip_policy_rule_set(
 | 
			
		||||
                    subnet)
 | 
			
		||||
@@ -224,8 +242,10 @@ class QuarkIpam(object):
 | 
			
		||||
            db_api.mac_address_update(context, mac, deallocated=True,
 | 
			
		||||
                                      deallocated_at=timeutils.utcnow())
 | 
			
		||||
 | 
			
		||||
    def select_subnet(self, context, net_id, ip_address, **filters):
 | 
			
		||||
    def select_subnet(self, context, net_id, ip_address, segment_id,
 | 
			
		||||
                      **filters):
 | 
			
		||||
        subnets = db_api.subnet_find_allocation_counts(context, net_id,
 | 
			
		||||
                                                       segment_id=segment_id,
 | 
			
		||||
                                                       scope=db_api.ALL,
 | 
			
		||||
                                                       **filters)
 | 
			
		||||
        for subnet, ips_in_subnet in subnets:
 | 
			
		||||
@@ -247,11 +267,13 @@ class QuarkIpamANY(QuarkIpam):
 | 
			
		||||
        return "ANY"
 | 
			
		||||
 | 
			
		||||
    def _choose_available_subnet(self, context, net_id, version=None,
 | 
			
		||||
                                 ip_address=None, reallocated_ips=None):
 | 
			
		||||
                                 segment_id=None, ip_address=None,
 | 
			
		||||
                                 reallocated_ips=None):
 | 
			
		||||
        filters = {}
 | 
			
		||||
        if version:
 | 
			
		||||
            filters["ip_version"] = version
 | 
			
		||||
        subnet = self.select_subnet(context, net_id, ip_address, **filters)
 | 
			
		||||
        subnet = self.select_subnet(context, net_id, ip_address, segment_id,
 | 
			
		||||
                                    **filters)
 | 
			
		||||
        if subnet:
 | 
			
		||||
            return [subnet]
 | 
			
		||||
        raise exceptions.IpAddressGenerationFailure(net_id=net_id)
 | 
			
		||||
@@ -273,17 +295,19 @@ class QuarkIpamBOTH(QuarkIpam):
 | 
			
		||||
 | 
			
		||||
    def attempt_to_reallocate_ip(self, context, net_id, port_id,
 | 
			
		||||
                                 reuse_after, version=None,
 | 
			
		||||
                                 ip_address=None):
 | 
			
		||||
                                 ip_address=None, segment_id=None):
 | 
			
		||||
        both_versions = []
 | 
			
		||||
        with context.session.begin(subtransactions=True):
 | 
			
		||||
            for ver in (4, 6):
 | 
			
		||||
                address = super(QuarkIpamBOTH, self).attempt_to_reallocate_ip(
 | 
			
		||||
                    context, net_id, port_id, reuse_after, ver, ip_address)
 | 
			
		||||
                    context, net_id, port_id, reuse_after, ver, ip_address,
 | 
			
		||||
                    segment_id)
 | 
			
		||||
                both_versions.extend(address)
 | 
			
		||||
        return both_versions
 | 
			
		||||
 | 
			
		||||
    def _choose_available_subnet(self, context, net_id, version=None,
 | 
			
		||||
                                 ip_address=None, reallocated_ips=None):
 | 
			
		||||
                                 segment_id=None, ip_address=None,
 | 
			
		||||
                                 reallocated_ips=None):
 | 
			
		||||
        both_subnet_versions = []
 | 
			
		||||
        need_versions = [4, 6]
 | 
			
		||||
        for i in reallocated_ips:
 | 
			
		||||
@@ -292,7 +316,8 @@ class QuarkIpamBOTH(QuarkIpam):
 | 
			
		||||
        filters = {}
 | 
			
		||||
        for ver in need_versions:
 | 
			
		||||
            filters["ip_version"] = ver
 | 
			
		||||
            sub = self.select_subnet(context, net_id, ip_address, **filters)
 | 
			
		||||
            sub = self.select_subnet(context, net_id, ip_address, segment_id,
 | 
			
		||||
                                     **filters)
 | 
			
		||||
 | 
			
		||||
            if sub:
 | 
			
		||||
                both_subnet_versions.append(sub)
 | 
			
		||||
@@ -308,9 +333,10 @@ class QuarkIpamBOTHREQ(QuarkIpamBOTH):
 | 
			
		||||
        return "BOTH_REQUIRED"
 | 
			
		||||
 | 
			
		||||
    def _choose_available_subnet(self, context, net_id, version=None,
 | 
			
		||||
                                 ip_address=None, reallocated_ips=None):
 | 
			
		||||
                                 segment_id=None, ip_address=None,
 | 
			
		||||
                                 reallocated_ips=None):
 | 
			
		||||
        subnets = super(QuarkIpamBOTHREQ, self)._choose_available_subnet(
 | 
			
		||||
            context, net_id, version, ip_address, reallocated_ips)
 | 
			
		||||
            context, net_id, version, segment_id, ip_address, reallocated_ips)
 | 
			
		||||
 | 
			
		||||
        if len(reallocated_ips) + len(subnets) < 2:
 | 
			
		||||
            raise exceptions.IpAddressGenerationFailure(net_id=net_id)
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,7 @@ from oslo.config import cfg
 | 
			
		||||
 | 
			
		||||
from quark.db import api as db_api
 | 
			
		||||
from quark.drivers import registry
 | 
			
		||||
from quark import exceptions as q_exc
 | 
			
		||||
from quark import ipam
 | 
			
		||||
from quark import network_strategy
 | 
			
		||||
from quark import plugin_views as v
 | 
			
		||||
@@ -55,18 +56,19 @@ def create_port(context, port):
 | 
			
		||||
    with context.session.begin():
 | 
			
		||||
        port_id = uuidutils.generate_uuid()
 | 
			
		||||
 | 
			
		||||
        net = db_api.network_find(context, id=net_id,
 | 
			
		||||
                                  segment_id=segment_id, scope=db_api.ONE)
 | 
			
		||||
        net = db_api.network_find(context, id=net_id, scope=db_api.ONE)
 | 
			
		||||
        if not net:
 | 
			
		||||
            # Maybe it's a tenant network
 | 
			
		||||
            net = db_api.network_find(context, id=net_id, scope=db_api.ONE)
 | 
			
		||||
            if not net:
 | 
			
		||||
                raise exceptions.NetworkNotFound(net_id=net_id)
 | 
			
		||||
            raise exceptions.NetworkNotFound(net_id=net_id)
 | 
			
		||||
 | 
			
		||||
        if not STRATEGY.is_parent_network(net_id):
 | 
			
		||||
            # We don't honor segmented networks when they aren't "shared"
 | 
			
		||||
            segment_id = None
 | 
			
		||||
            quota.QUOTAS.limit_check(
 | 
			
		||||
                context, context.tenant_id,
 | 
			
		||||
                ports_per_network=len(net.get('ports', [])) + 1)
 | 
			
		||||
        else:
 | 
			
		||||
            if not segment_id:
 | 
			
		||||
                raise q_exc.AmbiguousNetworkId(net_id=net_id)
 | 
			
		||||
 | 
			
		||||
        ipam_driver = ipam.IPAM_REGISTRY.get_strategy(net["ipam_strategy"])
 | 
			
		||||
        if fixed_ips:
 | 
			
		||||
@@ -79,10 +81,11 @@ def create_port(context, port):
 | 
			
		||||
                        msg="subnet_id and ip_address required")
 | 
			
		||||
                addresses.extend(ipam_driver.allocate_ip_address(
 | 
			
		||||
                    context, net["id"], port_id, CONF.QUARK.ipam_reuse_after,
 | 
			
		||||
                    ip_address=ip_address))
 | 
			
		||||
                    segment_id=segment_id, ip_address=ip_address))
 | 
			
		||||
        else:
 | 
			
		||||
            addresses.extend(ipam_driver.allocate_ip_address(
 | 
			
		||||
                context, net["id"], port_id, CONF.QUARK.ipam_reuse_after))
 | 
			
		||||
                context, net["id"], port_id, CONF.QUARK.ipam_reuse_after,
 | 
			
		||||
                segment_id=segment_id))
 | 
			
		||||
 | 
			
		||||
        group_ids, security_groups = v.make_security_group_list(
 | 
			
		||||
            context, port["port"].pop("security_groups", None))
 | 
			
		||||
 
 | 
			
		||||
@@ -99,6 +99,10 @@ def create_subnet(context, subnet):
 | 
			
		||||
        dns_ips = utils.pop_param(sub_attrs, "dns_nameservers", [])
 | 
			
		||||
        host_routes = utils.pop_param(sub_attrs, "host_routes", [])
 | 
			
		||||
        allocation_pools = utils.pop_param(sub_attrs, "allocation_pools", None)
 | 
			
		||||
 | 
			
		||||
        if not context.is_admin and "segment_id" in sub_attrs:
 | 
			
		||||
            sub_attrs.pop("segment_id")
 | 
			
		||||
 | 
			
		||||
        sub_attrs["network"] = net
 | 
			
		||||
 | 
			
		||||
        new_subnet = db_api.subnet_create(context, **sub_attrs)
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,7 @@ from neutron.extensions import securitygroup as sg_ext
 | 
			
		||||
 | 
			
		||||
from quark.db import api as quark_db_api
 | 
			
		||||
from quark.db import models
 | 
			
		||||
from quark import exceptions as q_exc
 | 
			
		||||
from quark import network_strategy
 | 
			
		||||
from quark.plugin_modules import ports as quark_ports
 | 
			
		||||
from quark.tests import test_quark_plugin
 | 
			
		||||
@@ -185,6 +186,30 @@ class TestQuarkCreatePort(test_quark_plugin.TestQuarkPlugin):
 | 
			
		||||
            for key in expected.keys():
 | 
			
		||||
                self.assertEqual(result[key], expected[key])
 | 
			
		||||
 | 
			
		||||
    def test_create_port_segment_id_on_unshared_net_ignored(self):
 | 
			
		||||
        network = dict(id=1)
 | 
			
		||||
        mac = dict(address="AA:BB:CC:DD:EE:FF")
 | 
			
		||||
        port_name = "foobar"
 | 
			
		||||
        ip = dict()
 | 
			
		||||
        port = dict(port=dict(mac_address=mac["address"], network_id=1,
 | 
			
		||||
                              tenant_id=self.context.tenant_id, device_id=2,
 | 
			
		||||
                              segment_id="cell01", name=port_name))
 | 
			
		||||
        expected = {'status': "ACTIVE",
 | 
			
		||||
                    'name': port_name,
 | 
			
		||||
                    'device_owner': None,
 | 
			
		||||
                    'mac_address': mac["address"],
 | 
			
		||||
                    'network_id': network["id"],
 | 
			
		||||
                    'tenant_id': self.context.tenant_id,
 | 
			
		||||
                    'admin_state_up': None,
 | 
			
		||||
                    'fixed_ips': [],
 | 
			
		||||
                    'device_id': 2}
 | 
			
		||||
        with self._stubs(port=port["port"], network=network, addr=ip,
 | 
			
		||||
                         mac=mac) as port_create:
 | 
			
		||||
            result = self.plugin.create_port(self.context, port)
 | 
			
		||||
            self.assertTrue(port_create.called)
 | 
			
		||||
            for key in expected.keys():
 | 
			
		||||
                self.assertEqual(result[key], expected[key])
 | 
			
		||||
 | 
			
		||||
    def test_create_port_mac_address_not_specified(self):
 | 
			
		||||
        network = dict(id=1)
 | 
			
		||||
        mac = dict(address="AA:BB:CC:DD:EE:FF")
 | 
			
		||||
@@ -532,6 +557,7 @@ class TestQuarkCreatePortOnSharedNetworks(test_quark_plugin.TestQuarkPlugin):
 | 
			
		||||
        port = dict(port=dict(mac_address=mac["address"],
 | 
			
		||||
                              network_id="public_network",
 | 
			
		||||
                              tenant_id=self.context.tenant_id, device_id=2,
 | 
			
		||||
                              segment_id="cell01",
 | 
			
		||||
                              name=port_name))
 | 
			
		||||
        with self._stubs(port=port["port"], network=network, addr=ip, mac=mac):
 | 
			
		||||
            try:
 | 
			
		||||
@@ -539,6 +565,19 @@ class TestQuarkCreatePortOnSharedNetworks(test_quark_plugin.TestQuarkPlugin):
 | 
			
		||||
            except Exception:
 | 
			
		||||
                self.fail("create_port raised OverQuota")
 | 
			
		||||
 | 
			
		||||
    def test_create_port_shared_net_no_segment_id_fails(self):
 | 
			
		||||
        network = dict(id=1, ports=[models.Port()])
 | 
			
		||||
        mac = dict(address="AA:BB:CC:DD:EE:FF")
 | 
			
		||||
        port_name = "foobar"
 | 
			
		||||
        ip = dict()
 | 
			
		||||
        port = dict(port=dict(mac_address=mac["address"],
 | 
			
		||||
                              network_id="public_network",
 | 
			
		||||
                              tenant_id=self.context.tenant_id, device_id=2,
 | 
			
		||||
                              name=port_name))
 | 
			
		||||
        with self._stubs(port=port["port"], network=network, addr=ip, mac=mac):
 | 
			
		||||
            with self.assertRaises(q_exc.AmbiguousNetworkId):
 | 
			
		||||
                self.plugin.create_port(self.context, port)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestQuarkGetPortCount(test_quark_plugin.TestQuarkPlugin):
 | 
			
		||||
    def test_get_port_count(self):
 | 
			
		||||
 
 | 
			
		||||
@@ -306,6 +306,41 @@ class TestQuarkCreateSubnet(test_quark_plugin.TestQuarkPlugin):
 | 
			
		||||
                else:
 | 
			
		||||
                    self.assertEqual(res[key], subnet["subnet"][key])
 | 
			
		||||
 | 
			
		||||
    def test_create_subnet_not_admin_segment_id_ignored(self):
 | 
			
		||||
        routes = [dict(cidr="0.0.0.0/0", gateway="0.0.0.0")]
 | 
			
		||||
        subnet = dict(
 | 
			
		||||
            subnet=dict(network_id=1,
 | 
			
		||||
                        tenant_id=self.context.tenant_id, ip_version=4,
 | 
			
		||||
                        cidr="172.16.0.0/24", gateway_ip="0.0.0.0",
 | 
			
		||||
                        dns_nameservers=neutron_attrs.ATTR_NOT_SPECIFIED,
 | 
			
		||||
                        host_routes=neutron_attrs.ATTR_NOT_SPECIFIED,
 | 
			
		||||
                        enable_dhcp=None))
 | 
			
		||||
        network = dict(network_id=1)
 | 
			
		||||
        with self._stubs(
 | 
			
		||||
            subnet=subnet["subnet"],
 | 
			
		||||
            network=network,
 | 
			
		||||
            routes=routes
 | 
			
		||||
        ) as (subnet_create, dns_create, route_create):
 | 
			
		||||
            dns_nameservers = subnet["subnet"].pop("dns_nameservers")
 | 
			
		||||
            host_routes = subnet["subnet"].pop("host_routes")
 | 
			
		||||
            subnet_request = copy.deepcopy(subnet)
 | 
			
		||||
            subnet_request["subnet"]["dns_nameservers"] = dns_nameservers
 | 
			
		||||
            subnet_request["subnet"]["host_routes"] = host_routes
 | 
			
		||||
            subnet_request["subnet"]["segment_id"] = "cell01"
 | 
			
		||||
            res = self.plugin.create_subnet(self.context,
 | 
			
		||||
                                            subnet_request)
 | 
			
		||||
            self.assertEqual(subnet_create.call_count, 1)
 | 
			
		||||
            self.assertTrue("segment_id" not in subnet_create.called_with)
 | 
			
		||||
 | 
			
		||||
            self.assertEqual(dns_create.call_count, 0)
 | 
			
		||||
            self.assertEqual(route_create.call_count, 1)
 | 
			
		||||
            for key in subnet["subnet"].keys():
 | 
			
		||||
                if key == "host_routes":
 | 
			
		||||
                    self.assertEqual(res[key][0]["destination"], "0.0.0.0/0")
 | 
			
		||||
                    self.assertEqual(res[key][0]["nexthop"], "0.0.0.0")
 | 
			
		||||
                else:
 | 
			
		||||
                    self.assertEqual(res[key], subnet["subnet"][key])
 | 
			
		||||
 | 
			
		||||
    def test_create_subnet_no_network_fails(self):
 | 
			
		||||
        subnet = dict(subnet=dict(network_id=1))
 | 
			
		||||
        with self._stubs(subnet=dict(), network=None):
 | 
			
		||||
 
 | 
			
		||||
@@ -225,10 +225,13 @@ class QuarkIpamTestBothIpAllocation(QuarkIpamBaseTest):
 | 
			
		||||
        self.context.session.add = mock.Mock()
 | 
			
		||||
        with contextlib.nested(
 | 
			
		||||
            mock.patch("%s.ip_address_find" % db_mod),
 | 
			
		||||
            mock.patch("%s.subnet_find_allocation_counts" % db_mod)
 | 
			
		||||
        ) as (addr_find, subnet_find):
 | 
			
		||||
            mock.patch("%s.subnet_find_allocation_counts" % db_mod),
 | 
			
		||||
            mock.patch("%s.subnet_find" % db_mod)
 | 
			
		||||
        ) as (addr_find, subnet_alloc_find, subnet_find):
 | 
			
		||||
            addr_find.side_effect = addresses
 | 
			
		||||
            subnet_find.side_effect = subnets
 | 
			
		||||
            if subnets and len(subnets[0]):
 | 
			
		||||
                subnet_find.return_value = [subnets[0][0][0]]
 | 
			
		||||
            subnet_alloc_find.side_effect = subnets
 | 
			
		||||
            yield
 | 
			
		||||
 | 
			
		||||
    def test_allocate_new_ip_address_two_empty_subnets(self):
 | 
			
		||||
@@ -292,6 +295,32 @@ class QuarkIpamTestBothIpAllocation(QuarkIpamBaseTest):
 | 
			
		||||
            self.assertEqual(address[1]["address"], 0)
 | 
			
		||||
            self.assertEqual(address[1]["version"], 6)
 | 
			
		||||
 | 
			
		||||
    def test_reallocate_deallocated_v4_ip_shared_net(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=0, network=dict(ip_policy=None),
 | 
			
		||||
                       ip_policy=None)
 | 
			
		||||
        address = models.IPAddress()
 | 
			
		||||
        address["address"] = 4
 | 
			
		||||
        address["version"] = 4
 | 
			
		||||
        address["subnet"] = models.Subnet(cidr="0.0.0.0/24")
 | 
			
		||||
        with self._stubs(subnets=[[(subnet6, 0)]],
 | 
			
		||||
                         addresses=[address, None, None]):
 | 
			
		||||
            address = self.ipam.allocate_ip_address(self.context, 0, 0, 0,
 | 
			
		||||
                                                    segment_id="cell01")
 | 
			
		||||
            self.assertEqual(len(address), 2)
 | 
			
		||||
            self.assertEqual(address[0]["address"], 4)
 | 
			
		||||
            self.assertEqual(address[0]["version"], 4)
 | 
			
		||||
            self.assertEqual(address[1]["address"], 0)
 | 
			
		||||
            self.assertEqual(address[1]["version"], 6)
 | 
			
		||||
 | 
			
		||||
    def test_reallocate_deallocated_v4_ip_shared_net_no_subs_raises(self):
 | 
			
		||||
        with self._stubs(subnets=[],
 | 
			
		||||
                         addresses=[None]):
 | 
			
		||||
            with self.assertRaises(exceptions.IpAddressGenerationFailure):
 | 
			
		||||
                self.ipam.allocate_ip_address(self.context, 0, 0, 0,
 | 
			
		||||
                                              segment_id="cell01")
 | 
			
		||||
 | 
			
		||||
    def test_reallocate_deallocated_v4_ip_no_avail_subnets(self):
 | 
			
		||||
        address = models.IPAddress()
 | 
			
		||||
        address["address"] = 4
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user