Skip to content

Commit 57d2bca

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "Remove duplicate rows in MySQL query output" into stable/2023.1
2 parents 94cf7a4 + f96691b commit 57d2bca

File tree

3 files changed

+52
-36
lines changed

3 files changed

+52
-36
lines changed

neutron/objects/router.py

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010
# License for the specific language governing permissions and limitations
1111
# under the License.
1212

13-
import itertools
14-
1513
import netaddr
1614

1715
from neutron_lib.api.definitions import availability_zone as az_def
@@ -409,20 +407,11 @@ def get_scoped_floating_ips(cls, context, router_ids):
409407

410408
# Filter out on router_ids
411409
query = query.filter(l3.FloatingIP.router_id.in_(router_ids))
412-
return cls._unique_floatingip_iterator(context, query)
413410

414-
@classmethod
415-
def _unique_floatingip_iterator(cls, context, query):
416-
"""Iterates over only one row per floating ip. Ignores others."""
417-
# Group rows by fip id. They must be sorted by same.
418-
q = query.order_by(l3.FloatingIP.id)
419-
keyfunc = lambda row: row[0]['id']
420-
group_iterator = itertools.groupby(q, keyfunc)
421-
422-
# Just hit the first row of each group
423-
for key, value in group_iterator:
424-
# pylint: disable=stop-iteration-return
425-
row = list(next(value))
411+
# Remove duplicate rows based on FIP IDs
412+
query = query.group_by(l3.FloatingIP.id)
413+
414+
for row in query:
426415
yield (cls._load_object(context, row[0]), row[1])
427416

428417
@classmethod

neutron/tests/unit/db/test_l3_db.py

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -252,27 +252,6 @@ def test__make_floatingip_dict_with_scope(self, make_fip_dict):
252252
'fixed_ip_address_scope': mock.sentinel.address_scope_id,
253253
'id': mock.sentinel.fip_ip}, result)
254254

255-
def test__unique_floatingip_iterator(self):
256-
context = mock.MagicMock()
257-
query = mock.MagicMock()
258-
query.order_by().__iter__.return_value = [
259-
({'id': 'id1'}, 'scope1'),
260-
({'id': 'id1'}, 'scope1'),
261-
({'id': 'id2'}, 'scope2'),
262-
({'id': 'id2'}, 'scope2'),
263-
({'id': 'id2'}, 'scope2'),
264-
({'id': 'id3'}, 'scope3')]
265-
query.reset_mock()
266-
with mock.patch.object(
267-
l3_obj.FloatingIP, '_load_object',
268-
side_effect=({'id': 'id1'}, {'id': 'id2'}, {'id': 'id3'})):
269-
result = list(
270-
l3_obj.FloatingIP._unique_floatingip_iterator(context, query))
271-
query.order_by.assert_called_once_with(l3_models.FloatingIP.id)
272-
self.assertEqual([({'id': 'id1'}, 'scope1'),
273-
({'id': 'id2'}, 'scope2'),
274-
({'id': 'id3'}, 'scope3')], result)
275-
276255
@mock.patch.object(directory, 'get_plugin')
277256
def test_prevent_l3_port_deletion_port_not_found(self, gp):
278257
# port not found doesn't prevent

neutron/tests/unit/objects/test_router.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,11 @@
1212
# License for the specific language governing permissions and limitations
1313
# under the License.
1414

15+
from itertools import chain
1516
from unittest import mock
1617

18+
import netaddr
19+
1720
from neutron_lib.db import api as db_api
1821
from oslo_utils import uuidutils
1922

@@ -293,6 +296,51 @@ def test_v1_2_to_v1_1_drops_qos_network_policy_id(self):
293296
self.assertNotIn('qos_network_policy_id',
294297
obj_v1_1['versioned_object.data'])
295298

299+
def test_get_scoped_floating_ips(self):
300+
def compare_results(router_ids, original_fips):
301+
self.assertCountEqual(
302+
original_fips,
303+
[
304+
fip[0].id
305+
for fip in router.FloatingIP.get_scoped_floating_ips(
306+
self.context, router_ids)
307+
]
308+
)
309+
310+
# Setup three routers, networks and external networks
311+
routers = {}
312+
for i in range(3):
313+
router_id = self._create_test_router_id(name=f'router-{i}')
314+
routers[router_id] = []
315+
net_id = self._create_test_network_id()
316+
fip_net_id = self._create_external_network_id()
317+
318+
# Create three subnets and three FIPs using the
319+
# aforementioned networks and routers
320+
for j in range(3):
321+
self._create_test_subnet_id(net_id)
322+
fip = router.FloatingIP(
323+
self.context,
324+
floating_ip_address=netaddr.IPAddress(f'10.{i}.{j}.3'),
325+
floating_network_id=fip_net_id,
326+
floating_port_id=self._create_test_port_id(
327+
network_id=fip_net_id),
328+
fixed_port_id=self._create_test_port_id(
329+
network_id=net_id),
330+
router_id=router_id,
331+
)
332+
fip.create()
333+
routers[router_id].append(fip.id)
334+
335+
# For each router we created, fetch the fips and ensure the
336+
# results match what we originally created
337+
for router_id, original_fips in routers.items():
338+
compare_results([router_id], original_fips)
339+
340+
# Now try to fetch all the fips for all the routers at once
341+
original_fips = list(chain.from_iterable(routers.values()))
342+
compare_results(routers.keys(), original_fips)
343+
296344

297345
class DvrFipGatewayPortAgentBindingTestCase(
298346
obj_test_base.BaseObjectIfaceTestCase):

0 commit comments

Comments
 (0)