예제 #1
0
    def test_get_all_chassis_gateway_bindings(self):
        self._load_nb_db()
        bindings = self.nb_ovn_idl.get_all_chassis_gateway_bindings()
        expected = {
            'host-1': [
                utils.ovn_lrouter_port_name('orp-id-a1'),
                utils.ovn_lrouter_port_name('orp-id-a2')
            ],
            'host-2': [utils.ovn_lrouter_port_name('orp-id-b2')],
            ovn_const.OVN_GATEWAY_INVALID_CHASSIS:
            [utils.ovn_name('orp-id-a3')]
        }
        self.assertItemsEqual(bindings, expected)

        bindings = self.nb_ovn_idl.get_all_chassis_gateway_bindings([])
        self.assertItemsEqual(bindings, expected)

        bindings = self.nb_ovn_idl.get_all_chassis_gateway_bindings(['host-1'])
        expected = {
            'host-1': [
                utils.ovn_lrouter_port_name('orp-id-a1'),
                utils.ovn_lrouter_port_name('orp-id-a2')
            ]
        }
        self.assertItemsEqual(bindings, expected)
예제 #2
0
 def test_get_unhosted_gateways(self):
     self._load_nb_db()
     # Test only host-1 in the valid list
     unhosted_gateways = self.nb_ovn_idl.get_unhosted_gateways(['host-1'])
     expected = {
         utils.ovn_lrouter_port_name('orp-id-b2'): {
             ovn_const.OVN_GATEWAY_CHASSIS_KEY: 'host-2'
         },
         utils.ovn_lrouter_port_name('orp-id-a3'): {
             ovn_const.OVN_GATEWAY_CHASSIS_KEY:
             ovn_const.OVN_GATEWAY_INVALID_CHASSIS
         }
     }
     self.assertItemsEqual(unhosted_gateways, expected)
     # Test both host-1, host-2 in valid list
     unhosted_gateways = self.nb_ovn_idl.get_unhosted_gateways(
         ['host-1', 'host-2'])
     expected = {
         utils.ovn_lrouter_port_name('orp-id-a3'): {
             ovn_const.OVN_GATEWAY_CHASSIS_KEY:
             ovn_const.OVN_GATEWAY_INVALID_CHASSIS
         }
     }
     self.assertItemsEqual(unhosted_gateways, expected)
     # Schedule unhosted_gateways on host-2
     for unhosted_gateway in unhosted_gateways:
         router_row = self._find_ovsdb_fake_row(self.lrp_table, 'name',
                                                unhosted_gateway)
         setattr(router_row, 'options',
                 {ovn_const.OVN_GATEWAY_CHASSIS_KEY: 'host-2'})
     unhosted_gateways = self.nb_ovn_idl.get_unhosted_gateways(
         ['host-1', 'host-2'])
     self.assertItemsEqual(unhosted_gateways, {})
예제 #3
0
    def _disjoin_lrouter_and_gw_lrouter(self, router, transit_net_ports):
        router_id = router['id']
        lrouter_name = utils.ovn_name(router_id)
        gw_lrouter_name = utils.ovn_gateway_router_name(router_id)

        gtrp_ip = transit_net_ports['gtsp']['ip']
        gtrp_name = utils.ovn_lrouter_port_name(utils.ovn_gtsp_name(router_id))
        dtrp_name = utils.ovn_lrouter_port_name(utils.ovn_dtsp_name(router_id))

        lswitch_name = utils.ovn_transit_ls_name(router_id)
        dtsp_name = utils.ovn_dtsp_name(router_id)
        gtsp_name = utils.ovn_gtsp_name(router_id)

        with self._ovn.transaction(check_error=True) as txn:
            # 1. Delete default static route in gateway router
            txn.add(self._ovn.delete_static_route(
                lrouter_name, ip_prefix="0.0.0.0/0", nexthop=gtrp_ip))
            # 2. Delete gtrp port
            txn.add(self._ovn.delete_lrouter_port(gtrp_name, gw_lrouter_name))
            # 3. Delete dtrp port
            txn.add(self._ovn.delete_lrouter_port(dtrp_name, lrouter_name))
            # 4. Delete gtsp port
            txn.add(self._ovn.delete_lswitch_port(gtsp_name, lswitch_name))
            # 5. Delete dtsp port
            txn.add(self._ovn.delete_lswitch_port(dtsp_name, lswitch_name))
            # 6. Delete transit logical switch
            txn.add(self._ovn.delete_lswitch(lswitch_name))
예제 #4
0
    def _join_lrouter_and_gw_lrouter(self, router, transit_net_ports):
        router_id = router['id']
        lswitch_name = utils.ovn_transit_ls_name(router_id)

        dtsp_name = utils.ovn_dtsp_name(router_id)
        dtsp_addresses = transit_net_ports['dtsp']['addresses']

        gtsp_name = utils.ovn_gtsp_name(router_id)
        gtsp_addresses = transit_net_ports['gtsp']['addresses']

        gw_lrouter_name = utils.ovn_gateway_router_name(router_id)
        lrouter_name = utils.ovn_name(router_id)

        gtrp_name = utils.ovn_lrouter_port_name(utils.ovn_gtsp_name(router_id))
        gtrp_mac = transit_net_ports['gtsp']['mac_address']
        gtrp_ip = transit_net_ports['gtsp']['ip']
        cidr = netaddr.IPNetwork(self._l3_admin_net_cidr)
        gtrp_network = "%s/%s" % (gtrp_ip, str(cidr.prefixlen))

        dtrp_name = utils.ovn_lrouter_port_name(utils.ovn_dtsp_name(router_id))
        dtrp_mac = transit_net_ports['dtsp']['mac_address']
        dtrp_ip = transit_net_ports['dtsp']['ip']
        dtrp_network = "%s/%s" % (dtrp_ip, str(cidr.prefixlen))

        with self._ovn.transaction(check_error=True) as txn:
            # 1. Create a transit logical switch
            txn.add(self._ovn.create_lswitch(lswitch_name=lswitch_name))
            # 2. Add dtsp port
            txn.add(self._ovn.create_lswitch_port(lport_name=dtsp_name,
                                                  lswitch_name=lswitch_name,
                                                  addresses=dtsp_addresses,
                                                  enabled='True'))
            # 3. Add gtsp port
            txn.add(self._ovn.create_lswitch_port(lport_name=gtsp_name,
                                                  lswitch_name=lswitch_name,
                                                  addresses=gtsp_addresses,
                                                  enabled='True'))
            # 4. Add dtrp port in logical router
            txn.add(self._ovn.add_lrouter_port(name=dtrp_name,
                                               lrouter=lrouter_name,
                                               mac=dtrp_mac,
                                               networks=dtrp_network))
            txn.add(self._ovn.set_lrouter_port_in_lswitch_port(
                utils.ovn_dtsp_name(router_id), dtrp_name))

            # 5. Add gtrp port in gateway router
            txn.add(self._ovn.add_lrouter_port(name=gtrp_name,
                                               lrouter=gw_lrouter_name,
                                               mac=gtrp_mac,
                                               networks=gtrp_network))
            txn.add(self._ovn.set_lrouter_port_in_lswitch_port(
                utils.ovn_gtsp_name(router_id), gtrp_name))
            # 6. Add default static route in gateway router with nexthop as
            # gtrp ip
            txn.add(self._ovn.add_static_route(lrouter_name,
                                               ip_prefix='0.0.0.0/0',
                                               nexthop=gtrp_ip))
예제 #5
0
 def test_get_gateway_chassis_binding(self):
     self._load_nb_db()
     chassis = self.nb_ovn_idl.get_gateway_chassis_binding(
         utils.ovn_lrouter_port_name('orp-id-a1'))
     self.assertEqual(chassis, 'host-1')
     chassis = self.nb_ovn_idl.get_gateway_chassis_binding(
         utils.ovn_lrouter_port_name('orp-id-b2'))
     self.assertEqual(chassis, 'host-2')
     chassis = self.nb_ovn_idl.get_gateway_chassis_binding(
         utils.ovn_lrouter_port_name('orp-id-a3'))
     self.assertIsNone(chassis)
     chassis = self.nb_ovn_idl.get_gateway_chassis_binding(
         utils.ovn_lrouter_port_name('orp-id-b3'))
     self.assertIsNone(chassis)
     chassis = self.nb_ovn_idl.get_gateway_chassis_binding('bad')
     self.assertIsNone(chassis)
예제 #6
0
    def create_lrouter_port_in_ovn(self, context, router_id, port):
        """Create lrouter port in OVN

        @param router id : LRouter ID for the port that needs to be created
        @param port : LRouter port that needs to be created
        @return: Nothing
        """
        subnet_id = port['fixed_ips'][0]['subnet_id']
        subnet = self._plugin.get_subnet(context, subnet_id)
        lrouter = utils.ovn_name(router_id)
        cidr = netaddr.IPNetwork(subnet['cidr'])
        network = "%s/%s" % (port['fixed_ips'][0]['ip_address'],
                             str(cidr.prefixlen))

        lrouter_port_name = utils.ovn_lrouter_port_name(port['id'])
        with self._ovn.transaction(check_error=True) as txn:
            txn.add(
                self._ovn.add_lrouter_port(name=lrouter_port_name,
                                           lrouter=lrouter,
                                           mac=port['mac_address'],
                                           network=network))

            txn.add(
                self._ovn.set_lrouter_port_in_lswitch_port(
                    port['id'], lrouter_port_name))
예제 #7
0
 def test_get_gateway_chassis_binding(self):
     self._load_nb_db()
     chassis = self.nb_ovn_idl.get_gateway_chassis_binding(
         utils.ovn_lrouter_port_name('orp-id-a1'))
     self.assertEqual(chassis, ['host-1'])
     chassis = self.nb_ovn_idl.get_gateway_chassis_binding(
         utils.ovn_lrouter_port_name('orp-id-b2'))
     self.assertEqual(chassis, ['host-2'])
     chassis = self.nb_ovn_idl.get_gateway_chassis_binding(
         utils.ovn_lrouter_port_name('orp-id-a3'))
     self.assertEqual(chassis, ['neutron-ovn-invalid-chassis'])
     chassis = self.nb_ovn_idl.get_gateway_chassis_binding(
         utils.ovn_lrouter_port_name('orp-id-b3'))
     self.assertEqual(chassis, [])
     chassis = self.nb_ovn_idl.get_gateway_chassis_binding('bad')
     self.assertEqual(chassis, [])
예제 #8
0
    def _test_router_port_ipv6_ra_configs_helper(
            self, cidr='aef0::/64', ip_version=6,
            address_mode=n_consts.IPV6_SLAAC,):
        router1 = self._create_router('router1')
        n1 = self._make_network(self.fmt, 'n1', True)
        if ip_version == 6:
            kwargs = {'ip_version': 6, 'cidr': 'aef0::/64',
                      'ipv6_address_mode': address_mode,
                      'ipv6_ra_mode': address_mode}
        else:
            kwargs = {'ip_version': 4, 'cidr': '10.0.0.0/24'}

        res = self._create_subnet(self.fmt, n1['network']['id'],
                                  **kwargs)

        n1_s1 = self.deserialize(self.fmt, res)
        n1_s1_id = n1_s1['subnet']['id']
        router_iface_info = self.l3_plugin.add_router_interface(
            self.context, router1['id'], {'subnet_id': n1_s1_id})

        lrp_name = ovn_utils.ovn_lrouter_port_name(
            router_iface_info['port_id'])
        if ip_version == 6:
            expected_ra_configs = {
                'address_mode': ovn_utils.get_ovn_ipv6_address_mode(
                    address_mode),
                'send_periodic': 'true',
                'mtu': '1450'}
        else:
            expected_ra_configs = {}
        self._validate_router_ipv6_ra_configs(lrp_name, expected_ra_configs)
예제 #9
0
    def update_lrouter_port_in_ovn(self,
                                   context,
                                   router_id,
                                   port,
                                   networks=None):
        """Update lrouter port in OVN

        @param router id : LRouter ID for the port that needs to be updated
        @param port : LRouter port that needs to be updated
        @param networks : networks needs to be updated for LRouter port
        @return: Nothing
        """
        networks = networks or self.get_networks_for_lrouter_port(
            context, port['fixed_ips'])

        lrouter_port_name = utils.ovn_lrouter_port_name(port['id'])
        update = {'networks': networks}
        with self._ovn.transaction(check_error=True) as txn:
            txn.add(
                self._ovn.update_lrouter_port(name=lrouter_port_name,
                                              if_exists=False,
                                              **update))
            txn.add(
                self._ovn.set_lrouter_port_in_lswitch_port(
                    port['id'], lrouter_port_name))
예제 #10
0
 def get_lrouter_port(self, lrp_name):
     # TODO(mangelajo): Implement lrp_get() ovsdbapp and use from here
     if uuidutils.is_uuid_like(lrp_name):
         lrp_name = utils.ovn_lrouter_port_name(lrp_name)
     lrp = self.db_find_rows('Logical_Router_Port', ('name', '=', lrp_name))
     result = lrp.execute(check_error=True)
     return result[0] if result else None
예제 #11
0
    def create_lrouter_port_in_ovn(self, context, router_id, port):
        """Create lrouter port in OVN

         @param router_id : LRouter ID for the port that needs to be created
         @param port : LRouter port that needs to be created
         @return: Nothing
         """
        lrouter = utils.ovn_name(router_id)
        networks = self.get_networks_for_lrouter_port(context,
                                                      port['fixed_ips'])

        lrouter_port_name = utils.ovn_lrouter_port_name(port['id'])
        is_gw_port = n_const.DEVICE_OWNER_ROUTER_GW == port.get('device_owner')
        columns = {}
        if is_gw_port:
            selected_chassis = self.scheduler.select(self._ovn, self._sb_ovn,
                                                     lrouter_port_name)
            columns['options'] = {
                ovn_const.OVN_GATEWAY_CHASSIS_KEY: selected_chassis
            }
        with self._ovn.transaction(check_error=True) as txn:
            txn.add(
                self._ovn.add_lrouter_port(name=lrouter_port_name,
                                           lrouter=lrouter,
                                           mac=port['mac_address'],
                                           networks=networks,
                                           **columns))
            txn.add(
                self._ovn.set_lrouter_port_in_lswitch_port(
                    port['id'], lrouter_port_name))
예제 #12
0
    def add_router_interface(self, context, router_id, interface_info):
        router_interface_info = super(OVNPlugin, self).add_router_interface(
            context, router_id, interface_info)

        if not config.is_ovn_l3():
            LOG.debug("OVN L3 mode is disabled, skipping "
                      "add_router_interface")
            return router_interface_info

        port = self.get_port(context, router_interface_info['port_id'])
        subnet_id = port['fixed_ips'][0]['subnet_id']
        subnet = self.get_subnet(context, subnet_id)
        lrouter = utils.ovn_name(router_id)
        cidr = netaddr.IPNetwork(subnet['cidr'])
        network = "%s/%s" % (port['fixed_ips'][0]['ip_address'],
                             str(cidr.prefixlen))

        lrouter_port_name = utils.ovn_lrouter_port_name(port['id'])
        with self._ovn.transaction(check_error=True) as txn:
            txn.add(self._ovn.add_lrouter_port(name=lrouter_port_name,
                                               lrouter=lrouter,
                                               mac=port['mac_address'],
                                               network=network))

            txn.add(self._ovn.set_lrouter_port_in_lport(port['id'],
                                                        lrouter_port_name))
        return router_interface_info
예제 #13
0
    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:
            transit_net_ports = self._get_transit_network_ports()
            nexthop = transit_net_ports['dtsp']['ip']
            gw_lrouter_name = utils.ovn_gateway_router_name(router_id)
            if self._is_snat_enabled(router):
                self._update_snat_and_static_routes_for_networks(
                    context, router, networks=[cidr], nexthop=nexthop,
                    enable_snat=True, update_static_routes=True)
            else:
                route = {'destination': cidr, 'nexthop': nexthop}
                self._update_lrouter_routes(
                    context, router_id, add=[route], remove=[],
                    lrouter_name=gw_lrouter_name)
        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
예제 #14
0
 def remove_router_interface(self, context, router_id, interface_info):
     router_interface_info = \
         super(OVNL3RouterPlugin, self).remove_router_interface(
             context, router_id, interface_info)
     port_id = router_interface_info['port_id']
     self._ovn.delete_lrouter_port(utils.ovn_lrouter_port_name(port_id),
                                   utils.ovn_name(router_id),
                                   if_exists=False
                                   ).execute(check_error=True)
     return router_interface_info
예제 #15
0
    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
예제 #16
0
    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
예제 #17
0
    def run_idl(self, txn):
        # TODO(lucasagomes): Remove this check after OVS 2.8.2 is tagged
        # (prior to that, the external_ids column didn't exist in this
        # table).
        if not self.api.is_col_present('Logical_Router_Static_Route',
                                       'external_ids'):
            return

        try:
            lrouter = idlutils.row_by_value(self.api.idl, 'Logical_Router',
                                            'name', self.lrouter)
        except idlutils.RowNotFound:
            if self.if_exists:
                return
            msg = _("Logical Router %s does not exist") % self.lrouter
            raise RuntimeError(msg)

        lrouter.verify('static_routes')
        static_routes = getattr(lrouter, 'static_routes', [])
        for route in static_routes:
            external_ids = getattr(route, 'external_ids', {})
            if ovn_const.OVN_ROUTER_IS_EXT_GW in external_ids:
                _delvalue_from_list(lrouter, 'static_routes', route)
                route.delete()
                break

        lrouter.verify('nat')
        nats = getattr(lrouter, 'nat', [])
        for nat in nats:
            if nat.type != 'snat':
                continue
            _delvalue_from_list(lrouter, 'nat', nat)
            nat.delete()

        lrouter_ext_ids = getattr(lrouter, 'external_ids', {})
        gw_port_id = lrouter_ext_ids.get(ovn_const.OVN_GW_PORT_EXT_ID_KEY)
        if not gw_port_id:
            return

        try:
            lrouter_port = idlutils.row_by_value(
                self.api.idl, 'Logical_Router_Port', 'name',
                utils.ovn_lrouter_port_name(gw_port_id))
        except idlutils.RowNotFound:
            return

        _delvalue_from_list(lrouter, 'ports', lrouter_port)
예제 #18
0
 def remove_router_interface(self, context, router_id, interface_info):
     router_interface_info = \
         super(OVNL3RouterPlugin, self).remove_router_interface(
             context, router_id, interface_info)
     port_id = router_interface_info['port_id']
     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)
     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)
     return router_interface_info
예제 #19
0
 def remove_router_interface(self, context, router_id, interface_info):
     router_interface_info = \
         super(OVNL3RouterPlugin, self).remove_router_interface(
             context, router_id, interface_info)
     port_id = router_interface_info['port_id']
     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)
     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)
     return router_interface_info
예제 #20
0
    def update_lrouter_port_in_ovn(self, context, router_id, port):
        """Update lrouter port in OVN

        @param router id : LRouter ID for the port that needs to be updated
        @param port : LRouter port that needs to be updated
        @return: Nothing
        """
        lrouter = utils.ovn_name(router_id)
        networks = self.get_networks_for_lrouter_port(context,
                                                      port['fixed_ips'])

        lrouter_port_name = utils.ovn_lrouter_port_name(port['id'])
        update = {'networks': networks}
        with self._ovn.transaction(check_error=True) as txn:
            txn.add(self._ovn.update_lrouter_port(name=lrouter_port_name,
                                                  lrouter=lrouter,
                                                  if_exists=False,
                                                  **update))
            txn.add(self._ovn.set_lrouter_port_in_lswitch_port(
                    port['id'], lrouter_port_name))
예제 #21
0
    def create_lrouter_port_in_ovn(self, context, router_id, port):
        """Create lrouter port in OVN

        @param router id : LRouter ID for the port that needs to be created
        @param port : LRouter port that needs to be created
        @return: Nothing
        """
        lrouter = utils.ovn_name(router_id)
        networks = self.get_networks_for_lrouter_port(context,
                                                      port['fixed_ips'])

        lrouter_port_name = utils.ovn_lrouter_port_name(port['id'])
        with self._ovn.transaction(check_error=True) as txn:
            txn.add(self._ovn.add_lrouter_port(name=lrouter_port_name,
                                               lrouter=lrouter,
                                               mac=port['mac_address'],
                                               networks=networks))

            txn.add(self._ovn.set_lrouter_port_in_lswitch_port(
                    port['id'], lrouter_port_name))
예제 #22
0
    def _delete_router_ext_gw(self, context, router_id, router,
                              networks=None):
        gw_port_id = router['gw_port_id']
        gw_lrouter_name = utils.ovn_name(router_id)
        ext_gw_ip = self._get_external_gateway_ip(context, router)
        router_ip = self._get_router_ip(context, router)
        # Only get networks when networks is None
        networks = self._get_v4_network_of_all_router_ports(
            context, router_id) if networks is None else networks

        with self._ovn.transaction(check_error=True) as txn:
            txn.add(self._ovn.delete_static_route(gw_lrouter_name,
                                                  ip_prefix='0.0.0.0/0',
                                                  nexthop=ext_gw_ip))
            txn.add(self._ovn.delete_lrouter_port(
                utils.ovn_lrouter_port_name(gw_port_id),
                gw_lrouter_name))
            for network in networks:
                txn.add(self._ovn.delete_nat_rule_in_lrouter(
                    gw_lrouter_name, type='snat', logical_ip=network,
                    external_ip=router_ip))
예제 #23
0
    def create_lrouter_port_in_ovn(self, context, router_id, port):
        """Create lrouter port in OVN

        @param router id : LRouter ID for the port that needs to be created
        @param port : LRouter port that needs to be created
        @return: Nothing
        """
        lrouter = utils.ovn_name(router_id)
        networks = self.get_networks_for_lrouter_port(context,
                                                      port['fixed_ips'])

        lrouter_port_name = utils.ovn_lrouter_port_name(port['id'])
        with self._ovn.transaction(check_error=True) as txn:
            txn.add(
                self._ovn.add_lrouter_port(name=lrouter_port_name,
                                           lrouter=lrouter,
                                           mac=port['mac_address'],
                                           networks=networks))

            txn.add(
                self._ovn.set_lrouter_port_in_lswitch_port(
                    port['id'], lrouter_port_name))
예제 #24
0
    def remove_router_interface(self, context, router_id, interface_info):
        if not config.is_ovn_l3():
            LOG.debug("OVN L3 mode is disabled, skipping "
                      "remove_router_interface")
            return super(OVNPlugin, self).remove_router_interface(
                context, router_id, interface_info)
        # TODO(chandrav)
        # Need to rework this code to get the port_id when the incoming request
        # contains only the subnet_id. Also need to figure out if OVN needs to
        # care about multiple prefix subnets on a single router interface.
        # This code is duplicated from neutron. Probably a better thing to do
        # is to handle everything in the plugin and just call delete_port
        # update_port.
        port_id = None
        if 'port_id' in interface_info:
            port_id = interface_info['port_id']
        elif 'subnet_id' in interface_info:
            subnet_id = interface_info['subnet_id']
            subnet = self.get_subnet(context, subnet_id)
            device_filter = {'device_id': [router_id],
                             'device_owner': [const.DEVICE_OWNER_ROUTER_INTF],
                             'network_id': [subnet['network_id']]}
            ports = super(OVNPlugin, self).get_ports(context,
                                                     filters=device_filter)
            for p in ports:
                port_subnets = [fip['subnet_id'] for fip in p['fixed_ips']]
                if subnet_id in port_subnets and len(port_subnets) == 1:
                    port_id = p['id']
                    break

        router_interface_info = super(OVNPlugin, self).remove_router_interface(
            context, router_id, interface_info)

        if port_id is not None:
            self._ovn.delete_lrouter_port(utils.ovn_lrouter_port_name(port_id),
                                          utils.ovn_name(router_id),
                                          if_exists=False
                                          ).execute(check_error=True)
        return router_interface_info
예제 #25
0
 def _delete_router_ext_gw(self, context, router_id, router,
                           transit_net_ports=None, cleanup=None):
     transit_net_ports = transit_net_ports or \
         self._get_transit_network_ports()
     cleanup = cleanup or []
     gw_port_id = router['gw_port_id']
     gw_lrouter_name = utils.ovn_gateway_router_name(router_id)
     ext_gw_ip = self._get_external_gateway_ip(context, router)
     if 'join' in cleanup or not cleanup:
         self._disjoin_lrouter_and_gw_lrouter(router, transit_net_ports)
     with self._ovn.transaction(check_error=True) as txn:
         if 'ext_gw_ip_nexthop' in cleanup or not cleanup:
             txn.add(self._ovn.delete_static_route(gw_lrouter_name,
                                                   ip_prefix='0.0.0.0/0',
                                                   nexthop=ext_gw_ip))
         if 'ext_gw_port' in cleanup or not cleanup:
             txn.add(self._ovn.delete_lrouter_port(
                 utils.ovn_lrouter_port_name(gw_port_id),
                 gw_lrouter_name))
     if 'gw_router' in cleanup or not cleanup:
         self._delete_lrouter_in_ovn(router_id, is_gateway_router=True)
     self._check_and_delete_l3_admin_net(context)
예제 #26
0
    def create_lrouter_port_in_ovn(self, context, router_id, port):
        """Create lrouter port in OVN

        @param router id : LRouter ID for the port that needs to be created
        @param port : LRouter port that needs to be created
        @return: Nothing
        """
        subnet_id = port['fixed_ips'][0]['subnet_id']
        subnet = self._plugin.get_subnet(context, subnet_id)
        lrouter = utils.ovn_name(router_id)
        cidr = netaddr.IPNetwork(subnet['cidr'])
        network = "%s/%s" % (port['fixed_ips'][0]['ip_address'],
                             str(cidr.prefixlen))

        lrouter_port_name = utils.ovn_lrouter_port_name(port['id'])
        with self._ovn.transaction(check_error=True) as txn:
            txn.add(self._ovn.add_lrouter_port(name=lrouter_port_name,
                                               lrouter=lrouter,
                                               mac=port['mac_address'],
                                               network=network))

            txn.add(self._ovn.set_lrouter_port_in_lport(port['id'],
                                                        lrouter_port_name))
예제 #27
0
class TestNBImplIdlOvn(TestDBImplIdlOvn):

    fake_set = {
        'lswitches': [{
            'name': utils.ovn_name('ls-id-1'),
            'external_ids': {
                ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY: 'ls-name-1'
            }
        }, {
            'name': utils.ovn_name('ls-id-2'),
            'external_ids': {
                ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY: 'ls-name-2'
            }
        }, {
            'name': utils.ovn_name('ls-id-3'),
            'external_ids': {
                ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY: 'ls-name-3'
            }
        }, {
            'name': 'ls-id-4',
            'external_ids': {
                'not-neutron:network_name': 'ls-name-4'
            }
        }, {
            'name': utils.ovn_name('ls-id-5'),
            'external_ids': {
                ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY: 'ls-name-5'
            }
        }],
        'lswitch_ports': [{
            'name': 'lsp-id-11',
            'addresses': ['10.0.1.1'],
            'external_ids': {
                ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-name-11'
            }
        }, {
            'name': 'lsp-id-12',
            'addresses': ['10.0.1.2'],
            'external_ids': {
                ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-name-12'
            }
        }, {
            'name': 'lsp-rp-id-1',
            'addresses': ['10.0.1.254'],
            'external_ids': {
                ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-rp-name-1'
            },
            'options': {
                'router-port': utils.ovn_lrouter_port_name('orp-id-a1')
            }
        }, {
            'name': 'provnet-ls-id-1',
            'addresses': ['unknown'],
            'external_ids': {},
            'options': {
                'network_name': 'physnet1'
            }
        }, {
            'name': 'lsp-id-21',
            'addresses': ['10.0.2.1'],
            'external_ids': {
                ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-name-21'
            }
        }, {
            'name': 'lsp-id-22',
            'addresses': ['10.0.2.2'],
            'external_ids': {}
        }, {
            'name': 'lsp-id-23',
            'addresses': ['10.0.2.3'],
            'external_ids': {
                'not-neutron:port_name': 'lsp-name-23'
            }
        }, {
            'name': 'lsp-rp-id-2',
            'addresses': ['10.0.2.254'],
            'external_ids': {
                ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-rp-name-2'
            },
            'options': {
                'router-port': utils.ovn_lrouter_port_name('orp-id-a2')
            }
        }, {
            'name': 'provnet-ls-id-2',
            'addresses': ['unknown'],
            'external_ids': {},
            'options': {
                'network_name': 'physnet2'
            }
        }, {
            'name': 'lsp-id-31',
            'addresses': ['10.0.3.1'],
            'external_ids': {
                ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-name-31'
            }
        }, {
            'name': 'lsp-id-32',
            'addresses': ['10.0.3.2'],
            'external_ids': {
                ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-name-32'
            }
        }, {
            'name': 'lsp-rp-id-3',
            'addresses': ['10.0.3.254'],
            'external_ids': {
                ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-rp-name-3'
            },
            'options': {
                'router-port': utils.ovn_lrouter_port_name('orp-id-a3')
            }
        }, {
            'name': 'lsp-vpn-id-3',
            'addresses': ['10.0.3.253'],
            'external_ids': {
                ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-vpn-name-3'
            }
        }, {
            'name': 'lsp-id-41',
            'addresses': ['20.0.1.1'],
            'external_ids': {
                'not-neutron:port_name': 'lsp-name-41'
            }
        }, {
            'name': 'lsp-rp-id-4',
            'addresses': ['20.0.1.254'],
            'external_ids': {},
            'options': {
                'router-port': 'xrp-id-b1'
            }
        }, {
            'name': 'lsp-id-51',
            'addresses': ['20.0.2.1'],
            'external_ids': {
                ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-name-51'
            }
        }, {
            'name': 'lsp-id-52',
            'addresses': ['20.0.2.2'],
            'external_ids': {
                ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-name-52'
            }
        }, {
            'name': 'lsp-rp-id-5',
            'addresses': ['20.0.2.254'],
            'external_ids': {
                ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-rp-name-5'
            },
            'options': {
                'router-port': utils.ovn_lrouter_port_name('orp-id-b2')
            }
        }, {
            'name': 'lsp-vpn-id-5',
            'addresses': ['20.0.2.253'],
            'external_ids': {
                ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-vpn-name-5'
            }
        }],
        'lrouters': [{
            'name': utils.ovn_name('lr-id-a'),
            'external_ids': {
                ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY: 'lr-name-a'
            }
        }, {
            'name': utils.ovn_name('lr-id-b'),
            'external_ids': {
                ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY: 'lr-name-b'
            }
        }, {
            'name': utils.ovn_name('lr-id-c'),
            'external_ids': {
                ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY: 'lr-name-c'
            }
        }, {
            'name': utils.ovn_name('lr-id-d'),
            'external_ids': {
                ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY: 'lr-name-d'
            }
        }, {
            'name': utils.ovn_name('lr-id-e'),
            'external_ids': {
                ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY: 'lr-name-e'
            }
        }],
        'lrouter_ports': [{
            'name': utils.ovn_lrouter_port_name('orp-id-a1'),
            'external_ids': {},
            'networks': ['10.0.1.0/24'],
            'options': {
                ovn_const.OVN_GATEWAY_CHASSIS_KEY: 'host-1'
            }
        }, {
            'name': utils.ovn_lrouter_port_name('orp-id-a2'),
            'external_ids': {},
            'networks': ['10.0.2.0/24'],
            'options': {
                ovn_const.OVN_GATEWAY_CHASSIS_KEY: 'host-1'
            }
        }, {
            'name': utils.ovn_lrouter_port_name('orp-id-a3'),
            'external_ids': {},
            'networks': ['10.0.3.0/24'],
            'options': {
                ovn_const.OVN_GATEWAY_CHASSIS_KEY:
                ovn_const.OVN_GATEWAY_INVALID_CHASSIS
            }
        }, {
            'name': 'xrp-id-b1',
            'external_ids': {},
            'networks': ['20.0.1.0/24']
        }, {
            'name': utils.ovn_lrouter_port_name('orp-id-b2'),
            'external_ids': {},
            'networks': ['20.0.2.0/24'],
            'options': {
                ovn_const.OVN_GATEWAY_CHASSIS_KEY: 'host-2'
            }
        }, {
            'name': utils.ovn_lrouter_port_name('orp-id-b3'),
            'external_ids': {},
            'networks': ['20.0.3.0/24'],
            'options': {}
        }],
        'static_routes': [{
            'ip_prefix': '20.0.0.0/16',
            'nexthop': '10.0.3.253'
        }, {
            'ip_prefix': '10.0.0.0/16',
            'nexthop': '20.0.2.253'
        }],
        'nats': [{
            'external_ip': '10.0.3.1',
            'logical_ip': '20.0.0.0/16',
            'type': 'snat'
        }, {
            'external_ip': '20.0.2.1',
            'logical_ip': '10.0.0.0/24',
            'type': 'snat'
        }, {
            'external_ip': '20.0.2.4',
            'logical_ip': '10.0.0.4',
            'type': 'dnat_and_snat',
            'external_mac': [],
            'logical_port': []
        }, {
            'external_ip': '20.0.2.5',
            'logical_ip': '10.0.0.5',
            'type': 'dnat_and_snat',
            'external_mac': ['00:01:02:03:04:05'],
            'logical_port': ['lsp-id-001']
        }],
        'acls': [{
            'unit_test_id': 1,
            'action': 'allow-related',
            'direction': 'from-lport',
            'external_ids': {
                'neutron:lport': 'lsp-id-11'
            },
            'match': 'inport == "lsp-id-11" && ip4'
        }, {
            'unit_test_id': 2,
            'action': 'allow-related',
            'direction': 'to-lport',
            'external_ids': {
                'neutron:lport': 'lsp-id-11'
            },
            'match': 'outport == "lsp-id-11" && ip4.src == $as_ip4_id_1'
        }, {
            'unit_test_id': 3,
            'action': 'allow-related',
            'direction': 'from-lport',
            'external_ids': {
                'neutron:lport': 'lsp-id-12'
            },
            'match': 'inport == "lsp-id-12" && ip4'
        }, {
            'unit_test_id': 4,
            'action': 'allow-related',
            'direction': 'to-lport',
            'external_ids': {
                'neutron:lport': 'lsp-id-12'
            },
            'match': 'outport == "lsp-id-12" && ip4.src == $as_ip4_id_1'
        }, {
            'unit_test_id': 5,
            'action': 'allow-related',
            'direction': 'from-lport',
            'external_ids': {
                'neutron:lport': 'lsp-id-21'
            },
            'match': 'inport == "lsp-id-21" && ip4'
        }, {
            'unit_test_id': 6,
            'action': 'allow-related',
            'direction': 'to-lport',
            'external_ids': {
                'neutron:lport': 'lsp-id-21'
            },
            'match': 'outport == "lsp-id-21" && ip4.src == $as_ip4_id_2'
        }, {
            'unit_test_id': 7,
            'action': 'allow-related',
            'direction': 'from-lport',
            'external_ids': {
                'neutron:lport': 'lsp-id-41'
            },
            'match': 'inport == "lsp-id-41" && ip4'
        }, {
            'unit_test_id': 8,
            'action': 'allow-related',
            'direction': 'to-lport',
            'external_ids': {
                'neutron:lport': 'lsp-id-41'
            },
            'match': 'outport == "lsp-id-41" && ip4.src == $as_ip4_id_4'
        }, {
            'unit_test_id': 9,
            'action': 'allow-related',
            'direction': 'from-lport',
            'external_ids': {
                'neutron:lport': 'lsp-id-52'
            },
            'match': 'inport == "lsp-id-52" && ip4'
        }, {
            'unit_test_id': 10,
            'action': 'allow-related',
            'direction': 'to-lport',
            'external_ids': {
                'neutron:lport': 'lsp-id-52'
            },
            'match': 'outport == "lsp-id-52" && ip4.src == $as_ip4_id_5'
        }],
        'dhcp_options': [{
            'cidr': '10.0.1.0/24',
            'external_ids': {
                'subnet_id': 'subnet-id-10-0-1-0'
            },
            'options': {
                'mtu': '1442',
                'router': '10.0.1.254'
            }
        }, {
            'cidr': '10.0.2.0/24',
            'external_ids': {
                'subnet_id': 'subnet-id-10-0-2-0'
            },
            'options': {
                'mtu': '1442',
                'router': '10.0.2.254'
            }
        }, {
            'cidr': '10.0.1.0/26',
            'external_ids': {
                'subnet_id': 'subnet-id-10-0-1-0',
                'port_id': 'lsp-vpn-id-3'
            },
            'options': {
                'mtu': '1442',
                'router': '10.0.1.1'
            }
        }, {
            'cidr': '20.0.1.0/24',
            'external_ids': {
                'subnet_id': 'subnet-id-20-0-1-0'
            },
            'options': {
                'mtu': '1442',
                'router': '20.0.1.254'
            }
        }, {
            'cidr': '20.0.2.0/24',
            'external_ids': {
                'subnet_id': 'subnet-id-20-0-2-0',
                'port_id': 'lsp-vpn-id-5'
            },
            'options': {
                'mtu': '1442',
                'router': '20.0.2.254'
            }
        }, {
            'cidr': '2001:dba::/64',
            'external_ids': {
                'subnet_id': 'subnet-id-2001-dba',
                'port_id': 'lsp-vpn-id-5'
            },
            'options': {
                'server_id': '12:34:56:78:9a:bc'
            }
        }, {
            'cidr': '30.0.1.0/24',
            'external_ids': {
                'port_id': 'port-id-30-0-1-0'
            },
            'options': {
                'mtu': '1442',
                'router': '30.0.2.254'
            }
        }, {
            'cidr': '30.0.2.0/24',
            'external_ids': {},
            'options': {}
        }],
        'address_sets': [
            {
                'name': '$as_ip4_id_1',
                'addresses': ['10.0.1.1', '10.0.1.2'],
                'external_ids': {
                    ovn_const.OVN_SG_EXT_ID_KEY: 'id_1'
                }
            },
            {
                'name': '$as_ip4_id_2',
                'addresses': ['10.0.2.1'],
                'external_ids': {
                    ovn_const.OVN_SG_EXT_ID_KEY: 'id_2'
                }
            },
            {
                'name': '$as_ip4_id_3',
                'addresses': ['10.0.3.1', '10.0.3.2'],
                'external_ids': {
                    ovn_const.OVN_SG_EXT_ID_KEY: 'id_3'
                }
            },
            {
                'name': '$as_ip4_id_4',
                'addresses': ['20.0.1.1', '20.0.1.2'],
                'external_ids': {}
            },
            {
                'name': '$as_ip4_id_5',
                'addresses': ['20.0.2.1', '20.0.2.2'],
                'external_ids': {
                    ovn_const.OVN_SG_EXT_ID_KEY: 'id_5'
                }
            },
        ]
    }

    fake_associations = {
        'lstolsp': {
            utils.ovn_name('ls-id-1'):
            ['lsp-id-11', 'lsp-id-12', 'lsp-rp-id-1', 'provnet-ls-id-1'],
            utils.ovn_name('ls-id-2'): [
                'lsp-id-21', 'lsp-id-22', 'lsp-id-23', 'lsp-rp-id-2',
                'provnet-ls-id-2'
            ],
            utils.ovn_name('ls-id-3'):
            ['lsp-id-31', 'lsp-id-32', 'lsp-rp-id-3', 'lsp-vpn-id-3'],
            'ls-id-4': ['lsp-id-41', 'lsp-rp-id-4'],
            utils.ovn_name('ls-id-5'):
            ['lsp-id-51', 'lsp-id-52', 'lsp-rp-id-5', 'lsp-vpn-id-5']
        },
        'lrtolrp': {
            utils.ovn_name('lr-id-a'): [
                utils.ovn_lrouter_port_name('orp-id-a1'),
                utils.ovn_lrouter_port_name('orp-id-a2'),
                utils.ovn_lrouter_port_name('orp-id-a3')
            ],
            utils.ovn_name('lr-id-b'):
            ['xrp-id-b1',
             utils.ovn_lrouter_port_name('orp-id-b2')]
        },
        'lrtosroute': {
            utils.ovn_name('lr-id-a'): ['20.0.0.0/16'],
            utils.ovn_name('lr-id-b'): ['10.0.0.0/16']
        },
        'lrtonat': {
            utils.ovn_name('lr-id-a'): ['10.0.3.1'],
            utils.ovn_name('lr-id-b'): ['20.0.2.1', '20.0.2.4', '20.0.2.5'],
        },
        'lstoacl': {
            utils.ovn_name('ls-id-1'): [1, 2, 3, 4],
            utils.ovn_name('ls-id-2'): [5, 6],
            'ls-id-4': [7, 8],
            utils.ovn_name('ls-id-5'): [9, 10]
        }
    }

    def setUp(self):
        super(TestNBImplIdlOvn, self).setUp()

        self.lswitch_table = fakes.FakeOvsdbTable.create_one_ovsdb_table()
        self.lsp_table = fakes.FakeOvsdbTable.create_one_ovsdb_table()
        self.lrouter_table = fakes.FakeOvsdbTable.create_one_ovsdb_table()
        self.lrp_table = fakes.FakeOvsdbTable.create_one_ovsdb_table()
        self.sroute_table = fakes.FakeOvsdbTable.create_one_ovsdb_table()
        self.nat_table = fakes.FakeOvsdbTable.create_one_ovsdb_table()
        self.acl_table = fakes.FakeOvsdbTable.create_one_ovsdb_table()
        self.dhcp_table = fakes.FakeOvsdbTable.create_one_ovsdb_table()
        self.address_set_table = fakes.FakeOvsdbTable.create_one_ovsdb_table()

        self._tables = {}
        self._tables['Logical_Switch'] = self.lswitch_table
        self._tables['Logical_Switch_Port'] = self.lsp_table
        self._tables['Logical_Router'] = self.lrouter_table
        self._tables['Logical_Router_Port'] = self.lrp_table
        self._tables['Logical_Router_Static_Route'] = self.sroute_table
        self._tables['ACL'] = self.acl_table
        self._tables['DHCP_Options'] = self.dhcp_table
        self._tables['Address_Set'] = self.address_set_table

        with mock.patch.object(impl_idl_ovn,
                               'get_connection',
                               return_value=mock.Mock()):
            impl_idl_ovn.OvsdbNbOvnIdl.ovsdb_connection = None
            self.nb_ovn_idl = impl_idl_ovn.OvsdbNbOvnIdl(mock.Mock())

        self.nb_ovn_idl.idl.tables = self._tables

    def _load_nb_db(self):
        # Load Switches and Switch Ports
        fake_lswitches = TestNBImplIdlOvn.fake_set['lswitches']
        self._load_ovsdb_fake_rows(self.lswitch_table, fake_lswitches)
        fake_lsps = TestNBImplIdlOvn.fake_set['lswitch_ports']
        self._load_ovsdb_fake_rows(self.lsp_table, fake_lsps)
        # Associate switches and ports
        self._construct_ovsdb_references(
            TestNBImplIdlOvn.fake_associations['lstolsp'], self.lswitch_table,
            self.lsp_table, 'name', 'name', 'ports')
        # Load Routers and Router Ports
        fake_lrouters = TestNBImplIdlOvn.fake_set['lrouters']
        self._load_ovsdb_fake_rows(self.lrouter_table, fake_lrouters)
        fake_lrps = TestNBImplIdlOvn.fake_set['lrouter_ports']
        self._load_ovsdb_fake_rows(self.lrp_table, fake_lrps)
        # Associate routers and router ports
        self._construct_ovsdb_references(
            TestNBImplIdlOvn.fake_associations['lrtolrp'], self.lrouter_table,
            self.lrp_table, 'name', 'name', 'ports')
        # Load static routes
        fake_sroutes = TestNBImplIdlOvn.fake_set['static_routes']
        self._load_ovsdb_fake_rows(self.sroute_table, fake_sroutes)
        # Associate routers and static routes
        self._construct_ovsdb_references(
            TestNBImplIdlOvn.fake_associations['lrtosroute'],
            self.lrouter_table, self.sroute_table, 'name', 'ip_prefix',
            'static_routes')
        # Load nats
        fake_nats = TestNBImplIdlOvn.fake_set['nats']
        self._load_ovsdb_fake_rows(self.nat_table, fake_nats)
        # Associate routers and nats
        self._construct_ovsdb_references(
            TestNBImplIdlOvn.fake_associations['lrtonat'], self.lrouter_table,
            self.nat_table, 'name', 'external_ip', 'nat')
        # Load acls
        fake_acls = TestNBImplIdlOvn.fake_set['acls']
        self._load_ovsdb_fake_rows(self.acl_table, fake_acls)
        # Associate switches and acls
        self._construct_ovsdb_references(
            TestNBImplIdlOvn.fake_associations['lstoacl'], self.lswitch_table,
            self.acl_table, 'name', 'unit_test_id', 'acls')
        # Load dhcp options
        fake_dhcp_options = TestNBImplIdlOvn.fake_set['dhcp_options']
        self._load_ovsdb_fake_rows(self.dhcp_table, fake_dhcp_options)
        # Load address sets
        fake_address_sets = TestNBImplIdlOvn.fake_set['address_sets']
        self._load_ovsdb_fake_rows(self.address_set_table, fake_address_sets)

    @mock.patch.object(impl_idl_ovn.OvsdbNbOvnIdl, 'ovsdb_connection', None)
    @mock.patch.object(impl_idl_ovn, 'get_connection', mock.Mock())
    def test_setting_ovsdb_probe_timeout_default_value(self):
        inst = impl_idl_ovn.OvsdbNbOvnIdl(mock.Mock())
        inst.idl._session.reconnect.set_probe_interval.assert_called_with(
            60000)

    @mock.patch.object(impl_idl_ovn.OvsdbNbOvnIdl, 'ovsdb_connection', None)
    @mock.patch.object(impl_idl_ovn, 'get_connection', mock.Mock())
    @mock.patch.object(config, 'get_ovn_ovsdb_probe_interval')
    def test_setting_ovsdb_probe_timeout(self, mock_get_probe_interval):
        mock_get_probe_interval.return_value = 5000
        inst = impl_idl_ovn.OvsdbNbOvnIdl(mock.Mock())
        inst.idl._session.reconnect.set_probe_interval.assert_called_with(5000)

    def test_get_all_logical_switches_with_ports(self):
        # Test empty
        mapping = self.nb_ovn_idl.get_all_logical_switches_with_ports()
        self.assertItemsEqual(mapping, {})
        # Test loaded values
        self._load_nb_db()
        mapping = self.nb_ovn_idl.get_all_logical_switches_with_ports()
        expected = [{
            'name': utils.ovn_name('ls-id-1'),
            'ports': ['lsp-id-11', 'lsp-id-12', 'lsp-rp-id-1'],
            'provnet_port': 'provnet-ls-id-1'
        }, {
            'name': utils.ovn_name('ls-id-2'),
            'ports': ['lsp-id-21', 'lsp-rp-id-2'],
            'provnet_port': 'provnet-ls-id-2'
        }, {
            'name':
            utils.ovn_name('ls-id-3'),
            'ports': ['lsp-id-31', 'lsp-id-32', 'lsp-rp-id-3', 'lsp-vpn-id-3'],
            'provnet_port':
            None
        }, {
            'name':
            utils.ovn_name('ls-id-5'),
            'ports': ['lsp-id-51', 'lsp-id-52', 'lsp-rp-id-5', 'lsp-vpn-id-5'],
            'provnet_port':
            None
        }]
        self.assertItemsEqual(mapping, expected)

    def test_get_all_logical_routers_with_rports(self):
        # Test empty
        mapping = self.nb_ovn_idl.get_all_logical_switches_with_ports()
        self.assertItemsEqual(mapping, {})
        # Test loaded values
        self._load_nb_db()
        mapping = self.nb_ovn_idl.get_all_logical_routers_with_rports()
        expected = [{
            'name':
            'lr-id-a',
            'ports': {
                'orp-id-a1': ['10.0.1.0/24'],
                'orp-id-a2': ['10.0.2.0/24'],
                'orp-id-a3': ['10.0.3.0/24']
            },
            'static_routes': [{
                'destination': '20.0.0.0/16',
                'nexthop': '10.0.3.253'
            }],
            'snats': [{
                'external_ip': '10.0.3.1',
                'logical_ip': '20.0.0.0/16',
                'type': 'snat'
            }],
            'dnat_and_snats': []
        }, {
            'name':
            'lr-id-b',
            'ports': {
                'xrp-id-b1': ['20.0.1.0/24'],
                'orp-id-b2': ['20.0.2.0/24']
            },
            'static_routes': [{
                'destination': '10.0.0.0/16',
                'nexthop': '20.0.2.253'
            }],
            'snats': [{
                'external_ip': '20.0.2.1',
                'logical_ip': '10.0.0.0/24',
                'type': 'snat'
            }],
            'dnat_and_snats': [{
                'external_ip': '20.0.2.4',
                'logical_ip': '10.0.0.4',
                'type': 'dnat_and_snat'
            }, {
                'external_ip': '20.0.2.5',
                'logical_ip': '10.0.0.5',
                'type': 'dnat_and_snat',
                'external_mac': '00:01:02:03:04:05',
                'logical_port': 'lsp-id-001'
            }]
        }, {
            'name': 'lr-id-c',
            'ports': {},
            'static_routes': [],
            'snats': [],
            'dnat_and_snats': []
        }, {
            'name': 'lr-id-d',
            'ports': {},
            'static_routes': [],
            'snats': [],
            'dnat_and_snats': []
        }, {
            'name': 'lr-id-e',
            'ports': {},
            'static_routes': [],
            'snats': [],
            'dnat_and_snats': []
        }]
        self.assertItemsEqual(mapping, expected)

    def test_get_acls_for_lswitches(self):
        self._load_nb_db()
        # Test neutron switches
        lswitches = ['ls-id-1', 'ls-id-2', 'ls-id-3', 'ls-id-5']
        acl_values, acl_objs, lswitch_ovsdb_dict = \
            self.nb_ovn_idl.get_acls_for_lswitches(lswitches)
        excepted_acl_values = {
            'lsp-id-11': [{
                'action': 'allow-related',
                'lport': 'lsp-id-11',
                'lswitch': 'neutron-ls-id-1',
                'external_ids': {
                    'neutron:lport': 'lsp-id-11'
                },
                'direction': 'from-lport',
                'match': 'inport == "lsp-id-11" && ip4'
            }, {
                'action':
                'allow-related',
                'lport':
                'lsp-id-11',
                'lswitch':
                'neutron-ls-id-1',
                'external_ids': {
                    'neutron:lport': 'lsp-id-11'
                },
                'direction':
                'to-lport',
                'match':
                'outport == "lsp-id-11" && ip4.src == $as_ip4_id_1'
            }],
            'lsp-id-12': [{
                'action': 'allow-related',
                'lport': 'lsp-id-12',
                'lswitch': 'neutron-ls-id-1',
                'external_ids': {
                    'neutron:lport': 'lsp-id-12'
                },
                'direction': 'from-lport',
                'match': 'inport == "lsp-id-12" && ip4'
            }, {
                'action':
                'allow-related',
                'lport':
                'lsp-id-12',
                'lswitch':
                'neutron-ls-id-1',
                'external_ids': {
                    'neutron:lport': 'lsp-id-12'
                },
                'direction':
                'to-lport',
                'match':
                'outport == "lsp-id-12" && ip4.src == $as_ip4_id_1'
            }],
            'lsp-id-21': [{
                'action': 'allow-related',
                'lport': 'lsp-id-21',
                'lswitch': 'neutron-ls-id-2',
                'external_ids': {
                    'neutron:lport': 'lsp-id-21'
                },
                'direction': 'from-lport',
                'match': 'inport == "lsp-id-21" && ip4'
            }, {
                'action':
                'allow-related',
                'lport':
                'lsp-id-21',
                'lswitch':
                'neutron-ls-id-2',
                'external_ids': {
                    'neutron:lport': 'lsp-id-21'
                },
                'direction':
                'to-lport',
                'match':
                'outport == "lsp-id-21" && ip4.src == $as_ip4_id_2'
            }],
            'lsp-id-52': [{
                'action': 'allow-related',
                'lport': 'lsp-id-52',
                'lswitch': 'neutron-ls-id-5',
                'external_ids': {
                    'neutron:lport': 'lsp-id-52'
                },
                'direction': 'from-lport',
                'match': 'inport == "lsp-id-52" && ip4'
            }, {
                'action':
                'allow-related',
                'lport':
                'lsp-id-52',
                'lswitch':
                'neutron-ls-id-5',
                'external_ids': {
                    'neutron:lport': 'lsp-id-52'
                },
                'direction':
                'to-lport',
                'match':
                'outport == "lsp-id-52" && ip4.src == $as_ip4_id_5'
            }]
        }
        self.assertItemsEqual(acl_values, excepted_acl_values)
        self.assertEqual(len(acl_objs), 8)
        self.assertEqual(len(lswitch_ovsdb_dict), len(lswitches))

        # Test non-neutron switches
        lswitches = ['ls-id-4']
        acl_values, acl_objs, lswitch_ovsdb_dict = \
            self.nb_ovn_idl.get_acls_for_lswitches(lswitches)
        self.assertItemsEqual(acl_values, {})
        self.assertEqual(len(acl_objs), 0)
        self.assertEqual(len(lswitch_ovsdb_dict), 0)

    def test_get_all_chassis_gateway_bindings(self):
        self._load_nb_db()
        bindings = self.nb_ovn_idl.get_all_chassis_gateway_bindings()
        expected = {
            'host-1': [
                utils.ovn_lrouter_port_name('orp-id-a1'),
                utils.ovn_lrouter_port_name('orp-id-a2')
            ],
            'host-2': [utils.ovn_lrouter_port_name('orp-id-b2')],
            ovn_const.OVN_GATEWAY_INVALID_CHASSIS:
            [utils.ovn_name('orp-id-a3')]
        }
        self.assertItemsEqual(bindings, expected)

        bindings = self.nb_ovn_idl.get_all_chassis_gateway_bindings([])
        self.assertItemsEqual(bindings, expected)

        bindings = self.nb_ovn_idl.get_all_chassis_gateway_bindings(['host-1'])
        expected = {
            'host-1': [
                utils.ovn_lrouter_port_name('orp-id-a1'),
                utils.ovn_lrouter_port_name('orp-id-a2')
            ]
        }
        self.assertItemsEqual(bindings, expected)

    def test_get_gateway_chassis_binding(self):
        self._load_nb_db()
        chassis = self.nb_ovn_idl.get_gateway_chassis_binding(
            utils.ovn_lrouter_port_name('orp-id-a1'))
        self.assertEqual(chassis, ['host-1'])
        chassis = self.nb_ovn_idl.get_gateway_chassis_binding(
            utils.ovn_lrouter_port_name('orp-id-b2'))
        self.assertEqual(chassis, ['host-2'])
        chassis = self.nb_ovn_idl.get_gateway_chassis_binding(
            utils.ovn_lrouter_port_name('orp-id-a3'))
        self.assertEqual(chassis, ['neutron-ovn-invalid-chassis'])
        chassis = self.nb_ovn_idl.get_gateway_chassis_binding(
            utils.ovn_lrouter_port_name('orp-id-b3'))
        self.assertEqual(chassis, [])
        chassis = self.nb_ovn_idl.get_gateway_chassis_binding('bad')
        self.assertEqual(chassis, [])

    def test_get_unhosted_gateways(self):
        self._load_nb_db()
        # Test only host-1 in the valid list
        unhosted_gateways = self.nb_ovn_idl.get_unhosted_gateways(
            {}, {'host-1': 'physnet1'}, [])
        expected = [
            'lrp-orp-id-a1', 'lrp-orp-id-a2', 'lrp-orp-id-a3', 'lrp-orp-id-b2'
        ]
        self.assertItemsEqual(unhosted_gateways, expected)
        # Test both host-1, host-2 in valid list
        unhosted_gateways = self.nb_ovn_idl.get_unhosted_gateways(
            {}, {
                'host-1': 'physnet1',
                'host-2': 'physnet2'
            }, [])
        self.assertItemsEqual(unhosted_gateways, expected)
        # Schedule unhosted_gateways on host-2
        for unhosted_gateway in unhosted_gateways:
            router_row = self._find_ovsdb_fake_row(self.lrp_table, 'name',
                                                   unhosted_gateway)
            setattr(router_row, 'options',
                    {ovn_const.OVN_GATEWAY_CHASSIS_KEY: 'host-2'})
        unhosted_gateways = self.nb_ovn_idl.get_unhosted_gateways(
            {}, {
                'host-1': 'physnet1',
                'host-2': 'physnet2'
            }, [])
        self.assertItemsEqual(unhosted_gateways, expected)

    def test_unhosted_gateway_max_chassis(self):
        gw_chassis_table = fakes.FakeOvsdbTable.create_one_ovsdb_table()
        self._tables['Gateway_Chassis'] = gw_chassis_table
        gw_chassis = collections.namedtuple('gw_chassis',
                                            'chassis_name priority')
        TestNBImplIdlOvn.fake_set['lrouter_ports'][0]['gateway_chassis'] = [
            gw_chassis(chassis_name='host-%s' % x, priority=x)
            for x in range(1, 6)
        ]
        for port in TestNBImplIdlOvn.fake_set['lrouter_ports'][1:]:
            port['gateway_chassis'] = []
        self._load_nb_db()
        unhosted_gateways = self.nb_ovn_idl.get_unhosted_gateways(
            {}, {
                'host-1': 'physnet1',
                'host-2': 'physnet2',
                'host-3': 'physnet1',
                'host-4': 'physnet2',
                'host-5': 'physnet1',
                'host-6': 'physnet2'
            }, [])
        expected = []
        self.assertItemsEqual(unhosted_gateways, expected)

    def test_get_subnet_dhcp_options(self):
        self._load_nb_db()
        subnet_options = self.nb_ovn_idl.get_subnet_dhcp_options(
            'subnet-id-10-0-2-0')
        expected_row = self._find_ovsdb_fake_row(self.dhcp_table, 'cidr',
                                                 '10.0.2.0/24')
        self.assertEqual(
            {
                'subnet': {
                    'cidr': expected_row.cidr,
                    'external_ids': expected_row.external_ids,
                    'options': expected_row.options,
                    'uuid': expected_row.uuid
                },
                'ports': []
            }, subnet_options)
        subnet_options = self.nb_ovn_idl.get_subnet_dhcp_options(
            'subnet-id-11-0-2-0')['subnet']
        self.assertIsNone(subnet_options)
        subnet_options = self.nb_ovn_idl.get_subnet_dhcp_options(
            'port-id-30-0-1-0')['subnet']
        self.assertIsNone(subnet_options)

    def test_get_subnet_dhcp_options_with_ports(self):
        # Test empty
        subnet_options = self.nb_ovn_idl.get_subnet_dhcp_options(
            'subnet-id-10-0-1-0', with_ports=True)
        self.assertItemsEqual({'subnet': None, 'ports': []}, subnet_options)
        # Test loaded values
        self._load_nb_db()
        # Test getting both subnet and port dhcp options
        subnet_options = self.nb_ovn_idl.get_subnet_dhcp_options(
            'subnet-id-10-0-1-0', with_ports=True)
        dhcp_rows = [
            self._find_ovsdb_fake_row(self.dhcp_table, 'cidr', '10.0.1.0/24'),
            self._find_ovsdb_fake_row(self.dhcp_table, 'cidr', '10.0.1.0/26')
        ]
        expected_rows = [{
            'cidr': dhcp_row.cidr,
            'external_ids': dhcp_row.external_ids,
            'options': dhcp_row.options,
            'uuid': dhcp_row.uuid
        } for dhcp_row in dhcp_rows]
        self.assertItemsEqual(expected_rows, [subnet_options['subnet']] +
                              subnet_options['ports'])
        # Test getting only subnet dhcp options
        subnet_options = self.nb_ovn_idl.get_subnet_dhcp_options(
            'subnet-id-10-0-2-0', with_ports=True)
        dhcp_rows = [
            self._find_ovsdb_fake_row(self.dhcp_table, 'cidr', '10.0.2.0/24')
        ]
        expected_rows = [{
            'cidr': dhcp_row.cidr,
            'external_ids': dhcp_row.external_ids,
            'options': dhcp_row.options,
            'uuid': dhcp_row.uuid
        } for dhcp_row in dhcp_rows]
        self.assertItemsEqual(expected_rows, [subnet_options['subnet']] +
                              subnet_options['ports'])
        # Test getting no dhcp options
        subnet_options = self.nb_ovn_idl.get_subnet_dhcp_options(
            'subnet-id-11-0-2-0', with_ports=True)
        self.assertItemsEqual({'subnet': None, 'ports': []}, subnet_options)

    def test_get_subnets_dhcp_options(self):
        self._load_nb_db()

        def get_row_dict(row):
            return {
                'cidr': row.cidr,
                'external_ids': row.external_ids,
                'options': row.options,
                'uuid': row.uuid
            }

        subnets_options = self.nb_ovn_idl.get_subnets_dhcp_options(
            ['subnet-id-10-0-1-0', 'subnet-id-10-0-2-0'])
        expected_rows = [
            get_row_dict(
                self._find_ovsdb_fake_row(self.dhcp_table, 'cidr', cidr))
            for cidr in ('10.0.1.0/24', '10.0.2.0/24')
        ]
        self.assertItemsEqual(expected_rows, subnets_options)

        subnets_options = self.nb_ovn_idl.get_subnets_dhcp_options(
            ['subnet-id-11-0-2-0', 'subnet-id-20-0-1-0'])
        expected_row = get_row_dict(
            self._find_ovsdb_fake_row(self.dhcp_table, 'cidr', '20.0.1.0/24'))
        self.assertItemsEqual([expected_row], subnets_options)

        subnets_options = self.nb_ovn_idl.get_subnets_dhcp_options(
            ['port-id-30-0-1-0', 'fake-not-exist'])
        self.assertEqual([], subnets_options)

    def test_get_all_dhcp_options(self):
        self._load_nb_db()
        dhcp_options = self.nb_ovn_idl.get_all_dhcp_options()
        self.assertEqual(len(dhcp_options['subnets']), 3)
        self.assertEqual(len(dhcp_options['ports_v4']), 2)

    def test_get_address_sets(self):
        self._load_nb_db()
        address_sets = self.nb_ovn_idl.get_address_sets()
        self.assertEqual(len(address_sets), 4)

    def test_get_port_group_not_supported(self):
        self._load_nb_db()
        # Make sure that PG tables doesn't exist in fake db.
        self._tables.pop('Port_Group', None)
        port_group = self.nb_ovn_idl.get_port_group(str(uuid.uuid4()))
        self.assertIsNone(port_group)

    def test_get_port_groups_not_supported(self):
        self._load_nb_db()
        # Make sure that PG tables doesn't exist in fake db.
        self._tables.pop('Port_Group', None)
        port_groups = self.nb_ovn_idl.get_port_groups()
        self.assertEqual({}, port_groups)
예제 #28
0
    def sync_routers_and_rports(self, ctx):
        """Sync Routers between neutron and NB.

        @param ctx: neutron context
        @type  ctx: object of type neutron.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')
        db_routers = {}
        db_router_ports = {}
        for router in self.l3_plugin.get_routers(ctx):
            db_routers[router['id']] = router

        interfaces = self.l3_plugin._get_sync_interfaces(ctx,
                                                         db_routers.keys())
        for interface in interfaces:
            db_router_ports[interface['id']] = interface
        lrouters = self.ovn_api.get_all_logical_routers_with_rports()
        del_lrouters_list = []
        del_lrouter_ports_list = []
        update_sroutes_list = []
        for lrouter in lrouters:
            if lrouter['name'] in db_routers:
                for lrport in lrouter['ports']:
                    if lrport in db_router_ports:
                        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 = []
                ovn_routes = lrouter['static_routes']
                add_routes, del_routes = n_utils.diff_list_of_dict(
                    ovn_routes, db_routes)
                update_sroutes_list.append({'id': lrouter['name'],
                                            'add': add_routes,
                                            'del': del_routes})
                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': []})
                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'])

        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("Router %s static routes %s found in "
                                "Neutron but not in OVN", sroute['id'],
                                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("Router %s static routes %s found in "
                                "OVN but not in Neutron", sroute['id'],
                                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']))
        LOG.debug('OVN-NB Sync routers and router ports finished')
예제 #29
0
    def sync_routers_and_rports(self, ctx):
        """Sync Routers between neutron and NB.

        @param ctx: neutron context
        @type  ctx: object of type neutron.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')
        db_routers = {}
        db_router_ports = {}
        db_gateway_ports = {}
        db_fips = {}
        for router in self.l3_plugin.get_routers(ctx):
            db_routers[router['id']] = router
            db_routers[router['id']]['floating_ips'] = []

        db_routers_keys = db_routers.keys()
        fips = self.l3_plugin.get_floatingips(ctx,
                                              {'router_id': db_routers_keys})
        interfaces = self.l3_plugin._get_sync_interfaces(
            ctx, db_routers_keys, [
                lib_constants.DEVICE_OWNER_ROUTER_INTF,
                lib_constants.DEVICE_OWNER_ROUTER_GW
            ])
        for fip in fips:
            db_routers[fip['router_id']]['floating_ips'].append({
                'floating_ip_address':
                fip['floating_ip_address'],
                'fixed_ip_address':
                fip['fixed_ip_address']
            })
        for interface in interfaces:
            if (interface['device_owner'] ==
                    lib_constants.DEVICE_OWNER_ROUTER_INTF):
                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']))
            else:
                db_gateway_ports[interface['id']] = interface
                db_gateway_ports[interface['id']]['networks'] = sorted(
                    self.l3_plugin.get_networks_for_lrouter_port(
                        ctx, interface['fixed_ips']))

        LOG.debug("Get neutron routers=%s", db_routers)
        LOG.debug("Get neutron router ports=%s", db_router_ports)
        LOG.debug("Get neutron gateway ports=%s", db_gateway_ports)

        lrouters = self.ovn_api.get_all_logical_routers_with_rports()
        LOG.debug("Get ovn routers=%s", lrouters)
        del_lrouters_list = []
        del_lrouter_ports_list = []
        del_gwrouter_ports_list = []
        update_sroutes_list = []
        update_fips_list = []
        update_lrport_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']
                        })

                for gwport, gwport_nets in lrouter['gateway_ports'].items():
                    if gwport in db_gateway_ports:
                        del db_gateway_ports[gwport]
                    else:
                        del_gwrouter_ports_list.append({
                            'port':
                            gwport,
                            'lrouter':
                            lrouter['name'],
                            'next_hop':
                            lrouter['next_hop']
                        })

                if 'routes' in db_routers[lrouter['name']]:
                    db_routes = db_routers[lrouter['name']]['routes']
                else:
                    db_routes = []
                ovn_routes = lrouter['static_routes']
                add_routes, del_routes = n_utils.diff_list_of_dict(
                    ovn_routes, db_routes)
                update_sroutes_list.append({
                    'id': lrouter['name'],
                    'del': del_routes,
                    'add': add_routes
                })
                ovn_fips = lrouter['floating_ips']
                db_fips = db_routers[lrouter['name']]['floating_ips']
                add_fips, del_fips = n_utils.diff_list_of_dict(
                    ovn_fips, db_fips)
                update_fips_list.append({
                    'id': lrouter['name'],
                    'add': add_fips,
                    'del': del_fips
                })
                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 'floating_ips' in router:
                        update_fips_list.append({
                            'id': router['id'],
                            'add': router['floating_ips'],
                            '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'])

        for gwport_info in del_gwrouter_ports_list:
            LOG.warning(
                _LW("Gateway Port found in OVN but not in "
                    "Neutron, port_id=%s"), gwport_info['port'])
            if self.mode == SYNC_MODE_REPAIR:
                LOG.warning(_LW("Deleting the port %s from OVN NB DB"),
                            gwport_info['port'])
                self.l3_plugin.delete_gw_router_port_in_ovn(
                    gwport_info['lrouter'], gwport_info['port'],
                    gwport_info['next_hop'])

        for gwp_id, gwport in db_gateway_ports.items():
            LOG.warning(
                _LW("Gateway Port found in Neutron but not in OVN "
                    "DB, gateway port_id=%s"), gwport['id'])
            if self.mode == SYNC_MODE_REPAIR:
                try:
                    LOG.warning(
                        _LW("Creating the gateway port %s in "
                            "OVN NB DB"), gwport['id'])
                    self.l3_plugin.create_gw_router_port_in_ovn(
                        ctx, gwport['device_id'], gwport)
                except RuntimeError:
                    LOG.warning(
                        _LW("Create gateway port in OVN "
                            "NB failed for"
                            " gateway port %s"), gwport['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'])))
                    txn.add(
                        self.ovn_api.delete_lrouter(
                            utils.ovn_gateway_name(lrouter['name'])))
                    txn.add(
                        self.ovn_api.delete_lswitch(
                            utils.ovn_transit_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 ifip in fip['del']:
                            txn.add(
                                self.ovn_api.delete_nat(
                                    utils.ovn_gateway_name(fip['id']),
                                    logical_ip=ifip['fixed_ip_address'],
                                    external_ip=ifip['floating_ip_address'],
                                    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 ifip in fip['add']:
                            txn.add(
                                self.ovn_api.add_nat(
                                    utils.ovn_gateway_name(fip['id']),
                                    logical_ip=ifip['fixed_ip_address'],
                                    external_ip=ifip['floating_ip_address'],
                                    type='dnat_and_snat'))

        LOG.debug('OVN-NB Sync routers and router ports finished')
예제 #30
0
    def sync_routers_and_rports(self, ctx):
        """Sync Routers between neutron and NB.

        @param ctx: neutron context
        @type  ctx: object of type neutron.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
        """
        LOG.debug('OVN-NB Sync Routers and Router ports started')
        db_routers = {}
        db_router_ports = {}
        for router in self.core_plugin.get_routers(ctx):
            db_routers[router['id']] = router

        interfaces = self.core_plugin._get_sync_interfaces(ctx,
                                                           db_routers.keys())
        for interface in interfaces:
            db_router_ports[interface['id']] = interface
        lrouters = self.ovn_api.get_all_logical_routers_with_rports()
        del_lrouters_list = []
        del_lrouter_ports_list = []
        for lrouter in lrouters:
            if lrouter['name'] in db_routers:
                for lrport in lrouter['ports']:
                    if lrport in db_router_ports:
                        del db_router_ports[lrport]
                    else:
                        del_lrouter_ports_list.append(
                            {'port': lrport, 'lrouter': lrouter['name']})
                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.core_plugin.create_lrouter_in_ovn(router)
                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.core_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'])

        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))
        LOG.debug('OVN-NB Sync routers and router ports finished')
예제 #31
0
class TestNBImplIdlOvn(TestDBImplIdlOvn):

    fake_set = {
        'lswitches': [{
            'name': utils.ovn_name('ls-id-1'),
            'external_ids': {
                ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY: 'ls-name-1'
            }
        }, {
            'name': utils.ovn_name('ls-id-2'),
            'external_ids': {
                ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY: 'ls-name-2'
            }
        }, {
            'name': utils.ovn_name('ls-id-3'),
            'external_ids': {
                ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY: 'ls-name-3'
            }
        }, {
            'name': 'ls-id-4',
            'external_ids': {
                'not-neutron:network_name': 'ls-name-4'
            }
        }, {
            'name': utils.ovn_name('ls-id-5'),
            'external_ids': {
                ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY: 'ls-name-5'
            }
        }],
        'lswitch_ports': [{
            'name': 'lsp-id-11',
            'addresses': ['10.0.1.1'],
            'external_ids': {
                ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-name-11'
            }
        }, {
            'name': 'lsp-id-12',
            'addresses': ['10.0.1.2'],
            'external_ids': {
                ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-name-12'
            }
        }, {
            'name': 'lsp-rp-id-1',
            'addresses': ['10.0.1.254'],
            'external_ids': {
                ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-rp-name-1'
            },
            'options': {
                'router-port': utils.ovn_lrouter_port_name('orp-id-a1')
            }
        }, {
            'name': 'provnet-ls-id-1',
            'addresses': ['unknown'],
            'external_ids': {},
            'options': {
                'network_name': 'physnet1'
            }
        }, {
            'name': 'lsp-id-21',
            'addresses': ['10.0.2.1'],
            'external_ids': {
                ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-name-21'
            }
        }, {
            'name': 'lsp-id-22',
            'addresses': ['10.0.2.2'],
            'external_ids': {}
        }, {
            'name': 'lsp-id-23',
            'addresses': ['10.0.2.3'],
            'external_ids': {
                'not-neutron:port_name': 'lsp-name-23'
            }
        }, {
            'name': 'lsp-rp-id-2',
            'addresses': ['10.0.2.254'],
            'external_ids': {
                ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-rp-name-2'
            },
            'options': {
                'router-port': utils.ovn_lrouter_port_name('orp-id-a2')
            }
        }, {
            'name': 'provnet-ls-id-2',
            'addresses': ['unknown'],
            'external_ids': {},
            'options': {
                'network_name': 'physnet2'
            }
        }, {
            'name': 'lsp-id-31',
            'addresses': ['10.0.3.1'],
            'external_ids': {
                ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-name-31'
            }
        }, {
            'name': 'lsp-id-32',
            'addresses': ['10.0.3.2'],
            'external_ids': {
                ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-name-32'
            }
        }, {
            'name': 'lsp-rp-id-3',
            'addresses': ['10.0.3.254'],
            'external_ids': {
                ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-rp-name-3'
            },
            'options': {
                'router-port': utils.ovn_lrouter_port_name('orp-id-a3')
            }
        }, {
            'name': 'lsp-vpn-id-3',
            'addresses': ['10.0.3.253'],
            'external_ids': {
                ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-vpn-name-3'
            }
        }, {
            'name': 'lsp-id-41',
            'addresses': ['20.0.1.1'],
            'external_ids': {
                'not-neutron:port_name': 'lsp-name-41'
            }
        }, {
            'name': 'lsp-rp-id-4',
            'addresses': ['20.0.1.254'],
            'external_ids': {},
            'options': {
                'router-port': 'xrp-id-b1'
            }
        }, {
            'name': 'lsp-id-51',
            'addresses': ['20.0.2.1'],
            'external_ids': {
                ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-name-51'
            }
        }, {
            'name': 'lsp-id-52',
            'addresses': ['20.0.2.2'],
            'external_ids': {
                ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-name-52'
            }
        }, {
            'name': 'lsp-rp-id-5',
            'addresses': ['20.0.2.254'],
            'external_ids': {
                ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-rp-name-5'
            },
            'options': {
                'router-port': utils.ovn_lrouter_port_name('orp-id-b2')
            }
        }, {
            'name': 'lsp-vpn-id-5',
            'addresses': ['20.0.2.253'],
            'external_ids': {
                ovn_const.OVN_PORT_NAME_EXT_ID_KEY: 'lsp-vpn-name-5'
            }
        }],
        'lrouters': [{
            'name': utils.ovn_name('lr-id-a'),
            'external_ids': {
                ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY: 'lr-name-a'
            }
        }, {
            'name': utils.ovn_name('lr-id-b'),
            'external_ids': {
                ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY: 'lr-name-b'
            }
        }, {
            'name': utils.ovn_name('lr-id-c'),
            'external_ids': {
                ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY: 'lr-name-c'
            }
        }, {
            'name': utils.ovn_name('lr-id-d'),
            'external_ids': {
                ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY: 'lr-name-d'
            }
        }, {
            'name': utils.ovn_name('lr-id-e'),
            'external_ids': {
                ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY: 'lr-name-e'
            }
        }],
        'lrouter_ports': [{
            'name': utils.ovn_lrouter_port_name('orp-id-a1'),
            'external_ids': {},
            'networks': ['10.0.1.0/24'],
            'options': {
                ovn_const.OVN_GATEWAY_CHASSIS_KEY: 'host-1'
            }
        }, {
            'name': utils.ovn_lrouter_port_name('orp-id-a2'),
            'external_ids': {},
            'networks': ['10.0.2.0/24'],
            'options': {
                ovn_const.OVN_GATEWAY_CHASSIS_KEY: 'host-1'
            }
        }, {
            'name': utils.ovn_lrouter_port_name('orp-id-a3'),
            'external_ids': {},
            'networks': ['10.0.3.0/24'],
            'options': {
                ovn_const.OVN_GATEWAY_CHASSIS_KEY:
                ovn_const.OVN_GATEWAY_INVALID_CHASSIS
            }
        }, {
            'name': 'xrp-id-b1',
            'external_ids': {},
            'networks': ['20.0.1.0/24']
        }, {
            'name': utils.ovn_lrouter_port_name('orp-id-b2'),
            'external_ids': {},
            'networks': ['20.0.2.0/24'],
            'options': {
                ovn_const.OVN_GATEWAY_CHASSIS_KEY: 'host-2'
            }
        }, {
            'name': utils.ovn_lrouter_port_name('orp-id-b3'),
            'external_ids': {},
            'networks': ['20.0.3.0/24'],
            'options': {}
        }],
        'static_routes': [{
            'ip_prefix': '20.0.0.0/16',
            'nexthop': '10.0.3.253'
        }, {
            'ip_prefix': '10.0.0.0/16',
            'nexthop': '20.0.2.253'
        }],
        'nats': [{
            'external_ip': '10.0.3.1',
            'logical_ip': '20.0.0.0/16',
            'type': 'snat'
        }, {
            'external_ip': '20.0.2.1',
            'logical_ip': '10.0.0.0/24',
            'type': 'snat'
        }, {
            'external_ip': '20.0.2.4',
            'logical_ip': '10.0.0.4',
            'type': 'dnat_and_snat'
        }],
        'acls': [{
            'unit_test_id': 1,
            'action': 'allow-related',
            'direction': 'from-lport',
            'external_ids': {
                'neutron:lport': 'lsp-id-11'
            },
            'match': 'inport == "lsp-id-11" && ip4'
        }, {
            'unit_test_id': 2,
            'action': 'allow-related',
            'direction': 'to-lport',
            'external_ids': {
                'neutron:lport': 'lsp-id-11'
            },
            'match': 'outport == "lsp-id-11" && ip4.src == $as_ip4_id_1'
        }, {
            'unit_test_id': 3,
            'action': 'allow-related',
            'direction': 'from-lport',
            'external_ids': {
                'neutron:lport': 'lsp-id-12'
            },
            'match': 'inport == "lsp-id-12" && ip4'
        }, {
            'unit_test_id': 4,
            'action': 'allow-related',
            'direction': 'to-lport',
            'external_ids': {
                'neutron:lport': 'lsp-id-12'
            },
            'match': 'outport == "lsp-id-12" && ip4.src == $as_ip4_id_1'
        }, {
            'unit_test_id': 5,
            'action': 'allow-related',
            'direction': 'from-lport',
            'external_ids': {
                'neutron:lport': 'lsp-id-21'
            },
            'match': 'inport == "lsp-id-21" && ip4'
        }, {
            'unit_test_id': 6,
            'action': 'allow-related',
            'direction': 'to-lport',
            'external_ids': {
                'neutron:lport': 'lsp-id-21'
            },
            'match': 'outport == "lsp-id-21" && ip4.src == $as_ip4_id_2'
        }, {
            'unit_test_id': 7,
            'action': 'allow-related',
            'direction': 'from-lport',
            'external_ids': {
                'neutron:lport': 'lsp-id-41'
            },
            'match': 'inport == "lsp-id-41" && ip4'
        }, {
            'unit_test_id': 8,
            'action': 'allow-related',
            'direction': 'to-lport',
            'external_ids': {
                'neutron:lport': 'lsp-id-41'
            },
            'match': 'outport == "lsp-id-41" && ip4.src == $as_ip4_id_4'
        }, {
            'unit_test_id': 9,
            'action': 'allow-related',
            'direction': 'from-lport',
            'external_ids': {
                'neutron:lport': 'lsp-id-52'
            },
            'match': 'inport == "lsp-id-52" && ip4'
        }, {
            'unit_test_id': 10,
            'action': 'allow-related',
            'direction': 'to-lport',
            'external_ids': {
                'neutron:lport': 'lsp-id-52'
            },
            'match': 'outport == "lsp-id-52" && ip4.src == $as_ip4_id_5'
        }],
        'dhcp_options': [{
            'cidr': '10.0.1.0/24',
            'external_ids': {
                'subnet_id': 'subnet-id-10-0-1-0'
            },
            'options': {
                'mtu': '1442',
                'router': '10.0.1.254'
            }
        }, {
            'cidr': '10.0.2.0/24',
            'external_ids': {
                'subnet_id': 'subnet-id-10-0-2-0'
            },
            'options': {
                'mtu': '1442',
                'router': '10.0.2.254'
            }
        }, {
            'cidr': '10.0.3.0/24',
            'external_ids': {
                'subnet_id': 'subnet-id-10-0-3-0',
                'port_id': 'lsp-vpn-id-3'
            },
            'options': {
                'mtu': '1442',
                'router': '10.0.3.254'
            }
        }, {
            'cidr': '20.0.1.0/24',
            'external_ids': {
                'subnet_id': 'subnet-id-20-0-1-0'
            },
            'options': {
                'mtu': '1442',
                'router': '20.0.1.254'
            }
        }, {
            'cidr': '20.0.2.0/24',
            'external_ids': {
                'subnet_id': 'subnet-id-20-0-2-0',
                'port_id': 'lsp-vpn-id-5'
            },
            'options': {
                'mtu': '1442',
                'router': '20.0.2.254'
            }
        }, {
            'cidr': '2001:dba::/64',
            'external_ids': {
                'subnet_id': 'subnet-id-2001-dba',
                'port_id': 'lsp-vpn-id-5'
            },
            'options': {
                'server_id': '12:34:56:78:9a:bc'
            }
        }, {
            'cidr': '30.0.1.0/24',
            'external_ids': {
                'port_id': 'port-id-30-0-1-0'
            },
            'options': {
                'mtu': '1442',
                'router': '30.0.2.254'
            }
        }, {
            'cidr': '30.0.2.0/24',
            'external_ids': {},
            'options': {}
        }],
        'address_sets': [
            {
                'name': '$as_ip4_id_1',
                'addresses': ['10.0.1.1', '10.0.1.2'],
                'external_ids': {
                    ovn_const.OVN_SG_NAME_EXT_ID_KEY: 'id_1'
                }
            },
            {
                'name': '$as_ip4_id_2',
                'addresses': ['10.0.2.1'],
                'external_ids': {
                    ovn_const.OVN_SG_NAME_EXT_ID_KEY: 'id_2'
                }
            },
            {
                'name': '$as_ip4_id_3',
                'addresses': ['10.0.3.1', '10.0.3.2'],
                'external_ids': {
                    ovn_const.OVN_SG_NAME_EXT_ID_KEY: 'id_3'
                }
            },
            {
                'name': '$as_ip4_id_4',
                'addresses': ['20.0.1.1', '20.0.1.2'],
                'external_ids': {}
            },
            {
                'name': '$as_ip4_id_5',
                'addresses': ['20.0.2.1', '20.0.2.2'],
                'external_ids': {
                    ovn_const.OVN_SG_NAME_EXT_ID_KEY: 'id_5'
                }
            },
        ]
    }

    fake_associations = {
        'lstolsp': {
            utils.ovn_name('ls-id-1'):
            ['lsp-id-11', 'lsp-id-12', 'lsp-rp-id-1', 'provnet-ls-id-1'],
            utils.ovn_name('ls-id-2'): [
                'lsp-id-21', 'lsp-id-22', 'lsp-id-23', 'lsp-rp-id-2',
                'provnet-ls-id-2'
            ],
            utils.ovn_name('ls-id-3'):
            ['lsp-id-31', 'lsp-id-32', 'lsp-rp-id-3', 'lsp-vpn-id-3'],
            'ls-id-4': ['lsp-id-41', 'lsp-rp-id-4'],
            utils.ovn_name('ls-id-5'):
            ['lsp-id-51', 'lsp-id-52', 'lsp-rp-id-5', 'lsp-vpn-id-5']
        },
        'lrtolrp': {
            utils.ovn_name('lr-id-a'): [
                utils.ovn_lrouter_port_name('orp-id-a1'),
                utils.ovn_lrouter_port_name('orp-id-a2'),
                utils.ovn_lrouter_port_name('orp-id-a3')
            ],
            utils.ovn_name('lr-id-b'):
            ['xrp-id-b1',
             utils.ovn_lrouter_port_name('orp-id-b2')]
        },
        'lrtosroute': {
            utils.ovn_name('lr-id-a'): ['20.0.0.0/16'],
            utils.ovn_name('lr-id-b'): ['10.0.0.0/16']
        },
        'lrtonat': {
            utils.ovn_name('lr-id-a'): ['10.0.3.1'],
            utils.ovn_name('lr-id-b'): ['20.0.2.1', '20.0.2.4'],
        },
        'lstoacl': {
            utils.ovn_name('ls-id-1'): [1, 2, 3, 4],
            utils.ovn_name('ls-id-2'): [5, 6],
            'ls-id-4': [7, 8],
            utils.ovn_name('ls-id-5'): [9, 10]
        }
    }

    def setUp(self):
        super(TestNBImplIdlOvn, self).setUp()

        self.lswitch_table = fakes.FakeOvsdbTable.create_one_ovsdb_table()
        self.lsp_table = fakes.FakeOvsdbTable.create_one_ovsdb_table()
        self.lrouter_table = fakes.FakeOvsdbTable.create_one_ovsdb_table()
        self.lrp_table = fakes.FakeOvsdbTable.create_one_ovsdb_table()
        self.sroute_table = fakes.FakeOvsdbTable.create_one_ovsdb_table()
        self.nat_table = fakes.FakeOvsdbTable.create_one_ovsdb_table()
        self.acl_table = fakes.FakeOvsdbTable.create_one_ovsdb_table()
        self.dhcp_table = fakes.FakeOvsdbTable.create_one_ovsdb_table()
        self.address_set_table = fakes.FakeOvsdbTable.create_one_ovsdb_table()

        self._tables = {}
        self._tables['Logical_Switch'] = self.lswitch_table
        self._tables['Logical_Switch_Port'] = self.lsp_table
        self._tables['Logical_Router'] = self.lrouter_table
        self._tables['Logical_Router_Port'] = self.lrp_table
        self._tables['Logical_Router_Static_Route'] = self.sroute_table
        self._tables['ACL'] = self.acl_table
        self._tables['DHCP_Options'] = self.dhcp_table
        self._tables['Address_Set'] = self.address_set_table

        with mock.patch.object(impl_idl_ovn,
                               'get_connection',
                               return_value=mock.Mock()):
            impl_idl_ovn.OvsdbNbOvnIdl.ovsdb_connection = None
            self.nb_ovn_idl = impl_idl_ovn.OvsdbNbOvnIdl(self)

        self.nb_ovn_idl.idl.tables = self._tables

    def _load_nb_db(self):
        # Load Switches and Switch Ports
        fake_lswitches = TestNBImplIdlOvn.fake_set['lswitches']
        self._load_ovsdb_fake_rows(self.lswitch_table, fake_lswitches)
        fake_lsps = TestNBImplIdlOvn.fake_set['lswitch_ports']
        self._load_ovsdb_fake_rows(self.lsp_table, fake_lsps)
        # Associate switches and ports
        self._construct_ovsdb_references(
            TestNBImplIdlOvn.fake_associations['lstolsp'], self.lswitch_table,
            self.lsp_table, 'name', 'name', 'ports')
        # Load Routers and Router Ports
        fake_lrouters = TestNBImplIdlOvn.fake_set['lrouters']
        self._load_ovsdb_fake_rows(self.lrouter_table, fake_lrouters)
        fake_lrps = TestNBImplIdlOvn.fake_set['lrouter_ports']
        self._load_ovsdb_fake_rows(self.lrp_table, fake_lrps)
        # Associate routers and router ports
        self._construct_ovsdb_references(
            TestNBImplIdlOvn.fake_associations['lrtolrp'], self.lrouter_table,
            self.lrp_table, 'name', 'name', 'ports')
        # Load static routes
        fake_sroutes = TestNBImplIdlOvn.fake_set['static_routes']
        self._load_ovsdb_fake_rows(self.sroute_table, fake_sroutes)
        # Associate routers and static routes
        self._construct_ovsdb_references(
            TestNBImplIdlOvn.fake_associations['lrtosroute'],
            self.lrouter_table, self.sroute_table, 'name', 'ip_prefix',
            'static_routes')
        # Load nats
        fake_nats = TestNBImplIdlOvn.fake_set['nats']
        self._load_ovsdb_fake_rows(self.nat_table, fake_nats)
        # Associate routers and nats
        self._construct_ovsdb_references(
            TestNBImplIdlOvn.fake_associations['lrtonat'], self.lrouter_table,
            self.nat_table, 'name', 'external_ip', 'nat')
        # Load acls
        fake_acls = TestNBImplIdlOvn.fake_set['acls']
        self._load_ovsdb_fake_rows(self.acl_table, fake_acls)
        # Associate switches and acls
        self._construct_ovsdb_references(
            TestNBImplIdlOvn.fake_associations['lstoacl'], self.lswitch_table,
            self.acl_table, 'name', 'unit_test_id', 'acls')
        # Load dhcp options
        fake_dhcp_options = TestNBImplIdlOvn.fake_set['dhcp_options']
        self._load_ovsdb_fake_rows(self.dhcp_table, fake_dhcp_options)
        # Load address sets
        fake_address_sets = TestNBImplIdlOvn.fake_set['address_sets']
        self._load_ovsdb_fake_rows(self.address_set_table, fake_address_sets)

    def test_get_all_logical_switches_with_ports(self):
        # Test empty
        mapping = self.nb_ovn_idl.get_all_logical_switches_with_ports()
        self.assertItemsEqual(mapping, {})
        # Test loaded values
        self._load_nb_db()
        mapping = self.nb_ovn_idl.get_all_logical_switches_with_ports()
        expected = [{
            'name': utils.ovn_name('ls-id-1'),
            'ports': ['lsp-id-11', 'lsp-id-12', 'lsp-rp-id-1'],
            'provnet_port': 'provnet-ls-id-1'
        }, {
            'name': utils.ovn_name('ls-id-2'),
            'ports': ['lsp-id-21', 'lsp-rp-id-2'],
            'provnet_port': 'provnet-ls-id-2'
        }, {
            'name':
            utils.ovn_name('ls-id-3'),
            'ports': ['lsp-id-31', 'lsp-id-32', 'lsp-rp-id-3', 'lsp-vpn-id-3'],
            'provnet_port':
            None
        }, {
            'name':
            utils.ovn_name('ls-id-5'),
            'ports': ['lsp-id-51', 'lsp-id-52', 'lsp-rp-id-5', 'lsp-vpn-id-5'],
            'provnet_port':
            None
        }]
        self.assertItemsEqual(mapping, expected)

    def test_get_all_logical_routers_with_rports(self):
        # Test empty
        mapping = self.nb_ovn_idl.get_all_logical_switches_with_ports()
        self.assertItemsEqual(mapping, {})
        # Test loaded values
        self._load_nb_db()
        mapping = self.nb_ovn_idl.get_all_logical_routers_with_rports()
        expected = [{
            'name':
            'lr-id-a',
            'ports': {
                'orp-id-a1': ['10.0.1.0/24'],
                'orp-id-a2': ['10.0.2.0/24'],
                'orp-id-a3': ['10.0.3.0/24']
            },
            'static_routes': [{
                'destination': '20.0.0.0/16',
                'nexthop': '10.0.3.253'
            }],
            'snats': [{
                'external_ip': '10.0.3.1',
                'logical_ip': '20.0.0.0/16',
                'type': 'snat'
            }],
            'dnat_and_snats': []
        }, {
            'name':
            'lr-id-b',
            'ports': {
                'xrp-id-b1': ['20.0.1.0/24'],
                'orp-id-b2': ['20.0.2.0/24']
            },
            'static_routes': [{
                'destination': '10.0.0.0/16',
                'nexthop': '20.0.2.253'
            }],
            'snats': [{
                'external_ip': '20.0.2.1',
                'logical_ip': '10.0.0.0/24',
                'type': 'snat'
            }],
            'dnat_and_snats': [{
                'external_ip': '20.0.2.4',
                'logical_ip': '10.0.0.4',
                'type': 'dnat_and_snat'
            }]
        }, {
            'name': 'lr-id-c',
            'ports': {},
            'static_routes': [],
            'snats': [],
            'dnat_and_snats': []
        }, {
            'name': 'lr-id-d',
            'ports': {},
            'static_routes': [],
            'snats': [],
            'dnat_and_snats': []
        }, {
            'name': 'lr-id-e',
            'ports': {},
            'static_routes': [],
            'snats': [],
            'dnat_and_snats': []
        }]
        self.assertItemsEqual(mapping, expected)

    def test_get_acls_for_lswitches(self):
        self._load_nb_db()
        # Test neutron switches
        lswitches = ['ls-id-1', 'ls-id-2', 'ls-id-3', 'ls-id-5']
        acl_values, acl_objs, lswitch_ovsdb_dict = \
            self.nb_ovn_idl.get_acls_for_lswitches(lswitches)
        excepted_acl_values = {
            'lsp-id-11': [{
                'action': 'allow-related',
                'lport': 'lsp-id-11',
                'lswitch': 'neutron-ls-id-1',
                'external_ids': {
                    'neutron:lport': 'lsp-id-11'
                },
                'direction': 'from-lport',
                'match': 'inport == "lsp-id-11" && ip4'
            }, {
                'action':
                'allow-related',
                'lport':
                'lsp-id-11',
                'lswitch':
                'neutron-ls-id-1',
                'external_ids': {
                    'neutron:lport': 'lsp-id-11'
                },
                'direction':
                'to-lport',
                'match':
                'outport == "lsp-id-11" && ip4.src == $as_ip4_id_1'
            }],
            'lsp-id-12': [{
                'action': 'allow-related',
                'lport': 'lsp-id-12',
                'lswitch': 'neutron-ls-id-1',
                'external_ids': {
                    'neutron:lport': 'lsp-id-12'
                },
                'direction': 'from-lport',
                'match': 'inport == "lsp-id-12" && ip4'
            }, {
                'action':
                'allow-related',
                'lport':
                'lsp-id-12',
                'lswitch':
                'neutron-ls-id-1',
                'external_ids': {
                    'neutron:lport': 'lsp-id-12'
                },
                'direction':
                'to-lport',
                'match':
                'outport == "lsp-id-12" && ip4.src == $as_ip4_id_1'
            }],
            'lsp-id-21': [{
                'action': 'allow-related',
                'lport': 'lsp-id-21',
                'lswitch': 'neutron-ls-id-2',
                'external_ids': {
                    'neutron:lport': 'lsp-id-21'
                },
                'direction': 'from-lport',
                'match': 'inport == "lsp-id-21" && ip4'
            }, {
                'action':
                'allow-related',
                'lport':
                'lsp-id-21',
                'lswitch':
                'neutron-ls-id-2',
                'external_ids': {
                    'neutron:lport': 'lsp-id-21'
                },
                'direction':
                'to-lport',
                'match':
                'outport == "lsp-id-21" && ip4.src == $as_ip4_id_2'
            }],
            'lsp-id-52': [{
                'action': 'allow-related',
                'lport': 'lsp-id-52',
                'lswitch': 'neutron-ls-id-5',
                'external_ids': {
                    'neutron:lport': 'lsp-id-52'
                },
                'direction': 'from-lport',
                'match': 'inport == "lsp-id-52" && ip4'
            }, {
                'action':
                'allow-related',
                'lport':
                'lsp-id-52',
                'lswitch':
                'neutron-ls-id-5',
                'external_ids': {
                    'neutron:lport': 'lsp-id-52'
                },
                'direction':
                'to-lport',
                'match':
                'outport == "lsp-id-52" && ip4.src == $as_ip4_id_5'
            }]
        }
        self.assertItemsEqual(acl_values, excepted_acl_values)
        self.assertEqual(len(acl_objs), 8)
        self.assertEqual(len(lswitch_ovsdb_dict), len(lswitches))

        # Test non-neutron switches
        lswitches = ['ls-id-4']
        acl_values, acl_objs, lswitch_ovsdb_dict = \
            self.nb_ovn_idl.get_acls_for_lswitches(lswitches)
        self.assertItemsEqual(acl_values, {})
        self.assertEqual(len(acl_objs), 0)
        self.assertEqual(len(lswitch_ovsdb_dict), 0)

    def test_get_all_chassis_gateway_bindings(self):
        self._load_nb_db()
        bindings = self.nb_ovn_idl.get_all_chassis_gateway_bindings()
        expected = {
            'host-1': [
                utils.ovn_lrouter_port_name('orp-id-a1'),
                utils.ovn_lrouter_port_name('orp-id-a2')
            ],
            'host-2': [utils.ovn_lrouter_port_name('orp-id-b2')],
            ovn_const.OVN_GATEWAY_INVALID_CHASSIS:
            [utils.ovn_name('orp-id-a3')]
        }
        self.assertItemsEqual(bindings, expected)

        bindings = self.nb_ovn_idl.get_all_chassis_gateway_bindings([])
        self.assertItemsEqual(bindings, expected)

        bindings = self.nb_ovn_idl.get_all_chassis_gateway_bindings(['host-1'])
        expected = {
            'host-1': [
                utils.ovn_lrouter_port_name('orp-id-a1'),
                utils.ovn_lrouter_port_name('orp-id-a2')
            ]
        }
        self.assertItemsEqual(bindings, expected)

    def test_get_gateway_chassis_binding(self):
        self._load_nb_db()
        chassis = self.nb_ovn_idl.get_gateway_chassis_binding(
            utils.ovn_lrouter_port_name('orp-id-a1'))
        self.assertEqual(chassis, 'host-1')
        chassis = self.nb_ovn_idl.get_gateway_chassis_binding(
            utils.ovn_lrouter_port_name('orp-id-b2'))
        self.assertEqual(chassis, 'host-2')
        chassis = self.nb_ovn_idl.get_gateway_chassis_binding(
            utils.ovn_lrouter_port_name('orp-id-a3'))
        self.assertIsNone(chassis)
        chassis = self.nb_ovn_idl.get_gateway_chassis_binding(
            utils.ovn_lrouter_port_name('orp-id-b3'))
        self.assertIsNone(chassis)
        chassis = self.nb_ovn_idl.get_gateway_chassis_binding('bad')
        self.assertIsNone(chassis)

    def test_get_unhosted_gateways(self):
        self._load_nb_db()
        # Test only host-1 in the valid list
        unhosted_gateways = self.nb_ovn_idl.get_unhosted_gateways(['host-1'])
        expected = {
            utils.ovn_lrouter_port_name('orp-id-b2'): {
                ovn_const.OVN_GATEWAY_CHASSIS_KEY: 'host-2'
            },
            utils.ovn_lrouter_port_name('orp-id-a3'): {
                ovn_const.OVN_GATEWAY_CHASSIS_KEY:
                ovn_const.OVN_GATEWAY_INVALID_CHASSIS
            }
        }
        self.assertItemsEqual(unhosted_gateways, expected)
        # Test both host-1, host-2 in valid list
        unhosted_gateways = self.nb_ovn_idl.get_unhosted_gateways(
            ['host-1', 'host-2'])
        expected = {
            utils.ovn_lrouter_port_name('orp-id-a3'): {
                ovn_const.OVN_GATEWAY_CHASSIS_KEY:
                ovn_const.OVN_GATEWAY_INVALID_CHASSIS
            }
        }
        self.assertItemsEqual(unhosted_gateways, expected)
        # Schedule unhosted_gateways on host-2
        for unhosted_gateway in unhosted_gateways:
            router_row = self._find_ovsdb_fake_row(self.lrp_table, 'name',
                                                   unhosted_gateway)
            setattr(router_row, 'options',
                    {ovn_const.OVN_GATEWAY_CHASSIS_KEY: 'host-2'})
        unhosted_gateways = self.nb_ovn_idl.get_unhosted_gateways(
            ['host-1', 'host-2'])
        self.assertItemsEqual(unhosted_gateways, {})

    def test_get_subnet_dhcp_options(self):
        self._load_nb_db()
        subnet_options = self.nb_ovn_idl.get_subnet_dhcp_options(
            'subnet-id-10-0-2-0')
        expected_row = self._find_ovsdb_fake_row(self.dhcp_table, 'cidr',
                                                 '10.0.2.0/24')
        self.assertEqual(
            {
                'cidr': expected_row.cidr,
                'external_ids': expected_row.external_ids,
                'options': expected_row.options,
                'uuid': expected_row.uuid
            }, subnet_options)
        subnet_options = self.nb_ovn_idl.get_subnet_dhcp_options(
            'subnet-id-11-0-2-0')
        self.assertIsNone(subnet_options)
        subnet_options = self.nb_ovn_idl.get_subnet_dhcp_options(
            'port-id-30-0-1-0')
        self.assertIsNone(subnet_options)

    def test_get_subnets_dhcp_options(self):
        self._load_nb_db()
        get_row_dict = lambda row: {
            'cidr': row.cidr,
            'external_ids': row.external_ids,
            'options': row.options,
            'uuid': row.uuid
        }

        subnets_options = self.nb_ovn_idl.get_subnets_dhcp_options(
            ['subnet-id-10-0-1-0', 'subnet-id-10-0-2-0'])
        expected_rows = [
            get_row_dict(
                self._find_ovsdb_fake_row(self.dhcp_table, 'cidr', cidr))
            for cidr in ('10.0.1.0/24', '10.0.2.0/24')
        ]
        self.assertItemsEqual(expected_rows, subnets_options)

        subnets_options = self.nb_ovn_idl.get_subnets_dhcp_options(
            ['subnet-id-11-0-2-0', 'subnet-id-20-0-1-0'])
        expected_row = get_row_dict(
            self._find_ovsdb_fake_row(self.dhcp_table, 'cidr', '20.0.1.0/24'))
        self.assertItemsEqual([expected_row], subnets_options)

        subnets_options = self.nb_ovn_idl.get_subnets_dhcp_options(
            ['port-id-30-0-1-0', 'fake-not-exist'])
        self.assertEqual([], subnets_options)

    def test_get_all_dhcp_options(self):
        self._load_nb_db()
        dhcp_options = self.nb_ovn_idl.get_all_dhcp_options()
        self.assertEqual(len(dhcp_options['subnets']), 3)
        self.assertEqual(len(dhcp_options['ports_v4']), 2)

    def test_compose_dhcp_options_commands(self):
        # TODO(azbiswas): Implement in seperate patch
        pass

    def test_get_address_sets(self):
        self._load_nb_db()
        address_sets = self.nb_ovn_idl.get_address_sets()
        self.assertEqual(len(address_sets), 4)
예제 #32
0
    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

            router_name = utils.ovn_gateway_router_name(router_id)
            transit_net_ports = self._get_transit_network_ports()
            nexthop = transit_net_ports['dtsp']['ip']

            if self._is_snat_enabled(router):
                self._update_snat_and_static_routes_for_networks(
                    context, router, networks=[cidr], nexthop=nexthop,
                    enable_snat=False, update_static_routes=True)
            else:
                route = {'destination': cidr, 'nexthop': nexthop}
                self._update_lrouter_routes(
                    context, router_id, add=[route], remove=[],
                    lrouter_name=router_name)
        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
예제 #33
0
 def _find_router_port_row_by_port_id(self, port_id):
     for row in self.nb_api._tables['Logical_Router_Port'].rows.values():
         if row.name == utils.ovn_lrouter_port_name(port_id):
             return row
예제 #34
0
    def sync_routers_and_rports(self, ctx):
        """Sync Routers between neutron and NB.

        @param ctx: neutron context
        @type  ctx: object of type neutron.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
        """
        LOG.debug('OVN-NB Sync Routers and Router ports started')
        db_routers = {}
        db_router_ports = {}
        for router in self.core_plugin.get_routers(ctx):
            db_routers[router['id']] = router

        interfaces = self.core_plugin._get_sync_interfaces(ctx,
                                                           db_routers.keys())
        for interface in interfaces:
            db_router_ports[interface['id']] = interface
        lrouters = self.ovn_api.get_all_logical_routers_with_rports()
        del_lrouters_list = []
        del_lrouter_ports_list = []
        for lrouter in lrouters:
            if lrouter['name'] in db_routers:
                for lrport in lrouter['ports']:
                    if lrport in db_router_ports:
                        del db_router_ports[lrport]
                    else:
                        del_lrouter_ports_list.append(
                            {'port': lrport, 'lrouter': lrouter['name']})
                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.core_plugin.create_lrouter_in_ovn(router)
                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.core_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'])

        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))
        LOG.debug('OVN-NB Sync routers and router ports finished')
예제 #35
0
    def sync_routers_and_rports(self, ctx):
        """Sync Routers between neutron and NB.

        @param ctx: neutron context
        @type  ctx: object of type neutron.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')
        db_routers = {}
        db_router_ports = {}
        for router in self.l3_plugin.get_routers(ctx):
            db_routers[router['id']] = router

        interfaces = self.l3_plugin._get_sync_interfaces(
            ctx, db_routers.keys())
        for interface in interfaces:
            db_router_ports[interface['id']] = interface
        lrouters = self.ovn_api.get_all_logical_routers_with_rports()
        del_lrouters_list = []
        del_lrouter_ports_list = []
        update_sroutes_list = []
        for lrouter in lrouters:
            if lrouter['name'] in db_routers:
                for lrport in lrouter['ports']:
                    if lrport in db_router_ports:
                        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 = []
                ovn_routes = lrouter['static_routes']
                add_routes, del_routes = n_utils.diff_list_of_dict(
                    ovn_routes, db_routes)
                update_sroutes_list.append({
                    'id': lrouter['name'],
                    'add': add_routes,
                    'del': del_routes
                })
                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': []
                        })
                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'])

        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']))
        LOG.debug('OVN-NB Sync routers and router ports finished')
예제 #36
0
    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()))