def remove_router_interface(self, context, router_id, interface_info): router_interface_info = \ super(OVNL3RouterPlugin, self).remove_router_interface( context, router_id, interface_info) router = self.get_router(context, router_id) port_id = router_interface_info['port_id'] multi_prefix = False port_removed = False try: port = self._plugin.get_port(context, port_id) # The router interface port still exists, call ovn to update it. self._ovn_client.update_router_port(port, bump_db_rev=False) multi_prefix = True except n_exc.PortNotFound: # The router interface port doesn't exist any more, # we will call ovn to delete it once we remove the snat # rules in the router itself if we have to port_removed = True if not router.get(l3.EXTERNAL_GW_INFO): if port_removed: self._ovn_client.delete_router_port(port_id, router_id) return router_interface_info try: cidr = None if multi_prefix: subnet = self._plugin.get_subnet(context, interface_info['subnet_id']) if subnet['ip_version'] == 4: cidr = subnet['cidr'] else: subnet_ids = router_interface_info.get('subnet_ids') for subnet_id in subnet_ids: subnet = self._plugin.get_subnet(context, subnet_id) if subnet['ip_version'] == 4: cidr = subnet['cidr'] break if utils.is_snat_enabled(router) and cidr: self._ovn_client.update_nat_rules(router, networks=[cidr], enable_snat=False) except Exception: with excutils.save_and_reraise_exception(): super(OVNL3RouterPlugin, self).add_router_interface(context, router_id, interface_info) LOG.error('Error is deleting snat') # NOTE(mangelajo): If the port doesn't exist anymore, we delete the # router port as the last operation and update the revision database # to ensure consistency if port_removed: self._ovn_client.delete_router_port(port_id, router_id) else: # otherwise, we just update the revision database db_rev.bump_revision(port, ovn_const.TYPE_ROUTER_PORTS) return router_interface_info
def add_router_interface(self, context, router_id, interface_info): router_interface_info = \ super(OVNL3RouterPlugin, self).add_router_interface( context, router_id, interface_info) port = self._plugin.get_port(context, router_interface_info['port_id']) multi_prefix = False if (len(router_interface_info['subnet_ids']) == 1 and len(port['fixed_ips']) > 1): # NOTE(lizk) It's adding a subnet onto an already existing router # interface port, try to update lrouter port 'networks' column. self.update_lrouter_port_in_ovn(context, router_id, port) multi_prefix = True else: self.create_lrouter_port_in_ovn(context, router_id, port) router = self.get_router(context, router_id) if not router.get(l3.EXTERNAL_GW_INFO): return router_interface_info cidr = None for fixed_ip in port['fixed_ips']: subnet = self._plugin.get_subnet(context, fixed_ip['subnet_id']) if multi_prefix: if 'subnet_id' in interface_info: if subnet['id'] is not interface_info['subnet_id']: continue if subnet['ip_version'] == 4: cidr = subnet['cidr'] if not cidr: return router_interface_info try: if utils.is_snat_enabled(router): self._update_snat_for_networks(context, router, networks=[cidr], enable_snat=True) except Exception: with excutils.save_and_reraise_exception(): self._ovn.delete_lrouter_port( utils.ovn_lrouter_port_name(port['id']), utils.ovn_name(router_id)).execute(check_error=True) super(OVNL3RouterPlugin, self).remove_router_interface(context, router_id, router_interface_info) LOG.error( _LE('Error updating snat for subnet %(subnet)s in ' 'router %(router)s'), { 'subnet': router_interface_info['subnet_id'], 'router': router_id }) return router_interface_info
def remove_router_interface(self, context, router_id, interface_info): router_interface_info = \ super(OVNL3RouterPlugin, self).remove_router_interface( context, router_id, interface_info) router = self.get_router(context, router_id) port_id = router_interface_info['port_id'] multi_prefix = False try: port = self._plugin.get_port(context, port_id) # The router interface port still exists, call ovn to update it. self.update_lrouter_port_in_ovn(context, router_id, port) multi_prefix = True except n_exc.PortNotFound: # The router interface port doesn't exist any more, call ovn to # delete it. self._ovn.delete_lrouter_port( utils.ovn_lrouter_port_name(port_id), utils.ovn_name(router_id), if_exists=False).execute(check_error=True) if not router.get(l3.EXTERNAL_GW_INFO): return router_interface_info try: cidr = None if multi_prefix: subnet = self._plugin.get_subnet(context, interface_info['subnet_id']) if subnet['ip_version'] == 4: cidr = subnet['cidr'] else: subnet_ids = router_interface_info.get('subnet_ids') for subnet_id in subnet_ids: subnet = self._plugin.get_subnet(context, subnet_id) if subnet['ip_version'] == 4: cidr = subnet['cidr'] break if not cidr: return router_interface_info if utils.is_snat_enabled(router): self._update_snat_for_networks(context, router, networks=[cidr], enable_snat=False) except Exception: with excutils.save_and_reraise_exception(): super(OVNL3RouterPlugin, self).add_router_interface(context, router_id, interface_info) LOG.error(_LE('Error is deleting snat')) return router_interface_info
def _add_router_ext_gw(self, context, router): router_id = router['id'] lrouter_name = utils.ovn_name(router['id']) # 1. Add the external gateway router port. ext_gw_ip = self._get_external_gateway_ip(context, router) gw_port_id = router['gw_port_id'] port = self._plugin.get_port(context.elevated(), gw_port_id) try: self.create_lrouter_port_in_ovn(context.elevated(), router_id, port) except Exception: with excutils.save_and_reraise_exception(): self._delete_router_ext_gw(context, router_id, router) LOG.error( _LE('Unable to add external router port %(id)s to' 'lrouter %(name)s'), { 'id': port['id'], 'name': lrouter_name }) # 2. Add default route with nexthop as ext_gw_ip route = [{'destination': '0.0.0.0/0', 'nexthop': ext_gw_ip}] try: self._update_lrouter_routes(context, router_id, route, []) except Exception: with excutils.save_and_reraise_exception(): self._delete_router_ext_gw(context, router_id, router) LOG.error( _LE('Error updating routes %(route)s in lrouter ' '%(name)s'), { 'route': route, 'name': lrouter_name }) # 3. Add snat rules for tenant networks in lrouter if snat is enabled if utils.is_snat_enabled(router): try: networks = self._get_v4_network_of_all_router_ports( context, router_id) if networks: self._update_snat_for_networks(context, router, networks, enable_snat=True) except Exception: with excutils.save_and_reraise_exception(): self._delete_router_ext_gw(context, router_id, router) LOG.error(_LE('Error in updating SNAT for lrouter %s'), lrouter_name)
def sync_routers_and_rports(self, ctx): """Sync Routers between neutron and NB. @param ctx: neutron_lib.context @type ctx: object of type neutron_lib.context.Context @var db_routers: List of Routers from neutron DB @var db_router_ports: List of Router ports from neutron DB @var lrouters: NB dictionary of logical routers and the corresponding logical router ports. vs list-of-acls @var del_lrouters_list: List of Routers that need to be deleted from NB @var del_lrouter_ports_list: List of Router ports that need to be deleted from NB @return: Nothing """ if not config.is_ovn_l3(): LOG.debug("OVN L3 mode is disabled, skipping " "sync routers and router ports") return LOG.debug('OVN-NB Sync Routers and Router ports started @ %s' % str(datetime.now())) db_routers = {} db_extends = {} db_router_ports = {} for router in self.l3_plugin.get_routers(ctx): db_routers[router['id']] = router db_extends[router['id']] = {} db_extends[router['id']]['routes'] = [] db_extends[router['id']]['snats'] = [] db_extends[router['id']]['fips'] = [] if not router.get(l3.EXTERNAL_GW_INFO): continue r_ip, gw_ip = self.l3_plugin.get_external_router_and_gateway_ip( ctx, router) if gw_ip: db_extends[router['id']]['routes'].append({ 'destination': '0.0.0.0/0', 'nexthop': gw_ip }) if r_ip and utils.is_snat_enabled(router): networks = self.l3_plugin._get_v4_network_of_all_router_ports( ctx, router['id']) for network in networks: db_extends[router['id']]['snats'].append({ 'logical_ip': network, 'external_ip': r_ip, 'type': 'snat' }) fips = self.l3_plugin.get_floatingips( ctx, {'router_id': list(db_routers.keys())}) for fip in fips: db_extends[fip['router_id']]['fips'].append({ 'external_ip': fip['floating_ip_address'], 'logical_ip': fip['fixed_ip_address'], 'type': 'dnat_and_snat' }) interfaces = self.l3_plugin._get_sync_interfaces( ctx, db_routers.keys(), [ constants.DEVICE_OWNER_ROUTER_INTF, constants.DEVICE_OWNER_ROUTER_GW ]) for interface in interfaces: db_router_ports[interface['id']] = interface db_router_ports[interface['id']]['networks'] = sorted( self.l3_plugin.get_networks_for_lrouter_port( ctx, interface['fixed_ips'])) lrouters = self.ovn_api.get_all_logical_routers_with_rports() del_lrouters_list = [] del_lrouter_ports_list = [] update_sroutes_list = [] update_lrport_list = [] update_snats_list = [] update_fips_list = [] for lrouter in lrouters: if lrouter['name'] in db_routers: for lrport, lrport_nets in lrouter['ports'].items(): if lrport in db_router_ports: db_lrport_nets = db_router_ports[lrport]['networks'] if db_lrport_nets != sorted(lrport_nets): update_lrport_list.append( (lrouter['name'], db_router_ports[lrport])) del db_router_ports[lrport] else: del_lrouter_ports_list.append({ 'port': lrport, 'lrouter': lrouter['name'] }) if 'routes' in db_routers[lrouter['name']]: db_routes = db_routers[lrouter['name']]['routes'] else: db_routes = [] if 'routes' in db_extends[lrouter['name']]: db_routes.extend(db_extends[lrouter['name']]['routes']) ovn_routes = lrouter['static_routes'] add_routes, del_routes = helpers.diff_list_of_dict( ovn_routes, db_routes) update_sroutes_list.append({ 'id': lrouter['name'], 'add': add_routes, 'del': del_routes }) ovn_fips = lrouter['dnat_and_snats'] db_fips = db_extends[lrouter['name']]['fips'] add_fips, del_fips = helpers.diff_list_of_dict( ovn_fips, db_fips) update_fips_list.append({ 'id': lrouter['name'], 'add': add_fips, 'del': del_fips }) ovn_nats = lrouter['snats'] db_snats = db_extends[lrouter['name']]['snats'] add_snats, del_snats = helpers.diff_list_of_dict( ovn_nats, db_snats) update_snats_list.append({ 'id': lrouter['name'], 'add': add_snats, 'del': del_snats }) del db_routers[lrouter['name']] else: del_lrouters_list.append(lrouter) for r_id, router in db_routers.items(): LOG.warning( _LW("Router found in Neutron but not in " "OVN DB, router id=%s"), router['id']) if self.mode == SYNC_MODE_REPAIR: try: LOG.warning(_LW("Creating the router %s in OVN NB DB"), router['id']) self.l3_plugin.create_lrouter_in_ovn(router) if 'routes' in router: update_sroutes_list.append({ 'id': router['id'], 'add': router['routes'], 'del': [] }) if 'routes' in db_extends[router['id']]: update_sroutes_list.append({ 'id': router['id'], 'add': db_extends[router['id']]['routes'], 'del': [] }) if 'snats' in db_extends[router['id']]: update_snats_list.append({ 'id': router['id'], 'add': db_extends[router['id']]['snats'], 'del': [] }) if 'fips' in db_extends[router['id']]: update_fips_list.append({ 'id': router['id'], 'add': db_extends[router['id']]['fips'], 'del': [] }) except RuntimeError: LOG.warning( _LW("Create router in OVN NB failed for" " router %s"), router['id']) for rp_id, rrport in db_router_ports.items(): LOG.warning( _LW("Router Port found in Neutron but not in OVN " "DB, router port_id=%s"), rrport['id']) if self.mode == SYNC_MODE_REPAIR: try: LOG.warning( _LW("Creating the router port %s in " "OVN NB DB"), rrport['id']) self.l3_plugin.create_lrouter_port_in_ovn( ctx, rrport['device_id'], rrport) except RuntimeError: LOG.warning( _LW("Create router port in OVN " "NB failed for" " router port %s"), rrport['id']) for router_id, rport in update_lrport_list: LOG.warning( _LW("Router Port port_id=%s needs to be updated" " for networks changed"), rport['id']) if self.mode == SYNC_MODE_REPAIR: try: LOG.warning( _LW("Updating networks on router port %s in " "OVN NB DB"), rport['id']) self.l3_plugin.update_lrouter_port_in_ovn( ctx, router_id, rport, rport['networks']) except RuntimeError: LOG.warning( _LW("Update router port networks in OVN " "NB failed for" " router port %s"), rport['id']) with self.ovn_api.transaction(check_error=True) as txn: for lrouter in del_lrouters_list: LOG.warning( _LW("Router found in OVN but not in " "Neutron, router id=%s"), lrouter['name']) if self.mode == SYNC_MODE_REPAIR: LOG.warning(_LW("Deleting the router %s from OVN NB DB"), lrouter['name']) txn.add( self.ovn_api.delete_lrouter( utils.ovn_name(lrouter['name']))) for lrport_info in del_lrouter_ports_list: LOG.warning( _LW("Router Port found in OVN but not in " "Neutron, port_id=%s"), lrport_info['port']) if self.mode == SYNC_MODE_REPAIR: LOG.warning(_LW("Deleting the port %s from OVN NB DB"), lrport_info['port']) txn.add( self.ovn_api.delete_lrouter_port( utils.ovn_lrouter_port_name(lrport_info['port']), utils.ovn_name(lrport_info['lrouter']), if_exists=False)) for sroute in update_sroutes_list: if sroute['add']: LOG.warning( _LW("Router %(id)s static routes %(route)s " "found in Neutron but not in OVN"), { 'id': sroute['id'], 'route': sroute['add'] }) if self.mode == SYNC_MODE_REPAIR: LOG.warning(_LW("Add static routes %s to OVN NB DB"), sroute['add']) for route in sroute['add']: txn.add( self.ovn_api.add_static_route( utils.ovn_name(sroute['id']), ip_prefix=route['destination'], nexthop=route['nexthop'])) if sroute['del']: LOG.warning( _LW("Router %(id)s static routes %(route)s " "found in OVN but not in Neutron"), { 'id': sroute['id'], 'route': sroute['del'] }) if self.mode == SYNC_MODE_REPAIR: LOG.warning( _LW("Delete static routes %s from OVN " "NB DB"), sroute['del']) for route in sroute['del']: txn.add( self.ovn_api.delete_static_route( utils.ovn_name(sroute['id']), ip_prefix=route['destination'], nexthop=route['nexthop'])) for fip in update_fips_list: if fip['del']: LOG.warning( _LW("Router %(id)s floating ips %(fip)s " "found in OVN but not in Neutron"), { 'id': fip['id'], 'fip': fip['del'] }) if self.mode == SYNC_MODE_REPAIR: LOG.warning( _LW("Delete floating ips %s from OVN NB DB"), fip['del']) for nat in fip['del']: txn.add( self.ovn_api.delete_nat_rule_in_lrouter( utils.ovn_name(fip['id']), logical_ip=nat['logical_ip'], external_ip=nat['external_ip'], type='dnat_and_snat')) if fip['add']: LOG.warning( _LW("Router %(id)s floating ips %(fip)s " "found in Neutron but not in OVN"), { 'id': fip['id'], 'fip': fip['add'] }) if self.mode == SYNC_MODE_REPAIR: LOG.warning(_LW("Add floating ips %s to OVN NB DB"), fip['add']) for nat in fip['add']: txn.add( self.ovn_api.add_nat_rule_in_lrouter( utils.ovn_name(fip['id']), logical_ip=nat['logical_ip'], external_ip=nat['external_ip'], type='dnat_and_snat')) for snat in update_snats_list: if snat['del']: LOG.warning( _LW("Router %(id)s snat %(snat)s " "found in OVN but not in Neutron"), { 'id': snat['id'], 'snat': snat['del'] }) if self.mode == SYNC_MODE_REPAIR: LOG.warning(_LW("Delete snats %s from OVN NB DB"), snat['del']) for nat in snat['del']: txn.add( self.ovn_api.delete_nat_rule_in_lrouter( utils.ovn_name(snat['id']), logical_ip=nat['logical_ip'], external_ip=nat['external_ip'], type='snat')) if snat['add']: LOG.warning( _LW("Router %(id)s snat %(snat)s " "found in Neutron but not in OVN"), { 'id': snat['id'], 'snat': snat['add'] }) if self.mode == SYNC_MODE_REPAIR: LOG.warning(_LW("Add snats %s to OVN NB DB"), snat['add']) for nat in snat['add']: txn.add( self.ovn_api.add_nat_rule_in_lrouter( utils.ovn_name(snat['id']), logical_ip=nat['logical_ip'], external_ip=nat['external_ip'], type='snat')) LOG.debug('OVN-NB Sync routers and router ports finished %s' % str(datetime.now()))