SECURITY_GROUP = 'security_group'
SECURITY_GROUP_RULE = 'security_group_rule'
SUBNET = 'subnet'
+SUBNET_GATEWAY = 'subnet_gateway'
s['allocation_pools'] = range_pools
# If either gateway_ip or allocation_pools were specified
- gateway_ip = s.get('gateway_ip')
- if gateway_ip is not None or s.get('allocation_pools') is not None:
- if gateway_ip is None:
- gateway_ip = db_subnet.gateway_ip
+ new_gateway_ip = s.get('gateway_ip')
+ gateway_ip_changed = (new_gateway_ip and
+ new_gateway_ip != db_subnet.gateway_ip)
+ if gateway_ip_changed or s.get('allocation_pools') is not None:
+ gateway_ip = new_gateway_ip or db_subnet.gateway_ip
pools = range_pools if range_pools is not None else db_pools
self.ipam.validate_gw_out_of_pools(gateway_ip, pools)
+ if gateway_ip_changed:
+ # Provide pre-update notification not to break plugins that don't
+ # support gateway ip change
+ kwargs = {'context': context, 'subnet_id': id,
+ 'network_id': db_subnet.network_id}
+ registry.notify(resources.SUBNET_GATEWAY, events.BEFORE_UPDATE,
+ self, **kwargs)
+
with context.session.begin(subtransactions=True):
subnet, changes = self.ipam.update_db_subnet(context, id, s,
db_pools)
l3_rpc_notifier = l3_rpc_agent_api.L3AgentNotifyAPI()
l3_rpc_notifier.routers_updated(context, routers)
+ if gateway_ip_changed:
+ kwargs = {'context': context, 'subnet_id': id,
+ 'network_id': db_subnet.network_id}
+ registry.notify(resources.SUBNET_GATEWAY, events.AFTER_UPDATE,
+ self, **kwargs)
+
return result
def _subnet_check_ip_allocations(self, context, subnet_id):
l3plugin.notify_routers_updated(context, router_ids)
+def _notify_subnet_gateway_ip_update(resource, event, trigger, **kwargs):
+ l3plugin = manager.NeutronManager.get_service_plugins().get(
+ constants.L3_ROUTER_NAT)
+ if not l3plugin:
+ return
+ context = kwargs['context']
+ network_id = kwargs['network_id']
+ subnet_id = kwargs['subnet_id']
+ query = context.session.query(models_v2.Port).filter_by(
+ network_id=network_id,
+ device_owner=l3_constants.DEVICE_OWNER_ROUTER_GW)
+ query = query.join(models_v2.Port.fixed_ips).filter(
+ models_v2.IPAllocation.subnet_id == subnet_id)
+ router_ids = set(port['device_id'] for port in query)
+ for router_id in router_ids:
+ l3plugin.notify_router_updated(context, router_id)
+
+
def subscribe():
registry.subscribe(
_prevent_l3_port_delete_callback, resources.PORT, events.BEFORE_DELETE)
registry.subscribe(
_notify_routers_callback, resources.PORT, events.AFTER_DELETE)
+ registry.subscribe(
+ _notify_subnet_gateway_ip_update, resources.SUBNET_GATEWAY,
+ 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
from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api
from neutron.api.rpc.handlers import l3_rpc
from neutron.api.v2 import attributes
+from neutron.callbacks import events
from neutron.callbacks import exceptions
from neutron.callbacks import registry
+from neutron.callbacks import resources
from neutron.common import constants as l3_constants
from neutron.common import exceptions as n_exc
from neutron import context
routers = plugin.get_routers(ctx)
self.assertEqual(0, len(routers))
+ def test_update_subnet_gateway_for_external_net(self):
+ """Test to make sure notification to routers occurs when the gateway
+ ip address of a subnet of the external network is changed.
+ """
+ plugin = manager.NeutronManager.get_service_plugins()[
+ service_constants.L3_ROUTER_NAT]
+ if not hasattr(plugin, 'l3_rpc_notifier'):
+ self.skipTest("Plugin does not support l3_rpc_notifier")
+ # make sure the callback is registered.
+ registry.subscribe(
+ l3_db._notify_subnet_gateway_ip_update, resources.SUBNET_GATEWAY,
+ events.AFTER_UPDATE)
+ with mock.patch.object(plugin.l3_rpc_notifier,
+ 'routers_updated') as chk_method:
+ with self.network() as network:
+ allocation_pools = [{'start': '120.0.0.3',
+ 'end': '120.0.0.254'}]
+ with self.subnet(network=network,
+ gateway_ip='120.0.0.1',
+ allocation_pools=allocation_pools,
+ cidr='120.0.0.0/24') as subnet:
+ kwargs = {
+ 'device_owner': l3_constants.DEVICE_OWNER_ROUTER_GW,
+ 'device_id': 'fake_device'}
+ with self.port(subnet=subnet, **kwargs):
+ data = {'subnet': {'gateway_ip': '120.0.0.2'}}
+ req = self.new_update_request('subnets', data,
+ subnet['subnet']['id'])
+ res = self.deserialize(self.fmt,
+ req.get_response(self.api))
+ self.assertEqual(res['subnet']['gateway_ip'],
+ data['subnet']['gateway_ip'])
+ chk_method.assert_called_with(mock.ANY,
+ ['fake_device'], None)
+
class L3AgentDbTestCaseBase(L3NatTestCaseMixin):