Ejemplo n.º 1
0
    def delete_port_postcommit(self, context):
        """Delete a port.

        :param context: PortContext instance describing the current
        state of the port, prior to the call to delete it.

        Called after the transaction completes. Call can block, though
        will block the entire process so care should be taken to not
        drastically affect performance.  Runtime errors are not
        expected, and will not prevent the resource from being
        deleted.
        """
        port = context.current
        self._delete_lsp_dhcpv4_options(port)
        with self._nb_ovn.transaction(check_error=True) as txn:
            txn.add(self._nb_ovn.delete_lswitch_port(port['id'],
                    utils.ovn_name(port['network_id'])))
            txn.add(self._nb_ovn.delete_acl(
                    utils.ovn_name(port['network_id']), port['id']))

            if port.get('fixed_ips'):
                addresses = ovn_acl.acl_port_ips(port)
                for sg_id in port.get('security_groups', []):
                    for ip_version in addresses:
                        if addresses[ip_version]:
                            txn.add(self._nb_ovn.update_address_set(
                                name=utils.ovn_addrset_name(sg_id, ip_version),
                                addrs_add=None,
                                addrs_remove=addresses[ip_version]))
Ejemplo n.º 2
0
 def _get_update_data_without_compare(self):
     lswitch_ovsdb_dict = {}
     for switch_name in self.lswitch_names:
         switch_name = utils.ovn_name(switch_name)
         lswitch = idlutils.row_by_value(self.api.idl, 'Logical_Switch',
                                         'name', switch_name)
         lswitch_ovsdb_dict[switch_name] = lswitch
     if self.is_add_acl:
         acl_add_values_dict = {}
         for port in self.port_list:
             switch_name = utils.ovn_name(port['network_id'])
             if switch_name not in acl_add_values_dict:
                 acl_add_values_dict[switch_name] = []
             if port['id'] in self.acl_new_values_dict:
                 acl_add_values_dict[switch_name].append(
                     self.acl_new_values_dict[port['id']])
         acl_del_objs_dict = {}
     else:
         acl_add_values_dict = {}
         acl_del_objs_dict = {}
         del_acl_matches = []
         for acl_dict in self.acl_new_values_dict.values():
             del_acl_matches.append(acl_dict['match'])
         for switch_name, lswitch in six.iteritems(lswitch_ovsdb_dict):
             if switch_name not in acl_del_objs_dict:
                 acl_del_objs_dict[switch_name] = []
             lswitch.verify('acls')
             acls = getattr(lswitch, 'acls', [])
             for acl in acls:
                 if getattr(acl, 'match') in del_acl_matches:
                     acl_del_objs_dict[switch_name].append(acl)
     return lswitch_ovsdb_dict, acl_del_objs_dict, acl_add_values_dict
Ejemplo n.º 3
0
def add_acl_dhcp(port, subnet, ovn_dhcp=True):
    # Allow DHCP requests for OVN native DHCP service, while responses are
    # allowed in ovn-northd.
    # Allow both DHCP requests and responses to pass for other DHCP services.
    # We do this even if DHCP isn't enabled for the subnet
    acl_list = []
    if not ovn_dhcp:
        acl = {"lswitch": utils.ovn_name(port['network_id']),
               "lport": port['id'],
               "priority": ovn_const.ACL_PRIORITY_ALLOW,
               "action": ovn_const.ACL_ACTION_ALLOW,
               "log": False,
               "name": [],
               "severity": [],
               "direction": 'to-lport',
               "match": ('outport == "%s" && ip4 && ip4.src == %s && '
                         'udp && udp.src == 67 && udp.dst == 68'
                         ) % (port['id'], subnet['cidr']),
               "external_ids": {'neutron:lport': port['id']}}
        acl_list.append(acl)
    acl = {"lswitch": utils.ovn_name(port['network_id']),
           "lport": port['id'],
           "priority": ovn_const.ACL_PRIORITY_ALLOW,
           "action": ovn_const.ACL_ACTION_ALLOW,
           "log": False,
           "name": [],
           "severity": [],
           "direction": 'from-lport',
           "match": ('inport == "%s" && ip4 && '
                     'ip4.dst == {255.255.255.255, %s} && '
                     'udp && udp.src == 68 && udp.dst == 67'
                     ) % (port['id'], subnet['cidr']),
           "external_ids": {'neutron:lport': port['id']}}
    acl_list.append(acl)
    return acl_list
Ejemplo n.º 4
0
    def delete_port_postcommit(self, context):
        """Delete a port.

        :param context: PortContext instance describing the current
        state of the port, prior to the call to delete it.

        Called after the transaction completes. Call can block, though
        will block the entire process so care should be taken to not
        drastically affect performance.  Runtime errors are not
        expected, and will not prevent the resource from being
        deleted.
        """
        port = context.current
        with self._nb_ovn.transaction(check_error=True) as txn:
            txn.add(self._nb_ovn.delete_lswitch_port(port['id'],
                    utils.ovn_name(port['network_id'])))
            txn.add(self._nb_ovn.delete_acl(
                    utils.ovn_name(port['network_id']), port['id']))

            if port.get('fixed_ips'):
                addresses = ovn_acl.acl_port_ips(port)
                for sg_id in port.get('security_groups', []):
                    for ip_version in addresses:
                        if addresses[ip_version]:
                            txn.add(self._nb_ovn.update_address_set(
                                name=utils.ovn_addrset_name(sg_id, ip_version),
                                addrs_add=None,
                                addrs_remove=addresses[ip_version]))

            # NOTE(lizk): Always try to clean port dhcp options, to make sure
            # no orphaned DHCP_Options row related to port left behind, which
            # may be created in get_port_dhcpv4_options.
            cmd = self._get_delete_lsp_dhcpv4_options_cmd(port)
            if cmd:
                txn.add(cmd)
Ejemplo n.º 5
0
 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)
Ejemplo n.º 6
0
 def _get_update_data_without_compare(self):
     lswitch_ovsdb_dict = {}
     for switch_name in self.lswitch_names:
         switch_name = utils.ovn_name(switch_name)
         lswitch = idlutils.row_by_value(self.api.idl, 'Logical_Switch',
                                         'name', switch_name)
         lswitch_ovsdb_dict[switch_name] = lswitch
     if self.is_add_acl:
         acl_add_values_dict = {}
         for port in self.port_list:
             switch_name = utils.ovn_name(port['network_id'])
             if switch_name not in acl_add_values_dict:
                 acl_add_values_dict[switch_name] = []
             if port['id'] in self.acl_new_values_dict:
                 acl_add_values_dict[switch_name].append(
                     self.acl_new_values_dict[port['id']])
         acl_del_objs_dict = {}
     else:
         acl_add_values_dict = {}
         acl_del_objs_dict = {}
         del_acl_matches = []
         for acl_dict in self.acl_new_values_dict.values():
             del_acl_matches.append(acl_dict['match'])
         for switch_name, lswitch in six.iteritems(lswitch_ovsdb_dict):
             if switch_name not in acl_del_objs_dict:
                 acl_del_objs_dict[switch_name] = []
             lswitch.verify('acls')
             acls = getattr(lswitch, 'acls', [])
             for acl in acls:
                 if getattr(acl, 'match') in del_acl_matches:
                     acl_del_objs_dict[switch_name].append(acl)
     return lswitch_ovsdb_dict, acl_del_objs_dict, acl_add_values_dict
Ejemplo n.º 7
0
    def delete_port_postcommit(self, context):
        """Delete a port.

        :param context: PortContext instance describing the current
        state of the port, prior to the call to delete it.

        Called after the transaction completes. Call can block, though
        will block the entire process so care should be taken to not
        drastically affect performance.  Runtime errors are not
        expected, and will not prevent the resource from being
        deleted.
        """
        port = context.current
        with self._nb_ovn.transaction(check_error=True) as txn:
            txn.add(self._nb_ovn.delete_lswitch_port(port['id'],
                    utils.ovn_name(port['network_id'])))
            txn.add(self._nb_ovn.delete_acl(
                    utils.ovn_name(port['network_id']), port['id']))

            if port.get('fixed_ips'):
                addresses = ovn_acl.acl_port_ips(port)
                for sg_id in port.get('security_groups', []):
                    for ip_version in addresses:
                        if addresses[ip_version]:
                            txn.add(self._nb_ovn.update_address_set(
                                name=utils.ovn_addrset_name(sg_id, ip_version),
                                addrs_add=None,
                                addrs_remove=addresses[ip_version]))
Ejemplo n.º 8
0
def add_acl_dhcp(port, subnet):
    # Allow DHCP responses through from source IPs on the local subnet.
    # We do this even if DHCP isn't enabled.  It could be enabled later.
    # We could hook into handling when it's enabled/disabled for a subnet,
    # but this code is temporary anyway.  It's likely no longer needed
    # once OVN native DHCP support merges, which is under development and
    # review already.
    # TODO(russellb) Remove this once OVN native DHCP support is merged.
    acl_list = []
    acl = {"lswitch": utils.ovn_name(port['network_id']),
           "lport": port['id'],
           "priority": ovn_const.ACL_PRIORITY_ALLOW,
           "action": ovn_const.ACL_ACTION_ALLOW,
           "log": False,
           "direction": 'to-lport',
           "match": ('outport == "%s" && ip4 && ip4.src == %s && '
                     'udp && udp.src == 67 && udp.dst == 68'
                     ) % (port['id'], subnet['cidr']),
           "external_ids": {'neutron:lport': port['id']}}
    acl_list.append(acl)
    acl = {"lswitch": utils.ovn_name(port['network_id']),
           "lport": port['id'],
           "priority": ovn_const.ACL_PRIORITY_ALLOW,
           "action": ovn_const.ACL_ACTION_ALLOW,
           "log": False,
           "direction": 'from-lport',
           "match": ('inport == "%s" && ip4 && '
                     '(ip4.dst == 255.255.255.255 || '
                     'ip4.dst == %s) && '
                     'udp && udp.src == 68 && udp.dst == 67'
                     ) % (port['id'], subnet['cidr']),
           "external_ids": {'neutron:lport': port['id']}}
    acl_list.append(acl)
    return acl_list
Ejemplo n.º 9
0
    def delete_port(self, context, port_id, l3_port_check=True):
        port = self.get_port(context, port_id)
        try:
            # If this is a port on a provider network, we just need to delete
            # the special logical switch for this port, and the 2 ports on the
            # switch will get garbage collected.  Note that if the switch
            # doesn't exist, we'll get an exception without actually having to
            # execute a transaction with the remote db.  The check is local.
            self._ovn.delete_lswitch(utils.ovn_name(port["id"]), if_exists=False).execute(
                check_error=True, log_errors=False
            )
        except RuntimeError:
            # If the switch doesn't exist, we'll get a RuntimeError, meaning
            # we just need to delete a port.
            with self._ovn.transaction(check_error=True) as txn:
                txn.add(self._ovn.delete_lport(port_id, utils.ovn_name(port["network_id"])))
                txn.add(self._ovn.delete_acl(utils.ovn_name(port["network_id"]), port["id"]))

        # NOTE(russellb): If this port had a security group applied with a rule
        # that used "remote_group_id", technically we could update the ACLs for
        # all ports on that security group to remove references to this port
        # we're deleting.  However, it's harmless to leave it for now and saves
        # some additional churn in the OVN db.  References to this port will
        # get automatically removed the next time something else triggers a
        # refresh of ACLs for ports on that security group.

        with context.session.begin(subtransactions=True):
            self.disassociate_floatingips(context, port_id)
            super(OVNPlugin, self).delete_port(context, port_id)
Ejemplo n.º 10
0
    def delete_port_postcommit(self, context):
        """Delete a port.

        :param context: PortContext instance describing the current
        state of the port, prior to the call to delete it.

        Called after the transaction completes. Call can block, though
        will block the entire process so care should be taken to not
        drastically affect performance.  Runtime errors are not
        expected, and will not prevent the resource from being
        deleted.
        """
        port = context.current
        with self._nb_ovn.transaction(check_error=True) as txn:
            txn.add(self._nb_ovn.delete_lswitch_port(port['id'],
                    utils.ovn_name(port['network_id'])))
            txn.add(self._nb_ovn.delete_acl(
                    utils.ovn_name(port['network_id']), port['id']))

        admin_context = n_context.get_admin_context()
        sg_ids = port.get('security_groups', [])
        num_fixed_ips = len(port.get('fixed_ips'))
        if num_fixed_ips:
            for sg_id in sg_ids:
                ovn_acl.refresh_remote_security_group(
                    self._plugin, admin_context, self._nb_ovn, sg_id)
Ejemplo n.º 11
0
    def delete_port(self, context, port_id, l3_port_check=True):
        port = self.get_port(context, port_id)
        try:
            # If this is a port on a provider network, we just need to delete
            # the special logical switch for this port, and the 2 ports on the
            # switch will get garbage collected.  Note that if the switch
            # doesn't exist, we'll get an exception without actually having to
            # execute a transaction with the remote db.  The check is local.
            self._ovn.delete_lswitch(
                utils.ovn_name(port['id']), if_exists=False).execute(
                    check_error=True, log_errors=False)
        except RuntimeError:
            # If the switch doesn't exist, we'll get a RuntimeError, meaning
            # we just need to delete a port.
            with self._ovn.transaction(check_error=True) as txn:
                txn.add(self._ovn.delete_lport(port_id,
                        utils.ovn_name(port['network_id'])))
                txn.add(self._ovn.delete_acl(
                        utils.ovn_name(port['network_id']), port['id']))

        sg_ids = port.get('security_groups', [])

        with context.session.begin(subtransactions=True):
            self.disassociate_floatingips(context, port_id)
            super(OVNPlugin, self).delete_port(context, port_id)

        for sg_id in sg_ids:
            self._refresh_remote_security_group(context, sg_id)
Ejemplo n.º 12
0
 def test_get_unhosted_routers(self):
     self._load_nb_db()
     # Test only host-1 in the valid list
     unhosted_routers = self.nb_ovn_idl.get_unhosted_routers(['host-1'])
     expected = {
         utils.ovn_name('lr-id-c'): 'host-2',
         utils.ovn_name('lr-id-d'): {
             'chassis': ovn_const.OVN_GATEWAY_INVALID_CHASSIS
         }
     }
     self.assertItemsEqual(unhosted_routers, expected)
     # Test both host-1, host-2 in valid list
     unhosted_routers = self.nb_ovn_idl.get_unhosted_routers(
         ['host-1', 'host-2'])
     expected = {
         utils.ovn_name('lr-id-d'): {
             'chassis': ovn_const.OVN_GATEWAY_INVALID_CHASSIS
         }
     }
     self.assertItemsEqual(unhosted_routers, expected)
     # Schedule unhosted_routers on host-2
     for unhosted_router in unhosted_routers:
         router_row = self._find_ovsdb_fake_row(self.lrouter_table, 'name',
                                                unhosted_router)
         setattr(router_row, 'options', {'chassis': 'host-2'})
     unhosted_routers = self.nb_ovn_idl.get_unhosted_routers(
         ['host-1', 'host-2'])
     self.assertItemsEqual(unhosted_routers, {})
Ejemplo n.º 13
0
    def delete_port_postcommit(self, context):
        """Delete a port.

        :param context: PortContext instance describing the current
        state of the port, prior to the call to delete it.

        Called after the transaction completes. Call can block, though
        will block the entire process so care should be taken to not
        drastically affect performance.  Runtime errors are not
        expected, and will not prevent the resource from being
        deleted.
        """
        port = context.current
        with self._ovn.transaction(check_error=True) as txn:
            txn.add(
                self._ovn.delete_lport(port['id'],
                                       utils.ovn_name(port['network_id'])))
            txn.add(
                self._ovn.delete_acl(utils.ovn_name(port['network_id']),
                                     port['id']))

        admin_context = n_context.get_admin_context()
        sg_ids = port.get('security_groups', [])
        num_fixed_ips = len(port.get('fixed_ips'))
        if num_fixed_ips:
            for sg_id in sg_ids:
                self._refresh_remote_security_group(admin_context, sg_id)
Ejemplo n.º 14
0
def add_acl_dhcp(port, subnet):
    # Allow DHCP responses through from source IPs on the local subnet.
    # We do this even if DHCP isn't enabled.  It could be enabled later.
    # We could hook into handling when it's enabled/disabled for a subnet,
    # but this code is temporary anyway.  It's likely no longer needed
    # once OVN native DHCP support merges, which is under development and
    # review already.
    # TODO(russellb) Remove this once OVN native DHCP support is merged.
    acl_list = []
    acl = {"lswitch": utils.ovn_name(port['network_id']),
           "lport": port['id'],
           "priority": ovn_const.ACL_PRIORITY_ALLOW,
           "action": ovn_const.ACL_ACTION_ALLOW,
           "log": False,
           "direction": 'to-lport',
           "match": ('outport == "%s" && ip4 && ip4.src == %s && '
                     'udp && udp.src == 67 && udp.dst == 68'
                     ) % (port['id'], subnet['cidr']),
           "external_ids": {'neutron:lport': port['id']}}
    acl_list.append(acl)
    acl = {"lswitch": utils.ovn_name(port['network_id']),
           "lport": port['id'],
           "priority": ovn_const.ACL_PRIORITY_ALLOW,
           "action": ovn_const.ACL_ACTION_ALLOW,
           "log": False,
           "direction": 'from-lport',
           "match": ('inport == "%s" && ip4 && '
                     '(ip4.dst == 255.255.255.255 || '
                     'ip4.dst == %s) && '
                     'udp && udp.src == 68 && udp.dst == 67'
                     ) % (port['id'], subnet['cidr']),
           "external_ids": {'neutron:lport': port['id']}}
    acl_list.append(acl)
    return acl_list
Ejemplo n.º 15
0
def add_acl_dhcp(port, subnet):
    # Allow DHCP responses through from source IPs on the local subnet.
    # We do this even if DHCP isn't enabled for the subnet.  It could be
    # enabled later. We could hook into handling when it's enabled/disabled
    # for a subnet, but this only used when OVN native DHCP is disabled.
    acl_list = []
    acl = {
        "lswitch":
        utils.ovn_name(port['network_id']),
        "lport":
        port['id'],
        "priority":
        ovn_const.ACL_PRIORITY_ALLOW,
        "action":
        ovn_const.ACL_ACTION_ALLOW,
        "log":
        False,
        "direction":
        'to-lport',
        "match": ('outport == "%s" && ip4 && ip4.src == %s && '
                  'udp && udp.src == 67 && udp.dst == 68') %
        (port['id'], subnet['cidr']),
        "external_ids": {
            'neutron:lport': port['id']
        }
    }
    acl_list.append(acl)
    acl = {
        "lswitch":
        utils.ovn_name(port['network_id']),
        "lport":
        port['id'],
        "priority":
        ovn_const.ACL_PRIORITY_ALLOW,
        "action":
        ovn_const.ACL_ACTION_ALLOW,
        "log":
        False,
        "direction":
        'from-lport',
        "match": ('inport == "%s" && ip4 && '
                  '(ip4.dst == 255.255.255.255 || '
                  'ip4.dst == %s) && '
                  'udp && udp.src == 68 && udp.dst == 67') %
        (port['id'], subnet['cidr']),
        "external_ids": {
            'neutron:lport': port['id']
        }
    }
    acl_list.append(acl)
    return acl_list
Ejemplo n.º 16
0
    def get_acls_for_lswitches(self, lswitch_names):
        """Get the existing set of acls that belong to the logical switches

        @param lswitch_names: List of logical switch names
        @type lswitch_names: []
        @var acl_values_dict: A dictionary indexed by port_id containing the
                              list of acl values in string format that belong
                              to that port
        @var acl_obj_dict: A dictionary indexed by acl value containing the
                           corresponding acl idl object.
        @var lswitch_ovsdb_dict: A dictionary mapping from logical switch
                                 name to lswitch idl object
        @return: (acl_values_dict, acl_obj_dict, lswitch_ovsdb_dict)
        """
        acl_values_dict = {}
        acl_obj_dict = {}
        lswitch_ovsdb_dict = {}
        for lswitch_name in lswitch_names:
            try:
                lswitch = idlutils.row_by_value(self.idl, 'Logical_Switch',
                                                'name',
                                                utils.ovn_name(lswitch_name))
            except idlutils.RowNotFound:
                # It is possible for the logical switch to be deleted
                # while we are searching for it by name in idl.
                continue
            lswitch_ovsdb_dict[lswitch_name] = lswitch
            acls = getattr(lswitch, 'acls', [])

            # Iterate over each acl in a lswitch and store the acl in
            # a key:value representation for e.g. acl_string. This
            # key:value representation can invoke the code -
            # self._ovn.add_acl(**acl_string)
            for acl in acls:
                ext_ids = getattr(acl, 'external_ids', {})
                port_id = ext_ids.get('neutron:lport')
                acl_list = acl_values_dict.setdefault(port_id, [])
                acl_string = {
                    'lport': port_id,
                    'lswitch': utils.ovn_name(lswitch_name)
                }
                for acl_key in getattr(acl, "_data", {}):
                    try:
                        acl_string[acl_key] = getattr(acl, acl_key)
                    except AttributeError:
                        pass
                acl_obj_dict[str(acl_string)] = acl
                acl_list.append(acl_string)
        return acl_values_dict, acl_obj_dict, lswitch_ovsdb_dict
Ejemplo n.º 17
0
    def get_acls_for_lswitches(self, lswitch_names):
        """Get the existing set of acls that belong to the logical switches

        @param lswitch_names: List of logical switch names
        @type lswitch_names: []
        @var acl_values_dict: A dictionary indexed by port_id containing the
                              list of acl values in string format that belong
                              to that port
        @var acl_obj_dict: A dictionary indexed by acl value containing the
                           corresponding acl idl object.
        @var lswitch_ovsdb_dict: A dictionary mapping from logical switch
                                 name to lswitch idl object
        @return: (acl_values_dict, acl_obj_dict, lswitch_ovsdb_dict)
        """
        acl_values_dict = {}
        acl_obj_dict = {}
        lswitch_ovsdb_dict = {}
        for lswitch_name in lswitch_names:
            try:
                lswitch = idlutils.row_by_value(self.idl,
                                                'Logical_Switch',
                                                'name',
                                                utils.ovn_name(lswitch_name))
            except idlutils.RowNotFound:
                # It is possible for the logical switch to be deleted
                # while we are searching for it by name in idl.
                continue
            lswitch_ovsdb_dict[lswitch_name] = lswitch
            acls = getattr(lswitch, 'acls', [])

            # Iterate over each acl in a lswitch and store the acl in
            # a key:value representation for e.g. acl_string. This
            # key:value representation can invoke the code -
            # self._ovn.add_acl(**acl_string)
            for acl in acls:
                ext_ids = getattr(acl, 'external_ids', {})
                port_id = ext_ids.get('neutron:lport')
                acl_list = acl_values_dict.setdefault(port_id, [])
                acl_string = {'lport': port_id,
                              'lswitch': utils.ovn_name(lswitch_name)}
                for acl_key in six.iterkeys(getattr(acl, "_data", {})):
                    try:
                        acl_string[acl_key] = getattr(acl, acl_key)
                    except AttributeError:
                        pass
                acl_obj_dict[str(acl_string)] = acl
                acl_list.append(acl_string)
        return acl_values_dict, acl_obj_dict, lswitch_ovsdb_dict
Ejemplo n.º 18
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
Ejemplo n.º 19
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
        """
        lrouter = utils.ovn_name(router_id)
        if not networks:
            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))
Ejemplo n.º 20
0
    def _sync_networks(self, ctx):
        LOG.debug("OVN-NB Sync networks started")

        lswitches = self.ovn_api.get_all_logical_switches_ids()

        for network in self.core_plugin.get_networks(ctx):
            try:
                if self.mode == SYNC_MODE_REPAIR:
                    self.core_plugin.create_network_in_ovn(network)
                res = lswitches.pop(utils.ovn_name(
                                    network['id']), None)
                if self.mode == SYNC_MODE_LOG:
                    if res is None:
                        LOG.warn(_LW("Network found in Neutron but not in OVN"
                                     "DB, network_id=%s"),
                                 network['id'])

            except RuntimeError:
                LOG.warn(_LW("Create network failed for "
                             "network %s"), network['id'])

        # Only delete logical switch if it was previously created by neutron
        with self.ovn_api.transaction() as txn:
            for lswitch, ext_ids in lswitches.items():
                if ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY in ext_ids:
                    if self.mode == SYNC_MODE_REPAIR:
                        txn.add(self.ovn_api.delete_lswitch(lswitch))
                    if self.mode == SYNC_MODE_LOG:
                        LOG.warn(_LW("Network found in OVN but not in Neutron,"
                                     " network_name=%s"),
                                 (ext_ids
                                  [ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY]))

        LOG.debug("OVN-NB Sync networks finished")
Ejemplo n.º 21
0
 def _update_floating_ip_in_ovn(self, context, router_id, update,
                                associate=True):
     fip_apis = {}
     fip_apis['nat'] = self._ovn.add_nat_rule_in_lrouter if \
         associate else self._ovn.delete_nat_rule_in_lrouter
     fip_apis['garp'] = self._ovn.add_nat_ip_to_lrport_peer_options if \
         associate else self._ovn.delete_nat_ip_from_lrport_peer_options
     gw_lrouter_name = utils.ovn_gateway_router_name(router_id)
     try:
         with self._ovn.transaction(check_error=True) as txn:
             if associate:
                 # TODO(chandrav): Since the floating ip port is not
                 # bound to any chassis, packets destined to floating ip
                 # will be dropped. To overcome this, delete the floating
                 # ip port. Proper fix for this would be to redirect packets
                 # destined to floating ip to the router port. This would
                 # require changes in ovn-northd.
                 txn.add(self._ovn.delete_lswitch_port(
                     update['fip_port_id'],
                     utils.ovn_name(update['fip_net_id'])))
             txn.add(fip_apis['nat'](gw_lrouter_name, type='dnat_and_snat',
                                     logical_ip=update['logical_ip'],
                                     external_ip=update['external_ip']))
             txn.add(fip_apis['garp'](update['gw_port_id'],
                                      nat_ip=update['external_ip']))
     except Exception:
         with excutils.save_and_reraise_exception():
             LOG.error(_LE('Unable to update NAT rule in gateway router'))
Ejemplo n.º 22
0
 def test_get_router_chassis_binding(self):
     self._load_nb_db()
     chassis = self.nb_ovn_idl.get_router_chassis_binding(
         utils.ovn_name('lr-id-a'))
     self.assertEqual(chassis, 'host-1')
     chassis = self.nb_ovn_idl.get_router_chassis_binding(
         utils.ovn_name('lr-id-c'))
     self.assertEqual(chassis, 'host-2')
     chassis = self.nb_ovn_idl.get_router_chassis_binding(
         utils.ovn_name('lr-id-d'))
     self.assertEqual(chassis, None)
     chassis = self.nb_ovn_idl.get_router_chassis_binding(
         utils.ovn_name('lr-id-e'))
     self.assertEqual(chassis, None)
     chassis = self.nb_ovn_idl.get_router_chassis_binding('bad')
     self.assertEqual(chassis, None)
Ejemplo n.º 23
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))
Ejemplo n.º 24
0
    def _get_lrouter_connected_to_nexthop(self, context, router_id,
                                          router_ports, nexthop):
        """Find lrouter connected to nexthop

        @param router_id: router id
        @param router_ports: router ports in router
        @param nexthop: nexthop
        @return: distributed logical router name or gateway router name or None
        """

        lrouter_name = None
        for port in router_ports:
            found_nexthop = False
            for fixed_ip in port.get('fixed_ips', []):
                subnet_id = fixed_ip['subnet_id']
                subnet = self._plugin.get_subnet(context.elevated(), subnet_id)
                network = netaddr.IPNetwork(subnet['cidr'])
                if netaddr.IPAddress(nexthop) in network:
                    if port['device_owner'] == n_const.DEVICE_OWNER_ROUTER_GW:
                        # Nexthop is in external network
                        lrouter_name = utils.ovn_gateway_router_name(router_id)
                    else:
                        # Next hop is in tenant network
                        lrouter_name = utils.ovn_name(router_id)
                    found_nexthop = True
                    break
            if found_nexthop:
                break
        if not lrouter_name:
            raise exc.L3RouterPluginStaticRouteError(nexthop=nexthop,
                                                     router=router_id)

        return lrouter_name
Ejemplo n.º 25
0
 def delete_network(self, context, network_id):
     with context.session.begin(subtransactions=True):
         super(OVNPlugin, self).delete_network(context,
                                               network_id)
     self._ovn.delete_lswitch(
         utils.ovn_name(network_id), if_exists=True).execute(
             check_error=True)
Ejemplo n.º 26
0
 def test_get_router_chassis_binding(self):
     self._load_nb_db()
     chassis = self.nb_ovn_idl.get_router_chassis_binding(
         utils.ovn_name('lr-id-a'))
     self.assertEqual(chassis, 'host-1')
     chassis = self.nb_ovn_idl.get_router_chassis_binding(
         utils.ovn_name('lr-id-c'))
     self.assertEqual(chassis, 'host-2')
     chassis = self.nb_ovn_idl.get_router_chassis_binding(
         utils.ovn_name('lr-id-d'))
     self.assertEqual(chassis, None)
     chassis = self.nb_ovn_idl.get_router_chassis_binding(
         utils.ovn_name('lr-id-e'))
     self.assertEqual(chassis, None)
     chassis = self.nb_ovn_idl.get_router_chassis_binding('bad')
     self.assertEqual(chassis, None)
Ejemplo n.º 27
0
 def _add_acl_dhcp(self, context, port, txn, subnet_cache):
     # Allow DHCP responses through from source IPs on the local subnet.
     # We do this even if DHCP isn't enabled.  It could be enabled later.
     # We could hook into handling when it's enabled/disabled for a subnet,
     # but this code is temporary anyway.  It's likely no longer needed
     # once OVN native DHCP support merges, which is under development and
     # review already.
     # TODO(russellb) Remove this once OVN native DHCP support is merged.
     for ip in port['fixed_ips']:
         if ip['subnet_id'] in subnet_cache:
             subnet = subnet_cache[ip['subnet_id']]
         else:
             subnet = self.get_subnet(context, ip['subnet_id'])
             subnet_cache[ip['subnet_id']] = subnet
         if subnet['ip_version'] != 4:
             continue
         txn.add(self._ovn.add_acl(
             lswitch=utils.ovn_name(port['network_id']),
             lport=port['id'],
             priority=ACL_PRIORITY_ALLOW,
             action='allow',
             log=False,
             direction='to-lport',
             match=('outport == "%s" && ip4 && ip4.src == %s && '
                    'udp && udp.src == 67 && udp.dst == 68'
                    ) % (port['id'], subnet['cidr']),
             external_ids={'neutron:lport': port['id']}))
Ejemplo n.º 28
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))

        self._ovn.add_lrouter_port(port['id'], lrouter,
                                   mac=port['mac_address'],
                                   network=network).execute(check_error=True)
        # TODO(chandrav)
        # The following code is to update the options column in the lport
        # table with {router-port: "UUID of logical_router_port"}. Ideally this
        # should have been handled ine one transaction with add_lrouter_port,
        # but due to a bug in idl, we are forced to update it in a separate
        # transaction. After the transaction is committed idl does not update
        # the UUID that is part of a string from old to new.
        self._ovn.set_lrouter_port_in_lport(port['id']).execute(
            check_error=True)
        return router_interface_info
 def test_acl_update_compare_acls(self):
     fake_sg_rule = \
         fakes.FakeSecurityGroupRule.create_one_security_group_rule().info()
     fake_port = fakes.FakePort.create_one_port().info()
     fake_add_acl = fakes.FakeOvsdbRow.create_one_ovsdb_row(
         attrs={'match': 'add_acl'})
     fake_del_acl = fakes.FakeOvsdbRow.create_one_ovsdb_row(
         attrs={'match': 'del_acl'})
     fake_lswitch = fakes.FakeOvsdbRow.create_one_ovsdb_row(
         attrs={'name': ovn_utils.ovn_name(fake_port['network_id']),
                'acls': []})
     add_acl = ovn_acl.add_sg_rule_acl_for_port(
         fake_port, fake_sg_rule, 'add_acl')
     self.ovn_api.get_acls_for_lswitches.return_value = (
         {fake_port['id']: [fake_del_acl.match]},
         {fake_del_acl.match: fake_del_acl},
         {fake_lswitch.name.replace('neutron-', ''): fake_lswitch})
     cmd = commands.UpdateACLsCommand(
         self.ovn_api, [fake_port['network_id']],
         [fake_port], {fake_port['id']: [add_acl]},
         need_compare=True)
     self.transaction.insert.return_value = fake_add_acl
     cmd.run_idl(self.transaction)
     self.transaction.insert.assert_called_once_with(
         self.ovn_api.acl_table)
     fake_lswitch.verify.assert_called_with('acls')
     self.assertEqual([fake_add_acl.uuid], fake_lswitch.acls)
Ejemplo n.º 30
0
    def create_lrouter_in_ovn(self, router, is_gateway_router=None):
        """Create lrouter in OVN

        @param router: Router to be created in OVN
        @param is_gateway_router: Is router ovn gateway router
        @param nexthop: Nexthop for router
        @return: Nothing
        """

        external_ids = {ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY:
                        router.get('name', 'no_router_name')}
        enabled = router.get('admin_state_up')
        options = {}

        if is_gateway_router:
            lrouter_name = utils.ovn_gateway_router_name(router['id'])
            chassis = self.scheduler.select(self._ovn, self._sb_ovn,
                                            lrouter_name)
            options = {'chassis': chassis}
        else:
            lrouter_name = utils.ovn_name(router['id'])

        with self._ovn.transaction(check_error=True) as txn:
            txn.add(self._ovn.create_lrouter(lrouter_name,
                                             external_ids=external_ids,
                                             enabled=enabled,
                                             options=options))
Ejemplo n.º 31
0
    def create_network_in_ovn(self,
                              network,
                              ext_ids,
                              physnet=None,
                              segid=None):
        # Create a logical switch with a name equal to the Neutron network
        # UUID.  This provides an easy way to refer to the logical switch
        # without having to track what UUID OVN assigned to it.
        ext_ids.update(
            {ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY: network['name']})

        lswitch_name = utils.ovn_name(network['id'])
        with self._nb_ovn.transaction(check_error=True) as txn:
            txn.add(
                self._nb_ovn.create_lswitch(lswitch_name=lswitch_name,
                                            external_ids=ext_ids))
            if physnet:
                vlan_id = None
                if segid is not None:
                    vlan_id = int(segid)
                txn.add(
                    self._nb_ovn.create_lswitch_port(
                        lport_name='provnet-%s' % network['id'],
                        lswitch_name=lswitch_name,
                        addresses=['unknown'],
                        external_ids=None,
                        type='localnet',
                        tag=vlan_id,
                        options={'network_name': physnet}))
        return network
Ejemplo n.º 32
0
 def _delete_lrouter_in_ovn(self, id, is_gateway_router=False):
     if is_gateway_router:
         lrouter_name = utils.ovn_gateway_router_name(id)
     else:
         lrouter_name = utils.ovn_name(id)
     with self._ovn.transaction(check_error=True) as txn:
         txn.add(self._ovn.delete_lrouter(lrouter_name))
Ejemplo n.º 33
0
    def _update_port_in_ovn(self, original_port, port, ovn_port_info):
        external_ids = {
            ovn_const.OVN_PORT_NAME_EXT_ID_KEY: port['name']}
        admin_context = n_context.get_admin_context()
        sg_cache = {}
        sg_ports_cache = {}
        subnet_cache = {}

        with self._ovn.transaction(check_error=True) as txn:
            txn.add(self._ovn.set_lport(lport_name=port['id'],
                    addresses=ovn_port_info.addresses,
                    external_ids=external_ids,
                    parent_name=ovn_port_info.parent_name,
                    tag=ovn_port_info.tag,
                    type=ovn_port_info.type,
                    options=ovn_port_info.options,
                    enabled=port['admin_state_up'],
                    port_security=ovn_port_info.port_security))
            # Note that the ovsdb IDL suppresses the transaction down to what
            # has actually changed.
            txn.add(self._ovn.delete_acl(
                    utils.ovn_name(port['network_id']),
                    port['id']))
            acls_new = ovn_acl.add_acls(self._plugin,
                                        admin_context,
                                        port,
                                        sg_cache,
                                        sg_ports_cache,
                                        subnet_cache)
            for acl in acls_new:
                txn.add(self._ovn.add_acl(**acl))

        # Refresh remote security groups for changed security groups
        old_sg_ids = set(original_port.get('security_groups', []))
        new_sg_ids = set(port.get('security_groups', []))
        detached_sg_ids = old_sg_ids - new_sg_ids
        attached_sg_ids = new_sg_ids - old_sg_ids

        if (len(port.get('fixed_ips')) == 0 and
                len(original_port.get('fixed_ips')) == 0):
            # No need to process remote security group if the port
            # didn't have any IP Addresses.
            return port

        for sg_id in (attached_sg_ids | detached_sg_ids):
            ovn_acl.refresh_remote_security_group(
                self._plugin, admin_context, self._ovn, sg_id,
                sg_cache, sg_ports_cache,
                subnet_cache, [port['id']])

        # Refresh remote security groups if remote_group_match_ip is set
        if original_port.get('fixed_ips') != port.get('fixed_ips'):
            # We have refreshed attached and detached security groups, so
            # now we only need to take care of unchanged security groups.
            unchanged_sg_ids = new_sg_ids & old_sg_ids
            for sg_id in unchanged_sg_ids:
                ovn_acl.refresh_remote_security_group(
                    self._plugin, admin_context, self._ovn, sg_id,
                    sg_cache, sg_ports_cache,
                    subnet_cache, [port['id']])
Ejemplo n.º 34
0
    def create_port_in_ovn(self, port, ovn_port_info):
        external_ids = {ovn_const.OVN_PORT_NAME_EXT_ID_KEY: port['name']}
        lswitch_name = utils.ovn_name(port['network_id'])
        admin_context = n_context.get_admin_context()
        sg_cache = {}
        sg_ports_cache = {}
        subnet_cache = {}

        with self._ovn.transaction(check_error=True) as txn:
            # The lport_name *must* be neutron port['id'].  It must match the
            # iface-id set in the Interfaces table of the Open_vSwitch
            # database which nova sets to be the port ID.
            txn.add(
                self._ovn.create_lport(
                    lport_name=port['id'],
                    lswitch_name=lswitch_name,
                    addresses=ovn_port_info.addresses,
                    external_ids=external_ids,
                    parent_name=ovn_port_info.parent_name,
                    tag=ovn_port_info.tag,
                    enabled=port.get('admin_state_up'),
                    options=ovn_port_info.options,
                    type=ovn_port_info.type,
                    port_security=ovn_port_info.port_security))
            acls_new = self._add_acls(admin_context, port, sg_cache,
                                      sg_ports_cache, subnet_cache)
            for acl in acls_new:
                txn.add(self._ovn.add_acl(**acl))

        if len(port.get('fixed_ips')):
            for sg_id in port.get('security_groups', []):
                self._refresh_remote_security_group(admin_context, sg_id,
                                                    sg_cache, sg_ports_cache,
                                                    subnet_cache, [port['id']])
Ejemplo n.º 35
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))
Ejemplo n.º 36
0
 def delete_gw_router_port_in_ovn(self, router_id, gw_port_id,
                                  provnet_gw_ip):
     LOG.debug("Class OVNL3RouterPlugin:::")
     # delete default route on dvr-router if exists.
     dvr_default_route = {
         'destination': ovn_const.OVN_DEFAULT_ROUTE_CIDR,
         'nexthop': ovn_const.OVN_GATEWAY_TRANSIT_PORT_IP
     }
     dvr_router_name = utils.ovn_name(router_id)
     gw_router_name = utils.ovn_gateway_name(router_id)
     with self._ovn.transaction(check_error=True) as txn:
         # 1. clear default route of dvr-router.
         txn.add(
             self._ovn.delete_static_route(
                 dvr_router_name,
                 ip_prefix=dvr_default_route['destination'],
                 nexthop=dvr_default_route['nexthop']))
         # 2. remove gw port of gw-router.
         txn.add(
             self._ovn.delete_lrouter_port('lrp-%s' % gw_port_id,
                                           lrouter=gw_router_name))
         # 3. clear default snat on gw-router.
         txn.add(
             self._ovn.delete_nat(gw_router_name, logical_ip='0.0.0.0/0'))
         # 4. clear default route on gw-router.
         txn.add(
             self._ovn.delete_static_route(
                 gw_router_name,
                 ip_prefix=ovn_const.OVN_DEFAULT_ROUTE_CIDR,
                 nexthop=provnet_gw_ip))
Ejemplo n.º 37
0
    def _sync_networks(self, ctx):
        LOG.debug("OVN-NB Sync networks started")

        lswitches = self.ovn_api.get_all_logical_switches_ids()

        for network in self.core_plugin.get_networks(ctx):
            net_context = driver_context.NetworkContext(self.core_plugin, ctx, network)
            try:
                if self.mode == SYNC_MODE_REPAIR:
                    self.driver.create_network_postcommit(net_context)
                res = lswitches.pop(utils.ovn_name(net_context.current["id"]), None)
                if self.mode == SYNC_MODE_LOG:
                    if res is None:
                        LOG.warn(
                            _LW("Network found in Neutron but not in OVN" "DB, network_id=%s"),
                            net_context.current["id"],
                        )

            except RuntimeError:
                LOG.warn(_LW("Create network postcommit failed for " "network %s"), network["id"])

        # Only delete logical switch if it was previously created by neutron
        with self.ovn_api.transaction() as txn:
            for lswitch, ext_ids in lswitches.items():
                if ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY in ext_ids:
                    if self.mode == SYNC_MODE_REPAIR:
                        txn.add(self.ovn_api.delete_lswitch(lswitch))
                    if self.mode == SYNC_MODE_LOG:
                        LOG.warn(
                            _LW("Network found in OVN but not in Neutron," " network_name=%s"),
                            (ext_ids[ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY]),
                        )

        LOG.debug("OVN-NB Sync networks finished")
Ejemplo n.º 38
0
 def _update_acls_for_security_group(self, context, security_group_id,
                                     sg_ports_cache=None,
                                     exclude_ports=None,
                                     subnet_cache=None):
     # Update ACLs for all ports using this security group.  Note that the
     # ovsdb IDL suppresses the transaction down to what has actually
     # changed.
     if exclude_ports is None:
         exclude_ports = []
     filters = {'security_group_id': [security_group_id]}
     sg_ports = self._get_port_security_group_bindings(context, filters)
     with self._ovn.transaction(check_error=True) as txn:
         sg_cache = {}
         if sg_ports_cache is None:
             sg_ports_cache = {}
         if subnet_cache is None:
             subnet_cache = {}
         for binding in sg_ports:
             if binding['port_id'] in exclude_ports:
                 continue
             port = self.get_port(context, binding['port_id'])
             txn.add(self._ovn.delete_acl(
                     utils.ovn_name(port['network_id']), port['id']))
             self._add_acls(context, port, txn, sg_cache, sg_ports_cache,
                            subnet_cache)
Ejemplo n.º 39
0
    def from_neutron_router(router):

        def _get_subnet_id(gw_info):
            subnet_id = ''
            ext_ips = gw_info.get('external_fixed_ips', [])
            if ext_ips:
                subnet_id = ext_ips[0]['subnet_id']
            return subnet_id

        external_ids = {
            ovn_const.OVN_GW_PORT_EXT_ID_KEY: router.get('gw_port_id') or '',
            ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY:
                router.get('name', 'no_router_name')}

        # Get the routes
        routes = []
        for r in router.get('routes', []):
            routes.append(FakeStaticRoute(ip_prefix=r['destination'],
                                          nexthop=r['nexthop'],
                                          external_ids={}))

        gw_info = router.get(l3.EXTERNAL_GW_INFO)
        if gw_info:
            external_ids = {
                ovn_const.OVN_ROUTER_IS_EXT_GW: 'true',
                ovn_const.OVN_SUBNET_EXT_ID_KEY: _get_subnet_id(gw_info)}
            routes.append(FakeStaticRoute(
                ip_prefix='0.0.0.0/0', nexthop='',
                external_ids=external_ids))

        return FakeOVNRouter.create_one_router(
            {'external_ids': external_ids,
             'enabled': router.get('admin_state_up') or False,
             'name': utils.ovn_name(router['id']),
             'static_routes': routes})
Ejemplo n.º 40
0
 def delete_port(self, context, port_id, l3_port_check=True):
     port = self.get_port(context, port_id)
     self._ovn.delete_lport(port_id, utils.ovn_name(
         port['network_id'])).execute(check_error=True)
     with context.session.begin():
         self.disassociate_floatingips(context, port_id)
         super(OVNPlugin, self).delete_port(context, port_id)
Ejemplo n.º 41
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))
Ejemplo n.º 42
0
    def create_port_in_ovn(self, port, ovn_port_info):
        external_ids = {ovn_const.OVN_PORT_NAME_EXT_ID_KEY: port['name']}
        lswitch_name = utils.ovn_name(port['network_id'])
        admin_context = n_context.get_admin_context()
        sg_cache = {}
        sg_ports_cache = {}
        subnet_cache = {}

        with self._nb_ovn.transaction(check_error=True) as txn:
            # The lport_name *must* be neutron port['id'].  It must match the
            # iface-id set in the Interfaces table of the Open_vSwitch
            # database which nova sets to be the port ID.
            txn.add(self._nb_ovn.create_lswitch_port(
                    lport_name=port['id'],
                    lswitch_name=lswitch_name,
                    addresses=ovn_port_info.addresses,
                    external_ids=external_ids,
                    parent_name=ovn_port_info.parent_name,
                    tag=ovn_port_info.tag,
                    enabled=port.get('admin_state_up'),
                    options=ovn_port_info.options,
                    type=ovn_port_info.type,
                    port_security=ovn_port_info.port_security))
            acls_new = ovn_acl.add_acls(self._plugin, admin_context,
                                        port, sg_cache, sg_ports_cache,
                                        subnet_cache)
            for acl in acls_new:
                txn.add(self._nb_ovn.add_acl(**acl))

        if len(port.get('fixed_ips')):
            for sg_id in port.get('security_groups', []):
                ovn_acl.refresh_remote_security_group(
                    self._plugin, admin_context, self._nb_ovn,
                    sg_id, sg_cache, sg_ports_cache,
                    subnet_cache, [port['id']])
Ejemplo n.º 43
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)
Ejemplo n.º 44
0
    def create_network_in_ovn(self, network, ext_ids,
                              physnet=None, segid=None):
        # Create a logical switch with a name equal to the Neutron network
        # UUID.  This provides an easy way to refer to the logical switch
        # without having to track what UUID OVN assigned to it.
        ext_ids.update({
            ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY: network['name']
        })

        lswitch_name = utils.ovn_name(network['id'])
        with self._nb_ovn.transaction(check_error=True) as txn:
            txn.add(self._nb_ovn.create_lswitch(
                lswitch_name=lswitch_name,
                external_ids=ext_ids))
            if physnet:
                vlan_id = None
                if segid is not None:
                    vlan_id = int(segid)
                txn.add(self._nb_ovn.create_lswitch_port(
                    lport_name='provnet-%s' % network['id'],
                    lswitch_name=lswitch_name,
                    addresses=['unknown'],
                    external_ids=None,
                    type='localnet',
                    tag=vlan_id,
                    options={'network_name': physnet}))
        return network
Ejemplo n.º 45
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
Ejemplo n.º 46
0
    def _create_port_in_ovn(self, ctx, port):
        # Remove any old ACLs for the port to avoid creating duplicate ACLs.
        self.ovn_api.delete_acl(utils.ovn_name(port['network_id']),
                                port['id']).execute(check_error=True)

        # Create the port in OVN. This will include ACL and Address Set
        # updates as needed.
        self._ovn_client.create_port(port)
Ejemplo n.º 47
0
    def create_router(self, context, router):
        router = super(OVNPlugin, self).create_router(context, router)
        router_name = utils.ovn_name(router["id"])
        external_ids = {ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY: router.get("name", "no_router_name")}
        self._ovn.create_lrouter(router_name, external_ids=external_ids).execute(check_error=True)

        # TODO(gsagie) rollback router creation on OVN failure
        return router
Ejemplo n.º 48
0
    def update_router(self, context, id, router):
        router = super(OVNPlugin, self).update_router(context, id, router)
        router_name = utils.ovn_name(router["id"])
        external_ids = {ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY: router.get("name", "no_router_name")}
        self._ovn.update_lrouter(router_name, external_ids=external_ids).execute(check_error=True)

        # TODO(Sisir) Rollback router update on OVN NB DB Update Failure.
        return router
Ejemplo n.º 49
0
 def delete_port(self, context, port_id, l3_port_check=True):
     port = self.get_port(context, port_id)
     self._ovn.delete_lport(port_id,
                            utils.ovn_name(port['network_id'])
                            ).execute(check_error=True)
     with context.session.begin():
         self.disassociate_floatingips(context, port_id)
         super(OVNPlugin, self).delete_port(context, port_id)
Ejemplo n.º 50
0
    def _update_floating_ip_in_ovn(self,
                                   context,
                                   router_id,
                                   update,
                                   associate=True):
        fip_apis = {}
        fip_apis['nat'] = self._ovn.add_nat_rule_in_lrouter if \
            associate else self._ovn.delete_nat_rule_in_lrouter
        gw_lrouter_name = utils.ovn_name(router_id)
        try:
            with self._ovn.transaction(check_error=True) as txn:
                nat_rule_args = (gw_lrouter_name, )
                if associate:
                    # TODO(chandrav): Since the floating ip port is not
                    # bound to any chassis, packets destined to floating ip
                    # will be dropped. To overcome this, delete the floating
                    # ip port. Proper fix for this would be to redirect packets
                    # destined to floating ip to the router port. This would
                    # require changes in ovn-northd.
                    txn.add(
                        self._ovn.delete_lswitch_port(
                            update['fip_port_id'],
                            utils.ovn_name(update['fip_net_id'])))

                    # Get the list of nat rules and check if the external_ip
                    # with type 'dnat_and_snat' already exists or not.
                    # If exists, set the new value.
                    # This happens when the port associated to a floating ip
                    # is deleted before the disassociation.
                    lrouter_nat_rules = self._ovn.get_lrouter_nat_rules(
                        gw_lrouter_name)
                    for nat_rule in lrouter_nat_rules:
                        if nat_rule['external_ip'] == update['external_ip'] \
                                and nat_rule['type'] == 'dnat_and_snat':
                            fip_apis['nat'] = self._ovn.set_nat_rule_in_lrouter
                            nat_rule_args = (gw_lrouter_name, nat_rule['uuid'])
                            break

                txn.add(fip_apis['nat'](*nat_rule_args,
                                        type='dnat_and_snat',
                                        logical_ip=update['logical_ip'],
                                        external_ip=update['external_ip']))
        except Exception:
            with excutils.save_and_reraise_exception():
                LOG.error(_LE('Unable to update NAT rule in gateway router'))
Ejemplo n.º 51
0
    def delete_port_postcommit(self, context):
        """Delete a port.

        :param context: PortContext instance describing the current
        state of the port, prior to the call to delete it.

        Called after the transaction completes. Call can block, though
        will block the entire process so care should be taken to not
        drastically affect performance.  Runtime errors are not
        expected, and will not prevent the resource from being
        deleted.
        """
        port = context.current
        with self._nb_ovn.transaction(check_error=True) as txn:
            txn.add(
                self._nb_ovn.delete_lswitch_port(
                    port['id'], utils.ovn_name(port['network_id'])))
            txn.add(
                self._nb_ovn.delete_acl(utils.ovn_name(port['network_id']),
                                        port['id']))

            if port.get('fixed_ips'):
                addresses = ovn_acl.acl_port_ips(port)
                for sg_id in port.get('security_groups', []):
                    for ip_version in addresses:
                        if addresses[ip_version]:
                            txn.add(
                                self._nb_ovn.update_address_set(
                                    name=utils.ovn_addrset_name(
                                        sg_id, ip_version),
                                    addrs_add=None,
                                    addrs_remove=addresses[ip_version]))

                # Delete the DHCP_Options row if created for this port.
                # A separate DHCP_Options row would have be created if the port
                # has extra DHCP options defined.
                for fixed_ip in port['fixed_ips']:
                    if netaddr.IPAddress(fixed_ip['ip_address']).version == 4:
                        lsp_dhcp_options = self._nb_ovn.get_port_dhcp_options(
                            fixed_ip['subnet_id'], port['id'])
                        if lsp_dhcp_options:
                            txn.add(
                                self._nb_ovn.delete_dhcp_options(
                                    lsp_dhcp_options['uuid']))
                            break
Ejemplo n.º 52
0
    def get_floatingip_by_ips(self, router_id, logical_ip, external_ip):
        if not all([router_id, logical_ip, external_ip]):
            return

        for nat in self.get_lrouter_nat_rules(utils.ovn_name(router_id)):
            if (nat['type'] == 'dnat_and_snat' and
               nat['logical_ip'] == logical_ip and
               nat['external_ip'] == external_ip):
                return nat
Ejemplo n.º 53
0
    def get_lrouter(self, lrouter_name):
        if uuidutils.is_uuid_like(lrouter_name):
            lrouter_name = utils.ovn_name(lrouter_name)

        # TODO(lucasagomes): Use lr_get() once we start refactoring this
        # API to use methods from ovsdbapp.
        lr = self.db_find_rows('Logical_Router', ('name', '=', lrouter_name))
        result = lr.execute(check_error=True)
        return result[0] if result else None
Ejemplo n.º 54
0
 def create_network_postcommit(self, context):
     network = context.current
     # Create a logical switch with a name equal to the Neutron network
     # UUID.  This provides an easy way to refer to the logical switch
     # without having to track what UUID OVN assigned to it.
     external_ids = {ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY: network['name']}
     self._ovn.create_lswitch(lswitch_name=utils.ovn_name(network['id']),
                              external_ids=external_ids).execute(
                                  check_error=True)
Ejemplo n.º 55
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))
Ejemplo n.º 56
0
    def _update_port_in_ovn(self, original_port, port, ovn_port_info):
        external_ids = {ovn_const.OVN_PORT_NAME_EXT_ID_KEY: port['name']}
        admin_context = n_context.get_admin_context()
        sg_cache = {}
        sg_ports_cache = {}
        subnet_cache = {}

        with self._nb_ovn.transaction(check_error=True) as txn:
            txn.add(
                self._nb_ovn.set_lswitch_port(
                    lport_name=port['id'],
                    addresses=ovn_port_info.addresses,
                    external_ids=external_ids,
                    parent_name=ovn_port_info.parent_name,
                    tag=ovn_port_info.tag,
                    type=ovn_port_info.type,
                    options=ovn_port_info.options,
                    enabled=port['admin_state_up'],
                    port_security=ovn_port_info.port_security))
            # Note that the ovsdb IDL suppresses the transaction down to what
            # has actually changed.
            txn.add(
                self._nb_ovn.delete_acl(utils.ovn_name(port['network_id']),
                                        port['id']))
            acls_new = ovn_acl.add_acls(self._plugin, admin_context, port,
                                        sg_cache, sg_ports_cache, subnet_cache)
            for acl in acls_new:
                txn.add(self._nb_ovn.add_acl(**acl))

        # Refresh remote security groups for changed security groups
        old_sg_ids = set(original_port.get('security_groups', []))
        new_sg_ids = set(port.get('security_groups', []))
        detached_sg_ids = old_sg_ids - new_sg_ids
        attached_sg_ids = new_sg_ids - old_sg_ids

        if (len(port.get('fixed_ips')) == 0
                and len(original_port.get('fixed_ips')) == 0):
            # No need to process remote security group if the port
            # didn't have any IP Addresses.
            return port

        for sg_id in (attached_sg_ids | detached_sg_ids):
            ovn_acl.refresh_remote_security_group(self._plugin, admin_context,
                                                  self._nb_ovn, sg_id,
                                                  sg_cache, sg_ports_cache,
                                                  subnet_cache, [port['id']])

        # Refresh remote security groups if remote_group_match_ip is set
        if original_port.get('fixed_ips') != port.get('fixed_ips'):
            # We have refreshed attached and detached security groups, so
            # now we only need to take care of unchanged security groups.
            unchanged_sg_ids = new_sg_ids & old_sg_ids
            for sg_id in unchanged_sg_ids:
                ovn_acl.refresh_remote_security_group(
                    self._plugin, admin_context, self._nb_ovn, sg_id, sg_cache,
                    sg_ports_cache, subnet_cache, [port['id']])
Ejemplo n.º 57
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
Ejemplo n.º 58
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
Ejemplo n.º 59
0
    def create_network_in_ovn(self, network):
        # Create a logical switch with a name equal to the Neutron network
        # UUID.  This provides an easy way to refer to the logical switch
        # without having to track what UUID OVN assigned to it.
        external_ids = {ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY: network['name']}

        # TODO(arosen): Undo logical switch creation on failure
        self._ovn.create_lswitch(
            lswitch_name=utils.ovn_name(network['id']),
            external_ids=external_ids).execute(check_error=True)
        return network
Ejemplo n.º 60
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