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:
@@ -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'
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user