Merge "Show project access for volume type"
This commit is contained in:
commit
3e1d54299f
@ -145,7 +145,6 @@ volume type show
|
||||
|
||||
Display volume type details
|
||||
|
||||
|
||||
.. program:: volume type show
|
||||
.. code:: bash
|
||||
|
||||
|
@ -76,6 +76,38 @@ class FakeTransfer(object):
|
||||
return transfer
|
||||
|
||||
|
||||
class FakeTypeAccess(object):
|
||||
"""Fake one or more volume type access."""
|
||||
|
||||
@staticmethod
|
||||
def create_one_type_access(attrs=None):
|
||||
"""Create a fake volume type access for project.
|
||||
|
||||
:param Dictionary attrs:
|
||||
A dictionary with all attributes
|
||||
:return:
|
||||
A FakeResource object, with Volume_type_ID and Project_ID.
|
||||
"""
|
||||
if attrs is None:
|
||||
attrs = {}
|
||||
|
||||
# Set default attributes.
|
||||
type_access_attrs = {
|
||||
'volume_type_id': 'volume-type-id-' + uuid.uuid4().hex,
|
||||
'project_id': 'project-id-' + uuid.uuid4().hex,
|
||||
}
|
||||
|
||||
# Overwrite default attributes.
|
||||
type_access_attrs.update(attrs)
|
||||
|
||||
type_access = fakes.FakeResource(
|
||||
None,
|
||||
type_access_attrs,
|
||||
loaded=True)
|
||||
|
||||
return type_access
|
||||
|
||||
|
||||
class FakeServiceClient(object):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
@ -666,6 +698,7 @@ class FakeType(object):
|
||||
"name": 'type-name-' + uuid.uuid4().hex,
|
||||
"description": 'type-description-' + uuid.uuid4().hex,
|
||||
"extra_specs": {"foo": "bar"},
|
||||
"is_public": True,
|
||||
}
|
||||
|
||||
# Overwrite default attributes.
|
||||
|
@ -13,6 +13,7 @@
|
||||
#
|
||||
|
||||
import copy
|
||||
import mock
|
||||
|
||||
from osc_lib import exceptions
|
||||
from osc_lib import utils
|
||||
@ -46,6 +47,7 @@ class TestTypeCreate(TestType):
|
||||
columns = (
|
||||
'description',
|
||||
'id',
|
||||
'is_public',
|
||||
'name',
|
||||
)
|
||||
|
||||
@ -56,6 +58,7 @@ class TestTypeCreate(TestType):
|
||||
self.data = (
|
||||
self.new_volume_type.description,
|
||||
self.new_volume_type.id,
|
||||
True,
|
||||
self.new_volume_type.name,
|
||||
)
|
||||
|
||||
@ -357,8 +360,10 @@ class TestTypeSet(TestType):
|
||||
class TestTypeShow(TestType):
|
||||
|
||||
columns = (
|
||||
'access_project_ids',
|
||||
'description',
|
||||
'id',
|
||||
'is_public',
|
||||
'name',
|
||||
'properties',
|
||||
)
|
||||
@ -368,8 +373,10 @@ class TestTypeShow(TestType):
|
||||
|
||||
self.volume_type = volume_fakes.FakeType.create_one_type()
|
||||
self.data = (
|
||||
None,
|
||||
self.volume_type.description,
|
||||
self.volume_type.id,
|
||||
True,
|
||||
self.volume_type.name,
|
||||
utils.format_dict(self.volume_type.extra_specs)
|
||||
)
|
||||
@ -394,6 +401,71 @@ class TestTypeShow(TestType):
|
||||
self.assertEqual(self.columns, columns)
|
||||
self.assertEqual(self.data, data)
|
||||
|
||||
def test_type_show_with_access(self):
|
||||
arglist = [
|
||||
self.volume_type.id
|
||||
]
|
||||
verifylist = [
|
||||
("volume_type", self.volume_type.id)
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
private_type = volume_fakes.FakeType.create_one_type(
|
||||
attrs={'is_public': False})
|
||||
type_access_list = volume_fakes.FakeTypeAccess.create_one_type_access()
|
||||
with mock.patch.object(self.types_mock, 'get',
|
||||
return_value=private_type):
|
||||
with mock.patch.object(self.types_access_mock, 'list',
|
||||
return_value=[type_access_list]):
|
||||
columns, data = self.cmd.take_action(parsed_args)
|
||||
self.types_mock.get.assert_called_once_with(
|
||||
self.volume_type.id)
|
||||
self.types_access_mock.list.assert_called_once_with(
|
||||
private_type.id)
|
||||
|
||||
self.assertEqual(self.columns, columns)
|
||||
private_type_data = (
|
||||
utils.format_list([type_access_list.project_id]),
|
||||
private_type.description,
|
||||
private_type.id,
|
||||
private_type.is_public,
|
||||
private_type.name,
|
||||
utils.format_dict(private_type.extra_specs)
|
||||
)
|
||||
self.assertEqual(private_type_data, data)
|
||||
|
||||
def test_type_show_with_list_access_exec(self):
|
||||
arglist = [
|
||||
self.volume_type.id
|
||||
]
|
||||
verifylist = [
|
||||
("volume_type", self.volume_type.id)
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
private_type = volume_fakes.FakeType.create_one_type(
|
||||
attrs={'is_public': False})
|
||||
with mock.patch.object(self.types_mock, 'get',
|
||||
return_value=private_type):
|
||||
with mock.patch.object(self.types_access_mock, 'list',
|
||||
side_effect=Exception()):
|
||||
columns, data = self.cmd.take_action(parsed_args)
|
||||
self.types_mock.get.assert_called_once_with(
|
||||
self.volume_type.id)
|
||||
self.types_access_mock.list.assert_called_once_with(
|
||||
private_type.id)
|
||||
|
||||
self.assertEqual(self.columns, columns)
|
||||
private_type_data = (
|
||||
None,
|
||||
private_type.description,
|
||||
private_type.id,
|
||||
private_type.is_public,
|
||||
private_type.name,
|
||||
utils.format_dict(private_type.extra_specs)
|
||||
)
|
||||
self.assertEqual(private_type_data, data)
|
||||
|
||||
|
||||
class TestTypeUnset(TestType):
|
||||
|
||||
|
@ -282,8 +282,24 @@ class ShowVolumeType(command.ShowOne):
|
||||
volume_client = self.app.client_manager.volume
|
||||
volume_type = utils.find_resource(
|
||||
volume_client.volume_types, parsed_args.volume_type)
|
||||
properties = utils.format_dict(volume_type._info.pop('extra_specs'))
|
||||
properties = utils.format_dict(
|
||||
volume_type._info.pop('extra_specs', {}))
|
||||
volume_type._info.update({'properties': properties})
|
||||
access_project_ids = None
|
||||
if not volume_type.is_public:
|
||||
try:
|
||||
volume_type_access = volume_client.volume_type_access.list(
|
||||
volume_type.id)
|
||||
project_ids = [utils.get_field(item, 'project_id')
|
||||
for item in volume_type_access]
|
||||
# TODO(Rui Chen): This format list case can be removed after
|
||||
# patch https://review.openstack.org/#/c/330223/ merged.
|
||||
access_project_ids = utils.format_list(project_ids)
|
||||
except Exception as e:
|
||||
msg = _('Failed to get access project list for volume type '
|
||||
'%(type)s: %(e)s')
|
||||
LOG.error(msg % {'type': volume_type.id, 'e': e})
|
||||
volume_type._info.update({'access_project_ids': access_project_ids})
|
||||
return zip(*sorted(six.iteritems(volume_type._info)))
|
||||
|
||||
|
||||
|
@ -0,0 +1,10 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Show project access details for private volume type.
|
||||
|
||||
An user can list projects which have access to
|
||||
a specific private volume type by using
|
||||
``volume type show <volume-type>``
|
||||
|
||||
[Bug `1554891 <https://bugs.launchpad.net/python-openstackclient/+bug/1554891>`_]
|
Loading…
Reference in New Issue
Block a user