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 d7b090702523cb07ae4aed0338f9e80fe30791a6)
This commit is contained in:
parent
ab7712ede5
commit
b61c340b09
@ -31,6 +31,7 @@ from octavia.common import data_models
|
|||||||
from octavia.common import exceptions
|
from octavia.common import exceptions
|
||||||
from octavia.common import validate
|
from octavia.common import validate
|
||||||
from octavia.db import prepare as db_prepare
|
from octavia.db import prepare as db_prepare
|
||||||
|
from octavia.i18n import _
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@ -365,12 +366,21 @@ class MembersController(MemberController):
|
|||||||
# Find members that are brand new or updated
|
# Find members that are brand new or updated
|
||||||
new_members = []
|
new_members = []
|
||||||
updated_members = []
|
updated_members = []
|
||||||
|
updated_member_uniques = set()
|
||||||
for m in members:
|
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)
|
validate.ip_not_reserved(m.address)
|
||||||
new_members.append(m)
|
new_members.append(m)
|
||||||
else:
|
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)
|
updated_members.append(m)
|
||||||
|
|
||||||
# Find members that are deleted
|
# Find members that are deleted
|
||||||
|
@ -913,6 +913,38 @@ class TestMember(base.BaseAPITest):
|
|||||||
m_subnet_exists.assert_called_once_with(
|
m_subnet_exists.assert_called_once_with(
|
||||||
member1['subnet_id'], context=mock.ANY)
|
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.driver_factory.get_driver')
|
||||||
@mock.patch('octavia.api.drivers.utils.call_provider')
|
@mock.patch('octavia.api.drivers.utils.call_provider')
|
||||||
def test_update_members_subnet_not_found(
|
def test_update_members_subnet_not_found(
|
||||||
|
@ -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.
|
Loading…
x
Reference in New Issue
Block a user