Add check for duplicate members in batch update

If a user requests a batch update of the members and includes the same
member twice in the body of the request, taskflow triggers an exception
(because of a duplicate Atom) and the load balancer is stuck in
PENDING_UPDATE. Now the API denies such calls and returns a
ValidationException if a member is updated more than once in the same
call.

Story 2010399
Task 46777

Change-Id: Ide2d5e637e5feb2dad43f952d5be1a195da1ae37
(cherry picked from commit d7b0907025)
(cherry picked from commit b61c340b09)
This commit is contained in:
Gregory Thiemonge 2022-11-10 10:13:46 +01:00
parent 8832cefaa0
commit 70ecbcc8be
3 changed files with 54 additions and 2 deletions

View File

@ -31,6 +31,7 @@ from octavia.common import data_models
from octavia.common import exceptions
from octavia.common import validate
from octavia.db import prepare as db_prepare
from octavia.i18n import _
LOG = logging.getLogger(__name__)
@ -366,12 +367,21 @@ class MembersController(MemberController):
# Find members that are brand new or updated
new_members = []
updated_members = []
updated_member_uniques = set()
for m in members:
if (m.address, m.protocol_port) not in old_member_uniques:
key = (m.address, m.protocol_port)
if key not in old_member_uniques:
validate.ip_not_reserved(m.address)
new_members.append(m)
else:
m.id = old_member_uniques[(m.address, m.protocol_port)]
m.id = old_member_uniques[key]
if key in updated_member_uniques:
LOG.error("Member %s is updated multiple times in "
"the same batch request.", m.id)
raise exceptions.ValidationException(
detail=_("Member must be updated only once in the "
"same request."))
updated_member_uniques.add(key)
updated_members.append(m)
# Find members that are deleted

View File

@ -913,6 +913,38 @@ class TestMember(base.BaseAPITest):
m_subnet_exists.assert_called_once_with(
member1['subnet_id'], context=mock.ANY)
@mock.patch('octavia.api.drivers.driver_factory.get_driver')
@mock.patch('octavia.api.drivers.utils.call_provider')
def test_update_members_member_duplicate(
self, mock_provider, mock_get_driver):
mock_driver = mock.MagicMock()
mock_driver.name = 'noop_driver'
mock_get_driver.return_value = mock_driver
subnet_id = uuidutils.generate_uuid()
member1 = {'address': '192.0.2.1', 'protocol_port': 80,
'project_id': self.project_id, 'subnet_id': subnet_id}
req_dict = [member1]
body = {self.root_tag_list: req_dict}
path = self.MEMBERS_PATH.format(pool_id=self.pool_id)
self.put(path, body, status=202)
self.set_lb_status(self.lb_id)
# Same member (same address and protocol_port) updated twice in the
# same PUT request
member1 = {'address': '192.0.2.1', 'protocol_port': 80,
'project_id': self.project_id, 'subnet_id': subnet_id,
'name': 'member1'}
member2 = {'address': '192.0.2.1', 'protocol_port': 80,
'project_id': self.project_id, 'subnet_id': subnet_id,
'name': 'member2'}
req_dict = [member1, member2]
body = {self.root_tag_list: req_dict}
self.put(path, body, status=400)
@mock.patch('octavia.api.drivers.driver_factory.get_driver')
@mock.patch('octavia.api.drivers.utils.call_provider')
def test_update_members_subnet_not_found(

View File

@ -0,0 +1,10 @@
---
fixes:
- |
Added a validation step in the batch member API request that checks if a
member is included multiple times in the list of updated members, this
additional check prevents the load balancer from being stuck in
PENDING_UPDATE. Duplicate members in the batch member flow triggered an
exception in Taskflow.
The API now returns 400 (ValidationException) if a member is already
present in the body of the request.