From 5d760e41bbb581fbd868c05a2dcd55106ea728b2 Mon Sep 17 00:00:00 2001 From: Luis Tomas Bolivar Date: Fri, 17 Feb 2023 16:43:34 +0100 Subject: [PATCH] Ensure OVN LB on provider are only exposed in the right node We need to wait to expose the ovn lb with VIP on the provider network for them to have members (and therefore have an entry on the Load_Balancer table at the SB DB) so that it gets associated to one specific cr-lrp (the one that will reply to ARPs) This patch partly reverts/adapt what was done in [1] to fix the problem of exposing the VIPs in all the nodes with cr-lrp connected to the same provider network [1] https://review.opendev.org/c/x/ovn-bgp-agent/+/873073 Change-Id: Idc1022bc0593c21e272d138757391800f12415f1 --- ovn_bgp_agent/constants.py | 2 + .../drivers/openstack/ovn_bgp_driver.py | 60 +++++----- ovn_bgp_agent/drivers/openstack/utils/ovn.py | 46 ++++++-- .../openstack/watchers/base_watcher.py | 9 ++ .../drivers/openstack/watchers/bgp_watcher.py | 55 +++++++++ .../drivers/openstack/test_ovn_bgp_driver.py | 18 +-- .../unit/drivers/openstack/utils/test_ovn.py | 66 +++++++++-- .../openstack/watchers/test_bgp_watcher.py | 110 ++++++++++++++++++ 8 files changed, 306 insertions(+), 60 deletions(-) diff --git a/ovn_bgp_agent/constants.py b/ovn_bgp_agent/constants.py index 019b30e4..86841301 100644 --- a/ovn_bgp_agent/constants.py +++ b/ovn_bgp_agent/constants.py @@ -21,6 +21,8 @@ OVN_CHASSISREDIRECT_VIF_PORT_TYPE = "chassisredirect" OVN_LOCALNET_VIF_PORT_TYPE = "localnet" OVN_CIDRS_EXT_ID_KEY = 'neutron:cidrs' +OVN_PORT_NAME_EXT_ID_KEY = 'neutron:port_name' +LB_VIP_PORT_PREFIX = "ovn-lb-vip-" OVS_RULE_COOKIE = "999" OVS_VRF_RULE_COOKIE = "998" diff --git a/ovn_bgp_agent/drivers/openstack/ovn_bgp_driver.py b/ovn_bgp_agent/drivers/openstack/ovn_bgp_driver.py index c96cdece..1c82fa5a 100644 --- a/ovn_bgp_agent/drivers/openstack/ovn_bgp_driver.py +++ b/ovn_bgp_agent/drivers/openstack/ovn_bgp_driver.py @@ -37,7 +37,7 @@ LOG = logging.getLogger(__name__) # LOG.setLevel(logging.DEBUG) # logging.basicConfig(level=logging.DEBUG) -OVN_TABLES = ["Port_Binding", "Chassis", "Datapath_Binding"] +OVN_TABLES = ["Port_Binding", "Chassis", "Datapath_Binding", "Load_Balancer"] class OVNBGPDriver(driver_api.AgentDriverBase): @@ -134,13 +134,14 @@ class OVNBGPDriver(driver_api.AgentDriverBase): "PortBindingChassisDeletedEvent", "FIPSetEvent", "FIPUnsetEvent", - "OVNLBVIPPortEvent", + "OVNLBMemberCreateDeleteEvent", "ChassisCreateEvent"]) if self._expose_tenant_networks: events.update(["SubnetRouterAttachedEvent", "SubnetRouterDetachedEvent", "TenantPortCreatedEvent", - "TenantPortDeletedEvent"]) + "TenantPortDeletedEvent", + "OVNLBVIPPortEvent"]) return events @lockutils.synchronized('bgp') @@ -228,8 +229,9 @@ class OVNBGPDriver(driver_api.AgentDriverBase): # add missing routes/ips related to ovn-octavia loadbalancers # on the provider networks - ovn_lb_vips = self.sb_idl.get_ovn_lb_vips_on_provider_datapath( - cr_lrp_info['provider_datapath']) + cr_lrp_subnets_dp = set(cr_lrp_info['subnets_datapath'].values()) + ovn_lb_vips = self.sb_idl.get_ovn_lb_vips_on_cr_lrp( + cr_lrp_info['provider_datapath'], cr_lrp_subnets_dp) for ovn_lb_port, ovn_lb_ip in ovn_lb_vips.items(): self._expose_ovn_lb_on_provider(ovn_lb_ip, ovn_lb_port, @@ -429,34 +431,30 @@ class OVNBGPDriver(driver_api.AgentDriverBase): def _process_ovn_lb(self, ip, row, action): try: - provider_network = self.sb_idl.is_provider_network(row.datapath) + if (not self._expose_tenant_networks or + self.sb_idl.is_provider_network(row.datapath)): + return except agent_exc.DatapathNotFound: # There is no need to expose anything related to a removed # datapath - LOG.debug("LoadBalancer with VIP %s not being exposed as its " - "associated datapath %s was removed", ip, row.datapath) + LOG.debug("LoadBalancer with VIP %s not being exposed/withdraw as" + " its associated datapath %s was removed", ip, + row.datapath) return - if not provider_network: - 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) + return self._expose_remote_ip([ip], row) if action == constants.WITHDRAW: - self._withdraw_ovn_lb_on_provider(vip_port, local_lb_cr_lrp) + return self._withdraw_remote_ip([ip], row) + # if unknown action return + return + + @lockutils.synchronized('bgp') + def expose_ovn_lb_on_provider(self, ip, vip_port, cr_lrp_port): + self._expose_ovn_lb_on_provider(ip, vip_port, cr_lrp_port) + + @lockutils.synchronized('bgp') + def withdraw_ovn_lb_on_provider(self, vip_port, cr_lrp_port): + self._withdraw_ovn_lb_on_provider(vip_port, cr_lrp_port) def _expose_ovn_lb_on_provider(self, ip, ovn_lb_vip_port, cr_lrp, exposed_ips=None, ovn_ip_rules=None): @@ -829,8 +827,12 @@ class OVNBGPDriver(driver_api.AgentDriverBase): for lrp in lrp_ports: self._process_lrp_port(lrp, cr_lrp_port) - ovn_lb_vips = self.sb_idl.get_ovn_lb_vips_on_provider_datapath( - provider_datapath) + cr_lrp_provider_dp = self.ovn_local_cr_lrps[cr_lrp_port][ + 'provider_datapath'] + cr_lrp_subnets_dp = set(self.ovn_local_cr_lrps[cr_lrp_port][ + 'subnets_datapath'].values()) + ovn_lb_vips = self.sb_idl.get_ovn_lb_vips_on_cr_lrp( + cr_lrp_provider_dp, cr_lrp_subnets_dp) for ovn_lb_port, ovn_lb_ip in ovn_lb_vips.items(): self._expose_ovn_lb_on_provider(ovn_lb_ip, ovn_lb_port, diff --git a/ovn_bgp_agent/drivers/openstack/utils/ovn.py b/ovn_bgp_agent/drivers/openstack/utils/ovn.py index d0a7c38d..6e8fc2b1 100644 --- a/ovn_bgp_agent/drivers/openstack/utils/ovn.py +++ b/ovn_bgp_agent/drivers/openstack/utils/ovn.py @@ -312,11 +312,16 @@ 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_vips_on_provider_datapath(self, datapath): + def get_ovn_lb(self, name): + cmd = self.db_find_rows('Load_Balancer', ('name', '=', name)) + lb_info = cmd.execute(check_error=True) + return lb_info[0] if lb_info else [] + + def get_ovn_lb_vips_on_cr_lrp(self, provider_dp, subnets_dp): # 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), + ('datapath', '=', provider_dp), ('type', '=', constants.OVN_VM_VIF_PORT_TYPE), ('chassis', '=', []), ('mac', '=', []), @@ -325,10 +330,37 @@ class OvsdbSbOvnIdl(sb_impl_idl.OvnSbApiIdlImpl, Backend): 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: + port_name = row.external_ids.get( + constants.OVN_PORT_NAME_EXT_ID_KEY) + if (not port_name or + len(port_name) <= len(constants.LB_VIP_PORT_PREFIX)): continue - ovn_lb_ip = ext_n_cidr.split(" ")[0].split("/")[0] - lbs[row.logical_port] = ovn_lb_ip - + lb_name = port_name[len(constants.LB_VIP_PORT_PREFIX):] + lb = self.get_ovn_lb(lb_name) + if not lb: + continue + if hasattr(lb, 'datapath_group'): + lb_dp = lb.datapath_group[0].datapaths + else: + lb_dp = lb.datapaths + if set(lb_dp).intersection(subnets_dp): + ip_info = row.external_ids.get(constants.OVN_CIDRS_EXT_ID_KEY) + if not ip_info: + continue + lb_ip = ip_info.split(" ")[0].split("/")[0] + lbs[row.logical_port] = lb_ip return lbs + + def get_ovn_vip_port(self, name): + # ovn-sbctl find port_binding type=\"\" chassis=[] mac=[] up=false + cmd = self.db_find_rows('Port_Binding', + ('type', '=', constants.OVN_VM_VIF_PORT_TYPE), + ('chassis', '=', []), + ('mac', '=', []), + ('up', '=', False)) + + for row in cmd.execute(check_error=True): + port_name = row.external_ids.get( + constants.OVN_PORT_NAME_EXT_ID_KEY) + if port_name[len(constants.LB_VIP_PORT_PREFIX):] == name: + return row diff --git a/ovn_bgp_agent/drivers/openstack/watchers/base_watcher.py b/ovn_bgp_agent/drivers/openstack/watchers/base_watcher.py index 724fdacf..11ffec81 100644 --- a/ovn_bgp_agent/drivers/openstack/watchers/base_watcher.py +++ b/ovn_bgp_agent/drivers/openstack/watchers/base_watcher.py @@ -25,3 +25,12 @@ 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__ diff --git a/ovn_bgp_agent/drivers/openstack/watchers/bgp_watcher.py b/ovn_bgp_agent/drivers/openstack/watchers/bgp_watcher.py index 7e19b64a..a32b078b 100644 --- a/ovn_bgp_agent/drivers/openstack/watchers/bgp_watcher.py +++ b/ovn_bgp_agent/drivers/openstack/watchers/bgp_watcher.py @@ -350,6 +350,61 @@ class OVNLBVIPPortEvent(base_watcher.PortBindingChassisEvent): self.agent.expose_ovn_lb(ovn_lb_ip, row) +class OVNLBMemberCreateDeleteEvent(base_watcher.OVNLBMemberEvent): + def __init__(self, bgp_agent): + events = (self.ROW_CREATE, self.ROW_DELETE,) + super(OVNLBMemberCreateDeleteEvent, self).__init__( + bgp_agent, events) + + def match_fn(self, event, row, old): + # 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 = [] + if hasattr(row, 'datapath_group'): + if row.datapath_group: + dp_datapaths = row.datapath_group[0].datapaths + if dp_datapaths: + row_dp = dp_datapaths + + vip_port = self.agent.sb_idl.get_ovn_vip_port(row.name) + if not vip_port: + return + associated_cr_lrp_port = None + for cr_lrp_port, cr_lrp_info in self.agent.ovn_local_cr_lrps.items(): + if vip_port.datapath != cr_lrp_info.get('provider_datapath'): + continue + if set(row_dp).intersection(set( + cr_lrp_info.get('subnets_datapath').values())): + associated_cr_lrp_port = cr_lrp_port + break + else: + 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( + vip_port.logical_port, associated_cr_lrp_port) + if event == self.ROW_CREATE: + vip_ip = vip_port.external_ids.get( + constants.OVN_CIDRS_EXT_ID_KEY) + if not vip_ip: + return + vip_ip = vip_ip.split(" ")[0].split("/")[0] + return self.agent.expose_ovn_lb_on_provider( + vip_ip, vip_port.logical_port, associated_cr_lrp_port) + + class ChassisCreateEventBase(row_event.RowEvent): table = None diff --git a/ovn_bgp_agent/tests/unit/drivers/openstack/test_ovn_bgp_driver.py b/ovn_bgp_agent/tests/unit/drivers/openstack/test_ovn_bgp_driver.py index 47773603..edfeb723 100644 --- a/ovn_bgp_agent/tests/unit/drivers/openstack/test_ovn_bgp_driver.py +++ b/ovn_bgp_agent/tests/unit/drivers/openstack/test_ovn_bgp_driver.py @@ -835,10 +835,6 @@ class TestOVNBGPDriver(test_base.TestCase): 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' @@ -853,12 +849,7 @@ class TestOVNBGPDriver(test_base.TestCase): 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() @@ -866,12 +857,7 @@ class TestOVNBGPDriver(test_base.TestCase): 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) @@ -1114,7 +1100,7 @@ class TestOVNBGPDriver(test_base.TestCase): ovn_lb_vip = '172.24.4.5' ovn_lb_vips = {'fake-vip-port': ovn_lb_vip} - self.sb_idl.get_ovn_lb_vips_on_provider_datapath.return_value = ( + self.sb_idl.get_ovn_lb_vips_on_cr_lrp.return_value = ( ovn_lb_vips) mock_expose_ovn_lb = mock.patch.object( self.bgp_driver, '_expose_ovn_lb_on_provider').start() @@ -1556,7 +1542,7 @@ class TestOVNBGPDriver(test_base.TestCase): dp_port0 = mock.Mock() self.sb_idl.get_lrp_ports_for_router.return_value = [dp_port0] ovn_lb_vips = {'fake-vip-port': 'fake-vip-ip'} - self.sb_idl.get_ovn_lb_vips_on_provider_datapath.return_value = ( + self.sb_idl.get_ovn_lb_vips_on_cr_lrp.return_value = ( ovn_lb_vips) self.bgp_driver._expose_cr_lrp_port( diff --git a/ovn_bgp_agent/tests/unit/drivers/openstack/utils/test_ovn.py b/ovn_bgp_agent/tests/unit/drivers/openstack/utils/test_ovn.py index b569b0fe..d06c153b 100644 --- a/ovn_bgp_agent/tests/unit/drivers/openstack/utils/test_ovn.py +++ b/ovn_bgp_agent/tests/unit/drivers/openstack/utils/test_ovn.py @@ -488,19 +488,69 @@ class TestOvsdbSbOvnIdl(test_base.TestCase): self.assertEqual([port1], ret) - def test_get_ovn_lb_vips_on_provider_datapath(self): + def test_get_ovn_lb(self): + fake_lb_info = 'fake-lbinfo' + lb = 'fake-lb' + self.sb_idl.db_find_rows.return_value.execute.return_value = [ + fake_lb_info] + ret = self.sb_idl.get_ovn_lb(lb) + + self.assertEqual(fake_lb_info, ret) + self.sb_idl.db_find_rows.assert_called_once_with( + 'Load_Balancer', ('name', '=', lb)) + + def test_get_ovn_lb_empty(self): + lb = 'fake-port' + self.sb_idl.db_find_rows.return_value.execute.return_value = [] + ret = self.sb_idl.get_ovn_lb(lb) + + self.assertEqual([], ret) + self.sb_idl.db_find_rows.assert_called_once_with( + 'Load_Balancer', ('name', '=', lb)) + + def test_get_ovn_lb_vips_on_cr_lrp(self): + lb1_name = 'ovn-lb-vip-fake-lb1' + lb2_name = 'ovn-lb-vip-fake-lb2' + provider_dp = 'fake-provider-dp' + subnets_dp = ['fake-subnet-dp'] + dp1 = fakes.create_object({'datapaths': ['fake-subnet-dp']}) + lb1 = fakes.create_object({'datapath_group': [dp1]}) 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': {}}) + 'external_ids': {constants.OVN_CIDRS_EXT_ID_KEY: '10.0.0.15/24', + constants.OVN_PORT_NAME_EXT_ID_KEY: lb1_name}}) + port1 = fakes.create_object({ + 'logical_port': 'fake-port-1', + 'external_ids': {constants.OVN_CIDRS_EXT_ID_KEY: '10.0.0.16/24'}}) + port2 = fakes.create_object({ + 'logical_port': 'fake-port-0', + 'external_ids': {constants.OVN_CIDRS_EXT_ID_KEY: '10.0.0.17/24', + constants.OVN_PORT_NAME_EXT_ID_KEY: lb2_name}}) self.sb_idl.db_find_rows.return_value.execute.return_value = [ - port0, port1] + port0, port1, port2] - 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) + with mock.patch.object(self.sb_idl, 'get_ovn_lb') as mock_p: + mock_p.side_effect = (lb1, []) + + ret = self.sb_idl.get_ovn_lb_vips_on_cr_lrp(provider_dp, + subnets_dp) + expected_return = {'fake-port-0': '10.0.0.15'} + self.assertEqual(expected_return, ret) + + def test_get_ovn_vip_port(self): + lb_name = 'ovn-lb-vip-fake-lb' + lb1 = fakes.create_object( + {'external_ids': { + constants.OVN_PORT_NAME_EXT_ID_KEY: 'different-name'}}) + lb2 = fakes.create_object( + {'external_ids': {constants.OVN_PORT_NAME_EXT_ID_KEY: lb_name}}) + + self.sb_idl.db_find_rows.return_value.execute.return_value = [ + lb1, lb2] + ret = self.sb_idl.get_ovn_vip_port('fake-lb') + + self.assertEqual(lb2, ret) class TestOvnSbIdl(test_base.TestCase): diff --git a/ovn_bgp_agent/tests/unit/drivers/openstack/watchers/test_bgp_watcher.py b/ovn_bgp_agent/tests/unit/drivers/openstack/watchers/test_bgp_watcher.py index a929eca7..a5916ba8 100644 --- a/ovn_bgp_agent/tests/unit/drivers/openstack/watchers/test_bgp_watcher.py +++ b/ovn_bgp_agent/tests/unit/drivers/openstack/watchers/test_bgp_watcher.py @@ -810,6 +810,116 @@ class OVNLBVIPPortEvent(test_base.TestCase): self.agent.withdraw_ovn_lb.assert_not_called() +class TestOVNLBMemberCreateDeleteEvent(test_base.TestCase): + + def setUp(self): + super(TestOVNLBMemberCreateDeleteEvent, 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.OVNLBMemberCreateDeleteEvent(self.agent) + + def test_match_fn(self): + self.assertTrue(self.event.match_fn(mock.Mock(), mock.Mock(), + mock.Mock())) + + def test_match_fn_no_cr_lrp(self): + self.agent.ovn_local_cr_lrps = {} + self.assertFalse(self.event.match_fn(mock.Mock(), mock.Mock(), + mock.Mock())) + + def test_run(self): + dpg1 = utils.create_row(_uuid='fake_dp_group', + datapaths=['s_dp1']) + row = utils.create_row(name='ovn-lb1', + datapath_group=[dpg1], + vips={'172.24.100.66:80': '10.0.0.5:8080'}) + vip_port = utils.create_row( + datapath='dp1', + logical_port='ovn-lb-port-1', + external_ids={constants.OVN_CIDRS_EXT_ID_KEY: '172.24.100.66/26'}) + self.agent.sb_idl.get_ovn_vip_port.return_value = vip_port + self.event.run(self.event.ROW_CREATE, row, mock.Mock()) + self.agent.expose_ovn_lb_on_provider.assert_called_once_with( + '172.24.100.66', 'ovn-lb-port-1', 'cr-lrp1') + self.agent.withdraw_ovn_lb_on_provider.assert_not_called() + + def test_run_no_vip_port(self): + dpg1 = utils.create_row(_uuid='fake_dp_group', + datapaths=['s_dp1']) + row = utils.create_row(name='ovn-lb1', + datapath_group=[dpg1], + vips={'172.24.100.66:80': '10.0.0.5:8080'}) + self.agent.sb_idl.get_ovn_vip_port.return_value = [] + self.event.run(self.event.ROW_CREATE, row, mock.Mock()) + self.agent.expose_ovn_lb_on_provider.assert_not_called() + self.agent.withdraw_ovn_lb_on_provider.assert_not_called() + + def test_run_different_provider(self): + dpg1 = utils.create_row(_uuid='fake_dp_group', + datapaths=['s_dp1']) + row = utils.create_row(name='ovn-lb1', + datapath_group=[dpg1], + vips={'172.24.100.66:80': '10.0.0.5:8080'}) + vip_port = utils.create_row( + datapath='dp2', + logical_port='ovn-lb-port-1', + external_ids={constants.OVN_CIDRS_EXT_ID_KEY: '172.24.100.66/26'}) + self.agent.sb_idl.get_ovn_vip_port.return_value = vip_port + self.event.run(self.event.ROW_CREATE, row, mock.Mock()) + self.agent.expose_ovn_lb_on_provider.assert_not_called() + self.agent.withdraw_ovn_lb_on_provider.assert_not_called() + + def test_run_no_cr_lrp_match(self): + dpg1 = utils.create_row(_uuid='fake_dp_group', + datapaths=['s_dp2']) + row = utils.create_row(name='ovn-lb1', + datapath_group=[dpg1], + vips={'172.24.100.66:80': '10.0.0.5:8080'}) + vip_port = utils.create_row( + datapath='dp1', + logical_port='ovn-lb-port-1', + external_ids={constants.OVN_CIDRS_EXT_ID_KEY: '172.24.100.66/26'}) + self.agent.sb_idl.get_ovn_vip_port.return_value = vip_port + self.event.run(self.event.ROW_CREATE, row, mock.Mock()) + self.agent.expose_ovn_lb_on_provider.assert_not_called() + self.agent.withdraw_ovn_lb_on_provider.assert_not_called() + + def test_run_no_vip(self): + dpg1 = utils.create_row(_uuid='fake_dp_group', + datapaths=['s_dp1']) + row = utils.create_row(name='ovn-lb1', + datapath_group=[dpg1], + vips={'172.24.100.66:80': '10.0.0.5:8080'}) + vip_port = utils.create_row( + datapath='dp1', + logical_port='port-1', + external_ids={}) + self.agent.sb_idl.get_ovn_vip_port.return_value = vip_port + self.event.run(self.event.ROW_CREATE, row, mock.Mock()) + 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): + dpg1 = utils.create_row(_uuid='fake_dp_group', + datapaths=['s_dp1']) + row = utils.create_row(name='ovn-lb1', + datapath_group=[dpg1], + vips={'172.24.100.66:80': '10.0.0.5:8080'}) + vip_port = utils.create_row( + datapath='dp1', + logical_port='ovn-lb-port-1', + external_ids={constants.OVN_CIDRS_EXT_ID_KEY: '172.24.100.66/26'}) + self.agent.sb_idl.get_ovn_vip_port.return_value = vip_port + self.event.run(self.event.ROW_DELETE, row, mock.Mock()) + self.agent.withdraw_ovn_lb_on_provider.assert_called_once_with( + 'ovn-lb-port-1', 'cr-lrp1') + self.agent.expose_ovn_lb_on_provider.assert_not_called() + + class TestChassisCreateEvent(test_base.TestCase): _event = bgp_watcher.ChassisCreateEvent