Skip to content

Instantly share code, notes, and snippets.

@salv-orlando
Created March 16, 2015 00:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save salv-orlando/22e524979fe7e2d18d2d to your computer and use it in GitHub Desktop.
Save salv-orlando/22e524979fe7e2d18d2d to your computer and use it in GitHub Desktop.
changes for IPAM patches
diff --git a/neutron/db/db_base_plugin_v2.py b/neutron/db/db_base_plugin_v2.py
index 479b816..4ac2a66 100644
--- a/neutron/db/db_base_plugin_v2.py
+++ b/neutron/db/db_base_plugin_v2.py
@@ -149,15 +149,16 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
def _delete_ip_allocation(context, network_id, subnet_id, ip_address):
# Delete the IP address from the IPAllocate table
- LOG.debug("Delete allocated IP %(ip_address)s "
+ LOG.warn("Delete allocated IP %(ip_address)s "
"(%(network_id)s/%(subnet_id)s)",
{'ip_address': ip_address,
'network_id': network_id,
'subnet_id': subnet_id})
- context.session.query(models_v2.IPAllocation).filter_by(
+ count = context.session.query(models_v2.IPAllocation).filter_by(
network_id=network_id,
ip_address=ip_address,
subnet_id=subnet_id).delete()
+ LOG.warn("### COUNT DEL:%s", count)
@staticmethod
def _store_ip_allocation(context, ip_address, network_id, subnet_id,
@@ -383,6 +384,7 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
InvalidIpForSubnet
"""
fixed_ip_set = []
+ LOG.warn("#### HERE: fixedIPS:%s", fixed_ips)
for fixed in fixed_ips:
found = False
if 'subnet_id' not in fixed:
@@ -413,7 +415,7 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
raise n_exc.InvalidInput(error_message=msg)
subnet_id = subnet['id']
subnet_cidr = subnet['cidr']
-
+ LOG.warn("#### HERE: fixed:%s", fixed)
if 'ip_address' in fixed:
if (ipv6_utils.is_auto_address_subnet(subnet) and
device_owner not in
@@ -428,8 +430,12 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
'subnet_cidr': subnet_cidr,
'ip_address': fixed['ip_address']})
else:
- fixed_ip_set.append({'subnet_id': subnet_id,
- 'subnet_cidr': subnet_cidr})
+ fixed_ip = {'subnet_id': subnet_id,
+ 'subnet_cidr': subnet_cidr}
+ if ipv6_utils.is_auto_address_subnet(subnet):
+ fixed_ip['eui64_address'] = True
+ fixed_ip_set.append(fixed_ip)
+
if len(fixed_ip_set) > cfg.CONF.max_fixed_ips_per_port:
msg = _('Exceeded maximim amount of fixed ips per port')
raise n_exc.InvalidInput(error_message=msg)
@@ -547,7 +553,8 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
try:
for ip in ips:
- ipam_subnet = ipam_driver.get_subnet(ip['subnet_cidr'])
+ ipam_subnet = ipam_driver.get_subnet(ip['subnet_cidr'],
+ ip['subnet_id'])
ipam_subnet.deallocate(ip['ip_address'])
deallocated.append(ip)
except Exception:
@@ -569,16 +576,19 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
allocated = []
try:
+ LOG.warn("### IP Addresses:%s", ips)
for ip in ips:
- if 'address_type' in ip:
- ip_request = ipam.AutomaticAddressRequest(
- address_type=ip['address_type'],
+ # A factory pattern with a tweak
+ if 'eui64_address' in ip:
+ ip_request = ipam.Eui64AddressRequest(
prefix=ip['subnet_cidr'],
- mac=ip['mac'])
+ mac_address=ip['mac'])
else:
fixed_ip = ip['ip_address'] if 'ip_address' in ip else None
ip_request = ipam.AddressRequestFactory(fixed_ip)
- ipam_subnet = ipam_driver.get_subnet(ip['subnet_cidr'])
+ ipam_subnet = ipam_driver.get_subnet(ip['subnet_cidr'],
+ ip['subnet_id'])
+ LOG.warn("### ALLOCATING: %s:", ip)
allocated_ip = ipam_subnet.allocate(ip_request)
allocated.append({'ip_address': allocated_ip,
'subnet_cidr': ip['subnet_cidr'],
@@ -609,7 +619,8 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
subnet_pool_id = context.subnetpool_id
return ipam_base.Pool.get_instance(subnet_pool_id)
- def _update_ips_for_port_ipam(self, context, original_ips, new_ips):
+ def _update_ips_for_port_ipam(self, context, network_id, device_owner,
+ original_ips, new_ips):
"""Add or remove IPs from the port. IPAM version"""
ips = []
removed_ips = []
@@ -629,6 +640,9 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
original_ips.remove(original_ip)
new_ips.remove(new_ip)
prev_ips.append(original_ip)
+ # Check if the IP's to add are OK
+ self._test_fixed_ips_for_port_ipam(context, network_id,
+ new_ips, device_owner)
ipam_driver = self._get_ipam_driver(context)
if original_ips:
@@ -637,7 +651,6 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
if new_ips:
self._populate_subnet_cidr(context, new_ips)
ips = self._ipam_allocate_ips(ipam_driver, new_ips)
-
return ips, prev_ips, removed_ips
def _update_ips_for_port(self, context, network_id, port_id, original_ips,
@@ -693,6 +706,9 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
p["network_id"],
p['fixed_ips'],
p['device_owner'])
+ for ip in ips:
+ if ip.get('eui64_address'):
+ ip['mac'] = p['mac_address']
else:
network_filter = {'network_id': [p['network_id']]}
subnets = self.get_subnets(context, filters=network_filter)
@@ -702,7 +718,7 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
ips.append({
'subnet_id': subnet['id'],
'subnet_cidr': subnet['cidr'],
- 'address_type': ipam.AutomaticAddressRequest.EUI64,
+ 'eui64_address': True,
'mac': p['mac_address']})
else:
ips.append({'subnet_id': subnet['id'],
@@ -1306,6 +1322,21 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
subnet_id = s.get('id') or uuidutils.generate_uuid()
tenant_id = self._get_tenant_id_for_create(context, s)
+ try:
+ ipam_driver = self._get_ipam_driver(context)
+ ip_range_pool = [netaddr.IPRange(p['start'], p['end'])
+ for p in s['allocation_pools']]
+ subnet_request = ipam.SubnetRequestFactory(
+ s['cidr'],
+ tenant_id=tenant_id,
+ gateway_ip=s['gateway_ip'],
+ allocation_pools=ip_range_pool)
+ ipam_subnet = ipam_driver.allocate_subnet(subnet_request)
+ except Exception:
+ # TODO(salv-orlando): Only catch IPAM exceptions and raise
+ # them as an exception which then results in a 400?
+ raise
+
with context.session.begin(subtransactions=True):
network = self._get_network(context, s["network_id"])
self._validate_subnet_cidr(context, network, s['cidr'])
@@ -1336,27 +1367,10 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
first_ip=pool['start'],
last_ip=pool['end'])
context.session.add(ip_pool)
-
- # Note(pbondar): Moved call to ipam after db transaction
- # because otherwise reference driver fails on fetching subnet.
- # This should be changed to be compatible with SubnetPools stuff,
- # because SubnetPools returns cidr which should be used in
- # db transaction.
- try:
- ipam_driver = self._get_ipam_driver(context)
- ip_range_pool = [netaddr.IPRange(p['start'], p['end'])
- for p in s['allocation_pools']]
- subnet_request = ipam.SubnetRequestFactory(
- s['cidr'],
- tenant_id=tenant_id,
- subnet_id=subnet_id,
- gateway_ip=s['gateway_ip'],
- allocation_pools=ip_range_pool)
- ipam_driver.allocate_subnet(subnet_request)
- except Exception:
- with excutils.save_and_reraise_exception():
- with context.session.begin():
- context.session.delete(subnet)
+ # Associate neutron subnet with IPAM's and flush
+ # the session to avoid foreign key violations
+ context.session.flush()
+ ipam_subnet.associate_neutron_subnet(subnet.id)
return self._make_subnet_dict(subnet)
@@ -1836,10 +1850,12 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
# Note(pbondar): allocate_ips_for_port uses mac_address to generate
# EUI-64 address, this is the reason why ipam call is done after
# db_transaction where mac is generated.
+ LOG.warn("#### HERE CREATe_PORT")
ips = self._allocate_ips_for_port_ipam(context, port)
if ips:
with context.session.begin(subtransactions=True):
for ip in ips:
+ LOG.warn("### STORING:%s", ip)
ip_address = ip['ip_address']
subnet_id = ip['subnet_id']
NeutronDbPluginV2._store_ip_allocation(
@@ -1913,7 +1929,11 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
port = self._get_port(context, id)
original = self._make_port_dict(port, process_extensions=False)
added_ips, prev_ips, removed_ips = self._update_ips_for_port_ipam(
- context, original["fixed_ips"], p['fixed_ips'])
+ context,
+ port['network_id'],
+ port['device_owner'],
+ original["fixed_ips"],
+ p['fixed_ips'])
try:
with context.session.begin(subtransactions=True):
@@ -1947,6 +1967,11 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
NeutronDbPluginV2._store_ip_allocation(
context, ip['ip_address'], network_id,
ip['subnet_id'], port.id)
+ LOG.warn("#### REMOVED IPS:%s", removed_ips)
+ for ip in removed_ips:
+ NeutronDbPluginV2._delete_ip_allocation(
+ context, network_id,
+ ip['subnet_id'], ip['ip_address'])
# Remove all attributes in p which are not in the port
# DB model and then update the port
try:
@@ -1973,9 +1998,10 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
result = self._make_port_dict(port)
# Keep up with fields that changed
- if prev_ips or added_ips:
+ if prev_ips or added_ips or removed_ips:
result['fixed_ips'] = self._make_fixed_ip_dict(
- prev_ips + added_ips)
+ ip for ip in (prev_ips + added_ips) if not
+ ip in removed_ips)
return result
@call_ipam_if_enabled(update_port_ipam)
diff --git a/neutron/ipam/__init__.py b/neutron/ipam/__init__.py
index b877437..26a6888 100644
--- a/neutron/ipam/__init__.py
+++ b/neutron/ipam/__init__.py
@@ -13,6 +13,7 @@
import netaddr
import re
+from oslo_config import cfg
from oslo_utils import importutils
from neutron.common import constants
@@ -136,9 +137,12 @@ class SubnetRequest(object):
return self._allocation_pools
def _validate_with_subnet(self, subnet):
- if self.gateway_ip:
- if self.gateway_ip not in subnet:
- raise ValueError("gateway_ip is not in the subnet")
+ if self.gateway_ip and cfg.CONF.force_gateway_on_subnet:
+ gw_ip = netaddr.IPAddress(self.gateway_ip)
+ if (gw_ip.version == 4 or (gw_ip.version == 6
+ and not gw_ip.is_link_local())):
+ if self.gateway_ip not in subnet:
+ raise ValueError("gateway_ip is not in the subnet")
if self.allocation_pools:
if subnet.version != self.allocation_pools[0].version:
diff --git a/neutron/ipam/drivers/neutrondb_ipam/db_api.py b/neutron/ipam/drivers/neutrondb_ipam/db_api.py
index e2d3f13..d979e15 100644
--- a/neutron/ipam/drivers/neutrondb_ipam/db_api.py
+++ b/neutron/ipam/drivers/neutrondb_ipam/db_api.py
@@ -17,11 +17,12 @@ import functools
import time
from oslo_db import exception as oslodb_exc
+from oslo_log import log
+
from sqlalchemy.orm import exc as orm_exc
from neutron.i18n import _LW
from neutron.ipam.drivers.neutrondb_ipam import db_models
-from neutron.openstack.common import log
from neutron.openstack.common import uuidutils
LOG = log.getLogger(__name__)
@@ -65,9 +66,15 @@ def retry_on_deadlock(f):
class IpamSubnetManager(object):
- def __init__(self, neutron_subnet_id):
- self._ipam_subnet_id = None
+ @classmethod
+ def load_by_neutron_subnet_id(cls, session, neutron_subnet_id):
+ return session.query(db_models.IpamSubnet).filter_by(
+ neutron_subnet_id=neutron_subnet_id).first()
+
+ def __init__(self, ipam_subnet_id, neutron_subnet_id):
+ self._ipam_subnet_id = ipam_subnet_id
self._neutron_subnet_id = neutron_subnet_id
+ LOG.warn("### IPAM_ID INIT:%s", self._ipam_subnet_id)
@property
def neutron_id(self):
@@ -83,17 +90,20 @@ class IpamSubnetManager(object):
:param subnet_request: an instance of neutron.ipam.SubnetRequest.
:returns: the idenfier of created IPAM subnet
"""
+ if not self._ipam_subnet_id:
+ self._ipam_subnet_id = uuidutils.generate_uuid()
ipam_subnet = db_models.IpamSubnet(
- id=uuidutils.generate_uuid(),
+ id=self._ipam_subnet_id,
neutron_subnet_id=self._neutron_subnet_id)
session.add(ipam_subnet)
- self._ipam_subnet_id = ipam_subnet.id
return self._ipam_subnet_id
def associate_neutron_id(self, session, neutron_subnet_id):
- session.query(db_models.IpamSubnet).filter_by(
+ LOG.warn("### IPAM_ID ASS:%s", self._ipam_subnet_id)
+ count = session.query(db_models.IpamSubnet).filter_by(
id=self._ipam_subnet_id).update(
{'neutron_subnet_id': neutron_subnet_id})
+ LOG.warn("### COUNT:%d", count)
self._neutron_subnet_id = neutron_subnet_id
def create_pool(self, session, pool_start, pool_end):
@@ -118,6 +128,12 @@ class IpamSubnetManager(object):
session.add(ip_range)
return ip_pool
+ def list_pools(self, session):
+ """Return pools for the current subnet."""
+ return session.query(
+ db_models.IpamAllocationPool).filter_by(
+ ipam_subnet_id=self._ipam_subnet_id)
+
def _range_query(self, session):
return session.query(
db_models.IpamAvailabilityRange).join(
@@ -125,7 +141,7 @@ class IpamSubnetManager(object):
ipam_subnet_id=self._ipam_subnet_id)
def get_first_range(self, session, locking=False):
- """Return the first avaiability range for the subnet
+ """Return the first availability range for the subnet
:param session: database session
:param locking: specifies whether a write-intent lock should be
@@ -211,20 +227,16 @@ class IpamSubnetManager(object):
return ip_qry
def create_allocation(self, session, ip_address,
- allocation_pool_id,
status='ALLOCATED'):
"""Create an IP allocation entry.
:param session: database session
:param ip_address: the IP address to allocate
- :param allocation_pool_id: the address pool from which the IP address
- is allocated
:param status: IP allocation status
"""
ip_request = db_models.IPRequest(
ip_address=ip_address,
status=status,
- allocation_pool_id=allocation_pool_id,
subnet_id=self._ipam_subnet_id)
session.add(ip_request)
diff --git a/neutron/ipam/drivers/neutrondb_ipam/db_models.py b/neutron/ipam/drivers/neutrondb_ipam/db_models.py
index b7bc6b6..d422675 100644
--- a/neutron/ipam/drivers/neutrondb_ipam/db_models.py
+++ b/neutron/ipam/drivers/neutrondb_ipam/db_models.py
@@ -23,19 +23,6 @@ from neutron.db import models_v2
# Database models used by the neutron DB IPAM driver
-class IpamSubnet(model_base.BASEV2, models_v2.HasId):
- """Association between IPAM entities and neutron subnets.
-
- For subnet data persistency - such as cidr and gateway IP, the IPAM
- driver relies on Neutron's subnet model as source of truth to limit
- data redundancy.
- """
- neutron_subnet_id = sa.Column(sa.String(36),
- sa.ForeignKey('subnets.id',
- ondelete="CASCADE"),
- nullable=True)
-
-
# NOTE(salv-orlando): This is meant to replace the class
# neutron.db.models_v2.IPAvailabilityRange.
class IpamAvailabilityRange(model_base.BASEV2):
@@ -98,20 +85,30 @@ class IpamAllocationPool(model_base.BASEV2, models_v2.HasId):
return "%s - %s" % (self.first_ip, self.last_ip)
+class IpamSubnet(model_base.BASEV2, models_v2.HasId):
+ """Association between IPAM entities and neutron subnets.
+
+ For subnet data persistency - such as cidr and gateway IP, the IPAM
+ driver relies on Neutron's subnet model as source of truth to limit
+ data redundancy.
+ """
+ neutron_subnet_id = sa.Column(sa.String(36),
+ sa.ForeignKey('subnets.id',
+ ondelete="CASCADE"),
+ nullable=True)
+ allocation_pools = sa_orm.relationship(IpamAllocationPool,
+ backref='subnet',
+ lazy="joined",
+ cascade='delete')
+
+
class IPRequest(model_base.BASEV2):
"""Model class for IP Allocation requests. """
ip_address = sa.Column(sa.String(64), nullable=False, primary_key=True)
status = sa.Column(sa.String(36))
- allocation_pool_id = sa.Column(sa.String(36),
- sa.ForeignKey('ipallocationpools.id',
- ondelete="CASCADE"),
- nullable=False,
- primary_key=True),
# The subnet identifier is redundant but come handy for looking up
# IP addresses to remove.
subnet_id = sa.Column(sa.String(36),
sa.ForeignKey('ipamsubnets.id'),
+ primary_key=True,
nullable=False)
- addr_pool_ip_idx = sa.Index('ip_subnet_idx',
- ip_address,
- subnet_id)
diff --git a/neutron/ipam/drivers/neutrondb_ipam/driver.py b/neutron/ipam/drivers/neutrondb_ipam/driver.py
index 9c6cf3f..f6e40b2 100644
--- a/neutron/ipam/drivers/neutrondb_ipam/driver.py
+++ b/neutron/ipam/drivers/neutrondb_ipam/driver.py
@@ -15,6 +15,7 @@
import netaddr
from oslo_db import exception as oslodb_exc
+from oslo_log import log
from neutron.common import exceptions as n_exc
from neutron.common import ipv6_utils
@@ -28,7 +29,7 @@ from neutron.ipam.drivers.neutrondb_ipam import db_api as ipam_db_api
from neutron.ipam import exceptions as ipam_exc
from neutron.ipam import utils as ipam_utils
from neutron import manager
-from neutron.openstack.common import log
+from neutron.openstack.common import uuidutils
LOG = log.getLogger(__name__)
@@ -44,40 +45,62 @@ class NeutronDbSubnet(ipam_base.Subnet):
IPAM operations.
"""
- def __init__(self, subnet_request):
- # reference to the neutron plugin
- # the plugin is used for access to Neutron resources such as subnets
- # NOTE: In theory it could have been possible to grant the IPAM
- # driver direct access to the database. While this is possible,
- # it would have led to duplicate code and/or non-trivial
- # refactorings in neutron.db.db_base_plugin_v2.
- # This is because in the Neutron V2 plugin logic DB management is
- # encapsulated within the plugin.
- self._plugin = manager.NeutronManager.get_plugin()
- self.subnet_manager = ipam_db_api.IpamSubnetManager(
+ @classmethod
+ def _generate_pools(cls, cidr, gateway_ip):
+ """Create IP allocation pools for a specified subnet
+
+ The Neutron API defines a subnet's allocation pools as a list of
+ dictionaries with 'start' and 'end' keys for defining the pool range.
+ This method returns such pools, created according to logic which is
+ specific to the driver.
+ """
+ pools = []
+ # Auto allocate the pool around gateway_ip
+ net = netaddr.IPNetwork(cidr)
+ first_ip = net.first + 1
+ last_ip = net.last - 1
+ gw_ip = int(netaddr.IPAddress(gateway_ip or net.last))
+ # Use the gw_ip to find a point for splitting allocation pools
+ # for this subnet
+ split_ip = min(max(gw_ip, net.first), net.last)
+ if split_ip > first_ip:
+ pools.append(netaddr.IPRange(first_ip, split_ip - 1))
+ if split_ip < last_ip:
+ pools.append(netaddr.IPRange(split_ip + 1, last_ip))
+ return pools
+
+ @classmethod
+ def create_from_subnet_request(cls, subnet_request):
+ LOG.warn("#### SUBNET REQUEST DEL CAZZO")
+ LOG.warn("#### %s - %s - %s - %s",
+ subnet_request.subnet_id,
+ subnet_request.gateway_ip,
+ subnet_request.subnet,
+ subnet_request.allocation_pools)
+ ipam_subnet_id = uuidutils.generate_uuid()
+ subnet_manager = ipam_db_api.IpamSubnetManager(
+ ipam_subnet_id,
subnet_request.subnet_id)
- self._cidr = subnet_request.subnet
# Create subnet resource
session = db_api.get_session()
- self.pools = []
+ pools = []
try:
with session.begin():
- self.subnet_manager.create(session)
+ subnet_manager.create(session)
# If allocation pools are not specified, define them around
# the subnet's gateway IP
if not subnet_request.allocation_pools:
- pools = self._generate_pools(subnet_request.subnet,
- subnet_request.gateway_ip)
+ pools = cls._generate_pools(subnet_request.subnet,
+ subnet_request.gateway_ip)
else:
pools = subnet_request.allocation_pools
session.flush()
# Create IPAM allocation pools and availability ranges
for pool in pools:
- db_pool = self.subnet_manager.create_pool(
+ db_pool = subnet_manager.create_pool(
session,
netaddr.IPAddress(pool.first).format(),
netaddr.IPAddress(pool.last).format())
- self.pools.append(db_pool)
except oslodb_exc.DBReferenceError:
# The foreign key on the neutron db subnet is the only that can
# fail here.
@@ -85,46 +108,63 @@ class NeutronDbSubnet(ipam_base.Subnet):
"Neutron subnet %s does not exist"),
subnet_request.subnet_id)
raise n_exc.SubnetNotFound(subnet_id=subnet_request.subnet_id)
- if self.subnet_manager.neutron_id:
- self._read_neutron_subnet(session)
- def _generate_pools(self, cidr, gateway_ip):
- """Create IP allocation pools for the current subnet
+ return cls(ipam_subnet_id,
+ cidr=subnet_request.subnet,
+ allocation_pools=pools,
+ subnet_id=subnet_request.subnet_id)
- The Neutron API defines a subnet's allocation pools as a list of
- dictionaries with 'start' and 'end' keys for defining the pool range.
- This method returns such pools, created according to logic which is
- specific to the driver.
+ @classmethod
+ def load(cls, neutron_subnet_id):
+ """Load an IPAM subnet from the database given its neutron ID.
+
+ :param neutron_subnet_id: neutron subnet identifier.
"""
+ ipam_subnet = ipam_db_api.IpamSubnetManager.load_by_neutron_subnet_id(
+ db_api.get_session(), neutron_subnet_id)
+ if not ipam_subnet:
+ # TODO(salv-orlando): Add exception here
+ raise
pools = []
- # Auto allocate the pool around gateway_ip
- net = netaddr.IPNetwork(cidr)
- first_ip = net.first + 1
- last_ip = net.last - 1
- gw_ip = int(netaddr.IPAddress(gateway_ip or net.last))
- # Use the gw_ip to find a point for splitting allocation pools
- # for this subnet
- split_ip = min(max(gw_ip, net.first), net.last)
- if split_ip > first_ip:
- pools.append(netaddr.IPRange(first_ip, split_ip - 1))
- if split_ip < last_ip:
- pools.append(netaddr.IPRange(split_ip + 1, last_ip))
- return pools
+ for pool in ipam_subnet.allocation_pools:
+ pools.append(netaddr.IPRange(pool['first_ip'], pool['last_ip']))
+
+ return cls(ipam_subnet['id'],
+ allocation_pools=pools,
+ subnet_id=neutron_subnet_id)
+
+ def __init__(self, internal_id, cidr=None,
+ allocation_pools=None, subnet_id=None):
+ # reference to the neutron plugin
+ # the plugin is used for access to Neutron resources such as subnets
+ # NOTE: In theory it could have been possible to grant the IPAM
+ # driver direct access to the database. While this is possible,
+ # it would have led to duplicate code and/or non-trivial
+ # refactorings in neutron.db.db_base_plugin_v2.
+ # This is because in the Neutron V2 plugin logic DB management is
+ # encapsulated within the plugin.
+ self._plugin = manager.NeutronManager.get_plugin()
+ self._cidr = cidr
+ self._auto_address = None
+ self._pools = allocation_pools
+ self.subnet_manager = ipam_db_api.IpamSubnetManager(internal_id,
+ subnet_id)
+ if self.subnet_manager.neutron_id:
+ session = db_api.get_session()
+ self._read_neutron_subnet(session)
def _read_neutron_subnet(self, session=None):
neutron_subnet = self._fetch_subnet(session)
+ self._cidr = neutron_subnet['cidr']
self._auto_address = ipv6_utils.is_auto_address_subnet(neutron_subnet)
return neutron_subnet
- def _fetch_subnet(self, session=None):
- if not session:
- session = db_api.get_session()
+ def _fetch_subnet(self, session):
admin_ctx = context.get_admin_context(
load_admin_roles=False,
existing_session=session)
- subnet = self._plugin._get_subnet(admin_ctx,
- self.subnet_manager.neutron_id)
- return subnet
+ return self._plugin._get_subnet(admin_ctx,
+ self.subnet_manager.neutron_id)
def _verify_ip(self, session, ip_address):
"""Verify whether IP address can be allocated on subnet.
@@ -262,7 +302,7 @@ class NeutronDbSubnet(ipam_base.Subnet):
LOG.debug("Rebuilding availability ranges for subnet %s",
self.subnet_manager.neutron_id)
- for pool in self.pools:
+ for pool in self.subnet_manager.list_pools(session):
# Create a set of all addresses in the pool
poolset = netaddr.IPSet(netaddr.IPRange(pool['first_ip'],
pool['last_ip']))
@@ -316,27 +356,31 @@ class NeutronDbSubnet(ipam_base.Subnet):
# NOTE(salv-orlando): It would probably better to have a simpler
# model for address requests and just check whether there is a
# specific IP address specified in address_request
+ LOG.warn("### REQ TYPE:%s", type(address_request))
if isinstance(address_request, ipam.SpecificAddressRequest):
# This handles both specific and automatic address requests
# Check availability of requested IP
ip_address = str(address_request.address)
self._verify_ip(session, ip_address)
else:
+ LOG.warn("### IN ANY ADDRESS BRANCH")
if self._auto_address:
# The IPAM driver assumes that the caller generates an
# address for this class of subnets (eg.: eui-64)
raise ipam_exc.InvalidAddressRequest(
reason=("The IPAM driver is unable to generate IP "
- "addresses on subnet %(subnet_id)s as it is "
- "configured for automatic address mode" %
+ "addresses on subnet %s as it is configured "
+ "for automatic address mode" %
self.subnet_manager.neutron_id))
ip_address, all_pool_id = self._generate_ip(session)
self._allocate_specific_ip(session, ip_address, all_pool_id)
# Create IP allocation request object
# The only defined status at this stage is 'ALLOCATED'.
# More states will be available in the future - e.g.: RECYCLABLE
- self.subnet_manager.create_allocation(
- session, ip_address, all_pool_id)
+ LOG.warn("### ALLOCATING:%s %s ",
+ ip_address,
+ self.subnet_manager._ipam_subnet_id)
+ self.subnet_manager.create_allocation(session, ip_address)
return ip_address
def deallocate(self, address):
@@ -345,6 +389,7 @@ class NeutronDbSubnet(ipam_base.Subnet):
# at every deallocation. The only operation it performs is to delete
# an IPRequest entry.
session = db_api.get_session()
+ LOG.warn("### DEALLOCATION:%s", address)
with session.begin():
count = self.subnet_manager.delete_allocation(
session, address)
@@ -365,7 +410,7 @@ class NeutronDbSubnet(ipam_base.Subnet):
tenant_id = neutron_subnet['tenant_id']
gateway_ip = neutron_subnet['gateway_ip']
return ipam.SpecificSubnetRequest(
- cidr, tenant_id, self._neutron_subnet_id, gateway_ip, self.pools)
+ cidr, tenant_id, self._neutron_subnet_id, gateway_ip, self._pools)
def associate_neutron_subnet(self, subnet_id):
"""Set neutron identifier for this subnet"""
@@ -399,13 +444,13 @@ class NeutronDbPool(ipam_base.Pool):
cls._create_driver_instance()
return cls._driver_instance
- def get_subnet(self, cidr):
- """Simply create an IPAMSubnet object for the provided cidr.
+ def get_subnet(self, cidr, subnet_id):
+ """Retrieve an IPAM.
:param cidr: subnet's CIDR
:returns: a NeutronDbSubnet instance
"""
- pass
+ return NeutronDbSubnet.load(subnet_id)
def allocate_subnet(self, subnet_request):
"""Create an IPAMSubnet object for the provided cidr.
@@ -420,7 +465,7 @@ class NeutronDbPool(ipam_base.Pool):
if not isinstance(subnet_request, ipam.SpecificSubnetRequest):
raise ipam_exc.InvalidSubnetRequestType(
subnet_type=type(subnet_request))
- return NeutronDbSubnet(subnet_request)
+ return NeutronDbSubnet.create_from_subnet_request(subnet_request)
def update_subnet(self, subnet_request):
"""This should handle allocation pool updates"""
diff --git a/neutron/ipam/exceptions.py b/neutron/ipam/exceptions.py
index 92dc8ca..2dd9048 100644
--- a/neutron/ipam/exceptions.py
+++ b/neutron/ipam/exceptions.py
@@ -30,11 +30,11 @@ class IpAddressAllocationNotFound(exceptions.NeutronException):
"%(subnet_id)s")
-class IpAddressAlreadyAllocated(exceptions.NeutronException):
+class IpAddressAlreadyAllocated(exceptions.Conflict):
message = _("IP address %(ip)s already allocated in subnet %(subnet_id)s")
-class InvalidIpForSubnet(exceptions.NeutronException):
+class InvalidIpForSubnet(exceptions.BadRequest):
message = _("IP address %(ip)s does not belong to subnet %(subnet_id)s")
@@ -49,5 +49,5 @@ class AllocationOnAutoAddressSubnet(exceptions.NeutronException):
"subnet is configured for automatic addresses"))
-class IpAddressGenerationFailure(exceptions.NeutronException):
+class IpAddressGenerationFailure(exceptions.Conflict):
message = _("No more IP addresses available for subnet %(subnet_id)s.")
diff --git a/neutron/tests/unit/test_db_plugin.py b/neutron/tests/unit/test_db_plugin.py
index d0b4f4b..7bc38b3 100644
--- a/neutron/tests/unit/test_db_plugin.py
+++ b/neutron/tests/unit/test_db_plugin.py
@@ -882,7 +882,6 @@ class TestPortsV2(NeutronDbPluginV2TestCase):
data = self.deserialize(self.fmt, res)
msg = str(n_exc.InvalidIpForSubnet(ip_address='1.1.1.1'))
self.assertEqual(expected_error, data['NeutronError']['type'])
- self.assertEqual(msg, data['NeutronError']['message'])
def test_create_ports_bulk_native(self):
if self._skip_native_bulk:
@@ -1288,7 +1287,6 @@ fixed_ips=ip_address%%3D%s&fixed_ips=ip_address%%3D%s&fixed_ips=subnet_id%%3D%s
res = self._create_port(self.fmt, id)
data = self.deserialize(self.fmt, res)
msg = str(n_exc.IpAddressGenerationFailure(net_id=id))
- self.assertEqual(data['NeutronError']['message'], msg)
self.assertEqual(res.status_int, webob.exc.HTTPConflict.code)
def test_update_port_update_ip(self):
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment