From 9d145e0be29c996015f9019307aef88a553e01ad Mon Sep 17 00:00:00 2001 From: Roberto Polli Date: Fri, 23 Dec 2016 14:52:20 +0100 Subject: [PATCH] Added list_flavor_access. Change-Id: Ia983486ec4d587dd436e6a9c0b443be8ce1a7102 --- doc/source/model.rst | 13 +++++++++ ...d-list_flavor_access-e038253e953e6586.yaml | 4 +++ shade/_tasks.py | 5 ++++ shade/_utils.py | 11 ++++++++ shade/operatorcloud.py | 17 ++++++++++++ shade/tests/functional/test_flavor.py | 6 +++++ shade/tests/unit/test_flavors.py | 27 +++++++++++++++++++ 7 files changed, 83 insertions(+) create mode 100644 releasenotes/notes/add-list_flavor_access-e038253e953e6586.yaml diff --git a/doc/source/model.rst b/doc/source/model.rst index 0f5f2aff9..e084578fd 100644 --- a/doc/source/model.rst +++ b/doc/source/model.rst @@ -87,6 +87,19 @@ A flavor for a Nova Server. extra_specs=dict(), properties=dict()) + +Flavor Access +------ + +An access entry for a Nova Flavor. + +.. code-block:: python + + FlavorAccess = dict( + flavor_id=str(), + project_id=str()) + + Image ----- diff --git a/releasenotes/notes/add-list_flavor_access-e038253e953e6586.yaml b/releasenotes/notes/add-list_flavor_access-e038253e953e6586.yaml new file mode 100644 index 000000000..12f289f8b --- /dev/null +++ b/releasenotes/notes/add-list_flavor_access-e038253e953e6586.yaml @@ -0,0 +1,4 @@ +--- +features: + - Add a list_flavor_access method to list all + the projects/tenants allowed to access a given flavor. diff --git a/shade/_tasks.py b/shade/_tasks.py index 95c0002cf..0f697758c 100644 --- a/shade/_tasks.py +++ b/shade/_tasks.py @@ -102,6 +102,11 @@ class FlavorGet(task_manager.Task): return client.nova_client.flavors.get(**self.args) +class FlavorListAccess(task_manager.Task): + def main(self, client): + return client.nova_client.flavor_access.list(**self.args) + + class FlavorAddAccess(task_manager.Task): def main(self, client): return client.nova_client.flavor_access.add_tenant_access( diff --git a/shade/_utils.py b/shade/_utils.py index 1e938fceb..9b2e94003 100644 --- a/shade/_utils.py +++ b/shade/_utils.py @@ -303,6 +303,17 @@ def normalize_roles(roles): return meta.obj_list_to_dict(ret) +def normalize_flavor_accesses(flavor_accesses): + """Normalize Flavor access list.""" + return [munch.Munch( + dict( + flavor_id=acl.get('flavor_id'), + project_id=acl.get('project_id') or acl.get('tenant_id'), + ) + ) for acl in flavor_accesses + ] + + def normalize_stacks(stacks): """ Normalize Stack Object """ for stack in stacks: diff --git a/shade/operatorcloud.py b/shade/operatorcloud.py index 5f8f4e0f0..20bf3b5eb 100644 --- a/shade/operatorcloud.py +++ b/shade/operatorcloud.py @@ -1593,6 +1593,23 @@ class OperatorCloud(openstackcloud.OpenStackCloud): """ self._mod_flavor_access('remove', flavor_id, project_id) + def list_flavor_access(self, flavor_id): + """List access from a private flavor for a project/tenant. + + :param string flavor_id: ID of the private flavor. + + :returns: a list of ``munch.Munch`` containing the access description + + :raises: OpenStackCloudException on operation error. + """ + with _utils.shade_exceptions("Error trying to list access from " + "flavor ID {flavor}".format( + flavor=flavor_id)): + projects = self.manager.submit_task( + _tasks.FlavorListAccess(flavor=flavor_id) + ) + return _utils.normalize_flavor_accesses(projects) + def create_role(self, name): """Create a Keystone role. diff --git a/shade/tests/functional/test_flavor.py b/shade/tests/functional/test_flavor.py index 90a21ab96..6dbe91cee 100644 --- a/shade/tests/functional/test_flavor.py +++ b/shade/tests/functional/test_flavor.py @@ -129,6 +129,12 @@ class TestFlavor(base.BaseFunctionalTestCase): self.assertEqual(1, len(flavors)) self.assertEqual(priv_flavor_name, flavors[0]['name']) + # Now see if the 'demo' user has access to it without needing + # the demo_cloud access. + acls = self.operator_cloud.list_flavor_access(new_flavor['id']) + self.assertEqual(1, len(acls)) + self.assertEqual(project['id'], acls[0]['project_id']) + # Now revoke the access and make sure we can't find it self.operator_cloud.remove_flavor_access(new_flavor['id'], project['id']) diff --git a/shade/tests/unit/test_flavors.py b/shade/tests/unit/test_flavors.py index d78b9fc5d..6943a9213 100644 --- a/shade/tests/unit/test_flavors.py +++ b/shade/tests/unit/test_flavors.py @@ -14,6 +14,7 @@ import mock +from shade.tests.fakes import FakeFlavor, FakeProject import shade from keystoneauth1.fixture import keystoneauth_betamax @@ -117,9 +118,35 @@ class TestFlavors(base.TestCase): flavor='flavor_id', tenant='tenant_id' ) + @mock.patch.object(shade.OpenStackCloud, 'nova_client') + def test_add_flavor_access_by_flavor(self, mock_nova): + flavor = FakeFlavor(id='flavor_id', name='flavor_name', ram=None) + tenant = FakeProject('tenant_id') + self.op_cloud.add_flavor_access(flavor, tenant) + mock_nova.flavor_access.add_tenant_access.assert_called_once_with( + flavor=flavor, tenant=tenant + ) + @mock.patch.object(shade.OpenStackCloud, 'nova_client') def test_remove_flavor_access(self, mock_nova): self.op_cloud.remove_flavor_access('flavor_id', 'tenant_id') mock_nova.flavor_access.remove_tenant_access.assert_called_once_with( flavor='flavor_id', tenant='tenant_id' ) + + @mock.patch.object(shade.OpenStackCloud, 'nova_client') + def test_list_flavor_access(self, mock_nova): + mock_nova.flavors.list.return_value = [FakeFlavor( + id='flavor_id', name='flavor_name', ram=None)] + self.op_cloud.list_flavor_access('flavor_id') + mock_nova.flavor_access.list.assert_called_once_with( + flavor='flavor_id' + ) + + @mock.patch.object(shade.OpenStackCloud, 'nova_client') + def test_list_flavor_access_by_flavor(self, mock_nova): + flavor = FakeFlavor(id='flavor_id', name='flavor_name', ram=None) + self.op_cloud.list_flavor_access(flavor) + mock_nova.flavor_access.list.assert_called_once_with( + flavor=flavor + )