Add FlavorDetail for extra information
The current Flavor can only receive the basics returned by a GET /flavors, which is just name, id, and links. A call to /flavors/detail gives a number of added details, some of which were already in the Flavor resource but not set. This change follows with how a few other basic/detail requests were done, and exposes it through the proxy with details coming by default (since they're likely expected, and most useful). This change depends on list handling nonpaginated bodies.t Change-Id: I7bebbdc2aa86c0300b86a8b8daa34888121dbcff
This commit is contained in:
committed by
Brian Curtin
parent
d76624895c
commit
9ef6dc3bdc
@@ -10,3 +10,12 @@ The ``Flavor`` class inherits from :class:`~openstack.resource.Resource`.
|
||||
|
||||
.. autoclass:: openstack.compute.v2.flavor.Flavor
|
||||
:members:
|
||||
|
||||
The FlavorDetail Class
|
||||
----------------------
|
||||
|
||||
The ``FlavorDetail`` class inherits from
|
||||
:class:`~openstack.compute.v2.flavor.Flavor`.
|
||||
|
||||
.. autoclass:: openstack.compute.v2.flavor.FlavorDetail
|
||||
:members:
|
||||
|
||||
@@ -31,20 +31,30 @@ class Proxy(object):
|
||||
def list_extensions(self):
|
||||
return extension.Extension.list(self.session)
|
||||
|
||||
def find_flavor(self, name_or_id):
|
||||
return flavor.Flavor.find(self.session, name_or_id)
|
||||
|
||||
def create_flavor(self, **data):
|
||||
return flavor.Flavor(data).create(self.session)
|
||||
|
||||
def delete_flavor(self, **data):
|
||||
flavor.Flavor(data).delete(self.session)
|
||||
|
||||
def find_flavor(self, name_or_id):
|
||||
return flavor.Flavor.find(self.session, name_or_id)
|
||||
|
||||
def get_flavor(self, **data):
|
||||
return flavor.Flavor(data).get(self.session)
|
||||
|
||||
def list_flavors(self, **params):
|
||||
return flavor.Flavor.list(self.session, **params)
|
||||
def list_flavors(self, details=True, **params):
|
||||
"""Return a generator of flavors
|
||||
|
||||
:param bool details: When ``True``, returns
|
||||
:class:`~openstack.compute.v2.flavor.FlavorDetail` objects,
|
||||
otherwise :class:`~openstack.compute.v2.flavor.Flavor`.
|
||||
*Default: ``True``*
|
||||
|
||||
:returns: A generator of flavor objects
|
||||
"""
|
||||
flv = flavor.FlavorDetail if details else flavor.Flavor
|
||||
return flv.list(self.session, paginate=False, **params)
|
||||
|
||||
def update_flavor(self, **data):
|
||||
return flavor.Flavor(data).update(self.session)
|
||||
|
||||
@@ -28,17 +28,30 @@ class Flavor(resource.Resource):
|
||||
allow_list = True
|
||||
|
||||
# Properties
|
||||
#: Size of the disk this flavor offers. *Type: int*
|
||||
disk = resource.prop('disk', type=int)
|
||||
#: ``True`` if this is a publicly visible flavor. ``False`` if this is
|
||||
#: a private image. *Type: bool*
|
||||
is_public = resource.prop('os-flavor-access:is_public', type=bool)
|
||||
#: Links pertaining to this flavor. This is a list of dictionaries,
|
||||
#: each including keys ``href`` and ``rel``.
|
||||
links = resource.prop('links')
|
||||
#: The name of this flavor.
|
||||
name = resource.prop('name')
|
||||
|
||||
|
||||
class FlavorDetail(Flavor):
|
||||
base_path = '/flavors/detail'
|
||||
|
||||
#: Size of the disk this flavor offers. *Type: int*
|
||||
disk = resource.prop('disk', type=int)
|
||||
#: ``True`` if this is a publicly visible flavor. ``False`` if this is
|
||||
#: a private image. *Type: bool*
|
||||
is_public = resource.prop('os-flavor-access:is_public', type=bool)
|
||||
#: The amount of RAM (in MB) this flavor offers. *Type: int*
|
||||
ram = resource.prop('ram', type=int)
|
||||
#: The number of virtual CPUs this flavor offers. *Type: int*
|
||||
vcpus = resource.prop('vcpus', type=int)
|
||||
#: Size of the swap partitions.
|
||||
swap = resource.prop('swap')
|
||||
#: Size of the ephemeral data disk attached to this server. *Type: int*
|
||||
ephemeral = resource.prop('OS-FLV-EXT-DATA:ephemeral', type=int)
|
||||
#: ``True`` if this flavor is disabled, ``False`` if not.
|
||||
disabled = resource.prop('OS-FLV-DISABLED:disabled')
|
||||
#: The bandwidth scaling factor this flavor receives on the network.
|
||||
rxtx_factor = resource.prop('rxtx_factor', type=float)
|
||||
|
||||
@@ -15,16 +15,26 @@ import testtools
|
||||
from openstack.compute.v2 import flavor
|
||||
|
||||
IDENTIFIER = 'IDENTIFIER'
|
||||
EXAMPLE = {
|
||||
'disk': 1,
|
||||
BASIC_EXAMPLE = {
|
||||
'id': IDENTIFIER,
|
||||
'os-flavor-access:is_public': True,
|
||||
'links': '4',
|
||||
'name': '5',
|
||||
'ram': 6,
|
||||
'vcpus': 7,
|
||||
}
|
||||
|
||||
DETAILS = {
|
||||
'disk': 1,
|
||||
'os-flavor-access:is_public': True,
|
||||
'ram': 3,
|
||||
'vcpus': 4,
|
||||
'swap': 5,
|
||||
'OS-FLV-EXT-DATA:ephemeral': 6,
|
||||
'OS-FLV-DISABLED:disabled': False,
|
||||
'rxtx_factor': 8.0
|
||||
}
|
||||
|
||||
DETAIL_EXAMPLE = BASIC_EXAMPLE.copy()
|
||||
DETAIL_EXAMPLE.update(DETAILS)
|
||||
|
||||
|
||||
class TestFlavor(testtools.TestCase):
|
||||
|
||||
@@ -40,12 +50,37 @@ class TestFlavor(testtools.TestCase):
|
||||
self.assertTrue(sot.allow_delete)
|
||||
self.assertTrue(sot.allow_list)
|
||||
|
||||
def test_make_it(self):
|
||||
sot = flavor.Flavor(EXAMPLE)
|
||||
self.assertEqual(EXAMPLE['disk'], sot.disk)
|
||||
self.assertEqual(EXAMPLE['id'], sot.id)
|
||||
self.assertEqual(EXAMPLE['os-flavor-access:is_public'], sot.is_public)
|
||||
self.assertEqual(EXAMPLE['links'], sot.links)
|
||||
self.assertEqual(EXAMPLE['name'], sot.name)
|
||||
self.assertEqual(EXAMPLE['ram'], sot.ram)
|
||||
self.assertEqual(EXAMPLE['vcpus'], sot.vcpus)
|
||||
def test_make_basic(self):
|
||||
sot = flavor.Flavor(BASIC_EXAMPLE)
|
||||
self.assertEqual(BASIC_EXAMPLE['id'], sot.id)
|
||||
self.assertEqual(BASIC_EXAMPLE['links'], sot.links)
|
||||
self.assertEqual(BASIC_EXAMPLE['name'], sot.name)
|
||||
|
||||
def test_detail(self):
|
||||
sot = flavor.FlavorDetail()
|
||||
self.assertEqual('flavor', sot.resource_key)
|
||||
self.assertEqual('flavors', sot.resources_key)
|
||||
self.assertEqual('/flavors/detail', sot.base_path)
|
||||
self.assertEqual('compute', sot.service.service_type)
|
||||
self.assertTrue(sot.allow_create)
|
||||
self.assertTrue(sot.allow_retrieve)
|
||||
self.assertTrue(sot.allow_update)
|
||||
self.assertTrue(sot.allow_delete)
|
||||
self.assertTrue(sot.allow_list)
|
||||
|
||||
def test_make_detail(self):
|
||||
sot = flavor.FlavorDetail(DETAIL_EXAMPLE)
|
||||
self.assertEqual(DETAIL_EXAMPLE['id'], sot.id)
|
||||
self.assertEqual(DETAIL_EXAMPLE['links'], sot.links)
|
||||
self.assertEqual(DETAIL_EXAMPLE['name'], sot.name)
|
||||
self.assertEqual(DETAIL_EXAMPLE['disk'], sot.disk)
|
||||
self.assertEqual(DETAIL_EXAMPLE['os-flavor-access:is_public'],
|
||||
sot.is_public)
|
||||
self.assertEqual(DETAIL_EXAMPLE['ram'], sot.ram)
|
||||
self.assertEqual(DETAIL_EXAMPLE['vcpus'], sot.vcpus)
|
||||
self.assertEqual(DETAIL_EXAMPLE['swap'], sot.swap)
|
||||
self.assertEqual(DETAIL_EXAMPLE['OS-FLV-EXT-DATA:ephemeral'],
|
||||
sot.ephemeral)
|
||||
self.assertEqual(DETAIL_EXAMPLE['OS-FLV-DISABLED:disabled'],
|
||||
sot.disabled)
|
||||
self.assertEqual(DETAIL_EXAMPLE['rxtx_factor'], sot.rxtx_factor)
|
||||
|
||||
@@ -43,9 +43,17 @@ class TestComputeProxy(test_proxy_base.TestProxyBase):
|
||||
self.verify_get('openstack.compute.v2.flavor.Flavor.get',
|
||||
self.proxy.get_flavor)
|
||||
|
||||
def test_flavor_list(self):
|
||||
def test_flavor_list_basic(self):
|
||||
self.verify_list('openstack.compute.v2.flavor.Flavor.list',
|
||||
self.proxy.list_flavors)
|
||||
self.proxy.list_flavors,
|
||||
method_kwargs={"details": False},
|
||||
expected_kwargs={"paginate": False})
|
||||
|
||||
def test_flavor_list_detail(self):
|
||||
self.verify_list('openstack.compute.v2.flavor.FlavorDetail.list',
|
||||
self.proxy.list_flavors,
|
||||
method_kwargs={"details": True},
|
||||
expected_kwargs={"paginate": False})
|
||||
|
||||
def test_flavor_update(self):
|
||||
self.verify_update('openstack.compute.v2.flavor.Flavor.update',
|
||||
|
||||
@@ -20,31 +20,46 @@ class TestProxyBase(base.TestCase):
|
||||
super(TestProxyBase, self).setUp()
|
||||
self.session = mock.MagicMock()
|
||||
|
||||
def _verify(self, mock_method, test_method, method_args=None,
|
||||
expected=None):
|
||||
def _verify(self, mock_method, test_method,
|
||||
method_args=None, method_kwargs=None,
|
||||
expected_args=None, expected_kwargs=None,
|
||||
expected_result=None):
|
||||
with mock.patch(mock_method) as mocked:
|
||||
mocked.return_value = expected
|
||||
if method_args is not None:
|
||||
self.assertEqual(expected, test_method(method_args))
|
||||
mocked.assert_called_with(self.session, method_args)
|
||||
mocked.return_value = expected_result
|
||||
if any([method_args, method_kwargs]):
|
||||
method_args = method_args or ()
|
||||
method_kwargs = method_kwargs or {}
|
||||
expected_args = expected_args or ()
|
||||
expected_kwargs = expected_kwargs or {}
|
||||
|
||||
self.assertEqual(expected_result, test_method(*method_args,
|
||||
**method_kwargs))
|
||||
mocked.assert_called_with(self.session,
|
||||
*expected_args, **expected_kwargs)
|
||||
else:
|
||||
self.assertEqual(expected, test_method())
|
||||
self.assertEqual(expected_result, test_method())
|
||||
mocked.assert_called_with(self.session)
|
||||
|
||||
def verify_create(self, mock_method, test_method):
|
||||
self._verify(mock_method, test_method, expected="result")
|
||||
def verify_create(self, mock_method, test_method, **kwargs):
|
||||
self._verify(mock_method, test_method, expected_result="result",
|
||||
**kwargs)
|
||||
|
||||
def verify_delete(self, mock_method, test_method):
|
||||
self._verify(mock_method, test_method)
|
||||
def verify_delete(self, mock_method, test_method, **kwargs):
|
||||
self._verify(mock_method, test_method, **kwargs)
|
||||
|
||||
def verify_get(self, mock_method, test_method):
|
||||
self._verify(mock_method, test_method, expected="result")
|
||||
def verify_get(self, mock_method, test_method, **kwargs):
|
||||
self._verify(mock_method, test_method, expected_result="result",
|
||||
**kwargs)
|
||||
|
||||
def verify_find(self, mock_method, test_method):
|
||||
self._verify(mock_method, test_method, ["name_or_id"], "result")
|
||||
def verify_find(self, mock_method, test_method, **kwargs):
|
||||
self._verify(mock_method, test_method, method_args=["name_or_id"],
|
||||
expected_args=["name_or_id"], expected_result="result",
|
||||
**kwargs)
|
||||
|
||||
def verify_list(self, mock_method, test_method):
|
||||
self._verify(mock_method, test_method, expected=["result"])
|
||||
def verify_list(self, mock_method, test_method, **kwargs):
|
||||
self._verify(mock_method, test_method, expected_result=["result"],
|
||||
**kwargs)
|
||||
|
||||
def verify_update(self, mock_method, test_method):
|
||||
self._verify(mock_method, test_method, expected="result")
|
||||
def verify_update(self, mock_method, test_method, **kwargs):
|
||||
self._verify(mock_method, test_method, expected_result="result",
|
||||
**kwargs)
|
||||
|
||||
Reference in New Issue
Block a user