Ensure ovn loadbalancers are properly managed

After change [1] in ovn-octavia, there is no information at the
Load_Balancer table on OVN SB DB related to the provider network.
This means the logic to managed the OVN loadbalancer with VIPs on
the provider networks needed to be updated to account for it.

[1] https://review.opendev.org/c/openstack/ovn-octavia-provider/+/871263

Change-Id: I6fbfb3eeb115c3a528d580561868e45ac72e8318
This commit is contained in:
Luis Tomas Bolivar 2023-02-08 14:38:48 +01:00
parent b42a05ac30
commit 9dfaefde44
9 changed files with 219 additions and 463 deletions

View File

@ -53,3 +53,6 @@ LINK_DOWN = "down"
SUBNET_POOL_ADDR_SCOPE4 = "neutron:subnet_pool_addr_scope4"
SUBNET_POOL_ADDR_SCOPE6 = "neutron:subnet_pool_addr_scope6"
EXPOSE = "expose"
WITHDRAW = "withdraw"

View File

@ -37,7 +37,7 @@ LOG = logging.getLogger(__name__)
# LOG.setLevel(logging.DEBUG)
# logging.basicConfig(level=logging.DEBUG)
OVN_TABLES = ["Port_Binding", "Chassis", "Datapath_Binding", "Load_Balancer"]
OVN_TABLES = ["Port_Binding", "Chassis", "Datapath_Binding"]
class OVNBGPDriver(driver_api.AgentDriverBase):
@ -134,14 +134,13 @@ class OVNBGPDriver(driver_api.AgentDriverBase):
"PortBindingChassisDeletedEvent",
"FIPSetEvent",
"FIPUnsetEvent",
"OVNLBMemberUpdateEvent",
"OVNLBVIPPortEvent",
"ChassisCreateEvent"])
if self._expose_tenant_networks:
events.update(["SubnetRouterAttachedEvent",
"SubnetRouterDetachedEvent",
"TenantPortCreatedEvent",
"TenantPortDeletedEvent",
"OVNLBTenantPortEvent"])
"TenantPortDeletedEvent"])
return events
@lockutils.synchronized('bgp')
@ -229,11 +228,12 @@ class OVNBGPDriver(driver_api.AgentDriverBase):
# add missing routes/ips related to ovn-octavia loadbalancers
# on the provider networks
ovn_lbs = self.sb_idl.get_ovn_lb_on_provider_datapath(
ovn_lb_vips = self.sb_idl.get_ovn_lb_vips_on_provider_datapath(
cr_lrp_info['provider_datapath'])
for ovn_lb in ovn_lbs:
self._process_ovn_lb(ovn_lb, cr_lrp_port, exposed_ips,
ovn_ip_rules)
for ovn_lb_port, ovn_lb_ip in ovn_lb_vips.items():
self._expose_ovn_lb_on_provider(ovn_lb_ip,
ovn_lb_port,
cr_lrp_port)
# remove extra routes/ips
# remove all the leftovers on the list of current ips on dev OVN
@ -407,12 +407,39 @@ class OVNBGPDriver(driver_api.AgentDriverBase):
return None, None
@lockutils.synchronized('bgp')
def expose_ovn_lb_on_provider(self, ovn_lb, ip, cr_lrp):
self._expose_ovn_lb_on_provider(ovn_lb, ip, cr_lrp)
def expose_ovn_lb(self, ip, row):
self._process_ovn_lb(ip, row, constants.EXPOSE)
def _expose_ovn_lb_on_provider(self, ovn_lb, ip, cr_lrp):
self.ovn_local_cr_lrps[cr_lrp]['ovn_lbs'].append(ovn_lb)
self.ovn_lb_vips.setdefault(ovn_lb, []).append(ip)
@lockutils.synchronized('bgp')
def withdraw_ovn_lb(self, ip, row):
self._process_ovn_lb(ip, row, constants.WITHDRAW)
def _process_ovn_lb(self, ip, row, action):
if not self.sb_idl.is_provider_network(row.datapath):
if not self._expose_tenant_networks:
return
if action == constants.EXPOSE:
return self._expose_remote_ip([ip], row)
if action == constants.WITHDRAW:
return self._withdraw_remote_ip([ip], row)
# if unknown action return
return
local_lb_cr_lrp = None
for cr_lrp_port, cr_lrp_info in self.ovn_local_cr_lrps.items():
if cr_lrp_info['provider_datapath'] == row.datapath:
local_lb_cr_lrp = cr_lrp_port
break
if not local_lb_cr_lrp:
return
vip_port = row.logical_port
if action == constants.EXPOSE:
self._expose_ovn_lb_on_provider(ip, vip_port, local_lb_cr_lrp)
if action == constants.WITHDRAW:
self._withdraw_ovn_lb_on_provider(vip_port, local_lb_cr_lrp)
def _expose_ovn_lb_on_provider(self, ip, ovn_lb_vip_port, cr_lrp):
self.ovn_local_cr_lrps[cr_lrp]['ovn_lb_vips'].append(ovn_lb_vip_port)
self.ovn_lb_vips.setdefault(ovn_lb_vip_port, []).append(ip)
bridge_device = self.ovn_local_cr_lrps[cr_lrp]['bridge_device']
bridge_vlan = self.ovn_local_cr_lrps[cr_lrp]['bridge_vlan']
@ -421,21 +448,21 @@ class OVNBGPDriver(driver_api.AgentDriverBase):
bridge_vlan=bridge_vlan)
LOG.debug("Added BGP route for loadbalancer VIP %s", ip)
@lockutils.synchronized('bgp')
def withdraw_ovn_lb_on_provider(self, ovn_lb, cr_lrp):
def _withdraw_ovn_lb_on_provider(self, ovn_lb_vip_port, cr_lrp):
bridge_device = self.ovn_local_cr_lrps[cr_lrp]['bridge_device']
bridge_vlan = self.ovn_local_cr_lrps[cr_lrp]['bridge_vlan']
for ip in self.ovn_lb_vips[ovn_lb].copy():
for ip in self.ovn_lb_vips[ovn_lb_vip_port].copy():
LOG.debug("Deleting BGP route for loadbalancer VIP %s", ip)
self._withdraw_provider_port([ip], None,
bridge_device=bridge_device,
bridge_vlan=bridge_vlan)
if ip in self.ovn_lb_vips[ovn_lb]:
self.ovn_lb_vips[ovn_lb].remove(ip)
if ip in self.ovn_lb_vips[ovn_lb_vip_port]:
self.ovn_lb_vips[ovn_lb_vip_port].remove(ip)
LOG.debug("Deleted BGP route for loadbalancer VIP %s", ip)
if ovn_lb in self.ovn_local_cr_lrps[cr_lrp]['ovn_lbs']:
self.ovn_local_cr_lrps[cr_lrp]['ovn_lbs'].remove(ovn_lb)
if ovn_lb_vip_port in self.ovn_local_cr_lrps[cr_lrp]['ovn_lb_vips']:
self.ovn_local_cr_lrps[cr_lrp]['ovn_lb_vips'].remove(
ovn_lb_vip_port)
@lockutils.synchronized('bgp')
def expose_ip(self, ips, row, associated_port=None):
@ -521,7 +548,7 @@ class OVNBGPDriver(driver_api.AgentDriverBase):
'mac': mac,
'subnets_datapath': {},
'subnets_cidr': [],
'ovn_lbs': [],
'ovn_lb_vips': [],
'bridge_vlan': bridge_vlan,
'bridge_device': bridge_device
}
@ -620,6 +647,9 @@ class OVNBGPDriver(driver_api.AgentDriverBase):
@lockutils.synchronized('bgp')
def expose_remote_ip(self, ips, row):
self._expose_remote_ip(ips, row)
def _expose_remote_ip(self, ips, row):
if (self.sb_idl.is_provider_network(row.datapath) or
not self._expose_tenant_networks):
return
@ -650,6 +680,9 @@ class OVNBGPDriver(driver_api.AgentDriverBase):
@lockutils.synchronized('bgp')
def withdraw_remote_ip(self, ips, row, chassis=None):
self._withdraw_remote_ip(ips, row, chassis)
def _withdraw_remote_ip(self, ips, row, chassis=None):
if (self.sb_idl.is_provider_network(row.datapath) or
not self._expose_tenant_networks):
return
@ -690,7 +723,7 @@ class OVNBGPDriver(driver_api.AgentDriverBase):
'mac': mac,
'subnets_datapath': {},
'subnets_cidr': [],
'ovn_lbs': [],
'ovn_lb_vips': [],
'bridge_vlan': bridge_vlan,
'bridge_device': bridge_device
}
@ -725,32 +758,6 @@ class OVNBGPDriver(driver_api.AgentDriverBase):
exposed_ips=exposed_ips,
ovn_ip_rules=ovn_ip_rules)
def _process_ovn_lb(self, ovn_lb, cr_lrp_port, exposed_ips=[],
ovn_ip_rules={}):
if hasattr(ovn_lb, 'datapath_group'):
ovn_lb_datapaths = ovn_lb.datapath_group[0].datapaths
else:
ovn_lb_datapaths = ovn_lb.datapaths
for ovn_dp in ovn_lb_datapaths:
if ovn_dp in self.ovn_local_cr_lrps[
cr_lrp_port]['subnets_datapath'].values():
break
else:
return
for vip in ovn_lb.vips.keys():
ip = driver_utils.parse_vip_from_lb_table(vip)
self._expose_ovn_lb_on_provider(ovn_lb.name, ip, cr_lrp_port)
if exposed_ips and ip in exposed_ips:
exposed_ips.remove(ip)
if ovn_ip_rules:
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)
def _expose_cr_lrp_port(self, ips, mac, bridge_device, bridge_vlan,
router_datapath, provider_datapath, cr_lrp_port):
LOG.debug("Adding BGP route for CR-LRP Port %s", ips)
@ -770,10 +777,12 @@ class OVNBGPDriver(driver_api.AgentDriverBase):
for lrp in lrp_ports:
self._process_lrp_port(lrp, cr_lrp_port)
ovn_lbs = self.sb_idl.get_ovn_lb_on_provider_datapath(
ovn_lb_vips = self.sb_idl.get_ovn_lb_vips_on_provider_datapath(
provider_datapath)
for ovn_lb in ovn_lbs:
self._process_ovn_lb(ovn_lb, cr_lrp_port)
for ovn_lb_port, ovn_lb_ip in ovn_lb_vips.items():
self._expose_ovn_lb_on_provider(ovn_lb_ip,
ovn_lb_port,
cr_lrp_port)
def _withdraw_cr_lrp_port(self, ips, mac, bridge_device, bridge_vlan,
provider_datapath, cr_lrp_port):
@ -805,10 +814,12 @@ class OVNBGPDriver(driver_api.AgentDriverBase):
# check if there are loadbalancers associated to the router,
# and if so delete the needed routes/rules
ovn_lbs = self.ovn_local_cr_lrps[cr_lrp_port]['ovn_lbs'].copy()
for ovn_lb in ovn_lbs:
self.withdraw_ovn_lb_on_provider(ovn_lb, cr_lrp_port)
self.ovn_local_cr_lrps[cr_lrp_port]['ovn_lbs'].remove(ovn_lb)
ovn_lb_vips = self.ovn_local_cr_lrps[cr_lrp_port][
'ovn_lb_vips'].copy()
for ovn_lb_vip in ovn_lb_vips:
self._withdraw_ovn_lb_on_provider(ovn_lb_vip, cr_lrp_port)
self.ovn_local_cr_lrps[cr_lrp_port]['ovn_lb_vips'].remove(
ovn_lb_vip)
try:
del self.ovn_local_cr_lrps[cr_lrp_port]
except KeyError:

View File

@ -21,19 +21,6 @@ from ovn_bgp_agent.utils import linux_net
LOG = logging.getLogger(__name__)
def parse_vip_from_lb_table(vip):
# The VIP format comes from the Load_Balancer table as is like:
# (ipv4): 172.24.100.66:80
# (ipv6): [2001:db8::f816:3eff:fe55:ef1e]:80
vip_split = vip.split("]:")
if len(vip_split) == 1: # ipv4
return vip.split(":")[0]
if len(vip_split) == 2: # ipv6
return vip_split[0].split("[")[1]
LOG.error("Malformated VIP at Load Balancer SB table: %s", vip)
def is_ipv6_gua(ip):
if linux_net.get_ip_version(ip) != constants.IP_VERSION_6:
return False

View File

@ -303,27 +303,23 @@ class OvsdbSbOvnIdl(sb_impl_idl.OvnSbApiIdlImpl, Backend):
datapath, port_type=constants.OVN_VIRTUAL_VIF_PORT_TYPE)
return [r for r in rows if r.chassis and r.chassis[0].name == chassis]
def get_ovn_lb_on_provider_datapath(self, datapath):
# TODO(ltomasbo): Once ovsdbapp supports {>=} operator we can query
# it directly with:
# ovn_lbs = self.db_find_rows(
# 'Load_Balancer',
# ('datapaths', '{>=}', [datapath])).execute(check_error=True)
# return [ovn_lb for ovn_lb in ovn_lbs if len(ovn_lb.datapaths) > 1]
ovn_lbs = self.db_list_rows('Load_Balancer').execute(
check_error=True)
def get_ovn_lb_vips_on_provider_datapath(self, datapath):
# return {vip_port: vip_ip, vip_port2: vip_ip2, ...}
# ovn-sbctl find port_binding type=\"\" chassis=[] mac=[] up=false
cmd = self.db_find_rows('Port_Binding',
('datapath', '=', datapath),
('type', '=', constants.OVN_VM_VIF_PORT_TYPE),
('chassis', '=', []),
('mac', '=', []),
('up', '=', False))
lbs = {}
for row in cmd.execute(check_error=True):
# This is depending on the external-id information added by
# neutron, regarding the neutron:cidrs
ext_n_cidr = row.external_ids.get(constants.OVN_CIDRS_EXT_ID_KEY)
if not ext_n_cidr:
continue
ovn_lb_ip = ext_n_cidr.split(" ")[0].split("/")[0]
lbs[row.logical_port] = ovn_lb_ip
lbs = []
for ovn_lb in ovn_lbs:
if hasattr(ovn_lb, 'datapath_group'):
if ovn_lb.datapath_group:
dp_group_datapaths = ovn_lb.datapath_group[0].datapaths
if (len(dp_group_datapaths) > 1 and
datapath in dp_group_datapaths):
lbs.append(ovn_lb)
else:
# TODO(ltomasbo): Once usage of datapath_group is common, we
# should remove the checks for datapaths
if len(ovn_lb.datapaths) > 1 and datapath in ovn_lb.datapaths:
lbs.append(ovn_lb)
return lbs

View File

@ -25,12 +25,3 @@ class PortBindingChassisEvent(row_event.RowEvent):
def _check_ip_associated(self, mac):
return len(mac.split(' ')) > 1
class OVNLBMemberEvent(row_event.RowEvent):
def __init__(self, bgp_agent, events):
self.agent = bgp_agent
table = 'Load_Balancer'
super(OVNLBMemberEvent, self).__init__(
events, table, None)
self.event_name = self.__class__.__name__

View File

@ -17,7 +17,6 @@ from oslo_log import log as logging
from ovsdbapp.backend.ovs_idl import event as row_event
from ovn_bgp_agent import constants
from ovn_bgp_agent.drivers.openstack.utils import driver_utils
from ovn_bgp_agent.drivers.openstack.watchers import base_watcher
@ -315,18 +314,20 @@ class TenantPortDeletedEvent(base_watcher.PortBindingChassisEvent):
self.agent.withdraw_remote_ip(ips, row, chassis)
class OVNLBTenantPortEvent(base_watcher.PortBindingChassisEvent):
class OVNLBVIPPortEvent(base_watcher.PortBindingChassisEvent):
def __init__(self, bgp_agent):
events = (self.ROW_CREATE, self.ROW_DELETE,)
super(OVNLBTenantPortEvent, self).__init__(
super(OVNLBVIPPortEvent, self).__init__(
bgp_agent, events)
def match_fn(self, event, row, old):
# The ovn lb balancers are exposed through the cr-lrp, so if the
# local agent does not have any cr-lrp associated there is no need
# to process the event
try:
# it should not have mac, no chassis, and status down
if not row.mac and not row.chassis and not row.up[0]:
if self.agent.ovn_local_lrps != []:
return True
return bool(self.agent.ovn_local_cr_lrps)
return False
except (IndexError, AttributeError):
return False
@ -344,111 +345,9 @@ class OVNLBTenantPortEvent(base_watcher.PortBindingChassisEvent):
ovn_lb_ip = ext_n_cidr.split(" ")[0].split("/")[0]
if event == self.ROW_DELETE:
self.agent.withdraw_remote_ip([ovn_lb_ip], row)
self.agent.withdraw_ovn_lb(ovn_lb_ip, row)
if event == self.ROW_CREATE:
self.agent.expose_remote_ip([ovn_lb_ip], row)
class OVNLBMemberUpdateEvent(base_watcher.OVNLBMemberEvent):
def __init__(self, bgp_agent):
events = (self.ROW_UPDATE, self.ROW_DELETE,)
super(OVNLBMemberUpdateEvent, self).__init__(
bgp_agent, events)
def match_fn(self, event, row, old):
# Only interested in update events related to associated datapaths
if event == self.ROW_DELETE:
return bool(self.agent.ovn_local_cr_lrps)
if hasattr(row, 'datapath_group'):
try:
# NOTE(ltomasbo): We need to account for datapaths on
# datapath_group being updated instead of changing the group
if row.datapath_group and old.datapath_group:
if (row.datapath_group[0].datapaths ==
old.datapath_group[0].datapaths):
return False
except (IndexError, AttributeError):
return False
else:
# TODO(ltomasbo): Once usage of datapath_group is common, we
# should remove the checks for datapaths
try:
if row.datapaths == old.datapaths:
return False
except AttributeError:
return False
# Only process event if the local node has a cr-lrp ports associated
return bool(self.agent.ovn_local_cr_lrps)
def run(self, event, row, old):
# Only process event if the local node has a cr-lrp port whose provider
# datapath is included into the loadbalancer. This means the
# loadbalancer has the VIP on a provider network
# Also, the cr-lrp port needs to have subnet datapaths (LS) associated
# to it that include the load balancer
try:
row_dp = row.datapaths
except AttributeError:
row_dp = []
try:
old_dp = old.datapaths
except AttributeError:
old_dp = []
if hasattr(row, 'datapath_group'):
if row.datapath_group:
dp_datapaths = row.datapath_group[0].datapaths
if dp_datapaths:
row_dp = dp_datapaths
if hasattr(old, 'datapath_group'):
if old.datapath_group:
dp_datapaths = old.datapath_group[0].datapaths
if dp_datapaths:
old_dp = dp_datapaths
ovn_lb_cr_lrp = ""
for cr_lrp_port, cr_lrp_info in self.agent.ovn_local_cr_lrps.items():
if cr_lrp_info.get('provider_datapath') not in row_dp:
continue
if event == self.ROW_DELETE or not row.vips:
if row.name in cr_lrp_info['ovn_lbs']:
ovn_lb_cr_lrp = cr_lrp_port
break
else:
# old.datapath needed for members deletion on different subnet
match_subnets_datapaths = [
subnet_dp for subnet_dp in cr_lrp_info[
'subnets_datapath'].values()
if (subnet_dp in row_dp)]
if match_subnets_datapaths:
ovn_lb_cr_lrp = cr_lrp_port
break
if not ovn_lb_cr_lrp:
return
with _SYNC_STATE_LOCK.read_lock():
if event == self.ROW_DELETE:
# loadbalancer deleted. Withdraw the VIP through the cr-lrp
return self.agent.withdraw_ovn_lb_on_provider(row.name,
ovn_lb_cr_lrp)
if not row.vips:
# last member deleted. Withdraw the VIP through the cr-lrp
return self.agent.withdraw_ovn_lb_on_provider(row.name,
ovn_lb_cr_lrp)
# NOTE(ltomasbo): It is assumed that the rest of the datapaths in
# the datapaths fields belongs to networks (Logical_Switch)
# connected to the provider network datapath through a single
# router (cr-lrp)
if len(old_dp) <= 1 and len(row_dp) > 1:
# first member added, time to expose the VIP through the cr-lrp
for vip in row.vips.keys():
ip = driver_utils.parse_vip_from_lb_table(vip)
if ip:
return self.agent.expose_ovn_lb_on_provider(
row.name, ip, ovn_lb_cr_lrp)
self.agent.expose_ovn_lb(ovn_lb_ip, row)
class ChassisCreateEventBase(row_event.RowEvent):

View File

@ -53,9 +53,9 @@ class TestOVNBGPDriver(test_base.TestCase):
self.ipv6 = '2002::1234:abcd:ffff:c0a8:101'
self.fip = '172.24.4.33'
self.mac = 'aa:bb:cc:dd:ee:ff'
self.loadbalancer = 'fake-lb'
self.loadbalancer_vip_port = 'fake-lb-vip-port'
self.bgp_driver.ovn_lb_vips = {
self.loadbalancer: [self.ipv4, self.ipv6]}
self.loadbalancer_vip_port: [self.ipv4, self.ipv6]}
self.bgp_driver.ovs_idl = self.mock_ovs_idl
self.cr_lrp0 = 'cr-fake-logical-port'
@ -67,7 +67,7 @@ class TestOVNBGPDriver(test_base.TestCase):
'ips': [self.fip],
'subnets_datapath': {self.lrp0: 'fake-lrp-dp'},
'subnets_cidr': ['192.168.1.1/24'],
'ovn_lbs': [],
'ovn_lb_vips': [],
'bridge_device': self.bridge,
'bridge_vlan': None},
self.cr_lrp1: {'provider_datapath': 'fake-provider-dp2'}}
@ -816,11 +816,82 @@ class TestOVNBGPDriver(test_base.TestCase):
ret = self.bgp_driver._get_bridge_for_datapath('fake-dp')
self.assertEqual((None, None), ret)
def test_expose_ovn_lb_on_provider(self):
def test_expose_ovn_lb(self):
mock_process_ovn_lb = mock.patch.object(
self.bgp_driver, '_process_ovn_lb').start()
self.bgp_driver.expose_ovn_lb('fake-ip', 'fake-row')
mock_process_ovn_lb.assert_called_once_with(
'fake-ip', 'fake-row', constants.EXPOSE)
def test_withdraw_ovn_lb(self):
mock_process_ovn_lb = mock.patch.object(
self.bgp_driver, '_process_ovn_lb').start()
self.bgp_driver.withdraw_ovn_lb('fake-ip', 'fake-row')
mock_process_ovn_lb.assert_called_once_with(
'fake-ip', 'fake-row', constants.WITHDRAW)
def _test_process_ovn_lb(self, action, provider=False):
mock_expose_remote_ip = mock.patch.object(
self.bgp_driver, '_expose_remote_ip').start()
mock_withdraw_remote_ip = mock.patch.object(
self.bgp_driver, '_withdraw_remote_ip').start()
mock_expose_ovn_lb = mock.patch.object(
self.bgp_driver, '_expose_ovn_lb_on_provider').start()
mock_withdraw_ovn_lb = mock.patch.object(
self.bgp_driver, '_withdraw_ovn_lb_on_provider').start()
self.sb_idl.is_provider_network.return_value = provider
ip = 'fake-vip-ip'
row = fakes.create_object({
'logical_port': 'fake-vip',
'type': constants.OVN_VM_VIF_PORT_TYPE,
'datapath': 'fake-provider-dp'})
self.bgp_driver._process_ovn_lb(ip, row, action)
if action == constants.EXPOSE:
if provider:
mock_expose_remote_ip.assert_not_called()
mock_withdraw_remote_ip.assert_not_called()
mock_expose_ovn_lb.assert_called_once_with(
ip, row.logical_port, self.cr_lrp0)
mock_withdraw_ovn_lb.assert_not_called()
else:
mock_expose_ovn_lb.assert_not_called()
mock_withdraw_ovn_lb.assert_not_called()
mock_expose_remote_ip.assert_called_once_with([ip], row)
mock_withdraw_remote_ip.assert_not_called()
if action == constants.WITHDRAW:
if provider:
mock_expose_remote_ip.assert_not_called()
mock_withdraw_remote_ip.assert_not_called()
mock_expose_ovn_lb.assert_not_called()
mock_withdraw_ovn_lb.assert_called_once_with(
row.logical_port, self.cr_lrp0)
else:
mock_expose_ovn_lb.assert_not_called()
mock_withdraw_ovn_lb.assert_not_called()
mock_expose_remote_ip.assert_not_called()
mock_withdraw_remote_ip.assert_called_once_with([ip], row)
def test__process_ovn_lb_expose_provider(self):
self._test_process_ovn_lb(action=constants.EXPOSE, provider=True)
def test__process_ovn_lb_expose_no_provider(self):
self._test_process_ovn_lb(action=constants.EXPOSE)
def test__process_ovn_lb_withdraw_provider(self):
self._test_process_ovn_lb(action=constants.WITHDRAW, provider=True)
def test__process_ovn_lb_withdraw_no_provider(self):
self._test_process_ovn_lb(action=constants.WITHDRAW)
def test__expose_ovn_lb_on_provider(self):
mock_expose_provider_port = mock.patch.object(
self.bgp_driver, '_expose_provider_port').start()
self.bgp_driver.expose_ovn_lb_on_provider(
self.loadbalancer, self.ipv4, self.cr_lrp0)
self.bgp_driver._expose_ovn_lb_on_provider(
self.ipv4, self.loadbalancer_vip_port, self.cr_lrp0)
# Assert that the add methods were called
mock_expose_provider_port.assert_called_once_with(
@ -829,10 +900,10 @@ class TestOVNBGPDriver(test_base.TestCase):
@mock.patch.object(linux_net, 'del_ip_route')
@mock.patch.object(linux_net, 'del_ip_rule')
@mock.patch.object(linux_net, 'del_ips_from_dev')
def test_withdraw_ovn_lb_on_provider(
def test__withdraw_ovn_lb_on_provider(
self, mock_del_ip_dev, mock_del_rule, mock_del_route):
self.bgp_driver.withdraw_ovn_lb_on_provider(
self.loadbalancer, self.cr_lrp0)
self.bgp_driver._withdraw_ovn_lb_on_provider(
self.loadbalancer_vip_port, self.cr_lrp0)
# Assert that the del methods were called
expected_calls = [mock.call(CONF.bgp_nic, [self.ipv4]),
@ -1048,17 +1119,11 @@ class TestOVNBGPDriver(test_base.TestCase):
'datapath': 'fake-router-dp'})
ovn_lb_vip = '172.24.4.5'
ovn_lb_vip_port = ovn_lb_vip + ':80'
ovn_lb1 = fakes.create_object({
'name': 'ovn_lb1', 'datapaths': [self.cr_lrp0, 'fake-lrp-dp'],
'vips': {ovn_lb_vip_port: '192.168.100.5:::8080'}})
ovn_lb2 = fakes.create_object({
'name': 'ovn_lb2', 'datapaths': [self.cr_lrp0, 'fake-lrp1-db'],
'vips': {'172.24.4.6:80': '192.168.200.5:::8080'}})
self.sb_idl.get_ovn_lb_on_provider_datapath.return_value = [ovn_lb1,
ovn_lb2]
mock_process_ovn_lb = mock.patch.object(
self.bgp_driver, '_process_ovn_lb').start()
ovn_lb_vips = {'fake-vip-port': ovn_lb_vip}
self.sb_idl.get_ovn_lb_vips_on_provider_datapath.return_value = (
ovn_lb_vips)
mock_expose_ovn_lb = mock.patch.object(
self.bgp_driver, '_expose_ovn_lb_on_provider').start()
ips = [self.ipv4, self.ipv6]
self.bgp_driver.expose_ip(ips, row)
@ -1085,10 +1150,8 @@ class TestOVNBGPDriver(test_base.TestCase):
mock.call(lrp1, self.cr_lrp0),
mock.call(lrp2, self.cr_lrp0)]
mock_process_lrp_port.assert_has_calls(expected_calls)
expected_calls = [mock.call(ovn_lb1, self.cr_lrp0),
mock.call(ovn_lb2, self.cr_lrp0)]
mock_process_ovn_lb.assert_has_calls(expected_calls)
mock_expose_ovn_lb.assert_called_once_with(
ovn_lb_vip, 'fake-vip-port', self.cr_lrp0)
@mock.patch.object(linux_net, 'del_ip_route')
@mock.patch.object(linux_net, 'del_ip_rule')
@ -1490,16 +1553,17 @@ class TestOVNBGPDriver(test_base.TestCase):
self.bgp_driver, '_expose_provider_port').start()
mock_process_lrp_port = mock.patch.object(
self.bgp_driver, '_process_lrp_port').start()
mock_process_ovn_lb = mock.patch.object(
self.bgp_driver, '_process_ovn_lb').start()
mock_expose_ovn_lb = mock.patch.object(
self.bgp_driver, '_expose_ovn_lb_on_provider').start()
ips = [self.ipv4, self.ipv6]
mock_ip_version.side_effect = [constants.IP_VERSION_4,
constants.IP_VERSION_6]
dp_port0 = mock.Mock()
self.sb_idl.get_lrp_ports_for_router.return_value = [dp_port0]
ovn_lb = mock.Mock()
self.sb_idl.get_ovn_lb_on_provider_datapath.return_value = [ovn_lb]
ovn_lb_vips = {'fake-vip-port': 'fake-vip-ip'}
self.sb_idl.get_ovn_lb_vips_on_provider_datapath.return_value = (
ovn_lb_vips)
self.bgp_driver._expose_cr_lrp_port(
ips, self.mac, self.bridge, None, router_datapath='fake-router-dp',
@ -1511,7 +1575,8 @@ class TestOVNBGPDriver(test_base.TestCase):
lladdr=self.mac)
mock_ndp_proxy.assert_called_once_with(self.ipv6, self.bridge, None)
mock_process_lrp_port.assert_called_once_with(dp_port0, self.cr_lrp0)
mock_process_ovn_lb.assert_called_once_with(ovn_lb, self.cr_lrp0)
mock_expose_ovn_lb.assert_called_once_with(
'fake-vip-ip', 'fake-vip-port', self.cr_lrp0)
@mock.patch.object(linux_net, 'del_ndp_proxy')
@mock.patch.object(linux_net, 'get_ip_version')
@ -1521,12 +1586,12 @@ class TestOVNBGPDriver(test_base.TestCase):
mock_withdraw_lrp_port = mock.patch.object(
self.bgp_driver, '_withdraw_lrp_port').start()
mock_withdraw_ovn_lb_on_provider = mock.patch.object(
self.bgp_driver, 'withdraw_ovn_lb_on_provider').start()
self.bgp_driver, '_withdraw_ovn_lb_on_provider').start()
ips = [self.ipv4, self.ipv6]
mock_ip_version.side_effect = [constants.IP_VERSION_4,
constants.IP_VERSION_6]
ovn_lb = mock.Mock()
ovn_lb_vip_port = mock.Mock()
gateway = {
'ips': ips,
'provider_datapath': 'fake-provider-dp',
@ -1534,7 +1599,7 @@ class TestOVNBGPDriver(test_base.TestCase):
'bridge_device': self.bridge,
'bridge_vlan': 10,
'mac': self.mac,
'ovn_lbs': [ovn_lb]}
'ovn_lb_vips': [ovn_lb_vip_port]}
self.bgp_driver.ovn_local_cr_lrps = {'gateway_port': gateway}
self.bgp_driver._withdraw_cr_lrp_port(
@ -1549,7 +1614,7 @@ class TestOVNBGPDriver(test_base.TestCase):
mock_withdraw_lrp_port.assert_called_once_with('192.168.1.1/24', None,
'gateway_port')
mock_withdraw_ovn_lb_on_provider.assert_called_once_with(
ovn_lb, 'gateway_port')
ovn_lb_vip_port, 'gateway_port')
@mock.patch.object(linux_net, 'add_ip_route')
@mock.patch.object(linux_net, 'add_ip_rule')

View File

@ -26,7 +26,6 @@ from ovn_bgp_agent.drivers.openstack.utils import ovn as ovn_utils
from ovn_bgp_agent import exceptions
from ovn_bgp_agent.tests import base as test_base
from ovn_bgp_agent.tests.unit import fakes
from ovn_bgp_agent.tests import utils
CONF = cfg.CONF
@ -489,46 +488,19 @@ class TestOvsdbSbOvnIdl(test_base.TestCase):
self.assertEqual([port1], ret)
def test_get_ovn_lb_on_provider_datapath(self):
dp = 'fake-datapath'
dpg1 = utils.create_row(_uuid='fake_dp_group',
datapaths=['dp1'])
dpg2 = utils.create_row(_uuid='fake_dp_group',
datapaths=['dp1', dp])
dpg3 = utils.create_row(_uuid='fake_dp_group',
datapaths=[dp])
def test_get_ovn_lb_vips_on_provider_datapath(self):
port0 = fakes.create_object({
'logical_port': 'fake-port-0',
'external_ids': {constants.OVN_CIDRS_EXT_ID_KEY: '10.0.0.15/24'}})
port1 = fakes.create_object({'logical_port': 'fake-port-1',
'external_ids': {}})
ovn_lb1 = fakes.create_object(
{'name': 'ovn-lb1', 'datapath_group': [dpg1]})
ovn_lb2 = fakes.create_object(
{'name': 'ovn-lb2', 'datapath_group': [dpg2]})
ovn_lb3 = fakes.create_object(
{'name': 'ovn-lb3', 'datapath_group': [dpg3]})
self.sb_idl.db_find_rows.return_value.execute.return_value = [
port0, port1]
self.sb_idl.db_list_rows.return_value.execute.return_value = [
ovn_lb1, ovn_lb2, ovn_lb3]
ret = self.sb_idl.get_ovn_lb_on_provider_datapath(dp)
self.assertIn(ovn_lb2, ret)
self.assertNotIn(ovn_lb1, ret)
self.assertNotIn(ovn_lb3, ret)
def test_get_ovn_lb_on_provider_datapath_no_dadtapath_group(self):
dp = 'fake-datapath'
ovn_lb1 = fakes.create_object(
{'name': 'ovn-lb1', 'datapaths': ['dp1']})
ovn_lb2 = fakes.create_object(
{'name': 'ovn-lb2', 'datapaths': ['dp1', dp]})
ovn_lb3 = fakes.create_object(
{'name': 'ovn-lb3', 'datapaths': [dp]})
self.sb_idl.db_list_rows.return_value.execute.return_value = [
ovn_lb1, ovn_lb2, ovn_lb3]
ret = self.sb_idl.get_ovn_lb_on_provider_datapath(dp)
self.assertIn(ovn_lb2, ret)
self.assertNotIn(ovn_lb1, ret)
self.assertNotIn(ovn_lb3, ret)
ret = self.sb_idl.get_ovn_lb_vips_on_provider_datapath('fake-datapath')
expected_return = {'fake-port-0': '10.0.0.15'}
self.assertEqual(expected_return, ret)
class TestOvnSbIdl(test_base.TestCase):

View File

@ -736,14 +736,14 @@ class TestTenantPortDeletedEvent(test_base.TestCase):
['10.10.1.16'], row, mock.ANY)
class TestOVNLBTenantPortEvent(test_base.TestCase):
class OVNLBVIPPortEvent(test_base.TestCase):
def setUp(self):
super(TestOVNLBTenantPortEvent, self).setUp()
super(OVNLBVIPPortEvent, self).setUp()
self.chassis = '935f91fa-b8f8-47b9-8b1b-3a7a90ef7c26'
self.agent = mock.Mock(chassis=self.chassis)
self.agent.ovn_local_lrps = ['172.24.100.111']
self.event = bgp_watcher.OVNLBTenantPortEvent(self.agent)
self.agent.ovn_local_cr_lrps = {'fake-cr-lrp-port': {}}
self.event = bgp_watcher.OVNLBVIPPortEvent(self.agent)
def test_match_fn(self):
row = utils.create_row(chassis=[], mac=[], up=[False])
@ -763,12 +763,12 @@ class TestOVNLBTenantPortEvent(test_base.TestCase):
row = utils.create_row(chassis=[], mac=[], up=[True])
self.assertFalse(self.event.match_fn(mock.Mock(), row, mock.Mock()))
def test_match_fn_empty_ovn_local_lrps(self):
self.agent.ovn_local_lrps = []
def test_match_fn_empty_ovn_local_cr_lrps(self):
self.agent.ovn_local_cr_lrps = []
row = utils.create_row(chassis=[], mac=[], up=[False])
self.assertFalse(self.event.match_fn(mock.Mock(), row, mock.Mock()))
def test_match_fn_index_error(self):
def test_match_fn_attribute_error(self):
row = utils.create_row(mac=[])
self.assertFalse(self.event.match_fn(mock.Mock(), row, mock.Mock()))
@ -779,8 +779,8 @@ class TestOVNLBTenantPortEvent(test_base.TestCase):
up=[False], external_ids={
constants.OVN_CIDRS_EXT_ID_KEY: "10.10.1.16/24"})
self.event.run(event, row, mock.Mock())
self.agent.expose_remote_ip.assert_called_once_with(
['10.10.1.16'], row)
self.agent.expose_ovn_lb.assert_called_once_with(
'10.10.1.16', row)
def test_run_delete(self):
event = self.event.ROW_DELETE
@ -789,16 +789,16 @@ class TestOVNLBTenantPortEvent(test_base.TestCase):
up=[False], external_ids={
constants.OVN_CIDRS_EXT_ID_KEY: "10.10.1.16/24"})
self.event.run(event, row, mock.Mock())
self.agent.withdraw_remote_ip.assert_called_once_with(
['10.10.1.16'], row)
self.agent.withdraw_ovn_lb.assert_called_once_with(
'10.10.1.16', row)
def test_run_no_external_id(self):
row = utils.create_row(
type=constants.OVN_VM_VIF_PORT_TYPE, mac=[], chassis=[],
up=[False], external_ids={})
self.event.run(mock.Mock(), row, mock.Mock())
self.agent.expose_remote_ip.assert_not_called()
self.agent.withdraw_remote_ip.assert_not_called()
self.agent.expose_ovn_lb.assert_not_called()
self.agent.withdraw_ovn_lb.assert_not_called()
def test_run_wrong_type(self):
row = utils.create_row(
@ -806,176 +806,8 @@ class TestOVNLBTenantPortEvent(test_base.TestCase):
up=[False], external_ids={
constants.OVN_CIDRS_EXT_ID_KEY: "10.10.1.16/24"})
self.event.run(mock.Mock(), row, mock.Mock())
self.agent.expose_remote_ip.assert_not_called()
self.agent.withdraw_remote_ip.assert_not_called()
class TestOVNLBMemberUpdateEvent(test_base.TestCase):
def setUp(self):
super(TestOVNLBMemberUpdateEvent, self).setUp()
self.chassis = '935f91fa-b8f8-47b9-8b1b-3a7a90ef7c26'
self.agent = mock.Mock(chassis=self.chassis)
self.agent.ovn_local_cr_lrps = {
'cr-lrp1': {'provider_datapath': 'dp1',
'subnets_datapath': {'lrp1': 's_dp1'},
'ovn_lbs': 'ovn-lb1'}}
self.event = bgp_watcher.OVNLBMemberUpdateEvent(self.agent)
def test_match_fn(self):
row = utils.create_row(datapaths=['dp1', 'dp2'])
old = utils.create_row(datapaths=['dp1'])
self.assertTrue(self.event.match_fn(mock.Mock(), row, old))
def test_match_fn_no_dp_change(self):
row = utils.create_row(datapaths=['dp1'])
old = utils.create_row(datapaths=['dp1'])
self.assertFalse(self.event.match_fn(mock.Mock(), row, old))
def test_match_fn_removed_dp(self):
row = utils.create_row(datapaths=['dp1'])
old = utils.create_row(datapaths=[])
self.assertTrue(self.event.match_fn(mock.Mock(), row, old))
def test_match_fn_no_cr_lrp(self):
self.agent.ovn_local_cr_lrps = {}
row = utils.create_row(datapaths=['dp1'])
old = utils.create_row(datapaths=['dp1', 'dp2'])
self.assertFalse(self.event.match_fn(mock.Mock(), row, old))
def test_match_fn_attribute_error(self):
row = utils.create_row(mac=[])
self.assertFalse(self.event.match_fn(mock.Mock(), row, mock.Mock()))
def test_match_fn_delete(self):
event = self.event.ROW_DELETE
row = utils.create_row(mac=[])
self.assertTrue(self.event.match_fn(event, row, mock.Mock()))
def test_match_fn_dp_group(self):
dpg1 = utils.create_row(_uuid='fake_dp_group',
datapaths=['dp1', 'dp2'])
dpg2 = utils.create_row(_uuid='fake_dp_group',
datapaths=['dp1'])
row = utils.create_row(datapath_group=[dpg1])
old = utils.create_row(datapath_group=[dpg2])
self.assertTrue(self.event.match_fn(mock.Mock(), row, old))
def test_match_fn_dp_group_attribute_error(self):
dpg1 = utils.create_row(_uuid='fake_dp_group')
row = utils.create_row(datapath_group=[dpg1])
old = utils.create_row(datapath_group=[dpg1])
self.assertFalse(self.event.match_fn(mock.Mock(), row, old))
def test_match_fn_dp_group_no_dp_change(self):
dpg1 = utils.create_row(_uuid='fake_dp_group',
datapaths=['dp1', 'dp2'])
row = utils.create_row(datapath_group=[dpg1])
old = utils.create_row(datapath_group=[dpg1])
self.assertFalse(self.event.match_fn(mock.Mock(), row, old))
def test_match_fn_dp_group_no_old_dp(self):
dpg1 = utils.create_row(_uuid='fake_dp_group',
datapaths=['dp1', 'dp2'])
row = utils.create_row(datapath_group=[dpg1])
old = utils.create_row(datapath_group=[])
self.assertTrue(self.event.match_fn(mock.Mock(), row, old))
def test_run(self):
dpg1 = utils.create_row(_uuid='fake_dp_group',
datapaths=['dp1', 's_dp1'])
dpg2 = utils.create_row(_uuid='fake_dp_group',
datapaths=['dp1'])
row = utils.create_row(name='ovn-lb1',
datapath_group=[dpg1],
vips={'172.24.100.66:80': '10.0.0.5:8080'})
old = utils.create_row(name='ovn-lb1',
datapath_group=[dpg2],
vips={'172.24.100.66:80': '10.0.0.5:8080'})
self.event.run(mock.Mock(), row, old)
self.agent.expose_ovn_lb_on_provider.assert_called_once_with(
'ovn-lb1', '172.24.100.66', 'cr-lrp1')
self.agent.withdraw_ovn_lb_on_provider.assert_not_called()
def test_run_no_provider_dp(self):
dpg1 = utils.create_row(_uuid='fake_dp_group',
datapaths=['s_dp2'])
row = utils.create_row(name='ovn-lb1',
datapath_group=[dpg1])
self.event.run(mock.Mock(), row, row)
self.agent.expose_ovn_lb_on_provider.assert_not_called()
self.agent.withdraw_ovn_lb_on_provider.assert_not_called()
def test_run_removed_dp(self):
dpg1 = utils.create_row(_uuid='fake_dp_group',
datapaths=['dp1'])
dpg2 = utils.create_row(_uuid='fake_dp_group',
datapaths=['dp1', 's_dp1'])
row = utils.create_row(name='ovn-lb1',
datapath_group=[dpg1],
vips={})
old = utils.create_row(name='ovn-lb1',
datapath_group=[dpg2],
vips={'172.24.100.66:80': '10.0.0.5:8080'})
self.event.run(mock.Mock(), row, old)
self.agent.expose_ovn_lb_on_provider.assert_not_called()
self.agent.withdraw_ovn_lb_on_provider.assert_called_once_with(
'ovn-lb1', 'cr-lrp1')
def test_run_no_match_subnets_dp(self):
dpg1 = utils.create_row(_uuid='fake_dp_group',
datapaths=['dp1', 's_dp2'])
dpg2 = utils.create_row(_uuid='fake_dp_group',
datapaths=['dp1'])
row = utils.create_row(name='ovn-lb1',
datapath_group=[dpg1],
vips={'172.24.100.66:80': '10.0.0.5:8080'})
old = utils.create_row(name='ovn-lb1',
datapath_group=[dpg2],
vips={'172.24.100.66:80': '10.0.0.5:8080'})
self.event.run(mock.Mock(), row, old)
self.agent.expose_ovn_lb_on_provider.assert_not_called()
self.agent.withdraw_ovn_lb_on_provider.assert_not_called()
def test_run_no_member(self):
dpg1 = utils.create_row(_uuid='fake_dp_group',
datapaths=['dp1'])
row = utils.create_row(name='ovn-lb2',
datapath_group=[dpg1],
vips={})
old = utils.create_row(name='ovn-lb2',
datapath_group=[dpg1])
self.event.run(mock.Mock(), row, old)
self.agent.expose_ovn_lb_on_provider.assert_not_called()
self.agent.withdraw_ovn_lb_on_provider.assert_not_called()
def test_run_member_removal(self):
dpg1 = utils.create_row(_uuid='fake_dp_group',
datapaths=['dp1', 's_dp1'])
dpg2 = utils.create_row(_uuid='fake_dp_group',
datapaths=['dp1', 's_dp1', 's_dp2'])
row = utils.create_row(name='ovn-lb1',
datapath_group=[dpg1],
vips={'172.24.100.66:80': '10.0.0.5:8080'})
old = utils.create_row(name='ovn-lb1',
datapath_group=[dpg2],
vips={'172.24.100.66:80':
'10.0.0.5:8080,10.0.0.6:8080'})
self.event.run(mock.Mock(), row, old)
self.agent.expose_ovn_lb_on_provider.assert_not_called()
self.agent.withdraw_ovn_lb_on_provider.assert_not_called()
def test_run_delete(self):
event = self.event.ROW_DELETE
dpg1 = utils.create_row(_uuid='fake_dp_group',
datapaths=['dp1', 's_dp1'])
row = utils.create_row(name='ovn-lb1',
datapath_group=[dpg1])
old = utils.create_row()
self.event.run(event, row, old)
self.agent.expose_ovn_lb_on_provider.assert_not_called()
self.agent.withdraw_ovn_lb_on_provider.assert_called_once_with(
'ovn-lb1', 'cr-lrp1')
self.agent.expose_ovn_lb.assert_not_called()
self.agent.withdraw_ovn_lb.assert_not_called()
class TestChassisCreateEvent(test_base.TestCase):