Allow to create ovn loadbalancer on dual-stack provider networks

This patch ensures that loadbalancer can be created with the VIP
on the provider networks when they are dual-stack (ipv4 and ipv6
subnets).

NOTE: added neutron_lib import constants to make use of the same
constant used in master, otherwise pep8 gate was failing.

Closes-Bug: #1959903
Change-Id: Ic0e6c02defb5c98791a3c3d99ff2b7cfc0ccfe2e
(cherry picked from commit 1ac25e8814)
This commit is contained in:
Luis Tomas Bolivar 2022-02-03 14:48:39 +01:00
parent 279cc0c946
commit 7a51f79d62
3 changed files with 26 additions and 12 deletions

View File

@ -28,6 +28,7 @@ OVN_DEVICE_OWNER_EXT_ID_KEY = 'neutron:device_owner'
OVN_FIP_EXT_ID_KEY = 'neutron:fip_id'
OVN_FIP_PORT_EXT_ID_KEY = 'neutron:fip_port_id'
OVN_GW_PORT_EXT_ID_KEY = 'neutron:gw_port_id'
OVN_PORT_CIDR_EXT_ID_KEY = 'neutron:cidrs'
LB_EXT_IDS_LS_REFS_KEY = 'ls_refs'
LB_EXT_IDS_LR_REF_KEY = 'lr_ref'

View File

@ -18,6 +18,7 @@ import re
import threading
import netaddr
from neutron_lib import constants as n_const
from neutronclient.common import exceptions as n_exc
from octavia_lib.api.drivers import data_models as o_datamodels
from octavia_lib.api.drivers import driver_lib as o_driver_lib
@ -622,10 +623,14 @@ class OvnProviderHelper(object):
def _find_lr_of_ls(self, ovn_ls, subnet_gateway_ip=None):
lsp_router_port = None
for port in ovn_ls.ports or []:
if port.type == 'router':
if (port.type == 'router' and
port.external_ids.get(
ovn_const.OVN_DEVICE_OWNER_EXT_ID_KEY) ==
n_const.DEVICE_OWNER_ROUTER_INTF):
if subnet_gateway_ip:
port_cidr = netaddr.IPNetwork(
port.external_ids['neutron:cidrs']).ip
port.external_ids[
ovn_const.OVN_PORT_CIDR_EXT_ID_KEY]).ip
if netaddr.IPAddress(subnet_gateway_ip) != port_cidr:
continue
lsp_router_port = port
@ -1517,9 +1522,8 @@ class OvnProviderHelper(object):
ovn_lr = self._find_lr_of_ls(
ovn_ls, subnet['subnet'].get('gateway_ip'))
if ovn_lr:
for net in self._find_ls_for_lr(ovn_lr):
commands.append(self.ovn_nbdb_api.ls_lb_add(
net, ovn_lb.uuid, may_exist=True))
commands.extend(self._update_lb_to_lr_association(
ovn_lb, ovn_lr))
except n_exc.NotFound:
pass
except idlutils.RowNotFound:

View File

@ -14,6 +14,7 @@
import copy
from unittest import mock
from neutron_lib import constants as n_const
from neutronclient.common import exceptions as n_exc
from octavia_lib.api.drivers import data_models
from octavia_lib.api.drivers import exceptions
@ -1170,20 +1171,22 @@ class TestOvnProviderHelper(ovn_base.TestOvnOctaviaBase):
self.assertEqual(status['members'][0]['operating_status'],
constants.OFFLINE)
@mock.patch.object(ovn_helper.OvnProviderHelper, '_find_ls_for_lr')
@mock.patch.object(ovn_helper.OvnProviderHelper, '_find_ovn_lb_by_pool_id')
@mock.patch.object(ovn_helper.OvnProviderHelper, '_find_lr_of_ls')
@mock.patch('ovn_octavia_provider.common.clients.get_neutron_client')
def test_member_create_lb_add_from_lr(self, net_cli, f_lr, f_ls):
def test_member_create_lb_add_from_lr(self, net_cli, f_lr, folbpi):
fake_subnet = fakes.FakeSubnet.create_one_subnet()
net_cli.return_value.show_subnet.return_value = {'subnet': fake_subnet}
f_lr.return_value = self.router
f_ls.return_value = [self.network]
pool_key = 'pool_%s' % self.pool_id
folbpi.return_value = (pool_key, self.ovn_lb)
self.ovn_lb.external_ids = mock.MagicMock()
status = self.helper.member_create(self.member)
self.assertEqual(status['loadbalancers'][0]['provisioning_status'],
constants.ACTIVE)
f_lr.assert_called_once_with(self.network, fake_subnet['gateway_ip'])
f_ls.assert_called_once_with(self.router)
self.helper._update_lb_to_lr_association.assert_called_once_with(
self.ovn_lb, self.router)
@mock.patch.object(ovn_helper.OvnProviderHelper, '_find_ls_for_lr')
@mock.patch.object(ovn_helper.OvnProviderHelper, '_find_lr_of_ls')
@ -1805,7 +1808,9 @@ class TestOvnProviderHelper(ovn_base.TestOvnOctaviaBase):
attrs={
'external_ids': {
ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY: 'router1',
'neutron:cidrs': '10.10.10.1/24'},
'neutron:cidrs': '10.10.10.1/24',
ovn_const.OVN_DEVICE_OWNER_EXT_ID_KEY:
n_const.DEVICE_OWNER_ROUTER_INTF},
'type': 'router',
'options': {
'router-port': 'lrp-foo-name'},
@ -1814,7 +1819,9 @@ class TestOvnProviderHelper(ovn_base.TestOvnOctaviaBase):
attrs={
'external_ids': {
ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY: 'router2',
'neutron:cidrs': '10.10.10.2/24'},
'neutron:cidrs': '10.10.10.2/24',
ovn_const.OVN_DEVICE_OWNER_EXT_ID_KEY:
n_const.DEVICE_OWNER_ROUTER_INTF},
'type': 'router',
'options': {
'router-port': 'lrp-bar-name'},
@ -1839,7 +1846,9 @@ class TestOvnProviderHelper(ovn_base.TestOvnOctaviaBase):
lsp = fakes.FakeOvsdbRow.create_one_ovsdb_row(
attrs={
'external_ids': {
ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY: 'router1'},
ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY: 'router1',
ovn_const.OVN_DEVICE_OWNER_EXT_ID_KEY:
n_const.DEVICE_OWNER_ROUTER_INTF},
'type': 'router',
'options': {
'router-port': 'lrp-lrp-foo-name'}