diff --git a/neutron_lib/api/validators/__init__.py b/neutron_lib/api/validators/__init__.py index f1856af6f..83bde1e2c 100644 --- a/neutron_lib/api/validators/__init__.py +++ b/neutron_lib/api/validators/__init__.py @@ -78,14 +78,14 @@ def _collect_duplicates(data_list): :param data_list: A list of items to check for duplicates. The list may include dict items. - :returns: A set of items that are duplicates in data_list. If no - duplicates are found, the returned set is empty. + :returns: A list of items that are duplicates in data_list. If no + duplicates are found, the returned list is empty. """ seen = [] - dups = set() + dups = [] for datum in data_list: - if datum in seen: - dups.add(datum) + if datum in seen and datum not in dups: + dups.append(datum) continue seen.append(datum) return dups diff --git a/neutron_lib/tests/unit/api/validators/test_validators.py b/neutron_lib/tests/unit/api/validators/test_validators.py index 5da007edb..0a0d0aba7 100644 --- a/neutron_lib/tests/unit/api/validators/test_validators.py +++ b/neutron_lib/tests/unit/api/validators/test_validators.py @@ -971,6 +971,24 @@ class TestAttributeValidation(base.BaseTestCase): msg = validators._validate_list_of_items_non_empty(mock.Mock(), items) self.assertEqual("List should not be empty", msg) + def test__validate_list_of_items_collect_duplicates(self): + items = ['a', 'b', 'duplicate_1', 'duplicate_2', 'duplicate_1', + 'duplicate_2', 'duplicate_2', 'c'] + msg = validators._validate_list_of_items(mock.Mock(), items) + error = ("Duplicate items in the list: '%s'" + % 'duplicate_1, duplicate_2') + self.assertEqual(error, msg) + + items = [['a', 'b'], ['c', 'd'], ['a', 'b']] + msg = validators._validate_list_of_items(mock.Mock(), items) + error = "Duplicate items in the list: '%s'" % ['a', 'b'] + self.assertEqual(error, msg) + + items = [{'a': 'b'}, {'c': 'd'}, {'a': 'b'}] + msg = validators._validate_list_of_items(mock.Mock(), items) + error = "Duplicate items in the list: '%s'" % {'a': 'b'} + self.assertEqual(error, msg) + def test_validate_dict_type(self): for value in (None, True, '1', []): self.assertEqual("'%s' is not a dictionary" % value, diff --git a/releasenotes/notes/fix-api-list-validation-collect-duplicates-f4d45bf5d5abbdff.yaml b/releasenotes/notes/fix-api-list-validation-collect-duplicates-f4d45bf5d5abbdff.yaml new file mode 100644 index 000000000..b33abede4 --- /dev/null +++ b/releasenotes/notes/fix-api-list-validation-collect-duplicates-f4d45bf5d5abbdff.yaml @@ -0,0 +1,8 @@ +--- +fixes: + - | + Fixed an issue where API validation for duplicate entries in list values + would fail with a TypeError in case the values in the list was of type + ``dict``. + See bug: `1956785 `_. +