diff --git a/doc/source/users/resources/compute/v2/flavor.rst b/doc/source/users/resources/compute/v2/flavor.rst index a414b4fb..9f62f96f 100644 --- a/doc/source/users/resources/compute/v2/flavor.rst +++ b/doc/source/users/resources/compute/v2/flavor.rst @@ -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: diff --git a/openstack/compute/v2/_proxy.py b/openstack/compute/v2/_proxy.py index 4fcdfb23..dd158fae 100644 --- a/openstack/compute/v2/_proxy.py +++ b/openstack/compute/v2/_proxy.py @@ -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) diff --git a/openstack/compute/v2/flavor.py b/openstack/compute/v2/flavor.py index 3388ec20..e84263d8 100644 --- a/openstack/compute/v2/flavor.py +++ b/openstack/compute/v2/flavor.py @@ -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) diff --git a/openstack/tests/compute/v2/test_flavor.py b/openstack/tests/compute/v2/test_flavor.py index 05d5a807..840be672 100644 --- a/openstack/tests/compute/v2/test_flavor.py +++ b/openstack/tests/compute/v2/test_flavor.py @@ -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) diff --git a/openstack/tests/compute/v2/test_proxy.py b/openstack/tests/compute/v2/test_proxy.py index ad286b71..cfd36700 100644 --- a/openstack/tests/compute/v2/test_proxy.py +++ b/openstack/tests/compute/v2/test_proxy.py @@ -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', diff --git a/openstack/tests/test_proxy_base.py b/openstack/tests/test_proxy_base.py index d4a1fbf1..b1daccde 100644 --- a/openstack/tests/test_proxy_base.py +++ b/openstack/tests/test_proxy_base.py @@ -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)