Re-implemented IP policies with offset and length

* Implemented CRUD ip_policies plugin methods
* Implemented plugin ip_policy unit tests
* Added IP Policy logic to ipam.allocate_ip_address
* Implemented ip_policy ipam unit tests
* Implemented ip_policies extension
* Added ip_policies db models
* Implemented db_api ip_policy methods
* Implemented _make_ip_policy_dict view
* Fixed existing tests

An IP Policy is defined on a subnet or network definining what IPs to
exclude from allocation. From a user perspective an IP Policy looks
like the following:

{
  "id": <id>,
  "tenant_id": <tenant_id>,
  "name": "foobar",
  "subnet_ids": [] (list of uuids),
  "network_ids": [] (list of uuids),
  "exclude": list of dictionaries (e.g. [{"offset": -1, "length": 3}])
}

"exclude" is always required. One of "subnet_ids" or "network_ids"
is required. Only one policy is allowed per network.

A default policy is enabled on all subnets if no ip policies are
specified on that subnet or its network. This default policy is
established via JSON configuration as "default_ip_policy". The
default for "default_ip_policy" is no policy.
This commit is contained in:
Amir Sadoughi
2013-07-24 20:57:52 -05:00
parent 7f4c1c7cdc
commit c6ca88568d
12 changed files with 404 additions and 147 deletions

View File

@@ -42,6 +42,11 @@ class IPPoliciesController(wsgi.Controller):
return {RESOURCE_NAME: return {RESOURCE_NAME:
self._plugin.create_ip_policy(request.context, body)} self._plugin.create_ip_policy(request.context, body)}
def update(self, request, id, body=None):
body = self._deserialize(request.body, request.get_content_type())
return {RESOURCE_NAME:
self._plugin.update_ip_policy(request.context, id, body)}
def index(self, request): def index(self, request):
context = request.context context = request.context
return {RESOURCE_COLLECTION: return {RESOURCE_COLLECTION:

View File

@@ -489,13 +489,14 @@ def security_group_rule_delete(context, rule):
def ip_policy_create(context, **ip_policy_dict): def ip_policy_create(context, **ip_policy_dict):
new_policy = models.IPPolicy() new_policy = models.IPPolicy()
exclude_set = ip_policy_dict.pop("exclude") ranges = ip_policy_dict.pop("exclude")
for ip_cidr in exclude_set.iter_cidrs(): for arange in ranges:
new_policy["exclude"].append(models.IPPolicyRule( new_policy["exclude"].append(models.IPPolicyRange(
address=int(ip_cidr.ip), offset=arange["offset"],
prefix=ip_cidr.prefixlen)) length=arange["length"]))
new_policy.update(ip_policy_dict) new_policy.update(ip_policy_dict)
new_policy["tenant_id"] = context.tenant_id
context.session.add(new_policy) context.session.add(new_policy)
return new_policy return new_policy
@@ -507,5 +508,19 @@ def ip_policy_find(context, **filters):
return query.filter(*model_filters) return query.filter(*model_filters)
def ip_policy_update(context, ip_policy, **ip_policy_dict):
ranges = ip_policy_dict.pop("exclude", [])
if ranges:
ip_policy["exclude"] = []
for arange in ranges:
ip_policy["exclude"].append(models.IPPolicyRange(
offset=arange["offset"],
length=arange["length"]))
ip_policy.update(ip_policy_dict)
context.session.add(ip_policy)
return ip_policy
def ip_policy_delete(context, ip_policy): def ip_policy_delete(context, ip_policy):
context.session.delete(ip_policy) context.session.delete(ip_policy)

View File

@@ -27,11 +27,22 @@ from neutron.db import models_v2 as models
from neutron.openstack.common import log as logging from neutron.openstack.common import log as logging
from neutron.openstack.common import timeutils from neutron.openstack.common import timeutils
from oslo.config import cfg
from quark.db import custom_types from quark.db import custom_types
import json
HasId = models.HasId HasId = models.HasId
LOG = logging.getLogger("neutron.quark.db.models") LOG = logging.getLogger("neutron.quark.db.models")
CONF = cfg.CONF
quark_opts = [
cfg.StrOpt('default_ip_policy', default='{}',
help=_("Default IP allocation policy"))
]
CONF.register_opts(quark_opts, "QUARK")
def _default_list_getset(collection_class, proxy): def _default_list_getset(collection_class, proxy):
@@ -225,7 +236,8 @@ class Subnet(BASEV2, models.HasId, models.HasTenant, IsHazTags):
primaryjoin="DNSNameserver.subnet_id==Subnet.id", primaryjoin="DNSNameserver.subnet_id==Subnet.id",
backref='subnet', backref='subnet',
cascade='delete') cascade='delete')
ip_policy = orm.relationship("IPPolicy", uselist=False, backref="subnet") ip_policy_id = sa.Column(sa.String(36),
sa.ForeignKey("quark_ip_policy.id"))
port_ip_association_table = sa.Table( port_ip_association_table = sa.Table(
@@ -331,25 +343,68 @@ class MacAddressRange(BASEV2, models.HasId):
backref="mac_address_range") backref="mac_address_range")
class IPPolicy(BASEV2, models.HasId): class IPPolicy(BASEV2, models.HasId, models.HasTenant):
__tablename__ = "quark_ip_policy" __tablename__ = "quark_ip_policy"
subnet_id = sa.Column(sa.String(36), sa.ForeignKey("quark_subnets.id", networks = orm.relationship(
ondelete="CASCADE")) "Network",
network_id = sa.Column(sa.String(36), sa.ForeignKey("quark_networks.id", primaryjoin="IPPolicy.id==Network.ip_policy_id",
ondelete="CASCADE")) backref="ip_policy")
subnets = orm.relationship(
"Subnet",
primaryjoin="IPPolicy.id==Subnet.ip_policy_id",
backref="ip_policy")
exclude = orm.relationship(
"IPPolicyRange",
primaryjoin="IPPolicy.id==IPPolicyRange.ip_policy_id",
backref="ip_policy")
name = sa.Column(sa.String(255), nullable=True)
join = "IPPolicy.id==IPPolicyRule.ip_policy_id" class JSONIPPolicy(object):
exclude = orm.relationship("IPPolicyRule", def __init__(self, policy=None):
primaryjoin=join, self.policy = {}
backref="ip_policy") if not policy:
self._compile_policy(CONF.QUARK.default_ip_policy)
else:
self._compile_policy(policy)
def _compile_policy(self, policy):
self.policy = json.loads(policy)
def __getattr__(self, name):
return getattr(self.policy, name)
DEFAULT_POLICY = JSONIPPolicy()
@staticmethod
def get_ip_policy_rule_set(subnet):
ip_policy = subnet["ip_policy"] or \
subnet["network"]["ip_policy"] or \
dict()
ip_policy_ranges = ip_policy.get("exclude", []) + \
IPPolicy.DEFAULT_POLICY.get("exclude", [])
ip_policy_rules = netaddr.IPSet()
subnet_net = netaddr.IPNetwork(subnet["cidr"])
for arange in ip_policy_ranges:
end_index = arange["offset"] + arange["length"]
end_index_wraps_around = (arange["offset"] < 0 and end_index >= 0)
if end_index_wraps_around:
second_index = min(end_index, len(subnet_net))
end_index = None
ip_policy_rules |= netaddr.IPSet(subnet_net[:second_index])
ip_policy_rules |= netaddr.IPSet(
subnet_net[arange["offset"]:end_index])
return ip_policy_rules
class IPPolicyRule(BASEV2, models.HasId): class IPPolicyRange(BASEV2, models.HasId):
__tablename__ = "quark_ip_policy_rules" __tablename__ = "quark_ip_policy_rules"
ip_policy_id = sa.Column(sa.String(36), sa.ForeignKey( ip_policy_id = sa.Column(sa.String(36), sa.ForeignKey(
"quark_ip_policy.id", ondelete="CASCADE")) "quark_ip_policy.id", ondelete="CASCADE"))
address = sa.Column(custom_types.INET())
prefix = sa.Column(sa.Integer()) offset = sa.Column(sa.Integer())
length = sa.Column(sa.Integer())
class Network(BASEV2, models.HasTenant, models.HasId): class Network(BASEV2, models.HasTenant, models.HasId):
@@ -357,4 +412,5 @@ class Network(BASEV2, models.HasTenant, models.HasId):
name = sa.Column(sa.String(255)) name = sa.Column(sa.String(255))
ports = orm.relationship(Port, backref='network') ports = orm.relationship(Port, backref='network')
subnets = orm.relationship(Subnet, backref='network') subnets = orm.relationship(Subnet, backref='network')
ip_policy = orm.relationship(IPPolicy, uselist=False, backref="network") ip_policy_id = sa.Column(sa.String(36),
sa.ForeignKey("quark_ip_policy.id"))

View File

@@ -65,5 +65,9 @@ class IPPolicyAlreadyExists(exceptions.NeutronException):
message = _("IP Policy %(id)s already exists for %(n_id)s") message = _("IP Policy %(id)s already exists for %(n_id)s")
class IPPolicyInUse(exceptions.InUse):
message = _("IP allocation policy %(id) in use.")
class DriverLimitReached(exceptions.InvalidInput): class DriverLimitReached(exceptions.InvalidInput):
message = _("Driver has reached limit on resource '%(limit)s'") message = _("Driver has reached limit on resource '%(limit)s'")

View File

@@ -24,25 +24,13 @@ from neutron.openstack.common import log as logging
from neutron.openstack.common import timeutils from neutron.openstack.common import timeutils
from quark.db import api as db_api from quark.db import api as db_api
from quark.db import models
LOG = logging.getLogger("neutron") LOG = logging.getLogger("neutron")
class QuarkIpam(object): class QuarkIpam(object):
@staticmethod
def get_ip_policy_rule_set(subnet):
ip_policy = subnet["ip_policy"] or \
subnet["network"]["ip_policy"] or \
dict()
ip_policy_rules = ip_policy.get("exclude", [])
ip_policy_rules = netaddr.IPSet(
[netaddr.IPNetwork((int(ippr["address"]), ippr["prefix"]))
for ippr in ip_policy_rules])
subnet_set = netaddr.IPSet([netaddr.IPNetwork(subnet["cidr"])])
ip_policy_rules = subnet_set & ip_policy_rules
return ip_policy_rules
def _choose_available_subnet(self, context, net_id, version=None, def _choose_available_subnet(self, context, net_id, version=None,
ip_address=None): ip_address=None):
filters = {} filters = {}
@@ -58,7 +46,8 @@ class QuarkIpam(object):
ip_policy_rules = None ip_policy_rules = None
if not ip_address: if not ip_address:
ip_policy_rules = self.get_ip_policy_rule_set(subnet) ip_policy_rules = models.IPPolicy.get_ip_policy_rule_set(
subnet)
policy_size = ip_policy_rules.size if ip_policy_rules else 0 policy_size = ip_policy_rules.size if ip_policy_rules else 0
if ipnet.size > (ips_in_subnet + policy_size): if ipnet.size > (ips_in_subnet + policy_size):
return subnet return subnet
@@ -118,7 +107,7 @@ class QuarkIpam(object):
subnet = self._choose_available_subnet( subnet = self._choose_available_subnet(
elevated, net_id, ip_address=ip_address, version=version) elevated, net_id, ip_address=ip_address, version=version)
ip_policy_rules = self.get_ip_policy_rule_set(subnet) ip_policy_rules = models.IPPolicy.get_ip_policy_rule_set(subnet)
# Creating this IP for the first time # Creating this IP for the first time
next_ip = None next_ip = None

View File

@@ -149,6 +149,9 @@ class Plugin(neutron_plugin_base_v2.NeutronPluginBaseV2,
def get_ip_policies(self, context, **filters): def get_ip_policies(self, context, **filters):
return ip_policies.get_ip_policies(context, **filters) return ip_policies.get_ip_policies(context, **filters)
def update_ip_policy(self, context, id, ip_policy):
return ip_policies.update_ip_policy(context, id, ip_policy)
def delete_ip_policy(self, context, id): def delete_ip_policy(self, context, id):
return ip_policies.delete_ip_policy(context, id) return ip_policies.delete_ip_policy(context, id)

View File

@@ -13,8 +13,6 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import netaddr
from neutron.common import exceptions from neutron.common import exceptions
from neutron.openstack.common import log as logging from neutron.openstack.common import log as logging
from oslo.config import cfg from oslo.config import cfg
@@ -23,10 +21,8 @@ from quark.db import api as db_api
from quark import exceptions as quark_exceptions from quark import exceptions as quark_exceptions
from quark import plugin_views as v from quark import plugin_views as v
CONF = cfg.CONF CONF = cfg.CONF
LOG = logging.getLogger("neutron.quark") LOG = logging.getLogger("neutron.quark")
DEFAULT_SG_UUID = "00000000-0000-0000-0000-000000000000"
def create_ip_policy(context, ip_policy): def create_ip_policy(context, ip_policy):
@@ -38,29 +34,35 @@ def create_ip_policy(context, ip_policy):
raise exceptions.BadRequest(resource="ip_policy", raise exceptions.BadRequest(resource="ip_policy",
msg="Empty ip_policy.exclude regions") msg="Empty ip_policy.exclude regions")
ipp["exclude"] = netaddr.IPSet(ipp["exclude"]) network_ids = ipp.get("network_ids")
network_id = ipp.get("network_id") subnet_ids = ipp.get("subnet_ids")
subnet_id = ipp.get("subnet_id")
model = None if not subnet_ids and not network_ids:
if subnet_id:
model = db_api.subnet_find(context, id=subnet_id, scope=db_api.ONE)
if not model:
raise exceptions.SubnetNotFound(id=subnet_id)
elif network_id:
model = db_api.network_find(context, id=network_id,
scope=db_api.ONE)
if not model:
raise exceptions.NetworkNotFound(id=network_id)
else:
raise exceptions.BadRequest( raise exceptions.BadRequest(
resource="ip_policy", resource="ip_policy",
msg="network_id or subnet_id unspecified") msg="network_ids or subnet_ids not specified")
models = []
if subnet_ids:
subnets = db_api.subnet_find(
context, id=subnet_ids, scope=db_api.ALL)
if not subnets:
raise exceptions.SubnetNotFound(id=subnet_ids)
models.extend(subnets)
if network_ids:
nets = db_api.network_find(
context, id=network_ids, scope=db_api.ALL)
if not nets:
raise exceptions.NetworkNotFound(net_id=network_ids)
models.extend(nets)
for model in models:
if model["ip_policy"]:
raise quark_exceptions.IPPolicyAlreadyExists(
id=model["ip_policy"]["id"], n_id=model["id"])
model["ip_policy"] = db_api.ip_policy_create(context, **ipp)
if model["ip_policy"]:
raise quark_exceptions.IPPolicyAlreadyExists(
id=model["ip_policy"]["id"], n_id=model["id"])
model["ip_policy"] = db_api.ip_policy_create(context, **ipp)
return v._make_ip_policy_dict(model["ip_policy"]) return v._make_ip_policy_dict(model["ip_policy"])
@@ -78,9 +80,51 @@ def get_ip_policies(context, **filters):
return [v._make_ip_policy_dict(ipp) for ipp in ipps] return [v._make_ip_policy_dict(ipp) for ipp in ipps]
def update_ip_policy(context, id, ip_policy):
LOG.info("update_ip_policy for tenant %s" % context.tenant_id)
ipp = ip_policy["ip_policy"]
ipp_db = db_api.ip_policy_find(context, id=id, scope=db_api.ONE)
if not ipp_db:
raise quark_exceptions.IPPolicyNotFound(id=id)
network_ids = ipp.get("network_ids")
subnet_ids = ipp.get("subnet_ids")
models = []
if subnet_ids:
for subnet in ipp_db["subnets"]:
subnet["ip_policy"] = None
subnets = db_api.subnet_find(
context, id=subnet_ids, scope=db_api.ALL)
if len(subnets) != len(subnet_ids):
raise exceptions.SubnetNotFound(id=subnet_ids)
models.extend(subnets)
if network_ids:
for network in ipp_db["networks"]:
network["ip_policy"] = None
nets = db_api.network_find(context, id=network_ids, scope=db_api.ALL)
if len(nets) != len(network_ids):
raise exceptions.NetworkNotFound(net_id=network_ids)
models.extend(nets)
for model in models:
if model["ip_policy"]:
raise quark_exceptions.IPPolicyAlreadyExists(
id=model["ip_policy"]["id"], n_id=model["id"])
model["ip_policy"] = ipp_db
ipp_db = db_api.ip_policy_update(context, ipp_db, **ipp)
return v._make_ip_policy_dict(ipp_db)
def delete_ip_policy(context, id): def delete_ip_policy(context, id):
LOG.info("delete_ip_policy %s for tenant %s" % (id, context.tenant_id)) LOG.info("delete_ip_policy %s for tenant %s" % (id, context.tenant_id))
ipp = db_api.ip_policy_find(context, id=id, scope=db_api.ONE) ipp = db_api.ip_policy_find(context, id=id, scope=db_api.ONE)
if not ipp: if not ipp:
raise quark_exceptions.IPPolicyNotFound(id=id) raise quark_exceptions.IPPolicyNotFound(id=id)
if ipp["networks"] or ipp["subnets"]:
raise quark_exceptions.IPPolicyInUse(id=id)
db_api.ip_policy_delete(context, ipp) db_api.ip_policy_delete(context, ipp)

View File

@@ -94,7 +94,7 @@ def create_subnet(context, subnet):
gateway_ip = utils.pop_param(sub_attrs, "gateway_ip", str(cidr[1])) gateway_ip = utils.pop_param(sub_attrs, "gateway_ip", str(cidr[1]))
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", []) allocation_pools = utils.pop_param(sub_attrs, "allocation_pools", None)
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)
@@ -116,13 +116,20 @@ def create_subnet(context, subnet):
new_subnet["dns_nameservers"].append(db_api.dns_create( new_subnet["dns_nameservers"].append(db_api.dns_create(
context, ip=netaddr.IPAddress(dns_ip))) context, ip=netaddr.IPAddress(dns_ip)))
if allocation_pools: if isinstance(allocation_pools, list):
exclude = netaddr.IPSet([cidr]) ranges = []
cidrset = netaddr.IPSet([netaddr.IPNetwork(new_subnet["cidr"])])
for p in allocation_pools: for p in allocation_pools:
x = netaddr.IPSet(netaddr.IPRange(p["start"], p["end"])) cidrset -= netaddr.IPSet(netaddr.IPRange(p["start"], p["end"]))
exclude = exclude - x non_allocation_pools = v._pools_from_cidr(cidrset)
for p in non_allocation_pools:
r = netaddr.IPRange(p["start"], p["end"])
ranges.append(dict(
length=len(r),
offset=int(r[0]) - int(cidr[0])))
new_subnet["ip_policy"] = db_api.ip_policy_create(context, new_subnet["ip_policy"] = db_api.ip_policy_create(context,
exclude=exclude) exclude=ranges)
subnet_dict = v._make_subnet_dict(new_subnet, subnet_dict = v._make_subnet_dict(new_subnet,
default_route=routes.DEFAULT_ROUTE) default_route=routes.DEFAULT_ROUTE)
subnet_dict["gateway_ip"] = gateway_ip subnet_dict["gateway_ip"] = gateway_ip

View File

@@ -22,7 +22,7 @@ import netaddr
from neutron.extensions import securitygroup as sg_ext from neutron.extensions import securitygroup as sg_ext
from quark.db import api as db_api from quark.db import api as db_api
from quark.ipam import QuarkIpam from quark.db import models
from quark import network_strategy from quark import network_strategy
from quark import utils from quark import utils
@@ -44,35 +44,38 @@ def _make_network_dict(network, fields=None):
return res return res
def _pools_from_cidr(cidr):
cidrs = cidr.iter_cidrs()
if len(cidrs) == 0:
return []
if len(cidrs) == 1:
return [dict(start=str(cidrs[0][0]),
end=str(cidrs[0][-1]))]
pool_start = cidrs[0][0]
prev_cidr_end = cidrs[0][-1]
pools = []
for cidr in cidrs[1:]:
cidr_start = cidr[0]
if prev_cidr_end + 1 != cidr_start:
pools.append(dict(start=str(pool_start),
end=str(prev_cidr_end)))
pool_start = cidr_start
prev_cidr_end = cidr[-1]
pools.append(dict(start=str(pool_start), end=str(prev_cidr_end)))
return pools
def _make_subnet_dict(subnet, default_route=None, fields=None): def _make_subnet_dict(subnet, default_route=None, fields=None):
dns_nameservers = [str(netaddr.IPAddress(dns["ip"])) dns_nameservers = [str(netaddr.IPAddress(dns["ip"]))
for dns in subnet.get("dns_nameservers")] for dns in subnet.get("dns_nameservers")]
net_id = STRATEGY.get_parent_network(subnet["network_id"]) net_id = STRATEGY.get_parent_network(subnet["network_id"])
def _allocation_pools(subnet): def _allocation_pools(subnet):
ip_policy_rules = QuarkIpam.get_ip_policy_rule_set(subnet) ip_policy_rules = models.IPPolicy.get_ip_policy_rule_set(subnet)
cidr = netaddr.IPSet([netaddr.IPNetwork(subnet["cidr"])]) cidr = netaddr.IPSet([netaddr.IPNetwork(subnet["cidr"])])
allocatable = cidr - ip_policy_rules allocatable = cidr - ip_policy_rules
return _pools_from_cidr(allocatable)
cidrs = allocatable.iter_cidrs()
if len(cidrs) == 0:
return []
if len(cidrs) == 1:
return [dict(start=str(cidrs[0][0]),
end=str(cidrs[0][-1]))]
pool_start = cidrs[0][0]
prev_cidr_end = cidrs[0][-1]
pools = []
for cidr in cidrs[1:]:
cidr_start = cidr[0]
if prev_cidr_end + 1 != cidr_start:
pools.append(dict(start=str(pool_start),
end=str(prev_cidr_end)))
pool_start = cidr_start
prev_cidr_end = cidr[-1]
pools.append(dict(start=str(pool_start), end=str(prev_cidr_end)))
return pools
res = {"id": subnet.get("id"), res = {"id": subnet.get("id"),
"name": subnet.get("name"), "name": subnet.get("name"),
@@ -200,11 +203,13 @@ def _make_ip_dict(address):
def _make_ip_policy_dict(ipp): def _make_ip_policy_dict(ipp):
excludes = [str(netaddr.IPNetwork((int(ippr["address"]), ippr["prefix"]))) excludes = [dict(offset=range["offset"], length=range["length"])
for ippr in ipp["exclude"]] for range in ipp["exclude"]]
return {"id": ipp["id"], return {"id": ipp["id"],
"subnet_id": ipp["subnet_id"], "tenant_id": ipp["tenant_id"],
"network_id": ipp["network_id"], "name": ipp["name"],
"subnet_ids": [s["id"] for s in ipp["subnets"]],
"network_ids": [n["id"] for n in ipp["networks"]],
"exclude": excludes} "exclude": excludes}

View File

@@ -37,36 +37,42 @@ class TestQuarkGetIpPolicies(test_quark_plugin.TestQuarkPlugin):
self.plugin.get_ip_policy(self.context, 1) self.plugin.get_ip_policy(self.context, 1)
def test_get_ip_policy(self): def test_get_ip_policy(self):
address = int(netaddr.IPAddress("1.1.1.1"))
ip_policy = dict( ip_policy = dict(
id=1, id=1,
subnet_id=1, tenant_id=1,
network_id=2, name="foo",
exclude=[dict(address=address, prefix=24)]) subnets=[dict(id=1)],
networks=[dict(id=2)],
exclude=[dict(offset=1, length=256)])
with self._stubs(ip_policy): with self._stubs(ip_policy):
resp = self.plugin.get_ip_policy(self.context, 1) resp = self.plugin.get_ip_policy(self.context, 1)
self.assertEqual(len(resp.keys()), 4) self.assertEqual(len(resp.keys()), 6)
self.assertEqual(resp["id"], 1) self.assertEqual(resp["id"], 1)
self.assertEqual(resp["subnet_id"], 1) self.assertEqual(resp["name"], "foo")
self.assertEqual(resp["network_id"], 2) self.assertEqual(resp["subnet_ids"], [1])
self.assertEqual(resp["exclude"], ["1.1.1.1/24"]) self.assertEqual(resp["network_ids"], [2])
self.assertEqual(resp["exclude"], ip_policy["exclude"])
self.assertEqual(resp["tenant_id"], 1)
def test_get_ip_policies(self): def test_get_ip_policies(self):
address = int(netaddr.IPAddress("1.1.1.1"))
ip_policy = dict( ip_policy = dict(
id=1, id=1,
subnet_id=1, tenant_id=1,
network_id=2, name="foo",
exclude=[dict(address=address, prefix=24)]) subnets=[dict(id=1)],
networks=[dict(id=2)],
exclude=[dict(offset=1, length=256)])
with self._stubs([ip_policy]): with self._stubs([ip_policy]):
resp = self.plugin.get_ip_policies(self.context) resp = self.plugin.get_ip_policies(self.context)
self.assertEqual(len(resp), 1) self.assertEqual(len(resp), 1)
resp = resp[0] resp = resp[0]
self.assertEqual(len(resp.keys()), 4) self.assertEqual(len(resp.keys()), 6)
self.assertEqual(resp["id"], 1) self.assertEqual(resp["id"], 1)
self.assertEqual(resp["subnet_id"], 1) self.assertEqual(resp["subnet_ids"], [1])
self.assertEqual(resp["network_id"], 2) self.assertEqual(resp["network_ids"], [2])
self.assertEqual(resp["exclude"], ["1.1.1.1/24"]) self.assertEqual(resp["exclude"], ip_policy["exclude"])
self.assertEqual(resp["name"], "foo")
self.assertEqual(resp["tenant_id"], 1)
class TestQuarkCreateIpPolicies(test_quark_plugin.TestQuarkPlugin): class TestQuarkCreateIpPolicies(test_quark_plugin.TestQuarkPlugin):
@@ -78,8 +84,8 @@ class TestQuarkCreateIpPolicies(test_quark_plugin.TestQuarkPlugin):
mock.patch("%s.network_find" % db_mod), mock.patch("%s.network_find" % db_mod),
mock.patch("%s.ip_policy_create" % db_mod), mock.patch("%s.ip_policy_create" % db_mod),
) as (subnet_find, net_find, ip_policy_create): ) as (subnet_find, net_find, ip_policy_create):
subnet_find.return_value = subnet subnet_find.return_value = [subnet] if subnet else None
net_find.return_value = net net_find.return_value = [net] if net else None
ip_policy_create.return_value = ip_policy ip_policy_create.return_value = ip_policy
yield ip_policy_create yield ip_policy_create
@@ -99,28 +105,28 @@ class TestQuarkCreateIpPolicies(test_quark_plugin.TestQuarkPlugin):
with self._stubs(None): with self._stubs(None):
with self.assertRaises(exceptions.SubnetNotFound): with self.assertRaises(exceptions.SubnetNotFound):
self.plugin.create_ip_policy(self.context, dict( self.plugin.create_ip_policy(self.context, dict(
ip_policy=dict(subnet_id=1, ip_policy=dict(subnet_ids=[1],
exclude=["1.1.1.1/24"]))) exclude=["1.1.1.1/24"])))
def test_create_ip_policy_invalid_network(self): def test_create_ip_policy_invalid_network(self):
with self._stubs(None): with self._stubs(None):
with self.assertRaises(exceptions.NetworkNotFound): with self.assertRaises(exceptions.NetworkNotFound):
self.plugin.create_ip_policy(self.context, dict( self.plugin.create_ip_policy(self.context, dict(
ip_policy=dict(network_id=1, ip_policy=dict(network_ids=[1],
exclude=["1.1.1.1/24"]))) exclude=["1.1.1.1/24"])))
def test_create_ip_policy_network_ip_policy_already_exists(self): def test_create_ip_policy_network_ip_policy_already_exists(self):
with self._stubs(None, net=dict(id=1, ip_policy=dict(id=2))): with self._stubs(None, net=dict(id=1, ip_policy=dict(id=2))):
with self.assertRaises(quark_exceptions.IPPolicyAlreadyExists): with self.assertRaises(quark_exceptions.IPPolicyAlreadyExists):
self.plugin.create_ip_policy(self.context, dict( self.plugin.create_ip_policy(self.context, dict(
ip_policy=dict(network_id=1, ip_policy=dict(network_ids=[1],
exclude=["1.1.1.1/24"]))) exclude=["1.1.1.1/24"])))
def test_create_ip_policy_subnet_ip_policy_already_exists(self): def test_create_ip_policy_subnet_ip_policy_already_exists(self):
with self._stubs(None, subnet=dict(id=1, ip_policy=dict(id=2))): with self._stubs(None, subnet=dict(id=1, ip_policy=dict(id=2))):
with self.assertRaises(quark_exceptions.IPPolicyAlreadyExists): with self.assertRaises(quark_exceptions.IPPolicyAlreadyExists):
self.plugin.create_ip_policy(self.context, dict( self.plugin.create_ip_policy(self.context, dict(
ip_policy=dict(subnet_id=1, ip_policy=dict(subnet_ids=[1],
exclude=["1.1.1.1/24"]))) exclude=["1.1.1.1/24"])))
def test_create_ip_policy_network(self): def test_create_ip_policy_network(self):
@@ -130,12 +136,12 @@ class TestQuarkCreateIpPolicies(test_quark_plugin.TestQuarkPlugin):
with self._stubs(ipp, net=dict(id=1, ip_policy=dict(id=2))): with self._stubs(ipp, net=dict(id=1, ip_policy=dict(id=2))):
with self.assertRaises(quark_exceptions.IPPolicyAlreadyExists): with self.assertRaises(quark_exceptions.IPPolicyAlreadyExists):
resp = self.plugin.create_ip_policy(self.context, dict( resp = self.plugin.create_ip_policy(self.context, dict(
ip_policy=dict(network_id=1, ip_policy=dict(network_ids=[1],
exclude=["1.1.1.1/24"]))) exclude=["1.1.1.1/24"])))
self.assertEqual(len(resp.keys()), 3) self.assertEqual(len(resp.keys()), 3)
self.assertIsNone(resp["subnet_id"]) self.assertIsNone(resp["subnet_ids"])
self.assertEqual(resp["network_id"], 1) self.assertEqual(resp["network_ids"], 1)
self.assertEqual(resp["exclude"], ["1.1.1.1/24"]) self.assertEqual(resp["exclude"], [dict()])
def test_create_ip_policy_subnet(self): def test_create_ip_policy_subnet(self):
ipp = dict(subnet_id=1, network_id=None, ipp = dict(subnet_id=1, network_id=None,
@@ -144,7 +150,7 @@ class TestQuarkCreateIpPolicies(test_quark_plugin.TestQuarkPlugin):
with self._stubs(ipp, subnet=dict(id=1, ip_policy=dict(id=2))): with self._stubs(ipp, subnet=dict(id=1, ip_policy=dict(id=2))):
with self.assertRaises(quark_exceptions.IPPolicyAlreadyExists): with self.assertRaises(quark_exceptions.IPPolicyAlreadyExists):
resp = self.plugin.create_ip_policy(self.context, dict( resp = self.plugin.create_ip_policy(self.context, dict(
ip_policy=dict(subnet_id=1, ip_policy=dict(subnet_ids=[1],
exclude=["1.1.1.1/24"]))) exclude=["1.1.1.1/24"])))
self.assertEqual(len(resp.keys()), 3) self.assertEqual(len(resp.keys()), 3)
self.assertEqual(resp["subnet_id"], 1) self.assertEqual(resp["subnet_id"], 1)
@@ -152,17 +158,109 @@ class TestQuarkCreateIpPolicies(test_quark_plugin.TestQuarkPlugin):
self.assertEqual(resp["exclude"], ["1.1.1.1/24"]) self.assertEqual(resp["exclude"], ["1.1.1.1/24"])
def test_create_ip_policy(self): def test_create_ip_policy(self):
ipp = dict(subnet_id=1, network_id=None, id=1, ipp = dict(
exclude=[dict(address=int(netaddr.IPAddress("1.1.1.1")), subnets=[dict(id=1)],
prefix=24)]) networks=[],
id=1,
tenant_id=1,
exclude=[dict(offset=0, length=256)],
name="foo")
with self._stubs(ipp, subnet=dict(id=1, ip_policy=None)): with self._stubs(ipp, subnet=dict(id=1, ip_policy=None)):
resp = self.plugin.create_ip_policy(self.context, dict( resp = self.plugin.create_ip_policy(self.context, dict(
ip_policy=dict(subnet_id=1, ip_policy=dict(subnet_ids=[1],
exclude=["1.1.1.1/24"]))) exclude=[dict(offset=0, length=256)])))
self.assertEqual(len(resp.keys()), 4) self.assertEqual(len(resp.keys()), 6)
self.assertEqual(resp["subnet_id"], 1) self.assertEqual(resp["subnet_ids"], [1])
self.assertIsNone(resp["network_id"]) self.assertEqual(resp["network_ids"], [])
self.assertEqual(resp["exclude"], ["1.1.1.1/24"]) self.assertEqual(resp["exclude"],
[dict(offset=0, length=256)])
self.assertEqual(resp["name"], "foo")
self.assertEqual(resp["tenant_id"], 1)
class TestQuarkUpdateIpPolicies(test_quark_plugin.TestQuarkPlugin):
@contextlib.contextmanager
def _stubs(self, ip_policy, subnets=None, networks=None):
if not subnets:
subnets = []
if not networks:
networks = []
db_mod = "quark.db.api"
with contextlib.nested(
mock.patch("%s.ip_policy_find" % db_mod),
mock.patch("%s.subnet_find" % db_mod),
mock.patch("%s.network_find" % db_mod),
mock.patch("%s.ip_policy_update" % db_mod),
) as (ip_policy_find, subnet_find, network_find, ip_policy_update):
ip_policy_find.return_value = ip_policy
subnet_find.return_value = subnets
network_find.return_value = networks
yield ip_policy_update
def test_update_ip_policy_not_found(self):
with self._stubs(None) as (ip_policy_update):
with self.assertRaises(quark_exceptions.IPPolicyNotFound):
self.plugin.update_ip_policy(self.context, 1,
dict(ip_policy=None))
self.assertEqual(ip_policy_update.called, 0)
def test_update_ip_policy_subnets_not_found(self):
ipp = dict(id=1, subnets=[])
with self._stubs(ipp) as (ip_policy_update):
with self.assertRaises(exceptions.SubnetNotFound):
self.plugin.update_ip_policy(
self.context,
1,
dict(ip_policy=dict(subnet_ids=[100])))
self.assertEqual(ip_policy_update.called, 0)
def test_update_ip_policy_subnets_already_exists(self):
ipp = dict(id=1, subnets=[dict()])
with self._stubs(
ipp, subnets=[dict(id=1, ip_policy=dict(id=1))]
) as (ip_policy_update):
with self.assertRaises(quark_exceptions.IPPolicyAlreadyExists):
self.plugin.update_ip_policy(
self.context,
1,
dict(ip_policy=dict(subnet_ids=[100])))
self.assertEqual(ip_policy_update.called, 0)
def test_update_ip_policy_subnets(self):
ipp = dict(id=1, subnets=[dict()],
exclude=[dict(offset=0, length=256)],
name="foo", tenant_id=1)
with self._stubs(
ipp, subnets=[dict(id=1, ip_policy=None)]
) as (ip_policy_update):
self.plugin.update_ip_policy(
self.context,
1,
dict(ip_policy=dict(subnet_ids=[100])))
self.assertEqual(ip_policy_update.called, 1)
def test_update_ip_policy_networks_not_found(self):
ipp = dict(id=1, networks=[])
with self._stubs(ipp) as (ip_policy_update):
with self.assertRaises(exceptions.NetworkNotFound):
self.plugin.update_ip_policy(
self.context,
1,
dict(ip_policy=dict(network_ids=[100])))
self.assertEqual(ip_policy_update.called, 0)
def test_update_ip_policy_networks(self):
ipp = dict(id=1, networks=[dict()],
exclude=[dict(offset=0, length=256)],
name="foo", tenant_id=1)
with self._stubs(
ipp, networks=[dict(id=1, ip_policy=None)]
) as (ip_policy_update):
self.plugin.update_ip_policy(
self.context,
1,
dict(ip_policy=dict(network_ids=[100])))
self.assertEqual(ip_policy_update.called, 1)
class TestQuarkDeleteIpPolicies(test_quark_plugin.TestQuarkPlugin): class TestQuarkDeleteIpPolicies(test_quark_plugin.TestQuarkPlugin):
@@ -181,13 +279,16 @@ class TestQuarkDeleteIpPolicies(test_quark_plugin.TestQuarkPlugin):
with self.assertRaises(quark_exceptions.IPPolicyNotFound): with self.assertRaises(quark_exceptions.IPPolicyNotFound):
self.plugin.delete_ip_policy(self.context, 1) self.plugin.delete_ip_policy(self.context, 1)
def test_delete_ip_policy_in_use(self):
with self._stubs(dict(networks=True)):
with self.assertRaises(quark_exceptions.IPPolicyInUse):
self.plugin.delete_ip_policy(self.context, 1)
def test_delete_ip_policy(self): def test_delete_ip_policy(self):
address = int(netaddr.IPAddress("1.1.1.1"))
ip_policy = dict( ip_policy = dict(
id=1, id=1,
subnet_id=1, networks=[],
network_id=2, subnets=[])
exclude=[dict(address=address, prefix=24)])
with self._stubs(ip_policy) as (ip_policy_find, ip_policy_delete): with self._stubs(ip_policy) as (ip_policy_find, ip_policy_delete):
self.plugin.delete_ip_policy(self.context, 1) self.plugin.delete_ip_policy(self.context, 1)
self.assertEqual(ip_policy_find.call_count, 1) self.assertEqual(ip_policy_find.call_count, 1)

View File

@@ -179,22 +179,31 @@ class TestQuarkCreateSubnetAllocationPools(test_quark_plugin.TestQuarkPlugin):
with contextlib.nested( with contextlib.nested(
mock.patch("quark.db.api.network_find"), mock.patch("quark.db.api.network_find"),
mock.patch("quark.db.api.subnet_find"), mock.patch("quark.db.api.subnet_find"),
mock.patch("quark.db.api.subnet_create") mock.patch("quark.db.api.subnet_create"),
) as (net_find, subnet_find, subnet_create): ) as (net_find, subnet_find, subnet_create):
net_find.return_value = s["network"] net_find.return_value = s["network"]
subnet_find.return_value = [] subnet_find.return_value = []
subnet_create.return_value = s subnet_create.return_value = s
yield subnet_create yield subnet_create
def setUp(self):
super(TestQuarkCreateSubnetAllocationPools, self).setUp()
models.IPPolicy.DEFAULT_POLICY = models.IPPolicy(
exclude=[models.IPPolicyRange(offset=-1, length=3)])
def tearDown(self):
super(TestQuarkCreateSubnetAllocationPools, self).tearDown()
models.IPPolicy.DEFAULT_POLICY = {}
def test_create_subnet_allocation_pools_zero(self): def test_create_subnet_allocation_pools_zero(self):
s = dict(subnet=dict( s = dict(subnet=dict(
cidr="192.168.1.1/24", cidr="192.168.1.1/24",
network_id=1)) network_id=1))
with self._stubs(s["subnet"]) as subnet_create: with self._stubs(s["subnet"]) as (subnet_create):
resp = self.plugin.create_subnet(self.context, s) resp = self.plugin.create_subnet(self.context, s)
self.assertEqual(subnet_create.call_count, 1) self.assertEqual(subnet_create.call_count, 1)
self.assertEqual(resp["allocation_pools"], self.assertEqual(resp["allocation_pools"],
[dict(start="192.168.1.0", end="192.168.1.255")]) [dict(start="192.168.1.2", end="192.168.1.254")])
def test_create_subnet_allocation_pools_one(self): def test_create_subnet_allocation_pools_one(self):
pools = [dict(start="192.168.1.10", end="192.168.1.20")] pools = [dict(start="192.168.1.10", end="192.168.1.20")]
@@ -202,7 +211,7 @@ class TestQuarkCreateSubnetAllocationPools(test_quark_plugin.TestQuarkPlugin):
allocation_pools=pools, allocation_pools=pools,
cidr="192.168.1.1/24", cidr="192.168.1.1/24",
network_id=1)) network_id=1))
with self._stubs(s["subnet"]) as subnet_create: with self._stubs(s["subnet"]) as (subnet_create):
resp = self.plugin.create_subnet(self.context, s) resp = self.plugin.create_subnet(self.context, s)
self.assertEqual(subnet_create.call_count, 1) self.assertEqual(subnet_create.call_count, 1)
self.assertEqual(resp["allocation_pools"], pools) self.assertEqual(resp["allocation_pools"], pools)
@@ -214,7 +223,18 @@ class TestQuarkCreateSubnetAllocationPools(test_quark_plugin.TestQuarkPlugin):
allocation_pools=pools, allocation_pools=pools,
cidr="192.168.1.1/24", cidr="192.168.1.1/24",
network_id=1)) network_id=1))
with self._stubs(s["subnet"]) as subnet_create: with self._stubs(s["subnet"]) as (subnet_create):
resp = self.plugin.create_subnet(self.context, s)
self.assertEqual(subnet_create.call_count, 1)
self.assertEqual(resp["allocation_pools"], pools)
def test_create_subnet_allocation_pools_empty_list(self):
pools = []
s = dict(subnet=dict(
allocation_pools=pools,
cidr="192.168.1.1/24",
network_id=1))
with self._stubs(s["subnet"]) as (subnet_create):
resp = self.plugin.create_subnet(self.context, s) resp = self.plugin.create_subnet(self.context, s)
self.assertEqual(subnet_create.call_count, 1) self.assertEqual(subnet_create.call_count, 1)
self.assertEqual(resp["allocation_pools"], pools) self.assertEqual(resp["allocation_pools"], pools)

View File

@@ -391,7 +391,7 @@ class TestQuarkIpPoliciesIpAllocation(QuarkIpamBaseTest):
cidr="0.0.0.0/24", ip_version=4, cidr="0.0.0.0/24", ip_version=4,
next_auto_assign_ip=0, network=dict(ip_policy=None), next_auto_assign_ip=0, network=dict(ip_policy=None),
ip_policy=dict(exclude= ip_policy=dict(exclude=
[dict(address=0, prefix=24)])) [dict(offset=0, length=256)]))
with self._stubs(subnets=[(subnet, 0)], addresses=[None, None]): with self._stubs(subnets=[(subnet, 0)], addresses=[None, None]):
with self.assertRaises(exceptions.IpAddressGenerationFailure): with self.assertRaises(exceptions.IpAddressGenerationFailure):
self.ipam.allocate_ip_address(self.context, 0, 0, 0, version=4) self.ipam.allocate_ip_address(self.context, 0, 0, 0, version=4)
@@ -401,8 +401,18 @@ class TestQuarkIpPoliciesIpAllocation(QuarkIpamBaseTest):
cidr="0.0.0.0/24", ip_version=4, cidr="0.0.0.0/24", ip_version=4,
next_auto_assign_ip=0, network=dict(ip_policy=None), next_auto_assign_ip=0, network=dict(ip_policy=None),
ip_policy=dict(exclude= ip_policy=dict(exclude=
[dict(address=0, prefix=32), [dict(offset=0, length=2)]))
dict(address=1, prefix=32)])) with self._stubs(subnets=[(subnet, 0)], addresses=[None, None]):
address = self.ipam.allocate_ip_address(self.context, 0, 0, 0,
version=4)
self.assertEqual(address["address"], 2)
def test_ip_policy_on_subnet_negative_offset(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=
[dict(offset=-1, length=3)]))
with self._stubs(subnets=[(subnet, 0)], addresses=[None, None]): with self._stubs(subnets=[(subnet, 0)], addresses=[None, None]):
address = self.ipam.allocate_ip_address(self.context, 0, 0, 0, address = self.ipam.allocate_ip_address(self.context, 0, 0, 0,
version=4) version=4)
@@ -410,8 +420,7 @@ class TestQuarkIpPoliciesIpAllocation(QuarkIpamBaseTest):
def test_ip_policy_on_network(self): def test_ip_policy_on_network(self):
net = dict(ip_policy=dict(exclude= net = dict(ip_policy=dict(exclude=
[dict(address=0, prefix=32), [dict(offset=0, length=2)]))
dict(address=1, prefix=32)]))
subnet = dict(id=1, first_ip=0, last_ip=255, subnet = dict(id=1, first_ip=0, last_ip=255,
cidr="0.0.0.0/24", ip_version=4, cidr="0.0.0.0/24", ip_version=4,
next_auto_assign_ip=0, network=net, next_auto_assign_ip=0, network=net,
@@ -423,9 +432,8 @@ class TestQuarkIpPoliciesIpAllocation(QuarkIpamBaseTest):
def test_ip_policy_on_network_exclusion_intersection(self): def test_ip_policy_on_network_exclusion_intersection(self):
net = dict(ip_policy=dict(exclude= net = dict(ip_policy=dict(exclude=
[dict(address=0, prefix=32), [dict(offset=0, length=2),
dict(address=1, prefix=32), dict(offset=254, length=1)]))
dict(address=254, prefix=32)]))
subnet = dict(id=1, first_ip=0, last_ip=63, subnet = dict(id=1, first_ip=0, last_ip=63,
cidr="0.0.0.0/30", ip_version=4, cidr="0.0.0.0/30", ip_version=4,
next_auto_assign_ip=0, network=net, next_auto_assign_ip=0, network=net,
@@ -437,14 +445,14 @@ class TestQuarkIpPoliciesIpAllocation(QuarkIpamBaseTest):
def test_ip_policy_on_both_subnet_preferred(self): def test_ip_policy_on_both_subnet_preferred(self):
net = dict(ip_policy=dict(exclude= net = dict(ip_policy=dict(exclude=
[dict(address=0, prefix=32), [dict(offset=0, length=1),
dict(address=1, prefix=32)])) dict(offset=1, length=1)]))
subnet = dict(id=1, first_ip=0, last_ip=255, subnet = dict(id=1, first_ip=0, last_ip=255,
cidr="0.0.0.0/24", ip_version=4, cidr="0.0.0.0/24", ip_version=4,
next_auto_assign_ip=0, network=net, next_auto_assign_ip=0, network=net,
ip_policy=dict(exclude= ip_policy=dict(exclude=
[dict(address=254, prefix=32), [dict(offset=254, length=1),
dict(address=255, prefix=32)])) dict(offset=255, length=1)]))
with self._stubs(subnets=[(subnet, 0)], addresses=[None, None]): with self._stubs(subnets=[(subnet, 0)], addresses=[None, None]):
address = self.ipam.allocate_ip_address(self.context, 0, 0, 0, address = self.ipam.allocate_ip_address(self.context, 0, 0, 0,
version=4) version=4)
@@ -454,7 +462,7 @@ class TestQuarkIpPoliciesIpAllocation(QuarkIpamBaseTest):
subnet1 = dict(id=1, first_ip=0, last_ip=255, subnet1 = dict(id=1, first_ip=0, last_ip=255,
cidr="0.0.0.0/24", ip_version=4, cidr="0.0.0.0/24", ip_version=4,
network=dict(ip_policy=None), network=dict(ip_policy=None),
ip_policy=dict(exclude=[dict(address=240, prefix=32)])) ip_policy=dict(exclude=[dict(offset=240, length=1)]))
subnets = [(subnet1, 1)] subnets = [(subnet1, 1)]
with self._stubs(subnets=subnets, addresses=[None, None]): with self._stubs(subnets=subnets, addresses=[None, None]):
address = self.ipam.allocate_ip_address( address = self.ipam.allocate_ip_address(