From 746a1a84d750574cbb8dc603ad90cd546bf86a67 Mon Sep 17 00:00:00 2001 From: David Shrewsbury Date: Thu, 23 Apr 2015 14:10:35 -0400 Subject: [PATCH] Update flavor API for new get/list/search API The flavor API methods now support the get/list/search style interface. This required changing the format of the flavor list from a list of {flavor_id: flavor_obj} dicts to a list of converted flavor dicts. Change-Id: I593493c16f441470e9f210592c4baf2fa81664b5 --- shade/__init__.py | 53 ++++++++++++++++++-------------- shade/tests/unit/test_caching.py | 9 +++--- shade/tests/unit/test_shade.py | 50 ++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 27 deletions(-) diff --git a/shade/__init__.py b/shade/__init__.py index 6fce7707d..535f804ed 100644 --- a/shade/__init__.py +++ b/shade/__init__.py @@ -671,36 +671,30 @@ class OpenStackCloud(object): def get_region(self): return self.region_name - @property - def flavor_cache(self): - return self.get_flavor_cache() - - @_cache_on_arguments() - def get_flavor_cache(self): - return {flavor.id: flavor for flavor in - self.manager.submitTask(_tasks.FlavorList())} - def get_flavor_name(self, flavor_id): - flavor = self.flavor_cache.get(flavor_id, None) + flavor = self.get_flavor(flavor_id) if flavor: - return flavor.name - return None - - def get_flavor(self, name_or_id): - for id, flavor in self.flavor_cache.items(): - if name_or_id in (id, flavor.name): - return flavor + return flavor['name'] return None def get_flavor_by_ram(self, ram, include=None): - for flavor in sorted( - self.flavor_cache.values(), - key=operator.attrgetter('ram')): - if (flavor.ram >= ram and - (not include or include in flavor.name)): + """Get a flavor based on amount of RAM available. + + Finds the flavor with the least amount of RAM that is at least + as much as the specified amount. If `include` is given, further + filter based on matching flavor name. + + :param int ram: Minimum amount of RAM. + :param string include: If given, will return a flavor whose name + contains this string as a substring. + """ + flavors = self.list_flavors() + for flavor in sorted(flavors, key=operator.itemgetter('ram')): + if (flavor['ram'] >= ram and + (not include or include in flavor['name'])): return flavor raise OpenStackCloudException( - "Cloud not find a flavor with {ram} and '{include}'".format( + "Could not find a flavor with {ram} and '{include}'".format( ram=ram, include=include)) def get_endpoint(self, service_type): @@ -826,6 +820,10 @@ class OpenStackCloud(object): volumes = self.list_volumes() return self._filter_list(volumes, name_or_id, filters) + def search_flavors(self, name_or_id=None, filters=None): + flavors = self.list_flavors() + return self._filter_list(flavors, name_or_id, filters) + def list_networks(self): return self.manager.submitTask(_tasks.NetworkList())['networks'] @@ -844,6 +842,12 @@ class OpenStackCloud(object): self.manager.submitTask(_tasks.VolumeList()) ) + @_cache_on_arguments() + def list_flavors(self): + return meta.obj_list_to_dict( + self.manager.submitTask(_tasks.FlavorList()) + ) + def get_network(self, name_or_id, filters=None): return self._get_entity(self.search_networks, name_or_id, filters) @@ -856,6 +860,9 @@ class OpenStackCloud(object): def get_volume(self, name_or_id, filters=None): return self._get_entity(self.search_volumes, name_or_id, filters) + def get_flavor(self, name_or_id, filters=None): + return self._get_entity(self.search_flavors, name_or_id, filters) + # TODO(Shrews): This will eventually need to support tenant ID and # provider networks, which are admin-level params. def create_network(self, name, shared=False, admin_state_up=True): diff --git a/shade/tests/unit/test_caching.py b/shade/tests/unit/test_caching.py index 9b12f7ffe..7f2c1784c 100644 --- a/shade/tests/unit/test_caching.py +++ b/shade/tests/unit/test_caching.py @@ -216,17 +216,18 @@ class TestMemoryCache(base.TestCase): self.assertTrue(keystone_mock.users.delete.was_called) @mock.patch.object(shade.OpenStackCloud, 'nova_client') - def test_get_flavor_cache(self, nova_mock): + def test_list_flavors(self, nova_mock): nova_mock.flavors.list.return_value = [] - self.assertEqual({}, self.cloud.get_flavor_cache()) + self.assertEqual([], self.cloud.list_flavors()) class Flavor(object): id = '555' name = 'vanilla' fake_flavor = Flavor() + fake_flavor_dict = meta.obj_to_dict(fake_flavor) nova_mock.flavors.list.return_value = [fake_flavor] - self.cloud.get_flavor_cache.invalidate(self.cloud) - self.assertEqual({'555': fake_flavor}, self.cloud.get_flavor_cache()) + self.cloud.list_flavors.invalidate(self.cloud) + self.assertEqual([fake_flavor_dict], self.cloud.list_flavors()) @mock.patch.object(shade.OpenStackCloud, 'glance_client') def test_list_images(self, glance_mock): diff --git a/shade/tests/unit/test_shade.py b/shade/tests/unit/test_shade.py index 216aff6d3..c0516ede2 100644 --- a/shade/tests/unit/test_shade.py +++ b/shade/tests/unit/test_shade.py @@ -15,6 +15,7 @@ import mock import shade +from shade import meta from shade.tests.unit import base @@ -193,6 +194,55 @@ class TestShade(base.TestCase): self.cloud.update_subnet('123', subnet_name='goofy') self.assertTrue(mock_client.update_subnet.called) + @mock.patch.object(shade.OpenStackCloud, 'list_flavors') + def test_get_flavor_by_ram(self, mock_list): + class Flavor1(object): + id = '1' + name = 'vanilla ice cream' + ram = 100 + + class Flavor2(object): + id = '2' + name = 'chocolate ice cream' + ram = 200 + + vanilla = meta.obj_to_dict(Flavor1()) + chocolate = meta.obj_to_dict(Flavor2()) + mock_list.return_value = [vanilla, chocolate] + flavor = self.cloud.get_flavor_by_ram(ram=150) + self.assertEquals(chocolate, flavor) + + @mock.patch.object(shade.OpenStackCloud, 'list_flavors') + def test_get_flavor_by_ram_and_include(self, mock_list): + class Flavor1(object): + id = '1' + name = 'vanilla ice cream' + ram = 100 + + class Flavor2(object): + id = '2' + name = 'chocolate ice cream' + ram = 200 + + class Flavor3(object): + id = '3' + name = 'strawberry ice cream' + ram = 250 + + vanilla = meta.obj_to_dict(Flavor1()) + chocolate = meta.obj_to_dict(Flavor2()) + strawberry = meta.obj_to_dict(Flavor3()) + mock_list.return_value = [vanilla, chocolate, strawberry] + flavor = self.cloud.get_flavor_by_ram(ram=150, include='strawberry') + self.assertEquals(strawberry, flavor) + + @mock.patch.object(shade.OpenStackCloud, 'list_flavors') + def test_get_flavor_by_ram_not_found(self, mock_list): + mock_list.return_value = [] + self.assertRaises(shade.OpenStackCloudException, + self.cloud.get_flavor_by_ram, + ram=100) + class TestShadeOperator(base.TestCase):