Cache neutron extension list across requests
Also improves the warning message of UnhashableWarning and test_neutron UnhashableWarning. Closes-Bug: #1747204 Change-Id: I876e3219dac570e8f7b10c5c126df74ca73f5197
This commit is contained in:
parent
33068ecca4
commit
b068d91c60
@ -99,8 +99,9 @@ def memoized(func):
|
|||||||
# that case, we can't cache anything and simply always call the
|
# that case, we can't cache anything and simply always call the
|
||||||
# decorated function.
|
# decorated function.
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
"The key %r is not hashable and cannot be memoized."
|
"The key of %s %s is not hashable and cannot be memoized: %r\n"
|
||||||
% (key,), UnhashableKeyWarning, 2)
|
% (func.__module__, func.__name__, key),
|
||||||
|
UnhashableKeyWarning, 2)
|
||||||
value = func(*args, **kwargs)
|
value = func(*args, **kwargs)
|
||||||
return value
|
return value
|
||||||
return wrapped
|
return wrapped
|
||||||
|
@ -35,6 +35,7 @@ import six
|
|||||||
from horizon import exceptions
|
from horizon import exceptions
|
||||||
from horizon import messages
|
from horizon import messages
|
||||||
from horizon.utils.memoized import memoized
|
from horizon.utils.memoized import memoized
|
||||||
|
from horizon.utils.memoized import memoized_with_request
|
||||||
from openstack_dashboard.api import base
|
from openstack_dashboard.api import base
|
||||||
from openstack_dashboard.api import nova
|
from openstack_dashboard.api import nova
|
||||||
from openstack_dashboard.contrib.developer.profiler import api as profiler
|
from openstack_dashboard.contrib.developer.profiler import api as profiler
|
||||||
@ -760,13 +761,22 @@ def get_ipver_str(ip_version):
|
|||||||
return IP_VERSION_DICT.get(ip_version, '')
|
return IP_VERSION_DICT.get(ip_version, '')
|
||||||
|
|
||||||
|
|
||||||
@memoized
|
def get_auth_params_from_request(request):
|
||||||
def neutronclient(request):
|
return (
|
||||||
|
request.user.token.id,
|
||||||
|
base.url_for(request, 'network'),
|
||||||
|
base.url_for(request, 'identity')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@memoized_with_request(get_auth_params_from_request)
|
||||||
|
def neutronclient(request_auth_params):
|
||||||
|
token_id, neutron_url, auth_url = request_auth_params
|
||||||
insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False)
|
insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False)
|
||||||
cacert = getattr(settings, 'OPENSTACK_SSL_CACERT', None)
|
cacert = getattr(settings, 'OPENSTACK_SSL_CACERT', None)
|
||||||
c = neutron_client.Client(token=request.user.token.id,
|
c = neutron_client.Client(token=token_id,
|
||||||
auth_url=base.url_for(request, 'identity'),
|
auth_url=auth_url,
|
||||||
endpoint_url=base.url_for(request, 'network'),
|
endpoint_url=neutron_url,
|
||||||
insecure=insecure, ca_cert=cacert)
|
insecure=insecure, ca_cert=cacert)
|
||||||
return c
|
return c
|
||||||
|
|
||||||
@ -1704,10 +1714,14 @@ def _server_get_addresses(request, server, ports, floating_ips, network_names):
|
|||||||
|
|
||||||
|
|
||||||
@profiler.trace
|
@profiler.trace
|
||||||
@memoized
|
@memoized_with_request(neutronclient)
|
||||||
def list_extensions(request):
|
def list_extensions(neutron_api):
|
||||||
|
"""List neutron extensions.
|
||||||
|
|
||||||
|
:param request: django request object
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
extensions_list = neutronclient(request).list_extensions()
|
extensions_list = neutron_api.list_extensions()
|
||||||
except exceptions.ServiceCatalogException:
|
except exceptions.ServiceCatalogException:
|
||||||
return {}
|
return {}
|
||||||
if 'extensions' in extensions_list:
|
if 'extensions' in extensions_list:
|
||||||
@ -1717,10 +1731,13 @@ def list_extensions(request):
|
|||||||
|
|
||||||
|
|
||||||
@profiler.trace
|
@profiler.trace
|
||||||
@memoized
|
|
||||||
def is_extension_supported(request, extension_alias):
|
def is_extension_supported(request, extension_alias):
|
||||||
extensions = list_extensions(request)
|
"""Check if a specified extension is supported.
|
||||||
|
|
||||||
|
:param request: django request object
|
||||||
|
:param extension_alias: neutron extension alias
|
||||||
|
"""
|
||||||
|
extensions = list_extensions(request)
|
||||||
for extension in extensions:
|
for extension in extensions:
|
||||||
if extension['alias'] == extension_alias:
|
if extension['alias'] == extension_alias:
|
||||||
return True
|
return True
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
import copy
|
import copy
|
||||||
|
|
||||||
|
import mock
|
||||||
from mox3.mox import IsA
|
from mox3.mox import IsA
|
||||||
import netaddr
|
import netaddr
|
||||||
from neutronclient.common import exceptions as neutron_exc
|
from neutronclient.common import exceptions as neutron_exc
|
||||||
@ -393,13 +394,16 @@ class NeutronApiTests(test.APITestCase):
|
|||||||
for p in ret_val:
|
for p in ret_val:
|
||||||
self.assertIsInstance(p, api.neutron.Port)
|
self.assertIsInstance(p, api.neutron.Port)
|
||||||
|
|
||||||
def test_port_list_with_trunk_types(self):
|
@mock.patch.object(api.neutron, 'is_extension_supported')
|
||||||
|
def test_port_list_with_trunk_types(self, mock_is_extension_supported):
|
||||||
ports = self.api_tp_ports.list()
|
ports = self.api_tp_ports.list()
|
||||||
trunks = self.api_tp_trunks.list()
|
trunks = self.api_tp_trunks.list()
|
||||||
|
|
||||||
|
# list_extensions is decorated with memoized_with_request,
|
||||||
|
# stub_neutronclient is not called. We need to mock it separately.
|
||||||
|
mock_is_extension_supported.return_value = True # trunk
|
||||||
|
|
||||||
neutronclient = self.stub_neutronclient()
|
neutronclient = self.stub_neutronclient()
|
||||||
neutronclient.list_extensions() \
|
|
||||||
.AndReturn({'extensions': self.api_extensions.list()})
|
|
||||||
neutronclient.list_ports().AndReturn({'ports': ports})
|
neutronclient.list_ports().AndReturn({'ports': ports})
|
||||||
neutronclient.list_trunks().AndReturn({'trunks': trunks})
|
neutronclient.list_trunks().AndReturn({'trunks': trunks})
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
@ -428,13 +432,19 @@ class NeutronApiTests(test.APITestCase):
|
|||||||
self.assertEqual(expected_subport_ids, subport_ids)
|
self.assertEqual(expected_subport_ids, subport_ids)
|
||||||
self.assertEqual(expected_normal_port_ids, normal_port_ids)
|
self.assertEqual(expected_normal_port_ids, normal_port_ids)
|
||||||
|
|
||||||
def test_port_list_with_trunk_types_without_trunk_extension(self):
|
mock_is_extension_supported.assert_called_once_with(
|
||||||
extensions = [ext for ext in self.api_extensions.list()
|
test.IsHttpRequest(), 'trunk')
|
||||||
if ext['alias'] != 'trunk']
|
|
||||||
|
@mock.patch.object(api.neutron, 'is_extension_supported')
|
||||||
|
def test_port_list_with_trunk_types_without_trunk_extension(
|
||||||
|
self, mock_is_extension_supported):
|
||||||
ports = self.api_tp_ports.list()
|
ports = self.api_tp_ports.list()
|
||||||
|
|
||||||
|
# list_extensions is decorated with memoized_with_request,
|
||||||
|
# the simpliest way is to mock it directly.
|
||||||
|
mock_is_extension_supported.return_value = False # trunk
|
||||||
|
|
||||||
neutronclient = self.stub_neutronclient()
|
neutronclient = self.stub_neutronclient()
|
||||||
neutronclient.list_extensions().AndReturn({'extensions': extensions})
|
|
||||||
neutronclient.list_ports().AndReturn({'ports': ports})
|
neutronclient.list_ports().AndReturn({'ports': ports})
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
@ -447,6 +457,9 @@ class NeutronApiTests(test.APITestCase):
|
|||||||
# instances of Port class.
|
# instances of Port class.
|
||||||
self.assertTrue(all(isinstance(p, api.neutron.Port) for p in ret_val))
|
self.assertTrue(all(isinstance(p, api.neutron.Port) for p in ret_val))
|
||||||
|
|
||||||
|
mock_is_extension_supported.assert_called_once_with(
|
||||||
|
test.IsHttpRequest(), 'trunk')
|
||||||
|
|
||||||
def test_port_get(self):
|
def test_port_get(self):
|
||||||
port = {'port': self.api_ports.first()}
|
port = {'port': self.api_ports.first()}
|
||||||
port_id = self.api_ports.first()['id']
|
port_id = self.api_ports.first()['id']
|
||||||
@ -715,12 +728,13 @@ class NeutronApiTests(test.APITestCase):
|
|||||||
api.neutron.router_remove_interface(
|
api.neutron.router_remove_interface(
|
||||||
self.request, router_id, port_id=fake_port)
|
self.request, router_id, port_id=fake_port)
|
||||||
|
|
||||||
def test_is_extension_supported(self):
|
# stub_neutronclient does not work because api.neutron.list_extensions
|
||||||
neutronclient = self.stub_neutronclient()
|
# is decorated with memoized_with_request, so we need to mock
|
||||||
neutronclient.list_extensions() \
|
# neutronclient.v2_0.client directly.
|
||||||
.AndReturn({'extensions': self.api_extensions.list()})
|
@mock.patch('neutronclient.v2_0.client.Client.list_extensions')
|
||||||
self.mox.ReplayAll()
|
def test_is_extension_supported(self, mock_list_extensions):
|
||||||
|
extensions = self.api_extensions.list()
|
||||||
|
mock_list_extensions.return_value = {'extensions': extensions}
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
api.neutron.is_extension_supported(self.request, 'quotas'))
|
api.neutron.is_extension_supported(self.request, 'quotas'))
|
||||||
self.assertFalse(
|
self.assertFalse(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user