Skip to content

Commit 5e82025

Browse files
authored
Merge pull request #70 from stackhpc/upstream/master-2025-07-21
Synchronise master with upstream
2 parents 134740d + 5895ae7 commit 5e82025

File tree

10 files changed

+171
-42
lines changed

10 files changed

+171
-42
lines changed

doc/requirements.txt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
# The order of packages is significant, because pip processes them in the order
2-
# of appearance. Changing the order has an impact on the overall integration
3-
# process, which may cause wedges in the gate later.
41
sphinx>=2.0.0,!=2.1.0 # BSD
52
sphinxcontrib-svg2pdfconverter>=0.1.0 # BSD
63
openstackdocstheme>=2.2.1 # Apache-2.0

ovn_octavia_provider/helper.py

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1904,14 +1904,14 @@ def lb_update(self, loadbalancer):
19041904
if str(ovn_lb.external_ids['enabled']) != str(lb_enabled):
19051905
commands = []
19061906
enable_info = {'enabled': str(lb_enabled)}
1907-
ovn_lb.external_ids['enabled'] = str(lb_enabled)
1907+
ovn_lb_external_ids = ovn_lb.external_ids
1908+
ovn_lb_external_ids.update(enable_info)
19081909
commands.append(
19091910
self.ovn_nbdb_api.db_set('Load_Balancer', ovn_lb.uuid,
19101911
('external_ids', enable_info))
19111912
)
19121913
commands.extend(
1913-
self._refresh_lb_vips(ovn_lb,
1914-
ovn_lb.external_ids))
1914+
self._refresh_lb_vips(ovn_lb, ovn_lb_external_ids))
19151915
self._execute_commands(commands)
19161916
if lb_enabled:
19171917
operating_status = constants.ONLINE
@@ -3520,18 +3520,52 @@ def _update_lbhc_vip_port(self, lbhc, vip_port):
35203520
self._execute_commands(commands)
35213521
return True
35223522

3523-
def _update_ip_port_mappings(self, ovn_lb, backend_ip, port_name, src_ip,
3524-
delete=False):
3525-
3523+
def _update_ip_port_mappings(
3524+
self,
3525+
ovn_lb,
3526+
backend_ip,
3527+
port_name,
3528+
src_ip,
3529+
pool_key,
3530+
delete=False):
35263531
# ip_port_mappings:${MEMBER_IP}=${LSP_NAME_MEMBER}:${HEALTH_SRC}
35273532
# where:
35283533
# MEMBER_IP: IP of member_lsp
35293534
# LSP_NAME_MEMBER: Logical switch port
35303535
# HEALTH_SRC: source IP of hm_port
35313536

35323537
if delete:
3533-
self.ovn_nbdb_api.lb_del_ip_port_mapping(ovn_lb.uuid,
3534-
backend_ip).execute()
3538+
# Before removing a member from ip_port_mappings,
3539+
# make sure no other
3540+
# pool uses the same member.
3541+
other_members = []
3542+
for k, v in ovn_lb.external_ids.items():
3543+
if ovn_const.LB_EXT_IDS_POOL_PREFIX in k and k != pool_key:
3544+
other_members.extend(self._extract_member_info(
3545+
ovn_lb.external_ids[k]))
3546+
member_statuses = ovn_lb.external_ids.get(
3547+
ovn_const.OVN_MEMBER_STATUS_KEY)
3548+
try:
3549+
member_statuses = jsonutils.loads(member_statuses)
3550+
except TypeError:
3551+
LOG.debug("No member status in external_ids: %s",
3552+
str(member_statuses))
3553+
member_statuses = {}
3554+
execute_delete = True
3555+
for member_id in [item[3] for item in other_members
3556+
if item[0] == backend_ip]:
3557+
if member_statuses.get(member_id, '') != constants.NO_MONITOR:
3558+
execute_delete = False
3559+
LOG.debug(
3560+
f"Backend {backend_ip} still in use by member"
3561+
f" {member_id}, "
3562+
f"so it won't be removed"
3563+
)
3564+
break
3565+
if execute_delete:
3566+
LOG.debug(f"Removing ip_port_mapping for {backend_ip}")
3567+
self.ovn_nbdb_api.lb_del_ip_port_mapping(
3568+
ovn_lb.uuid, backend_ip).execute()
35353569
else:
35363570
self.ovn_nbdb_api.lb_add_ip_port_mapping(ovn_lb.uuid,
35373571
backend_ip,
@@ -3620,8 +3654,11 @@ def _update_hm_member(self, ovn_lb, pool_key, backend_ip, delete=False):
36203654
'member': mb_ip,
36213655
'pool': pool_key})
36223656
return None
3623-
self._update_ip_port_mappings(ovn_lb, backend_ip,
3624-
member_lsp.name, hm_source_ip,
3657+
self._update_ip_port_mappings(ovn_lb,
3658+
backend_ip,
3659+
member_lsp.name,
3660+
hm_source_ip,
3661+
pool_key,
36253662
delete)
36263663
return constants.ONLINE
36273664

ovn_octavia_provider/tests/unit/test_helper.py

Lines changed: 108 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -336,15 +336,23 @@ def test__clean_ip_port_mappings_two_hm_pools_not_sharing_members(self):
336336
def test__update_ip_port_mappings_del_backend_member(self):
337337
src_ip = '10.22.33.4'
338338
self.helper._update_ip_port_mappings(
339-
self.ovn_lb, self.member_address, 'a-logical-port', src_ip,
339+
self.ovn_lb,
340+
self.member_address,
341+
'a-logical-port',
342+
src_ip,
343+
'test_pool_key',
340344
delete=True)
341345
self.helper.ovn_nbdb_api.lb_del_ip_port_mapping.\
342346
assert_called_once_with(self.ovn_lb.uuid, self.member_address)
343347

344348
def test__update_ip_port_mappings_add_backend_member(self):
345349
src_ip = '10.22.33.4'
346350
self.helper._update_ip_port_mappings(
347-
self.ovn_lb, self.member_address, 'a-logical-port', src_ip)
351+
self.ovn_lb,
352+
self.member_address,
353+
'a-logical-port',
354+
src_ip,
355+
'test_pool_key')
348356
self.helper.ovn_nbdb_api.lb_add_ip_port_mapping.\
349357
assert_called_once_with(self.ovn_lb.uuid, self.member_address,
350358
'a-logical-port', src_ip)
@@ -353,7 +361,11 @@ def test__update_ip_port_mappings_del_backend_member_ipv6(self):
353361
member_address = 'fda2:918e:5869:0:f816:3eff:feab:cdef'
354362
src_ip = 'fda2:918e:5869:0:f816:3eff:fecd:398a'
355363
self.helper._update_ip_port_mappings(
356-
self.ovn_lb, member_address, 'a-logical-port', src_ip,
364+
self.ovn_lb,
365+
member_address,
366+
'a-logical-port',
367+
src_ip,
368+
'test_pool_key',
357369
delete=True)
358370
self.helper.ovn_nbdb_api.lb_del_ip_port_mapping.\
359371
assert_called_once_with(self.ovn_lb.uuid, member_address)
@@ -362,10 +374,17 @@ def test__update_ip_port_mappings_add_backend_member_ipv6(self):
362374
member_address = 'fda2:918e:5869:0:f816:3eff:feab:cdef'
363375
src_ip = 'fda2:918e:5869:0:f816:3eff:fecd:398a'
364376
self.helper._update_ip_port_mappings(
365-
self.ovn_lb, member_address, 'a-logical-port', src_ip)
377+
self.ovn_lb,
378+
member_address,
379+
'a-logical-port',
380+
src_ip,
381+
'test_pool_key')
366382
self.helper.ovn_nbdb_api.lb_add_ip_port_mapping.\
367383
assert_called_once_with(
368-
self.ovn_lb.uuid, member_address, 'a-logical-port', src_ip)
384+
self.ovn_lb.uuid,
385+
member_address,
386+
'a-logical-port',
387+
src_ip)
369388

370389
def test__update_external_ids_member_status(self):
371390
self.helper._update_external_ids_member_status(
@@ -374,7 +393,10 @@ def test__update_external_ids_member_status(self):
374393
ovn_const.OVN_MEMBER_STATUS_KEY: '{"%s": "%s"}'
375394
% (self.member_id, constants.NO_MONITOR)}
376395
self.helper.ovn_nbdb_api.db_set.assert_called_once_with(
377-
'Load_Balancer', self.ovn_lb.uuid, ('external_ids', member_status))
396+
'Load_Balancer',
397+
self.ovn_lb.uuid,
398+
('external_ids',
399+
member_status))
378400

379401
def test__update_external_ids_member_status_delete(self):
380402
self.helper._update_external_ids_member_status(
@@ -390,7 +412,10 @@ def test__update_external_ids_member_status_delete_not_found(self):
390412
ovn_const.OVN_MEMBER_STATUS_KEY: '{"%s": "%s"}'
391413
% (self.member_id, constants.NO_MONITOR)}
392414
self.helper.ovn_nbdb_api.db_set.assert_called_once_with(
393-
'Load_Balancer', self.ovn_lb.uuid, ('external_ids', member_status))
415+
'Load_Balancer',
416+
self.ovn_lb.uuid,
417+
('external_ids',
418+
member_status))
394419

395420
def test__find_member_status(self):
396421
status = self.helper._find_member_status(self.ovn_lb, self.member_id)
@@ -1506,29 +1531,27 @@ def test_lb_update_disabled(self, refresh_vips):
15061531
@mock.patch.object(ovn_helper.OvnProviderHelper, '_refresh_lb_vips')
15071532
def test_lb_update_enabled(self, refresh_vips):
15081533
# Change the mock, its enabled by default.
1509-
self.ovn_lb.external_ids.update({'enabled': False})
1510-
self.lb['admin_state_up'] = True
1534+
self.lb[constants.ADMIN_STATE_UP] = False
15111535
status = self.helper.lb_update(self.lb)
15121536
self.assertEqual(status['loadbalancers'][0]['provisioning_status'],
15131537
constants.ACTIVE)
15141538
self.assertEqual(status['loadbalancers'][0]['operating_status'],
1515-
constants.ONLINE)
1539+
constants.OFFLINE)
15161540
refresh_vips.assert_called_once_with(
15171541
self.ovn_lb, self.ovn_lb.external_ids)
15181542
self.helper.ovn_nbdb_api.db_set.assert_called_once_with(
15191543
'Load_Balancer', self.ovn_lb.uuid,
1520-
('external_ids', {'enabled': 'True'}))
1544+
('external_ids', {'enabled': 'False'}))
15211545
# update to re-enable
1522-
self.ovn_lb.external_ids.update({'enabled': True})
1523-
self.lb['admin_state_up'] = True
1546+
self.lb[constants.ADMIN_STATE_UP] = True
15241547
status = self.helper.lb_update(self.lb)
15251548
self.assertEqual(status['loadbalancers'][0]['provisioning_status'],
15261549
constants.ACTIVE)
15271550
self.assertEqual(status['loadbalancers'][0]['operating_status'],
15281551
constants.ONLINE)
1529-
refresh_vips.assert_called_once_with(
1552+
refresh_vips.assert_called_with(
15301553
self.ovn_lb, self.ovn_lb.external_ids)
1531-
self.helper.ovn_nbdb_api.db_set.assert_called_once_with(
1554+
self.helper.ovn_nbdb_api.db_set.assert_called_with(
15321555
'Load_Balancer', self.ovn_lb.uuid,
15331556
('external_ids', {'enabled': 'True'}))
15341557

@@ -6746,3 +6769,73 @@ def test_refresh_lb_vips_returns_db_operations_when_is_sync_false(
67466769
self.ovn_lb.uuid,
67476770
('vips', {'vip1:port1': 'ip1:port1,ip2:port1'})
67486771
)
6772+
6773+
def test_update_ip_port_mappings_add(self):
6774+
# Setup mock OVN load balancer
6775+
ovn_lb = mock.Mock()
6776+
ovn_lb.uuid = 'test-lb-uuid'
6777+
ovn_lb.external_ids = {}
6778+
6779+
# Call the method with delete=False
6780+
self.helper._update_ip_port_mappings(
6781+
ovn_lb, '10.0.0.1', 'port1', '192.168.0.1', 'pool1', delete=False
6782+
)
6783+
6784+
# Assert that lb_add_ip_port_mapping was called
6785+
self.helper.ovn_nbdb_api.lb_add_ip_port_mapping\
6786+
.assert_called_once_with(
6787+
'test-lb-uuid',
6788+
'10.0.0.1',
6789+
'port1',
6790+
'192.168.0.1',
6791+
)
6792+
6793+
def test_update_ip_port_mappings_delete_minimal(self):
6794+
ovn_lb = mock.Mock()
6795+
ovn_lb.uuid = 'test-lb-uuid'
6796+
ovn_lb.external_ids = {}
6797+
# Patch _extract_member_info to return no other members
6798+
self.helper._extract_member_info = mock.Mock(return_value=[])
6799+
# Also patch ovn_nbdb_api call
6800+
self.helper.ovn_nbdb_api.lb_del_ip_port_mapping = mock.Mock()
6801+
self.helper.ovn_nbdb_api.lb_add_ip_port_mapping = mock.Mock()
6802+
self.helper._update_ip_port_mappings(
6803+
ovn_lb,
6804+
backend_ip='10.0.0.1',
6805+
port_name='dummy-port',
6806+
src_ip='192.168.0.1',
6807+
pool_key='pool-test',
6808+
delete=True
6809+
)
6810+
self.helper.ovn_nbdb_api.\
6811+
lb_del_ip_port_mapping.\
6812+
assert_called_once_with(
6813+
'test-lb-uuid',
6814+
'10.0.0.1'
6815+
)
6816+
6817+
def test_update_ip_port_mappings_delete_with_other_members_present(self):
6818+
ovn_lb = mock.Mock()
6819+
ovn_lb.uuid = 'test-lb-uuid'
6820+
ovn_lb.external_ids = {
6821+
"pool_A": "member_memberA_10.0.0.1:80_subnetA",
6822+
"pool_B": "member_memberB_10.0.0.1:80_subnetA",
6823+
"neutron:member_statuses": '{"memberB": "ONLINE"}'
6824+
}
6825+
6826+
self.helper.ovn_nbdb_api.lb_del_ip_port_mapping = mock.Mock()
6827+
self.helper.ovn_nbdb_api.lb_add_ip_port_mapping = mock.Mock()
6828+
6829+
# Call the method under test
6830+
self.helper._update_ip_port_mappings(
6831+
ovn_lb,
6832+
backend_ip='10.0.0.1',
6833+
port_name='dummy-port',
6834+
src_ip='192.168.0.1',
6835+
pool_key='pool_A',
6836+
delete=True
6837+
)
6838+
6839+
# Should not call delete because memberB is ONLINE and shares the IP
6840+
self.helper.ovn_nbdb_api.lb_del_ip_port_mapping.assert_not_called()
6841+
self.helper.ovn_nbdb_api.lb_add_ip_port_mapping.assert_not_called()
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
fixes:
3+
- |
4+
Enabling and disabling Load Balancers with the OVN provider now works
5+
properly, assigning or removing VIP definitions in the OVN Northbound
6+
database according to the issued command.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
upgrade:
3+
- |
4+
Support for Python 3.9 has been removed. Now Python 3.10 is the minimum
5+
version supported.

requirements.txt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,6 @@
22
# date but we do not test them so no guarantee of having them all correct. If
33
# you find any incorrect lower bounds, let us know or propose a fix.
44

5-
# The order of packages is significant, because pip processes them in the order
6-
# of appearance. Changing the order has an impact on the overall integration
7-
# process, which may cause wedges in the gate later.
8-
95
keystoneauth1>=3.14.0 # Apache-2.0
106
netaddr>=0.7.18 # BSD
117
neutron-lib>=3.8.0 # Apache-2.0

setup.cfg

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ description_file =
66
author = OpenStack
77
author_email = [email protected]
88
home_page = https://docs.openstack.org/ovn-octavia-provider/latest/
9-
python_requires = >=3.9
9+
python_requires = >=3.10
1010
classifier =
1111
Environment :: OpenStack
1212
Intended Audience :: Information Technology
@@ -15,7 +15,6 @@ classifier =
1515
Operating System :: POSIX :: Linux
1616
Programming Language :: Python
1717
Programming Language :: Python :: 3
18-
Programming Language :: Python :: 3.9
1918
Programming Language :: Python :: 3.10
2019
Programming Language :: Python :: 3.11
2120
Programming Language :: Python :: 3.12

test-requirements.txt

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
1-
# The order of packages is significant, because pip processes them in the order
2-
# of appearance. Changing the order has an impact on the overall integration
3-
# process, which may cause wedges in the gate later.
4-
51
hacking>=6.1.0,<6.2.0 # Apache-2.0
62

73
bandit!=1.6.0,>=1.1.0 # Apache-2.0
84
coverage!=4.4,>=4.0 # Apache-2.0
95
flake8-import-order>=0.18.0,<0.19.0 # LGPLv3
10-
python-subunit>=1.0.0 # Apache-2.0/BSD
116
oslotest>=3.2.0 # Apache-2.0
127
stestr>=1.0.0 # Apache-2.0
138
pylint>=2.6.0 # GPLv2

tox.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ commands =
7171
stestr run --no-subunit-trace {posargs}
7272
coverage combine
7373
coverage html -d cover
74-
coverage xml -o cover/coverage.x
74+
coverage xml -o cover/coverage.xml
7575
coverage report --fail-under=92 --skip-covered
7676

7777
[testenv:docs]

zuul.d/base.yaml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,13 @@
7575
vars:
7676
devstack_localrc:
7777
Q_AGENT: ovn
78+
OVN_AGENT_EXTENSIONS: 'metadata'
7879
# NOTE(ralonsoh): during the eventlet removal, the "logger" mech
7980
# driver has been removed from this list. Re-add it once the removal
8081
# is finished or the mech driver does not call monkey_patch().
8182
Q_ML2_PLUGIN_MECHANISM_DRIVERS: ovn
8283
Q_ML2_PLUGIN_TYPE_DRIVERS: local,flat,vlan,geneve
8384
Q_ML2_TENANT_NETWORK_TYPE: geneve
84-
USE_PYTHON3: True
8585
TEMPEST_PLUGINS: '/opt/stack/octavia-tempest-plugin'
8686
OCTAVIA_NODE: api
8787
OCTAVIA_TEMPEST_PLUGIN_CUSTOMIZE_IMAGE: true
@@ -128,7 +128,8 @@
128128
q-l3: false
129129
ovn-northd: true
130130
ovn-controller: true
131-
q-ovn-metadata-agent: true
131+
q-ovn-metadata-agent: false
132+
q-ovn-agent: true
132133
octavia: true
133134
o-api: true
134135
o-da: true

0 commit comments

Comments
 (0)