Filter ports in list_ports when batching is in effect

When caching is in effect, we always fetch ports in a raw list with no
push-down filters. However, people calling list_ports with the filters
argument should expect it to work whether push-down conditions are used
or not. Wrap the return in a filter list so that we apply the filters
client-side in that case.

Story: 2004207
Task: 27716
Change-Id: Ieabb3193c320bd9c0e569e2e7cef983529a6fbdb
This commit is contained in:
Monty Taylor 2018-10-30 08:39:47 -05:00
parent 66c94fd083
commit 965775622d
No known key found for this signature in database
GPG Key ID: 7BAE94BC7141A594
4 changed files with 38 additions and 4 deletions

View File

@ -1702,8 +1702,6 @@ class _OpenStackCloudMixin(_normalize.Normalizer):
if filters and self._PORT_AGE == 0:
return self._list_ports(filters)
# Translate None from search interface to empty {} for kwargs below
filters = {}
if (time.time() - self._ports_time) >= self._PORT_AGE:
# Since we're using cached data anyway, we don't need to
# have more than one thread actually submit the list
@ -1716,11 +1714,14 @@ class _OpenStackCloudMixin(_normalize.Normalizer):
if self._ports_lock.acquire(first_run):
try:
if not (first_run and self._ports is not None):
self._ports = self._list_ports(filters)
self._ports = self._list_ports({})
self._ports_time = time.time()
finally:
self._ports_lock.release()
return self._ports
# Wrap the return with filter_list so that if filters were passed
# but we were batching/caching and thus always fetching the whole
# list from the cloud, we still return a filtered list.
return _utils._filter_list(self._ports, None, filters or {})
def _list_ports(self, filters):
resp = self.network.get("/ports.json", params=filters)

View File

@ -21,6 +21,7 @@ from openstack.cloud import meta
from openstack import exceptions
from openstack.tests import fakes
from openstack.tests.unit import base
from openstack.tests.unit.cloud import test_port
# Mock out the gettext function so that the task schema can be copypasta
@ -534,6 +535,25 @@ class TestMemoryCache(base.TestCase):
],
self.cloud.list_images())
def test_list_ports_filtered(self):
down_port = test_port.TestPort.mock_neutron_port_create_rep['port']
active_port = down_port.copy()
active_port['status'] = 'ACTIVE'
# We're testing to make sure a query string isn't passed when we're
# caching, but that the results are still filtered.
self.register_uris([
dict(method='GET',
uri=self.get_mock_url(
'network', 'public', append=['v2.0', 'ports.json']),
json={'ports': [
down_port,
active_port,
]}),
])
ports = self.cloud.list_ports(filters={'status': 'DOWN'})
self.assertItemsEqual([down_port], ports)
self.assert_calls()
class TestCacheIgnoresQueuedStatus(base.TestCase):

View File

@ -241,6 +241,18 @@ class TestPort(base.TestCase):
self.assertItemsEqual(self.mock_neutron_port_list_rep['ports'], ports)
self.assert_calls()
def test_list_ports_filtered(self):
self.register_uris([
dict(method='GET',
uri=self.get_mock_url(
'network', 'public', append=['v2.0', 'ports.json'],
qs_elements=['status=DOWN']),
json=self.mock_neutron_port_list_rep)
])
ports = self.cloud.list_ports(filters={'status': 'DOWN'})
self.assertItemsEqual(self.mock_neutron_port_list_rep['ports'], ports)
self.assert_calls()
def test_list_ports_exception(self):
self.register_uris([
dict(method='GET',

View File

@ -3,6 +3,7 @@ cache:
class: dogpile.cache.memory
expiration:
server: 1
port: 1
clouds:
_test_cloud_:
auth: