Merge "Always call ipam driver on subnet update"
This commit is contained in:
commit
2a305c5630
@ -13,6 +13,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
|
||||
import netaddr
|
||||
from oslo_db import exception as db_exc
|
||||
from oslo_log import log as logging
|
||||
@ -137,9 +139,6 @@ class IpamPluggableBackend(ipam_backend_mixin.IpamBackendMixin):
|
||||
return allocated
|
||||
|
||||
def _ipam_update_allocation_pools(self, context, ipam_driver, subnet):
|
||||
self.validate_allocation_pools(subnet['allocation_pools'],
|
||||
subnet['cidr'])
|
||||
|
||||
factory = ipam_driver.get_subnet_request_factory()
|
||||
subnet_request = factory.get_request(context, subnet, None)
|
||||
|
||||
@ -341,22 +340,22 @@ class IpamPluggableBackend(ipam_backend_mixin.IpamBackendMixin):
|
||||
port['fixed_ips'])
|
||||
|
||||
def update_db_subnet(self, context, id, s, old_pools):
|
||||
# 'allocation_pools' is removed from 's' in
|
||||
# _update_subnet_allocation_pools (ipam_backend_mixin),
|
||||
# so create unchanged copy for ipam driver
|
||||
subnet_copy = copy.deepcopy(s)
|
||||
subnet, changes = super(IpamPluggableBackend, self).update_db_subnet(
|
||||
context, id, s, old_pools)
|
||||
ipam_driver = driver.Pool.get_instance(None, context)
|
||||
if "allocation_pools" in s:
|
||||
self._ipam_update_allocation_pools(context, ipam_driver, s)
|
||||
|
||||
try:
|
||||
subnet, changes = super(IpamPluggableBackend,
|
||||
self).update_db_subnet(context, id,
|
||||
s, old_pools)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
if "allocation_pools" in s and old_pools:
|
||||
LOG.error(
|
||||
_LE("An exception occurred during subnet update. "
|
||||
"Reverting allocation pool changes"))
|
||||
s['allocation_pools'] = old_pools
|
||||
self._ipam_update_allocation_pools(context, ipam_driver, s)
|
||||
# Set old allocation pools if no new pools are provided by user.
|
||||
# Passing old pools allows to call ipam driver on each subnet update
|
||||
# even if allocation pools are not changed. So custom ipam drivers
|
||||
# are able to track other fields changes on subnet update.
|
||||
if 'allocation_pools' not in subnet_copy:
|
||||
subnet_copy['allocation_pools'] = old_pools
|
||||
self._ipam_update_allocation_pools(context, ipam_driver, subnet_copy)
|
||||
|
||||
return subnet, changes
|
||||
|
||||
def add_auto_addrs_on_network_ports(self, context, subnet, ipam_subnet):
|
||||
|
@ -62,9 +62,11 @@ class TestDbBasePluginIpam(test_db_base.NeutronDbPluginV2TestCase):
|
||||
self.tenant_id = uuidutils.generate_uuid()
|
||||
self.subnet_id = uuidutils.generate_uuid()
|
||||
|
||||
def _prepare_mocks(self, address_factory=None):
|
||||
def _prepare_mocks(self, address_factory=None, subnet_factory=None):
|
||||
if address_factory is None:
|
||||
address_factory = ipam_req.AddressRequestFactory
|
||||
if subnet_factory is None:
|
||||
subnet_factory = ipam_req.SubnetRequestFactory
|
||||
|
||||
mocks = {
|
||||
'driver': mock.Mock(),
|
||||
@ -79,7 +81,7 @@ class TestDbBasePluginIpam(test_db_base.NeutronDbPluginV2TestCase):
|
||||
mocks['driver'].get_subnet.return_value = mocks['subnet']
|
||||
mocks['driver'].allocate_subnet.return_value = mocks['subnet']
|
||||
mocks['driver'].get_subnet_request_factory.return_value = (
|
||||
ipam_req.SubnetRequestFactory)
|
||||
subnet_factory)
|
||||
mocks['driver'].get_address_request_factory.return_value = (
|
||||
address_factory)
|
||||
mocks['subnet'].get_details.return_value = mocks['subnet_request']
|
||||
@ -90,8 +92,10 @@ class TestDbBasePluginIpam(test_db_base.NeutronDbPluginV2TestCase):
|
||||
mocks['ipam'] = ipam_pluggable_backend.IpamPluggableBackend()
|
||||
return mocks
|
||||
|
||||
def _prepare_mocks_with_pool_mock(self, pool_mock, address_factory=None):
|
||||
mocks = self._prepare_mocks(address_factory=address_factory)
|
||||
def _prepare_mocks_with_pool_mock(self, pool_mock, address_factory=None,
|
||||
subnet_factory=None):
|
||||
mocks = self._prepare_mocks(address_factory=address_factory,
|
||||
subnet_factory=subnet_factory)
|
||||
pool_mock.get_instance.return_value = mocks['driver']
|
||||
return mocks
|
||||
|
||||
@ -583,3 +587,52 @@ class TestDbBasePluginIpam(test_db_base.NeutronDbPluginV2TestCase):
|
||||
ip_dict)
|
||||
# Verify incoming port dict is not changed ('id' is not added to it)
|
||||
self.assertIsNone(port_dict['port'].get('id'))
|
||||
|
||||
def _test_update_db_subnet(self, pool_mock, subnet, expected_subnet,
|
||||
old_pools):
|
||||
subnet_factory = mock.Mock()
|
||||
context = mock.Mock()
|
||||
|
||||
mocks = self._prepare_mocks_with_pool_mock(
|
||||
pool_mock, subnet_factory=subnet_factory)
|
||||
|
||||
mocks['ipam'] = ipam_pluggable_backend.IpamPluggableBackend()
|
||||
mocks['ipam'].update_db_subnet(context, id, subnet, old_pools)
|
||||
|
||||
mocks['driver'].get_subnet_request_factory.assert_called_once_with()
|
||||
subnet_factory.get_request.assert_called_once_with(context,
|
||||
expected_subnet,
|
||||
None)
|
||||
|
||||
@mock.patch('neutron.ipam.driver.Pool')
|
||||
def test_update_db_subnet_unchanged_pools(self, pool_mock):
|
||||
old_pools = [netaddr.IPRange('192.1.1.2', '192.1.1.254')]
|
||||
subnet = {'id': uuidutils.generate_uuid(),
|
||||
'network_id': uuidutils.generate_uuid(),
|
||||
'cidr': '192.1.1.0/24',
|
||||
'ipv6_address_mode': None,
|
||||
'ipv6_ra_mode': None}
|
||||
subnet_with_pools = subnet.copy()
|
||||
subnet_with_pools['allocation_pools'] = old_pools
|
||||
# if subnet has no allocation pools set, then old pools has to
|
||||
# be added to subnet dict passed to request factory
|
||||
self._test_update_db_subnet(pool_mock, subnet, subnet_with_pools,
|
||||
old_pools)
|
||||
|
||||
@mock.patch('neutron.ipam.driver.Pool')
|
||||
def test_update_db_subnet_new_pools(self, pool_mock):
|
||||
old_pools = [netaddr.IPRange('192.1.1.2', '192.1.1.254')]
|
||||
subnet = {'id': uuidutils.generate_uuid(),
|
||||
'network_id': uuidutils.generate_uuid(),
|
||||
'cidr': '192.1.1.0/24',
|
||||
'allocation_pools': [
|
||||
netaddr.IPRange('192.1.1.10', '192.1.1.254')],
|
||||
'ipv6_address_mode': None,
|
||||
'ipv6_ra_mode': None}
|
||||
# make a copy of subnet for validation, since update_subnet changes
|
||||
# incoming subnet dict
|
||||
expected_subnet = subnet.copy()
|
||||
# validate that subnet passed to request factory is the same as
|
||||
# incoming one, i.e. new pools in it are not overwritten by old pools
|
||||
self._test_update_db_subnet(pool_mock, subnet, expected_subnet,
|
||||
old_pools)
|
||||
|
Loading…
x
Reference in New Issue
Block a user