From 5fe9408d2e53ac24a578d0ed568a4cede303fe58 Mon Sep 17 00:00:00 2001 From: Christian Berendt <berendt@b1-systems.de> Date: Wed, 17 Jul 2013 16:48:04 +0200 Subject: [PATCH] make findall in novaclient/base.py more efficient Use /resources instead of /resources/detail to resolve the resource ID by the name and load the details of the resource in a separate step. This reduces the overhead to resolve the resource ID and results in a better runtime performance. This patch does not solve the issue that the name resolving takes place on the client side. For solving this issue new Nova API methods are necessary. fixes bug #1202179 Change-Id: Ib753b1d090cb74b2d137c68f6899dad4ae2ec1ca --- novaclient/base.py | 19 +++++++++++++++--- novaclient/tests/v1_1/fakes.py | 28 +++++++++++++++++++++++++++ novaclient/tests/v1_1/test_images.py | 3 ++- novaclient/tests/v1_1/test_servers.py | 3 ++- novaclient/tests/v1_1/test_shell.py | 24 ++++++++++++++++++----- 5 files changed, 67 insertions(+), 10 deletions(-) diff --git a/novaclient/base.py b/novaclient/base.py index c37fa3486..adcf29d5a 100644 --- a/novaclient/base.py +++ b/novaclient/base.py @@ -22,6 +22,7 @@ Base utilities to build API operation managers and objects on top of. import abc import contextlib import hashlib +import inspect import os import six @@ -197,11 +198,23 @@ class ManagerWithFind(Manager): found = [] searches = kwargs.items() - for obj in self.list(): + detailed = True + if 'detailed' in inspect.getargspec(self.list).args: + detailed = ("human_id" not in kwargs and + "name" not in kwargs and + "display_name" not in kwargs) + listing = self.list(detailed=detailed) + else: + listing = self.list() + + for obj in listing: try: if all(getattr(obj, attr) == value - for (attr, value) in searches): - found.append(obj) + for (attr, value) in searches): + if detailed: + found.append(obj) + else: + found.append(self.get(obj.id)) except AttributeError: continue diff --git a/novaclient/tests/v1_1/fakes.py b/novaclient/tests/v1_1/fakes.py index cc24c7d42..7673317fc 100644 --- a/novaclient/tests/v1_1/fakes.py +++ b/novaclient/tests/v1_1/fakes.py @@ -1720,6 +1720,34 @@ class FakeHTTPClient(base_client.HTTPClient): "links": ''}], "metadata": {}}]}) + def get_volumes(self, **kw): + return (200, {}, {"volumes": [ + {"display_name": "Work", + "display_description": "volume for work", + "status": "ATTACHED", + "id": "15e59938-07d5-11e1-90e3-e3dffe0c5983", + "created_at": "2011-09-09T00:00:00Z", + "attached": "2011-11-11T00:00:00Z", + "size": 1024, + "attachments": [ + {"id": "3333", + "links": ''}], + "metadata": {}}]}) + + def get_volumes_15e59938_07d5_11e1_90e3_e3dffe0c5983(self, **kw): + return (200, {}, {"volume": + {"display_name": "Work", + "display_description": "volume for work", + "status": "ATTACHED", + "id": "15e59938-07d5-11e1-90e3-e3dffe0c5983", + "created_at": "2011-09-09T00:00:00Z", + "attached": "2011-11-11T00:00:00Z", + "size": 1024, + "attachments": [ + {"id": "3333", + "links": ''}], + "metadata": {}}}) + def post_volumes(self, **kw): return (200, {}, {"volume": {"status": "creating", diff --git a/novaclient/tests/v1_1/test_images.py b/novaclient/tests/v1_1/test_images.py index b545cedd5..5f9cfacd0 100644 --- a/novaclient/tests/v1_1/test_images.py +++ b/novaclient/tests/v1_1/test_images.py @@ -45,7 +45,8 @@ class ImagesTest(utils.TestCase): def test_find(self): i = cs.images.find(name="CentOS 5.2") self.assertEqual(i.id, 1) - cs.assert_called('GET', '/images/detail') + cs.assert_called('GET', '/images', pos=-2) + cs.assert_called('GET', '/images/1', pos=-1) iml = cs.images.findall(status='SAVING') self.assertEqual(len(iml), 1) diff --git a/novaclient/tests/v1_1/test_servers.py b/novaclient/tests/v1_1/test_servers.py index 6fec98bac..e04e7887b 100644 --- a/novaclient/tests/v1_1/test_servers.py +++ b/novaclient/tests/v1_1/test_servers.py @@ -171,7 +171,8 @@ class ServersTest(utils.TestCase): def test_find(self): server = cs.servers.find(name='sample-server') - cs.assert_called('GET', '/servers/detail') + cs.assert_called('GET', '/servers', pos=-2) + cs.assert_called('GET', '/servers/1234', pos=-1) self.assertEqual(server.name, 'sample-server') self.assertRaises(exceptions.NoUniqueMatch, cs.servers.find, diff --git a/novaclient/tests/v1_1/test_shell.py b/novaclient/tests/v1_1/test_shell.py index 5cf99d6e2..1ca22c62f 100644 --- a/novaclient/tests/v1_1/test_shell.py +++ b/novaclient/tests/v1_1/test_shell.py @@ -536,16 +536,21 @@ class ShellTest(utils.TestCase): def test_rebuild(self): self.run_command('rebuild sample-server 1') + self.assert_called('GET', '/servers', pos=-8) + self.assert_called('GET', '/servers/1234', pos=-7) + self.assert_called('GET', '/images/1', pos=-6) self.assert_called('POST', '/servers/1234/action', - {'rebuild': {'imageRef': 1}}, pos=-4) - self.assert_called('GET', '/servers/detail', pos=-3) + {'rebuild': {'imageRef': 1}}, pos=-5) self.assert_called('GET', '/flavors/1', pos=-2) self.assert_called('GET', '/images/2') self.run_command('rebuild sample-server 1 --rebuild-password asdf') + self.assert_called('GET', '/servers', pos=-8) + self.assert_called('GET', '/servers/1234', pos=-7) + self.assert_called('GET', '/images/1', pos=-6) self.assert_called('POST', '/servers/1234/action', {'rebuild': {'imageRef': 1, 'adminPass': 'asdf'}}, - pos=-4) + pos=-5) self.assert_called('GET', '/flavors/1', pos=-2) self.assert_called('GET', '/images/2') @@ -668,7 +673,11 @@ class ShellTest(utils.TestCase): self.assert_called('DELETE', '/servers/1234', pos=-3) self.assert_called('DELETE', '/servers/5678', pos=-1) self.run_command('delete sample-server sample-server2') - self.assert_called('DELETE', '/servers/1234', pos=-3) + self.assert_called('GET', '/servers', pos=-6) + self.assert_called('GET', '/servers/1234', pos=-5) + self.assert_called('DELETE', '/servers/1234', pos=-4) + self.assert_called('GET', '/servers', pos=-3) + self.assert_called('GET', '/servers/5678', pos=-2) self.assert_called('DELETE', '/servers/5678', pos=-1) def test_delete_two_with_one_nonexistent(self): @@ -1518,7 +1527,12 @@ class ShellTest(utils.TestCase): def test_volume_show(self): self.run_command('volume-show Work') - self.assert_called('GET', '/volumes/detail') + self.assert_called('GET', '/volumes', pos=-2) + self.assert_called( + 'GET', + '/volumes/15e59938-07d5-11e1-90e3-e3dffe0c5983', + pos=-1 + ) def test_volume_create(self): self.run_command('volume-create 2 --display-name Work')