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 = {
|
EXTENDED_ATTRIBUTES_2_0 = {
|
||||||
'subnets': {
|
'subnets': {
|
||||||
|
"segment_id": {"allow_post": True, "default": False},
|
||||||
"enable_dhcp": {'allow_post': False, 'allow_put': False,
|
"enable_dhcp": {'allow_post': False, 'allow_put': False,
|
||||||
'default': False,
|
'default': False,
|
||||||
'is_visible': True},
|
'is_visible': True},
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ for _name, klass in inspect.getmembers(models, inspect.isclass):
|
|||||||
|
|
||||||
def _listify(filters):
|
def _listify(filters):
|
||||||
for key in ["name", "network_id", "id", "device_id", "tenant_id",
|
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 key in filters:
|
||||||
if not filters[key]:
|
if not filters[key]:
|
||||||
continue
|
continue
|
||||||
@@ -74,6 +74,9 @@ def _model_query(context, model, filters, fields=None):
|
|||||||
if filters.get("mac_address"):
|
if filters.get("mac_address"):
|
||||||
model_filters.append(model.mac_address.in_(filters["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"):
|
if filters.get("id"):
|
||||||
model_filters.append(model.id.in_(filters["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)
|
model_filters.append(model.deallocated_at <= reuse)
|
||||||
|
|
||||||
if filters.get("subnet_id"):
|
if filters.get("subnet_id"):
|
||||||
model_filters.append(model.subnet_id ==
|
model_filters.append(model.subnet_id.in_(filters["subnet_id"]))
|
||||||
filters["subnet_id"])
|
|
||||||
|
|
||||||
if filters.get("deallocated"):
|
if filters.get("deallocated"):
|
||||||
model_filters.append(model.deallocated == filters["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)
|
query = query.filter(models.Subnet.network_id == net_id)
|
||||||
if "ip_version" in filters:
|
if "ip_version" in filters:
|
||||||
query = query.filter(models.Subnet.ip_version == filters["ip_version"])
|
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
|
return query
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -201,6 +201,7 @@ class Subnet(BASEV2, models.HasId, IsHazTags):
|
|||||||
network_id = sa.Column(sa.String(36), sa.ForeignKey('quark_networks.id'))
|
network_id = sa.Column(sa.String(36), sa.ForeignKey('quark_networks.id'))
|
||||||
_cidr = sa.Column(sa.String(64), nullable=False)
|
_cidr = sa.Column(sa.String(64), nullable=False)
|
||||||
tenant_id = sa.Column(sa.String(255), index=True)
|
tenant_id = sa.Column(sa.String(255), index=True)
|
||||||
|
segment_id = sa.Column(sa.String(255), index=True)
|
||||||
|
|
||||||
@hybrid.hybrid_property
|
@hybrid.hybrid_property
|
||||||
def cidr(self):
|
def cidr(self):
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ class RouteNotFound(exceptions.NotFound):
|
|||||||
message = _("Route %(route_id)s not found.")
|
message = _("Route %(route_id)s not found.")
|
||||||
|
|
||||||
|
|
||||||
class AmbiguousNetworkId(exceptions.NeutronException):
|
class AmbiguousNetworkId(exceptions.InvalidInput):
|
||||||
message = _("Segment ID required for network %(net_id)s.")
|
msg = _("Segment ID required for network %(net_id)s.")
|
||||||
|
|
||||||
|
|
||||||
class AmbigiousLswitchCount(exceptions.NeutronException):
|
class AmbigiousLswitchCount(exceptions.NeutronException):
|
||||||
|
|||||||
@@ -78,7 +78,8 @@ class QuarkIpam(object):
|
|||||||
raise exceptions.MacAddressGenerationFailure(net_id=net_id)
|
raise exceptions.MacAddressGenerationFailure(net_id=net_id)
|
||||||
|
|
||||||
def attempt_to_reallocate_ip(self, context, net_id, port_id, reuse_after,
|
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]
|
version = version or [4, 6]
|
||||||
elevated = context.elevated()
|
elevated = context.elevated()
|
||||||
|
|
||||||
@@ -87,10 +88,26 @@ class QuarkIpam(object):
|
|||||||
# is really wrong)
|
# is really wrong)
|
||||||
for times in xrange(3):
|
for times in xrange(3):
|
||||||
with context.session.begin(subtransactions=True):
|
with context.session.begin(subtransactions=True):
|
||||||
address = db_api.ip_address_find(
|
|
||||||
elevated, network_id=net_id, reuse_after=reuse_after,
|
sub_ids = []
|
||||||
deallocated=True, scope=db_api.ONE, ip_address=ip_address,
|
if segment_id:
|
||||||
lock_mode=True, version=version, order_by="address")
|
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:
|
if address:
|
||||||
#NOTE(mdietz): We should always be in the CIDR but we've
|
#NOTE(mdietz): We should always be in the CIDR but we've
|
||||||
@@ -138,7 +155,7 @@ class QuarkIpam(object):
|
|||||||
return next_ip
|
return next_ip
|
||||||
|
|
||||||
def allocate_ip_address(self, context, net_id, port_id, reuse_after,
|
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()
|
elevated = context.elevated()
|
||||||
if ip_address:
|
if ip_address:
|
||||||
ip_address = netaddr.IPAddress(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,
|
realloc_ips = self.attempt_to_reallocate_ip(context, net_id,
|
||||||
port_id, reuse_after,
|
port_id, reuse_after,
|
||||||
version=None,
|
version=None,
|
||||||
ip_address=None)
|
ip_address=None,
|
||||||
|
segment_id=segment_id)
|
||||||
if self.is_strategy_satisfied(realloc_ips):
|
if self.is_strategy_satisfied(realloc_ips):
|
||||||
return realloc_ips
|
return realloc_ips
|
||||||
new_addresses.extend(realloc_ips)
|
new_addresses.extend(realloc_ips)
|
||||||
with context.session.begin(subtransactions=True):
|
with context.session.begin(subtransactions=True):
|
||||||
subnets = self._choose_available_subnet(
|
subnets = self._choose_available_subnet(
|
||||||
elevated, net_id, version, ip_address=ip_address,
|
elevated, net_id, version, segment_id=segment_id,
|
||||||
reallocated_ips=realloc_ips)
|
ip_address=ip_address, reallocated_ips=realloc_ips)
|
||||||
for subnet in subnets:
|
for subnet in subnets:
|
||||||
ip_policy_rules = models.IPPolicy.get_ip_policy_rule_set(
|
ip_policy_rules = models.IPPolicy.get_ip_policy_rule_set(
|
||||||
subnet)
|
subnet)
|
||||||
@@ -224,8 +242,10 @@ class QuarkIpam(object):
|
|||||||
db_api.mac_address_update(context, mac, deallocated=True,
|
db_api.mac_address_update(context, mac, deallocated=True,
|
||||||
deallocated_at=timeutils.utcnow())
|
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,
|
subnets = db_api.subnet_find_allocation_counts(context, net_id,
|
||||||
|
segment_id=segment_id,
|
||||||
scope=db_api.ALL,
|
scope=db_api.ALL,
|
||||||
**filters)
|
**filters)
|
||||||
for subnet, ips_in_subnet in subnets:
|
for subnet, ips_in_subnet in subnets:
|
||||||
@@ -247,11 +267,13 @@ class QuarkIpamANY(QuarkIpam):
|
|||||||
return "ANY"
|
return "ANY"
|
||||||
|
|
||||||
def _choose_available_subnet(self, context, net_id, version=None,
|
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 = {}
|
filters = {}
|
||||||
if version:
|
if version:
|
||||||
filters["ip_version"] = 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:
|
if subnet:
|
||||||
return [subnet]
|
return [subnet]
|
||||||
raise exceptions.IpAddressGenerationFailure(net_id=net_id)
|
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,
|
def attempt_to_reallocate_ip(self, context, net_id, port_id,
|
||||||
reuse_after, version=None,
|
reuse_after, version=None,
|
||||||
ip_address=None):
|
ip_address=None, segment_id=None):
|
||||||
both_versions = []
|
both_versions = []
|
||||||
with context.session.begin(subtransactions=True):
|
with context.session.begin(subtransactions=True):
|
||||||
for ver in (4, 6):
|
for ver in (4, 6):
|
||||||
address = super(QuarkIpamBOTH, self).attempt_to_reallocate_ip(
|
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)
|
both_versions.extend(address)
|
||||||
return both_versions
|
return both_versions
|
||||||
|
|
||||||
def _choose_available_subnet(self, context, net_id, version=None,
|
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 = []
|
both_subnet_versions = []
|
||||||
need_versions = [4, 6]
|
need_versions = [4, 6]
|
||||||
for i in reallocated_ips:
|
for i in reallocated_ips:
|
||||||
@@ -292,7 +316,8 @@ class QuarkIpamBOTH(QuarkIpam):
|
|||||||
filters = {}
|
filters = {}
|
||||||
for ver in need_versions:
|
for ver in need_versions:
|
||||||
filters["ip_version"] = ver
|
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:
|
if sub:
|
||||||
both_subnet_versions.append(sub)
|
both_subnet_versions.append(sub)
|
||||||
@@ -308,9 +333,10 @@ class QuarkIpamBOTHREQ(QuarkIpamBOTH):
|
|||||||
return "BOTH_REQUIRED"
|
return "BOTH_REQUIRED"
|
||||||
|
|
||||||
def _choose_available_subnet(self, context, net_id, version=None,
|
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(
|
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:
|
if len(reallocated_ips) + len(subnets) < 2:
|
||||||
raise exceptions.IpAddressGenerationFailure(net_id=net_id)
|
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.db import api as db_api
|
||||||
from quark.drivers import registry
|
from quark.drivers import registry
|
||||||
|
from quark import exceptions as q_exc
|
||||||
from quark import ipam
|
from quark import ipam
|
||||||
from quark import network_strategy
|
from quark import network_strategy
|
||||||
from quark import plugin_views as v
|
from quark import plugin_views as v
|
||||||
@@ -55,18 +56,19 @@ def create_port(context, port):
|
|||||||
with context.session.begin():
|
with context.session.begin():
|
||||||
port_id = uuidutils.generate_uuid()
|
port_id = uuidutils.generate_uuid()
|
||||||
|
|
||||||
net = db_api.network_find(context, id=net_id,
|
net = db_api.network_find(context, id=net_id, scope=db_api.ONE)
|
||||||
segment_id=segment_id, scope=db_api.ONE)
|
|
||||||
if not net:
|
if not net:
|
||||||
# Maybe it's a tenant network
|
raise exceptions.NetworkNotFound(net_id=net_id)
|
||||||
net = db_api.network_find(context, id=net_id, scope=db_api.ONE)
|
|
||||||
if not net:
|
|
||||||
raise exceptions.NetworkNotFound(net_id=net_id)
|
|
||||||
|
|
||||||
if not STRATEGY.is_parent_network(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(
|
quota.QUOTAS.limit_check(
|
||||||
context, context.tenant_id,
|
context, context.tenant_id,
|
||||||
ports_per_network=len(net.get('ports', [])) + 1)
|
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"])
|
ipam_driver = ipam.IPAM_REGISTRY.get_strategy(net["ipam_strategy"])
|
||||||
if fixed_ips:
|
if fixed_ips:
|
||||||
@@ -79,10 +81,11 @@ def create_port(context, port):
|
|||||||
msg="subnet_id and ip_address required")
|
msg="subnet_id and ip_address required")
|
||||||
addresses.extend(ipam_driver.allocate_ip_address(
|
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,
|
||||||
ip_address=ip_address))
|
segment_id=segment_id, ip_address=ip_address))
|
||||||
else:
|
else:
|
||||||
addresses.extend(ipam_driver.allocate_ip_address(
|
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(
|
group_ids, security_groups = v.make_security_group_list(
|
||||||
context, port["port"].pop("security_groups", None))
|
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", [])
|
dns_ips = utils.pop_param(sub_attrs, "dns_nameservers", [])
|
||||||
host_routes = utils.pop_param(sub_attrs, "host_routes", [])
|
host_routes = utils.pop_param(sub_attrs, "host_routes", [])
|
||||||
allocation_pools = utils.pop_param(sub_attrs, "allocation_pools", None)
|
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
|
sub_attrs["network"] = net
|
||||||
|
|
||||||
new_subnet = db_api.subnet_create(context, **sub_attrs)
|
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 api as quark_db_api
|
||||||
from quark.db import models
|
from quark.db import models
|
||||||
|
from quark import exceptions as q_exc
|
||||||
from quark import network_strategy
|
from quark import network_strategy
|
||||||
from quark.plugin_modules import ports as quark_ports
|
from quark.plugin_modules import ports as quark_ports
|
||||||
from quark.tests import test_quark_plugin
|
from quark.tests import test_quark_plugin
|
||||||
@@ -185,6 +186,30 @@ class TestQuarkCreatePort(test_quark_plugin.TestQuarkPlugin):
|
|||||||
for key in expected.keys():
|
for key in expected.keys():
|
||||||
self.assertEqual(result[key], expected[key])
|
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):
|
def test_create_port_mac_address_not_specified(self):
|
||||||
network = dict(id=1)
|
network = dict(id=1)
|
||||||
mac = dict(address="AA:BB:CC:DD:EE:FF")
|
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"],
|
port = dict(port=dict(mac_address=mac["address"],
|
||||||
network_id="public_network",
|
network_id="public_network",
|
||||||
tenant_id=self.context.tenant_id, device_id=2,
|
tenant_id=self.context.tenant_id, device_id=2,
|
||||||
|
segment_id="cell01",
|
||||||
name=port_name))
|
name=port_name))
|
||||||
with self._stubs(port=port["port"], network=network, addr=ip, mac=mac):
|
with self._stubs(port=port["port"], network=network, addr=ip, mac=mac):
|
||||||
try:
|
try:
|
||||||
@@ -539,6 +565,19 @@ class TestQuarkCreatePortOnSharedNetworks(test_quark_plugin.TestQuarkPlugin):
|
|||||||
except Exception:
|
except Exception:
|
||||||
self.fail("create_port raised OverQuota")
|
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):
|
class TestQuarkGetPortCount(test_quark_plugin.TestQuarkPlugin):
|
||||||
def test_get_port_count(self):
|
def test_get_port_count(self):
|
||||||
|
|||||||
@@ -306,6 +306,41 @@ class TestQuarkCreateSubnet(test_quark_plugin.TestQuarkPlugin):
|
|||||||
else:
|
else:
|
||||||
self.assertEqual(res[key], subnet["subnet"][key])
|
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):
|
def test_create_subnet_no_network_fails(self):
|
||||||
subnet = dict(subnet=dict(network_id=1))
|
subnet = dict(subnet=dict(network_id=1))
|
||||||
with self._stubs(subnet=dict(), network=None):
|
with self._stubs(subnet=dict(), network=None):
|
||||||
|
|||||||
@@ -225,10 +225,13 @@ class QuarkIpamTestBothIpAllocation(QuarkIpamBaseTest):
|
|||||||
self.context.session.add = mock.Mock()
|
self.context.session.add = mock.Mock()
|
||||||
with contextlib.nested(
|
with contextlib.nested(
|
||||||
mock.patch("%s.ip_address_find" % db_mod),
|
mock.patch("%s.ip_address_find" % db_mod),
|
||||||
mock.patch("%s.subnet_find_allocation_counts" % db_mod)
|
mock.patch("%s.subnet_find_allocation_counts" % db_mod),
|
||||||
) as (addr_find, subnet_find):
|
mock.patch("%s.subnet_find" % db_mod)
|
||||||
|
) as (addr_find, subnet_alloc_find, subnet_find):
|
||||||
addr_find.side_effect = addresses
|
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
|
yield
|
||||||
|
|
||||||
def test_allocate_new_ip_address_two_empty_subnets(self):
|
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]["address"], 0)
|
||||||
self.assertEqual(address[1]["version"], 6)
|
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):
|
def test_reallocate_deallocated_v4_ip_no_avail_subnets(self):
|
||||||
address = models.IPAddress()
|
address = models.IPAddress()
|
||||||
address["address"] = 4
|
address["address"] = 4
|
||||||
|
|||||||
Reference in New Issue
Block a user