Update related router when subnetpool change scope

When the address scope of a subnetpool changes, the scope of related
subnet will change too. If this subnet has been connected to a router,
the related routing information needs to be updated.
This patch will fire the router update to correlative routers. The
routers will get all things updated in the update action.

Change-Id: Ia2eb484eb949a8d42fc909291de7be07b002adda
Partially-Implements: blueprint address-scopes
This commit is contained in:
Hong Hui Xiao 2015-11-29 09:00:23 -05:00 committed by Carl Baldwin
parent 0482c534c8
commit e1df8b54d2
7 changed files with 145 additions and 0 deletions

View File

@ -21,3 +21,4 @@ SECURITY_GROUP = 'security_group'
SECURITY_GROUP_RULE = 'security_group_rule'
SUBNET = 'subnet'
SUBNET_GATEWAY = 'subnet_gateway'
SUBNETPOOL_ADDRESS_SCOPE = 'subnetpool_address_scope'

View File

@ -59,6 +59,8 @@ ROUTER_INTERFACE_OWNERS = (DEVICE_OWNER_ROUTER_INTF,
ROUTER_INTERFACE_OWNERS_SNAT = (DEVICE_OWNER_ROUTER_INTF,
DEVICE_OWNER_DVR_INTERFACE,
DEVICE_OWNER_ROUTER_SNAT)
ROUTER_PORT_OWNERS = ROUTER_INTERFACE_OWNERS_SNAT + (DEVICE_OWNER_ROUTER_GW,)
L3_AGENT_MODE_DVR = 'dvr'
L3_AGENT_MODE_DVR_SNAT = 'dvr_snat'
L3_AGENT_MODE_LEGACY = 'legacy'

View File

@ -1030,12 +1030,24 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon,
self._validate_address_scope_id(context, reader.address_scope_id,
id, reader.prefixes,
reader.ip_version)
address_scope_changed = (orig_sp.address_scope_id !=
reader.address_scope_id)
orig_sp.update(self._filter_non_model_columns(
reader.subnetpool,
models_v2.SubnetPool))
self._update_subnetpool_prefixes(context,
reader.prefixes,
id)
if address_scope_changed:
# Notify about the update of subnetpool's address scope
kwargs = {'context': context, 'subnetpool_id': id}
registry.notify(resources.SUBNETPOOL_ADDRESS_SCOPE,
events.AFTER_UPDATE,
self.update_subnetpool,
**kwargs)
for key in ['min_prefixlen', 'max_prefixlen', 'default_prefixlen']:
updated['key'] = str(updated[key])

View File

@ -1654,6 +1654,27 @@ def _notify_subnet_gateway_ip_update(resource, event, trigger, **kwargs):
l3plugin.notify_router_updated(context, router_id)
def _notify_subnetpool_address_scope_update(resource, event,
trigger, **kwargs):
context = kwargs['context']
subnetpool_id = kwargs['subnetpool_id']
query = context.session.query(RouterPort.router_id)
query = query.join(models_v2.Port)
query = query.join(
models_v2.Subnet,
models_v2.Subnet.network_id == models_v2.Port.network_id)
query = query.filter(
models_v2.Subnet.subnetpool_id == subnetpool_id,
RouterPort.port_type.in_(l3_constants.ROUTER_PORT_OWNERS))
query = query.distinct()
router_ids = [r[0] for r in query]
l3plugin = manager.NeutronManager.get_service_plugins().get(
constants.L3_ROUTER_NAT)
l3plugin.notify_routers_updated(context, router_ids)
def subscribe():
registry.subscribe(
_prevent_l3_port_delete_callback, resources.PORT, events.BEFORE_DELETE)
@ -1662,6 +1683,10 @@ def subscribe():
registry.subscribe(
_notify_subnet_gateway_ip_update, resources.SUBNET_GATEWAY,
events.AFTER_UPDATE)
registry.subscribe(
_notify_subnetpool_address_scope_update,
resources.SUBNETPOOL_ADDRESS_SCOPE,
events.AFTER_UPDATE)
# NOTE(armax): multiple l3 service plugins (potentially out of tree) inherit
# from l3_db and may need the callbacks to be processed. Having an implicit

View File

@ -16,6 +16,9 @@
import mock
import testtools
from neutron.callbacks import events
from neutron.callbacks import registry
from neutron.callbacks import resources
from neutron.common import exceptions as n_exc
from neutron.db import l3_db
from neutron.extensions import l3
@ -185,3 +188,14 @@ class TestL3_NAT_dbonly_mixin(base.BaseTestCase):
self.db.get_floatingip = mock.Mock()
with testtools.ExpectedException(n_exc.ServicePortInUse):
self.db.prevent_l3_port_deletion(mock.Mock(), None)
@mock.patch.object(l3_db, '_notify_subnetpool_address_scope_update')
def test_subscribe_address_scope_of_subnetpool(self, notify):
l3_db.subscribe()
registry.notify(resources.SUBNETPOOL_ADDRESS_SCOPE,
events.AFTER_UPDATE, mock.ANY, context=mock.ANY,
subnetpool_id='fake_id')
notify.assert_called_once_with(resources.SUBNETPOOL_ADDRESS_SCOPE,
events.AFTER_UPDATE, mock.ANY,
context=mock.ANY,
subnetpool_id='fake_id')

View File

@ -14,10 +14,14 @@
import contextlib
import mock
import netaddr
import webob.exc
from neutron.api.v2 import attributes as attr
from neutron.callbacks import events
from neutron.callbacks import registry
from neutron.callbacks import resources
from neutron.common import constants
from neutron import context
from neutron.db import address_scope_db
@ -370,6 +374,62 @@ class TestSubnetPoolsWithAddressScopes(AddressScopeTestCase):
self._compare_resource(res, update_data['subnetpool'],
'subnetpool')
def _test_update_subnetpool_address_scope_notify(self, as_change=True):
with self.address_scope(name='foo-address-scope') as addr_scope:
foo_as_id = addr_scope['address_scope']['id']
subnet = netaddr.IPNetwork('10.10.10.0/24')
initial_subnetpool = self._test_create_subnetpool(
[subnet.cidr], name='foo-sp',
min_prefixlen='21', address_scope_id=foo_as_id)
subnetpool_id = initial_subnetpool['subnetpool']['id']
with self.address_scope(name='bar-address-scope') as other_as, \
self.network() as network:
data = {'subnet': {
'network_id': network['network']['id'],
'subnetpool_id': subnetpool_id,
'prefixlen': 24,
'ip_version': 4,
'tenant_id': network['network']['tenant_id']}}
req = self.new_create_request('subnets', data)
subnet = self.deserialize(self.fmt,
req.get_response(self.api))
with mock.patch.object(registry, 'notify') as notify:
plugin = db_base_plugin_v2.NeutronDbPluginV2()
plugin.is_address_scope_owned_by_tenant = mock.Mock(
return_value=True)
plugin._validate_address_scope_id = mock.Mock()
ctx = context.get_admin_context()
bar_as_id = other_as['address_scope']['id']
data = {'subnetpool': {
'name': 'bar-sp'}}
if as_change:
data['subnetpool']['address_scope_id'] = bar_as_id
updated_sp = plugin.update_subnetpool(
ctx, subnetpool_id, data)
self.assertEqual('bar-sp', updated_sp['name'])
if as_change:
self.assertEqual(bar_as_id,
updated_sp['address_scope_id'])
notify.assert_called_once_with(
resources.SUBNETPOOL_ADDRESS_SCOPE,
events.AFTER_UPDATE,
plugin.update_subnetpool, context=ctx,
subnetpool_id=subnetpool_id)
else:
self.assertEqual(foo_as_id,
updated_sp['address_scope_id'])
self.assertFalse(notify.called)
def test_update_subnetpool_address_scope_notify(self):
self._test_update_subnetpool_address_scope_notify()
def test_not_update_subnetpool_address_scope_not_notify(self):
self._test_update_subnetpool_address_scope_notify(False)
def test_delete_address_scope_in_use(self):
with self.address_scope(name='foo-address-scope') as addr_scope:
address_scope_id = addr_scope['address_scope']['id']

View File

@ -2683,6 +2683,37 @@ class L3NatTestCaseBase(L3NatTestCaseMixin):
chk_method.assert_called_with(mock.ANY,
['fake_device'], None)
def test__notify_subnetpool_address_scope_update(self):
plugin = manager.NeutronManager.get_service_plugins()[
service_constants.L3_ROUTER_NAT]
tenant_id = _uuid()
with mock.patch.object(
plugin, 'notify_routers_updated') as chk_method, \
self.subnetpool(prefixes=['10.0.0.0/24'],
admin=True, name='sp',
tenant_id=tenant_id) as subnetpool, \
self.router(tenant_id=tenant_id) as router, \
self.network(tenant_id=tenant_id) as network:
subnetpool_id = subnetpool['subnetpool']['id']
data = {'subnet': {
'network_id': network['network']['id'],
'subnetpool_id': subnetpool_id,
'prefixlen': 24,
'ip_version': 4,
'tenant_id': tenant_id}}
req = self.new_create_request('subnets', data)
subnet = self.deserialize(self.fmt, req.get_response(self.api))
admin_ctx = context.get_admin_context()
plugin.add_router_interface(
admin_ctx,
router['router']['id'], {'subnet_id': subnet['subnet']['id']})
l3_db._notify_subnetpool_address_scope_update(
mock.ANY, mock.ANY, mock.ANY,
context=admin_ctx, subnetpool_id=subnetpool_id)
chk_method.assert_called_with(admin_ctx, [router['router']['id']])
class L3AgentDbTestCaseBase(L3NatTestCaseMixin):