Allow IPAM backend switch
Changed inheritance chain for NeutronDbPluginV2 to allow switching from non-pluggable to pluggable IPAM implementation. IpamNonPluggableBackend methods are called on it's instance, instead of previous way where IpamNonPluggableBackend was parent for NeutronDbPluginV2. It allows switching IPAM implementation in set_ipam_backend (IpamNonPluggableBackend to IpamPluggableBackend). All methods that became public in IpamNonPluggableBackend were renamed. This is refactoring step before Pluggable IPAM can be applied. Partially-Implements: blueprint neutron-ipam Change-Id: I81806a43ecc6f0a7b293ce3e70d09d1e266b9f02
This commit is contained in:
parent
1e3b4f119a
commit
b522896c31
@ -228,7 +228,7 @@ class DbBasePluginCommon(common_db_mixin.CommonDbMixin):
|
||||
return self._fields(res, fields)
|
||||
|
||||
def _make_subnet_args(self, shared, detail,
|
||||
subnet, subnetpool_id=None):
|
||||
subnet, subnetpool_id):
|
||||
gateway_ip = str(detail.gateway_ip) if detail.gateway_ip else None
|
||||
args = {'tenant_id': detail.tenant_id,
|
||||
'id': detail.subnet_id,
|
||||
|
@ -15,7 +15,6 @@
|
||||
|
||||
import netaddr
|
||||
from oslo_config import cfg
|
||||
from oslo_db import api as oslo_db_api
|
||||
from oslo_db import exception as db_exc
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import excutils
|
||||
@ -33,6 +32,7 @@ from neutron.common import exceptions as n_exc
|
||||
from neutron.common import ipv6_utils
|
||||
from neutron import context as ctx
|
||||
from neutron.db import api as db_api
|
||||
from neutron.db import db_base_plugin_common
|
||||
from neutron.db import ipam_non_pluggable_backend
|
||||
from neutron.db import models_v2
|
||||
from neutron.db import sqlalchemyutils
|
||||
@ -66,7 +66,7 @@ def _check_subnet_not_used(context, subnet_id):
|
||||
raise n_exc.SubnetInUse(subnet_id=subnet_id, reason=e)
|
||||
|
||||
|
||||
class NeutronDbPluginV2(ipam_non_pluggable_backend.IpamNonPluggableBackend,
|
||||
class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon,
|
||||
neutron_plugin_base_v2.NeutronPluginBaseV2):
|
||||
"""V2 Neutron plugin interface implementation using SQLAlchemy models.
|
||||
|
||||
@ -84,6 +84,7 @@ class NeutronDbPluginV2(ipam_non_pluggable_backend.IpamNonPluggableBackend,
|
||||
__native_sorting_support = True
|
||||
|
||||
def __init__(self):
|
||||
self.set_ipam_backend()
|
||||
if cfg.CONF.notify_nova_on_port_status_changes:
|
||||
from neutron.notifiers import nova
|
||||
# NOTE(arosen) These event listeners are here to hook into when
|
||||
@ -96,6 +97,9 @@ class NeutronDbPluginV2(ipam_non_pluggable_backend.IpamNonPluggableBackend,
|
||||
event.listen(models_v2.Port.status, 'set',
|
||||
self.nova_notifier.record_port_status_changed)
|
||||
|
||||
def set_ipam_backend(self):
|
||||
self.ipam = ipam_non_pluggable_backend.IpamNonPluggableBackend()
|
||||
|
||||
def _validate_host_route(self, route, ip_version):
|
||||
try:
|
||||
netaddr.IPNetwork(route['destination'])
|
||||
@ -439,18 +443,15 @@ class NeutronDbPluginV2(ipam_non_pluggable_backend.IpamNonPluggableBackend,
|
||||
external_gateway_info}}
|
||||
l3plugin.update_router(context, id, info)
|
||||
|
||||
@oslo_db_api.wrap_db_retry(max_retries=db_api.MAX_RETRIES,
|
||||
retry_on_request=True,
|
||||
retry_on_deadlock=True)
|
||||
def _create_subnet(self, context, subnet, subnetpool_id):
|
||||
s = subnet['subnet']
|
||||
|
||||
with context.session.begin(subtransactions=True):
|
||||
network = self._get_network(context, s["network_id"])
|
||||
subnet = self._allocate_subnet(context,
|
||||
network,
|
||||
s,
|
||||
subnetpool_id)
|
||||
subnet = self.ipam.allocate_subnet(context,
|
||||
network,
|
||||
s,
|
||||
subnetpool_id)
|
||||
if hasattr(network, 'external') and network.external:
|
||||
self._update_router_gw_ports(context,
|
||||
network,
|
||||
@ -458,7 +459,7 @@ class NeutronDbPluginV2(ipam_non_pluggable_backend.IpamNonPluggableBackend,
|
||||
# If this subnet supports auto-addressing, then update any
|
||||
# internal ports on the network with addresses for this subnet.
|
||||
if ipv6_utils.is_auto_address_subnet(subnet):
|
||||
self._add_auto_addrs_on_network_ports(context, subnet)
|
||||
self.ipam.add_auto_addrs_on_network_ports(context, subnet)
|
||||
return self._make_subnet_dict(subnet)
|
||||
|
||||
def _get_subnetpool_id(self, subnet):
|
||||
@ -514,7 +515,7 @@ class NeutronDbPluginV2(ipam_non_pluggable_backend.IpamNonPluggableBackend,
|
||||
s['tenant_id'] = self._get_tenant_id_for_create(context, s)
|
||||
subnetpool_id = self._get_subnetpool_id(s)
|
||||
if subnetpool_id:
|
||||
self._validate_pools_with_subnetpool(s)
|
||||
self.ipam.validate_pools_with_subnetpool(s)
|
||||
else:
|
||||
if not has_cidr:
|
||||
msg = _('A cidr must be specified in the absence of a '
|
||||
@ -548,10 +549,11 @@ class NeutronDbPluginV2(ipam_non_pluggable_backend.IpamNonPluggableBackend,
|
||||
allocation_pools = [{'start': p['first_ip'],
|
||||
'end': p['last_ip']}
|
||||
for p in db_subnet.allocation_pools]
|
||||
self._validate_gw_out_of_pools(s["gateway_ip"], allocation_pools)
|
||||
self.ipam.validate_gw_out_of_pools(s["gateway_ip"],
|
||||
allocation_pools)
|
||||
|
||||
with context.session.begin(subtransactions=True):
|
||||
subnet, changes = self._update_db_subnet(context, id, s)
|
||||
subnet, changes = self.ipam.update_db_subnet(context, id, s)
|
||||
result = self._make_subnet_dict(subnet)
|
||||
# Keep up with fields that changed
|
||||
result.update(changes)
|
||||
@ -832,7 +834,7 @@ class NeutronDbPluginV2(ipam_non_pluggable_backend.IpamNonPluggableBackend,
|
||||
db_port = self._create_port_with_mac(
|
||||
context, network_id, port_data, p['mac_address'])
|
||||
|
||||
self._allocate_ips_for_port_and_store(context, port, port_id)
|
||||
self.ipam.allocate_ips_for_port_and_store(context, port, port_id)
|
||||
|
||||
return self._make_port_dict(db_port, process_extensions=False)
|
||||
|
||||
@ -859,8 +861,8 @@ class NeutronDbPluginV2(ipam_non_pluggable_backend.IpamNonPluggableBackend,
|
||||
port = self._get_port(context, id)
|
||||
new_mac = new_port.get('mac_address')
|
||||
self._validate_port_for_update(context, port, new_port, new_mac)
|
||||
changes = self._update_port_with_ips(context, port,
|
||||
new_port, new_mac)
|
||||
changes = self.ipam.update_port_with_ips(context, port,
|
||||
new_port, new_mac)
|
||||
result = self._make_port_dict(port)
|
||||
# Keep up with fields that changed
|
||||
if changes.original or changes.add or changes.remove:
|
||||
@ -870,7 +872,7 @@ class NeutronDbPluginV2(ipam_non_pluggable_backend.IpamNonPluggableBackend,
|
||||
|
||||
def delete_port(self, context, id):
|
||||
with context.session.begin(subtransactions=True):
|
||||
self._delete_port(context, id)
|
||||
self.ipam.delete_port(context, id)
|
||||
|
||||
def delete_ports_by_device_id(self, context, device_id, network_id=None):
|
||||
query = (context.session.query(models_v2.Port.id)
|
||||
|
@ -52,7 +52,7 @@ class IpamBackendMixin(db_base_plugin_common.DbBasePluginCommon):
|
||||
return str(netaddr.IPNetwork(cidr_net).network + 1)
|
||||
return subnet.get('gateway_ip')
|
||||
|
||||
def _validate_pools_with_subnetpool(self, subnet):
|
||||
def validate_pools_with_subnetpool(self, subnet):
|
||||
"""Verifies that allocation pools are set correctly
|
||||
|
||||
Allocation pools can be set for specific subnet request only
|
||||
@ -155,7 +155,7 @@ class IpamBackendMixin(db_base_plugin_common.DbBasePluginCommon):
|
||||
del s['allocation_pools']
|
||||
return result_pools
|
||||
|
||||
def _update_db_subnet(self, context, subnet_id, s):
|
||||
def update_db_subnet(self, context, subnet_id, s):
|
||||
changes = {}
|
||||
if "dns_nameservers" in s:
|
||||
changes['dns_nameservers'] = (
|
||||
@ -298,12 +298,11 @@ class IpamBackendMixin(db_base_plugin_common.DbBasePluginCommon):
|
||||
|
||||
self._validate_allocation_pools(allocation_pools, cidr)
|
||||
if gateway_ip:
|
||||
self._validate_gw_out_of_pools(gateway_ip,
|
||||
allocation_pools)
|
||||
self.validate_gw_out_of_pools(gateway_ip, allocation_pools)
|
||||
return [netaddr.IPRange(p['start'], p['end'])
|
||||
for p in allocation_pools]
|
||||
|
||||
def _validate_gw_out_of_pools(self, gateway_ip, pools):
|
||||
def validate_gw_out_of_pools(self, gateway_ip, pools):
|
||||
for allocation_pool in pools:
|
||||
pool_range = netaddr.IPRange(
|
||||
allocation_pool['start'],
|
||||
@ -369,7 +368,7 @@ class IpamBackendMixin(db_base_plugin_common.DbBasePluginCommon):
|
||||
original=prev_ips,
|
||||
remove=remove_ips)
|
||||
|
||||
def _delete_port(self, context, port_id):
|
||||
def delete_port(self, context, port_id):
|
||||
query = (context.session.query(models_v2.Port).
|
||||
enable_eagerloads(False).filter_by(id=port_id))
|
||||
if not context.is_admin:
|
||||
@ -403,7 +402,7 @@ class IpamBackendMixin(db_base_plugin_common.DbBasePluginCommon):
|
||||
nexthop=rt['nexthop'])
|
||||
context.session.add(route)
|
||||
|
||||
self._save_allocation_pools(context, subnet,
|
||||
subnet_request.allocation_pools)
|
||||
self.save_allocation_pools(context, subnet,
|
||||
subnet_request.allocation_pools)
|
||||
|
||||
return subnet
|
||||
|
@ -186,7 +186,7 @@ class IpamNonPluggableBackend(ipam_backend_mixin.IpamBackendMixin):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _save_allocation_pools(self, context, subnet, allocation_pools):
|
||||
def save_allocation_pools(self, context, subnet, allocation_pools):
|
||||
for pool in allocation_pools:
|
||||
first_ip = str(netaddr.IPAddress(pool.first, pool.version))
|
||||
last_ip = str(netaddr.IPAddress(pool.last, pool.version))
|
||||
@ -200,7 +200,7 @@ class IpamNonPluggableBackend(ipam_backend_mixin.IpamBackendMixin):
|
||||
last_ip=last_ip)
|
||||
context.session.add(ip_range)
|
||||
|
||||
def _allocate_ips_for_port_and_store(self, context, port, port_id):
|
||||
def allocate_ips_for_port_and_store(self, context, port, port_id):
|
||||
network_id = port['port']['network_id']
|
||||
ips = self._allocate_ips_for_port(context, port)
|
||||
if ips:
|
||||
@ -210,7 +210,7 @@ class IpamNonPluggableBackend(ipam_backend_mixin.IpamBackendMixin):
|
||||
self._store_ip_allocation(context, ip_address, network_id,
|
||||
subnet_id, port_id)
|
||||
|
||||
def _update_port_with_ips(self, context, db_port, new_port, new_mac):
|
||||
def update_port_with_ips(self, context, db_port, new_port, new_mac):
|
||||
changes = self.Changes(add=[], original=[], remove=[])
|
||||
# Check if the IPs need to be updated
|
||||
network_id = db_port['network_id']
|
||||
@ -431,7 +431,7 @@ class IpamNonPluggableBackend(ipam_backend_mixin.IpamBackendMixin):
|
||||
|
||||
return ips
|
||||
|
||||
def _add_auto_addrs_on_network_ports(self, context, subnet):
|
||||
def add_auto_addrs_on_network_ports(self, context, subnet):
|
||||
"""For an auto-address subnet, add addrs for ports on the net."""
|
||||
with context.session.begin(subtransactions=True):
|
||||
network_id = subnet['network_id']
|
||||
@ -470,7 +470,7 @@ class IpamNonPluggableBackend(ipam_backend_mixin.IpamBackendMixin):
|
||||
ip_address=ip_address)
|
||||
return ip_address
|
||||
|
||||
def _allocate_subnet(self, context, network, subnet, subnetpool_id):
|
||||
def allocate_subnet(self, context, network, subnet, subnetpool_id):
|
||||
subnetpool = None
|
||||
if subnetpool_id:
|
||||
subnetpool = self._get_subnetpool(context, subnetpool_id)
|
||||
|
@ -40,6 +40,7 @@ from neutron.common import ipv6_utils
|
||||
from neutron.common import test_lib
|
||||
from neutron.common import utils
|
||||
from neutron import context
|
||||
from neutron.db import db_base_plugin_common
|
||||
from neutron.db import db_base_plugin_v2
|
||||
from neutron.db import ipam_non_pluggable_backend as non_ipam
|
||||
from neutron.db import models_v2
|
||||
@ -1626,7 +1627,7 @@ fixed_ips=ip_address%%3D%s&fixed_ips=ip_address%%3D%s&fixed_ips=subnet_id%%3D%s
|
||||
self.assertEqual(res.status_int,
|
||||
webob.exc.HTTPClientError.code)
|
||||
|
||||
@mock.patch.object(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
@mock.patch.object(non_ipam.IpamNonPluggableBackend,
|
||||
'_allocate_specific_ip')
|
||||
def test_requested_fixed_ip_address_v6_slaac_router_iface(
|
||||
self, alloc_specific_ip):
|
||||
@ -3812,6 +3813,9 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase):
|
||||
'dummy_key', 'dummy_key_table')
|
||||
mock.patch.object(orm.Session, 'add',
|
||||
side_effect=db_ref_err_for_ipalloc).start()
|
||||
mock.patch.object(non_ipam.IpamNonPluggableBackend,
|
||||
'_get_subnet',
|
||||
return_value=mock.Mock()).start()
|
||||
# Add an IPv6 auto-address subnet to the network
|
||||
v6_subnet = self._make_subnet(self.fmt, network, 'fe80::1',
|
||||
'fe80::/64', ip_version=6,
|
||||
@ -5374,7 +5378,7 @@ class TestNeutronDbPluginV2(base.BaseTestCase):
|
||||
context.session.query.side_effect = return_queries_side_effect
|
||||
subnets = [mock.MagicMock()]
|
||||
|
||||
db_base_plugin_v2.NeutronDbPluginV2._rebuild_availability_ranges(
|
||||
non_ipam.IpamNonPluggableBackend._rebuild_availability_ranges(
|
||||
context, subnets)
|
||||
|
||||
actual = [[args[0].allocation_pool_id,
|
||||
@ -5437,15 +5441,18 @@ class TestNeutronDbPluginV2(base.BaseTestCase):
|
||||
expected)
|
||||
|
||||
def _test__allocate_ips_for_port(self, subnets, port, expected):
|
||||
# this test is incompatible with pluggable ipam, because subnets
|
||||
# were not actually created, so no ipam_subnet exists
|
||||
cfg.CONF.set_override("ipam_driver", None)
|
||||
plugin = db_base_plugin_v2.NeutronDbPluginV2()
|
||||
with mock.patch.object(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
with mock.patch.object(db_base_plugin_common.DbBasePluginCommon,
|
||||
'_get_subnets') as get_subnets:
|
||||
with mock.patch.object(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
with mock.patch.object(non_ipam.IpamNonPluggableBackend,
|
||||
'_check_unique_ip') as check_unique:
|
||||
context = mock.Mock()
|
||||
get_subnets.return_value = subnets
|
||||
check_unique.return_value = True
|
||||
actual = plugin._allocate_ips_for_port(context, port)
|
||||
actual = plugin.ipam._allocate_ips_for_port(context, port)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test__allocate_ips_for_port_2_slaac_subnets(self):
|
||||
@ -5537,7 +5544,7 @@ class NeutronDbPluginV2AsMixinTestCase(NeutronDbPluginV2TestCase,
|
||||
ip_version=4)]
|
||||
new_subnetpool_id = None
|
||||
self.assertRaises(n_exc.NetworkSubnetPoolAffinityError,
|
||||
self.plugin._validate_network_subnetpools,
|
||||
self.plugin.ipam._validate_network_subnetpools,
|
||||
network, new_subnetpool_id, 4)
|
||||
|
||||
|
||||
|
@ -1577,6 +1577,8 @@ class TestMl2PluginCreateUpdateDeletePort(base.BaseTestCase):
|
||||
'get_service_plugins',
|
||||
return_value={'L3_ROUTER_NAT': l3plugin}):
|
||||
plugin = self._create_plugin_for_create_update_port(mock.Mock())
|
||||
# Set backend manually here since __init__ was mocked
|
||||
plugin.set_ipam_backend()
|
||||
# deleting the port will call registry.notify, which will
|
||||
# run the transaction balancing function defined in this test
|
||||
plugin.delete_port(self.context, 'fake_id')
|
||||
|
2
neutron/tests/unit/test_ipam.py
Executable file → Normal file
2
neutron/tests/unit/test_ipam.py
Executable file → Normal file
@ -314,6 +314,8 @@ class TestSubnetRequestFactory(IpamSubnetRequestTestCase):
|
||||
'prefixlen': prefixlen,
|
||||
'ip_version': ip_version,
|
||||
'tenant_id': self.tenant_id,
|
||||
'gateway_ip': None,
|
||||
'allocation_pools': None,
|
||||
'id': id or self.subnet_id}
|
||||
subnetpool = {'ip_version': ip_version,
|
||||
'default_prefixlen': prefixlen}
|
||||
|
Loading…
Reference in New Issue
Block a user