1 # Copyright (c) 2014 OpenStack Foundation. All rights reserved.
3 # Licensed under the Apache License, Version 2.0 (the "License"); you may
4 # not use this file except in compliance with the License. You may obtain
5 # a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 # License for the specific language governing permissions and limitations
16 from oslo_config import cfg
17 from oslo_log import log as logging
18 from oslo_utils import excutils
21 from neutron._i18n import _, _LI, _LW
22 from neutron.api.v2 import attributes
23 from neutron.callbacks import events
24 from neutron.callbacks import exceptions
25 from neutron.callbacks import registry
26 from neutron.callbacks import resources
27 from neutron.common import constants as l3_const
28 from neutron.common import exceptions as n_exc
29 from neutron.common import utils as n_utils
30 from neutron.db import l3_attrs_db
31 from neutron.db import l3_db
32 from neutron.db import l3_dvrscheduler_db as l3_dvrsched_db
33 from neutron.extensions import l3
34 from neutron.extensions import portbindings
35 from neutron import manager
36 from neutron.plugins.common import constants
37 from neutron.plugins.common import utils as p_utils
40 LOG = logging.getLogger(__name__)
41 router_distributed_opts = [
42 cfg.BoolOpt('router_distributed',
44 help=_("System-wide flag to determine the type of router "
45 "that tenants can create. Only admin can override.")),
47 cfg.CONF.register_opts(router_distributed_opts)
50 class L3_NAT_with_dvr_db_mixin(l3_db.L3_NAT_db_mixin,
51 l3_attrs_db.ExtraAttributesMixin):
52 """Mixin class to enable DVR support."""
54 router_device_owners = (
55 l3_db.L3_NAT_db_mixin.router_device_owners +
56 (l3_const.DEVICE_OWNER_DVR_INTERFACE,
57 l3_const.DEVICE_OWNER_ROUTER_SNAT,
58 l3_const.DEVICE_OWNER_AGENT_GW))
61 l3_attrs_db.ExtraAttributesMixin.extra_attributes + [{
62 'name': "distributed",
63 'default': cfg.CONF.router_distributed
66 def _create_router_db(self, context, router, tenant_id):
67 """Create a router db object with dvr additions."""
68 router['distributed'] = is_distributed_router(router)
69 with context.session.begin(subtransactions=True):
71 L3_NAT_with_dvr_db_mixin, self)._create_router_db(
72 context, router, tenant_id)
73 self._process_extra_attr_router_create(context, router_db, router)
76 def _validate_router_migration(self, context, router_db, router_res):
77 """Allow centralized -> distributed state transition only."""
78 if (router_db.extra_attributes.distributed and
79 router_res.get('distributed') is False):
80 LOG.info(_LI("Centralizing distributed router %s "
81 "is not supported"), router_db['id'])
82 raise n_exc.BadRequest(
84 msg=_("Migration from distributed router to centralized is "
86 elif (not router_db.extra_attributes.distributed and
87 router_res.get('distributed')):
88 # router should be disabled in order for upgrade
89 if router_db.admin_state_up:
90 msg = _('Cannot upgrade active router to distributed. Please '
91 'set router admin_state_up to False prior to upgrade.')
92 raise n_exc.BadRequest(resource='router', msg=msg)
94 # Notify advanced services of the imminent state transition
97 kwargs = {'context': context, 'router': router_db}
99 resources.ROUTER, events.BEFORE_UPDATE, self, **kwargs)
100 except exceptions.CallbackFailure as e:
101 with excutils.save_and_reraise_exception():
102 # NOTE(armax): preserve old check's behavior
103 if len(e.errors) == 1:
104 raise e.errors[0].error
105 raise l3.RouterInUse(router_id=router_db['id'],
108 def _update_distributed_attr(
109 self, context, router_id, router_db, data):
110 """Update the model to support the dvr case of a router."""
111 if data.get('distributed'):
112 old_owner = l3_const.DEVICE_OWNER_ROUTER_INTF
113 new_owner = l3_const.DEVICE_OWNER_DVR_INTERFACE
114 for rp in router_db.attached_ports.filter_by(port_type=old_owner):
115 rp.port_type = new_owner
116 rp.port.device_owner = new_owner
118 def _update_router_db(self, context, router_id, data):
119 with context.session.begin(subtransactions=True):
121 L3_NAT_with_dvr_db_mixin, self)._update_router_db(
122 context, router_id, data)
123 migrating_to_distributed = (
124 not router_db.extra_attributes.distributed and
125 data.get('distributed') is True)
126 self._validate_router_migration(context, router_db, data)
127 router_db.extra_attributes.update(data)
128 self._update_distributed_attr(
129 context, router_id, router_db, data)
130 if migrating_to_distributed:
131 if router_db['gw_port_id']:
132 # If the Legacy router is getting migrated to a DVR
133 # router, make sure to create corresponding
134 # snat interface ports that are to be consumed by
136 if not self._create_snat_intf_ports_if_not_exists(
137 context.elevated(), router_db):
138 LOG.debug("SNAT interface ports not created: %s",
140 cur_agents = self.list_l3_agents_hosting_router(
141 context, router_db['id'])['agents']
142 for agent in cur_agents:
143 self._unbind_router(context, router_db['id'],
147 def _delete_current_gw_port(self, context, router_id, router, new_network):
149 Overriden here to handle deletion of dvr internal ports.
151 If there is a valid router update with gateway port to be deleted,
152 then go ahead and delete the csnat ports and the floatingip
153 agent gateway port associated with the dvr router.
157 router.gw_port['network_id'] if router.gw_port else None)
159 super(L3_NAT_with_dvr_db_mixin,
160 self)._delete_current_gw_port(context, router_id,
162 if (is_distributed_router(router) and
163 gw_ext_net_id != new_network and gw_ext_net_id is not None):
164 self.delete_csnat_router_interface_ports(
165 context.elevated(), router)
166 # NOTE(Swami): Delete the Floatingip agent gateway port
167 # on all hosts when it is the last gateway port in the
168 # given external network.
169 filters = {'network_id': [gw_ext_net_id],
170 'device_owner': [l3_const.DEVICE_OWNER_ROUTER_GW]}
171 ext_net_gw_ports = self._core_plugin.get_ports(
172 context.elevated(), filters)
173 if not ext_net_gw_ports:
174 self.delete_floatingip_agent_gateway_port(
175 context.elevated(), None, gw_ext_net_id)
176 # Send the information to all the L3 Agent hosts
177 # to clean up the fip namespace as it is no longer required.
178 self.l3_rpc_notifier.delete_fipnamespace_for_ext_net(
179 context, gw_ext_net_id)
181 def _create_gw_port(self, context, router_id, router, new_network,
183 super(L3_NAT_with_dvr_db_mixin,
184 self)._create_gw_port(context, router_id, router, new_network,
186 # Make sure that the gateway port exists before creating the
187 # snat interface ports for distributed router.
188 if router.extra_attributes.distributed and router.gw_port:
189 snat_p_list = self._create_snat_intf_ports_if_not_exists(
190 context.elevated(), router)
192 LOG.debug("SNAT interface ports not created: %s", snat_p_list)
194 def _get_device_owner(self, context, router=None):
195 """Get device_owner for the specified router."""
196 router_is_uuid = isinstance(router, six.string_types)
198 router = self._get_router(context, router)
199 if is_distributed_router(router):
200 return l3_const.DEVICE_OWNER_DVR_INTERFACE
201 return super(L3_NAT_with_dvr_db_mixin,
202 self)._get_device_owner(context, router)
204 def _update_fip_assoc(self, context, fip, floatingip_db, external_port):
205 """Override to create floating agent gw port for DVR.
207 Floating IP Agent gateway port will be created when a
208 floatingIP association happens.
210 fip_port = fip.get('port_id')
211 super(L3_NAT_with_dvr_db_mixin, self)._update_fip_assoc(
212 context, fip, floatingip_db, external_port)
213 associate_fip = fip_port and floatingip_db['id']
214 if associate_fip and floatingip_db.get('router_id'):
215 admin_ctx = context.elevated()
216 router_dict = self.get_router(
217 admin_ctx, floatingip_db['router_id'])
218 # Check if distributed router and then create the
219 # FloatingIP agent gateway port
220 if router_dict.get('distributed'):
221 hostid = self._get_dvr_service_port_hostid(
224 # FIXME (Swami): This FIP Agent Gateway port should be
225 # created only once and there should not be a duplicate
226 # for the same host. Until we find a good solution for
227 # augmenting multiple server requests we should use the
230 self.create_fip_agent_gw_port_if_not_exists(
231 admin_ctx, external_port['network_id'],
233 LOG.debug("FIP Agent gateway port: %s", fip_agent_port)
235 def _get_floatingip_on_port(self, context, port_id=None):
236 """Helper function to retrieve the fip associated with port."""
237 fip_qry = context.session.query(l3_db.FloatingIP)
238 floating_ip = fip_qry.filter_by(fixed_port_id=port_id)
239 return floating_ip.first()
241 def add_router_interface(self, context, router_id, interface_info):
242 add_by_port, add_by_sub = self._validate_interface_info(interface_info)
243 router = self._get_router(context, router_id)
244 device_owner = self._get_device_owner(context, router)
246 # This should be True unless adding an IPv6 prefix to an existing port
250 port, subnets = self._add_interface_by_port(
251 context, router, interface_info['port_id'], device_owner)
253 port, subnets, new_port = self._add_interface_by_subnet(
254 context, router, interface_info['subnet_id'], device_owner)
259 if router.extra_attributes.distributed and router.gw_port:
261 admin_context = context.elevated()
262 self._add_csnat_router_interface_port(
263 admin_context, router, port['network_id'],
264 port['fixed_ips'][-1]['subnet_id'])
266 with excutils.save_and_reraise_exception():
267 # we need to preserve the original state prior
268 # the request by rolling back the port creation
269 # that led to new_port=True
270 self._core_plugin.delete_port(
271 admin_context, port['id'])
273 with context.session.begin(subtransactions=True):
274 router_port = l3_db.RouterPort(
277 port_type=device_owner
279 context.session.add(router_port)
281 # NOTE: For IPv6 additional subnets added to the same
282 # network we need to update the CSNAT port with respective
284 elif subnet and port:
285 fixed_ip = {'subnet_id': subnet['id']}
286 if subnet['ip_version'] == 6:
287 # Add new prefix to an existing ipv6 csnat port with the
288 # same network id if one exists
289 cs_port = self._find_router_port_by_network_and_device_owner(
290 router, subnet['network_id'],
291 l3_const.DEVICE_OWNER_ROUTER_SNAT)
293 fixed_ips = list(cs_port['port']['fixed_ips'])
294 fixed_ips.append(fixed_ip)
295 updated_port = self._core_plugin.update_port(
297 cs_port['port_id'], {'port': {'fixed_ips': fixed_ips}})
298 LOG.debug("CSNAT port updated for IPv6 subnet: "
300 router_interface_info = self._make_router_interface_info(
301 router_id, port['tenant_id'], port['id'], subnet['id'],
303 self.notify_router_interface_action(
304 context, router_interface_info, 'add')
305 return router_interface_info
307 def _port_has_ipv6_address(self, port, csnat_port_check=True):
308 """Overridden to return False if DVR SNAT port."""
310 if port['device_owner'] == l3_const.DEVICE_OWNER_ROUTER_SNAT:
312 return super(L3_NAT_with_dvr_db_mixin,
313 self)._port_has_ipv6_address(port)
315 def _find_router_port_by_network_and_device_owner(
316 self, router, net_id, device_owner):
317 for port in router.attached_ports:
319 if (p['network_id'] == net_id and
320 p['device_owner'] == device_owner and
321 self._port_has_ipv6_address(p, csnat_port_check=False)):
324 def _check_for_multiprefix_csnat_port_and_update(
325 self, context, router, subnet, port):
326 """Checks if the csnat port contains multiple ipv6 prefixes.
328 If the csnat port contains multiple ipv6 prefixes for the given
329 network when a router interface is deleted, make sure we don't
330 delete the port when a single subnet is deleted and just update
331 it with the right fixed_ip.
332 This function returns true if it is a multiprefix port.
334 subnet_id = subnet['id']
336 # If router has a gateway port, check if it has IPV6 subnet
338 self._find_router_port_by_network_and_device_owner(
339 router, subnet['network_id'],
340 l3_const.DEVICE_OWNER_ROUTER_SNAT))
343 [fixedip for fixedip in
344 cs_port['port']['fixed_ips']
345 if fixedip['subnet_id'] != subnet_id])
347 # multiple prefix port - delete prefix from port
348 self._core_plugin.update_port(
350 cs_port['port_id'], {'port': {'fixed_ips': fixed_ips}})
354 def _check_dvr_router_remove_required_and_notify_agent(
355 self, context, router, port, subnet):
356 if router.extra_attributes.distributed:
357 is_multiple_prefix_csport = (
358 self._check_for_multiprefix_csnat_port_and_update(
359 context, router, subnet, port))
360 if not is_multiple_prefix_csport:
361 # Single prefix port - go ahead and delete the port
362 self.delete_csnat_router_interface_ports(
363 context.elevated(), router, subnet_id=subnet['id'])
364 plugin = manager.NeutronManager.get_service_plugins().get(
365 constants.L3_ROUTER_NAT)
366 l3_agents = plugin.get_l3_agents_hosting_routers(context,
368 subnet_ids = plugin.get_subnet_ids_on_router(
369 context, router['id'])
371 for l3_agent in l3_agents:
372 if not plugin.check_dvr_serviceable_ports_on_host(
373 context, l3_agent['host'], subnet_ids):
374 plugin.remove_router_from_l3_agent(
375 context, l3_agent['id'], router['id'])
376 router_interface_info = self._make_router_interface_info(
377 router['id'], port['tenant_id'], port['id'], subnet['id'],
379 self.notify_router_interface_action(
380 context, router_interface_info, 'remove')
381 return router_interface_info
383 def remove_router_interface(self, context, router_id, interface_info):
384 remove_by_port, remove_by_subnet = (
385 self._validate_interface_info(interface_info, for_removal=True)
387 port_id = interface_info.get('port_id')
388 subnet_id = interface_info.get('subnet_id')
389 router = self._get_router(context, router_id)
390 device_owner = self._get_device_owner(context, router)
393 port, subnets = self._remove_interface_by_port(
394 context, router_id, port_id, subnet_id, device_owner)
396 # remove_by_subnet is not used here, because the validation logic of
397 # _validate_interface_info ensures that at least one of remote_by_*
400 port, subnets = self._remove_interface_by_subnet(
401 context, router_id, subnet_id, device_owner)
404 router_interface_info = (
405 self._check_dvr_router_remove_required_and_notify_agent(
406 context, router, port, subnet))
407 return router_interface_info
409 def _get_snat_sync_interfaces(self, context, router_ids):
410 """Query router interfaces that relate to list of router_ids."""
413 qry = context.session.query(l3_db.RouterPort)
415 l3_db.RouterPort.router_id.in_(router_ids),
416 l3_db.RouterPort.port_type == l3_const.DEVICE_OWNER_ROUTER_SNAT
418 interfaces = collections.defaultdict(list)
420 interfaces[rp.router_id].append(
421 self._core_plugin._make_port_dict(rp.port, None))
422 LOG.debug("Return the SNAT ports: %s", interfaces)
425 def _build_routers_list(self, context, routers, gw_ports):
426 # Perform a single query up front for all routers
429 router_ids = [r['id'] for r in routers]
430 snat_binding = l3_dvrsched_db.CentralizedSnatL3AgentBinding
431 query = (context.session.query(snat_binding).
432 filter(snat_binding.router_id.in_(router_ids))).all()
433 bindings = dict((b.router_id, b) for b in query)
436 gw_port_id = rtr['gw_port_id']
437 # Collect gw ports only if available
438 if gw_port_id and gw_ports.get(gw_port_id):
439 rtr['gw_port'] = gw_ports[gw_port_id]
440 if 'enable_snat' in rtr[l3.EXTERNAL_GW_INFO]:
441 rtr['enable_snat'] = (
442 rtr[l3.EXTERNAL_GW_INFO]['enable_snat'])
444 binding = bindings.get(rtr['id'])
446 rtr['gw_port_host'] = None
447 LOG.debug('No snat is bound to router %s', rtr['id'])
450 rtr['gw_port_host'] = binding.l3_agent.host
454 def _process_routers(self, context, routers):
456 snat_intfs_by_router_id = self._get_snat_sync_interfaces(
457 context, [r['id'] for r in routers])
458 for router in routers:
459 routers_dict[router['id']] = router
460 if router['gw_port_id']:
461 snat_router_intfs = snat_intfs_by_router_id[router['id']]
462 LOG.debug("SNAT ports returned: %s ", snat_router_intfs)
463 router[l3_const.SNAT_ROUTER_INTF_KEY] = snat_router_intfs
466 def _process_floating_ips_dvr(self, context, routers_dict,
467 floating_ips, host, agent):
468 fip_sync_interfaces = None
469 LOG.debug("FIP Agent : %s ", agent.id)
470 for floating_ip in floating_ips:
471 router = routers_dict.get(floating_ip['router_id'])
473 router_floatingips = router.get(l3_const.FLOATINGIP_KEY, [])
474 if router['distributed']:
475 if floating_ip.get('host', None) != host:
477 LOG.debug("Floating IP host: %s", floating_ip['host'])
478 router_floatingips.append(floating_ip)
479 router[l3_const.FLOATINGIP_KEY] = router_floatingips
480 if not fip_sync_interfaces:
481 fip_sync_interfaces = self._get_fip_sync_interfaces(
483 LOG.debug("FIP Agent ports: %s", fip_sync_interfaces)
484 router[l3_const.FLOATINGIP_AGENT_INTF_KEY] = (
487 def _get_fip_sync_interfaces(self, context, fip_agent_id):
488 """Query router interfaces that relate to list of router_ids."""
491 filters = {'device_id': [fip_agent_id],
492 'device_owner': [l3_const.DEVICE_OWNER_AGENT_GW]}
493 interfaces = self._core_plugin.get_ports(context.elevated(), filters)
494 LOG.debug("Return the FIP ports: %s ", interfaces)
497 def _get_dvr_sync_data(self, context, host, agent, router_ids=None,
499 routers, interfaces, floating_ips = self._get_router_info_list(
500 context, router_ids=router_ids, active=active,
501 device_owners=l3_const.ROUTER_INTERFACE_OWNERS)
502 dvr_router_ids = set(router['id'] for router in routers
503 if is_distributed_router(router))
504 floating_ip_port_ids = [fip['port_id'] for fip in floating_ips
505 if fip['router_id'] in dvr_router_ids]
506 if floating_ip_port_ids:
507 port_filter = {portbindings.HOST_ID: [host],
508 'id': floating_ip_port_ids}
509 ports = self._core_plugin.get_ports(context, port_filter)
510 port_dict = dict((port['id'], port) for port in ports)
511 # Add the port binding host to the floatingip dictionary
512 for fip in floating_ips:
513 vm_port = port_dict.get(fip['port_id'], None)
515 fip['host'] = self._get_dvr_service_port_hostid(
516 context, fip['port_id'], port=vm_port)
517 routers_dict = self._process_routers(context, routers)
518 self._process_floating_ips_dvr(context, routers_dict,
519 floating_ips, host, agent)
520 ports_to_populate = []
521 for router in routers_dict.values():
522 if router.get('gw_port'):
523 ports_to_populate.append(router['gw_port'])
524 if router.get(l3_const.FLOATINGIP_AGENT_INTF_KEY):
525 ports_to_populate += router[l3_const.FLOATINGIP_AGENT_INTF_KEY]
526 if router.get(l3_const.SNAT_ROUTER_INTF_KEY):
527 ports_to_populate += router[l3_const.SNAT_ROUTER_INTF_KEY]
528 ports_to_populate += interfaces
529 self._populate_subnets_for_ports(context, ports_to_populate)
530 self._process_interfaces(routers_dict, interfaces)
531 return list(routers_dict.values())
533 def _get_dvr_service_port_hostid(self, context, port_id, port=None):
534 """Returns the portbinding host_id for dvr service port."""
535 port_db = port or self._core_plugin.get_port(context, port_id)
536 device_owner = port_db['device_owner'] if port_db else ""
537 if (n_utils.is_dvr_serviced(device_owner) or
538 device_owner == l3_const.DEVICE_OWNER_AGENT_GW):
539 return port_db[portbindings.HOST_ID]
541 def _get_agent_gw_ports_exist_for_network(
542 self, context, network_id, host, agent_id):
543 """Return agent gw port if exist, or None otherwise."""
545 LOG.debug("Network not specified")
549 'network_id': [network_id],
550 'device_id': [agent_id],
551 'device_owner': [l3_const.DEVICE_OWNER_AGENT_GW]
553 ports = self._core_plugin.get_ports(context, filters)
557 def delete_floatingip_agent_gateway_port(
558 self, context, host_id, ext_net_id):
559 """Function to delete FIP gateway port with given ext_net_id."""
560 # delete any fip agent gw port
561 device_filter = {'device_owner': [l3_const.DEVICE_OWNER_AGENT_GW],
562 'network_id': [ext_net_id]}
563 ports = self._core_plugin.get_ports(context,
564 filters=device_filter)
566 if not host_id or p[portbindings.HOST_ID] == host_id:
567 self._core_plugin.ipam.delete_port(context, p['id'])
571 def create_fip_agent_gw_port_if_not_exists(
572 self, context, network_id, host):
573 """Function to return the FIP Agent GW port.
575 This function will create a FIP Agent GW port
576 if required. If the port already exists, it
577 will return the existing port and will not
580 l3_agent_db = self._get_agent_by_type_and_host(
581 context, l3_const.AGENT_TYPE_L3, host)
583 LOG.debug("Agent ID exists: %s", l3_agent_db['id'])
584 f_port = self._get_agent_gw_ports_exist_for_network(
585 context, network_id, host, l3_agent_db['id'])
587 LOG.info(_LI('Agent Gateway port does not exist,'
588 ' so create one: %s'), f_port)
589 port_data = {'tenant_id': '',
590 'network_id': network_id,
591 'device_id': l3_agent_db['id'],
592 'device_owner': l3_const.DEVICE_OWNER_AGENT_GW,
593 portbindings.HOST_ID: host,
594 'admin_state_up': True,
596 agent_port = p_utils.create_port(self._core_plugin, context,
599 self._populate_subnets_for_ports(context, [agent_port])
601 msg = _("Unable to create the Agent Gateway Port")
602 raise n_exc.BadRequest(resource='router', msg=msg)
604 self._populate_subnets_for_ports(context, [f_port])
607 def _get_snat_interface_ports_for_router(self, context, router_id):
608 """Return all existing snat_router_interface ports."""
609 qry = context.session.query(l3_db.RouterPort)
612 port_type=l3_const.DEVICE_OWNER_ROUTER_SNAT
615 ports = [self._core_plugin._make_port_dict(rp.port, None)
619 def _add_csnat_router_interface_port(
620 self, context, router, network_id, subnet_id, do_pop=True):
621 """Add SNAT interface to the specified router and subnet."""
622 port_data = {'tenant_id': '',
623 'network_id': network_id,
624 'fixed_ips': [{'subnet_id': subnet_id}],
625 'device_id': router.id,
626 'device_owner': l3_const.DEVICE_OWNER_ROUTER_SNAT,
627 'admin_state_up': True,
629 snat_port = p_utils.create_port(self._core_plugin, context,
632 msg = _("Unable to create the SNAT Interface Port")
633 raise n_exc.BadRequest(resource='router', msg=msg)
635 with context.session.begin(subtransactions=True):
636 router_port = l3_db.RouterPort(
637 port_id=snat_port['id'],
639 port_type=l3_const.DEVICE_OWNER_ROUTER_SNAT
641 context.session.add(router_port)
644 return self._populate_subnets_for_ports(context, [snat_port])
647 def _create_snat_intf_ports_if_not_exists(self, context, router):
648 """Function to return the snat interface port list.
650 This function will return the snat interface port list
651 if it exists. If the port does not exist it will create
652 new ports and then return the list.
654 port_list = self._get_snat_interface_ports_for_router(
657 self._populate_subnets_for_ports(context, port_list)
663 router.attached_ports.filter_by(
664 port_type=l3_const.DEVICE_OWNER_DVR_INTERFACE
667 LOG.info(_LI('SNAT interface port list does not exist,'
668 ' so create one: %s'), port_list)
669 for intf in int_ports:
671 # Passing the subnet for the port to make sure the IP's
672 # are assigned on the right subnet if multiple subnet
674 snat_port = self._add_csnat_router_interface_port(
675 context, router, intf['network_id'],
676 intf['fixed_ips'][0]['subnet_id'], do_pop=False)
677 port_list.append(snat_port)
679 self._populate_subnets_for_ports(context, port_list)
682 def _generate_arp_table_and_notify_agent(
683 self, context, fixed_ip, mac_address, notifier):
684 """Generates the arp table entry and notifies the l3 agent."""
685 ip_address = fixed_ip['ip_address']
686 subnet = fixed_ip['subnet_id']
687 filters = {'fixed_ips': {'subnet_id': [subnet]}}
688 ports = self._core_plugin.get_ports(context, filters=filters)
690 if port['device_owner'] == l3_const.DEVICE_OWNER_DVR_INTERFACE:
691 router_id = port['device_id']
692 router_dict = self._get_router(context, router_id)
693 if router_dict.extra_attributes.distributed:
694 arp_table = {'ip_address': ip_address,
695 'mac_address': mac_address,
697 notifier(context, router_id, arp_table)
700 def update_arp_entry_for_dvr_service_port(
701 self, context, port_dict, action):
702 """Notify L3 agents of ARP table entry for dvr service port.
704 When a dvr service port goes up or down, look for the DVR
705 router on the port's subnet, and send the ARP details to all
706 L3 agents hosting the router.
709 # Check this is a valid VM or service port
710 if not (n_utils.is_dvr_serviced(port_dict['device_owner']) and
711 port_dict['fixed_ips']):
713 changed_fixed_ips = port_dict['fixed_ips']
714 for fixed_ip in changed_fixed_ips:
716 notifier = self.l3_rpc_notifier.add_arp_entry
717 elif action == "del":
718 notifier = self.l3_rpc_notifier.del_arp_entry
722 self._generate_arp_table_and_notify_agent(
723 context, fixed_ip, port_dict['mac_address'], notifier)
725 def delete_csnat_router_interface_ports(self, context,
726 router, subnet_id=None):
727 # Each csnat router interface port is associated
728 # with a subnet, so we need to pass the subnet id to
729 # delete the right ports.
731 # TODO(markmcclain): This is suboptimal but was left to reduce
732 # changeset size since it is late in cycle
735 router.attached_ports.filter_by(
736 port_type=l3_const.DEVICE_OWNER_ROUTER_SNAT)
740 c_snat_ports = self._core_plugin.get_ports(
742 filters={'id': ports}
744 for p in c_snat_ports:
745 if subnet_id is None:
746 self._core_plugin.delete_port(context,
750 if p['fixed_ips'][0]['subnet_id'] == subnet_id:
751 LOG.debug("Subnet matches: %s", subnet_id)
752 self._core_plugin.delete_port(context,
756 def create_floatingip(self, context, floatingip,
757 initial_status=l3_const.FLOATINGIP_STATUS_ACTIVE):
758 floating_ip = self._create_floatingip(
759 context, floatingip, initial_status)
760 self._notify_floating_ip_change(context, floating_ip)
763 def _notify_floating_ip_change(self, context, floating_ip):
764 router_id = floating_ip['router_id']
765 fixed_port_id = floating_ip['port_id']
766 # we need to notify agents only in case Floating IP is associated
767 if not router_id or not fixed_port_id:
771 # using admin context as router may belong to admin tenant
772 router = self._get_router(context.elevated(), router_id)
773 except l3.RouterNotFound:
774 LOG.warning(_LW("Router %s was not found. "
775 "Skipping agent notification."),
779 if is_distributed_router(router):
780 host = self._get_dvr_service_port_hostid(context, fixed_port_id)
781 self.l3_rpc_notifier.routers_updated_on_host(
782 context, [router_id], host)
784 self.notify_router_updated(context, router_id)
786 def update_floatingip(self, context, id, floatingip):
787 old_floatingip, floatingip = self._update_floatingip(
788 context, id, floatingip)
789 self._notify_floating_ip_change(context, old_floatingip)
790 if (floatingip['router_id'] != old_floatingip['router_id'] or
791 floatingip['port_id'] != old_floatingip['port_id']):
792 self._notify_floating_ip_change(context, floatingip)
795 def delete_floatingip(self, context, id):
796 floating_ip = self._delete_floatingip(context, id)
797 self._notify_floating_ip_change(context, floating_ip)
800 def is_distributed_router(router):
801 """Return True if router to be handled is distributed."""
803 # See if router is a DB object first
804 requested_router_type = router.extra_attributes.distributed
805 except AttributeError:
806 # if not, try to see if it is a request body
807 requested_router_type = router.get('distributed')
808 if attributes.is_attr_set(requested_router_type):
809 return requested_router_type
810 return cfg.CONF.router_distributed