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
This commit is contained in:
David Shrewsbury 2015-04-23 14:10:35 -04:00
parent b7462cef4f
commit 746a1a84d7
3 changed files with 85 additions and 27 deletions

View File

@ -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):

View File

@ -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):

View File

@ -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):