]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Allow IPAM backend switch
authorPavel Bondar <pbondar@infoblox.com>
Wed, 4 Feb 2015 13:13:28 +0000 (16:13 +0300)
committerPavel Bondar <pbondar@infoblox.com>
Thu, 9 Jul 2015 08:58:22 +0000 (11:58 +0300)
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

neutron/db/db_base_plugin_common.py
neutron/db/db_base_plugin_v2.py
neutron/db/ipam_backend_mixin.py
neutron/db/ipam_non_pluggable_backend.py
neutron/tests/unit/db/test_db_base_plugin_v2.py
neutron/tests/unit/plugins/ml2/test_plugin.py
neutron/tests/unit/test_ipam.py [changed mode: 0755->0644]

index 1b795c0752c4026d3e4653e18b0de6030a415e6b..9cf1ba6bb1d536a85596d8c2616b9227be1f5955 100644 (file)
@@ -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,
index a30cb7fb6266c07ce3801292c6ba71e85012a89c..d2b5f89972fdc49989affe84ae9cae4b23f38409 100644 (file)
@@ -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)
index f50d160a3a4681fa1ca9d9f9f9cbfe1e1a2018aa..d4de20937af7754ae5bbf2c107eef0ec05cad44c 100644 (file)
@@ -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
index 45ba102f686bcc3706e2102a95f4204061366453..9346bcb7bbca46ce2cf167ee939a9ba10a691137 100644 (file)
@@ -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)
index c396213c98b0fd0f1f851496bf0d9a8d7ea370d9..faab63dcfc33098a3d1ee8538dfb5388dd1ce2c3 100644 (file)
@@ -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)
 
 
index dc4f9ea3fa30f25bce280e9bd5e09504e550315d..7f8fe7dbfe765630f72cd5e3c94ceb976fb4a859 100644 (file)
@@ -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')
old mode 100755 (executable)
new mode 100644 (file)
index 9321594..0b5bbe2
@@ -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}