Add initial wiring configuration to a common function

This is to make the NB driver a bit more independent of the mode
choosen to expose the IPs.

Change-Id: I02e3a0b15123f38995459f011b92d83c138576d6
This commit is contained in:
Luis Tomas Bolivar 2023-04-03 08:18:51 +02:00
parent db9d9aee9f
commit 6e59cf66ca
7 changed files with 287 additions and 187 deletions

View File

@ -13,7 +13,6 @@
# limitations under the License.
import collections
import pyroute2
import threading
from oslo_concurrency import lockutils
@ -69,10 +68,10 @@ class NBOVNBGPDriver(driver_api.AgentDriverBase):
self.ovn_local_cr_lrps = {}
self.ovn_local_lrps = {}
# {ovn_lb: VIP1, VIP2}
self.ovn_lb_vips = collections.defaultdict()
# {'ls_name': {'bridge_device': X, 'bridge_vlan': Y}}
self.ovn_fips = {} # {'fip': {'bridge_device': X, 'bridge_vlan': Y}}
# {'ls_name': ['ip': {'bridge_device': X, 'bridge_vlan': Y}]}
self._exposed_ips = {}
self._ovs_flows = collections.defaultdict()
self.ovn_provider_ls = {}
# dict instead of list to speed up look ups
self.ovn_tenant_ls = {} # {'ls_name': True}
@ -135,89 +134,36 @@ class NBOVNBGPDriver(driver_api.AgentDriverBase):
CONF.expose_ipv6_gua_tenant_networks)
self._init_vars()
LOG.debug("Configuring br-ex default rule and routing tables for "
"each provider network")
flows_info = {}
# 1) Get bridge mappings: xxxx:br-ex,yyyy:br-ex2
LOG.debug("Configuring default wiring for each provider network")
# Get bridge mappings: xxxx:br-ex,yyyy:br-ex2
bridge_mappings = self.ovs_idl.get_ovn_bridge_mappings()
# 2) Get macs for bridge mappings
extra_routes = {}
with pyroute2.NDB() as ndb:
for bridge_index, bridge_mapping in enumerate(bridge_mappings, 1):
network = bridge_mapping.split(":")[0]
bridge = bridge_mapping.split(":")[1]
self.ovn_bridge_mappings[network] = bridge
if not extra_routes.get(bridge):
extra_routes[bridge] = (
linux_net.ensure_routing_table_for_bridge(
self.ovn_routing_tables, bridge,
CONF.bgp_vrf_table_id))
vlan_tag = self.nb_idl.get_network_vlan_tag_by_network_name(
network)
if vlan_tag:
vlan_tag = vlan_tag[0]
linux_net.ensure_vlan_device_for_network(bridge,
vlan_tag)
linux_net.ensure_arp_ndp_enabled_for_bridge(bridge,
bridge_index,
vlan_tag)
if flows_info.get(bridge):
continue
flows_info[bridge] = {
'mac': ndb.interfaces[bridge]['address'],
'in_port': set([])}
# 3) Get in_port for bridge mappings (br-ex, br-ex2)
ovs.get_ovs_flows_info(bridge, flows_info,
constants.OVS_RULE_COOKIE)
# 4) Add/Remove flows for each bridge mappings
ovs.remove_extra_ovs_flows(flows_info, constants.OVS_RULE_COOKIE)
# Apply base configuration for each bridge
self.ovn_bridge_mappings = wire_utils.ensure_base_wiring_config(
self.nb_idl, bridge_mappings, self.ovn_routing_tables)
LOG.debug("Syncing current routes.")
exposed_ips = linux_net.get_exposed_ips(CONF.bgp_nic)
# get the rules pointing to ovn bridges
ovn_ip_rules = linux_net.get_ovn_ip_rules(
self.ovn_routing_tables.values())
# add missing routes/ips for IPs on provider network
ports = self.nb_idl.get_active_ports_on_chassis(self.chassis)
for port in ports:
if port.type not in [constants.OVN_VM_VIF_PORT_TYPE,
constants.OVN_VIRTUAL_VIF_PORT_TYPE]:
continue
self._ensure_port_exposed(port, exposed_ips, ovn_ip_rules)
self._ensure_port_exposed(port)
# remove extra routes/ips
# remove all the leftovers on the list of current ips on dev OVN
linux_net.delete_exposed_ips(exposed_ips, CONF.bgp_nic)
# remove all the leftovers on the list of current ip rules for ovn
# bridges
linux_net.delete_ip_rules(ovn_ip_rules)
# remove extra wiring leftovers
wire_utils.cleanup_wiring(self.ovn_bridge_mappings,
self._exposed_ips,
self.ovn_routing_tables,
self.ovn_routing_tables_routes)
# remove all the extra rules not needed
linux_net.delete_bridge_ip_routes(self.ovn_routing_tables,
self.ovn_routing_tables_routes,
extra_routes)
def _ensure_port_exposed(self, port, exposed_ips, ovn_ip_rules):
def _ensure_port_exposed(self, port):
port_fip = port.external_ids.get(constants.OVN_FIP_EXT_ID_KEY)
if port_fip:
external_ip, ls_name = self.get_port_external_ip_and_ls(port.name)
if not external_ip or not ls_name:
return
if self._expose_fip(external_ip, ls_name):
ip_version = linux_net.get_ip_version(external_ip)
if ip_version == constants.IP_VERSION_6:
ip_dst = "{}/128".format(external_ip)
else:
ip_dst = "{}/32".format(external_ip)
if external_ip in exposed_ips:
exposed_ips.remove(external_ip)
ovn_ip_rules.pop(ip_dst, None)
return
return self._expose_fip(external_ip, ls_name, port)
logical_switch = port.external_ids.get(
constants.OVN_LS_NAME_EXT_ID_KEY)
@ -242,38 +188,35 @@ class NBOVNBGPDriver(driver_api.AgentDriverBase):
'bridge_device': bridge_device,
'bridge_vlan': bridge_vlan}
ips = port.addresses[0].strip().split(' ')[1:]
ips_adv = self._expose_ip(ips, bridge_device, bridge_vlan, port.type,
port.external_ids.get(
constants.OVN_CIDRS_EXT_ID_KEY))
for ip in ips_adv:
ip_version = linux_net.get_ip_version(ip)
if ip_version == constants.IP_VERSION_6:
ip_dst = "{}/128".format(ip)
else:
ip_dst = "{}/32".format(ip)
if ip in exposed_ips:
exposed_ips.remove(ip)
ovn_ip_rules.pop(ip_dst, None)
self._expose_ip(ips, logical_switch, bridge_device, bridge_vlan,
port.type, port.external_ids.get(
constants.OVN_CIDRS_EXT_ID_KEY))
def _expose_provider_port(self, port_ips, bridge_device, bridge_vlan,
proxy_cidrs=None):
def _expose_provider_port(self, port_ips, logical_switch, bridge_device,
bridge_vlan, proxy_cidrs=None):
# Connect to OVN
if wire_utils.wire_provider_port(
self.ovn_routing_tables_routes, port_ips, bridge_device,
bridge_vlan, self.ovn_routing_tables[bridge_device],
proxy_cidrs):
bridge_vlan, self.ovn_routing_tables, proxy_cidrs):
# Expose the IP now that it is connected
bgp_utils.announce_ips(port_ips)
for ip in port_ips:
self._exposed_ips.setdefault(logical_switch, {}).update(
{ip: {'bridge_device': bridge_device,
'bridge_vlan': bridge_vlan}})
def _withdraw_provider_port(self, port_ips, bridge_device, bridge_vlan,
proxy_cidrs=None):
def _withdraw_provider_port(self, port_ips, logical_switch, bridge_device,
bridge_vlan, proxy_cidrs=None):
# Withdraw IP before disconnecting it
bgp_utils.withdraw_ips(port_ips)
# Disconnect IP from OVN
wire_utils.unwire_provider_port(
self.ovn_routing_tables_routes, port_ips, bridge_device,
bridge_vlan, self.ovn_routing_tables[bridge_device], proxy_cidrs)
bridge_vlan, self.ovn_routing_tables, proxy_cidrs)
for ip in port_ips:
if self._exposed_ips.get(logical_switch, {}).get(ip):
self._exposed_ips[logical_switch].pop(ip)
def _get_bridge_for_localnet_port(self, localnet):
bridge_device = None
@ -309,20 +252,23 @@ class NBOVNBGPDriver(driver_api.AgentDriverBase):
self.ovn_provider_ls[logical_switch] = {
'bridge_device': bridge_device,
'bridge_vlan': bridge_vlan}
return self._expose_ip(ips, bridge_device, bridge_vlan,
return self._expose_ip(ips, logical_switch, bridge_device, bridge_vlan,
port_type=row.type, cidr=row.external_ids.get(
constants.OVN_CIDRS_EXT_ID_KEY))
def _expose_ip(self, ips, bridge_device, bridge_vlan, port_type, cidr):
def _expose_ip(self, ips, logical_switch, bridge_device, bridge_vlan,
port_type, cidr):
LOG.debug("Adding BGP route for logical port with ip %s", ips)
if cidr and port_type == constants.OVN_VIRTUAL_VIF_PORT_TYPE:
# NOTE: For Amphora Load Balancer with IPv6 VIP on the provider
# network, we need a NDP Proxy so that the traffic from the
# amphora can properly be redirected back
self._expose_provider_port(ips, bridge_device, bridge_vlan, [cidr])
self._expose_provider_port(ips, logical_switch, bridge_device,
bridge_vlan, [cidr])
else:
self._expose_provider_port(ips, bridge_device, bridge_vlan)
self._expose_provider_port(ips, logical_switch, bridge_device,
bridge_vlan)
LOG.debug("Added BGP route for logical port with ip %s", ips)
return ips
@ -357,10 +303,11 @@ class NBOVNBGPDriver(driver_api.AgentDriverBase):
proxy_cidr = n_cidr
LOG.debug("Deleting BGP route for logical port with ip %s", ips)
if proxy_cidr:
self._withdraw_provider_port(ips, bridge_device, bridge_vlan,
[proxy_cidr])
self._withdraw_provider_port(ips, logical_switch, bridge_device,
bridge_vlan, [proxy_cidr])
else:
self._withdraw_provider_port(ips, bridge_device, bridge_vlan)
self._withdraw_provider_port(ips, logical_switch, bridge_device,
bridge_vlan)
LOG.debug("Deleted BGP route for logical port with ip %s", ips)
def _get_ls_localnet_info(self, logical_switch):
@ -383,7 +330,7 @@ class NBOVNBGPDriver(driver_api.AgentDriverBase):
return nat_entry.external_ip, "neutron-{}".format(net_id)
@lockutils.synchronized('nbbgp')
def expose_fip(self, ip, logical_switch):
def expose_fip(self, ip, logical_switch, row):
'''Advertice BGP route by adding IP to device.
This methods ensures BGP advertises the FIP associated to a VM in a
@ -395,22 +342,26 @@ class NBOVNBGPDriver(driver_api.AgentDriverBase):
VRF), and adds the IP of:
- VM FIP
'''
return self._expose_fip(ip, logical_switch)
return self._expose_fip(ip, logical_switch, row)
def _expose_fip(self, ip, logical_switch):
def _expose_fip(self, ip, logical_switch, row):
bridge_device, bridge_vlan = self._get_ls_localnet_info(logical_switch)
if not bridge_device:
# This means it is not a provider network
return False
tenant_logical_switch = row.external_ids.get(
constants.OVN_LS_NAME_EXT_ID_KEY)
if not tenant_logical_switch:
return
self.ovn_tenant_ls[tenant_logical_switch] = True
LOG.debug("Adding BGP route for FIP with ip %s", ip)
self._expose_provider_port([ip], bridge_device, bridge_vlan)
self.ovn_fips[ip] = {'bridge_device': bridge_device,
'bridge_vlan': bridge_vlan}
self._expose_provider_port([ip], tenant_logical_switch, bridge_device,
bridge_vlan)
LOG.debug("Added BGP route for FIP with ip %s", ip)
return True
@lockutils.synchronized('nbbgp')
def withdraw_fip(self, ip):
def withdraw_fip(self, ip, row):
'''Withdraw BGP route by removing IP from device.
This methods ensures BGP withdraw an advertised the FIP associated to
@ -422,7 +373,11 @@ class NBOVNBGPDriver(driver_api.AgentDriverBase):
VRF), and removes the IP of:
- VM FIP
'''
fip_info = self.ovn_fips.get(ip)
tenant_logical_switch = row.external_ids.get(
constants.OVN_LS_NAME_EXT_ID_KEY)
if not tenant_logical_switch:
return
fip_info = self._exposed_ips.get(tenant_logical_switch, {}).get(ip)
if not fip_info:
# No information to withdraw the FIP
return
@ -430,7 +385,8 @@ class NBOVNBGPDriver(driver_api.AgentDriverBase):
bridge_vlan = fip_info['bridge_vlan']
LOG.debug("Deleting BGP route for FIP with ip %s", ip)
self._withdraw_provider_port([ip], bridge_device, bridge_vlan)
self._withdraw_provider_port([ip], tenant_logical_switch,
bridge_device, bridge_vlan)
LOG.debug("Deleted BGP route for FIP with ip %s", ip)
@lockutils.synchronized('nbbgp')

View File

@ -319,8 +319,7 @@ class OVNBGPDriver(driver_api.AgentDriverBase):
# Connect to OVN
if wire_utils.wire_provider_port(
self.ovn_routing_tables_routes, port_ips, bridge_device,
bridge_vlan, self.ovn_routing_tables[bridge_device],
proxy_cidrs, lladdr):
bridge_vlan, self.ovn_routing_tables, proxy_cidrs, lladdr):
# Expose the IP now that it is connected
bgp_utils.announce_ips(port_ips)
return True
@ -389,8 +388,7 @@ class OVNBGPDriver(driver_api.AgentDriverBase):
return False
return wire_utils.unwire_provider_port(
self.ovn_routing_tables_routes, port_ips, bridge_device,
bridge_vlan, self.ovn_routing_tables[bridge_device], proxy_cidrs,
lladdr)
bridge_vlan, self.ovn_routing_tables, proxy_cidrs, lladdr)
def _get_bridge_for_datapath(self, datapath):
network_name, network_tag = self.sb_idl.get_network_name_and_tag(
@ -963,7 +961,7 @@ class OVNBGPDriver(driver_api.AgentDriverBase):
if not wire_utils.wire_lrp_port(
self.ovn_routing_tables_routes, ip, bridge_device, bridge_vlan,
self.ovn_routing_tables[bridge_device], cr_lrp_ips):
self.ovn_routing_tables, cr_lrp_ips):
LOG.warning("Not able to expose subnet with IP %s", ip)
return
if ovn_ip_rules:
@ -1028,7 +1026,7 @@ class OVNBGPDriver(driver_api.AgentDriverBase):
# Disconnect the network to OVN
wire_utils.unwire_lrp_port(
self.ovn_routing_tables_routes, ip, bridge_device, bridge_vlan,
self.ovn_routing_tables[bridge_device], cr_lrp_ips)
self.ovn_routing_tables, cr_lrp_ips)
@lockutils.synchronized('bgp')
def expose_subnet(self, ip, row):

View File

@ -12,10 +12,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import pyroute2
from oslo_config import cfg
from oslo_log import log as logging
from ovn_bgp_agent import constants
from ovn_bgp_agent.drivers.openstack.utils import ovs
from ovn_bgp_agent import exceptions as agent_exc
from ovn_bgp_agent.utils import linux_net
@ -24,6 +27,89 @@ CONF = cfg.CONF
LOG = logging.getLogger(__name__)
def ensure_base_wiring_config(idl, bridge_mappings, routing_tables):
if CONF.exposing_method == constants.EXPOSE_METHOD_UNDERLAY:
return _ensure_base_wiring_config_underlay(idl, bridge_mappings,
routing_tables)
elif CONF.exposing_method == constants.EXPOSE_METHOD_OVN:
raise NotImplementedError()
def _ensure_base_wiring_config_underlay(idl, bridge_mappings, routing_tables):
ovn_bridge_mappings = {}
for bridge_index, bridge_mapping in enumerate(bridge_mappings, 1):
network = bridge_mapping.split(":")[0]
bridge = bridge_mapping.split(":")[1]
ovn_bridge_mappings[network] = bridge
linux_net.ensure_routing_table_for_bridge(
routing_tables, bridge, CONF.bgp_vrf_table_id)
vlan_tag = idl.get_network_vlan_tag_by_network_name(network)
if vlan_tag:
vlan_tag = vlan_tag[0]
linux_net.ensure_vlan_device_for_network(bridge,
vlan_tag)
linux_net.ensure_arp_ndp_enabled_for_bridge(bridge,
bridge_index,
vlan_tag)
return ovn_bridge_mappings
def cleanup_wiring(bridge_mappings, exposed_ips, routing_tables,
routing_tables_routes):
if CONF.exposing_method == constants.EXPOSE_METHOD_UNDERLAY:
return _cleanup_wiring_underlay(bridge_mappings, exposed_ips,
routing_tables, routing_tables_routes)
elif CONF.exposing_method == constants.EXPOSE_METHOD_OVN:
raise NotImplementedError()
def _cleanup_wiring_underlay(bridge_mappings, exposed_ips, routing_tables,
routing_tables_routes):
current_ips = linux_net.get_exposed_ips(CONF.bgp_nic)
expected_ips = [ip for ip_dict in exposed_ips.values()
for ip in ip_dict.keys()]
ips_to_delete = [ip for ip in current_ips if ip not in expected_ips]
linux_net.delete_exposed_ips(ips_to_delete, CONF.bgp_nic)
# get flows and delete the extra ones
flows_info = {}
extra_routes = {}
with pyroute2.NDB() as ndb:
for bridge in bridge_mappings.values():
if flows_info.get(bridge):
continue
flows_info[bridge] = {
'mac': ndb.interfaces[bridge]['address'],
'in_port': set([])}
ovs.get_ovs_flows_info(bridge, flows_info,
constants.OVS_RULE_COOKIE)
extra_routes[bridge] = (
linux_net.get_extra_routing_table_for_bridge(routing_tables,
bridge))
ovs.remove_extra_ovs_flows(flows_info, constants.OVS_RULE_COOKIE)
# get rules and delete the old ones
ovn_ip_rules = linux_net.get_ovn_ip_rules(routing_tables.values())
if ovn_ip_rules:
for ip in expected_ips:
ip_version = linux_net.get_ip_version(ip)
if ip_version == constants.IP_VERSION_6:
ip_dst = "{}/128".format(ip)
else:
ip_dst = "{}/32".format(ip)
ovn_ip_rules.pop(ip_dst, None)
linux_net.delete_ip_rules(ovn_ip_rules)
# remove all the extra rules not needed
linux_net.delete_bridge_ip_routes(routing_tables, routing_tables_routes,
extra_routes)
def wire_provider_port(routing_tables_routes, port_ips, bridge_device,
bridge_vlan, routing_table, proxy_cidrs, lladdr=None):
if CONF.exposing_method == constants.EXPOSE_METHOD_UNDERLAY:
@ -59,16 +145,17 @@ def _wire_provider_port_underlay(routing_tables_routes, port_ips,
dev = bridge_device
if bridge_vlan:
dev = '{}.{}'.format(dev, bridge_vlan)
linux_net.add_ip_rule(ip, routing_table, dev=dev,
lladdr=lladdr)
linux_net.add_ip_rule(ip, routing_table[bridge_device],
dev=dev, lladdr=lladdr)
else:
linux_net.add_ip_rule(ip, routing_table)
linux_net.add_ip_rule(ip, routing_table[bridge_device])
except agent_exc.InvalidPortIP:
LOG.exception("Invalid IP to create a rule for port on the "
"provider network: %s", ip)
return False
linux_net.add_ip_route(routing_tables_routes, ip, routing_table,
bridge_device, vlan=bridge_vlan)
linux_net.add_ip_route(routing_tables_routes, ip,
routing_table[bridge_device], bridge_device,
vlan=bridge_vlan)
if proxy_cidrs:
# add proxy ndp config for ipv6
for n_cidr in proxy_cidrs:
@ -92,21 +179,22 @@ def _unwire_provider_port_underlay(routing_tables_routes, port_ips,
dev = bridge_device
if bridge_vlan:
dev = '{}.{}'.format(dev, bridge_vlan)
linux_net.del_ip_rule(cr_lrp_ip, routing_table, dev=dev,
lladdr=lladdr)
linux_net.del_ip_rule(cr_lrp_ip, routing_table[bridge_device],
dev=dev, lladdr=lladdr)
except agent_exc.InvalidPortIP:
LOG.exception("Invalid IP to delete a rule for the "
"provider port: %s", cr_lrp_ip)
return False
else:
try:
linux_net.del_ip_rule(ip, routing_table)
linux_net.del_ip_rule(ip, routing_table[bridge_device])
except agent_exc.InvalidPortIP:
LOG.exception("Invalid IP to delete a rule for the "
"provider port: %s", ip)
return False
linux_net.del_ip_route(routing_tables_routes, ip, routing_table,
bridge_device, vlan=bridge_vlan)
linux_net.del_ip_route(routing_tables_routes, ip,
routing_table[bridge_device], bridge_device,
vlan=bridge_vlan)
if proxy_cidrs:
for n_cidr in proxy_cidrs:
if linux_net.get_ip_version(n_cidr) == constants.IP_VERSION_6:
@ -120,7 +208,7 @@ def wire_lrp_port(routing_tables_routes, ip, bridge_device, bridge_vlan,
return False
LOG.debug("Adding IP Rules for network %s", ip)
try:
linux_net.add_ip_rule(ip, routing_table)
linux_net.add_ip_rule(ip, routing_table[bridge_device])
except agent_exc.InvalidPortIP:
LOG.exception("Invalid IP to create a rule for the lrp (network "
"router interface) port: %s", ip)
@ -136,7 +224,7 @@ def wire_lrp_port(routing_tables_routes, ip, bridge_device, bridge_vlan,
linux_net.add_ip_route(
routing_tables_routes,
ip.split("/")[0],
routing_table,
routing_table[bridge_device],
bridge_device,
vlan=bridge_vlan,
mask=ip.split("/")[1],
@ -152,7 +240,7 @@ def unwire_lrp_port(routing_tables_routes, ip, bridge_device, bridge_vlan,
return False
LOG.debug("Deleting IP Rules for network %s", ip)
try:
linux_net.del_ip_rule(ip, routing_table)
linux_net.del_ip_rule(ip, routing_table[bridge_device])
except agent_exc.InvalidPortIP:
LOG.exception("Invalid IP to delete a rule for the "
"lrp (network router interface) port: %s", ip)
@ -166,7 +254,7 @@ def unwire_lrp_port(routing_tables_routes, ip, bridge_device, bridge_vlan,
linux_net.del_ip_route(
routing_tables_routes,
ip.split("/")[0],
routing_table,
routing_table[bridge_device],
bridge_device,
vlan=bridge_vlan,
mask=ip.split("/")[1],

View File

@ -141,7 +141,7 @@ class LogicalSwitchPortFIPCreateEvent(base_watcher.LSPChassisEvent):
return
with _SYNC_STATE_LOCK.read_lock():
self.agent.expose_fip(external_ip, ls_name)
self.agent.expose_fip(external_ip, ls_name, row)
class LogicalSwitchPortFIPDeleteEvent(base_watcher.LSPChassisEvent):
@ -204,7 +204,7 @@ class LogicalSwitchPortFIPDeleteEvent(base_watcher.LSPChassisEvent):
if not fip:
return
with _SYNC_STATE_LOCK.read_lock():
self.agent.withdraw_fip(fip)
self.agent.withdraw_fip(fip, row)
class LocalnetCreateDeleteEvent(base_watcher.LSPChassisEvent):

View File

@ -114,6 +114,7 @@ class TestNBOVNBGPDriver(test_base.TestCase):
mock_ensure_ovn_dev.assert_called_once_with(
CONF.bgp_nic, CONF.bgp_vrf)
@mock.patch.object(linux_net, 'get_extra_routing_table_for_bridge')
@mock.patch.object(linux_net, 'delete_bridge_ip_routes')
@mock.patch.object(linux_net, 'delete_ip_rules')
@mock.patch.object(linux_net, 'delete_exposed_ips')
@ -127,7 +128,8 @@ class TestNBOVNBGPDriver(test_base.TestCase):
def test_sync(self, mock_routing_bridge, mock_ensure_vlan_network,
mock_ensure_arp, mock_flows_info, mock_remove_flows,
mock_exposed_ips, mock_get_ip_rules, mock_del_exposed_ips,
mock_del_ip_riles, moock_del_ip_routes):
mock_del_ip_rules, mock_del_ip_routes,
mock_get_extra_route):
self.mock_ovs_idl.get_ovn_bridge_mappings.return_value = [
'net0:bridge0', 'net1:bridge1']
self.nb_idl.get_network_vlan_tag_by_network_name.side_effect = (
@ -174,21 +176,16 @@ class TestNBOVNBGPDriver(test_base.TestCase):
'bridge1': {'mac': mock.ANY, 'in_port': set()}},
constants.OVS_RULE_COOKIE)
mock_get_ip_rules.assert_called_once()
mock_ensure_port_exposed.assert_called_once_with(
port0, ips, fake_ip_rules)
mock_ensure_port_exposed.assert_called_once_with(port0)
mock_del_exposed_ips.assert_called_once_with(
ips, CONF.bgp_nic)
mock_del_ip_riles.assert_called_once_with(fake_ip_rules)
moock_del_ip_routes.assert_called_once_with(
{}, mock.ANY,
{'bridge0': ['fake-route'], 'bridge1': ['fake-route']})
mock_del_ip_rules.assert_called_once_with(fake_ip_rules)
mock_del_ip_routes.assert_called_once()
def test__ensure_port_exposed_fip(self):
port0 = fakes.create_object({
'name': 'port-0',
'external_ids': {constants.OVN_FIP_EXT_ID_KEY: "fip"}})
exposed_ips = ["192.168.0.10"]
ovn_ip_rules = {"192.168.0.10/32": "rule1"}
mock_get_port_external_ip_and_ls = mock.patch.object(
self.nb_bgp_driver, 'get_port_external_ip_and_ls').start()
@ -199,22 +196,18 @@ class TestNBOVNBGPDriver(test_base.TestCase):
mock_expose_ip = mock.patch.object(
self.nb_bgp_driver, '_expose_ip').start()
self.nb_bgp_driver._ensure_port_exposed(port0, exposed_ips,
ovn_ip_rules)
self.nb_bgp_driver._ensure_port_exposed(port0)
mock_get_port_external_ip_and_ls.assert_called_once_with(port0.name)
mock_expose_fip.assert_called_once_with("192.168.0.10", "test-ls")
mock_expose_fip.assert_called_once_with("192.168.0.10", "test-ls",
port0)
mock_expose_ip.assert_not_called()
self.assertEqual(exposed_ips, [])
self.assertEqual(ovn_ip_rules, {})
def test__ensure_port_exposed_tenant_ls(self):
port0 = fakes.create_object({
'name': 'port-0',
'external_ids': {constants.OVN_LS_NAME_EXT_ID_KEY: "test-ls"}})
self.nb_bgp_driver.ovn_tenant_ls = {"test-ls": True}
exposed_ips = ["192.168.0.10"]
ovn_ip_rules = {"192.168.0.10/32": "rule1"}
mock_get_port_external_ip_and_ls = mock.patch.object(
self.nb_bgp_driver, 'get_port_external_ip_and_ls').start()
@ -223,14 +216,11 @@ class TestNBOVNBGPDriver(test_base.TestCase):
mock_expose_ip = mock.patch.object(
self.nb_bgp_driver, '_expose_ip').start()
self.nb_bgp_driver._ensure_port_exposed(port0, exposed_ips,
ovn_ip_rules)
self.nb_bgp_driver._ensure_port_exposed(port0)
mock_get_port_external_ip_and_ls.assert_not_called()
mock_expose_fip.assert_not_called()
mock_expose_ip.assert_not_called()
self.assertEqual(exposed_ips, ["192.168.0.10"])
self.assertEqual(ovn_ip_rules, {"192.168.0.10/32": "rule1"})
@mock.patch.object(linux_net, 'get_ip_version')
def test__ensure_port_exposed_no_fip_no_tenant_ls(self, mock_ip_version):
@ -242,8 +232,6 @@ class TestNBOVNBGPDriver(test_base.TestCase):
self.nb_bgp_driver.ovn_tenant_ls = {}
self.nb_bgp_driver.ovn_provider_ls = {}
exposed_ips = ["192.168.0.10"]
ovn_ip_rules = {"192.168.0.10/32": "rule1"}
mock_get_port_external_ip_and_ls = mock.patch.object(
self.nb_bgp_driver, 'get_port_external_ip_and_ls').start()
@ -257,17 +245,14 @@ class TestNBOVNBGPDriver(test_base.TestCase):
mock_get_ls_localnet_info.return_value = ("br-ex", 10)
mock_ip_version.return_value = constants.IP_VERSION_4
self.nb_bgp_driver._ensure_port_exposed(port0, exposed_ips,
ovn_ip_rules)
self.nb_bgp_driver._ensure_port_exposed(port0)
mock_get_port_external_ip_and_ls.assert_not_called()
mock_get_ls_localnet_info.assert_called_once_with('test-ls')
mock_expose_fip.assert_not_called()
mock_expose_ip.assert_called_once_with(["192.168.0.10"], "br-ex", 10,
constants.OVN_VM_VIF_PORT_TYPE,
None)
self.assertEqual(exposed_ips, [])
self.assertEqual(ovn_ip_rules, {})
mock_expose_ip.assert_called_once_with(
["192.168.0.10"], "test-ls", "br-ex", 10,
constants.OVN_VM_VIF_PORT_TYPE, None)
@mock.patch.object(wire_utils, 'wire_provider_port')
@mock.patch.object(bgp_utils, 'announce_ips')
@ -279,12 +264,12 @@ class TestNBOVNBGPDriver(test_base.TestCase):
bridge_vlan = None
proxy_cidrs = ['192.168.0.0/24']
self.nb_bgp_driver._expose_provider_port(port_ips, bridge_device,
bridge_vlan, proxy_cidrs)
self.nb_bgp_driver._expose_provider_port(
port_ips, 'teset-ls', bridge_device, bridge_vlan, proxy_cidrs)
mock_wire_provider_port.assert_called_once_with(
self.ovn_routing_tables_routes, port_ips, bridge_device,
bridge_vlan, self.ovn_routing_tables[bridge_device], proxy_cidrs)
bridge_vlan, self.ovn_routing_tables, proxy_cidrs)
mock_announce_ips.assert_called_once_with(port_ips)
@mock.patch.object(wire_utils, 'wire_provider_port')
@ -297,12 +282,12 @@ class TestNBOVNBGPDriver(test_base.TestCase):
bridge_vlan = None
proxy_cidrs = ['192.168.0.0/24']
self.nb_bgp_driver._expose_provider_port(port_ips, bridge_device,
bridge_vlan, proxy_cidrs)
self.nb_bgp_driver._expose_provider_port(
port_ips, 'test-ls', bridge_device, bridge_vlan, proxy_cidrs)
mock_wire_provider_port.assert_called_once_with(
self.ovn_routing_tables_routes, port_ips, bridge_device,
bridge_vlan, self.ovn_routing_tables[bridge_device], proxy_cidrs)
bridge_vlan, self.ovn_routing_tables, proxy_cidrs)
mock_announce_ips.assert_not_called()
@mock.patch.object(wire_utils, 'unwire_provider_port')
@ -314,13 +299,13 @@ class TestNBOVNBGPDriver(test_base.TestCase):
bridge_vlan = None
proxy_cidrs = ['192.168.0.0/24']
self.nb_bgp_driver._withdraw_provider_port(port_ips, bridge_device,
bridge_vlan, proxy_cidrs)
self.nb_bgp_driver._withdraw_provider_port(
port_ips, 'test-ls', bridge_device, bridge_vlan, proxy_cidrs)
mock_withdraw_ips.assert_called_once_with(port_ips)
mock_unwire_provider_port.assert_called_once_with(
self.ovn_routing_tables_routes, port_ips, bridge_device,
bridge_vlan, self.ovn_routing_tables[bridge_device], proxy_cidrs)
bridge_vlan, self.ovn_routing_tables, proxy_cidrs)
def test__get_bridge_for_localnet_port(self):
localnet = fakes.create_object({
@ -363,10 +348,11 @@ class TestNBOVNBGPDriver(test_base.TestCase):
self.assertEqual(self.nb_bgp_driver.ovn_provider_ls[logical_switch],
{'bridge_device': 'br-ex', 'bridge_vlan': 10})
if row.type == constants.OVN_VIRTUAL_VIF_PORT_TYPE and cidr:
mock_expose_provider_port.assert_called_once_with(ips, 'br-ex',
10, [cidr])
mock_expose_provider_port.assert_called_once_with(
ips, 'test-ls', 'br-ex', 10, [cidr])
else:
mock_expose_provider_port.assert_called_once_with(ips, 'br-ex', 10)
mock_expose_provider_port.assert_called_once_with(
ips, 'test-ls', 'br-ex', 10)
def test_expose_ip(self):
ips = [self.ipv4, self.ipv6]
@ -422,11 +408,11 @@ class TestNBOVNBGPDriver(test_base.TestCase):
mock_get_ls_localnet_info.assert_called_once_with(logical_switch)
if row.type == constants.OVN_VIRTUAL_VIF_PORT_TYPE and cidr:
mock_withdraw_provider_port.assert_called_once_with(ips, 'br-ex',
10, [cidr])
mock_withdraw_provider_port.assert_called_once_with(
ips, 'test-ls', 'br-ex', 10, [cidr])
else:
mock_withdraw_provider_port.assert_called_once_with(ips, 'br-ex',
10)
mock_withdraw_provider_port.assert_called_once_with(
ips, 'test-ls', 'br-ex', 10)
def test_withdraw_ip(self):
ips = [self.ipv4, self.ipv6]
@ -527,13 +513,14 @@ class TestNBOVNBGPDriver(test_base.TestCase):
mock_get_ls_localnet_info.return_value = ('br-ex', 100)
mock_expose_provider_port = mock.patch.object(
self.nb_bgp_driver, '_expose_provider_port').start()
row = fakes.create_object({
'external_ids': {constants.OVN_LS_NAME_EXT_ID_KEY: 'test-ls'}})
ret = self.nb_bgp_driver.expose_fip(ip, logical_switch)
ret = self.nb_bgp_driver.expose_fip(ip, logical_switch, row)
mock_get_ls_localnet_info.assert_called_once_with(logical_switch)
mock_expose_provider_port.assert_called_once_with([ip], 'br-ex', 100)
self.assertEqual(self.nb_bgp_driver.ovn_fips[ip],
{'bridge_device': 'br-ex', 'bridge_vlan': 100})
mock_expose_provider_port.assert_called_once_with([ip], 'test-ls',
'br-ex', 100)
self.assertTrue(ret)
def test_expose_fip_no_device(self):
@ -544,29 +531,37 @@ class TestNBOVNBGPDriver(test_base.TestCase):
mock_get_ls_localnet_info.return_value = (None, None)
mock_expose_provider_port = mock.patch.object(
self.nb_bgp_driver, '_expose_provider_port').start()
row = fakes.create_object({
'external_ids': {constants.OVN_LS_NAME_EXT_ID_KEY: 'test-ls'}})
ret = self.nb_bgp_driver.expose_fip(ip, logical_switch)
ret = self.nb_bgp_driver.expose_fip(ip, logical_switch, row)
mock_get_ls_localnet_info.assert_called_once_with(logical_switch)
mock_expose_provider_port.assert_not_called()
self.assertNotIn(ip, self.nb_bgp_driver.ovn_fips)
self.assertNotIn(
ip, self.nb_bgp_driver._exposed_ips.get('test-ls', {}).keys())
self.assertFalse(ret)
def test_withdraw_fip(self):
ip = '10.0.0.1'
self.nb_bgp_driver.ovn_fips = {ip: {'bridge_device': 'br-ex',
'bridge_vlan': 100}}
self.nb_bgp_driver._exposed_ips['test-ls'] = {
ip: {'bridge_device': 'br-ex', 'bridge_vlan': 100}}
mock_withdraw_provider_port = mock.patch.object(
self.nb_bgp_driver, '_withdraw_provider_port').start()
row = fakes.create_object({
'external_ids': {constants.OVN_LS_NAME_EXT_ID_KEY: 'test-ls'}})
self.nb_bgp_driver.withdraw_fip(ip)
mock_withdraw_provider_port.assert_called_once_with([ip], "br-ex", 100)
self.nb_bgp_driver.withdraw_fip(ip, row)
mock_withdraw_provider_port.assert_called_once_with([ip], 'test-ls',
'br-ex', 100)
def test_withdraw_fip_not_found(self):
ip = '10.0.0.1'
self.nb_bgp_driver.ovn_fips = {}
self.nb_bgp_driver._exposed_ips = {}
mock_withdraw_provider_port = mock.patch.object(
self.nb_bgp_driver, '_withdraw_provider_port').start()
row = fakes.create_object({
'external_ids': {constants.OVN_LS_NAME_EXT_ID_KEY: 'test-ls'}})
self.nb_bgp_driver.withdraw_fip(ip)
self.nb_bgp_driver.withdraw_fip(ip, row)
mock_withdraw_provider_port.assert_not_called()

View File

@ -258,7 +258,8 @@ class TestLogicalSwitchPortFIPCreateEvent(test_base.TestCase):
row = utils.create_row(type=constants.OVN_VM_VIF_PORT_TYPE,
name='net-id')
self.event.run(mock.Mock(), row, mock.Mock())
self.agent.expose_fip.assert_called_once_with(external_ip, ls_name)
self.agent.expose_fip.assert_called_once_with(external_ip, ls_name,
row)
def test_run_no_external_ip(self):
external_ip = None
@ -368,7 +369,7 @@ class TestLogicalSwitchPortFIPDeleteEvent(test_base.TestCase):
constants.OVN_FIP_EXT_ID_KEY: 'fip-ip'},
up=True)
self.event.run(mock.Mock(), row, mock.Mock())
self.agent.withdraw_fip.assert_called_once_with('fip-ip')
self.agent.withdraw_fip.assert_called_once_with('fip-ip', row)
def test_run_no_fip(self):
row = utils.create_row(type=constants.OVN_VM_VIF_PORT_TYPE,

View File

@ -247,6 +247,68 @@ def _ensure_routing_table_routes(ovn_routing_tables, bridge):
return extra_routes
@tenacity.retry(
retry=tenacity.retry_if_exception_type(
netlink_exceptions.NetlinkDumpInterrupted),
wait=tenacity.wait_exponential(multiplier=0.02, max=1),
stop=tenacity.stop_after_delay(8),
reraise=True)
def get_extra_routing_table_for_bridge(ovn_routing_tables, bridge):
extra_routes = []
with pyroute2.NDB() as ndb:
table_route_dsts = set(
[
(r.dst, r.dst_len)
for r in ndb.routes.summary().filter(
table=ovn_routing_tables[bridge]
)
]
)
if not table_route_dsts:
return extra_routes
for (dst, dst_len) in table_route_dsts:
if not dst: # default route
try:
route = ndb.routes[
{'table': ovn_routing_tables[bridge],
'dst': '',
'family': AF_INET}]
if (bridge != ndb.interfaces[{'index': route['oif']}][
'ifname']):
extra_routes.append(route)
except KeyError:
pass # no ipv4 default rule
try:
route_6 = ndb.routes[
{'table': ovn_routing_tables[bridge],
'dst': '',
'family': AF_INET6}]
if (bridge != ndb.interfaces[{'index': route_6['oif']}][
'ifname']):
extra_routes.append(route_6)
except KeyError:
pass # no ipv6 default rule
else:
if get_ip_version(dst) == constants.IP_VERSION_6:
extra_routes.append(
ndb.routes[{'table': ovn_routing_tables[bridge],
'dst': dst,
'dst_len': dst_len,
'family': AF_INET6}]
)
else:
extra_routes.append(
ndb.routes[{'table': ovn_routing_tables[bridge],
'dst': dst,
'dst_len': dst_len,
'family': AF_INET}]
)
return extra_routes
def ensure_vlan_device_for_network(bridge, vlan_tag):
ovn_bgp_agent.privileged.linux_net.ensure_vlan_device_for_network(bridge,
vlan_tag)