Created
March 16, 2015 00:21
-
-
Save salv-orlando/22e524979fe7e2d18d2d to your computer and use it in GitHub Desktop.
changes for IPAM patches
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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