Add sync floating IP support

This patch add sync for FIP
Also correct hm_sync for FIP

Closes-Bug: #2045415

Co-authored-by: Fernando Royo <froyo@redhat.com>
Co-authored-by: Rico Lin <ricolin@ricolky.com>

Change-Id: I6dd6812e78b6ff42a27c6d1bf004250810e8b9ab
This commit is contained in:
ricolin
2024-09-12 14:50:41 +08:00
committed by Fernando Royo
parent 57af2e4089
commit c16e20e87c
5 changed files with 549 additions and 61 deletions

View File

@@ -88,7 +88,9 @@ REQ_TYPE_EXIT = 'exit'
# Request information constants
REQ_INFO_ACTION_ASSOCIATE = 'associate'
REQ_INFO_ACTION_SYNC = 'sync'
REQ_INFO_ACTION_DISASSOCIATE = 'disassociate'
REQ_INFO_MEMBER_ADDED = 'member_added'
REQ_INFO_MEMBER_DELETED = 'member_deleted'

View File

@@ -798,6 +798,59 @@ class OvnProviderDriver(driver_base.ProviderDriver):
ovn_lb)
self._ovn_helper._update_status_to_octavia(status)
def _fip_sync(self, loadbalancer):
LOG.info("Starting sync floating IP for loadbalancer "
f"{loadbalancer.loadbalancer_id}")
if not loadbalancer.vip_port_id or not loadbalancer.vip_network_id:
LOG.debug("VIP Port or Network not set for loadbalancer "
f"{loadbalancer.loadbalancer_id}, skip FIP sync.")
return
# Try to get FIP from neutron
fips = self._ovn_helper.get_fip_from_vip(loadbalancer)
# get FIP from LSP
vip_lsp = self._ovn_helper.get_lsp(
port_id=loadbalancer.vip_port_id,
network_id=loadbalancer.vip_network_id)
lsp_fip = vip_lsp.external_ids.get(
ovn_const.OVN_PORT_FIP_EXT_ID_KEY) if vip_lsp else None
if fips:
neutron_fip = fips[0].floating_ip_address
if not vip_lsp:
LOG.warn(
"Logic Switch Port not found for port "
f"{loadbalancer.vip_port_id}. "
"Skip sync FIP for loadbalancer "
f"{loadbalancer.loadbalancer_id}. Please "
"run command `neutron-ovn-db-sync-util` "
"first to sync OVN DB with Neutron DB.")
return
if lsp_fip != neutron_fip:
LOG.warn(
"Floating IP not consistent between Logic Switch "
f"Port and Neutron. Found FIP {lsp_fip} "
f"in LSP {vip_lsp.name}, but we have {neutron_fip} from "
"Neutron. Skip sync FIP for "
f"loadbalancer {loadbalancer.loadbalancer_id}. "
"Please run command `neutron-ovn-db-sync-util` "
"first to sync OVN DB with Neutron DB.")
return
self._ovn_helper.vip_port_update_handler(
vip_lp=vip_lsp, fip=lsp_fip,
action=ovn_const.REQ_INFO_ACTION_SYNC)
else:
LOG.warn("Floating IP not found for loadbalancer "
f"{loadbalancer.loadbalancer_id}")
if lsp_fip:
LOG.warn(
"Floating IP not consistent between Logic Switch "
f"Port and Neutron. Found FIP {lsp_fip} configured "
f"in LSP {vip_lsp.name}, but no FIP configured from "
"Neutron. Please run command `neutron-ovn-db-sync-util` "
"first to sync OVN DB with Neutron DB.")
def do_sync(self, **lb_filters):
LOG.info(f"Starting sync OVN DB with Loadbalancer filter {lb_filters}")
octavia_client = clients.get_octavia_client()
@@ -841,3 +894,4 @@ class OvnProviderDriver(driver_base.ProviderDriver):
provider_pools if provider_pools else o_datamodels.Unset
)
self._ensure_loadbalancer(provider_lb)
self._fip_sync(provider_lb)

View File

@@ -355,8 +355,11 @@ class OvnProviderHelper():
if port:
request_info['vip_related'] = [
ip['ip_address'] for ip in port.fixed_ips]
self.add_request({'type': ovn_const.REQ_TYPE_HANDLE_VIP_FIP,
'info': request_info})
if action != ovn_const.REQ_INFO_ACTION_SYNC:
self.add_request({'type': ovn_const.REQ_TYPE_HANDLE_VIP_FIP,
'info': request_info})
else:
self.handle_vip_fip(request_info)
def _find_lb_in_ls(self, network):
"""Find LB associated to a Network using Network information
@@ -2966,8 +2969,13 @@ class OvnProviderHelper():
additional_vip_fip = fip_info.get('additional_vip_fip', False)
external_ids = copy.deepcopy(ovn_lb.external_ids)
commands = []
need_ext_set = True
need_hc_set = True
if fip_info['action'] == ovn_const.REQ_INFO_ACTION_ASSOCIATE:
if fip_info['action'] in (
ovn_const.REQ_INFO_ACTION_ASSOCIATE,
ovn_const.REQ_INFO_ACTION_SYNC
):
if additional_vip_fip:
existing_addi_vip_fip = external_ids.get(
ovn_const.LB_EXT_IDS_ADDIT_VIP_FIP_KEY, [])
@@ -2984,31 +2992,51 @@ class OvnProviderHelper():
fip_info['vip_fip'])
vip_fip_info = {
ovn_const.LB_EXT_IDS_VIP_FIP_KEY: fip_info['vip_fip']}
commands.append(
self.ovn_nbdb_api.db_set('Load_Balancer', ovn_lb.uuid,
('external_ids', vip_fip_info)))
for lb_hc in ovn_lb.health_check:
if self._get_vip_lbhc(lb_hc) in fip_info['vip_related']:
vip = fip_info['vip_fip']
lb_hc_external_ids = copy.deepcopy(lb_hc.external_ids)
lb_hc_external_ids[ovn_const.LB_EXT_IDS_HM_VIP] = vip
if self._check_lbhc_vip_format(lb_hc.vip):
port = lb_hc.vip.rsplit(':')[-1]
vip += ':' + port
else:
vip = ''
kwargs = {
'vip': vip,
'options': lb_hc.options,
'external_ids': lb_hc_external_ids}
with self.ovn_nbdb_api.transaction(
check_error=True) as txn:
fip_lbhc = txn.add(self.ovn_nbdb_api.db_create(
'Load_Balancer_Health_Check', **kwargs))
txn.add(self.ovn_nbdb_api.db_add(
'Load_Balancer', ovn_lb.uuid,
'health_check', fip_lbhc))
if fip_info['action'] == ovn_const.REQ_INFO_ACTION_SYNC:
# Don't need to trigger OVN DB set if external_ids not changed
need_ext_set = not all(
ovn_lb.external_ids.get(k) ==
v for k, v in vip_fip_info.items()
)
# For sync scenario, check if FIP VIP already in health_check
for lb_hc in ovn_lb.health_check:
# All lbhc in health_check are already checked
# at this stage of sync workflow in hm_purge.
# So we should be able to just check health_check.
if self._get_vip_lbhc(lb_hc) == fip_info['vip_fip']:
need_hc_set = False
break
if need_ext_set:
commands.append(
self.ovn_nbdb_api.db_set(
'Load_Balancer', ovn_lb.uuid, (
'external_ids', vip_fip_info)))
if need_hc_set:
for lb_hc in ovn_lb.health_check:
if self._get_vip_lbhc(lb_hc) in fip_info['vip_related']:
vip = fip_info['vip_fip']
lb_hc_external_ids = copy.deepcopy(lb_hc.external_ids)
lb_hc_external_ids[ovn_const.LB_EXT_IDS_HM_VIP] = vip
if self._check_lbhc_vip_format(lb_hc.vip):
port = lb_hc.vip.rsplit(':')[-1]
vip += ':' + port
else:
vip = ''
kwargs = {
'vip': vip,
'options': lb_hc.options,
'external_ids': lb_hc_external_ids}
with self.ovn_nbdb_api.transaction(
check_error=True) as txn:
fip_lbhc = txn.add(self.ovn_nbdb_api.db_create(
'Load_Balancer_Health_Check', **kwargs))
txn.add(self.ovn_nbdb_api.db_add(
'Load_Balancer', ovn_lb.uuid,
'health_check', fip_lbhc))
else:
# For disassociate case
existing_addi_vip_fip_need_updated = False
existing_addi_vip_fip = external_ids.get(
ovn_const.LB_EXT_IDS_ADDIT_VIP_FIP_KEY, [])
@@ -3052,8 +3080,14 @@ class OvnProviderHelper():
commands.append(self.ovn_nbdb_api.db_destroy(
'Load_Balancer_Health_Check', lb_hc.uuid))
break
commands.extend(self._refresh_lb_vips(ovn_lb, external_ids))
commands.extend(
self._refresh_lb_vips(
ovn_lb,
external_ids,
is_sync=(
fip_info['action'] == ovn_const.REQ_INFO_ACTION_SYNC)
)
)
self._execute_commands(commands)
def handle_member_dvr(self, info):
@@ -3140,6 +3174,18 @@ class OvnProviderHelper():
{'member': info['id'],
'fip': fip.external_ip})
def get_lsp(self, port_id, network_id):
ls_name = utils.ovn_name(network_id)
try:
ls = self.ovn_nbdb_api.lookup('Logical_Switch', ls_name)
except idlutils.RowNotFound:
LOG.warn(f"Logical Switch {ls_name} not found.")
return
for port in ls.ports:
if port_id in port.name:
# We found particular port
return port
def _get_member_lsp(self, member_ip, member_subnet_id):
neutron_client = clients.get_neutron_client()
try:
@@ -3160,6 +3206,15 @@ class OvnProviderHelper():
# We found particular port
return port
def get_fip_from_vip(self, lb):
neutron_client = clients.get_neutron_client()
try:
return list(neutron_client.ips(port_id=lb.vip_port_id))
except openstack.exceptions.HttpException as e:
LOG.warn("Error on fetch fip for "
f"{lb.loadbalancer_id} "
f"Error: {str(e)}")
def _add_lbhc(self, ovn_lb, pool_key, info):
hm_id = info[constants.ID]
status = {constants.ID: hm_id,
@@ -3586,7 +3641,7 @@ class OvnProviderHelper():
raise idlutils.RowNotFound(table='Load_Balancer_Health_Check',
col='external_ids', match=hm_id)
def _find_ovn_lb_from_hm_id(self, hm_id):
def _find_ovn_lb_from_hm_id(self, hm_id, lbhc_vip=None):
lbs = self.ovn_nbdb_api.db_list_rows(
'Load_Balancer').execute(check_error=True)
ovn_lb = None
@@ -3597,7 +3652,14 @@ class OvnProviderHelper():
break
try:
lbhcs = self._lookup_lbhcs_by_hm_id(hm_id)
lbhcs_by_hm_id = self._lookup_lbhcs_by_hm_id(hm_id)
if lbhc_vip:
lbhcs = []
for lbhc in lbhcs_by_hm_id:
if lbhc.vip == lbhc_vip:
lbhcs.append(lbhc)
else:
lbhcs = lbhcs_by_hm_id
except idlutils.RowNotFound:
LOG.debug("Loadbalancer health check %s not found!", hm_id)
return [], ovn_lb
@@ -4112,17 +4174,17 @@ class OvnProviderHelper():
continue
fetch_hc_ids.extend([str(lbhc.uuid) for lbhc in lbhcs])
for hc_id in ovn_lb.health_check:
if str(hc_id.uuid) not in fetch_hc_ids:
for lbhc in ovn_lb.health_check:
if str(lbhc.uuid) not in fetch_hc_ids:
commands = []
commands.append(
self.ovn_nbdb_api.db_remove(
'Load_Balancer', ovn_lb.uuid,
'health_check', hc_id.uuid))
'health_check', lbhc.uuid))
commands.append(
self.ovn_nbdb_api.db_destroy(
'Load_Balancer_Health_Check',
hc_id.uuid))
lbhc.uuid))
try:
self._execute_commands(commands)
except idlutils.RowNotFound:

View File

@@ -18,6 +18,7 @@ from octavia_lib.api.drivers import data_models
from octavia_lib.api.drivers import driver_lib as o_driver_lib
from octavia_lib.api.drivers import exceptions
from octavia_lib.common import constants
import openstack
from oslo_utils import uuidutils
from ovsdbapp.backend.ovs_idl import idlutils
@@ -27,6 +28,7 @@ from ovn_octavia_provider.common import exceptions as ovn_exc
from ovn_octavia_provider import driver as ovn_driver
from ovn_octavia_provider import helper as ovn_helper
from ovn_octavia_provider.tests.unit import base as ovn_base
from ovn_octavia_provider.tests.unit import fakes
class TestOvnProviderDriver(ovn_base.TestOvnOctaviaBase):
@@ -46,8 +48,9 @@ class TestOvnProviderDriver(ovn_base.TestOvnOctaviaBase):
self.member_port, self.member_subnet_id))
self.ovn_lb = mock.MagicMock()
self.ovn_lb.name = 'foo_ovn_lb'
self.fake_vip = '10.22.33.4'
self.ovn_lb.external_ids = {
ovn_const.LB_EXT_IDS_VIP_KEY: '10.22.33.4',
ovn_const.LB_EXT_IDS_VIP_KEY: self.fake_vip,
'pool_%s' % self.pool_id: self.member_line,
'listener_%s' % self.listener_id: '80:pool_%s' % self.pool_id}
self.ovn_lb_addi_vips = mock.MagicMock()
@@ -282,6 +285,36 @@ class TestOvnProviderDriver(ovn_base.TestOvnOctaviaBase):
ovn_helper.OvnProviderHelper,
'_check_ip_in_subnet',
return_value=True).start()
self.fake_fip = '1.2.3.4'
self.lsp = fakes.FakeOvsdbRow.create_one_ovsdb_row(
attrs={
'external_ids': {
ovn_const.OVN_PORT_FIP_EXT_ID_KEY: self.fake_fip,
ovn_const.OVN_PORT_NAME_EXT_ID_KEY:
'ovn-lb-vip-d571f37e-5708-48a1-8ba8-fc5d9ce36eac',
},
'type': 'localnet',
'options': {},
})
self.lsp_addi = fakes.FakeOvsdbRow.create_one_ovsdb_row(
attrs={
'external_ids': {
ovn_const.OVN_PORT_FIP_EXT_ID_KEY: self.fake_fip,
ovn_const.OVN_PORT_NAME_EXT_ID_KEY:
'ovn-lb-vip-additional--\
19d4c20e-05ff-4534-9d5c-214f2979db40',
},
'type': 'localnet',
'options': {},
})
self.mock_get_lsp = mock.patch.object(
ovn_helper.OvnProviderHelper,
'get_lsp',
return_value=self.lsp).start()
self.mock_find_ovn_lbs_with_retry = mock.patch.object(
ovn_helper.OvnProviderHelper,
'_find_ovn_lbs_with_retry',
return_value=[self.ovn_lb]).start()
def test_check_for_allowed_cidrs_exception(self):
self.assertRaises(exceptions.UnsupportedOptionError,
@@ -798,6 +831,167 @@ class TestOvnProviderDriver(ovn_base.TestOvnOctaviaBase):
self.driver.loadbalancer_create(self.ref_lb0)
self.mock_add_request.assert_has_calls(calls)
@mock.patch.object(ovn_helper.OvnProviderHelper, 'vip_port_update_handler')
@mock.patch('ovn_octavia_provider.common.clients.get_neutron_client')
def test_fip_sync_basic(self, net_cli, m_vpu):
net_cli.return_value.ips.return_value = [
mock.Mock(floating_ip_address=self.fake_fip)]
self.ref_lb_fully_sync_populated.vip_port_id = 'foo'
self.driver._fip_sync(self.ref_lb_fully_sync_populated)
m_vpu.assert_called_once_with(
vip_lp=self.lsp, fip=self.fake_fip,
action=ovn_const.REQ_INFO_ACTION_SYNC)
@mock.patch('ovn_octavia_provider.common.clients.get_neutron_client')
def test_fip_sync(self, net_cli):
net_cli.return_value.ips.return_value = [
mock.Mock(floating_ip_address=self.fake_fip)]
fake_port = fakes.FakePort.create_one_port()
fake_port['fixed_ips'][0]['ip_address'] = self.fake_vip
net_cli.return_value.get_port.return_value = fake_port
self.ref_lb_fully_sync_populated.vip_port_id = 'foo'
with mock.patch.object(ovn_helper.OvnProviderHelper,
'handle_vip_fip') as mock_handle_vip_fip:
info = {
'ovn_lb': self.ovn_lb,
'vip_fip': self.fake_fip,
'vip_related': [self.fake_vip],
'additional_vip_fip': False,
'action': ovn_const.REQ_INFO_ACTION_SYNC
}
self.driver._fip_sync(self.ref_lb_fully_sync_populated)
mock_handle_vip_fip.assert_called_once_with(info)
@mock.patch('ovn_octavia_provider.common.clients.get_neutron_client')
def test_fip_sync_ovn_lb_not_found(self, net_cli):
self.mock_find_ovn_lbs_with_retry.side_effect = [
idlutils.RowNotFound]
net_cli.return_value.ips.return_value = [
mock.Mock(floating_ip_address=self.fake_fip)]
self.ref_lb_fully_sync_populated.vip_port_id = 'foo'
with mock.patch.object(ovn_helper, 'LOG') as m_l:
self.driver._fip_sync(self.ref_lb_fully_sync_populated)
m_l.debug.assert_called()
self.mock_add_request.assert_not_called()
@mock.patch('ovn_octavia_provider.common.clients.get_neutron_client')
def test_fip_sync_addi(self, net_cli):
net_cli.return_value.ips.return_value = [
mock.Mock(floating_ip_address=self.fake_fip)]
fake_port = fakes.FakePort.create_one_port()
self.mock_get_lsp.return_value = self.lsp_addi
fake_port['fixed_ips'][0]['ip_address'] = self.fake_vip
net_cli.return_value.get_port.return_value = fake_port
self.ref_lb_fully_sync_populated.vip_port_id = 'foo'
with mock.patch.object(ovn_helper.OvnProviderHelper,
'handle_vip_fip') as mock_handle_vip_fip:
info = {
'ovn_lb': self.ovn_lb,
'vip_fip': self.fake_fip,
'vip_related': [self.fake_vip],
'additional_vip_fip': True,
'action': ovn_const.REQ_INFO_ACTION_SYNC
}
self.driver._fip_sync(self.ref_lb_fully_sync_populated)
mock_handle_vip_fip.assert_called_once_with(info)
@mock.patch('ovn_octavia_provider.common.clients.get_neutron_client')
def test_fip_sync_os_err(self, net_cli):
net_cli.return_value.ips.side_effect = [
openstack.exceptions.HttpException]
self.ref_lb_fully_sync_populated.vip_port_id = 'foo'
calls = [
mock.call('Floating IP not found for loadbalancer '
f'{self.ref_lb_fully_sync_populated.loadbalancer_id}'),
mock.call('Floating IP not consistent between Logic Switch Port '
f'and Neutron. Found FIP {self.fake_fip} configured in '
f'LSP {self.lsp.name}, but no FIP configured from '
'Neutron. Please run command `neutron-ovn-db-sync-util` '
'first to sync OVN DB with Neutron DB.')]
with mock.patch.object(ovn_driver, 'LOG') as m_l:
self.driver._fip_sync(self.ref_lb_fully_sync_populated)
m_l.warn.assert_has_calls(calls)
@mock.patch('ovn_octavia_provider.common.clients.get_neutron_client')
def test_fip_sync_lsp_mismatch(self, net_cli):
net_cli.return_value.ips.return_value = [
mock.Mock(floating_ip_address='1.2.3.5')]
self.ref_lb_fully_sync_populated.vip_port_id = 'foo'
msg = ('Floating IP not consistent between Logic Switch Port and '
f'Neutron. Found FIP {self.fake_fip} in LSP {self.lsp.name}, '
'but we have 1.2.3.5 from Neutron. Skip sync FIP for '
'loadbalancer '
f'{self.ref_lb_fully_sync_populated.loadbalancer_id}. '
'Please run command `neutron-ovn-db-sync-util` first to '
'sync OVN DB with Neutron DB.')
with mock.patch.object(ovn_driver, 'LOG') as m_l:
self.driver._fip_sync(self.ref_lb_fully_sync_populated)
m_l.warn.assert_called_once_with(msg)
@mock.patch('ovn_octavia_provider.common.clients.get_neutron_client')
def test_fip_sync_lsp_not_found(self, net_cli):
net_cli.return_value.ips.return_value = [
mock.Mock(floating_ip_address='1.2.3.4')]
self.ref_lb_fully_sync_populated.vip_port_id = 'foo'
self.mock_get_lsp.return_value = None
msg = (
'Logic Switch Port not found for port foo. Skip sync FIP for '
'loadbalancer '
f'{self.ref_lb_fully_sync_populated.loadbalancer_id}. Please '
'run command `neutron-ovn-db-sync-util` first to sync OVN DB '
'with Neutron DB.')
with mock.patch.object(ovn_driver, 'LOG') as m_l:
self.driver._fip_sync(self.ref_lb_fully_sync_populated)
m_l.warn.assert_called_once_with(msg)
@mock.patch('ovn_octavia_provider.common.clients.get_neutron_client')
def test_fip_sync_no_neutron_fip(self, net_cli):
net_cli.return_value.ips.return_value = []
self.ref_lb_fully_sync_populated.vip_port_id = 'foo'
calls = [
mock.call('Floating IP not found for loadbalancer '
f'{self.ref_lb_fully_sync_populated.loadbalancer_id}'),
mock.call('Floating IP not consistent between Logic Switch Port '
f'and Neutron. Found FIP {self.fake_fip} configured in '
f'LSP {self.lsp.name}, but no FIP configured from '
'Neutron. Please run command `neutron-ovn-db-sync-util` '
'first to sync OVN DB with Neutron DB.')]
self.driver.pool_create(self.ref_pool)
self.mock_add_request
with mock.patch.object(ovn_driver, 'LOG') as m_l:
self.driver._fip_sync(self.ref_lb_fully_sync_populated)
m_l.warn.assert_has_calls(calls)
@mock.patch('ovn_octavia_provider.common.clients.get_neutron_client')
def test_fip_sync_no_neutron_fip_no_lsp(self, net_cli):
net_cli.return_value.ips.return_value = []
self.ref_lb_fully_sync_populated.vip_port_id = 'foo'
self.mock_get_lsp.return_value = None
self.driver._fip_sync(self.ref_lb_fully_sync_populated)
@mock.patch('ovn_octavia_provider.common.clients.get_neutron_client')
def test_fip_sync_no_port_id(self, net_cli):
net_cli.return_value.ips.return_value = [
mock.Mock(floating_ip_address='1.2.3.4')]
msg = ('VIP Port or Network not set for loadbalancer '
f'{self.ref_lb_fully_sync_populated.loadbalancer_id}, '
'skip FIP sync.')
with mock.patch.object(ovn_driver, 'LOG') as m_l:
self.driver._fip_sync(self.ref_lb_fully_sync_populated)
m_l.debug.assert_called_once_with(msg)
def test_loadbalancer_create_additional_vips(self):
info = {'id': self.ref_lb2.loadbalancer_id,
'vip_address': self.ref_lb2.vip_address,

View File

@@ -4006,6 +4006,42 @@ class TestOvnProviderHelper(ovn_base.TestOvnOctaviaBase):
self.switch_port_event.run(mock.ANY, row, old)
self.mock_add_request.assert_not_called()
@mock.patch('ovn_octavia_provider.helper.OvnProviderHelper.'
'_find_ovn_lbs')
@mock.patch('ovn_octavia_provider.common.clients.get_neutron_client')
def test_vip_port_update_handler_no_port(self, net_cli, lb):
lb1 = mock.MagicMock()
lb.return_value = [lb1]
self.switch_port_event = ovn_event.LogicalSwitchPortUpdateEvent(
self.helper)
net_cli.return_value.get_port.return_value = None
port_name = '%s%s' % (ovn_const.LB_VIP_PORT_PREFIX, 'foo')
attrs = {'external_ids':
{ovn_const.OVN_PORT_NAME_EXT_ID_KEY: port_name}}
row = fakes.FakeOvsdbRow.create_one_ovsdb_row(
attrs=attrs)
attrs_old = {'external_ids':
{ovn_const.OVN_PORT_NAME_EXT_ID_KEY: port_name,
ovn_const.OVN_PORT_FIP_EXT_ID_KEY: '172.24.4.40'}}
old = fakes.FakeOvsdbRow.create_one_ovsdb_row(
attrs=attrs_old)
self.switch_port_event.run(mock.ANY, row, old)
expected = {
'type': 'handle_vip_fip',
'info': {
'action': ovn_const.REQ_INFO_ACTION_DISASSOCIATE,
'vip_fip': '172.24.4.40',
'vip_related': [],
'additional_vip_fip': False,
'ovn_lb': lb1
}
}
self.mock_add_request.assert_called_once_with(expected)
@mock.patch.object(ovn_helper.OvnProviderHelper, '_execute_commands')
def test__update_lb_to_lr_association_retry(self, execute):
self._update_lb_to_lr_association.stop()
@@ -4047,7 +4083,7 @@ class TestOvnProviderHelper(ovn_base.TestOvnOctaviaBase):
self.router)
@mock.patch('ovn_octavia_provider.helper.OvnProviderHelper.'
'_find_ovn_lbs')
'_find_ovn_lbs_with_retry')
@mock.patch('ovn_octavia_provider.common.clients.get_neutron_client')
def test_vip_port_update_handler_multiple_lbs(self, net_cli, lb):
lb1 = mock.MagicMock()
@@ -4074,7 +4110,7 @@ class TestOvnProviderHelper(ovn_base.TestOvnOctaviaBase):
def expected_call(lb):
return {'type': 'handle_vip_fip',
'info':
{'action': mock.ANY,
{'action': ovn_const.REQ_INFO_ACTION_DISASSOCIATE,
'vip_fip': '172.24.4.40',
'vip_related': [fake_port.fixed_ips[0]['ip_address']],
'additional_vip_fip': False,
@@ -4110,17 +4146,16 @@ class TestOvnProviderHelper(ovn_base.TestOvnOctaviaBase):
attrs=attrs_old)
self.switch_port_event.run(mock.ANY, row, old)
def expected_call(lb):
return {'type': 'handle_vip_fip',
'info':
{'action': ovn_const.REQ_INFO_ACTION_DISASSOCIATE,
'vip_fip': fip,
'vip_related': [fake_port.fixed_ips[0]['ip_address']],
'additional_vip_fip': True,
'ovn_lb': lb}}
expected = {
'type': 'handle_vip_fip',
'info': {
'action': ovn_const.REQ_INFO_ACTION_DISASSOCIATE,
'vip_fip': fip,
'vip_related': [fake_port.fixed_ips[0]['ip_address']],
'additional_vip_fip': True,
'ovn_lb': lb1}}
self.mock_add_request.assert_has_calls([
mock.call(expected_call(lb1))])
self.mock_add_request.assert_has_calls([mock.call(expected)])
@mock.patch('ovn_octavia_provider.helper.OvnProviderHelper.'
'_find_ovn_lbs')
@@ -4147,17 +4182,16 @@ class TestOvnProviderHelper(ovn_base.TestOvnOctaviaBase):
attrs=attrs_old)
self.switch_port_event.run(mock.ANY, row, old)
def expected_call(lb):
return {'type': 'handle_vip_fip',
'info':
{'action': ovn_const.REQ_INFO_ACTION_ASSOCIATE,
'vip_fip': '10.0.0.99',
'vip_related': [fake_port.fixed_ips[0]['ip_address']],
'additional_vip_fip': True,
'ovn_lb': lb}}
expected = {
'type': 'handle_vip_fip',
'info': {
'action': ovn_const.REQ_INFO_ACTION_ASSOCIATE,
'vip_fip': '10.0.0.99',
'vip_related': [fake_port.fixed_ips[0]['ip_address']],
'additional_vip_fip': True,
'ovn_lb': lb1}}
self.mock_add_request.assert_has_calls([
mock.call(expected_call(lb1))])
self.mock_add_request.assert_has_calls([mock.call(expected)])
lb1 = mock.MagicMock()
vip_fip = '10.0.0.123'
@@ -4172,7 +4206,7 @@ class TestOvnProviderHelper(ovn_base.TestOvnOctaviaBase):
self.switch_port_event.run(mock.ANY, row, old)
self.mock_add_request.reset()
self.mock_add_request.assert_has_calls([
mock.call(expected_call(lb1))])
mock.call(expected)])
@mock.patch('ovn_octavia_provider.helper.OvnProviderHelper.'
'_find_ovn_lbs')
@@ -4271,6 +4305,149 @@ class TestOvnProviderHelper(ovn_base.TestOvnOctaviaBase):
mock.call.db_set('Load_Balancer', lb.uuid, ('vips', {}))]
self.helper.ovn_nbdb_api.assert_has_calls(calls)
@mock.patch('ovn_octavia_provider.helper.OvnProviderHelper.'
'_find_ovn_lbs')
def test_handle_vip_fip_sync(self, fb):
lb = mock.MagicMock()
fip_info = {
'action': 'sync',
'vip_fip': '10.0.0.123',
'ovn_lb': lb}
members = 'member_%s_%s:%s_%s' % (self.member_id,
self.member_address,
self.member_port,
self.member_subnet_id)
external_ids = {
'listener_foo': '80:pool_%s' % self.pool_id,
'pool_%s' % self.pool_id: members,
'neutron:vip': '172.26.21.20'}
lb.external_ids = external_ids
fb.return_value = lb
self.helper.handle_vip_fip(fip_info)
expected_db_set_calls = [
mock.call('Load_Balancer', lb.uuid,
('external_ids', {'neutron:vip_fip': '10.0.0.123'})),
mock.call('Load_Balancer', lb.uuid,
('vips', {'10.0.0.123:80': '192.168.2.149:1010',
'172.26.21.20:80': '192.168.2.149:1010'}))
]
self.helper.ovn_nbdb_api.db_set.assert_has_calls(expected_db_set_calls)
self.helper.ovn_nbdb_api.db_clear.assert_called_once_with(
'Load_Balancer', lb.uuid, 'vips')
@mock.patch('ovn_octavia_provider.helper.OvnProviderHelper.'
'_find_ovn_lbs')
def test_handle_vip_fip_sync_same_ext(self, fb):
lb = mock.MagicMock()
fip_info = {
'action': 'sync',
'vip_fip': '10.0.0.123',
'ovn_lb': lb}
members = 'member_%s_%s:%s_%s' % (self.member_id,
self.member_address,
self.member_port,
self.member_subnet_id)
external_ids = {
'listener_foo': '80:pool_%s' % self.pool_id,
'pool_%s' % self.pool_id: members,
'neutron:vip': '172.26.21.20',
'neutron:vip_fip': '10.0.0.123',
}
lb.external_ids = external_ids
fb.return_value = lb
self.helper.handle_vip_fip(fip_info)
expected_db_set_calls = [
mock.call('Load_Balancer', lb.uuid,
('vips', {'10.0.0.123:80': '192.168.2.149:1010',
'172.26.21.20:80': '192.168.2.149:1010'}))
]
self.helper.ovn_nbdb_api.db_set.assert_has_calls(expected_db_set_calls)
self.helper.ovn_nbdb_api.db_clear.assert_called_once_with(
'Load_Balancer', lb.uuid, 'vips')
@mock.patch('ovn_octavia_provider.helper.OvnProviderHelper.'
'_find_ovn_lbs')
def test_handle_vip_fip_sync_hm_exist(self, fb):
lb = mock.MagicMock()
vip_fip = '10.0.0.123'
fip_info = {
'action': 'sync',
'vip_fip': '10.0.0.123',
'ovn_lb': lb}
members = 'member_%s_%s:%s_%s' % (self.member_id,
self.member_address,
self.member_port,
self.member_subnet_id)
external_ids = {
'listener_foo': '80:pool_%s' % self.pool_id,
'pool_%s' % self.pool_id: members,
'neutron:vip': '172.26.21.20',
'neutron:vip_fip': '10.0.0.123',
}
lb_hc_fip = mock.MagicMock()
lb_hc_fip.uuid = "fake_lb_hc_fip"
lb_hc_fip.vip = f"{vip_fip}:80"
lb_hc_fip.external_ids = {
ovn_const.LB_EXT_IDS_HM_KEY: 'foo',
ovn_const.LB_EXT_IDS_HM_POOL_KEY: 'pool_foo',
ovn_const.LB_EXT_IDS_HM_VIP: vip_fip}
lb.health_check = [lb_hc_fip]
lb.vips = {
'172.26.21.20:80': '192.168.2.149:1010',
'10.0.0.123:80': '192.168.2.149:1010'
}
lb.external_ids = external_ids
fb.return_value = lb
self.helper.handle_vip_fip(fip_info)
self.helper.ovn_nbdb_api.db_set.assert_not_called()
self.helper.ovn_nbdb_api.db_clear.assert_not_called()
def test_get_lsp(self):
self.helper.ovn_nbdb_api.lookup.side_effect = [idlutils.RowNotFound]
port_id = '664224ab-a7c4-4918-8d81-0712947eb600'
network_id = 'c75cb020-f789-432e-b50e-89a20e5e463d'
with mock.patch.object(ovn_helper, 'LOG') as m_l:
self.assertIsNone(self.helper.get_lsp(port_id=port_id,
network_id=network_id))
m_l.warn.assert_called_once_with(
f'Logical Switch neutron-{network_id} not found.')
def test_get_lsp_port_not_found(self):
self.helper.ovn_nbdb_api.lookup.return_value = mock.MagicMock(ports=[])
port_id = '664224ab-a7c4-4918-8d81-0712947eb600'
network_id = 'c75cb020-f789-432e-b50e-89a20e5e463d'
self.assertIsNone(self.helper.get_lsp(port_id=port_id,
network_id=network_id))
def test_get_lsp_port_found(self):
port_id = '664224ab-a7c4-4918-8d81-0712947eb600'
network_id = 'c75cb020-f789-432e-b50e-89a20e5e463d'
port = mock.MagicMock()
port.name = port_id
self.helper.ovn_nbdb_api.lookup.return_value = mock.MagicMock(
ports=[port])
self.assertEqual(self.helper.get_lsp(
port_id=port_id, network_id=network_id), port)
def test_get_lsp_port_found_many_ports(self):
port_id = '664224ab-a7c4-4918-8d81-0712947eb600'
network_id = 'c75cb020-f789-432e-b50e-89a20e5e463d'
port = mock.MagicMock()
port.name = port_id
port2 = mock.MagicMock()
port2.name = 'foo'
self.helper.ovn_nbdb_api.lookup.return_value = mock.MagicMock(
ports=[port2, port])
self.assertEqual(self.helper.get_lsp(
port_id=port_id, network_id=network_id), port)
@mock.patch('ovn_octavia_provider.helper.OvnProviderHelper.'
'_find_ovn_lbs')
def test_handle_vip_fip_associate(self, fb):
@@ -5047,8 +5224,7 @@ class TestOvnProviderHelper(ovn_base.TestOvnOctaviaBase):
@mock.patch('ovn_octavia_provider.common.clients.get_neutron_client')
@mock.patch.object(ovn_helper.OvnProviderHelper, '_update_hm_member')
@mock.patch.object(ovn_helper.OvnProviderHelper, '_find_ovn_lb_by_pool_id')
def _test_hm_create(self, protocol, members, fip, folbpi, uhm,
net_cli):
def _test_hm_create(self, protocol, members, fip, folbpi, uhm, net_cli):
self._get_pool_listeners.stop()
fake_subnet = fakes.FakeSubnet.create_one_subnet()
pool_key = 'pool_%s' % self.pool_id