Skip to content

Commit f469fd8

Browse files
Allow LB members to mix IPv4 and IPv6 for the multivip LB
When creating a load balancer with both IPv4 and IPv6 protocols for the LB VIP and additional_vips field, it is essential to allow the mixing of IPv4 and IPv6 backend members. This patch enables this use case and ensures that the 'vips' field in the OVN NB DB associates IPv4-type LB VIPs with IPv4 members and IPv6-type LB VIPs with IPv6 members exclusively. Closes-Bug: 2047055 Change-Id: I173a6456e8a5f776cac207390e670afa34f83d7c
1 parent 4249ab8 commit f469fd8

File tree

7 files changed

+109
-45
lines changed

7 files changed

+109
-45
lines changed

ovn_octavia_provider/driver.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -265,9 +265,22 @@ def _ip_version_differs(self, member):
265265
_, ovn_lb = self._ovn_helper._find_ovn_lb_by_pool_id(member.pool_id)
266266
if not ovn_lb:
267267
return False
268-
lb_vip = ovn_lb.external_ids[ovn_const.LB_EXT_IDS_VIP_KEY]
269-
return netaddr.IPNetwork(lb_vip).version != (
270-
netaddr.IPNetwork(member.address).version)
268+
lb_vips = [ovn_lb.external_ids.get(
269+
ovn_const.LB_EXT_IDS_VIP_KEY)]
270+
if ovn_const.LB_EXT_IDS_ADDIT_VIP_KEY in ovn_lb.external_ids:
271+
lb_vips.extend(ovn_lb.external_ids.get(
272+
ovn_const.LB_EXT_IDS_ADDIT_VIP_KEY).split(','))
273+
274+
# NOTE(froyo): Allow mixing member IP version when VIP LB and any
275+
# additional vip is also mixing version
276+
vip_version = netaddr.IPNetwork(lb_vips[0]).version
277+
vips_mixed = any(netaddr.IPNetwork(vip).version != vip_version
278+
for vip in lb_vips if vip)
279+
280+
if vips_mixed:
281+
return False
282+
else:
283+
return vip_version != (netaddr.IPNetwork(member.address).version)
271284

272285
def member_create(self, member):
273286
# Validate monitoring options if present

ovn_octavia_provider/helper.py

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -968,30 +968,36 @@ def _frame_vip_ips(self, ovn_lb, lb_external_ids):
968968
if pool_id not in lb_external_ids or not lb_external_ids[pool_id]:
969969
continue
970970

971-
ips = []
971+
ips_v4 = []
972+
ips_v6 = []
972973
for mb_ip, mb_port, mb_subnet, mb_id in self._extract_member_info(
973974
lb_external_ids[pool_id]):
974975
if not self._is_member_offline(ovn_lb, mb_id):
975-
if netaddr.IPNetwork(mb_ip).version == 6:
976-
ips.append(f'[{mb_ip}]:{mb_port}')
976+
if netaddr.IPNetwork(
977+
mb_ip).version == n_const.IP_VERSION_6:
978+
ips_v6.append(f'[{mb_ip}]:{mb_port}')
977979
else:
978-
ips.append(f'{mb_ip}:{mb_port}')
979-
if ips:
980-
for lb_vip in lb_vips:
981-
if netaddr.IPNetwork(lb_vip).version == 6:
982-
lb_vip = f'[{lb_vip}]'
983-
vip_ips[lb_vip + ':' + vip_port] = ','.join(ips)
984-
985-
if vip_fip:
986-
if netaddr.IPNetwork(vip_fip).version == 6:
987-
vip_fip = f'[{vip_fip}]'
988-
vip_ips[vip_fip + ':' + vip_port] = ','.join(ips)
989-
990-
if additional_vip_fips:
991-
for addi_vip_fip in additional_vip_fips.split(','):
992-
if netaddr.IPNetwork(addi_vip_fip).version == 6:
993-
addi_vip_fip = f'[{addi_vip_fip}]'
994-
vip_ips[addi_vip_fip + ':' + vip_port] = ','.join(ips)
980+
ips_v4.append(f'{mb_ip}:{mb_port}')
981+
982+
for lb_vip in lb_vips:
983+
if ips_v4 and netaddr.IPNetwork(
984+
lb_vip).version == n_const.IP_VERSION_4:
985+
vip_ips[lb_vip + ':' + vip_port] = ','.join(ips_v4)
986+
if ips_v6 and netaddr.IPNetwork(
987+
lb_vip).version == n_const.IP_VERSION_6:
988+
lb_vip = f'[{lb_vip}]'
989+
vip_ips[lb_vip + ':' + vip_port] = ','.join(ips_v6)
990+
991+
if ips_v4 and vip_fip:
992+
if netaddr.IPNetwork(vip_fip).version == n_const.IP_VERSION_4:
993+
vip_ips[vip_fip + ':' + vip_port] = ','.join(ips_v4)
994+
995+
if ips_v4 and additional_vip_fips:
996+
for addi_vip_fip in additional_vip_fips.split(','):
997+
if netaddr.IPNetwork(
998+
addi_vip_fip).version == n_const.IP_VERSION_4:
999+
vip_ips[addi_vip_fip + ':' + vip_port] = ','.join(
1000+
ips_v4)
9951001
return vip_ips
9961002

9971003
def _refresh_lb_vips(self, ovn_lb, lb_external_ids):
@@ -2646,7 +2652,7 @@ def _add_lbhc(self, ovn_lb, pool_key, info):
26462652
# then this could just be self.ovn_nbdb_api.lb_hm_add()
26472653
external_ids_vip = copy.deepcopy(external_ids)
26482654
external_ids_vip[ovn_const.LB_EXT_IDS_HM_VIP] = vip
2649-
if netaddr.IPNetwork(vip).version == 6:
2655+
if netaddr.IPNetwork(vip).version == n_const.IP_VERSION_6:
26502656
vip = f'[{vip}]'
26512657
kwargs = {
26522658
'vip': vip + ':' + str(vip_port) if vip_port else '',
@@ -2673,7 +2679,8 @@ def _add_lbhc(self, ovn_lb, pool_key, info):
26732679
external_ids_fip = copy.deepcopy(external_ids)
26742680
for fip in fips:
26752681
external_ids_fip[ovn_const.LB_EXT_IDS_HM_VIP] = fip
2676-
if netaddr.IPNetwork(fip).version == 6:
2682+
if netaddr.IPNetwork(
2683+
fip).version == n_const.IP_VERSION_6:
26772684
fip = f'[{fip}]'
26782685
fip_kwargs = {
26792686
'vip': fip + ':' + str(vip_port)
@@ -2705,7 +2712,7 @@ def _update_lbhc_vip_port(self, lbhc, vip_port):
27052712
# will be empty, so get it from lbhc external_ids
27062713
vip = lbhc.external_ids.get(ovn_const.LB_EXT_IDS_HM_VIP, '')
27072714
if vip:
2708-
if netaddr.IPNetwork(vip).version == 6:
2715+
if netaddr.IPNetwork(vip).version == n_const.IP_VERSION_6:
27092716
vip = f'[{vip}]'
27102717
vip = vip + ':' + str(vip_port)
27112718
commands = []
@@ -3100,11 +3107,11 @@ def _get_lbs_on_hm_event(self, row):
31003107
hm_source_ip = str(row.src_ip)
31013108
member_ip = str(row.ip)
31023109
member_src = f'{row.logical_port}:'
3103-
if netaddr.IPNetwork(hm_source_ip).version == 6:
3110+
if netaddr.IPNetwork(hm_source_ip).version == n_const.IP_VERSION_6:
31043111
member_src += f'[{hm_source_ip}]'
31053112
else:
31063113
member_src += f'{hm_source_ip}'
3107-
if netaddr.IPNetwork(member_ip).version == 6:
3114+
if netaddr.IPNetwork(member_ip).version == n_const.IP_VERSION_6:
31083115
member_ip = f'[{member_ip}]'
31093116
mappings[member_ip] = member_src
31103117
lbs = self.ovn_nbdb_api.db_find_rows(

ovn_octavia_provider/tests/functional/base.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from unittest import mock
1818

1919
from neutron.common import utils as n_utils
20+
from neutron_lib import constants as n_const
2021
from neutron_lib.plugins import directory
2122
from octavia_lib.api.drivers import data_models as octavia_data_model
2223
from octavia_lib.api.drivers import driver_lib
@@ -283,9 +284,10 @@ def _create_net(self, name):
283284
n1 = self._make_network(self.fmt, name, True)
284285
return n1
285286

286-
def _create_subnet_from_net(self, net, cidr, router_id=None):
287+
def _create_subnet_from_net(self, net, cidr, router_id=None,
288+
ip_version=n_const.IP_VERSION_4):
287289
res = self._create_subnet(self.fmt, net['network']['id'],
288-
cidr)
290+
cidr, ip_version=ip_version)
289291
subnet = self.deserialize(self.fmt, res)['subnet']
290292
self._local_net_cache[subnet['id']] = net['network']['id']
291293
self._local_cidr_cache[subnet['id']] = subnet['cidr']

ovn_octavia_provider/tests/functional/test_driver.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313
# License for the specific language governing permissions and limitations
1414
# under the License.
1515

16+
from neutron_lib import constants as n_const
1617
from octavia_lib.api.drivers import exceptions as o_exceptions
1718
from octavia_lib.common import constants as o_constants
18-
1919
from oslo_utils import uuidutils
2020

2121
from ovn_octavia_provider.tests.functional import base as ovn_base
@@ -29,7 +29,8 @@ def test_loadbalancer(self):
2929
sbnet_info = self._create_subnet_from_net(network_N1, '10.0.0.0/24',
3030
router_id=r1_id)
3131
sbnet_additional_info = self._create_subnet_from_net(
32-
network_N1, '10.0.1.0/24', router_id=r1_id)
32+
network_N1, '2001:db8:0:1::/64', router_id=r1_id,
33+
ip_version=n_const.IP_VERSION_6)
3334
additional_vips_list = [{
3435
'ip_address': sbnet_additional_info[2],
3536
'port_id': sbnet_additional_info[3],
@@ -266,7 +267,8 @@ def test_hm(self):
266267
sbnet_info = self._create_subnet_from_net(network_N1, '10.0.0.0/24',
267268
router_id=r1_id)
268269
sbnet_additional_info = self._create_subnet_from_net(
269-
network_N1, '10.1.1.0/24', router_id=r1_id)
270+
network_N1, '2001:db8:0:1::/64', router_id=r1_id,
271+
ip_version=n_const.IP_VERSION_6)
270272
additional_vips_list = [{
271273
'ip_address': sbnet_additional_info[2],
272274
'port_id': sbnet_additional_info[3],

ovn_octavia_provider/tests/unit/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def setUp(self):
5050
self.vip_output = {'vip_network_id': self.vip_dict['vip_network_id'],
5151
'vip_subnet_id': self.vip_dict['vip_subnet_id']}
5252
self.additional_vips = [{
53-
'ip_address': '192.148.110.109',
53+
'ip_address': '2001:db8:0:1::12',
5454
'network_id': self.vip_dict['vip_network_id'],
5555
'port_id': uuidutils.generate_uuid(),
5656
'subnet_id': uuidutils.generate_uuid()

ovn_octavia_provider/tests/unit/test_driver.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,24 @@ def setUp(self):
3737
'member_%s_%s:%s_%s' %
3838
(self.member_id, self.member_address,
3939
self.member_port, self.member_subnet_id))
40+
self.member_line_additional_vips = (
41+
'member_%s_%s:%s_%s' %
42+
(self.member_id, self.member_address,
43+
self.member_port, self.member_subnet_id))
4044
self.ovn_lb = mock.MagicMock()
4145
self.ovn_lb.name = 'foo_ovn_lb'
4246
self.ovn_lb.external_ids = {
4347
ovn_const.LB_EXT_IDS_VIP_KEY: '10.22.33.4',
4448
'pool_%s' % self.pool_id: self.member_line,
4549
'listener_%s' % self.listener_id: '80:pool_%s' % self.pool_id}
50+
self.ovn_lb_addi_vips = mock.MagicMock()
51+
self.ovn_lb_addi_vips.name = 'foo_ovn_lb_addi_vips'
52+
self.ovn_lb_addi_vips.external_ids = {
53+
ovn_const.LB_EXT_IDS_VIP_KEY: '10.22.33.4',
54+
ovn_const.LB_EXT_IDS_ADDIT_VIP_KEY: '2001:db8:0:1::203',
55+
'pool_%s' % self.pool_id: ','.join([
56+
self.member_line, self.member_line_additional_vips]),
57+
'listener_%s' % self.listener_id: '80:pool_%s' % self.pool_id}
4658
self.mock_add_request = add_req_thread.start()
4759
self.project_id = uuidutils.generate_uuid()
4860

@@ -251,6 +263,15 @@ def test__ip_version_differs(self):
251263
self.ref_member.address = 'fc00::1'
252264
self.assertTrue(self.driver._ip_version_differs(self.ref_member))
253265

266+
def test__ip_version_differs_lb_additional_vips(self):
267+
self.mock_find_ovn_lb_by_pool_id = mock.patch.object(
268+
ovn_helper.OvnProviderHelper,
269+
'_find_ovn_lb_by_pool_id').start()
270+
self.mock_find_ovn_lb_by_pool_id.return_value = (_,
271+
self.ovn_lb_addi_vips)
272+
self.ref_member.address = 'fc00::1'
273+
self.assertFalse(self.driver._ip_version_differs(self.ref_member))
274+
254275
def test__ip_version_differs_lb_not_found(self):
255276
self.mock_find_ovn_lb_by_pool_id = mock.patch.object(
256277
ovn_helper.OvnProviderHelper,

ovn_octavia_provider/tests/unit/test_helper.py

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4165,24 +4165,45 @@ def test__frame_lb_vips_no_vip_fip(self):
41654165
expected = {'10.22.33.4:80': '192.168.2.149:1010'}
41664166
self.assertEqual(expected, ret)
41674167

4168-
def test__frame_lb_vips_additional_vips(self):
4168+
def test__frame_lb_vips_additional_vips_only_member_ipv4(self):
41694169
self.ovn_lb.external_ids[ovn_const.LB_EXT_IDS_ADDIT_VIP_KEY] = \
41704170
'10.24.34.4,2001:db8::1'
41714171
ret = self.helper._frame_vip_ips(self.ovn_lb, self.ovn_lb.external_ids)
41724172
expected = {'10.22.33.4:80': '192.168.2.149:1010',
41734173
'10.24.34.4:80': '192.168.2.149:1010',
4174-
'[2001:db8::1]:80': '192.168.2.149:1010',
41754174
'123.123.123.123:80': '192.168.2.149:1010'}
41764175
self.assertEqual(expected, ret)
41774176

4178-
def test__frame_lb_vips_additional_vip_fips(self):
4179-
self.ovn_lb.external_ids[ovn_const.LB_EXT_IDS_ADDIT_VIP_FIP_KEY] = \
4180-
'172.24.34.4,2001:db8::1'
4177+
def test__frame_lb_vips_additional_vips_mixing_member_ipv4_ipv6(self):
4178+
self.ovn_lb.external_ids[ovn_const.LB_EXT_IDS_ADDIT_VIP_KEY] = \
4179+
'10.24.34.4,2001:db8::1'
4180+
self.member_address = '2001:db8::3'
4181+
self.member_line = (
4182+
'member_%s_%s:%s_%s' %
4183+
(self.member_id, self.member_address,
4184+
self.member_port, self.member_subnet_id))
4185+
self.ovn_lb.external_ids['pool_%s' % self.pool_id] = ','.join([
4186+
self.ovn_lb.external_ids['pool_%s' % self.pool_id],
4187+
self.member_line])
4188+
41814189
ret = self.helper._frame_vip_ips(self.ovn_lb, self.ovn_lb.external_ids)
41824190
expected = {'10.22.33.4:80': '192.168.2.149:1010',
4183-
'172.24.34.4:80': '192.168.2.149:1010',
4184-
'[2001:db8::1]:80': '192.168.2.149:1010',
4185-
'123.123.123.123:80': '192.168.2.149:1010'}
4191+
'10.24.34.4:80': '192.168.2.149:1010',
4192+
'123.123.123.123:80': '192.168.2.149:1010',
4193+
'[2001:db8::1]:80': '[2001:db8::3]:1010'}
4194+
self.assertEqual(expected, ret)
4195+
4196+
def test__frame_lb_vips_additional_vips_only_member_ipv6(self):
4197+
self.ovn_lb.external_ids[ovn_const.LB_EXT_IDS_ADDIT_VIP_KEY] = \
4198+
'10.24.34.4,2001:db8::1'
4199+
self.member_address = '2001:db8::3'
4200+
self.member_line = (
4201+
'member_%s_%s:%s_%s' %
4202+
(self.member_id, self.member_address,
4203+
self.member_port, self.member_subnet_id))
4204+
self.ovn_lb.external_ids['pool_%s' % self.pool_id] = self.member_line
4205+
ret = self.helper._frame_vip_ips(self.ovn_lb, self.ovn_lb.external_ids)
4206+
expected = {'[2001:db8::1]:80': '[2001:db8::3]:1010'}
41864207
self.assertEqual(expected, ret)
41874208

41884209
def test__frame_lb_vips_disabled(self):
@@ -4198,12 +4219,10 @@ def test__frame_lb_vips_ipv6(self):
41984219
self.member_port, self.member_subnet_id))
41994220
self.ovn_lb.external_ids = {
42004221
ovn_const.LB_EXT_IDS_VIP_KEY: 'fc00::',
4201-
ovn_const.LB_EXT_IDS_VIP_FIP_KEY: '2002::',
42024222
'pool_%s' % self.pool_id: self.member_line,
42034223
'listener_%s' % self.listener_id: '80:pool_%s' % self.pool_id}
42044224
ret = self.helper._frame_vip_ips(self.ovn_lb, self.ovn_lb.external_ids)
4205-
expected = {'[2002::]:80': '[2001:db8::1]:1010',
4206-
'[fc00::]:80': '[2001:db8::1]:1010'}
4225+
expected = {'[fc00::]:80': '[2001:db8::1]:1010'}
42074226
self.assertEqual(expected, ret)
42084227

42094228
def test_check_lb_protocol(self):

0 commit comments

Comments
 (0)